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
SlideChangeBase(boost::optional<SlideSharedPtr> const & leavingSlide,const SlideSharedPtr & pEnteringSlide,const SoundPlayerSharedPtr & pSoundPlayer,const UnoViewContainer & rViewContainer,ScreenUpdater & rScreenUpdater,EventMultiplexer & rEventMultiplexer,bool bCreateLeavingSprites,bool bCreateEnteringSprites)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
getLeavingBitmap(const ViewEntry & rViewEntry) const73 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
getEnteringBitmap(const ViewEntry & rViewEntry) const82 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
createBitmap(const UnoViewSharedPtr & rView,const boost::optional<SlideSharedPtr> & rSlide) const91 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
getEnteringSlideSizePixel(const UnoViewSharedPtr & pView) const149 ::basegfx::B2ISize SlideChangeBase::getEnteringSlideSizePixel( const UnoViewSharedPtr& pView ) const
150 {
151 return getSlideSizePixel( mpEnteringSlide->getSlideSize(),
152 pView );
153 }
154
getLeavingSlideSizePixel(const UnoViewSharedPtr & pView) const155 ::basegfx::B2ISize SlideChangeBase::getLeavingSlideSizePixel( const UnoViewSharedPtr& pView ) const
156 {
157 return getSlideSizePixel( (*maLeavingSlide)->getSlideSize(),
158 pView );
159 }
160
161
renderBitmap(SlideBitmapSharedPtr const & pSlideBitmap,cppcanvas::CanvasSharedPtr const & pCanvas)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
prefetch(const AnimatableShapeSharedPtr &,const ShapeAttributeLayerSharedPtr &)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
start(const AnimatableShapeSharedPtr & rShape,const ShapeAttributeLayerSharedPtr & rLayer)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
end()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
operator ()(double nValue)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
performIn(const cppcanvas::CustomSpriteSharedPtr &,const ViewEntry &,const cppcanvas::CanvasSharedPtr &,double)372 void SlideChangeBase::performIn(
373 const cppcanvas::CustomSpriteSharedPtr& /*rSprite*/,
374 const ViewEntry& /*rViewEntry*/,
375 const cppcanvas::CanvasSharedPtr& /*rDestinationCanvas*/,
376 double /*t*/ )
377 {
378 }
379
performOut(const cppcanvas::CustomSpriteSharedPtr &,const ViewEntry &,const cppcanvas::CanvasSharedPtr &,double)380 void SlideChangeBase::performOut(
381 const cppcanvas::CustomSpriteSharedPtr& /*rSprite*/,
382 const ViewEntry& /*rViewEntry*/,
383 const cppcanvas::CanvasSharedPtr& /*rDestinationCanvas*/,
384 double /*t*/ )
385 {
386 }
387
getUnderlyingValue() const388 double SlideChangeBase::getUnderlyingValue() const
389 {
390 return 0.0; // though this should be used in concert with
391 // ActivitiesFactory::createSimpleActivity, better
392 // explicitly name our start value.
393 // Permissible range for operator() above is [0,1]
394 }
395
viewAdded(const UnoViewSharedPtr & rView)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
viewRemoved(const UnoViewSharedPtr & rView)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
viewChanged(const UnoViewSharedPtr & rView)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
viewsChanged()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
createSprite(UnoViewSharedPtr const & pView,basegfx::B2DSize const & rSpriteSize,double nPrio) const475 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
addSprites(ViewEntry & rEntry)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
clearViewEntry(ViewEntry & rEntry)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