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_cppcanvas.hxx" 30 31 #include <tools/gen.hxx> 32 33 #include <canvas/debug.hxx> 34 #include <canvas/verbosetrace.hxx> 35 #include <canvas/canvastools.hxx> 36 37 #include <rtl/logfile.hxx> 38 39 #include <com/sun/star/rendering/XBitmap.hpp> 40 #include <com/sun/star/rendering/XCanvas.hpp> 41 42 #include <rtl/math.hxx> 43 44 #include <vcl/metaact.hxx> 45 #include <vcl/bitmapex.hxx> 46 #include <vcl/canvastools.hxx> 47 #include <vcl/svapp.hxx> 48 #include <vcl/outdev.hxx> 49 #include <vcl/virdev.hxx> 50 #include <vcl/virdev.hxx> 51 #include <vcl/gdimtf.hxx> 52 #include <vcl/gradient.hxx> 53 54 #include <canvas/canvastools.hxx> 55 56 #include <basegfx/range/b2drange.hxx> 57 #include <basegfx/point/b2dpoint.hxx> 58 #include <basegfx/vector/b2dsize.hxx> 59 #include <basegfx/numeric/ftools.hxx> 60 #include <basegfx/matrix/b2dhommatrix.hxx> 61 #include <basegfx/tuple/b2dtuple.hxx> 62 #include <basegfx/tools/canvastools.hxx> 63 64 #include <boost/utility.hpp> 65 66 #include "transparencygroupaction.hxx" 67 #include "outdevstate.hxx" 68 #include "mtftools.hxx" 69 #include "cppcanvas/vclfactory.hxx" 70 71 72 using namespace ::com::sun::star; 73 74 namespace cppcanvas 75 { 76 namespace internal 77 { 78 // free support functions 79 // ====================== 80 namespace 81 { 82 class TransparencyGroupAction : public Action, private ::boost::noncopyable 83 { 84 public: 85 /** Create new transparency group action. 86 87 @param rGroupMtf 88 Metafile that groups all actions to be rendered 89 transparent 90 91 @param rParms 92 Render parameters 93 94 @param rDstPoint 95 Left, top edge of destination, in current state 96 coordinate system 97 98 @param rDstSize 99 Size of the transparency group object, in current 100 state coordinate system. 101 102 @param nAlpha 103 Alpha value, must be in the range [0,1] 104 */ 105 TransparencyGroupAction( MtfAutoPtr& rGroupMtf, 106 const Renderer::Parameters& rParms, 107 const ::basegfx::B2DPoint& rDstPoint, 108 const ::basegfx::B2DVector& rDstSize, 109 double nAlpha, 110 const CanvasSharedPtr& rCanvas, 111 const OutDevState& rState ); 112 113 /** Create new transparency group action. 114 115 @param rGroupMtf 116 Metafile that groups all actions to be rendered 117 transparent. 118 119 @param rAlphaGradient 120 VCL gradient, to be rendered into the action's alpha 121 channel. 122 123 @param rParms 124 Render parameters 125 126 @param rDstPoint 127 Left, top edge of destination, in current state 128 coordinate system 129 130 @param rDstSize 131 Size of the transparency group object, in current 132 state coordinate system. 133 */ 134 TransparencyGroupAction( MtfAutoPtr& rGroupMtf, 135 GradientAutoPtr& rAlphaGradient, 136 const Renderer::Parameters& rParms, 137 const ::basegfx::B2DPoint& rDstPoint, 138 const ::basegfx::B2DVector& rDstSize, 139 const CanvasSharedPtr& rCanvas, 140 const OutDevState& rState ); 141 142 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const; 143 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation, 144 const Subset& rSubset ) const; 145 146 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const; 147 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation, 148 const Subset& rSubset ) const; 149 150 virtual sal_Int32 getActionCount() const; 151 152 private: 153 MtfAutoPtr mpGroupMtf; 154 GradientAutoPtr mpAlphaGradient; 155 156 const Renderer::Parameters maParms; 157 158 const ::basegfx::B2DSize maDstSize; 159 160 mutable uno::Reference< rendering::XBitmap > mxBufferBitmap; // contains last rendered version 161 mutable ::basegfx::B2DHomMatrix maLastTransformation; // contains last active transformation 162 mutable Subset maLastSubset; // contains last effective subset 163 164 // transformation for 165 // mxBufferBitmap content 166 CanvasSharedPtr mpCanvas; 167 rendering::RenderState maState; 168 const double mnAlpha; 169 }; 170 171 172 /** Setup transformation such that the next render call is 173 moved rPoint away, and scaled according to the ratio 174 given by src and dst size. 175 */ 176 void implSetupTransform( rendering::RenderState& rRenderState, 177 const ::basegfx::B2DPoint& rDstPoint ) 178 { 179 ::basegfx::B2DHomMatrix aLocalTransformation; 180 181 aLocalTransformation.translate( rDstPoint.getX(), 182 rDstPoint.getY() ); 183 ::canvas::tools::appendToRenderState( rRenderState, 184 aLocalTransformation ); 185 } 186 187 TransparencyGroupAction::TransparencyGroupAction( MtfAutoPtr& rGroupMtf, 188 const Renderer::Parameters& rParms, 189 const ::basegfx::B2DPoint& rDstPoint, 190 const ::basegfx::B2DVector& rDstSize, 191 double nAlpha, 192 const CanvasSharedPtr& rCanvas, 193 const OutDevState& rState ) : 194 mpGroupMtf( rGroupMtf ), 195 mpAlphaGradient(), 196 maParms( rParms ), 197 maDstSize( rDstSize ), 198 mxBufferBitmap(), 199 maLastTransformation(), 200 mpCanvas( rCanvas ), 201 maState(), 202 mnAlpha( nAlpha ) 203 { 204 tools::initRenderState(maState,rState); 205 implSetupTransform( maState, rDstPoint ); 206 207 // correct clip (which is relative to original transform) 208 tools::modifyClip( maState, 209 rState, 210 rCanvas, 211 rDstPoint, 212 NULL, 213 NULL ); 214 215 maLastSubset.mnSubsetBegin = 0; 216 maLastSubset.mnSubsetEnd = -1; 217 } 218 219 TransparencyGroupAction::TransparencyGroupAction( MtfAutoPtr& rGroupMtf, 220 GradientAutoPtr& rAlphaGradient, 221 const Renderer::Parameters& rParms, 222 const ::basegfx::B2DPoint& rDstPoint, 223 const ::basegfx::B2DVector& rDstSize, 224 const CanvasSharedPtr& rCanvas, 225 const OutDevState& rState ) : 226 mpGroupMtf( rGroupMtf ), 227 mpAlphaGradient( rAlphaGradient ), 228 maParms( rParms ), 229 maDstSize( rDstSize ), 230 mxBufferBitmap(), 231 maLastTransformation(), 232 mpCanvas( rCanvas ), 233 maState(), 234 mnAlpha( 1.0 ) 235 { 236 tools::initRenderState(maState,rState); 237 implSetupTransform( maState, rDstPoint ); 238 239 // correct clip (which is relative to original transform) 240 tools::modifyClip( maState, 241 rState, 242 rCanvas, 243 rDstPoint, 244 NULL, 245 NULL ); 246 247 maLastSubset.mnSubsetBegin = 0; 248 maLastSubset.mnSubsetEnd = -1; 249 } 250 251 // TODO(P3): The whole float transparency handling is a mess, 252 // this should be refactored. What's more, the old idea of 253 // having only internal 'metaactions', and not the original 254 // GDIMetaFile now looks a lot less attractive. Try to move 255 // into the direction of having a direct GDIMetaFile2XCanvas 256 // renderer, and maybe a separate metafile XCanvas 257 // implementation. 258 bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix& rTransformation, 259 const Subset& rSubset ) const 260 { 261 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TransparencyGroupAction::render()" ); 262 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TransparencyGroupAction: 0x%X", this ); 263 264 // determine overall transformation matrix (render, view, 265 // and passed transformation) 266 ::basegfx::B2DHomMatrix aTransform; 267 ::canvas::tools::getRenderStateTransform( aTransform, maState ); 268 aTransform = rTransformation * aTransform; 269 270 ::basegfx::B2DHomMatrix aTotalTransform; 271 ::canvas::tools::getViewStateTransform( aTotalTransform, mpCanvas->getViewState() ); 272 aTotalTransform = aTotalTransform * aTransform; 273 274 // since pure translational changes to the transformation 275 // does not matter, remove them before comparing 276 aTotalTransform.set( 0, 2, 0.0 ); 277 aTotalTransform.set( 1, 2, 0.0 ); 278 279 // if there's no buffer bitmap, or as soon as the 280 // total transformation changes, we've got to 281 // re-render the bitmap 282 if( !mxBufferBitmap.is() || 283 aTotalTransform != maLastTransformation || 284 rSubset.mnSubsetBegin != maLastSubset.mnSubsetBegin || 285 rSubset.mnSubsetEnd != maLastSubset.mnSubsetEnd ) 286 { 287 DBG_TESTSOLARMUTEX(); 288 289 // determine total scaling factor of the 290 // transformation matrix - need to make the bitmap 291 // large enough 292 ::basegfx::B2DTuple aScale; 293 ::basegfx::B2DTuple aTranslate; 294 double nRotate; 295 double nShearX; 296 if( !aTotalTransform.decompose( aScale, 297 aTranslate, 298 nRotate, 299 nShearX ) ) 300 { 301 OSL_ENSURE( false, 302 "TransparencyGroupAction::render(): non-decomposable transformation" ); 303 return false; 304 } 305 306 // output size of metafile 307 ::Size aOutputSizePixel( ::basegfx::fround( aScale.getX() * maDstSize.getX() ), 308 ::basegfx::fround( aScale.getY() * maDstSize.getY() ) ); 309 310 // pixel size of cache bitmap: round up to nearest int 311 ::Size aBitmapSizePixel( static_cast<sal_Int32>( aScale.getX() * maDstSize.getX() )+1, 312 static_cast<sal_Int32>( aScale.getY() * maDstSize.getY() )+1 ); 313 314 ::Point aEmptyPoint; 315 316 // render our content into an appropriately sized 317 // VirtualDevice with alpha channel 318 VirtualDevice aVDev( 319 *::Application::GetDefaultDevice(), 0, 0 ); 320 aVDev.SetOutputSizePixel( aBitmapSizePixel ); 321 aVDev.SetMapMode(); 322 323 if( rSubset.mnSubsetBegin != 0 || 324 rSubset.mnSubsetEnd != -1 ) 325 { 326 // true subset - extract referenced 327 // metaactions from mpGroupMtf 328 GDIMetaFile aMtf; 329 MetaAction* pCurrAct; 330 int nCurrActionIndex; 331 332 // extract subset actions 333 for( nCurrActionIndex=0, 334 pCurrAct=mpGroupMtf->FirstAction(); 335 pCurrAct; 336 ++nCurrActionIndex, pCurrAct = mpGroupMtf->NextAction() ) 337 { 338 switch( pCurrAct->GetType() ) 339 { 340 case META_PUSH_ACTION: 341 case META_POP_ACTION: 342 case META_CLIPREGION_ACTION: 343 case META_ISECTRECTCLIPREGION_ACTION: 344 case META_ISECTREGIONCLIPREGION_ACTION: 345 case META_MOVECLIPREGION_ACTION: 346 case META_LINECOLOR_ACTION: 347 case META_FILLCOLOR_ACTION: 348 case META_TEXTCOLOR_ACTION: 349 case META_TEXTFILLCOLOR_ACTION: 350 case META_TEXTLINECOLOR_ACTION: 351 case META_TEXTALIGN_ACTION: 352 case META_FONT_ACTION: 353 case META_RASTEROP_ACTION: 354 case META_REFPOINT_ACTION: 355 case META_LAYOUTMODE_ACTION: 356 // state-changing action - copy as-is 357 aMtf.AddAction( pCurrAct->Clone() ); 358 break; 359 360 case META_GRADIENT_ACTION: 361 case META_HATCH_ACTION: 362 case META_EPS_ACTION: 363 case META_COMMENT_ACTION: 364 case META_POINT_ACTION: 365 case META_PIXEL_ACTION: 366 case META_LINE_ACTION: 367 case META_RECT_ACTION: 368 case META_ROUNDRECT_ACTION: 369 case META_ELLIPSE_ACTION: 370 case META_ARC_ACTION: 371 case META_PIE_ACTION: 372 case META_CHORD_ACTION: 373 case META_POLYLINE_ACTION: 374 case META_POLYGON_ACTION: 375 case META_POLYPOLYGON_ACTION: 376 case META_BMP_ACTION: 377 case META_BMPSCALE_ACTION: 378 case META_BMPSCALEPART_ACTION: 379 case META_BMPEX_ACTION: 380 case META_BMPEXSCALE_ACTION: 381 case META_BMPEXSCALEPART_ACTION: 382 case META_MASK_ACTION: 383 case META_MASKSCALE_ACTION: 384 case META_MASKSCALEPART_ACTION: 385 case META_GRADIENTEX_ACTION: 386 case META_WALLPAPER_ACTION: 387 case META_TRANSPARENT_ACTION: 388 case META_FLOATTRANSPARENT_ACTION: 389 case META_TEXT_ACTION: 390 case META_TEXTARRAY_ACTION: 391 case META_TEXTLINE_ACTION: 392 case META_TEXTRECT_ACTION: 393 case META_STRETCHTEXT_ACTION: 394 case META_RENDERGRAPHIC_ACTION: 395 // output-generating action - only 396 // copy, if we're within the 397 // requested subset 398 if( rSubset.mnSubsetBegin <= nCurrActionIndex && 399 rSubset.mnSubsetEnd > nCurrActionIndex ) 400 { 401 aMtf.AddAction( pCurrAct->Clone() ); 402 } 403 break; 404 405 default: 406 OSL_ENSURE( false, 407 "Unknown meta action type encountered" ); 408 break; 409 } 410 } 411 412 aVDev.DrawTransparent( aMtf, 413 aEmptyPoint, 414 aOutputSizePixel, 415 *mpAlphaGradient ); 416 } 417 else 418 { 419 // no subsetting - render whole mtf 420 aVDev.DrawTransparent( *mpGroupMtf, 421 aEmptyPoint, 422 aOutputSizePixel, 423 *mpAlphaGradient ); 424 } 425 426 427 // update buffered bitmap and transformation 428 BitmapSharedPtr aBmp( VCLFactory::getInstance().createBitmap( 429 mpCanvas, 430 aVDev.GetBitmapEx( 431 aEmptyPoint, 432 aBitmapSizePixel ) ) ); 433 mxBufferBitmap = aBmp->getUNOBitmap(); 434 maLastTransformation = aTotalTransform; 435 maLastSubset = rSubset; 436 } 437 438 // determine target transformation (we can't simply pass 439 // aTotalTransform as assembled above, since we must take 440 // the canvas' view state as is, it might contain clipping 441 // (which, in turn, is relative to the view 442 // transformation)) 443 444 // given that aTotalTransform is the identity 445 // transformation, we could simply render our bitmap 446 // as-is. Now, since the mxBufferBitmap content already 447 // accounts for scale changes in the overall 448 // transformation, we must factor this out 449 // before. Generally, the transformation matrix should be 450 // structured like this: 451 // Translation*Rotation*Shear*Scale. Thus, to neutralize 452 // the contained scaling, we've got to right-multiply with 453 // the inverse. 454 ::basegfx::B2ISize aBmpSize( 455 ::basegfx::unotools::b2ISizeFromIntegerSize2D( mxBufferBitmap->getSize() ) ); 456 457 ::basegfx::B2DHomMatrix aScaleCorrection; 458 aScaleCorrection.scale( (double)maDstSize.getX() / aBmpSize.getX(), 459 (double)maDstSize.getY() / aBmpSize.getY() ); 460 aTransform = aTransform * aScaleCorrection; 461 462 rendering::RenderState aLocalState( maState ); 463 ::canvas::tools::setRenderStateTransform(aLocalState, aTransform); 464 465 #ifdef SPECIAL_DEBUG 466 aLocalState.Clip.clear(); 467 aLocalState.DeviceColor = 468 ::vcl::unotools::colorToDoubleSequence( 469 ::Color( 0x80FF0000 ), 470 mpCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() ); 471 472 if( maState.Clip.is() ) 473 mpCanvas->getUNOCanvas()->fillPolyPolygon( maState.Clip, 474 mpCanvas->getViewState(), 475 aLocalState ); 476 477 aLocalState.DeviceColor = maState.DeviceColor; 478 #endif 479 480 if( ::rtl::math::approxEqual(mnAlpha, 1.0) ) 481 { 482 // no further alpha changes necessary -> draw directly 483 mpCanvas->getUNOCanvas()->drawBitmap( mxBufferBitmap, 484 mpCanvas->getViewState(), 485 aLocalState ); 486 } 487 else 488 { 489 // add alpha modulation value to DeviceColor 490 uno::Sequence<rendering::ARGBColor> aCols(1); 491 aCols[0] = rendering::ARGBColor( mnAlpha, 1.0, 1.0, 1.0); 492 aLocalState.DeviceColor = 493 mpCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace()->convertFromARGB( 494 aCols); 495 496 mpCanvas->getUNOCanvas()->drawBitmapModulated( mxBufferBitmap, 497 mpCanvas->getViewState(), 498 aLocalState ); 499 } 500 501 return true; 502 } 503 504 // TODO(P3): The whole float transparency handling is a mess, 505 // this should be refactored. What's more, the old idea of 506 // having only internal 'metaactions', and not the original 507 // GDIMetaFile now looks a lot less attractive. Try to move 508 // into the direction of having a direct GDIMetaFile2XCanvas 509 // renderer, and maybe a separate metafile XCanvas 510 // implementation. 511 bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const 512 { 513 Subset aSubset; 514 515 aSubset.mnSubsetBegin = 0; 516 aSubset.mnSubsetEnd = -1; 517 518 return render( rTransformation, aSubset ); 519 } 520 521 ::basegfx::B2DRange TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const 522 { 523 rendering::RenderState aLocalState( maState ); 524 ::canvas::tools::prependToRenderState(aLocalState, rTransformation); 525 526 return tools::calcDevicePixelBounds( 527 ::basegfx::B2DRange( 0,0, 528 maDstSize.getX(), 529 maDstSize.getY() ), 530 mpCanvas->getViewState(), 531 aLocalState ); 532 } 533 534 ::basegfx::B2DRange TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation, 535 const Subset& rSubset ) const 536 { 537 // TODO(F3): Currently, the bounds for 538 // TransparencyGroupAction subsets equal those of the 539 // full set, although this action is able to render 540 // true subsets. 541 542 // polygon only contains a single action, empty bounds 543 // if subset requests different range 544 if( rSubset.mnSubsetBegin != 0 || 545 rSubset.mnSubsetEnd != 1 ) 546 return ::basegfx::B2DRange(); 547 548 return getBounds( rTransformation ); 549 } 550 551 sal_Int32 TransparencyGroupAction::getActionCount() const 552 { 553 return mpGroupMtf.get() ? mpGroupMtf->GetActionCount() : 0; 554 } 555 556 } 557 558 ActionSharedPtr TransparencyGroupActionFactory::createTransparencyGroupAction( MtfAutoPtr& rGroupMtf, 559 const Renderer::Parameters& rParms, 560 const ::basegfx::B2DPoint& rDstPoint, 561 const ::basegfx::B2DVector& rDstSize, 562 double nAlpha, 563 const CanvasSharedPtr& rCanvas, 564 const OutDevState& rState ) 565 { 566 return ActionSharedPtr( new TransparencyGroupAction(rGroupMtf, 567 rParms, 568 rDstPoint, 569 rDstSize, 570 nAlpha, 571 rCanvas, 572 rState ) ); 573 } 574 575 ActionSharedPtr TransparencyGroupActionFactory::createTransparencyGroupAction( MtfAutoPtr& rGroupMtf, 576 GradientAutoPtr& rAlphaGradient, 577 const Renderer::Parameters& rParms, 578 const ::basegfx::B2DPoint& rDstPoint, 579 const ::basegfx::B2DVector& rDstSize, 580 const CanvasSharedPtr& rCanvas, 581 const OutDevState& rState ) 582 { 583 return ActionSharedPtr( new TransparencyGroupAction(rGroupMtf, 584 rAlphaGradient, 585 rParms, 586 rDstPoint, 587 rDstSize, 588 rCanvas, 589 rState ) ); 590 } 591 592 } 593 } 594