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