tdf#153162 Animation load optimization

Loading a big GIF animation can freeze LO for a long time, so:

changed getAnimationFromGraphic, it can load parts of a big animation.
It can be called several times to load the whole animation.
Now it can load animation while it is playing.

It may still load smaller animations at once before it is rendered.
At first it load frames that sum maximum 5 million pixels.
(But minimum 10 frame.)

Changed the Graphic parameter to shared_ptr, so it won’t be deleted
until the whole animation is loaded.

Change-Id: I5ac16d7ee4883dbaefb604cd07757d19e5aa2939
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166608
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
Tested-by: Caolán McNamara <caolan.mcnamara@collabora.com>
diff --git a/slideshow/source/engine/shapes/drawshape.cxx b/slideshow/source/engine/shapes/drawshape.cxx
index 185b76c..de89822 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 6c71d15..efa5529 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 0ca2673..d4f5592 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 );
    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)
    ScopedVclPtrInstance<VirtualDevice> pVDevMask(DeviceFormat::WITHOUT_ALPHA);
    pVDevMask->SetOutputSizePixel( aAnimSize );
    pVDevMask->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 ) );
        // 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 )
        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);
                    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;
                }
                else

                case Disposal::Back:
                {
                    BitmapEx aTmpMask(aMask.GetBitmap(), aMask);
                    pVDevMask->DrawBitmapEx(rAnimationFrame.maPositionPixel,
                                            aTmpMask );
                    // #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;
                }
                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::Previous:
                {
                    const tools::Rectangle aRect(rAnimationFrame.maPositionPixel, rContent.GetSizePixel());
                    pVDevMask->SetFillColor( COL_BLACK);
                    pVDevMask->SetLineColor();
                    pVDevMask->DrawRect(aRect);
                    pVDev->DrawBitmapEx(rAnimationFrame.maPositionPixel,
                                        rAnimationFrame.maBitmapEx);
                    pVDevMask->DrawBitmap(rAnimationFrame.maPositionPixel,
                                          rAnimationFrame.maBitmapEx.GetAlphaMask().GetBitmap());
                    break;
                }
                else
                {
                    pVDevMask->DrawBitmap(rAnimationFrame.maPositionPixel, aMask.GetBitmap());
                }
                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;
            }

            // 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);
        }

        // 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;
        }

        // 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 8395efe..c23b16a 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 7823a55..33cb424 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