#120616# Corrected SVG gradient primitive and it's decomposition
[LibreOffice.git] / drawinglayer / source / primitive2d / svggradientprimitive2d.cxx
blob98dc1ca966d3f9fe8e726847137192ffe0e30d27
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 //////////////////////////////////////////////////////////////////////////////
39 namespace
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));
46 if(nSteps)
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));
58 return nSteps;
60 } // end of anonymous namespace
62 //////////////////////////////////////////////////////////////////////////////
64 namespace drawinglayer
66 namespace primitive2d
68 Primitive2DSequence SvgGradientHelper::createSingleGradientEntryFill() const
70 const SvgGradientEntryVector& rEntries = getGradientEntries();
71 const sal_uInt32 nCount(rEntries.size());
72 Primitive2DSequence xRetval;
74 if(nCount)
76 const SvgGradientEntry& rSingleEntry = rEntries[nCount - 1];
77 const double fOpacity(rSingleEntry.getOpacity());
79 if(fOpacity > 0.0)
81 Primitive2DReference xRef(
82 new PolyPolygonColorPrimitive2D(
83 getPolyPolygon(),
84 rSingleEntry.getColor()));
86 if(fOpacity < 1.0)
88 const Primitive2DSequence aContent(&xRef, 1);
90 xRef = Primitive2DReference(
91 new UnifiedTransparencePrimitive2D(
92 aContent,
93 1.0 - fOpacity));
96 xRetval = Primitive2DSequence(&xRef, 1);
99 else
101 OSL_ENSURE(false, "Single gradient entry construction without entry (!)");
104 return xRetval;
107 void SvgGradientHelper::checkPreconditions()
109 mbPreconditionsChecked = true;
110 const SvgGradientEntryVector& rEntries = getGradientEntries();
112 if(rEntries.empty())
114 // no fill at all
116 else
118 const sal_uInt32 nCount(rEntries.size());
120 if(1 == nCount)
122 // fill with single existing color
123 setSingleEntry();
125 else
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()))
139 // invisible
140 mbFullyOpaque = false;
142 else if(basegfx::fTools::equal(rCandidate.getOpacity(), 1.0))
144 // completely opaque
145 bAllInvisible = false;
147 else
149 // opacity
150 bAllInvisible = false;
151 mbFullyOpaque = false;
155 if(bAllInvisible)
157 // all invisible, nothing to do
159 else
161 const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange());
163 if(aPolyRange.isEmpty())
165 // no range to fill, nothing to do
167 else
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
176 else
178 mbCreatesContent = true;
186 double SvgGradientHelper::createRun(
187 Primitive2DVector& rTargetColor,
188 Primitive2DVector& rTargetOpacity,
189 double fPos,
190 double fMax,
191 const SvgGradientEntryVector& rEntries,
192 sal_Int32 nOffset) const
194 const sal_uInt32 nCount(rEntries.size());
196 if(nCount)
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);
232 else
234 OSL_ENSURE(false, "GradientAtom creation without ColorStops (!)");
235 fPos = fMax;
238 return fPos;
241 Primitive2DSequence SvgGradientHelper::createResult(
242 const Primitive2DVector& rTargetColor,
243 const Primitive2DVector& rTargetOpacity,
244 const basegfx::B2DHomMatrix& rUnitGradientToObject,
245 bool bInvert) const
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(
258 aTargetColorEntries,
259 aTargetOpacityEntries);
261 xRefContent = new TransformPrimitive2D(
262 rUnitGradientToObject,
263 Primitive2DSequence(&xRefOpacity, 1));
265 else
267 xRefContent = new TransformPrimitive2D(
268 rUnitGradientToObject,
269 aTargetColorEntries);
272 xRefContent = new MaskPrimitive2D(
273 getPolyPolygon(),
274 Primitive2DSequence(&xRefContent, 1));
276 xRetval = Primitive2DSequence(&xRefContent, 1);
279 return xRetval;
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),
290 maStart(rStart),
291 maSpreadMethod(aSpreadMethod),
292 mbPreconditionsChecked(false),
293 mbCreatesContent(false),
294 mbSingleEntry(false),
295 mbFullyOpaque(true),
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()
322 // call parent
323 SvgGradientHelper::checkPreconditions();
325 if(getCreatesContent())
327 // Check Vector
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
333 setSingleEntry();
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 (!)");
350 else
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();
376 if(getSingleEntry())
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;
414 else
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));
474 // add negative runs
475 fPos = 0.0;
476 sal_Int32 nOffset(0);
478 while(fPos > aUnitRange.getMinX())
480 fPos -= 1.0;
481 nOffset++;
483 basegfx::B2DHomMatrix aTransform;
484 const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2));
486 if(bMirror)
488 aTransform.scale(-1.0, 1.0);
489 aTransform.translate(fPos + 1.0, 0.0);
491 else
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));
504 // add positive runs
505 fPos = 1.0;
506 nOffset = 1;
508 while(fPos < aUnitRange.getMaxX())
510 basegfx::B2DHomMatrix aTransform;
511 const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2));
513 if(bMirror)
515 aTransform.scale(-1.0, 1.0);
516 aTransform.translate(fPos + 1.0, 0.0);
518 else
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));
530 fPos += 1.0;
531 nOffset++;
537 xRetval = createResult(aTargetColor, aTargetOpacity, aUnitGradientToObject);
540 return xRetval;
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),
552 maEnd(rEnd)
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());
567 return false;
570 basegfx::B2DRange SvgLinearGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
572 // return ObjectRange
573 return getPolyPolygon().getB2DRange();
576 // provide unique ID
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()
590 // call parent
591 SvgGradientHelper::checkPreconditions();
593 if(getCreatesContent())
595 // Check Radius
596 if(basegfx::fTools::equalZero(getRadius()))
598 // fill with single color using last stop color
599 setSingleEntry();
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 (!)");
616 else
618 const double fScaleFrom(rFrom.getOffset() + nOffset);
619 const double fScaleTo(rTo.getOffset() + nOffset);
621 if(isFocalSet())
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));
631 else
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);
644 if(isFocalSet())
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));
654 else
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(
687 SvgGradientEntry(
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();
704 if(getSingleEntry())
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;
738 else
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;
769 if(0.0 < fMax)
771 // prepare maFocalVector
772 if(isFocalSet())
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));
781 if(fPos < fMax)
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);
789 while(fPos < fMax)
791 const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2));
793 if(bMirror)
795 createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getMirroredGradientEntries(), nOffset);
797 else
799 createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getGradientEntries(), nOffset);
802 nOffset++;
803 fPos += 1.0;
808 xRetval = createResult(aTargetColor, aTargetOpacity, aUnitGradientToObject, true);
811 return xRetval;
814 SvgRadialGradientPrimitive2D::SvgRadialGradientPrimitive2D(
815 const basegfx::B2DPolyPolygon& rPolyPolygon,
816 const SvgGradientEntryVector& rGradientEntries,
817 const basegfx::B2DPoint& rStart,
818 double fRadius,
819 bool bUseUnitCoordinates,
820 SpreadMethod aSpreadMethod,
821 const basegfx::B2DPoint* pFocal)
822 : BufferedDecompositionPrimitive2D(),
823 SvgGradientHelper(rPolyPolygon, rGradientEntries, rStart, bUseUnitCoordinates, aSpreadMethod),
824 mfRadius(fRadius),
825 maFocal(rStart),
826 maFocalVector(0.0, 0.0),
827 maFocalLength(0.0),
828 maMirroredGradientEntries(),
829 mbFocalSet(false)
831 if(pFocal)
833 maFocal = *pFocal;
834 maFocalVector = maFocal - getStart();
835 mbFocalSet = true;
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())
851 if(isFocalSet())
853 return getFocal() == rCompare.getFocal();
855 else
857 return true;
863 return false;
866 basegfx::B2DRange SvgRadialGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
868 // return ObjectRange
869 return getPolyPolygon().getB2DRange();
872 // provide unique ID
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)
899 double fStart(0.0);
900 double fStep(fDelta / nSteps);
901 const basegfx::B2DPolygon aPolygon(
902 basegfx::tools::createPolygonFromRect(
903 basegfx::B2DRange(
904 getOffsetA() - fDiscreteUnit,
905 0.0,
906 getOffsetA() + fStep + fDiscreteUnit,
907 1.0)));
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));
923 return xRetval;
926 SvgLinearAtomPrimitive2D::SvgLinearAtomPrimitive2D(
927 const basegfx::BColor& aColorA, double fOffsetA,
928 const basegfx::BColor& aColorB, double fOffsetB)
929 : DiscreteMetricDependentPrimitive2D(),
930 maColorA(aColorA),
931 maColorB(aColorB),
932 mfOffsetA(fOffsetA),
933 mfOffsetB(fOffsetB)
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());
954 return false;
957 // provide unique ID
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;
995 if(isTranslateSet())
997 const basegfx::B2DVector aTranslate(
998 basegfx::interpolate(
999 getTranslateA(),
1000 getTranslateB(),
1001 fUnitScale));
1003 aTransform = basegfx::tools::createScaleTranslateB2DHomMatrix(
1004 fEndScale,
1005 fEndScale,
1006 aTranslate.getX(),
1007 aTranslate.getY());
1009 else
1011 aTransform = basegfx::tools::createScaleB2DHomMatrix(
1012 fEndScale,
1013 fEndScale);
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));
1025 return xRetval;
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(),
1032 maColorA(aColorA),
1033 maColorB(aColorB),
1034 mfScaleA(fScaleA),
1035 mfScaleB(fScaleB),
1036 mpTranslate(0)
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);
1054 if(mpTranslate)
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(),
1065 maColorA(aColorA),
1066 maColorB(aColorB),
1067 mfScaleA(fScaleA),
1068 mfScaleB(fScaleB),
1069 mpTranslate(0)
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()
1085 if(mpTranslate)
1087 delete mpTranslate;
1088 mpTranslate = 0;
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())
1110 return true;
1115 return false;
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: */