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