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>
34 using namespace com::sun::star
;
39 sal_uInt32
calculateStepsForSvgGradient(const basegfx::BColor
& rColorA
, const basegfx::BColor
& rColorB
, double fDelta
, double fDiscreteUnit
)
41 // use color distance, assume to do every color step (full quality)
42 sal_uInt32
nSteps(basegfx::fround(rColorA
.getDistance(rColorB
) * 255.0));
46 // calc discrete length to change color all 1.5 discrete units (pixels)
47 const sal_uInt32
nDistSteps(basegfx::fround(fDelta
/ (fDiscreteUnit
* 1.5)));
49 nSteps
= std::min(nSteps
, nDistSteps
);
52 // roughly cut when too big or too small
53 nSteps
= std::min(nSteps
, sal_uInt32(255));
54 nSteps
= std::max(nSteps
, sal_uInt32(1));
58 } // end of anonymous namespace
61 namespace drawinglayer
65 void SvgGradientHelper::createSingleGradientEntryFill(Primitive2DContainer
& rContainer
) const
67 const SvgGradientEntryVector
& rEntries
= getGradientEntries();
68 const sal_uInt32
nCount(rEntries
.size());
72 const SvgGradientEntry
& rSingleEntry
= rEntries
[nCount
- 1];
73 const double fOpacity(rSingleEntry
.getOpacity());
77 Primitive2DReference
xRef(
78 new PolyPolygonColorPrimitive2D(
80 rSingleEntry
.getColor()));
84 const Primitive2DContainer aContent
{ xRef
};
86 xRef
= Primitive2DReference(
87 new UnifiedTransparencePrimitive2D(
92 rContainer
.push_back(xRef
);
97 OSL_ENSURE(false, "Single gradient entry construction without entry (!)");
101 void SvgGradientHelper::checkPreconditions()
103 mbPreconditionsChecked
= true;
104 const SvgGradientEntryVector
& rEntries
= getGradientEntries();
112 const sal_uInt32
nCount(rEntries
.size());
116 // fill with single existing color
121 // sort maGradientEntries when more than one
122 std::sort(maGradientEntries
.begin(), maGradientEntries
.end());
124 // gradient with at least two colors
125 bool bAllInvisible(true);
127 for(sal_uInt32
a(0); a
< nCount
; a
++)
129 const SvgGradientEntry
& rCandidate
= rEntries
[a
];
131 if(basegfx::fTools::equalZero(rCandidate
.getOpacity()))
134 mbFullyOpaque
= false;
136 else if(basegfx::fTools::equal(rCandidate
.getOpacity(), 1.0))
139 bAllInvisible
= false;
144 bAllInvisible
= false;
145 mbFullyOpaque
= false;
151 // all invisible, nothing to do
155 const basegfx::B2DRange
aPolyRange(getPolyPolygon().getB2DRange());
157 if(aPolyRange
.isEmpty())
159 // no range to fill, nothing to do
163 const double fPolyWidth(aPolyRange
.getWidth());
164 const double fPolyHeight(aPolyRange
.getHeight());
166 if(basegfx::fTools::equalZero(fPolyWidth
) || basegfx::fTools::equalZero(fPolyHeight
))
168 // no width/height to fill, nothing to do
172 mbCreatesContent
= true;
180 double SvgGradientHelper::createRun(
181 Primitive2DContainer
& rTargetColor
,
182 Primitive2DContainer
& rTargetOpacity
,
185 const SvgGradientEntryVector
& rEntries
,
186 sal_Int32 nOffset
) const
188 const sal_uInt32
nCount(rEntries
.size());
192 const SvgGradientEntry
& rStart
= rEntries
[0];
193 const bool bCreateStartPad(fPos
< 0.0 && SpreadMethod::Pad
== getSpreadMethod());
194 const bool bCreateStartFill(rStart
.getOffset() > 0.0);
195 sal_uInt32
nIndex(0);
197 if(bCreateStartPad
|| bCreateStartFill
)
199 const SvgGradientEntry
aTemp(bCreateStartPad
? fPos
: 0.0, rStart
.getColor(), rStart
.getOpacity());
201 createAtom(rTargetColor
, rTargetOpacity
, aTemp
, rStart
, nOffset
);
202 fPos
= rStart
.getOffset();
205 while(fPos
< 1.0 && nIndex
+ 1 < nCount
)
207 const SvgGradientEntry
& rCandidateA
= rEntries
[nIndex
++];
208 const SvgGradientEntry
& rCandidateB
= rEntries
[nIndex
];
210 createAtom(rTargetColor
, rTargetOpacity
, rCandidateA
, rCandidateB
, nOffset
);
211 fPos
= rCandidateB
.getOffset();
214 const SvgGradientEntry
& rEnd
= rEntries
[nCount
- 1];
215 const bool bCreateEndPad(fPos
< fMax
&& SpreadMethod::Pad
== getSpreadMethod());
216 const bool bCreateEndFill(rEnd
.getOffset() < 1.0);
218 if(bCreateEndPad
|| bCreateEndFill
)
220 fPos
= bCreateEndPad
? fMax
: 1.0;
221 const SvgGradientEntry
aTemp(fPos
, rEnd
.getColor(), rEnd
.getOpacity());
223 createAtom(rTargetColor
, rTargetOpacity
, rEnd
, aTemp
, nOffset
);
228 OSL_ENSURE(false, "GradientAtom creation without ColorStops (!)");
235 void SvgGradientHelper::createResult(
236 Primitive2DContainer
& rContainer
,
237 const Primitive2DContainer
& rTargetColor
,
238 const Primitive2DContainer
& rTargetOpacity
,
239 const basegfx::B2DHomMatrix
& rUnitGradientToObject
,
242 const Primitive2DContainer
aTargetColorEntries(rTargetColor
.maybeInvert(bInvert
));
243 const Primitive2DContainer
aTargetOpacityEntries(rTargetOpacity
.maybeInvert(bInvert
));
245 if(!aTargetColorEntries
.empty())
247 Primitive2DReference xRefContent
;
249 if(!aTargetOpacityEntries
.empty())
251 const Primitive2DReference xRefOpacity
= new TransparencePrimitive2D(
253 aTargetOpacityEntries
);
255 xRefContent
= new TransformPrimitive2D(
256 rUnitGradientToObject
,
257 Primitive2DContainer
{ xRefOpacity
});
261 xRefContent
= new TransformPrimitive2D(
262 rUnitGradientToObject
,
263 aTargetColorEntries
);
266 rContainer
.push_back(new MaskPrimitive2D(
268 Primitive2DContainer
{ xRefContent
}));
272 SvgGradientHelper::SvgGradientHelper(
273 const basegfx::B2DHomMatrix
& rGradientTransform
,
274 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
275 const SvgGradientEntryVector
& rGradientEntries
,
276 const basegfx::B2DPoint
& rStart
,
277 bool bUseUnitCoordinates
,
278 SpreadMethod aSpreadMethod
)
279 : maGradientTransform(rGradientTransform
),
280 maPolyPolygon(rPolyPolygon
),
281 maGradientEntries(rGradientEntries
),
283 maSpreadMethod(aSpreadMethod
),
284 mbPreconditionsChecked(false),
285 mbCreatesContent(false),
286 mbSingleEntry(false),
288 mbUseUnitCoordinates(bUseUnitCoordinates
)
292 SvgGradientHelper::~SvgGradientHelper()
296 bool SvgGradientHelper::operator==(const SvgGradientHelper
& rSvgGradientHelper
) const
298 const SvgGradientHelper
& rCompare
= rSvgGradientHelper
;
300 return (getGradientTransform() == rCompare
.getGradientTransform()
301 && getPolyPolygon() == rCompare
.getPolyPolygon()
302 && getGradientEntries() == rCompare
.getGradientEntries()
303 && getStart() == rCompare
.getStart()
304 && getUseUnitCoordinates() == rCompare
.getUseUnitCoordinates()
305 && getSpreadMethod() == rCompare
.getSpreadMethod());
308 } // end of namespace primitive2d
309 } // end of namespace drawinglayer
312 namespace drawinglayer
314 namespace primitive2d
316 void SvgLinearGradientPrimitive2D::checkPreconditions()
319 SvgGradientHelper::checkPreconditions();
321 if(getCreatesContent())
324 const basegfx::B2DVector
aVector(getEnd() - getStart());
326 if(basegfx::fTools::equalZero(aVector
.getX()) && basegfx::fTools::equalZero(aVector
.getY()))
328 // fill with single color using last stop color
334 void SvgLinearGradientPrimitive2D::createAtom(
335 Primitive2DContainer
& rTargetColor
,
336 Primitive2DContainer
& rTargetOpacity
,
337 const SvgGradientEntry
& rFrom
,
338 const SvgGradientEntry
& rTo
,
339 sal_Int32 nOffset
) const
341 // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset())
342 if(rFrom
.getOffset() == rTo
.getOffset())
344 OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)");
348 rTargetColor
.push_back(
349 new SvgLinearAtomPrimitive2D(
350 rFrom
.getColor(), rFrom
.getOffset() + nOffset
,
351 rTo
.getColor(), rTo
.getOffset() + nOffset
));
353 if(!getFullyOpaque())
355 const double fTransFrom(1.0 - rFrom
.getOpacity());
356 const double fTransTo(1.0 - rTo
.getOpacity());
357 const basegfx::BColor
aColorFrom(fTransFrom
, fTransFrom
, fTransFrom
);
358 const basegfx::BColor
aColorTo(fTransTo
, fTransTo
, fTransTo
);
360 rTargetOpacity
.push_back(
361 new SvgLinearAtomPrimitive2D(
362 aColorFrom
, rFrom
.getOffset() + nOffset
,
363 aColorTo
, rTo
.getOffset() + nOffset
));
368 void SvgLinearGradientPrimitive2D::create2DDecomposition(Primitive2DContainer
& rContainer
, const geometry::ViewInformation2D
& /*rViewInformation*/) const
370 if(!getPreconditionsChecked())
372 const_cast< SvgLinearGradientPrimitive2D
* >(this)->checkPreconditions();
377 // fill with last existing color
378 createSingleGradientEntryFill(rContainer
);
380 else if(getCreatesContent())
382 // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely
383 // invisible, width and height to fill are not empty
384 const basegfx::B2DRange
aPolyRange(getPolyPolygon().getB2DRange());
385 const double fPolyWidth(aPolyRange
.getWidth());
386 const double fPolyHeight(aPolyRange
.getHeight());
388 // create ObjectTransform based on polygon range
389 const basegfx::B2DHomMatrix
aObjectTransform(
390 basegfx::utils::createScaleTranslateB2DHomMatrix(
391 fPolyWidth
, fPolyHeight
,
392 aPolyRange
.getMinX(), aPolyRange
.getMinY()));
393 basegfx::B2DHomMatrix aUnitGradientToObject
;
395 if(getUseUnitCoordinates())
397 // interpret in unit coordinate system -> object aspect ratio will scale result
398 // create unit transform from unit vector [0.0 .. 1.0] along the X-Axis to given
399 // gradient vector defined by Start,End
400 const basegfx::B2DVector
aVector(getEnd() - getStart());
401 const double fVectorLength(aVector
.getLength());
403 aUnitGradientToObject
.scale(fVectorLength
, 1.0);
404 aUnitGradientToObject
.rotate(atan2(aVector
.getY(), aVector
.getX()));
405 aUnitGradientToObject
.translate(getStart().getX(), getStart().getY());
407 aUnitGradientToObject
*= getGradientTransform();
409 // create full transform from unit gradient coordinates to object coordinates
410 // including the SvgGradient transformation
411 aUnitGradientToObject
*= aObjectTransform
;
415 // interpret in object coordinate system -> object aspect ratio will not scale result
416 const basegfx::B2DPoint
aStart(aObjectTransform
* getStart());
417 const basegfx::B2DPoint
aEnd(aObjectTransform
* getEnd());
418 const basegfx::B2DVector
aVector(aEnd
- aStart
);
420 aUnitGradientToObject
.scale(aVector
.getLength(), 1.0);
421 aUnitGradientToObject
.rotate(atan2(aVector
.getY(), aVector
.getX()));
422 aUnitGradientToObject
.translate(aStart
.getX(), aStart
.getY());
424 aUnitGradientToObject
*= getGradientTransform();
427 // create inverse from it
428 basegfx::B2DHomMatrix
aObjectToUnitGradient(aUnitGradientToObject
);
429 aObjectToUnitGradient
.invert();
431 // back-transform polygon to unit gradient coordinates and get
432 // UnitRage. This is the range the gradient has to cover
433 basegfx::B2DPolyPolygon
aUnitPoly(getPolyPolygon());
434 aUnitPoly
.transform(aObjectToUnitGradient
);
435 const basegfx::B2DRange
aUnitRange(aUnitPoly
.getB2DRange());
437 // prepare result vectors
438 Primitive2DContainer aTargetColor
;
439 Primitive2DContainer aTargetOpacity
;
441 if(basegfx::fTools::more(aUnitRange
.getWidth(), 0.0))
443 // add a pre-multiply to aUnitGradientToObject to allow
444 // multiplication of the polygon(xl, 0.0, xr, 1.0)
445 const basegfx::B2DHomMatrix
aPreMultiply(
446 basegfx::utils::createScaleTranslateB2DHomMatrix(
447 1.0, aUnitRange
.getHeight(), 0.0, aUnitRange
.getMinY()));
448 aUnitGradientToObject
= aUnitGradientToObject
* aPreMultiply
;
450 // create central run, may also already do all necessary when
451 // SpreadMethod::Pad is set as SpreadMethod and/or the range is smaller
452 double fPos(createRun(aTargetColor
, aTargetOpacity
, aUnitRange
.getMinX(), aUnitRange
.getMaxX(), getGradientEntries(), 0));
454 if(fPos
< aUnitRange
.getMaxX())
456 // can only happen when SpreadMethod is SpreadMethod::Reflect or SpreadMethod::Repeat,
457 // else the start and end pads are already created and fPos == aUnitRange.getMaxX().
458 // Its possible to express the repeated linear gradient by adding the
459 // transformed central run. Create it this way
460 Primitive2DContainer
aTargetColorEntries(aTargetColor
.maybeInvert());
461 Primitive2DContainer
aTargetOpacityEntries(aTargetOpacity
.maybeInvert());
462 aTargetColor
.clear();
463 aTargetOpacity
.clear();
465 if(!aTargetColorEntries
.empty())
467 // add original central run as group primitive
468 aTargetColor
.push_back(new GroupPrimitive2D(aTargetColorEntries
));
470 if(!aTargetOpacityEntries
.empty())
472 aTargetOpacity
.push_back(new GroupPrimitive2D(aTargetOpacityEntries
));
477 sal_Int32
nOffset(0);
479 while(fPos
> aUnitRange
.getMinX())
484 basegfx::B2DHomMatrix aTransform
;
485 const bool bMirror(SpreadMethod::Reflect
== getSpreadMethod() && (nOffset
% 2));
489 aTransform
.scale(-1.0, 1.0);
490 aTransform
.translate(fPos
+ 1.0, 0.0);
494 aTransform
.translate(fPos
, 0.0);
497 aTargetColor
.push_back(new TransformPrimitive2D(aTransform
, aTargetColorEntries
));
499 if(!aTargetOpacityEntries
.empty())
501 aTargetOpacity
.push_back(new TransformPrimitive2D(aTransform
, aTargetOpacityEntries
));
509 while(fPos
< aUnitRange
.getMaxX())
511 basegfx::B2DHomMatrix aTransform
;
512 const bool bMirror(SpreadMethod::Reflect
== getSpreadMethod() && (nOffset
% 2));
516 aTransform
.scale(-1.0, 1.0);
517 aTransform
.translate(fPos
+ 1.0, 0.0);
521 aTransform
.translate(fPos
, 0.0);
524 aTargetColor
.push_back(new TransformPrimitive2D(aTransform
, aTargetColorEntries
));
526 if(!aTargetOpacityEntries
.empty())
528 aTargetOpacity
.push_back(new TransformPrimitive2D(aTransform
, aTargetOpacityEntries
));
538 createResult(rContainer
, aTargetColor
, aTargetOpacity
, aUnitGradientToObject
);
542 SvgLinearGradientPrimitive2D::SvgLinearGradientPrimitive2D(
543 const basegfx::B2DHomMatrix
& rGradientTransform
,
544 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
545 const SvgGradientEntryVector
& rGradientEntries
,
546 const basegfx::B2DPoint
& rStart
,
547 const basegfx::B2DPoint
& rEnd
,
548 bool bUseUnitCoordinates
,
549 SpreadMethod aSpreadMethod
)
550 : BufferedDecompositionPrimitive2D(),
551 SvgGradientHelper(rGradientTransform
, rPolyPolygon
, rGradientEntries
, rStart
, bUseUnitCoordinates
, aSpreadMethod
),
556 SvgLinearGradientPrimitive2D::~SvgLinearGradientPrimitive2D()
560 bool SvgLinearGradientPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
562 const SvgGradientHelper
* pSvgGradientHelper
= dynamic_cast< const SvgGradientHelper
* >(&rPrimitive
);
564 if(pSvgGradientHelper
&& SvgGradientHelper::operator==(*pSvgGradientHelper
))
566 const SvgLinearGradientPrimitive2D
& rCompare
= static_cast< const SvgLinearGradientPrimitive2D
& >(rPrimitive
);
568 return (getEnd() == rCompare
.getEnd());
574 basegfx::B2DRange
SvgLinearGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D
& /*rViewInformation*/) const
576 // return ObjectRange
577 return getPolyPolygon().getB2DRange();
581 ImplPrimitive2DIDBlock(SvgLinearGradientPrimitive2D
, PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D
)
583 } // end of namespace primitive2d
584 } // end of namespace drawinglayer
587 namespace drawinglayer
589 namespace primitive2d
591 void SvgRadialGradientPrimitive2D::checkPreconditions()
594 SvgGradientHelper::checkPreconditions();
596 if(getCreatesContent())
599 if(basegfx::fTools::equalZero(getRadius()))
601 // fill with single color using last stop color
607 void SvgRadialGradientPrimitive2D::createAtom(
608 Primitive2DContainer
& rTargetColor
,
609 Primitive2DContainer
& rTargetOpacity
,
610 const SvgGradientEntry
& rFrom
,
611 const SvgGradientEntry
& rTo
,
612 sal_Int32 nOffset
) const
614 // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset())
615 if(rFrom
.getOffset() == rTo
.getOffset())
617 OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)");
621 const double fScaleFrom(rFrom
.getOffset() + nOffset
);
622 const double fScaleTo(rTo
.getOffset() + nOffset
);
626 const basegfx::B2DVector
aTranslateFrom(maFocalVector
* (maFocalLength
- fScaleFrom
));
627 const basegfx::B2DVector
aTranslateTo(maFocalVector
* (maFocalLength
- fScaleTo
));
629 rTargetColor
.push_back(
630 new SvgRadialAtomPrimitive2D(
631 rFrom
.getColor(), fScaleFrom
, aTranslateFrom
,
632 rTo
.getColor(), fScaleTo
, aTranslateTo
));
636 rTargetColor
.push_back(
637 new SvgRadialAtomPrimitive2D(
638 rFrom
.getColor(), fScaleFrom
,
639 rTo
.getColor(), fScaleTo
));
642 if(!getFullyOpaque())
644 const double fTransFrom(1.0 - rFrom
.getOpacity());
645 const double fTransTo(1.0 - rTo
.getOpacity());
646 const basegfx::BColor
aColorFrom(fTransFrom
, fTransFrom
, fTransFrom
);
647 const basegfx::BColor
aColorTo(fTransTo
, fTransTo
, fTransTo
);
651 const basegfx::B2DVector
aTranslateFrom(maFocalVector
* (maFocalLength
- fScaleFrom
));
652 const basegfx::B2DVector
aTranslateTo(maFocalVector
* (maFocalLength
- fScaleTo
));
654 rTargetOpacity
.push_back(
655 new SvgRadialAtomPrimitive2D(
656 aColorFrom
, fScaleFrom
, aTranslateFrom
,
657 aColorTo
, fScaleTo
, aTranslateTo
));
661 rTargetOpacity
.push_back(
662 new SvgRadialAtomPrimitive2D(
663 aColorFrom
, fScaleFrom
,
664 aColorTo
, fScaleTo
));
670 const SvgGradientEntryVector
& SvgRadialGradientPrimitive2D::getMirroredGradientEntries() const
672 if(maMirroredGradientEntries
.empty() && !getGradientEntries().empty())
674 const_cast< SvgRadialGradientPrimitive2D
* >(this)->createMirroredGradientEntries();
677 return maMirroredGradientEntries
;
680 void SvgRadialGradientPrimitive2D::createMirroredGradientEntries()
682 if(maMirroredGradientEntries
.empty() && !getGradientEntries().empty())
684 const sal_uInt32
nCount(getGradientEntries().size());
685 maMirroredGradientEntries
.clear();
686 maMirroredGradientEntries
.reserve(nCount
);
688 for(sal_uInt32
a(0); a
< nCount
; a
++)
690 const SvgGradientEntry
& rCandidate
= getGradientEntries()[nCount
- 1 - a
];
692 maMirroredGradientEntries
.emplace_back(
693 1.0 - rCandidate
.getOffset(),
694 rCandidate
.getColor(),
695 rCandidate
.getOpacity());
700 void SvgRadialGradientPrimitive2D::create2DDecomposition(Primitive2DContainer
& rContainer
, const geometry::ViewInformation2D
& /*rViewInformation*/) const
702 if(!getPreconditionsChecked())
704 const_cast< SvgRadialGradientPrimitive2D
* >(this)->checkPreconditions();
709 // fill with last existing color
710 createSingleGradientEntryFill(rContainer
);
712 else if(getCreatesContent())
714 // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely
715 // invisible, width and height to fill are not empty
716 const basegfx::B2DRange
aPolyRange(getPolyPolygon().getB2DRange());
717 const double fPolyWidth(aPolyRange
.getWidth());
718 const double fPolyHeight(aPolyRange
.getHeight());
720 // create ObjectTransform based on polygon range
721 const basegfx::B2DHomMatrix
aObjectTransform(
722 basegfx::utils::createScaleTranslateB2DHomMatrix(
723 fPolyWidth
, fPolyHeight
,
724 aPolyRange
.getMinX(), aPolyRange
.getMinY()));
725 basegfx::B2DHomMatrix aUnitGradientToObject
;
727 if(getUseUnitCoordinates())
729 // interpret in unit coordinate system -> object aspect ratio will scale result
730 // create unit transform from unit vector to given linear gradient vector
731 aUnitGradientToObject
.scale(getRadius(), getRadius());
732 aUnitGradientToObject
.translate(getStart().getX(), getStart().getY());
734 if(!getGradientTransform().isIdentity())
736 aUnitGradientToObject
= getGradientTransform() * aUnitGradientToObject
;
739 // create full transform from unit gradient coordinates to object coordinates
740 // including the SvgGradient transformation
741 aUnitGradientToObject
= aObjectTransform
* aUnitGradientToObject
;
745 // interpret in object coordinate system -> object aspect ratio will not scale result
746 // use X-Axis with radius, it was already made relative to object width when coming from
748 const double fRadius((aObjectTransform
* basegfx::B2DVector(getRadius(), 0.0)).getLength());
749 const basegfx::B2DPoint
aStart(aObjectTransform
* getStart());
751 aUnitGradientToObject
.scale(fRadius
, fRadius
);
752 aUnitGradientToObject
.translate(aStart
.getX(), aStart
.getY());
754 aUnitGradientToObject
*= getGradientTransform();
757 // create inverse from it
758 basegfx::B2DHomMatrix
aObjectToUnitGradient(aUnitGradientToObject
);
759 aObjectToUnitGradient
.invert();
761 // back-transform polygon to unit gradient coordinates and get
762 // UnitRage. This is the range the gradient has to cover
763 basegfx::B2DPolyPolygon
aUnitPoly(getPolyPolygon());
764 aUnitPoly
.transform(aObjectToUnitGradient
);
765 const basegfx::B2DRange
aUnitRange(aUnitPoly
.getB2DRange());
767 // create range which the gradient has to cover to cover the whole given geometry.
768 // For circle, go from 0.0 to max radius in all directions (the corners)
769 double fMax(basegfx::B2DVector(aUnitRange
.getMinimum()).getLength());
770 fMax
= std::max(fMax
, basegfx::B2DVector(aUnitRange
.getMaximum()).getLength());
771 fMax
= std::max(fMax
, basegfx::B2DVector(aUnitRange
.getMinX(), aUnitRange
.getMaxY()).getLength());
772 fMax
= std::max(fMax
, basegfx::B2DVector(aUnitRange
.getMaxX(), aUnitRange
.getMinY()).getLength());
774 // prepare result vectors
775 Primitive2DContainer aTargetColor
;
776 Primitive2DContainer aTargetOpacity
;
780 // prepare maFocalVector
783 const_cast< SvgRadialGradientPrimitive2D
* >(this)->maFocalLength
= fMax
;
786 // create central run, may also already do all necessary when
787 // SpreadMethod::Pad is set as SpreadMethod and/or the range is smaller
788 double fPos(createRun(aTargetColor
, aTargetOpacity
, 0.0, fMax
, getGradientEntries(), 0));
792 // can only happen when SpreadMethod is SpreadMethod::Reflect or SpreadMethod::Repeat,
793 // else the start and end pads are already created and fPos == fMax.
794 // For radial there is no way to transform the already created
795 // central run, it needs to be created from 1.0 to fMax
796 sal_Int32
nOffset(1);
800 const bool bMirror(SpreadMethod::Reflect
== getSpreadMethod() && (nOffset
% 2));
804 createRun(aTargetColor
, aTargetOpacity
, 0.0, fMax
, getMirroredGradientEntries(), nOffset
);
808 createRun(aTargetColor
, aTargetOpacity
, 0.0, fMax
, getGradientEntries(), nOffset
);
817 createResult(rContainer
, aTargetColor
, aTargetOpacity
, aUnitGradientToObject
, true);
821 SvgRadialGradientPrimitive2D::SvgRadialGradientPrimitive2D(
822 const basegfx::B2DHomMatrix
& rGradientTransform
,
823 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
824 const SvgGradientEntryVector
& rGradientEntries
,
825 const basegfx::B2DPoint
& rStart
,
827 bool bUseUnitCoordinates
,
828 SpreadMethod aSpreadMethod
,
829 const basegfx::B2DPoint
* pFocal
)
830 : BufferedDecompositionPrimitive2D(),
831 SvgGradientHelper(rGradientTransform
, rPolyPolygon
, rGradientEntries
, rStart
, bUseUnitCoordinates
, aSpreadMethod
),
834 maFocalVector(0.0, 0.0),
836 maMirroredGradientEntries(),
839 if(pFocal
&& !pFocal
->equal(getStart()))
842 maFocalVector
= maFocal
- getStart();
847 SvgRadialGradientPrimitive2D::~SvgRadialGradientPrimitive2D()
851 bool SvgRadialGradientPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
853 const SvgGradientHelper
* pSvgGradientHelper
= dynamic_cast< const SvgGradientHelper
* >(&rPrimitive
);
855 if(pSvgGradientHelper
&& SvgGradientHelper::operator==(*pSvgGradientHelper
))
857 const SvgRadialGradientPrimitive2D
& rCompare
= static_cast< const SvgRadialGradientPrimitive2D
& >(rPrimitive
);
859 if(getRadius() == rCompare
.getRadius())
861 if(isFocalSet() == rCompare
.isFocalSet())
865 return getFocal() == rCompare
.getFocal();
878 basegfx::B2DRange
SvgRadialGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D
& /*rViewInformation*/) const
880 // return ObjectRange
881 return getPolyPolygon().getB2DRange();
885 ImplPrimitive2DIDBlock(SvgRadialGradientPrimitive2D
, PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D
)
887 } // end of namespace primitive2d
888 } // end of namespace drawinglayer
891 // SvgLinearAtomPrimitive2D class
893 namespace drawinglayer
895 namespace primitive2d
897 void SvgLinearAtomPrimitive2D::create2DDecomposition(Primitive2DContainer
& rContainer
, const geometry::ViewInformation2D
& /*rViewInformation*/) const
899 const double fDelta(getOffsetB() - getOffsetA());
901 if(!basegfx::fTools::equalZero(fDelta
))
903 // use one discrete unit for overlap (one pixel)
904 const double fDiscreteUnit(getDiscreteUnit());
906 // use color distance and discrete lengths to calculate step count
907 const sal_uInt32
nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDelta
, fDiscreteUnit
));
909 // tdf#117949 Use a small amount of discrete overlap at the edges. Usually this
910 // should be exactly 0.0 and 1.0, but there were cases when this gets clipped
911 // against the mask polygon which got numerically problematic.
912 // This change is unnecessary in that respect, but avoids that numerical havoc
913 // by at the same time doing no real harm AFAIK
914 // TTTT: Remove again when clipping is fixed (!)
916 // prepare polygon in needed width at start position (with discrete overlap)
917 const basegfx::B2DPolygon
aPolygon(
918 basegfx::utils::createPolygonFromRect(
920 getOffsetA() - fDiscreteUnit
,
921 -0.0001, // TTTT -> should be 0.0, see comment above
922 getOffsetA() + (fDelta
/ nSteps
) + fDiscreteUnit
,
923 1.0001))); // TTTT -> should be 1.0, see comment above
925 // prepare loop (inside to outside, [0.0 .. 1.0[)
926 double fUnitScale(0.0);
927 const double fUnitStep(1.0 / nSteps
);
929 for(sal_uInt32
a(0); a
< nSteps
; a
++, fUnitScale
+= fUnitStep
)
931 basegfx::B2DPolygon
aNew(aPolygon
);
933 aNew
.transform(basegfx::utils::createTranslateB2DHomMatrix(fDelta
* fUnitScale
, 0.0));
934 rContainer
.push_back(new PolyPolygonColorPrimitive2D(
935 basegfx::B2DPolyPolygon(aNew
),
936 basegfx::interpolate(getColorA(), getColorB(), fUnitScale
)));
941 SvgLinearAtomPrimitive2D::SvgLinearAtomPrimitive2D(
942 const basegfx::BColor
& aColorA
, double fOffsetA
,
943 const basegfx::BColor
& aColorB
, double fOffsetB
)
944 : DiscreteMetricDependentPrimitive2D(),
950 if(mfOffsetA
> mfOffsetB
)
952 OSL_ENSURE(false, "Wrong offset order (!)");
953 std::swap(mfOffsetA
, mfOffsetB
);
957 bool SvgLinearAtomPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
959 if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive
))
961 const SvgLinearAtomPrimitive2D
& rCompare
= static_cast< const SvgLinearAtomPrimitive2D
& >(rPrimitive
);
963 return (getColorA() == rCompare
.getColorA()
964 && getColorB() == rCompare
.getColorB()
965 && getOffsetA() == rCompare
.getOffsetA()
966 && getOffsetB() == rCompare
.getOffsetB());
973 ImplPrimitive2DIDBlock(SvgLinearAtomPrimitive2D
, PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D
)
975 } // end of namespace primitive2d
976 } // end of namespace drawinglayer
979 // SvgRadialAtomPrimitive2D class
981 namespace drawinglayer
983 namespace primitive2d
985 void SvgRadialAtomPrimitive2D::create2DDecomposition(Primitive2DContainer
& rContainer
, const geometry::ViewInformation2D
& /*rViewInformation*/) const
987 const double fDeltaScale(getScaleB() - getScaleA());
989 if(!basegfx::fTools::equalZero(fDeltaScale
))
991 // use one discrete unit for overlap (one pixel)
992 const double fDiscreteUnit(getDiscreteUnit());
994 // use color distance and discrete lengths to calculate step count
995 const sal_uInt32
nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDeltaScale
, fDiscreteUnit
));
997 // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes)
998 double fUnitScale(0.0);
999 const double fUnitStep(1.0 / nSteps
);
1001 for(sal_uInt32
a(0); a
< nSteps
; a
++, fUnitScale
+= fUnitStep
)
1003 basegfx::B2DHomMatrix aTransform
;
1004 const double fEndScale(getScaleB() - (fDeltaScale
* fUnitScale
));
1006 if(isTranslateSet())
1008 const basegfx::B2DVector
aTranslate(
1009 basegfx::interpolate(
1014 aTransform
= basegfx::utils::createScaleTranslateB2DHomMatrix(
1022 aTransform
= basegfx::utils::createScaleB2DHomMatrix(
1027 basegfx::B2DPolygon
aNew(basegfx::utils::createPolygonFromUnitCircle());
1029 aNew
.transform(aTransform
);
1030 rContainer
.push_back(new PolyPolygonColorPrimitive2D(
1031 basegfx::B2DPolyPolygon(aNew
),
1032 basegfx::interpolate(getColorB(), getColorA(), fUnitScale
)));
1037 SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D(
1038 const basegfx::BColor
& aColorA
, double fScaleA
, const basegfx::B2DVector
& rTranslateA
,
1039 const basegfx::BColor
& aColorB
, double fScaleB
, const basegfx::B2DVector
& rTranslateB
)
1040 : DiscreteMetricDependentPrimitive2D(),
1046 // check and evtl. set translations
1047 if(!rTranslateA
.equal(rTranslateB
))
1049 mpTranslate
.reset( new VectorPair(rTranslateA
, rTranslateB
) );
1052 // scale A and B have to be positive
1053 mfScaleA
= std::max(mfScaleA
, 0.0);
1054 mfScaleB
= std::max(mfScaleB
, 0.0);
1056 // scale B has to be bigger than scale A; swap if different
1057 if(mfScaleA
> mfScaleB
)
1059 OSL_ENSURE(false, "Wrong offset order (!)");
1060 std::swap(mfScaleA
, mfScaleB
);
1064 std::swap(mpTranslate
->maTranslateA
, mpTranslate
->maTranslateB
);
1069 SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D(
1070 const basegfx::BColor
& aColorA
, double fScaleA
,
1071 const basegfx::BColor
& aColorB
, double fScaleB
)
1072 : DiscreteMetricDependentPrimitive2D(),
1078 // scale A and B have to be positive
1079 mfScaleA
= std::max(mfScaleA
, 0.0);
1080 mfScaleB
= std::max(mfScaleB
, 0.0);
1082 // scale B has to be bigger than scale A; swap if different
1083 if(mfScaleA
> mfScaleB
)
1085 OSL_ENSURE(false, "Wrong offset order (!)");
1086 std::swap(mfScaleA
, mfScaleB
);
1090 SvgRadialAtomPrimitive2D::~SvgRadialAtomPrimitive2D()
1094 bool SvgRadialAtomPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
1096 if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive
))
1098 const SvgRadialAtomPrimitive2D
& rCompare
= static_cast< const SvgRadialAtomPrimitive2D
& >(rPrimitive
);
1100 if(getColorA() == rCompare
.getColorA()
1101 && getColorB() == rCompare
.getColorB()
1102 && getScaleA() == rCompare
.getScaleA()
1103 && getScaleB() == rCompare
.getScaleB())
1105 if(isTranslateSet() && rCompare
.isTranslateSet())
1107 return (getTranslateA() == rCompare
.getTranslateA()
1108 && getTranslateB() == rCompare
.getTranslateB());
1110 else if(!isTranslateSet() && !rCompare
.isTranslateSet())
1120 // provide unique ID
1121 ImplPrimitive2DIDBlock(SvgRadialAtomPrimitive2D
, PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D
)
1123 } // end of namespace primitive2d
1124 } // end of namespace drawinglayer
1126 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */