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 */ implSetupTransform(rendering::RenderState & rRenderState,const::basegfx::B2DPoint & rDstPoint)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 TransparencyGroupAction(MtfAutoPtr & rGroupMtf,const Renderer::Parameters & rParms,const::basegfx::B2DPoint & rDstPoint,const::basegfx::B2DVector & rDstSize,double nAlpha,const CanvasSharedPtr & rCanvas,const OutDevState & rState)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 TransparencyGroupAction(MtfAutoPtr & rGroupMtf,GradientAutoPtr & rAlphaGradient,const Renderer::Parameters & rParms,const::basegfx::B2DPoint & rDstPoint,const::basegfx::B2DVector & rDstSize,const CanvasSharedPtr & rCanvas,const OutDevState & rState)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. render(const::basegfx::B2DHomMatrix & rTransformation,const Subset & rSubset) const254 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 // output-generating action - only 391 // copy, if we're within the 392 // requested subset 393 if( rSubset.mnSubsetBegin <= nCurrActionIndex && 394 rSubset.mnSubsetEnd > nCurrActionIndex ) 395 { 396 aMtf.AddAction( pCurrAct->Clone() ); 397 } 398 break; 399 400 default: 401 OSL_ENSURE( false, 402 "Unknown meta action type encountered" ); 403 break; 404 } 405 } 406 407 aVDev.DrawTransparent( aMtf, 408 aEmptyPoint, 409 aOutputSizePixel, 410 *mpAlphaGradient ); 411 } 412 else 413 { 414 // no subsetting - render whole mtf 415 aVDev.DrawTransparent( *mpGroupMtf, 416 aEmptyPoint, 417 aOutputSizePixel, 418 *mpAlphaGradient ); 419 } 420 421 422 // update buffered bitmap and transformation 423 BitmapSharedPtr aBmp( VCLFactory::getInstance().createBitmap( 424 mpCanvas, 425 aVDev.GetBitmapEx( 426 aEmptyPoint, 427 aBitmapSizePixel ) ) ); 428 mxBufferBitmap = aBmp->getUNOBitmap(); 429 maLastTransformation = aTotalTransform; 430 maLastSubset = rSubset; 431 } 432 433 // determine target transformation (we can't simply pass 434 // aTotalTransform as assembled above, since we must take 435 // the canvas' view state as is, it might contain clipping 436 // (which, in turn, is relative to the view 437 // transformation)) 438 439 // given that aTotalTransform is the identity 440 // transformation, we could simply render our bitmap 441 // as-is. Now, since the mxBufferBitmap content already 442 // accounts for scale changes in the overall 443 // transformation, we must factor this out 444 // before. Generally, the transformation matrix should be 445 // structured like this: 446 // Translation*Rotation*Shear*Scale. Thus, to neutralize 447 // the contained scaling, we've got to right-multiply with 448 // the inverse. 449 ::basegfx::B2ISize aBmpSize( 450 ::basegfx::unotools::b2ISizeFromIntegerSize2D( mxBufferBitmap->getSize() ) ); 451 452 ::basegfx::B2DHomMatrix aScaleCorrection; 453 aScaleCorrection.scale( (double)maDstSize.getX() / aBmpSize.getX(), 454 (double)maDstSize.getY() / aBmpSize.getY() ); 455 aTransform = aTransform * aScaleCorrection; 456 457 rendering::RenderState aLocalState( maState ); 458 ::canvas::tools::setRenderStateTransform(aLocalState, aTransform); 459 460 #ifdef SPECIAL_DEBUG 461 aLocalState.Clip.clear(); 462 aLocalState.DeviceColor = 463 ::vcl::unotools::colorToDoubleSequence( 464 ::Color( 0x80FF0000 ), 465 mpCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() ); 466 467 if( maState.Clip.is() ) 468 mpCanvas->getUNOCanvas()->fillPolyPolygon( maState.Clip, 469 mpCanvas->getViewState(), 470 aLocalState ); 471 472 aLocalState.DeviceColor = maState.DeviceColor; 473 #endif 474 475 if( ::rtl::math::approxEqual(mnAlpha, 1.0) ) 476 { 477 // no further alpha changes necessary -> draw directly 478 mpCanvas->getUNOCanvas()->drawBitmap( mxBufferBitmap, 479 mpCanvas->getViewState(), 480 aLocalState ); 481 } 482 else 483 { 484 // add alpha modulation value to DeviceColor 485 uno::Sequence<rendering::ARGBColor> aCols(1); 486 aCols[0] = rendering::ARGBColor( mnAlpha, 1.0, 1.0, 1.0); 487 aLocalState.DeviceColor = 488 mpCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace()->convertFromARGB( 489 aCols); 490 491 mpCanvas->getUNOCanvas()->drawBitmapModulated( mxBufferBitmap, 492 mpCanvas->getViewState(), 493 aLocalState ); 494 } 495 496 return true; 497 } 498 499 // TODO(P3): The whole float transparency handling is a mess, 500 // this should be refactored. What's more, the old idea of 501 // having only internal 'metaactions', and not the original 502 // GDIMetaFile now looks a lot less attractive. Try to move 503 // into the direction of having a direct GDIMetaFile2XCanvas 504 // renderer, and maybe a separate metafile XCanvas 505 // implementation. render(const::basegfx::B2DHomMatrix & rTransformation) const506 bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const 507 { 508 Subset aSubset; 509 510 aSubset.mnSubsetBegin = 0; 511 aSubset.mnSubsetEnd = -1; 512 513 return render( rTransformation, aSubset ); 514 } 515 getBounds(const::basegfx::B2DHomMatrix & rTransformation) const516 ::basegfx::B2DRange TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const 517 { 518 rendering::RenderState aLocalState( maState ); 519 ::canvas::tools::prependToRenderState(aLocalState, rTransformation); 520 521 return tools::calcDevicePixelBounds( 522 ::basegfx::B2DRange( 0,0, 523 maDstSize.getX(), 524 maDstSize.getY() ), 525 mpCanvas->getViewState(), 526 aLocalState ); 527 } 528 getBounds(const::basegfx::B2DHomMatrix & rTransformation,const Subset & rSubset) const529 ::basegfx::B2DRange TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation, 530 const Subset& rSubset ) const 531 { 532 // TODO(F3): Currently, the bounds for 533 // TransparencyGroupAction subsets equal those of the 534 // full set, although this action is able to render 535 // true subsets. 536 537 // polygon only contains a single action, empty bounds 538 // if subset requests different range 539 if( rSubset.mnSubsetBegin != 0 || 540 rSubset.mnSubsetEnd != 1 ) 541 return ::basegfx::B2DRange(); 542 543 return getBounds( rTransformation ); 544 } 545 getActionCount() const546 sal_Int32 TransparencyGroupAction::getActionCount() const 547 { 548 return mpGroupMtf.get() ? mpGroupMtf->GetActionCount() : 0; 549 } 550 551 } 552 createTransparencyGroupAction(MtfAutoPtr & rGroupMtf,const Renderer::Parameters & rParms,const::basegfx::B2DPoint & rDstPoint,const::basegfx::B2DVector & rDstSize,double nAlpha,const CanvasSharedPtr & rCanvas,const OutDevState & rState)553 ActionSharedPtr TransparencyGroupActionFactory::createTransparencyGroupAction( MtfAutoPtr& rGroupMtf, 554 const Renderer::Parameters& rParms, 555 const ::basegfx::B2DPoint& rDstPoint, 556 const ::basegfx::B2DVector& rDstSize, 557 double nAlpha, 558 const CanvasSharedPtr& rCanvas, 559 const OutDevState& rState ) 560 { 561 return ActionSharedPtr( new TransparencyGroupAction(rGroupMtf, 562 rParms, 563 rDstPoint, 564 rDstSize, 565 nAlpha, 566 rCanvas, 567 rState ) ); 568 } 569 createTransparencyGroupAction(MtfAutoPtr & rGroupMtf,GradientAutoPtr & rAlphaGradient,const Renderer::Parameters & rParms,const::basegfx::B2DPoint & rDstPoint,const::basegfx::B2DVector & rDstSize,const CanvasSharedPtr & rCanvas,const OutDevState & rState)570 ActionSharedPtr TransparencyGroupActionFactory::createTransparencyGroupAction( MtfAutoPtr& rGroupMtf, 571 GradientAutoPtr& rAlphaGradient, 572 const Renderer::Parameters& rParms, 573 const ::basegfx::B2DPoint& rDstPoint, 574 const ::basegfx::B2DVector& rDstSize, 575 const CanvasSharedPtr& rCanvas, 576 const OutDevState& rState ) 577 { 578 return ActionSharedPtr( new TransparencyGroupAction(rGroupMtf, 579 rAlphaGradient, 580 rParms, 581 rDstPoint, 582 rDstSize, 583 rCanvas, 584 rState ) ); 585 } 586 587 } 588 } 589