diff --git a/basegfx/inc/basegfx/matrix/b2dhommatrixtools.hxx b/basegfx/inc/basegfx/matrix/b2dhommatrixtools.hxx index 0b200b812bed..54d961d40ac2 100644 --- a/basegfx/inc/basegfx/matrix/b2dhommatrixtools.hxx +++ b/basegfx/inc/basegfx/matrix/b2dhommatrixtools.hxx @@ -36,6 +36,8 @@ #include #include +namespace rtl { class OUString; } + /////////////////////////////////////////////////////////////////////////////// namespace basegfx @@ -79,6 +81,10 @@ namespace basegfx double getRotate() const { const_cast< DecomposedB2DHomMatrixContainer* >(this)->impCheckDecompose(); return mfRotate; } double getShearX() const { const_cast< DecomposedB2DHomMatrixContainer* >(this)->impCheckDecompose(); return mfShearX; } }; + + /// Returns a string with svg's "matrix(m00,m10,m01,m11,m02,m12)" representation + ::rtl::OUString exportToSvg( const B2DHomMatrix& rMatrix ); + } // end of namespace basegfx /////////////////////////////////////////////////////////////////////////////// diff --git a/basegfx/inc/basegfx/numeric/ftools.hxx b/basegfx/inc/basegfx/numeric/ftools.hxx index 5003ede0c4cf..6cecbecc1f9f 100644 --- a/basegfx/inc/basegfx/numeric/ftools.hxx +++ b/basegfx/inc/basegfx/numeric/ftools.hxx @@ -112,7 +112,7 @@ namespace basegfx /** clamp given value against given minimum and maximum values */ - template const T& clamp(const T& value, const T& minimum, const T& maximum) + template inline const T& clamp(const T& value, const T& minimum, const T& maximum) { if(value < minimum) { diff --git a/basegfx/inc/basegfx/tools/gradienttools.hxx b/basegfx/inc/basegfx/tools/gradienttools.hxx index 0c7f2ab2c060..dbcc5a3221f0 100644 --- a/basegfx/inc/basegfx/tools/gradienttools.hxx +++ b/basegfx/inc/basegfx/tools/gradienttools.hxx @@ -37,6 +37,9 @@ #include #include +#include +#include + namespace basegfx { /** Gradient definition as used in ODF 1.2 @@ -78,6 +81,8 @@ namespace basegfx { /** Create matrix for ODF's linear gradient definition + Note that odf linear gradients are varying in y direction. + @param o_rGradientInfo Receives the calculated texture transformation matrix (for use with standard [0,1]x[0,1] texture coordinates) @@ -109,7 +114,7 @@ namespace basegfx @param rUV Current uv coordinate. Values outside [0,1] will be - clamped. + clamped. Assumes gradient color varies along the y axis. @param rGradInfo Gradient info, for transformation and number of steps @@ -129,6 +134,14 @@ namespace basegfx /** Create matrix for ODF's axial gradient definition + Note that odf axial gradients are varying in y + direction. Note further that you can map the axial + gradient to a linear gradient (in case you want or need to + avoid an extra gradient renderer), by using + createLinearODFGradientInfo() instead, shifting the + resulting texture transformation by 0.5 to the top and + appending the same stop colors again, but mirrored. + @param o_rGradientInfo Receives the calculated texture transformation matrix (for use with standard [0,1]x[0,1] texture coordinates) @@ -160,7 +173,7 @@ namespace basegfx @param rUV Current uv coordinate. Values outside [0,1] will be - clamped. + clamped. Assumes gradient color varies along the y axis. @param rGradInfo Gradient info, for transformation and number of steps @@ -394,7 +407,6 @@ namespace basegfx { return getSquareGradientAlpha(rUV, rGradInfo); // only matrix setup differs } - } } diff --git a/basegfx/inc/basegfx/tools/keystoplerp.hxx b/basegfx/inc/basegfx/tools/keystoplerp.hxx new file mode 100644 index 000000000000..a54b3485b1a1 --- /dev/null +++ b/basegfx/inc/basegfx/tools/keystoplerp.hxx @@ -0,0 +1,100 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: canvastools.hxx,v $ + * $Revision: 1.10 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _BGFX_TOOLS_KEYSTOPLERP_HXX +#define _BGFX_TOOLS_KEYSTOPLERP_HXX + +#include +#include + +namespace com{ namespace sun{ namespace star{ namespace uno { + template class Sequence; +}}}} + +namespace basegfx +{ + namespace tools + { + /** Lerp in a vector of key stops + + This class holds a key stop vector and provides the + functionality to lerp inside it. Useful e.g. for + multi-stop gradients, or the SMIL key time activity. + + For those, given a global [0,1] lerp alpha, one need to + find the suitable bucket index from key stop vector, and + then calculate the relative alpha between the two buckets + found. + */ + class KeyStopLerp + { + public: + typedef std::pair ResultType; + + /** Create lerper with given vector of stops + + @param rKeyStops + + Vector of stops, must contain at least two elements + (though preferrably more, otherwise you probably don't + need key stop lerping in the first place). All + elements must be of monotonically increasing value. + */ + explicit KeyStopLerp( const std::vector& rKeyStops ); + + /** Create lerper with given sequence of stops + + @param rKeyStops + + Sequence of stops, must contain at least two elements + (though preferrably more, otherwise you probably don't + need key stop lerping in the first place). All + elements must be of monotonically increasing value. + */ + explicit KeyStopLerp( const ::com::sun::star::uno::Sequence& rKeyStops ); + + /** Find two nearest bucket index & interpolate + + @param fAlpha + Find bucket index i, with keyStops[i] < fAlpha <= + keyStops[i+1]. Return new alpha value in [0,1), + proportional to fAlpha's position between keyStops[i] + and keyStops[i+1] + */ + ResultType lerp(double fAlpha) const; + + private: + std::vector maKeyStops; + mutable std::ptrdiff_t mnLastIndex; + }; + } +} + +#endif diff --git a/basegfx/inc/basegfx/tools/lerp.hxx b/basegfx/inc/basegfx/tools/lerp.hxx new file mode 100644 index 000000000000..590ef34c2009 --- /dev/null +++ b/basegfx/inc/basegfx/tools/lerp.hxx @@ -0,0 +1,60 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: lerp.hxx,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _BGFX_TOOLS_LERP_HXX +#define _BGFX_TOOLS_LERP_HXX + +#include + +namespace basegfx +{ + namespace tools + { + /** Generic linear interpolator + + @tpl ValueType + Must have operator+ and operator* defined, and should + have value semantics. + + @param t + As usual, t must be in the [0,1] range + */ + template< typename ValueType > ValueType lerp( const ValueType& rFrom, + const ValueType& rTo, + double t ) + { + // This is only to suppress a double->int warning. All other + // types should be okay here. + return static_cast( (1.0-t)*rFrom + t*rTo ); + } + } +} + +#endif /* _BGFX_TOOLS_LERP_HXX */ diff --git a/basegfx/prj/d.lst b/basegfx/prj/d.lst index a58cd33e4f9c..170796920945 100644 --- a/basegfx/prj/d.lst +++ b/basegfx/prj/d.lst @@ -89,6 +89,8 @@ mkdir: %_DEST%\inc%_EXT%\basegfx\tuple mkdir: %_DEST%\inc%_EXT%\basegfx\tools ..\inc\basegfx\tools\canvastools.hxx %_DEST%\inc%_EXT%\basegfx\tools\canvastools.hxx +..\inc\basegfx\tools\keystoplerp.hxx %_DEST%\inc%_EXT%\basegfx\tools\keystoplerp.hxx +..\inc\basegfx\tools\lerp.hxx %_DEST%\inc%_EXT%\basegfx\tools\lerp.hxx ..\inc\basegfx\tools\unopolypolygon.hxx %_DEST%\inc%_EXT%\basegfx\tools\unopolypolygon.hxx ..\inc\basegfx\tools\rectcliptools.hxx %_DEST%\inc%_EXT%\basegfx\tools\rectcliptools.hxx ..\inc\basegfx\tools\tools.hxx %_DEST%\inc%_EXT%\basegfx\tools\tools.hxx diff --git a/basegfx/source/matrix/b2dhommatrixtools.cxx b/basegfx/source/matrix/b2dhommatrixtools.cxx index 59a1ff432fc7..366a08a1d202 100644 --- a/basegfx/source/matrix/b2dhommatrixtools.cxx +++ b/basegfx/source/matrix/b2dhommatrixtools.cxx @@ -33,11 +33,39 @@ #include "precompiled_basegfx.hxx" #include +#include +#include /////////////////////////////////////////////////////////////////////////////// namespace basegfx { + ::rtl::OUString exportToSvg( const B2DHomMatrix& rMatrix ) + { + rtl::OUStringBuffer aStrBuf; + aStrBuf.appendAscii("matrix("); + + aStrBuf.append(rMatrix.get(0,0)); + aStrBuf.appendAscii(", "); + + aStrBuf.append(rMatrix.get(1,0)); + aStrBuf.appendAscii(", "); + + aStrBuf.append(rMatrix.get(0,1)); + aStrBuf.appendAscii(", "); + + aStrBuf.append(rMatrix.get(1,1)); + aStrBuf.appendAscii(", "); + + aStrBuf.append(rMatrix.get(0,2)); + aStrBuf.appendAscii(", "); + + aStrBuf.append(rMatrix.get(1,2)); + aStrBuf.appendAscii(")"); + + return aStrBuf.makeStringAndClear(); + } + } // end of namespace basegfx /////////////////////////////////////////////////////////////////////////////// diff --git a/basegfx/source/tools/gradienttools.cxx b/basegfx/source/tools/gradienttools.cxx index 9e78039cd590..337f9bd8e52b 100644 --- a/basegfx/source/tools/gradienttools.cxx +++ b/basegfx/source/tools/gradienttools.cxx @@ -52,6 +52,8 @@ namespace basegfx o_rGradientInfo.maBackTextureTransform.identity(); o_rGradientInfo.mnSteps = nSteps; + fAngle = -fAngle; + double fTargetSizeX(rTargetRange.getWidth()); double fTargetSizeY(rTargetRange.getHeight()); double fTargetOffsetX(rTargetRange.getMinX()); @@ -70,7 +72,23 @@ namespace basegfx fTargetSizeY = fNewY; } - // add object scale before rotate + double fSizeWithoutBorder=0; + double fTranslateY=0; + if( bAxial ) + { + fSizeWithoutBorder = (1.0 - fBorder) * 0.5; + fTranslateY = 0.5; + } + else + { + fSizeWithoutBorder = 1.0 - fBorder; + fTranslateY = fBorder; + } + + if(!fTools::equal(fSizeWithoutBorder, 0.0)) + o_rGradientInfo.maTextureTransform.scale(1.0, fSizeWithoutBorder); + + o_rGradientInfo.maTextureTransform.translate(0.0, fTranslateY); o_rGradientInfo.maTextureTransform.scale(fTargetSizeX, fTargetSizeY); // add texture rotate after scale to keep perpendicular angles @@ -90,24 +108,9 @@ namespace basegfx // prepare aspect for texture o_rGradientInfo.mfAspectRatio = (0.0 != fTargetSizeY) ? fTargetSizeX / fTargetSizeY : 1.0; - // build transform from u,v to [0.0 .. 1.0]. As base, use inverse texture transform + // build transform from u,v to [0.0 .. 1.0]. o_rGradientInfo.maBackTextureTransform = o_rGradientInfo.maTextureTransform; o_rGradientInfo.maBackTextureTransform.invert(); - - double fSizeWithoutBorder=0; - if( bAxial ) - { - fSizeWithoutBorder = (1.0 - fBorder) * 0.5; - o_rGradientInfo.maBackTextureTransform.translate(0.0, -0.5); - } - else - { - fSizeWithoutBorder = 1.0 - fBorder; - o_rGradientInfo.maBackTextureTransform.translate(0.0, -fBorder); - } - - if(!fTools::equal(fSizeWithoutBorder, 0.0)) - o_rGradientInfo.maBackTextureTransform.scale(1.0, 1.0 / fSizeWithoutBorder); } /** Most of the setup for radial & ellipsoidal gradient is the same, @@ -125,6 +128,8 @@ namespace basegfx o_rGradientInfo.maBackTextureTransform.identity(); o_rGradientInfo.mnSteps = nSteps; + fAngle = -fAngle; + double fTargetSizeX(rTargetRange.getWidth()); double fTargetSizeY(rTargetRange.getHeight()); double fTargetOffsetX(rTargetRange.getMinX()); @@ -147,7 +152,11 @@ namespace basegfx fTargetSizeY = 1.4142 * fTargetSizeY; } - // add object scale before rotate + const double fHalfBorder((1.0 - fBorder) * 0.5); + if(!fTools::equal(fHalfBorder, 0.0)) + o_rGradientInfo.maTextureTransform.scale(fHalfBorder, fHalfBorder); + + o_rGradientInfo.maTextureTransform.translate(0.5, 0.5); o_rGradientInfo.maTextureTransform.scale(fTargetSizeX, fTargetSizeY); if( !bCircular ) @@ -155,9 +164,8 @@ namespace basegfx // add texture rotate after scale to keep perpendicular angles if(0.0 != fAngle) { - B2DPoint aCenter(0.5, 0.5); - aCenter *= o_rGradientInfo.maTextureTransform; - + const B2DPoint aCenter(0.5*fTargetSizeX, + 0.5*fTargetSizeY); o_rGradientInfo.maTextureTransform.translate(-aCenter.getX(), -aCenter.getY()); o_rGradientInfo.maTextureTransform.rotate(fAngle); o_rGradientInfo.maTextureTransform.translate(aCenter.getX(), aCenter.getY()); @@ -178,17 +186,9 @@ namespace basegfx // prepare aspect for texture o_rGradientInfo.mfAspectRatio = (0.0 != fTargetSizeY) ? fTargetSizeX / fTargetSizeY : 1.0; - // build transform from u,v to [0.0 .. 1.0]. As base, use inverse texture transform + // build transform from u,v to [0.0 .. 1.0]. o_rGradientInfo.maBackTextureTransform = o_rGradientInfo.maTextureTransform; o_rGradientInfo.maBackTextureTransform.invert(); - o_rGradientInfo.maBackTextureTransform.translate(-0.5, -0.5); - const double fHalfBorder((1.0 - fBorder) * 0.5); - - if(!fTools::equal(fHalfBorder, 0.0)) - { - const double fFactor(1.0 / fHalfBorder); - o_rGradientInfo.maBackTextureTransform.scale(fFactor, fFactor); - } } /** Setup for rect & square gradient is exactly the same. Factored out @@ -205,6 +205,8 @@ namespace basegfx o_rGradientInfo.maBackTextureTransform.identity(); o_rGradientInfo.mnSteps = nSteps; + fAngle = -fAngle; + double fTargetSizeX(rTargetRange.getWidth()); double fTargetSizeY(rTargetRange.getHeight()); double fTargetOffsetX(rTargetRange.getMinX()); @@ -223,15 +225,18 @@ namespace basegfx fTargetSizeY = fNewY; } - // add object scale before rotate + const double fHalfBorder((1.0 - fBorder) * 0.5); + if(!fTools::equal(fHalfBorder, 0.0)) + o_rGradientInfo.maTextureTransform.scale(fHalfBorder, fHalfBorder); + + o_rGradientInfo.maTextureTransform.translate(0.5, 0.5); o_rGradientInfo.maTextureTransform.scale(fTargetSizeX, fTargetSizeY); // add texture rotate after scale to keep perpendicular angles if(0.0 != fAngle) { - B2DPoint aCenter(0.5, 0.5); - aCenter *= o_rGradientInfo.maTextureTransform; - + const B2DPoint aCenter(0.5*fTargetSizeX, + 0.5*fTargetSizeY); o_rGradientInfo.maTextureTransform.translate(-aCenter.getX(), -aCenter.getY()); o_rGradientInfo.maTextureTransform.rotate(fAngle); o_rGradientInfo.maTextureTransform.translate(aCenter.getX(), aCenter.getY()); @@ -254,14 +259,6 @@ namespace basegfx // build transform from u,v to [0.0 .. 1.0]. As base, use inverse texture transform o_rGradientInfo.maBackTextureTransform = o_rGradientInfo.maTextureTransform; o_rGradientInfo.maBackTextureTransform.invert(); - o_rGradientInfo.maBackTextureTransform.translate(-0.5, -0.5); - const double fHalfBorder((1.0 - fBorder) * 0.5); - - if(!fTools::equal(fHalfBorder, 0.0)) - { - const double fFactor(1.0 / fHalfBorder); - o_rGradientInfo.maBackTextureTransform.scale(fFactor, fFactor); - } } namespace tools diff --git a/basegfx/source/tools/keystoplerp.cxx b/basegfx/source/tools/keystoplerp.cxx new file mode 100644 index 000000000000..99c74a350a69 --- /dev/null +++ b/basegfx/source/tools/keystoplerp.cxx @@ -0,0 +1,104 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: canvastools.hxx,v $ + * $Revision: 1.10 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "basegfx/tools/keystoplerp.hxx" +#include + +#include + +static void validateInput(const std::vector& rKeyStops) +{ + (void)rKeyStops; +#ifdef DBG_UTIL + OSL_ENSURE( rKeyStops.size() > 1, + "KeyStopLerp::KeyStopLerp(): key stop vector must have two entries or more" ); + + // rKeyStops must be sorted in ascending order + for( ::std::size_t i=1, len=rKeyStops.size(); i rKeyStops[i] ) + OSL_ENSURE( false, + "KeyStopLerp::KeyStopLerp(): time vector is not sorted in ascending order!" ); + } +#endif +} + +namespace basegfx +{ + namespace tools + { + KeyStopLerp::KeyStopLerp( const std::vector& rKeyStops ) : + maKeyStops(rKeyStops), + mnLastIndex(0) + { + validateInput(maKeyStops); + } + + KeyStopLerp::KeyStopLerp( const ::com::sun::star::uno::Sequence& rKeyStops ) : + maKeyStops(rKeyStops.getLength()), + mnLastIndex(0) + { + std::copy( rKeyStops.getConstArray(), + rKeyStops.getConstArray()+rKeyStops.getLength(), + maKeyStops.begin() ); + validateInput(maKeyStops); + } + + KeyStopLerp::ResultType KeyStopLerp::lerp(double fAlpha) const + { + // cached value still okay? + if( maKeyStops.at(mnLastIndex) < fAlpha || + maKeyStops.at(mnLastIndex+1) >= fAlpha ) + { + // nope, find new index + mnLastIndex = std::min( + maKeyStops.size()-2, + // range is ensured by max below + std::max( + 0, + std::distance( maKeyStops.begin(), + std::lower_bound( maKeyStops.begin(), + maKeyStops.end(), + fAlpha )) - 1 )); + } + + // lerp between stop and stop+1 + const double fRawLerp= + (fAlpha-maKeyStops.at(mnLastIndex)) / + (maKeyStops.at(mnLastIndex+1) - maKeyStops.at(mnLastIndex)); + + // clamp to permissible range (input fAlpha might be + // everything) + return ResultType( + mnLastIndex, + clamp(fRawLerp,0.0,1.0)); + } + } +} diff --git a/basegfx/source/tools/makefile.mk b/basegfx/source/tools/makefile.mk index 1bede8b22d88..d55bbae12f8f 100755 --- a/basegfx/source/tools/makefile.mk +++ b/basegfx/source/tools/makefile.mk @@ -44,6 +44,7 @@ ENABLE_EXCEPTIONS=TRUE SLOFILES= $(SLO)$/canvastools.obj \ $(SLO)$/gradienttools.obj \ $(SLO)$/debugplotter.obj \ + $(SLO)$/keystoplerp.obj \ $(SLO)$/liangbarsky.obj \ $(SLO)$/tools.obj \ $(SLO)$/unopolypolygon.obj diff --git a/basegfx/test/basegfxtools.cxx b/basegfx/test/basegfxtools.cxx new file mode 100644 index 000000000000..6ace65c5894d --- /dev/null +++ b/basegfx/test/basegfxtools.cxx @@ -0,0 +1,119 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: basegfx2d.cxx,v $ + * $Revision: 1.14 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basegfx.hxx" +// autogenerated file with codegen.pl + +#include + +#include +#include + +#include + +using namespace ::basegfx; +using namespace ::boost::tuples; + +namespace basegfxtools +{ + +class KeyStopLerpTest : public CppUnit::TestFixture +{ + tools::KeyStopLerp maKeyStops; + + static std::vector getTestVector() + { + std::vector aStops(3); + aStops[0] = 0.1; + aStops[1] = 0.5; + aStops[2] = 0.9; + return aStops; + } + +public: + KeyStopLerpTest() : + maKeyStops(getTestVector()) + {} + + void setUp() + {} + + void tearDown() + {} + + void test() + { + double fAlpha; + std::ptrdiff_t nIndex; + + tie(nIndex,fAlpha) = maKeyStops.lerp(-1.0); + CPPUNIT_ASSERT_MESSAGE("-1.0", nIndex==0 && fAlpha==0.0); + + tie(nIndex,fAlpha) = maKeyStops.lerp(0.1); + CPPUNIT_ASSERT_MESSAGE("0.1", nIndex==0 && fAlpha==0.0); + + tie(nIndex,fAlpha) = maKeyStops.lerp(0.3); + CPPUNIT_ASSERT_MESSAGE("0.3", nIndex==0 && fTools::equal(fAlpha,0.5)); + + tie(nIndex,fAlpha) = maKeyStops.lerp(0.5); + CPPUNIT_ASSERT_MESSAGE("0.5", nIndex==0 && fTools::equal(fAlpha,1.0)); + + tie(nIndex,fAlpha) = maKeyStops.lerp(0.51); + CPPUNIT_ASSERT_MESSAGE("0.51", nIndex==1 && fTools::equal(fAlpha,0.025)); + + tie(nIndex,fAlpha) = maKeyStops.lerp(0.9); + CPPUNIT_ASSERT_MESSAGE("0.51", nIndex==1 && fTools::equal(fAlpha,1.0)); + + tie(nIndex,fAlpha) = maKeyStops.lerp(1.0); + CPPUNIT_ASSERT_MESSAGE("0.51", nIndex==1 && fAlpha==1.0); + } + + // Change the following lines only, if you add, remove or rename + // member functions of the current class, + // because these macros are need by auto register mechanism. + + CPPUNIT_TEST_SUITE(KeyStopLerpTest); + CPPUNIT_TEST(test); + CPPUNIT_TEST_SUITE_END(); +}; + +// ----------------------------------------------------------------------------- +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfxtools::KeyStopLerpTest, "basegfxtools"); +} // namespace basegfxtools + + +// ----------------------------------------------------------------------------- + +// this macro creates an empty function, which will called by the RegisterAllFunctions() +// to let the user the possibility to also register some functions by hand. +// NOADDITIONAL; + diff --git a/basegfx/test/makefile.mk b/basegfx/test/makefile.mk index 1bd4d59b98d5..bc44daffd27c 100644 --- a/basegfx/test/makefile.mk +++ b/basegfx/test/makefile.mk @@ -46,6 +46,7 @@ SHL1OBJS= \ $(SLO)$/basegfx1d.obj \ $(SLO)$/basegfx2d.obj \ $(SLO)$/basegfx3d.obj \ + $(SLO)$/basegfxtools.obj \ $(SLO)$/testtools.obj # linking statically against basegfx parts diff --git a/canvas/inc/canvas/base/graphicdevicebase.hxx b/canvas/inc/canvas/base/graphicdevicebase.hxx index d0cd621f65a2..1105fea55eab 100644 --- a/canvas/inc/canvas/base/graphicdevicebase.hxx +++ b/canvas/inc/canvas/base/graphicdevicebase.hxx @@ -33,11 +33,11 @@ #include #include +#include #include #include #include #include -#include #include #include @@ -50,8 +50,7 @@ namespace canvas /** Helper template base class for XGraphicDevice implementations. This base class provides partial implementations of the - XGraphicDevice-related interface, such as - XParametricPolyPolygon2DFactory and XColorSpace. + XGraphicDevice-related interface, such as XColorSpace. This template basically interposes itself between the full interface you implement (i.e. not restricted to XGraphicDevice @@ -249,7 +248,7 @@ namespace canvas return maDeviceHelper.createVolatileAlphaBitmap( this, size ); } - virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XParametricPolyPolygon2DFactory > SAL_CALL getParametricPolyPolygonFactory( ) throw (::com::sun::star::uno::RuntimeException) + virtual ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > SAL_CALL getParametricPolyPolygonFactory( ) throw (::com::sun::star::uno::RuntimeException) { return this; } @@ -268,79 +267,26 @@ namespace canvas return maDeviceHelper.enterFullScreenMode( bEnter ); } - // XParametricPolyPolygon2DFactory - virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XParametricPolyPolygon2D > SAL_CALL createLinearHorizontalGradient( const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< double > >& colors, const ::com::sun::star::uno::Sequence< double >& stops ) throw (::com::sun::star::lang::IllegalArgumentException, - ::com::sun::star::uno::RuntimeException) + // XMultiServiceFactory + virtual ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > SAL_CALL createInstance( const ::rtl::OUString& aServiceSpecifier ) throw (::com::sun::star::uno::Exception, ::com::sun::star::uno::RuntimeException) { return ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XParametricPolyPolygon2D >( - ParametricPolyPolygon::createLinearHorizontalGradient( this, - colors, - stops ) ); + ParametricPolyPolygon::create(this, + aServiceSpecifier, + ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any >())); } - virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XParametricPolyPolygon2D > SAL_CALL createAxialHorizontalGradient( const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< double > >& colors, const ::com::sun::star::uno::Sequence< double >& stops ) throw (::com::sun::star::lang::IllegalArgumentException, - ::com::sun::star::uno::RuntimeException) + virtual ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > SAL_CALL createInstanceWithArguments( const ::rtl::OUString& aServiceSpecifier, const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any >& Arguments ) throw (::com::sun::star::uno::Exception, ::com::sun::star::uno::RuntimeException) { return ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XParametricPolyPolygon2D >( - ParametricPolyPolygon::createAxialHorizontalGradient( this, - colors, - stops ) ); + ParametricPolyPolygon::create(this, + aServiceSpecifier, + Arguments)); } - virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XParametricPolyPolygon2D > SAL_CALL createEllipticalGradient( const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< double > >& colors, const ::com::sun::star::uno::Sequence< double >& stops, const ::com::sun::star::geometry::RealRectangle2D& boundRect ) throw (::com::sun::star::lang::IllegalArgumentException, - ::com::sun::star::uno::RuntimeException) + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getAvailableServiceNames( ) throw (::com::sun::star::uno::RuntimeException) { - return ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XParametricPolyPolygon2D >( - ParametricPolyPolygon::createEllipticalGradient( this, - colors, - stops, - boundRect ) ); - } - - virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XParametricPolyPolygon2D > SAL_CALL createRectangularGradient( const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< double > >& colors, const ::com::sun::star::uno::Sequence< double >& stops, const ::com::sun::star::geometry::RealRectangle2D& boundRect ) throw (::com::sun::star::lang::IllegalArgumentException, - ::com::sun::star::uno::RuntimeException) - { - return ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XParametricPolyPolygon2D >( - ParametricPolyPolygon::createRectangularGradient( this, - colors, - stops, - boundRect ) ); - } - - virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XParametricPolyPolygon2D > SAL_CALL createVerticalLinesHatch( const ::com::sun::star::uno::Sequence< double >& /*leftColor*/, - const ::com::sun::star::uno::Sequence< double >& /*rightColor*/ ) throw (::com::sun::star::lang::IllegalArgumentException, - ::com::sun::star::uno::RuntimeException) - { - // TODO(F1): hatch factory NYI - return ::com::sun::star::uno::Reference< - ::com::sun::star::rendering::XParametricPolyPolygon2D >(); - } - - virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XParametricPolyPolygon2D > SAL_CALL createOrthogonalLinesHatch( const ::com::sun::star::uno::Sequence< double >& /*leftTopColor*/, - const ::com::sun::star::uno::Sequence< double >& /*rightBottomColor*/ ) throw (::com::sun::star::lang::IllegalArgumentException, - ::com::sun::star::uno::RuntimeException) - { - // TODO(F1): hatch factory NYI - return ::com::sun::star::uno::Reference< - ::com::sun::star::rendering::XParametricPolyPolygon2D >(); - } - - virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XParametricPolyPolygon2D > SAL_CALL createThreeCrossingLinesHatch( const ::com::sun::star::uno::Sequence< double >& /*startColor*/, - const ::com::sun::star::uno::Sequence< double >& /*endColor*/ ) throw (::com::sun::star::lang::IllegalArgumentException, - ::com::sun::star::uno::RuntimeException) - { - // TODO(F1): hatch factory NYI - return ::com::sun::star::uno::Reference< - ::com::sun::star::rendering::XParametricPolyPolygon2D >(); - } - - virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XParametricPolyPolygon2D > SAL_CALL createFourCrossingLinesHatch( const ::com::sun::star::uno::Sequence< double >& /*startColor*/, - const ::com::sun::star::uno::Sequence< double >& /*endColor*/ ) throw (::com::sun::star::lang::IllegalArgumentException, - ::com::sun::star::uno::RuntimeException) - { - // TODO(F1): hatch factory NYI - return ::com::sun::star::uno::Reference< - ::com::sun::star::rendering::XParametricPolyPolygon2D >(); + return ParametricPolyPolygon::getAvailableServiceNames(); } diff --git a/canvas/inc/canvas/canvastools.hxx b/canvas/inc/canvas/canvastools.hxx index 3942ab2726ef..345bcd9187ae 100644 --- a/canvas/inc/canvas/canvastools.hxx +++ b/canvas/inc/canvas/canvastools.hxx @@ -417,28 +417,6 @@ namespace canvas */ ::basegfx::B2IRange spritePixelAreaFromB2DRange( const ::basegfx::B2DRange& rRange ); - /** This method clamps the given value to the specified range - - @param val - The value to clamp - - @param minVal - The minimal value val is allowed to attain - - @param maxVal - The maximal value val is allowed to attain - - @return the clamped value - */ - template< typename T > T clamp( T val, - T minVal, - T maxVal ) - { - return ::std::max( minVal, - ::std::min( maxVal, - val ) ); - } - /** Retrieve various internal properties of the actual canvas implementation. This method retrieves a bunch of internal, implementation- diff --git a/canvas/inc/canvas/parametricpolypolygon.hxx b/canvas/inc/canvas/parametricpolypolygon.hxx index 9d0883192094..b93f1c27a682 100644 --- a/canvas/inc/canvas/parametricpolypolygon.hxx +++ b/canvas/inc/canvas/parametricpolypolygon.hxx @@ -33,7 +33,7 @@ #include #include -#include +#include #include #include #include @@ -62,7 +62,6 @@ namespace canvas enum GradientType { GRADIENT_LINEAR, - GRADIENT_AXIAL, GRADIENT_ELLIPTICAL, GRADIENT_RECTANGULAR }; @@ -103,24 +102,11 @@ namespace canvas const GradientType meType; }; - static ParametricPolyPolygon* createLinearHorizontalGradient( const ::com::sun::star::uno::Reference< - ::com::sun::star::rendering::XGraphicDevice >& rDevice, - const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< double > >& colors, - const ::com::sun::star::uno::Sequence< double >& stops ); - static ParametricPolyPolygon* createAxialHorizontalGradient( const ::com::sun::star::uno::Reference< - ::com::sun::star::rendering::XGraphicDevice >& rDevice, - const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< double > >& colors, - const ::com::sun::star::uno::Sequence< double >& stops ); - static ParametricPolyPolygon* createEllipticalGradient( const ::com::sun::star::uno::Reference< - ::com::sun::star::rendering::XGraphicDevice >& rDevice, - const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< double > >& colors, - const ::com::sun::star::uno::Sequence< double >& stops, - const ::com::sun::star::geometry::RealRectangle2D& boundRect ); - static ParametricPolyPolygon* createRectangularGradient( const ::com::sun::star::uno::Reference< - ::com::sun::star::rendering::XGraphicDevice >& rDevice, - const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< double > >& colors, - const ::com::sun::star::uno::Sequence< double >& stops, - const ::com::sun::star::geometry::RealRectangle2D& boundRect ); + static ::com::sun::star::uno::Sequence< ::rtl::OUString > getAvailableServiceNames(); + static ParametricPolyPolygon* create( + const ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XGraphicDevice >& rDevice, + const ::rtl::OUString& rServiceName, + const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any >& rArgs ); /// Dispose all internal references virtual void SAL_CALL disposing(); @@ -143,6 +129,20 @@ namespace canvas ~ParametricPolyPolygon(); // we're a ref-counted UNO class. _We_ destroy ourselves. private: + static ParametricPolyPolygon* createLinearHorizontalGradient( const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XGraphicDevice >& rDevice, + const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< double > >& colors, + const ::com::sun::star::uno::Sequence< double >& stops ); + static ParametricPolyPolygon* createEllipticalGradient( const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XGraphicDevice >& rDevice, + const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< double > >& colors, + const ::com::sun::star::uno::Sequence< double >& stops, + double fAspect ); + static ParametricPolyPolygon* createRectangularGradient( const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XGraphicDevice >& rDevice, + const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< double > >& colors, + const ::com::sun::star::uno::Sequence< double >& stops, + double fAspect ); /// Private, because objects can only be created from the static factories ParametricPolyPolygon( const ::com::sun::star::uno::Reference< diff --git a/canvas/source/cairo/cairo_canvas.hxx b/canvas/source/cairo/cairo_canvas.hxx index de2b9f8693a1..8fa56b5f4187 100644 --- a/canvas/source/cairo/cairo_canvas.hxx +++ b/canvas/source/cairo/cairo_canvas.hxx @@ -44,7 +44,6 @@ #include #include #include -#include #include #include @@ -68,7 +67,7 @@ namespace cairocanvas typedef ::cppu::WeakComponentImplHelper7< ::com::sun::star::rendering::XBitmapCanvas, ::com::sun::star::rendering::XIntegerBitmap, ::com::sun::star::rendering::XGraphicDevice, - ::com::sun::star::rendering::XParametricPolyPolygon2DFactory, + ::com::sun::star::lang::XMultiServiceFactory, ::com::sun::star::util::XUpdatable, ::com::sun::star::beans::XPropertySet, ::com::sun::star::lang::XServiceName > GraphicDeviceBase_Base; diff --git a/canvas/source/cairo/cairo_canvashelper.cxx b/canvas/source/cairo/cairo_canvashelper.cxx index 9cf2dd978759..942e5edb06a6 100644 --- a/canvas/source/cairo/cairo_canvashelper.cxx +++ b/canvas/source/cairo/cairo_canvashelper.cxx @@ -56,6 +56,8 @@ #include #include #include +#include +#include #include #include @@ -73,6 +75,7 @@ #include "cairo_canvashelper.hxx" #include "cairo_canvasbitmap.hxx" +#include #include using namespace ::cairo; @@ -122,9 +125,29 @@ namespace cairocanvas mpCairo = pSurface->getCairo(); } + static void setColor( Cairo* pCairo, + const uno::Sequence& rColor ) + { + if( rColor.getLength() > 3 ) + { + const double alpha = rColor[3]; + + cairo_set_source_rgba( pCairo, + alpha*rColor[0], + alpha*rColor[1], + alpha*rColor[2], + alpha ); + } + else if( rColor.getLength() == 3 ) + cairo_set_source_rgb( pCairo, + rColor[0], + rColor[1], + rColor[2] ); + } + void CanvasHelper::useStates( const rendering::ViewState& viewState, const rendering::RenderState& renderState, - bool setColor ) + bool bSetColor ) { Matrix aViewMatrix; Matrix aRenderMatrix; @@ -158,19 +181,8 @@ namespace cairocanvas OSL_TRACE ("render clip END"); } - if( setColor ) { - if( renderState.DeviceColor.getLength() > 3 ) - cairo_set_source_rgba( mpCairo.get(), - renderState.DeviceColor [0], - renderState.DeviceColor [1], - renderState.DeviceColor [2], - renderState.DeviceColor [3] ); - else if (renderState.DeviceColor.getLength() == 3) - cairo_set_source_rgb( mpCairo.get(), - renderState.DeviceColor [0], - renderState.DeviceColor [1], - renderState.DeviceColor [2] ); - } + if( bSetColor ) + setColor(mpCairo.get(),renderState.DeviceColor); cairo_operator_t compositingMode( CAIRO_OPERATOR_OVER ); switch( renderState.CompositeOperation ) @@ -665,11 +677,33 @@ namespace cairocanvas double alpha = rColor[3]; // cairo expects premultiplied alpha cairo_pattern_add_color_stop_rgba( pPattern, stop, rColor[0]*alpha, rColor[1]*alpha, rColor[2]*alpha, alpha ); - //cairo_pattern_add_color_stop_rgba( pPattern, stop, rColor[0], rColor[1], rColor[2], alpha ); } } } + static uno::Sequence lerp(const uno::Sequence& rLeft, const uno::Sequence& rRight, double fAlpha) + { + if( rLeft.getLength() == 3 ) + { + uno::Sequence aRes(3); + aRes[0] = basegfx::tools::lerp(rLeft[0],rRight[0],fAlpha); + aRes[1] = basegfx::tools::lerp(rLeft[1],rRight[1],fAlpha); + aRes[2] = basegfx::tools::lerp(rLeft[2],rRight[2],fAlpha); + return aRes; + } + else if( rLeft.getLength() == 4 ) + { + uno::Sequence aRes(4); + aRes[0] = basegfx::tools::lerp(rLeft[0],rRight[0],fAlpha); + aRes[1] = basegfx::tools::lerp(rLeft[1],rRight[1],fAlpha); + aRes[2] = basegfx::tools::lerp(rLeft[2],rRight[2],fAlpha); + aRes[3] = basegfx::tools::lerp(rLeft[3],rRight[3],fAlpha); + return aRes; + } + + return uno::Sequence(); + } + static Pattern* patternFromParametricPolyPolygon( ::canvas::ParametricPolyPolygon& rPolygon ) { Pattern* pPattern = NULL; @@ -678,7 +712,6 @@ namespace cairocanvas // undef macros from vclenum.hxx which conflicts with GradientType enum values #undef GRADIENT_LINEAR -#undef GRADIENT_AXIAL #undef GRADIENT_ELLIPTICAL switch( aValues.meType ) { @@ -691,26 +724,17 @@ namespace cairocanvas addColorStops( pPattern, aValues.maColors, aValues.maStops ); break; - // FIXME: NYI - case ::canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR: - case ::canvas::ParametricPolyPolygon::GRADIENT_AXIAL: - x0 = 0; - y0 = 0; - x1 = 1; - y1 = 0; - pPattern = cairo_pattern_create_linear( x0, y0, x1, y1 ); - addColorStops( pPattern, aValues.maColors, aValues.maStops ); - break; - case ::canvas::ParametricPolyPolygon::GRADIENT_ELLIPTICAL: - cx = 0.5; - cy = 0.5; + cx = 0; + cy = 0; r0 = 0; - r1 = 0.5; + r1 = 1; - pPattern = cairo_pattern_create_radial( cx, cy, r0, cx, cy, r1 ); + pPattern = cairo_pattern_create_radial( cx, cy, r0, cy, cy, r1 ); addColorStops( pPattern, aValues.maColors, aValues.maStops, true ); break; + default: + break; } return pPattern; @@ -719,7 +743,8 @@ namespace cairocanvas static void doOperation( Operation aOperation, Cairo* pCairo, const uno::Sequence< rendering::Texture >* pTextures, - const SurfaceProviderRef& pDevice ) + const SurfaceProviderRef& pDevice, + const basegfx::B2DRange& rBounds ) { switch( aOperation ) { case Fill: @@ -790,19 +815,70 @@ namespace cairocanvas cairo_matrix_init( &aTextureMatrix, aTransform.m00, aTransform.m10, aTransform.m01, aTransform.m11, aTransform.m02, aTransform.m12); - Pattern* pPattern = patternFromParametricPolyPolygon( *pPolyImpl ); - - if( pPattern ) { - OSL_TRACE( "filling with pattern" ); - + if( pPolyImpl->getValues().meType == canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR ) + { + // no general path gradient yet in cairo; emulate then cairo_save( pCairo ); + cairo_clip( pCairo ); + + // fill bound rect with start color + cairo_rectangle( pCairo, rBounds.getMinX(), rBounds.getMinY(), + rBounds.getWidth(), rBounds.getHeight() ); + setColor(pCairo,pPolyImpl->getValues().maColors[0]); + cairo_fill(pCairo); + + cairo_transform( pCairo, &aTextureMatrix ); + + // longest line in gradient bound rect + const unsigned int nGradientSize( + static_cast( + ::basegfx::B2DVector(rBounds.getMinimum() - rBounds.getMaximum()).getLength() + 1.0 ) ); + + // typical number for pixel of the same color (strip size) + const unsigned int nStripSize( nGradientSize < 50 ? 2 : 4 ); + + // use at least three steps, and at utmost the number of color + // steps + const unsigned int nStepCount( + ::std::max( + 3U, + ::std::min( + nGradientSize / nStripSize, + 128U )) + 1 ); + + const uno::Sequence* pColors=&pPolyImpl->getValues().maColors[0]; + basegfx::tools::KeyStopLerp aLerper(pPolyImpl->getValues().maStops); + for( unsigned int i=1; i rv = uno::Reference< rendering::XCachedPrimitive >(NULL); - geometry::IntegerSize2D aBitmapSize = rSize; + geometry::IntegerSize2D aBitmapSize = rSize; if( mpCairo ) { cairo_save( mpCairo.get() ); @@ -1198,38 +1274,38 @@ namespace cairocanvas ::rtl::math::approxEqual( aMatrix.y0, 0 ) && basegfx::fround( rSize.Width * aMatrix.xx ) > 8 && basegfx::fround( rSize.Height* aMatrix.yy ) > 8 ) - { - double dWidth, dHeight; + { + double dWidth, dHeight; - dWidth = basegfx::fround( rSize.Width * aMatrix.xx ); - dHeight = basegfx::fround( rSize.Height* aMatrix.yy ); - aBitmapSize.Width = static_cast( dWidth ); - aBitmapSize.Height = static_cast( dHeight ); + dWidth = basegfx::fround( rSize.Width * aMatrix.xx ); + dHeight = basegfx::fround( rSize.Height* aMatrix.yy ); + aBitmapSize.Width = static_cast( dWidth ); + aBitmapSize.Height = static_cast( dHeight ); - SurfaceSharedPtr pScaledSurface = mpSurfaceProvider->createSurface( - ::basegfx::B2ISize( aBitmapSize.Width, aBitmapSize.Height ), - bHasAlpha ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR ); - CairoSharedPtr pCairo = pScaledSurface->getCairo(); + SurfaceSharedPtr pScaledSurface = mpSurfaceProvider->createSurface( + ::basegfx::B2ISize( aBitmapSize.Width, aBitmapSize.Height ), + bHasAlpha ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR ); + CairoSharedPtr pCairo = pScaledSurface->getCairo(); - cairo_set_operator( pCairo.get(), CAIRO_OPERATOR_SOURCE ); - // add 0.5px to size to avoid rounding errors in cairo, leading sometimes to random data on the image right/bottom borders - cairo_scale( pCairo.get(), (dWidth+0.5)/rSize.Width, (dHeight+0.5)/rSize.Height ); - cairo_set_source_surface( pCairo.get(), pSurface->getCairoSurface().get(), 0, 0 ); - cairo_paint( pCairo.get() ); + cairo_set_operator( pCairo.get(), CAIRO_OPERATOR_SOURCE ); + // add 0.5px to size to avoid rounding errors in cairo, leading sometimes to random data on the image right/bottom borders + cairo_scale( pCairo.get(), (dWidth+0.5)/rSize.Width, (dHeight+0.5)/rSize.Height ); + cairo_set_source_surface( pCairo.get(), pSurface->getCairoSurface().get(), 0, 0 ); + cairo_paint( pCairo.get() ); - pSurface = pScaledSurface; + pSurface = pScaledSurface; - aMatrix.xx = aMatrix.yy = 1; - cairo_set_matrix( mpCairo.get(), &aMatrix ); + aMatrix.xx = aMatrix.yy = 1; + cairo_set_matrix( mpCairo.get(), &aMatrix ); - rv = uno::Reference< rendering::XCachedPrimitive >( - new CachedBitmap( pSurface, viewState, renderState, - // cast away const, need to - // change refcount (as this is - // ~invisible to client code, - // still logically const) - const_cast< rendering::XCanvas* >(pCanvas)) ); - } + rv = uno::Reference< rendering::XCachedPrimitive >( + new CachedBitmap( pSurface, viewState, renderState, + // cast away const, need to + // change refcount (as this is + // ~invisible to client code, + // still logically const) + const_cast< rendering::XCanvas* >(pCanvas)) ); + } if( !bHasAlpha && mbHaveAlpha ) { @@ -1270,7 +1346,11 @@ namespace cairocanvas cairo_set_operator( mpCairo.get(), CAIRO_OPERATOR_SOURCE ); cairo_rectangle( mpCairo.get(), 0, 0, aBitmapSize.Width, aBitmapSize.Height ); cairo_clip( mpCairo.get() ); - cairo_paint( mpCairo.get() ); + + if( bModulateColors ) + cairo_paint_with_alpha( mpCairo.get(), renderState.DeviceColor[3] ); + else + cairo_paint( mpCairo.get() ); cairo_restore( mpCairo.get() ); } else OSL_TRACE ("CanvasHelper called after it was disposed"); @@ -1309,15 +1389,35 @@ namespace cairocanvas return rv; } - uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* , - const uno::Reference< rendering::XBitmap >& /*xBitmap*/, - const rendering::ViewState& /*viewState*/, - const rendering::RenderState& /*renderState*/ ) + uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas, + const uno::Reference< rendering::XBitmap >& xBitmap, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) { - // TODO(F3): Implement modulated bitmap! +#ifdef CAIRO_CANVAS_PERF_TRACE + struct timespec aTimer; + mxDevice->startPerfTrace( &aTimer ); +#endif - // TODO(P1): Provide caching here. - return uno::Reference< rendering::XCachedPrimitive >(NULL); + uno::Reference< rendering::XCachedPrimitive > rv; + unsigned char* data = NULL; + bool bHasAlpha = false; + SurfaceSharedPtr pSurface = surfaceFromXBitmap( xBitmap, mpSurfaceProvider, data, bHasAlpha ); + geometry::IntegerSize2D aSize = xBitmap->getSize(); + + if( pSurface ) { + rv = implDrawBitmapSurface( pCanvas, pSurface, viewState, renderState, aSize, true, bHasAlpha ); + + if( data ) + free( data ); + } else + rv = uno::Reference< rendering::XCachedPrimitive >(NULL); + +#ifdef CAIRO_CANVAS_PERF_TRACE + mxDevice->stopPerfTrace( &aTimer, "drawBitmap" ); +#endif + + return rv; } uno::Reference< rendering::XGraphicDevice > CanvasHelper::getDevice() diff --git a/canvas/source/cairo/cairo_spritecanvas.hxx b/canvas/source/cairo/cairo_spritecanvas.hxx index bdbb42c6da12..210908552d73 100644 --- a/canvas/source/cairo/cairo_spritecanvas.hxx +++ b/canvas/source/cairo/cairo_spritecanvas.hxx @@ -42,7 +42,6 @@ #include #include #include -#include #include #include @@ -66,7 +65,7 @@ namespace cairocanvas typedef ::cppu::WeakComponentImplHelper9< ::com::sun::star::rendering::XSpriteCanvas, ::com::sun::star::rendering::XIntegerBitmap, ::com::sun::star::rendering::XGraphicDevice, - ::com::sun::star::rendering::XParametricPolyPolygon2DFactory, + ::com::sun::star::lang::XMultiServiceFactory, ::com::sun::star::rendering::XBufferController, ::com::sun::star::awt::XWindowListener, ::com::sun::star::util::XUpdatable, diff --git a/canvas/source/null/null_spritecanvas.hxx b/canvas/source/null/null_spritecanvas.hxx index 341f2a5a95a6..278bcd9a8e7c 100644 --- a/canvas/source/null/null_spritecanvas.hxx +++ b/canvas/source/null/null_spritecanvas.hxx @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include #include @@ -60,7 +60,7 @@ namespace nullcanvas typedef ::cppu::WeakComponentImplHelper8< ::com::sun::star::rendering::XSpriteCanvas, ::com::sun::star::rendering::XIntegerBitmap, ::com::sun::star::rendering::XGraphicDevice, - ::com::sun::star::rendering::XParametricPolyPolygon2DFactory, + ::com::sun::star::lang::XMultiServiceFactory, ::com::sun::star::rendering::XBufferController, ::com::sun::star::awt::XWindowListener, ::com::sun::star::beans::XPropertySet, diff --git a/canvas/source/tools/parametricpolypolygon.cxx b/canvas/source/tools/parametricpolypolygon.cxx index bfce116393d0..222b668a5fb0 100644 --- a/canvas/source/tools/parametricpolypolygon.cxx +++ b/canvas/source/tools/parametricpolypolygon.cxx @@ -53,6 +53,87 @@ using namespace ::com::sun::star; namespace canvas { + uno::Sequence ParametricPolyPolygon::getAvailableServiceNames() + { + uno::Sequence aRet(3); + aRet[0] = rtl::OUString::createFromAscii("LinearGradient"); + aRet[1] = rtl::OUString::createFromAscii("EllipticalGradient"); + aRet[2] = rtl::OUString::createFromAscii("RectangularGradient"); + + return aRet; + } + + ParametricPolyPolygon* ParametricPolyPolygon::create( + const uno::Reference< rendering::XGraphicDevice >& rDevice, + const ::rtl::OUString& rServiceName, + const uno::Sequence< uno::Any >& rArgs ) + { + uno::Sequence< uno::Sequence< double > > colorSequence(2); + uno::Sequence< double > colorStops(2); + double fAspectRatio=1.0; + + // defaults + uno::Sequence< rendering::RGBColor > rgbColors(1); + rgbColors[0] = rendering::RGBColor(0,0,0); + colorSequence[0] = rDevice->getDeviceColorSpace()->convertFromRGB(rgbColors); + rgbColors[0] = rendering::RGBColor(1,1,1); + colorSequence[1] = rDevice->getDeviceColorSpace()->convertFromRGB(rgbColors); + colorStops[0] = 0; + colorStops[1] = 1; + + // extract args + for( sal_Int32 i=0; i>= aProp) ) + { + if( aProp.Name.equalsAscii("Colors") ) + { + aProp.Value >>= colorSequence; + } + else if( aProp.Name.equalsAscii("Stops") ) + { + aProp.Value >>= colorStops; + } + else if( aProp.Name.equalsAscii("AspectRatio") ) + { + aProp.Value >>= fAspectRatio; + } + } + } + + if( rServiceName.equalsAscii("LinearGradient") ) + { + return createLinearHorizontalGradient(rDevice, colorSequence, colorStops); + } + else if( rServiceName.equalsAscii("EllipticalGradient") ) + { + return createEllipticalGradient(rDevice, colorSequence, colorStops, fAspectRatio); + } + else if( rServiceName.equalsAscii("RectangularGradient") ) + { + return createRectangularGradient(rDevice, colorSequence, colorStops, fAspectRatio); + } + else if( rServiceName.equalsAscii("VerticalLineHatch") ) + { + // TODO: NYI + } + else if( rServiceName.equalsAscii("OrthogonalLinesHatch") ) + { + // TODO: NYI + } + else if( rServiceName.equalsAscii("ThreeCrossingLinesHatch") ) + { + // TODO: NYI + } + else if( rServiceName.equalsAscii("FourCrossingLinesHatch") ) + { + // TODO: NYI + } + + return NULL; + } + ParametricPolyPolygon* ParametricPolyPolygon::createLinearHorizontalGradient( const uno::Reference< rendering::XGraphicDevice >& rDevice, const uno::Sequence< uno::Sequence< double > >& colors, @@ -63,58 +144,35 @@ namespace canvas return new ParametricPolyPolygon( rDevice, GRADIENT_LINEAR, colors, stops ); } - ParametricPolyPolygon* ParametricPolyPolygon::createAxialHorizontalGradient( - const uno::Reference< rendering::XGraphicDevice >& rDevice, - const uno::Sequence< uno::Sequence< double > >& colors, - const uno::Sequence< double >& stops ) - { - // TODO(P2): hold gradient brush statically, and only setup - // the colors - return new ParametricPolyPolygon( rDevice, GRADIENT_AXIAL, colors, stops ); - } - - namespace - { - double calcAspectRatio( const geometry::RealRectangle2D& rBoundRect ) - { - const double nWidth( rBoundRect.X2 - rBoundRect.X1 ); - const double nHeight( rBoundRect.Y2 - rBoundRect.Y1 ); - - return ::basegfx::fTools::equalZero( nHeight ) ? 1.0 : fabs( nWidth / nHeight ); - } - } - ParametricPolyPolygon* ParametricPolyPolygon::createEllipticalGradient( const uno::Reference< rendering::XGraphicDevice >& rDevice, const uno::Sequence< uno::Sequence< double > >& colors, const uno::Sequence< double >& stops, - const geometry::RealRectangle2D& boundRect ) + double fAspectRatio ) { // TODO(P2): hold gradient polygon statically, and only setup // the colors return new ParametricPolyPolygon( rDevice, ::basegfx::tools::createPolygonFromCircle( - ::basegfx::B2DPoint( 0.5, 0.5), 0.5 ), + ::basegfx::B2DPoint(0,0), 1 ), GRADIENT_ELLIPTICAL, - colors, stops, - calcAspectRatio( boundRect ) ); + colors, stops, fAspectRatio ); } ParametricPolyPolygon* ParametricPolyPolygon::createRectangularGradient( const uno::Reference< rendering::XGraphicDevice >& rDevice, const uno::Sequence< uno::Sequence< double > >& colors, const uno::Sequence< double >& stops, - const geometry::RealRectangle2D& boundRect ) + double fAspectRatio ) { // TODO(P2): hold gradient polygon statically, and only setup // the colors return new ParametricPolyPolygon( rDevice, ::basegfx::tools::createPolygonFromRect( - ::basegfx::B2DRectangle( 0.0, 0.0, 1.0, 1.0 ) ), + ::basegfx::B2DRectangle( -1, -1, 1, 1 ) ), GRADIENT_RECTANGULAR, - colors, stops, - calcAspectRatio( boundRect ) ); + colors, stops, fAspectRatio ); } void SAL_CALL ParametricPolyPolygon::disposing() diff --git a/canvas/source/vcl/canvas.hxx b/canvas/source/vcl/canvas.hxx index 2279f817a406..c4e1a2853532 100644 --- a/canvas/source/vcl/canvas.hxx +++ b/canvas/source/vcl/canvas.hxx @@ -41,7 +41,6 @@ #include #include #include -#include #include #include @@ -63,7 +62,7 @@ namespace vclcanvas typedef ::cppu::WeakComponentImplHelper7< ::com::sun::star::rendering::XBitmapCanvas, ::com::sun::star::rendering::XIntegerBitmap, ::com::sun::star::rendering::XGraphicDevice, - ::com::sun::star::rendering::XParametricPolyPolygon2DFactory, + ::com::sun::star::lang::XMultiServiceFactory, ::com::sun::star::util::XUpdatable, ::com::sun::star::beans::XPropertySet, ::com::sun::star::lang::XServiceName > GraphicDeviceBase_Base; diff --git a/canvas/source/vcl/canvashelper_texturefill.cxx b/canvas/source/vcl/canvashelper_texturefill.cxx index 571a8c4fc5a3..5219c6349186 100644 --- a/canvas/source/vcl/canvashelper_texturefill.cxx +++ b/canvas/source/vcl/canvashelper_texturefill.cxx @@ -57,6 +57,8 @@ #include #include #include +#include +#include #include #include @@ -65,6 +67,9 @@ #include #include +#include +#include + #include "spritecanvas.hxx" #include "canvashelper.hxx" #include "impltools.hxx" @@ -118,17 +123,13 @@ namespace vclcanvas Since most of the code for linear and axial gradients are the same, we've a unified method here */ - void fillGeneralLinearGradient( OutputDevice& rOutDev, - const ::basegfx::B2DHomMatrix& rTextureTransform, - const ::Rectangle& rBounds, - int nStepCount, - const ::Color& rColor1, - const ::Color& rColor2, - bool bFillNonOverlapping, - bool bAxialGradient ) + void fillLinearGradient( OutputDevice& rOutDev, + const ::basegfx::B2DHomMatrix& rTextureTransform, + const ::Rectangle& rBounds, + unsigned int nStepCount, + const ::canvas::ParametricPolyPolygon::Values& rValues, + const std::vector< ::Color >& rColors ) { - (void)bFillNonOverlapping; - // determine general position of gradient in relation to // the bound rect // ===================================================== @@ -207,36 +208,26 @@ namespace vclcanvas // iteratively render all other strips // ----------------------------------- - // ensure that nStepCount is odd, to have a well-defined - // middle index for axial gradients. - if( bAxialGradient && !(nStepCount % 2) ) + // ensure that nStepCount matches color stop parity, to + // have a well-defined middle color e.g. for axial + // gradients. + if( (rColors.size() % 2) != (nStepCount % 2) ) ++nStepCount; - const int nStepCountHalved( nStepCount / 2 ); + basegfx::tools::KeyStopLerp aLerper(rValues.maStops); // only iterate nStepCount-1 steps, as the last strip is // explicitely painted below - for( int i=0; i& rColors ) { const ::basegfx::B2DPolygon& rGradientPoly( rValues.maGradientPoly ); @@ -369,9 +319,6 @@ namespace vclcanvas // apply scaling (possibly anisotrophic) to inner polygon // ------------------------------------------------------ - // move center of scaling to origin - aInnerPolygonTransformMatrix.translate( -0.5, -0.5 ); - // scale inner polygon according to aspect ratio: for // wider-than-tall bounds (nAspectRatio > 1.0), the inner // polygon, representing the gradient focus, must have @@ -396,9 +343,6 @@ namespace vclcanvas aInnerPolygonTransformMatrix.scale( 0.0, 0.0 ); } - // move origin back to former center of polygon - aInnerPolygonTransformMatrix.translate( 0.5, 0.5 ); - // and finally, add texture transform to it. aInnerPolygonTransformMatrix *= rTextureTransform; @@ -406,8 +350,8 @@ namespace vclcanvas aInnerPoly.transform( aInnerPolygonTransformMatrix ); - const sal_Int32 nNumPoints( aOuterPoly.count() ); - ::Polygon aTempPoly( static_cast(nNumPoints+1) ); + const sal_uInt32 nNumPoints( aOuterPoly.count() ); + ::Polygon aTempPoly( static_cast(nNumPoints+1) ); // increase number of steps by one: polygonal gradients have // the outermost polygon rendered in rColor2, and the @@ -425,37 +369,42 @@ namespace vclcanvas // color). ++nStepCount; + basegfx::tools::KeyStopLerp aLerper(rValues.maStops); + if( !bFillNonOverlapping ) { // fill background - rOutDev.SetFillColor( rColor1 ); + rOutDev.SetFillColor( rColors.front() ); rOutDev.DrawRect( rBounds ); // render polygon // ============== - for( int i=1,p; i 0 if( i && !(i % 10) ) @@ -506,17 +461,14 @@ namespace vclcanvas // calculate the inner polygon, which is actually the // start of the _next_ color strip. Thus, i+1 - // calc interpolation parameter in [0,1] range - const double nT( (nStepCount-i-1)/double(nStepCount) ); - for( p=0; p& rColors, const ::basegfx::B2DHomMatrix& rTextureTransform, const ::Rectangle& rBounds, - int nStepCount, + unsigned int nStepCount, bool bFillNonOverlapping ) { switch( rValues.meType ) { case ::canvas::ParametricPolyPolygon::GRADIENT_LINEAR: fillLinearGradient( rOutDev, - rColor1, - rColor2, rTextureTransform, rBounds, nStepCount, - bFillNonOverlapping ); - break; - - case ::canvas::ParametricPolyPolygon::GRADIENT_AXIAL: - fillAxialGradient( rOutDev, - rColor1, - rColor2, - rTextureTransform, - rBounds, - nStepCount, - bFillNonOverlapping ); + rValues, + rColors ); break; case ::canvas::ParametricPolyPolygon::GRADIENT_ELLIPTICAL: // FALLTHROUGH intended case ::canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR: fillPolygonalGradient( rOutDev, - rValues, - rColor1, - rColor2, rTextureTransform, rBounds, nStepCount, - bFillNonOverlapping ); + bFillNonOverlapping, + rValues, + rColors ); break; default: @@ -597,11 +536,19 @@ namespace vclcanvas } } + int numColorSteps( const ::Color& rColor1, const ::Color& rColor2 ) + { + return ::std::max( + labs( rColor1.GetRed() - rColor2.GetRed() ), + ::std::max( + labs( rColor1.GetGreen() - rColor2.GetGreen() ), + labs( rColor1.GetBlue() - rColor2.GetBlue() ) ) ); + } + bool gradientFill( OutputDevice& rOutDev, OutputDevice* p2ndOutDev, const ::canvas::ParametricPolyPolygon::Values& rValues, - const ::Color& rColor1, - const ::Color& rColor2, + const std::vector< ::Color >& rColors, const PolyPolygon& rPoly, const rendering::ViewState& viewState, const rendering::RenderState& renderState, @@ -646,12 +593,9 @@ namespace vclcanvas // calc step size // -------------- - const int nColorSteps( - ::std::max( - labs( rColor1.GetRed() - rColor2.GetRed() ), - ::std::max( - labs( rColor1.GetGreen() - rColor2.GetGreen() ), - labs( rColor1.GetBlue() - rColor2.GetBlue() ) ) ) ); + int nColorSteps = 0; + for( size_t i=0; iIntersectClipRegion( aPolygonDeviceRectOrig ); doGradientFill( *p2ndOutDev, rValues, - rColor1, - rColor2, + rColors, aTextureTransform, aPolygonDeviceRectOrig, nStepCount, @@ -723,8 +665,7 @@ namespace vclcanvas doGradientFill( rOutDev, rValues, - rColor1, - rColor2, + rColors, aTextureTransform, aPolygonDeviceRectOrig, nStepCount, @@ -737,8 +678,7 @@ namespace vclcanvas p2ndOutDev->SetClipRegion( aPolyClipRegion ); doGradientFill( *p2ndOutDev, rValues, - rColor1, - rColor2, + rColors, aTextureTransform, aPolygonDeviceRectOrig, nStepCount, @@ -753,8 +693,7 @@ namespace vclcanvas rOutDev.SetRasterOp( ROP_XOR ); doGradientFill( rOutDev, rValues, - rColor1, - rColor2, + rColors, aTextureTransform, aPolygonDeviceRectOrig, nStepCount, @@ -765,8 +704,7 @@ namespace vclcanvas rOutDev.SetRasterOp( ROP_XOR ); doGradientFill( rOutDev, rValues, - rColor1, - rColor2, + rColors, aTextureTransform, aPolygonDeviceRectOrig, nStepCount, @@ -779,8 +717,7 @@ namespace vclcanvas p2ndOutDev->SetRasterOp( ROP_XOR ); doGradientFill( *p2ndOutDev, rValues, - rColor1, - rColor2, + rColors, aTextureTransform, aPolygonDeviceRectOrig, nStepCount, @@ -791,8 +728,7 @@ namespace vclcanvas p2ndOutDev->SetRasterOp( ROP_XOR ); doGradientFill( *p2ndOutDev, rValues, - rColor1, - rColor2, + rColors, aTextureTransform, aPolygonDeviceRectOrig, nStepCount, @@ -855,33 +791,41 @@ namespace vclcanvas ::canvas::ParametricPolyPolygon* pGradient = dynamic_cast< ::canvas::ParametricPolyPolygon* >( textures[0].Gradient.get() ); - if( pGradient ) + if( pGradient && pGradient->getValues().maColors.getLength() ) { // copy state from Gradient polypoly locally // (given object might change!) const ::canvas::ParametricPolyPolygon::Values& rValues( pGradient->getValues() ); - // TODO: use all the colors and place them on given positions/stops - const ::Color aColor1( - ::vcl::unotools::stdColorSpaceSequenceToColor( - rValues.maColors [0] ) ); - const ::Color aColor2( - ::vcl::unotools::stdColorSpaceSequenceToColor( - rValues.maColors [rValues.maColors.getLength () - 1] ) ); + if( rValues.maColors.getLength() < 2 ) + { + rendering::RenderState aTempState=renderState; + aTempState.DeviceColor = rValues.maColors[0]; + fillPolyPolygon(pCanvas, xPolyPolygon, viewState, aTempState); + } + else + { + std::vector< ::Color > aColors(rValues.maColors.getLength()); + std::transform(&rValues.maColors[0], + &rValues.maColors[0]+rValues.maColors.getLength(), + aColors.begin(), + boost::bind( + &vcl::unotools::stdColorSpaceSequenceToColor, + _1)); - // TODO(E1): Return value - // TODO(F1): FillRule - gradientFill( mpOutDev->getOutDev(), - mp2ndOutDev.get() ? &mp2ndOutDev->getOutDev() : (OutputDevice*)NULL, - rValues, - aColor1, - aColor2, - aPolyPoly, - viewState, - renderState, - textures[0], - nTransparency ); + // TODO(E1): Return value + // TODO(F1): FillRule + gradientFill( mpOutDev->getOutDev(), + mp2ndOutDev.get() ? &mp2ndOutDev->getOutDev() : (OutputDevice*)NULL, + rValues, + aColors, + aPolyPoly, + viewState, + renderState, + textures[0], + nTransparency ); + } } else { diff --git a/canvas/source/vcl/spritecanvas.hxx b/canvas/source/vcl/spritecanvas.hxx index 545eeeed4577..66762bcb65b7 100644 --- a/canvas/source/vcl/spritecanvas.hxx +++ b/canvas/source/vcl/spritecanvas.hxx @@ -42,7 +42,6 @@ #include #include #include -#include #include #include @@ -65,7 +64,7 @@ namespace vclcanvas typedef ::cppu::WeakComponentImplHelper9< ::com::sun::star::rendering::XSpriteCanvas, ::com::sun::star::rendering::XIntegerBitmap, ::com::sun::star::rendering::XGraphicDevice, - ::com::sun::star::rendering::XParametricPolyPolygon2DFactory, + ::com::sun::star::lang::XMultiServiceFactory, ::com::sun::star::rendering::XBufferController, ::com::sun::star::awt::XWindowListener, ::com::sun::star::util::XUpdatable, diff --git a/cppcanvas/source/mtfrenderer/implrenderer.cxx b/cppcanvas/source/mtfrenderer/implrenderer.cxx index c6f9a295b332..daef89bf2905 100644 --- a/cppcanvas/source/mtfrenderer/implrenderer.cxx +++ b/cppcanvas/source/mtfrenderer/implrenderer.cxx @@ -49,7 +49,6 @@ #include #include -#include #include #include #include @@ -61,6 +60,7 @@ #include #include +#include #include #include #include @@ -590,13 +590,12 @@ namespace cppcanvas // discernible difference should be visible. nSteps > 64 ) { - uno::Reference< rendering::XParametricPolyPolygon2DFactory > xFactory( + uno::Reference< lang::XMultiServiceFactory> xFactory( rParms.mrCanvas->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() ); if( xFactory.is() ) { - ::basegfx::B2DHomMatrix aTextureTransformation; - rendering::Texture aTexture; + rendering::Texture aTexture; aTexture.RepeatModeX = rendering::TexturingMode::CLAMP; aTexture.RepeatModeY = rendering::TexturingMode::CLAMP; @@ -631,242 +630,118 @@ namespace cppcanvas uno::Sequence< uno::Sequence < double > > aColors(2); uno::Sequence< double > aStops(2); - aStops[0] = 0.0; - aStops[1] = 1.0; + if( rGradient.GetStyle() == GRADIENT_AXIAL ) + { + aStops.realloc(3); + aColors.realloc(3); - aColors[0] = aStartColor; - aColors[1] = aEndColor; + aStops[0] = 0.0; + aStops[1] = 0.5; + aStops[2] = 1.0; + aColors[0] = aEndColor; + aColors[1] = aStartColor; + aColors[2] = aEndColor; + } + else + { + aStops[0] = 0.0; + aStops[1] = 1.0; - // Setup texture transformation - // ---------------------------- + aColors[0] = aStartColor; + aColors[1] = aEndColor; + } const ::basegfx::B2DRectangle aBounds( ::basegfx::tools::getRange(aDevicePoly) ); + const ::basegfx::B2DVector aOffset( + rGradient.GetOfsX() / 100.0, + rGradient.GetOfsY() / 100.0); + double fRotation( rGradient.GetAngle() * M_PI / 1800.0 ); + const double fBorder( rGradient.GetBorder() / 100.0 ); - // setup rotation angle. VCL rotates - // counter-clockwise, while canvas transformation - // rotates clockwise - double nRotation( -rGradient.GetAngle() * M_PI / 1800.0 ); + basegfx::B2DHomMatrix aRot90; + aRot90.rotate(M_PI_2); + basegfx::ODFGradientInfo aGradInfo; + rtl::OUString aGradientService; switch( rGradient.GetStyle() ) { case GRADIENT_LINEAR: - // FALLTHROUGH intended + basegfx::tools::createLinearODFGradientInfo(aGradInfo, + aBounds, + nSteps, + fBorder, + fRotation); + // map odf to svg gradient orientation - x + // instead of y direction + aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aRot90; + aGradientService = rtl::OUString::createFromAscii("LinearGradient"); + break; + case GRADIENT_AXIAL: { - // standard orientation for VCL linear - // gradient is vertical, thus, rotate 90 - // degrees - nRotation += M_PI/2.0; + basegfx::tools::createLinearODFGradientInfo(aGradInfo, + aBounds, + nSteps, + fBorder, + fRotation); + // map odf to svg gradient orientation - x + // instead of y direction + aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aRot90; - const double nBorder( - ::basegfx::pruneScaleValue( - (1.0 - rGradient.GetBorder() / 100.0) ) ); + // map odf axial gradient to 3-stop linear + // gradient - shift left by 0.5 + basegfx::B2DHomMatrix aShift; + aShift.translate(-0.5,0); + aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aShift; - // shrink texture, to account for border - // (only in x direction, linear gradient - // is constant in y direction, anyway) - aTextureTransformation.scale( nBorder, - 1.0 ); - - // linear gradients don't respect offsets - // (they are implicitely assumed to be - // 50%). linear gradients don't have - // border on both sides, only on the - // startColor side, axial gradients have - // border on both sides. As both gradients - // are invariant in y direction: leave y - // offset alone. - double nOffsetX( rGradient.GetBorder() / 200.0 ); - - // determine type of gradient (and necessary - // transformation matrix, should it be emulated by a - // generic gradient) - switch( rGradient.GetStyle() ) - { - case GRADIENT_LINEAR: - nOffsetX = rGradient.GetBorder() / 100.0; - aTexture.Gradient = xFactory->createLinearHorizontalGradient( aColors, - aStops ); - break; - - case GRADIENT_AXIAL: - // vcl considers center color as start color - ::std::swap(aColors[0],aColors[1]); - aTexture.Gradient = xFactory->createAxialHorizontalGradient( aColors, - aStops ); - break; - - default: // other cases can't happen - break; - } - - // apply border offset values - aTextureTransformation.translate( nOffsetX, - 0.0 ); - - // rotate texture according to gradient rotation - aTextureTransformation.translate( -0.5, -0.5 ); - aTextureTransformation.rotate( nRotation ); - - // to let the first strip of a rotated - // gradient start at the _edge_ of the - // bound rect (and not, due to rotation, - // slightly inside), slightly enlarge the - // gradient: - // - // y/2 sin(alpha) + x/2 cos(alpha) - // - // (values to change are not actual - // gradient scales, but original bound - // rect dimensions. Since we still want - // the border setting to apply after that, - // we multiply with that as above for - // nScaleX) - const double nScale( - ::basegfx::pruneScaleValue( - fabs( aBounds.getHeight()*sin(nRotation) ) + - fabs( aBounds.getWidth()*cos(nRotation) ))); - - aTextureTransformation.scale( nScale, nScale ); - - // translate back origin to center of - // primitive - aTextureTransformation.translate( 0.5*aBounds.getWidth(), - 0.5*aBounds.getHeight() ); + aGradientService = rtl::OUString::createFromAscii("LinearGradient"); + break; } - break; case GRADIENT_RADIAL: - // FALLTHROUGH intended + basegfx::tools::createRadialODFGradientInfo(aGradInfo, + aBounds, + aOffset, + nSteps, + fBorder); + aGradientService = rtl::OUString::createFromAscii("EllipticalGradient"); + break; + case GRADIENT_ELLIPTICAL: - // FALLTHROUGH intended + basegfx::tools::createEllipticalODFGradientInfo(aGradInfo, + aBounds, + aOffset, + nSteps, + fBorder, + fRotation); + aGradientService = rtl::OUString::createFromAscii("EllipticalGradient"); + break; + case GRADIENT_SQUARE: - // FALLTHROUGH intended + basegfx::tools::createSquareODFGradientInfo(aGradInfo, + aBounds, + aOffset, + nSteps, + fBorder, + fRotation); + aGradientService = rtl::OUString::createFromAscii("RectangularGradient"); + break; + case GRADIENT_RECT: - { - // determine scale factors for the gradient (must - // be scaled up from [0,1]x[0,1] rect to object - // bounds). Will potentially changed in switch - // statement below. - // Respect border value, while doing so, the VCL - // gradient's border will effectively shrink the - // resulting gradient. - double nScaleX( aBounds.getWidth() * (1.0 - rGradient.GetBorder() / 100.0) ); - double nScaleY( aBounds.getHeight()* (1.0 - rGradient.GetBorder() / 100.0) ); - - // determine offset values. Since the border is - // divided half-by-half to both sides of the - // gradient, divide translation offset by an - // additional 2. Also respect offset here, but - // since VCL gradients have their center at [0,0] - // for zero offset, but canvas gradients have - // their top, left edge aligned with the - // primitive, and offset of 50% effectively must - // yield zero shift. Both values will potentially - // be adapted in switch statement below. - double nOffsetX( aBounds.getWidth() * - (2.0 * rGradient.GetOfsX() - 100.0 + rGradient.GetBorder()) / 200.0 ); - double nOffsetY( aBounds.getHeight() * - (2.0 * rGradient.GetOfsY() - 100.0 + rGradient.GetBorder()) / 200.0 ); - - // determine type of gradient (and necessary - // transformation matrix, should it be emulated by a - // generic gradient) - switch( rGradient.GetStyle() ) - { - case GRADIENT_RADIAL: - { - // create isotrophic scaling - if( nScaleX > nScaleY ) - { - nOffsetY -= (nScaleX - nScaleY) * 0.5; - nScaleY = nScaleX; - } - else - { - nOffsetX -= (nScaleY - nScaleX) * 0.5; - nScaleX = nScaleY; - } - - // enlarge gradient to match bound rect diagonal - aTextureTransformation.translate( -0.5, -0.5 ); - const double nScale( hypot(aBounds.getWidth(), aBounds.getHeight()) / nScaleX ); - aTextureTransformation.scale( nScale, nScale ); - aTextureTransformation.translate( 0.5, 0.5 ); - - aTexture.Gradient = xFactory->createEllipticalGradient( aColors, - aStops, - geometry::RealRectangle2D(0.0,0.0, - 1.0,1.0) ); - } - break; - - case GRADIENT_ELLIPTICAL: - { - // enlarge gradient slightly - aTextureTransformation.translate( -0.5, -0.5 ); - const double nSqrt2( sqrt(2.0) ); - aTextureTransformation.scale( nSqrt2,nSqrt2 ); - aTextureTransformation.translate( 0.5, 0.5 ); - - aTexture.Gradient = xFactory->createEllipticalGradient( - aColors, - aStops, - ::basegfx::unotools::rectangle2DFromB2DRectangle( - aBounds )); - } - break; - - case GRADIENT_SQUARE: - // create isotrophic scaling - if( nScaleX > nScaleY ) - { - nOffsetY -= (nScaleX - nScaleY) * 0.5; - nScaleY = nScaleX; - } - else - { - nOffsetX -= (nScaleY - nScaleX) * 0.5; - nScaleX = nScaleY; - } - - aTexture.Gradient = xFactory->createRectangularGradient( aColors, - aStops, - geometry::RealRectangle2D(0.0,0.0, - 1.0,1.0) ); - break; - - case GRADIENT_RECT: - aTexture.Gradient = xFactory->createRectangularGradient( - aColors, - aStops, - ::basegfx::unotools::rectangle2DFromB2DRectangle( - aBounds ) ); - break; - - default: // other cases can't happen - break; - } - - nScaleX = ::basegfx::pruneScaleValue( nScaleX ); - nScaleY = ::basegfx::pruneScaleValue( nScaleY ); - - aTextureTransformation.scale( nScaleX, nScaleY ); - - // rotate texture according to gradient rotation - aTextureTransformation.translate( -0.5*nScaleX, -0.5*nScaleY ); - aTextureTransformation.rotate( nRotation ); - aTextureTransformation.translate( 0.5*nScaleX, 0.5*nScaleY ); - - aTextureTransformation.translate( nOffsetX, nOffsetY ); - } - break; + basegfx::tools::createRectangularODFGradientInfo(aGradInfo, + aBounds, + aOffset, + nSteps, + fBorder, + fRotation); + aGradientService = rtl::OUString::createFromAscii("RectangularGradient"); + break; default: ENSURE_OR_THROW( false, - "ImplRenderer::createGradientAction(): Unexpected gradient type" ); + "ImplRenderer::createGradientAction(): Unexpected gradient type" ); break; } @@ -877,31 +752,49 @@ namespace cppcanvas // gradient will always display at the origin, and // not within the polygon bound (which might be // miles away from the origin). - aTextureTransformation.translate( aBounds.getMinX(), - aBounds.getMinY() ); - + aGradInfo.maTextureTransform.translate( aBounds.getMinX(), + aBounds.getMinY() ); ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform, - aTextureTransformation ); + aGradInfo.maTextureTransform ); - ActionSharedPtr pPolyAction( - internal::PolyPolyActionFactory::createPolyPolyAction( - aDevicePoly, - rParms.mrCanvas, - getState( rParms.mrStates ), - aTexture ) ); + uno::Sequence args(3); + beans::PropertyValue aProp; + aProp.Name = rtl::OUString::createFromAscii("Colors"); + aProp.Value <<= aColors; + args[0] <<= aProp; + aProp.Name = rtl::OUString::createFromAscii("Stops"); + aProp.Value <<= aStops; + args[1] <<= aProp; + aProp.Name = rtl::OUString::createFromAscii("AspectRatio"); + aProp.Value <<= aGradInfo.mfAspectRatio; + args[2] <<= aProp; - if( pPolyAction ) + aTexture.Gradient.set( + xFactory->createInstanceWithArguments(aGradientService, + args), + uno::UNO_QUERY); + if( aTexture.Gradient.is() ) { - maActions.push_back( - MtfAction( - pPolyAction, - rParms.mrCurrActionIndex ) ); + ActionSharedPtr pPolyAction( + internal::PolyPolyActionFactory::createPolyPolyAction( + aDevicePoly, + rParms.mrCanvas, + getState( rParms.mrStates ), + aTexture ) ); - rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1; + if( pPolyAction ) + { + maActions.push_back( + MtfAction( + pPolyAction, + rParms.mrCurrActionIndex ) ); + + rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1; + } + + // done, using native gradients + return; } - - // done, using native gradients - return; } }