/************************************************************** * * 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 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_basegfx.hxx" #include #include #include #include namespace basegfx { bool ODFGradientInfo::operator==(const ODFGradientInfo& rODFGradientInfo) const { return getTextureTransform() == rODFGradientInfo.getTextureTransform() && getAspectRatio() == rODFGradientInfo.getAspectRatio() && getSteps() == rODFGradientInfo.getSteps(); } 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. */ 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::tools::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. */ 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(sqrt((fTargetSizeX * fTargetSizeX) + (fTargetSizeY * fTargetSizeY))); fTargetOffsetX -= (fOriginalDiag - fTargetSizeX) / 2.0; fTargetOffsetY -= (fOriginalDiag - fTargetSizeY) / 2.0; fTargetSizeX = fOriginalDiag; fTargetSizeY = fOriginalDiag; } else { fTargetOffsetX -= (0.4142 / 2.0 ) * fTargetSizeX; fTargetOffsetY -= (0.4142 / 2.0 ) * fTargetSizeY; fTargetSizeX = 1.4142 * fTargetSizeX; fTargetSizeY = 1.4142 * 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::tools::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((0.0 != fTargetSizeY) ? fTargetSizeX / fTargetSizeY : 1.0); return ODFGradientInfo(aTextureTransform, fAspectRatio, nSteps); } /** Setup for rect & square gradient is exactly the same. Factored out here. */ 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::tools::createRotateAroundPoint(aCenter, fAngle); } // add defined offsets after rotation if(!fTools::equal(0.5, rOffset.getX()) || !fTools::equal(0.5, rOffset.getY())) { // use scaled target size fTargetOffsetX += (rOffset.getX() - 0.5) * fTargetSizeX; fTargetOffsetY += (rOffset.getY() - 0.5) * fTargetSizeY; } // add object translate aTextureTransform.translate(fTargetOffsetX, fTargetOffsetY); // prepare aspect for texture const double fAspectRatio((0.0 != fTargetSizeY) ? fTargetSizeX / fTargetSizeY : 1.0); return ODFGradientInfo(aTextureTransform, fAspectRatio, nSteps); } namespace tools { 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); const double t(clamp(aCoor.getY(), 0.0, 1.0)); const sal_uInt32 nSteps(rGradInfo.getSteps()); if(nSteps) { return floor(t * nSteps) / double(nSteps + 1L); } return t; } double getAxialGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) { const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV); const double t(clamp(fabs(aCoor.getY()), 0.0, 1.0)); const sal_uInt32 nSteps(rGradInfo.getSteps()); const double fInternalSteps((nSteps * 2) - 1); if(nSteps) { return floor(((t * fInternalSteps) + 1.0) / 2.0) / double(nSteps - 1L); } return t; } double getRadialGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) { const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV); const double fDist(clamp(aCoor.getX() * aCoor.getX() + aCoor.getY() * aCoor.getY(), 0.0, 1.0)); const double t(1.0 - sqrt(fDist)); const sal_uInt32 nSteps(rGradInfo.getSteps()); if(nSteps) { return floor(t * nSteps) / double(nSteps - 1L); } return t; } double getEllipticalGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) { return getRadialGradientAlpha(rUV, rGradInfo); // only matrix setup differs } double getSquareGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) { const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV); const double fAbsX(fabs(aCoor.getX())); const double fAbsY(fabs(aCoor.getY())); if(fTools::moreOrEqual(fAbsX, 1.0) || fTools::moreOrEqual(fAbsY, 1.0)) { return 0.0; } const double t(1.0 - std::max(fAbsX, fAbsY)); const sal_uInt32 nSteps(rGradInfo.getSteps()); if(nSteps) { return floor(t * nSteps) / double(nSteps - 1L); } return t; } double getRectangularGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) { return getSquareGradientAlpha(rUV, rGradInfo); // only matrix setup differs } } // namespace tools } // namespace basegfx // eof