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 34*cdf0e10cSrcweir #include <rtl/math.hxx> 35*cdf0e10cSrcweir 36*cdf0e10cSrcweir #include <com/sun/star/rendering/TextDirection.hpp> 37*cdf0e10cSrcweir #include <com/sun/star/rendering/TexturingMode.hpp> 38*cdf0e10cSrcweir #include <com/sun/star/rendering/PathCapType.hpp> 39*cdf0e10cSrcweir #include <com/sun/star/rendering/PathJoinType.hpp> 40*cdf0e10cSrcweir 41*cdf0e10cSrcweir #include <tools/poly.hxx> 42*cdf0e10cSrcweir #include <vcl/window.hxx> 43*cdf0e10cSrcweir #include <vcl/bitmapex.hxx> 44*cdf0e10cSrcweir #include <vcl/bmpacc.hxx> 45*cdf0e10cSrcweir #include <vcl/virdev.hxx> 46*cdf0e10cSrcweir #include <vcl/canvastools.hxx> 47*cdf0e10cSrcweir 48*cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrix.hxx> 49*cdf0e10cSrcweir #include <basegfx/range/b2drectangle.hxx> 50*cdf0e10cSrcweir #include <basegfx/point/b2dpoint.hxx> 51*cdf0e10cSrcweir #include <basegfx/vector/b2dsize.hxx> 52*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygon.hxx> 53*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygontools.hxx> 54*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolypolygontools.hxx> 55*cdf0e10cSrcweir #include <basegfx/polygon/b2dlinegeometry.hxx> 56*cdf0e10cSrcweir #include <basegfx/tools/tools.hxx> 57*cdf0e10cSrcweir #include <basegfx/tools/lerp.hxx> 58*cdf0e10cSrcweir #include <basegfx/tools/keystoplerp.hxx> 59*cdf0e10cSrcweir #include <basegfx/tools/canvastools.hxx> 60*cdf0e10cSrcweir #include <basegfx/numeric/ftools.hxx> 61*cdf0e10cSrcweir 62*cdf0e10cSrcweir #include <comphelper/sequence.hxx> 63*cdf0e10cSrcweir 64*cdf0e10cSrcweir #include <canvas/canvastools.hxx> 65*cdf0e10cSrcweir #include <canvas/parametricpolypolygon.hxx> 66*cdf0e10cSrcweir 67*cdf0e10cSrcweir #include <boost/bind.hpp> 68*cdf0e10cSrcweir #include <boost/tuple/tuple.hpp> 69*cdf0e10cSrcweir 70*cdf0e10cSrcweir #include "spritecanvas.hxx" 71*cdf0e10cSrcweir #include "canvashelper.hxx" 72*cdf0e10cSrcweir #include "impltools.hxx" 73*cdf0e10cSrcweir 74*cdf0e10cSrcweir 75*cdf0e10cSrcweir using namespace ::com::sun::star; 76*cdf0e10cSrcweir 77*cdf0e10cSrcweir namespace vclcanvas 78*cdf0e10cSrcweir { 79*cdf0e10cSrcweir namespace 80*cdf0e10cSrcweir { 81*cdf0e10cSrcweir bool textureFill( OutputDevice& rOutDev, 82*cdf0e10cSrcweir GraphicObject& rGraphic, 83*cdf0e10cSrcweir const ::Point& rPosPixel, 84*cdf0e10cSrcweir const ::Size& rNextTileX, 85*cdf0e10cSrcweir const ::Size& rNextTileY, 86*cdf0e10cSrcweir sal_Int32 nTilesX, 87*cdf0e10cSrcweir sal_Int32 nTilesY, 88*cdf0e10cSrcweir const ::Size& rTileSize, 89*cdf0e10cSrcweir const GraphicAttr& rAttr) 90*cdf0e10cSrcweir { 91*cdf0e10cSrcweir bool bRet( false ); 92*cdf0e10cSrcweir Point aCurrPos; 93*cdf0e10cSrcweir int nX, nY; 94*cdf0e10cSrcweir 95*cdf0e10cSrcweir for( nY=0; nY < nTilesY; ++nY ) 96*cdf0e10cSrcweir { 97*cdf0e10cSrcweir aCurrPos.X() = rPosPixel.X() + nY*rNextTileY.Width(); 98*cdf0e10cSrcweir aCurrPos.Y() = rPosPixel.Y() + nY*rNextTileY.Height(); 99*cdf0e10cSrcweir 100*cdf0e10cSrcweir for( nX=0; nX < nTilesX; ++nX ) 101*cdf0e10cSrcweir { 102*cdf0e10cSrcweir // update return value. This method should return true, if 103*cdf0e10cSrcweir // at least one of the looped Draws succeeded. 104*cdf0e10cSrcweir bRet |= ( sal_True == rGraphic.Draw( &rOutDev, 105*cdf0e10cSrcweir aCurrPos, 106*cdf0e10cSrcweir rTileSize, 107*cdf0e10cSrcweir &rAttr ) ); 108*cdf0e10cSrcweir 109*cdf0e10cSrcweir aCurrPos.X() += rNextTileX.Width(); 110*cdf0e10cSrcweir aCurrPos.Y() += rNextTileX.Height(); 111*cdf0e10cSrcweir } 112*cdf0e10cSrcweir } 113*cdf0e10cSrcweir 114*cdf0e10cSrcweir return bRet; 115*cdf0e10cSrcweir } 116*cdf0e10cSrcweir 117*cdf0e10cSrcweir 118*cdf0e10cSrcweir /** Fill linear or axial gradient 119*cdf0e10cSrcweir 120*cdf0e10cSrcweir Since most of the code for linear and axial gradients are 121*cdf0e10cSrcweir the same, we've a unified method here 122*cdf0e10cSrcweir */ 123*cdf0e10cSrcweir void fillLinearGradient( OutputDevice& rOutDev, 124*cdf0e10cSrcweir const ::basegfx::B2DHomMatrix& rTextureTransform, 125*cdf0e10cSrcweir const ::Rectangle& rBounds, 126*cdf0e10cSrcweir unsigned int nStepCount, 127*cdf0e10cSrcweir const ::canvas::ParametricPolyPolygon::Values& rValues, 128*cdf0e10cSrcweir const std::vector< ::Color >& rColors ) 129*cdf0e10cSrcweir { 130*cdf0e10cSrcweir // determine general position of gradient in relation to 131*cdf0e10cSrcweir // the bound rect 132*cdf0e10cSrcweir // ===================================================== 133*cdf0e10cSrcweir 134*cdf0e10cSrcweir ::basegfx::B2DPoint aLeftTop( 0.0, 0.0 ); 135*cdf0e10cSrcweir ::basegfx::B2DPoint aLeftBottom( 0.0, 1.0 ); 136*cdf0e10cSrcweir ::basegfx::B2DPoint aRightTop( 1.0, 0.0 ); 137*cdf0e10cSrcweir ::basegfx::B2DPoint aRightBottom( 1.0, 1.0 ); 138*cdf0e10cSrcweir 139*cdf0e10cSrcweir aLeftTop *= rTextureTransform; 140*cdf0e10cSrcweir aLeftBottom *= rTextureTransform; 141*cdf0e10cSrcweir aRightTop *= rTextureTransform; 142*cdf0e10cSrcweir aRightBottom*= rTextureTransform; 143*cdf0e10cSrcweir 144*cdf0e10cSrcweir // calc length of bound rect diagonal 145*cdf0e10cSrcweir const ::basegfx::B2DVector aBoundRectDiagonal( 146*cdf0e10cSrcweir ::vcl::unotools::b2DPointFromPoint( rBounds.TopLeft() ) - 147*cdf0e10cSrcweir ::vcl::unotools::b2DPointFromPoint( rBounds.BottomRight() ) ); 148*cdf0e10cSrcweir const double nDiagonalLength( aBoundRectDiagonal.getLength() ); 149*cdf0e10cSrcweir 150*cdf0e10cSrcweir // create direction of gradient: 151*cdf0e10cSrcweir // _______ 152*cdf0e10cSrcweir // | | | 153*cdf0e10cSrcweir // -> | | | ... 154*cdf0e10cSrcweir // | | | 155*cdf0e10cSrcweir // ------- 156*cdf0e10cSrcweir ::basegfx::B2DVector aDirection( aRightTop - aLeftTop ); 157*cdf0e10cSrcweir aDirection.normalize(); 158*cdf0e10cSrcweir 159*cdf0e10cSrcweir // now, we potentially have to enlarge our gradient area 160*cdf0e10cSrcweir // atop and below the transformed [0,1]x[0,1] unit rect, 161*cdf0e10cSrcweir // for the gradient to fill the complete bound rect. 162*cdf0e10cSrcweir ::basegfx::tools::infiniteLineFromParallelogram( aLeftTop, 163*cdf0e10cSrcweir aLeftBottom, 164*cdf0e10cSrcweir aRightTop, 165*cdf0e10cSrcweir aRightBottom, 166*cdf0e10cSrcweir ::vcl::unotools::b2DRectangleFromRectangle( rBounds ) ); 167*cdf0e10cSrcweir 168*cdf0e10cSrcweir 169*cdf0e10cSrcweir // render gradient 170*cdf0e10cSrcweir // =============== 171*cdf0e10cSrcweir 172*cdf0e10cSrcweir // for linear gradients, it's easy to render 173*cdf0e10cSrcweir // non-overlapping polygons: just split the gradient into 174*cdf0e10cSrcweir // nStepCount small strips. Prepare the strip now. 175*cdf0e10cSrcweir 176*cdf0e10cSrcweir // For performance reasons, we create a temporary VCL 177*cdf0e10cSrcweir // polygon here, keep it all the way and only change the 178*cdf0e10cSrcweir // vertex values in the loop below (as ::Polygon is a 179*cdf0e10cSrcweir // pimpl class, creating one every loop turn would really 180*cdf0e10cSrcweir // stress the mem allocator) 181*cdf0e10cSrcweir ::Polygon aTempPoly( static_cast<sal_uInt16>(5) ); 182*cdf0e10cSrcweir 183*cdf0e10cSrcweir OSL_ENSURE( nStepCount >= 3, 184*cdf0e10cSrcweir "fillLinearGradient(): stepcount smaller than 3" ); 185*cdf0e10cSrcweir 186*cdf0e10cSrcweir 187*cdf0e10cSrcweir // fill initial strip (extending two times the bound rect's 188*cdf0e10cSrcweir // diagonal to the 'left' 189*cdf0e10cSrcweir // ------------------------------------------------------ 190*cdf0e10cSrcweir 191*cdf0e10cSrcweir // calculate left edge, by moving left edge of the 192*cdf0e10cSrcweir // gradient rect two times the bound rect's diagonal to 193*cdf0e10cSrcweir // the 'left'. Since we postpone actual rendering into the 194*cdf0e10cSrcweir // loop below, we set the _right_ edge here, which will be 195*cdf0e10cSrcweir // readily copied into the left edge in the loop below 196*cdf0e10cSrcweir const ::basegfx::B2DPoint& rPoint1( aLeftTop - 2.0*nDiagonalLength*aDirection ); 197*cdf0e10cSrcweir aTempPoly[1] = ::Point( ::basegfx::fround( rPoint1.getX() ), 198*cdf0e10cSrcweir ::basegfx::fround( rPoint1.getY() ) ); 199*cdf0e10cSrcweir 200*cdf0e10cSrcweir const ::basegfx::B2DPoint& rPoint2( aLeftBottom - 2.0*nDiagonalLength*aDirection ); 201*cdf0e10cSrcweir aTempPoly[2] = ::Point( ::basegfx::fround( rPoint2.getX() ), 202*cdf0e10cSrcweir ::basegfx::fround( rPoint2.getY() ) ); 203*cdf0e10cSrcweir 204*cdf0e10cSrcweir 205*cdf0e10cSrcweir // iteratively render all other strips 206*cdf0e10cSrcweir // ----------------------------------- 207*cdf0e10cSrcweir 208*cdf0e10cSrcweir // ensure that nStepCount matches color stop parity, to 209*cdf0e10cSrcweir // have a well-defined middle color e.g. for axial 210*cdf0e10cSrcweir // gradients. 211*cdf0e10cSrcweir if( (rColors.size() % 2) != (nStepCount % 2) ) 212*cdf0e10cSrcweir ++nStepCount; 213*cdf0e10cSrcweir 214*cdf0e10cSrcweir rOutDev.SetLineColor(); 215*cdf0e10cSrcweir 216*cdf0e10cSrcweir basegfx::tools::KeyStopLerp aLerper(rValues.maStops); 217*cdf0e10cSrcweir 218*cdf0e10cSrcweir // only iterate nStepCount-1 steps, as the last strip is 219*cdf0e10cSrcweir // explicitely painted below 220*cdf0e10cSrcweir for( unsigned int i=0; i<nStepCount-1; ++i ) 221*cdf0e10cSrcweir { 222*cdf0e10cSrcweir std::ptrdiff_t nIndex; 223*cdf0e10cSrcweir double fAlpha; 224*cdf0e10cSrcweir boost::tuples::tie(nIndex,fAlpha)=aLerper.lerp(double(i)/nStepCount); 225*cdf0e10cSrcweir 226*cdf0e10cSrcweir rOutDev.SetFillColor( 227*cdf0e10cSrcweir Color( (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetRed(),rColors[nIndex+1].GetRed(),fAlpha)), 228*cdf0e10cSrcweir (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetGreen(),rColors[nIndex+1].GetGreen(),fAlpha)), 229*cdf0e10cSrcweir (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetBlue(),rColors[nIndex+1].GetBlue(),fAlpha)) )); 230*cdf0e10cSrcweir 231*cdf0e10cSrcweir // copy right egde of polygon to left edge (and also 232*cdf0e10cSrcweir // copy the closing point) 233*cdf0e10cSrcweir aTempPoly[0] = aTempPoly[4] = aTempPoly[1]; 234*cdf0e10cSrcweir aTempPoly[3] = aTempPoly[2]; 235*cdf0e10cSrcweir 236*cdf0e10cSrcweir // calculate new right edge, from interpolating 237*cdf0e10cSrcweir // between start and end line. Note that i is 238*cdf0e10cSrcweir // increased by one, to account for the fact that we 239*cdf0e10cSrcweir // calculate the right border here (whereas the fill 240*cdf0e10cSrcweir // color is governed by the left edge) 241*cdf0e10cSrcweir const ::basegfx::B2DPoint& rPoint3( 242*cdf0e10cSrcweir (nStepCount - i-1)/double(nStepCount)*aLeftTop + 243*cdf0e10cSrcweir (i+1)/double(nStepCount)*aRightTop ); 244*cdf0e10cSrcweir aTempPoly[1] = ::Point( ::basegfx::fround( rPoint3.getX() ), 245*cdf0e10cSrcweir ::basegfx::fround( rPoint3.getY() ) ); 246*cdf0e10cSrcweir 247*cdf0e10cSrcweir const ::basegfx::B2DPoint& rPoint4( 248*cdf0e10cSrcweir (nStepCount - i-1)/double(nStepCount)*aLeftBottom + 249*cdf0e10cSrcweir (i+1)/double(nStepCount)*aRightBottom ); 250*cdf0e10cSrcweir aTempPoly[2] = ::Point( ::basegfx::fround( rPoint4.getX() ), 251*cdf0e10cSrcweir ::basegfx::fround( rPoint4.getY() ) ); 252*cdf0e10cSrcweir 253*cdf0e10cSrcweir rOutDev.DrawPolygon( aTempPoly ); 254*cdf0e10cSrcweir } 255*cdf0e10cSrcweir 256*cdf0e10cSrcweir // fill final strip (extending two times the bound rect's 257*cdf0e10cSrcweir // diagonal to the 'right' 258*cdf0e10cSrcweir // ------------------------------------------------------ 259*cdf0e10cSrcweir 260*cdf0e10cSrcweir // copy right egde of polygon to left edge (and also 261*cdf0e10cSrcweir // copy the closing point) 262*cdf0e10cSrcweir aTempPoly[0] = aTempPoly[4] = aTempPoly[1]; 263*cdf0e10cSrcweir aTempPoly[3] = aTempPoly[2]; 264*cdf0e10cSrcweir 265*cdf0e10cSrcweir // calculate new right edge, by moving right edge of the 266*cdf0e10cSrcweir // gradient rect two times the bound rect's diagonal to 267*cdf0e10cSrcweir // the 'right'. 268*cdf0e10cSrcweir const ::basegfx::B2DPoint& rPoint3( aRightTop + 2.0*nDiagonalLength*aDirection ); 269*cdf0e10cSrcweir aTempPoly[0] = aTempPoly[4] = ::Point( ::basegfx::fround( rPoint3.getX() ), 270*cdf0e10cSrcweir ::basegfx::fround( rPoint3.getY() ) ); 271*cdf0e10cSrcweir 272*cdf0e10cSrcweir const ::basegfx::B2DPoint& rPoint4( aRightBottom + 2.0*nDiagonalLength*aDirection ); 273*cdf0e10cSrcweir aTempPoly[3] = ::Point( ::basegfx::fround( rPoint4.getX() ), 274*cdf0e10cSrcweir ::basegfx::fround( rPoint4.getY() ) ); 275*cdf0e10cSrcweir 276*cdf0e10cSrcweir rOutDev.SetFillColor( rColors.back() ); 277*cdf0e10cSrcweir 278*cdf0e10cSrcweir rOutDev.DrawPolygon( aTempPoly ); 279*cdf0e10cSrcweir } 280*cdf0e10cSrcweir 281*cdf0e10cSrcweir void fillPolygonalGradient( OutputDevice& rOutDev, 282*cdf0e10cSrcweir const ::basegfx::B2DHomMatrix& rTextureTransform, 283*cdf0e10cSrcweir const ::Rectangle& rBounds, 284*cdf0e10cSrcweir unsigned int nStepCount, 285*cdf0e10cSrcweir bool bFillNonOverlapping, 286*cdf0e10cSrcweir const ::canvas::ParametricPolyPolygon::Values& rValues, 287*cdf0e10cSrcweir const std::vector< ::Color >& rColors ) 288*cdf0e10cSrcweir { 289*cdf0e10cSrcweir const ::basegfx::B2DPolygon& rGradientPoly( rValues.maGradientPoly ); 290*cdf0e10cSrcweir 291*cdf0e10cSrcweir ENSURE_OR_THROW( rGradientPoly.count() > 2, 292*cdf0e10cSrcweir "fillPolygonalGradient(): polygon without area given" ); 293*cdf0e10cSrcweir 294*cdf0e10cSrcweir // For performance reasons, we create a temporary VCL polygon 295*cdf0e10cSrcweir // here, keep it all the way and only change the vertex values 296*cdf0e10cSrcweir // in the loop below (as ::Polygon is a pimpl class, creating 297*cdf0e10cSrcweir // one every loop turn would really stress the mem allocator) 298*cdf0e10cSrcweir ::basegfx::B2DPolygon aOuterPoly( rGradientPoly ); 299*cdf0e10cSrcweir ::basegfx::B2DPolygon aInnerPoly; 300*cdf0e10cSrcweir 301*cdf0e10cSrcweir // subdivide polygon _before_ rendering, would otherwise have 302*cdf0e10cSrcweir // to be performed on every loop turn. 303*cdf0e10cSrcweir if( aOuterPoly.areControlPointsUsed() ) 304*cdf0e10cSrcweir aOuterPoly = ::basegfx::tools::adaptiveSubdivideByAngle(aOuterPoly); 305*cdf0e10cSrcweir 306*cdf0e10cSrcweir aInnerPoly = aOuterPoly; 307*cdf0e10cSrcweir 308*cdf0e10cSrcweir // only transform outer polygon _after_ copying it into 309*cdf0e10cSrcweir // aInnerPoly, because inner polygon has to be scaled before 310*cdf0e10cSrcweir // the actual texture transformation takes place 311*cdf0e10cSrcweir aOuterPoly.transform( rTextureTransform ); 312*cdf0e10cSrcweir 313*cdf0e10cSrcweir // determine overall transformation for inner polygon (might 314*cdf0e10cSrcweir // have to be prefixed by anisotrophic scaling) 315*cdf0e10cSrcweir ::basegfx::B2DHomMatrix aInnerPolygonTransformMatrix; 316*cdf0e10cSrcweir 317*cdf0e10cSrcweir 318*cdf0e10cSrcweir // apply scaling (possibly anisotrophic) to inner polygon 319*cdf0e10cSrcweir // ------------------------------------------------------ 320*cdf0e10cSrcweir 321*cdf0e10cSrcweir // scale inner polygon according to aspect ratio: for 322*cdf0e10cSrcweir // wider-than-tall bounds (nAspectRatio > 1.0), the inner 323*cdf0e10cSrcweir // polygon, representing the gradient focus, must have 324*cdf0e10cSrcweir // non-zero width. Specifically, a bound rect twice as wide as 325*cdf0e10cSrcweir // tall has a focus polygon of half it's width. 326*cdf0e10cSrcweir const double nAspectRatio( rValues.mnAspectRatio ); 327*cdf0e10cSrcweir if( nAspectRatio > 1.0 ) 328*cdf0e10cSrcweir { 329*cdf0e10cSrcweir // width > height case 330*cdf0e10cSrcweir aInnerPolygonTransformMatrix.scale( 1.0 - 1.0/nAspectRatio, 331*cdf0e10cSrcweir 0.0 ); 332*cdf0e10cSrcweir } 333*cdf0e10cSrcweir else if( nAspectRatio < 1.0 ) 334*cdf0e10cSrcweir { 335*cdf0e10cSrcweir // width < height case 336*cdf0e10cSrcweir aInnerPolygonTransformMatrix.scale( 0.0, 337*cdf0e10cSrcweir 1.0 - nAspectRatio ); 338*cdf0e10cSrcweir } 339*cdf0e10cSrcweir else 340*cdf0e10cSrcweir { 341*cdf0e10cSrcweir // isotrophic case 342*cdf0e10cSrcweir aInnerPolygonTransformMatrix.scale( 0.0, 0.0 ); 343*cdf0e10cSrcweir } 344*cdf0e10cSrcweir 345*cdf0e10cSrcweir // and finally, add texture transform to it. 346*cdf0e10cSrcweir aInnerPolygonTransformMatrix *= rTextureTransform; 347*cdf0e10cSrcweir 348*cdf0e10cSrcweir // apply final matrix to polygon 349*cdf0e10cSrcweir aInnerPoly.transform( aInnerPolygonTransformMatrix ); 350*cdf0e10cSrcweir 351*cdf0e10cSrcweir 352*cdf0e10cSrcweir const sal_uInt32 nNumPoints( aOuterPoly.count() ); 353*cdf0e10cSrcweir ::Polygon aTempPoly( static_cast<sal_uInt16>(nNumPoints+1) ); 354*cdf0e10cSrcweir 355*cdf0e10cSrcweir // increase number of steps by one: polygonal gradients have 356*cdf0e10cSrcweir // the outermost polygon rendered in rColor2, and the 357*cdf0e10cSrcweir // innermost in rColor1. The innermost polygon will never 358*cdf0e10cSrcweir // have zero area, thus, we must divide the interval into 359*cdf0e10cSrcweir // nStepCount+1 steps. For example, to create 3 steps: 360*cdf0e10cSrcweir // 361*cdf0e10cSrcweir // | | 362*cdf0e10cSrcweir // |-------|-------|-------| 363*cdf0e10cSrcweir // | | 364*cdf0e10cSrcweir // 3 2 1 0 365*cdf0e10cSrcweir // 366*cdf0e10cSrcweir // This yields 4 tick marks, where 0 is never attained (since 367*cdf0e10cSrcweir // zero-area polygons typically don't display perceivable 368*cdf0e10cSrcweir // color). 369*cdf0e10cSrcweir ++nStepCount; 370*cdf0e10cSrcweir 371*cdf0e10cSrcweir rOutDev.SetLineColor(); 372*cdf0e10cSrcweir 373*cdf0e10cSrcweir basegfx::tools::KeyStopLerp aLerper(rValues.maStops); 374*cdf0e10cSrcweir 375*cdf0e10cSrcweir if( !bFillNonOverlapping ) 376*cdf0e10cSrcweir { 377*cdf0e10cSrcweir // fill background 378*cdf0e10cSrcweir rOutDev.SetFillColor( rColors.front() ); 379*cdf0e10cSrcweir rOutDev.DrawRect( rBounds ); 380*cdf0e10cSrcweir 381*cdf0e10cSrcweir // render polygon 382*cdf0e10cSrcweir // ============== 383*cdf0e10cSrcweir 384*cdf0e10cSrcweir for( unsigned int i=1,p; i<nStepCount; ++i ) 385*cdf0e10cSrcweir { 386*cdf0e10cSrcweir const double fT( i/double(nStepCount) ); 387*cdf0e10cSrcweir 388*cdf0e10cSrcweir std::ptrdiff_t nIndex; 389*cdf0e10cSrcweir double fAlpha; 390*cdf0e10cSrcweir boost::tuples::tie(nIndex,fAlpha)=aLerper.lerp(fT); 391*cdf0e10cSrcweir 392*cdf0e10cSrcweir // lerp color 393*cdf0e10cSrcweir rOutDev.SetFillColor( 394*cdf0e10cSrcweir Color( (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetRed(),rColors[nIndex+1].GetRed(),fAlpha)), 395*cdf0e10cSrcweir (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetGreen(),rColors[nIndex+1].GetGreen(),fAlpha)), 396*cdf0e10cSrcweir (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetBlue(),rColors[nIndex+1].GetBlue(),fAlpha)) )); 397*cdf0e10cSrcweir 398*cdf0e10cSrcweir // scale and render polygon, by interpolating between 399*cdf0e10cSrcweir // outer and inner polygon. 400*cdf0e10cSrcweir 401*cdf0e10cSrcweir for( p=0; p<nNumPoints; ++p ) 402*cdf0e10cSrcweir { 403*cdf0e10cSrcweir const ::basegfx::B2DPoint& rOuterPoint( aOuterPoly.getB2DPoint(p) ); 404*cdf0e10cSrcweir const ::basegfx::B2DPoint& rInnerPoint( aInnerPoly.getB2DPoint(p) ); 405*cdf0e10cSrcweir 406*cdf0e10cSrcweir aTempPoly[(sal_uInt16)p] = ::Point( 407*cdf0e10cSrcweir basegfx::fround( fT*rInnerPoint.getX() + (1-fT)*rOuterPoint.getX() ), 408*cdf0e10cSrcweir basegfx::fround( fT*rInnerPoint.getY() + (1-fT)*rOuterPoint.getY() ) ); 409*cdf0e10cSrcweir } 410*cdf0e10cSrcweir 411*cdf0e10cSrcweir // close polygon explicitely 412*cdf0e10cSrcweir aTempPoly[(sal_uInt16)p] = aTempPoly[0]; 413*cdf0e10cSrcweir 414*cdf0e10cSrcweir // TODO(P1): compare with vcl/source/gdi/outdev4.cxx, 415*cdf0e10cSrcweir // OutputDevice::ImplDrawComplexGradient(), there's a note 416*cdf0e10cSrcweir // that on some VDev's, rendering disjunct poly-polygons 417*cdf0e10cSrcweir // is faster! 418*cdf0e10cSrcweir rOutDev.DrawPolygon( aTempPoly ); 419*cdf0e10cSrcweir } 420*cdf0e10cSrcweir } 421*cdf0e10cSrcweir else 422*cdf0e10cSrcweir { 423*cdf0e10cSrcweir // render polygon 424*cdf0e10cSrcweir // ============== 425*cdf0e10cSrcweir 426*cdf0e10cSrcweir // For performance reasons, we create a temporary VCL polygon 427*cdf0e10cSrcweir // here, keep it all the way and only change the vertex values 428*cdf0e10cSrcweir // in the loop below (as ::Polygon is a pimpl class, creating 429*cdf0e10cSrcweir // one every loop turn would really stress the mem allocator) 430*cdf0e10cSrcweir ::PolyPolygon aTempPolyPoly; 431*cdf0e10cSrcweir ::Polygon aTempPoly2( static_cast<sal_uInt16>(nNumPoints+1) ); 432*cdf0e10cSrcweir 433*cdf0e10cSrcweir aTempPoly2[0] = rBounds.TopLeft(); 434*cdf0e10cSrcweir aTempPoly2[1] = rBounds.TopRight(); 435*cdf0e10cSrcweir aTempPoly2[2] = rBounds.BottomRight(); 436*cdf0e10cSrcweir aTempPoly2[3] = rBounds.BottomLeft(); 437*cdf0e10cSrcweir aTempPoly2[4] = rBounds.TopLeft(); 438*cdf0e10cSrcweir 439*cdf0e10cSrcweir aTempPolyPoly.Insert( aTempPoly ); 440*cdf0e10cSrcweir aTempPolyPoly.Insert( aTempPoly2 ); 441*cdf0e10cSrcweir 442*cdf0e10cSrcweir for( unsigned int i=0,p; i<nStepCount; ++i ) 443*cdf0e10cSrcweir { 444*cdf0e10cSrcweir const double fT( (i+1)/double(nStepCount) ); 445*cdf0e10cSrcweir 446*cdf0e10cSrcweir std::ptrdiff_t nIndex; 447*cdf0e10cSrcweir double fAlpha; 448*cdf0e10cSrcweir boost::tuples::tie(nIndex,fAlpha)=aLerper.lerp(fT); 449*cdf0e10cSrcweir 450*cdf0e10cSrcweir // lerp color 451*cdf0e10cSrcweir rOutDev.SetFillColor( 452*cdf0e10cSrcweir Color( (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetRed(),rColors[nIndex+1].GetRed(),fAlpha)), 453*cdf0e10cSrcweir (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetGreen(),rColors[nIndex+1].GetGreen(),fAlpha)), 454*cdf0e10cSrcweir (sal_uInt8)(basegfx::tools::lerp(rColors[nIndex].GetBlue(),rColors[nIndex+1].GetBlue(),fAlpha)) )); 455*cdf0e10cSrcweir 456*cdf0e10cSrcweir #if defined(VERBOSE) && OSL_DEBUG_LEVEL > 0 457*cdf0e10cSrcweir if( i && !(i % 10) ) 458*cdf0e10cSrcweir rOutDev.SetFillColor( COL_RED ); 459*cdf0e10cSrcweir #endif 460*cdf0e10cSrcweir 461*cdf0e10cSrcweir // scale and render polygon. Note that here, we 462*cdf0e10cSrcweir // calculate the inner polygon, which is actually the 463*cdf0e10cSrcweir // start of the _next_ color strip. Thus, i+1 464*cdf0e10cSrcweir 465*cdf0e10cSrcweir for( p=0; p<nNumPoints; ++p ) 466*cdf0e10cSrcweir { 467*cdf0e10cSrcweir const ::basegfx::B2DPoint& rOuterPoint( aOuterPoly.getB2DPoint(p) ); 468*cdf0e10cSrcweir const ::basegfx::B2DPoint& rInnerPoint( aInnerPoly.getB2DPoint(p) ); 469*cdf0e10cSrcweir 470*cdf0e10cSrcweir aTempPoly[(sal_uInt16)p] = ::Point( 471*cdf0e10cSrcweir basegfx::fround( fT*rInnerPoint.getX() + (1-fT)*rOuterPoint.getX() ), 472*cdf0e10cSrcweir basegfx::fround( fT*rInnerPoint.getY() + (1-fT)*rOuterPoint.getY() ) ); 473*cdf0e10cSrcweir } 474*cdf0e10cSrcweir 475*cdf0e10cSrcweir // close polygon explicitely 476*cdf0e10cSrcweir aTempPoly[(sal_uInt16)p] = aTempPoly[0]; 477*cdf0e10cSrcweir 478*cdf0e10cSrcweir // swap inner and outer polygon 479*cdf0e10cSrcweir aTempPolyPoly.Replace( aTempPolyPoly.GetObject( 1 ), 0 ); 480*cdf0e10cSrcweir 481*cdf0e10cSrcweir if( i+1<nStepCount ) 482*cdf0e10cSrcweir { 483*cdf0e10cSrcweir // assign new inner polygon. Note that with this 484*cdf0e10cSrcweir // formulation, the internal pimpl objects for both 485*cdf0e10cSrcweir // temp polygons and the polypolygon remain identical, 486*cdf0e10cSrcweir // minimizing heap accesses (only a Polygon wrapper 487*cdf0e10cSrcweir // object is freed and deleted twice during this swap). 488*cdf0e10cSrcweir aTempPolyPoly.Replace( aTempPoly, 1 ); 489*cdf0e10cSrcweir } 490*cdf0e10cSrcweir else 491*cdf0e10cSrcweir { 492*cdf0e10cSrcweir // last, i.e. inner strip. Now, the inner polygon 493*cdf0e10cSrcweir // has zero area anyway, and to not leave holes in 494*cdf0e10cSrcweir // the gradient, finally render a simple polygon: 495*cdf0e10cSrcweir aTempPolyPoly.Remove( 1 ); 496*cdf0e10cSrcweir } 497*cdf0e10cSrcweir 498*cdf0e10cSrcweir rOutDev.DrawPolyPolygon( aTempPolyPoly ); 499*cdf0e10cSrcweir } 500*cdf0e10cSrcweir } 501*cdf0e10cSrcweir } 502*cdf0e10cSrcweir 503*cdf0e10cSrcweir void doGradientFill( OutputDevice& rOutDev, 504*cdf0e10cSrcweir const ::canvas::ParametricPolyPolygon::Values& rValues, 505*cdf0e10cSrcweir const std::vector< ::Color >& rColors, 506*cdf0e10cSrcweir const ::basegfx::B2DHomMatrix& rTextureTransform, 507*cdf0e10cSrcweir const ::Rectangle& rBounds, 508*cdf0e10cSrcweir unsigned int nStepCount, 509*cdf0e10cSrcweir bool bFillNonOverlapping ) 510*cdf0e10cSrcweir { 511*cdf0e10cSrcweir switch( rValues.meType ) 512*cdf0e10cSrcweir { 513*cdf0e10cSrcweir case ::canvas::ParametricPolyPolygon::GRADIENT_LINEAR: 514*cdf0e10cSrcweir fillLinearGradient( rOutDev, 515*cdf0e10cSrcweir rTextureTransform, 516*cdf0e10cSrcweir rBounds, 517*cdf0e10cSrcweir nStepCount, 518*cdf0e10cSrcweir rValues, 519*cdf0e10cSrcweir rColors ); 520*cdf0e10cSrcweir break; 521*cdf0e10cSrcweir 522*cdf0e10cSrcweir case ::canvas::ParametricPolyPolygon::GRADIENT_ELLIPTICAL: 523*cdf0e10cSrcweir // FALLTHROUGH intended 524*cdf0e10cSrcweir case ::canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR: 525*cdf0e10cSrcweir fillPolygonalGradient( rOutDev, 526*cdf0e10cSrcweir rTextureTransform, 527*cdf0e10cSrcweir rBounds, 528*cdf0e10cSrcweir nStepCount, 529*cdf0e10cSrcweir bFillNonOverlapping, 530*cdf0e10cSrcweir rValues, 531*cdf0e10cSrcweir rColors ); 532*cdf0e10cSrcweir break; 533*cdf0e10cSrcweir 534*cdf0e10cSrcweir default: 535*cdf0e10cSrcweir ENSURE_OR_THROW( false, 536*cdf0e10cSrcweir "CanvasHelper::doGradientFill(): Unexpected case" ); 537*cdf0e10cSrcweir } 538*cdf0e10cSrcweir } 539*cdf0e10cSrcweir 540*cdf0e10cSrcweir int numColorSteps( const ::Color& rColor1, const ::Color& rColor2 ) 541*cdf0e10cSrcweir { 542*cdf0e10cSrcweir return ::std::max( 543*cdf0e10cSrcweir labs( rColor1.GetRed() - rColor2.GetRed() ), 544*cdf0e10cSrcweir ::std::max( 545*cdf0e10cSrcweir labs( rColor1.GetGreen() - rColor2.GetGreen() ), 546*cdf0e10cSrcweir labs( rColor1.GetBlue() - rColor2.GetBlue() ) ) ); 547*cdf0e10cSrcweir } 548*cdf0e10cSrcweir 549*cdf0e10cSrcweir bool gradientFill( OutputDevice& rOutDev, 550*cdf0e10cSrcweir OutputDevice* p2ndOutDev, 551*cdf0e10cSrcweir const ::canvas::ParametricPolyPolygon::Values& rValues, 552*cdf0e10cSrcweir const std::vector< ::Color >& rColors, 553*cdf0e10cSrcweir const PolyPolygon& rPoly, 554*cdf0e10cSrcweir const rendering::ViewState& viewState, 555*cdf0e10cSrcweir const rendering::RenderState& renderState, 556*cdf0e10cSrcweir const rendering::Texture& texture, 557*cdf0e10cSrcweir int nTransparency ) 558*cdf0e10cSrcweir { 559*cdf0e10cSrcweir (void)nTransparency; 560*cdf0e10cSrcweir 561*cdf0e10cSrcweir // TODO(T2): It is maybe necessary to lock here, should 562*cdf0e10cSrcweir // maGradientPoly someday cease to be const. But then, beware of 563*cdf0e10cSrcweir // deadlocks, canvashelper calls this method with locked own 564*cdf0e10cSrcweir // mutex. 565*cdf0e10cSrcweir 566*cdf0e10cSrcweir // calc step size 567*cdf0e10cSrcweir // -------------- 568*cdf0e10cSrcweir int nColorSteps = 0; 569*cdf0e10cSrcweir for( size_t i=0; i<rColors.size()-1; ++i ) 570*cdf0e10cSrcweir nColorSteps += numColorSteps(rColors[i],rColors[i+1]); 571*cdf0e10cSrcweir 572*cdf0e10cSrcweir ::basegfx::B2DHomMatrix aTotalTransform; 573*cdf0e10cSrcweir const int nStepCount= 574*cdf0e10cSrcweir ::canvas::tools::calcGradientStepCount(aTotalTransform, 575*cdf0e10cSrcweir viewState, 576*cdf0e10cSrcweir renderState, 577*cdf0e10cSrcweir texture, 578*cdf0e10cSrcweir nColorSteps); 579*cdf0e10cSrcweir 580*cdf0e10cSrcweir rOutDev.SetLineColor(); 581*cdf0e10cSrcweir 582*cdf0e10cSrcweir // determine maximal bound rect of texture-filled 583*cdf0e10cSrcweir // polygon 584*cdf0e10cSrcweir const ::Rectangle aPolygonDeviceRectOrig( 585*cdf0e10cSrcweir rPoly.GetBoundRect() ); 586*cdf0e10cSrcweir 587*cdf0e10cSrcweir if( tools::isRectangle( rPoly ) ) 588*cdf0e10cSrcweir { 589*cdf0e10cSrcweir // use optimized output path 590*cdf0e10cSrcweir // ------------------------- 591*cdf0e10cSrcweir 592*cdf0e10cSrcweir // this distinction really looks like a 593*cdf0e10cSrcweir // micro-optimisation, but in fact greatly speeds up 594*cdf0e10cSrcweir // especially complex gradients. That's because when using 595*cdf0e10cSrcweir // clipping, we can output polygons instead of 596*cdf0e10cSrcweir // poly-polygons, and don't have to output the gradient 597*cdf0e10cSrcweir // twice for XOR 598*cdf0e10cSrcweir 599*cdf0e10cSrcweir rOutDev.Push( PUSH_CLIPREGION ); 600*cdf0e10cSrcweir rOutDev.IntersectClipRegion( aPolygonDeviceRectOrig ); 601*cdf0e10cSrcweir doGradientFill( rOutDev, 602*cdf0e10cSrcweir rValues, 603*cdf0e10cSrcweir rColors, 604*cdf0e10cSrcweir aTotalTransform, 605*cdf0e10cSrcweir aPolygonDeviceRectOrig, 606*cdf0e10cSrcweir nStepCount, 607*cdf0e10cSrcweir false ); 608*cdf0e10cSrcweir rOutDev.Pop(); 609*cdf0e10cSrcweir 610*cdf0e10cSrcweir if( p2ndOutDev ) 611*cdf0e10cSrcweir { 612*cdf0e10cSrcweir p2ndOutDev->Push( PUSH_CLIPREGION ); 613*cdf0e10cSrcweir p2ndOutDev->IntersectClipRegion( aPolygonDeviceRectOrig ); 614*cdf0e10cSrcweir doGradientFill( *p2ndOutDev, 615*cdf0e10cSrcweir rValues, 616*cdf0e10cSrcweir rColors, 617*cdf0e10cSrcweir aTotalTransform, 618*cdf0e10cSrcweir aPolygonDeviceRectOrig, 619*cdf0e10cSrcweir nStepCount, 620*cdf0e10cSrcweir false ); 621*cdf0e10cSrcweir p2ndOutDev->Pop(); 622*cdf0e10cSrcweir } 623*cdf0e10cSrcweir } 624*cdf0e10cSrcweir else 625*cdf0e10cSrcweir #if defined(QUARTZ) // TODO: other ports should avoid the XOR-trick too (implementation vs. interface!) 626*cdf0e10cSrcweir { 627*cdf0e10cSrcweir const Region aPolyClipRegion( rPoly ); 628*cdf0e10cSrcweir 629*cdf0e10cSrcweir rOutDev.Push( PUSH_CLIPREGION ); 630*cdf0e10cSrcweir rOutDev.SetClipRegion( aPolyClipRegion ); 631*cdf0e10cSrcweir 632*cdf0e10cSrcweir doGradientFill( rOutDev, 633*cdf0e10cSrcweir rValues, 634*cdf0e10cSrcweir rColors, 635*cdf0e10cSrcweir aTotalTransform, 636*cdf0e10cSrcweir aPolygonDeviceRectOrig, 637*cdf0e10cSrcweir nStepCount, 638*cdf0e10cSrcweir false ); 639*cdf0e10cSrcweir rOutDev.Pop(); 640*cdf0e10cSrcweir 641*cdf0e10cSrcweir if( p2ndOutDev ) 642*cdf0e10cSrcweir { 643*cdf0e10cSrcweir p2ndOutDev->Push( PUSH_CLIPREGION ); 644*cdf0e10cSrcweir p2ndOutDev->SetClipRegion( aPolyClipRegion ); 645*cdf0e10cSrcweir doGradientFill( *p2ndOutDev, 646*cdf0e10cSrcweir rValues, 647*cdf0e10cSrcweir rColors, 648*cdf0e10cSrcweir aTotalTransform, 649*cdf0e10cSrcweir aPolygonDeviceRectOrig, 650*cdf0e10cSrcweir nStepCount, 651*cdf0e10cSrcweir false ); 652*cdf0e10cSrcweir p2ndOutDev->Pop(); 653*cdf0e10cSrcweir } 654*cdf0e10cSrcweir } 655*cdf0e10cSrcweir #else // TODO: remove once doing the XOR-trick in the canvas-layer becomes redundant 656*cdf0e10cSrcweir { 657*cdf0e10cSrcweir // output gradient the hard way: XORing out the polygon 658*cdf0e10cSrcweir rOutDev.Push( PUSH_RASTEROP ); 659*cdf0e10cSrcweir rOutDev.SetRasterOp( ROP_XOR ); 660*cdf0e10cSrcweir doGradientFill( rOutDev, 661*cdf0e10cSrcweir rValues, 662*cdf0e10cSrcweir rColors, 663*cdf0e10cSrcweir aTotalTransform, 664*cdf0e10cSrcweir aPolygonDeviceRectOrig, 665*cdf0e10cSrcweir nStepCount, 666*cdf0e10cSrcweir true ); 667*cdf0e10cSrcweir rOutDev.SetFillColor( COL_BLACK ); 668*cdf0e10cSrcweir rOutDev.SetRasterOp( ROP_0 ); 669*cdf0e10cSrcweir rOutDev.DrawPolyPolygon( rPoly ); 670*cdf0e10cSrcweir rOutDev.SetRasterOp( ROP_XOR ); 671*cdf0e10cSrcweir doGradientFill( rOutDev, 672*cdf0e10cSrcweir rValues, 673*cdf0e10cSrcweir rColors, 674*cdf0e10cSrcweir aTotalTransform, 675*cdf0e10cSrcweir aPolygonDeviceRectOrig, 676*cdf0e10cSrcweir nStepCount, 677*cdf0e10cSrcweir true ); 678*cdf0e10cSrcweir rOutDev.Pop(); 679*cdf0e10cSrcweir 680*cdf0e10cSrcweir if( p2ndOutDev ) 681*cdf0e10cSrcweir { 682*cdf0e10cSrcweir p2ndOutDev->Push( PUSH_RASTEROP ); 683*cdf0e10cSrcweir p2ndOutDev->SetRasterOp( ROP_XOR ); 684*cdf0e10cSrcweir doGradientFill( *p2ndOutDev, 685*cdf0e10cSrcweir rValues, 686*cdf0e10cSrcweir rColors, 687*cdf0e10cSrcweir aTotalTransform, 688*cdf0e10cSrcweir aPolygonDeviceRectOrig, 689*cdf0e10cSrcweir nStepCount, 690*cdf0e10cSrcweir true ); 691*cdf0e10cSrcweir p2ndOutDev->SetFillColor( COL_BLACK ); 692*cdf0e10cSrcweir p2ndOutDev->SetRasterOp( ROP_0 ); 693*cdf0e10cSrcweir p2ndOutDev->DrawPolyPolygon( rPoly ); 694*cdf0e10cSrcweir p2ndOutDev->SetRasterOp( ROP_XOR ); 695*cdf0e10cSrcweir doGradientFill( *p2ndOutDev, 696*cdf0e10cSrcweir rValues, 697*cdf0e10cSrcweir rColors, 698*cdf0e10cSrcweir aTotalTransform, 699*cdf0e10cSrcweir aPolygonDeviceRectOrig, 700*cdf0e10cSrcweir nStepCount, 701*cdf0e10cSrcweir true ); 702*cdf0e10cSrcweir p2ndOutDev->Pop(); 703*cdf0e10cSrcweir } 704*cdf0e10cSrcweir } 705*cdf0e10cSrcweir #endif // complex-clipping vs. XOR-trick 706*cdf0e10cSrcweir 707*cdf0e10cSrcweir #if 0 //defined(VERBOSE) && OSL_DEBUG_LEVEL > 0 708*cdf0e10cSrcweir { 709*cdf0e10cSrcweir ::basegfx::B2DRectangle aRect(0.0, 0.0, 1.0, 1.0); 710*cdf0e10cSrcweir ::basegfx::B2DRectangle aTextureDeviceRect; 711*cdf0e10cSrcweir ::basegfx::B2DHomMatrix aTextureTransform; 712*cdf0e10cSrcweir ::canvas::tools::calcTransformedRectBounds( aTextureDeviceRect, 713*cdf0e10cSrcweir aRect, 714*cdf0e10cSrcweir aTextureTransform ); 715*cdf0e10cSrcweir rOutDev.SetLineColor( COL_RED ); 716*cdf0e10cSrcweir rOutDev.SetFillColor(); 717*cdf0e10cSrcweir rOutDev.DrawRect( ::vcl::unotools::rectangleFromB2DRectangle( aTextureDeviceRect ) ); 718*cdf0e10cSrcweir 719*cdf0e10cSrcweir rOutDev.SetLineColor( COL_BLUE ); 720*cdf0e10cSrcweir ::Polygon aPoly1( 721*cdf0e10cSrcweir ::vcl::unotools::rectangleFromB2DRectangle( aRect )); 722*cdf0e10cSrcweir ::basegfx::B2DPolygon aPoly2( aPoly1.getB2DPolygon() ); 723*cdf0e10cSrcweir aPoly2.transform( aTextureTransform ); 724*cdf0e10cSrcweir ::Polygon aPoly3( aPoly2 ); 725*cdf0e10cSrcweir rOutDev.DrawPolygon( aPoly3 ); 726*cdf0e10cSrcweir } 727*cdf0e10cSrcweir #endif 728*cdf0e10cSrcweir 729*cdf0e10cSrcweir return true; 730*cdf0e10cSrcweir } 731*cdf0e10cSrcweir } 732*cdf0e10cSrcweir 733*cdf0e10cSrcweir uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTexturedPolyPolygon( const rendering::XCanvas* pCanvas, 734*cdf0e10cSrcweir const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, 735*cdf0e10cSrcweir const rendering::ViewState& viewState, 736*cdf0e10cSrcweir const rendering::RenderState& renderState, 737*cdf0e10cSrcweir const uno::Sequence< rendering::Texture >& textures ) 738*cdf0e10cSrcweir { 739*cdf0e10cSrcweir ENSURE_ARG_OR_THROW( xPolyPolygon.is(), 740*cdf0e10cSrcweir "CanvasHelper::fillPolyPolygon(): polygon is NULL"); 741*cdf0e10cSrcweir ENSURE_ARG_OR_THROW( textures.getLength(), 742*cdf0e10cSrcweir "CanvasHelper::fillTexturedPolyPolygon: empty texture sequence"); 743*cdf0e10cSrcweir 744*cdf0e10cSrcweir if( mpOutDev ) 745*cdf0e10cSrcweir { 746*cdf0e10cSrcweir tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev ); 747*cdf0e10cSrcweir 748*cdf0e10cSrcweir const int nTransparency( setupOutDevState( viewState, renderState, IGNORE_COLOR ) ); 749*cdf0e10cSrcweir PolyPolygon aPolyPoly( tools::mapPolyPolygon( 750*cdf0e10cSrcweir ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon), 751*cdf0e10cSrcweir viewState, renderState ) ); 752*cdf0e10cSrcweir 753*cdf0e10cSrcweir // TODO(F1): Multi-texturing 754*cdf0e10cSrcweir if( textures[0].Gradient.is() ) 755*cdf0e10cSrcweir { 756*cdf0e10cSrcweir // try to cast XParametricPolyPolygon2D reference to 757*cdf0e10cSrcweir // our implementation class. 758*cdf0e10cSrcweir ::canvas::ParametricPolyPolygon* pGradient = 759*cdf0e10cSrcweir dynamic_cast< ::canvas::ParametricPolyPolygon* >( textures[0].Gradient.get() ); 760*cdf0e10cSrcweir 761*cdf0e10cSrcweir if( pGradient && pGradient->getValues().maColors.getLength() ) 762*cdf0e10cSrcweir { 763*cdf0e10cSrcweir // copy state from Gradient polypoly locally 764*cdf0e10cSrcweir // (given object might change!) 765*cdf0e10cSrcweir const ::canvas::ParametricPolyPolygon::Values& rValues( 766*cdf0e10cSrcweir pGradient->getValues() ); 767*cdf0e10cSrcweir 768*cdf0e10cSrcweir if( rValues.maColors.getLength() < 2 ) 769*cdf0e10cSrcweir { 770*cdf0e10cSrcweir rendering::RenderState aTempState=renderState; 771*cdf0e10cSrcweir aTempState.DeviceColor = rValues.maColors[0]; 772*cdf0e10cSrcweir fillPolyPolygon(pCanvas, xPolyPolygon, viewState, aTempState); 773*cdf0e10cSrcweir } 774*cdf0e10cSrcweir else 775*cdf0e10cSrcweir { 776*cdf0e10cSrcweir std::vector< ::Color > aColors(rValues.maColors.getLength()); 777*cdf0e10cSrcweir std::transform(&rValues.maColors[0], 778*cdf0e10cSrcweir &rValues.maColors[0]+rValues.maColors.getLength(), 779*cdf0e10cSrcweir aColors.begin(), 780*cdf0e10cSrcweir boost::bind( 781*cdf0e10cSrcweir &vcl::unotools::stdColorSpaceSequenceToColor, 782*cdf0e10cSrcweir _1)); 783*cdf0e10cSrcweir 784*cdf0e10cSrcweir // TODO(E1): Return value 785*cdf0e10cSrcweir // TODO(F1): FillRule 786*cdf0e10cSrcweir gradientFill( mpOutDev->getOutDev(), 787*cdf0e10cSrcweir mp2ndOutDev.get() ? &mp2ndOutDev->getOutDev() : (OutputDevice*)NULL, 788*cdf0e10cSrcweir rValues, 789*cdf0e10cSrcweir aColors, 790*cdf0e10cSrcweir aPolyPoly, 791*cdf0e10cSrcweir viewState, 792*cdf0e10cSrcweir renderState, 793*cdf0e10cSrcweir textures[0], 794*cdf0e10cSrcweir nTransparency ); 795*cdf0e10cSrcweir } 796*cdf0e10cSrcweir } 797*cdf0e10cSrcweir else 798*cdf0e10cSrcweir { 799*cdf0e10cSrcweir // TODO(F1): The generic case is missing here 800*cdf0e10cSrcweir ENSURE_OR_THROW( false, 801*cdf0e10cSrcweir "CanvasHelper::fillTexturedPolyPolygon(): unknown parametric polygon encountered" ); 802*cdf0e10cSrcweir } 803*cdf0e10cSrcweir } 804*cdf0e10cSrcweir else if( textures[0].Bitmap.is() ) 805*cdf0e10cSrcweir { 806*cdf0e10cSrcweir const geometry::IntegerSize2D aBmpSize( textures[0].Bitmap->getSize() ); 807*cdf0e10cSrcweir 808*cdf0e10cSrcweir ENSURE_ARG_OR_THROW( aBmpSize.Width != 0 && 809*cdf0e10cSrcweir aBmpSize.Height != 0, 810*cdf0e10cSrcweir "CanvasHelper::fillTexturedPolyPolygon(): zero-sized texture bitmap" ); 811*cdf0e10cSrcweir 812*cdf0e10cSrcweir // determine maximal bound rect of texture-filled 813*cdf0e10cSrcweir // polygon 814*cdf0e10cSrcweir const ::Rectangle aPolygonDeviceRect( 815*cdf0e10cSrcweir aPolyPoly.GetBoundRect() ); 816*cdf0e10cSrcweir 817*cdf0e10cSrcweir 818*cdf0e10cSrcweir // first of all, determine whether we have a 819*cdf0e10cSrcweir // drawBitmap() in disguise 820*cdf0e10cSrcweir // ========================================= 821*cdf0e10cSrcweir 822*cdf0e10cSrcweir const bool bRectangularPolygon( tools::isRectangle( aPolyPoly ) ); 823*cdf0e10cSrcweir 824*cdf0e10cSrcweir ::basegfx::B2DHomMatrix aTotalTransform; 825*cdf0e10cSrcweir ::canvas::tools::mergeViewAndRenderTransform(aTotalTransform, 826*cdf0e10cSrcweir viewState, 827*cdf0e10cSrcweir renderState); 828*cdf0e10cSrcweir ::basegfx::B2DHomMatrix aTextureTransform; 829*cdf0e10cSrcweir ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform, 830*cdf0e10cSrcweir textures[0].AffineTransform ); 831*cdf0e10cSrcweir 832*cdf0e10cSrcweir aTotalTransform *= aTextureTransform; 833*cdf0e10cSrcweir 834*cdf0e10cSrcweir const ::basegfx::B2DRectangle aRect(0.0, 0.0, 1.0, 1.0); 835*cdf0e10cSrcweir ::basegfx::B2DRectangle aTextureDeviceRect; 836*cdf0e10cSrcweir ::canvas::tools::calcTransformedRectBounds( aTextureDeviceRect, 837*cdf0e10cSrcweir aRect, 838*cdf0e10cSrcweir aTotalTransform ); 839*cdf0e10cSrcweir 840*cdf0e10cSrcweir const ::Rectangle aIntegerTextureDeviceRect( 841*cdf0e10cSrcweir ::vcl::unotools::rectangleFromB2DRectangle( aTextureDeviceRect ) ); 842*cdf0e10cSrcweir 843*cdf0e10cSrcweir if( bRectangularPolygon && 844*cdf0e10cSrcweir aIntegerTextureDeviceRect == aPolygonDeviceRect ) 845*cdf0e10cSrcweir { 846*cdf0e10cSrcweir rendering::RenderState aLocalState( renderState ); 847*cdf0e10cSrcweir ::canvas::tools::appendToRenderState(aLocalState, 848*cdf0e10cSrcweir aTextureTransform); 849*cdf0e10cSrcweir ::basegfx::B2DHomMatrix aScaleCorrection; 850*cdf0e10cSrcweir aScaleCorrection.scale( 1.0/aBmpSize.Width, 851*cdf0e10cSrcweir 1.0/aBmpSize.Height ); 852*cdf0e10cSrcweir ::canvas::tools::appendToRenderState(aLocalState, 853*cdf0e10cSrcweir aScaleCorrection); 854*cdf0e10cSrcweir 855*cdf0e10cSrcweir // need alpha modulation? 856*cdf0e10cSrcweir if( !::rtl::math::approxEqual( textures[0].Alpha, 857*cdf0e10cSrcweir 1.0 ) ) 858*cdf0e10cSrcweir { 859*cdf0e10cSrcweir // setup alpha modulation values 860*cdf0e10cSrcweir aLocalState.DeviceColor.realloc(4); 861*cdf0e10cSrcweir double* pColor = aLocalState.DeviceColor.getArray(); 862*cdf0e10cSrcweir pColor[0] = 863*cdf0e10cSrcweir pColor[1] = 864*cdf0e10cSrcweir pColor[2] = 0.0; 865*cdf0e10cSrcweir pColor[3] = textures[0].Alpha; 866*cdf0e10cSrcweir 867*cdf0e10cSrcweir return drawBitmapModulated( pCanvas, 868*cdf0e10cSrcweir textures[0].Bitmap, 869*cdf0e10cSrcweir viewState, 870*cdf0e10cSrcweir aLocalState ); 871*cdf0e10cSrcweir } 872*cdf0e10cSrcweir else 873*cdf0e10cSrcweir { 874*cdf0e10cSrcweir return drawBitmap( pCanvas, 875*cdf0e10cSrcweir textures[0].Bitmap, 876*cdf0e10cSrcweir viewState, 877*cdf0e10cSrcweir aLocalState ); 878*cdf0e10cSrcweir } 879*cdf0e10cSrcweir } 880*cdf0e10cSrcweir else 881*cdf0e10cSrcweir { 882*cdf0e10cSrcweir // No easy mapping to drawBitmap() - calculate 883*cdf0e10cSrcweir // texturing parameters 884*cdf0e10cSrcweir // =========================================== 885*cdf0e10cSrcweir 886*cdf0e10cSrcweir BitmapEx aBmpEx( tools::bitmapExFromXBitmap( textures[0].Bitmap ) ); 887*cdf0e10cSrcweir 888*cdf0e10cSrcweir // scale down bitmap to [0,1]x[0,1] rect, as required 889*cdf0e10cSrcweir // from the XCanvas interface. 890*cdf0e10cSrcweir ::basegfx::B2DHomMatrix aScaling; 891*cdf0e10cSrcweir ::basegfx::B2DHomMatrix aPureTotalTransform; // pure view*render*texture transform 892*cdf0e10cSrcweir aScaling.scale( 1.0/aBmpSize.Width, 893*cdf0e10cSrcweir 1.0/aBmpSize.Height ); 894*cdf0e10cSrcweir 895*cdf0e10cSrcweir aTotalTransform = aTextureTransform * aScaling; 896*cdf0e10cSrcweir aPureTotalTransform = aTextureTransform; 897*cdf0e10cSrcweir 898*cdf0e10cSrcweir // combine with view and render transform 899*cdf0e10cSrcweir ::basegfx::B2DHomMatrix aMatrix; 900*cdf0e10cSrcweir ::canvas::tools::mergeViewAndRenderTransform(aMatrix, viewState, renderState); 901*cdf0e10cSrcweir 902*cdf0e10cSrcweir // combine all three transformations into one 903*cdf0e10cSrcweir // global texture-to-device-space transformation 904*cdf0e10cSrcweir aTotalTransform *= aMatrix; 905*cdf0e10cSrcweir aPureTotalTransform *= aMatrix; 906*cdf0e10cSrcweir 907*cdf0e10cSrcweir // analyze transformation, and setup an 908*cdf0e10cSrcweir // appropriate GraphicObject 909*cdf0e10cSrcweir ::basegfx::B2DVector aScale; 910*cdf0e10cSrcweir ::basegfx::B2DPoint aOutputPos; 911*cdf0e10cSrcweir double nRotate; 912*cdf0e10cSrcweir double nShearX; 913*cdf0e10cSrcweir aTotalTransform.decompose( aScale, aOutputPos, nRotate, nShearX ); 914*cdf0e10cSrcweir 915*cdf0e10cSrcweir GraphicAttr aGrfAttr; 916*cdf0e10cSrcweir GraphicObjectSharedPtr pGrfObj; 917*cdf0e10cSrcweir 918*cdf0e10cSrcweir if( ::basegfx::fTools::equalZero( nShearX ) ) 919*cdf0e10cSrcweir { 920*cdf0e10cSrcweir // no shear, GraphicObject is enough (the 921*cdf0e10cSrcweir // GraphicObject only supports scaling, rotation 922*cdf0e10cSrcweir // and translation) 923*cdf0e10cSrcweir 924*cdf0e10cSrcweir // setup GraphicAttr 925*cdf0e10cSrcweir aGrfAttr.SetMirrorFlags( 926*cdf0e10cSrcweir ( aScale.getX() < 0.0 ? BMP_MIRROR_HORZ : 0 ) | 927*cdf0e10cSrcweir ( aScale.getY() < 0.0 ? BMP_MIRROR_VERT : 0 ) ); 928*cdf0e10cSrcweir aGrfAttr.SetRotation( static_cast< sal_uInt16 >(::basegfx::fround( nRotate*10.0 )) ); 929*cdf0e10cSrcweir 930*cdf0e10cSrcweir pGrfObj.reset( new GraphicObject( aBmpEx ) ); 931*cdf0e10cSrcweir } 932*cdf0e10cSrcweir else 933*cdf0e10cSrcweir { 934*cdf0e10cSrcweir // complex transformation, use generic affine bitmap 935*cdf0e10cSrcweir // transformation 936*cdf0e10cSrcweir aBmpEx = tools::transformBitmap( aBmpEx, 937*cdf0e10cSrcweir aTotalTransform, 938*cdf0e10cSrcweir uno::Sequence< double >(), 939*cdf0e10cSrcweir tools::MODULATE_NONE); 940*cdf0e10cSrcweir 941*cdf0e10cSrcweir pGrfObj.reset( new GraphicObject( aBmpEx ) ); 942*cdf0e10cSrcweir 943*cdf0e10cSrcweir // clear scale values, generated bitmap already 944*cdf0e10cSrcweir // contains scaling 945*cdf0e10cSrcweir aScale.setX( 0.0 ); aScale.setY( 0.0 ); 946*cdf0e10cSrcweir } 947*cdf0e10cSrcweir 948*cdf0e10cSrcweir 949*cdf0e10cSrcweir // render texture tiled into polygon 950*cdf0e10cSrcweir // ================================= 951*cdf0e10cSrcweir 952*cdf0e10cSrcweir // calc device space direction vectors. We employ 953*cdf0e10cSrcweir // the followin approach for tiled output: the 954*cdf0e10cSrcweir // texture bitmap is output in texture space 955*cdf0e10cSrcweir // x-major order, i.e. tile neighbors in texture 956*cdf0e10cSrcweir // space x direction are rendered back-to-back in 957*cdf0e10cSrcweir // device coordinate space (after the full device 958*cdf0e10cSrcweir // transformation). Thus, the aNextTile* vectors 959*cdf0e10cSrcweir // denote the output position updates in device 960*cdf0e10cSrcweir // space, to get from one tile to the next. 961*cdf0e10cSrcweir ::basegfx::B2DVector aNextTileX( 1.0, 0.0 ); 962*cdf0e10cSrcweir ::basegfx::B2DVector aNextTileY( 0.0, 1.0 ); 963*cdf0e10cSrcweir aNextTileX *= aPureTotalTransform; 964*cdf0e10cSrcweir aNextTileY *= aPureTotalTransform; 965*cdf0e10cSrcweir 966*cdf0e10cSrcweir ::basegfx::B2DHomMatrix aInverseTextureTransform( aPureTotalTransform ); 967*cdf0e10cSrcweir 968*cdf0e10cSrcweir ENSURE_ARG_OR_THROW( aInverseTextureTransform.isInvertible(), 969*cdf0e10cSrcweir "CanvasHelper::fillTexturedPolyPolygon(): singular texture matrix" ); 970*cdf0e10cSrcweir 971*cdf0e10cSrcweir aInverseTextureTransform.invert(); 972*cdf0e10cSrcweir 973*cdf0e10cSrcweir // calc bound rect of extended texture area in 974*cdf0e10cSrcweir // device coordinates. Therefore, we first calc 975*cdf0e10cSrcweir // the area of the polygon bound rect in texture 976*cdf0e10cSrcweir // space. To maintain texture phase, this bound 977*cdf0e10cSrcweir // rect is then extended to integer coordinates 978*cdf0e10cSrcweir // (extended, because shrinking might leave some 979*cdf0e10cSrcweir // inner polygon areas unfilled). 980*cdf0e10cSrcweir // Finally, the bound rect is transformed back to 981*cdf0e10cSrcweir // device coordinate space, were we determine the 982*cdf0e10cSrcweir // start point from it. 983*cdf0e10cSrcweir ::basegfx::B2DRectangle aTextureSpacePolygonRect; 984*cdf0e10cSrcweir ::canvas::tools::calcTransformedRectBounds( aTextureSpacePolygonRect, 985*cdf0e10cSrcweir ::vcl::unotools::b2DRectangleFromRectangle( 986*cdf0e10cSrcweir aPolygonDeviceRect ), 987*cdf0e10cSrcweir aInverseTextureTransform ); 988*cdf0e10cSrcweir 989*cdf0e10cSrcweir // calc left, top of extended polygon rect in 990*cdf0e10cSrcweir // texture space, create one-texture instance rect 991*cdf0e10cSrcweir // from it (i.e. rect from start point extending 992*cdf0e10cSrcweir // 1.0 units to the right and 1.0 units to the 993*cdf0e10cSrcweir // bottom). Note that the rounding employed here 994*cdf0e10cSrcweir // is a bit subtle, since we need to round up/down 995*cdf0e10cSrcweir // as _soon_ as any fractional amount is 996*cdf0e10cSrcweir // encountered. This is to ensure that the full 997*cdf0e10cSrcweir // polygon area is filled with texture tiles. 998*cdf0e10cSrcweir const sal_Int32 nX1( ::canvas::tools::roundDown( aTextureSpacePolygonRect.getMinX() ) ); 999*cdf0e10cSrcweir const sal_Int32 nY1( ::canvas::tools::roundDown( aTextureSpacePolygonRect.getMinY() ) ); 1000*cdf0e10cSrcweir const sal_Int32 nX2( ::canvas::tools::roundUp( aTextureSpacePolygonRect.getMaxX() ) ); 1001*cdf0e10cSrcweir const sal_Int32 nY2( ::canvas::tools::roundUp( aTextureSpacePolygonRect.getMaxY() ) ); 1002*cdf0e10cSrcweir const ::basegfx::B2DRectangle aSingleTextureRect( 1003*cdf0e10cSrcweir nX1, nY1, 1004*cdf0e10cSrcweir nX1 + 1.0, 1005*cdf0e10cSrcweir nY1 + 1.0 ); 1006*cdf0e10cSrcweir 1007*cdf0e10cSrcweir // and convert back to device space 1008*cdf0e10cSrcweir ::basegfx::B2DRectangle aSingleDeviceTextureRect; 1009*cdf0e10cSrcweir ::canvas::tools::calcTransformedRectBounds( aSingleDeviceTextureRect, 1010*cdf0e10cSrcweir aSingleTextureRect, 1011*cdf0e10cSrcweir aPureTotalTransform ); 1012*cdf0e10cSrcweir 1013*cdf0e10cSrcweir const ::Point aPtRepeat( ::vcl::unotools::pointFromB2DPoint( 1014*cdf0e10cSrcweir aSingleDeviceTextureRect.getMinimum() ) ); 1015*cdf0e10cSrcweir const ::Size aSz( ::basegfx::fround( aScale.getX() * aBmpSize.Width ), 1016*cdf0e10cSrcweir ::basegfx::fround( aScale.getY() * aBmpSize.Height ) ); 1017*cdf0e10cSrcweir const ::Size aIntegerNextTileX( ::vcl::unotools::sizeFromB2DSize(aNextTileX) ); 1018*cdf0e10cSrcweir const ::Size aIntegerNextTileY( ::vcl::unotools::sizeFromB2DSize(aNextTileY) ); 1019*cdf0e10cSrcweir 1020*cdf0e10cSrcweir const ::Point aPt( textures[0].RepeatModeX == rendering::TexturingMode::NONE ? 1021*cdf0e10cSrcweir ::basegfx::fround( aOutputPos.getX() ) : aPtRepeat.X(), 1022*cdf0e10cSrcweir textures[0].RepeatModeY == rendering::TexturingMode::NONE ? 1023*cdf0e10cSrcweir ::basegfx::fround( aOutputPos.getY() ) : aPtRepeat.Y() ); 1024*cdf0e10cSrcweir const sal_Int32 nTilesX( textures[0].RepeatModeX == rendering::TexturingMode::NONE ? 1025*cdf0e10cSrcweir 1 : nX2 - nX1 ); 1026*cdf0e10cSrcweir const sal_Int32 nTilesY( textures[0].RepeatModeX == rendering::TexturingMode::NONE ? 1027*cdf0e10cSrcweir 1 : nY2 - nY1 ); 1028*cdf0e10cSrcweir 1029*cdf0e10cSrcweir OutputDevice& rOutDev( mpOutDev->getOutDev() ); 1030*cdf0e10cSrcweir 1031*cdf0e10cSrcweir if( bRectangularPolygon ) 1032*cdf0e10cSrcweir { 1033*cdf0e10cSrcweir // use optimized output path 1034*cdf0e10cSrcweir // ------------------------- 1035*cdf0e10cSrcweir 1036*cdf0e10cSrcweir // this distinction really looks like a 1037*cdf0e10cSrcweir // micro-optimisation, but in fact greatly speeds up 1038*cdf0e10cSrcweir // especially complex fills. That's because when using 1039*cdf0e10cSrcweir // clipping, we can output polygons instead of 1040*cdf0e10cSrcweir // poly-polygons, and don't have to output the gradient 1041*cdf0e10cSrcweir // twice for XOR 1042*cdf0e10cSrcweir 1043*cdf0e10cSrcweir // setup alpha modulation 1044*cdf0e10cSrcweir if( !::rtl::math::approxEqual( textures[0].Alpha, 1045*cdf0e10cSrcweir 1.0 ) ) 1046*cdf0e10cSrcweir { 1047*cdf0e10cSrcweir // TODO(F1): Note that the GraphicManager has 1048*cdf0e10cSrcweir // a subtle difference in how it calculates 1049*cdf0e10cSrcweir // the resulting alpha value: it's using the 1050*cdf0e10cSrcweir // inverse alpha values (i.e. 'transparency'), 1051*cdf0e10cSrcweir // and calculates transOrig + transModulate, 1052*cdf0e10cSrcweir // instead of transOrig + transModulate - 1053*cdf0e10cSrcweir // transOrig*transModulate (which would be 1054*cdf0e10cSrcweir // equivalent to the origAlpha*modulateAlpha 1055*cdf0e10cSrcweir // the DX canvas performs) 1056*cdf0e10cSrcweir aGrfAttr.SetTransparency( 1057*cdf0e10cSrcweir static_cast< sal_uInt8 >( 1058*cdf0e10cSrcweir ::basegfx::fround( 255.0*( 1.0 - textures[0].Alpha ) ) ) ); 1059*cdf0e10cSrcweir } 1060*cdf0e10cSrcweir 1061*cdf0e10cSrcweir rOutDev.IntersectClipRegion( aPolygonDeviceRect ); 1062*cdf0e10cSrcweir textureFill( rOutDev, 1063*cdf0e10cSrcweir *pGrfObj, 1064*cdf0e10cSrcweir aPt, 1065*cdf0e10cSrcweir aIntegerNextTileX, 1066*cdf0e10cSrcweir aIntegerNextTileY, 1067*cdf0e10cSrcweir nTilesX, 1068*cdf0e10cSrcweir nTilesY, 1069*cdf0e10cSrcweir aSz, 1070*cdf0e10cSrcweir aGrfAttr ); 1071*cdf0e10cSrcweir 1072*cdf0e10cSrcweir if( mp2ndOutDev ) 1073*cdf0e10cSrcweir { 1074*cdf0e10cSrcweir OutputDevice& r2ndOutDev( mp2ndOutDev->getOutDev() ); 1075*cdf0e10cSrcweir r2ndOutDev.IntersectClipRegion( aPolygonDeviceRect ); 1076*cdf0e10cSrcweir textureFill( r2ndOutDev, 1077*cdf0e10cSrcweir *pGrfObj, 1078*cdf0e10cSrcweir aPt, 1079*cdf0e10cSrcweir aIntegerNextTileX, 1080*cdf0e10cSrcweir aIntegerNextTileY, 1081*cdf0e10cSrcweir nTilesX, 1082*cdf0e10cSrcweir nTilesY, 1083*cdf0e10cSrcweir aSz, 1084*cdf0e10cSrcweir aGrfAttr ); 1085*cdf0e10cSrcweir } 1086*cdf0e10cSrcweir } 1087*cdf0e10cSrcweir else 1088*cdf0e10cSrcweir { 1089*cdf0e10cSrcweir // output texture the hard way: XORing out the 1090*cdf0e10cSrcweir // polygon 1091*cdf0e10cSrcweir // =========================================== 1092*cdf0e10cSrcweir 1093*cdf0e10cSrcweir if( !::rtl::math::approxEqual( textures[0].Alpha, 1094*cdf0e10cSrcweir 1.0 ) ) 1095*cdf0e10cSrcweir { 1096*cdf0e10cSrcweir // uh-oh. alpha blending is required, 1097*cdf0e10cSrcweir // cannot do direct XOR, but have to 1098*cdf0e10cSrcweir // prepare the filled polygon within a 1099*cdf0e10cSrcweir // VDev 1100*cdf0e10cSrcweir VirtualDevice aVDev( rOutDev ); 1101*cdf0e10cSrcweir aVDev.SetOutputSizePixel( aPolygonDeviceRect.GetSize() ); 1102*cdf0e10cSrcweir 1103*cdf0e10cSrcweir // shift output to origin of VDev 1104*cdf0e10cSrcweir const ::Point aOutPos( aPt - aPolygonDeviceRect.TopLeft() ); 1105*cdf0e10cSrcweir aPolyPoly.Translate( ::Point( -aPolygonDeviceRect.Left(), 1106*cdf0e10cSrcweir -aPolygonDeviceRect.Top() ) ); 1107*cdf0e10cSrcweir 1108*cdf0e10cSrcweir const Region aPolyClipRegion( aPolyPoly ); 1109*cdf0e10cSrcweir 1110*cdf0e10cSrcweir aVDev.SetClipRegion( aPolyClipRegion ); 1111*cdf0e10cSrcweir textureFill( aVDev, 1112*cdf0e10cSrcweir *pGrfObj, 1113*cdf0e10cSrcweir aOutPos, 1114*cdf0e10cSrcweir aIntegerNextTileX, 1115*cdf0e10cSrcweir aIntegerNextTileY, 1116*cdf0e10cSrcweir nTilesX, 1117*cdf0e10cSrcweir nTilesY, 1118*cdf0e10cSrcweir aSz, 1119*cdf0e10cSrcweir aGrfAttr ); 1120*cdf0e10cSrcweir 1121*cdf0e10cSrcweir // output VDev content alpha-blended to 1122*cdf0e10cSrcweir // target position. 1123*cdf0e10cSrcweir const ::Point aEmptyPoint; 1124*cdf0e10cSrcweir Bitmap aContentBmp( 1125*cdf0e10cSrcweir aVDev.GetBitmap( aEmptyPoint, 1126*cdf0e10cSrcweir aVDev.GetOutputSizePixel() ) ); 1127*cdf0e10cSrcweir 1128*cdf0e10cSrcweir sal_uInt8 nCol( static_cast< sal_uInt8 >( 1129*cdf0e10cSrcweir ::basegfx::fround( 255.0*( 1.0 - textures[0].Alpha ) ) ) ); 1130*cdf0e10cSrcweir AlphaMask aAlpha( aVDev.GetOutputSizePixel(), 1131*cdf0e10cSrcweir &nCol ); 1132*cdf0e10cSrcweir 1133*cdf0e10cSrcweir BitmapEx aOutputBmpEx( aContentBmp, aAlpha ); 1134*cdf0e10cSrcweir rOutDev.DrawBitmapEx( aPolygonDeviceRect.TopLeft(), 1135*cdf0e10cSrcweir aOutputBmpEx ); 1136*cdf0e10cSrcweir 1137*cdf0e10cSrcweir if( mp2ndOutDev ) 1138*cdf0e10cSrcweir mp2ndOutDev->getOutDev().DrawBitmapEx( aPolygonDeviceRect.TopLeft(), 1139*cdf0e10cSrcweir aOutputBmpEx ); 1140*cdf0e10cSrcweir } 1141*cdf0e10cSrcweir else 1142*cdf0e10cSrcweir { 1143*cdf0e10cSrcweir const Region aPolyClipRegion( aPolyPoly ); 1144*cdf0e10cSrcweir 1145*cdf0e10cSrcweir rOutDev.Push( PUSH_CLIPREGION ); 1146*cdf0e10cSrcweir rOutDev.SetClipRegion( aPolyClipRegion ); 1147*cdf0e10cSrcweir 1148*cdf0e10cSrcweir textureFill( rOutDev, 1149*cdf0e10cSrcweir *pGrfObj, 1150*cdf0e10cSrcweir aPt, 1151*cdf0e10cSrcweir aIntegerNextTileX, 1152*cdf0e10cSrcweir aIntegerNextTileY, 1153*cdf0e10cSrcweir nTilesX, 1154*cdf0e10cSrcweir nTilesY, 1155*cdf0e10cSrcweir aSz, 1156*cdf0e10cSrcweir aGrfAttr ); 1157*cdf0e10cSrcweir rOutDev.Pop(); 1158*cdf0e10cSrcweir 1159*cdf0e10cSrcweir if( mp2ndOutDev ) 1160*cdf0e10cSrcweir { 1161*cdf0e10cSrcweir OutputDevice& r2ndOutDev( mp2ndOutDev->getOutDev() ); 1162*cdf0e10cSrcweir r2ndOutDev.Push( PUSH_CLIPREGION ); 1163*cdf0e10cSrcweir 1164*cdf0e10cSrcweir r2ndOutDev.SetClipRegion( aPolyClipRegion ); 1165*cdf0e10cSrcweir textureFill( r2ndOutDev, 1166*cdf0e10cSrcweir *pGrfObj, 1167*cdf0e10cSrcweir aPt, 1168*cdf0e10cSrcweir aIntegerNextTileX, 1169*cdf0e10cSrcweir aIntegerNextTileY, 1170*cdf0e10cSrcweir nTilesX, 1171*cdf0e10cSrcweir nTilesY, 1172*cdf0e10cSrcweir aSz, 1173*cdf0e10cSrcweir aGrfAttr ); 1174*cdf0e10cSrcweir r2ndOutDev.Pop(); 1175*cdf0e10cSrcweir } 1176*cdf0e10cSrcweir } 1177*cdf0e10cSrcweir } 1178*cdf0e10cSrcweir } 1179*cdf0e10cSrcweir } 1180*cdf0e10cSrcweir } 1181*cdf0e10cSrcweir 1182*cdf0e10cSrcweir // TODO(P1): Provide caching here. 1183*cdf0e10cSrcweir return uno::Reference< rendering::XCachedPrimitive >(NULL); 1184*cdf0e10cSrcweir } 1185*cdf0e10cSrcweir 1186*cdf0e10cSrcweir } 1187