diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /slideshow/source/engine/slide/slideimpl.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'slideshow/source/engine/slide/slideimpl.cxx')
-rw-r--r-- | slideshow/source/engine/slide/slideimpl.cxx | 1130 |
1 files changed, 1130 insertions, 0 deletions
diff --git a/slideshow/source/engine/slide/slideimpl.cxx b/slideshow/source/engine/slide/slideimpl.cxx new file mode 100644 index 000000000..033af8756 --- /dev/null +++ b/slideshow/source/engine/slide/slideimpl.cxx @@ -0,0 +1,1130 @@ +/* -*- 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 <osl/diagnose.hxx> +#include <tools/diagnose_ex.h> +#include <cppcanvas/basegfxfactory.hxx> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/point/b2dpoint.hxx> + +#include <com/sun/star/awt/SystemPointer.hpp> +#include <com/sun/star/drawing/XMasterPageTarget.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/presentation/ParagraphTarget.hpp> +#include <com/sun/star/presentation/EffectNodeType.hpp> + +#include <slide.hxx> +#include <slideshowcontext.hxx> +#include "slideanimations.hxx" +#include <doctreenode.hxx> +#include <screenupdater.hxx> +#include <cursormanager.hxx> +#include <shapeimporter.hxx> +#include <slideshowexceptions.hxx> +#include <eventqueue.hxx> +#include <activitiesqueue.hxx> +#include "layermanager.hxx" +#include "shapemanagerimpl.hxx" +#include <usereventqueue.hxx> +#include "userpaintoverlay.hxx" +#include "targetpropertiescreator.hxx" +#include <tools.hxx> +#include <box2dtools.hxx> +#include <vcl/graphicfilter.hxx> +#include <svx/svdograf.hxx> + +using namespace ::com::sun::star; + + +namespace slideshow::internal +{ +namespace +{ + +class SlideImpl : public Slide, + public CursorManager, + public ViewEventHandler, + public ::osl::DebugBase<SlideImpl> +{ +public: + SlideImpl( const uno::Reference<drawing::XDrawPage>& xDrawPage, + const uno::Reference<drawing::XDrawPagesSupplier>& xDrawPages, + const uno::Reference<animations::XAnimationNode>& xRootNode, + EventQueue& rEventQueue, + EventMultiplexer& rEventMultiplexer, + ScreenUpdater& rScreenUpdater, + ActivitiesQueue& rActivitiesQueue, + UserEventQueue& rUserEventQueue, + CursorManager& rCursorManager, + MediaFileManager& rMediaFileManager, + const UnoViewContainer& rViewContainer, + const uno::Reference<uno::XComponentContext>& xContext, + const ShapeEventListenerMap& rShapeListenerMap, + const ShapeCursorMap& rShapeCursorMap, + PolyPolygonVector&& rPolyPolygonVector, + RGBColor const& rUserPaintColor, + double dUserPaintStrokeWidth, + bool bUserPaintEnabled, + bool bIntrinsicAnimationsAllowed, + bool bDisableAnimationZOrder ); + + virtual ~SlideImpl() override; + + + // Slide interface + + + virtual void prefetch() override; + virtual void show( bool ) override; + virtual void hide() override; + + virtual basegfx::B2ISize getSlideSize() const override; + virtual uno::Reference<drawing::XDrawPage > getXDrawPage() const override; + virtual uno::Reference<animations::XAnimationNode> getXAnimationNode() const override; + virtual PolyPolygonVector getPolygons() override; + virtual void drawPolygons() const override; + virtual bool isPaintOverlayActive() const override; + virtual void enablePaintOverlay() override; + virtual void update_settings( bool bUserPaintEnabled, RGBColor const& aUserPaintColor, double dUserPaintStrokeWidth ) override; + + + // TODO(F2): Rework SlideBitmap to no longer be based on XBitmap, + // but on canvas-independent basegfx bitmaps + virtual SlideBitmapSharedPtr getCurrentSlideBitmap( const UnoViewSharedPtr& rView ) const override; + + +private: + // ViewEventHandler + virtual void viewAdded( const UnoViewSharedPtr& rView ) override; + virtual void viewRemoved( const UnoViewSharedPtr& rView ) override; + virtual void viewChanged( const UnoViewSharedPtr& rView ) override; + virtual void viewsChanged() override; + + // CursorManager + virtual bool requestCursor( sal_Int16 nCursorShape ) override; + virtual void resetCursor() override; + + void activatePaintOverlay(); + void deactivatePaintOverlay(); + + /** Query whether the slide has animations at all + + If the slide doesn't have animations, show() displays + only static content. If an event is registered with + registerSlideEndEvent(), this event will be + immediately activated at the end of the show() method. + + @return true, if this slide has animations, false + otherwise + */ + bool isAnimated(); + + /// Set all Shapes to their initial attributes for slideshow + bool applyInitialShapeAttributes( const css::uno::Reference< css::animations::XAnimationNode >& xRootAnimationNode ); + + /// Set shapes to attributes corresponding to initial or final state of slide + void applyShapeAttributes( + const css::uno::Reference< css::animations::XAnimationNode >& xRootAnimationNode, + bool bInitial) const; + + /// Renders current slide content to bitmap + SlideBitmapSharedPtr createCurrentSlideBitmap( + const UnoViewSharedPtr& rView, + ::basegfx::B2ISize const & rSlideSize ) const; + + /// Prefetch all shapes (not the animations) + bool loadShapes(); + + /// Retrieve slide size from XDrawPage + basegfx::B2ISize getSlideSizeImpl() const; + + /// Prefetch show, but don't call applyInitialShapeAttributes() + bool implPrefetchShow(); + + /// Add Polygons to the member maPolygons + void addPolygons(const PolyPolygonVector& rPolygons); + + // Types + // ===== + + enum SlideAnimationState + { + CONSTRUCTING_STATE=0, + INITIAL_STATE=1, + SHOWING_STATE=2, + FINAL_STATE=3, + SlideAnimationState_NUM_ENTRIES=4 + }; + + typedef std::vector< SlideBitmapSharedPtr > VectorOfSlideBitmaps; + /** Vector of slide bitmaps. + + Since the bitmap content is sensitive to animation + effects, we have an inner vector containing a distinct + bitmap for each of the SlideAnimationStates. + */ + typedef ::std::vector< std::pair< UnoViewSharedPtr, + VectorOfSlideBitmaps > > VectorOfVectorOfSlideBitmaps; + + + // Member variables + // ================ + + /// The page model object + uno::Reference< drawing::XDrawPage > mxDrawPage; + uno::Reference< drawing::XDrawPagesSupplier > mxDrawPagesSupplier; + uno::Reference< animations::XAnimationNode > mxRootNode; + + LayerManagerSharedPtr mpLayerManager; + std::shared_ptr<ShapeManagerImpl> mpShapeManager; + std::shared_ptr<SubsettableShapeManager> mpSubsettableShapeManager; + box2d::utils::Box2DWorldSharedPtr mpBox2DWorld; + + /// Contains common objects needed throughout the slideshow + SlideShowContext maContext; + + /// parent cursor manager + CursorManager& mrCursorManager; + + /// Handles the animation and event generation for us + SlideAnimations maAnimations; + PolyPolygonVector maPolygons; + + RGBColor maUserPaintColor; + double mdUserPaintStrokeWidth; + UserPaintOverlaySharedPtr mpPaintOverlay; + + /// Bitmaps with slide content at various states + mutable VectorOfVectorOfSlideBitmaps maSlideBitmaps; + + SlideAnimationState meAnimationState; + + const basegfx::B2ISize maSlideSize; + + sal_Int16 mnCurrentCursor; + + /// True, when intrinsic shape animations are allowed + bool mbIntrinsicAnimationsAllowed; + + /// True, when user paint overlay is enabled + bool mbUserPaintOverlayEnabled; + + /// True, if initial load of all page shapes succeeded + bool mbShapesLoaded; + + /// True, if initial load of all animation info succeeded + bool mbShowLoaded; + + /** True, if this slide is not static. + + If this slide has animated content, this variable will + be true, and false otherwise. + */ + bool mbHaveAnimations; + + /** True, if this slide has a main animation sequence. + + If this slide has animation content, which in turn has + a main animation sequence (which must be fully run + before EventMultiplexer::notifySlideAnimationsEnd() is + called), this member is true. + */ + bool mbMainSequenceFound; + + /// When true, show() was called. Slide hidden otherwise. + bool mbActive; + + /// When true, enablePaintOverlay was called and mbUserPaintOverlay = true + bool mbPaintOverlayActive; + + /// When true, final state attributes are already applied to shapes + bool mbFinalStateApplied; +}; + + +void slideRenderer( SlideImpl const * pSlide, const UnoViewSharedPtr& rView ) +{ + // fully clear view content to background color + rView->clearAll(); + + SlideBitmapSharedPtr pBitmap( pSlide->getCurrentSlideBitmap( rView ) ); + ::cppcanvas::CanvasSharedPtr pCanvas( rView->getCanvas() ); + + const ::basegfx::B2DHomMatrix aViewTransform( rView->getTransformation() ); + const ::basegfx::B2DPoint aOutPosPixel( aViewTransform * ::basegfx::B2DPoint() ); + + // setup a canvas with device coordinate space, the slide + // bitmap already has the correct dimension. + ::cppcanvas::CanvasSharedPtr pDevicePixelCanvas( pCanvas->clone() ); + pDevicePixelCanvas->setTransformation( ::basegfx::B2DHomMatrix() ); + + // render at given output position + pBitmap->move( aOutPosPixel ); + + // clear clip (might have been changed, e.g. from comb + // transition) + pBitmap->clip( ::basegfx::B2DPolyPolygon() ); + pBitmap->draw( pDevicePixelCanvas ); +} + + +SlideImpl::SlideImpl( const uno::Reference< drawing::XDrawPage >& xDrawPage, + const uno::Reference<drawing::XDrawPagesSupplier>& xDrawPages, + const uno::Reference< animations::XAnimationNode >& xRootNode, + EventQueue& rEventQueue, + EventMultiplexer& rEventMultiplexer, + ScreenUpdater& rScreenUpdater, + ActivitiesQueue& rActivitiesQueue, + UserEventQueue& rUserEventQueue, + CursorManager& rCursorManager, + MediaFileManager& rMediaFileManager, + const UnoViewContainer& rViewContainer, + const uno::Reference< uno::XComponentContext >& xComponentContext, + const ShapeEventListenerMap& rShapeListenerMap, + const ShapeCursorMap& rShapeCursorMap, + PolyPolygonVector&& rPolyPolygonVector, + RGBColor const& aUserPaintColor, + double dUserPaintStrokeWidth, + bool bUserPaintEnabled, + bool bIntrinsicAnimationsAllowed, + bool bDisableAnimationZOrder ) : + mxDrawPage( xDrawPage ), + mxDrawPagesSupplier( xDrawPages ), + mxRootNode( xRootNode ), + mpLayerManager( std::make_shared<LayerManager>( + rViewContainer, + bDisableAnimationZOrder) ), + mpShapeManager( std::make_shared<ShapeManagerImpl>( + rEventMultiplexer, + mpLayerManager, + rCursorManager, + rShapeListenerMap, + rShapeCursorMap, + xDrawPage)), + mpSubsettableShapeManager( mpShapeManager ), + mpBox2DWorld( std::make_shared<box2d::utils::box2DWorld>( + basegfx::B2DSize( getSlideSizeImpl() ) ) ), + maContext( mpSubsettableShapeManager, + rEventQueue, + rEventMultiplexer, + rScreenUpdater, + rActivitiesQueue, + rUserEventQueue, + *this, + rMediaFileManager, + rViewContainer, + xComponentContext, + mpBox2DWorld ), + mrCursorManager( rCursorManager ), + maAnimations( maContext, + basegfx::B2DSize( getSlideSizeImpl() ) ), + maPolygons(std::move(rPolyPolygonVector)), + maUserPaintColor(aUserPaintColor), + mdUserPaintStrokeWidth(dUserPaintStrokeWidth), + mpPaintOverlay(), + maSlideBitmaps(), + meAnimationState( CONSTRUCTING_STATE ), + maSlideSize(getSlideSizeImpl()), + mnCurrentCursor( awt::SystemPointer::ARROW ), + mbIntrinsicAnimationsAllowed( bIntrinsicAnimationsAllowed ), + mbUserPaintOverlayEnabled(bUserPaintEnabled), + mbShapesLoaded( false ), + mbShowLoaded( false ), + mbHaveAnimations( false ), + mbMainSequenceFound( false ), + mbActive( false ), + mbPaintOverlayActive( false ), + mbFinalStateApplied( false ) +{ + // clone already existing views for slide bitmaps + for( const auto& rView : rViewContainer ) + viewAdded( rView ); + + // register screen update (LayerManager needs to signal pending + // updates) + maContext.mrScreenUpdater.addViewUpdate(mpShapeManager); +} + +void SlideImpl::update_settings( bool bUserPaintEnabled, RGBColor const& aUserPaintColor, double dUserPaintStrokeWidth ) +{ + maUserPaintColor = aUserPaintColor; + mdUserPaintStrokeWidth = dUserPaintStrokeWidth; + mbUserPaintOverlayEnabled = bUserPaintEnabled; +} + +SlideImpl::~SlideImpl() +{ + if( mpShapeManager ) + { + maContext.mrScreenUpdater.removeViewUpdate(mpShapeManager); + mpShapeManager->dispose(); + + // TODO(Q3): Make sure LayerManager (and thus Shapes) dies + // first, because SlideShowContext has SubsettableShapeManager + // as reference member. + mpLayerManager.reset(); + } +} + +void SlideImpl::prefetch() +{ + if( !mxRootNode.is() ) + return; + + // Try to prefetch all graphics from the page. This will be done + // in threads to be more efficient than loading them on-demand one by one. + std::vector<Graphic*> graphics; + for (sal_Int32 i = 0; i < mxDrawPage->getCount(); i++) + { + com::sun::star::uno::Reference<com::sun::star::drawing::XShape> xShape(mxDrawPage->getByIndex(i), com::sun::star::uno::UNO_QUERY_THROW); + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape); + if (!pObj) + continue; + if( SdrGrafObj* grafObj = dynamic_cast<SdrGrafObj*>(pObj)) + if( !grafObj->GetGraphic().isAvailable()) + graphics.push_back( const_cast<Graphic*>(&grafObj->GetGraphic())); + } + if(graphics.size() > 1) // threading does not help with loading just one + GraphicFilter::GetGraphicFilter().MakeGraphicsAvailableThreaded( graphics ); + + applyInitialShapeAttributes(mxRootNode); +} + +void SlideImpl::show( bool bSlideBackgroundPainted ) +{ + if( mbActive ) + return; // already active + + if( !mpShapeManager || !mpLayerManager ) + return; // disposed + + // set initial shape attributes (e.g. hide shapes that have + // 'appear' effect set) + if( !applyInitialShapeAttributes(mxRootNode) ) + return; + + // activate and take over view - clears view, if necessary + mbActive = true; + requestCursor( mnCurrentCursor ); + + // enable shape management & event broadcasting for shapes of this + // slide. Also enables LayerManager to record updates. Currently, + // never let LayerManager render initial slide content, use + // buffered slide bitmaps instead. + mpShapeManager->activate(); + + + // render slide to screen, if requested + if( !bSlideBackgroundPainted ) + { + for( const auto& rContext : maContext.mrViewContainer ) + slideRenderer( this, rContext ); + + maContext.mrScreenUpdater.notifyUpdate(); + } + + + // fire up animations + const bool bIsAnimated( isAnimated() ); + if( bIsAnimated ) + maAnimations.start(); // feeds initial events into queue + + // NOTE: this looks slightly weird, but is indeed correct: + // as isAnimated() might return false, _although_ there is + // a main sequence (because the animation nodes don't + // contain any executable effects), we gotta check both + // conditions here. + if( !bIsAnimated || !mbMainSequenceFound ) + { + // manually trigger a slide animation end event (we don't have + // animations at all, or we don't have a main animation + // sequence, but if we had, it'd end now). Note that having + // animations alone does not matter here, as only main + // sequence animations prevents showing the next slide on + // nextEvent(). + maContext.mrEventMultiplexer.notifySlideAnimationsEnd(); + } + + // enable shape-intrinsic animations (drawing layer animations or + // GIF animations) + if( mbIntrinsicAnimationsAllowed ) + mpSubsettableShapeManager->notifyIntrinsicAnimationsEnabled(); + + // enable paint overlay, if maUserPaintColor is valid + activatePaintOverlay(); + + + // from now on, animations might be showing + meAnimationState = SHOWING_STATE; +} + +void SlideImpl::hide() +{ + if( !mbActive || !mpShapeManager ) + return; // already hidden/disposed + + + // from now on, all animations are stopped + meAnimationState = FINAL_STATE; + + + // disable user paint overlay under all circumstances, + // this slide now ceases to be active. + deactivatePaintOverlay(); + + + // switch off all shape-intrinsic animations. + mpSubsettableShapeManager->notifyIntrinsicAnimationsDisabled(); + + // force-end all SMIL animations, too + maAnimations.end(); + + + // disable shape management & event broadcasting for shapes of this + // slide. Also disables LayerManager. + mpShapeManager->deactivate(); + + // vanish from view + resetCursor(); + mbActive = false; +} + +basegfx::B2ISize SlideImpl::getSlideSize() const +{ + return maSlideSize; +} + +uno::Reference<drawing::XDrawPage > SlideImpl::getXDrawPage() const +{ + return mxDrawPage; +} + +uno::Reference<animations::XAnimationNode> SlideImpl::getXAnimationNode() const +{ + return mxRootNode; +} + +PolyPolygonVector SlideImpl::getPolygons() +{ + if(mbPaintOverlayActive) + maPolygons = mpPaintOverlay->getPolygons(); + return maPolygons; +} + +SlideBitmapSharedPtr SlideImpl::getCurrentSlideBitmap( const UnoViewSharedPtr& rView ) const +{ + // search corresponding entry in maSlideBitmaps (which + // contains the views as the key) + VectorOfVectorOfSlideBitmaps::iterator aIter; + const VectorOfVectorOfSlideBitmaps::iterator aEnd( maSlideBitmaps.end() ); + if( (aIter=std::find_if( maSlideBitmaps.begin(), + aEnd, + [&rView] + ( const VectorOfVectorOfSlideBitmaps::value_type& cp ) + { return rView == cp.first; } ) ) == aEnd ) + { + // corresponding view not found - maybe view was not + // added to Slide? + ENSURE_OR_THROW( false, + "SlideImpl::getInitialSlideBitmap(): view does not " + "match any of the added ones" ); + } + + // ensure that the show is loaded + if( !mbShowLoaded ) + { + // only prefetch and init shapes when not done already + // (otherwise, at least applyInitialShapeAttributes() will be + // called twice for initial slide rendering). Furthermore, + // applyInitialShapeAttributes() _always_ performs + // initializations, which would be highly unwanted during a + // running show. OTOH, a slide whose mbShowLoaded is false is + // guaranteed not be running a show. + + // set initial shape attributes (e.g. hide 'appear' effect + // shapes) + if( !const_cast<SlideImpl*>(this)->applyInitialShapeAttributes( mxRootNode ) ) + ENSURE_OR_THROW(false, + "SlideImpl::getCurrentSlideBitmap(): Cannot " + "apply initial attributes"); + } + + SlideBitmapSharedPtr& rBitmap( aIter->second.at( meAnimationState )); + const ::basegfx::B2ISize& rSlideSize( + getSlideSizePixel( ::basegfx::B2DSize( getSlideSize() ), + rView )); + + // is the bitmap valid (actually existent, and of correct + // size)? + if( !rBitmap || rBitmap->getSize() != rSlideSize ) + { + // no bitmap there yet, or wrong size - create one + rBitmap = createCurrentSlideBitmap(rView, rSlideSize); + } + + return rBitmap; +} + + +// private methods + + +void SlideImpl::viewAdded( const UnoViewSharedPtr& rView ) +{ + maSlideBitmaps.emplace_back( rView, + VectorOfSlideBitmaps(SlideAnimationState_NUM_ENTRIES) ); + + if( mpLayerManager ) + mpLayerManager->viewAdded( rView ); +} + +void SlideImpl::viewRemoved( const UnoViewSharedPtr& rView ) +{ + if( mpLayerManager ) + mpLayerManager->viewRemoved( rView ); + + const VectorOfVectorOfSlideBitmaps::iterator aEnd( maSlideBitmaps.end() ); + maSlideBitmaps.erase( + std::remove_if( maSlideBitmaps.begin(), + aEnd, + [&rView] + ( const VectorOfVectorOfSlideBitmaps::value_type& cp ) + { return rView == cp.first; } ), + aEnd ); +} + +void SlideImpl::viewChanged( const UnoViewSharedPtr& rView ) +{ + // nothing to do for the Slide - getCurrentSlideBitmap() lazily + // handles bitmap resizes + if( mbActive && mpLayerManager ) + mpLayerManager->viewChanged(rView); +} + +void SlideImpl::viewsChanged() +{ + // nothing to do for the Slide - getCurrentSlideBitmap() lazily + // handles bitmap resizes + if( mbActive && mpLayerManager ) + mpLayerManager->viewsChanged(); +} + +bool SlideImpl::requestCursor( sal_Int16 nCursorShape ) +{ + mnCurrentCursor = nCursorShape; + return mrCursorManager.requestCursor(mnCurrentCursor); +} + +void SlideImpl::resetCursor() +{ + mnCurrentCursor = awt::SystemPointer::ARROW; + mrCursorManager.resetCursor(); +} + +bool SlideImpl::isAnimated() +{ + // prefetch, but don't apply initial shape attributes + if( !implPrefetchShow() ) + return false; + + return mbHaveAnimations && maAnimations.isAnimated(); +} + +SlideBitmapSharedPtr SlideImpl::createCurrentSlideBitmap( const UnoViewSharedPtr& rView, + const ::basegfx::B2ISize& rBmpSize ) const +{ + ENSURE_OR_THROW( rView && rView->getCanvas(), + "SlideImpl::createCurrentSlideBitmap(): Invalid view" ); + ENSURE_OR_THROW( mpLayerManager, + "SlideImpl::createCurrentSlideBitmap(): Invalid layer manager" ); + ENSURE_OR_THROW( mbShowLoaded, + "SlideImpl::createCurrentSlideBitmap(): No show loaded" ); + + // tdf#96083 ensure end state settings are applied to shapes once when bitmap gets re-rendered + // in that state + if(!mbFinalStateApplied && FINAL_STATE == meAnimationState && mxRootNode.is()) + { + const_cast< SlideImpl* >(this)->mbFinalStateApplied = true; + applyShapeAttributes(mxRootNode, false); + } + + ::cppcanvas::CanvasSharedPtr pCanvas( rView->getCanvas() ); + + // create a bitmap of appropriate size + ::cppcanvas::BitmapSharedPtr pBitmap( + ::cppcanvas::BaseGfxFactory::createBitmap( + pCanvas, + rBmpSize ) ); + + ENSURE_OR_THROW( pBitmap, + "SlideImpl::createCurrentSlideBitmap(): Cannot create page bitmap" ); + + ::cppcanvas::BitmapCanvasSharedPtr pBitmapCanvas( pBitmap->getBitmapCanvas() ); + + ENSURE_OR_THROW( pBitmapCanvas, + "SlideImpl::createCurrentSlideBitmap(): Cannot create page bitmap canvas" ); + + // apply linear part of destination canvas transformation (linear means in this context: + // transformation without any translational components) + ::basegfx::B2DHomMatrix aLinearTransform( rView->getTransformation() ); + aLinearTransform.set( 0, 2, 0.0 ); + aLinearTransform.set( 1, 2, 0.0 ); + pBitmapCanvas->setTransformation( aLinearTransform ); + + // output all shapes to bitmap + initSlideBackground( pBitmapCanvas, rBmpSize ); + mpLayerManager->renderTo( pBitmapCanvas ); + + return std::make_shared<SlideBitmap>( pBitmap ); +} + +class MainSequenceSearcher +{ +public: + MainSequenceSearcher() + { + maSearchKey.Name = "node-type"; + maSearchKey.Value <<= presentation::EffectNodeType::MAIN_SEQUENCE; + } + + void operator()( const uno::Reference< animations::XAnimationNode >& xChildNode ) + { + uno::Sequence< beans::NamedValue > aUserData( xChildNode->getUserData() ); + + if( findNamedValue( aUserData, maSearchKey ) ) + { + maMainSequence = xChildNode; + } + } + + const uno::Reference< animations::XAnimationNode >& getMainSequence() const + { + return maMainSequence; + } + +private: + beans::NamedValue maSearchKey; + uno::Reference< animations::XAnimationNode > maMainSequence; +}; + +bool SlideImpl::implPrefetchShow() +{ + if( mbShowLoaded ) + return true; + + ENSURE_OR_RETURN_FALSE( mxDrawPage.is(), + "SlideImpl::implPrefetchShow(): Invalid draw page" ); + ENSURE_OR_RETURN_FALSE( mpLayerManager, + "SlideImpl::implPrefetchShow(): Invalid layer manager" ); + + // fetch desired page content + // ========================== + + if( !loadShapes() ) + return false; + + // New animations framework: import the shape effect info + // ====================================================== + + try + { + if( mxRootNode.is() ) + { + if( !maAnimations.importAnimations( mxRootNode ) ) + { + OSL_FAIL( "SlideImpl::implPrefetchShow(): have animation nodes, " + "but import animations failed." ); + + // could not import animation framework, + // _although_ some animation nodes are there - + // this is an error (not finding animations at + // all is okay - might be a static slide) + return false; + } + + // now check whether we've got a main sequence (if + // not, we must manually call + // EventMultiplexer::notifySlideAnimationsEnd() + // above, as e.g. interactive sequences alone + // don't block nextEvent() from issuing the next + // slide) + MainSequenceSearcher aSearcher; + if( for_each_childNode( mxRootNode, aSearcher ) ) + mbMainSequenceFound = aSearcher.getMainSequence().is(); + + // import successfully done + mbHaveAnimations = true; + } + } + catch( uno::RuntimeException& ) + { + throw; + } + catch( uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "slideshow", "" ); + // TODO(E2): Error handling. For now, bail out + } + + mbShowLoaded = true; + + return true; +} + +void SlideImpl::enablePaintOverlay() +{ + if( !mbUserPaintOverlayEnabled || !mbPaintOverlayActive ) + { + mbUserPaintOverlayEnabled = true; + activatePaintOverlay(); + } +} + +void SlideImpl::activatePaintOverlay() +{ + if( mbUserPaintOverlayEnabled || !maPolygons.empty() ) + { + mpPaintOverlay = UserPaintOverlay::create( maUserPaintColor, + mdUserPaintStrokeWidth, + maContext, + std::vector(maPolygons), + mbUserPaintOverlayEnabled ); + mbPaintOverlayActive = true; + } +} + +void SlideImpl::drawPolygons() const +{ + if( mpPaintOverlay ) + mpPaintOverlay->drawPolygons(); +} + +void SlideImpl::addPolygons(const PolyPolygonVector& rPolygons) +{ + maPolygons.insert(maPolygons.end(), rPolygons.begin(), rPolygons.end()); +} + +bool SlideImpl::isPaintOverlayActive() const +{ + return mbPaintOverlayActive; +} + +void SlideImpl::deactivatePaintOverlay() +{ + if(mbPaintOverlayActive) + maPolygons = mpPaintOverlay->getPolygons(); + + mpPaintOverlay.reset(); + mbPaintOverlayActive = false; +} + +void SlideImpl::applyShapeAttributes( + const css::uno::Reference< css::animations::XAnimationNode >& xRootAnimationNode, + bool bInitial) const +{ + const uno::Sequence< animations::TargetProperties > aProps( + TargetPropertiesCreator::createTargetProperties( xRootAnimationNode, bInitial ) ); + + // apply extracted values to our shapes + for( const auto& rProp : aProps ) + { + sal_Int16 nParaIndex( -1 ); + uno::Reference< drawing::XShape > xShape( rProp.Target, + uno::UNO_QUERY ); + + if( !xShape.is() ) + { + // not a shape target. Maybe a ParagraphTarget? + presentation::ParagraphTarget aParaTarget; + + if( rProp.Target >>= aParaTarget ) + { + // yep, ParagraphTarget found - extract shape + // and index + xShape = aParaTarget.Shape; + nParaIndex = aParaTarget.Paragraph; + } + } + + if( xShape.is() ) + { + ShapeSharedPtr pShape( mpLayerManager->lookupShape( xShape ) ); + + if( !pShape ) + { + OSL_FAIL( "SlideImpl::applyInitialShapeAttributes(): no shape found for given target" ); + continue; + } + + AttributableShapeSharedPtr pAttrShape( + ::std::dynamic_pointer_cast< AttributableShape >( pShape ) ); + + if( !pAttrShape ) + { + OSL_FAIL( "SlideImpl::applyInitialShapeAttributes(): shape found does not " + "implement AttributableShape interface" ); + continue; + } + + if( nParaIndex != -1 ) + { + // our target is a paragraph subset, thus look + // this up first. + const DocTreeNodeSupplier& rNodeSupplier( pAttrShape->getTreeNodeSupplier() ); + + if( rNodeSupplier.getNumberOfTreeNodes( + DocTreeNode::NodeType::LogicalParagraph ) <= nParaIndex ) + { + OSL_FAIL( "SlideImpl::applyInitialShapeAttributes(): shape found does not " + "provide a subset for requested paragraph index" ); + continue; + } + + pAttrShape = pAttrShape->getSubset( + rNodeSupplier.getTreeNode( + nParaIndex, + DocTreeNode::NodeType::LogicalParagraph ) ); + + if( !pAttrShape ) + { + OSL_FAIL( "SlideImpl::applyInitialShapeAttributes(): shape found does not " + "provide a subset for requested paragraph index" ); + continue; + } + } + + const uno::Sequence< beans::NamedValue >& rShapeProps( rProp.Properties ); + for( const auto& rShapeProp : rShapeProps ) + { + bool bVisible=false; + if( rShapeProp.Name.equalsIgnoreAsciiCase("visibility") && + extractValue( bVisible, + rShapeProp.Value, + pShape, + ::basegfx::B2DSize( getSlideSize() ) )) + { + pAttrShape->setVisibility( bVisible ); + } + else + { + OSL_FAIL( "SlideImpl::applyInitialShapeAttributes(): Unexpected " + "(and unimplemented) property encountered" ); + } + } + } + } +} + +bool SlideImpl::applyInitialShapeAttributes( + const uno::Reference< animations::XAnimationNode >& xRootAnimationNode ) +{ + if( !implPrefetchShow() ) + return false; + + if( !xRootAnimationNode.is() ) + { + meAnimationState = INITIAL_STATE; + + return true; // no animations - no attributes to apply - + // succeeded + } + + applyShapeAttributes(xRootAnimationNode, true); + + meAnimationState = INITIAL_STATE; + + return true; +} + +bool SlideImpl::loadShapes() +{ + if( mbShapesLoaded ) + return true; + + ENSURE_OR_RETURN_FALSE( mxDrawPage.is(), + "SlideImpl::loadShapes(): Invalid draw page" ); + ENSURE_OR_RETURN_FALSE( mpLayerManager, + "SlideImpl::loadShapes(): Invalid layer manager" ); + + // fetch desired page content + // ========================== + + // also take master page content + uno::Reference< drawing::XDrawPage > xMasterPage; + uno::Reference< drawing::XShapes > xMasterPageShapes; + sal_Int32 nCurrCount(0); + + uno::Reference< drawing::XMasterPageTarget > xMasterPageTarget( mxDrawPage, + uno::UNO_QUERY ); + if( xMasterPageTarget.is() ) + { + xMasterPage = xMasterPageTarget->getMasterPage(); + xMasterPageShapes = xMasterPage; + + if( xMasterPage.is() && xMasterPageShapes.is() ) + { + // TODO(P2): maybe cache master pages here (or treat the + // masterpage as a single metafile. At least currently, + // masterpages do not contain animation effects) + try + { + // load the masterpage shapes + + ShapeImporter aMPShapesFunctor( xMasterPage, + mxDrawPage, + mxDrawPagesSupplier, + maContext, + 0, /* shape num starts at 0 */ + true ); + + mpLayerManager->addShape( + aMPShapesFunctor.importBackgroundShape() ); + + while( !aMPShapesFunctor.isImportDone() ) + { + ShapeSharedPtr const& rShape( + aMPShapesFunctor.importShape() ); + if( rShape ) + { + rShape->setIsForeground(false); + mpLayerManager->addShape( rShape ); + } + } + addPolygons(aMPShapesFunctor.getPolygons()); + + nCurrCount = static_cast<sal_Int32>(aMPShapesFunctor.getImportedShapesCount()); + } + catch( uno::RuntimeException& ) + { + throw; + } + catch( ShapeLoadFailedException& ) + { + // TODO(E2): Error handling. For now, bail out + TOOLS_WARN_EXCEPTION( "slideshow", "SlideImpl::loadShapes(): caught ShapeLoadFailedException" ); + return false; + + } + catch( uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "slideshow", "" ); + return false; + } + } + } + + try + { + // load the normal page shapes + + + ShapeImporter aShapesFunctor( mxDrawPage, + mxDrawPage, + mxDrawPagesSupplier, + maContext, + nCurrCount, + false ); + + while( !aShapesFunctor.isImportDone() ) + { + ShapeSharedPtr const& rShape( + aShapesFunctor.importShape() ); + if( rShape ) + mpLayerManager->addShape( rShape ); + } + addPolygons(aShapesFunctor.getPolygons()); + } + catch( uno::RuntimeException& ) + { + throw; + } + catch( ShapeLoadFailedException& ) + { + // TODO(E2): Error handling. For now, bail out + TOOLS_WARN_EXCEPTION( "slideshow", "SlideImpl::loadShapes(): caught ShapeLoadFailedException" ); + return false; + } + catch( uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "slideshow", "" ); + return false; + } + + mbShapesLoaded = true; + + return true; +} + +basegfx::B2ISize SlideImpl::getSlideSizeImpl() const +{ + uno::Reference< beans::XPropertySet > xPropSet( + mxDrawPage, uno::UNO_QUERY_THROW ); + + sal_Int32 nDocWidth = 0; + sal_Int32 nDocHeight = 0; + xPropSet->getPropertyValue("Width") >>= nDocWidth; + xPropSet->getPropertyValue("Height") >>= nDocHeight; + + return basegfx::B2ISize( nDocWidth, nDocHeight ); +} + +} // namespace + + +SlideSharedPtr createSlide( const uno::Reference< drawing::XDrawPage >& xDrawPage, + const uno::Reference<drawing::XDrawPagesSupplier>& xDrawPages, + const uno::Reference< animations::XAnimationNode >& xRootNode, + EventQueue& rEventQueue, + EventMultiplexer& rEventMultiplexer, + ScreenUpdater& rScreenUpdater, + ActivitiesQueue& rActivitiesQueue, + UserEventQueue& rUserEventQueue, + CursorManager& rCursorManager, + MediaFileManager& rMediaFileManager, + const UnoViewContainer& rViewContainer, + const uno::Reference< uno::XComponentContext >& xComponentContext, + const ShapeEventListenerMap& rShapeListenerMap, + const ShapeCursorMap& rShapeCursorMap, + PolyPolygonVector&& rPolyPolygonVector, + RGBColor const& rUserPaintColor, + double dUserPaintStrokeWidth, + bool bUserPaintEnabled, + bool bIntrinsicAnimationsAllowed, + bool bDisableAnimationZOrder ) +{ + auto pRet = std::make_shared<SlideImpl>( xDrawPage, xDrawPages, xRootNode, rEventQueue, + rEventMultiplexer, rScreenUpdater, + rActivitiesQueue, rUserEventQueue, + rCursorManager, rMediaFileManager, rViewContainer, + xComponentContext, rShapeListenerMap, + rShapeCursorMap, std::move(rPolyPolygonVector), rUserPaintColor, + dUserPaintStrokeWidth, bUserPaintEnabled, + bIntrinsicAnimationsAllowed, + bDisableAnimationZOrder ); + + rEventMultiplexer.addViewHandler( pRet ); + + return pRet; +} + +} // namespace slideshow + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |