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