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