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