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