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