Support buffering SystemDependent GraphicData (II)

In this step I have changed all calls that use a
B2DPolyPolygon and do filled graphics, added support for
providing needed transformation which will -if supported-
be used. Added buffering of SystemDependentData at
B2DPolyPolygon for that purpose, see comments describing
the current possibilities in the Gdiplus implementation.

Moved lifetime creation/cleanup of SystemDependentDataManager
to ImplSVData due to cleanup problems in the clang build

Tried to use a std::unique_ptr to hold the instance
of a SystemDependentDataBuffer at ImplSVData and cleanup
inside DeInitVCL() right before ::ImplDeInitScheduler. This
works in principle, but scheduler shutdown triggers
ProcessEventsToIdle which leads to repaints and re-creates
the buffer. Will now do exactly as was done with GdiPlusBuffer
before, a simple local static incarnation and a call to
SetStatic() in constructor

Splitted SystemDependentDataBuffer and Timer due to
different LifeTimes. Timer needs to be destructed
earlier than SystemDependentDataBuffer, before
Scheduler::ImplDeInitScheduler() is called from
DeInitVCL()

Change-Id: I2134e4346a183a4cee1be3428c51541cc8867c11
Reviewed-on: https://gerrit.libreoffice.org/60102
Tested-by: Jenkins
Reviewed-by: Armin Le Grand <Armin.Le.Grand@cib.de>
diff --git a/basegfx/source/polygon/b2dpolygon.cxx b/basegfx/source/polygon/b2dpolygon.cxx
index c94f262..3638424 100644
--- a/basegfx/source/polygon/b2dpolygon.cxx
+++ b/basegfx/source/polygon/b2dpolygon.cxx
@@ -467,7 +467,8 @@ private:

public:
    ImplBufferedData()
    :   mpDefaultSubdivision(),
    :   basegfx::SystemDependentDataHolder(),
        mpDefaultSubdivision(),
        mpB2DRange()
    {
    }
diff --git a/basegfx/source/polygon/b2dpolypolygon.cxx b/basegfx/source/polygon/b2dpolypolygon.cxx
index a1ac530..0901eaf 100644
--- a/basegfx/source/polygon/b2dpolypolygon.cxx
+++ b/basegfx/source/polygon/b2dpolypolygon.cxx
@@ -22,24 +22,65 @@
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>

#include <basegfx/utils/systemdependentdata.hxx>
#include <functional>
#include <algorithm>

class ImplB2DPolyPolygon
{
    basegfx::B2DPolygonVector                   maPolygons;
    basegfx::B2DPolygonVector                               maPolygons;
    std::unique_ptr< basegfx::SystemDependentDataHolder >   mpSystemDependentDataHolder;

public:
    ImplB2DPolyPolygon() : maPolygons()
    ImplB2DPolyPolygon()
    :   maPolygons(),
        mpSystemDependentDataHolder()
    {
    }

    explicit ImplB2DPolyPolygon(const basegfx::B2DPolygon& rToBeCopied) :
        maPolygons(1,rToBeCopied)
    explicit ImplB2DPolyPolygon(const ImplB2DPolyPolygon& rSource)
    :   maPolygons(rSource.maPolygons),
        mpSystemDependentDataHolder()
    {
    }

    explicit ImplB2DPolyPolygon(const basegfx::B2DPolygon& rToBeCopied)
    :   maPolygons(1,rToBeCopied),
        mpSystemDependentDataHolder()
    {
    }

    ImplB2DPolyPolygon& operator=(const ImplB2DPolyPolygon& rSource)
    {
        if (this != &rSource)
        {
            maPolygons = rSource.maPolygons;
            mpSystemDependentDataHolder.reset();
        }

        return *this;
    }

    void addOrReplaceSystemDependentData(basegfx::SystemDependentData_SharedPtr& rData)
    {
        if(!mpSystemDependentDataHolder)
        {
            mpSystemDependentDataHolder.reset(new basegfx::SystemDependentDataHolder());
        }

        mpSystemDependentDataHolder->addOrReplaceSystemDependentData(rData);
    }

    basegfx::SystemDependentData_SharedPtr getSystemDependentData(size_t hash_code) const
    {
        if(!mpSystemDependentDataHolder)
        {
            return basegfx::SystemDependentData_SharedPtr();
        }

        return mpSystemDependentDataHolder->getSystemDependentData(hash_code);
    }

    bool operator==(const ImplB2DPolyPolygon& rPolygonList) const
    {
        // same polygon count?
@@ -385,6 +426,24 @@ namespace basegfx
    {
        return mpPolyPolygon->end();
    }

    void B2DPolyPolygon::addOrReplaceSystemDependentDataInternal(SystemDependentData_SharedPtr& rData) const
    {
        // Need to get ImplB2DPolyPolygon* from cow_wrapper *without*
        // calling make_unique() here - we do not want to
        // 'modify' the ImplB2DPolyPolygon, but add buffered data that
        // is valid for all referencing instances
        const B2DPolyPolygon* pMe(this);
        const ImplB2DPolyPolygon* pMyImpl(pMe->mpPolyPolygon.get());

        const_cast<ImplB2DPolyPolygon*>(pMyImpl)->addOrReplaceSystemDependentData(rData);
    }

    SystemDependentData_SharedPtr B2DPolyPolygon::getSystemDependantDataInternal(size_t hash_code) const
    {
        return mpPolyPolygon->getSystemDependentData(hash_code);
    }

} // end of namespace basegfx

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
index 3295a97..002ae53 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
@@ -107,9 +107,7 @@ namespace drawinglayer

        void VclPixelProcessor2D::tryDrawPolyPolygonColorPrimitive2DDirect(const drawinglayer::primitive2d::PolyPolygonColorPrimitive2D& rSource, double fTransparency)
        {
            basegfx::B2DPolyPolygon aLocalPolyPolygon(rSource.getB2DPolyPolygon());

            if(!aLocalPolyPolygon.count())
            if(!rSource.getB2DPolyPolygon().count())
            {
                // no geometry, done
                return;
@@ -119,9 +117,9 @@ namespace drawinglayer

            mpOutputDevice->SetFillColor(Color(aPolygonColor));
            mpOutputDevice->SetLineColor();
            aLocalPolyPolygon.transform(maCurrentTransformation);
            mpOutputDevice->DrawTransparent(
                aLocalPolyPolygon,
                maCurrentTransformation,
                rSource.getB2DPolyPolygon(),
                fTransparency);
        }

diff --git a/include/basegfx/polygon/b2dpolypolygon.hxx b/include/basegfx/polygon/b2dpolypolygon.hxx
index fff49fb..65e3b97 100644
--- a/include/basegfx/polygon/b2dpolypolygon.hxx
+++ b/include/basegfx/polygon/b2dpolypolygon.hxx
@@ -126,6 +126,26 @@ namespace basegfx
        const B2DPolygon* end() const;
        B2DPolygon* begin();
        B2DPolygon* end();

        // exclusive management op's for SystemDependentData at B2DPolygon
        template<class T>
        std::shared_ptr<T> getSystemDependentData() const
        {
            return std::static_pointer_cast<T>(getSystemDependantDataInternal(typeid(T).hash_code()));
        }

        template<class T, class... Args>
        std::shared_ptr<T> addOrReplaceSystemDependentData(SystemDependentDataManager& manager, Args&&... args) const
        {
            std::shared_ptr<T> r = std::make_shared<T>(manager, std::forward<Args>(args)...);
            basegfx::SystemDependentData_SharedPtr r2(r);
            addOrReplaceSystemDependentDataInternal(r2);
            return r;
        }

    private:
        void addOrReplaceSystemDependentDataInternal(SystemDependentData_SharedPtr& rData) const;
        SystemDependentData_SharedPtr getSystemDependantDataInternal(size_t hash_code) const;
    };

    // typedef for a vector of B2DPolyPolygons
diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx
index f015f52..13ac5a5d 100644
--- a/include/vcl/outdev.hxx
+++ b/include/vcl/outdev.hxx
@@ -1611,7 +1611,12 @@ public:


    void                        DrawTransparent( const tools::PolyPolygon& rPolyPoly, sal_uInt16 nTransparencePercent );
    void                        DrawTransparent( const basegfx::B2DPolyPolygon& rB2DPolyPoly, double fTransparency);

    void                        DrawTransparent(
                                    const basegfx::B2DHomMatrix& rObjectTransform,
                                    const basegfx::B2DPolyPolygon& rB2DPolyPoly,
                                    double fTransparency);

    void                        DrawTransparent(
                                        const GDIMetaFile& rMtf, const Point& rPos, const Size& rSize,
                                        const Gradient& rTransparenceGradient );
diff --git a/sd/source/ui/slidesorter/view/SlsInsertionIndicatorOverlay.cxx b/sd/source/ui/slidesorter/view/SlsInsertionIndicatorOverlay.cxx
index 5615003..af3fa0a 100644
--- a/sd/source/ui/slidesorter/view/SlsInsertionIndicatorOverlay.cxx
+++ b/sd/source/ui/slidesorter/view/SlsInsertionIndicatorOverlay.cxx
@@ -215,6 +215,7 @@ Point InsertionIndicatorOverlay::PaintRepresentatives (
        rContent.SetFillColor(COL_BLACK);
        rContent.SetLineColor();
        rContent.DrawTransparent(
            basegfx::B2DHomMatrix(),
            ::basegfx::B2DPolyPolygon(::basegfx::utils::createPolygonFromRect(
                ::basegfx::B2DRectangle(aBox.Left(), aBox.Top(), aBox.Right()+1, aBox.Bottom()+1),
                0,
diff --git a/vcl/headless/svpgdi.cxx b/vcl/headless/svpgdi.cxx
index 5acd288..56bd740 100644
--- a/vcl/headless/svpgdi.cxx
+++ b/vcl/headless/svpgdi.cxx
@@ -714,7 +714,10 @@ void SvpSalGraphics::drawPixel( long nX, long nY, Color nColor )
    m_aLineColor = SALCOLOR_NONE;
    m_aFillColor = nColor;

    drawPolyPolygon(basegfx::B2DPolyPolygon(aRect));
    drawPolyPolygon(
        basegfx::B2DHomMatrix(),
        basegfx::B2DPolyPolygon(aRect),
        0.0);

    m_aFillColor = aOrigFillColor;
    m_aLineColor = aOrigLineColor;
@@ -732,7 +735,12 @@ void SvpSalGraphics::drawRect( long nX, long nY, long nWidth, long nHeight )
    {
        basegfx::B2DPolygon aRect = basegfx::utils::createPolygonFromRect(basegfx::B2DRectangle(nX, nY, nX+nWidth, nY+nHeight));
        m_aFillColor = aOrigFillColor;
        drawPolyPolygon(basegfx::B2DPolyPolygon(aRect));

        drawPolyPolygon(
            basegfx::B2DHomMatrix(),
            basegfx::B2DPolyPolygon(aRect),
            0.0);

        m_aFillColor = SALCOLOR_NONE;
    }

@@ -741,7 +749,12 @@ void SvpSalGraphics::drawRect( long nX, long nY, long nWidth, long nHeight )
        // need same -1 hack as X11SalGraphicsImpl::drawRect
        basegfx::B2DPolygon aRect = basegfx::utils::createPolygonFromRect(basegfx::B2DRectangle( nX, nY, nX+nWidth-1, nY+nHeight-1));
        m_aLineColor = aOrigLineColor;
        drawPolyPolygon(basegfx::B2DPolyPolygon(aRect));

        drawPolyPolygon(
            basegfx::B2DHomMatrix(),
            basegfx::B2DPolyPolygon(aRect),
            0.0);

        m_aLineColor = SALCOLOR_NONE;
    }

@@ -775,7 +788,10 @@ void SvpSalGraphics::drawPolygon(sal_uInt32 nPoints, const SalPoint* pPtAry)
    for (sal_uInt32 i = 1; i < nPoints; ++i)
        aPoly.setB2DPoint(i, basegfx::B2DPoint(pPtAry[i].mnX, pPtAry[i].mnY));

    drawPolyPolygon(basegfx::B2DPolyPolygon(aPoly));
    drawPolyPolygon(
        basegfx::B2DHomMatrix(),
        basegfx::B2DPolyPolygon(aPoly),
        0.0);
}

void SvpSalGraphics::drawPolyPolygon(sal_uInt32 nPoly,
@@ -798,7 +814,10 @@ void SvpSalGraphics::drawPolyPolygon(sal_uInt32 nPoly,
        }
    }

    drawPolyPolygon(aPolyPoly);
    drawPolyPolygon(
        basegfx::B2DHomMatrix(),
        aPolyPoly,
        0.0);
}

basegfx::B2DPoint impPixelSnap(
@@ -1061,7 +1080,7 @@ bool SvpSalGraphics::drawPolyLine(
    bool bPixelSnapHairline)
{
    // short circuit if there is nothing to do
    if(0 == rPolyLine.count())
    if(0 == rPolyLine.count() || fTransparency < 0.0 || fTransparency >= 1.0)
    {
        return true;
    }
@@ -1111,7 +1130,7 @@ bool SvpSalGraphics::drawPolyLine(
    bool bPixelSnapHairline)
{
    // short circuit if there is nothing to do
    if(0 == rPolyLine.count())
    if(0 == rPolyLine.count() || fTransparency < 0.0 || fTransparency >= 1.0)
    {
        return true;
    }
@@ -1283,7 +1302,7 @@ bool SvpSalGraphics::drawPolyLine(

        // copy and add to buffering mechanism
        pSystemDependentData_CairoPath = rPolyLine.addOrReplaceSystemDependentData<SystemDependentData_CairoPath>(
            SalGraphics::getSystemDependentDataManager(),
            ImplGetSystemDependentDataManager(),
            cairo_copy_path(cr));

        // fill data of buffered data
@@ -1337,36 +1356,68 @@ bool SvpSalGraphics::drawPolyPolygonBezier( sal_uInt32,
    return false;
}

void SvpSalGraphics::setupPolyPolygon(cairo_t* cr, const basegfx::B2DPolyPolygon& rPolyPoly)
{
    clipRegion(cr);

    for (const auto & rPoly : rPolyPoly)
    {
        // PixelOffset used: Was dependent of 'm_aLineColor != SALCOLOR_NONE'
        // Adapt setupPolyPolygon-users to set a linear transformation to achieve PixelOffset
        AddPolygonToPath(
            cr,
            rPoly,
            basegfx::B2DHomMatrix(),
            !getAntiAliasB2DDraw(),
            false);
    }
}

bool SvpSalGraphics::drawPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPoly, double fTransparency)
bool SvpSalGraphics::drawPolyPolygon(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolyPolygon& rPolyPolygon,
    double fTransparency)
{
    const bool bHasFill(m_aFillColor != SALCOLOR_NONE);
    const bool bHasLine(m_aLineColor != SALCOLOR_NONE);

    if(0 == rPolyPoly.count() || !(bHasFill || bHasLine))
    if(0 == rPolyPolygon.count() || !(bHasFill || bHasLine) || fTransparency < 0.0 || fTransparency >= 1.0)
    {
        return true;
    }

    cairo_t* cr = getCairoContext(true);
    clipRegion(cr);

    setupPolyPolygon(cr, rPolyPoly);
    // Set full (Object-to-Device) transformation - if used
    if(!rObjectToDevice.isIdentity())
    {
        cairo_matrix_t aMatrix;

        cairo_matrix_init(
            &aMatrix,
            rObjectToDevice.get( 0, 0 ),
            rObjectToDevice.get( 1, 0 ),
            rObjectToDevice.get( 0, 1 ),
            rObjectToDevice.get( 1, 1 ),
            rObjectToDevice.get( 0, 2 ),
            rObjectToDevice.get( 1, 2 ));
        cairo_set_matrix(cr, &aMatrix);
    }

    // try to access buffered data
    std::shared_ptr<SystemDependentData_CairoPath> pSystemDependentData_CairoPath(
        rPolyPolygon.getSystemDependentData<SystemDependentData_CairoPath>());

    if(pSystemDependentData_CairoPath)
    {
        // re-use data
        cairo_append_path(cr, pSystemDependentData_CairoPath->getCairoPath());
    }
    else
    {
        // create data
        for (const auto & rPoly : rPolyPolygon)
        {
            // PixelOffset used: Was dependent of 'm_aLineColor != SALCOLOR_NONE'
            // Adapt setupPolyPolygon-users to set a linear transformation to achieve PixelOffset
            AddPolygonToPath(
                cr,
                rPoly,
                rObjectToDevice,
                !getAntiAliasB2DDraw(),
                false);
        }

        // copy and add to buffering mechanism
        // for decisions how/what to buffer, see Note in WinSalGraphicsImpl::drawPolyPolygon
        pSystemDependentData_CairoPath = rPolyPolygon.addOrReplaceSystemDependentData<SystemDependentData_CairoPath>(
            ImplGetSystemDependentDataManager(),
            cairo_copy_path(cr));
    }

    // To make releaseCairoContext work, use empty extents
    basegfx::B2DRange extents;
@@ -1426,51 +1477,6 @@ void SvpSalGraphics::applyColor(cairo_t *cr, Color aColor)
    }
}

void SvpSalGraphics::drawPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPoly)
{
    const bool bHasFill(m_aFillColor != SALCOLOR_NONE);
    const bool bHasLine(m_aLineColor != SALCOLOR_NONE);

    if(0 == rPolyPoly.count() || !(bHasFill || bHasLine))
    {
        return;
    }

    cairo_t* cr = getCairoContext(true);

    setupPolyPolygon(cr, rPolyPoly);

    // To make releaseCairoContext work, use empty extents
    basegfx::B2DRange extents;

    if (bHasFill)
    {
        applyColor(cr, m_aFillColor);

        // Get FillDamage (will be extended for LineDamage below)
        extents = getClippedFillDamage(cr);

        cairo_fill_preserve(cr);
    }

    if (bHasLine)
    {
        // PixelOffset used: Set PixelOffset as linear transformation
        cairo_matrix_t aMatrix;
        cairo_matrix_init_translate(&aMatrix, 0.5, 0.5);
        cairo_set_matrix(cr, &aMatrix);

        applyColor(cr, m_aLineColor);

        // expand with possible StrokeDamage
        extents.expand(getClippedStrokeDamage(cr));

        cairo_stroke_preserve(cr);
    }

    releaseCairoContext(cr, true, extents);
}

void SvpSalGraphics::copyArea( long nDestX,
                               long nDestY,
                               long nSrcX,
diff --git a/vcl/inc/headless/svpgdi.hxx b/vcl/inc/headless/svpgdi.hxx
index 158c331..9c7ee03 100644
--- a/vcl/inc/headless/svpgdi.hxx
+++ b/vcl/inc/headless/svpgdi.hxx
@@ -122,9 +122,8 @@ private:
    void copySource(const SalTwoRect& rTR, cairo_surface_t* source);
    void copyWithOperator(const SalTwoRect& rTR, cairo_surface_t* source,
                          cairo_operator_t eOp = CAIRO_OPERATOR_SOURCE);
    void setupPolyPolygon(cairo_t* cr, const basegfx::B2DPolyPolygon& rPolyPoly);
    void applyColor(cairo_t *cr, Color rColor);
    void drawPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPoly);

protected:
    vcl::Region                         m_aClipRegion;
    SvpCairoTextRender                  m_aTextRenderImpl;
@@ -200,7 +199,12 @@ public:
    virtual void            drawPixel( long nX, long nY, Color nColor ) override;
    virtual void            drawLine( long nX1, long nY1, long nX2, long nY2 ) override;
    virtual void            drawRect( long nX, long nY, long nWidth, long nHeight ) override;
    virtual bool            drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) override;

    virtual bool            drawPolyPolygon(
                                const basegfx::B2DHomMatrix& rObjectToDevice,
                                const basegfx::B2DPolyPolygon&,
                                double fTransparency ) override;

    virtual bool            drawPolyLine(
                                const basegfx::B2DHomMatrix& rObjectToDevice,
                                const basegfx::B2DPolygon&,
diff --git a/vcl/inc/openglgdiimpl.hxx b/vcl/inc/openglgdiimpl.hxx
index f2fd9b7..bc19dcd 100644
--- a/vcl/inc/openglgdiimpl.hxx
+++ b/vcl/inc/openglgdiimpl.hxx
@@ -249,7 +249,11 @@ public:
    virtual void drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry ) override;

    virtual void drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, PCONSTSALPOINT* pPtAry ) override;
    virtual bool drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) override;

    virtual bool drawPolyPolygon(
                const basegfx::B2DHomMatrix& rObjectToDevice,
                const basegfx::B2DPolyPolygon&,
                double fTransparency) override;

    virtual bool drawPolyLine(
                const basegfx::B2DHomMatrix& rObjectToDevice,
diff --git a/vcl/inc/qt5/Qt5Graphics.hxx b/vcl/inc/qt5/Qt5Graphics.hxx
index 6bde41f..76c3914 100644
--- a/vcl/inc/qt5/Qt5Graphics.hxx
+++ b/vcl/inc/qt5/Qt5Graphics.hxx
@@ -108,7 +108,8 @@ public:
    virtual void drawPolygon(sal_uInt32 nPoints, const SalPoint* pPtAry) override;
    virtual void drawPolyPolygon(sal_uInt32 nPoly, const sal_uInt32* pPoints,
                                 PCONSTSALPOINT* pPtAry) override;
    virtual bool drawPolyPolygon(const basegfx::B2DPolyPolygon&, double fTransparency) override;
    virtual bool drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectToDevice,
                                 const basegfx::B2DPolyPolygon&, double fTransparency) override;
    virtual bool drawPolyLineBezier(sal_uInt32 nPoints, const SalPoint* pPtAry,
                                    const PolyFlags* pFlgAry) override;
    virtual bool drawPolygonBezier(sal_uInt32 nPoints, const SalPoint* pPtAry,
diff --git a/vcl/inc/quartz/salgdi.h b/vcl/inc/quartz/salgdi.h
index b03fa5b..24b5268 100644
--- a/vcl/inc/quartz/salgdi.h
+++ b/vcl/inc/quartz/salgdi.h
@@ -224,7 +224,10 @@ public:
    virtual void            drawPolyLine( sal_uInt32 nPoints, const SalPoint* pPtAry ) override;
    virtual void            drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry ) override;
    virtual void            drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, PCONSTSALPOINT* pPtAry ) override;
    virtual bool            drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) override;
    virtual bool            drawPolyPolygon(
                                const basegfx::B2DHomMatrix& rObjectToDevice,
                                const basegfx::B2DPolyPolygon&,
                                double fTransparency) override;
    virtual bool            drawPolyLineBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const PolyFlags* pFlgAry ) override;
    virtual bool            drawPolygonBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const PolyFlags* pFlgAry ) override;
    virtual bool            drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints, const SalPoint* const* pPtAry, const PolyFlags* const* pFlgAry ) override;
diff --git a/vcl/inc/salgdi.hxx b/vcl/inc/salgdi.hxx
index 31c4848..19fac92 100644
--- a/vcl/inc/salgdi.hxx
+++ b/vcl/inc/salgdi.hxx
@@ -55,7 +55,6 @@ namespace basegfx {
    class B2DVector;
    class B2DPolygon;
    class B2DPolyPolygon;
    class SystemDependentDataManager;
}

typedef sal_Unicode sal_Ucs; // TODO: use sal_UCS4 instead of sal_Unicode
@@ -78,10 +77,6 @@ public:

    virtual SalGraphicsImpl*    GetImpl() const = 0;

    // access to single global managing instance of a basegfx::SystemDependentDataManager,
    // used to handle graphic data in system-dependent form
    static basegfx::SystemDependentDataManager& getSystemDependentDataManager();

    /// Check that our mpImpl is OpenGL and return the context, otherwise NULL.
    rtl::Reference<OpenGLContext> GetOpenGLContext() const;

@@ -244,6 +239,7 @@ public:
                                    const OutputDevice *pOutDev );

    bool                        DrawPolyPolygon(
                                    const basegfx::B2DHomMatrix& rObjectToDevice,
                                    const basegfx::B2DPolyPolygon &i_rPolyPolygon,
                                    double i_fTransparency,
                                    const OutputDevice *i_pOutDev);
@@ -464,7 +460,11 @@ protected:
    virtual void                drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry ) = 0;

    virtual void                drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, PCONSTSALPOINT* pPtAry ) = 0;
    virtual bool                drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) = 0;

    virtual bool                drawPolyPolygon(
                                    const basegfx::B2DHomMatrix& rObjectToDevice,
                                    const basegfx::B2DPolyPolygon&,
                                    double fTransparency) = 0;

    virtual bool                drawPolyLine(
                                    const basegfx::B2DHomMatrix& rObjectToDevice,
diff --git a/vcl/inc/salgdiimpl.hxx b/vcl/inc/salgdiimpl.hxx
index 8e545d0..d4023bd 100644
--- a/vcl/inc/salgdiimpl.hxx
+++ b/vcl/inc/salgdiimpl.hxx
@@ -100,7 +100,11 @@ public:
    virtual void drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry ) = 0;

    virtual void drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, PCONSTSALPOINT* pPtAry ) = 0;
    virtual bool drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) = 0;

    virtual bool drawPolyPolygon(
        const basegfx::B2DHomMatrix& rObjectToDevice,
        const basegfx::B2DPolyPolygon&,
        double fTransparency) = 0;

    virtual bool drawPolyLine(
                const basegfx::B2DHomMatrix& rObjectToDevice,
diff --git a/vcl/inc/svdata.hxx b/vcl/inc/svdata.hxx
index 679debc..6ef2795 100644
--- a/vcl/inc/svdata.hxx
+++ b/vcl/inc/svdata.hxx
@@ -106,6 +106,11 @@ namespace vcl
    class Window;
}

namespace basegfx
{
    class SystemDependentDataManager;
}

class LocaleConfigurationListener : public utl::ConfigurationListener
{
public:
@@ -372,6 +377,7 @@ struct ImplSVData
css::uno::Reference<css::i18n::XCharacterClassification> const& ImplGetCharClass();

void        ImplDeInitSVData();
VCL_PLUGIN_PUBLIC basegfx::SystemDependentDataManager& ImplGetSystemDependentDataManager();
VCL_PLUGIN_PUBLIC vcl::Window* ImplGetDefaultWindow();
VCL_PLUGIN_PUBLIC vcl::Window* ImplGetDefaultContextWindow();
VCL_PLUGIN_PUBLIC const std::locale& ImplGetResLocale();
diff --git a/vcl/inc/unx/genpspgraphics.h b/vcl/inc/unx/genpspgraphics.h
index 3f6bae6..bddcb88 100644
--- a/vcl/inc/unx/genpspgraphics.h
+++ b/vcl/inc/unx/genpspgraphics.h
@@ -126,8 +126,12 @@ public:
    virtual void            drawPolyPolygon( sal_uInt32 nPoly,
                                             const sal_uInt32* pPoints,
                                             PCONSTSALPOINT* pPtAry ) override;
    virtual bool            drawPolyPolygon( const basegfx::B2DPolyPolygon&,
                                             double fTransparency ) override;

    virtual bool            drawPolyPolygon(
                                const basegfx::B2DHomMatrix& rObjectToDevice,
                                const basegfx::B2DPolyPolygon&,
                                double fTransparency) override;

    virtual bool            drawPolyLine(
                                const basegfx::B2DHomMatrix& rObjectToDevice,
                                const basegfx::B2DPolygon&,
diff --git a/vcl/inc/unx/salgdi.h b/vcl/inc/unx/salgdi.h
index 8b73c45..d6054ea 100644
--- a/vcl/inc/unx/salgdi.h
+++ b/vcl/inc/unx/salgdi.h
@@ -160,7 +160,10 @@ public:
                                        const sal_uInt32* pPoints,
                                        PCONSTSALPOINT* pPtAry ) override;

    virtual bool                    drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) override;
    virtual bool                    drawPolyPolygon(
                                        const basegfx::B2DHomMatrix& rObjectToDevice,
                                        const basegfx::B2DPolyPolygon&,
                                        double fTransparency) override;

    virtual bool                    drawPolyLine(
                                        const basegfx::B2DHomMatrix& rObjectToDevice,
diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h
index 9f30f57..e8fe72c 100644
--- a/vcl/inc/win/salgdi.h
+++ b/vcl/inc/win/salgdi.h
@@ -221,7 +221,10 @@ protected:
    virtual void        drawPolyLine( sal_uInt32 nPoints, const SalPoint* pPtAry ) override;
    virtual void        drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry ) override;
    virtual void        drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, PCONSTSALPOINT* pPtAry ) override;
    virtual bool        drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) override;
    virtual bool        drawPolyPolygon(
        const basegfx::B2DHomMatrix& rObjectToDevice,
        const basegfx::B2DPolyPolygon&,
        double fTransparency) override;
    virtual bool        drawPolyLine(
        const basegfx::B2DHomMatrix& rObjectToDevice,
        const basegfx::B2DPolygon&,
diff --git a/vcl/opengl/gdiimpl.cxx b/vcl/opengl/gdiimpl.cxx
index 4c966bd..082c8d6 100644
--- a/vcl/opengl/gdiimpl.cxx
+++ b/vcl/opengl/gdiimpl.cxx
@@ -1567,7 +1567,10 @@ void OpenGLSalGraphicsImpl::drawPolygon( sal_uInt32 nPoints, const SalPoint* pPt
    for (sal_uInt32 i = 1; i < nPoints; ++i)
        aPoly.setB2DPoint(i, basegfx::B2DPoint(pPtAry[i].mnX, pPtAry[i].mnY));

    drawPolyPolygon(basegfx::B2DPolyPolygon(aPoly), 0.0);
    drawPolyPolygon(
        basegfx::B2DHomMatrix(),
        basegfx::B2DPolyPolygon(aPoly),
        0.0);
}

void OpenGLSalGraphicsImpl::drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPointCounts, PCONSTSALPOINT* pPtAry )
@@ -1589,13 +1592,30 @@ void OpenGLSalGraphicsImpl::drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32*
        }
    }

    drawPolyPolygon(aPolyPoly, 0.0);
    drawPolyPolygon(
        basegfx::B2DHomMatrix(),
        aPolyPoly,
        0.0);
}

bool OpenGLSalGraphicsImpl::drawPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon, double fTransparency)
bool OpenGLSalGraphicsImpl::drawPolyPolygon(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolyPolygon& rPolyPolygon,
    double fTransparency)
{
    VCL_GL_INFO("::drawPolyPolygon " << rPolyPolygon.getB2DRange());
    mpRenderList->addDrawPolyPolygon(rPolyPolygon, fTransparency, mnLineColor, mnFillColor, mrParent.getAntiAliasB2DDraw());

    // Fallback: Transform to DeviceCoordinates
    basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon);
    aPolyPolygon.transform(rObjectToDevice);

    mpRenderList->addDrawPolyPolygon(
        aPolyPolygon,
        fTransparency,
        mnLineColor,
        mnFillColor,
        mrParent.getAntiAliasB2DDraw());

    PostBatchDraw();
    return true;
}
diff --git a/vcl/qt5/Qt5Graphics_GDI.cxx b/vcl/qt5/Qt5Graphics_GDI.cxx
index 3b8d2ee..f8ab00e 100644
--- a/vcl/qt5/Qt5Graphics_GDI.cxx
+++ b/vcl/qt5/Qt5Graphics_GDI.cxx
@@ -279,7 +279,8 @@ void Qt5Graphics::drawPolyPolygon(sal_uInt32 nPolyCount, const sal_uInt32* pPoin
    aPainter.update(aPath.boundingRect());
}

bool Qt5Graphics::drawPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPoly, double fTransparency)
bool Qt5Graphics::drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectToDevice,
                                  const basegfx::B2DPolyPolygon& rPolyPolygon, double fTransparency)
{
    // ignore invisible polygons
    if (SALCOLOR_NONE == m_aFillColor && SALCOLOR_NONE == m_aLineColor)
@@ -287,9 +288,13 @@ bool Qt5Graphics::drawPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPoly, doub
    if ((fTransparency >= 1.0) || (fTransparency < 0))
        return true;

    // Fallback: Transform to DeviceCoordinates
    basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon);
    aPolyPolygon.transform(rObjectToDevice);

    QPainterPath aPath;
    // ignore empty polygons
    if (!AddPolyPolygonToPath(aPath, rPolyPoly, !getAntiAliasB2DDraw(),
    if (!AddPolyPolygonToPath(aPath, aPolyPolygon, !getAntiAliasB2DDraw(),
                              m_aLineColor != SALCOLOR_NONE))
        return true;

diff --git a/vcl/quartz/salgdicommon.cxx b/vcl/quartz/salgdicommon.cxx
index 09adf78..59d8365 100644
--- a/vcl/quartz/salgdicommon.cxx
+++ b/vcl/quartz/salgdicommon.cxx
@@ -1072,13 +1072,15 @@ bool AquaSalGraphics::drawPolyLineBezier( sal_uInt32, const SalPoint*, const Pol
    return false;
}

bool AquaSalGraphics::drawPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly,
                                       double fTransparency )
bool AquaSalGraphics::drawPolyPolygon(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolyPolygon& rPolyPolygon,
    double fTransparency)
{
    DBG_DRAW_OPERATION("drawPolyPolygon", true);

    // short circuit if there is nothing to do
    const int nPolyCount = rPolyPoly.count();
    const int nPolyCount = rPolyPolygon.count();
    if( nPolyCount <= 0 )
    {
        DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyPolygon");
@@ -1092,12 +1094,16 @@ bool AquaSalGraphics::drawPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly,
        return true;
    }

    // Fallback: Transform to DeviceCoordinates
    basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon);
    aPolyPolygon.transform(rObjectToDevice);

    // setup poly-polygon path
    CGMutablePathRef xPath = CGPathCreateMutable();
    SAL_INFO( "vcl.cg", "CGPathCreateMutable() = " << xPath );
    for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx )
    {
        const basegfx::B2DPolygon rPolygon = rPolyPoly.getB2DPolygon( nPolyIdx );
        const basegfx::B2DPolygon rPolygon = rPolyPolygon.getB2DPolygon( nPolyIdx );
        AddPolygonToPath( xPath, rPolygon, true, !getAntiAliasB2DDraw(), IsPenVisible() );
    }

diff --git a/vcl/source/app/scheduler.cxx b/vcl/source/app/scheduler.cxx
index 93fcfec..9e5c3bc 100644
--- a/vcl/source/app/scheduler.cxx
+++ b/vcl/source/app/scheduler.cxx
@@ -155,7 +155,7 @@ void Scheduler::ImplDeInitScheduler()
                        || !strcmp( pTask->GetDebugName(), "svtools::GraphicCache maReleaseTimer" )
                        || !strcmp( pTask->GetDebugName(), "svtools::GraphicObject mpSwapOutTimer" )
                        || !strcmp( pTask->GetDebugName(), "svx OLEObjCache pTimer UnloadCheck" )
                        || !strcmp( pTask->GetDebugName(), "vcl::win GdiPlusBuffer aGdiPlusBuffer" )
                        || !strcmp( pTask->GetDebugName(), "vcl SystemDependentDataBuffer aSystemDependentDataBuffer" )
                        ))
                {
                    sIgnored = " (ignored)";
diff --git a/vcl/source/app/svdata.cxx b/vcl/source/app/svdata.cxx
index ce3ed13..251da9e 100644
--- a/vcl/source/app/svdata.cxx
+++ b/vcl/source/app/svdata.cxx
@@ -60,6 +60,9 @@
#if HAVE_FEATURE_OPENGL
#include <vcl/opengl/OpenGLContext.hxx>
#endif
#include <basegfx/utils/systemdependentdata.hxx>
#include <cppuhelper/basemutex.hxx>
#include <o3tl/make_unique.hxx>

using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
@@ -97,6 +100,135 @@ void ImplDeInitSVData()
    pSVData->maPaperNames.clear();
}

namespace
{
    typedef ::std::map< basegfx::SystemDependentData_SharedPtr, sal_uInt32 > EntryMap;

    class SystemDependentDataBuffer : public basegfx::SystemDependentDataManager, protected cppu::BaseMutex
    {
    private:
        std::unique_ptr<Timer>  maTimer;
        EntryMap                maEntries;

        DECL_LINK(implTimeoutHdl, Timer *, void);

    public:
        SystemDependentDataBuffer(const sal_Char* pDebugName)
        :   basegfx::SystemDependentDataManager(),
            maTimer(o3tl::make_unique<Timer>(pDebugName)),
            maEntries()
        {
            maTimer->SetTimeout(1000);
            maTimer->SetInvokeHandler(LINK(this, SystemDependentDataBuffer, implTimeoutHdl));
        }

        virtual ~SystemDependentDataBuffer() override
        {
            flushAll();
        }

        void startUsage(basegfx::SystemDependentData_SharedPtr& rData) override
        {
            ::osl::MutexGuard aGuard(m_aMutex);
            EntryMap::iterator aFound(maEntries.find(rData));

            if(aFound == maEntries.end())
            {
                if(maEntries.empty() && maTimer)
                {
                    maTimer->Start();
                }

                maEntries[rData] = rData->getHoldCycles();
            }
        }

        void endUsage(basegfx::SystemDependentData_SharedPtr& rData) override
        {
            ::osl::MutexGuard aGuard(m_aMutex);
            EntryMap::iterator aFound(maEntries.find(rData));

            if(aFound != maEntries.end())
            {
                maEntries.erase(aFound);

                if(maEntries.empty() && maTimer)
                {
                    maTimer->Stop();
                }
            }
        }

        void touchUsage(basegfx::SystemDependentData_SharedPtr& rData) override
        {
            ::osl::MutexGuard aGuard(m_aMutex);
            EntryMap::iterator aFound(maEntries.find(rData));

            if(aFound != maEntries.end())
            {
                aFound->second = rData->getHoldCycles();
            }
        }

        void flushAll() override
        {
            ::osl::MutexGuard aGuard(m_aMutex);
            EntryMap::iterator aIter(maEntries.begin());

            if(maTimer)
            {
                maTimer->Stop();
                maTimer.reset();
            }

            while(aIter != maEntries.end())
            {
                EntryMap::iterator aDelete(aIter);
                ++aIter;
                maEntries.erase(aDelete);
            }
        }
    };

    IMPL_LINK_NOARG(SystemDependentDataBuffer, implTimeoutHdl, Timer *, void)
    {
        ::osl::MutexGuard aGuard(m_aMutex);
        EntryMap::iterator aIter(maEntries.begin());

        while(aIter != maEntries.end())
        {
            if(aIter->second)
            {
                aIter->second--;
                ++aIter;
            }
            else
            {
                EntryMap::iterator aDelete(aIter);
                ++aIter;
                maEntries.erase(aDelete);

                if(maEntries.empty() && maTimer)
                {
                    maTimer->Stop();
                }
            }
        }

        if(!maEntries.empty() && maTimer)
        {
            maTimer->Start();
        }
    }
}

basegfx::SystemDependentDataManager& ImplGetSystemDependentDataManager()
{
    static SystemDependentDataBuffer aSystemDependentDataBuffer("vcl SystemDependentDataBuffer aSystemDependentDataBuffer");

    return aSystemDependentDataBuffer;
}

/// Returns either the application window, or the default GL context window
vcl::Window* ImplGetDefaultWindow()
{
diff --git a/vcl/source/app/svmain.cxx b/vcl/source/app/svmain.cxx
index beed242..b4cfa73 100644
--- a/vcl/source/app/svmain.cxx
+++ b/vcl/source/app/svmain.cxx
@@ -428,9 +428,6 @@ void DeInitVCL()
    }
    ImplSVData* pSVData = ImplGetSVData();

    // cleanup SystemDependentData
    SalGraphics::getSystemDependentDataManager().flushAll();

    // lp#1560328: clear cache before disposing rest of VCL
    if(pSVData->mpBlendFrameCache)
        pSVData->mpBlendFrameCache->m_aLastResult.Clear();
@@ -481,6 +478,9 @@ void DeInitVCL()

    pSVData->mpSettingsConfigItem.reset();

    // empty and deactivate the SystemDependentDataManager
    ImplGetSystemDependentDataManager().flushAll();

    Scheduler::ImplDeInitScheduler();

    pSVData->maWinData.maMsgBoxImgList.clear();
diff --git a/vcl/source/gdi/salgdilayout.cxx b/vcl/source/gdi/salgdilayout.cxx
index d39bd33..d28c751 100644
--- a/vcl/source/gdi/salgdilayout.cxx
+++ b/vcl/source/gdi/salgdilayout.cxx
@@ -31,11 +31,8 @@
#include <salgdi.hxx>
#include <salframe.hxx>
#include <basegfx/numeric/ftools.hxx> //for F_PI180
#include <basegfx/utils/systemdependentdata.hxx>
#include <cppuhelper/basemutex.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <o3tl/make_unique.hxx>

// The only common SalFrame method

@@ -69,131 +66,6 @@ SalGraphics::~SalGraphics()
{
}

basegfx::SystemDependentDataManager& SalGraphics::getSystemDependentDataManager()
{
    typedef ::std::map< basegfx::SystemDependentData_SharedPtr, sal_uInt32 > EntryMap;

    class SystemDependentDataBuffer : public basegfx::SystemDependentDataManager, protected cppu::BaseMutex, public Timer
    {
    private:
        EntryMap        maEntries;

    public:
        SystemDependentDataBuffer( const sal_Char *pDebugName )
        :   basegfx::SystemDependentDataManager(),
            Timer( pDebugName ),
            maEntries()
        {
            SetTimeout(1000);
            SetStatic();
        }

        virtual ~SystemDependentDataBuffer() override
        {
            Stop();
        }

        void startUsage(basegfx::SystemDependentData_SharedPtr& rData) override
        {
            ::osl::MutexGuard aGuard(m_aMutex);
            EntryMap::iterator aFound(maEntries.find(rData));

            if(aFound == maEntries.end())
            {
                if(maEntries.empty())
                {
                    Start();
                }

                maEntries[rData] = rData->getHoldCycles();
            }
        }

        void endUsage(basegfx::SystemDependentData_SharedPtr& rData) override
        {
            ::osl::MutexGuard aGuard(m_aMutex);
            EntryMap::iterator aFound(maEntries.find(rData));

            if(aFound != maEntries.end())
            {
                maEntries.erase(aFound);

                if(maEntries.empty())
                {
                    Stop();
                }
            }
        }

        void touchUsage(basegfx::SystemDependentData_SharedPtr& rData) override
        {
            ::osl::MutexGuard aGuard(m_aMutex);
            EntryMap::iterator aFound(maEntries.find(rData));

            if(aFound != maEntries.end())
            {
                aFound->second = rData->getHoldCycles();
            }
        }

        void flushAll() override
        {
            ::osl::MutexGuard aGuard(m_aMutex);
            EntryMap::iterator aIter(maEntries.begin());

            Stop();

            while(aIter != maEntries.end())
            {
                EntryMap::iterator aDelete(aIter);
                ++aIter;
                maEntries.erase(aDelete);
            }
        }

        // from parent Timer
        virtual void Invoke() override
        {
            ::osl::MutexGuard aGuard(m_aMutex);
            EntryMap::iterator aIter(maEntries.begin());

            while(aIter != maEntries.end())
            {
                if(aIter->second)
                {
                    aIter->second--;
                    ++aIter;
                }
                else
                {
                    EntryMap::iterator aDelete(aIter);
                    ++aIter;
                    maEntries.erase(aDelete);

                    if(maEntries.empty())
                    {
                        Stop();
                    }
                }
            }

            if(!maEntries.empty())
            {
                Start();
            }
        }
    };

    static std::unique_ptr<SystemDependentDataBuffer> aSystemDependentDataBuffer;

    if(!aSystemDependentDataBuffer)
    {
        aSystemDependentDataBuffer = o3tl::make_unique<SystemDependentDataBuffer>(nullptr);
    }

    return *aSystemDependentDataBuffer.get();
}

#if HAVE_FEATURE_OPENGL

namespace
@@ -589,17 +461,50 @@ void SalGraphics::DrawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, 
        drawPolyPolygon( nPoly, pPoints, pPtAry );
}

bool SalGraphics::DrawPolyPolygon( const basegfx::B2DPolyPolygon& i_rPolyPolygon, double i_fTransparency, const OutputDevice* i_pOutDev )
bool SalGraphics::DrawPolyPolygon(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolyPolygon& i_rPolyPolygon,
    double i_fTransparency,
    const OutputDevice* i_pOutDev)
{
    bool bRet = false;
    if( (m_nLayout & SalLayoutFlags::BiDiRtl) || (i_pOutDev && i_pOutDev->IsRTLEnabled()) )
    {
        basegfx::B2DPolyPolygon aMirror( mirror( i_rPolyPolygon, i_pOutDev ) );
        bRet = drawPolyPolygon( aMirror, i_fTransparency );
        // mirroring set
        const basegfx::B2DHomMatrix& rMirror(getMirror(i_pOutDev));

        if(!rMirror.isIdentity())
        {
            if(rObjectToDevice.isIdentity())
            {
                // There is no ObjectToDevice transformation set. We can just
                // use rMirror, that would be the result of the linear combination
                return drawPolyPolygon(
                    rMirror,
                    i_rPolyPolygon,
                    i_fTransparency);
            }
            else
            {
                // Create the linear combination
                basegfx::B2DHomMatrix aLinearCombination(rObjectToDevice);
                basegfx::B2DHomMatrix aObjectToDeviceInv(rObjectToDevice);

                aLinearCombination = rMirror * aLinearCombination;
                aObjectToDeviceInv.invert();
                aLinearCombination = aObjectToDeviceInv * aLinearCombination;

                return drawPolyPolygon(
                    aLinearCombination,
                    i_rPolyPolygon,
                    i_fTransparency);
            }
        }
    }
    else
        bRet = drawPolyPolygon( i_rPolyPolygon, i_fTransparency );
    return bRet;

    return drawPolyPolygon(
        rObjectToDevice,
        i_rPolyPolygon,
        i_fTransparency);
}

bool SalGraphics::DrawPolyLineBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const PolyFlags* pFlgAry, const OutputDevice* pOutDev )
diff --git a/vcl/source/outdev/line.cxx b/vcl/source/outdev/line.cxx
index fe16d67..fecacba 100644
--- a/vcl/source/outdev/line.cxx
+++ b/vcl/source/outdev/line.cxx
@@ -133,10 +133,6 @@ void OutputDevice::DrawLine( const Point& rStartPt, const Point& rEndPt )
        aB2DPolyLine.transform( aTransform );

        const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
        // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
        // {
        //     aB2DPolyLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine);
        // }

        if( mpGraphics->DrawPolyLine(
            basegfx::B2DHomMatrix(),
@@ -287,7 +283,11 @@ void OutputDevice::drawLine( basegfx::B2DPolyPolygon aLinePolyPolygon, const Lin

        if(bTryAA)
        {
            bDone = mpGraphics->DrawPolyPolygon(aFillPolyPolygon, 0.0, this);
            bDone = mpGraphics->DrawPolyPolygon(
                basegfx::B2DHomMatrix(),
                aFillPolyPolygon,
                0.0,
                this);
        }

        if(!bDone)
diff --git a/vcl/source/outdev/polygon.cxx b/vcl/source/outdev/polygon.cxx
index 94ad52a..1496b1a 100644
--- a/vcl/source/outdev/polygon.cxx
+++ b/vcl/source/outdev/polygon.cxx
@@ -70,17 +70,23 @@ void OutputDevice::DrawPolyPolygon( const tools::PolyPolygon& rPolyPoly )
       RasterOp::OverPaint == GetRasterOp() &&
       (IsLineColor() || IsFillColor()))
    {
        const basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation();
        const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation());
        basegfx::B2DPolyPolygon aB2DPolyPolygon(rPolyPoly.getB2DPolyPolygon());
        bool bSuccess(true);

        // transform the polygon and ensure closed
        aB2DPolyPolygon.transform(aTransform);
        aB2DPolyPolygon.setClosed(true);
        // ensure closed - may be asserted, will prevent buffering
        if(!aB2DPolyPolygon.isClosed())
        {
            aB2DPolyPolygon.setClosed(true);
        }

        if(IsFillColor())
        {
            bSuccess = mpGraphics->DrawPolyPolygon(aB2DPolyPolygon, 0.0, this);
            bSuccess = mpGraphics->DrawPolyPolygon(
                aTransform,
                aB2DPolyPolygon,
                0.0,
                this);
        }

        if(bSuccess && IsLineColor())
@@ -88,15 +94,10 @@ void OutputDevice::DrawPolyPolygon( const tools::PolyPolygon& rPolyPoly )
            const basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
            const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);

            // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
            // {
            //     aB2DPolyPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyPolygon);
            // }

            for(sal_uInt32 a(0); bSuccess && a < aB2DPolyPolygon.count(); a++)
            {
                bSuccess = mpGraphics->DrawPolyLine(
                    basegfx::B2DHomMatrix(),
                    aTransform,
                    aB2DPolyPolygon.getB2DPolygon(a),
                    0.0,
                    aB2DLineWidth,
@@ -187,17 +188,23 @@ void OutputDevice::DrawPolygon( const tools::Polygon& rPoly )
       RasterOp::OverPaint == GetRasterOp() &&
       (IsLineColor() || IsFillColor()))
    {
        const basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation();
        const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation());
        basegfx::B2DPolygon aB2DPolygon(rPoly.getB2DPolygon());
        bool bSuccess(true);

        // transform the polygon and ensure closed
        aB2DPolygon.transform(aTransform);
        aB2DPolygon.setClosed(true);
        // ensure closed - maybe assert, hinders bufering
        if(!aB2DPolygon.isClosed())
        {
            aB2DPolygon.setClosed(true);
        }

        if(IsFillColor())
        {
            bSuccess = mpGraphics->DrawPolyPolygon(basegfx::B2DPolyPolygon(aB2DPolygon), 0.0, this);
            bSuccess = mpGraphics->DrawPolyPolygon(
                aTransform,
                basegfx::B2DPolyPolygon(aB2DPolygon),
                0.0,
                this);
        }

        if(bSuccess && IsLineColor())
@@ -205,13 +212,8 @@ void OutputDevice::DrawPolygon( const tools::Polygon& rPoly )
            const basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
            const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);

            // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
            // {
            //     aB2DPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolygon);
            // }

            bSuccess = mpGraphics->DrawPolyLine(
                basegfx::B2DHomMatrix(),
                aTransform,
                aB2DPolygon,
                0.0,
                aB2DLineWidth,
@@ -298,13 +300,19 @@ void OutputDevice::ImplDrawPolyPolygonWithB2DPolyPolygon(const basegfx::B2DPolyP
        basegfx::B2DPolyPolygon aB2DPolyPolygon(rB2DPolyPoly);
        bool bSuccess(true);

        // transform the polygon and ensure closed
        aB2DPolyPolygon.transform(aTransform);
        aB2DPolyPolygon.setClosed(true);
        // ensure closed - maybe assert, hinders buffering
        if(!aB2DPolyPolygon.isClosed())
        {
            aB2DPolyPolygon.setClosed(true);
        }

        if(IsFillColor())
        {
            bSuccess = mpGraphics->DrawPolyPolygon(aB2DPolyPolygon, 0.0, this);
            bSuccess = mpGraphics->DrawPolyPolygon(
                aTransform,
                aB2DPolyPolygon,
                0.0,
                this);
        }

        if(bSuccess && IsLineColor())
@@ -312,15 +320,10 @@ void OutputDevice::ImplDrawPolyPolygonWithB2DPolyPolygon(const basegfx::B2DPolyP
            const basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
            const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);

            // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
            // {
            //     aB2DPolyPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyPolygon);
            // }

            for(sal_uInt32 a(0);bSuccess && a < aB2DPolyPolygon.count(); a++)
            {
                bSuccess = mpGraphics->DrawPolyLine(
                    basegfx::B2DHomMatrix(),
                    aTransform,
                    aB2DPolyPolygon.getB2DPolygon(a),
                    0.0,
                    aB2DLineWidth,
diff --git a/vcl/source/outdev/polyline.cxx b/vcl/source/outdev/polyline.cxx
index b5c06b9..da8be42 100644
--- a/vcl/source/outdev/polyline.cxx
+++ b/vcl/source/outdev/polyline.cxx
@@ -71,14 +71,6 @@ void OutputDevice::DrawPolyLine( const tools::Polygon& rPoly )
    const basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
    const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);

    // transform the polygon - do not (!)
    // aB2DPolyLine.transform( aTransform );

    // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
    // {
    //     aB2DPolyLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine);
    // }

    if(mpGraphics->DrawPolyLine(
        aTransform,
        aB2DPolyLine,
@@ -350,27 +342,7 @@ bool OutputDevice::DrawPolyLineDirect(
        const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation() * rObjectTransform);
        const bool bLineWidthZero(basegfx::fTools::equalZero(fLineWidth));
        const basegfx::B2DVector aB2DLineWidth(bLineWidthZero ? 1.0 : fLineWidth, bLineWidthZero ? 1.0 : fLineWidth);

        // transform the line width if used
        // if( fLineWidth != 0.0 )
        // {
        //     aB2DLineWidth = aTransform * basegfx::B2DVector( fLineWidth, fLineWidth );
        // }

        // transform the polygon - no!
        // basegfx::B2DPolygon aB2DPolygon(rB2DPolygon);
        // aB2DPolygon.transform(aTransform);

        const bool bPixelSnapHairline((mnAntialiasing & AntialiasingFlags::PixelSnapHairline) && rB2DPolygon.count() < 1000);
        // if((mnAntialiasing & AntialiasingFlags::PixelSnapHairline) &&
        //    aB2DPolygon.count() < 1000)
        // {
        //     // #i98289#, #i101491#
        //     // better to remove doubles on device coordinates. Also assume from a given amount
        //     // of points that the single edges are not long enough to smooth
        //     aB2DPolygon.removeDoublePoints();
        //     aB2DPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolygon);
        // }

        // draw the polyline
        bool bDrawSuccess = mpGraphics->DrawPolyLine(
diff --git a/vcl/source/outdev/transparent.cxx b/vcl/source/outdev/transparent.cxx
index ecd63b6..5a9bd59 100644
--- a/vcl/source/outdev/transparent.cxx
+++ b/vcl/source/outdev/transparent.cxx
@@ -212,7 +212,10 @@ void OutputDevice::ImplPrintTransparent( const Bitmap& rBmp, const Bitmap& rMask
// void OutputDevice::DrawPolyPolygon( const basegfx::B2DPolyPolygon& rB2DPolyPoly )
// so when changes are made here do not forget to make changes there, too

void OutputDevice::DrawTransparent( const basegfx::B2DPolyPolygon& rB2DPolyPoly, double fTransparency)
void OutputDevice::DrawTransparent(
    const basegfx::B2DHomMatrix& rObjectTransform,
    const basegfx::B2DPolyPolygon& rB2DPolyPoly,
    double fTransparency)
{
    assert(!is_double_buffered_window());

@@ -241,16 +244,27 @@ void OutputDevice::DrawTransparent( const basegfx::B2DPolyPolygon& rB2DPolyPoly,
       (RasterOp::OverPaint == GetRasterOp()) )
    {
        // b2dpolygon support not implemented yet on non-UNX platforms
        const basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation();
        basegfx::B2DPolyPolygon aB2DPolyPolygon(rB2DPolyPoly);

        // transform the polygon into device space and ensure it is closed
        aB2DPolyPolygon.transform( aTransform );
        aB2DPolyPolygon.setClosed( true );
        // ensure it is closed
        if(!aB2DPolyPolygon.isClosed())
        {
            // maybe assert, prevents buffering due to making a copy
            aB2DPolyPolygon.setClosed( true );
        }

        bool bDrawnOk = true;
        // create ObjectToDevice transformation
        const basegfx::B2DHomMatrix aFullTransform(ImplGetDeviceTransformation() * rObjectTransform);
        bool bDrawnOk(true);

        if( IsFillColor() )
            bDrawnOk = mpGraphics->DrawPolyPolygon( aB2DPolyPolygon, fTransparency, this );
        {
            bDrawnOk = mpGraphics->DrawPolyPolygon(
                aFullTransform,
                aB2DPolyPolygon,
                fTransparency,
                this);
        }

        if( bDrawnOk && IsLineColor() )
        {
@@ -263,7 +277,7 @@ void OutputDevice::DrawTransparent( const basegfx::B2DPolyPolygon& rB2DPolyPoly,
                const basegfx::B2DPolygon aOnePoly(aB2DPolyPolygon.getB2DPolygon(nPolyIdx));

                mpGraphics->DrawPolyLine(
                    basegfx::B2DHomMatrix(),
                    aFullTransform,
                    aOnePoly,
                    fTransparency,
                    aHairlineWidth,
@@ -338,9 +352,8 @@ bool OutputDevice::DrawTransparentNatively ( const tools::PolyPolygon& rPolyPoly
            InitFillColor();

        // get the polygon in device coordinates
        basegfx::B2DPolyPolygon aB2DPolyPolygon( rPolyPoly.getB2DPolyPolygon() );
        const basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation();
        aB2DPolyPolygon.transform( aTransform );
        basegfx::B2DPolyPolygon aB2DPolyPolygon(rPolyPoly.getB2DPolyPolygon());
        const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation());

        const double fTransparency = 0.01 * nTransparencePercent;
        if( mbFillColor )
@@ -354,13 +367,18 @@ bool OutputDevice::DrawTransparentNatively ( const tools::PolyPolygon& rPolyPoly
            // functionality and we use the fallback some lines below (which is not very good,
            // though. For now, WinSalGraphics::drawPolyPolygon will detect printer usage and
            // correct the wrong mapping (see there for details)
            bDrawn = mpGraphics->DrawPolyPolygon( aB2DPolyPolygon, fTransparency, this );
            bDrawn = mpGraphics->DrawPolyPolygon(
                aTransform,
                aB2DPolyPolygon,
                fTransparency,
                this);
        }

        if( mbLineColor )
        {
            // disable the fill color for now
            mpGraphics->SetFillColor();

            // draw the border line
            const basegfx::B2DVector aLineWidths( 1, 1 );
            const sal_uInt32 nPolyCount(aB2DPolyPolygon.count());
@@ -371,7 +389,7 @@ bool OutputDevice::DrawTransparentNatively ( const tools::PolyPolygon& rPolyPoly
                const basegfx::B2DPolygon aPolygon(aB2DPolyPolygon.getB2DPolygon(nPolyIdx));

                bDrawn = mpGraphics->DrawPolyLine(
                    basegfx::B2DHomMatrix(),
                    aTransform,
                    aPolygon,
                    fTransparency,
                    aLineWidths,
@@ -381,6 +399,7 @@ bool OutputDevice::DrawTransparentNatively ( const tools::PolyPolygon& rPolyPoly
                    bPixelSnapHairline,
                    this );
            }

            // prepare to restore the fill color
            mbInitFillColor = mbFillColor;
        }
diff --git a/vcl/unx/generic/gdi/gdiimpl.cxx b/vcl/unx/generic/gdi/gdiimpl.cxx
index c650f9d..a5fcf86 100644
--- a/vcl/unx/generic/gdi/gdiimpl.cxx
+++ b/vcl/unx/generic/gdi/gdiimpl.cxx
@@ -1442,10 +1442,13 @@ bool X11SalGraphicsImpl::drawEPS( long,long,long,long,void*,sal_uLong )
}

// draw a poly-polygon
bool X11SalGraphicsImpl::drawPolyPolygon( const basegfx::B2DPolyPolygon& rOrigPolyPoly, double fTransparency )
bool X11SalGraphicsImpl::drawPolyPolygon(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolyPolygon& rPolyPolygon,
    double fTransparency)
{
    // nothing to do for empty polypolygons
    const int nOrigPolyCount = rOrigPolyPoly.count();
    const int nOrigPolyCount = rPolyPolygon.count();
    if( nOrigPolyCount <= 0 )
        return true;

@@ -1464,21 +1467,24 @@ bool X11SalGraphicsImpl::drawPolyPolygon( const basegfx::B2DPolyPolygon& rOrigPo
    if( pRenderEnv )
        return false;

    // Fallback: Transform to DeviceCoordinates
    basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon);
    aPolyPolygon.transform(rObjectToDevice);

    // snap to raster if requested
    basegfx::B2DPolyPolygon aPolyPoly = rOrigPolyPoly;
    const bool bSnapToRaster = !mrParent.getAntiAliasB2DDraw();
    if( bSnapToRaster )
        aPolyPoly = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges( aPolyPoly );
        aPolyPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges( aPolyPolygon );

    // don't bother with polygons outside of visible area
    const basegfx::B2DRange aViewRange( 0, 0, GetGraphicsWidth(), GetGraphicsHeight() );
    aPolyPoly = basegfx::utils::clipPolyPolygonOnRange( aPolyPoly, aViewRange, true, false );
    if( !aPolyPoly.count() )
    aPolyPolygon = basegfx::utils::clipPolyPolygonOnRange( aPolyPolygon, aViewRange, true, false );
    if( !aPolyPolygon.count() )
        return true;

    // tessellate the polypolygon into trapezoids
    basegfx::B2DTrapezoidVector aB2DTrapVector;
    basegfx::utils::trapezoidSubdivide( aB2DTrapVector, aPolyPoly );
    basegfx::utils::trapezoidSubdivide( aB2DTrapVector, aPolyPolygon );
    const int nTrapCount = aB2DTrapVector.size();
    if( !nTrapCount )
        return true;
@@ -1654,7 +1660,12 @@ bool X11SalGraphicsImpl::drawPolyLine(
    for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx )
    {
        const basegfx::B2DPolyPolygon aOnePoly( aAreaPolyPoly.getB2DPolygon( nPolyIdx ) );
        bDrawnOk = drawPolyPolygon( aOnePoly, fTransparency );

        bDrawnOk = drawPolyPolygon(
            basegfx::B2DHomMatrix(),
            aOnePoly,
            fTransparency);

        if( !bDrawnOk )
            break;
    }
diff --git a/vcl/unx/generic/gdi/gdiimpl.hxx b/vcl/unx/generic/gdi/gdiimpl.hxx
index f738e1e..ac80ec6 100644
--- a/vcl/unx/generic/gdi/gdiimpl.hxx
+++ b/vcl/unx/generic/gdi/gdiimpl.hxx
@@ -157,7 +157,11 @@ public:
    virtual void drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry ) override;

    virtual void drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, PCONSTSALPOINT* pPtAry ) override;
    virtual bool drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) override;

    virtual bool drawPolyPolygon(
                const basegfx::B2DHomMatrix& rObjectToDevice,
                const basegfx::B2DPolyPolygon&,
                double fTransparency) override;

    virtual bool drawPolyLine(
                const basegfx::B2DHomMatrix& rObjectToDevice,
diff --git a/vcl/unx/generic/gdi/salgdi.cxx b/vcl/unx/generic/gdi/salgdi.cxx
index c6eff6d..632d08d 100644
--- a/vcl/unx/generic/gdi/salgdi.cxx
+++ b/vcl/unx/generic/gdi/salgdi.cxx
@@ -577,14 +577,17 @@ css::uno::Any X11SalGraphics::GetNativeSurfaceHandle(cairo::SurfaceSharedPtr& rS
#endif // ENABLE_CAIRO_CANVAS

// draw a poly-polygon
bool X11SalGraphics::drawPolyPolygon( const basegfx::B2DPolyPolygon& rOrigPolyPoly, double fTransparency )
bool X11SalGraphics::drawPolyPolygon(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolyPolygon& rPolyPolygon,
    double fTransparency)
{
    if(fTransparency >= 1.0)
    {
        return true;
    }

    const sal_uInt32 nPolyCount(rOrigPolyPoly.count());
    const sal_uInt32 nPolyCount(rPolyPolygon.count());

    if(nPolyCount <= 0)
    {
@@ -592,6 +595,10 @@ bool X11SalGraphics::drawPolyPolygon( const basegfx::B2DPolyPolygon& rOrigPolyPo
    }

#if ENABLE_CAIRO_CANVAS
    // Fallback: Transform to DeviceCoordinates
    basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon);
    aPolyPolygon.transform(rObjectToDevice);

    if(SALCOLOR_NONE == mnFillColor && SALCOLOR_NONE == mnPenColor)
    {
        return true;
@@ -602,12 +609,11 @@ bool X11SalGraphics::drawPolyPolygon( const basegfx::B2DPolyPolygon& rOrigPolyPo
    if (!m_bOpenGL && bUseCairoForPolygons && SupportsCairo())
    {
        // snap to raster if requested
        basegfx::B2DPolyPolygon aPolyPoly(rOrigPolyPoly);
        const bool bSnapPoints(!getAntiAliasB2DDraw());

        if(bSnapPoints)
        {
            aPolyPoly = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPoly);
            aPolyPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygon);
        }

        cairo_t* cr = getCairoContext();
@@ -615,7 +621,7 @@ bool X11SalGraphics::drawPolyPolygon( const basegfx::B2DPolyPolygon& rOrigPolyPo

        for(sal_uInt32 a(0); a < nPolyCount; ++a)
        {
            const basegfx::B2DPolygon aPolygon(aPolyPoly.getB2DPolygon(a));
            const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(a));
            const sal_uInt32 nPointCount(aPolygon.count());

            if(nPointCount)
@@ -684,7 +690,10 @@ bool X11SalGraphics::drawPolyPolygon( const basegfx::B2DPolyPolygon& rOrigPolyPo
    }
#endif // ENABLE_CAIRO_CANVAS

    return mxImpl->drawPolyPolygon( rOrigPolyPoly, fTransparency );
    return mxImpl->drawPolyPolygon(
        rObjectToDevice,
        rPolyPolygon,
        fTransparency);
}

#if ENABLE_CAIRO_CANVAS
diff --git a/vcl/unx/generic/print/genpspgraphics.cxx b/vcl/unx/generic/print/genpspgraphics.cxx
index 4ee35c8..3a8f951 100644
--- a/vcl/unx/generic/print/genpspgraphics.cxx
+++ b/vcl/unx/generic/print/genpspgraphics.cxx
@@ -404,7 +404,10 @@ void GenPspGraphics::drawPolyPolygon( sal_uInt32           nPoly,
    m_pPrinterGfx->DrawPolyPolygon (nPoly, pPoints, reinterpret_cast<const Point**>(pPtAry));
}

bool GenPspGraphics::drawPolyPolygon( const basegfx::B2DPolyPolygon&, double /*fTransparency*/ )
bool GenPspGraphics::drawPolyPolygon(
    const basegfx::B2DHomMatrix& /*rObjectToDevice*/,
    const basegfx::B2DPolyPolygon&,
    double /*fTransparency*/)
{
        // TODO: implement and advertise OutDevSupportType::B2DDraw support
        return false;
diff --git a/vcl/win/gdi/gdiimpl.cxx b/vcl/win/gdi/gdiimpl.cxx
index 33d89b6..4dcd86a 100644
--- a/vcl/win/gdi/gdiimpl.cxx
+++ b/vcl/win/gdi/gdiimpl.cxx
@@ -1951,71 +1951,6 @@ void impAddB2DPolygonToGDIPlusGraphicsPathReal(
    }
}

bool WinSalGraphicsImpl::drawPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPolygon, double fTransparency)
{
    const sal_uInt32 nCount(rPolyPolygon.count());

    if(mbBrush && nCount && (fTransparency >= 0.0 && fTransparency < 1.0))
    {
        Gdiplus::Graphics aGraphics(mrParent.getHDC());
        const sal_uInt8 aTrans(sal_uInt8(255) - static_cast<sal_uInt8>(basegfx::fround(fTransparency * 255.0)));
        const Gdiplus::Color aTestColor(aTrans, maFillColor.GetRed(), maFillColor.GetGreen(), maFillColor.GetBlue());
        const Gdiplus::SolidBrush aSolidBrush(aTestColor.GetValue());
        Gdiplus::GraphicsPath aGraphicsPath(Gdiplus::FillModeAlternate);

        for(sal_uInt32 a(0); a < nCount; a++)
        {
            if(0 != a)
            {
                // #i101491# not needed for first run
                aGraphicsPath.StartFigure();
            }

            impAddB2DPolygonToGDIPlusGraphicsPathReal(
                aGraphicsPath,
                rPolyPolygon.getB2DPolygon(a),
                basegfx::B2DHomMatrix(),
                false,
                false);

            aGraphicsPath.CloseFigure();
        }

        if(mrParent.getAntiAliasB2DDraw())
        {
            aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
        }
        else
        {
            aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
        }

        if(mrParent.isPrinter())
        {
            // #i121591#
            // Normally GdiPlus should not be used for printing at all since printers cannot
            // print transparent filled polygon geometry and normally this does not happen
            // since OutputDevice::RemoveTransparenciesFromMetaFile is used as preparation
            // and no transparent parts should remain for printing. But this can be overridden
            // by the user and thus happens. This call can only come (currently) from
            // OutputDevice::DrawTransparent, see comments there with the same TaskID.
            // If it is used, the mapping for the printer is wrong and needs to be corrected. I
            // checked that there is *no* transformation set and estimated that a stable factor
            // dependent of the printer's DPI is used. Create and set a transformation here to
            // correct this.
            const Gdiplus::REAL aDpiX(aGraphics.GetDpiX());
            const Gdiplus::REAL aDpiY(aGraphics.GetDpiY());

            aGraphics.ResetTransform();
            aGraphics.ScaleTransform(Gdiplus::REAL(100.0) / aDpiX, Gdiplus::REAL(100.0) / aDpiY, Gdiplus::MatrixOrderAppend);
        }

        aGraphics.FillPath(&aSolidBrush, &aGraphicsPath);
    }

     return true;
}

class SystemDependentData_GraphicsPath : public basegfx::SystemDependentData
{
private:
@@ -2040,6 +1975,150 @@ SystemDependentData_GraphicsPath::SystemDependentData_GraphicsPath(
{
}

bool WinSalGraphicsImpl::drawPolyPolygon(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolyPolygon& rPolyPolygon,
    double fTransparency)
{
    const sal_uInt32 nCount(rPolyPolygon.count());

    if(!mbBrush || 0 == nCount || fTransparency < 0.0 || fTransparency > 1.0)
    {
        return true;
    }

    Gdiplus::Graphics aGraphics(mrParent.getHDC());
    const sal_uInt8 aTrans(sal_uInt8(255) - static_cast<sal_uInt8>(basegfx::fround(fTransparency * 255.0)));
    const Gdiplus::Color aTestColor(aTrans, maFillColor.GetRed(), maFillColor.GetGreen(), maFillColor.GetBlue());
    const Gdiplus::SolidBrush aSolidBrush(aTestColor.GetValue());

    // Set full (Object-to-Device) transformation - if used
    if(rObjectToDevice.isIdentity())
    {
        aGraphics.ResetTransform();
    }
    else
    {
        Gdiplus::Matrix aMatrix;

        aMatrix.SetElements(
            rObjectToDevice.get(0, 0),
            rObjectToDevice.get(1, 0),
            rObjectToDevice.get(0, 1),
            rObjectToDevice.get(1, 1),
            rObjectToDevice.get(0, 2),
            rObjectToDevice.get(1, 2));
        aGraphics.SetTransform(&aMatrix);
    }

    // try to access buffered data
    std::shared_ptr<SystemDependentData_GraphicsPath> pSystemDependentData_GraphicsPath(
        rPolyPolygon.getSystemDependentData<SystemDependentData_GraphicsPath>());

    if(!pSystemDependentData_GraphicsPath)
    {
        // add to buffering mechanism
        pSystemDependentData_GraphicsPath = rPolyPolygon.addOrReplaceSystemDependentData<SystemDependentData_GraphicsPath>(
            ImplGetSystemDependentDataManager());

        // Note: In principle we could use the same buffered geometry at line
        // and fill polygons. Checked that in a first try, used
        // GraphicsPath::AddPath from Gdiplus combined with below used
        // StartFigure/CloseFigure, worked well (thus the line-draw version
        // may create non-cloded partial Polygon data).
        //
        // But in current reality it gets not used due to e.g.
        // SdrPathPrimitive2D::create2DDecomposition creating transformed
        // line and fill polygon-primitives (what could be changed).
        //
        // There will probably be more hindrances here in other rendering paths
        // which could all be found - intention to do this would be: Use more
        // transformations, less modifications of B2DPolygons/B2DPolyPolygons.
        //
        // A fix for SdrPathPrimitive2D would be to create the sub-geometry
        // and embed into a TransformPrimitive2D containing the transformation.
        //
        // A 2nd problem is that the NoLineJoin mode (basegfx::B2DLineJoin::NONE
        // && rLineWidths > 0.0) creates polygon fill infos that are not reusable
        // for the fill case (see ::drawPolyLine bnelow) - thus we would need a
        // bool and/or two system-dependent paths buffered - doable, but complicated.
        //
        // All in all: Make B2DPolyPolygon a SystemDependentDataProvider and buffer
        // the whole to-be-filled PolyPolygon independent from evtl. line-polygon
        // (at least for now...)

        // create data
        for(sal_uInt32 a(0); a < nCount; a++)
        {
            if(0 != a)
            {
                // #i101491# not needed for first run
                pSystemDependentData_GraphicsPath->getGraphicsPath().StartFigure();
            }

            impAddB2DPolygonToGDIPlusGraphicsPathReal(
                pSystemDependentData_GraphicsPath->getGraphicsPath(),
                rPolyPolygon.getB2DPolygon(a),
                rObjectToDevice, // not used due to the two 'false' values below, but to not forget later
                false,
                false);

            pSystemDependentData_GraphicsPath->getGraphicsPath().CloseFigure();
        }
    }

    if(mrParent.getAntiAliasB2DDraw())
    {
        aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
    }
    else
    {
        aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
    }

    if(mrParent.isPrinter())
    {
        // #i121591#
        // Normally GdiPlus should not be used for printing at all since printers cannot
        // print transparent filled polygon geometry and normally this does not happen
        // since OutputDevice::RemoveTransparenciesFromMetaFile is used as preparation
        // and no transparent parts should remain for printing. But this can be overridden
        // by the user and thus happens. This call can only come (currently) from
        // OutputDevice::DrawTransparent, see comments there with the same TaskID.
        // If it is used, the mapping for the printer is wrong and needs to be corrected. I
        // checked that there is *no* transformation set and estimated that a stable factor
        // dependent of the printer's DPI is used. Create and set a transformation here to
        // correct this.
        const Gdiplus::REAL aDpiX(aGraphics.GetDpiX());
        const Gdiplus::REAL aDpiY(aGraphics.GetDpiY());

        // Now the transformation maybe/is already used (see above), so do
        // modify it without resetting to not destroy it.
        // I double-checked with MS docu that Gdiplus::MatrixOrderAppend does what
        // we need - in our notation, would be a multiply from left to execute
        // current transform first and this scale last.
        // I tried to trigger this code using Print from the menu and various
        // targets, but got no hit, thus maybe obsolete anyways. If someone knows
        // more, feel free to remove it.
        // One more hint: This *may* also be needed now in ::drawPolyLine below
        // since it also uses transformations now.
        //
        // aGraphics.ResetTransform();

        aGraphics.ScaleTransform(
            Gdiplus::REAL(100.0) / aDpiX,
            Gdiplus::REAL(100.0) / aDpiY,
            Gdiplus::MatrixOrderAppend);
    }

    // use created or buffered data
    aGraphics.FillPath(
        &aSolidBrush,
        &pSystemDependentData_GraphicsPath->getGraphicsPath());

    return true;
}

bool WinSalGraphicsImpl::drawPolyLine(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolygon& rPolygon,
@@ -2062,15 +2141,24 @@ bool WinSalGraphicsImpl::drawPolyLine(
    bool bNoLineJoin(false);
    Gdiplus::Matrix aMatrix;

    // Set full (Object-to-Device) transformation
    aMatrix.SetElements(
        rObjectToDevice.get(0, 0),
        rObjectToDevice.get(1, 0),
        rObjectToDevice.get(0, 1),
        rObjectToDevice.get(1, 1),
        rObjectToDevice.get(0, 2),
        rObjectToDevice.get(1, 2));
    aGraphics.SetTransform(&aMatrix);
    // Set full (Object-to-Device) transformation - if used
    if(rObjectToDevice.isIdentity())
    {
        aGraphics.ResetTransform();
    }
    else
    {
        Gdiplus::Matrix aMatrix;

        aMatrix.SetElements(
            rObjectToDevice.get(0, 0),
            rObjectToDevice.get(1, 0),
            rObjectToDevice.get(0, 1),
            rObjectToDevice.get(1, 1),
            rObjectToDevice.get(0, 2),
            rObjectToDevice.get(1, 2));
        aGraphics.SetTransform(&aMatrix);
    }

    switch(eLineJoin)
    {
@@ -2145,7 +2233,7 @@ bool WinSalGraphicsImpl::drawPolyLine(
    {
        // add to buffering mechanism
        pSystemDependentData_GraphicsPath = rPolygon.addOrReplaceSystemDependentData<SystemDependentData_GraphicsPath>(
            SalGraphics::getSystemDependentDataManager());
            ImplGetSystemDependentDataManager());

        // fill data of buffered data
        pSystemDependentData_GraphicsPath->setPixelSnapHairline(bPixelSnapHairline);
diff --git a/vcl/win/gdi/gdiimpl.hxx b/vcl/win/gdi/gdiimpl.hxx
index 83d4125..94a6de0 100644
--- a/vcl/win/gdi/gdiimpl.hxx
+++ b/vcl/win/gdi/gdiimpl.hxx
@@ -106,7 +106,11 @@ public:
    virtual void drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry ) override;

    virtual void drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, PCONSTSALPOINT* pPtAry ) override;
    virtual bool drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) override;

    virtual bool drawPolyPolygon(
                const basegfx::B2DHomMatrix& rObjectToDevice,
                const basegfx::B2DPolyPolygon&,
                double fTransparency) override;

    virtual bool drawPolyLine(
                const basegfx::B2DHomMatrix& rObjectToDevice,
diff --git a/vcl/win/gdi/salbmp.cxx b/vcl/win/gdi/salbmp.cxx
index b61103e..664eedb 100644
--- a/vcl/win/gdi/salbmp.cxx
+++ b/vcl/win/gdi/salbmp.cxx
@@ -146,7 +146,7 @@ std::shared_ptr< Gdiplus::Bitmap > WinSalBitmap::ImplGetGdiPlusBitmap(const WinS
    {
        // add to buffering mechanism
        pSystemDependentData_GdiPlusBitmap = addOrReplaceSystemDependentData<SystemDependentData_GdiPlusBitmap>(
            SalGraphics::getSystemDependentDataManager());
            ImplGetSystemDependentDataManager());

        // create and set data
        if(pAlphaSource)
diff --git a/vcl/win/gdi/salgdi_gdiplus.cxx b/vcl/win/gdi/salgdi_gdiplus.cxx
index 1f536e1..99c7d0e 100644
--- a/vcl/win/gdi/salgdi_gdiplus.cxx
+++ b/vcl/win/gdi/salgdi_gdiplus.cxx
@@ -26,9 +26,15 @@

#include "gdiimpl.hxx"

bool WinSalGraphics::drawPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPolygon, double fTransparency)
bool WinSalGraphics::drawPolyPolygon(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolyPolygon& rPolyPolygon,
    double fTransparency)
{
    return mpImpl->drawPolyPolygon( rPolyPolygon, fTransparency );
    return mpImpl->drawPolyPolygon(
        rObjectToDevice,
        rPolyPolygon,
        fTransparency);
}

bool WinSalGraphics::drawPolyLine(