fdo#74697 Add Bluez 5 support for impress remote.
[LibreOffice.git] / drawinglayer / source / primitive2d / svggradientprimitive2d.cxx
blob03280707ffdecf163e7f99ec6909d7dc95de79c9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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>
33 //////////////////////////////////////////////////////////////////////////////
35 using namespace com::sun::star;
37 //////////////////////////////////////////////////////////////////////////////
39 namespace
41 sal_uInt32 calculateStepsForSvgGradient(const basegfx::BColor& rColorA, const basegfx::BColor& rColorB, double fDelta, double fDiscreteUnit)
43 // use color distance, assume to do every color step (full quality)
44 sal_uInt32 nSteps(basegfx::fround(rColorA.getDistance(rColorB) * 255.0));
46 if(nSteps)
48 // calc discrete length to change color all 1.5 disctete units (pixels)
49 const sal_uInt32 nDistSteps(basegfx::fround(fDelta / (fDiscreteUnit * 1.5)));
51 nSteps = std::min(nSteps, nDistSteps);
54 // roughly cut when too big or too small
55 nSteps = std::min(nSteps, sal_uInt32(255));
56 nSteps = std::max(nSteps, sal_uInt32(1));
58 return nSteps;
60 } // end of anonymous namespace
62 //////////////////////////////////////////////////////////////////////////////
64 namespace drawinglayer
66 namespace primitive2d
68 Primitive2DSequence SvgGradientHelper::createSingleGradientEntryFill() const
70 const SvgGradientEntryVector& rEntries = getGradientEntries();
71 const sal_uInt32 nCount(rEntries.size());
72 Primitive2DSequence xRetval;
74 if(nCount)
76 const SvgGradientEntry& rSingleEntry = rEntries[nCount - 1];
77 const double fOpacity(rSingleEntry.getOpacity());
79 if(fOpacity > 0.0)
81 Primitive2DReference xRef(
82 new PolyPolygonColorPrimitive2D(
83 getPolyPolygon(),
84 rSingleEntry.getColor()));
86 if(fOpacity < 1.0)
88 const Primitive2DSequence aContent(&xRef, 1);
90 xRef = Primitive2DReference(
91 new UnifiedTransparencePrimitive2D(
92 aContent,
93 1.0 - fOpacity));
96 xRetval = Primitive2DSequence(&xRef, 1);
99 else
101 OSL_ENSURE(false, "Single gradient entry construction without entry (!)");
104 return xRetval;
107 void SvgGradientHelper::checkPreconditions()
109 mbPreconditionsChecked = true;
110 const SvgGradientEntryVector& rEntries = getGradientEntries();
112 if(rEntries.empty())
114 // no fill at all
116 else
118 const sal_uInt32 nCount(rEntries.size());
120 if(1 == nCount)
122 // fill with single existing color
123 setSingleEntry();
125 else
127 // sort maGradientEntries when more than one
128 std::sort(maGradientEntries.begin(), maGradientEntries.end());
130 // gradient with at least two colors
131 bool bAllInvisible(true);
133 for(sal_uInt32 a(0); a < nCount; a++)
135 const SvgGradientEntry& rCandidate = rEntries[a];
137 if(basegfx::fTools::equalZero(rCandidate.getOpacity()))
139 // invisible
140 mbFullyOpaque = false;
142 else if(basegfx::fTools::equal(rCandidate.getOpacity(), 1.0))
144 // completely opaque
145 bAllInvisible = false;
147 else
149 // opacity
150 bAllInvisible = false;
151 mbFullyOpaque = false;
155 if(bAllInvisible)
157 // all invisible, nothing to do
159 else
161 const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange());
163 if(aPolyRange.isEmpty())
165 // no range to fill, nothing to do
167 else
169 const double fPolyWidth(aPolyRange.getWidth());
170 const double fPolyHeight(aPolyRange.getHeight());
172 if(basegfx::fTools::equalZero(fPolyWidth) || basegfx::fTools::equalZero(fPolyHeight))
174 // no width/height to fill, nothing to do
176 else
178 mbCreatesContent = true;
186 double SvgGradientHelper::createRun(
187 Primitive2DVector& rTargetColor,
188 Primitive2DVector& rTargetOpacity,
189 double fPos,
190 double fMax,
191 const SvgGradientEntryVector& rEntries,
192 sal_Int32 nOffset) const
194 const sal_uInt32 nCount(rEntries.size());
196 if(nCount)
198 const SvgGradientEntry& rStart = rEntries[0];
199 const bool bCreateStartPad(fPos < 0.0 && Spread_pad == getSpreadMethod());
200 const bool bCreateStartFill(rStart.getOffset() > 0.0);
201 sal_uInt32 nIndex(0);
203 if(bCreateStartPad || bCreateStartFill)
205 const SvgGradientEntry aTemp(bCreateStartPad ? fPos : 0.0, rStart.getColor(), rStart.getOpacity());
207 createAtom(rTargetColor, rTargetOpacity, aTemp, rStart, nOffset);
208 fPos = rStart.getOffset();
211 while(fPos < 1.0 && nIndex + 1 < nCount)
213 const SvgGradientEntry& rCandidateA = rEntries[nIndex++];
214 const SvgGradientEntry& rCandidateB = rEntries[nIndex];
216 createAtom(rTargetColor, rTargetOpacity, rCandidateA, rCandidateB, nOffset);
217 fPos = rCandidateB.getOffset();
220 const SvgGradientEntry& rEnd = rEntries[nCount - 1];
221 const bool bCreateEndPad(fPos < fMax && Spread_pad == getSpreadMethod());
222 const bool bCreateEndFill(rEnd.getOffset() < 1.0);
224 if(bCreateEndPad || bCreateEndFill)
226 fPos = bCreateEndPad ? fMax : 1.0;
227 const SvgGradientEntry aTemp(fPos, rEnd.getColor(), rEnd.getOpacity());
229 createAtom(rTargetColor, rTargetOpacity, rEnd, aTemp, nOffset);
232 else
234 OSL_ENSURE(false, "GradientAtom creation without ColorStops (!)");
235 fPos = fMax;
238 return fPos;
241 Primitive2DSequence SvgGradientHelper::createResult(
242 const Primitive2DVector& rTargetColor,
243 const Primitive2DVector& rTargetOpacity,
244 const basegfx::B2DHomMatrix& rUnitGradientToObject,
245 bool bInvert) const
247 Primitive2DSequence xRetval;
248 const Primitive2DSequence aTargetColorEntries(Primitive2DVectorToPrimitive2DSequence(rTargetColor, bInvert));
249 const Primitive2DSequence aTargetOpacityEntries(Primitive2DVectorToPrimitive2DSequence(rTargetOpacity, bInvert));
251 if(aTargetColorEntries.hasElements())
253 Primitive2DReference xRefContent;
255 if(aTargetOpacityEntries.hasElements())
257 const Primitive2DReference xRefOpacity = new TransparencePrimitive2D(
258 aTargetColorEntries,
259 aTargetOpacityEntries);
261 xRefContent = new TransformPrimitive2D(
262 rUnitGradientToObject,
263 Primitive2DSequence(&xRefOpacity, 1));
265 else
267 xRefContent = new TransformPrimitive2D(
268 rUnitGradientToObject,
269 aTargetColorEntries);
272 xRefContent = new MaskPrimitive2D(
273 getPolyPolygon(),
274 Primitive2DSequence(&xRefContent, 1));
276 xRetval = Primitive2DSequence(&xRefContent, 1);
279 return xRetval;
282 SvgGradientHelper::SvgGradientHelper(
283 const basegfx::B2DPolyPolygon& rPolyPolygon,
284 const SvgGradientEntryVector& rGradientEntries,
285 const basegfx::B2DPoint& rStart,
286 bool bUseUnitCoordinates,
287 SpreadMethod aSpreadMethod)
288 : maPolyPolygon(rPolyPolygon),
289 maGradientEntries(rGradientEntries),
290 maStart(rStart),
291 maSpreadMethod(aSpreadMethod),
292 mbPreconditionsChecked(false),
293 mbCreatesContent(false),
294 mbSingleEntry(false),
295 mbFullyOpaque(true),
296 mbUseUnitCoordinates(bUseUnitCoordinates)
300 bool SvgGradientHelper::equalTo(const SvgGradientHelper& rSvgGradientHelper) const
302 const SvgGradientHelper& rCompare = static_cast< const SvgGradientHelper& >(rSvgGradientHelper);
304 return (getPolyPolygon() == rCompare.getPolyPolygon()
305 && getGradientEntries() == rCompare.getGradientEntries()
306 && getStart() == rCompare.getStart()
307 && getUseUnitCoordinates() == rCompare.getUseUnitCoordinates()
308 && getSpreadMethod() == rCompare.getSpreadMethod());
311 } // end of namespace primitive2d
312 } // end of namespace drawinglayer
314 //////////////////////////////////////////////////////////////////////////////
316 namespace drawinglayer
318 namespace primitive2d
320 void SvgLinearGradientPrimitive2D::checkPreconditions()
322 // call parent
323 SvgGradientHelper::checkPreconditions();
325 if(getCreatesContent())
327 // Check Vector
328 const basegfx::B2DVector aVector(getEnd() - getStart());
330 if(basegfx::fTools::equalZero(aVector.getX()) && basegfx::fTools::equalZero(aVector.getY()))
332 // fill with single color using last stop color
333 setSingleEntry();
338 void SvgLinearGradientPrimitive2D::createAtom(
339 Primitive2DVector& rTargetColor,
340 Primitive2DVector& rTargetOpacity,
341 const SvgGradientEntry& rFrom,
342 const SvgGradientEntry& rTo,
343 sal_Int32 nOffset) const
345 // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset())
346 if(rFrom.getOffset() == rTo.getOffset())
348 OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)");
350 else
352 rTargetColor.push_back(
353 new SvgLinearAtomPrimitive2D(
354 rFrom.getColor(), rFrom.getOffset() + nOffset,
355 rTo.getColor(), rTo.getOffset() + nOffset));
357 if(!getFullyOpaque())
359 const double fTransFrom(1.0 - rFrom.getOpacity());
360 const double fTransTo(1.0 - rTo.getOpacity());
361 const basegfx::BColor aColorFrom(fTransFrom, fTransFrom, fTransFrom);
362 const basegfx::BColor aColorTo(fTransTo, fTransTo, fTransTo);
364 rTargetOpacity.push_back(
365 new SvgLinearAtomPrimitive2D(
366 aColorFrom, rFrom.getOffset() + nOffset,
367 aColorTo, rTo.getOffset() + nOffset));
372 Primitive2DSequence SvgLinearGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
374 Primitive2DSequence xRetval;
376 if(!getPreconditionsChecked())
378 const_cast< SvgLinearGradientPrimitive2D* >(this)->checkPreconditions();
381 if(getSingleEntry())
383 // fill with last existing color
384 xRetval = createSingleGradientEntryFill();
386 else if(getCreatesContent())
388 // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely
389 // invisible, width and height to fill are not empty
390 const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange());
391 const double fPolyWidth(aPolyRange.getWidth());
392 const double fPolyHeight(aPolyRange.getHeight());
394 // create ObjectTransform based on polygon range
395 const basegfx::B2DHomMatrix aObjectTransform(
396 basegfx::tools::createScaleTranslateB2DHomMatrix(
397 fPolyWidth, fPolyHeight,
398 aPolyRange.getMinX(), aPolyRange.getMinY()));
399 basegfx::B2DHomMatrix aUnitGradientToObject;
401 if(getUseUnitCoordinates())
403 // interpret in unit coordinate system -> object aspect ratio will scale result
404 // create unit transform from unit vector [0.0 .. 1.0] along the X-Axis to given
405 // gradient vector defined by Start,End
406 const basegfx::B2DVector aVector(getEnd() - getStart());
407 const double fVectorLength(aVector.getLength());
408 basegfx::B2DHomMatrix aUnitGradientToGradient;
410 aUnitGradientToGradient.scale(fVectorLength, 1.0);
411 aUnitGradientToGradient.rotate(atan2(aVector.getY(), aVector.getX()));
412 aUnitGradientToGradient.translate(getStart().getX(), getStart().getY());
414 // create full transform from unit gradient coordinates to object coordinates
415 // including the SvgGradient transformation
416 aUnitGradientToObject = aObjectTransform * aUnitGradientToGradient;
418 else
420 // interpret in object coordinate system -> object aspect ratio will not scale result
421 const basegfx::B2DPoint aStart(aObjectTransform * getStart());
422 const basegfx::B2DPoint aEnd(aObjectTransform * getEnd());
423 const basegfx::B2DVector aVector(aEnd - aStart);
425 aUnitGradientToObject.scale(aVector.getLength(), 1.0);
426 aUnitGradientToObject.rotate(atan2(aVector.getY(), aVector.getX()));
427 aUnitGradientToObject.translate(aStart.getX(), aStart.getY());
430 // create inverse from it
431 basegfx::B2DHomMatrix aObjectToUnitGradient(aUnitGradientToObject);
432 aObjectToUnitGradient.invert();
434 // back-transform polygon to unit gradient coordinates and get
435 // UnitRage. This is the range the gradient has to cover
436 basegfx::B2DPolyPolygon aUnitPoly(getPolyPolygon());
437 aUnitPoly.transform(aObjectToUnitGradient);
438 const basegfx::B2DRange aUnitRange(aUnitPoly.getB2DRange());
440 // prepare result vectors
441 Primitive2DVector aTargetColor;
442 Primitive2DVector aTargetOpacity;
444 if(basegfx::fTools::more(aUnitRange.getWidth(), 0.0))
446 // add a pre-multiply to aUnitGradientToObject to allow
447 // multiplication of the polygon(xl, 0.0, xr, 1.0)
448 const basegfx::B2DHomMatrix aPreMultiply(
449 basegfx::tools::createScaleTranslateB2DHomMatrix(
450 1.0, aUnitRange.getHeight(), 0.0, aUnitRange.getMinY()));
451 aUnitGradientToObject = aUnitGradientToObject * aPreMultiply;
453 // create central run, may also already do all necessary when
454 // Spread_pad is set as SpreadMethod and/or the range is smaller
455 double fPos(createRun(aTargetColor, aTargetOpacity, aUnitRange.getMinX(), aUnitRange.getMaxX(), getGradientEntries(), 0));
457 if(fPos < aUnitRange.getMaxX())
459 // can only happen when SpreadMethod is Spread_reflect or Spread_repeat,
460 // else the start and end pads are already created and fPos == aUnitRange.getMaxX().
461 // Its possible to express the repeated linear gradient by adding the
462 // transformed central run. Crete it this way
463 Primitive2DSequence aTargetColorEntries(Primitive2DVectorToPrimitive2DSequence(aTargetColor));
464 Primitive2DSequence aTargetOpacityEntries(Primitive2DVectorToPrimitive2DSequence(aTargetOpacity));
465 aTargetColor.clear();
466 aTargetOpacity.clear();
468 if(aTargetColorEntries.hasElements())
470 // add original central run as group primitive
471 aTargetColor.push_back(new GroupPrimitive2D(aTargetColorEntries));
473 if(aTargetOpacityEntries.hasElements())
475 aTargetOpacity.push_back(new GroupPrimitive2D(aTargetOpacityEntries));
478 // add negative runs
479 fPos = 0.0;
480 sal_Int32 nOffset(0);
482 while(fPos > aUnitRange.getMinX())
484 fPos -= 1.0;
485 nOffset++;
487 basegfx::B2DHomMatrix aTransform;
488 const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2));
490 if(bMirror)
492 aTransform.scale(-1.0, 1.0);
493 aTransform.translate(fPos + 1.0, 0.0);
495 else
497 aTransform.translate(fPos, 0.0);
500 aTargetColor.push_back(new TransformPrimitive2D(aTransform, aTargetColorEntries));
502 if(aTargetOpacityEntries.hasElements())
504 aTargetOpacity.push_back(new TransformPrimitive2D(aTransform, aTargetOpacityEntries));
508 // add positive runs
509 fPos = 1.0;
510 nOffset = 1;
512 while(fPos < aUnitRange.getMaxX())
514 basegfx::B2DHomMatrix aTransform;
515 const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2));
517 if(bMirror)
519 aTransform.scale(-1.0, 1.0);
520 aTransform.translate(fPos + 1.0, 0.0);
522 else
524 aTransform.translate(fPos, 0.0);
527 aTargetColor.push_back(new TransformPrimitive2D(aTransform, aTargetColorEntries));
529 if(aTargetOpacityEntries.hasElements())
531 aTargetOpacity.push_back(new TransformPrimitive2D(aTransform, aTargetOpacityEntries));
534 fPos += 1.0;
535 nOffset++;
541 xRetval = createResult(aTargetColor, aTargetOpacity, aUnitGradientToObject);
544 return xRetval;
547 SvgLinearGradientPrimitive2D::SvgLinearGradientPrimitive2D(
548 const basegfx::B2DPolyPolygon& rPolyPolygon,
549 const SvgGradientEntryVector& rGradientEntries,
550 const basegfx::B2DPoint& rStart,
551 const basegfx::B2DPoint& rEnd,
552 bool bUseUnitCoordinates,
553 SpreadMethod aSpreadMethod)
554 : BufferedDecompositionPrimitive2D(),
555 SvgGradientHelper(rPolyPolygon, rGradientEntries, rStart, bUseUnitCoordinates, aSpreadMethod),
556 maEnd(rEnd)
560 SvgLinearGradientPrimitive2D::~SvgLinearGradientPrimitive2D()
564 bool SvgLinearGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
566 const SvgGradientHelper* pSvgGradientHelper = dynamic_cast< const SvgGradientHelper* >(&rPrimitive);
568 if(pSvgGradientHelper && SvgGradientHelper::equalTo(*pSvgGradientHelper))
570 const SvgLinearGradientPrimitive2D& rCompare = static_cast< const SvgLinearGradientPrimitive2D& >(rPrimitive);
572 return (getEnd() == rCompare.getEnd());
575 return false;
578 basegfx::B2DRange SvgLinearGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
580 // return ObjectRange
581 return getPolyPolygon().getB2DRange();
584 // provide unique ID
585 ImplPrimitive2DIDBlock(SvgLinearGradientPrimitive2D, PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D)
587 } // end of namespace primitive2d
588 } // end of namespace drawinglayer
590 //////////////////////////////////////////////////////////////////////////////
592 namespace drawinglayer
594 namespace primitive2d
596 void SvgRadialGradientPrimitive2D::checkPreconditions()
598 // call parent
599 SvgGradientHelper::checkPreconditions();
601 if(getCreatesContent())
603 // Check Radius
604 if(basegfx::fTools::equalZero(getRadius()))
606 // fill with single color using last stop color
607 setSingleEntry();
612 void SvgRadialGradientPrimitive2D::createAtom(
613 Primitive2DVector& rTargetColor,
614 Primitive2DVector& rTargetOpacity,
615 const SvgGradientEntry& rFrom,
616 const SvgGradientEntry& rTo,
617 sal_Int32 nOffset) const
619 // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset())
620 if(rFrom.getOffset() == rTo.getOffset())
622 OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)");
624 else
626 const double fScaleFrom(rFrom.getOffset() + nOffset);
627 const double fScaleTo(rTo.getOffset() + nOffset);
629 if(isFocalSet())
631 const basegfx::B2DVector aTranslateFrom(maFocalVector * (maFocalLength - fScaleFrom));
632 const basegfx::B2DVector aTranslateTo(maFocalVector * (maFocalLength - fScaleTo));
634 rTargetColor.push_back(
635 new SvgRadialAtomPrimitive2D(
636 rFrom.getColor(), fScaleFrom, aTranslateFrom,
637 rTo.getColor(), fScaleTo, aTranslateTo));
639 else
641 rTargetColor.push_back(
642 new SvgRadialAtomPrimitive2D(
643 rFrom.getColor(), fScaleFrom,
644 rTo.getColor(), fScaleTo));
647 if(!getFullyOpaque())
649 const double fTransFrom(1.0 - rFrom.getOpacity());
650 const double fTransTo(1.0 - rTo.getOpacity());
651 const basegfx::BColor aColorFrom(fTransFrom, fTransFrom, fTransFrom);
652 const basegfx::BColor aColorTo(fTransTo, fTransTo, fTransTo);
654 if(isFocalSet())
656 const basegfx::B2DVector aTranslateFrom(maFocalVector * (maFocalLength - fScaleFrom));
657 const basegfx::B2DVector aTranslateTo(maFocalVector * (maFocalLength - fScaleTo));
659 rTargetOpacity.push_back(
660 new SvgRadialAtomPrimitive2D(
661 aColorFrom, fScaleFrom, aTranslateFrom,
662 aColorTo, fScaleTo, aTranslateTo));
664 else
666 rTargetOpacity.push_back(
667 new SvgRadialAtomPrimitive2D(
668 aColorFrom, fScaleFrom,
669 aColorTo, fScaleTo));
675 const SvgGradientEntryVector& SvgRadialGradientPrimitive2D::getMirroredGradientEntries() const
677 if(maMirroredGradientEntries.empty() && !getGradientEntries().empty())
679 const_cast< SvgRadialGradientPrimitive2D* >(this)->createMirroredGradientEntries();
682 return maMirroredGradientEntries;
685 void SvgRadialGradientPrimitive2D::createMirroredGradientEntries()
687 if(maMirroredGradientEntries.empty() && !getGradientEntries().empty())
689 const sal_uInt32 nCount(getGradientEntries().size());
690 maMirroredGradientEntries.clear();
691 maMirroredGradientEntries.reserve(nCount);
693 for(sal_uInt32 a(0); a < nCount; a++)
695 const SvgGradientEntry& rCandidate = getGradientEntries()[nCount - 1 - a];
697 maMirroredGradientEntries.push_back(
698 SvgGradientEntry(
699 1.0 - rCandidate.getOffset(),
700 rCandidate.getColor(),
701 rCandidate.getOpacity()));
706 Primitive2DSequence SvgRadialGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
708 Primitive2DSequence xRetval;
710 if(!getPreconditionsChecked())
712 const_cast< SvgRadialGradientPrimitive2D* >(this)->checkPreconditions();
715 if(getSingleEntry())
717 // fill with last existing color
718 xRetval = createSingleGradientEntryFill();
720 else if(getCreatesContent())
722 // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely
723 // invisible, width and height to fill are not empty
724 const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange());
725 const double fPolyWidth(aPolyRange.getWidth());
726 const double fPolyHeight(aPolyRange.getHeight());
728 // create ObjectTransform based on polygon range
729 const basegfx::B2DHomMatrix aObjectTransform(
730 basegfx::tools::createScaleTranslateB2DHomMatrix(
731 fPolyWidth, fPolyHeight,
732 aPolyRange.getMinX(), aPolyRange.getMinY()));
733 basegfx::B2DHomMatrix aUnitGradientToObject;
735 if(getUseUnitCoordinates())
737 // interpret in unit coordinate system -> object aspect ratio will scale result
738 // create unit transform from unit vector to given linear gradient vector
739 basegfx::B2DHomMatrix aUnitGradientToGradient;
741 aUnitGradientToGradient.scale(getRadius(), getRadius());
742 aUnitGradientToGradient.translate(getStart().getX(), getStart().getY());
744 // create full transform from unit gradient coordinates to object coordinates
745 // including the SvgGradient transformation
746 aUnitGradientToObject = aObjectTransform * aUnitGradientToGradient;
748 else
750 // interpret in object coordinate system -> object aspect ratio will not scale result
751 const double fRadius((aObjectTransform * basegfx::B2DVector(getRadius(), 0.0)).getLength());
752 const basegfx::B2DPoint aStart(aObjectTransform * getStart());
754 aUnitGradientToObject.scale(fRadius, fRadius);
755 aUnitGradientToObject.translate(aStart.getX(), aStart.getY());
758 // create inverse from it
759 basegfx::B2DHomMatrix aObjectToUnitGradient(aUnitGradientToObject);
760 aObjectToUnitGradient.invert();
762 // back-transform polygon to unit gradient coordinates and get
763 // UnitRage. This is the range the gradient has to cover
764 basegfx::B2DPolyPolygon aUnitPoly(getPolyPolygon());
765 aUnitPoly.transform(aObjectToUnitGradient);
766 const basegfx::B2DRange aUnitRange(aUnitPoly.getB2DRange());
768 // create range which the gradient has to cover to cover the whole given geometry.
769 // For circle, go from 0.0 to max radius in all directions (the corners)
770 double fMax(basegfx::B2DVector(aUnitRange.getMinimum()).getLength());
771 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMaximum()).getLength());
772 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMinX(), aUnitRange.getMaxY()).getLength());
773 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMaxX(), aUnitRange.getMinY()).getLength());
775 // prepare result vectors
776 Primitive2DVector aTargetColor;
777 Primitive2DVector aTargetOpacity;
779 if(0.0 < fMax)
781 // prepare maFocalVector
782 if(isFocalSet())
784 const_cast< SvgRadialGradientPrimitive2D* >(this)->maFocalLength = fMax;
787 // create central run, may also already do all necessary when
788 // Spread_pad is set as SpreadMethod and/or the range is smaller
789 double fPos(createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getGradientEntries(), 0));
791 if(fPos < fMax)
793 // can only happen when SpreadMethod is Spread_reflect or Spread_repeat,
794 // else the start and end pads are already created and fPos == fMax.
795 // For radial there is no way to transform the already created
796 // central run, it needs to be created from 1.0 to fMax
797 sal_Int32 nOffset(1);
799 while(fPos < fMax)
801 const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2));
803 if(bMirror)
805 createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getMirroredGradientEntries(), nOffset);
807 else
809 createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getGradientEntries(), nOffset);
812 nOffset++;
813 fPos += 1.0;
818 xRetval = createResult(aTargetColor, aTargetOpacity, aUnitGradientToObject, true);
821 return xRetval;
824 SvgRadialGradientPrimitive2D::SvgRadialGradientPrimitive2D(
825 const basegfx::B2DPolyPolygon& rPolyPolygon,
826 const SvgGradientEntryVector& rGradientEntries,
827 const basegfx::B2DPoint& rStart,
828 double fRadius,
829 bool bUseUnitCoordinates,
830 SpreadMethod aSpreadMethod,
831 const basegfx::B2DPoint* pFocal)
832 : BufferedDecompositionPrimitive2D(),
833 SvgGradientHelper(rPolyPolygon, rGradientEntries, rStart, bUseUnitCoordinates, aSpreadMethod),
834 mfRadius(fRadius),
835 maFocal(rStart),
836 maFocalVector(0.0, 0.0),
837 maFocalLength(0.0),
838 maMirroredGradientEntries(),
839 mbFocalSet(false)
841 if(pFocal && !pFocal->equal(getStart()))
843 maFocal = *pFocal;
844 maFocalVector = maFocal - getStart();
845 mbFocalSet = true;
849 SvgRadialGradientPrimitive2D::~SvgRadialGradientPrimitive2D()
853 bool SvgRadialGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
855 const SvgGradientHelper* pSvgGradientHelper = dynamic_cast< const SvgGradientHelper* >(&rPrimitive);
857 if(pSvgGradientHelper && SvgGradientHelper::equalTo(*pSvgGradientHelper))
859 const SvgRadialGradientPrimitive2D& rCompare = static_cast< const SvgRadialGradientPrimitive2D& >(rPrimitive);
861 if(getRadius() == rCompare.getRadius())
863 if(isFocalSet() == rCompare.isFocalSet())
865 if(isFocalSet())
867 return getFocal() == rCompare.getFocal();
869 else
871 return true;
877 return false;
880 basegfx::B2DRange SvgRadialGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
882 // return ObjectRange
883 return getPolyPolygon().getB2DRange();
886 // provide unique ID
887 ImplPrimitive2DIDBlock(SvgRadialGradientPrimitive2D, PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D)
889 } // end of namespace primitive2d
890 } // end of namespace drawinglayer
892 //////////////////////////////////////////////////////////////////////////////
893 // SvgLinearAtomPrimitive2D class
895 namespace drawinglayer
897 namespace primitive2d
899 Primitive2DSequence SvgLinearAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
901 Primitive2DSequence xRetval;
902 const double fDelta(getOffsetB() - getOffsetA());
904 if(!basegfx::fTools::equalZero(fDelta))
906 // use one discrete unit for overlap (one pixel)
907 const double fDiscreteUnit(getDiscreteUnit());
909 // use color distance and discrete lengths to calculate step count
910 const sal_uInt32 nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDelta, fDiscreteUnit));
912 // prepare polygon in needed width at start position (with discrete overlap)
913 const basegfx::B2DPolygon aPolygon(
914 basegfx::tools::createPolygonFromRect(
915 basegfx::B2DRange(
916 getOffsetA() - fDiscreteUnit,
917 0.0,
918 getOffsetA() + (fDelta / nSteps) + fDiscreteUnit,
919 1.0)));
921 // prepare loop (inside to outside, [0.0 .. 1.0[)
922 double fUnitScale(0.0);
923 const double fUnitStep(1.0 / nSteps);
925 // prepare result set (known size)
926 xRetval.realloc(nSteps);
928 for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
930 basegfx::B2DPolygon aNew(aPolygon);
932 aNew.transform(basegfx::tools::createTranslateB2DHomMatrix(fDelta * fUnitScale, 0.0));
933 xRetval[a] = new PolyPolygonColorPrimitive2D(
934 basegfx::B2DPolyPolygon(aNew),
935 basegfx::interpolate(getColorA(), getColorB(), fUnitScale));
939 return xRetval;
942 SvgLinearAtomPrimitive2D::SvgLinearAtomPrimitive2D(
943 const basegfx::BColor& aColorA, double fOffsetA,
944 const basegfx::BColor& aColorB, double fOffsetB)
945 : DiscreteMetricDependentPrimitive2D(),
946 maColorA(aColorA),
947 maColorB(aColorB),
948 mfOffsetA(fOffsetA),
949 mfOffsetB(fOffsetB)
951 if(mfOffsetA > mfOffsetB)
953 OSL_ENSURE(false, "Wrong offset order (!)");
954 ::std::swap(mfOffsetA, mfOffsetB);
958 bool SvgLinearAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
960 if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
962 const SvgLinearAtomPrimitive2D& rCompare = static_cast< const SvgLinearAtomPrimitive2D& >(rPrimitive);
964 return (getColorA() == rCompare.getColorA()
965 && getColorB() == rCompare.getColorB()
966 && getOffsetA() == rCompare.getOffsetA()
967 && getOffsetB() == rCompare.getOffsetB());
970 return false;
973 // provide unique ID
974 ImplPrimitive2DIDBlock(SvgLinearAtomPrimitive2D, PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D)
976 } // end of namespace primitive2d
977 } // end of namespace drawinglayer
979 //////////////////////////////////////////////////////////////////////////////
980 // SvgRadialAtomPrimitive2D class
982 namespace drawinglayer
984 namespace primitive2d
986 Primitive2DSequence SvgRadialAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
988 Primitive2DSequence xRetval;
989 const double fDeltaScale(getScaleB() - getScaleA());
991 if(!basegfx::fTools::equalZero(fDeltaScale))
993 // use one discrete unit for overlap (one pixel)
994 const double fDiscreteUnit(getDiscreteUnit());
996 // use color distance and discrete lengths to calculate step count
997 const sal_uInt32 nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDeltaScale, fDiscreteUnit));
999 // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes)
1000 double fUnitScale(0.0);
1001 const double fUnitStep(1.0 / nSteps);
1003 // prepare result set (known size)
1004 xRetval.realloc(nSteps);
1006 for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
1008 basegfx::B2DHomMatrix aTransform;
1009 const double fEndScale(getScaleB() - (fDeltaScale * fUnitScale));
1011 if(isTranslateSet())
1013 const basegfx::B2DVector aTranslate(
1014 basegfx::interpolate(
1015 getTranslateB(),
1016 getTranslateA(),
1017 fUnitScale));
1019 aTransform = basegfx::tools::createScaleTranslateB2DHomMatrix(
1020 fEndScale,
1021 fEndScale,
1022 aTranslate.getX(),
1023 aTranslate.getY());
1025 else
1027 aTransform = basegfx::tools::createScaleB2DHomMatrix(
1028 fEndScale,
1029 fEndScale);
1032 basegfx::B2DPolygon aNew(basegfx::tools::createPolygonFromUnitCircle());
1034 aNew.transform(aTransform);
1035 xRetval[a] = new PolyPolygonColorPrimitive2D(
1036 basegfx::B2DPolyPolygon(aNew),
1037 basegfx::interpolate(getColorB(), getColorA(), fUnitScale));
1041 return xRetval;
1044 SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D(
1045 const basegfx::BColor& aColorA, double fScaleA, const basegfx::B2DVector& rTranslateA,
1046 const basegfx::BColor& aColorB, double fScaleB, const basegfx::B2DVector& rTranslateB)
1047 : DiscreteMetricDependentPrimitive2D(),
1048 maColorA(aColorA),
1049 maColorB(aColorB),
1050 mfScaleA(fScaleA),
1051 mfScaleB(fScaleB),
1052 mpTranslate(0)
1054 // check and evtl. set translations
1055 if(!rTranslateA.equal(rTranslateB))
1057 mpTranslate = new VectorPair(rTranslateA, rTranslateB);
1060 // scale A and B have to be positive
1061 mfScaleA = ::std::max(mfScaleA, 0.0);
1062 mfScaleB = ::std::max(mfScaleB, 0.0);
1064 // scale B has to be bigger than scale A; swap if different
1065 if(mfScaleA > mfScaleB)
1067 OSL_ENSURE(false, "Wrong offset order (!)");
1068 ::std::swap(mfScaleA, mfScaleB);
1070 if(mpTranslate)
1072 ::std::swap(mpTranslate->maTranslateA, mpTranslate->maTranslateB);
1077 SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D(
1078 const basegfx::BColor& aColorA, double fScaleA,
1079 const basegfx::BColor& aColorB, double fScaleB)
1080 : DiscreteMetricDependentPrimitive2D(),
1081 maColorA(aColorA),
1082 maColorB(aColorB),
1083 mfScaleA(fScaleA),
1084 mfScaleB(fScaleB),
1085 mpTranslate(0)
1087 // scale A and B have to be positive
1088 mfScaleA = ::std::max(mfScaleA, 0.0);
1089 mfScaleB = ::std::max(mfScaleB, 0.0);
1091 // scale B has to be bigger than scale A; swap if different
1092 if(mfScaleA > mfScaleB)
1094 OSL_ENSURE(false, "Wrong offset order (!)");
1095 ::std::swap(mfScaleA, mfScaleB);
1099 SvgRadialAtomPrimitive2D::~SvgRadialAtomPrimitive2D()
1101 if(mpTranslate)
1103 delete mpTranslate;
1104 mpTranslate = 0;
1108 bool SvgRadialAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
1110 if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
1112 const SvgRadialAtomPrimitive2D& rCompare = static_cast< const SvgRadialAtomPrimitive2D& >(rPrimitive);
1114 if(getColorA() == rCompare.getColorA()
1115 && getColorB() == rCompare.getColorB()
1116 && getScaleA() == rCompare.getScaleA()
1117 && getScaleB() == rCompare.getScaleB())
1119 if(isTranslateSet() && rCompare.isTranslateSet())
1121 return (getTranslateA() == rCompare.getTranslateA()
1122 && getTranslateB() == rCompare.getTranslateB());
1124 else if(!isTranslateSet() && !rCompare.isTranslateSet())
1126 return true;
1131 return false;
1134 // provide unique ID
1135 ImplPrimitive2DIDBlock(SvgRadialAtomPrimitive2D, PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D)
1137 } // end of namespace primitive2d
1138 } // end of namespace drawinglayer
1140 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */