diff options
-rw-r--r-- | slideshow/source/engine/shapes/drawshape.cxx | 62 | ||||
-rw-r--r-- | slideshow/source/engine/shapes/drawshape.hxx | 37 | ||||
-rw-r--r-- | slideshow/source/engine/shapes/gdimtftools.cxx | 248 | ||||
-rw-r--r-- | slideshow/source/engine/shapes/gdimtftools.hxx | 24 | ||||
-rw-r--r-- | slideshow/source/engine/shapes/shapeimporter.cxx | 7 |
5 files changed, 251 insertions, 127 deletions
diff --git a/slideshow/source/engine/shapes/drawshape.cxx b/slideshow/source/engine/shapes/drawshape.cxx index 185b76cc23d3..de89822dac58 100644 --- a/slideshow/source/engine/shapes/drawshape.cxx +++ b/slideshow/source/engine/shapes/drawshape.cxx @@ -343,6 +343,7 @@ namespace slideshow::internal mxPage( xContainingPage ), maAnimationFrames(), // empty, we don't have no intrinsic animation mnCurrFrame(0), + mpGraphicLoader(), mpCurrMtf(), mnCurrMtfLoadFlags( bForeignSource ? MTF_LOAD_FOREIGN_SOURCE : MTF_LOAD_NONE ), @@ -418,12 +419,13 @@ namespace slideshow::internal DrawShape::DrawShape( const uno::Reference< drawing::XShape >& xShape, uno::Reference< drawing::XDrawPage > xContainingPage, double nPrio, - const Graphic& rGraphic, + std::shared_ptr<Graphic> pGraphic, const SlideShowContext& rContext ) : mxShape( xShape ), mxPage(std::move( xContainingPage )), maAnimationFrames(), mnCurrFrame(0), + mpGraphicLoader(), mpCurrMtf(), mnCurrMtfLoadFlags( MTF_LOAD_NONE ), maCurrentShapeUnitBounds(), @@ -450,12 +452,25 @@ namespace slideshow::internal mbDrawingLayerAnim( false ), mbContainsPageField( false ) { - ENSURE_OR_THROW( rGraphic.IsAnimated(), + ENSURE_OR_THROW( pGraphic->IsAnimated(), "DrawShape::DrawShape(): Graphic is no animation" ); - getAnimationFromGraphic( maAnimationFrames, - mnAnimationLoopCount, - rGraphic ); + ::Animation aAnimation(pGraphic->GetAnimation()); + const Size aAnimSize(aAnimation.GetDisplaySizePixel()); + tools::Long nBitmapPixels = aAnimSize.getWidth() * aAnimSize.getHeight(); + + tools::Long nFramesToLoad = aAnimation.Count(); + + // if the Animation is bigger then 5 million pixels, we do not load the + // whole animation now. + if (nBitmapPixels * aAnimation.Count() > 5000000) + { + nFramesToLoad = 5000000 / nBitmapPixels; + if (nFramesToLoad < 10) + nFramesToLoad = 10; + } + mpGraphicLoader = ::std::make_unique<DelayedGraphicLoader>(pGraphic); + getSomeAnimationFramesFromGraphic(nFramesToLoad); ENSURE_OR_THROW( !maAnimationFrames.empty() && maAnimationFrames.front().mpMtf, @@ -475,6 +490,7 @@ namespace slideshow::internal maAnimationFrames(), // don't copy animations for subsets, // only the current frame! mnCurrFrame(0), + mpGraphicLoader(), mpCurrMtf( rSrc.mpCurrMtf ), mnCurrMtfLoadFlags( rSrc.mnCurrMtfLoadFlags ), maCurrentShapeUnitBounds(), @@ -550,13 +566,13 @@ namespace slideshow::internal const uno::Reference< drawing::XShape >& xShape, const uno::Reference< drawing::XDrawPage >& xContainingPage, double nPrio, - const Graphic& rGraphic, + std::shared_ptr<Graphic> pGraphic, const SlideShowContext& rContext ) { DrawShapeSharedPtr pShape( new DrawShape(xShape, xContainingPage, nPrio, - rGraphic, + pGraphic, rContext) ); if( pShape->hasIntrinsicAnimation() ) @@ -847,6 +863,10 @@ namespace slideshow::internal ENSURE_OR_RETURN_VOID( nCurrFrame < maAnimationFrames.size(), "DrawShape::setIntrinsicAnimationFrame(): frame index out of bounds" ); + // Load 1 more frame if needed. (make sure the current frame is loded) + if (mpGraphicLoader) + getSomeAnimationFramesFromGraphic(1, nCurrFrame); + if( mnCurrFrame != nCurrFrame ) { mnCurrFrame = nCurrFrame; @@ -1235,6 +1255,34 @@ namespace slideshow::internal { return maSubsetting.getSubsetTreeNode( rParentNode, nNodeIndex, eNodeType ); } + + void DrawShape::getSomeAnimationFramesFromGraphic(::std::size_t nFrameCount, + ::std::size_t nLastToLoad /* = 0*/) + { + OSL_ASSERT(mpGraphicLoader); + + //load nFrameCount frames or to nLastToLoad + ::std::size_t nFramesToLoad = nFrameCount; + if (nLastToLoad > mpGraphicLoader->mnLoadedFrames + nFrameCount) + nFramesToLoad = nLastToLoad - mpGraphicLoader->mnLoadedFrames; + + getAnimationFromGraphic(maAnimationFrames, mnAnimationLoopCount, + mpGraphicLoader->mpGraphic, mpGraphicLoader->mpVDev, + mpGraphicLoader->mpVDevMask, mpGraphicLoader->mnLoadedFrames, + nFramesToLoad); + + // If the Animation is fully loaded, no need to load anymore. + if (mpGraphicLoader->mnLoadedFrames >= maAnimationFrames.size()) + { + mpGraphicLoader.reset(); + } + } + + DelayedGraphicLoader::DelayedGraphicLoader(std::shared_ptr<Graphic> pGraphic) + : mpGraphic(pGraphic) + , mpVDevMask(DeviceFormat::WITHOUT_ALPHA) + { + } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/drawshape.hxx b/slideshow/source/engine/shapes/drawshape.hxx index 6c71d151b6fa..efa55299f435 100644 --- a/slideshow/source/engine/shapes/drawshape.hxx +++ b/slideshow/source/engine/shapes/drawshape.hxx @@ -30,6 +30,8 @@ #include "viewshape.hxx" #include <hyperlinkarea.hxx> +#include <vcl/virdev.hxx> + #include <optional> #include <vector> @@ -43,6 +45,22 @@ namespace slideshow::internal class DrawShape; typedef ::std::shared_ptr< DrawShape > DrawShapeSharedPtr; + /** This Struct store data needed to make Animation from Graphic. + If the Animation is too big, we won't load all of it at once. + From time to time we load small parts until it is completely loaded. + Because of it, we have to keep some data alive until the animation + is fully loaded. + */ + struct DelayedGraphicLoader + { + sal_uInt16 mnLoadedFrames = 0; + ::std::shared_ptr<Graphic> mpGraphic; + ScopedVclPtrInstance<VirtualDevice> mpVDev; + ScopedVclPtrInstance<VirtualDevice> mpVDevMask; + + DelayedGraphicLoader(std::shared_ptr<Graphic> pGraphic); + }; + /** This class is the representation of a draw document's XShape, and implements the Shape, AnimatableShape, and AttributableShape interfaces. @@ -107,7 +125,8 @@ namespace slideshow::internal const css::uno::Reference< css::drawing::XShape >& xShape, const css::uno::Reference< css::drawing::XDrawPage >& xContainingPage, double nPrio, - const Graphic& rGraphic, + std::shared_ptr<Graphic> pGraphic, + const SlideShowContext& rContext ); // throw ShapeLoadFailedException; virtual css::uno::Reference< css::drawing::XShape > getXShape() const override; @@ -199,6 +218,19 @@ namespace slideshow::internal */ GDIMetaFileSharedPtr const & forceScrollTextMetaFile(); + /** extract some Animation Frames from the Graphic + set in DelayedGraphicLoader. + + @param nFrameCount + Load this many frames. + + @param nLastToLoad + If nLastToLoad > nFrameCount + loadedFrames then + Load frames until this frame. + */ + void getSomeAnimationFramesFromGraphic(::std::size_t nFrameCount, + ::std::size_t nLastToLoad = 0); + private: /** Create a shape for the given XShape @@ -251,7 +283,7 @@ namespace slideshow::internal DrawShape( const css::uno::Reference< css::drawing::XShape >& xShape, css::uno::Reference< css::drawing::XDrawPage > xContainingPage, double nPrio, - const Graphic& rGraphic, + std::shared_ptr<Graphic> pGraphic, const SlideShowContext& rContext ); // throw ShapeLoadFailedException; /** Private copy constructor @@ -282,6 +314,7 @@ namespace slideshow::internal */ mutable VectorOfMtfAnimationFrames maAnimationFrames; ::std::size_t mnCurrFrame; + ::std::unique_ptr<DelayedGraphicLoader> mpGraphicLoader; //to load more Frames later /// Metafile of currently active frame (static for shapes w/o intrinsic animation) mutable GDIMetaFileSharedPtr mpCurrMtf; diff --git a/slideshow/source/engine/shapes/gdimtftools.cxx b/slideshow/source/engine/shapes/gdimtftools.cxx index 0ca2673daa5f..d4f55928562f 100644 --- a/slideshow/source/engine/shapes/gdimtftools.cxx +++ b/slideshow/source/engine/shapes/gdimtftools.cxx @@ -248,101 +248,119 @@ sal_Int32 getNextActionOffset( MetaAction * pCurrAct ) bool getAnimationFromGraphic( VectorOfMtfAnimationFrames& o_rFrames, sal_uInt32& o_rLoopCount, - const Graphic& rGraphic ) + std::shared_ptr<Graphic> pGraphic, + ScopedVclPtrInstance<VirtualDevice>& pVDev, + ScopedVclPtrInstance<VirtualDevice>& pVDevMask, + sal_uInt16& mnLoadedFrames, + sal_uInt16 nFramesToLoad ) { - o_rFrames.clear(); + bool bFirstRun = mnLoadedFrames == 0; + if (bFirstRun) + o_rFrames.clear(); - if( !rGraphic.IsAnimated() ) + if( !pGraphic->IsAnimated() ) return false; // some loop invariants - ::Animation aAnimation( rGraphic.GetAnimation() ); + ::Animation aAnimation( pGraphic->GetAnimation() ); const Point aEmptyPoint; const Size aAnimSize( aAnimation.GetDisplaySizePixel() ); - // setup VDev, into which all bitmaps are painted (want to - // normalize animations to n bitmaps of same size. An Animation, - // though, can contain bitmaps of varying sizes and different - // update modes) - ScopedVclPtrInstance< VirtualDevice > pVDev; - pVDev->SetOutputSizePixel( aAnimSize ); - pVDev->EnableMapMode( false ); - - // setup mask VDev (alpha VDev is currently rather slow) - ScopedVclPtrInstance<VirtualDevice> pVDevMask(DeviceFormat::WITHOUT_ALPHA); - pVDevMask->SetOutputSizePixel( aAnimSize ); - pVDevMask->EnableMapMode( false ); - - // tdf#156630 make erase calls fill with transparency - pVDev->SetBackground( Wallpaper( COL_BLACK ) ); - pVDevMask->SetBackground( Wallpaper( COL_ALPHA_TRANSPARENT ) ); - - o_rLoopCount = aAnimation.GetLoopCount(); - - for( sal_uInt16 i=0, nCount=aAnimation.Count(); i<nCount; ++i ) + if (bFirstRun) + { + // setup VDev, into which all bitmaps are painted (want to + // normalize animations to n bitmaps of same size. An Animation, + // though, can contain bitmaps of varying sizes and different + // update modes) + pVDev->SetOutputSizePixel(aAnimSize); + pVDev->EnableMapMode(false); + + // setup mask VDev (alpha VDev is currently rather slow) + pVDevMask->SetOutputSizePixel(aAnimSize); + pVDevMask->EnableMapMode(false); + + // tdf#156630 make erase calls fill with transparency + pVDev->SetBackground(Wallpaper(COL_BLACK)); + pVDevMask->SetBackground(Wallpaper(COL_ALPHA_TRANSPARENT)); + + o_rLoopCount = aAnimation.GetLoopCount(); + } + sal_uInt16 nCount = aAnimation.Count(); + if (!bFirstRun && mnLoadedFrames + nFramesToLoad < nCount) + nCount = mnLoadedFrames + nFramesToLoad; + for (sal_uInt16 i = mnLoadedFrames; i < nCount; ++i) { const AnimationFrame& rAnimationFrame( aAnimation.Get(i) ); - switch(rAnimationFrame.meDisposal) + bool bCalculateNow = !bFirstRun || i < nFramesToLoad; + if (bCalculateNow) { - case Disposal::Not: + switch (rAnimationFrame.meDisposal) { - pVDev->DrawBitmapEx(rAnimationFrame.maPositionPixel, - rAnimationFrame.maBitmapEx); - AlphaMask aMask = rAnimationFrame.maBitmapEx.GetAlphaMask(); - - if( aMask.IsEmpty() ) + case Disposal::Not: { - const tools::Rectangle aRect(aEmptyPoint, - pVDevMask->GetOutputSizePixel()); - const Wallpaper aWallpaper(COL_BLACK); - pVDevMask->DrawWallpaper(aRect, - aWallpaper); - } - else - { - BitmapEx aTmpMask(aMask.GetBitmap(), aMask); - pVDevMask->DrawBitmapEx(rAnimationFrame.maPositionPixel, - aTmpMask ); + pVDev->DrawBitmapEx(rAnimationFrame.maPositionPixel, + rAnimationFrame.maBitmapEx); + AlphaMask aMask = rAnimationFrame.maBitmapEx.GetAlphaMask(); + + if (aMask.IsEmpty()) + { + const tools::Rectangle aRect(aEmptyPoint, pVDevMask->GetOutputSizePixel()); + const Wallpaper aWallpaper(COL_BLACK); + pVDevMask->DrawWallpaper(aRect, aWallpaper); + } + else + { + BitmapEx aTmpMask(aMask.GetBitmap(), aMask); + pVDevMask->DrawBitmapEx(rAnimationFrame.maPositionPixel, aTmpMask); + } + break; } - break; - } - - case Disposal::Back: - { - // #i70772# react on no mask - const AlphaMask aMask(rAnimationFrame.maBitmapEx.GetAlphaMask()); - const Bitmap & rContent(rAnimationFrame.maBitmapEx.GetBitmap()); - pVDevMask->Erase(); - pVDev->DrawBitmap(rAnimationFrame.maPositionPixel, rContent); - - if(aMask.IsEmpty()) + case Disposal::Back: { - const tools::Rectangle aRect(rAnimationFrame.maPositionPixel, rContent.GetSizePixel()); - pVDevMask->SetFillColor( COL_BLACK); - pVDevMask->SetLineColor(); - pVDevMask->DrawRect(aRect); + // #i70772# react on no mask + const AlphaMask aMask(rAnimationFrame.maBitmapEx.GetAlphaMask()); + const Bitmap& rContent(rAnimationFrame.maBitmapEx.GetBitmap()); + + pVDevMask->Erase(); + pVDev->DrawBitmap(rAnimationFrame.maPositionPixel, rContent); + + if (aMask.IsEmpty()) + { + const tools::Rectangle aRect(rAnimationFrame.maPositionPixel, + rContent.GetSizePixel()); + pVDevMask->SetFillColor(COL_BLACK); + pVDevMask->SetLineColor(); + pVDevMask->DrawRect(aRect); + } + else + { + pVDevMask->DrawBitmap(rAnimationFrame.maPositionPixel, aMask.GetBitmap()); + } + break; } - else + + case Disposal::Previous: { - pVDevMask->DrawBitmap(rAnimationFrame.maPositionPixel, aMask.GetBitmap()); + pVDev->DrawBitmapEx(rAnimationFrame.maPositionPixel, + rAnimationFrame.maBitmapEx); + pVDevMask->DrawBitmap(rAnimationFrame.maPositionPixel, + rAnimationFrame.maBitmapEx.GetAlphaMask().GetBitmap()); + break; } - break; - } - - case Disposal::Previous : - { - pVDev->DrawBitmapEx(rAnimationFrame.maPositionPixel, - rAnimationFrame.maBitmapEx); - pVDevMask->DrawBitmap(rAnimationFrame.maPositionPixel, - rAnimationFrame.maBitmapEx.GetAlphaMask().GetBitmap()); - break; } } - // extract current aVDev content into a new animation // frame - GDIMetaFileSharedPtr pMtf = std::make_shared<GDIMetaFile>(); + GDIMetaFileSharedPtr pMtf; + if (bFirstRun) + { + pMtf = std::make_shared<GDIMetaFile>(); + } + else + { + pMtf = o_rFrames[i].mpMtf; + } bool useAlphaMask = false; #if defined(MACOSX) || defined(IOS) useAlphaMask = true; @@ -351,52 +369,58 @@ bool getAnimationFromGraphic( VectorOfMtfAnimationFrames& o_rFrames, if( SkiaHelper::isVCLSkiaEnabled()) useAlphaMask = true; #endif - if( useAlphaMask ) + if (bCalculateNow) { - AlphaMask aAlphaMask(pVDevMask->GetBitmap(aEmptyPoint, aAnimSize)); - pMtf->AddAction( - new MetaBmpExAction( aEmptyPoint, - BitmapEx( - pVDev->GetBitmap( - aEmptyPoint, - aAnimSize ), - aAlphaMask))); + if( useAlphaMask ) + { + AlphaMask aAlphaMask(pVDevMask->GetBitmap(aEmptyPoint, aAnimSize)); + pMtf->AddAction( + new MetaBmpExAction( aEmptyPoint, + BitmapEx( + pVDev->GetBitmap( + aEmptyPoint, + aAnimSize ), + aAlphaMask))); + } + else + { + Bitmap aAlphaMask = pVDevMask->GetBitmap(aEmptyPoint, aAnimSize); + aAlphaMask.Invert(); // convert from transparency to alpha + pMtf->AddAction( + new MetaBmpExAction( aEmptyPoint, + BitmapEx( + pVDev->GetBitmap( + aEmptyPoint, + aAnimSize ), + aAlphaMask))); + } + mnLoadedFrames = i+1; } - else + if (bFirstRun) { - Bitmap aAlphaMask = pVDevMask->GetBitmap(aEmptyPoint, aAnimSize); - aAlphaMask.Invert(); // convert from transparency to alpha - pMtf->AddAction( - new MetaBmpExAction( aEmptyPoint, - BitmapEx( - pVDev->GetBitmap( - aEmptyPoint, - aAnimSize ), - aAlphaMask))); - } + // setup mtf dimensions and pref map mode (for + // simplicity, keep it all in pixel. the metafile + // renderer scales it down to (1, 1) box anyway) + pMtf->SetPrefMapMode(MapMode()); + pMtf->SetPrefSize(aAnimSize); + + // Take care of special value for MultiPage TIFFs. ATM these shall just + // show their first page for _quite_ some time. + sal_Int32 nWaitTime100thSeconds(rAnimationFrame.mnWait); + if (ANIMATION_TIMEOUT_ON_CLICK == nWaitTime100thSeconds) + { + // ATM the huge value would block the timer, so use a long + // time to show first page (whole day) + nWaitTime100thSeconds = 100 * 60 * 60 * 24; + } - // setup mtf dimensions and pref map mode (for - // simplicity, keep it all in pixel. the metafile - // renderer scales it down to (1, 1) box anyway) - pMtf->SetPrefMapMode( MapMode() ); - pMtf->SetPrefSize( aAnimSize ); + // There are animated GIFs with no WaitTime set. Take 0.1 sec, the + // same duration that is used by the edit view. + if (nWaitTime100thSeconds == 0) + nWaitTime100thSeconds = 10; - // Take care of special value for MultiPage TIFFs. ATM these shall just - // show their first page for _quite_ some time. - sal_Int32 nWaitTime100thSeconds(rAnimationFrame.mnWait); - if( ANIMATION_TIMEOUT_ON_CLICK == nWaitTime100thSeconds ) - { - // ATM the huge value would block the timer, so use a long - // time to show first page (whole day) - nWaitTime100thSeconds = 100 * 60 * 60 * 24; + o_rFrames.emplace_back(pMtf, nWaitTime100thSeconds / 100.0); } - - // There are animated GIFs with no WaitTime set. Take 0.1 sec, the - // same duration that is used by the edit view. - if( nWaitTime100thSeconds == 0 ) - nWaitTime100thSeconds = 10; - - o_rFrames.emplace_back( pMtf, nWaitTime100thSeconds / 100.0 ); } return !o_rFrames.empty(); diff --git a/slideshow/source/engine/shapes/gdimtftools.hxx b/slideshow/source/engine/shapes/gdimtftools.hxx index 8395efeb00a2..c23b16ae53c5 100644 --- a/slideshow/source/engine/shapes/gdimtftools.hxx +++ b/slideshow/source/engine/shapes/gdimtftools.hxx @@ -27,6 +27,8 @@ #include <basegfx/range/b2drectangle.hxx> +#include <vcl/virdev.hxx> + #include <tools.hxx> #include <utility> @@ -101,6 +103,8 @@ namespace slideshow::internal sal_Int32 getNextActionOffset( MetaAction * pCurrAct ); /** Extract a vector of animation frames from given Graphic. + It can be used to extract only a few frames, and can be + called later to extract more. (If the Animation is big) @param o_rFrames Resulting vector of animated metafiles @@ -108,12 +112,28 @@ namespace slideshow::internal @param o_rLoopCount Number of times the bitmap animation shall be repeated - @param rGraphic + @param pGraphic Input graphic object, to extract animations from + + @param pVDev, pVDevMask + Virtual devices. We don't want to create new everytime we load some frames. + + @param nLoadedFrames + The count of loaded Frames. + + @param nFramesToLoad + The count of Frames need to be extracted now. + Bigger nFramesToLoad not result an error, the function will + stop extracting at the end of the animation anyway. */ + bool getAnimationFromGraphic(VectorOfMtfAnimationFrames& o_rFrames, sal_uInt32& o_rLoopCount, - const Graphic& rGraphic); + std::shared_ptr<Graphic> pGraphic, + ScopedVclPtrInstance<VirtualDevice> &pVDev, + ScopedVclPtrInstance<VirtualDevice> &pVDevMask, + sal_uInt16& nLoadedFrames, + sal_uInt16 nFramesToLoad); /** Retrieve scroll text animation rectangles from given metafile diff --git a/slideshow/source/engine/shapes/shapeimporter.cxx b/slideshow/source/engine/shapes/shapeimporter.cxx index 7823a5588efd..33cb42469d31 100644 --- a/slideshow/source/engine/shapes/shapeimporter.cxx +++ b/slideshow/source/engine/shapes/shapeimporter.cxx @@ -311,9 +311,8 @@ ShapeSharedPtr ShapeImporter::createShape( // fetch readily transformed and color-modified // graphic - - Graphic aGraphic( - xGraphicObject->GetTransformedGraphic( + std::shared_ptr<Graphic> pGraphic + = ::std::make_shared<Graphic>(xGraphicObject->GetTransformedGraphic( xGraphicObject->GetPrefSize(), xGraphicObject->GetPrefMapMode(), aGraphAttrs ) ); @@ -321,7 +320,7 @@ ShapeSharedPtr ShapeImporter::createShape( return DrawShape::create( xCurrShape, mxPage, mnAscendingPrio, - aGraphic, + pGraphic, mrContext ); } else |