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