1*cdf0e10cSrcweir /************************************************************************* 2*cdf0e10cSrcweir * 3*cdf0e10cSrcweir * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4*cdf0e10cSrcweir * 5*cdf0e10cSrcweir * Copyright 2000, 2010 Oracle and/or its affiliates. 6*cdf0e10cSrcweir * 7*cdf0e10cSrcweir * OpenOffice.org - a multi-platform office productivity suite 8*cdf0e10cSrcweir * 9*cdf0e10cSrcweir * This file is part of OpenOffice.org. 10*cdf0e10cSrcweir * 11*cdf0e10cSrcweir * OpenOffice.org is free software: you can redistribute it and/or modify 12*cdf0e10cSrcweir * it under the terms of the GNU Lesser General Public License version 3 13*cdf0e10cSrcweir * only, as published by the Free Software Foundation. 14*cdf0e10cSrcweir * 15*cdf0e10cSrcweir * OpenOffice.org is distributed in the hope that it will be useful, 16*cdf0e10cSrcweir * but WITHOUT ANY WARRANTY; without even the implied warranty of 17*cdf0e10cSrcweir * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18*cdf0e10cSrcweir * GNU Lesser General Public License version 3 for more details 19*cdf0e10cSrcweir * (a copy is included in the LICENSE file that accompanied this code). 20*cdf0e10cSrcweir * 21*cdf0e10cSrcweir * You should have received a copy of the GNU Lesser General Public License 22*cdf0e10cSrcweir * version 3 along with OpenOffice.org. If not, see 23*cdf0e10cSrcweir * <http://www.openoffice.org/license.html> 24*cdf0e10cSrcweir * for a copy of the LGPLv3 License. 25*cdf0e10cSrcweir * 26*cdf0e10cSrcweir ************************************************************************/ 27*cdf0e10cSrcweir 28*cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove 29*cdf0e10cSrcweir #include "precompiled_canvas.hxx" 30*cdf0e10cSrcweir 31*cdf0e10cSrcweir #include <canvas/debug.hxx> 32*cdf0e10cSrcweir #include <tools/diagnose_ex.h> 33*cdf0e10cSrcweir #include <rtl/math.hxx> 34*cdf0e10cSrcweir 35*cdf0e10cSrcweir #include <com/sun/star/rendering/TexturingMode.hpp> 36*cdf0e10cSrcweir 37*cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrix.hxx> 38*cdf0e10cSrcweir #include <basegfx/point/b2dpoint.hxx> 39*cdf0e10cSrcweir #include <basegfx/range/b2drectangle.hxx> 40*cdf0e10cSrcweir #include <basegfx/numeric/ftools.hxx> 41*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygontools.hxx> 42*cdf0e10cSrcweir #include <basegfx/tools/tools.hxx> 43*cdf0e10cSrcweir #include <basegfx/tools/lerp.hxx> 44*cdf0e10cSrcweir #include <basegfx/tools/keystoplerp.hxx> 45*cdf0e10cSrcweir #include <basegfx/tools/canvastools.hxx> 46*cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrixtools.hxx> 47*cdf0e10cSrcweir 48*cdf0e10cSrcweir #include <canvas/parametricpolypolygon.hxx> 49*cdf0e10cSrcweir 50*cdf0e10cSrcweir #include "dx_spritecanvas.hxx" 51*cdf0e10cSrcweir #include "dx_canvashelper.hxx" 52*cdf0e10cSrcweir #include "dx_impltools.hxx" 53*cdf0e10cSrcweir 54*cdf0e10cSrcweir #include <boost/scoped_ptr.hpp> 55*cdf0e10cSrcweir #include <boost/bind.hpp> 56*cdf0e10cSrcweir #include <boost/tuple/tuple.hpp> 57*cdf0e10cSrcweir 58*cdf0e10cSrcweir 59*cdf0e10cSrcweir using namespace ::com::sun::star; 60*cdf0e10cSrcweir 61*cdf0e10cSrcweir namespace dxcanvas 62*cdf0e10cSrcweir { 63*cdf0e10cSrcweir namespace 64*cdf0e10cSrcweir { 65*cdf0e10cSrcweir typedef ::boost::shared_ptr< Gdiplus::PathGradientBrush > PathGradientBrushSharedPtr; 66*cdf0e10cSrcweir 67*cdf0e10cSrcweir bool fillLinearGradient( GraphicsSharedPtr& rGraphics, 68*cdf0e10cSrcweir const ::canvas::ParametricPolyPolygon::Values& /*rValues*/, 69*cdf0e10cSrcweir const std::vector< Gdiplus::Color >& rColors, 70*cdf0e10cSrcweir const std::vector< Gdiplus::REAL >& rStops, 71*cdf0e10cSrcweir const GraphicsPathSharedPtr& rFillPath, 72*cdf0e10cSrcweir const rendering::Texture& texture ) 73*cdf0e10cSrcweir { 74*cdf0e10cSrcweir // setup a linear gradient with given colors 75*cdf0e10cSrcweir // ----------------------------------------- 76*cdf0e10cSrcweir 77*cdf0e10cSrcweir Gdiplus::LinearGradientBrush aBrush( 78*cdf0e10cSrcweir Gdiplus::PointF(0.0f, 79*cdf0e10cSrcweir 0.5f), 80*cdf0e10cSrcweir Gdiplus::PointF(1.0f, 81*cdf0e10cSrcweir 0.5f), 82*cdf0e10cSrcweir rColors[0], 83*cdf0e10cSrcweir rColors[1] ); 84*cdf0e10cSrcweir 85*cdf0e10cSrcweir aBrush.SetInterpolationColors(&rColors[0], 86*cdf0e10cSrcweir &rStops[0], 87*cdf0e10cSrcweir rColors.size()); 88*cdf0e10cSrcweir 89*cdf0e10cSrcweir // render background color, as LinearGradientBrush does not 90*cdf0e10cSrcweir // properly support the WrapModeClamp repeat mode 91*cdf0e10cSrcweir Gdiplus::SolidBrush aBackgroundBrush( rColors[0] ); 92*cdf0e10cSrcweir rGraphics->FillPath( &aBackgroundBrush, rFillPath.get() ); 93*cdf0e10cSrcweir 94*cdf0e10cSrcweir // TODO(F2): This does not yet support other repeat modes 95*cdf0e10cSrcweir // except clamp, and probably also no multi-texturing 96*cdf0e10cSrcweir 97*cdf0e10cSrcweir // calculate parallelogram of gradient in object space, extend 98*cdf0e10cSrcweir // top and bottom of it such that they cover the whole fill 99*cdf0e10cSrcweir // path bound area 100*cdf0e10cSrcweir ::basegfx::B2DHomMatrix aTextureTransform; 101*cdf0e10cSrcweir ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform, 102*cdf0e10cSrcweir texture.AffineTransform ); 103*cdf0e10cSrcweir 104*cdf0e10cSrcweir ::basegfx::B2DPoint aLeftTop( 0.0, 0.0 ); 105*cdf0e10cSrcweir ::basegfx::B2DPoint aLeftBottom( 0.0, 1.0 ); 106*cdf0e10cSrcweir ::basegfx::B2DPoint aRightTop( 1.0, 0.0 ); 107*cdf0e10cSrcweir ::basegfx::B2DPoint aRightBottom( 1.0, 1.0 ); 108*cdf0e10cSrcweir 109*cdf0e10cSrcweir aLeftTop *= aTextureTransform; 110*cdf0e10cSrcweir aLeftBottom *= aTextureTransform; 111*cdf0e10cSrcweir aRightTop *= aTextureTransform; 112*cdf0e10cSrcweir aRightBottom*= aTextureTransform; 113*cdf0e10cSrcweir 114*cdf0e10cSrcweir Gdiplus::RectF aBounds; 115*cdf0e10cSrcweir rFillPath->GetBounds( &aBounds, NULL, NULL ); 116*cdf0e10cSrcweir 117*cdf0e10cSrcweir // now, we potentially have to enlarge our gradient area 118*cdf0e10cSrcweir // atop and below the transformed [0,1]x[0,1] unit rect, 119*cdf0e10cSrcweir // for the gradient to fill the complete bound rect. 120*cdf0e10cSrcweir ::basegfx::tools::infiniteLineFromParallelogram( aLeftTop, 121*cdf0e10cSrcweir aLeftBottom, 122*cdf0e10cSrcweir aRightTop, 123*cdf0e10cSrcweir aRightBottom, 124*cdf0e10cSrcweir tools::b2dRangeFromGdiPlusRectF( aBounds ) ); 125*cdf0e10cSrcweir 126*cdf0e10cSrcweir // calc length of bound rect diagonal 127*cdf0e10cSrcweir const double nDiagonalLength( 128*cdf0e10cSrcweir hypot( aBounds.Width, 129*cdf0e10cSrcweir aBounds.Height ) ); 130*cdf0e10cSrcweir 131*cdf0e10cSrcweir // generate a path which covers the 'right' side of the 132*cdf0e10cSrcweir // gradient, extending two times the bound rect diagonal to 133*cdf0e10cSrcweir // the right (and thus covering the whole half plane 'right' 134*cdf0e10cSrcweir // of the gradient). Take the middle of the gradient as the 135*cdf0e10cSrcweir // 'left' side of the polygon, to not fall victim to rounding 136*cdf0e10cSrcweir // errors at the edge. 137*cdf0e10cSrcweir ::basegfx::B2DVector aDirection( aLeftTop - aLeftBottom ); 138*cdf0e10cSrcweir aDirection = ::basegfx::getNormalizedPerpendicular( aDirection ); 139*cdf0e10cSrcweir aDirection *= nDiagonalLength; 140*cdf0e10cSrcweir 141*cdf0e10cSrcweir const ::basegfx::B2DPoint aHalfPlaneLeftTop( (aLeftTop + aRightTop) * 0.5 ); 142*cdf0e10cSrcweir const ::basegfx::B2DPoint aHalfPlaneLeftBottom( (aLeftBottom + aRightBottom) * 0.5 ); 143*cdf0e10cSrcweir const ::basegfx::B2DPoint aHalfPlaneRightTop( aRightTop + aDirection ); 144*cdf0e10cSrcweir const ::basegfx::B2DPoint aHalfPlaneRightBottom( aRightBottom + aDirection ); 145*cdf0e10cSrcweir 146*cdf0e10cSrcweir Gdiplus::GraphicsPath aSolidFillPath; 147*cdf0e10cSrcweir aSolidFillPath.AddLine( static_cast<Gdiplus::REAL>(aHalfPlaneLeftTop.getX()), 148*cdf0e10cSrcweir static_cast<Gdiplus::REAL>(aHalfPlaneLeftTop.getY()), 149*cdf0e10cSrcweir static_cast<Gdiplus::REAL>(aHalfPlaneRightTop.getX()), 150*cdf0e10cSrcweir static_cast<Gdiplus::REAL>(aHalfPlaneRightTop.getY()) ); 151*cdf0e10cSrcweir aSolidFillPath.AddLine( static_cast<Gdiplus::REAL>(aHalfPlaneRightBottom.getX()), 152*cdf0e10cSrcweir static_cast<Gdiplus::REAL>(aHalfPlaneRightBottom.getY()), 153*cdf0e10cSrcweir static_cast<Gdiplus::REAL>(aHalfPlaneLeftBottom.getX()), 154*cdf0e10cSrcweir static_cast<Gdiplus::REAL>(aHalfPlaneLeftBottom.getY()) ); 155*cdf0e10cSrcweir aSolidFillPath.CloseFigure(); 156*cdf0e10cSrcweir 157*cdf0e10cSrcweir // limit output to fill path, we've just generated a path that 158*cdf0e10cSrcweir // might be substantially larger 159*cdf0e10cSrcweir if( Gdiplus::Ok != rGraphics->SetClip( rFillPath.get(), 160*cdf0e10cSrcweir Gdiplus::CombineModeIntersect ) ) 161*cdf0e10cSrcweir { 162*cdf0e10cSrcweir return false; 163*cdf0e10cSrcweir } 164*cdf0e10cSrcweir 165*cdf0e10cSrcweir Gdiplus::SolidBrush aBackgroundBrush2( rColors.back() ); 166*cdf0e10cSrcweir rGraphics->FillPath( &aBackgroundBrush2, &aSolidFillPath ); 167*cdf0e10cSrcweir 168*cdf0e10cSrcweir // generate clip polygon from the extended parallelogram 169*cdf0e10cSrcweir // (exploit the feature that distinct lines in a figure are 170*cdf0e10cSrcweir // automatically closed by a straight line) 171*cdf0e10cSrcweir Gdiplus::GraphicsPath aClipPath; 172*cdf0e10cSrcweir aClipPath.AddLine( static_cast<Gdiplus::REAL>(aLeftTop.getX()), 173*cdf0e10cSrcweir static_cast<Gdiplus::REAL>(aLeftTop.getY()), 174*cdf0e10cSrcweir static_cast<Gdiplus::REAL>(aRightTop.getX()), 175*cdf0e10cSrcweir static_cast<Gdiplus::REAL>(aRightTop.getY()) ); 176*cdf0e10cSrcweir aClipPath.AddLine( static_cast<Gdiplus::REAL>(aRightBottom.getX()), 177*cdf0e10cSrcweir static_cast<Gdiplus::REAL>(aRightBottom.getY()), 178*cdf0e10cSrcweir static_cast<Gdiplus::REAL>(aLeftBottom.getX()), 179*cdf0e10cSrcweir static_cast<Gdiplus::REAL>(aLeftBottom.getY()) ); 180*cdf0e10cSrcweir aClipPath.CloseFigure(); 181*cdf0e10cSrcweir 182*cdf0e10cSrcweir // limit output to a _single_ strip of the gradient (have to 183*cdf0e10cSrcweir // clip here, since GDI+ wrapmode clamp does not work here) 184*cdf0e10cSrcweir if( Gdiplus::Ok != rGraphics->SetClip( &aClipPath, 185*cdf0e10cSrcweir Gdiplus::CombineModeIntersect ) ) 186*cdf0e10cSrcweir { 187*cdf0e10cSrcweir return false; 188*cdf0e10cSrcweir } 189*cdf0e10cSrcweir 190*cdf0e10cSrcweir // now, finally, output the gradient 191*cdf0e10cSrcweir Gdiplus::Matrix aMatrix; 192*cdf0e10cSrcweir tools::gdiPlusMatrixFromAffineMatrix2D( aMatrix, 193*cdf0e10cSrcweir texture.AffineTransform ); 194*cdf0e10cSrcweir aBrush.SetTransform( &aMatrix ); 195*cdf0e10cSrcweir 196*cdf0e10cSrcweir rGraphics->FillRectangle( &aBrush, aBounds ); 197*cdf0e10cSrcweir 198*cdf0e10cSrcweir return true; 199*cdf0e10cSrcweir } 200*cdf0e10cSrcweir 201*cdf0e10cSrcweir int numColorSteps( const Gdiplus::Color& rColor1, const Gdiplus::Color& rColor2 ) 202*cdf0e10cSrcweir { 203*cdf0e10cSrcweir return ::std::max( 204*cdf0e10cSrcweir labs( rColor1.GetRed() - rColor2.GetRed() ), 205*cdf0e10cSrcweir ::std::max( 206*cdf0e10cSrcweir labs( rColor1.GetGreen() - rColor2.GetGreen() ), 207*cdf0e10cSrcweir labs( rColor1.GetBlue() - rColor2.GetBlue() ) ) ); 208*cdf0e10cSrcweir } 209*cdf0e10cSrcweir 210*cdf0e10cSrcweir bool fillPolygonalGradient( const ::canvas::ParametricPolyPolygon::Values& rValues, 211*cdf0e10cSrcweir const std::vector< Gdiplus::Color >& rColors, 212*cdf0e10cSrcweir const std::vector< Gdiplus::REAL >& rStops, 213*cdf0e10cSrcweir GraphicsSharedPtr& rGraphics, 214*cdf0e10cSrcweir const GraphicsPathSharedPtr& rPath, 215*cdf0e10cSrcweir const rendering::ViewState& viewState, 216*cdf0e10cSrcweir const rendering::RenderState& renderState, 217*cdf0e10cSrcweir const rendering::Texture& texture ) 218*cdf0e10cSrcweir { 219*cdf0e10cSrcweir // copy original fill path object, might have to change it 220*cdf0e10cSrcweir // below 221*cdf0e10cSrcweir GraphicsPathSharedPtr pFillPath( rPath ); 222*cdf0e10cSrcweir const ::basegfx::B2DPolygon& rGradientPoly( rValues.maGradientPoly ); 223*cdf0e10cSrcweir 224*cdf0e10cSrcweir PathGradientBrushSharedPtr pGradientBrush; 225*cdf0e10cSrcweir 226*cdf0e10cSrcweir // fill background uniformly with end color 227*cdf0e10cSrcweir Gdiplus::SolidBrush aBackgroundBrush( rColors[0] ); 228*cdf0e10cSrcweir rGraphics->FillPath( &aBackgroundBrush, pFillPath.get() ); 229*cdf0e10cSrcweir 230*cdf0e10cSrcweir Gdiplus::Matrix aMatrix; 231*cdf0e10cSrcweir // scale focus according to aspect ratio: for wider-than-tall 232*cdf0e10cSrcweir // bounds (nAspectRatio > 1.0), the focus must have non-zero 233*cdf0e10cSrcweir // width. Specifically, a bound rect twice as wide as tall has 234*cdf0e10cSrcweir // a focus of half it's width. 235*cdf0e10cSrcweir if( !::rtl::math::approxEqual(rValues.mnAspectRatio, 236*cdf0e10cSrcweir 1.0) ) 237*cdf0e10cSrcweir { 238*cdf0e10cSrcweir // KLUDGE 1: 239*cdf0e10cSrcweir // 240*cdf0e10cSrcweir // And here comes the greatest shortcoming of the GDI+ 241*cdf0e10cSrcweir // gradients ever: SetFocusScales completely ignores 242*cdf0e10cSrcweir // transformations, both when set at the PathGradientBrush 243*cdf0e10cSrcweir // and for the world coordinate system. Thus, to correctly 244*cdf0e10cSrcweir // display anisotrophic path gradients, we have to render 245*cdf0e10cSrcweir // them by hand. WTF. 246*cdf0e10cSrcweir 247*cdf0e10cSrcweir // TODO(F2): This does not yet support other repeat modes 248*cdf0e10cSrcweir // except clamp, and probably also no multi-texturing 249*cdf0e10cSrcweir 250*cdf0e10cSrcweir // limit output to to-be-filled polygon 251*cdf0e10cSrcweir if( Gdiplus::Ok != rGraphics->SetClip( pFillPath.get(), 252*cdf0e10cSrcweir Gdiplus::CombineModeIntersect ) ) 253*cdf0e10cSrcweir { 254*cdf0e10cSrcweir return false; 255*cdf0e10cSrcweir } 256*cdf0e10cSrcweir 257*cdf0e10cSrcweir // disable anti-aliasing, if any 258*cdf0e10cSrcweir const Gdiplus::SmoothingMode eOldAAMode( rGraphics->GetSmoothingMode() ); 259*cdf0e10cSrcweir rGraphics->SetSmoothingMode( Gdiplus::SmoothingModeHighSpeed ); 260*cdf0e10cSrcweir 261*cdf0e10cSrcweir 262*cdf0e10cSrcweir // determine number of steps to use 263*cdf0e10cSrcweir // -------------------------------- 264*cdf0e10cSrcweir 265*cdf0e10cSrcweir // TODO(Q2): Unify step calculations with VCL canvas 266*cdf0e10cSrcweir int nColorSteps = 0; 267*cdf0e10cSrcweir for( size_t i=0; i<rColors.size()-1; ++i ) 268*cdf0e10cSrcweir nColorSteps += numColorSteps(rColors[i],rColors[i+1]); 269*cdf0e10cSrcweir ::basegfx::B2DHomMatrix aTotalTransform; 270*cdf0e10cSrcweir const int nStepCount= 271*cdf0e10cSrcweir ::canvas::tools::calcGradientStepCount(aTotalTransform, 272*cdf0e10cSrcweir viewState, 273*cdf0e10cSrcweir renderState, 274*cdf0e10cSrcweir texture, 275*cdf0e10cSrcweir nColorSteps); 276*cdf0e10cSrcweir 277*cdf0e10cSrcweir ::basegfx::B2DHomMatrix aTextureTransform; 278*cdf0e10cSrcweir ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform, 279*cdf0e10cSrcweir texture.AffineTransform ); 280*cdf0e10cSrcweir // determine overall transformation for inner polygon (might 281*cdf0e10cSrcweir // have to be prefixed by anisotrophic scaling) 282*cdf0e10cSrcweir ::basegfx::B2DHomMatrix aInnerPolygonTransformMatrix; 283*cdf0e10cSrcweir 284*cdf0e10cSrcweir // For performance reasons, we create a temporary VCL polygon 285*cdf0e10cSrcweir // here, keep it all the way and only change the vertex values 286*cdf0e10cSrcweir // in the loop below (as ::Polygon is a pimpl class, creating 287*cdf0e10cSrcweir // one every loop turn would really stress the mem allocator) 288*cdf0e10cSrcweir ::basegfx::B2DPolygon aOuterPoly( rGradientPoly ); 289*cdf0e10cSrcweir ::basegfx::B2DPolygon aInnerPoly; 290*cdf0e10cSrcweir 291*cdf0e10cSrcweir // subdivide polygon _before_ rendering, would otherwise have 292*cdf0e10cSrcweir // to be performed on every loop turn. 293*cdf0e10cSrcweir if( aOuterPoly.areControlPointsUsed() ) 294*cdf0e10cSrcweir aOuterPoly = ::basegfx::tools::adaptiveSubdivideByAngle(aOuterPoly); 295*cdf0e10cSrcweir 296*cdf0e10cSrcweir aInnerPoly = aOuterPoly; 297*cdf0e10cSrcweir aOuterPoly.transform(aTextureTransform); 298*cdf0e10cSrcweir 299*cdf0e10cSrcweir 300*cdf0e10cSrcweir // apply scaling (possibly anisotrophic) to inner polygon 301*cdf0e10cSrcweir // ------------------------------------------------------ 302*cdf0e10cSrcweir 303*cdf0e10cSrcweir // scale inner polygon according to aspect ratio: for 304*cdf0e10cSrcweir // wider-than-tall bounds (nAspectRatio > 1.0), the inner 305*cdf0e10cSrcweir // polygon, representing the gradient focus, must have 306*cdf0e10cSrcweir // non-zero width. Specifically, a bound rect twice as wide as 307*cdf0e10cSrcweir // tall has a focus polygon of half it's width. 308*cdf0e10cSrcweir const double nAspectRatio( rValues.mnAspectRatio ); 309*cdf0e10cSrcweir if( nAspectRatio > 1.0 ) 310*cdf0e10cSrcweir { 311*cdf0e10cSrcweir // width > height case 312*cdf0e10cSrcweir aInnerPolygonTransformMatrix.scale( 1.0 - 1.0/nAspectRatio, 313*cdf0e10cSrcweir 0.0 ); 314*cdf0e10cSrcweir } 315*cdf0e10cSrcweir else if( nAspectRatio < 1.0 ) 316*cdf0e10cSrcweir { 317*cdf0e10cSrcweir // width < height case 318*cdf0e10cSrcweir aInnerPolygonTransformMatrix.scale( 0.0, 319*cdf0e10cSrcweir 1.0 - nAspectRatio ); 320*cdf0e10cSrcweir } 321*cdf0e10cSrcweir else 322*cdf0e10cSrcweir { 323*cdf0e10cSrcweir // isotrophic case 324*cdf0e10cSrcweir aInnerPolygonTransformMatrix.scale( 0.0, 0.0 ); 325*cdf0e10cSrcweir } 326*cdf0e10cSrcweir 327*cdf0e10cSrcweir // and finally, add texture transform to it. 328*cdf0e10cSrcweir aInnerPolygonTransformMatrix *= aTextureTransform; 329*cdf0e10cSrcweir 330*cdf0e10cSrcweir // apply final matrix to polygon 331*cdf0e10cSrcweir aInnerPoly.transform( aInnerPolygonTransformMatrix ); 332*cdf0e10cSrcweir 333*cdf0e10cSrcweir Gdiplus::GraphicsPath aCurrPath; 334*cdf0e10cSrcweir Gdiplus::SolidBrush aFillBrush( rColors[0] ); 335*cdf0e10cSrcweir const sal_uInt32 nNumPoints( aOuterPoly.count() ); 336*cdf0e10cSrcweir basegfx::tools::KeyStopLerp aLerper(rValues.maStops); 337*cdf0e10cSrcweir for( int i=1; i<nStepCount; ++i ) 338*cdf0e10cSrcweir { 339*cdf0e10cSrcweir std::ptrdiff_t nIndex; 340*cdf0e10cSrcweir double fAlpha; 341*cdf0e10cSrcweir const double fT( i/double(nStepCount) ); 342*cdf0e10cSrcweir boost::tuples::tie(nIndex,fAlpha)=aLerper.lerp(fT); 343*cdf0e10cSrcweir 344*cdf0e10cSrcweir const Gdiplus::Color aFillColor( 345*cdf0e10cSrcweir static_cast<BYTE>( basegfx::tools::lerp(rColors[nIndex].GetRed(),rColors[nIndex+1].GetRed(),fAlpha) ), 346*cdf0e10cSrcweir static_cast<BYTE>( basegfx::tools::lerp(rColors[nIndex].GetGreen(),rColors[nIndex+1].GetGreen(),fAlpha) ), 347*cdf0e10cSrcweir static_cast<BYTE>( basegfx::tools::lerp(rColors[nIndex].GetBlue(),rColors[nIndex+1].GetBlue(),fAlpha) ) ); 348*cdf0e10cSrcweir 349*cdf0e10cSrcweir aFillBrush.SetColor( aFillColor ); 350*cdf0e10cSrcweir aCurrPath.Reset(); aCurrPath.StartFigure(); 351*cdf0e10cSrcweir for( unsigned int p=1; p<nNumPoints; ++p ) 352*cdf0e10cSrcweir { 353*cdf0e10cSrcweir const ::basegfx::B2DPoint& rOuterPoint1( aOuterPoly.getB2DPoint(p-1) ); 354*cdf0e10cSrcweir const ::basegfx::B2DPoint& rInnerPoint1( aInnerPoly.getB2DPoint(p-1) ); 355*cdf0e10cSrcweir const ::basegfx::B2DPoint& rOuterPoint2( aOuterPoly.getB2DPoint(p) ); 356*cdf0e10cSrcweir const ::basegfx::B2DPoint& rInnerPoint2( aInnerPoly.getB2DPoint(p) ); 357*cdf0e10cSrcweir 358*cdf0e10cSrcweir aCurrPath.AddLine( 359*cdf0e10cSrcweir Gdiplus::REAL(fT*rInnerPoint1.getX() + (1-fT)*rOuterPoint1.getX()), 360*cdf0e10cSrcweir Gdiplus::REAL(fT*rInnerPoint1.getY() + (1-fT)*rOuterPoint1.getY()), 361*cdf0e10cSrcweir Gdiplus::REAL(fT*rInnerPoint2.getX() + (1-fT)*rOuterPoint2.getX()), 362*cdf0e10cSrcweir Gdiplus::REAL(fT*rInnerPoint2.getY() + (1-fT)*rOuterPoint2.getY())); 363*cdf0e10cSrcweir } 364*cdf0e10cSrcweir aCurrPath.CloseFigure(); 365*cdf0e10cSrcweir 366*cdf0e10cSrcweir rGraphics->FillPath( &aFillBrush, &aCurrPath ); 367*cdf0e10cSrcweir } 368*cdf0e10cSrcweir 369*cdf0e10cSrcweir // reset to old anti-alias mode 370*cdf0e10cSrcweir rGraphics->SetSmoothingMode( eOldAAMode ); 371*cdf0e10cSrcweir } 372*cdf0e10cSrcweir else 373*cdf0e10cSrcweir { 374*cdf0e10cSrcweir // KLUDGE 2: 375*cdf0e10cSrcweir // 376*cdf0e10cSrcweir // We're generating a PathGradientBrush from scratch here, 377*cdf0e10cSrcweir // and put in a transformed GraphicsPath (transformed with 378*cdf0e10cSrcweir // the texture transform). This is because the 379*cdf0e10cSrcweir // straight-forward approach to store a Brush pointer at 380*cdf0e10cSrcweir // this class and set a texture transform via 381*cdf0e10cSrcweir // PathGradientBrush::SetTransform() is spoiled by MS: it 382*cdf0e10cSrcweir // seems that _either_ the texture transform, _or_ the 383*cdf0e10cSrcweir // transform at the Graphics can be set, but not both. If 384*cdf0e10cSrcweir // one sets both, only the translational components of the 385*cdf0e10cSrcweir // texture is respected. 386*cdf0e10cSrcweir 387*cdf0e10cSrcweir tools::gdiPlusMatrixFromAffineMatrix2D( aMatrix, 388*cdf0e10cSrcweir texture.AffineTransform ); 389*cdf0e10cSrcweir GraphicsPathSharedPtr pGradientPath( 390*cdf0e10cSrcweir tools::graphicsPathFromB2DPolygon( rValues.maGradientPoly )); 391*cdf0e10cSrcweir pGradientPath->Transform( &aMatrix ); 392*cdf0e10cSrcweir 393*cdf0e10cSrcweir pGradientBrush.reset( 394*cdf0e10cSrcweir new Gdiplus::PathGradientBrush( pGradientPath.get() ) ); 395*cdf0e10cSrcweir pGradientBrush->SetInterpolationColors( &rColors[0], 396*cdf0e10cSrcweir &rStops[0], 397*cdf0e10cSrcweir rStops.size() ); 398*cdf0e10cSrcweir 399*cdf0e10cSrcweir // explicitely setup center point. Since the center of GDI+ 400*cdf0e10cSrcweir // gradients are by default the _centroid_ of the path 401*cdf0e10cSrcweir // (i.e. the weighted sum of edge points), it will not 402*cdf0e10cSrcweir // necessarily coincide with our notion of center. 403*cdf0e10cSrcweir Gdiplus::PointF aCenterPoint(0, 0); 404*cdf0e10cSrcweir aMatrix.TransformPoints( &aCenterPoint ); 405*cdf0e10cSrcweir pGradientBrush->SetCenterPoint( aCenterPoint ); 406*cdf0e10cSrcweir 407*cdf0e10cSrcweir const bool bTileX( texture.RepeatModeX != rendering::TexturingMode::CLAMP ); 408*cdf0e10cSrcweir const bool bTileY( texture.RepeatModeY != rendering::TexturingMode::CLAMP ); 409*cdf0e10cSrcweir 410*cdf0e10cSrcweir if( bTileX && bTileY ) 411*cdf0e10cSrcweir pGradientBrush->SetWrapMode( Gdiplus::WrapModeTile ); 412*cdf0e10cSrcweir else 413*cdf0e10cSrcweir { 414*cdf0e10cSrcweir OSL_ENSURE( bTileY == bTileX, 415*cdf0e10cSrcweir "ParametricPolyPolygon::fillPolygonalGradient(): Cannot have repeat x and repeat y differ!" ); 416*cdf0e10cSrcweir 417*cdf0e10cSrcweir pGradientBrush->SetWrapMode( Gdiplus::WrapModeClamp ); 418*cdf0e10cSrcweir } 419*cdf0e10cSrcweir 420*cdf0e10cSrcweir // render actual gradient 421*cdf0e10cSrcweir rGraphics->FillPath( pGradientBrush.get(), pFillPath.get() ); 422*cdf0e10cSrcweir } 423*cdf0e10cSrcweir 424*cdf0e10cSrcweir #if defined(VERBOSE) && defined(DBG_UTIL) 425*cdf0e10cSrcweir Gdiplus::Pen aPen( Gdiplus::Color( 255, 255, 0, 0 ), 426*cdf0e10cSrcweir 0.0001f ); 427*cdf0e10cSrcweir 428*cdf0e10cSrcweir rGraphics->DrawRectangle( &aPen, 429*cdf0e10cSrcweir Gdiplus::RectF( 0.0f, 0.0f, 430*cdf0e10cSrcweir 1.0f, 1.0f ) ); 431*cdf0e10cSrcweir #endif 432*cdf0e10cSrcweir 433*cdf0e10cSrcweir return true; 434*cdf0e10cSrcweir } 435*cdf0e10cSrcweir 436*cdf0e10cSrcweir bool fillGradient( const ::canvas::ParametricPolyPolygon::Values& rValues, 437*cdf0e10cSrcweir const std::vector< Gdiplus::Color >& rColors, 438*cdf0e10cSrcweir const std::vector< Gdiplus::REAL >& rStops, 439*cdf0e10cSrcweir GraphicsSharedPtr& rGraphics, 440*cdf0e10cSrcweir const GraphicsPathSharedPtr& rPath, 441*cdf0e10cSrcweir const rendering::ViewState& viewState, 442*cdf0e10cSrcweir const rendering::RenderState& renderState, 443*cdf0e10cSrcweir const rendering::Texture& texture ) 444*cdf0e10cSrcweir { 445*cdf0e10cSrcweir switch( rValues.meType ) 446*cdf0e10cSrcweir { 447*cdf0e10cSrcweir case ::canvas::ParametricPolyPolygon::GRADIENT_LINEAR: 448*cdf0e10cSrcweir fillLinearGradient( rGraphics, 449*cdf0e10cSrcweir rValues, 450*cdf0e10cSrcweir rColors, 451*cdf0e10cSrcweir rStops, 452*cdf0e10cSrcweir rPath, 453*cdf0e10cSrcweir texture ); 454*cdf0e10cSrcweir break; 455*cdf0e10cSrcweir 456*cdf0e10cSrcweir case ::canvas::ParametricPolyPolygon::GRADIENT_ELLIPTICAL: 457*cdf0e10cSrcweir // FALLTHROUGH intended 458*cdf0e10cSrcweir case ::canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR: 459*cdf0e10cSrcweir fillPolygonalGradient( rValues, 460*cdf0e10cSrcweir rColors, 461*cdf0e10cSrcweir rStops, 462*cdf0e10cSrcweir rGraphics, 463*cdf0e10cSrcweir rPath, 464*cdf0e10cSrcweir viewState, 465*cdf0e10cSrcweir renderState, 466*cdf0e10cSrcweir texture ); 467*cdf0e10cSrcweir break; 468*cdf0e10cSrcweir 469*cdf0e10cSrcweir default: 470*cdf0e10cSrcweir ENSURE_OR_THROW( false, 471*cdf0e10cSrcweir "CanvasHelper::fillGradient(): Unexpected case" ); 472*cdf0e10cSrcweir } 473*cdf0e10cSrcweir 474*cdf0e10cSrcweir return true; 475*cdf0e10cSrcweir } 476*cdf0e10cSrcweir 477*cdf0e10cSrcweir void fillBitmap( const uno::Reference< rendering::XBitmap >& xBitmap, 478*cdf0e10cSrcweir GraphicsSharedPtr& rGraphics, 479*cdf0e10cSrcweir const GraphicsPathSharedPtr& rPath, 480*cdf0e10cSrcweir const rendering::Texture& rTexture ) 481*cdf0e10cSrcweir { 482*cdf0e10cSrcweir OSL_ENSURE( rTexture.RepeatModeX == 483*cdf0e10cSrcweir rTexture.RepeatModeY, 484*cdf0e10cSrcweir "CanvasHelper::fillBitmap(): GDI+ cannot handle differing X/Y repeat mode." ); 485*cdf0e10cSrcweir 486*cdf0e10cSrcweir const bool bClamp( rTexture.RepeatModeX == rendering::TexturingMode::NONE && 487*cdf0e10cSrcweir rTexture.RepeatModeY == rendering::TexturingMode::NONE ); 488*cdf0e10cSrcweir 489*cdf0e10cSrcweir const geometry::IntegerSize2D aBmpSize( xBitmap->getSize() ); 490*cdf0e10cSrcweir ENSURE_ARG_OR_THROW( aBmpSize.Width != 0 && 491*cdf0e10cSrcweir aBmpSize.Height != 0, 492*cdf0e10cSrcweir "CanvasHelper::fillBitmap(): zero-sized texture bitmap" ); 493*cdf0e10cSrcweir 494*cdf0e10cSrcweir // TODO(P3): Detect case that path is rectangle and 495*cdf0e10cSrcweir // bitmap is just scaled into that. Then, we can 496*cdf0e10cSrcweir // render directly, without generating a temporary 497*cdf0e10cSrcweir // GDI+ bitmap (this is significant, because drawing 498*cdf0e10cSrcweir // layer presents background object bitmap in that 499*cdf0e10cSrcweir // way!) 500*cdf0e10cSrcweir BitmapSharedPtr pBitmap( 501*cdf0e10cSrcweir tools::bitmapFromXBitmap( xBitmap ) ); 502*cdf0e10cSrcweir 503*cdf0e10cSrcweir TextureBrushSharedPtr pBrush; 504*cdf0e10cSrcweir if( ::rtl::math::approxEqual( rTexture.Alpha, 505*cdf0e10cSrcweir 1.0 ) ) 506*cdf0e10cSrcweir { 507*cdf0e10cSrcweir pBrush.reset( 508*cdf0e10cSrcweir new Gdiplus::TextureBrush( 509*cdf0e10cSrcweir pBitmap.get(), 510*cdf0e10cSrcweir bClamp ? Gdiplus::WrapModeClamp : Gdiplus::WrapModeTile ) ); 511*cdf0e10cSrcweir } 512*cdf0e10cSrcweir else 513*cdf0e10cSrcweir { 514*cdf0e10cSrcweir Gdiplus::ImageAttributes aImgAttr; 515*cdf0e10cSrcweir 516*cdf0e10cSrcweir tools::setModulateImageAttributes( aImgAttr, 517*cdf0e10cSrcweir 1.0, 518*cdf0e10cSrcweir 1.0, 519*cdf0e10cSrcweir 1.0, 520*cdf0e10cSrcweir rTexture.Alpha ); 521*cdf0e10cSrcweir 522*cdf0e10cSrcweir Gdiplus::Rect aRect(0,0, 523*cdf0e10cSrcweir aBmpSize.Width, 524*cdf0e10cSrcweir aBmpSize.Height); 525*cdf0e10cSrcweir pBrush.reset( 526*cdf0e10cSrcweir new Gdiplus::TextureBrush( 527*cdf0e10cSrcweir pBitmap.get(), 528*cdf0e10cSrcweir aRect, 529*cdf0e10cSrcweir &aImgAttr ) ); 530*cdf0e10cSrcweir 531*cdf0e10cSrcweir pBrush->SetWrapMode( 532*cdf0e10cSrcweir bClamp ? Gdiplus::WrapModeClamp : Gdiplus::WrapModeTile ); 533*cdf0e10cSrcweir } 534*cdf0e10cSrcweir 535*cdf0e10cSrcweir Gdiplus::Matrix aTextureTransform; 536*cdf0e10cSrcweir tools::gdiPlusMatrixFromAffineMatrix2D( aTextureTransform, 537*cdf0e10cSrcweir rTexture.AffineTransform ); 538*cdf0e10cSrcweir 539*cdf0e10cSrcweir // scale down bitmap to [0,1]x[0,1] rect, as required 540*cdf0e10cSrcweir // from the XCanvas interface. 541*cdf0e10cSrcweir pBrush->MultiplyTransform( &aTextureTransform ); 542*cdf0e10cSrcweir pBrush->ScaleTransform( static_cast< Gdiplus::REAL >(1.0/aBmpSize.Width), 543*cdf0e10cSrcweir static_cast< Gdiplus::REAL >(1.0/aBmpSize.Height) ); 544*cdf0e10cSrcweir 545*cdf0e10cSrcweir // TODO(F1): FillRule 546*cdf0e10cSrcweir ENSURE_OR_THROW( 547*cdf0e10cSrcweir Gdiplus::Ok == rGraphics->FillPath( pBrush.get(), 548*cdf0e10cSrcweir rPath.get() ), 549*cdf0e10cSrcweir "CanvasHelper::fillTexturedPolyPolygon(): GDI+ call failed" ); 550*cdf0e10cSrcweir } 551*cdf0e10cSrcweir } 552*cdf0e10cSrcweir 553*cdf0e10cSrcweir // ------------------------------------------------------------- 554*cdf0e10cSrcweir 555*cdf0e10cSrcweir uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTexturedPolyPolygon( const rendering::XCanvas* /*pCanvas*/, 556*cdf0e10cSrcweir const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, 557*cdf0e10cSrcweir const rendering::ViewState& viewState, 558*cdf0e10cSrcweir const rendering::RenderState& renderState, 559*cdf0e10cSrcweir const uno::Sequence< rendering::Texture >& textures ) 560*cdf0e10cSrcweir { 561*cdf0e10cSrcweir ENSURE_OR_THROW( xPolyPolygon.is(), 562*cdf0e10cSrcweir "CanvasHelper::fillTexturedPolyPolygon: polygon is NULL"); 563*cdf0e10cSrcweir ENSURE_OR_THROW( textures.getLength(), 564*cdf0e10cSrcweir "CanvasHelper::fillTexturedPolyPolygon: empty texture sequence"); 565*cdf0e10cSrcweir 566*cdf0e10cSrcweir if( needOutput() ) 567*cdf0e10cSrcweir { 568*cdf0e10cSrcweir GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() ); 569*cdf0e10cSrcweir 570*cdf0e10cSrcweir setupGraphicsState( pGraphics, viewState, renderState ); 571*cdf0e10cSrcweir 572*cdf0e10cSrcweir // TODO(F1): Multi-texturing 573*cdf0e10cSrcweir if( textures[0].Gradient.is() ) 574*cdf0e10cSrcweir { 575*cdf0e10cSrcweir // try to cast XParametricPolyPolygon2D reference to 576*cdf0e10cSrcweir // our implementation class. 577*cdf0e10cSrcweir ::canvas::ParametricPolyPolygon* pGradient = 578*cdf0e10cSrcweir dynamic_cast< ::canvas::ParametricPolyPolygon* >( textures[0].Gradient.get() ); 579*cdf0e10cSrcweir 580*cdf0e10cSrcweir if( pGradient ) 581*cdf0e10cSrcweir { 582*cdf0e10cSrcweir const ::canvas::ParametricPolyPolygon::Values& rValues( 583*cdf0e10cSrcweir pGradient->getValues() ); 584*cdf0e10cSrcweir 585*cdf0e10cSrcweir OSL_ASSERT(rValues.maColors.getLength() == rValues.maStops.getLength() 586*cdf0e10cSrcweir && rValues.maColors.getLength() > 1); 587*cdf0e10cSrcweir 588*cdf0e10cSrcweir std::vector< Gdiplus::Color > aColors(rValues.maColors.getLength()); 589*cdf0e10cSrcweir std::transform(&rValues.maColors[0], 590*cdf0e10cSrcweir &rValues.maColors[0]+rValues.maColors.getLength(), 591*cdf0e10cSrcweir aColors.begin(), 592*cdf0e10cSrcweir boost::bind( 593*cdf0e10cSrcweir (Gdiplus::ARGB (*)( const uno::Sequence< double >& ))( 594*cdf0e10cSrcweir &tools::sequenceToArgb), 595*cdf0e10cSrcweir _1)); 596*cdf0e10cSrcweir std::vector< Gdiplus::REAL > aStops; 597*cdf0e10cSrcweir comphelper::sequenceToContainer(aStops,rValues.maStops); 598*cdf0e10cSrcweir 599*cdf0e10cSrcweir // TODO(E1): Return value 600*cdf0e10cSrcweir // TODO(F1): FillRule 601*cdf0e10cSrcweir fillGradient( rValues, 602*cdf0e10cSrcweir aColors, 603*cdf0e10cSrcweir aStops, 604*cdf0e10cSrcweir pGraphics, 605*cdf0e10cSrcweir tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ), 606*cdf0e10cSrcweir viewState, 607*cdf0e10cSrcweir renderState, 608*cdf0e10cSrcweir textures[0] ); 609*cdf0e10cSrcweir } 610*cdf0e10cSrcweir } 611*cdf0e10cSrcweir else if( textures[0].Bitmap.is() ) 612*cdf0e10cSrcweir { 613*cdf0e10cSrcweir // TODO(E1): Return value 614*cdf0e10cSrcweir // TODO(F1): FillRule 615*cdf0e10cSrcweir fillBitmap( textures[0].Bitmap, 616*cdf0e10cSrcweir pGraphics, 617*cdf0e10cSrcweir tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ), 618*cdf0e10cSrcweir textures[0] ); 619*cdf0e10cSrcweir } 620*cdf0e10cSrcweir } 621*cdf0e10cSrcweir 622*cdf0e10cSrcweir // TODO(P1): Provide caching here. 623*cdf0e10cSrcweir return uno::Reference< rendering::XCachedPrimitive >(NULL); 624*cdf0e10cSrcweir } 625*cdf0e10cSrcweir } 626