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