1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_drawinglayer.hxx"
24 
25 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
26 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
27 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
28 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
29 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
30 #include <basegfx/matrix/b2dhommatrixtools.hxx>
31 #include <basegfx/polygon/b2dpolygontools.hxx>
32 #include <basegfx/polygon/b2dpolygon.hxx>
33 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
34 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
35 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
36 #include <drawinglayer/geometry/viewinformation2d.hxx>
37 
38 //////////////////////////////////////////////////////////////////////////////
39 
40 using namespace com::sun::star;
41 
42 //////////////////////////////////////////////////////////////////////////////
43 
44 namespace
45 {
46     sal_uInt32 calculateStepsForSvgGradient(const basegfx::BColor& rColorA, const basegfx::BColor& rColorB, double fDelta, double fDiscreteUnit)
47     {
48         // use color distance, assume to do every color step (full quality)
49         sal_uInt32 nSteps(basegfx::fround(rColorA.getDistance(rColorB) * 255.0));
50 
51         if(nSteps)
52         {
53             // calc discrete length to change color all 1.5 disctete units (pixels)
54             const sal_uInt32 nDistSteps(basegfx::fround(fDelta / (fDiscreteUnit * 1.5)));
55 
56             nSteps = std::min(nSteps, nDistSteps);
57         }
58 
59         // roughly cut when too big or too small
60         nSteps = std::min(nSteps, sal_uInt32(255));
61         nSteps = std::max(nSteps, sal_uInt32(1));
62 
63         return nSteps;
64     }
65 } // end of anonymous namespace
66 
67 //////////////////////////////////////////////////////////////////////////////
68 
69 namespace drawinglayer
70 {
71     namespace primitive2d
72     {
73         Primitive2DSequence SvgGradientHelper::createSingleGradientEntryFill() const
74         {
75             const SvgGradientEntryVector& rEntries = getGradientEntries();
76             const sal_uInt32 nCount(rEntries.size());
77             Primitive2DSequence xRetval;
78 
79             if(nCount)
80             {
81                 const SvgGradientEntry& rSingleEntry = rEntries[nCount - 1];
82                 const double fOpacity(rSingleEntry.getOpacity());
83 
84                 if(fOpacity > 0.0)
85                 {
86                     Primitive2DReference xRef(
87                         new PolyPolygonColorPrimitive2D(
88                             getPolyPolygon(),
89                             rSingleEntry.getColor()));
90 
91                     if(fOpacity < 1.0)
92                     {
93                         const Primitive2DSequence aContent(&xRef, 1);
94 
95                         xRef = Primitive2DReference(
96                             new UnifiedTransparencePrimitive2D(
97                                 aContent,
98                                 1.0 - fOpacity));
99                     }
100 
101                     xRetval = Primitive2DSequence(&xRef, 1);
102                 }
103             }
104             else
105             {
106                 OSL_ENSURE(false, "Single gradient entry construction without entry (!)");
107             }
108 
109             return xRetval;
110         }
111 
112         void SvgGradientHelper::checkPreconditions()
113         {
114             mbPreconditionsChecked = true;
115             const SvgGradientEntryVector& rEntries = getGradientEntries();
116 
117             if(rEntries.empty())
118             {
119                 // no fill at all
120             }
121             else
122             {
123                 const sal_uInt32 nCount(rEntries.size());
124 
125                 if(1 == nCount)
126                 {
127                     // fill with single existing color
128                     setSingleEntry();
129                 }
130                 else
131                 {
132                     // sort maGradientEntries when more than one
133                     std::sort(maGradientEntries.begin(), maGradientEntries.end());
134 
135                     // gradient with at least two colors
136                     bool bAllInvisible(true);
137 
138                     for(sal_uInt32 a(0); a < nCount; a++)
139                     {
140                         const SvgGradientEntry& rCandidate = rEntries[a];
141 
142                         if(basegfx::fTools::equalZero(rCandidate.getOpacity()))
143                         {
144                             // invisible
145                             mbFullyOpaque = false;
146                         }
147                         else if(basegfx::fTools::equal(rCandidate.getOpacity(), 1.0))
148                         {
149                             // completely opaque
150                             bAllInvisible = false;
151                         }
152                         else
153                         {
154                             // opacity
155                             bAllInvisible = false;
156                             mbFullyOpaque = false;
157                         }
158                     }
159 
160                     if(bAllInvisible)
161                     {
162                         // all invisible, nothing to do
163                     }
164                     else
165                     {
166                         const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange());
167 
168                         if(aPolyRange.isEmpty())
169                         {
170                             // no range to fill, nothing to do
171                         }
172                         else
173                         {
174                             const double fPolyWidth(aPolyRange.getWidth());
175                             const double fPolyHeight(aPolyRange.getHeight());
176 
177                             if(basegfx::fTools::equalZero(fPolyWidth) || basegfx::fTools::equalZero(fPolyHeight))
178                             {
179                                 // no width/height to fill, nothing to do
180                             }
181                             else
182                             {
183                                 mbCreatesContent = true;
184                             }
185                         }
186                     }
187                 }
188             }
189         }
190 
191         double SvgGradientHelper::createRun(
192             Primitive2DVector& rTargetColor,
193             Primitive2DVector& rTargetOpacity,
194             double fPos,
195             double fMax,
196             const SvgGradientEntryVector& rEntries,
197             sal_Int32 nOffset) const
198         {
199             const sal_uInt32 nCount(rEntries.size());
200 
201             if(nCount)
202             {
203                 const SvgGradientEntry& rStart = rEntries[0];
204                 const bool bCreateStartPad(fPos < 0.0 && Spread_pad == getSpreadMethod());
205                 const bool bCreateStartFill(rStart.getOffset() > 0.0);
206                 sal_uInt32 nIndex(0);
207 
208                 if(bCreateStartPad || bCreateStartFill)
209                 {
210                     const SvgGradientEntry aTemp(bCreateStartPad ? fPos : 0.0, rStart.getColor(), rStart.getOpacity());
211 
212                     createAtom(rTargetColor, rTargetOpacity, aTemp, rStart, nOffset);
213                     fPos = rStart.getOffset();
214                 }
215 
216                 while(fPos < 1.0 && nIndex + 1 < nCount)
217                 {
218                     const SvgGradientEntry& rCandidateA = rEntries[nIndex++];
219                     const SvgGradientEntry& rCandidateB = rEntries[nIndex];
220 
221                     createAtom(rTargetColor, rTargetOpacity, rCandidateA, rCandidateB, nOffset);
222                     fPos = rCandidateB.getOffset();
223                 }
224 
225                 const SvgGradientEntry& rEnd = rEntries[nCount - 1];
226                 const bool bCreateEndPad(fPos < fMax && Spread_pad == getSpreadMethod());
227                 const bool bCreateEndFill(rEnd.getOffset() < 1.0);
228 
229                 if(bCreateEndPad || bCreateEndFill)
230                 {
231                     fPos = bCreateEndPad ? fMax : 1.0;
232                     const SvgGradientEntry aTemp(fPos, rEnd.getColor(), rEnd.getOpacity());
233 
234                     createAtom(rTargetColor, rTargetOpacity, rEnd, aTemp, nOffset);
235                 }
236             }
237             else
238             {
239                 OSL_ENSURE(false, "GradientAtom creation without ColorStops (!)");
240                 fPos = fMax;
241             }
242 
243             return fPos;
244         }
245 
246         Primitive2DSequence SvgGradientHelper::createResult(
247             const Primitive2DVector& rTargetColor,
248             const Primitive2DVector& rTargetOpacity,
249             const basegfx::B2DHomMatrix& rUnitGradientToObject,
250             bool bInvert) const
251         {
252             Primitive2DSequence xRetval;
253             const Primitive2DSequence aTargetColorEntries(Primitive2DVectorToPrimitive2DSequence(rTargetColor, bInvert));
254             const Primitive2DSequence aTargetOpacityEntries(Primitive2DVectorToPrimitive2DSequence(rTargetOpacity, bInvert));
255 
256             if(aTargetColorEntries.hasElements())
257             {
258                 Primitive2DReference xRefContent;
259 
260                 if(aTargetOpacityEntries.hasElements())
261                 {
262                     const Primitive2DReference xRefOpacity = new TransparencePrimitive2D(
263                         aTargetColorEntries,
264                         aTargetOpacityEntries);
265 
266                     xRefContent = new TransformPrimitive2D(
267                         rUnitGradientToObject,
268                         Primitive2DSequence(&xRefOpacity, 1));
269                 }
270                 else
271                 {
272                     xRefContent = new TransformPrimitive2D(
273                         rUnitGradientToObject,
274                         aTargetColorEntries);
275                 }
276 
277                 xRefContent = new MaskPrimitive2D(
278                     getPolyPolygon(),
279                     Primitive2DSequence(&xRefContent, 1));
280 
281                 xRetval = Primitive2DSequence(&xRefContent, 1);
282             }
283 
284             return xRetval;
285         }
286 
287         SvgGradientHelper::SvgGradientHelper(
288             const basegfx::B2DHomMatrix& rGradientTransform,
289             const basegfx::B2DPolyPolygon& rPolyPolygon,
290             const SvgGradientEntryVector& rGradientEntries,
291             const basegfx::B2DPoint& rStart,
292             bool bUseUnitCoordinates,
293             SpreadMethod aSpreadMethod)
294         :   maGradientTransform(rGradientTransform),
295             maPolyPolygon(rPolyPolygon),
296             maGradientEntries(rGradientEntries),
297             maStart(rStart),
298             maSpreadMethod(aSpreadMethod),
299             mbPreconditionsChecked(false),
300             mbCreatesContent(false),
301             mbSingleEntry(false),
302             mbFullyOpaque(true),
303             mbUseUnitCoordinates(bUseUnitCoordinates)
304         {
305         }
306 
307         bool SvgGradientHelper::operator==(const SvgGradientHelper& rSvgGradientHelper) const
308         {
309             const SvgGradientHelper& rCompare = static_cast< const SvgGradientHelper& >(rSvgGradientHelper);
310 
311             return (getGradientTransform() == rCompare.getGradientTransform()
312                 && getPolyPolygon() == rCompare.getPolyPolygon()
313                 && getGradientEntries() == rCompare.getGradientEntries()
314                 && getStart() == rCompare.getStart()
315                 && getUseUnitCoordinates() == rCompare.getUseUnitCoordinates()
316                 && getSpreadMethod() == rCompare.getSpreadMethod());
317         }
318 
319     } // end of namespace primitive2d
320 } // end of namespace drawinglayer
321 
322 //////////////////////////////////////////////////////////////////////////////
323 
324 namespace drawinglayer
325 {
326 	namespace primitive2d
327     {
328         void SvgLinearGradientPrimitive2D::checkPreconditions()
329         {
330             // call parent
331             SvgGradientHelper::checkPreconditions();
332 
333             if(getCreatesContent())
334             {
335                 // Check Vector
336                 const basegfx::B2DVector aVector(getEnd() - getStart());
337 
338                 if(basegfx::fTools::equalZero(aVector.getX()) && basegfx::fTools::equalZero(aVector.getY()))
339                 {
340                     // fill with single color using last stop color
341                     setSingleEntry();
342                 }
343             }
344         }
345 
346         void SvgLinearGradientPrimitive2D::createAtom(
347             Primitive2DVector& rTargetColor,
348             Primitive2DVector& rTargetOpacity,
349             const SvgGradientEntry& rFrom,
350             const SvgGradientEntry& rTo,
351             sal_Int32 nOffset) const
352         {
353             // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset())
354             if(rFrom.getOffset() == rTo.getOffset())
355             {
356                 OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)");
357             }
358             else
359             {
360                 rTargetColor.push_back(
361                     new SvgLinearAtomPrimitive2D(
362                         rFrom.getColor(), rFrom.getOffset() + nOffset,
363                         rTo.getColor(), rTo.getOffset() + nOffset));
364 
365                 if(!getFullyOpaque())
366                 {
367                     const double fTransFrom(1.0 - rFrom.getOpacity());
368                     const double fTransTo(1.0 - rTo.getOpacity());
369                     const basegfx::BColor aColorFrom(fTransFrom, fTransFrom, fTransFrom);
370                     const basegfx::BColor aColorTo(fTransTo, fTransTo, fTransTo);
371 
372                     rTargetOpacity.push_back(
373                         new SvgLinearAtomPrimitive2D(
374                             aColorFrom, rFrom.getOffset() + nOffset,
375                             aColorTo, rTo.getOffset() + nOffset));
376                 }
377             }
378         }
379 
380         Primitive2DSequence SvgLinearGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
381         {
382             Primitive2DSequence xRetval;
383 
384             if(!getPreconditionsChecked())
385             {
386                 const_cast< SvgLinearGradientPrimitive2D* >(this)->checkPreconditions();
387             }
388 
389             if(getSingleEntry())
390             {
391                 // fill with last existing color
392                 xRetval = createSingleGradientEntryFill();
393             }
394             else if(getCreatesContent())
395             {
396                 // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely
397                 // invisible, width and height to fill are not empty
398                 const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange());
399                 const double fPolyWidth(aPolyRange.getWidth());
400                 const double fPolyHeight(aPolyRange.getHeight());
401 
402                 // create ObjectTransform based on polygon range
403                 const basegfx::B2DHomMatrix aObjectTransform(
404                     basegfx::tools::createScaleTranslateB2DHomMatrix(
405                         fPolyWidth, fPolyHeight,
406                         aPolyRange.getMinX(), aPolyRange.getMinY()));
407                 basegfx::B2DHomMatrix aUnitGradientToObject;
408 
409                 if(getUseUnitCoordinates())
410                 {
411                     // interpret in unit coordinate system -> object aspect ratio will scale result
412                     // create unit transform from unit vector [0.0 .. 1.0] along the X-Axis to given
413                     // gradient vector defined by Start,End
414                     const basegfx::B2DVector aVector(getEnd() - getStart());
415                     const double fVectorLength(aVector.getLength());
416 
417                     aUnitGradientToObject.scale(fVectorLength, 1.0);
418                     aUnitGradientToObject.rotate(atan2(aVector.getY(), aVector.getX()));
419                     aUnitGradientToObject.translate(getStart().getX(), getStart().getY());
420 
421                     if(!getGradientTransform().isIdentity())
422                     {
423                         aUnitGradientToObject = getGradientTransform() * aUnitGradientToObject;
424                     }
425 
426                     // create full transform from unit gradient coordinates to object coordinates
427                     // including the SvgGradient transformation
428                     aUnitGradientToObject = aObjectTransform * aUnitGradientToObject;
429                 }
430                 else
431                 {
432                     // interpret in object coordinate system -> object aspect ratio will not scale result
433                     const basegfx::B2DPoint aStart(aObjectTransform * getStart());
434                     const basegfx::B2DPoint aEnd(aObjectTransform * getEnd());
435                     const basegfx::B2DVector aVector(aEnd - aStart);
436 
437                     aUnitGradientToObject.scale(aVector.getLength(), 1.0);
438                     aUnitGradientToObject.rotate(atan2(aVector.getY(), aVector.getX()));
439                     aUnitGradientToObject.translate(aStart.getX(), aStart.getY());
440 
441                     if(!getGradientTransform().isIdentity())
442                     {
443                         aUnitGradientToObject = getGradientTransform() * aUnitGradientToObject;
444                     }
445                 }
446 
447                 // create inverse from it
448                 basegfx::B2DHomMatrix aObjectToUnitGradient(aUnitGradientToObject);
449                 aObjectToUnitGradient.invert();
450 
451                 // back-transform polygon to unit gradient coordinates and get
452                 // UnitRage. This is the range the gradient has to cover
453                 basegfx::B2DPolyPolygon aUnitPoly(getPolyPolygon());
454                 aUnitPoly.transform(aObjectToUnitGradient);
455                 const basegfx::B2DRange aUnitRange(aUnitPoly.getB2DRange());
456 
457                 // prepare result vectors
458                 Primitive2DVector aTargetColor;
459                 Primitive2DVector aTargetOpacity;
460 
461                 if(basegfx::fTools::more(aUnitRange.getWidth(), 0.0))
462                 {
463                     // add a pre-multiply to aUnitGradientToObject to allow
464                     // multiplication of the polygon(xl, 0.0, xr, 1.0)
465                     const basegfx::B2DHomMatrix aPreMultiply(
466                         basegfx::tools::createScaleTranslateB2DHomMatrix(
467                             1.0, aUnitRange.getHeight(), 0.0, aUnitRange.getMinY()));
468                     aUnitGradientToObject = aUnitGradientToObject * aPreMultiply;
469 
470                     // create central run, may also already do all necessary when
471                     // Spread_pad is set as SpreadMethod and/or the range is smaller
472                     double fPos(createRun(aTargetColor, aTargetOpacity, aUnitRange.getMinX(), aUnitRange.getMaxX(), getGradientEntries(), 0));
473 
474                     if(fPos < aUnitRange.getMaxX())
475                     {
476                         // can only happen when SpreadMethod is Spread_reflect or Spread_repeat,
477                         // else the start and end pads are already created and fPos == aUnitRange.getMaxX().
478                         // Its possible to express the repeated linear gradient by adding the
479                         // transformed central run. Crete it this way
480                         Primitive2DSequence aTargetColorEntries(Primitive2DVectorToPrimitive2DSequence(aTargetColor));
481                         Primitive2DSequence aTargetOpacityEntries(Primitive2DVectorToPrimitive2DSequence(aTargetOpacity));
482                         aTargetColor.clear();
483                         aTargetOpacity.clear();
484 
485                         if(aTargetColorEntries.hasElements())
486                         {
487                             // add original central run as group primitive
488                             aTargetColor.push_back(new GroupPrimitive2D(aTargetColorEntries));
489 
490                             if(aTargetOpacityEntries.hasElements())
491                             {
492                                 aTargetOpacity.push_back(new GroupPrimitive2D(aTargetOpacityEntries));
493                             }
494 
495                             // add negative runs
496                             fPos = 0.0;
497                             sal_Int32 nOffset(0);
498 
499                             while(fPos > aUnitRange.getMinX())
500                             {
501                                 fPos -= 1.0;
502                                 nOffset++;
503 
504                                 basegfx::B2DHomMatrix aTransform;
505                                 const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2));
506 
507                                 if(bMirror)
508                                 {
509                                     aTransform.scale(-1.0, 1.0);
510                                     aTransform.translate(fPos + 1.0, 0.0);
511                                 }
512                                 else
513                                 {
514                                     aTransform.translate(fPos, 0.0);
515                                 }
516 
517                                 aTargetColor.push_back(new TransformPrimitive2D(aTransform, aTargetColorEntries));
518 
519                                 if(aTargetOpacityEntries.hasElements())
520                                 {
521                                     aTargetOpacity.push_back(new TransformPrimitive2D(aTransform, aTargetOpacityEntries));
522                                 }
523                             }
524 
525                             // add positive runs
526                             fPos = 1.0;
527                             nOffset = 1;
528 
529                             while(fPos < aUnitRange.getMaxX())
530                             {
531                                 basegfx::B2DHomMatrix aTransform;
532                                 const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2));
533 
534                                 if(bMirror)
535                                 {
536                                     aTransform.scale(-1.0, 1.0);
537                                     aTransform.translate(fPos + 1.0, 0.0);
538                                 }
539                                 else
540                                 {
541                                     aTransform.translate(fPos, 0.0);
542                                 }
543 
544                                 aTargetColor.push_back(new TransformPrimitive2D(aTransform, aTargetColorEntries));
545 
546                                 if(aTargetOpacityEntries.hasElements())
547                                 {
548                                     aTargetOpacity.push_back(new TransformPrimitive2D(aTransform, aTargetOpacityEntries));
549                                 }
550 
551                                 fPos += 1.0;
552                                 nOffset++;
553                             }
554                         }
555                     }
556                 }
557 
558                 xRetval = createResult(aTargetColor, aTargetOpacity, aUnitGradientToObject);
559             }
560 
561             return xRetval;
562         }
563 
564         SvgLinearGradientPrimitive2D::SvgLinearGradientPrimitive2D(
565             const basegfx::B2DHomMatrix& rGradientTransform,
566             const basegfx::B2DPolyPolygon& rPolyPolygon,
567             const SvgGradientEntryVector& rGradientEntries,
568             const basegfx::B2DPoint& rStart,
569             const basegfx::B2DPoint& rEnd,
570             bool bUseUnitCoordinates,
571             SpreadMethod aSpreadMethod)
572         :   BufferedDecompositionPrimitive2D(),
573             SvgGradientHelper(rGradientTransform, rPolyPolygon, rGradientEntries, rStart, bUseUnitCoordinates, aSpreadMethod),
574             maEnd(rEnd)
575         {
576         }
577 
578         SvgLinearGradientPrimitive2D::~SvgLinearGradientPrimitive2D()
579         {
580         }
581 
582         bool SvgLinearGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
583         {
584             const SvgGradientHelper* pSvgGradientHelper = dynamic_cast< const SvgGradientHelper* >(&rPrimitive);
585 
586             if(pSvgGradientHelper && SvgGradientHelper::operator==(*pSvgGradientHelper))
587             {
588                 const SvgLinearGradientPrimitive2D& rCompare = static_cast< const SvgLinearGradientPrimitive2D& >(rPrimitive);
589 
590                 return (getEnd() == rCompare.getEnd());
591             }
592 
593             return false;
594         }
595 
596         basegfx::B2DRange SvgLinearGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
597         {
598             // return ObjectRange
599             return getPolyPolygon().getB2DRange();
600         }
601 
602         // provide unique ID
603         ImplPrimitrive2DIDBlock(SvgLinearGradientPrimitive2D, PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D)
604 
605     } // end of namespace primitive2d
606 } // end of namespace drawinglayer
607 
608 //////////////////////////////////////////////////////////////////////////////
609 
610 namespace drawinglayer
611 {
612     namespace primitive2d
613     {
614         void SvgRadialGradientPrimitive2D::checkPreconditions()
615         {
616             // call parent
617             SvgGradientHelper::checkPreconditions();
618 
619             if(getCreatesContent())
620             {
621                 // Check Radius
622                 if(basegfx::fTools::equalZero(getRadius()))
623                 {
624                     // fill with single color using last stop color
625                     setSingleEntry();
626                 }
627             }
628         }
629 
630         void SvgRadialGradientPrimitive2D::createAtom(
631             Primitive2DVector& rTargetColor,
632             Primitive2DVector& rTargetOpacity,
633             const SvgGradientEntry& rFrom,
634             const SvgGradientEntry& rTo,
635             sal_Int32 nOffset) const
636         {
637             // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset())
638             if(rFrom.getOffset() == rTo.getOffset())
639             {
640                 OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)");
641             }
642             else
643             {
644                 const double fScaleFrom(rFrom.getOffset() + nOffset);
645                 const double fScaleTo(rTo.getOffset() + nOffset);
646 
647                 if(isFocalSet())
648                 {
649                     const basegfx::B2DVector aTranslateFrom(maFocalVector * (maFocalLength - fScaleFrom));
650                     const basegfx::B2DVector aTranslateTo(maFocalVector * (maFocalLength - fScaleTo));
651 
652                     rTargetColor.push_back(
653                         new SvgRadialAtomPrimitive2D(
654                             rFrom.getColor(), fScaleFrom, aTranslateFrom,
655                             rTo.getColor(), fScaleTo, aTranslateTo));
656                 }
657                 else
658                 {
659                     rTargetColor.push_back(
660                         new SvgRadialAtomPrimitive2D(
661                             rFrom.getColor(), fScaleFrom,
662                             rTo.getColor(), fScaleTo));
663                 }
664 
665                 if(!getFullyOpaque())
666                 {
667                     const double fTransFrom(1.0 - rFrom.getOpacity());
668                     const double fTransTo(1.0 - rTo.getOpacity());
669                     const basegfx::BColor aColorFrom(fTransFrom, fTransFrom, fTransFrom);
670                     const basegfx::BColor aColorTo(fTransTo, fTransTo, fTransTo);
671 
672                     if(isFocalSet())
673                     {
674                         const basegfx::B2DVector aTranslateFrom(maFocalVector * (maFocalLength - fScaleFrom));
675                         const basegfx::B2DVector aTranslateTo(maFocalVector * (maFocalLength - fScaleTo));
676 
677                         rTargetOpacity.push_back(
678                             new SvgRadialAtomPrimitive2D(
679                                 aColorFrom, fScaleFrom, aTranslateFrom,
680                                 aColorTo, fScaleTo, aTranslateTo));
681                     }
682                     else
683                     {
684                         rTargetOpacity.push_back(
685                             new SvgRadialAtomPrimitive2D(
686                                 aColorFrom, fScaleFrom,
687                                 aColorTo, fScaleTo));
688                     }
689                 }
690             }
691         }
692 
693         const SvgGradientEntryVector& SvgRadialGradientPrimitive2D::getMirroredGradientEntries() const
694         {
695             if(maMirroredGradientEntries.empty() && !getGradientEntries().empty())
696             {
697                 const_cast< SvgRadialGradientPrimitive2D* >(this)->createMirroredGradientEntries();
698             }
699 
700             return maMirroredGradientEntries;
701         }
702 
703         void SvgRadialGradientPrimitive2D::createMirroredGradientEntries()
704         {
705             if(maMirroredGradientEntries.empty() && !getGradientEntries().empty())
706             {
707                 const sal_uInt32 nCount(getGradientEntries().size());
708                 maMirroredGradientEntries.clear();
709                 maMirroredGradientEntries.reserve(nCount);
710 
711                 for(sal_uInt32 a(0); a < nCount; a++)
712                 {
713                     const SvgGradientEntry& rCandidate = getGradientEntries()[nCount - 1 - a];
714 
715                     maMirroredGradientEntries.push_back(
716                         SvgGradientEntry(
717                             1.0 - rCandidate.getOffset(),
718                             rCandidate.getColor(),
719                             rCandidate.getOpacity()));
720                 }
721             }
722         }
723 
724         Primitive2DSequence SvgRadialGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
725         {
726             Primitive2DSequence xRetval;
727 
728             if(!getPreconditionsChecked())
729             {
730                 const_cast< SvgRadialGradientPrimitive2D* >(this)->checkPreconditions();
731             }
732 
733             if(getSingleEntry())
734             {
735                 // fill with last existing color
736                 xRetval = createSingleGradientEntryFill();
737             }
738             else if(getCreatesContent())
739             {
740                 // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely
741                 // invisible, width and height to fill are not empty
742                 const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange());
743                 const double fPolyWidth(aPolyRange.getWidth());
744                 const double fPolyHeight(aPolyRange.getHeight());
745 
746                 // create ObjectTransform based on polygon range
747                 const basegfx::B2DHomMatrix aObjectTransform(
748                     basegfx::tools::createScaleTranslateB2DHomMatrix(
749                         fPolyWidth, fPolyHeight,
750                         aPolyRange.getMinX(), aPolyRange.getMinY()));
751                 basegfx::B2DHomMatrix aUnitGradientToObject;
752 
753                 if(getUseUnitCoordinates())
754                 {
755                     // interpret in unit coordinate system -> object aspect ratio will scale result
756                     // create unit transform from unit vector to given linear gradient vector
757                     aUnitGradientToObject.scale(getRadius(), getRadius());
758                     aUnitGradientToObject.translate(getStart().getX(), getStart().getY());
759 
760                     if(!getGradientTransform().isIdentity())
761                     {
762                         aUnitGradientToObject = getGradientTransform() * aUnitGradientToObject;
763                     }
764 
765                     // create full transform from unit gradient coordinates to object coordinates
766                     // including the SvgGradient transformation
767                     aUnitGradientToObject = aObjectTransform * aUnitGradientToObject;
768                 }
769                 else
770                 {
771                     // interpret in object coordinate system -> object aspect ratio will not scale result
772                     // use X-Axis with radius, it was already made relative to object width when coming from
773                     // SVG import
774                     const double fRadius((aObjectTransform * basegfx::B2DVector(getRadius(), 0.0)).getLength());
775                     const basegfx::B2DPoint aStart(aObjectTransform * getStart());
776 
777                     aUnitGradientToObject.scale(fRadius, fRadius);
778                     aUnitGradientToObject.translate(aStart.getX(), aStart.getY());
779 
780                     if(!getGradientTransform().isIdentity())
781                     {
782                         aUnitGradientToObject = getGradientTransform() * aUnitGradientToObject;
783                     }
784                 }
785 
786                 // create inverse from it
787                 basegfx::B2DHomMatrix aObjectToUnitGradient(aUnitGradientToObject);
788                 aObjectToUnitGradient.invert();
789 
790                 // back-transform polygon to unit gradient coordinates and get
791                 // UnitRage. This is the range the gradient has to cover
792                 basegfx::B2DPolyPolygon aUnitPoly(getPolyPolygon());
793                 aUnitPoly.transform(aObjectToUnitGradient);
794                 const basegfx::B2DRange aUnitRange(aUnitPoly.getB2DRange());
795 
796                 // create range which the gradient has to cover to cover the whole given geometry.
797                 // For circle, go from 0.0 to max radius in all directions (the corners)
798                 double fMax(basegfx::B2DVector(aUnitRange.getMinimum()).getLength());
799                 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMaximum()).getLength());
800                 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMinX(), aUnitRange.getMaxY()).getLength());
801                 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMaxX(), aUnitRange.getMinY()).getLength());
802 
803                 // prepare result vectors
804                 Primitive2DVector aTargetColor;
805                 Primitive2DVector aTargetOpacity;
806 
807                 if(0.0 < fMax)
808                 {
809                     // prepare maFocalVector
810                     if(isFocalSet())
811                     {
812                         const_cast< SvgRadialGradientPrimitive2D* >(this)->maFocalLength = fMax;
813                     }
814 
815                     // create central run, may also already do all necessary when
816                     // Spread_pad is set as SpreadMethod and/or the range is smaller
817                     double fPos(createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getGradientEntries(), 0));
818 
819                     if(fPos < fMax)
820                     {
821                         // can only happen when SpreadMethod is Spread_reflect or Spread_repeat,
822                         // else the start and end pads are already created and fPos == fMax.
823                         // For radial there is no way to transform the already created
824                         // central run, it needs to be created from 1.0 to fMax
825                         sal_Int32 nOffset(1);
826 
827                         while(fPos < fMax)
828                         {
829                             const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2));
830 
831                             if(bMirror)
832                             {
833                                 createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getMirroredGradientEntries(), nOffset);
834                             }
835                             else
836                             {
837                                 createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getGradientEntries(), nOffset);
838                             }
839 
840                             nOffset++;
841                             fPos += 1.0;
842                         }
843                     }
844                 }
845 
846                 xRetval = createResult(aTargetColor, aTargetOpacity, aUnitGradientToObject, true);
847             }
848 
849             return xRetval;
850         }
851 
852         SvgRadialGradientPrimitive2D::SvgRadialGradientPrimitive2D(
853             const basegfx::B2DHomMatrix& rGradientTransform,
854             const basegfx::B2DPolyPolygon& rPolyPolygon,
855             const SvgGradientEntryVector& rGradientEntries,
856             const basegfx::B2DPoint& rStart,
857             double fRadius,
858             bool bUseUnitCoordinates,
859             SpreadMethod aSpreadMethod,
860             const basegfx::B2DPoint* pFocal)
861         :   BufferedDecompositionPrimitive2D(),
862             SvgGradientHelper(rGradientTransform, rPolyPolygon, rGradientEntries, rStart, bUseUnitCoordinates, aSpreadMethod),
863             mfRadius(fRadius),
864             maFocal(rStart),
865             maFocalVector(0.0, 0.0),
866             maFocalLength(0.0),
867             maMirroredGradientEntries(),
868             mbFocalSet(false)
869         {
870             if(pFocal && !pFocal->equal(getStart()))
871             {
872                 maFocal = *pFocal;
873                 maFocalVector = maFocal - getStart();
874                 mbFocalSet = true;
875             }
876         }
877 
878         SvgRadialGradientPrimitive2D::~SvgRadialGradientPrimitive2D()
879         {
880         }
881 
882         bool SvgRadialGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
883         {
884             const SvgGradientHelper* pSvgGradientHelper = dynamic_cast< const SvgGradientHelper* >(&rPrimitive);
885 
886             if(pSvgGradientHelper && SvgGradientHelper::operator==(*pSvgGradientHelper))
887             {
888                 const SvgRadialGradientPrimitive2D& rCompare = static_cast< const SvgRadialGradientPrimitive2D& >(rPrimitive);
889 
890                 if(getRadius() == rCompare.getRadius())
891                 {
892                     if(isFocalSet() == rCompare.isFocalSet())
893                     {
894                         if(isFocalSet())
895                         {
896                             return getFocal() == rCompare.getFocal();
897                         }
898                         else
899                         {
900                             return true;
901                         }
902                     }
903                 }
904             }
905 
906             return false;
907         }
908 
909         basegfx::B2DRange SvgRadialGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
910         {
911             // return ObjectRange
912             return getPolyPolygon().getB2DRange();
913         }
914 
915         // provide unique ID
916         ImplPrimitrive2DIDBlock(SvgRadialGradientPrimitive2D, PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D)
917 
918     } // end of namespace primitive2d
919 } // end of namespace drawinglayer
920 
921 //////////////////////////////////////////////////////////////////////////////
922 // SvgLinearAtomPrimitive2D class
923 
924 namespace drawinglayer
925 {
926     namespace primitive2d
927     {
928         Primitive2DSequence SvgLinearAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
929         {
930             Primitive2DSequence xRetval;
931             const double fDelta(getOffsetB() - getOffsetA());
932 
933             if(!basegfx::fTools::equalZero(fDelta))
934             {
935                 // use one discrete unit for overlap (one pixel)
936                 const double fDiscreteUnit(getDiscreteUnit());
937 
938                 // use color distance and discrete lengths to calculate step count
939                 const sal_uInt32 nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDelta, fDiscreteUnit));
940 
941                 // prepare polygon in needed width at start position (with discrete overlap)
942                 const basegfx::B2DPolygon aPolygon(
943                     basegfx::tools::createPolygonFromRect(
944                         basegfx::B2DRange(
945                             getOffsetA() - fDiscreteUnit,
946                             0.0,
947                             getOffsetA() + (fDelta / nSteps) + fDiscreteUnit,
948                             1.0)));
949 
950                 // prepare loop (inside to outside, [0.0 .. 1.0[)
951                 double fUnitScale(0.0);
952                 const double fUnitStep(1.0 / nSteps);
953 
954                 // prepare result set (known size)
955                 xRetval.realloc(nSteps);
956 
957                 for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
958                 {
959                     basegfx::B2DPolygon aNew(aPolygon);
960 
961                     aNew.transform(basegfx::tools::createTranslateB2DHomMatrix(fDelta * fUnitScale, 0.0));
962                     xRetval[a] = new PolyPolygonColorPrimitive2D(
963                         basegfx::B2DPolyPolygon(aNew),
964                         basegfx::interpolate(getColorA(), getColorB(), fUnitScale));
965                 }
966             }
967 
968             return xRetval;
969         }
970 
971         SvgLinearAtomPrimitive2D::SvgLinearAtomPrimitive2D(
972             const basegfx::BColor& aColorA, double fOffsetA,
973             const basegfx::BColor& aColorB, double fOffsetB)
974         :   DiscreteMetricDependentPrimitive2D(),
975             maColorA(aColorA),
976             maColorB(aColorB),
977             mfOffsetA(fOffsetA),
978             mfOffsetB(fOffsetB)
979         {
980             if(mfOffsetA > mfOffsetB)
981             {
982                 OSL_ENSURE(false, "Wrong offset order (!)");
983                 ::std::swap(mfOffsetA, mfOffsetB);
984             }
985         }
986 
987         bool SvgLinearAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
988         {
989             if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
990             {
991                 const SvgLinearAtomPrimitive2D& rCompare = static_cast< const SvgLinearAtomPrimitive2D& >(rPrimitive);
992 
993                 return (getColorA() == rCompare.getColorA()
994                     && getColorB() == rCompare.getColorB()
995                     && getOffsetA() == rCompare.getOffsetA()
996                     && getOffsetB() == rCompare.getOffsetB());
997             }
998 
999             return false;
1000         }
1001 
1002         // provide unique ID
1003         ImplPrimitrive2DIDBlock(SvgLinearAtomPrimitive2D, PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D)
1004 
1005     } // end of namespace primitive2d
1006 } // end of namespace drawinglayer
1007 
1008 //////////////////////////////////////////////////////////////////////////////
1009 // SvgRadialAtomPrimitive2D class
1010 
1011 namespace drawinglayer
1012 {
1013     namespace primitive2d
1014     {
1015         Primitive2DSequence SvgRadialAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
1016         {
1017             Primitive2DSequence xRetval;
1018             const double fDeltaScale(getScaleB() - getScaleA());
1019 
1020             if(!basegfx::fTools::equalZero(fDeltaScale))
1021             {
1022                 // use one discrete unit for overlap (one pixel)
1023                 const double fDiscreteUnit(getDiscreteUnit());
1024 
1025                 // use color distance and discrete lengths to calculate step count
1026                 const sal_uInt32 nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDeltaScale, fDiscreteUnit));
1027 
1028                 // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes)
1029                 double fUnitScale(0.0);
1030                 const double fUnitStep(1.0 / nSteps);
1031 
1032                 // prepare result set (known size)
1033                 xRetval.realloc(nSteps);
1034 
1035                 for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
1036                 {
1037                     basegfx::B2DHomMatrix aTransform;
1038                     const double fEndScale(getScaleB() - (fDeltaScale * fUnitScale));
1039 
1040                     if(isTranslateSet())
1041                     {
1042                         const basegfx::B2DVector aTranslate(
1043                             basegfx::interpolate(
1044                                 getTranslateB(),
1045                                 getTranslateA(),
1046                                 fUnitScale));
1047 
1048                         aTransform = basegfx::tools::createScaleTranslateB2DHomMatrix(
1049                             fEndScale,
1050                             fEndScale,
1051                             aTranslate.getX(),
1052                             aTranslate.getY());
1053                     }
1054                     else
1055                     {
1056                         aTransform = basegfx::tools::createScaleB2DHomMatrix(
1057                             fEndScale,
1058                             fEndScale);
1059                     }
1060 
1061                     basegfx::B2DPolygon aNew(basegfx::tools::createPolygonFromUnitCircle());
1062 
1063                     aNew.transform(aTransform);
1064                     xRetval[a] = new PolyPolygonColorPrimitive2D(
1065                         basegfx::B2DPolyPolygon(aNew),
1066                         basegfx::interpolate(getColorB(), getColorA(), fUnitScale));
1067                 }
1068             }
1069 
1070             return xRetval;
1071         }
1072 
1073         SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D(
1074             const basegfx::BColor& aColorA, double fScaleA, const basegfx::B2DVector& rTranslateA,
1075             const basegfx::BColor& aColorB, double fScaleB, const basegfx::B2DVector& rTranslateB)
1076         :   DiscreteMetricDependentPrimitive2D(),
1077             maColorA(aColorA),
1078             maColorB(aColorB),
1079             mfScaleA(fScaleA),
1080             mfScaleB(fScaleB),
1081             mpTranslate(0)
1082         {
1083             // check and evtl. set translations
1084             if(!rTranslateA.equal(rTranslateB))
1085             {
1086                 mpTranslate = new VectorPair(rTranslateA, rTranslateB);
1087             }
1088 
1089             // scale A and B have to be positive
1090             mfScaleA = ::std::max(mfScaleA, 0.0);
1091             mfScaleB = ::std::max(mfScaleB, 0.0);
1092 
1093             // scale B has to be bigger than scale A; swap if different
1094             if(mfScaleA > mfScaleB)
1095             {
1096                 OSL_ENSURE(false, "Wrong offset order (!)");
1097                 ::std::swap(mfScaleA, mfScaleB);
1098 
1099                 if(mpTranslate)
1100                 {
1101                     ::std::swap(mpTranslate->maTranslateA, mpTranslate->maTranslateB);
1102                 }
1103             }
1104         }
1105 
1106         SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D(
1107             const basegfx::BColor& aColorA, double fScaleA,
1108             const basegfx::BColor& aColorB, double fScaleB)
1109         :   DiscreteMetricDependentPrimitive2D(),
1110             maColorA(aColorA),
1111             maColorB(aColorB),
1112             mfScaleA(fScaleA),
1113             mfScaleB(fScaleB),
1114             mpTranslate(0)
1115         {
1116             // scale A and B have to be positive
1117             mfScaleA = ::std::max(mfScaleA, 0.0);
1118             mfScaleB = ::std::max(mfScaleB, 0.0);
1119 
1120             // scale B has to be bigger than scale A; swap if different
1121             if(mfScaleA > mfScaleB)
1122             {
1123                 OSL_ENSURE(false, "Wrong offset order (!)");
1124                 ::std::swap(mfScaleA, mfScaleB);
1125             }
1126         }
1127 
1128         SvgRadialAtomPrimitive2D::~SvgRadialAtomPrimitive2D()
1129         {
1130             if(mpTranslate)
1131             {
1132                 delete mpTranslate;
1133                 mpTranslate = 0;
1134             }
1135         }
1136 
1137         bool SvgRadialAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
1138         {
1139             if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
1140             {
1141                 const SvgRadialAtomPrimitive2D& rCompare = static_cast< const SvgRadialAtomPrimitive2D& >(rPrimitive);
1142 
1143                 if(getColorA() == rCompare.getColorA()
1144                     && getColorB() == rCompare.getColorB()
1145                     && getScaleA() == rCompare.getScaleA()
1146                     && getScaleB() == rCompare.getScaleB())
1147                 {
1148                     if(isTranslateSet() && rCompare.isTranslateSet())
1149                     {
1150                         return (getTranslateA() == rCompare.getTranslateA()
1151                             && getTranslateB() == rCompare.getTranslateB());
1152                     }
1153                     else if(!isTranslateSet() && !rCompare.isTranslateSet())
1154                     {
1155                         return true;
1156                     }
1157                 }
1158             }
1159 
1160             return false;
1161         }
1162 
1163         // provide unique ID
1164         ImplPrimitrive2DIDBlock(SvgRadialAtomPrimitive2D, PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D)
1165 
1166     } // end of namespace primitive2d
1167 } // end of namespace drawinglayer
1168 
1169 //////////////////////////////////////////////////////////////////////////////
1170 // eof
1171