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