2007/06/15 20:38:59 thb 1.7.12.8: #i78433# Posting myself an event, to delay-prefetch incoming slide bitmap (follow-up issue for the root cause fix: i78544 2007/05/10 20:32:41 thb 1.7.12.7: #i37778# Reworked LayerManager::updateShapeLayers - now much better aligned with general shape update, and avoids superfluous rendering; made unit tests work again; passing down slide background repaint status to LayerManager; relaxed preconditions for Shape::getUpdateArea() - no longer requires views to work; now catching singular view matrix and using sensible default at API border 2007/05/02 21:37:24 thb 1.7.12.6: #i37778# Changed calls to cppcanvas::Canvas::getTransformation() to ViewLayer::getTransformation() where appropriate (because that's more direct); added initial Slide rendering back in (this time optional) 2007/04/30 07:26:34 thb 1.7.12.5: #i37778# Rendering slide bitmap at the end of a slide transition, instead of during Slide::show(), to avoid temporary display of previous slide after transition sprite vanished. 2007/01/31 14:30:33 thb 1.7.12.4: #i37778# removed View::isContentDestroyed() and mbContentValid distinction on View::clear() - clear() now always clears view the hard way; added explicit screen update to CombTransition, which bypasses SlideChangeBase functionality 2007/01/31 12:29:06 thb 1.7.12.3: #i37778# Slide animations now notify screen updates themselves 2007/01/31 11:25:17 thb 1.7.12.2: #i37778# Added prefetch to Animation interface (to facilitate prefetching - nice for slide transitions, which otherwise lag noticeably while generating the slide bitmap); brought tests up to par, re-enabling unit tests and demo show 2007/01/29 14:02:17 thb 1.7.12.1: Issue number: #i37778# Larger slideshow refactoring. Wrote design and coding style manifest, and adapted the code to actually conform to this. In detail: - cleaned up ownership/disposable/weak_ptr story. removed hacks and explicit Disposable implementations, where workaround were available - removed object mutices, where superfluous - reworked EventMultiplexer (using templatized listener class now), added more events. EventMultiplexer now serves as a true blackboard - reworked directory structure: disjunct parts are now physically separated into directories, instantiation happens via factories & abstract interfaces - added CursorManager, to make setting mouse cursor less hackish - reworked DrawShape, to implement SeparateListener pattern - reworked IntrinsicAnimationActivity, to avoid cyclic references - modified hyperlink & shape cursor handling to communicate via EventMultiplexer - renamed & cleaned up files (presentation.cxx now named slideshowimpl.cxx, etc.) - added first version of the z-order fix to layer/layermanager - cleaned up include guards and include syntax
541 lines
18 KiB
C++
541 lines
18 KiB
C++
/*************************************************************************
|
|
*
|
|
* OpenOffice.org - a multi-platform office productivity suite
|
|
*
|
|
* $RCSfile: slidechangebase.cxx,v $
|
|
*
|
|
* $Revision: 1.8 $
|
|
*
|
|
* last change: $Author: obo $ $Date: 2007-07-17 14:59:50 $
|
|
*
|
|
* The Contents of this file are made available subject to
|
|
* the terms of GNU Lesser General Public License Version 2.1.
|
|
*
|
|
*
|
|
* GNU Lesser General Public License Version 2.1
|
|
* =============================================
|
|
* Copyright 2005 by Sun Microsystems, Inc.
|
|
* 901 San Antonio Road, Palo Alto, CA 94303, USA
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License version 2.1, as published by the Free Software Foundation.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
* MA 02111-1307 USA
|
|
*
|
|
************************************************************************/
|
|
|
|
// MARKER(update_precomp.py): autogen include statement, do not remove
|
|
#include "precompiled_slideshow.hxx"
|
|
|
|
#include <canvas/debug.hxx>
|
|
#include <canvas/canvastools.hxx>
|
|
#include <basegfx/numeric/ftools.hxx>
|
|
#include <basegfx/polygon/b2dpolygontools.hxx>
|
|
#include <basegfx/polygon/b2dpolypolygontools.hxx>
|
|
#include <cppcanvas/basegfxfactory.hxx>
|
|
|
|
#include "slidechangebase.hxx"
|
|
#include "tools.hxx"
|
|
|
|
#include <boost/bind.hpp>
|
|
#include <algorithm>
|
|
|
|
using namespace com::sun::star;
|
|
|
|
namespace slideshow {
|
|
namespace internal {
|
|
|
|
SlideChangeBase::SlideChangeBase( boost::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_AND_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,
|
|
boost::optional<SlideSharedPtr>(mpEnteringSlide) );
|
|
|
|
return rViewEntry.mpEnteringBitmap;
|
|
}
|
|
|
|
SlideBitmapSharedPtr SlideChangeBase::createBitmap( const UnoViewSharedPtr& rView,
|
|
const boost::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( mpEnteringSlide->getSlideSize(),
|
|
rView ));
|
|
|
|
cppcanvas::CanvasSharedPtr pCanvas( rView->getCanvas() );
|
|
|
|
// create a bitmap of appropriate size
|
|
cppcanvas::BitmapSharedPtr pBitmap(
|
|
cppcanvas::BaseGfxFactory::getInstance().createBitmap(
|
|
pCanvas,
|
|
slideSizePixel ) );
|
|
|
|
ENSURE_AND_THROW(
|
|
pBitmap,
|
|
"SlideChangeBase::createBitmap(): Cannot create page bitmap" );
|
|
|
|
cppcanvas::BitmapCanvasSharedPtr pBitmapCanvas(
|
|
pBitmap->getBitmapCanvas() );
|
|
|
|
ENSURE_AND_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.reset( new SlideBitmap( pBitmap ));
|
|
}
|
|
else
|
|
{
|
|
pRet = pSlide->getCurrentSlideBitmap( rView );
|
|
}
|
|
|
|
return pRet;
|
|
}
|
|
|
|
::basegfx::B2ISize SlideChangeBase::getEnteringSlideSizePixel( const UnoViewSharedPtr& pView ) const
|
|
{
|
|
return getSlideSizePixel( mpEnteringSlide->getSlideSize(),
|
|
pView );
|
|
}
|
|
|
|
::basegfx::B2ISize SlideChangeBase::getLeavingSlideSizePixel( const UnoViewSharedPtr& pView ) const
|
|
{
|
|
return getSlideSizePixel( (*maLeavingSlide)->getSlideSize(),
|
|
pView );
|
|
}
|
|
|
|
|
|
void SlideChangeBase::renderBitmap(
|
|
SlideBitmapSharedPtr const & pSlideBitmap,
|
|
cppcanvas::CanvasSharedPtr const & pCanvas )
|
|
{
|
|
if( pSlideBitmap && pCanvas )
|
|
{
|
|
// 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() );
|
|
basegfx::B2DHomMatrix transform;
|
|
// render at output position, don't modify bitmap object (no move!):
|
|
transform.translate( pageOrigin.getX(), pageOrigin.getY() );
|
|
|
|
pDevicePixelCanvas->setTransformation( transform );
|
|
pSlideBitmap->draw( pDevicePixelCanvas );
|
|
}
|
|
}
|
|
|
|
void SlideChangeBase::prefetch( const AnimatableShapeSharedPtr&,
|
|
const ShapeAttributeLayerSharedPtr& )
|
|
{
|
|
// we're a one-shot activity, and already finished
|
|
if( mbFinished || mbPrefetched )
|
|
return;
|
|
|
|
// register ourselves for view change events
|
|
mrEventMultiplexer.addViewHandler( shared_from_this() );
|
|
|
|
// init views and create slide bitmaps
|
|
std::for_each( mrViewContainer.begin(),
|
|
mrViewContainer.end(),
|
|
boost::bind( &SlideChangeBase::viewAdded,
|
|
this,
|
|
_1 ));
|
|
|
|
mbPrefetched = true;
|
|
}
|
|
|
|
void SlideChangeBase::start( const AnimatableShapeSharedPtr& rShape,
|
|
const ShapeAttributeLayerSharedPtr& rLayer )
|
|
{
|
|
// we're a one-shot activity, and already finished
|
|
if( mbFinished )
|
|
return;
|
|
|
|
prefetch(rShape,rLayer); // no-op, if already done
|
|
|
|
// 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 */ );
|
|
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( 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::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
|
|
// explicitely 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.push_back( ViewEntry(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(),
|
|
boost::bind(
|
|
std::equal_to<UnoViewSharedPtr>(),
|
|
rView,
|
|
// select view:
|
|
boost::bind( &ViewEntry::getView, _1 ))),
|
|
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(),
|
|
boost::bind(
|
|
std::equal_to<UnoViewSharedPtr>(),
|
|
rView,
|
|
// select view:
|
|
boost::bind( &ViewEntry::getView, _1 ) )));
|
|
|
|
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;
|
|
|
|
ViewsVecT::iterator aIter( maViewData.begin() );
|
|
ViewsVecT::iterator const aEnd ( maViewData.end() );
|
|
while( aIter != aEnd )
|
|
{
|
|
// clear stale info (both bitmaps and sprites prolly need a
|
|
// resize)
|
|
clearViewEntry( *aIter );
|
|
addSprites( *aIter );
|
|
|
|
++aIter;
|
|
}
|
|
}
|
|
|
|
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,
|
|
leavingSlideSizePixel,
|
|
100 );
|
|
}
|
|
|
|
if( mbCreateEnteringSprites )
|
|
{
|
|
// create entering sprite:
|
|
const basegfx::B2ISize enteringSlideSizePixel(
|
|
getSlideSizePixel( mpEnteringSlide->getSlideSize(),
|
|
rEntry.mpView ));
|
|
|
|
rEntry.mpInSprite = createSprite( rEntry.mpView,
|
|
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 internal
|
|
} // namespace presentation
|