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