diff options
Diffstat (limited to 'slideshow/source/engine/transitions/slidechangebase.cxx')
-rw-r--r-- | slideshow/source/engine/transitions/slidechangebase.cxx | 510 |
1 files changed, 510 insertions, 0 deletions
diff --git a/slideshow/source/engine/transitions/slidechangebase.cxx b/slideshow/source/engine/transitions/slidechangebase.cxx new file mode 100644 index 000000000..bee40c948 --- /dev/null +++ b/slideshow/source/engine/transitions/slidechangebase.cxx @@ -0,0 +1,510 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <tools/diagnose_ex.h> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <cppcanvas/basegfxfactory.hxx> +#include <cppcanvas/customsprite.hxx> + +#include "slidechangebase.hxx" +#include <tools.hxx> + +#include <algorithm> + +using namespace com::sun::star; + +namespace slideshow::internal { + +SlideChangeBase::SlideChangeBase( std::optional<SlideSharedPtr> const & leavingSlide, + const SlideSharedPtr& pEnteringSlide, + const SoundPlayerSharedPtr& pSoundPlayer, + const UnoViewContainer& rViewContainer, + ScreenUpdater& rScreenUpdater, + EventMultiplexer& rEventMultiplexer, + bool bCreateLeavingSprites, + bool bCreateEnteringSprites ) : + mpSoundPlayer( pSoundPlayer ), + mrEventMultiplexer(rEventMultiplexer), + mrScreenUpdater(rScreenUpdater), + maLeavingSlide( leavingSlide ), + mpEnteringSlide( pEnteringSlide ), + maViewData(), + mrViewContainer(rViewContainer), + mbCreateLeavingSprites(bCreateLeavingSprites), + mbCreateEnteringSprites(bCreateEnteringSprites), + mbSpritesVisible(false), + mbFinished(false), + mbPrefetched(false) +{ + ENSURE_OR_THROW( + pEnteringSlide, + "SlideChangeBase::SlideChangeBase(): Invalid entering slide!" ); +} + +SlideBitmapSharedPtr SlideChangeBase::getLeavingBitmap( const ViewEntry& rViewEntry ) const +{ + if( !rViewEntry.mpLeavingBitmap ) + rViewEntry.mpLeavingBitmap = createBitmap(rViewEntry.mpView, + maLeavingSlide); + + return rViewEntry.mpLeavingBitmap; +} + +SlideBitmapSharedPtr SlideChangeBase::getEnteringBitmap( const ViewEntry& rViewEntry ) const +{ + if( !rViewEntry.mpEnteringBitmap ) + rViewEntry.mpEnteringBitmap = createBitmap( rViewEntry.mpView, + std::optional<SlideSharedPtr>(mpEnteringSlide) ); + + return rViewEntry.mpEnteringBitmap; +} + +SlideBitmapSharedPtr SlideChangeBase::createBitmap( const UnoViewSharedPtr& rView, + const std::optional<SlideSharedPtr>& rSlide ) const +{ + SlideBitmapSharedPtr pRet; + if( !rSlide ) + return pRet; + + SlideSharedPtr const & pSlide = *rSlide; + if( !pSlide ) + { + // TODO(P3): No need to generate a bitmap here. This only made + // the code more uniform. Faster would be to simply clear the + // sprite to black. + + // create empty, black-filled bitmap + const basegfx::B2ISize slideSizePixel( + getSlideSizePixel( basegfx::B2DSize( mpEnteringSlide->getSlideSize() ), + rView )); + + cppcanvas::CanvasSharedPtr pCanvas( rView->getCanvas() ); + + // create a bitmap of appropriate size + cppcanvas::BitmapSharedPtr pBitmap( + cppcanvas::BaseGfxFactory::createBitmap( + pCanvas, + slideSizePixel ) ); + + ENSURE_OR_THROW( + pBitmap, + "SlideChangeBase::createBitmap(): Cannot create page bitmap" ); + + cppcanvas::BitmapCanvasSharedPtr pBitmapCanvas( + pBitmap->getBitmapCanvas() ); + + ENSURE_OR_THROW( pBitmapCanvas, + "SlideChangeBase::createBitmap(): " + "Cannot create page bitmap canvas" ); + + // set transformation to identitiy (->device pixel) + pBitmapCanvas->setTransformation( ::basegfx::B2DHomMatrix() ); + + // clear bitmap to black + fillRect( pBitmapCanvas, + ::basegfx::B2DRectangle( 0.0, 0.0, + slideSizePixel.getX(), + slideSizePixel.getY() ), + 0x000000FFU ); + + pRet = std::make_shared<SlideBitmap>( pBitmap ); + } + else + { + pRet = pSlide->getCurrentSlideBitmap( rView ); + } + + return pRet; +} + +::basegfx::B2ISize SlideChangeBase::getEnteringSlideSizePixel( const UnoViewSharedPtr& pView ) const +{ + return getSlideSizePixel( basegfx::B2DSize( mpEnteringSlide->getSlideSize() ), + pView ); +} + +void SlideChangeBase::renderBitmap( + SlideBitmapSharedPtr const & pSlideBitmap, + cppcanvas::CanvasSharedPtr const & pCanvas ) +{ + if( !(pSlideBitmap && pCanvas) ) + return; + + // need to render without any transformation (we + // assume device units): + const basegfx::B2DHomMatrix viewTransform( + pCanvas->getTransformation() ); + const basegfx::B2DPoint pageOrigin( + viewTransform * basegfx::B2DPoint() ); + const cppcanvas::CanvasSharedPtr pDevicePixelCanvas( + pCanvas->clone() ); + + // render at output position, don't modify bitmap object (no move!): + const basegfx::B2DHomMatrix transform(basegfx::utils::createTranslateB2DHomMatrix( + pageOrigin.getX(), pageOrigin.getY())); + + pDevicePixelCanvas->setTransformation( transform ); + pSlideBitmap->draw( pDevicePixelCanvas ); +} + +void SlideChangeBase::prefetch() +{ + // we're a one-shot activity, and already finished + if( mbFinished || mbPrefetched ) + return; + + // register ourselves for view change events + mrEventMultiplexer.addViewHandler( std::dynamic_pointer_cast<ViewEventHandler>(shared_from_this()) ); + + // init views and create slide bitmaps + for( const auto& pView : mrViewContainer ) + viewAdded( pView ); + + mbPrefetched = true; +} + +void SlideChangeBase::start( const AnimatableShapeSharedPtr& /*rShape*/, + const ShapeAttributeLayerSharedPtr& /*rLayer*/ ) +{ + // we're a one-shot activity, and already finished + if( mbFinished ) + return; + + prefetch(); // no-op, if already done + + // get the subclasses a chance to do any specific initialization before run + for ( ViewsVecT::const_iterator aCurr( beginViews() ), aEnd( endViews() ); aCurr != aEnd; ++aCurr ) + prepareForRun( *aCurr, aCurr->mpView->getCanvas() ); + + // start accompanying sound effect, if any + if( mpSoundPlayer ) + { + mpSoundPlayer->startPlayback(); + // xxx todo: for now, presentation.cxx takes care about the slide + // #i50492# transition sound object, so just release it here + mpSoundPlayer.reset(); + } +} + +void SlideChangeBase::end() +{ + // we're a one-shot activity, and already finished + if( mbFinished ) + return; + + try + { + // draw fully entered bitmap: + ViewsVecT::const_iterator aCurr( beginViews() ); + const ViewsVecT::const_iterator aEnd( endViews() ); + while( aCurr != aEnd ) + { + // fully clear view content to background color + aCurr->mpView->clearAll(); + + const SlideBitmapSharedPtr pSlideBitmap( getEnteringBitmap( *aCurr )); + pSlideBitmap->clip( basegfx::B2DPolyPolygon() /* no clipping */ ); + aCurr->mpView->clearAll(); + renderBitmap( pSlideBitmap, + aCurr->mpView->getCanvas() ); + + ++aCurr; + } + } + catch( uno::Exception& ) + { + // make sure releasing below happens + } + + // swap changes to screen + mrScreenUpdater.notifyUpdate(); + + // make object dysfunctional + mbFinished = true; + ViewsVecT().swap(maViewData); + maLeavingSlide.reset(); + mpEnteringSlide.reset(); + + // sprites have been binned above + mbSpritesVisible = false; + + // remove also from event multiplexer, we're dead anyway + mrEventMultiplexer.removeViewHandler( std::dynamic_pointer_cast<ViewEventHandler>(shared_from_this()) ); +} + +bool SlideChangeBase::operator()( double nValue ) +{ + if( mbFinished ) + return false; + + const std::size_t nEntries( maViewData.size() ); + bool bSpritesVisible( mbSpritesVisible ); + + for( ::std::size_t i=0; i<nEntries; ++i ) + { + // calc sprite offsets. The enter/leaving bitmaps are only + // as large as the actual slides. For scaled-down + // presentations, we have to move the left, top edge of + // those bitmaps to the actual position, governed by the + // given view transform. The aSpritePosPixel local + // variable is already in device coordinate space + // (i.e. pixel). + + ViewEntry& rViewEntry( maViewData[i] ); + const ::cppcanvas::CanvasSharedPtr& rCanvas( rViewEntry.mpView->getCanvas() ); + ::cppcanvas::CustomSpriteSharedPtr& rInSprite( rViewEntry.mpInSprite ); + ::cppcanvas::CustomSpriteSharedPtr& rOutSprite( rViewEntry.mpOutSprite ); + + // TODO(F2): Properly respect clip here. + + // Might have to be transformed, too. + const ::basegfx::B2DHomMatrix aViewTransform( + rViewEntry.mpView->getTransformation() ); + const ::basegfx::B2DPoint aSpritePosPixel( + aViewTransform * ::basegfx::B2DPoint() ); + + // move sprite to final output position, in + // device coordinates + if( rOutSprite ) + rOutSprite->movePixel( aSpritePosPixel ); + if( rInSprite ) + rInSprite->movePixel( aSpritePosPixel ); + + if( !mbSpritesVisible ) + { + if( rOutSprite ) + { + // only render once: clipping is done + // exclusively with the sprite + const ::cppcanvas::CanvasSharedPtr pOutContentCanvas( + rOutSprite->getContentCanvas() ); + if( pOutContentCanvas) + { + // TODO(Q2): Use basegfx bitmaps here + + // TODO(F1): SlideBitmap is not fully portable + // between different canvases! + + // render the content + OSL_ASSERT( getLeavingBitmap( rViewEntry ) ); + if( getLeavingBitmap( rViewEntry ) ) + getLeavingBitmap( rViewEntry )->draw( pOutContentCanvas ); + } + } + + if( rInSprite ) + { + // only render once: clipping is done + // exclusively with the sprite + const ::cppcanvas::CanvasSharedPtr pInContentCanvas( + rInSprite->getContentCanvas() ); + if( pInContentCanvas ) + { + // TODO(Q2): Use basegfx bitmaps here + + // TODO(F1): SlideBitmap is not fully portable + // between different canvases! + + // render the content + getEnteringBitmap( rViewEntry )->draw( pInContentCanvas ); + } + } + } + + if( rOutSprite ) + performOut( rOutSprite, rViewEntry, rCanvas, nValue ); + if( rInSprite ) + performIn( rInSprite, rViewEntry, rCanvas, nValue ); + + // finishing deeds for first run. + if( !mbSpritesVisible) + { + // enable sprites: + if( rOutSprite ) + rOutSprite->show(); + if( rInSprite ) + rInSprite->show(); + bSpritesVisible = true; + } + } // for_each( sprite ) + + mbSpritesVisible = bSpritesVisible; + mrScreenUpdater.notifyUpdate(); + + return true; +} + +void SlideChangeBase::prepareForRun( + const ViewEntry& /* rViewEntry */, + const cppcanvas::CanvasSharedPtr& /* rDestinationCanvas */ ) +{ +} + +void SlideChangeBase::performIn( + const cppcanvas::CustomSpriteSharedPtr& /*rSprite*/, + const ViewEntry& /*rViewEntry*/, + const cppcanvas::CanvasSharedPtr& /*rDestinationCanvas*/, + double /*t*/ ) +{ +} + +void SlideChangeBase::performOut( + const cppcanvas::CustomSpriteSharedPtr& /*rSprite*/, + const ViewEntry& /*rViewEntry*/, + const cppcanvas::CanvasSharedPtr& /*rDestinationCanvas*/, + double /*t*/ ) +{ +} + +double SlideChangeBase::getUnderlyingValue() const +{ + return 0.0; // though this should be used in concert with + // ActivitiesFactory::createSimpleActivity, better + // explicitly name our start value. + // Permissible range for operator() above is [0,1] +} + +void SlideChangeBase::viewAdded( const UnoViewSharedPtr& rView ) +{ + // we're a one-shot activity, and already finished + if( mbFinished ) + return; + + maViewData.emplace_back(rView ); + + ViewEntry& rEntry( maViewData.back() ); + getEnteringBitmap( rEntry ); + getLeavingBitmap( rEntry ); + addSprites( rEntry ); +} + +void SlideChangeBase::viewRemoved( const UnoViewSharedPtr& rView ) +{ + // we're a one-shot activity, and already finished + if( mbFinished ) + return; + + // erase corresponding entry from maViewData + maViewData.erase( + std::remove_if( + maViewData.begin(), + maViewData.end(), + [rView]( const ViewEntry& rViewEntry ) + { return rView == rViewEntry.getView(); } ), + maViewData.end() ); +} + +void SlideChangeBase::viewChanged( const UnoViewSharedPtr& rView ) +{ + // we're a one-shot activity, and already finished + if( mbFinished ) + return; + + // find entry corresponding to modified view + ViewsVecT::iterator aModifiedEntry( + std::find_if( + maViewData.begin(), + maViewData.end(), + [rView]( const ViewEntry& rViewEntry ) + { return rView == rViewEntry.getView(); } ) ); + + OSL_ASSERT( aModifiedEntry != maViewData.end() ); + if( aModifiedEntry == maViewData.end() ) + return; + + // clear stale info (both bitmaps and sprites prolly need a + // resize) + clearViewEntry( *aModifiedEntry ); + addSprites( *aModifiedEntry ); +} + +void SlideChangeBase::viewsChanged() +{ + // we're a one-shot activity, and already finished + if( mbFinished ) + return; + + for( auto& rView : maViewData ) + { + // clear stale info (both bitmaps and sprites prolly need a + // resize) + clearViewEntry( rView ); + addSprites( rView ); + } +} + +cppcanvas::CustomSpriteSharedPtr SlideChangeBase::createSprite( + UnoViewSharedPtr const & pView, + basegfx::B2DSize const & rSpriteSize, + double nPrio ) const +{ + // TODO(P2): change to bitmapsprite once that's working + const cppcanvas::CustomSpriteSharedPtr pSprite( + pView->createSprite( rSpriteSize, + nPrio )); + + // alpha default is 0.0, which seems to be + // a bad idea when viewing content... + pSprite->setAlpha( 1.0 ); + if (mbSpritesVisible) + pSprite->show(); + + return pSprite; +} + +void SlideChangeBase::addSprites( ViewEntry& rEntry ) +{ + if( mbCreateLeavingSprites && maLeavingSlide ) + { + // create leaving sprite: + const basegfx::B2ISize leavingSlideSizePixel( + getLeavingBitmap( rEntry )->getSize() ); + + rEntry.mpOutSprite = createSprite( rEntry.mpView, + basegfx::B2DSize( leavingSlideSizePixel ), + 100 ); + } + + if( mbCreateEnteringSprites ) + { + // create entering sprite: + const basegfx::B2ISize enteringSlideSizePixel( + getSlideSizePixel( basegfx::B2DSize( mpEnteringSlide->getSlideSize() ), + rEntry.mpView )); + + rEntry.mpInSprite = createSprite( rEntry.mpView, + basegfx::B2DSize( enteringSlideSizePixel ), + 101 ); + } +} + +void SlideChangeBase::clearViewEntry( ViewEntry& rEntry ) +{ + // clear stale info (both bitmaps and sprites prolly need a + // resize) + rEntry.mpEnteringBitmap.reset(); + rEntry.mpLeavingBitmap.reset(); + rEntry.mpInSprite.reset(); + rEntry.mpOutSprite.reset(); +} + +} // namespace presentation + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |