Version 24.2.2.2, tag libreoffice-24.2.2.2
[LibreOffice.git] / basegfx / source / tools / gradienttools.cxx
blob8f3e8ae83c06789a6be88ec1a3f661827e8d6245
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 <basegfx/utils/gradienttools.hxx>
21 #include <basegfx/point/b2dpoint.hxx>
22 #include <basegfx/range/b2drange.hxx>
23 #include <basegfx/matrix/b2dhommatrixtools.hxx>
24 #include <com/sun/star/awt/Gradient2.hpp>
25 #include <osl/endian.h>
27 #include <algorithm>
28 #include <cmath>
30 namespace basegfx
32 bool ODFGradientInfo::operator==(const ODFGradientInfo& rODFGradientInfo) const
34 return getTextureTransform() == rODFGradientInfo.getTextureTransform()
35 && getAspectRatio() == rODFGradientInfo.getAspectRatio()
36 && getRequestedSteps() == rODFGradientInfo.getRequestedSteps();
39 const B2DHomMatrix& ODFGradientInfo::getBackTextureTransform() const
41 if(maBackTextureTransform.isIdentity())
43 const_cast< ODFGradientInfo* >(this)->maBackTextureTransform = getTextureTransform();
44 const_cast< ODFGradientInfo* >(this)->maBackTextureTransform.invert();
47 return maBackTextureTransform;
50 /** Most of the setup for linear & axial gradient is the same, except
51 for the border treatment. Factored out here.
53 static ODFGradientInfo init1DGradientInfo(
54 const B2DRange& rTargetRange,
55 sal_uInt32 nSteps,
56 double fBorder,
57 double fAngle,
58 bool bAxial)
60 B2DHomMatrix aTextureTransform;
62 fAngle = -fAngle;
64 double fTargetSizeX(rTargetRange.getWidth());
65 double fTargetSizeY(rTargetRange.getHeight());
66 double fTargetOffsetX(rTargetRange.getMinX());
67 double fTargetOffsetY(rTargetRange.getMinY());
69 // add object expansion
70 const bool bAngleUsed(!fTools::equalZero(fAngle));
72 if(bAngleUsed)
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);
79 fTargetOffsetX -= (fNewX - fTargetSizeX) / 2.0;
80 fTargetOffsetY -= (fNewY - fTargetSizeY) / 2.0;
81 fTargetSizeX = fNewX;
82 fTargetSizeY = fNewY;
85 const double fSizeWithoutBorder(1.0 - fBorder);
87 if(bAxial)
89 aTextureTransform.scale(1.0, fSizeWithoutBorder * 0.5);
90 aTextureTransform.translate(0.0, 0.5);
92 else
94 if(!fTools::equal(fSizeWithoutBorder, 1.0))
96 aTextureTransform.scale(1.0, fSizeWithoutBorder);
97 aTextureTransform.translate(0.0, fBorder);
101 aTextureTransform.scale(fTargetSizeX, fTargetSizeY);
103 // add texture rotate after scale to keep perpendicular angles
104 if(bAngleUsed)
106 const B2DPoint aCenter(0.5 * fTargetSizeX, 0.5 * fTargetSizeY);
108 aTextureTransform *= basegfx::utils::createRotateAroundPoint(aCenter, fAngle);
111 // add object translate
112 aTextureTransform.translate(fTargetOffsetX, fTargetOffsetY);
114 // prepare aspect for texture
115 const double fAspectRatio(fTools::equalZero(fTargetSizeY) ? 1.0 : fTargetSizeX / fTargetSizeY);
117 return ODFGradientInfo(aTextureTransform, fAspectRatio, nSteps);
120 /** Most of the setup for radial & ellipsoidal gradient is the same,
121 except for the border treatment. Factored out here.
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)
131 B2DHomMatrix aTextureTransform;
133 fAngle = -fAngle;
135 double fTargetSizeX(rTargetRange.getWidth());
136 double fTargetSizeY(rTargetRange.getHeight());
137 double fTargetOffsetX(rTargetRange.getMinX());
138 double fTargetOffsetY(rTargetRange.getMinY());
140 // add object expansion
141 if(bCircular)
143 const double fOriginalDiag(std::hypot(fTargetSizeX, fTargetSizeY));
145 fTargetOffsetX -= (fOriginalDiag - fTargetSizeX) / 2.0;
146 fTargetOffsetY -= (fOriginalDiag - fTargetSizeY) / 2.0;
147 fTargetSizeX = fOriginalDiag;
148 fTargetSizeY = fOriginalDiag;
150 else
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;
158 const double fHalfBorder((1.0 - fBorder) * 0.5);
160 aTextureTransform.scale(fHalfBorder, fHalfBorder);
161 aTextureTransform.translate(0.5, 0.5);
162 aTextureTransform.scale(fTargetSizeX, fTargetSizeY);
164 // add texture rotate after scale to keep perpendicular angles
165 if(!bCircular && !fTools::equalZero(fAngle))
167 const B2DPoint aCenter(0.5 * fTargetSizeX, 0.5 * fTargetSizeY);
169 aTextureTransform *= basegfx::utils::createRotateAroundPoint(aCenter, fAngle);
172 // add defined offsets after rotation
173 if(!fTools::equal(0.5, rOffset.getX()) || !fTools::equal(0.5, rOffset.getY()))
175 // use original target size
176 fTargetOffsetX += (rOffset.getX() - 0.5) * rTargetRange.getWidth();
177 fTargetOffsetY += (rOffset.getY() - 0.5) * rTargetRange.getHeight();
180 // add object translate
181 aTextureTransform.translate(fTargetOffsetX, fTargetOffsetY);
183 // prepare aspect for texture
184 const double fAspectRatio(fTargetSizeY == 0.0 ? 1.0 : (fTargetSizeX / fTargetSizeY));
186 return ODFGradientInfo(aTextureTransform, fAspectRatio, nSteps);
189 /** Setup for rect & square gradient is exactly the same. Factored out
190 here.
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)
200 B2DHomMatrix aTextureTransform;
202 fAngle = -fAngle;
204 double fTargetSizeX(rTargetRange.getWidth());
205 double fTargetSizeY(rTargetRange.getHeight());
206 double fTargetOffsetX(rTargetRange.getMinX());
207 double fTargetOffsetY(rTargetRange.getMinY());
209 // add object expansion
210 if(bSquare)
212 const double fSquareWidth(std::max(fTargetSizeX, fTargetSizeY));
214 fTargetOffsetX -= (fSquareWidth - fTargetSizeX) / 2.0;
215 fTargetOffsetY -= (fSquareWidth - fTargetSizeY) / 2.0;
216 fTargetSizeX = fTargetSizeY = fSquareWidth;
219 // add object expansion
220 const bool bAngleUsed(!fTools::equalZero(fAngle));
222 if(bAngleUsed)
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);
229 fTargetOffsetX -= (fNewX - fTargetSizeX) / 2.0;
230 fTargetOffsetY -= (fNewY - fTargetSizeY) / 2.0;
231 fTargetSizeX = fNewX;
232 fTargetSizeY = fNewY;
235 const double fHalfBorder((1.0 - fBorder) * 0.5);
237 aTextureTransform.scale(fHalfBorder, fHalfBorder);
238 aTextureTransform.translate(0.5, 0.5);
239 aTextureTransform.scale(fTargetSizeX, fTargetSizeY);
241 // add texture rotate after scale to keep perpendicular angles
242 if(bAngleUsed)
244 const B2DPoint aCenter(0.5 * fTargetSizeX, 0.5 * fTargetSizeY);
246 aTextureTransform *= basegfx::utils::createRotateAroundPoint(aCenter, fAngle);
249 // add defined offsets after rotation
250 if(!fTools::equal(0.5, rOffset.getX()) || !fTools::equal(0.5, rOffset.getY()))
252 // use original target size
253 fTargetOffsetX += (rOffset.getX() - 0.5) * rTargetRange.getWidth();
254 fTargetOffsetY += (rOffset.getY() - 0.5) * rTargetRange.getHeight();
257 // add object translate
258 aTextureTransform.translate(fTargetOffsetX, fTargetOffsetY);
260 // prepare aspect for texture
261 const double fAspectRatio(fTargetSizeY == 0.0 ? 1.0 : (fTargetSizeX / fTargetSizeY));
263 return ODFGradientInfo(aTextureTransform, fAspectRatio, nSteps);
266 namespace utils
268 /* Tooling method to extract data from given BGradient
269 to ColorStops, doing some corrections, partially based
270 on given SingleColor */
271 void prepareColorStops(
272 const basegfx::BGradient& rGradient,
273 BColorStops& rColorStops,
274 BColor& rSingleColor)
276 if (rGradient.GetColorStops().isSingleColor(rSingleColor))
278 // when single color, preserve value in rSingleColor
279 // and clear the ColorStops, done.
280 rColorStops.clear();
281 return;
284 const bool bAdaptStartEndIntensity(100 != rGradient.GetStartIntens() || 100 != rGradient.GetEndIntens());
285 const bool bAdaptBorder(0 != rGradient.GetBorder());
287 if (!bAdaptStartEndIntensity && !bAdaptBorder)
289 // copy unchanged ColorStops & done
290 rColorStops = rGradient.GetColorStops();
291 return;
294 // prepare a copy to work on
295 basegfx::BGradient aWorkCopy(rGradient);
297 if (bAdaptStartEndIntensity)
299 aWorkCopy.tryToApplyStartEndIntensity();
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))
305 rColorStops.clear();
306 return;
310 if (bAdaptBorder)
312 aWorkCopy.tryToApplyBorder();
315 // extract ColorStops, that's all we need here
316 rColorStops = aWorkCopy.GetColorStops();
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. */
323 void synchronizeColorStops(
324 BColorStops& rColorStops,
325 BColorStops& rAlphaStops,
326 const BColor& rSingleColor,
327 const BColor& rSingleAlpha)
329 if (rColorStops.empty())
331 if (rAlphaStops.empty())
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) };
342 else
344 // AlphaStops but no ColorStops
345 // create fallback synched with existing AlphaStops
346 for (const auto& cand : rAlphaStops)
348 rColorStops.emplace_back(cand.getStopOffset(), rSingleColor);
352 // preparations complete, we are done
353 return;
355 else if (rAlphaStops.empty())
357 // ColorStops but no AlphaStops
358 // create fallback AlphaStops synched with existing ColorStops using SingleAlpha
359 for (const auto& cand : rColorStops)
361 rAlphaStops.emplace_back(cand.getStopOffset(), rSingleAlpha);
364 // preparations complete, we are done
365 return;
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());
373 if (!bNeedToSyncronize)
375 // check for same StopOffsets
376 BColorStops::const_iterator aCurrColor(rColorStops.begin());
377 BColorStops::const_iterator aCurrAlpha(rAlphaStops.begin());
379 while (!bNeedToSyncronize &&
380 aCurrColor != rColorStops.end() &&
381 aCurrAlpha != rAlphaStops.end())
383 if (fTools::equal(aCurrColor->getStopOffset(), aCurrAlpha->getStopOffset()))
385 aCurrColor++;
386 aCurrAlpha++;
388 else
390 bNeedToSyncronize = true;
395 if (bNeedToSyncronize)
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);
406 do {
407 const bool bColor(aCurrColor != rColorStops.end());
408 const bool bAlpha(aCurrAlpha != rAlphaStops.end());
410 if (bColor && bAlpha)
412 const double fColorOff(aCurrColor->getStopOffset());
413 const double fAlphaOff(aCurrAlpha->getStopOffset());
415 if (fTools::less(fColorOff, fAlphaOff))
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++;
423 else if (fTools::more(fColorOff, fAlphaOff))
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++;
431 else
433 // equal: copy both, advance
434 aNewColor.emplace_back(fColorOff, aCurrColor->getStopColor());
435 aNewAlpha.emplace_back(fAlphaOff, aCurrAlpha->getStopColor());
436 aCurrColor++;
437 aCurrAlpha++;
440 else if (bColor)
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++;
448 else if (bAlpha)
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++;
456 else
458 // no more input, break do..while loop
459 break;
462 while(true);
464 if (bRealChange)
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
475 sal_uInt32 calculateNumberOfSteps(
476 sal_uInt32 nRequestedSteps,
477 const BColor& rStart,
478 const BColor& rEnd)
480 const sal_uInt32 nMaxSteps(sal_uInt32((rStart.getMaximumDistance(rEnd) * 127.5) + 0.5));
482 if (0 == nRequestedSteps)
484 nRequestedSteps = nMaxSteps;
487 if(nRequestedSteps > nMaxSteps)
489 nRequestedSteps = nMaxSteps;
492 return std::max(sal_uInt32(1), nRequestedSteps);
495 ODFGradientInfo createLinearODFGradientInfo(
496 const B2DRange& rTargetArea,
497 sal_uInt32 nSteps,
498 double fBorder,
499 double fAngle)
501 return init1DGradientInfo(
502 rTargetArea,
503 nSteps,
504 fBorder,
505 fAngle,
506 false);
509 ODFGradientInfo createAxialODFGradientInfo(
510 const B2DRange& rTargetArea,
511 sal_uInt32 nSteps,
512 double fBorder,
513 double fAngle)
515 return init1DGradientInfo(
516 rTargetArea,
517 nSteps,
518 fBorder,
519 fAngle,
520 true);
523 ODFGradientInfo createRadialODFGradientInfo(
524 const B2DRange& rTargetArea,
525 const B2DVector& rOffset,
526 sal_uInt32 nSteps,
527 double fBorder)
529 return initEllipticalGradientInfo(
530 rTargetArea,
531 rOffset,
532 nSteps,
533 fBorder,
534 0.0,
535 true);
538 ODFGradientInfo createEllipticalODFGradientInfo(
539 const B2DRange& rTargetArea,
540 const B2DVector& rOffset,
541 sal_uInt32 nSteps,
542 double fBorder,
543 double fAngle)
545 return initEllipticalGradientInfo(
546 rTargetArea,
547 rOffset,
548 nSteps,
549 fBorder,
550 fAngle,
551 false);
554 ODFGradientInfo createSquareODFGradientInfo(
555 const B2DRange& rTargetArea,
556 const B2DVector& rOffset,
557 sal_uInt32 nSteps,
558 double fBorder,
559 double fAngle)
561 return initRectGradientInfo(
562 rTargetArea,
563 rOffset,
564 nSteps,
565 fBorder,
566 fAngle,
567 true);
570 ODFGradientInfo createRectangularODFGradientInfo(
571 const B2DRange& rTargetArea,
572 const B2DVector& rOffset,
573 sal_uInt32 nSteps,
574 double fBorder,
575 double fAngle)
577 return initRectGradientInfo(
578 rTargetArea,
579 rOffset,
580 nSteps,
581 fBorder,
582 fAngle,
583 false);
586 double getLinearGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
588 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV);
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 // }
596 if(aCoor.getY() <= 0.0)
598 return 0.0; // start value for inside
601 if(aCoor.getY() >= 1.0)
603 return 1.0; // end value for outside
606 return aCoor.getY();
609 double getAxialGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
611 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV);
613 // Ignore X, this is not needed at all for Y-Oriented gradients
614 //if(aCoor.getX() < 0.0 || aCoor.getX() > 1.0)
616 // return 0.0;
619 const double fAbsY(fabs(aCoor.getY()));
621 if(fAbsY >= 1.0)
623 return 1.0; // use end value when outside in Y
626 return fAbsY;
629 double getRadialGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
631 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV);
633 if(aCoor.getX() < -1.0 || aCoor.getX() > 1.0 || aCoor.getY() < -1.0 || aCoor.getY() > 1.0)
635 return 0.0;
638 return 1.0 - std::hypot(aCoor.getX(), aCoor.getY());
641 double getEllipticalGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
643 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV);
645 if(aCoor.getX() < -1.0 || aCoor.getX() > 1.0 || aCoor.getY() < -1.0 || aCoor.getY() > 1.0)
647 return 0.0;
650 double fAspectRatio(rGradInfo.getAspectRatio());
651 double t(1.0);
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)
660 t = 1.0 - std::hypot(aCoor.getX() / fAspectRatio, aCoor.getY());
662 else if(fAspectRatio > 0.0)
664 t = 1.0 - std::hypot(aCoor.getX(), aCoor.getY() * fAspectRatio);
667 return t;
670 double getSquareGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
672 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV);
673 const double fAbsX(fabs(aCoor.getX()));
675 if(fAbsX >= 1.0)
677 return 0.0;
680 const double fAbsY(fabs(aCoor.getY()));
682 if(fAbsY >= 1.0)
684 return 0.0;
687 return 1.0 - std::max(fAbsX, fAbsY);
690 double getRectangularGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
692 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV);
693 double fAbsX(fabs(aCoor.getX()));
695 if(fAbsX >= 1.0)
697 return 0.0;
700 double fAbsY(fabs(aCoor.getY()));
702 if(fAbsY >= 1.0)
704 return 0.0;
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
725 // fAbsX' = ((fAspectRatio - 1) + fAbsX) / fAspectRatio
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
735 // fAbsX' * fAspectRatio = fAspectRatio - 1 + fAbsX
736 // fAbsX' * fAspectRatio - fAspectRatio + 1 = fAbsX
737 // fAbsX = (fAbsX' - 1) * fAspectRatio + 1
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)
744 fAbsX = ((fAbsX - 1) * fAspectRatio) + 1;
746 else if(fAspectRatio > 0.0)
748 fAbsY = ((fAbsY - 1) / fAspectRatio) + 1;
751 return 1.0 - std::max(fAbsX, fAbsY);
753 } // namespace utils
754 } // namespace basegfx
756 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */