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 #include <canvas/debug.hxx> 32 #include <tools/diagnose_ex.h> 33 #include <canvas/canvastools.hxx> 34 #include <basegfx/numeric/ftools.hxx> 35 #include <basegfx/polygon/b2dpolygontools.hxx> 36 #include <basegfx/polygon/b2dpolypolygontools.hxx> 37 #include <basegfx/matrix/b2dhommatrixtools.hxx> 38 #include <cppcanvas/basegfxfactory.hxx> 39 40 #include "slidechangebase.hxx" 41 #include "tools.hxx" 42 43 #include <boost/bind.hpp> 44 #include <algorithm> 45 46 using namespace com::sun::star; 47 48 namespace slideshow { 49 namespace internal { 50 51 SlideChangeBase::SlideChangeBase( boost::optional<SlideSharedPtr> const & leavingSlide, 52 const SlideSharedPtr& pEnteringSlide, 53 const SoundPlayerSharedPtr& pSoundPlayer, 54 const UnoViewContainer& rViewContainer, 55 ScreenUpdater& rScreenUpdater, 56 EventMultiplexer& rEventMultiplexer, 57 bool bCreateLeavingSprites, 58 bool bCreateEnteringSprites ) : 59 mpSoundPlayer( pSoundPlayer ), 60 mrEventMultiplexer(rEventMultiplexer), 61 mrScreenUpdater(rScreenUpdater), 62 maLeavingSlide( leavingSlide ), 63 mpEnteringSlide( pEnteringSlide ), 64 maViewData(), 65 mrViewContainer(rViewContainer), 66 mbCreateLeavingSprites(bCreateLeavingSprites), 67 mbCreateEnteringSprites(bCreateEnteringSprites), 68 mbSpritesVisible(false), 69 mbFinished(false), 70 mbPrefetched(false) 71 { 72 ENSURE_OR_THROW( 73 pEnteringSlide, 74 "SlideChangeBase::SlideChangeBase(): Invalid entering slide!" ); 75 } 76 77 SlideBitmapSharedPtr SlideChangeBase::getLeavingBitmap( const ViewEntry& rViewEntry ) const 78 { 79 if( !rViewEntry.mpLeavingBitmap ) 80 rViewEntry.mpLeavingBitmap = createBitmap(rViewEntry.mpView, 81 maLeavingSlide); 82 83 return rViewEntry.mpLeavingBitmap; 84 } 85 86 SlideBitmapSharedPtr SlideChangeBase::getEnteringBitmap( const ViewEntry& rViewEntry ) const 87 { 88 if( !rViewEntry.mpEnteringBitmap ) 89 rViewEntry.mpEnteringBitmap = createBitmap( rViewEntry.mpView, 90 boost::optional<SlideSharedPtr>(mpEnteringSlide) ); 91 92 return rViewEntry.mpEnteringBitmap; 93 } 94 95 SlideBitmapSharedPtr SlideChangeBase::createBitmap( const UnoViewSharedPtr& rView, 96 const boost::optional<SlideSharedPtr>& rSlide ) const 97 { 98 SlideBitmapSharedPtr pRet; 99 if( !rSlide ) 100 return pRet; 101 102 SlideSharedPtr const & pSlide = *rSlide; 103 if( !pSlide ) 104 { 105 // TODO(P3): No need to generate a bitmap here. This only made 106 // the code more uniform. Faster would be to simply clear the 107 // sprite to black. 108 109 // create empty, black-filled bitmap 110 const basegfx::B2ISize slideSizePixel( 111 getSlideSizePixel( mpEnteringSlide->getSlideSize(), 112 rView )); 113 114 cppcanvas::CanvasSharedPtr pCanvas( rView->getCanvas() ); 115 116 // create a bitmap of appropriate size 117 cppcanvas::BitmapSharedPtr pBitmap( 118 cppcanvas::BaseGfxFactory::getInstance().createBitmap( 119 pCanvas, 120 slideSizePixel ) ); 121 122 ENSURE_OR_THROW( 123 pBitmap, 124 "SlideChangeBase::createBitmap(): Cannot create page bitmap" ); 125 126 cppcanvas::BitmapCanvasSharedPtr pBitmapCanvas( 127 pBitmap->getBitmapCanvas() ); 128 129 ENSURE_OR_THROW( pBitmapCanvas, 130 "SlideChangeBase::createBitmap(): " 131 "Cannot create page bitmap canvas" ); 132 133 // set transformation to identitiy (->device pixel) 134 pBitmapCanvas->setTransformation( ::basegfx::B2DHomMatrix() ); 135 136 // clear bitmap to black 137 fillRect( pBitmapCanvas, 138 ::basegfx::B2DRectangle( 0.0, 0.0, 139 slideSizePixel.getX(), 140 slideSizePixel.getY() ), 141 0x000000FFU ); 142 143 pRet.reset( new SlideBitmap( pBitmap )); 144 } 145 else 146 { 147 pRet = pSlide->getCurrentSlideBitmap( rView ); 148 } 149 150 return pRet; 151 } 152 153 ::basegfx::B2ISize SlideChangeBase::getEnteringSlideSizePixel( const UnoViewSharedPtr& pView ) const 154 { 155 return getSlideSizePixel( mpEnteringSlide->getSlideSize(), 156 pView ); 157 } 158 159 ::basegfx::B2ISize SlideChangeBase::getLeavingSlideSizePixel( const UnoViewSharedPtr& pView ) const 160 { 161 return getSlideSizePixel( (*maLeavingSlide)->getSlideSize(), 162 pView ); 163 } 164 165 166 void SlideChangeBase::renderBitmap( 167 SlideBitmapSharedPtr const & pSlideBitmap, 168 cppcanvas::CanvasSharedPtr const & pCanvas ) 169 { 170 if( pSlideBitmap && pCanvas ) 171 { 172 // need to render without any transformation (we 173 // assume device units): 174 const basegfx::B2DHomMatrix viewTransform( 175 pCanvas->getTransformation() ); 176 const basegfx::B2DPoint pageOrigin( 177 viewTransform * basegfx::B2DPoint() ); 178 const cppcanvas::CanvasSharedPtr pDevicePixelCanvas( 179 pCanvas->clone() ); 180 181 // render at output position, don't modify bitmap object (no move!): 182 const basegfx::B2DHomMatrix transform(basegfx::tools::createTranslateB2DHomMatrix( 183 pageOrigin.getX(), pageOrigin.getY())); 184 185 pDevicePixelCanvas->setTransformation( transform ); 186 pSlideBitmap->draw( pDevicePixelCanvas ); 187 } 188 } 189 190 void SlideChangeBase::prefetch( const AnimatableShapeSharedPtr&, 191 const ShapeAttributeLayerSharedPtr& ) 192 { 193 // we're a one-shot activity, and already finished 194 if( mbFinished || mbPrefetched ) 195 return; 196 197 // register ourselves for view change events 198 mrEventMultiplexer.addViewHandler( shared_from_this() ); 199 200 // init views and create slide bitmaps 201 std::for_each( mrViewContainer.begin(), 202 mrViewContainer.end(), 203 boost::bind( &SlideChangeBase::viewAdded, 204 this, 205 _1 )); 206 207 mbPrefetched = true; 208 } 209 210 void SlideChangeBase::start( const AnimatableShapeSharedPtr& rShape, 211 const ShapeAttributeLayerSharedPtr& rLayer ) 212 { 213 // we're a one-shot activity, and already finished 214 if( mbFinished ) 215 return; 216 217 prefetch(rShape,rLayer); // no-op, if already done 218 219 // start accompanying sound effect, if any 220 if( mpSoundPlayer ) 221 { 222 mpSoundPlayer->startPlayback(); 223 // xxx todo: for now, presentation.cxx takes care about the slide 224 // #i50492# transition sound object, so just release it here 225 mpSoundPlayer.reset(); 226 } 227 } 228 229 void SlideChangeBase::end() 230 { 231 // we're a one-shot activity, and already finished 232 if( mbFinished ) 233 return; 234 235 try 236 { 237 // draw fully entered bitmap: 238 ViewsVecT::const_iterator aCurr( beginViews() ); 239 const ViewsVecT::const_iterator aEnd( endViews() ); 240 while( aCurr != aEnd ) 241 { 242 // fully clear view content to background color 243 aCurr->mpView->clearAll(); 244 245 const SlideBitmapSharedPtr pSlideBitmap( getEnteringBitmap( *aCurr )); 246 pSlideBitmap->clip( basegfx::B2DPolyPolygon() /* no clipping */ ); 247 renderBitmap( pSlideBitmap, 248 aCurr->mpView->getCanvas() ); 249 250 ++aCurr; 251 } 252 } 253 catch( uno::Exception& ) 254 { 255 // make sure releasing below happens 256 } 257 258 // swap changes to screen 259 mrScreenUpdater.notifyUpdate(); 260 261 // make object dysfunctional 262 mbFinished = true; 263 ViewsVecT().swap(maViewData); 264 maLeavingSlide.reset(); 265 mpEnteringSlide.reset(); 266 267 // sprites have been binned above 268 mbSpritesVisible = false; 269 270 // remove also from event multiplexer, we're dead anyway 271 mrEventMultiplexer.removeViewHandler( shared_from_this() ); 272 } 273 274 bool SlideChangeBase::operator()( double nValue ) 275 { 276 if( mbFinished ) 277 return false; 278 279 const std::size_t nEntries( maViewData.size() ); 280 bool bSpritesVisible( mbSpritesVisible ); 281 282 for( ::std::size_t i=0; i<nEntries; ++i ) 283 { 284 // calc sprite offsets. The enter/leaving bitmaps are only 285 // as large as the actual slides. For scaled-down 286 // presentations, we have to move the left, top edge of 287 // those bitmaps to the actual position, governed by the 288 // given view transform. The aSpritePosPixel local 289 // variable is already in device coordinate space 290 // (i.e. pixel). 291 292 ViewEntry& rViewEntry( maViewData[i] ); 293 const ::cppcanvas::CanvasSharedPtr& rCanvas( rViewEntry.mpView->getCanvas() ); 294 ::cppcanvas::CustomSpriteSharedPtr& rInSprite( rViewEntry.mpInSprite ); 295 ::cppcanvas::CustomSpriteSharedPtr& rOutSprite( rViewEntry.mpOutSprite ); 296 297 // TODO(F2): Properly respect clip here. 298 299 // Might have to be transformed, too. 300 const ::basegfx::B2DHomMatrix aViewTransform( 301 rViewEntry.mpView->getTransformation() ); 302 const ::basegfx::B2DPoint aSpritePosPixel( 303 aViewTransform * ::basegfx::B2DPoint() ); 304 305 // move sprite to final output position, in 306 // device coordinates 307 if( rOutSprite ) 308 rOutSprite->movePixel( aSpritePosPixel ); 309 if( rInSprite ) 310 rInSprite->movePixel( aSpritePosPixel ); 311 312 if( !mbSpritesVisible ) 313 { 314 if( rOutSprite ) 315 { 316 // only render once: clipping is done 317 // exclusively with the sprite 318 const ::cppcanvas::CanvasSharedPtr pOutContentCanvas( 319 rOutSprite->getContentCanvas() ); 320 if( pOutContentCanvas) 321 { 322 // TODO(Q2): Use basegfx bitmaps here 323 324 // TODO(F1): SlideBitmap is not fully portable 325 // between different canvases! 326 327 // render the content 328 OSL_ASSERT( getLeavingBitmap( rViewEntry ) ); 329 if( getLeavingBitmap( rViewEntry ) ) 330 getLeavingBitmap( rViewEntry )->draw( pOutContentCanvas ); 331 } 332 } 333 334 if( rInSprite ) 335 { 336 // only render once: clipping is done 337 // exclusively with the sprite 338 const ::cppcanvas::CanvasSharedPtr pInContentCanvas( 339 rInSprite->getContentCanvas() ); 340 if( pInContentCanvas ) 341 { 342 // TODO(Q2): Use basegfx bitmaps here 343 344 // TODO(F1): SlideBitmap is not fully portable 345 // between different canvases! 346 347 // render the content 348 getEnteringBitmap( rViewEntry )->draw( pInContentCanvas ); 349 } 350 } 351 } 352 353 if( rOutSprite ) 354 performOut( rOutSprite, rViewEntry, rCanvas, nValue ); 355 if( rInSprite ) 356 performIn( rInSprite, rViewEntry, rCanvas, nValue ); 357 358 // finishing deeds for first run. 359 if( !mbSpritesVisible) 360 { 361 // enable sprites: 362 if( rOutSprite ) 363 rOutSprite->show(); 364 if( rInSprite ) 365 rInSprite->show(); 366 bSpritesVisible = true; 367 } 368 } // for_each( sprite ) 369 370 mbSpritesVisible = bSpritesVisible; 371 mrScreenUpdater.notifyUpdate(); 372 373 return true; 374 } 375 376 void SlideChangeBase::performIn( 377 const cppcanvas::CustomSpriteSharedPtr& /*rSprite*/, 378 const ViewEntry& /*rViewEntry*/, 379 const cppcanvas::CanvasSharedPtr& /*rDestinationCanvas*/, 380 double /*t*/ ) 381 { 382 } 383 384 void SlideChangeBase::performOut( 385 const cppcanvas::CustomSpriteSharedPtr& /*rSprite*/, 386 const ViewEntry& /*rViewEntry*/, 387 const cppcanvas::CanvasSharedPtr& /*rDestinationCanvas*/, 388 double /*t*/ ) 389 { 390 } 391 392 double SlideChangeBase::getUnderlyingValue() const 393 { 394 return 0.0; // though this should be used in concert with 395 // ActivitiesFactory::createSimpleActivity, better 396 // explicitely name our start value. 397 // Permissible range for operator() above is [0,1] 398 } 399 400 void SlideChangeBase::viewAdded( const UnoViewSharedPtr& rView ) 401 { 402 // we're a one-shot activity, and already finished 403 if( mbFinished ) 404 return; 405 406 maViewData.push_back( ViewEntry(rView) ); 407 408 ViewEntry& rEntry( maViewData.back() ); 409 getEnteringBitmap( rEntry ); 410 getLeavingBitmap( rEntry ); 411 addSprites( rEntry ); 412 } 413 414 void SlideChangeBase::viewRemoved( const UnoViewSharedPtr& rView ) 415 { 416 // we're a one-shot activity, and already finished 417 if( mbFinished ) 418 return; 419 420 // erase corresponding entry from maViewData 421 maViewData.erase( 422 std::remove_if( 423 maViewData.begin(), 424 maViewData.end(), 425 boost::bind( 426 std::equal_to<UnoViewSharedPtr>(), 427 rView, 428 // select view: 429 boost::bind( &ViewEntry::getView, _1 ))), 430 maViewData.end() ); 431 } 432 433 void SlideChangeBase::viewChanged( const UnoViewSharedPtr& rView ) 434 { 435 // we're a one-shot activity, and already finished 436 if( mbFinished ) 437 return; 438 439 // find entry corresponding to modified view 440 ViewsVecT::iterator aModifiedEntry( 441 std::find_if( 442 maViewData.begin(), 443 maViewData.end(), 444 boost::bind( 445 std::equal_to<UnoViewSharedPtr>(), 446 rView, 447 // select view: 448 boost::bind( &ViewEntry::getView, _1 ) ))); 449 450 OSL_ASSERT( aModifiedEntry != maViewData.end() ); 451 if( aModifiedEntry == maViewData.end() ) 452 return; 453 454 // clear stale info (both bitmaps and sprites prolly need a 455 // resize) 456 clearViewEntry( *aModifiedEntry ); 457 addSprites( *aModifiedEntry ); 458 } 459 460 void SlideChangeBase::viewsChanged() 461 { 462 // we're a one-shot activity, and already finished 463 if( mbFinished ) 464 return; 465 466 ViewsVecT::iterator aIter( maViewData.begin() ); 467 ViewsVecT::iterator const aEnd ( maViewData.end() ); 468 while( aIter != aEnd ) 469 { 470 // clear stale info (both bitmaps and sprites prolly need a 471 // resize) 472 clearViewEntry( *aIter ); 473 addSprites( *aIter ); 474 475 ++aIter; 476 } 477 } 478 479 cppcanvas::CustomSpriteSharedPtr SlideChangeBase::createSprite( 480 UnoViewSharedPtr const & pView, 481 basegfx::B2DSize const & rSpriteSize, 482 double nPrio ) const 483 { 484 // TODO(P2): change to bitmapsprite once that's working 485 const cppcanvas::CustomSpriteSharedPtr pSprite( 486 pView->createSprite( rSpriteSize, 487 nPrio )); 488 489 // alpha default is 0.0, which seems to be 490 // a bad idea when viewing content... 491 pSprite->setAlpha( 1.0 ); 492 if (mbSpritesVisible) 493 pSprite->show(); 494 495 return pSprite; 496 } 497 498 void SlideChangeBase::addSprites( ViewEntry& rEntry ) 499 { 500 if( mbCreateLeavingSprites && maLeavingSlide ) 501 { 502 // create leaving sprite: 503 const basegfx::B2ISize leavingSlideSizePixel( 504 getLeavingBitmap( rEntry )->getSize() ); 505 506 rEntry.mpOutSprite = createSprite( rEntry.mpView, 507 leavingSlideSizePixel, 508 100 ); 509 } 510 511 if( mbCreateEnteringSprites ) 512 { 513 // create entering sprite: 514 const basegfx::B2ISize enteringSlideSizePixel( 515 getSlideSizePixel( mpEnteringSlide->getSlideSize(), 516 rEntry.mpView )); 517 518 rEntry.mpInSprite = createSprite( rEntry.mpView, 519 enteringSlideSizePixel, 520 101 ); 521 } 522 } 523 524 void SlideChangeBase::clearViewEntry( ViewEntry& rEntry ) 525 { 526 // clear stale info (both bitmaps and sprites prolly need a 527 // resize) 528 rEntry.mpEnteringBitmap.reset(); 529 rEntry.mpLeavingBitmap.reset(); 530 rEntry.mpInSprite.reset(); 531 rEntry.mpOutSprite.reset(); 532 } 533 534 } // namespace internal 535 } // namespace presentation 536