LibreOffice Module basegfx (master) 1
gradienttools.cxx
Go to the documentation of this file.
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
24#include <com/sun/star/awt/Gradient2.hpp>
25#include <osl/endian.h>
26
27#include <algorithm>
28#include <cmath>
29
30namespace basegfx
31{
32 bool ODFGradientInfo::operator==(const ODFGradientInfo& rODFGradientInfo) const
33 {
34 return getTextureTransform() == rODFGradientInfo.getTextureTransform()
35 && getAspectRatio() == rODFGradientInfo.getAspectRatio()
36 && getRequestedSteps() == rODFGradientInfo.getRequestedSteps();
37 }
38
39 const B2DHomMatrix& ODFGradientInfo::getBackTextureTransform() const
40 {
41 if(maBackTextureTransform.isIdentity())
42 {
43 const_cast< ODFGradientInfo* >(this)->maBackTextureTransform = getTextureTransform();
44 const_cast< ODFGradientInfo* >(this)->maBackTextureTransform.invert();
45 }
46
47 return maBackTextureTransform;
48 }
49
53 static ODFGradientInfo init1DGradientInfo(
54 const B2DRange& rTargetRange,
55 sal_uInt32 nSteps,
56 double fBorder,
57 double fAngle,
58 bool bAxial)
59 {
60 B2DHomMatrix aTextureTransform;
61
62 fAngle = -fAngle;
63
64 double fTargetSizeX(rTargetRange.getWidth());
65 double fTargetSizeY(rTargetRange.getHeight());
66 double fTargetOffsetX(rTargetRange.getMinX());
67 double fTargetOffsetY(rTargetRange.getMinY());
68
69 // add object expansion
70 const bool bAngleUsed(!fTools::equalZero(fAngle));
71
72 if(bAngleUsed)
73 {
74 const double fAbsCos(fabs(cos(fAngle)));
75 const double fAbsSin(fabs(sin(fAngle)));
76 const double fNewX(fTargetSizeX * fAbsCos + fTargetSizeY * fAbsSin);
77 const double fNewY(fTargetSizeY * fAbsCos + fTargetSizeX * fAbsSin);
78
79 fTargetOffsetX -= (fNewX - fTargetSizeX) / 2.0;
80 fTargetOffsetY -= (fNewY - fTargetSizeY) / 2.0;
81 fTargetSizeX = fNewX;
82 fTargetSizeY = fNewY;
83 }
84
85 const double fSizeWithoutBorder(1.0 - fBorder);
86
87 if(bAxial)
88 {
89 aTextureTransform.scale(1.0, fSizeWithoutBorder * 0.5);
90 aTextureTransform.translate(0.0, 0.5);
91 }
92 else
93 {
94 if(!fTools::equal(fSizeWithoutBorder, 1.0))
95 {
96 aTextureTransform.scale(1.0, fSizeWithoutBorder);
97 aTextureTransform.translate(0.0, fBorder);
98 }
99 }
100
101 aTextureTransform.scale(fTargetSizeX, fTargetSizeY);
102
103 // add texture rotate after scale to keep perpendicular angles
104 if(bAngleUsed)
105 {
106 const B2DPoint aCenter(0.5 * fTargetSizeX, 0.5 * fTargetSizeY);
107
108 aTextureTransform *= basegfx::utils::createRotateAroundPoint(aCenter, fAngle);
109 }
110
111 // add object translate
112 aTextureTransform.translate(fTargetOffsetX, fTargetOffsetY);
113
114 // prepare aspect for texture
115 const double fAspectRatio(fTools::equalZero(fTargetSizeY) ? 1.0 : fTargetSizeX / fTargetSizeY);
116
117 return ODFGradientInfo(aTextureTransform, fAspectRatio, nSteps);
118 }
119
123 static ODFGradientInfo initEllipticalGradientInfo(
124 const B2DRange& rTargetRange,
125 const B2DVector& rOffset,
126 sal_uInt32 nSteps,
127 double fBorder,
128 double fAngle,
129 bool bCircular)
130 {
131 B2DHomMatrix aTextureTransform;
132
133 fAngle = -fAngle;
134
135 double fTargetSizeX(rTargetRange.getWidth());
136 double fTargetSizeY(rTargetRange.getHeight());
137 double fTargetOffsetX(rTargetRange.getMinX());
138 double fTargetOffsetY(rTargetRange.getMinY());
139
140 // add object expansion
141 if(bCircular)
142 {
143 const double fOriginalDiag(std::hypot(fTargetSizeX, fTargetSizeY));
144
145 fTargetOffsetX -= (fOriginalDiag - fTargetSizeX) / 2.0;
146 fTargetOffsetY -= (fOriginalDiag - fTargetSizeY) / 2.0;
147 fTargetSizeX = fOriginalDiag;
148 fTargetSizeY = fOriginalDiag;
149 }
150 else
151 {
152 fTargetOffsetX -= ((M_SQRT2 - 1) / 2.0 ) * fTargetSizeX;
153 fTargetOffsetY -= ((M_SQRT2 - 1) / 2.0 ) * fTargetSizeY;
154 fTargetSizeX = M_SQRT2 * fTargetSizeX;
155 fTargetSizeY = M_SQRT2 * fTargetSizeY;
156 }
157
158 const double fHalfBorder((1.0 - fBorder) * 0.5);
159
160 aTextureTransform.scale(fHalfBorder, fHalfBorder);
161 aTextureTransform.translate(0.5, 0.5);
162 aTextureTransform.scale(fTargetSizeX, fTargetSizeY);
163
164 // add texture rotate after scale to keep perpendicular angles
165 if(!bCircular && !fTools::equalZero(fAngle))
166 {
167 const B2DPoint aCenter(0.5 * fTargetSizeX, 0.5 * fTargetSizeY);
168
169 aTextureTransform *= basegfx::utils::createRotateAroundPoint(aCenter, fAngle);
170 }
171
172 // add defined offsets after rotation
173 if(!fTools::equal(0.5, rOffset.getX()) || !fTools::equal(0.5, rOffset.getY()))
174 {
175 // use original target size
176 fTargetOffsetX += (rOffset.getX() - 0.5) * rTargetRange.getWidth();
177 fTargetOffsetY += (rOffset.getY() - 0.5) * rTargetRange.getHeight();
178 }
179
180 // add object translate
181 aTextureTransform.translate(fTargetOffsetX, fTargetOffsetY);
182
183 // prepare aspect for texture
184 const double fAspectRatio(fTargetSizeY == 0.0 ? 1.0 : (fTargetSizeX / fTargetSizeY));
185
186 return ODFGradientInfo(aTextureTransform, fAspectRatio, nSteps);
187 }
188
192 static ODFGradientInfo initRectGradientInfo(
193 const B2DRange& rTargetRange,
194 const B2DVector& rOffset,
195 sal_uInt32 nSteps,
196 double fBorder,
197 double fAngle,
198 bool bSquare)
199 {
200 B2DHomMatrix aTextureTransform;
201
202 fAngle = -fAngle;
203
204 double fTargetSizeX(rTargetRange.getWidth());
205 double fTargetSizeY(rTargetRange.getHeight());
206 double fTargetOffsetX(rTargetRange.getMinX());
207 double fTargetOffsetY(rTargetRange.getMinY());
208
209 // add object expansion
210 if(bSquare)
211 {
212 const double fSquareWidth(std::max(fTargetSizeX, fTargetSizeY));
213
214 fTargetOffsetX -= (fSquareWidth - fTargetSizeX) / 2.0;
215 fTargetOffsetY -= (fSquareWidth - fTargetSizeY) / 2.0;
216 fTargetSizeX = fTargetSizeY = fSquareWidth;
217 }
218
219 // add object expansion
220 const bool bAngleUsed(!fTools::equalZero(fAngle));
221
222 if(bAngleUsed)
223 {
224 const double fAbsCos(fabs(cos(fAngle)));
225 const double fAbsSin(fabs(sin(fAngle)));
226 const double fNewX(fTargetSizeX * fAbsCos + fTargetSizeY * fAbsSin);
227 const double fNewY(fTargetSizeY * fAbsCos + fTargetSizeX * fAbsSin);
228
229 fTargetOffsetX -= (fNewX - fTargetSizeX) / 2.0;
230 fTargetOffsetY -= (fNewY - fTargetSizeY) / 2.0;
231 fTargetSizeX = fNewX;
232 fTargetSizeY = fNewY;
233 }
234
235 const double fHalfBorder((1.0 - fBorder) * 0.5);
236
237 aTextureTransform.scale(fHalfBorder, fHalfBorder);
238 aTextureTransform.translate(0.5, 0.5);
239 aTextureTransform.scale(fTargetSizeX, fTargetSizeY);
240
241 // add texture rotate after scale to keep perpendicular angles
242 if(bAngleUsed)
243 {
244 const B2DPoint aCenter(0.5 * fTargetSizeX, 0.5 * fTargetSizeY);
245
246 aTextureTransform *= basegfx::utils::createRotateAroundPoint(aCenter, fAngle);
247 }
248
249 // add defined offsets after rotation
250 if(!fTools::equal(0.5, rOffset.getX()) || !fTools::equal(0.5, rOffset.getY()))
251 {
252 // use original target size
253 fTargetOffsetX += (rOffset.getX() - 0.5) * rTargetRange.getWidth();
254 fTargetOffsetY += (rOffset.getY() - 0.5) * rTargetRange.getHeight();
255 }
256
257 // add object translate
258 aTextureTransform.translate(fTargetOffsetX, fTargetOffsetY);
259
260 // prepare aspect for texture
261 const double fAspectRatio(fTargetSizeY == 0.0 ? 1.0 : (fTargetSizeX / fTargetSizeY));
262
263 return ODFGradientInfo(aTextureTransform, fAspectRatio, nSteps);
264 }
265
266 namespace utils
267 {
268 /* Tooling method to extract data from given BGradient
269 to ColorStops, doing some corrections, partially based
270 on given SingleColor */
272 const basegfx::BGradient& rGradient,
273 BColorStops& rColorStops,
274 BColor& rSingleColor)
275 {
276 if (rGradient.GetColorStops().isSingleColor(rSingleColor))
277 {
278 // when single color, preserve value in rSingleColor
279 // and clear the ColorStops, done.
280 rColorStops.clear();
281 return;
282 }
283
284 const bool bAdaptStartEndIntensity(100 != rGradient.GetStartIntens() || 100 != rGradient.GetEndIntens());
285 const bool bAdaptBorder(0 != rGradient.GetBorder());
286
287 if (!bAdaptStartEndIntensity && !bAdaptBorder)
288 {
289 // copy unchanged ColorStops & done
290 rColorStops = rGradient.GetColorStops();
291 return;
292 }
293
294 // prepare a copy to work on
295 basegfx::BGradient aWorkCopy(rGradient);
296
297 if (bAdaptStartEndIntensity)
298 {
299 aWorkCopy.tryToApplyStartEndIntensity();
300
301 // this can again lead to single color (e.g. both zero, so
302 // all black), so check again for it
303 if (aWorkCopy.GetColorStops().isSingleColor(rSingleColor))
304 {
305 rColorStops.clear();
306 return;
307 }
308 }
309
310 if (bAdaptBorder)
311 {
312 aWorkCopy.tryToApplyBorder();
313 }
314
315 // extract ColorStops, that's all we need here
316 rColorStops = aWorkCopy.GetColorStops();
317 }
318
319 /* Tooling method to synchronize the given ColorStops.
320 The intention is that a color GradientStops and an
321 alpha/transparence GradientStops gets synchronized
322 for export. */
324 BColorStops& rColorStops,
325 BColorStops& rAlphaStops,
326 const BColor& rSingleColor,
327 const BColor& rSingleAlpha)
328 {
329 if (rColorStops.empty())
330 {
331 if (rAlphaStops.empty())
332 {
333 // no AlphaStops and no ColorStops
334 // create two-stop fallbacks for both
335 rColorStops = BColorStops {
336 BColorStop(0.0, rSingleColor),
337 BColorStop(1.0, rSingleColor) };
338 rAlphaStops = BColorStops {
339 BColorStop(0.0, rSingleAlpha),
340 BColorStop(1.0, rSingleAlpha) };
341 }
342 else
343 {
344 // AlphaStops but no ColorStops
345 // create fallback synched with existing AlphaStops
346 for (const auto& cand : rAlphaStops)
347 {
348 rColorStops.emplace_back(cand.getStopOffset(), rSingleColor);
349 }
350 }
351
352 // preparations complete, we are done
353 return;
354 }
355 else if (rAlphaStops.empty())
356 {
357 // ColorStops but no AlphaStops
358 // create fallback AlphaStops synched with existing ColorStops using SingleAlpha
359 for (const auto& cand : rColorStops)
360 {
361 rAlphaStops.emplace_back(cand.getStopOffset(), rSingleAlpha);
362 }
363
364 // preparations complete, we are done
365 return;
366 }
367
368 // here we have ColorStops and AlphaStops not empty. Check if we need to
369 // synchronize both or if they are already usable/in a synched state so
370 // that they have same count and same StopOffsets
371 bool bNeedToSyncronize(rColorStops.size() != rAlphaStops.size());
372
373 if (!bNeedToSyncronize)
374 {
375 // check for same StopOffsets
376 BColorStops::const_iterator aCurrColor(rColorStops.begin());
377 BColorStops::const_iterator aCurrAlpha(rAlphaStops.begin());
378
379 while (!bNeedToSyncronize &&
380 aCurrColor != rColorStops.end() &&
381 aCurrAlpha != rAlphaStops.end())
382 {
383 if (fTools::equal(aCurrColor->getStopOffset(), aCurrAlpha->getStopOffset()))
384 {
385 aCurrColor++;
386 aCurrAlpha++;
387 }
388 else
389 {
390 bNeedToSyncronize = true;
391 }
392 }
393 }
394
395 if (bNeedToSyncronize)
396 {
397 // synchronize sizes & StopOffsets
398 BColorStops::const_iterator aCurrColor(rColorStops.begin());
399 BColorStops::const_iterator aCurrAlpha(rAlphaStops.begin());
400 BColorStops aNewColor;
401 BColorStops aNewAlpha;
402 BColorStops::BColorStopRange aColorStopRange;
403 BColorStops::BColorStopRange aAlphaStopRange;
404 bool bRealChange(false);
405
406 do {
407 const bool bColor(aCurrColor != rColorStops.end());
408 const bool bAlpha(aCurrAlpha != rAlphaStops.end());
409
410 if (bColor && bAlpha)
411 {
412 const double fColorOff(aCurrColor->getStopOffset());
413 const double fAlphaOff(aCurrAlpha->getStopOffset());
414
415 if (fTools::less(fColorOff, fAlphaOff))
416 {
417 // copy color, create alpha
418 aNewColor.emplace_back(fColorOff, aCurrColor->getStopColor());
419 aNewAlpha.emplace_back(fColorOff, rAlphaStops.getInterpolatedBColor(fColorOff, 0, aAlphaStopRange));
420 bRealChange = true;
421 aCurrColor++;
422 }
423 else if (fTools::more(fColorOff, fAlphaOff))
424 {
425 // copy alpha, create color
426 aNewColor.emplace_back(fAlphaOff, rColorStops.getInterpolatedBColor(fAlphaOff, 0, aColorStopRange));
427 aNewAlpha.emplace_back(fAlphaOff, aCurrAlpha->getStopColor());
428 bRealChange = true;
429 aCurrAlpha++;
430 }
431 else
432 {
433 // equal: copy both, advance
434 aNewColor.emplace_back(fColorOff, aCurrColor->getStopColor());
435 aNewAlpha.emplace_back(fAlphaOff, aCurrAlpha->getStopColor());
436 aCurrColor++;
437 aCurrAlpha++;
438 }
439 }
440 else if (bColor)
441 {
442 const double fColorOff(aCurrColor->getStopOffset());
443 aNewAlpha.emplace_back(fColorOff, rAlphaStops.getInterpolatedBColor(fColorOff, 0, aAlphaStopRange));
444 aNewColor.emplace_back(fColorOff, aCurrColor->getStopColor());
445 bRealChange = true;
446 aCurrColor++;
447 }
448 else if (bAlpha)
449 {
450 const double fAlphaOff(aCurrAlpha->getStopOffset());
451 aNewColor.emplace_back(fAlphaOff, rColorStops.getInterpolatedBColor(fAlphaOff, 0, aColorStopRange));
452 aNewAlpha.emplace_back(fAlphaOff, aCurrAlpha->getStopColor());
453 bRealChange = true;
454 aCurrAlpha++;
455 }
456 else
457 {
458 // no more input, break do..while loop
459 break;
460 }
461 }
462 while(true);
463
464 if (bRealChange)
465 {
466 // copy on 'real' change, that means data was added.
467 // This should always be the cease and should have been
468 // detected as such above, see bNeedToSyncronize
469 rColorStops = aNewColor;
470 rAlphaStops = aNewAlpha; // MCGR: tdf#155537 used wrong result here
471 }
472 }
473 }
474
476 sal_uInt32 nRequestedSteps,
477 const BColor& rStart,
478 const BColor& rEnd)
479 {
480 const sal_uInt32 nMaxSteps(sal_uInt32((rStart.getMaximumDistance(rEnd) * 127.5) + 0.5));
481
482 if (0 == nRequestedSteps)
483 {
484 nRequestedSteps = nMaxSteps;
485 }
486
487 if(nRequestedSteps > nMaxSteps)
488 {
489 nRequestedSteps = nMaxSteps;
490 }
491
492 return std::max(sal_uInt32(1), nRequestedSteps);
493 }
494
496 const B2DRange& rTargetArea,
497 sal_uInt32 nSteps,
498 double fBorder,
499 double fAngle)
500 {
501 return init1DGradientInfo(
502 rTargetArea,
503 nSteps,
504 fBorder,
505 fAngle,
506 false);
507 }
508
510 const B2DRange& rTargetArea,
511 sal_uInt32 nSteps,
512 double fBorder,
513 double fAngle)
514 {
515 return init1DGradientInfo(
516 rTargetArea,
517 nSteps,
518 fBorder,
519 fAngle,
520 true);
521 }
522
524 const B2DRange& rTargetArea,
525 const B2DVector& rOffset,
526 sal_uInt32 nSteps,
527 double fBorder)
528 {
530 rTargetArea,
531 rOffset,
532 nSteps,
533 fBorder,
534 0.0,
535 true);
536 }
537
539 const B2DRange& rTargetArea,
540 const B2DVector& rOffset,
541 sal_uInt32 nSteps,
542 double fBorder,
543 double fAngle)
544 {
546 rTargetArea,
547 rOffset,
548 nSteps,
549 fBorder,
550 fAngle,
551 false);
552 }
553
555 const B2DRange& rTargetArea,
556 const B2DVector& rOffset,
557 sal_uInt32 nSteps,
558 double fBorder,
559 double fAngle)
560 {
562 rTargetArea,
563 rOffset,
564 nSteps,
565 fBorder,
566 fAngle,
567 true);
568 }
569
571 const B2DRange& rTargetArea,
572 const B2DVector& rOffset,
573 sal_uInt32 nSteps,
574 double fBorder,
575 double fAngle)
576 {
578 rTargetArea,
579 rOffset,
580 nSteps,
581 fBorder,
582 fAngle,
583 false);
584 }
585
586 double getLinearGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
587 {
588 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV);
589
590 // Ignore X, this is not needed at all for Y-Oriented gradients
591 // if(aCoor.getX() < 0.0 || aCoor.getX() > 1.0)
592 // {
593 // return 0.0;
594 // }
595
596 if(aCoor.getY() <= 0.0)
597 {
598 return 0.0; // start value for inside
599 }
600
601 if(aCoor.getY() >= 1.0)
602 {
603 return 1.0; // end value for outside
604 }
605
606 return aCoor.getY();
607 }
608
609 double getAxialGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
610 {
611 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV);
612
613 // Ignore X, this is not needed at all for Y-Oriented gradients
614 //if(aCoor.getX() < 0.0 || aCoor.getX() > 1.0)
615 //{
616 // return 0.0;
617 //}
618
619 const double fAbsY(fabs(aCoor.getY()));
620
621 if(fAbsY >= 1.0)
622 {
623 return 1.0; // use end value when outside in Y
624 }
625
626 return fAbsY;
627 }
628
629 double getRadialGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
630 {
631 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV);
632
633 if(aCoor.getX() < -1.0 || aCoor.getX() > 1.0 || aCoor.getY() < -1.0 || aCoor.getY() > 1.0)
634 {
635 return 0.0;
636 }
637
638 return 1.0 - std::hypot(aCoor.getX(), aCoor.getY());
639 }
640
641 double getEllipticalGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
642 {
643 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV);
644
645 if(aCoor.getX() < -1.0 || aCoor.getX() > 1.0 || aCoor.getY() < -1.0 || aCoor.getY() > 1.0)
646 {
647 return 0.0;
648 }
649
650 double fAspectRatio(rGradInfo.getAspectRatio());
651 double t(1.0);
652
653 // MCGR: Similar to getRectangularGradientAlpha (please
654 // see there) we need to use aspect ratio here. Due to
655 // initEllipticalGradientInfo using M_SQRT2 to make this
656 // gradient look 'nicer' this correction seems not 100%
657 // correct, but is close enough for now
658 if(fAspectRatio > 1.0)
659 {
660 t = 1.0 - std::hypot(aCoor.getX() / fAspectRatio, aCoor.getY());
661 }
662 else if(fAspectRatio > 0.0)
663 {
664 t = 1.0 - std::hypot(aCoor.getX(), aCoor.getY() * fAspectRatio);
665 }
666
667 return t;
668 }
669
670 double getSquareGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
671 {
672 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV);
673 const double fAbsX(fabs(aCoor.getX()));
674
675 if(fAbsX >= 1.0)
676 {
677 return 0.0;
678 }
679
680 const double fAbsY(fabs(aCoor.getY()));
681
682 if(fAbsY >= 1.0)
683 {
684 return 0.0;
685 }
686
687 return 1.0 - std::max(fAbsX, fAbsY);
688 }
689
690 double getRectangularGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
691 {
692 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV);
693 double fAbsX(fabs(aCoor.getX()));
694
695 if(fAbsX >= 1.0)
696 {
697 return 0.0;
698 }
699
700 double fAbsY(fabs(aCoor.getY()));
701
702 if(fAbsY >= 1.0)
703 {
704 return 0.0;
705 }
706
707 // MCGR: Visualizations using the texturing method for
708 // displaying gradients (getBackTextureTransform is
709 // involved) show wrong results for GradientElliptical
710 // and GradientRect, this can be best seen when using
711 // less steps, e.g. just four. This thus has influence
712 // on cppcanvas (slideshow) and 3D textures, so needs
713 // to be corrected.
714 // Missing is to use the aspect ratio of the object
715 // in this [-1, -1, 1, 1] unified coordinate space
716 // after getBackTextureTransform is applied. Optically
717 // in the larger direction of the texturing the color
718 // step distances are too big *because* we are in that
719 // unit range now.
720 // To correct that, a kind of 'limo stretching' needs to
721 // be applied, adding space around the center
722 // proportional to the aspect ratio, so the intuitive
723 // idea would be to do
724 //
725 // fAbsX' = ((fAspectRatio - 1) + fAbsX) / fAspectRatio
726 //
727 // which scales from the center. This does not work, and
728 // after some thoughts it's clear why: It's not the
729 // position that needs to be moved (this cannot be
730 // changed), but the position *before* that scale has
731 // to be determined to get the correct, shifted color
732 // for the already 'new' position. Thus, turn around
733 // the expression as
734 //
735 // fAbsX' * fAspectRatio = fAspectRatio - 1 + fAbsX
736 // fAbsX' * fAspectRatio - fAspectRatio + 1 = fAbsX
737 // fAbsX = (fAbsX' - 1) * fAspectRatio + 1
738 //
739 // This works and can even be simply adapted for
740 // fAspectRatio < 1.0 aka vertical is bigger.
741 double fAspectRatio(rGradInfo.getAspectRatio());
742 if(fAspectRatio > 1.0)
743 {
744 fAbsX = ((fAbsX - 1) * fAspectRatio) + 1;
745 }
746 else if(fAspectRatio > 0.0)
747 {
748 fAbsY = ((fAbsY - 1) / fAspectRatio) + 1;
749 }
750
751 return 1.0 - std::max(fAbsX, fAbsY);
752 }
753 } // namespace utils
754} // namespace basegfx
755
756/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
XPropertyListType t
void translate(double fX, double fY)
void scale(double fX, double fY)
Base Point class with two double values.
Definition: b2dpoint.hxx:42
A two-dimensional interval over doubles.
Definition: b2drange.hxx:54
Base Point class with two double values.
Definition: b2dvector.hxx:40
bool isSingleColor(BColor &rSingleColor) const
Definition: bgradient.cxx:530
BColor getInterpolatedBColor(double fPosition, sal_uInt32 nRequestedSteps, BColorStopRange &rLastColorStopRange) const
Definition: bgradient.cxx:119
Base Color class with three double values.
Definition: bcolor.hxx:41
double getMaximumDistance(const BColor &rColor) const
Definition: bcolor.hxx:142
sal_uInt16 GetBorder() const
Definition: bgradient.hxx:336
sal_uInt16 GetStartIntens() const
Definition: bgradient.hxx:339
void tryToApplyStartEndIntensity()
Definition: bgradient.cxx:1093
const basegfx::BColorStops & GetColorStops() const
Definition: bgradient.hxx:334
sal_uInt16 GetEndIntens() const
Definition: bgradient.hxx:340
TYPE getWidth() const
return difference between upper and lower X value. returns 0 for empty sets.
Definition: Range2D.hxx:106
TYPE getMinX() const
get lower bound of the set. returns arbitrary values for empty sets.
Definition: Range2D.hxx:94
TYPE getMinY() const
get lower bound of the set. returns arbitrary values for empty sets.
Definition: Range2D.hxx:97
TYPE getHeight() const
return difference between upper and lower Y value. returns 0 for empty sets.
Definition: Range2D.hxx:109
TYPE getX() const
Get X-Coordinate of 2D Tuple.
Definition: Tuple2D.hxx:63
TYPE getY() const
Get Y-Coordinate of 2D Tuple.
Definition: Tuple2D.hxx:66
bool more(const T &rfValA, const T &rfValB)
Definition: ftools.hxx:194
bool equalZero(const T &rfVal)
Compare against small value.
Definition: ftools.hxx:156
bool equal(T const &rfValA, T const &rfValB)
Definition: ftools.hxx:169
bool less(const T &rfValA, const T &rfValB)
Definition: ftools.hxx:182
ODFGradientInfo createRadialODFGradientInfo(const B2DRange &rTargetArea, const B2DVector &rOffset, sal_uInt32 nSteps, double fBorder)
Create matrix for ODF's radial gradient definition.
double getAxialGradientAlpha(const B2DPoint &rUV, const ODFGradientInfo &rGradInfo)
Calculate axial gradient blend value.
ODFGradientInfo createAxialODFGradientInfo(const B2DRange &rTargetArea, sal_uInt32 nSteps, double fBorder, double fAngle)
Create matrix for ODF's axial gradient definition.
double getLinearGradientAlpha(const B2DPoint &rUV, const ODFGradientInfo &rGradInfo)
Calculate linear gradient blend value.
B2DHomMatrix createRotateAroundPoint(double fPointX, double fPointY, double fRadiant)
special for the often used case of rotation around a point
double getRectangularGradientAlpha(const B2DPoint &rUV, const ODFGradientInfo &rGradInfo)
Calculate rectangular gradient blend value.
double getEllipticalGradientAlpha(const B2DPoint &rUV, const ODFGradientInfo &rGradInfo)
Calculate elliptical gradient blend value.
double getSquareGradientAlpha(const B2DPoint &rUV, const ODFGradientInfo &rGradInfo)
Calculate square gradient blend value.
void synchronizeColorStops(BColorStops &rColorStops, BColorStops &rAlphaStops, const BColor &rSingleColor, const BColor &rSingleAlpha)
ODFGradientInfo createLinearODFGradientInfo(const B2DRange &rTargetArea, sal_uInt32 nSteps, double fBorder, double fAngle)
Create matrix for ODF's linear gradient definition.
void prepareColorStops(const basegfx::BGradient &rGradient, BColorStops &rColorStops, BColor &rSingleColor)
double getRadialGradientAlpha(const B2DPoint &rUV, const ODFGradientInfo &rGradInfo)
Calculate radial gradient blend value.
ODFGradientInfo createSquareODFGradientInfo(const B2DRange &rTargetArea, const B2DVector &rOffset, sal_uInt32 nSteps, double fBorder, double fAngle)
Create matrix for ODF's square gradient definition.
ODFGradientInfo createEllipticalODFGradientInfo(const B2DRange &rTargetArea, const B2DVector &rOffset, sal_uInt32 nSteps, double fBorder, double fAngle)
Create matrix for ODF's elliptical gradient definition.
ODFGradientInfo createRectangularODFGradientInfo(const B2DRange &rTargetArea, const B2DVector &rOffset, sal_uInt32 nSteps, double fBorder, double fAngle)
Create matrix for ODF's rectangular gradient definition.
sal_uInt32 calculateNumberOfSteps(sal_uInt32 nRequestedSteps, const BColor &rStart, const BColor &rEnd)
static ODFGradientInfo initEllipticalGradientInfo(const B2DRange &rTargetRange, const B2DVector &rOffset, sal_uInt32 nSteps, double fBorder, double fAngle, bool bCircular)
Most of the setup for radial & ellipsoidal gradient is the same, except for the border treatment.
static ODFGradientInfo init1DGradientInfo(const B2DRange &rTargetRange, sal_uInt32 nSteps, double fBorder, double fAngle, bool bAxial)
Most of the setup for linear & axial gradient is the same, except for the border treatment.
static ODFGradientInfo initRectGradientInfo(const B2DRange &rTargetRange, const B2DVector &rOffset, sal_uInt32 nSteps, double fBorder, double fAngle, bool bSquare)
Setup for rect & square gradient is exactly the same.