tdf#156630 eliminate opaque parts when drawing animated PNG images

Due to the switch from transparency to alpha in commit
81994cb2b8b32453a92bcb011830fcb884f22ff3, flip the background colors
for the VirtualDevices and invert an alpha mask.

On macOS, with Skia/Raster with a Retina display (i.e.  2.0 window
scale), the alpha mask gets upscaled. Also, when Skia is enabled,
the alpha mask gets inverted in the first export to PDF after
launching the application. These two bugs appear to be caused by
asynchronous rendering of the returned bitmap. So, we force a copy
of the alpha mask in case it changes before the bitmap is actually
drawn.

Lastly, respect system animation settings when determining if the image
should be animated.

Change-Id: I8144691a6c99bf8361b301b88d22172991463f26
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155429
Tested-by: Jenkins
Reviewed-by: Patrick Luby <plubius@neooffice.org>
diff --git a/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx b/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx
index f3c4cf69..7c9f7b3 100644
--- a/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx
+++ b/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx
@@ -104,6 +104,10 @@ namespace drawinglayer::primitive2d
                    maVirtualDeviceMask->EnableMapMode(false);
                    maVirtualDevice->SetOutputSizePixel(aTarget);
                    maVirtualDeviceMask->SetOutputSizePixel(aTarget);

                    // tdf#156630 make erase calls fill with transparency
                    maVirtualDevice->SetBackground(COL_BLACK);
                    maVirtualDeviceMask->SetBackground(COL_ALPHA_TRANSPARENT);
                }

                maVirtualDevice->Erase();
@@ -176,12 +180,26 @@ namespace drawinglayer::primitive2d
                BitmapEx bitmap;
                if( useAlphaMask )
                {
                    const AlphaMask aMaskBitmap(maVirtualDeviceMask->GetBitmap(Point(), maVirtualDeviceMask->GetOutputSizePixel()));
                    AlphaMask aMaskBitmap(maVirtualDeviceMask->GetBitmap(Point(), maVirtualDeviceMask->GetOutputSizePixel()));

                    // Related tdf#156630 force snapshot of alpha mask
                    // On macOS, with Skia/Raster with a Retina display (i.e.
                    // 2.0 window scale), the alpha mask gets upscaled. Also,
                    // when Skia is enabled, the alpha mask gets inverted in
                    // the first export to PDF after launching the application.
                    // These two bugs appear to be caused by asynchronous
                    // rendering of the returned bitmap. So, we force a copy
                    // of the alpha mask in case it changes before the bitmap
                    // is actually drawn.
                    AlphaMask::ScopedReadAccess pAccessAlpha(aMaskBitmap);

                    bitmap = BitmapEx(aMainBitmap, aMaskBitmap);
                }
                else
                {
                    const Bitmap aMaskBitmap(maVirtualDeviceMask->GetBitmap(Point(), maVirtualDeviceMask->GetOutputSizePixel()));
                    Bitmap aMaskBitmap(maVirtualDeviceMask->GetBitmap(Point(), maVirtualDeviceMask->GetOutputSizePixel()));
                    // tdf#156630 invert the alpha mask
                    aMaskBitmap.Invert(); // convert from transparency to alpha
                    bitmap = BitmapEx(aMainBitmap, aMaskBitmap);
                }

diff --git a/slideshow/source/engine/shapes/gdimtftools.cxx b/slideshow/source/engine/shapes/gdimtftools.cxx
index c5a215e..1928052 100644
--- a/slideshow/source/engine/shapes/gdimtftools.cxx
+++ b/slideshow/source/engine/shapes/gdimtftools.cxx
@@ -37,6 +37,7 @@
#include <vcl/gdimtf.hxx>
#include <vcl/animate/Animation.hxx>
#include <vcl/graph.hxx>
#include <vcl/skia/SkiaHelper.hxx>

#include <tools.hxx>

@@ -272,6 +273,10 @@ bool getAnimationFromGraphic( VectorOfMtfAnimationFrames&   o_rFrames,
    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 )
@@ -338,15 +343,49 @@ bool getAnimationFromGraphic( VectorOfMtfAnimationFrames&   o_rFrames,
        // extract current aVDev content into a new animation
        // frame
        GDIMetaFileSharedPtr pMtf = std::make_shared<GDIMetaFile>();
        Bitmap aAlphaMask = pVDevMask->GetBitmap(aEmptyPoint, aAnimSize);
        aAlphaMask.Invert(); // convert from transparency to alpha
        pMtf->AddAction(
            new MetaBmpExAction( aEmptyPoint,
                                 BitmapEx(
                                     pVDev->GetBitmap(
                                         aEmptyPoint,
                                         aAnimSize ),
                                     aAlphaMask)));
        bool useAlphaMask = false;
#if defined(MACOSX) || defined(IOS)
        useAlphaMask = true;
#else
        // GetBitmap()-> AlphaMask is optimized with SkiaSalBitmap::InterpretAs8Bit(), 1bpp mask is not.
        if( SkiaHelper::isVCLSkiaEnabled())
            useAlphaMask = true;
#endif
        if( useAlphaMask )
        {
            AlphaMask aAlphaMask(pVDevMask->GetBitmap(aEmptyPoint, aAnimSize));

            // Related tdf#156630 force snapshot of alpha mask
            // On macOS, with Skia/Raster with a Retina display (i.e.
            // 2.0 window scale), the alpha mask gets upscaled. Also,
            // when Skia is enabled, the alpha mask gets inverted in
            // the first export to PDF after launching the application.
            // These two bugs appear to be caused by asynchronous
            // rendering of the returned bitmap. So, we force a copy
            // of the alpha mask in case it changes before the bitmap
            // is actually drawn.
            AlphaMask::ScopedReadAccess pAccessAlpha(aAlphaMask);

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

        // setup mtf dimensions and pref map mode (for
        // simplicity, keep it all in pixel. the metafile
diff --git a/svx/source/sdr/contact/objectcontactofpageview.cxx b/svx/source/sdr/contact/objectcontactofpageview.cxx
index eadfe78..4bc227b 100644
--- a/svx/source/sdr/contact/objectcontactofpageview.cxx
+++ b/svx/source/sdr/contact/objectcontactofpageview.cxx
@@ -372,7 +372,9 @@ namespace sdr::contact
        {
            if (utl::ConfigManager::IsFuzzing())
                return true;
            return SvtAccessibilityOptions::GetIsAllowAnimatedGraphics();

            // Related tdf#156630 respect system animation setting
            return SvtAccessibilityOptions::GetIsAllowAnimatedGraphics() && !MiscSettings::GetUseReducedAnimation();
        }

        // print?
diff --git a/vcl/skia/osx/gdiimpl.cxx b/vcl/skia/osx/gdiimpl.cxx
index d774077..abe9bef 100644
--- a/vcl/skia/osx/gdiimpl.cxx
+++ b/vcl/skia/osx/gdiimpl.cxx
@@ -135,7 +135,10 @@ void AquaSkiaSalGraphicsImpl::flushSurfaceToScreenCG()
        // by creating a subset SkImage (which as is said above copies data), or set the x coordinate
        // to 0, which will then make rowBytes() match the actual data.
        mDirtyRect.fLeft = 0;
        assert(mDirtyRect.width() == pixmap.bounds().width());
        // Related tdf#156630 pixmaps can be wider than the dirty rectangle
        // This seems to most commonly occur when SAL_FORCE_HIDPI_SCALING=1
        // and the native window scale is 2.
        assert(mDirtyRect.width() <= pixmap.bounds().width());
    }

    // tdf#145843 Do not use CGBitmapContextCreate() to create a bitmap context