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