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 34 #include <rtl/logfile.hxx> 35 #include <rtl/math.hxx> 36 37 #include <com/sun/star/rendering/TexturingMode.hpp> 38 #include <com/sun/star/rendering/CompositeOperation.hpp> 39 #include <com/sun/star/rendering/RepaintResult.hpp> 40 #include <com/sun/star/rendering/PathCapType.hpp> 41 #include <com/sun/star/rendering/PathJoinType.hpp> 42 43 #include <basegfx/matrix/b2dhommatrix.hxx> 44 #include <basegfx/point/b2dpoint.hxx> 45 #include <basegfx/tools/canvastools.hxx> 46 #include <basegfx/matrix/b2dhommatrixtools.hxx> 47 48 #include <comphelper/sequence.hxx> 49 #include <canvas/canvastools.hxx> 50 51 #include "dx_spritecanvas.hxx" 52 #include "dx_impltools.hxx" 53 #include "dx_vcltools.hxx" 54 #include "dx_canvasfont.hxx" 55 #include "dx_textlayout.hxx" 56 #include "dx_canvashelper.hxx" 57 58 #include <algorithm> 59 60 61 using namespace ::com::sun::star; 62 63 namespace dxcanvas 64 { 65 namespace 66 { 67 Gdiplus::LineCap gdiCapFromCap( sal_Int8 nCapType ) 68 { 69 switch( nCapType ) 70 { 71 case rendering::PathCapType::BUTT: 72 return Gdiplus::LineCapFlat; 73 74 case rendering::PathCapType::ROUND: 75 return Gdiplus::LineCapRound; 76 77 case rendering::PathCapType::SQUARE: 78 return Gdiplus::LineCapSquare; 79 80 default: 81 ENSURE_OR_THROW( false, 82 "gdiCapFromCap(): Unexpected cap type" ); 83 } 84 85 return Gdiplus::LineCapFlat; 86 } 87 88 Gdiplus::LineJoin gdiJoinFromJoin( sal_Int8 nJoinType ) 89 { 90 switch( nJoinType ) 91 { 92 case rendering::PathJoinType::NONE: 93 OSL_ENSURE( false, 94 "gdiJoinFromJoin(): Join NONE not possible, mapping to MITER" ); 95 // FALLTHROUGH intended 96 case rendering::PathJoinType::MITER: 97 return Gdiplus::LineJoinMiter; 98 99 case rendering::PathJoinType::ROUND: 100 return Gdiplus::LineJoinRound; 101 102 case rendering::PathJoinType::BEVEL: 103 return Gdiplus::LineJoinBevel; 104 105 default: 106 ENSURE_OR_THROW( false, 107 "gdiJoinFromJoin(): Unexpected join type" ); 108 } 109 110 return Gdiplus::LineJoinMiter; 111 } 112 } 113 114 CanvasHelper::CanvasHelper() : 115 mpGdiPlusUser( GDIPlusUser::createInstance() ), 116 mpDevice( NULL ), 117 mpGraphicsProvider(), 118 maOutputOffset() 119 { 120 } 121 122 void CanvasHelper::disposing() 123 { 124 mpGraphicsProvider.reset(); 125 mpDevice = NULL; 126 mpGdiPlusUser.reset(); 127 } 128 129 void CanvasHelper::setDevice( rendering::XGraphicDevice& rDevice ) 130 { 131 mpDevice = &rDevice; 132 } 133 134 void CanvasHelper::setTarget( const GraphicsProviderSharedPtr& rTarget ) 135 { 136 ENSURE_OR_THROW( rTarget, 137 "CanvasHelper::setTarget(): Invalid target" ); 138 ENSURE_OR_THROW( !mpGraphicsProvider.get(), 139 "CanvasHelper::setTarget(): target set, old target would be overwritten" ); 140 141 mpGraphicsProvider = rTarget; 142 } 143 144 void CanvasHelper::setTarget( const GraphicsProviderSharedPtr& rTarget, 145 const ::basegfx::B2ISize& rOutputOffset ) 146 { 147 ENSURE_OR_THROW( rTarget, 148 "CanvasHelper::setTarget(): invalid target" ); 149 ENSURE_OR_THROW( !mpGraphicsProvider.get(), 150 "CanvasHelper::setTarget(): target set, old target would be overwritten" ); 151 152 mpGraphicsProvider = rTarget; 153 maOutputOffset = rOutputOffset; 154 } 155 156 void CanvasHelper::clear() 157 { 158 if( needOutput() ) 159 { 160 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() ); 161 Gdiplus::Color aClearColor = Gdiplus::Color((Gdiplus::ARGB)Gdiplus::Color::White); 162 163 ENSURE_OR_THROW( 164 Gdiplus::Ok == pGraphics->SetCompositingMode( 165 Gdiplus::CompositingModeSourceCopy ), // force set, don't blend 166 "CanvasHelper::clear(): GDI+ SetCompositingMode call failed" ); 167 ENSURE_OR_THROW( 168 Gdiplus::Ok == pGraphics->Clear( aClearColor ), 169 "CanvasHelper::clear(): GDI+ Clear call failed" ); 170 } 171 } 172 173 void CanvasHelper::drawPoint( const rendering::XCanvas* /*pCanvas*/, 174 const geometry::RealPoint2D& aPoint, 175 const rendering::ViewState& viewState, 176 const rendering::RenderState& renderState ) 177 { 178 if( needOutput() ) 179 { 180 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() ); 181 182 setupGraphicsState( pGraphics, viewState, renderState ); 183 184 Gdiplus::SolidBrush aBrush( 185 Gdiplus::Color( 186 tools::sequenceToArgb(renderState.DeviceColor)) ); 187 188 // determine size of one-by-one device pixel ellipse 189 Gdiplus::Matrix aMatrix; 190 pGraphics->GetTransform(&aMatrix); 191 aMatrix.Invert(); 192 Gdiplus::PointF vector(1, 1); 193 aMatrix.TransformVectors(&vector); 194 195 // paint a one-by-one circle, with the given point 196 // in the middle (rounded to float) 197 ENSURE_OR_THROW( 198 Gdiplus::Ok == pGraphics->FillEllipse( &aBrush, 199 // disambiguate call 200 Gdiplus::REAL(aPoint.X), 201 Gdiplus::REAL(aPoint.Y), 202 Gdiplus::REAL(vector.X), 203 Gdiplus::REAL(vector.Y) ), 204 "CanvasHelper::drawPoint(): GDI+ call failed" ); 205 } 206 } 207 208 void CanvasHelper::drawLine( const rendering::XCanvas* /*pCanvas*/, 209 const geometry::RealPoint2D& aStartPoint, 210 const geometry::RealPoint2D& aEndPoint, 211 const rendering::ViewState& viewState, 212 const rendering::RenderState& renderState ) 213 { 214 if( needOutput() ) 215 { 216 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() ); 217 218 setupGraphicsState( pGraphics, viewState, renderState ); 219 220 Gdiplus::Pen aPen( 221 Gdiplus::Color( 222 tools::sequenceToArgb(renderState.DeviceColor)), 223 Gdiplus::REAL(0.0) ); 224 225 // #122683# Switched precedence of pixel offset 226 // mode. Seemingly, polygon stroking needs 227 // PixelOffsetModeNone to achieve visually pleasing 228 // results, whereas all other operations (e.g. polygon 229 // fills, bitmaps) look better with PixelOffsetModeHalf. 230 const Gdiplus::PixelOffsetMode aOldMode( 231 pGraphics->GetPixelOffsetMode() ); 232 pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone ); 233 234 Gdiplus::Status hr = pGraphics->DrawLine( &aPen, 235 Gdiplus::REAL(aStartPoint.X), // disambiguate call 236 Gdiplus::REAL(aStartPoint.Y), 237 Gdiplus::REAL(aEndPoint.X), 238 Gdiplus::REAL(aEndPoint.Y) ); 239 pGraphics->SetPixelOffsetMode( aOldMode ); 240 241 ENSURE_OR_THROW( 242 Gdiplus::Ok == hr, 243 "CanvasHelper::drawLine(): GDI+ call failed" ); 244 } 245 } 246 247 void CanvasHelper::drawBezier( const rendering::XCanvas* /*pCanvas*/, 248 const geometry::RealBezierSegment2D& aBezierSegment, 249 const geometry::RealPoint2D& aEndPoint, 250 const rendering::ViewState& viewState, 251 const rendering::RenderState& renderState ) 252 { 253 if( needOutput() ) 254 { 255 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() ); 256 257 setupGraphicsState( pGraphics, viewState, renderState ); 258 259 Gdiplus::Pen aPen( 260 Gdiplus::Color( 261 tools::sequenceToArgb(renderState.DeviceColor)), 262 Gdiplus::REAL(0.0) ); 263 264 // #122683# Switched precedence of pixel offset 265 // mode. Seemingly, polygon stroking needs 266 // PixelOffsetModeNone to achieve visually pleasing 267 // results, whereas all other operations (e.g. polygon 268 // fills, bitmaps) look better with PixelOffsetModeHalf. 269 const Gdiplus::PixelOffsetMode aOldMode( 270 pGraphics->GetPixelOffsetMode() ); 271 pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone ); 272 273 Gdiplus::Status hr = pGraphics->DrawBezier( &aPen, 274 Gdiplus::REAL(aBezierSegment.Px), // disambiguate call 275 Gdiplus::REAL(aBezierSegment.Py), 276 Gdiplus::REAL(aBezierSegment.C1x), 277 Gdiplus::REAL(aBezierSegment.C1y), 278 Gdiplus::REAL(aEndPoint.X), 279 Gdiplus::REAL(aEndPoint.Y), 280 Gdiplus::REAL(aBezierSegment.C2x), 281 Gdiplus::REAL(aBezierSegment.C2y) ); 282 283 pGraphics->SetPixelOffsetMode( aOldMode ); 284 285 ENSURE_OR_THROW( 286 Gdiplus::Ok == hr, 287 "CanvasHelper::drawBezier(): GDI+ call failed" ); 288 } 289 } 290 291 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* /*pCanvas*/, 292 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, 293 const rendering::ViewState& viewState, 294 const rendering::RenderState& renderState ) 295 { 296 ENSURE_OR_THROW( xPolyPolygon.is(), 297 "CanvasHelper::drawPolyPolygon: polygon is NULL"); 298 299 if( needOutput() ) 300 { 301 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() ); 302 303 setupGraphicsState( pGraphics, viewState, renderState ); 304 305 Gdiplus::Pen aPen( 306 Gdiplus::Color( 307 tools::sequenceToArgb(renderState.DeviceColor)), 308 Gdiplus::REAL(0.0) ); 309 310 // #122683# Switched precedence of pixel offset 311 // mode. Seemingly, polygon stroking needs 312 // PixelOffsetModeNone to achieve visually pleasing 313 // results, whereas all other operations (e.g. polygon 314 // fills, bitmaps) look better with PixelOffsetModeHalf. 315 const Gdiplus::PixelOffsetMode aOldMode( 316 pGraphics->GetPixelOffsetMode() ); 317 pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone ); 318 319 GraphicsPathSharedPtr pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ) ); 320 321 // TODO(E1): Return value 322 Gdiplus::Status hr = pGraphics->DrawPath( &aPen, pPath.get() ); 323 324 pGraphics->SetPixelOffsetMode( aOldMode ); 325 326 ENSURE_OR_THROW( 327 Gdiplus::Ok == hr, 328 "CanvasHelper::drawPolyPolygon(): GDI+ call failed" ); 329 } 330 331 // TODO(P1): Provide caching here. 332 return uno::Reference< rendering::XCachedPrimitive >(NULL); 333 } 334 335 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas* /*pCanvas*/, 336 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, 337 const rendering::ViewState& viewState, 338 const rendering::RenderState& renderState, 339 const rendering::StrokeAttributes& strokeAttributes ) 340 { 341 ENSURE_OR_THROW( xPolyPolygon.is(), 342 "CanvasHelper::drawPolyPolygon: polygon is NULL"); 343 344 if( needOutput() ) 345 { 346 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() ); 347 348 setupGraphicsState( pGraphics, viewState, renderState ); 349 350 351 // Setup stroke pen 352 // ---------------- 353 354 Gdiplus::Pen aPen( 355 Gdiplus::Color( 356 tools::sequenceToArgb(renderState.DeviceColor)), 357 static_cast< Gdiplus::REAL >(strokeAttributes.StrokeWidth) ); 358 359 // #122683# Switched precedence of pixel offset 360 // mode. Seemingly, polygon stroking needs 361 // PixelOffsetModeNone to achieve visually pleasing 362 // results, whereas all other operations (e.g. polygon 363 // fills, bitmaps) look better with PixelOffsetModeHalf. 364 const Gdiplus::PixelOffsetMode aOldMode( 365 pGraphics->GetPixelOffsetMode() ); 366 pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone ); 367 368 const bool bIsMiter(rendering::PathJoinType::MITER == strokeAttributes.JoinType); 369 const bool bIsNone(rendering::PathJoinType::NONE == strokeAttributes.JoinType); 370 371 if(bIsMiter) 372 aPen.SetMiterLimit( static_cast< Gdiplus::REAL >(strokeAttributes.MiterLimit) ); 373 374 const ::std::vector< Gdiplus::REAL >& rDashArray( 375 ::comphelper::sequenceToContainer< ::std::vector< Gdiplus::REAL > >( 376 strokeAttributes.DashArray ) ); 377 if( !rDashArray.empty() ) 378 { 379 aPen.SetDashPattern( &rDashArray[0], 380 rDashArray.size() ); 381 } 382 aPen.SetLineCap( gdiCapFromCap(strokeAttributes.StartCapType), 383 gdiCapFromCap(strokeAttributes.EndCapType), 384 Gdiplus::DashCapFlat ); 385 if(!bIsNone) 386 aPen.SetLineJoin( gdiJoinFromJoin(strokeAttributes.JoinType) ); 387 388 GraphicsPathSharedPtr pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon, bIsNone ) ); 389 390 // TODO(E1): Return value 391 Gdiplus::Status hr = pGraphics->DrawPath( &aPen, pPath.get() ); 392 393 pGraphics->SetPixelOffsetMode( aOldMode ); 394 395 ENSURE_OR_THROW( 396 Gdiplus::Ok == hr, 397 "CanvasHelper::strokePolyPolygon(): GDI+ call failed" ); 398 } 399 400 // TODO(P1): Provide caching here. 401 return uno::Reference< rendering::XCachedPrimitive >(NULL); 402 } 403 404 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas* /*pCanvas*/, 405 const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/, 406 const rendering::ViewState& /*viewState*/, 407 const rendering::RenderState& /*renderState*/, 408 const uno::Sequence< rendering::Texture >& /*textures*/, 409 const rendering::StrokeAttributes& /*strokeAttributes*/ ) 410 { 411 // TODO 412 return uno::Reference< rendering::XCachedPrimitive >(NULL); 413 } 414 415 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas* /*pCanvas*/, 416 const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/, 417 const rendering::ViewState& /*viewState*/, 418 const rendering::RenderState& /*renderState*/, 419 const uno::Sequence< rendering::Texture >& /*textures*/, 420 const uno::Reference< geometry::XMapping2D >& /*xMapping*/, 421 const rendering::StrokeAttributes& /*strokeAttributes*/ ) 422 { 423 // TODO 424 return uno::Reference< rendering::XCachedPrimitive >(NULL); 425 } 426 427 uno::Reference< rendering::XPolyPolygon2D > CanvasHelper::queryStrokeShapes( const rendering::XCanvas* /*pCanvas*/, 428 const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/, 429 const rendering::ViewState& /*viewState*/, 430 const rendering::RenderState& /*renderState*/, 431 const rendering::StrokeAttributes& /*strokeAttributes*/ ) 432 { 433 // TODO 434 return uno::Reference< rendering::XPolyPolygon2D >(NULL); 435 } 436 437 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas* /*pCanvas*/, 438 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, 439 const rendering::ViewState& viewState, 440 const rendering::RenderState& renderState ) 441 { 442 ENSURE_OR_THROW( xPolyPolygon.is(), 443 "CanvasHelper::fillPolyPolygon: polygon is NULL"); 444 445 if( needOutput() ) 446 { 447 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() ); 448 449 setupGraphicsState( pGraphics, viewState, renderState ); 450 451 Gdiplus::SolidBrush aBrush( 452 tools::sequenceToArgb(renderState.DeviceColor)); 453 454 GraphicsPathSharedPtr pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ) ); 455 456 // TODO(F1): FillRule 457 ENSURE_OR_THROW( Gdiplus::Ok == pGraphics->FillPath( &aBrush, pPath.get() ), 458 "CanvasHelper::fillPolyPolygon(): GDI+ call failed " ); 459 } 460 461 // TODO(P1): Provide caching here. 462 return uno::Reference< rendering::XCachedPrimitive >(NULL); 463 } 464 465 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* /*pCanvas*/, 466 const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/, 467 const rendering::ViewState& /*viewState*/, 468 const rendering::RenderState& /*renderState*/, 469 const uno::Sequence< rendering::Texture >& /*textures*/, 470 const uno::Reference< geometry::XMapping2D >& /*xMapping*/ ) 471 { 472 // TODO 473 return uno::Reference< rendering::XCachedPrimitive >(NULL); 474 } 475 476 uno::Reference< rendering::XCanvasFont > CanvasHelper::createFont( const rendering::XCanvas* /*pCanvas*/, 477 const rendering::FontRequest& fontRequest, 478 const uno::Sequence< beans::PropertyValue >& extraFontProperties, 479 const geometry::Matrix2D& fontMatrix ) 480 { 481 if( needOutput() ) 482 { 483 return uno::Reference< rendering::XCanvasFont >( 484 new CanvasFont(fontRequest, extraFontProperties, fontMatrix ) ); 485 } 486 487 return uno::Reference< rendering::XCanvasFont >(); 488 } 489 490 uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* /*pCanvas*/, 491 const rendering::FontInfo& /*aFilter*/, 492 const uno::Sequence< beans::PropertyValue >& /*aFontProperties*/ ) 493 { 494 // TODO 495 return uno::Sequence< rendering::FontInfo >(); 496 } 497 498 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas* /*pCanvas*/, 499 const rendering::StringContext& text, 500 const uno::Reference< rendering::XCanvasFont >& xFont, 501 const rendering::ViewState& viewState, 502 const rendering::RenderState& renderState, 503 sal_Int8 /*textDirection*/ ) 504 { 505 ENSURE_OR_THROW( xFont.is(), 506 "CanvasHelper::drawText: font is NULL"); 507 508 if( needOutput() ) 509 { 510 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() ); 511 512 setupGraphicsState( pGraphics, viewState, renderState ); 513 514 Gdiplus::SolidBrush aBrush( 515 Gdiplus::Color( 516 tools::sequenceToArgb(renderState.DeviceColor))); 517 518 CanvasFont::ImplRef pFont( 519 tools::canvasFontFromXFont(xFont) ); 520 521 // Move glyphs up, such that output happens at the font 522 // baseline. 523 Gdiplus::PointF aPoint( 0.0, 524 static_cast<Gdiplus::REAL>(-(pFont->getFont()->GetSize()* 525 pFont->getCellAscent() / 526 pFont->getEmHeight())) ); 527 528 // TODO(F1): According to 529 // http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q307208, 530 // we might have to revert to GDI and ExTextOut here, 531 // since GDI+ takes the scalability a little bit too 532 // far... 533 534 // TODO(F2): Proper layout (BiDi, CTL)! IMHO must use 535 // DrawDriverString here, and perform layouting myself... 536 ENSURE_OR_THROW( 537 Gdiplus::Ok == pGraphics->DrawString( reinterpret_cast<LPCWSTR>( 538 text.Text.copy( text.StartPosition, 539 text.Length ).getStr()), 540 text.Length, 541 pFont->getFont().get(), 542 aPoint, 543 &aBrush ), 544 "CanvasHelper::drawText(): GDI+ call failed" ); 545 } 546 547 return uno::Reference< rendering::XCachedPrimitive >(NULL); 548 } 549 550 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* /*pCanvas*/, 551 const uno::Reference< rendering::XTextLayout >& xLayoutetText, 552 const rendering::ViewState& viewState, 553 const rendering::RenderState& renderState ) 554 { 555 ENSURE_OR_THROW( xLayoutetText.is(), 556 "CanvasHelper::drawTextLayout: layout is NULL"); 557 558 if( needOutput() ) 559 { 560 TextLayout* pTextLayout = 561 dynamic_cast< TextLayout* >( xLayoutetText.get() ); 562 563 ENSURE_OR_THROW( pTextLayout, 564 "CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" ); 565 566 pTextLayout->draw( mpGraphicsProvider->getGraphics(), 567 viewState, 568 renderState, 569 maOutputOffset, 570 mpDevice, 571 false ); 572 } 573 574 return uno::Reference< rendering::XCachedPrimitive >(NULL); 575 } 576 577 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* /*pCanvas*/, 578 const uno::Reference< rendering::XBitmap >& xBitmap, 579 const rendering::ViewState& viewState, 580 const rendering::RenderState& renderState ) 581 { 582 ENSURE_OR_THROW( xBitmap.is(), 583 "CanvasHelper::drawBitmap: bitmap is NULL"); 584 585 if( needOutput() ) 586 { 587 // check whether one of our own objects - need to retrieve 588 // bitmap _before_ calling 589 // GraphicsProvider::getGraphics(), to avoid locking our 590 // own surface. 591 BitmapSharedPtr pGdiBitmap; 592 BitmapProvider* pBitmap = dynamic_cast< BitmapProvider* >(xBitmap.get()); 593 if( pBitmap ) 594 { 595 IBitmapSharedPtr pDXBitmap( pBitmap->getBitmap() ); 596 if( pDXBitmap ) 597 pGdiBitmap = pDXBitmap->getBitmap(); 598 } 599 600 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() ); 601 setupGraphicsState( pGraphics, viewState, renderState ); 602 603 if( pGdiBitmap ) 604 tools::drawGdiPlusBitmap(pGraphics,pGdiBitmap); 605 else 606 tools::drawVCLBitmapFromXBitmap(pGraphics, 607 xBitmap); 608 } 609 610 // TODO(P1): Provide caching here. 611 return uno::Reference< rendering::XCachedPrimitive >(NULL); 612 } 613 614 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas, 615 const uno::Reference< rendering::XBitmap >& xBitmap, 616 const rendering::ViewState& viewState, 617 const rendering::RenderState& renderState ) 618 { 619 ENSURE_OR_THROW( xBitmap.is(), 620 "CanvasHelper::drawBitmap: bitmap is NULL"); 621 622 // no color set -> this is equivalent to a plain drawBitmap(), then 623 if( renderState.DeviceColor.getLength() < 3 ) 624 return drawBitmap( pCanvas, xBitmap, viewState, renderState ); 625 626 if( needOutput() ) 627 { 628 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() ); 629 630 setupGraphicsState( pGraphics, viewState, renderState ); 631 632 BitmapSharedPtr pBitmap( tools::bitmapFromXBitmap( xBitmap ) ); 633 Gdiplus::Rect aRect( 0, 0, 634 pBitmap->GetWidth(), 635 pBitmap->GetHeight() ); 636 637 // Setup an ImageAttributes with an alpha-modulating 638 // color matrix. 639 const rendering::ARGBColor& rARGBColor( 640 mpDevice->getDeviceColorSpace()->convertToARGB(renderState.DeviceColor)[0]); 641 642 Gdiplus::ImageAttributes aImgAttr; 643 tools::setModulateImageAttributes( aImgAttr, 644 rARGBColor.Red, 645 rARGBColor.Green, 646 rARGBColor.Blue, 647 rARGBColor.Alpha ); 648 649 ENSURE_OR_THROW( 650 Gdiplus::Ok == pGraphics->DrawImage( pBitmap.get(), 651 aRect, 652 0, 0, 653 pBitmap->GetWidth(), 654 pBitmap->GetHeight(), 655 Gdiplus::UnitPixel, 656 &aImgAttr, 657 NULL, 658 NULL ), 659 "CanvasHelper::drawBitmapModulated(): GDI+ call failed" ); 660 } 661 662 // TODO(P1): Provide caching here. 663 return uno::Reference< rendering::XCachedPrimitive >(NULL); 664 } 665 666 uno::Reference< rendering::XGraphicDevice > CanvasHelper::getDevice() 667 { 668 return uno::Reference< rendering::XGraphicDevice >(mpDevice); 669 } 670 671 // private helper 672 // -------------------------------------------------- 673 674 Gdiplus::CompositingMode CanvasHelper::calcCompositingMode( sal_Int8 nMode ) 675 { 676 Gdiplus::CompositingMode aRet( Gdiplus::CompositingModeSourceOver ); 677 678 switch( nMode ) 679 { 680 case rendering::CompositeOperation::OVER: 681 // FALLTHROUGH intended 682 case rendering::CompositeOperation::CLEAR: 683 aRet = Gdiplus::CompositingModeSourceOver; 684 break; 685 686 case rendering::CompositeOperation::SOURCE: 687 aRet = Gdiplus::CompositingModeSourceCopy; 688 break; 689 690 case rendering::CompositeOperation::DESTINATION: 691 // FALLTHROUGH intended 692 case rendering::CompositeOperation::UNDER: 693 // FALLTHROUGH intended 694 case rendering::CompositeOperation::INSIDE: 695 // FALLTHROUGH intended 696 case rendering::CompositeOperation::INSIDE_REVERSE: 697 // FALLTHROUGH intended 698 case rendering::CompositeOperation::OUTSIDE: 699 // FALLTHROUGH intended 700 case rendering::CompositeOperation::OUTSIDE_REVERSE: 701 // FALLTHROUGH intended 702 case rendering::CompositeOperation::ATOP: 703 // FALLTHROUGH intended 704 case rendering::CompositeOperation::ATOP_REVERSE: 705 // FALLTHROUGH intended 706 case rendering::CompositeOperation::XOR: 707 // FALLTHROUGH intended 708 case rendering::CompositeOperation::ADD: 709 // FALLTHROUGH intended 710 case rendering::CompositeOperation::SATURATE: 711 // TODO(F2): Problem, because GDI+ only knows about two compositing modes 712 aRet = Gdiplus::CompositingModeSourceOver; 713 break; 714 715 default: 716 ENSURE_OR_THROW( false, "CanvasHelper::calcCompositingMode: unexpected mode" ); 717 break; 718 } 719 720 return aRet; 721 } 722 723 void CanvasHelper::setupGraphicsState( GraphicsSharedPtr& rGraphics, 724 const rendering::ViewState& viewState, 725 const rendering::RenderState& renderState ) 726 { 727 ENSURE_OR_THROW( needOutput(), 728 "CanvasHelper::setupGraphicsState: primary graphics invalid" ); 729 ENSURE_OR_THROW( mpDevice, 730 "CanvasHelper::setupGraphicsState: reference device invalid" ); 731 732 // setup view transform first. Clipping e.g. depends on it 733 ::basegfx::B2DHomMatrix aTransform; 734 ::canvas::tools::getViewStateTransform(aTransform, viewState); 735 736 // add output offset 737 if( !maOutputOffset.equalZero() ) 738 { 739 const basegfx::B2DHomMatrix aOutputOffset(basegfx::tools::createTranslateB2DHomMatrix( 740 maOutputOffset.getX(), maOutputOffset.getY())); 741 aTransform = aOutputOffset * aTransform; 742 } 743 744 Gdiplus::Matrix aMatrix; 745 tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix, aTransform ); 746 747 ENSURE_OR_THROW( 748 Gdiplus::Ok == rGraphics->SetTransform( &aMatrix ), 749 "CanvasHelper::setupGraphicsState(): Failed to set GDI+ transformation" ); 750 751 // setup view and render state clipping 752 ENSURE_OR_THROW( 753 Gdiplus::Ok == rGraphics->ResetClip(), 754 "CanvasHelper::setupGraphicsState(): Failed to reset GDI+ clip" ); 755 756 if( viewState.Clip.is() ) 757 { 758 GraphicsPathSharedPtr aClipPath( tools::graphicsPathFromXPolyPolygon2D( viewState.Clip ) ); 759 760 // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+. 761 // Try SetClip( Rect ) or similar for simple clip paths (need some support in 762 // LinePolyPolygon, then) 763 ENSURE_OR_THROW( 764 Gdiplus::Ok == rGraphics->SetClip( aClipPath.get(), 765 Gdiplus::CombineModeIntersect ), 766 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" ); 767 } 768 769 // setup overall transform only now. View clip above was relative to 770 // view transform 771 ::canvas::tools::mergeViewAndRenderTransform(aTransform, 772 viewState, 773 renderState); 774 775 // add output offset 776 if( !maOutputOffset.equalZero() ) 777 { 778 const basegfx::B2DHomMatrix aOutputOffset(basegfx::tools::createTranslateB2DHomMatrix( 779 maOutputOffset.getX(), maOutputOffset.getY())); 780 aTransform = aOutputOffset * aTransform; 781 } 782 783 tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix, aTransform ); 784 785 ENSURE_OR_THROW( 786 Gdiplus::Ok == rGraphics->SetTransform( &aMatrix ), 787 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ transformation" ); 788 789 if( renderState.Clip.is() ) 790 { 791 GraphicsPathSharedPtr aClipPath( tools::graphicsPathFromXPolyPolygon2D( renderState.Clip ) ); 792 793 // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+. 794 // Try SetClip( Rect ) or similar for simple clip paths (need some support in 795 // LinePolyPolygon, then) 796 ENSURE_OR_THROW( 797 Gdiplus::Ok == rGraphics->SetClip( aClipPath.get(), 798 Gdiplus::CombineModeIntersect ), 799 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" ); 800 } 801 802 // setup compositing 803 const Gdiplus::CompositingMode eCompositing( calcCompositingMode( renderState.CompositeOperation ) ); 804 ENSURE_OR_THROW( 805 Gdiplus::Ok == rGraphics->SetCompositingMode( eCompositing ), 806 "CanvasHelper::setupGraphicsState(): Cannot set GDI* compositing mode)" ); 807 } 808 809 void CanvasHelper::flush() const 810 { 811 if( needOutput() ) 812 mpGraphicsProvider->getGraphics()->Flush( Gdiplus::FlushIntentionSync ); 813 } 814 } 815