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_slideshow.hxx" 26 27 // must be first 28 #include <canvas/debug.hxx> 29 #include <tools/diagnose_ex.h> 30 31 #include <math.h> 32 33 #include <rtl/logfile.hxx> 34 #include <rtl/math.hxx> 35 36 #include <com/sun/star/rendering/XCanvas.hpp> 37 #include <com/sun/star/rendering/XIntegerBitmap.hpp> 38 #include <com/sun/star/rendering/PanoseLetterForm.hpp> 39 #include <com/sun/star/awt/FontSlant.hpp> 40 41 #include <cppuhelper/exc_hlp.hxx> 42 #include <comphelper/anytostring.hxx> 43 44 #include <basegfx/polygon/b2dpolygontools.hxx> 45 #include <basegfx/numeric/ftools.hxx> 46 #include <basegfx/matrix/b2dhommatrix.hxx> 47 #include <basegfx/matrix/b2dhommatrixtools.hxx> 48 49 #include <canvas/verbosetrace.hxx> 50 #include <canvas/canvastools.hxx> 51 #include <cppcanvas/vclfactory.hxx> 52 #include <cppcanvas/basegfxfactory.hxx> 53 54 #include "viewshape.hxx" 55 #include "tools.hxx" 56 57 #include <boost/bind.hpp> 58 59 60 using namespace ::com::sun::star; 61 62 namespace slideshow 63 { 64 namespace internal 65 { 66 67 // TODO(F2): Provide sensible setup for mtf-related attributes (fill mode, 68 // char rotation etc.). Do that via mtf argument at this object 69 70 bool ViewShape::prefetch( RendererCacheEntry& io_rCacheEntry, 71 const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas, 72 const GDIMetaFileSharedPtr& rMtf, 73 const ShapeAttributeLayerSharedPtr& rAttr ) const 74 { 75 RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::prefetch()" ); 76 ENSURE_OR_RETURN_FALSE( rMtf, 77 "ViewShape::prefetch(): no valid metafile!" ); 78 79 if( rMtf != io_rCacheEntry.mpMtf || 80 rDestinationCanvas != io_rCacheEntry.getDestinationCanvas() ) 81 { 82 // buffered renderer invalid, re-create 83 ::cppcanvas::Renderer::Parameters aParms; 84 85 // rendering attribute override parameter struct. For 86 // every valid attribute, the corresponding struct 87 // member is filled, which in the metafile renderer 88 // forces rendering with the given attribute. 89 if( rAttr ) 90 { 91 if( rAttr->isFillColorValid() ) 92 { 93 // convert RGBColor to RGBA32 integer. Note 94 // that getIntegerColor() also truncates 95 // out-of-range values appropriately 96 aParms.maFillColor = 97 rAttr->getFillColor().getIntegerColor(); 98 } 99 if( rAttr->isLineColorValid() ) 100 { 101 // convert RGBColor to RGBA32 integer. Note 102 // that getIntegerColor() also truncates 103 // out-of-range values appropriately 104 aParms.maLineColor = 105 rAttr->getLineColor().getIntegerColor(); 106 } 107 if( rAttr->isCharColorValid() ) 108 { 109 // convert RGBColor to RGBA32 integer. Note 110 // that getIntegerColor() also truncates 111 // out-of-range values appropriately 112 aParms.maTextColor = 113 rAttr->getCharColor().getIntegerColor(); 114 } 115 if( rAttr->isDimColorValid() ) 116 { 117 // convert RGBColor to RGBA32 integer. Note 118 // that getIntegerColor() also truncates 119 // out-of-range values appropriately 120 121 // dim color overrides all other colors 122 aParms.maFillColor = 123 aParms.maLineColor = 124 aParms.maTextColor = 125 rAttr->getDimColor().getIntegerColor(); 126 } 127 if( rAttr->isFontFamilyValid() ) 128 { 129 aParms.maFontName = 130 rAttr->getFontFamily(); 131 } 132 if( rAttr->isCharScaleValid() ) 133 { 134 ::basegfx::B2DHomMatrix aMatrix; 135 136 // enlarge text by given scale factor. Do that 137 // with the middle of the shape as the center 138 // of scaling. 139 aMatrix.translate( -0.5, -0.5 ); 140 aMatrix.scale( rAttr->getCharScale(), 141 rAttr->getCharScale() ); 142 aMatrix.translate( 0.5, 0.5 ); 143 144 aParms.maTextTransformation = aMatrix; 145 } 146 if( rAttr->isCharWeightValid() ) 147 { 148 aParms.maFontWeight = 149 static_cast< sal_Int8 >( 150 ::basegfx::fround( 151 ::std::max( 0.0, 152 ::std::min( 11.0, 153 rAttr->getCharWeight() / 20.0 ) ) ) ); 154 } 155 if( rAttr->isCharPostureValid() ) 156 { 157 aParms.maFontLetterForm = 158 rAttr->getCharPosture() == awt::FontSlant_NONE ? 159 rendering::PanoseLetterForm::ANYTHING : 160 rendering::PanoseLetterForm::OBLIQUE_CONTACT; 161 } 162 if( rAttr->isUnderlineModeValid() ) 163 { 164 aParms.maFontUnderline = 165 rAttr->getUnderlineMode(); 166 } 167 } 168 169 io_rCacheEntry.mpRenderer = ::cppcanvas::VCLFactory::getInstance().createRenderer( rDestinationCanvas, 170 *rMtf.get(), 171 aParms ); 172 173 io_rCacheEntry.mpMtf = rMtf; 174 io_rCacheEntry.mpDestinationCanvas = rDestinationCanvas; 175 176 // also invalidate alpha compositing bitmap (created 177 // new renderer, which possibly generates different 178 // output). Do NOT invalidate, if we're incidentally 179 // rendering INTO it. 180 if( rDestinationCanvas != io_rCacheEntry.mpLastBitmapCanvas ) 181 { 182 io_rCacheEntry.mpLastBitmapCanvas.reset(); 183 io_rCacheEntry.mpLastBitmap.reset(); 184 } 185 } 186 187 return io_rCacheEntry.mpRenderer; 188 } 189 190 bool ViewShape::draw( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas, 191 const GDIMetaFileSharedPtr& rMtf, 192 const ShapeAttributeLayerSharedPtr& rAttr, 193 const ::basegfx::B2DHomMatrix& rTransform, 194 const ::basegfx::B2DPolyPolygon* pClip, 195 const VectorOfDocTreeNodes& rSubsets ) const 196 { 197 RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::draw()" ); 198 199 ::cppcanvas::RendererSharedPtr pRenderer( 200 getRenderer( rDestinationCanvas, rMtf, rAttr ) ); 201 202 ENSURE_OR_RETURN_FALSE( pRenderer, "ViewShape::draw(): Invalid renderer" ); 203 204 pRenderer->setTransformation( rTransform ); 205 #if defined(VERBOSE) && OSL_DEBUG_LEVEL > 0 206 rendering::RenderState aRenderState; 207 ::canvas::tools::initRenderState(aRenderState); 208 ::canvas::tools::setRenderStateTransform(aRenderState, 209 rTransform); 210 aRenderState.DeviceColor.realloc(4); 211 aRenderState.DeviceColor[0] = 1.0; 212 aRenderState.DeviceColor[1] = 0.0; 213 aRenderState.DeviceColor[2] = 0.0; 214 aRenderState.DeviceColor[3] = 1.0; 215 216 try 217 { 218 rDestinationCanvas->getUNOCanvas()->drawLine( geometry::RealPoint2D(0.0,0.0), 219 geometry::RealPoint2D(1.0,1.0), 220 rDestinationCanvas->getViewState(), 221 aRenderState ); 222 rDestinationCanvas->getUNOCanvas()->drawLine( geometry::RealPoint2D(1.0,0.0), 223 geometry::RealPoint2D(0.0,1.0), 224 rDestinationCanvas->getViewState(), 225 aRenderState ); 226 } 227 catch( uno::Exception& ) 228 { 229 DBG_UNHANDLED_EXCEPTION(); 230 } 231 #endif 232 if( pClip ) 233 pRenderer->setClip( *pClip ); 234 else 235 pRenderer->setClip(); 236 237 if( rSubsets.empty() ) 238 { 239 return pRenderer->draw(); 240 } 241 else 242 { 243 // render subsets of whole metafile 244 // -------------------------------- 245 246 bool bRet(true); 247 VectorOfDocTreeNodes::const_iterator aIter( rSubsets.begin() ); 248 const VectorOfDocTreeNodes::const_iterator aEnd ( rSubsets.end() ); 249 while( aIter != aEnd ) 250 { 251 if( !pRenderer->drawSubset( aIter->getStartIndex(), 252 aIter->getEndIndex() ) ) 253 bRet = false; 254 255 ++aIter; 256 } 257 258 return bRet; 259 } 260 } 261 262 namespace 263 { 264 /// Convert untransformed shape update area to device pixel. 265 ::basegfx::B2DRectangle shapeArea2AreaPixel( const ::basegfx::B2DHomMatrix& rCanvasTransformation, 266 const ::basegfx::B2DRectangle& rUntransformedArea ) 267 { 268 // convert area to pixel, and add anti-aliasing border 269 270 // TODO(P1): Should the view transform some 271 // day contain rotation/shear, transforming 272 // the original bounds with the total 273 // transformation might result in smaller 274 // overall bounds. 275 276 ::basegfx::B2DRectangle aBoundsPixel; 277 ::canvas::tools::calcTransformedRectBounds( aBoundsPixel, 278 rUntransformedArea, 279 rCanvasTransformation ); 280 281 // add antialiasing border around the shape (AA 282 // touches pixel _outside_ the nominal bound rect) 283 aBoundsPixel.grow( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE ); 284 285 return aBoundsPixel; 286 } 287 288 /// Convert shape unit rect to device pixel. 289 ::basegfx::B2DRectangle calcUpdateAreaPixel( const ::basegfx::B2DRectangle& rUnitBounds, 290 const ::basegfx::B2DHomMatrix& rShapeTransformation, 291 const ::basegfx::B2DHomMatrix& rCanvasTransformation, 292 const ShapeAttributeLayerSharedPtr& pAttr ) 293 { 294 // calc update area for whole shape (including 295 // character scaling) 296 return shapeArea2AreaPixel( rCanvasTransformation, 297 getShapeUpdateArea( rUnitBounds, 298 rShapeTransformation, 299 pAttr ) ); 300 } 301 } 302 303 bool ViewShape::renderSprite( const ViewLayerSharedPtr& rViewLayer, 304 const GDIMetaFileSharedPtr& rMtf, 305 const ::basegfx::B2DRectangle& rOrigBounds, 306 const ::basegfx::B2DRectangle& rBounds, 307 const ::basegfx::B2DRectangle& rUnitBounds, 308 int nUpdateFlags, 309 const ShapeAttributeLayerSharedPtr& pAttr, 310 const VectorOfDocTreeNodes& rSubsets, 311 double nPrio, 312 bool bIsVisible ) const 313 { 314 RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::renderSprite()" ); 315 316 // TODO(P1): For multiple views, it might pay off to reorg Shape and ViewShape, 317 // in that all the common setup steps here are refactored to Shape (would then 318 // have to be performed only _once_ per Shape paint). 319 320 if( !bIsVisible || 321 rUnitBounds.isEmpty() || 322 rOrigBounds.isEmpty() || 323 rBounds.isEmpty() ) 324 { 325 // shape is invisible or has zero size, no need to 326 // update anything. 327 if( mpSprite ) 328 mpSprite->hide(); 329 330 return true; 331 } 332 333 334 // calc sprite position, size and content transformation 335 // ===================================================== 336 337 // the shape transformation for a sprite is always a 338 // simple scale-up to the nominal shape size. Everything 339 // else is handled via the sprite transformation 340 ::basegfx::B2DHomMatrix aNonTranslationalShapeTransformation; 341 aNonTranslationalShapeTransformation.scale( rOrigBounds.getWidth(), 342 rOrigBounds.getHeight() ); 343 ::basegfx::B2DHomMatrix aShapeTransformation( aNonTranslationalShapeTransformation ); 344 aShapeTransformation.translate( rOrigBounds.getMinX(), 345 rOrigBounds.getMinY() ); 346 347 const ::basegfx::B2DHomMatrix& rCanvasTransform( 348 rViewLayer->getSpriteTransformation() ); 349 350 // area actually needed for the sprite 351 const ::basegfx::B2DRectangle& rSpriteBoundsPixel( 352 calcUpdateAreaPixel( rUnitBounds, 353 aShapeTransformation, 354 rCanvasTransform, 355 pAttr ) ); 356 357 // actual area for the shape (without subsetting, but 358 // including char scaling) 359 const ::basegfx::B2DRectangle& rShapeBoundsPixel( 360 calcUpdateAreaPixel( ::basegfx::B2DRectangle(0.0,0.0,1.0,1.0), 361 aShapeTransformation, 362 rCanvasTransform, 363 pAttr ) ); 364 365 // nominal area for the shape (without subsetting, without 366 // char scaling). NOTE: to cancel the shape translation, 367 // contained in rSpriteBoundsPixel, this is _without_ any 368 // translational component (fixed along with #121921#). 369 ::basegfx::B2DRectangle aLogShapeBounds; 370 const ::basegfx::B2DRectangle& rNominalShapeBoundsPixel( 371 shapeArea2AreaPixel( rCanvasTransform, 372 ::canvas::tools::calcTransformedRectBounds( 373 aLogShapeBounds, 374 ::basegfx::B2DRectangle(0.0,0.0,1.0,1.0), 375 aNonTranslationalShapeTransformation ) ) ); 376 377 // create (or resize) sprite with sprite's pixel size, if 378 // not done already 379 const ::basegfx::B2DSize& rSpriteSizePixel(rSpriteBoundsPixel.getRange()); 380 if( !mpSprite ) 381 { 382 mpSprite.reset( 383 new AnimatedSprite( mpViewLayer, 384 rSpriteSizePixel, 385 nPrio )); 386 } 387 else 388 { 389 // TODO(F2): when the sprite _actually_ gets resized, 390 // content needs a repaint! 391 mpSprite->resize( rSpriteSizePixel ); 392 } 393 394 ENSURE_OR_RETURN_FALSE( mpSprite, "ViewShape::renderSprite(): No sprite" ); 395 396 VERBOSE_TRACE( "ViewShape::renderSprite(): Rendering sprite 0x%X", 397 mpSprite.get() ); 398 399 400 // always show the sprite (might have been hidden before) 401 mpSprite->show(); 402 403 // determine center of sprite output position in pixel 404 // (assumption here: all shape transformations have the 405 // shape center as the pivot point). From that, subtract 406 // distance of rSpriteBoundsPixel's left, top edge from 407 // rShapeBoundsPixel's center. This moves the sprite at 408 // the appropriate output position within the virtual 409 // rShapeBoundsPixel area. 410 ::basegfx::B2DPoint aSpritePosPixel( rBounds.getCenter() ); 411 aSpritePosPixel *= rCanvasTransform; 412 aSpritePosPixel -= rShapeBoundsPixel.getCenter() - rSpriteBoundsPixel.getMinimum(); 413 414 // the difference between rShapeBoundsPixel and 415 // rSpriteBoundsPixel upper, left corner is: the offset we 416 // have to move sprite output to the right, top (to make 417 // the desired subset content visible at all) 418 const ::basegfx::B2DSize& rSpriteCorrectionOffset( 419 rSpriteBoundsPixel.getMinimum() - rNominalShapeBoundsPixel.getMinimum() ); 420 421 // offset added top, left for anti-aliasing (otherwise, 422 // shapes fully filling the sprite will have anti-aliased 423 // pixel cut off) 424 const ::basegfx::B2DSize aAAOffset( 425 ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE, 426 ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE ); 427 428 // set pixel output offset to sprite: we always leave 429 // ANTIALIASING_EXTRA_SIZE room atop and to the left, and, 430 // what's more, for subsetted shapes, we _have_ to cancel 431 // the effect of the shape renderer outputting the subset 432 // at its absolute position inside the shape, instead of 433 // at the origin. 434 // NOTE: As for now, sprites are always positioned on 435 // integer pixel positions on screen, have to round to 436 // nearest integer here, too (fixed along with #121921#) 437 mpSprite->setPixelOffset( 438 aAAOffset - ::basegfx::B2DSize( 439 ::basegfx::fround( rSpriteCorrectionOffset.getX() ), 440 ::basegfx::fround( rSpriteCorrectionOffset.getY() ) ) ); 441 442 // always set sprite position and transformation, since 443 // they do not relate directly to the update flags 444 // (e.g. sprite position changes when sprite size changes) 445 mpSprite->movePixel( aSpritePosPixel ); 446 mpSprite->transform( getSpriteTransformation( rSpriteSizePixel, 447 rOrigBounds.getRange(), 448 pAttr ) ); 449 450 451 // process flags 452 // ============= 453 454 bool bRedrawRequired( mbForceUpdate || (nUpdateFlags & FORCE) ); 455 456 if( mbForceUpdate || (nUpdateFlags & ALPHA) ) 457 { 458 mpSprite->setAlpha( (pAttr && pAttr->isAlphaValid()) ? 459 ::basegfx::clamp(pAttr->getAlpha(), 460 0.0, 461 1.0) : 462 1.0 ); 463 } 464 if( mbForceUpdate || (nUpdateFlags & CLIP) ) 465 { 466 if( pAttr && pAttr->isClipValid() ) 467 { 468 ::basegfx::B2DPolyPolygon aClipPoly( pAttr->getClip() ); 469 470 // extract linear part of canvas view transformation 471 // (linear means: without translational components) 472 ::basegfx::B2DHomMatrix aViewTransform( 473 mpViewLayer->getTransformation() ); 474 aViewTransform.set( 0, 2, 0.0 ); 475 aViewTransform.set( 1, 2, 0.0 ); 476 477 // make the clip 2*ANTIALIASING_EXTRA_SIZE larger 478 // such that it's again centered over the sprite. 479 aViewTransform.scale(rSpriteSizePixel.getX()/ 480 (rSpriteSizePixel.getX()-2*::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE), 481 rSpriteSizePixel.getY()/ 482 (rSpriteSizePixel.getY()-2*::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE)); 483 484 // transform clip polygon from view to device 485 // coordinate space 486 aClipPoly.transform( aViewTransform ); 487 488 mpSprite->clip( aClipPoly ); 489 } 490 else 491 mpSprite->clip(); 492 } 493 if( mbForceUpdate || (nUpdateFlags & CONTENT) ) 494 { 495 bRedrawRequired = true; 496 497 // TODO(P1): maybe provide some appearance change methods at 498 // the Renderer interface 499 500 // force the renderer to be regenerated below, for the 501 // different attributes to take effect 502 invalidateRenderer(); 503 } 504 505 mbForceUpdate = false; 506 507 if( !bRedrawRequired ) 508 return true; 509 510 511 // sprite needs repaint - output to sprite canvas 512 // ============================================== 513 514 ::cppcanvas::CanvasSharedPtr pContentCanvas( mpSprite->getContentCanvas() ); 515 516 return draw( pContentCanvas, 517 rMtf, 518 pAttr, 519 aShapeTransformation, 520 NULL, // clipping is done via Sprite::clip() 521 rSubsets ); 522 } 523 524 bool ViewShape::render( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas, 525 const GDIMetaFileSharedPtr& rMtf, 526 const ::basegfx::B2DRectangle& rBounds, 527 const ::basegfx::B2DRectangle& rUpdateBounds, 528 int nUpdateFlags, 529 const ShapeAttributeLayerSharedPtr& pAttr, 530 const VectorOfDocTreeNodes& rSubsets, 531 bool bIsVisible ) const 532 { 533 RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::render()" ); 534 535 // TODO(P1): For multiple views, it might pay off to reorg Shape and ViewShape, 536 // in that all the common setup steps here are refactored to Shape (would then 537 // have to be performed only _once_ per Shape paint). 538 539 if( !bIsVisible ) 540 { 541 VERBOSE_TRACE( "ViewShape::render(): skipping shape %X", this ); 542 543 // shape is invisible, no need to update anything. 544 return true; 545 } 546 547 // since we have no sprite here, _any_ update request 548 // translates into a required redraw. 549 bool bRedrawRequired( mbForceUpdate || nUpdateFlags != 0 ); 550 551 if( (nUpdateFlags & CONTENT) ) 552 { 553 // TODO(P1): maybe provide some appearance change methods at 554 // the Renderer interface 555 556 // force the renderer to be regenerated below, for the 557 // different attributes to take effect 558 invalidateRenderer(); 559 } 560 561 mbForceUpdate = false; 562 563 if( !bRedrawRequired ) 564 return true; 565 566 VERBOSE_TRACE( "ViewShape::render(): rendering shape %X at position (%f,%f)", 567 this, 568 rBounds.getMinX(), 569 rBounds.getMinY() ); 570 571 572 // shape needs repaint - setup all that's needed 573 // --------------------------------------------- 574 575 boost::optional<basegfx::B2DPolyPolygon> aClip; 576 577 if( pAttr ) 578 { 579 // setup clip poly 580 if( pAttr->isClipValid() ) 581 aClip.reset( pAttr->getClip() ); 582 583 // emulate global shape alpha by first rendering into 584 // a temp bitmap, and then to screen (this would have 585 // been much easier if we'd be currently a sprite - 586 // see above) 587 if( pAttr->isAlphaValid() ) 588 { 589 const double nAlpha( pAttr->getAlpha() ); 590 591 if( !::basegfx::fTools::equalZero( nAlpha ) && 592 !::rtl::math::approxEqual(nAlpha, 1.0) ) 593 { 594 // render with global alpha - have to prepare 595 // a bitmap, and render that with modulated 596 // alpha 597 // ------------------------------------------- 598 599 const ::basegfx::B2DHomMatrix aTransform( 600 getShapeTransformation( rBounds, 601 pAttr ) ); 602 603 // TODO(P1): Should the view transform some 604 // day contain rotation/shear, transforming 605 // the original bounds with the total 606 // transformation might result in smaller 607 // overall bounds. 608 609 // determine output rect of _shape update 610 // area_ in device pixel 611 const ::basegfx::B2DHomMatrix aCanvasTransform( 612 rDestinationCanvas->getTransformation() ); 613 ::basegfx::B2DRectangle aTmpRect; 614 ::canvas::tools::calcTransformedRectBounds( aTmpRect, 615 rUpdateBounds, 616 aCanvasTransform ); 617 618 // pixel size of cache bitmap: round up to 619 // nearest int 620 const ::basegfx::B2ISize aBmpSize( static_cast<sal_Int32>( aTmpRect.getWidth() )+1, 621 static_cast<sal_Int32>( aTmpRect.getHeight() )+1 ); 622 623 // try to fetch temporary surface for alpha 624 // compositing (to achieve the global alpha 625 // blend effect, have to first render shape as 626 // a whole, then blit that surface with global 627 // alpha to the destination) 628 const RendererCacheVector::iterator aCompositingSurface( 629 getCacheEntry( rDestinationCanvas ) ); 630 631 if( !aCompositingSurface->mpLastBitmapCanvas || 632 aCompositingSurface->mpLastBitmapCanvas->getSize() != aBmpSize ) 633 { 634 // create a bitmap of appropriate size 635 ::cppcanvas::BitmapSharedPtr pBitmap( 636 ::cppcanvas::BaseGfxFactory::getInstance().createAlphaBitmap( 637 rDestinationCanvas, 638 aBmpSize ) ); 639 640 ENSURE_OR_THROW(pBitmap, 641 "ViewShape::render(): Could not create compositing surface"); 642 643 aCompositingSurface->mpDestinationCanvas = rDestinationCanvas; 644 aCompositingSurface->mpLastBitmap = pBitmap; 645 aCompositingSurface->mpLastBitmapCanvas = pBitmap->getBitmapCanvas(); 646 } 647 648 // buffer aCompositingSurface iterator content 649 // - said one might get invalidated during 650 // draw() below. 651 ::cppcanvas::BitmapCanvasSharedPtr pBitmapCanvas( 652 aCompositingSurface->mpLastBitmapCanvas ); 653 654 ::cppcanvas::BitmapSharedPtr pBitmap( 655 aCompositingSurface->mpLastBitmap); 656 657 // setup bitmap canvas transformation - 658 // which happens to be the destination 659 // canvas transformation without any 660 // translational components. 661 // 662 // But then, the render transformation as 663 // calculated by getShapeTransformation() 664 // above outputs the shape at its real 665 // destination position. Thus, we have to 666 // offset the output back to the origin, 667 // for which we simply plug in the 668 // negative position of the left, top edge 669 // of the shape's bound rect in device 670 // pixel into aLinearTransform below. 671 ::basegfx::B2DHomMatrix aAdjustedCanvasTransform( aCanvasTransform ); 672 aAdjustedCanvasTransform.translate( -aTmpRect.getMinX(), 673 -aTmpRect.getMinY() ); 674 675 pBitmapCanvas->setTransformation( aAdjustedCanvasTransform ); 676 677 // TODO(P2): If no update flags, or only 678 // alpha_update is set, we can save us the 679 // rendering into the bitmap (uh, it's not 680 // _that_ easy - for a forced redraw, 681 // e.g. when ending an animation, we always 682 // get UPDATE_FORCE here). 683 684 // render into this bitmap 685 if( !draw( pBitmapCanvas, 686 rMtf, 687 pAttr, 688 aTransform, 689 !aClip ? NULL : &(*aClip), 690 rSubsets ) ) 691 { 692 return false; 693 } 694 695 // render bitmap to screen, with given global 696 // alpha. Since the bitmap already contains 697 // pixel-equivalent output, we have to use the 698 // inverse view transformation, adjusted with 699 // the final shape output position (note: 700 // cannot simply change the view 701 // transformation here, as that would affect a 702 // possibly set clip!) 703 ::basegfx::B2DHomMatrix aBitmapTransform( aCanvasTransform ); 704 OSL_ENSURE( aBitmapTransform.isInvertible(), 705 "ViewShape::render(): View transformation is singular!" ); 706 707 aBitmapTransform.invert(); 708 709 const basegfx::B2DHomMatrix aTranslation(basegfx::tools::createTranslateB2DHomMatrix( 710 aTmpRect.getMinX(), aTmpRect.getMinY())); 711 712 aBitmapTransform = aBitmapTransform * aTranslation; 713 pBitmap->setTransformation( aBitmapTransform ); 714 715 // finally, render bitmap alpha-modulated 716 pBitmap->drawAlphaModulated( nAlpha ); 717 718 return true; 719 } 720 } 721 } 722 723 // retrieve shape transformation, _with_ shape translation 724 // to actual page position. 725 const ::basegfx::B2DHomMatrix aTransform( 726 getShapeTransformation( rBounds, 727 pAttr ) ); 728 729 return draw( rDestinationCanvas, 730 rMtf, 731 pAttr, 732 aTransform, 733 !aClip ? NULL : &(*aClip), 734 rSubsets ); 735 } 736 737 738 // ------------------------------------------------------------------------------------- 739 740 ViewShape::ViewShape( const ViewLayerSharedPtr& rViewLayer ) : 741 mpViewLayer( rViewLayer ), 742 maRenderers(), 743 mpSprite(), 744 mbAnimationMode( false ), 745 mbForceUpdate( true ) 746 { 747 ENSURE_OR_THROW( mpViewLayer, "ViewShape::ViewShape(): Invalid View" ); 748 } 749 750 ViewLayerSharedPtr ViewShape::getViewLayer() const 751 { 752 return mpViewLayer; 753 } 754 755 ViewShape::RendererCacheVector::iterator ViewShape::getCacheEntry( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas ) const 756 { 757 // lookup destination canvas - is there already a renderer 758 // created for that target? 759 RendererCacheVector::iterator aIter; 760 const RendererCacheVector::iterator aEnd( maRenderers.end() ); 761 762 // already there? 763 if( (aIter=::std::find_if( maRenderers.begin(), 764 aEnd, 765 ::boost::bind( 766 ::std::equal_to< ::cppcanvas::CanvasSharedPtr >(), 767 ::boost::cref( rDestinationCanvas ), 768 ::boost::bind( 769 &RendererCacheEntry::getDestinationCanvas, 770 _1 ) ) ) ) == aEnd ) 771 { 772 if( maRenderers.size() >= MAX_RENDER_CACHE_ENTRIES ) 773 { 774 // cache size exceeded - prune entries. For now, 775 // simply remove the first one, which of course 776 // breaks for more complex access schemes. But in 777 // general, this leads to most recently used 778 // entries to reside at the end of the vector. 779 maRenderers.erase( maRenderers.begin() ); 780 781 // ATTENTION: after this, both aIter and aEnd are 782 // invalid! 783 } 784 785 // not yet in cache - add default-constructed cache 786 // entry, to have something to return 787 maRenderers.push_back( RendererCacheEntry() ); 788 aIter = maRenderers.end()-1; 789 } 790 791 return aIter; 792 } 793 794 ::cppcanvas::RendererSharedPtr ViewShape::getRenderer( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas, 795 const GDIMetaFileSharedPtr& rMtf, 796 const ShapeAttributeLayerSharedPtr& rAttr ) const 797 { 798 // lookup destination canvas - is there already a renderer 799 // created for that target? 800 const RendererCacheVector::iterator aIter( 801 getCacheEntry( rDestinationCanvas ) ); 802 803 // now we have a valid entry, either way. call prefetch() 804 // on it, nevertheless - maybe the metafile changed, and 805 // the renderer still needs an update (prefetch() will 806 // detect that) 807 if( prefetch( *aIter, 808 rDestinationCanvas, 809 rMtf, 810 rAttr ) ) 811 { 812 return aIter->mpRenderer; 813 } 814 else 815 { 816 // prefetch failed - renderer is invalid 817 return ::cppcanvas::RendererSharedPtr(); 818 } 819 } 820 821 void ViewShape::invalidateRenderer() const 822 { 823 // simply clear the cache. Subsequent getRenderer() calls 824 // will regenerate the Renderers. 825 maRenderers.clear(); 826 } 827 828 ::basegfx::B2DSize ViewShape::getAntialiasingBorder() const 829 { 830 ENSURE_OR_THROW( mpViewLayer->getCanvas(), 831 "ViewShape::getAntialiasingBorder(): Invalid ViewLayer canvas" ); 832 833 const ::basegfx::B2DHomMatrix& rViewTransform( 834 mpViewLayer->getTransformation() ); 835 836 // TODO(F1): As a quick shortcut (did not want to invert 837 // whole matrix here), taking only scale components of 838 // view transformation matrix. This will be wrong when 839 // e.g. shearing is involved. 840 const double nXBorder( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE / rViewTransform.get(0,0) ); 841 const double nYBorder( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE / rViewTransform.get(1,1) ); 842 843 return ::basegfx::B2DSize( nXBorder, 844 nYBorder ); 845 } 846 847 bool ViewShape::enterAnimationMode() 848 { 849 mbForceUpdate = true; 850 mbAnimationMode = true; 851 852 return true; 853 } 854 855 void ViewShape::leaveAnimationMode() 856 { 857 mpSprite.reset(); 858 mbAnimationMode = false; 859 mbForceUpdate = true; 860 } 861 862 bool ViewShape::update( const GDIMetaFileSharedPtr& rMtf, 863 const RenderArgs& rArgs, 864 int nUpdateFlags, 865 bool bIsVisible ) const 866 { 867 RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::update()" ); 868 ENSURE_OR_RETURN_FALSE( mpViewLayer->getCanvas(), "ViewShape::update(): Invalid layer canvas" ); 869 870 // Shall we render to a sprite, or to a plain canvas? 871 if( isBackgroundDetached() ) 872 return renderSprite( mpViewLayer, 873 rMtf, 874 rArgs.maOrigBounds, 875 rArgs.maBounds, 876 rArgs.maUnitBounds, 877 nUpdateFlags, 878 rArgs.mrAttr, 879 rArgs.mrSubsets, 880 rArgs.mnShapePriority, 881 bIsVisible ); 882 else 883 return render( mpViewLayer->getCanvas(), 884 rMtf, 885 rArgs.maBounds, 886 rArgs.maUpdateBounds, 887 nUpdateFlags, 888 rArgs.mrAttr, 889 rArgs.mrSubsets, 890 bIsVisible ); 891 } 892 893 } 894 } 895