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 const double fTransFrom(1.0 - rFrom
.getOpacity());
358 const double fTransTo(1.0 - rTo
.getOpacity());
360 rTargetOpacity
.push_back(
361 new SvgLinearAtomPrimitive2D(
362 basegfx::BColor(fTransFrom
, fTransFrom
, fTransFrom
), rFrom
.getOffset() + nOffset
,
363 basegfx::BColor(fTransTo
,fTransTo
, fTransTo
), rTo
.getOffset() + nOffset
));
367 Primitive2DSequence
SvgLinearGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
& /*rViewInformation*/) const
369 Primitive2DSequence xRetval
;
371 if(!getPreconditionsChecked())
373 const_cast< SvgLinearGradientPrimitive2D
* >(this)->checkPreconditions();
378 // fill with last existing color
379 xRetval
= createSingleGradientEntryFill();
381 else if(getCreatesContent())
383 // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely
384 // invisible, width and height to fill are not empty
385 const basegfx::B2DRange
aPolyRange(getPolyPolygon().getB2DRange());
386 const double fPolyWidth(aPolyRange
.getWidth());
387 const double fPolyHeight(aPolyRange
.getHeight());
389 // create ObjectTransform based on polygon range
390 const basegfx::B2DHomMatrix
aObjectTransform(
391 basegfx::tools::createScaleTranslateB2DHomMatrix(
392 fPolyWidth
, fPolyHeight
,
393 aPolyRange
.getMinX(), aPolyRange
.getMinY()));
394 basegfx::B2DHomMatrix aUnitGradientToObject
;
395 static bool bInterpretAbsolute(true);
397 if(getUseUnitCoordinates())
399 // interpret in unit coordinate system -> object aspect ratio will scale result
400 // create unit transform from unit vector [0.0 .. 1.0] along the X-Axis to given
401 // gradient vector defined by Start,End
402 const basegfx::B2DVector
aVector(getEnd() - getStart());
403 const double fVectorLength(aVector
.getLength());
404 basegfx::B2DHomMatrix aUnitGradientToGradient
;
406 aUnitGradientToGradient
.scale(fVectorLength
, 1.0);
407 aUnitGradientToGradient
.rotate(atan2(aVector
.getY(), aVector
.getX()));
408 aUnitGradientToGradient
.translate(getStart().getX(), getStart().getY());
410 // create full transform from unit gradient coordinates to object coordinates
411 // including the SvgGradient transformation
412 aUnitGradientToObject
= aObjectTransform
* aUnitGradientToGradient
;
416 // interpret in object coordinate system -> object aspect ratio will not scale result
417 const basegfx::B2DPoint
aStart(aObjectTransform
* getStart());
418 const basegfx::B2DPoint
aEnd(aObjectTransform
* getEnd());
419 const basegfx::B2DVector
aVector(aEnd
- aStart
);
421 aUnitGradientToObject
.scale(aVector
.getLength(), 1.0);
422 aUnitGradientToObject
.rotate(atan2(aVector
.getY(), aVector
.getX()));
423 aUnitGradientToObject
.translate(aStart
.getX(), aStart
.getY());
426 // create inverse from it
427 basegfx::B2DHomMatrix
aObjectToUnitGradient(aUnitGradientToObject
);
428 aObjectToUnitGradient
.invert();
430 // back-transform polygon to unit gradient coordinates and get
431 // UnitRage. This is the range the gradient has to cover
432 basegfx::B2DPolyPolygon
aUnitPoly(getPolyPolygon());
433 aUnitPoly
.transform(aObjectToUnitGradient
);
434 const basegfx::B2DRange
aUnitRange(aUnitPoly
.getB2DRange());
436 // prepare result vectors
437 Primitive2DVector aTargetColor
;
438 Primitive2DVector aTargetOpacity
;
440 if(basegfx::fTools::more(aUnitRange
.getWidth(), 0.0))
442 // add a pre-multiply to aUnitGradientToObject to allow
443 // multiplication of the polygon(xl, 0.0, xr, 1.0)
444 const basegfx::B2DHomMatrix
aPreMultiply(
445 basegfx::tools::createScaleTranslateB2DHomMatrix(
446 1.0, aUnitRange
.getHeight(), 0.0, aUnitRange
.getMinY()));
447 aUnitGradientToObject
= aUnitGradientToObject
* aPreMultiply
;
449 // create central run, may also already do all necessary when
450 // Spread_pad is set as SpreadMethod and/or the range is smaller
451 double fPos(createRun(aTargetColor
, aTargetOpacity
, aUnitRange
.getMinX(), aUnitRange
.getMaxX(), getGradientEntries(), 0));
453 if(fPos
< aUnitRange
.getMaxX())
455 // can only happen when SpreadMethod is Spread_reflect or Spread_repeat,
456 // else the start and end pads are already created and fPos == aUnitRange.getMaxX().
457 // Its possible to express the repeated linear gradient by adding the
458 // transformed central run. Crete it this way
459 Primitive2DSequence
aTargetColorEntries(Primitive2DVectorToPrimitive2DSequence(aTargetColor
));
460 Primitive2DSequence
aTargetOpacityEntries(Primitive2DVectorToPrimitive2DSequence(aTargetOpacity
));
461 aTargetColor
.clear();
462 aTargetOpacity
.clear();
464 if(aTargetColorEntries
.hasElements())
466 // add original central run as group primitive
467 aTargetColor
.push_back(new GroupPrimitive2D(aTargetColorEntries
));
469 if(aTargetOpacityEntries
.hasElements())
471 aTargetOpacity
.push_back(new GroupPrimitive2D(aTargetOpacityEntries
));
476 sal_Int32
nOffset(0);
478 while(fPos
> aUnitRange
.getMinX())
483 basegfx::B2DHomMatrix aTransform
;
484 const bool bMirror(Spread_reflect
== getSpreadMethod() && (nOffset
% 2));
488 aTransform
.scale(-1.0, 1.0);
489 aTransform
.translate(fPos
+ 1.0, 0.0);
493 aTransform
.translate(fPos
, 0.0);
496 aTargetColor
.push_back(new TransformPrimitive2D(aTransform
, aTargetColorEntries
));
498 if(aTargetOpacityEntries
.hasElements())
500 aTargetOpacity
.push_back(new TransformPrimitive2D(aTransform
, aTargetOpacityEntries
));
508 while(fPos
< aUnitRange
.getMaxX())
510 basegfx::B2DHomMatrix aTransform
;
511 const bool bMirror(Spread_reflect
== getSpreadMethod() && (nOffset
% 2));
515 aTransform
.scale(-1.0, 1.0);
516 aTransform
.translate(fPos
+ 1.0, 0.0);
520 aTransform
.translate(fPos
, 0.0);
523 aTargetColor
.push_back(new TransformPrimitive2D(aTransform
, aTargetColorEntries
));
525 if(aTargetOpacityEntries
.hasElements())
527 aTargetOpacity
.push_back(new TransformPrimitive2D(aTransform
, aTargetOpacityEntries
));
537 xRetval
= createResult(aTargetColor
, aTargetOpacity
, aUnitGradientToObject
);
543 SvgLinearGradientPrimitive2D::SvgLinearGradientPrimitive2D(
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(rPolyPolygon
, rGradientEntries
, rStart
, bUseUnitCoordinates
, aSpreadMethod
),
556 bool SvgLinearGradientPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
558 const SvgGradientHelper
* pSvgGradientHelper
= dynamic_cast< const SvgGradientHelper
* >(&rPrimitive
);
560 if(pSvgGradientHelper
&& SvgGradientHelper::equalTo(*pSvgGradientHelper
))
562 const SvgLinearGradientPrimitive2D
& rCompare
= static_cast< const SvgLinearGradientPrimitive2D
& >(rPrimitive
);
564 return (getEnd() == rCompare
.getEnd());
570 basegfx::B2DRange
SvgLinearGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D
& /*rViewInformation*/) const
572 // return ObjectRange
573 return getPolyPolygon().getB2DRange();
577 ImplPrimitrive2DIDBlock(SvgLinearGradientPrimitive2D
, PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D
)
579 } // end of namespace primitive2d
580 } // end of namespace drawinglayer
582 //////////////////////////////////////////////////////////////////////////////
584 namespace drawinglayer
586 namespace primitive2d
588 void SvgRadialGradientPrimitive2D::checkPreconditions()
591 SvgGradientHelper::checkPreconditions();
593 if(getCreatesContent())
596 if(basegfx::fTools::equalZero(getRadius()))
598 // fill with single color using last stop color
604 void SvgRadialGradientPrimitive2D::createAtom(
605 Primitive2DVector
& rTargetColor
,
606 Primitive2DVector
& rTargetOpacity
,
607 const SvgGradientEntry
& rFrom
,
608 const SvgGradientEntry
& rTo
,
609 sal_Int32 nOffset
) const
611 // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset())
612 if(rFrom
.getOffset() == rTo
.getOffset())
614 OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)");
618 const double fScaleFrom(rFrom
.getOffset() + nOffset
);
619 const double fScaleTo(rTo
.getOffset() + nOffset
);
623 const basegfx::B2DVector
aTranslateFrom(maFocalVector
* (maFocalLength
- fScaleFrom
));
624 const basegfx::B2DVector
aTranslateTo(maFocalVector
* (maFocalLength
- fScaleTo
));
626 rTargetColor
.push_back(
627 new SvgRadialAtomPrimitive2D(
628 rFrom
.getColor(), fScaleFrom
, aTranslateFrom
,
629 rTo
.getColor(), fScaleTo
, aTranslateTo
));
633 rTargetColor
.push_back(
634 new SvgRadialAtomPrimitive2D(
635 rFrom
.getColor(), fScaleFrom
,
636 rTo
.getColor(), fScaleTo
));
639 const double fTransFrom(1.0 - rFrom
.getOpacity());
640 const double fTransTo(1.0 - rTo
.getOpacity());
641 const basegfx::BColor
aColorFrom(fTransFrom
, fTransFrom
, fTransFrom
);
642 const basegfx::BColor
aColorTo(fTransTo
, fTransTo
, fTransTo
);
646 const basegfx::B2DVector
aTranslateFrom(maFocalVector
* (maFocalLength
- fScaleFrom
));
647 const basegfx::B2DVector
aTranslateTo(maFocalVector
* (maFocalLength
- fScaleTo
));
649 rTargetOpacity
.push_back(
650 new SvgRadialAtomPrimitive2D(
651 aColorFrom
, fScaleFrom
, aTranslateFrom
,
652 aColorTo
, fScaleTo
, aTranslateTo
));
656 rTargetOpacity
.push_back(
657 new SvgRadialAtomPrimitive2D(
658 aColorFrom
, fScaleFrom
,
659 aColorTo
, fScaleTo
));
664 const SvgGradientEntryVector
& SvgRadialGradientPrimitive2D::getMirroredGradientEntries() const
666 if(maMirroredGradientEntries
.empty() && !getGradientEntries().empty())
668 const_cast< SvgRadialGradientPrimitive2D
* >(this)->createMirroredGradientEntries();
671 return maMirroredGradientEntries
;
674 void SvgRadialGradientPrimitive2D::createMirroredGradientEntries()
676 if(maMirroredGradientEntries
.empty() && !getGradientEntries().empty())
678 const sal_uInt32
nCount(getGradientEntries().size());
679 maMirroredGradientEntries
.clear();
680 maMirroredGradientEntries
.reserve(nCount
);
682 for(sal_uInt32
a(0); a
< nCount
; a
++)
684 const SvgGradientEntry
& rCandidate
= getGradientEntries()[nCount
- 1 - a
];
686 maMirroredGradientEntries
.push_back(
688 1.0 - rCandidate
.getOffset(),
689 rCandidate
.getColor(),
690 rCandidate
.getOpacity()));
695 Primitive2DSequence
SvgRadialGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
& /*rViewInformation*/) const
697 Primitive2DSequence xRetval
;
699 if(!getPreconditionsChecked())
701 const_cast< SvgRadialGradientPrimitive2D
* >(this)->checkPreconditions();
706 // fill with last existing color
707 xRetval
= createSingleGradientEntryFill();
709 else if(getCreatesContent())
711 // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely
712 // invisible, width and height to fill are not empty
713 const basegfx::B2DRange
aPolyRange(getPolyPolygon().getB2DRange());
714 const double fPolyWidth(aPolyRange
.getWidth());
715 const double fPolyHeight(aPolyRange
.getHeight());
717 // create ObjectTransform based on polygon range
718 const basegfx::B2DHomMatrix
aObjectTransform(
719 basegfx::tools::createScaleTranslateB2DHomMatrix(
720 fPolyWidth
, fPolyHeight
,
721 aPolyRange
.getMinX(), aPolyRange
.getMinY()));
722 basegfx::B2DHomMatrix aUnitGradientToObject
;
723 static bool bInterpretAbsolute(true);
725 if(getUseUnitCoordinates())
727 // interpret in unit coordinate system -> object aspect ratio will scale result
728 // create unit transform from unit vector to given linear gradient vector
729 basegfx::B2DHomMatrix aUnitGradientToGradient
;
731 aUnitGradientToGradient
.scale(getRadius(), getRadius());
732 aUnitGradientToGradient
.translate(getStart().getX(), getStart().getY());
734 // create full transform from unit gradient coordinates to object coordinates
735 // including the SvgGradient transformation
736 aUnitGradientToObject
= aObjectTransform
* aUnitGradientToGradient
;
740 // interpret in object coordinate system -> object aspect ratio will not scale result
741 const double fRadius((aObjectTransform
* basegfx::B2DVector(getRadius(), 0.0)).getLength());
742 const basegfx::B2DPoint
aStart(aObjectTransform
* getStart());
744 aUnitGradientToObject
.scale(fRadius
, fRadius
);
745 aUnitGradientToObject
.translate(aStart
.getX(), aStart
.getY());
748 // create inverse from it
749 basegfx::B2DHomMatrix
aObjectToUnitGradient(aUnitGradientToObject
);
750 aObjectToUnitGradient
.invert();
752 // back-transform polygon to unit gradient coordinates and get
753 // UnitRage. This is the range the gradient has to cover
754 basegfx::B2DPolyPolygon
aUnitPoly(getPolyPolygon());
755 aUnitPoly
.transform(aObjectToUnitGradient
);
756 const basegfx::B2DRange
aUnitRange(aUnitPoly
.getB2DRange());
758 // create range which the gradient has to cover to cover the whole given geometry.
759 // For circle, go from 0.0 to max radius in all directions (the corners)
760 double fMax(basegfx::B2DVector(aUnitRange
.getMinimum()).getLength());
761 fMax
= std::max(fMax
, basegfx::B2DVector(aUnitRange
.getMaximum()).getLength());
762 fMax
= std::max(fMax
, basegfx::B2DVector(aUnitRange
.getMinX(), aUnitRange
.getMaxY()).getLength());
763 fMax
= std::max(fMax
, basegfx::B2DVector(aUnitRange
.getMaxX(), aUnitRange
.getMinY()).getLength());
765 // prepare result vectors
766 Primitive2DVector aTargetColor
;
767 Primitive2DVector aTargetOpacity
;
771 // prepare maFocalVector
774 const_cast< SvgRadialGradientPrimitive2D
* >(this)->maFocalLength
= fMax
;
777 // create central run, may also already do all necessary when
778 // Spread_pad is set as SpreadMethod and/or the range is smaller
779 double fPos(createRun(aTargetColor
, aTargetOpacity
, 0.0, fMax
, getGradientEntries(), 0));
783 // can only happen when SpreadMethod is Spread_reflect or Spread_repeat,
784 // else the start and end pads are already created and fPos == fMax.
785 // For radial there is no way to transform the already created
786 // central run, it needs to be created from 1.0 to fMax
787 sal_Int32
nOffset(1);
791 const bool bMirror(Spread_reflect
== getSpreadMethod() && (nOffset
% 2));
795 createRun(aTargetColor
, aTargetOpacity
, 0.0, fMax
, getMirroredGradientEntries(), nOffset
);
799 createRun(aTargetColor
, aTargetOpacity
, 0.0, fMax
, getGradientEntries(), nOffset
);
808 xRetval
= createResult(aTargetColor
, aTargetOpacity
, aUnitGradientToObject
, true);
814 SvgRadialGradientPrimitive2D::SvgRadialGradientPrimitive2D(
815 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
816 const SvgGradientEntryVector
& rGradientEntries
,
817 const basegfx::B2DPoint
& rStart
,
819 bool bUseUnitCoordinates
,
820 SpreadMethod aSpreadMethod
,
821 const basegfx::B2DPoint
* pFocal
)
822 : BufferedDecompositionPrimitive2D(),
823 SvgGradientHelper(rPolyPolygon
, rGradientEntries
, rStart
, bUseUnitCoordinates
, aSpreadMethod
),
826 maFocalVector(0.0, 0.0),
828 maMirroredGradientEntries(),
834 maFocalVector
= maFocal
- getStart();
839 bool SvgRadialGradientPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
841 const SvgGradientHelper
* pSvgGradientHelper
= dynamic_cast< const SvgGradientHelper
* >(&rPrimitive
);
843 if(pSvgGradientHelper
&& SvgGradientHelper::equalTo(*pSvgGradientHelper
))
845 const SvgRadialGradientPrimitive2D
& rCompare
= static_cast< const SvgRadialGradientPrimitive2D
& >(rPrimitive
);
847 if(getRadius() == rCompare
.getRadius())
849 if(isFocalSet() == rCompare
.isFocalSet())
853 return getFocal() == rCompare
.getFocal();
866 basegfx::B2DRange
SvgRadialGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D
& /*rViewInformation*/) const
868 // return ObjectRange
869 return getPolyPolygon().getB2DRange();
873 ImplPrimitrive2DIDBlock(SvgRadialGradientPrimitive2D
, PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D
)
875 } // end of namespace primitive2d
876 } // end of namespace drawinglayer
878 //////////////////////////////////////////////////////////////////////////////
879 // SvgLinearAtomPrimitive2D class
881 namespace drawinglayer
883 namespace primitive2d
885 Primitive2DSequence
SvgLinearAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
& /*rViewInformation*/) const
887 Primitive2DSequence xRetval
;
888 const double fDelta(getOffsetB() - getOffsetA());
890 if(!basegfx::fTools::equalZero(fDelta
))
892 // use one discrete unit for overlap (one pixel)
893 const double fDiscreteUnit(getDiscreteUnit());
895 // use color distance and discrete lengths to calculate step count
896 const sal_uInt32
nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDelta
, fDiscreteUnit
));
898 // prepare loop and polygon (with overlap for linear gradients)
900 double fStep(fDelta
/ nSteps
);
901 const basegfx::B2DPolygon
aPolygon(
902 basegfx::tools::createPolygonFromRect(
904 getOffsetA() - fDiscreteUnit
,
906 getOffsetA() + fStep
+ fDiscreteUnit
,
909 // loop and create primitives
910 xRetval
.realloc(nSteps
);
912 for(sal_uInt32
a(0); a
< nSteps
; a
++, fStart
+= fStep
)
914 basegfx::B2DPolygon
aNew(aPolygon
);
916 aNew
.transform(basegfx::tools::createTranslateB2DHomMatrix(fStart
, 0.0));
917 xRetval
[a
] = new PolyPolygonColorPrimitive2D(
918 basegfx::B2DPolyPolygon(aNew
),
919 basegfx::interpolate(getColorA(), getColorB(), fStart
/fDelta
));
926 SvgLinearAtomPrimitive2D::SvgLinearAtomPrimitive2D(
927 const basegfx::BColor
& aColorA
, double fOffsetA
,
928 const basegfx::BColor
& aColorB
, double fOffsetB
)
929 : DiscreteMetricDependentPrimitive2D(),
935 if(mfOffsetA
> mfOffsetB
)
937 OSL_ENSURE(false, "Wrong offset order (!)");
938 ::std::swap(mfOffsetA
, mfOffsetB
);
942 bool SvgLinearAtomPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
944 if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive
))
946 const SvgLinearAtomPrimitive2D
& rCompare
= static_cast< const SvgLinearAtomPrimitive2D
& >(rPrimitive
);
948 return (getColorA() == rCompare
.getColorA()
949 && getColorB() == rCompare
.getColorB()
950 && getOffsetA() == rCompare
.getOffsetA()
951 && getOffsetB() == rCompare
.getOffsetB());
958 ImplPrimitrive2DIDBlock(SvgLinearAtomPrimitive2D
, PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D
)
960 } // end of namespace primitive2d
961 } // end of namespace drawinglayer
963 //////////////////////////////////////////////////////////////////////////////
964 // SvgRadialAtomPrimitive2D class
966 namespace drawinglayer
968 namespace primitive2d
970 Primitive2DSequence
SvgRadialAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
& /*rViewInformation*/) const
972 Primitive2DSequence xRetval
;
973 const double fDeltaScale(getScaleB() - getScaleA());
975 if(!basegfx::fTools::equalZero(fDeltaScale
))
977 // use one discrete unit for overlap (one pixel)
978 const double fDiscreteUnit(getDiscreteUnit());
980 // use color distance and discrete lengths to calculate step count
981 const sal_uInt32
nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDeltaScale
, fDiscreteUnit
));
983 // prepare loop (outside to inside, full polygons, no polypolygons with holes)
984 double fEndScale(getScaleB());
985 double fStepScale(fDeltaScale
/ nSteps
);
987 // loop and create primitives
988 xRetval
.realloc(nSteps
);
990 for(sal_uInt32
a(0); a
< nSteps
; a
++, fEndScale
-= fStepScale
)
992 const double fUnitScale(fEndScale
/fDeltaScale
);
993 basegfx::B2DHomMatrix aTransform
;
997 const basegfx::B2DVector
aTranslate(
998 basegfx::interpolate(
1003 aTransform
= basegfx::tools::createScaleTranslateB2DHomMatrix(
1011 aTransform
= basegfx::tools::createScaleB2DHomMatrix(
1016 basegfx::B2DPolygon
aNew(basegfx::tools::createPolygonFromUnitCircle());
1018 aNew
.transform(aTransform
);
1019 xRetval
[a
] = new PolyPolygonColorPrimitive2D(
1020 basegfx::B2DPolyPolygon(aNew
),
1021 basegfx::interpolate(getColorA(), getColorB(), fUnitScale
));
1028 SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D(
1029 const basegfx::BColor
& aColorA
, double fScaleA
, const basegfx::B2DVector
& rTranslateA
,
1030 const basegfx::BColor
& aColorB
, double fScaleB
, const basegfx::B2DVector
& rTranslateB
)
1031 : DiscreteMetricDependentPrimitive2D(),
1038 // check and evtl. set translations
1039 if(!rTranslateA
.equal(rTranslateB
))
1041 mpTranslate
= new VectorPair(rTranslateA
, rTranslateB
);
1044 // scale A and B have to be positive
1045 mfScaleA
= ::std::max(mfScaleA
, 0.0);
1046 mfScaleB
= ::std::max(mfScaleB
, 0.0);
1048 // scale B has to be bigger than scale A; swap if different
1049 if(mfScaleA
> mfScaleB
)
1051 OSL_ENSURE(false, "Wrong offset order (!)");
1052 ::std::swap(mfScaleA
, mfScaleB
);
1056 ::std::swap(mpTranslate
->maTranslateA
, mpTranslate
->maTranslateB
);
1061 SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D(
1062 const basegfx::BColor
& aColorA
, double fScaleA
,
1063 const basegfx::BColor
& aColorB
, double fScaleB
)
1064 : DiscreteMetricDependentPrimitive2D(),
1071 // scale A and B have to be positive
1072 mfScaleA
= ::std::max(mfScaleA
, 0.0);
1073 mfScaleB
= ::std::max(mfScaleB
, 0.0);
1075 // scale B has to be bigger than scale A; swap if different
1076 if(mfScaleA
> mfScaleB
)
1078 OSL_ENSURE(false, "Wrong offset order (!)");
1079 ::std::swap(mfScaleA
, mfScaleB
);
1083 SvgRadialAtomPrimitive2D::~SvgRadialAtomPrimitive2D()
1092 bool SvgRadialAtomPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
1094 if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive
))
1096 const SvgRadialAtomPrimitive2D
& rCompare
= static_cast< const SvgRadialAtomPrimitive2D
& >(rPrimitive
);
1098 if(getColorA() == rCompare
.getColorA()
1099 && getColorB() == rCompare
.getColorB()
1100 && getScaleA() == rCompare
.getScaleA()
1101 && getScaleB() == rCompare
.getScaleB())
1103 if(isTranslateSet() && rCompare
.isTranslateSet())
1105 return (getTranslateA() == rCompare
.getTranslateA()
1106 && getTranslateB() == rCompare
.getTranslateB());
1108 else if(!isTranslateSet() && !rCompare
.isTranslateSet())
1118 // provide unique ID
1119 ImplPrimitrive2DIDBlock(SvgRadialAtomPrimitive2D
, PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D
)
1121 } // end of namespace primitive2d
1122 } // end of namespace drawinglayer
1124 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */