merge the formfield patch from ooo-build
[ooovba.git] / canvas / source / directx / dx_canvashelper_texturefill.cxx
blobb17e49d710c7071d68f0e85dd2937069af23f1f9
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: dx_canvashelper_texturefill.cxx,v $
10 * $Revision: 1.4 $
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;
58 namespace dxcanvas
60 namespace
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(
74 Gdiplus::PointF(0.0f,
75 0.5f),
76 Gdiplus::PointF(1.0f,
77 0.5f),
78 rColor1,
79 rColor2 );
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
91 // path bound area
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,
113 aLeftBottom,
114 aRightTop,
115 aRightBottom,
116 tools::b2dRangeFromGdiPlusRectF( aBounds ) );
118 // calc length of bound rect diagonal
119 const double nDiagonalLength(
120 hypot( aBounds.Width,
121 aBounds.Height ) );
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 ) )
154 return false;
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 ) )
179 return false;
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 );
190 return true;
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,
204 0.5f),
205 Gdiplus::PointF(1.0f,
206 0.5f),
207 rColor1,
208 rColor1 );
210 Gdiplus::Color aColors[] =
212 rColor1, // at 0.0
213 rColor2, // at 0.5
214 rColor1 // at 1.0
217 Gdiplus::REAL aPositions[] =
219 0.0,
220 0.5,
224 if( Gdiplus::Ok != aBrush.SetInterpolationColors( aColors,
225 aPositions,
226 sizeof( aPositions ) / sizeof(Gdiplus::REAL) ) )
228 return false;
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
241 // path bound area
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,
263 aLeftBottom,
264 aRightTop,
265 aRightBottom,
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 ) )
287 return false;
289 if( Gdiplus::Ok != rGraphics->SetClip( &aClipPath,
290 Gdiplus::CombineModeIntersect ) )
292 return false;
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 );
303 return true;
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[] =
315 rColor1
318 INT nCount(1);
320 pGradientBrush->SetSurroundColors( aColors,
321 &nCount );
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
339 // below
340 GraphicsPathSharedPtr pFillPath( rPath );
342 // clone original gradient path object, we need to change it
343 // below
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,
361 1.0) )
363 // KLUDGE 1:
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 ) )
379 return false;
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(
394 ::std::max(
395 labs( rColor1.GetRed() - rColor2.GetRed() ),
396 ::std::max(
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
414 // color steps.
415 const int nStepCount(
416 ::std::max(
418 ::std::min(
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
444 // debug=t)
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
464 // between 1.0 and
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
473 // between 1.0 and
474 // relative focus width
475 nCurrScale );
477 else
479 aScaleMatrix.scale( nCurrScale,
480 nCurrScale );
483 aScaleMatrix.translate( 0.5, 0.5 );
485 tools::gdiPlusMatrixFromB2DHomMatrix( aGDIScaleMatrix,
486 aScaleMatrix );
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 );
498 else
500 // KLUDGE 2:
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(
516 pGradientPath,
517 rColor1,
518 rColor2 );
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 );
533 else
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 ),
549 0.0001f );
551 rGraphics->DrawRectangle( &aPen,
552 Gdiplus::RectF( 0.0f, 0.0f,
553 1.0f, 1.0f ) );
554 #endif
556 return true;
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,
570 rColor1,
571 rColor2,
572 rPath,
573 texture );
574 break;
576 case ::canvas::ParametricPolyPolygon::GRADIENT_AXIAL:
577 fillAxialGradient( rGraphics,
578 rColor1,
579 rColor2,
580 rPath,
581 texture );
582 break;
584 case ::canvas::ParametricPolyPolygon::GRADIENT_ELLIPTICAL:
585 // FALLTHROUGH intended
586 case ::canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR:
587 fillPolygonalGradient( rValues,
588 rGraphics,
589 rColor1,
590 rColor2,
591 rPath,
592 texture );
593 break;
595 default:
596 ENSURE_OR_THROW( false,
597 "CanvasHelper::fillGradient(): Unexpected case" );
600 return true;
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
625 // way!)
626 BitmapSharedPtr pBitmap(
627 tools::bitmapFromXBitmap( xBitmap ) );
629 TextureBrushSharedPtr pBrush;
631 if( ::rtl::math::approxEqual( rTexture.Alpha,
632 1.0 ) )
634 pBrush.reset(
635 new Gdiplus::TextureBrush(
636 pBitmap.get(),
637 bClamp ? Gdiplus::WrapModeClamp : Gdiplus::WrapModeTile ) );
639 else
641 Gdiplus::ImageAttributes aImgAttr;
643 tools::setModulateImageAttributes( aImgAttr,
644 1.0,
645 1.0,
646 1.0,
647 rTexture.Alpha );
649 Gdiplus::Rect aRect(0,0,
650 aBmpSize.Width,
651 aBmpSize.Height);
652 pBrush.reset(
653 new Gdiplus::TextureBrush(
654 pBitmap.get(),
655 aRect,
656 &aImgAttr ) );
658 pBrush->SetWrapMode(
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
673 ENSURE_OR_THROW(
674 Gdiplus::Ok == rGraphics->FillPath( pBrush.get(),
675 rPath.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");
693 if( needOutput() )
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() );
707 if( pGradient )
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,
719 aColor1,
720 aColor2,
721 pGraphics,
722 tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ),
723 textures[0] );
726 else if( textures[0].Bitmap.is() )
728 // TODO(E1): Return value
729 // TODO(F1): FillRule
730 fillBitmap( textures[0].Bitmap,
731 pGraphics,
732 tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ),
733 textures[0] );
737 // TODO(P1): Provide caching here.
738 return uno::Reference< rendering::XCachedPrimitive >(NULL);