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