1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
21 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
22 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
23 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
24 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
25 #include <basegfx/matrix/b2dhommatrixtools.hxx>
26 #include <basegfx/polygon/b2dpolygontools.hxx>
27 #include <basegfx/polygon/b2dpolygon.hxx>
28 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
29 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
30 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
31 #include <drawinglayer/geometry/viewinformation2d.hxx>
33 //////////////////////////////////////////////////////////////////////////////
35 using namespace com::sun::star
;
37 //////////////////////////////////////////////////////////////////////////////
41 sal_uInt32
calculateStepsForSvgGradient(const basegfx::BColor
& rColorA
, const basegfx::BColor
& rColorB
, double fDelta
, double fDiscreteUnit
)
43 // use color distance, assume to do every color step (full quality)
44 sal_uInt32
nSteps(basegfx::fround(rColorA
.getDistance(rColorB
) * 255.0));
48 // calc discrete length to change color all 1.5 disctete units (pixels)
49 const sal_uInt32
nDistSteps(basegfx::fround(fDelta
/ (fDiscreteUnit
* 1.5)));
51 nSteps
= std::min(nSteps
, nDistSteps
);
54 // roughly cut when too big or too small
55 nSteps
= std::min(nSteps
, sal_uInt32(255));
56 nSteps
= std::max(nSteps
, sal_uInt32(1));
60 } // end of anonymous namespace
62 //////////////////////////////////////////////////////////////////////////////
64 namespace drawinglayer
68 Primitive2DSequence
SvgGradientHelper::createSingleGradientEntryFill() const
70 const SvgGradientEntryVector
& rEntries
= getGradientEntries();
71 const sal_uInt32
nCount(rEntries
.size());
72 Primitive2DSequence xRetval
;
76 const SvgGradientEntry
& rSingleEntry
= rEntries
[nCount
- 1];
77 const double fOpacity(rSingleEntry
.getOpacity());
81 Primitive2DReference
xRef(
82 new PolyPolygonColorPrimitive2D(
84 rSingleEntry
.getColor()));
88 const Primitive2DSequence
aContent(&xRef
, 1);
90 xRef
= Primitive2DReference(
91 new UnifiedTransparencePrimitive2D(
96 xRetval
= Primitive2DSequence(&xRef
, 1);
101 OSL_ENSURE(false, "Single gradient entry construction without entry (!)");
107 void SvgGradientHelper::checkPreconditions()
109 mbPreconditionsChecked
= true;
110 const SvgGradientEntryVector
& rEntries
= getGradientEntries();
118 const sal_uInt32
nCount(rEntries
.size());
122 // fill with single existing color
127 // sort maGradientEntries when more than one
128 std::sort(maGradientEntries
.begin(), maGradientEntries
.end());
130 // gradient with at least two colors
131 bool bAllInvisible(true);
133 for(sal_uInt32
a(0); a
< nCount
; a
++)
135 const SvgGradientEntry
& rCandidate
= rEntries
[a
];
137 if(basegfx::fTools::equalZero(rCandidate
.getOpacity()))
140 mbFullyOpaque
= false;
142 else if(basegfx::fTools::equal(rCandidate
.getOpacity(), 1.0))
145 bAllInvisible
= false;
150 bAllInvisible
= false;
151 mbFullyOpaque
= false;
157 // all invisible, nothing to do
161 const basegfx::B2DRange
aPolyRange(getPolyPolygon().getB2DRange());
163 if(aPolyRange
.isEmpty())
165 // no range to fill, nothing to do
169 const double fPolyWidth(aPolyRange
.getWidth());
170 const double fPolyHeight(aPolyRange
.getHeight());
172 if(basegfx::fTools::equalZero(fPolyWidth
) || basegfx::fTools::equalZero(fPolyHeight
))
174 // no width/height to fill, nothing to do
178 mbCreatesContent
= true;
186 double SvgGradientHelper::createRun(
187 Primitive2DVector
& rTargetColor
,
188 Primitive2DVector
& rTargetOpacity
,
191 const SvgGradientEntryVector
& rEntries
,
192 sal_Int32 nOffset
) const
194 const sal_uInt32
nCount(rEntries
.size());
198 const SvgGradientEntry
& rStart
= rEntries
[0];
199 const bool bCreateStartPad(fPos
< 0.0 && Spread_pad
== getSpreadMethod());
200 const bool bCreateStartFill(rStart
.getOffset() > 0.0);
201 sal_uInt32
nIndex(0);
203 if(bCreateStartPad
|| bCreateStartFill
)
205 const SvgGradientEntry
aTemp(bCreateStartPad
? fPos
: 0.0, rStart
.getColor(), rStart
.getOpacity());
207 createAtom(rTargetColor
, rTargetOpacity
, aTemp
, rStart
, nOffset
);
208 fPos
= rStart
.getOffset();
211 while(fPos
< 1.0 && nIndex
+ 1 < nCount
)
213 const SvgGradientEntry
& rCandidateA
= rEntries
[nIndex
++];
214 const SvgGradientEntry
& rCandidateB
= rEntries
[nIndex
];
216 createAtom(rTargetColor
, rTargetOpacity
, rCandidateA
, rCandidateB
, nOffset
);
217 fPos
= rCandidateB
.getOffset();
220 const SvgGradientEntry
& rEnd
= rEntries
[nCount
- 1];
221 const bool bCreateEndPad(fPos
< fMax
&& Spread_pad
== getSpreadMethod());
222 const bool bCreateEndFill(rEnd
.getOffset() < 1.0);
224 if(bCreateEndPad
|| bCreateEndFill
)
226 fPos
= bCreateEndPad
? fMax
: 1.0;
227 const SvgGradientEntry
aTemp(fPos
, rEnd
.getColor(), rEnd
.getOpacity());
229 createAtom(rTargetColor
, rTargetOpacity
, rEnd
, aTemp
, nOffset
);
234 OSL_ENSURE(false, "GradientAtom creation without ColorStops (!)");
241 Primitive2DSequence
SvgGradientHelper::createResult(
242 const Primitive2DVector
& rTargetColor
,
243 const Primitive2DVector
& rTargetOpacity
,
244 const basegfx::B2DHomMatrix
& rUnitGradientToObject
,
247 Primitive2DSequence xRetval
;
248 const Primitive2DSequence
aTargetColorEntries(Primitive2DVectorToPrimitive2DSequence(rTargetColor
, bInvert
));
249 const Primitive2DSequence
aTargetOpacityEntries(Primitive2DVectorToPrimitive2DSequence(rTargetOpacity
, bInvert
));
251 if(aTargetColorEntries
.hasElements())
253 Primitive2DReference xRefContent
;
255 if(aTargetOpacityEntries
.hasElements())
257 const Primitive2DReference xRefOpacity
= new TransparencePrimitive2D(
259 aTargetOpacityEntries
);
261 xRefContent
= new TransformPrimitive2D(
262 rUnitGradientToObject
,
263 Primitive2DSequence(&xRefOpacity
, 1));
267 xRefContent
= new TransformPrimitive2D(
268 rUnitGradientToObject
,
269 aTargetColorEntries
);
272 xRefContent
= new MaskPrimitive2D(
274 Primitive2DSequence(&xRefContent
, 1));
276 xRetval
= Primitive2DSequence(&xRefContent
, 1);
282 SvgGradientHelper::SvgGradientHelper(
283 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
284 const SvgGradientEntryVector
& rGradientEntries
,
285 const basegfx::B2DPoint
& rStart
,
286 bool bUseUnitCoordinates
,
287 SpreadMethod aSpreadMethod
)
288 : maPolyPolygon(rPolyPolygon
),
289 maGradientEntries(rGradientEntries
),
291 maSpreadMethod(aSpreadMethod
),
292 mbPreconditionsChecked(false),
293 mbCreatesContent(false),
294 mbSingleEntry(false),
296 mbUseUnitCoordinates(bUseUnitCoordinates
)
300 bool SvgGradientHelper::equalTo(const SvgGradientHelper
& rSvgGradientHelper
) const
302 const SvgGradientHelper
& rCompare
= static_cast< const SvgGradientHelper
& >(rSvgGradientHelper
);
304 return (getPolyPolygon() == rCompare
.getPolyPolygon()
305 && getGradientEntries() == rCompare
.getGradientEntries()
306 && getStart() == rCompare
.getStart()
307 && getUseUnitCoordinates() == rCompare
.getUseUnitCoordinates()
308 && getSpreadMethod() == rCompare
.getSpreadMethod());
311 } // end of namespace primitive2d
312 } // end of namespace drawinglayer
314 //////////////////////////////////////////////////////////////////////////////
316 namespace drawinglayer
318 namespace primitive2d
320 void SvgLinearGradientPrimitive2D::checkPreconditions()
323 SvgGradientHelper::checkPreconditions();
325 if(getCreatesContent())
328 const basegfx::B2DVector
aVector(getEnd() - getStart());
330 if(basegfx::fTools::equalZero(aVector
.getX()) && basegfx::fTools::equalZero(aVector
.getY()))
332 // fill with single color using last stop color
338 void SvgLinearGradientPrimitive2D::createAtom(
339 Primitive2DVector
& rTargetColor
,
340 Primitive2DVector
& rTargetOpacity
,
341 const SvgGradientEntry
& rFrom
,
342 const SvgGradientEntry
& rTo
,
343 sal_Int32 nOffset
) const
345 // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset())
346 if(rFrom
.getOffset() == rTo
.getOffset())
348 OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)");
352 rTargetColor
.push_back(
353 new SvgLinearAtomPrimitive2D(
354 rFrom
.getColor(), rFrom
.getOffset() + nOffset
,
355 rTo
.getColor(), rTo
.getOffset() + nOffset
));
357 if(!getFullyOpaque())
359 const double fTransFrom(1.0 - rFrom
.getOpacity());
360 const double fTransTo(1.0 - rTo
.getOpacity());
361 const basegfx::BColor
aColorFrom(fTransFrom
, fTransFrom
, fTransFrom
);
362 const basegfx::BColor
aColorTo(fTransTo
, fTransTo
, fTransTo
);
364 rTargetOpacity
.push_back(
365 new SvgLinearAtomPrimitive2D(
366 aColorFrom
, rFrom
.getOffset() + nOffset
,
367 aColorTo
, rTo
.getOffset() + nOffset
));
372 Primitive2DSequence
SvgLinearGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
& /*rViewInformation*/) const
374 Primitive2DSequence xRetval
;
376 if(!getPreconditionsChecked())
378 const_cast< SvgLinearGradientPrimitive2D
* >(this)->checkPreconditions();
383 // fill with last existing color
384 xRetval
= createSingleGradientEntryFill();
386 else if(getCreatesContent())
388 // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely
389 // invisible, width and height to fill are not empty
390 const basegfx::B2DRange
aPolyRange(getPolyPolygon().getB2DRange());
391 const double fPolyWidth(aPolyRange
.getWidth());
392 const double fPolyHeight(aPolyRange
.getHeight());
394 // create ObjectTransform based on polygon range
395 const basegfx::B2DHomMatrix
aObjectTransform(
396 basegfx::tools::createScaleTranslateB2DHomMatrix(
397 fPolyWidth
, fPolyHeight
,
398 aPolyRange
.getMinX(), aPolyRange
.getMinY()));
399 basegfx::B2DHomMatrix aUnitGradientToObject
;
401 if(getUseUnitCoordinates())
403 // interpret in unit coordinate system -> object aspect ratio will scale result
404 // create unit transform from unit vector [0.0 .. 1.0] along the X-Axis to given
405 // gradient vector defined by Start,End
406 const basegfx::B2DVector
aVector(getEnd() - getStart());
407 const double fVectorLength(aVector
.getLength());
408 basegfx::B2DHomMatrix aUnitGradientToGradient
;
410 aUnitGradientToGradient
.scale(fVectorLength
, 1.0);
411 aUnitGradientToGradient
.rotate(atan2(aVector
.getY(), aVector
.getX()));
412 aUnitGradientToGradient
.translate(getStart().getX(), getStart().getY());
414 // create full transform from unit gradient coordinates to object coordinates
415 // including the SvgGradient transformation
416 aUnitGradientToObject
= aObjectTransform
* aUnitGradientToGradient
;
420 // interpret in object coordinate system -> object aspect ratio will not scale result
421 const basegfx::B2DPoint
aStart(aObjectTransform
* getStart());
422 const basegfx::B2DPoint
aEnd(aObjectTransform
* getEnd());
423 const basegfx::B2DVector
aVector(aEnd
- aStart
);
425 aUnitGradientToObject
.scale(aVector
.getLength(), 1.0);
426 aUnitGradientToObject
.rotate(atan2(aVector
.getY(), aVector
.getX()));
427 aUnitGradientToObject
.translate(aStart
.getX(), aStart
.getY());
430 // create inverse from it
431 basegfx::B2DHomMatrix
aObjectToUnitGradient(aUnitGradientToObject
);
432 aObjectToUnitGradient
.invert();
434 // back-transform polygon to unit gradient coordinates and get
435 // UnitRage. This is the range the gradient has to cover
436 basegfx::B2DPolyPolygon
aUnitPoly(getPolyPolygon());
437 aUnitPoly
.transform(aObjectToUnitGradient
);
438 const basegfx::B2DRange
aUnitRange(aUnitPoly
.getB2DRange());
440 // prepare result vectors
441 Primitive2DVector aTargetColor
;
442 Primitive2DVector aTargetOpacity
;
444 if(basegfx::fTools::more(aUnitRange
.getWidth(), 0.0))
446 // add a pre-multiply to aUnitGradientToObject to allow
447 // multiplication of the polygon(xl, 0.0, xr, 1.0)
448 const basegfx::B2DHomMatrix
aPreMultiply(
449 basegfx::tools::createScaleTranslateB2DHomMatrix(
450 1.0, aUnitRange
.getHeight(), 0.0, aUnitRange
.getMinY()));
451 aUnitGradientToObject
= aUnitGradientToObject
* aPreMultiply
;
453 // create central run, may also already do all necessary when
454 // Spread_pad is set as SpreadMethod and/or the range is smaller
455 double fPos(createRun(aTargetColor
, aTargetOpacity
, aUnitRange
.getMinX(), aUnitRange
.getMaxX(), getGradientEntries(), 0));
457 if(fPos
< aUnitRange
.getMaxX())
459 // can only happen when SpreadMethod is Spread_reflect or Spread_repeat,
460 // else the start and end pads are already created and fPos == aUnitRange.getMaxX().
461 // Its possible to express the repeated linear gradient by adding the
462 // transformed central run. Crete it this way
463 Primitive2DSequence
aTargetColorEntries(Primitive2DVectorToPrimitive2DSequence(aTargetColor
));
464 Primitive2DSequence
aTargetOpacityEntries(Primitive2DVectorToPrimitive2DSequence(aTargetOpacity
));
465 aTargetColor
.clear();
466 aTargetOpacity
.clear();
468 if(aTargetColorEntries
.hasElements())
470 // add original central run as group primitive
471 aTargetColor
.push_back(new GroupPrimitive2D(aTargetColorEntries
));
473 if(aTargetOpacityEntries
.hasElements())
475 aTargetOpacity
.push_back(new GroupPrimitive2D(aTargetOpacityEntries
));
480 sal_Int32
nOffset(0);
482 while(fPos
> aUnitRange
.getMinX())
487 basegfx::B2DHomMatrix aTransform
;
488 const bool bMirror(Spread_reflect
== getSpreadMethod() && (nOffset
% 2));
492 aTransform
.scale(-1.0, 1.0);
493 aTransform
.translate(fPos
+ 1.0, 0.0);
497 aTransform
.translate(fPos
, 0.0);
500 aTargetColor
.push_back(new TransformPrimitive2D(aTransform
, aTargetColorEntries
));
502 if(aTargetOpacityEntries
.hasElements())
504 aTargetOpacity
.push_back(new TransformPrimitive2D(aTransform
, aTargetOpacityEntries
));
512 while(fPos
< aUnitRange
.getMaxX())
514 basegfx::B2DHomMatrix aTransform
;
515 const bool bMirror(Spread_reflect
== getSpreadMethod() && (nOffset
% 2));
519 aTransform
.scale(-1.0, 1.0);
520 aTransform
.translate(fPos
+ 1.0, 0.0);
524 aTransform
.translate(fPos
, 0.0);
527 aTargetColor
.push_back(new TransformPrimitive2D(aTransform
, aTargetColorEntries
));
529 if(aTargetOpacityEntries
.hasElements())
531 aTargetOpacity
.push_back(new TransformPrimitive2D(aTransform
, aTargetOpacityEntries
));
541 xRetval
= createResult(aTargetColor
, aTargetOpacity
, aUnitGradientToObject
);
547 SvgLinearGradientPrimitive2D::SvgLinearGradientPrimitive2D(
548 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
549 const SvgGradientEntryVector
& rGradientEntries
,
550 const basegfx::B2DPoint
& rStart
,
551 const basegfx::B2DPoint
& rEnd
,
552 bool bUseUnitCoordinates
,
553 SpreadMethod aSpreadMethod
)
554 : BufferedDecompositionPrimitive2D(),
555 SvgGradientHelper(rPolyPolygon
, rGradientEntries
, rStart
, bUseUnitCoordinates
, aSpreadMethod
),
560 SvgLinearGradientPrimitive2D::~SvgLinearGradientPrimitive2D()
564 bool SvgLinearGradientPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
566 const SvgGradientHelper
* pSvgGradientHelper
= dynamic_cast< const SvgGradientHelper
* >(&rPrimitive
);
568 if(pSvgGradientHelper
&& SvgGradientHelper::equalTo(*pSvgGradientHelper
))
570 const SvgLinearGradientPrimitive2D
& rCompare
= static_cast< const SvgLinearGradientPrimitive2D
& >(rPrimitive
);
572 return (getEnd() == rCompare
.getEnd());
578 basegfx::B2DRange
SvgLinearGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D
& /*rViewInformation*/) const
580 // return ObjectRange
581 return getPolyPolygon().getB2DRange();
585 ImplPrimitive2DIDBlock(SvgLinearGradientPrimitive2D
, PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D
)
587 } // end of namespace primitive2d
588 } // end of namespace drawinglayer
590 //////////////////////////////////////////////////////////////////////////////
592 namespace drawinglayer
594 namespace primitive2d
596 void SvgRadialGradientPrimitive2D::checkPreconditions()
599 SvgGradientHelper::checkPreconditions();
601 if(getCreatesContent())
604 if(basegfx::fTools::equalZero(getRadius()))
606 // fill with single color using last stop color
612 void SvgRadialGradientPrimitive2D::createAtom(
613 Primitive2DVector
& rTargetColor
,
614 Primitive2DVector
& rTargetOpacity
,
615 const SvgGradientEntry
& rFrom
,
616 const SvgGradientEntry
& rTo
,
617 sal_Int32 nOffset
) const
619 // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset())
620 if(rFrom
.getOffset() == rTo
.getOffset())
622 OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)");
626 const double fScaleFrom(rFrom
.getOffset() + nOffset
);
627 const double fScaleTo(rTo
.getOffset() + nOffset
);
631 const basegfx::B2DVector
aTranslateFrom(maFocalVector
* (maFocalLength
- fScaleFrom
));
632 const basegfx::B2DVector
aTranslateTo(maFocalVector
* (maFocalLength
- fScaleTo
));
634 rTargetColor
.push_back(
635 new SvgRadialAtomPrimitive2D(
636 rFrom
.getColor(), fScaleFrom
, aTranslateFrom
,
637 rTo
.getColor(), fScaleTo
, aTranslateTo
));
641 rTargetColor
.push_back(
642 new SvgRadialAtomPrimitive2D(
643 rFrom
.getColor(), fScaleFrom
,
644 rTo
.getColor(), fScaleTo
));
647 if(!getFullyOpaque())
649 const double fTransFrom(1.0 - rFrom
.getOpacity());
650 const double fTransTo(1.0 - rTo
.getOpacity());
651 const basegfx::BColor
aColorFrom(fTransFrom
, fTransFrom
, fTransFrom
);
652 const basegfx::BColor
aColorTo(fTransTo
, fTransTo
, fTransTo
);
656 const basegfx::B2DVector
aTranslateFrom(maFocalVector
* (maFocalLength
- fScaleFrom
));
657 const basegfx::B2DVector
aTranslateTo(maFocalVector
* (maFocalLength
- fScaleTo
));
659 rTargetOpacity
.push_back(
660 new SvgRadialAtomPrimitive2D(
661 aColorFrom
, fScaleFrom
, aTranslateFrom
,
662 aColorTo
, fScaleTo
, aTranslateTo
));
666 rTargetOpacity
.push_back(
667 new SvgRadialAtomPrimitive2D(
668 aColorFrom
, fScaleFrom
,
669 aColorTo
, fScaleTo
));
675 const SvgGradientEntryVector
& SvgRadialGradientPrimitive2D::getMirroredGradientEntries() const
677 if(maMirroredGradientEntries
.empty() && !getGradientEntries().empty())
679 const_cast< SvgRadialGradientPrimitive2D
* >(this)->createMirroredGradientEntries();
682 return maMirroredGradientEntries
;
685 void SvgRadialGradientPrimitive2D::createMirroredGradientEntries()
687 if(maMirroredGradientEntries
.empty() && !getGradientEntries().empty())
689 const sal_uInt32
nCount(getGradientEntries().size());
690 maMirroredGradientEntries
.clear();
691 maMirroredGradientEntries
.reserve(nCount
);
693 for(sal_uInt32
a(0); a
< nCount
; a
++)
695 const SvgGradientEntry
& rCandidate
= getGradientEntries()[nCount
- 1 - a
];
697 maMirroredGradientEntries
.push_back(
699 1.0 - rCandidate
.getOffset(),
700 rCandidate
.getColor(),
701 rCandidate
.getOpacity()));
706 Primitive2DSequence
SvgRadialGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
& /*rViewInformation*/) const
708 Primitive2DSequence xRetval
;
710 if(!getPreconditionsChecked())
712 const_cast< SvgRadialGradientPrimitive2D
* >(this)->checkPreconditions();
717 // fill with last existing color
718 xRetval
= createSingleGradientEntryFill();
720 else if(getCreatesContent())
722 // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely
723 // invisible, width and height to fill are not empty
724 const basegfx::B2DRange
aPolyRange(getPolyPolygon().getB2DRange());
725 const double fPolyWidth(aPolyRange
.getWidth());
726 const double fPolyHeight(aPolyRange
.getHeight());
728 // create ObjectTransform based on polygon range
729 const basegfx::B2DHomMatrix
aObjectTransform(
730 basegfx::tools::createScaleTranslateB2DHomMatrix(
731 fPolyWidth
, fPolyHeight
,
732 aPolyRange
.getMinX(), aPolyRange
.getMinY()));
733 basegfx::B2DHomMatrix aUnitGradientToObject
;
735 if(getUseUnitCoordinates())
737 // interpret in unit coordinate system -> object aspect ratio will scale result
738 // create unit transform from unit vector to given linear gradient vector
739 basegfx::B2DHomMatrix aUnitGradientToGradient
;
741 aUnitGradientToGradient
.scale(getRadius(), getRadius());
742 aUnitGradientToGradient
.translate(getStart().getX(), getStart().getY());
744 // create full transform from unit gradient coordinates to object coordinates
745 // including the SvgGradient transformation
746 aUnitGradientToObject
= aObjectTransform
* aUnitGradientToGradient
;
750 // interpret in object coordinate system -> object aspect ratio will not scale result
751 const double fRadius((aObjectTransform
* basegfx::B2DVector(getRadius(), 0.0)).getLength());
752 const basegfx::B2DPoint
aStart(aObjectTransform
* getStart());
754 aUnitGradientToObject
.scale(fRadius
, fRadius
);
755 aUnitGradientToObject
.translate(aStart
.getX(), aStart
.getY());
758 // create inverse from it
759 basegfx::B2DHomMatrix
aObjectToUnitGradient(aUnitGradientToObject
);
760 aObjectToUnitGradient
.invert();
762 // back-transform polygon to unit gradient coordinates and get
763 // UnitRage. This is the range the gradient has to cover
764 basegfx::B2DPolyPolygon
aUnitPoly(getPolyPolygon());
765 aUnitPoly
.transform(aObjectToUnitGradient
);
766 const basegfx::B2DRange
aUnitRange(aUnitPoly
.getB2DRange());
768 // create range which the gradient has to cover to cover the whole given geometry.
769 // For circle, go from 0.0 to max radius in all directions (the corners)
770 double fMax(basegfx::B2DVector(aUnitRange
.getMinimum()).getLength());
771 fMax
= std::max(fMax
, basegfx::B2DVector(aUnitRange
.getMaximum()).getLength());
772 fMax
= std::max(fMax
, basegfx::B2DVector(aUnitRange
.getMinX(), aUnitRange
.getMaxY()).getLength());
773 fMax
= std::max(fMax
, basegfx::B2DVector(aUnitRange
.getMaxX(), aUnitRange
.getMinY()).getLength());
775 // prepare result vectors
776 Primitive2DVector aTargetColor
;
777 Primitive2DVector aTargetOpacity
;
781 // prepare maFocalVector
784 const_cast< SvgRadialGradientPrimitive2D
* >(this)->maFocalLength
= fMax
;
787 // create central run, may also already do all necessary when
788 // Spread_pad is set as SpreadMethod and/or the range is smaller
789 double fPos(createRun(aTargetColor
, aTargetOpacity
, 0.0, fMax
, getGradientEntries(), 0));
793 // can only happen when SpreadMethod is Spread_reflect or Spread_repeat,
794 // else the start and end pads are already created and fPos == fMax.
795 // For radial there is no way to transform the already created
796 // central run, it needs to be created from 1.0 to fMax
797 sal_Int32
nOffset(1);
801 const bool bMirror(Spread_reflect
== getSpreadMethod() && (nOffset
% 2));
805 createRun(aTargetColor
, aTargetOpacity
, 0.0, fMax
, getMirroredGradientEntries(), nOffset
);
809 createRun(aTargetColor
, aTargetOpacity
, 0.0, fMax
, getGradientEntries(), nOffset
);
818 xRetval
= createResult(aTargetColor
, aTargetOpacity
, aUnitGradientToObject
, true);
824 SvgRadialGradientPrimitive2D::SvgRadialGradientPrimitive2D(
825 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
826 const SvgGradientEntryVector
& rGradientEntries
,
827 const basegfx::B2DPoint
& rStart
,
829 bool bUseUnitCoordinates
,
830 SpreadMethod aSpreadMethod
,
831 const basegfx::B2DPoint
* pFocal
)
832 : BufferedDecompositionPrimitive2D(),
833 SvgGradientHelper(rPolyPolygon
, rGradientEntries
, rStart
, bUseUnitCoordinates
, aSpreadMethod
),
836 maFocalVector(0.0, 0.0),
838 maMirroredGradientEntries(),
841 if(pFocal
&& !pFocal
->equal(getStart()))
844 maFocalVector
= maFocal
- getStart();
849 SvgRadialGradientPrimitive2D::~SvgRadialGradientPrimitive2D()
853 bool SvgRadialGradientPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
855 const SvgGradientHelper
* pSvgGradientHelper
= dynamic_cast< const SvgGradientHelper
* >(&rPrimitive
);
857 if(pSvgGradientHelper
&& SvgGradientHelper::equalTo(*pSvgGradientHelper
))
859 const SvgRadialGradientPrimitive2D
& rCompare
= static_cast< const SvgRadialGradientPrimitive2D
& >(rPrimitive
);
861 if(getRadius() == rCompare
.getRadius())
863 if(isFocalSet() == rCompare
.isFocalSet())
867 return getFocal() == rCompare
.getFocal();
880 basegfx::B2DRange
SvgRadialGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D
& /*rViewInformation*/) const
882 // return ObjectRange
883 return getPolyPolygon().getB2DRange();
887 ImplPrimitive2DIDBlock(SvgRadialGradientPrimitive2D
, PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D
)
889 } // end of namespace primitive2d
890 } // end of namespace drawinglayer
892 //////////////////////////////////////////////////////////////////////////////
893 // SvgLinearAtomPrimitive2D class
895 namespace drawinglayer
897 namespace primitive2d
899 Primitive2DSequence
SvgLinearAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
& /*rViewInformation*/) const
901 Primitive2DSequence xRetval
;
902 const double fDelta(getOffsetB() - getOffsetA());
904 if(!basegfx::fTools::equalZero(fDelta
))
906 // use one discrete unit for overlap (one pixel)
907 const double fDiscreteUnit(getDiscreteUnit());
909 // use color distance and discrete lengths to calculate step count
910 const sal_uInt32
nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDelta
, fDiscreteUnit
));
912 // prepare polygon in needed width at start position (with discrete overlap)
913 const basegfx::B2DPolygon
aPolygon(
914 basegfx::tools::createPolygonFromRect(
916 getOffsetA() - fDiscreteUnit
,
918 getOffsetA() + (fDelta
/ nSteps
) + fDiscreteUnit
,
921 // prepare loop (inside to outside, [0.0 .. 1.0[)
922 double fUnitScale(0.0);
923 const double fUnitStep(1.0 / nSteps
);
925 // prepare result set (known size)
926 xRetval
.realloc(nSteps
);
928 for(sal_uInt32
a(0); a
< nSteps
; a
++, fUnitScale
+= fUnitStep
)
930 basegfx::B2DPolygon
aNew(aPolygon
);
932 aNew
.transform(basegfx::tools::createTranslateB2DHomMatrix(fDelta
* fUnitScale
, 0.0));
933 xRetval
[a
] = new PolyPolygonColorPrimitive2D(
934 basegfx::B2DPolyPolygon(aNew
),
935 basegfx::interpolate(getColorA(), getColorB(), fUnitScale
));
942 SvgLinearAtomPrimitive2D::SvgLinearAtomPrimitive2D(
943 const basegfx::BColor
& aColorA
, double fOffsetA
,
944 const basegfx::BColor
& aColorB
, double fOffsetB
)
945 : DiscreteMetricDependentPrimitive2D(),
951 if(mfOffsetA
> mfOffsetB
)
953 OSL_ENSURE(false, "Wrong offset order (!)");
954 ::std::swap(mfOffsetA
, mfOffsetB
);
958 bool SvgLinearAtomPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
960 if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive
))
962 const SvgLinearAtomPrimitive2D
& rCompare
= static_cast< const SvgLinearAtomPrimitive2D
& >(rPrimitive
);
964 return (getColorA() == rCompare
.getColorA()
965 && getColorB() == rCompare
.getColorB()
966 && getOffsetA() == rCompare
.getOffsetA()
967 && getOffsetB() == rCompare
.getOffsetB());
974 ImplPrimitive2DIDBlock(SvgLinearAtomPrimitive2D
, PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D
)
976 } // end of namespace primitive2d
977 } // end of namespace drawinglayer
979 //////////////////////////////////////////////////////////////////////////////
980 // SvgRadialAtomPrimitive2D class
982 namespace drawinglayer
984 namespace primitive2d
986 Primitive2DSequence
SvgRadialAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
& /*rViewInformation*/) const
988 Primitive2DSequence xRetval
;
989 const double fDeltaScale(getScaleB() - getScaleA());
991 if(!basegfx::fTools::equalZero(fDeltaScale
))
993 // use one discrete unit for overlap (one pixel)
994 const double fDiscreteUnit(getDiscreteUnit());
996 // use color distance and discrete lengths to calculate step count
997 const sal_uInt32
nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDeltaScale
, fDiscreteUnit
));
999 // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes)
1000 double fUnitScale(0.0);
1001 const double fUnitStep(1.0 / nSteps
);
1003 // prepare result set (known size)
1004 xRetval
.realloc(nSteps
);
1006 for(sal_uInt32
a(0); a
< nSteps
; a
++, fUnitScale
+= fUnitStep
)
1008 basegfx::B2DHomMatrix aTransform
;
1009 const double fEndScale(getScaleB() - (fDeltaScale
* fUnitScale
));
1011 if(isTranslateSet())
1013 const basegfx::B2DVector
aTranslate(
1014 basegfx::interpolate(
1019 aTransform
= basegfx::tools::createScaleTranslateB2DHomMatrix(
1027 aTransform
= basegfx::tools::createScaleB2DHomMatrix(
1032 basegfx::B2DPolygon
aNew(basegfx::tools::createPolygonFromUnitCircle());
1034 aNew
.transform(aTransform
);
1035 xRetval
[a
] = new PolyPolygonColorPrimitive2D(
1036 basegfx::B2DPolyPolygon(aNew
),
1037 basegfx::interpolate(getColorB(), getColorA(), fUnitScale
));
1044 SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D(
1045 const basegfx::BColor
& aColorA
, double fScaleA
, const basegfx::B2DVector
& rTranslateA
,
1046 const basegfx::BColor
& aColorB
, double fScaleB
, const basegfx::B2DVector
& rTranslateB
)
1047 : DiscreteMetricDependentPrimitive2D(),
1054 // check and evtl. set translations
1055 if(!rTranslateA
.equal(rTranslateB
))
1057 mpTranslate
= new VectorPair(rTranslateA
, rTranslateB
);
1060 // scale A and B have to be positive
1061 mfScaleA
= ::std::max(mfScaleA
, 0.0);
1062 mfScaleB
= ::std::max(mfScaleB
, 0.0);
1064 // scale B has to be bigger than scale A; swap if different
1065 if(mfScaleA
> mfScaleB
)
1067 OSL_ENSURE(false, "Wrong offset order (!)");
1068 ::std::swap(mfScaleA
, mfScaleB
);
1072 ::std::swap(mpTranslate
->maTranslateA
, mpTranslate
->maTranslateB
);
1077 SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D(
1078 const basegfx::BColor
& aColorA
, double fScaleA
,
1079 const basegfx::BColor
& aColorB
, double fScaleB
)
1080 : DiscreteMetricDependentPrimitive2D(),
1087 // scale A and B have to be positive
1088 mfScaleA
= ::std::max(mfScaleA
, 0.0);
1089 mfScaleB
= ::std::max(mfScaleB
, 0.0);
1091 // scale B has to be bigger than scale A; swap if different
1092 if(mfScaleA
> mfScaleB
)
1094 OSL_ENSURE(false, "Wrong offset order (!)");
1095 ::std::swap(mfScaleA
, mfScaleB
);
1099 SvgRadialAtomPrimitive2D::~SvgRadialAtomPrimitive2D()
1108 bool SvgRadialAtomPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
1110 if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive
))
1112 const SvgRadialAtomPrimitive2D
& rCompare
= static_cast< const SvgRadialAtomPrimitive2D
& >(rPrimitive
);
1114 if(getColorA() == rCompare
.getColorA()
1115 && getColorB() == rCompare
.getColorB()
1116 && getScaleA() == rCompare
.getScaleA()
1117 && getScaleB() == rCompare
.getScaleB())
1119 if(isTranslateSet() && rCompare
.isTranslateSet())
1121 return (getTranslateA() == rCompare
.getTranslateA()
1122 && getTranslateB() == rCompare
.getTranslateB());
1124 else if(!isTranslateSet() && !rCompare
.isTranslateSet())
1134 // provide unique ID
1135 ImplPrimitive2DIDBlock(SvgRadialAtomPrimitive2D
, PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D
)
1137 } // end of namespace primitive2d
1138 } // end of namespace drawinglayer
1140 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */