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