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                     basegfx::B2DHomMatrix aUnitGradientToGradient;
417 
418                     aUnitGradientToGradient.scale(fVectorLength, 1.0);
419                     aUnitGradientToGradient.rotate(atan2(aVector.getY(), aVector.getX()));
420                     aUnitGradientToGradient.translate(getStart().getX(), getStart().getY());
421 
422                     // create full transform from unit gradient coordinates to object coordinates
423                     // including the SvgGradient transformation
424                     aUnitGradientToObject = aObjectTransform * aUnitGradientToGradient;
425                 }
426                 else
427                 {
428                     // interpret in object coordinate system -> object aspect ratio will not scale result
429                     const basegfx::B2DPoint aStart(aObjectTransform * getStart());
430                     const basegfx::B2DPoint aEnd(aObjectTransform * getEnd());
431                     const basegfx::B2DVector aVector(aEnd - aStart);
432 
433                     aUnitGradientToObject.scale(aVector.getLength(), 1.0);
434                     aUnitGradientToObject.rotate(atan2(aVector.getY(), aVector.getX()));
435                     aUnitGradientToObject.translate(aStart.getX(), aStart.getY());
436                 }
437 
438                 if(!getGradientTransform().isIdentity())
439                 {
440                     aUnitGradientToObject = getGradientTransform() * aUnitGradientToObject;
441                 }
442 
443                 // create inverse from it
444                 basegfx::B2DHomMatrix aObjectToUnitGradient(aUnitGradientToObject);
445                 aObjectToUnitGradient.invert();
446 
447                 // back-transform polygon to unit gradient coordinates and get
448                 // UnitRage. This is the range the gradient has to cover
449                 basegfx::B2DPolyPolygon aUnitPoly(getPolyPolygon());
450                 aUnitPoly.transform(aObjectToUnitGradient);
451                 const basegfx::B2DRange aUnitRange(aUnitPoly.getB2DRange());
452 
453                 // prepare result vectors
454                 Primitive2DVector aTargetColor;
455                 Primitive2DVector aTargetOpacity;
456 
457                 if(basegfx::fTools::more(aUnitRange.getWidth(), 0.0))
458                 {
459                     // add a pre-multiply to aUnitGradientToObject to allow
460                     // multiplication of the polygon(xl, 0.0, xr, 1.0)
461                     const basegfx::B2DHomMatrix aPreMultiply(
462                         basegfx::tools::createScaleTranslateB2DHomMatrix(
463                             1.0, aUnitRange.getHeight(), 0.0, aUnitRange.getMinY()));
464                     aUnitGradientToObject = aUnitGradientToObject * aPreMultiply;
465 
466                     // create central run, may also already do all necessary when
467                     // Spread_pad is set as SpreadMethod and/or the range is smaller
468                     double fPos(createRun(aTargetColor, aTargetOpacity, aUnitRange.getMinX(), aUnitRange.getMaxX(), getGradientEntries(), 0));
469 
470                     if(fPos < aUnitRange.getMaxX())
471                     {
472                         // can only happen when SpreadMethod is Spread_reflect or Spread_repeat,
473                         // else the start and end pads are already created and fPos == aUnitRange.getMaxX().
474                         // Its possible to express the repeated linear gradient by adding the
475                         // transformed central run. Crete it this way
476                         Primitive2DSequence aTargetColorEntries(Primitive2DVectorToPrimitive2DSequence(aTargetColor));
477                         Primitive2DSequence aTargetOpacityEntries(Primitive2DVectorToPrimitive2DSequence(aTargetOpacity));
478                         aTargetColor.clear();
479                         aTargetOpacity.clear();
480 
481                         if(aTargetColorEntries.hasElements())
482                         {
483                             // add original central run as group primitive
484                             aTargetColor.push_back(new GroupPrimitive2D(aTargetColorEntries));
485 
486                             if(aTargetOpacityEntries.hasElements())
487                             {
488                                 aTargetOpacity.push_back(new GroupPrimitive2D(aTargetOpacityEntries));
489                             }
490 
491                             // add negative runs
492                             fPos = 0.0;
493                             sal_Int32 nOffset(0);
494 
495                             while(fPos > aUnitRange.getMinX())
496                             {
497                                 fPos -= 1.0;
498                                 nOffset++;
499 
500                                 basegfx::B2DHomMatrix aTransform;
501                                 const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2));
502 
503                                 if(bMirror)
504                                 {
505                                     aTransform.scale(-1.0, 1.0);
506                                     aTransform.translate(fPos + 1.0, 0.0);
507                                 }
508                                 else
509                                 {
510                                     aTransform.translate(fPos, 0.0);
511                                 }
512 
513                                 aTargetColor.push_back(new TransformPrimitive2D(aTransform, aTargetColorEntries));
514 
515                                 if(aTargetOpacityEntries.hasElements())
516                                 {
517                                     aTargetOpacity.push_back(new TransformPrimitive2D(aTransform, aTargetOpacityEntries));
518                                 }
519                             }
520 
521                             // add positive runs
522                             fPos = 1.0;
523                             nOffset = 1;
524 
525                             while(fPos < aUnitRange.getMaxX())
526                             {
527                                 basegfx::B2DHomMatrix aTransform;
528                                 const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2));
529 
530                                 if(bMirror)
531                                 {
532                                     aTransform.scale(-1.0, 1.0);
533                                     aTransform.translate(fPos + 1.0, 0.0);
534                                 }
535                                 else
536                                 {
537                                     aTransform.translate(fPos, 0.0);
538                                 }
539 
540                                 aTargetColor.push_back(new TransformPrimitive2D(aTransform, aTargetColorEntries));
541 
542                                 if(aTargetOpacityEntries.hasElements())
543                                 {
544                                     aTargetOpacity.push_back(new TransformPrimitive2D(aTransform, aTargetOpacityEntries));
545                                 }
546 
547                                 fPos += 1.0;
548                                 nOffset++;
549                             }
550                         }
551                     }
552                 }
553 
554                 xRetval = createResult(aTargetColor, aTargetOpacity, aUnitGradientToObject);
555             }
556 
557             return xRetval;
558         }
559 
560         SvgLinearGradientPrimitive2D::SvgLinearGradientPrimitive2D(
561             const basegfx::B2DHomMatrix& rGradientTransform,
562             const basegfx::B2DPolyPolygon& rPolyPolygon,
563             const SvgGradientEntryVector& rGradientEntries,
564             const basegfx::B2DPoint& rStart,
565             const basegfx::B2DPoint& rEnd,
566             bool bUseUnitCoordinates,
567             SpreadMethod aSpreadMethod)
568         :   BufferedDecompositionPrimitive2D(),
569             SvgGradientHelper(rGradientTransform, rPolyPolygon, rGradientEntries, rStart, bUseUnitCoordinates, aSpreadMethod),
570             maEnd(rEnd)
571         {
572         }
573 
574         SvgLinearGradientPrimitive2D::~SvgLinearGradientPrimitive2D()
575         {
576         }
577 
578         bool SvgLinearGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
579         {
580             const SvgGradientHelper* pSvgGradientHelper = dynamic_cast< const SvgGradientHelper* >(&rPrimitive);
581 
582             if(pSvgGradientHelper && SvgGradientHelper::operator==(*pSvgGradientHelper))
583             {
584                 const SvgLinearGradientPrimitive2D& rCompare = static_cast< const SvgLinearGradientPrimitive2D& >(rPrimitive);
585 
586                 return (getEnd() == rCompare.getEnd());
587             }
588 
589             return false;
590         }
591 
592         basegfx::B2DRange SvgLinearGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
593         {
594             // return ObjectRange
595             return getPolyPolygon().getB2DRange();
596         }
597 
598         // provide unique ID
599         ImplPrimitrive2DIDBlock(SvgLinearGradientPrimitive2D, PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D)
600 
601     } // end of namespace primitive2d
602 } // end of namespace drawinglayer
603 
604 //////////////////////////////////////////////////////////////////////////////
605 
606 namespace drawinglayer
607 {
608     namespace primitive2d
609     {
610         void SvgRadialGradientPrimitive2D::checkPreconditions()
611         {
612             // call parent
613             SvgGradientHelper::checkPreconditions();
614 
615             if(getCreatesContent())
616             {
617                 // Check Radius
618                 if(basegfx::fTools::equalZero(getRadius()))
619                 {
620                     // fill with single color using last stop color
621                     setSingleEntry();
622                 }
623             }
624         }
625 
626         void SvgRadialGradientPrimitive2D::createAtom(
627             Primitive2DVector& rTargetColor,
628             Primitive2DVector& rTargetOpacity,
629             const SvgGradientEntry& rFrom,
630             const SvgGradientEntry& rTo,
631             sal_Int32 nOffset) const
632         {
633             // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset())
634             if(rFrom.getOffset() == rTo.getOffset())
635             {
636                 OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)");
637             }
638             else
639             {
640                 const double fScaleFrom(rFrom.getOffset() + nOffset);
641                 const double fScaleTo(rTo.getOffset() + nOffset);
642 
643                 if(isFocalSet())
644                 {
645                     const basegfx::B2DVector aTranslateFrom(maFocalVector * (maFocalLength - fScaleFrom));
646                     const basegfx::B2DVector aTranslateTo(maFocalVector * (maFocalLength - fScaleTo));
647 
648                     rTargetColor.push_back(
649                         new SvgRadialAtomPrimitive2D(
650                             rFrom.getColor(), fScaleFrom, aTranslateFrom,
651                             rTo.getColor(), fScaleTo, aTranslateTo));
652                 }
653                 else
654                 {
655                     rTargetColor.push_back(
656                         new SvgRadialAtomPrimitive2D(
657                             rFrom.getColor(), fScaleFrom,
658                             rTo.getColor(), fScaleTo));
659                 }
660 
661                 if(!getFullyOpaque())
662                 {
663                     const double fTransFrom(1.0 - rFrom.getOpacity());
664                     const double fTransTo(1.0 - rTo.getOpacity());
665                     const basegfx::BColor aColorFrom(fTransFrom, fTransFrom, fTransFrom);
666                     const basegfx::BColor aColorTo(fTransTo, fTransTo, fTransTo);
667 
668                     if(isFocalSet())
669                     {
670                         const basegfx::B2DVector aTranslateFrom(maFocalVector * (maFocalLength - fScaleFrom));
671                         const basegfx::B2DVector aTranslateTo(maFocalVector * (maFocalLength - fScaleTo));
672 
673                         rTargetOpacity.push_back(
674                             new SvgRadialAtomPrimitive2D(
675                                 aColorFrom, fScaleFrom, aTranslateFrom,
676                                 aColorTo, fScaleTo, aTranslateTo));
677                     }
678                     else
679                     {
680                         rTargetOpacity.push_back(
681                             new SvgRadialAtomPrimitive2D(
682                                 aColorFrom, fScaleFrom,
683                                 aColorTo, fScaleTo));
684                     }
685                 }
686             }
687         }
688 
689         const SvgGradientEntryVector& SvgRadialGradientPrimitive2D::getMirroredGradientEntries() const
690         {
691             if(maMirroredGradientEntries.empty() && !getGradientEntries().empty())
692             {
693                 const_cast< SvgRadialGradientPrimitive2D* >(this)->createMirroredGradientEntries();
694             }
695 
696             return maMirroredGradientEntries;
697         }
698 
699         void SvgRadialGradientPrimitive2D::createMirroredGradientEntries()
700         {
701             if(maMirroredGradientEntries.empty() && !getGradientEntries().empty())
702             {
703                 const sal_uInt32 nCount(getGradientEntries().size());
704                 maMirroredGradientEntries.clear();
705                 maMirroredGradientEntries.reserve(nCount);
706 
707                 for(sal_uInt32 a(0); a < nCount; a++)
708                 {
709                     const SvgGradientEntry& rCandidate = getGradientEntries()[nCount - 1 - a];
710 
711                     maMirroredGradientEntries.push_back(
712                         SvgGradientEntry(
713                             1.0 - rCandidate.getOffset(),
714                             rCandidate.getColor(),
715                             rCandidate.getOpacity()));
716                 }
717             }
718         }
719 
720         Primitive2DSequence SvgRadialGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
721         {
722             Primitive2DSequence xRetval;
723 
724             if(!getPreconditionsChecked())
725             {
726                 const_cast< SvgRadialGradientPrimitive2D* >(this)->checkPreconditions();
727             }
728 
729             if(getSingleEntry())
730             {
731                 // fill with last existing color
732                 xRetval = createSingleGradientEntryFill();
733             }
734             else if(getCreatesContent())
735             {
736                 // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely
737                 // invisible, width and height to fill are not empty
738                 const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange());
739                 const double fPolyWidth(aPolyRange.getWidth());
740                 const double fPolyHeight(aPolyRange.getHeight());
741 
742                 // create ObjectTransform based on polygon range
743                 const basegfx::B2DHomMatrix aObjectTransform(
744                     basegfx::tools::createScaleTranslateB2DHomMatrix(
745                         fPolyWidth, fPolyHeight,
746                         aPolyRange.getMinX(), aPolyRange.getMinY()));
747                 basegfx::B2DHomMatrix aUnitGradientToObject;
748 
749                 if(getUseUnitCoordinates())
750                 {
751                     // interpret in unit coordinate system -> object aspect ratio will scale result
752                     // create unit transform from unit vector to given linear gradient vector
753                     basegfx::B2DHomMatrix aUnitGradientToGradient;
754 
755                     aUnitGradientToGradient.scale(getRadius(), getRadius());
756                     aUnitGradientToGradient.translate(getStart().getX(), getStart().getY());
757 
758                     // create full transform from unit gradient coordinates to object coordinates
759                     // including the SvgGradient transformation
760                     aUnitGradientToObject = aObjectTransform * aUnitGradientToGradient;
761                 }
762                 else
763                 {
764                     // interpret in object coordinate system -> object aspect ratio will not scale result
765                     // use X-Axis with radius, it was already made relative to object width when coming from
766                     // SVG import
767                     const double fRadius((aObjectTransform * basegfx::B2DVector(getRadius(), 0.0)).getLength());
768                     const basegfx::B2DPoint aStart(aObjectTransform * getStart());
769 
770                     aUnitGradientToObject.scale(fRadius, fRadius);
771                     aUnitGradientToObject.translate(aStart.getX(), aStart.getY());
772                 }
773 
774                 if(!getGradientTransform().isIdentity())
775                 {
776                     aUnitGradientToObject = getGradientTransform() * aUnitGradientToObject;
777                 }
778 
779                 // create inverse from it
780                 basegfx::B2DHomMatrix aObjectToUnitGradient(aUnitGradientToObject);
781                 aObjectToUnitGradient.invert();
782 
783                 // back-transform polygon to unit gradient coordinates and get
784                 // UnitRage. This is the range the gradient has to cover
785                 basegfx::B2DPolyPolygon aUnitPoly(getPolyPolygon());
786                 aUnitPoly.transform(aObjectToUnitGradient);
787                 const basegfx::B2DRange aUnitRange(aUnitPoly.getB2DRange());
788 
789                 // create range which the gradient has to cover to cover the whole given geometry.
790                 // For circle, go from 0.0 to max radius in all directions (the corners)
791                 double fMax(basegfx::B2DVector(aUnitRange.getMinimum()).getLength());
792                 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMaximum()).getLength());
793                 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMinX(), aUnitRange.getMaxY()).getLength());
794                 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMaxX(), aUnitRange.getMinY()).getLength());
795 
796                 // prepare result vectors
797                 Primitive2DVector aTargetColor;
798                 Primitive2DVector aTargetOpacity;
799 
800                 if(0.0 < fMax)
801                 {
802                     // prepare maFocalVector
803                     if(isFocalSet())
804                     {
805                         const_cast< SvgRadialGradientPrimitive2D* >(this)->maFocalLength = fMax;
806                     }
807 
808                     // create central run, may also already do all necessary when
809                     // Spread_pad is set as SpreadMethod and/or the range is smaller
810                     double fPos(createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getGradientEntries(), 0));
811 
812                     if(fPos < fMax)
813                     {
814                         // can only happen when SpreadMethod is Spread_reflect or Spread_repeat,
815                         // else the start and end pads are already created and fPos == fMax.
816                         // For radial there is no way to transform the already created
817                         // central run, it needs to be created from 1.0 to fMax
818                         sal_Int32 nOffset(1);
819 
820                         while(fPos < fMax)
821                         {
822                             const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2));
823 
824                             if(bMirror)
825                             {
826                                 createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getMirroredGradientEntries(), nOffset);
827                             }
828                             else
829                             {
830                                 createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getGradientEntries(), nOffset);
831                             }
832 
833                             nOffset++;
834                             fPos += 1.0;
835                         }
836                     }
837                 }
838 
839                 xRetval = createResult(aTargetColor, aTargetOpacity, aUnitGradientToObject, true);
840             }
841 
842             return xRetval;
843         }
844 
845         SvgRadialGradientPrimitive2D::SvgRadialGradientPrimitive2D(
846             const basegfx::B2DHomMatrix& rGradientTransform,
847             const basegfx::B2DPolyPolygon& rPolyPolygon,
848             const SvgGradientEntryVector& rGradientEntries,
849             const basegfx::B2DPoint& rStart,
850             double fRadius,
851             bool bUseUnitCoordinates,
852             SpreadMethod aSpreadMethod,
853             const basegfx::B2DPoint* pFocal)
854         :   BufferedDecompositionPrimitive2D(),
855             SvgGradientHelper(rGradientTransform, rPolyPolygon, rGradientEntries, rStart, bUseUnitCoordinates, aSpreadMethod),
856             mfRadius(fRadius),
857             maFocal(rStart),
858             maFocalVector(0.0, 0.0),
859             maFocalLength(0.0),
860             maMirroredGradientEntries(),
861             mbFocalSet(false)
862         {
863             if(pFocal && !pFocal->equal(getStart()))
864             {
865                 maFocal = *pFocal;
866                 maFocalVector = maFocal - getStart();
867                 mbFocalSet = true;
868             }
869         }
870 
871         SvgRadialGradientPrimitive2D::~SvgRadialGradientPrimitive2D()
872         {
873         }
874 
875         bool SvgRadialGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
876         {
877             const SvgGradientHelper* pSvgGradientHelper = dynamic_cast< const SvgGradientHelper* >(&rPrimitive);
878 
879             if(pSvgGradientHelper && SvgGradientHelper::operator==(*pSvgGradientHelper))
880             {
881                 const SvgRadialGradientPrimitive2D& rCompare = static_cast< const SvgRadialGradientPrimitive2D& >(rPrimitive);
882 
883                 if(getRadius() == rCompare.getRadius())
884                 {
885                     if(isFocalSet() == rCompare.isFocalSet())
886                     {
887                         if(isFocalSet())
888                         {
889                             return getFocal() == rCompare.getFocal();
890                         }
891                         else
892                         {
893                             return true;
894                         }
895                     }
896                 }
897             }
898 
899             return false;
900         }
901 
902         basegfx::B2DRange SvgRadialGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
903         {
904             // return ObjectRange
905             return getPolyPolygon().getB2DRange();
906         }
907 
908         // provide unique ID
909         ImplPrimitrive2DIDBlock(SvgRadialGradientPrimitive2D, PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D)
910 
911     } // end of namespace primitive2d
912 } // end of namespace drawinglayer
913 
914 //////////////////////////////////////////////////////////////////////////////
915 // SvgLinearAtomPrimitive2D class
916 
917 namespace drawinglayer
918 {
919     namespace primitive2d
920     {
921         Primitive2DSequence SvgLinearAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
922         {
923             Primitive2DSequence xRetval;
924             const double fDelta(getOffsetB() - getOffsetA());
925 
926             if(!basegfx::fTools::equalZero(fDelta))
927             {
928                 // use one discrete unit for overlap (one pixel)
929                 const double fDiscreteUnit(getDiscreteUnit());
930 
931                 // use color distance and discrete lengths to calculate step count
932                 const sal_uInt32 nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDelta, fDiscreteUnit));
933 
934                 // prepare polygon in needed width at start position (with discrete overlap)
935                 const basegfx::B2DPolygon aPolygon(
936                     basegfx::tools::createPolygonFromRect(
937                         basegfx::B2DRange(
938                             getOffsetA() - fDiscreteUnit,
939                             0.0,
940                             getOffsetA() + (fDelta / nSteps) + fDiscreteUnit,
941                             1.0)));
942 
943                 // prepare loop (inside to outside, [0.0 .. 1.0[)
944                 double fUnitScale(0.0);
945                 const double fUnitStep(1.0 / nSteps);
946 
947                 // prepare result set (known size)
948                 xRetval.realloc(nSteps);
949 
950                 for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
951                 {
952                     basegfx::B2DPolygon aNew(aPolygon);
953 
954                     aNew.transform(basegfx::tools::createTranslateB2DHomMatrix(fDelta * fUnitScale, 0.0));
955                     xRetval[a] = new PolyPolygonColorPrimitive2D(
956                         basegfx::B2DPolyPolygon(aNew),
957                         basegfx::interpolate(getColorA(), getColorB(), fUnitScale));
958                 }
959             }
960 
961             return xRetval;
962         }
963 
964         SvgLinearAtomPrimitive2D::SvgLinearAtomPrimitive2D(
965             const basegfx::BColor& aColorA, double fOffsetA,
966             const basegfx::BColor& aColorB, double fOffsetB)
967         :   DiscreteMetricDependentPrimitive2D(),
968             maColorA(aColorA),
969             maColorB(aColorB),
970             mfOffsetA(fOffsetA),
971             mfOffsetB(fOffsetB)
972         {
973             if(mfOffsetA > mfOffsetB)
974             {
975                 OSL_ENSURE(false, "Wrong offset order (!)");
976                 ::std::swap(mfOffsetA, mfOffsetB);
977             }
978         }
979 
980         bool SvgLinearAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
981         {
982             if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
983             {
984                 const SvgLinearAtomPrimitive2D& rCompare = static_cast< const SvgLinearAtomPrimitive2D& >(rPrimitive);
985 
986                 return (getColorA() == rCompare.getColorA()
987                     && getColorB() == rCompare.getColorB()
988                     && getOffsetA() == rCompare.getOffsetA()
989                     && getOffsetB() == rCompare.getOffsetB());
990             }
991 
992             return false;
993         }
994 
995         // provide unique ID
996         ImplPrimitrive2DIDBlock(SvgLinearAtomPrimitive2D, PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D)
997 
998     } // end of namespace primitive2d
999 } // end of namespace drawinglayer
1000 
1001 //////////////////////////////////////////////////////////////////////////////
1002 // SvgRadialAtomPrimitive2D class
1003 
1004 namespace drawinglayer
1005 {
1006     namespace primitive2d
1007     {
1008         Primitive2DSequence SvgRadialAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
1009         {
1010             Primitive2DSequence xRetval;
1011             const double fDeltaScale(getScaleB() - getScaleA());
1012 
1013             if(!basegfx::fTools::equalZero(fDeltaScale))
1014             {
1015                 // use one discrete unit for overlap (one pixel)
1016                 const double fDiscreteUnit(getDiscreteUnit());
1017 
1018                 // use color distance and discrete lengths to calculate step count
1019                 const sal_uInt32 nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDeltaScale, fDiscreteUnit));
1020 
1021                 // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes)
1022                 double fUnitScale(0.0);
1023                 const double fUnitStep(1.0 / nSteps);
1024 
1025                 // prepare result set (known size)
1026                 xRetval.realloc(nSteps);
1027 
1028                 for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
1029                 {
1030                     basegfx::B2DHomMatrix aTransform;
1031                     const double fEndScale(getScaleB() - (fDeltaScale * fUnitScale));
1032 
1033                     if(isTranslateSet())
1034                     {
1035                         const basegfx::B2DVector aTranslate(
1036                             basegfx::interpolate(
1037                                 getTranslateB(),
1038                                 getTranslateA(),
1039                                 fUnitScale));
1040 
1041                         aTransform = basegfx::tools::createScaleTranslateB2DHomMatrix(
1042                             fEndScale,
1043                             fEndScale,
1044                             aTranslate.getX(),
1045                             aTranslate.getY());
1046                     }
1047                     else
1048                     {
1049                         aTransform = basegfx::tools::createScaleB2DHomMatrix(
1050                             fEndScale,
1051                             fEndScale);
1052                     }
1053 
1054                     basegfx::B2DPolygon aNew(basegfx::tools::createPolygonFromUnitCircle());
1055 
1056                     aNew.transform(aTransform);
1057                     xRetval[a] = new PolyPolygonColorPrimitive2D(
1058                         basegfx::B2DPolyPolygon(aNew),
1059                         basegfx::interpolate(getColorB(), getColorA(), fUnitScale));
1060                 }
1061             }
1062 
1063             return xRetval;
1064         }
1065 
1066         SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D(
1067             const basegfx::BColor& aColorA, double fScaleA, const basegfx::B2DVector& rTranslateA,
1068             const basegfx::BColor& aColorB, double fScaleB, const basegfx::B2DVector& rTranslateB)
1069         :   DiscreteMetricDependentPrimitive2D(),
1070             maColorA(aColorA),
1071             maColorB(aColorB),
1072             mfScaleA(fScaleA),
1073             mfScaleB(fScaleB),
1074             mpTranslate(0)
1075         {
1076             // check and evtl. set translations
1077             if(!rTranslateA.equal(rTranslateB))
1078             {
1079                 mpTranslate = new VectorPair(rTranslateA, rTranslateB);
1080             }
1081 
1082             // scale A and B have to be positive
1083             mfScaleA = ::std::max(mfScaleA, 0.0);
1084             mfScaleB = ::std::max(mfScaleB, 0.0);
1085 
1086             // scale B has to be bigger than scale A; swap if different
1087             if(mfScaleA > mfScaleB)
1088             {
1089                 OSL_ENSURE(false, "Wrong offset order (!)");
1090                 ::std::swap(mfScaleA, mfScaleB);
1091 
1092                 if(mpTranslate)
1093                 {
1094                     ::std::swap(mpTranslate->maTranslateA, mpTranslate->maTranslateB);
1095                 }
1096             }
1097         }
1098 
1099         SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D(
1100             const basegfx::BColor& aColorA, double fScaleA,
1101             const basegfx::BColor& aColorB, double fScaleB)
1102         :   DiscreteMetricDependentPrimitive2D(),
1103             maColorA(aColorA),
1104             maColorB(aColorB),
1105             mfScaleA(fScaleA),
1106             mfScaleB(fScaleB),
1107             mpTranslate(0)
1108         {
1109             // scale A and B have to be positive
1110             mfScaleA = ::std::max(mfScaleA, 0.0);
1111             mfScaleB = ::std::max(mfScaleB, 0.0);
1112 
1113             // scale B has to be bigger than scale A; swap if different
1114             if(mfScaleA > mfScaleB)
1115             {
1116                 OSL_ENSURE(false, "Wrong offset order (!)");
1117                 ::std::swap(mfScaleA, mfScaleB);
1118             }
1119         }
1120 
1121         SvgRadialAtomPrimitive2D::~SvgRadialAtomPrimitive2D()
1122         {
1123             if(mpTranslate)
1124             {
1125                 delete mpTranslate;
1126                 mpTranslate = 0;
1127             }
1128         }
1129 
1130         bool SvgRadialAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
1131         {
1132             if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
1133             {
1134                 const SvgRadialAtomPrimitive2D& rCompare = static_cast< const SvgRadialAtomPrimitive2D& >(rPrimitive);
1135 
1136                 if(getColorA() == rCompare.getColorA()
1137                     && getColorB() == rCompare.getColorB()
1138                     && getScaleA() == rCompare.getScaleA()
1139                     && getScaleB() == rCompare.getScaleB())
1140                 {
1141                     if(isTranslateSet() && rCompare.isTranslateSet())
1142                     {
1143                         return (getTranslateA() == rCompare.getTranslateA()
1144                             && getTranslateB() == rCompare.getTranslateB());
1145                     }
1146                     else if(!isTranslateSet() && !rCompare.isTranslateSet())
1147                     {
1148                         return true;
1149                     }
1150                 }
1151             }
1152 
1153             return false;
1154         }
1155 
1156         // provide unique ID
1157         ImplPrimitrive2DIDBlock(SvgRadialAtomPrimitive2D, PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D)
1158 
1159     } // end of namespace primitive2d
1160 } // end of namespace drawinglayer
1161 
1162 //////////////////////////////////////////////////////////////////////////////
1163 // eof
1164