1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_canvas.hxx" 30 31 #include <canvas/debug.hxx> 32 #include <tools/diagnose_ex.h> 33 #include <rtl/math.hxx> 34 35 #include <com/sun/star/rendering/TexturingMode.hpp> 36 37 #include <basegfx/matrix/b2dhommatrix.hxx> 38 #include <basegfx/point/b2dpoint.hxx> 39 #include <basegfx/range/b2drectangle.hxx> 40 #include <basegfx/numeric/ftools.hxx> 41 #include <basegfx/polygon/b2dpolygontools.hxx> 42 #include <basegfx/tools/tools.hxx> 43 #include <basegfx/tools/lerp.hxx> 44 #include <basegfx/tools/keystoplerp.hxx> 45 #include <basegfx/tools/canvastools.hxx> 46 #include <basegfx/matrix/b2dhommatrixtools.hxx> 47 48 #include <canvas/parametricpolypolygon.hxx> 49 50 #include "dx_spritecanvas.hxx" 51 #include "dx_canvashelper.hxx" 52 #include "dx_impltools.hxx" 53 54 #include <boost/scoped_ptr.hpp> 55 #include <boost/bind.hpp> 56 #include <boost/tuple/tuple.hpp> 57 58 59 using namespace ::com::sun::star; 60 61 namespace dxcanvas 62 { 63 namespace 64 { 65 typedef ::boost::shared_ptr< Gdiplus::PathGradientBrush > PathGradientBrushSharedPtr; 66 67 bool fillLinearGradient( GraphicsSharedPtr& rGraphics, 68 const ::canvas::ParametricPolyPolygon::Values& /*rValues*/, 69 const std::vector< Gdiplus::Color >& rColors, 70 const std::vector< Gdiplus::REAL >& rStops, 71 const GraphicsPathSharedPtr& rFillPath, 72 const rendering::Texture& texture ) 73 { 74 // setup a linear gradient with given colors 75 // ----------------------------------------- 76 77 Gdiplus::LinearGradientBrush aBrush( 78 Gdiplus::PointF(0.0f, 79 0.5f), 80 Gdiplus::PointF(1.0f, 81 0.5f), 82 rColors[0], 83 rColors[1] ); 84 85 aBrush.SetInterpolationColors(&rColors[0], 86 &rStops[0], 87 rColors.size()); 88 89 // render background color, as LinearGradientBrush does not 90 // properly support the WrapModeClamp repeat mode 91 Gdiplus::SolidBrush aBackgroundBrush( rColors[0] ); 92 rGraphics->FillPath( &aBackgroundBrush, rFillPath.get() ); 93 94 // TODO(F2): This does not yet support other repeat modes 95 // except clamp, and probably also no multi-texturing 96 97 // calculate parallelogram of gradient in object space, extend 98 // top and bottom of it such that they cover the whole fill 99 // path bound area 100 ::basegfx::B2DHomMatrix aTextureTransform; 101 ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform, 102 texture.AffineTransform ); 103 104 ::basegfx::B2DPoint aLeftTop( 0.0, 0.0 ); 105 ::basegfx::B2DPoint aLeftBottom( 0.0, 1.0 ); 106 ::basegfx::B2DPoint aRightTop( 1.0, 0.0 ); 107 ::basegfx::B2DPoint aRightBottom( 1.0, 1.0 ); 108 109 aLeftTop *= aTextureTransform; 110 aLeftBottom *= aTextureTransform; 111 aRightTop *= aTextureTransform; 112 aRightBottom*= aTextureTransform; 113 114 Gdiplus::RectF aBounds; 115 rFillPath->GetBounds( &aBounds, NULL, NULL ); 116 117 // now, we potentially have to enlarge our gradient area 118 // atop and below the transformed [0,1]x[0,1] unit rect, 119 // for the gradient to fill the complete bound rect. 120 ::basegfx::tools::infiniteLineFromParallelogram( aLeftTop, 121 aLeftBottom, 122 aRightTop, 123 aRightBottom, 124 tools::b2dRangeFromGdiPlusRectF( aBounds ) ); 125 126 // calc length of bound rect diagonal 127 const double nDiagonalLength( 128 hypot( aBounds.Width, 129 aBounds.Height ) ); 130 131 // generate a path which covers the 'right' side of the 132 // gradient, extending two times the bound rect diagonal to 133 // the right (and thus covering the whole half plane 'right' 134 // of the gradient). Take the middle of the gradient as the 135 // 'left' side of the polygon, to not fall victim to rounding 136 // errors at the edge. 137 ::basegfx::B2DVector aDirection( aLeftTop - aLeftBottom ); 138 aDirection = ::basegfx::getNormalizedPerpendicular( aDirection ); 139 aDirection *= nDiagonalLength; 140 141 const ::basegfx::B2DPoint aHalfPlaneLeftTop( (aLeftTop + aRightTop) * 0.5 ); 142 const ::basegfx::B2DPoint aHalfPlaneLeftBottom( (aLeftBottom + aRightBottom) * 0.5 ); 143 const ::basegfx::B2DPoint aHalfPlaneRightTop( aRightTop + aDirection ); 144 const ::basegfx::B2DPoint aHalfPlaneRightBottom( aRightBottom + aDirection ); 145 146 Gdiplus::GraphicsPath aSolidFillPath; 147 aSolidFillPath.AddLine( static_cast<Gdiplus::REAL>(aHalfPlaneLeftTop.getX()), 148 static_cast<Gdiplus::REAL>(aHalfPlaneLeftTop.getY()), 149 static_cast<Gdiplus::REAL>(aHalfPlaneRightTop.getX()), 150 static_cast<Gdiplus::REAL>(aHalfPlaneRightTop.getY()) ); 151 aSolidFillPath.AddLine( static_cast<Gdiplus::REAL>(aHalfPlaneRightBottom.getX()), 152 static_cast<Gdiplus::REAL>(aHalfPlaneRightBottom.getY()), 153 static_cast<Gdiplus::REAL>(aHalfPlaneLeftBottom.getX()), 154 static_cast<Gdiplus::REAL>(aHalfPlaneLeftBottom.getY()) ); 155 aSolidFillPath.CloseFigure(); 156 157 // limit output to fill path, we've just generated a path that 158 // might be substantially larger 159 if( Gdiplus::Ok != rGraphics->SetClip( rFillPath.get(), 160 Gdiplus::CombineModeIntersect ) ) 161 { 162 return false; 163 } 164 165 Gdiplus::SolidBrush aBackgroundBrush2( rColors.back() ); 166 rGraphics->FillPath( &aBackgroundBrush2, &aSolidFillPath ); 167 168 // generate clip polygon from the extended parallelogram 169 // (exploit the feature that distinct lines in a figure are 170 // automatically closed by a straight line) 171 Gdiplus::GraphicsPath aClipPath; 172 aClipPath.AddLine( static_cast<Gdiplus::REAL>(aLeftTop.getX()), 173 static_cast<Gdiplus::REAL>(aLeftTop.getY()), 174 static_cast<Gdiplus::REAL>(aRightTop.getX()), 175 static_cast<Gdiplus::REAL>(aRightTop.getY()) ); 176 aClipPath.AddLine( static_cast<Gdiplus::REAL>(aRightBottom.getX()), 177 static_cast<Gdiplus::REAL>(aRightBottom.getY()), 178 static_cast<Gdiplus::REAL>(aLeftBottom.getX()), 179 static_cast<Gdiplus::REAL>(aLeftBottom.getY()) ); 180 aClipPath.CloseFigure(); 181 182 // limit output to a _single_ strip of the gradient (have to 183 // clip here, since GDI+ wrapmode clamp does not work here) 184 if( Gdiplus::Ok != rGraphics->SetClip( &aClipPath, 185 Gdiplus::CombineModeIntersect ) ) 186 { 187 return false; 188 } 189 190 // now, finally, output the gradient 191 Gdiplus::Matrix aMatrix; 192 tools::gdiPlusMatrixFromAffineMatrix2D( aMatrix, 193 texture.AffineTransform ); 194 aBrush.SetTransform( &aMatrix ); 195 196 rGraphics->FillRectangle( &aBrush, aBounds ); 197 198 return true; 199 } 200 201 int numColorSteps( const Gdiplus::Color& rColor1, const Gdiplus::Color& rColor2 ) 202 { 203 return ::std::max( 204 labs( rColor1.GetRed() - rColor2.GetRed() ), 205 ::std::max( 206 labs( rColor1.GetGreen() - rColor2.GetGreen() ), 207 labs( rColor1.GetBlue() - rColor2.GetBlue() ) ) ); 208 } 209 210 bool fillPolygonalGradient( const ::canvas::ParametricPolyPolygon::Values& rValues, 211 const std::vector< Gdiplus::Color >& rColors, 212 const std::vector< Gdiplus::REAL >& rStops, 213 GraphicsSharedPtr& rGraphics, 214 const GraphicsPathSharedPtr& rPath, 215 const rendering::ViewState& viewState, 216 const rendering::RenderState& renderState, 217 const rendering::Texture& texture ) 218 { 219 // copy original fill path object, might have to change it 220 // below 221 GraphicsPathSharedPtr pFillPath( rPath ); 222 const ::basegfx::B2DPolygon& rGradientPoly( rValues.maGradientPoly ); 223 224 PathGradientBrushSharedPtr pGradientBrush; 225 226 // fill background uniformly with end color 227 Gdiplus::SolidBrush aBackgroundBrush( rColors[0] ); 228 rGraphics->FillPath( &aBackgroundBrush, pFillPath.get() ); 229 230 Gdiplus::Matrix aMatrix; 231 // scale focus according to aspect ratio: for wider-than-tall 232 // bounds (nAspectRatio > 1.0), the focus must have non-zero 233 // width. Specifically, a bound rect twice as wide as tall has 234 // a focus of half it's width. 235 if( !::rtl::math::approxEqual(rValues.mnAspectRatio, 236 1.0) ) 237 { 238 // KLUDGE 1: 239 // 240 // And here comes the greatest shortcoming of the GDI+ 241 // gradients ever: SetFocusScales completely ignores 242 // transformations, both when set at the PathGradientBrush 243 // and for the world coordinate system. Thus, to correctly 244 // display anisotrophic path gradients, we have to render 245 // them by hand. WTF. 246 247 // TODO(F2): This does not yet support other repeat modes 248 // except clamp, and probably also no multi-texturing 249 250 // limit output to to-be-filled polygon 251 if( Gdiplus::Ok != rGraphics->SetClip( pFillPath.get(), 252 Gdiplus::CombineModeIntersect ) ) 253 { 254 return false; 255 } 256 257 // disable anti-aliasing, if any 258 const Gdiplus::SmoothingMode eOldAAMode( rGraphics->GetSmoothingMode() ); 259 rGraphics->SetSmoothingMode( Gdiplus::SmoothingModeHighSpeed ); 260 261 262 // determine number of steps to use 263 // -------------------------------- 264 265 // TODO(Q2): Unify step calculations with VCL canvas 266 int nColorSteps = 0; 267 for( size_t i=0; i<rColors.size()-1; ++i ) 268 nColorSteps += numColorSteps(rColors[i],rColors[i+1]); 269 ::basegfx::B2DHomMatrix aTotalTransform; 270 const int nStepCount= 271 ::canvas::tools::calcGradientStepCount(aTotalTransform, 272 viewState, 273 renderState, 274 texture, 275 nColorSteps); 276 277 ::basegfx::B2DHomMatrix aTextureTransform; 278 ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform, 279 texture.AffineTransform ); 280 // determine overall transformation for inner polygon (might 281 // have to be prefixed by anisotrophic scaling) 282 ::basegfx::B2DHomMatrix aInnerPolygonTransformMatrix; 283 284 // For performance reasons, we create a temporary VCL polygon 285 // here, keep it all the way and only change the vertex values 286 // in the loop below (as ::Polygon is a pimpl class, creating 287 // one every loop turn would really stress the mem allocator) 288 ::basegfx::B2DPolygon aOuterPoly( rGradientPoly ); 289 ::basegfx::B2DPolygon aInnerPoly; 290 291 // subdivide polygon _before_ rendering, would otherwise have 292 // to be performed on every loop turn. 293 if( aOuterPoly.areControlPointsUsed() ) 294 aOuterPoly = ::basegfx::tools::adaptiveSubdivideByAngle(aOuterPoly); 295 296 aInnerPoly = aOuterPoly; 297 aOuterPoly.transform(aTextureTransform); 298 299 300 // apply scaling (possibly anisotrophic) to inner polygon 301 // ------------------------------------------------------ 302 303 // scale inner polygon according to aspect ratio: for 304 // wider-than-tall bounds (nAspectRatio > 1.0), the inner 305 // polygon, representing the gradient focus, must have 306 // non-zero width. Specifically, a bound rect twice as wide as 307 // tall has a focus polygon of half it's width. 308 const double nAspectRatio( rValues.mnAspectRatio ); 309 if( nAspectRatio > 1.0 ) 310 { 311 // width > height case 312 aInnerPolygonTransformMatrix.scale( 1.0 - 1.0/nAspectRatio, 313 0.0 ); 314 } 315 else if( nAspectRatio < 1.0 ) 316 { 317 // width < height case 318 aInnerPolygonTransformMatrix.scale( 0.0, 319 1.0 - nAspectRatio ); 320 } 321 else 322 { 323 // isotrophic case 324 aInnerPolygonTransformMatrix.scale( 0.0, 0.0 ); 325 } 326 327 // and finally, add texture transform to it. 328 aInnerPolygonTransformMatrix *= aTextureTransform; 329 330 // apply final matrix to polygon 331 aInnerPoly.transform( aInnerPolygonTransformMatrix ); 332 333 Gdiplus::GraphicsPath aCurrPath; 334 Gdiplus::SolidBrush aFillBrush( rColors[0] ); 335 const sal_uInt32 nNumPoints( aOuterPoly.count() ); 336 basegfx::tools::KeyStopLerp aLerper(rValues.maStops); 337 for( int i=1; i<nStepCount; ++i ) 338 { 339 std::ptrdiff_t nIndex; 340 double fAlpha; 341 const double fT( i/double(nStepCount) ); 342 boost::tuples::tie(nIndex,fAlpha)=aLerper.lerp(fT); 343 344 const Gdiplus::Color aFillColor( 345 static_cast<BYTE>( basegfx::tools::lerp(rColors[nIndex].GetRed(),rColors[nIndex+1].GetRed(),fAlpha) ), 346 static_cast<BYTE>( basegfx::tools::lerp(rColors[nIndex].GetGreen(),rColors[nIndex+1].GetGreen(),fAlpha) ), 347 static_cast<BYTE>( basegfx::tools::lerp(rColors[nIndex].GetBlue(),rColors[nIndex+1].GetBlue(),fAlpha) ) ); 348 349 aFillBrush.SetColor( aFillColor ); 350 aCurrPath.Reset(); aCurrPath.StartFigure(); 351 for( unsigned int p=1; p<nNumPoints; ++p ) 352 { 353 const ::basegfx::B2DPoint& rOuterPoint1( aOuterPoly.getB2DPoint(p-1) ); 354 const ::basegfx::B2DPoint& rInnerPoint1( aInnerPoly.getB2DPoint(p-1) ); 355 const ::basegfx::B2DPoint& rOuterPoint2( aOuterPoly.getB2DPoint(p) ); 356 const ::basegfx::B2DPoint& rInnerPoint2( aInnerPoly.getB2DPoint(p) ); 357 358 aCurrPath.AddLine( 359 Gdiplus::REAL(fT*rInnerPoint1.getX() + (1-fT)*rOuterPoint1.getX()), 360 Gdiplus::REAL(fT*rInnerPoint1.getY() + (1-fT)*rOuterPoint1.getY()), 361 Gdiplus::REAL(fT*rInnerPoint2.getX() + (1-fT)*rOuterPoint2.getX()), 362 Gdiplus::REAL(fT*rInnerPoint2.getY() + (1-fT)*rOuterPoint2.getY())); 363 } 364 aCurrPath.CloseFigure(); 365 366 rGraphics->FillPath( &aFillBrush, &aCurrPath ); 367 } 368 369 // reset to old anti-alias mode 370 rGraphics->SetSmoothingMode( eOldAAMode ); 371 } 372 else 373 { 374 // KLUDGE 2: 375 // 376 // We're generating a PathGradientBrush from scratch here, 377 // and put in a transformed GraphicsPath (transformed with 378 // the texture transform). This is because the 379 // straight-forward approach to store a Brush pointer at 380 // this class and set a texture transform via 381 // PathGradientBrush::SetTransform() is spoiled by MS: it 382 // seems that _either_ the texture transform, _or_ the 383 // transform at the Graphics can be set, but not both. If 384 // one sets both, only the translational components of the 385 // texture is respected. 386 387 tools::gdiPlusMatrixFromAffineMatrix2D( aMatrix, 388 texture.AffineTransform ); 389 GraphicsPathSharedPtr pGradientPath( 390 tools::graphicsPathFromB2DPolygon( rValues.maGradientPoly )); 391 pGradientPath->Transform( &aMatrix ); 392 393 pGradientBrush.reset( 394 new Gdiplus::PathGradientBrush( pGradientPath.get() ) ); 395 pGradientBrush->SetInterpolationColors( &rColors[0], 396 &rStops[0], 397 rStops.size() ); 398 399 // explicitely setup center point. Since the center of GDI+ 400 // gradients are by default the _centroid_ of the path 401 // (i.e. the weighted sum of edge points), it will not 402 // necessarily coincide with our notion of center. 403 Gdiplus::PointF aCenterPoint(0, 0); 404 aMatrix.TransformPoints( &aCenterPoint ); 405 pGradientBrush->SetCenterPoint( aCenterPoint ); 406 407 const bool bTileX( texture.RepeatModeX != rendering::TexturingMode::CLAMP ); 408 const bool bTileY( texture.RepeatModeY != rendering::TexturingMode::CLAMP ); 409 410 if( bTileX && bTileY ) 411 pGradientBrush->SetWrapMode( Gdiplus::WrapModeTile ); 412 else 413 { 414 OSL_ENSURE( bTileY == bTileX, 415 "ParametricPolyPolygon::fillPolygonalGradient(): Cannot have repeat x and repeat y differ!" ); 416 417 pGradientBrush->SetWrapMode( Gdiplus::WrapModeClamp ); 418 } 419 420 // render actual gradient 421 rGraphics->FillPath( pGradientBrush.get(), pFillPath.get() ); 422 } 423 424 #if defined(VERBOSE) && defined(DBG_UTIL) 425 Gdiplus::Pen aPen( Gdiplus::Color( 255, 255, 0, 0 ), 426 0.0001f ); 427 428 rGraphics->DrawRectangle( &aPen, 429 Gdiplus::RectF( 0.0f, 0.0f, 430 1.0f, 1.0f ) ); 431 #endif 432 433 return true; 434 } 435 436 bool fillGradient( const ::canvas::ParametricPolyPolygon::Values& rValues, 437 const std::vector< Gdiplus::Color >& rColors, 438 const std::vector< Gdiplus::REAL >& rStops, 439 GraphicsSharedPtr& rGraphics, 440 const GraphicsPathSharedPtr& rPath, 441 const rendering::ViewState& viewState, 442 const rendering::RenderState& renderState, 443 const rendering::Texture& texture ) 444 { 445 switch( rValues.meType ) 446 { 447 case ::canvas::ParametricPolyPolygon::GRADIENT_LINEAR: 448 fillLinearGradient( rGraphics, 449 rValues, 450 rColors, 451 rStops, 452 rPath, 453 texture ); 454 break; 455 456 case ::canvas::ParametricPolyPolygon::GRADIENT_ELLIPTICAL: 457 // FALLTHROUGH intended 458 case ::canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR: 459 fillPolygonalGradient( rValues, 460 rColors, 461 rStops, 462 rGraphics, 463 rPath, 464 viewState, 465 renderState, 466 texture ); 467 break; 468 469 default: 470 ENSURE_OR_THROW( false, 471 "CanvasHelper::fillGradient(): Unexpected case" ); 472 } 473 474 return true; 475 } 476 477 void fillBitmap( const uno::Reference< rendering::XBitmap >& xBitmap, 478 GraphicsSharedPtr& rGraphics, 479 const GraphicsPathSharedPtr& rPath, 480 const rendering::Texture& rTexture ) 481 { 482 OSL_ENSURE( rTexture.RepeatModeX == 483 rTexture.RepeatModeY, 484 "CanvasHelper::fillBitmap(): GDI+ cannot handle differing X/Y repeat mode." ); 485 486 const bool bClamp( rTexture.RepeatModeX == rendering::TexturingMode::NONE && 487 rTexture.RepeatModeY == rendering::TexturingMode::NONE ); 488 489 const geometry::IntegerSize2D aBmpSize( xBitmap->getSize() ); 490 ENSURE_ARG_OR_THROW( aBmpSize.Width != 0 && 491 aBmpSize.Height != 0, 492 "CanvasHelper::fillBitmap(): zero-sized texture bitmap" ); 493 494 // TODO(P3): Detect case that path is rectangle and 495 // bitmap is just scaled into that. Then, we can 496 // render directly, without generating a temporary 497 // GDI+ bitmap (this is significant, because drawing 498 // layer presents background object bitmap in that 499 // way!) 500 BitmapSharedPtr pBitmap( 501 tools::bitmapFromXBitmap( xBitmap ) ); 502 503 TextureBrushSharedPtr pBrush; 504 if( ::rtl::math::approxEqual( rTexture.Alpha, 505 1.0 ) ) 506 { 507 pBrush.reset( 508 new Gdiplus::TextureBrush( 509 pBitmap.get(), 510 bClamp ? Gdiplus::WrapModeClamp : Gdiplus::WrapModeTile ) ); 511 } 512 else 513 { 514 Gdiplus::ImageAttributes aImgAttr; 515 516 tools::setModulateImageAttributes( aImgAttr, 517 1.0, 518 1.0, 519 1.0, 520 rTexture.Alpha ); 521 522 Gdiplus::Rect aRect(0,0, 523 aBmpSize.Width, 524 aBmpSize.Height); 525 pBrush.reset( 526 new Gdiplus::TextureBrush( 527 pBitmap.get(), 528 aRect, 529 &aImgAttr ) ); 530 531 pBrush->SetWrapMode( 532 bClamp ? Gdiplus::WrapModeClamp : Gdiplus::WrapModeTile ); 533 } 534 535 Gdiplus::Matrix aTextureTransform; 536 tools::gdiPlusMatrixFromAffineMatrix2D( aTextureTransform, 537 rTexture.AffineTransform ); 538 539 // scale down bitmap to [0,1]x[0,1] rect, as required 540 // from the XCanvas interface. 541 pBrush->MultiplyTransform( &aTextureTransform ); 542 pBrush->ScaleTransform( static_cast< Gdiplus::REAL >(1.0/aBmpSize.Width), 543 static_cast< Gdiplus::REAL >(1.0/aBmpSize.Height) ); 544 545 // TODO(F1): FillRule 546 ENSURE_OR_THROW( 547 Gdiplus::Ok == rGraphics->FillPath( pBrush.get(), 548 rPath.get() ), 549 "CanvasHelper::fillTexturedPolyPolygon(): GDI+ call failed" ); 550 } 551 } 552 553 // ------------------------------------------------------------- 554 555 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTexturedPolyPolygon( const rendering::XCanvas* /*pCanvas*/, 556 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, 557 const rendering::ViewState& viewState, 558 const rendering::RenderState& renderState, 559 const uno::Sequence< rendering::Texture >& textures ) 560 { 561 ENSURE_OR_THROW( xPolyPolygon.is(), 562 "CanvasHelper::fillTexturedPolyPolygon: polygon is NULL"); 563 ENSURE_OR_THROW( textures.getLength(), 564 "CanvasHelper::fillTexturedPolyPolygon: empty texture sequence"); 565 566 if( needOutput() ) 567 { 568 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() ); 569 570 setupGraphicsState( pGraphics, viewState, renderState ); 571 572 // TODO(F1): Multi-texturing 573 if( textures[0].Gradient.is() ) 574 { 575 // try to cast XParametricPolyPolygon2D reference to 576 // our implementation class. 577 ::canvas::ParametricPolyPolygon* pGradient = 578 dynamic_cast< ::canvas::ParametricPolyPolygon* >( textures[0].Gradient.get() ); 579 580 if( pGradient ) 581 { 582 const ::canvas::ParametricPolyPolygon::Values& rValues( 583 pGradient->getValues() ); 584 585 OSL_ASSERT(rValues.maColors.getLength() == rValues.maStops.getLength() 586 && rValues.maColors.getLength() > 1); 587 588 std::vector< Gdiplus::Color > aColors(rValues.maColors.getLength()); 589 std::transform(&rValues.maColors[0], 590 &rValues.maColors[0]+rValues.maColors.getLength(), 591 aColors.begin(), 592 boost::bind( 593 (Gdiplus::ARGB (*)( const uno::Sequence< double >& ))( 594 &tools::sequenceToArgb), 595 _1)); 596 std::vector< Gdiplus::REAL > aStops; 597 comphelper::sequenceToContainer(aStops,rValues.maStops); 598 599 // TODO(E1): Return value 600 // TODO(F1): FillRule 601 fillGradient( rValues, 602 aColors, 603 aStops, 604 pGraphics, 605 tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ), 606 viewState, 607 renderState, 608 textures[0] ); 609 } 610 } 611 else if( textures[0].Bitmap.is() ) 612 { 613 // TODO(E1): Return value 614 // TODO(F1): FillRule 615 fillBitmap( textures[0].Bitmap, 616 pGraphics, 617 tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ), 618 textures[0] ); 619 } 620 } 621 622 // TODO(P1): Provide caching here. 623 return uno::Reference< rendering::XCachedPrimitive >(NULL); 624 } 625 } 626