diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /basegfx/source/tools/gradienttools.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'basegfx/source/tools/gradienttools.cxx')
-rw-r--r-- | basegfx/source/tools/gradienttools.cxx | 756 |
1 files changed, 756 insertions, 0 deletions
diff --git a/basegfx/source/tools/gradienttools.cxx b/basegfx/source/tools/gradienttools.cxx new file mode 100644 index 0000000000..8f3e8ae83c --- /dev/null +++ b/basegfx/source/tools/gradienttools.cxx @@ -0,0 +1,756 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <basegfx/utils/gradienttools.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/range/b2drange.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <com/sun/star/awt/Gradient2.hpp> +#include <osl/endian.h> + +#include <algorithm> +#include <cmath> + +namespace basegfx +{ + bool ODFGradientInfo::operator==(const ODFGradientInfo& rODFGradientInfo) const + { + return getTextureTransform() == rODFGradientInfo.getTextureTransform() + && getAspectRatio() == rODFGradientInfo.getAspectRatio() + && getRequestedSteps() == rODFGradientInfo.getRequestedSteps(); + } + + const B2DHomMatrix& ODFGradientInfo::getBackTextureTransform() const + { + if(maBackTextureTransform.isIdentity()) + { + const_cast< ODFGradientInfo* >(this)->maBackTextureTransform = getTextureTransform(); + const_cast< ODFGradientInfo* >(this)->maBackTextureTransform.invert(); + } + + return maBackTextureTransform; + } + + /** Most of the setup for linear & axial gradient is the same, except + for the border treatment. Factored out here. + */ + static ODFGradientInfo init1DGradientInfo( + const B2DRange& rTargetRange, + sal_uInt32 nSteps, + double fBorder, + double fAngle, + bool bAxial) + { + B2DHomMatrix aTextureTransform; + + fAngle = -fAngle; + + double fTargetSizeX(rTargetRange.getWidth()); + double fTargetSizeY(rTargetRange.getHeight()); + double fTargetOffsetX(rTargetRange.getMinX()); + double fTargetOffsetY(rTargetRange.getMinY()); + + // add object expansion + const bool bAngleUsed(!fTools::equalZero(fAngle)); + + if(bAngleUsed) + { + const double fAbsCos(fabs(cos(fAngle))); + const double fAbsSin(fabs(sin(fAngle))); + const double fNewX(fTargetSizeX * fAbsCos + fTargetSizeY * fAbsSin); + const double fNewY(fTargetSizeY * fAbsCos + fTargetSizeX * fAbsSin); + + fTargetOffsetX -= (fNewX - fTargetSizeX) / 2.0; + fTargetOffsetY -= (fNewY - fTargetSizeY) / 2.0; + fTargetSizeX = fNewX; + fTargetSizeY = fNewY; + } + + const double fSizeWithoutBorder(1.0 - fBorder); + + if(bAxial) + { + aTextureTransform.scale(1.0, fSizeWithoutBorder * 0.5); + aTextureTransform.translate(0.0, 0.5); + } + else + { + if(!fTools::equal(fSizeWithoutBorder, 1.0)) + { + aTextureTransform.scale(1.0, fSizeWithoutBorder); + aTextureTransform.translate(0.0, fBorder); + } + } + + aTextureTransform.scale(fTargetSizeX, fTargetSizeY); + + // add texture rotate after scale to keep perpendicular angles + if(bAngleUsed) + { + const B2DPoint aCenter(0.5 * fTargetSizeX, 0.5 * fTargetSizeY); + + aTextureTransform *= basegfx::utils::createRotateAroundPoint(aCenter, fAngle); + } + + // add object translate + aTextureTransform.translate(fTargetOffsetX, fTargetOffsetY); + + // prepare aspect for texture + const double fAspectRatio(fTools::equalZero(fTargetSizeY) ? 1.0 : fTargetSizeX / fTargetSizeY); + + return ODFGradientInfo(aTextureTransform, fAspectRatio, nSteps); + } + + /** Most of the setup for radial & ellipsoidal gradient is the same, + except for the border treatment. Factored out here. + */ + static ODFGradientInfo initEllipticalGradientInfo( + const B2DRange& rTargetRange, + const B2DVector& rOffset, + sal_uInt32 nSteps, + double fBorder, + double fAngle, + bool bCircular) + { + B2DHomMatrix aTextureTransform; + + fAngle = -fAngle; + + double fTargetSizeX(rTargetRange.getWidth()); + double fTargetSizeY(rTargetRange.getHeight()); + double fTargetOffsetX(rTargetRange.getMinX()); + double fTargetOffsetY(rTargetRange.getMinY()); + + // add object expansion + if(bCircular) + { + const double fOriginalDiag(std::hypot(fTargetSizeX, fTargetSizeY)); + + fTargetOffsetX -= (fOriginalDiag - fTargetSizeX) / 2.0; + fTargetOffsetY -= (fOriginalDiag - fTargetSizeY) / 2.0; + fTargetSizeX = fOriginalDiag; + fTargetSizeY = fOriginalDiag; + } + else + { + fTargetOffsetX -= ((M_SQRT2 - 1) / 2.0 ) * fTargetSizeX; + fTargetOffsetY -= ((M_SQRT2 - 1) / 2.0 ) * fTargetSizeY; + fTargetSizeX = M_SQRT2 * fTargetSizeX; + fTargetSizeY = M_SQRT2 * fTargetSizeY; + } + + const double fHalfBorder((1.0 - fBorder) * 0.5); + + aTextureTransform.scale(fHalfBorder, fHalfBorder); + aTextureTransform.translate(0.5, 0.5); + aTextureTransform.scale(fTargetSizeX, fTargetSizeY); + + // add texture rotate after scale to keep perpendicular angles + if(!bCircular && !fTools::equalZero(fAngle)) + { + const B2DPoint aCenter(0.5 * fTargetSizeX, 0.5 * fTargetSizeY); + + aTextureTransform *= basegfx::utils::createRotateAroundPoint(aCenter, fAngle); + } + + // add defined offsets after rotation + if(!fTools::equal(0.5, rOffset.getX()) || !fTools::equal(0.5, rOffset.getY())) + { + // use original target size + fTargetOffsetX += (rOffset.getX() - 0.5) * rTargetRange.getWidth(); + fTargetOffsetY += (rOffset.getY() - 0.5) * rTargetRange.getHeight(); + } + + // add object translate + aTextureTransform.translate(fTargetOffsetX, fTargetOffsetY); + + // prepare aspect for texture + const double fAspectRatio(fTargetSizeY == 0.0 ? 1.0 : (fTargetSizeX / fTargetSizeY)); + + return ODFGradientInfo(aTextureTransform, fAspectRatio, nSteps); + } + + /** Setup for rect & square gradient is exactly the same. Factored out + here. + */ + static ODFGradientInfo initRectGradientInfo( + const B2DRange& rTargetRange, + const B2DVector& rOffset, + sal_uInt32 nSteps, + double fBorder, + double fAngle, + bool bSquare) + { + B2DHomMatrix aTextureTransform; + + fAngle = -fAngle; + + double fTargetSizeX(rTargetRange.getWidth()); + double fTargetSizeY(rTargetRange.getHeight()); + double fTargetOffsetX(rTargetRange.getMinX()); + double fTargetOffsetY(rTargetRange.getMinY()); + + // add object expansion + if(bSquare) + { + const double fSquareWidth(std::max(fTargetSizeX, fTargetSizeY)); + + fTargetOffsetX -= (fSquareWidth - fTargetSizeX) / 2.0; + fTargetOffsetY -= (fSquareWidth - fTargetSizeY) / 2.0; + fTargetSizeX = fTargetSizeY = fSquareWidth; + } + + // add object expansion + const bool bAngleUsed(!fTools::equalZero(fAngle)); + + if(bAngleUsed) + { + const double fAbsCos(fabs(cos(fAngle))); + const double fAbsSin(fabs(sin(fAngle))); + const double fNewX(fTargetSizeX * fAbsCos + fTargetSizeY * fAbsSin); + const double fNewY(fTargetSizeY * fAbsCos + fTargetSizeX * fAbsSin); + + fTargetOffsetX -= (fNewX - fTargetSizeX) / 2.0; + fTargetOffsetY -= (fNewY - fTargetSizeY) / 2.0; + fTargetSizeX = fNewX; + fTargetSizeY = fNewY; + } + + const double fHalfBorder((1.0 - fBorder) * 0.5); + + aTextureTransform.scale(fHalfBorder, fHalfBorder); + aTextureTransform.translate(0.5, 0.5); + aTextureTransform.scale(fTargetSizeX, fTargetSizeY); + + // add texture rotate after scale to keep perpendicular angles + if(bAngleUsed) + { + const B2DPoint aCenter(0.5 * fTargetSizeX, 0.5 * fTargetSizeY); + + aTextureTransform *= basegfx::utils::createRotateAroundPoint(aCenter, fAngle); + } + + // add defined offsets after rotation + if(!fTools::equal(0.5, rOffset.getX()) || !fTools::equal(0.5, rOffset.getY())) + { + // use original target size + fTargetOffsetX += (rOffset.getX() - 0.5) * rTargetRange.getWidth(); + fTargetOffsetY += (rOffset.getY() - 0.5) * rTargetRange.getHeight(); + } + + // add object translate + aTextureTransform.translate(fTargetOffsetX, fTargetOffsetY); + + // prepare aspect for texture + const double fAspectRatio(fTargetSizeY == 0.0 ? 1.0 : (fTargetSizeX / fTargetSizeY)); + + return ODFGradientInfo(aTextureTransform, fAspectRatio, nSteps); + } + + namespace utils + { + /* Tooling method to extract data from given BGradient + to ColorStops, doing some corrections, partially based + on given SingleColor */ + void prepareColorStops( + const basegfx::BGradient& rGradient, + BColorStops& rColorStops, + BColor& rSingleColor) + { + if (rGradient.GetColorStops().isSingleColor(rSingleColor)) + { + // when single color, preserve value in rSingleColor + // and clear the ColorStops, done. + rColorStops.clear(); + return; + } + + const bool bAdaptStartEndIntensity(100 != rGradient.GetStartIntens() || 100 != rGradient.GetEndIntens()); + const bool bAdaptBorder(0 != rGradient.GetBorder()); + + if (!bAdaptStartEndIntensity && !bAdaptBorder) + { + // copy unchanged ColorStops & done + rColorStops = rGradient.GetColorStops(); + return; + } + + // prepare a copy to work on + basegfx::BGradient aWorkCopy(rGradient); + + if (bAdaptStartEndIntensity) + { + aWorkCopy.tryToApplyStartEndIntensity(); + + // this can again lead to single color (e.g. both zero, so + // all black), so check again for it + if (aWorkCopy.GetColorStops().isSingleColor(rSingleColor)) + { + rColorStops.clear(); + return; + } + } + + if (bAdaptBorder) + { + aWorkCopy.tryToApplyBorder(); + } + + // extract ColorStops, that's all we need here + rColorStops = aWorkCopy.GetColorStops(); + } + + /* Tooling method to synchronize the given ColorStops. + The intention is that a color GradientStops and an + alpha/transparence GradientStops gets synchronized + for export. */ + void synchronizeColorStops( + BColorStops& rColorStops, + BColorStops& rAlphaStops, + const BColor& rSingleColor, + const BColor& rSingleAlpha) + { + if (rColorStops.empty()) + { + if (rAlphaStops.empty()) + { + // no AlphaStops and no ColorStops + // create two-stop fallbacks for both + rColorStops = BColorStops { + BColorStop(0.0, rSingleColor), + BColorStop(1.0, rSingleColor) }; + rAlphaStops = BColorStops { + BColorStop(0.0, rSingleAlpha), + BColorStop(1.0, rSingleAlpha) }; + } + else + { + // AlphaStops but no ColorStops + // create fallback synched with existing AlphaStops + for (const auto& cand : rAlphaStops) + { + rColorStops.emplace_back(cand.getStopOffset(), rSingleColor); + } + } + + // preparations complete, we are done + return; + } + else if (rAlphaStops.empty()) + { + // ColorStops but no AlphaStops + // create fallback AlphaStops synched with existing ColorStops using SingleAlpha + for (const auto& cand : rColorStops) + { + rAlphaStops.emplace_back(cand.getStopOffset(), rSingleAlpha); + } + + // preparations complete, we are done + return; + } + + // here we have ColorStops and AlphaStops not empty. Check if we need to + // synchronize both or if they are already usable/in a synched state so + // that they have same count and same StopOffsets + bool bNeedToSyncronize(rColorStops.size() != rAlphaStops.size()); + + if (!bNeedToSyncronize) + { + // check for same StopOffsets + BColorStops::const_iterator aCurrColor(rColorStops.begin()); + BColorStops::const_iterator aCurrAlpha(rAlphaStops.begin()); + + while (!bNeedToSyncronize && + aCurrColor != rColorStops.end() && + aCurrAlpha != rAlphaStops.end()) + { + if (fTools::equal(aCurrColor->getStopOffset(), aCurrAlpha->getStopOffset())) + { + aCurrColor++; + aCurrAlpha++; + } + else + { + bNeedToSyncronize = true; + } + } + } + + if (bNeedToSyncronize) + { + // synchronize sizes & StopOffsets + BColorStops::const_iterator aCurrColor(rColorStops.begin()); + BColorStops::const_iterator aCurrAlpha(rAlphaStops.begin()); + BColorStops aNewColor; + BColorStops aNewAlpha; + BColorStops::BColorStopRange aColorStopRange; + BColorStops::BColorStopRange aAlphaStopRange; + bool bRealChange(false); + + do { + const bool bColor(aCurrColor != rColorStops.end()); + const bool bAlpha(aCurrAlpha != rAlphaStops.end()); + + if (bColor && bAlpha) + { + const double fColorOff(aCurrColor->getStopOffset()); + const double fAlphaOff(aCurrAlpha->getStopOffset()); + + if (fTools::less(fColorOff, fAlphaOff)) + { + // copy color, create alpha + aNewColor.emplace_back(fColorOff, aCurrColor->getStopColor()); + aNewAlpha.emplace_back(fColorOff, rAlphaStops.getInterpolatedBColor(fColorOff, 0, aAlphaStopRange)); + bRealChange = true; + aCurrColor++; + } + else if (fTools::more(fColorOff, fAlphaOff)) + { + // copy alpha, create color + aNewColor.emplace_back(fAlphaOff, rColorStops.getInterpolatedBColor(fAlphaOff, 0, aColorStopRange)); + aNewAlpha.emplace_back(fAlphaOff, aCurrAlpha->getStopColor()); + bRealChange = true; + aCurrAlpha++; + } + else + { + // equal: copy both, advance + aNewColor.emplace_back(fColorOff, aCurrColor->getStopColor()); + aNewAlpha.emplace_back(fAlphaOff, aCurrAlpha->getStopColor()); + aCurrColor++; + aCurrAlpha++; + } + } + else if (bColor) + { + const double fColorOff(aCurrColor->getStopOffset()); + aNewAlpha.emplace_back(fColorOff, rAlphaStops.getInterpolatedBColor(fColorOff, 0, aAlphaStopRange)); + aNewColor.emplace_back(fColorOff, aCurrColor->getStopColor()); + bRealChange = true; + aCurrColor++; + } + else if (bAlpha) + { + const double fAlphaOff(aCurrAlpha->getStopOffset()); + aNewColor.emplace_back(fAlphaOff, rColorStops.getInterpolatedBColor(fAlphaOff, 0, aColorStopRange)); + aNewAlpha.emplace_back(fAlphaOff, aCurrAlpha->getStopColor()); + bRealChange = true; + aCurrAlpha++; + } + else + { + // no more input, break do..while loop + break; + } + } + while(true); + + if (bRealChange) + { + // copy on 'real' change, that means data was added. + // This should always be the cease and should have been + // detected as such above, see bNeedToSyncronize + rColorStops = aNewColor; + rAlphaStops = aNewAlpha; // MCGR: tdf#155537 used wrong result here + } + } + } + + sal_uInt32 calculateNumberOfSteps( + sal_uInt32 nRequestedSteps, + const BColor& rStart, + const BColor& rEnd) + { + const sal_uInt32 nMaxSteps(sal_uInt32((rStart.getMaximumDistance(rEnd) * 127.5) + 0.5)); + + if (0 == nRequestedSteps) + { + nRequestedSteps = nMaxSteps; + } + + if(nRequestedSteps > nMaxSteps) + { + nRequestedSteps = nMaxSteps; + } + + return std::max(sal_uInt32(1), nRequestedSteps); + } + + ODFGradientInfo createLinearODFGradientInfo( + const B2DRange& rTargetArea, + sal_uInt32 nSteps, + double fBorder, + double fAngle) + { + return init1DGradientInfo( + rTargetArea, + nSteps, + fBorder, + fAngle, + false); + } + + ODFGradientInfo createAxialODFGradientInfo( + const B2DRange& rTargetArea, + sal_uInt32 nSteps, + double fBorder, + double fAngle) + { + return init1DGradientInfo( + rTargetArea, + nSteps, + fBorder, + fAngle, + true); + } + + ODFGradientInfo createRadialODFGradientInfo( + const B2DRange& rTargetArea, + const B2DVector& rOffset, + sal_uInt32 nSteps, + double fBorder) + { + return initEllipticalGradientInfo( + rTargetArea, + rOffset, + nSteps, + fBorder, + 0.0, + true); + } + + ODFGradientInfo createEllipticalODFGradientInfo( + const B2DRange& rTargetArea, + const B2DVector& rOffset, + sal_uInt32 nSteps, + double fBorder, + double fAngle) + { + return initEllipticalGradientInfo( + rTargetArea, + rOffset, + nSteps, + fBorder, + fAngle, + false); + } + + ODFGradientInfo createSquareODFGradientInfo( + const B2DRange& rTargetArea, + const B2DVector& rOffset, + sal_uInt32 nSteps, + double fBorder, + double fAngle) + { + return initRectGradientInfo( + rTargetArea, + rOffset, + nSteps, + fBorder, + fAngle, + true); + } + + ODFGradientInfo createRectangularODFGradientInfo( + const B2DRange& rTargetArea, + const B2DVector& rOffset, + sal_uInt32 nSteps, + double fBorder, + double fAngle) + { + return initRectGradientInfo( + rTargetArea, + rOffset, + nSteps, + fBorder, + fAngle, + false); + } + + double getLinearGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) + { + const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV); + + // Ignore X, this is not needed at all for Y-Oriented gradients + // if(aCoor.getX() < 0.0 || aCoor.getX() > 1.0) + // { + // return 0.0; + // } + + if(aCoor.getY() <= 0.0) + { + return 0.0; // start value for inside + } + + if(aCoor.getY() >= 1.0) + { + return 1.0; // end value for outside + } + + return aCoor.getY(); + } + + double getAxialGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) + { + const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV); + + // Ignore X, this is not needed at all for Y-Oriented gradients + //if(aCoor.getX() < 0.0 || aCoor.getX() > 1.0) + //{ + // return 0.0; + //} + + const double fAbsY(fabs(aCoor.getY())); + + if(fAbsY >= 1.0) + { + return 1.0; // use end value when outside in Y + } + + return fAbsY; + } + + double getRadialGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) + { + const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV); + + if(aCoor.getX() < -1.0 || aCoor.getX() > 1.0 || aCoor.getY() < -1.0 || aCoor.getY() > 1.0) + { + return 0.0; + } + + return 1.0 - std::hypot(aCoor.getX(), aCoor.getY()); + } + + double getEllipticalGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) + { + const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV); + + if(aCoor.getX() < -1.0 || aCoor.getX() > 1.0 || aCoor.getY() < -1.0 || aCoor.getY() > 1.0) + { + return 0.0; + } + + double fAspectRatio(rGradInfo.getAspectRatio()); + double t(1.0); + + // MCGR: Similar to getRectangularGradientAlpha (please + // see there) we need to use aspect ratio here. Due to + // initEllipticalGradientInfo using M_SQRT2 to make this + // gradient look 'nicer' this correction seems not 100% + // correct, but is close enough for now + if(fAspectRatio > 1.0) + { + t = 1.0 - std::hypot(aCoor.getX() / fAspectRatio, aCoor.getY()); + } + else if(fAspectRatio > 0.0) + { + t = 1.0 - std::hypot(aCoor.getX(), aCoor.getY() * fAspectRatio); + } + + return t; + } + + double getSquareGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) + { + const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV); + const double fAbsX(fabs(aCoor.getX())); + + if(fAbsX >= 1.0) + { + return 0.0; + } + + const double fAbsY(fabs(aCoor.getY())); + + if(fAbsY >= 1.0) + { + return 0.0; + } + + return 1.0 - std::max(fAbsX, fAbsY); + } + + double getRectangularGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) + { + const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV); + double fAbsX(fabs(aCoor.getX())); + + if(fAbsX >= 1.0) + { + return 0.0; + } + + double fAbsY(fabs(aCoor.getY())); + + if(fAbsY >= 1.0) + { + return 0.0; + } + + // MCGR: Visualizations using the texturing method for + // displaying gradients (getBackTextureTransform is + // involved) show wrong results for GradientElliptical + // and GradientRect, this can be best seen when using + // less steps, e.g. just four. This thus has influence + // on cppcanvas (slideshow) and 3D textures, so needs + // to be corrected. + // Missing is to use the aspect ratio of the object + // in this [-1, -1, 1, 1] unified coordinate space + // after getBackTextureTransform is applied. Optically + // in the larger direction of the texturing the color + // step distances are too big *because* we are in that + // unit range now. + // To correct that, a kind of 'limo stretching' needs to + // be applied, adding space around the center + // proportional to the aspect ratio, so the intuitive + // idea would be to do + // + // fAbsX' = ((fAspectRatio - 1) + fAbsX) / fAspectRatio + // + // which scales from the center. This does not work, and + // after some thoughts it's clear why: It's not the + // position that needs to be moved (this cannot be + // changed), but the position *before* that scale has + // to be determined to get the correct, shifted color + // for the already 'new' position. Thus, turn around + // the expression as + // + // fAbsX' * fAspectRatio = fAspectRatio - 1 + fAbsX + // fAbsX' * fAspectRatio - fAspectRatio + 1 = fAbsX + // fAbsX = (fAbsX' - 1) * fAspectRatio + 1 + // + // This works and can even be simply adapted for + // fAspectRatio < 1.0 aka vertical is bigger. + double fAspectRatio(rGradInfo.getAspectRatio()); + if(fAspectRatio > 1.0) + { + fAbsX = ((fAbsX - 1) * fAspectRatio) + 1; + } + else if(fAspectRatio > 0.0) + { + fAbsY = ((fAbsY - 1) / fAspectRatio) + 1; + } + + return 1.0 - std::max(fAbsX, fAbsY); + } + } // namespace utils +} // namespace basegfx + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |