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