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