1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <comphelper/diagnose_ex.hxx>
22 #include <basegfx/matrix/b2dhommatrixtools.hxx>
23 #include <cppcanvas/basegfxfactory.hxx>
24 #include <cppcanvas/customsprite.hxx>
26 #include "slidechangebase.hxx"
32 using namespace com::sun::star
;
34 namespace slideshow::internal
{
36 SlideChangeBase::SlideChangeBase( std::optional
<SlideSharedPtr
> leavingSlide
,
37 const SlideSharedPtr
& pEnteringSlide
,
38 SoundPlayerSharedPtr pSoundPlayer
,
39 const UnoViewContainer
& rViewContainer
,
40 ScreenUpdater
& rScreenUpdater
,
41 EventMultiplexer
& rEventMultiplexer
,
42 bool bCreateLeavingSprites
,
43 bool bCreateEnteringSprites
) :
44 mpSoundPlayer(std::move( pSoundPlayer
)),
45 mrEventMultiplexer(rEventMultiplexer
),
46 mrScreenUpdater(rScreenUpdater
),
47 maLeavingSlide(std::move( leavingSlide
)),
48 mpEnteringSlide( pEnteringSlide
),
50 mrViewContainer(rViewContainer
),
51 mbCreateLeavingSprites(bCreateLeavingSprites
),
52 mbCreateEnteringSprites(bCreateEnteringSprites
),
53 mbSpritesVisible(false),
59 "SlideChangeBase::SlideChangeBase(): Invalid entering slide!" );
62 SlideBitmapSharedPtr
SlideChangeBase::getLeavingBitmap( const ViewEntry
& rViewEntry
) const
64 if( !rViewEntry
.mpLeavingBitmap
)
65 rViewEntry
.mpLeavingBitmap
= createBitmap(rViewEntry
.mpView
,
68 return rViewEntry
.mpLeavingBitmap
;
71 SlideBitmapSharedPtr
SlideChangeBase::getEnteringBitmap( const ViewEntry
& rViewEntry
) const
73 if( !rViewEntry
.mpEnteringBitmap
)
74 rViewEntry
.mpEnteringBitmap
= createBitmap( rViewEntry
.mpView
,
75 std::optional
<SlideSharedPtr
>(mpEnteringSlide
) );
77 return rViewEntry
.mpEnteringBitmap
;
80 SlideBitmapSharedPtr
SlideChangeBase::createBitmap( const UnoViewSharedPtr
& rView
,
81 const std::optional
<SlideSharedPtr
>& rSlide
) const
83 SlideBitmapSharedPtr pRet
;
87 SlideSharedPtr
const & pSlide
= *rSlide
;
90 // TODO(P3): No need to generate a bitmap here. This only made
91 // the code more uniform. Faster would be to simply clear the
94 // create empty, black-filled bitmap
95 const basegfx::B2ISize
slideSizePixel(
96 getSlideSizePixel( basegfx::B2DVector( mpEnteringSlide
->getSlideSize() ),
99 cppcanvas::CanvasSharedPtr
pCanvas( rView
->getCanvas() );
101 // create a bitmap of appropriate size
102 cppcanvas::BitmapSharedPtr
pBitmap(
103 cppcanvas::BaseGfxFactory::createBitmap(
109 "SlideChangeBase::createBitmap(): Cannot create page bitmap" );
111 cppcanvas::BitmapCanvasSharedPtr
pBitmapCanvas(
112 pBitmap
->getBitmapCanvas() );
114 ENSURE_OR_THROW( pBitmapCanvas
,
115 "SlideChangeBase::createBitmap(): "
116 "Cannot create page bitmap canvas" );
118 // set transformation to identity (->device pixel)
119 pBitmapCanvas
->setTransformation( ::basegfx::B2DHomMatrix() );
121 // clear bitmap to black
122 fillRect( pBitmapCanvas
,
123 ::basegfx::B2DRectangle( 0.0, 0.0,
124 slideSizePixel
.getX(),
125 slideSizePixel
.getY() ),
128 pRet
= std::make_shared
<SlideBitmap
>( pBitmap
);
132 pRet
= pSlide
->getCurrentSlideBitmap( rView
);
138 ::basegfx::B2ISize
SlideChangeBase::getEnteringSlideSizePixel( const UnoViewSharedPtr
& pView
) const
140 return getSlideSizePixel( basegfx::B2DVector(mpEnteringSlide
->getSlideSize().getX(), mpEnteringSlide
->getSlideSize().getY()),
144 void SlideChangeBase::renderBitmap(
145 SlideBitmapSharedPtr
const & pSlideBitmap
,
146 cppcanvas::CanvasSharedPtr
const & pCanvas
)
148 if( !(pSlideBitmap
&& pCanvas
) )
151 // need to render without any transformation (we
152 // assume device units):
153 const basegfx::B2DHomMatrix
viewTransform(
154 pCanvas
->getTransformation() );
155 const basegfx::B2DPoint
pageOrigin(
156 viewTransform
* basegfx::B2DPoint() );
157 const cppcanvas::CanvasSharedPtr
pDevicePixelCanvas(
160 // render at output position, don't modify bitmap object (no move!):
161 const basegfx::B2DHomMatrix
transform(basegfx::utils::createTranslateB2DHomMatrix(
162 pageOrigin
.getX(), pageOrigin
.getY()));
164 pDevicePixelCanvas
->setTransformation( transform
);
165 pSlideBitmap
->draw( pDevicePixelCanvas
);
168 void SlideChangeBase::prefetch()
170 // we're a one-shot activity, and already finished
171 if( mbFinished
|| mbPrefetched
)
174 // register ourselves for view change events
175 mrEventMultiplexer
.addViewHandler( std::dynamic_pointer_cast
<ViewEventHandler
>(shared_from_this()) );
177 // init views and create slide bitmaps
178 for( const auto& pView
: mrViewContainer
)
184 void SlideChangeBase::start( const AnimatableShapeSharedPtr
& /*rShape*/,
185 const ShapeAttributeLayerSharedPtr
& /*rLayer*/ )
187 // we're a one-shot activity, and already finished
191 prefetch(); // no-op, if already done
193 // get the subclasses a chance to do any specific initialization before run
194 for ( ViewsVecT::const_iterator
aCurr( beginViews() ), aEnd( endViews() ); aCurr
!= aEnd
; ++aCurr
)
195 prepareForRun( *aCurr
, aCurr
->mpView
->getCanvas() );
197 // start accompanying sound effect, if any
200 mpSoundPlayer
->startPlayback();
201 // xxx todo: for now, presentation.cxx takes care about the slide
202 // #i50492# transition sound object, so just release it here
203 mpSoundPlayer
.reset();
207 void SlideChangeBase::end()
209 // we're a one-shot activity, and already finished
215 // draw fully entered bitmap:
216 ViewsVecT::const_iterator
aCurr( beginViews() );
217 const ViewsVecT::const_iterator
aEnd( endViews() );
218 while( aCurr
!= aEnd
)
220 // fully clear view content to background color
221 aCurr
->mpView
->clearAll();
223 const SlideBitmapSharedPtr
pSlideBitmap( getEnteringBitmap( *aCurr
));
224 pSlideBitmap
->clip( basegfx::B2DPolyPolygon() /* no clipping */ );
225 aCurr
->mpView
->clearAll();
226 renderBitmap( pSlideBitmap
,
227 aCurr
->mpView
->getCanvas() );
232 catch( uno::Exception
& )
234 // make sure releasing below happens
237 // swap changes to screen
238 mrScreenUpdater
.notifyUpdate();
240 // make object dysfunctional
242 ViewsVecT().swap(maViewData
);
243 maLeavingSlide
.reset();
244 mpEnteringSlide
.reset();
246 // sprites have been binned above
247 mbSpritesVisible
= false;
249 // remove also from event multiplexer, we're dead anyway
250 mrEventMultiplexer
.removeViewHandler( std::dynamic_pointer_cast
<ViewEventHandler
>(shared_from_this()) );
253 bool SlideChangeBase::operator()( double nValue
)
258 const std::size_t nEntries( maViewData
.size() );
259 bool bSpritesVisible( mbSpritesVisible
);
261 for( ::std::size_t i
=0; i
<nEntries
; ++i
)
263 // calc sprite offsets. The enter/leaving bitmaps are only
264 // as large as the actual slides. For scaled-down
265 // presentations, we have to move the left, top edge of
266 // those bitmaps to the actual position, governed by the
267 // given view transform. The aSpritePosPixel local
268 // variable is already in device coordinate space
271 ViewEntry
& rViewEntry( maViewData
[i
] );
272 const ::cppcanvas::CanvasSharedPtr
& rCanvas( rViewEntry
.mpView
->getCanvas() );
273 ::cppcanvas::CustomSpriteSharedPtr
& rInSprite( rViewEntry
.mpInSprite
);
274 ::cppcanvas::CustomSpriteSharedPtr
& rOutSprite( rViewEntry
.mpOutSprite
);
276 // TODO(F2): Properly respect clip here.
278 // Might have to be transformed, too.
279 const ::basegfx::B2DHomMatrix
aViewTransform(
280 rViewEntry
.mpView
->getTransformation() );
281 const ::basegfx::B2DPoint
aSpritePosPixel(
282 aViewTransform
* ::basegfx::B2DPoint() );
284 // move sprite to final output position, in
285 // device coordinates
287 rOutSprite
->movePixel( aSpritePosPixel
);
289 rInSprite
->movePixel( aSpritePosPixel
);
291 if( !mbSpritesVisible
)
295 // only render once: clipping is done
296 // exclusively with the sprite
297 const ::cppcanvas::CanvasSharedPtr
pOutContentCanvas(
298 rOutSprite
->getContentCanvas() );
299 if( pOutContentCanvas
)
301 // TODO(Q2): Use basegfx bitmaps here
303 // TODO(F1): SlideBitmap is not fully portable
304 // between different canvases!
306 // render the content
307 OSL_ASSERT( getLeavingBitmap( rViewEntry
) );
308 if( getLeavingBitmap( rViewEntry
) )
309 getLeavingBitmap( rViewEntry
)->draw( pOutContentCanvas
);
315 // only render once: clipping is done
316 // exclusively with the sprite
317 const ::cppcanvas::CanvasSharedPtr
pInContentCanvas(
318 rInSprite
->getContentCanvas() );
319 if( pInContentCanvas
)
321 // TODO(Q2): Use basegfx bitmaps here
323 // TODO(F1): SlideBitmap is not fully portable
324 // between different canvases!
326 // render the content
327 getEnteringBitmap( rViewEntry
)->draw( pInContentCanvas
);
333 performOut( rOutSprite
, rViewEntry
, rCanvas
, nValue
);
335 performIn( rInSprite
, rViewEntry
, rCanvas
, nValue
);
337 // finishing deeds for first run.
338 if( !mbSpritesVisible
)
345 bSpritesVisible
= true;
347 } // for_each( sprite )
349 mbSpritesVisible
= bSpritesVisible
;
350 mrScreenUpdater
.notifyUpdate();
355 void SlideChangeBase::prepareForRun(
356 const ViewEntry
& /* rViewEntry */,
357 const cppcanvas::CanvasSharedPtr
& /* rDestinationCanvas */ )
361 void SlideChangeBase::performIn(
362 const cppcanvas::CustomSpriteSharedPtr
& /*rSprite*/,
363 const ViewEntry
& /*rViewEntry*/,
364 const cppcanvas::CanvasSharedPtr
& /*rDestinationCanvas*/,
369 void SlideChangeBase::performOut(
370 const cppcanvas::CustomSpriteSharedPtr
& /*rSprite*/,
371 const ViewEntry
& /*rViewEntry*/,
372 const cppcanvas::CanvasSharedPtr
& /*rDestinationCanvas*/,
377 double SlideChangeBase::getUnderlyingValue() const
379 return 0.0; // though this should be used in concert with
380 // ActivitiesFactory::createSimpleActivity, better
381 // explicitly name our start value.
382 // Permissible range for operator() above is [0,1]
385 void SlideChangeBase::viewAdded( const UnoViewSharedPtr
& rView
)
387 // we're a one-shot activity, and already finished
391 maViewData
.emplace_back(rView
);
393 ViewEntry
& rEntry( maViewData
.back() );
394 getEnteringBitmap( rEntry
);
395 getLeavingBitmap( rEntry
);
396 addSprites( rEntry
);
399 void SlideChangeBase::viewRemoved( const UnoViewSharedPtr
& rView
)
401 // we're a one-shot activity, and already finished
405 // erase corresponding entry from maViewData
410 [rView
]( const ViewEntry
& rViewEntry
)
411 { return rView
== rViewEntry
.getView(); } ),
415 void SlideChangeBase::viewChanged( const UnoViewSharedPtr
& rView
)
417 // we're a one-shot activity, and already finished
421 // find entry corresponding to modified view
422 ViewsVecT::iterator
aModifiedEntry(
426 [rView
]( const ViewEntry
& rViewEntry
)
427 { return rView
== rViewEntry
.getView(); } ) );
429 OSL_ASSERT( aModifiedEntry
!= maViewData
.end() );
430 if( aModifiedEntry
== maViewData
.end() )
433 // clear stale info (both bitmaps and sprites prolly need a
435 clearViewEntry( *aModifiedEntry
);
436 addSprites( *aModifiedEntry
);
439 void SlideChangeBase::viewsChanged()
441 // we're a one-shot activity, and already finished
445 for( auto& rView
: maViewData
)
447 // clear stale info (both bitmaps and sprites prolly need a
449 clearViewEntry( rView
);
454 cppcanvas::CustomSpriteSharedPtr
SlideChangeBase::createSprite(
455 UnoViewSharedPtr
const & pView
,
456 basegfx::B2DSize
const & rSpriteSize
,
459 // TODO(P2): change to bitmapsprite once that's working
460 const cppcanvas::CustomSpriteSharedPtr
pSprite(
461 pView
->createSprite( rSpriteSize
,
464 // alpha default is 0.0, which seems to be
465 // a bad idea when viewing content...
466 pSprite
->setAlpha( 1.0 );
467 if (mbSpritesVisible
)
473 void SlideChangeBase::addSprites( ViewEntry
& rEntry
)
475 if( mbCreateLeavingSprites
&& maLeavingSlide
)
477 // create leaving sprite:
478 const basegfx::B2ISize
leavingSlideSizePixel(
479 getLeavingBitmap( rEntry
)->getSize() );
481 rEntry
.mpOutSprite
= createSprite( rEntry
.mpView
,
482 basegfx::B2DSize( leavingSlideSizePixel
),
486 if( mbCreateEnteringSprites
)
488 // create entering sprite:
489 const basegfx::B2ISize
enteringSlideSizePixel(
490 getSlideSizePixel( basegfx::B2DVector( mpEnteringSlide
->getSlideSize() ),
493 rEntry
.mpInSprite
= createSprite( rEntry
.mpView
,
494 basegfx::B2DSize( enteringSlideSizePixel
),
499 void SlideChangeBase::clearViewEntry( ViewEntry
& rEntry
)
501 // clear stale info (both bitmaps and sprites prolly need a
503 rEntry
.mpEnteringBitmap
.reset();
504 rEntry
.mpLeavingBitmap
.reset();
505 rEntry
.mpInSprite
.reset();
506 rEntry
.mpOutSprite
.reset();
509 } // namespace presentation
511 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */