1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: dx_canvashelper_texturefill.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_canvas.hxx"
34 #include <canvas/debug.hxx>
35 #include <tools/diagnose_ex.h>
36 #include <rtl/math.hxx>
38 #include <com/sun/star/rendering/TexturingMode.hpp>
40 #include <basegfx/matrix/b2dhommatrix.hxx>
41 #include <basegfx/point/b2dpoint.hxx>
42 #include <basegfx/range/b2drectangle.hxx>
43 #include <basegfx/numeric/ftools.hxx>
44 #include <basegfx/tools/tools.hxx>
45 #include <basegfx/tools/canvastools.hxx>
47 #include <canvas/parametricpolypolygon.hxx>
49 #include "dx_spritecanvas.hxx"
50 #include "dx_canvashelper.hxx"
51 #include "dx_impltools.hxx"
53 #include <boost/scoped_ptr.hpp>
56 using namespace ::com::sun::star
;
62 typedef ::boost::shared_ptr
< Gdiplus::PathGradientBrush
> PathGradientBrushSharedPtr
;
64 bool fillLinearGradient( GraphicsSharedPtr
& rGraphics
,
65 const Gdiplus::Color
& rColor1
,
66 const Gdiplus::Color
& rColor2
,
67 const GraphicsPathSharedPtr
& rFillPath
,
68 const rendering::Texture
& texture
)
70 // setup a linear gradient with two colors
71 // ---------------------------------------
73 Gdiplus::LinearGradientBrush
aBrush(
81 // render background color, as LinearGradientBrush does not
82 // properly support the WrapModeClamp repeat mode
83 Gdiplus::SolidBrush
aBackgroundBrush( rColor1
);
84 rGraphics
->FillPath( &aBackgroundBrush
, rFillPath
.get() );
86 // TODO(F2): This does not yet support other repeat modes
87 // except clamp, and probably also no multi-texturing
89 // calculate parallelogram of gradient in object space, extend
90 // top and bottom of it such that they cover the whole fill
92 ::basegfx::B2DHomMatrix aTextureTransform
;
93 ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform
,
94 texture
.AffineTransform
);
96 ::basegfx::B2DPoint
aLeftTop( 0.0, 0.0 );
97 ::basegfx::B2DPoint
aLeftBottom( 0.0, 1.0 );
98 ::basegfx::B2DPoint
aRightTop( 1.0, 0.0 );
99 ::basegfx::B2DPoint
aRightBottom( 1.0, 1.0 );
101 aLeftTop
*= aTextureTransform
;
102 aLeftBottom
*= aTextureTransform
;
103 aRightTop
*= aTextureTransform
;
104 aRightBottom
*= aTextureTransform
;
106 Gdiplus::RectF aBounds
;
107 rFillPath
->GetBounds( &aBounds
, NULL
, NULL
);
109 // now, we potentially have to enlarge our gradient area
110 // atop and below the transformed [0,1]x[0,1] unit rect,
111 // for the gradient to fill the complete bound rect.
112 ::basegfx::tools::infiniteLineFromParallelogram( aLeftTop
,
116 tools::b2dRangeFromGdiPlusRectF( aBounds
) );
118 // calc length of bound rect diagonal
119 const double nDiagonalLength(
120 hypot( aBounds
.Width
,
123 // generate a path which covers the 'right' side of the
124 // gradient, extending two times the bound rect diagonal to
125 // the right (and thus covering the whole half plane 'right'
126 // of the gradient). Take the middle of the gradient as the
127 // 'left' side of the polygon, to not fall victim to rounding
128 // errors at the edge.
129 ::basegfx::B2DVector
aDirection( aLeftTop
- aLeftBottom
);
130 aDirection
= ::basegfx::getNormalizedPerpendicular( aDirection
);
131 aDirection
*= nDiagonalLength
;
133 const ::basegfx::B2DPoint
aHalfPlaneLeftTop( (aLeftTop
+ aRightTop
) * 0.5 );
134 const ::basegfx::B2DPoint
aHalfPlaneLeftBottom( (aLeftBottom
+ aRightBottom
) * 0.5 );
135 const ::basegfx::B2DPoint
aHalfPlaneRightTop( aRightTop
+ aDirection
);
136 const ::basegfx::B2DPoint
aHalfPlaneRightBottom( aRightBottom
+ aDirection
);
138 Gdiplus::GraphicsPath aSolidFillPath
;
139 aSolidFillPath
.AddLine( static_cast<Gdiplus::REAL
>(aHalfPlaneLeftTop
.getX()),
140 static_cast<Gdiplus::REAL
>(aHalfPlaneLeftTop
.getY()),
141 static_cast<Gdiplus::REAL
>(aHalfPlaneRightTop
.getX()),
142 static_cast<Gdiplus::REAL
>(aHalfPlaneRightTop
.getY()) );
143 aSolidFillPath
.AddLine( static_cast<Gdiplus::REAL
>(aHalfPlaneRightBottom
.getX()),
144 static_cast<Gdiplus::REAL
>(aHalfPlaneRightBottom
.getY()),
145 static_cast<Gdiplus::REAL
>(aHalfPlaneLeftBottom
.getX()),
146 static_cast<Gdiplus::REAL
>(aHalfPlaneLeftBottom
.getY()) );
147 aSolidFillPath
.CloseFigure();
149 // limit output to fill path, we've just generated a path that
150 // might be substantially larger
151 if( Gdiplus::Ok
!= rGraphics
->SetClip( rFillPath
.get(),
152 Gdiplus::CombineModeIntersect
) )
157 Gdiplus::SolidBrush
aBackgroundBrush2( rColor2
);
158 rGraphics
->FillPath( &aBackgroundBrush2
, &aSolidFillPath
);
160 // generate clip polygon from the extended parallelogram
161 // (exploit the feature that distinct lines in a figure are
162 // automatically closed by a straight line)
163 Gdiplus::GraphicsPath aClipPath
;
164 aClipPath
.AddLine( static_cast<Gdiplus::REAL
>(aLeftTop
.getX()),
165 static_cast<Gdiplus::REAL
>(aLeftTop
.getY()),
166 static_cast<Gdiplus::REAL
>(aRightTop
.getX()),
167 static_cast<Gdiplus::REAL
>(aRightTop
.getY()) );
168 aClipPath
.AddLine( static_cast<Gdiplus::REAL
>(aRightBottom
.getX()),
169 static_cast<Gdiplus::REAL
>(aRightBottom
.getY()),
170 static_cast<Gdiplus::REAL
>(aLeftBottom
.getX()),
171 static_cast<Gdiplus::REAL
>(aLeftBottom
.getY()) );
172 aClipPath
.CloseFigure();
174 // limit output to a _single_ strip of the gradient (have to
175 // clip here, since GDI+ wrapmode clamp does not work here)
176 if( Gdiplus::Ok
!= rGraphics
->SetClip( &aClipPath
,
177 Gdiplus::CombineModeIntersect
) )
182 // now, finally, output the gradient
183 Gdiplus::Matrix aMatrix
;
184 tools::gdiPlusMatrixFromAffineMatrix2D( aMatrix
,
185 texture
.AffineTransform
);
186 aBrush
.SetTransform( &aMatrix
);
188 rGraphics
->FillRectangle( &aBrush
, aBounds
);
193 bool fillAxialGradient( GraphicsSharedPtr
& rGraphics
,
194 const Gdiplus::Color
& rColor1
,
195 const Gdiplus::Color
& rColor2
,
196 const GraphicsPathSharedPtr
& rFillPath
,
197 const rendering::Texture
& texture
)
199 // setup a linear gradient with three colors
200 // -----------------------------------------
202 Gdiplus::LinearGradientBrush
aBrush(
203 Gdiplus::PointF(0.0f
,
205 Gdiplus::PointF(1.0f
,
210 Gdiplus::Color aColors
[] =
217 Gdiplus::REAL aPositions
[] =
224 if( Gdiplus::Ok
!= aBrush
.SetInterpolationColors( aColors
,
226 sizeof( aPositions
) / sizeof(Gdiplus::REAL
) ) )
231 // render background color, as LinearGradientBrush does not
232 // properly support the WrapModeClamp repeat mode
233 Gdiplus::SolidBrush
aBackgroundBrush( rColor1
);
234 rGraphics
->FillPath( &aBackgroundBrush
, rFillPath
.get() );
236 // TODO(F2): This does not yet support other repeat modes
237 // except clamp, and probably also no multi-texturing
239 // calculate parallelogram of gradient in object space, extend
240 // top and bottom of it such that they cover the whole fill
242 ::basegfx::B2DHomMatrix aTextureTransform
;
243 ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform
,
244 texture
.AffineTransform
);
246 ::basegfx::B2DPoint
aLeftTop( 0.0, 0.0 );
247 ::basegfx::B2DPoint
aLeftBottom( 0.0, 1.0 );
248 ::basegfx::B2DPoint
aRightTop( 1.0, 0.0 );
249 ::basegfx::B2DPoint
aRightBottom( 1.0, 1.0 );
251 aLeftTop
*= aTextureTransform
;
252 aLeftBottom
*= aTextureTransform
;
253 aRightTop
*= aTextureTransform
;
254 aRightBottom
*= aTextureTransform
;
256 Gdiplus::RectF aBounds
;
257 rFillPath
->GetBounds( &aBounds
, NULL
, NULL
);
259 // now, we potentially have to enlarge our gradient area
260 // atop and below the transformed [0,1]x[0,1] unit rect,
261 // for the gradient to fill the complete bound rect.
262 ::basegfx::tools::infiniteLineFromParallelogram( aLeftTop
,
266 tools::b2dRangeFromGdiPlusRectF( aBounds
) );
268 // generate clip polygon from the extended parallelogram
269 // (exploit the feature that distinct lines in a figure are
270 // automatically closed by a straight line)
271 Gdiplus::GraphicsPath aClipPath
;
272 aClipPath
.AddLine( static_cast<Gdiplus::REAL
>(aLeftTop
.getX()),
273 static_cast<Gdiplus::REAL
>(aLeftTop
.getY()),
274 static_cast<Gdiplus::REAL
>(aRightTop
.getX()),
275 static_cast<Gdiplus::REAL
>(aRightTop
.getY()) );
276 aClipPath
.AddLine( static_cast<Gdiplus::REAL
>(aRightBottom
.getX()),
277 static_cast<Gdiplus::REAL
>(aRightBottom
.getY()),
278 static_cast<Gdiplus::REAL
>(aLeftBottom
.getX()),
279 static_cast<Gdiplus::REAL
>(aLeftBottom
.getY()) );
280 aClipPath
.CloseFigure();
282 // limit output to a _single_ strip of the gradient (have to
283 // clip here, since GDI+ wrapmode clamp does not work here)
284 if( Gdiplus::Ok
!= rGraphics
->SetClip( rFillPath
.get(),
285 Gdiplus::CombineModeIntersect
) )
289 if( Gdiplus::Ok
!= rGraphics
->SetClip( &aClipPath
,
290 Gdiplus::CombineModeIntersect
) )
295 // now, finally, output the gradient
296 Gdiplus::Matrix aMatrix
;
297 tools::gdiPlusMatrixFromAffineMatrix2D( aMatrix
,
298 texture
.AffineTransform
);
299 aBrush
.SetTransform( &aMatrix
);
301 rGraphics
->FillRectangle( &aBrush
, aBounds
);
306 PathGradientBrushSharedPtr
createPathGradientBrush( const GraphicsPathSharedPtr
& rGradientPath
,
307 const Gdiplus::Color
& rColor1
,
308 const Gdiplus::Color
& rColor2
)
310 PathGradientBrushSharedPtr
pGradientBrush(
311 new Gdiplus::PathGradientBrush( rGradientPath
.get() ) );
313 Gdiplus::Color aColors
[] =
320 pGradientBrush
->SetSurroundColors( aColors
,
322 pGradientBrush
->SetCenterColor( rColor2
);
324 return pGradientBrush
;
327 bool fillPolygonalGradient( const ::canvas::ParametricPolyPolygon::Values
& rValues
,
328 GraphicsSharedPtr
& rGraphics
,
329 const Gdiplus::Color
& rColor1
,
330 const Gdiplus::Color
& rColor2
,
331 const GraphicsPathSharedPtr
& rPath
,
332 const rendering::Texture
& texture
)
334 Gdiplus::Matrix aMatrix
;
335 tools::gdiPlusMatrixFromAffineMatrix2D( aMatrix
,
336 texture
.AffineTransform
);
338 // copy original fill path object, might have to change it
340 GraphicsPathSharedPtr
pFillPath( rPath
);
342 // clone original gradient path object, we need to change it
344 GraphicsPathSharedPtr
pGradientPath(
345 tools::graphicsPathFromB2DPolygon( rValues
.maGradientPoly
) );
347 ENSURE_OR_RETURN( pGradientPath
.get(),
348 "ParametricPolyPolygon::fillPolygonalGradient(): Could not clone path" );
350 PathGradientBrushSharedPtr pGradientBrush
;
352 // fill background uniformly with end color
353 Gdiplus::SolidBrush
aBackgroundBrush( rColor1
);
354 rGraphics
->FillPath( &aBackgroundBrush
, pFillPath
.get() );
356 // scale focus according to aspect ratio: for wider-than-tall
357 // bounds (nAspectRatio > 1.0), the focus must have non-zero
358 // width. Specifically, a bound rect twice as wide as tall has
359 // a focus of half it's width.
360 if( !::rtl::math::approxEqual(rValues
.mnAspectRatio
,
365 // And here comes the greatest shortcoming of the GDI+
366 // gradients ever: SetFocusScales completely ignores
367 // transformations, both when set at the PathGradientBrush
368 // and for the world coordinate system. Thus, to correctly
369 // display anisotrophic path gradients, we have to render
370 // them by hand. WTF.
372 // TODO(F2): This does not yet support other repeat modes
373 // except clamp, and probably also no multi-texturing
375 // limit output to to-be-filled polygon
376 if( Gdiplus::Ok
!= rGraphics
->SetClip( pFillPath
.get(),
377 Gdiplus::CombineModeIntersect
) )
382 rGraphics
->MultiplyTransform( &aMatrix
);
384 // disable anti-aliasing, if any
385 const Gdiplus::SmoothingMode
eOldAAMode( rGraphics
->GetSmoothingMode() );
386 rGraphics
->SetSmoothingMode( Gdiplus::SmoothingModeHighSpeed
);
389 // determine number of steps to use
390 // --------------------------------
392 // TODO(Q2): Unify step calculations with VCL canvas
393 const int nColorSteps(
395 labs( rColor1
.GetRed() - rColor2
.GetRed() ),
397 labs( rColor1
.GetGreen() - rColor2
.GetGreen() ),
398 labs( rColor1
.GetBlue() - rColor2
.GetBlue() ) ) ) );
400 Gdiplus::Matrix aWorldTransformMatrix
;
401 rGraphics
->GetTransform( &aWorldTransformMatrix
);
403 Gdiplus::RectF aBounds
;
404 pGradientPath
->GetBounds( &aBounds
, &aWorldTransformMatrix
, NULL
);
406 // longest line in gradient bound rect
407 const int nGradientSize(
408 static_cast<int>( hypot( aBounds
.Width
, aBounds
.Height
) + 1.0 ) );
410 // typical number for pixel of the same color (strip size)
411 const int nStripSize( 2 );
413 // use at least three steps, and at utmost the number of
415 const int nStepCount(
419 nGradientSize
/ nStripSize
,
420 nColorSteps
) ) + 1 );
423 Gdiplus::SolidBrush
aFillBrush( rColor1
);
424 Gdiplus::Matrix aGDIScaleMatrix
;
425 ::basegfx::B2DHomMatrix aScaleMatrix
;
427 // calc relative size for anisotrophic polygon scaling:
428 // when the aspect ratio is e.g. 2.0, that denotes a
429 // gradient which is twice as wide as high. Then, to
430 // generate a symmetric gradient, the x direction is only
431 // scaled to 0.5 times the gradient width. Similarly, when
432 // the aspect ratio is 4.0, the focus has 3/4 the width of
433 // the overall gradient.
434 const double nRelativeFocusSize( rValues
.mnAspectRatio
> 1.0 ?
435 1.0 - 1.0/rValues
.mnAspectRatio
:
436 1.0 - rValues
.mnAspectRatio
);
438 for( int i
=1; i
<nStepCount
; ++i
)
440 // lerp color. Funnily, the straight-forward integer
441 // lerp ((nStepCount - i)*val + i*val)/nStepCount gets
442 // fully botched by MSVC, at least for anything that
443 // really inlines inlines (i.e. every compile without
445 const double nFrac( (double)i
/nStepCount
);
447 const Gdiplus::Color
aFillColor(
448 static_cast<BYTE
>( (1.0 - nFrac
)*rColor1
.GetRed() + nFrac
*rColor2
.GetRed() ),
449 static_cast<BYTE
>( (1.0 - nFrac
)*rColor1
.GetGreen() + nFrac
*rColor2
.GetGreen() ),
450 static_cast<BYTE
>( (1.0 - nFrac
)*rColor1
.GetBlue() + nFrac
*rColor2
.GetBlue() ) );
452 aFillBrush
.SetColor( aFillColor
);
454 const double nCurrScale( (nStepCount
-i
)/(double)nStepCount
);
455 aScaleMatrix
.identity();
456 aScaleMatrix
.translate( -0.5, -0.5 );
458 // handle anisotrophic polygon scaling
459 if( rValues
.mnAspectRatio
< 1.0 )
461 // height > width case
462 aScaleMatrix
.scale( nCurrScale
,
463 // lerp with nCurrScale
465 // relative focus height
466 nCurrScale
+ (1.0-nCurrScale
)*nRelativeFocusSize
);
468 else if( rValues
.mnAspectRatio
> 1.0 )
470 // width > height case
471 aScaleMatrix
.scale( nCurrScale
+ (1.0-nCurrScale
)*nRelativeFocusSize
,
472 // lerp with nCurrScale
474 // relative focus width
479 aScaleMatrix
.scale( nCurrScale
,
483 aScaleMatrix
.translate( 0.5, 0.5 );
485 tools::gdiPlusMatrixFromB2DHomMatrix( aGDIScaleMatrix
,
488 GraphicsPathSharedPtr
pScaledGradientPath(
489 tools::graphicsPathFromB2DPolygon( rValues
.maGradientPoly
) );
490 pScaledGradientPath
->Transform( &aGDIScaleMatrix
);
492 rGraphics
->FillPath( &aFillBrush
, pScaledGradientPath
.get() );
495 // reset to old anti-alias mode
496 rGraphics
->SetSmoothingMode( eOldAAMode
);
502 // We're generating a PathGradientBrush from scratch here,
503 // and put in a transformed GraphicsPath (transformed with
504 // the texture transform). This is because the
505 // straight-forward approach to store a Brush pointer at
506 // this class and set a texture transform via
507 // PathGradientBrush::SetTransform() is spoiled by MS: it
508 // seems that _either_ the texture transform, _or_ the
509 // transform at the Graphics can be set, but not both. If
510 // one sets both, only the translational components of the
511 // texture is respected.
513 pGradientPath
->Transform( &aMatrix
);
515 pGradientBrush
= createPathGradientBrush(
520 // explicitely setup center point. Since the center of GDI+
521 // gradients are by default the _centroid_ of the path
522 // (i.e. the weighted sum of edge points), it will not
523 // necessarily coincide with our notion of center.
524 Gdiplus::PointF
aCenterPoint(0.5, 0.5);
525 aMatrix
.TransformPoints( &aCenterPoint
);
526 pGradientBrush
->SetCenterPoint( aCenterPoint
);
528 const bool bTileX( texture
.RepeatModeX
!= rendering::TexturingMode::CLAMP
);
529 const bool bTileY( texture
.RepeatModeY
!= rendering::TexturingMode::CLAMP
);
531 if( bTileX
&& bTileY
)
532 pGradientBrush
->SetWrapMode( Gdiplus::WrapModeTile
);
535 OSL_ENSURE( bTileY
== bTileX
,
536 "ParametricPolyPolygon::fillPolygonalGradient(): Cannot have repeat x and repeat y differ!" );
538 pGradientBrush
->SetWrapMode( Gdiplus::WrapModeClamp
);
541 // render actual gradient
542 rGraphics
->FillPath( pGradientBrush
.get(), pFillPath
.get() );
545 #if defined(VERBOSE) && defined(DBG_UTIL)
546 rGraphics
->MultiplyTransform( &aMatrix
);
548 Gdiplus::Pen
aPen( Gdiplus::Color( 255, 255, 0, 0 ),
551 rGraphics
->DrawRectangle( &aPen
,
552 Gdiplus::RectF( 0.0f
, 0.0f
,
559 bool fillGradient( const ::canvas::ParametricPolyPolygon::Values
& rValues
,
560 const Gdiplus::Color
& rColor1
,
561 const Gdiplus::Color
& rColor2
,
562 GraphicsSharedPtr
& rGraphics
,
563 const GraphicsPathSharedPtr
& rPath
,
564 const rendering::Texture
& texture
)
566 switch( rValues
.meType
)
568 case ::canvas::ParametricPolyPolygon::GRADIENT_LINEAR
:
569 fillLinearGradient( rGraphics
,
576 case ::canvas::ParametricPolyPolygon::GRADIENT_AXIAL
:
577 fillAxialGradient( rGraphics
,
584 case ::canvas::ParametricPolyPolygon::GRADIENT_ELLIPTICAL
:
585 // FALLTHROUGH intended
586 case ::canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR
:
587 fillPolygonalGradient( rValues
,
596 ENSURE_OR_THROW( false,
597 "CanvasHelper::fillGradient(): Unexpected case" );
603 void fillBitmap( const uno::Reference
< rendering::XBitmap
>& xBitmap
,
604 GraphicsSharedPtr
& rGraphics
,
605 const GraphicsPathSharedPtr
& rPath
,
606 const rendering::Texture
& rTexture
)
608 OSL_ENSURE( rTexture
.RepeatModeX
==
609 rTexture
.RepeatModeY
,
610 "CanvasHelper::fillBitmap(): GDI+ cannot handle differing X/Y repeat mode." );
612 const bool bClamp( rTexture
.RepeatModeX
== rendering::TexturingMode::CLAMP
&&
613 rTexture
.RepeatModeY
== rendering::TexturingMode::CLAMP
);
615 const geometry::IntegerSize2D
aBmpSize( xBitmap
->getSize() );
616 ENSURE_ARG_OR_THROW( aBmpSize
.Width
!= 0 &&
617 aBmpSize
.Height
!= 0,
618 "CanvasHelper::fillBitmap(): zero-sized texture bitmap" );
620 // TODO(P3): Detect case that path is rectangle and
621 // bitmap is just scaled into that. Then, we can
622 // render directly, without generating a temporary
623 // GDI+ bitmap (this is significant, because drawing
624 // layer presents background object bitmap in that
626 BitmapSharedPtr
pBitmap(
627 tools::bitmapFromXBitmap( xBitmap
) );
629 TextureBrushSharedPtr pBrush
;
631 if( ::rtl::math::approxEqual( rTexture
.Alpha
,
635 new Gdiplus::TextureBrush(
637 bClamp
? Gdiplus::WrapModeClamp
: Gdiplus::WrapModeTile
) );
641 Gdiplus::ImageAttributes aImgAttr
;
643 tools::setModulateImageAttributes( aImgAttr
,
649 Gdiplus::Rect
aRect(0,0,
653 new Gdiplus::TextureBrush(
659 bClamp
? Gdiplus::WrapModeClamp
: Gdiplus::WrapModeTile
);
662 Gdiplus::Matrix aTextureTransform
;
663 tools::gdiPlusMatrixFromAffineMatrix2D( aTextureTransform
,
664 rTexture
.AffineTransform
);
666 // scale down bitmap to [0,1]x[0,1] rect, as required
667 // from the XCanvas interface.
668 pBrush
->ScaleTransform( static_cast< Gdiplus::REAL
>(1.0/aBmpSize
.Width
),
669 static_cast< Gdiplus::REAL
>(1.0/aBmpSize
.Height
) );
670 pBrush
->MultiplyTransform( &aTextureTransform
);
672 // TODO(F1): FillRule
674 Gdiplus::Ok
== rGraphics
->FillPath( pBrush
.get(),
676 "CanvasHelper::fillTexturedPolyPolygon(): GDI+ call failed" );
680 // -------------------------------------------------------------
682 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillTexturedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
683 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
684 const rendering::ViewState
& viewState
,
685 const rendering::RenderState
& renderState
,
686 const uno::Sequence
< rendering::Texture
>& textures
)
688 ENSURE_OR_THROW( xPolyPolygon
.is(),
689 "CanvasHelper::fillTexturedPolyPolygon: polygon is NULL");
690 ENSURE_OR_THROW( textures
.getLength(),
691 "CanvasHelper::fillTexturedPolyPolygon: empty texture sequence");
695 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
697 setupGraphicsState( pGraphics
, viewState
, renderState
);
699 // TODO(F1): Multi-texturing
700 if( textures
[0].Gradient
.is() )
702 // try to cast XParametricPolyPolygon2D reference to
703 // our implementation class.
704 ::canvas::ParametricPolyPolygon
* pGradient
=
705 dynamic_cast< ::canvas::ParametricPolyPolygon
* >( textures
[0].Gradient
.get() );
709 const ::canvas::ParametricPolyPolygon::Values
& rValues(
710 pGradient
->getValues() );
712 // TODO: use all the colors and place them on given positions/stops
713 const Gdiplus::Color
aColor1(tools::sequenceToArgb(rValues
.maColors
[0]));
714 const Gdiplus::Color
aColor2(tools::sequenceToArgb(rValues
.maColors
[rValues
.maColors
.getLength () - 1] ));
716 // TODO(E1): Return value
717 // TODO(F1): FillRule
718 fillGradient( rValues
,
722 tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon
),
726 else if( textures
[0].Bitmap
.is() )
728 // TODO(E1): Return value
729 // TODO(F1): FillRule
730 fillBitmap( textures
[0].Bitmap
,
732 tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon
),
737 // TODO(P1): Provide caching here.
738 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);