[Libreoffice-commits] core.git: basegfx/Library_basegfx.mk basegfx/source cui/source drawinglayer/source include/basegfx include/vcl solenv/clang-format vcl/headless vcl/inc vcl/opengl vcl/qt5 vcl/quartz vcl/source vcl/unx vcl/win

Libreoffice Gerrit user logerrit at kemper.freedesktop.org
Thu Aug 30 17:49:15 UTC 2018


 basegfx/Library_basegfx.mk                              |    1 
 basegfx/source/polygon/b2dpolygon.cxx                   |   47 ++
 basegfx/source/tools/systemdependentdata.cxx            |  141 +++++++
 cui/source/dialogs/screenshotannotationdlg.cxx          |    2 
 drawinglayer/source/primitive2d/polygonprimitive2d.cxx  |    6 
 drawinglayer/source/processor2d/vclpixelprocessor2d.cxx |   42 +-
 include/basegfx/polygon/b2dpolygon.hxx                  |   24 +
 include/basegfx/utils/systemdependentdata.hxx           |  139 +++++++
 include/vcl/outdev.hxx                                  |    1 
 solenv/clang-format/blacklist                           |    2 
 vcl/headless/svpgdi.cxx                                 |  289 +++++++++++++---
 vcl/inc/headless/svpgdi.hxx                             |   26 -
 vcl/inc/openglgdiimpl.hxx                               |    4 
 vcl/inc/qt5/Qt5Graphics.hxx                             |    6 
 vcl/inc/quartz/salgdi.h                                 |    4 
 vcl/inc/salgdi.hxx                                      |   11 
 vcl/inc/salgdiimpl.hxx                                  |    4 
 vcl/inc/unx/genpspgraphics.h                            |   15 
 vcl/inc/unx/salgdi.h                                    |    4 
 vcl/inc/win/salbmp.h                                    |   32 -
 vcl/inc/win/salgdi.h                                    |    4 
 vcl/opengl/gdiimpl.cxx                                  |   45 +-
 vcl/qt5/Qt5Graphics_GDI.cxx                             |   23 -
 vcl/quartz/salgdicommon.cxx                             |   39 +-
 vcl/source/app/svmain.cxx                               |    5 
 vcl/source/gdi/salgdilayout.cxx                         |  183 +++++++++-
 vcl/source/outdev/line.cxx                              |   19 -
 vcl/source/outdev/polygon.cxx                           |   82 ++--
 vcl/source/outdev/polyline.cxx                          |  152 +++++---
 vcl/source/outdev/transparent.cxx                       |   20 -
 vcl/unx/generic/gdi/gdiimpl.cxx                         |   41 +-
 vcl/unx/generic/gdi/gdiimpl.hxx                         |    4 
 vcl/unx/generic/gdi/salgdi.cxx                          |   44 +-
 vcl/unx/generic/print/genpspgraphics.cxx                |    4 
 vcl/win/gdi/gdiimpl.cxx                                 |  280 +++++++++++----
 vcl/win/gdi/gdiimpl.hxx                                 |    4 
 vcl/win/gdi/salbmp.cxx                                  |  219 +++---------
 vcl/win/gdi/salgdi_gdiplus.cxx                          |   15 
 38 files changed, 1479 insertions(+), 504 deletions(-)

New commits:
commit b9fa01a8d1137a95af9865a3e47995734c40da6e
Author:     Armin Le Grand <Armin.Le.Grand at cib.de>
AuthorDate: Fri Aug 24 13:01:08 2018 +0200
Commit:     Armin Le Grand <Armin.Le.Grand at cib.de>
CommitDate: Thu Aug 30 19:48:46 2018 +0200

    Support buffering SystemDependent GraphicData
    
    This is a first step to allow buffering of system
    dependent data, especially (but not only) for the
    system-dependent implementations of graphic output.
    For example, for B2DPolygon and Win output, it allows
    buffering the Gdiplus::GraphicsPath instead of re-
    creating it all the time.
    To support that, the change includes forwarding the
    current transformation to the renderers in SalGraphics.
    The current state in VCL is to transform all and
    everything to device coordinates at every single
    paint.
    I have currently started to do this for ::drawPolyLine
    implementations. The fallbacks for all systems will
    at the start of that method just transform the data
    to device coordinates, so all works as before.
    This may also be done for FilledPolygon paint in a later
    step, but most urgent is FatLine painting.
    An arrangement of shared_ptr/weak_ptr is used so that
    either the instance buffering (in the example B2DPolygon)
    or the instance managing it can delete it. The instance
    managing it currently uses a 1s Timer and a cycle-lifetime
    management, but that can be extended in the future
    to e.g. include size hints, too.
    The mechanism it designed to support multiple Data per
    buffering element, e.g. for B2DPolygon at the same time
    system-dependent instances of Gdiplus and Cairo can be
    buffered, but also PDF-data.
    This is achieved semi-automatic by using
    typeid(class).hash_code() as key for organization.
    The mechanism will be used for now at B2DPolygon, but
    is not limited to. There is already a similar but less
    general buffer (see GdiPlusBuffer) that can and will
    be converted to use this new mechanism.
    
    Added vcl/headless Cairo renderer to support given
    ObjectToDevice transformation (not to transform given
    B2DPolygon)
    Added support for CairoPath buffered at B2DPolygon,
    seems to work well. Need to do more tests
    
    Moved usage to templates suggested by Noel Grandin
    (Noel Grandin <noelgrandin at gmail.com>), thanks for
    these suggestions. Adapted Win usage to that, too.
    
    Converted Win-specific GdiPlus BitmapBuffer to new
    mechanism, works well. Checked, the manager holds
    now a mix of bitmap and path data under Win
    
    Added a cleanup mechanism to flush all buffered data
    at DeInitVCL() using flushAll() at
    SystemDependentDataBuffer
    
    Adapted Linux-versions of ::drawPolyLine to support
    PixelSnapHairline, for now in a simplified version
    that still allows buffering. This will also be used
    (and use buffering) for the Cairo-fallback in
    X11SalGraphics
    
    Change-Id: I88d7e438a20b96ddab7707050893bdd590c098c7
    Reviewed-on: https://gerrit.libreoffice.org/59555
    Tested-by: Armin Le Grand <Armin.Le.Grand at cib.de>
    Reviewed-by: Armin Le Grand <Armin.Le.Grand at cib.de>

diff --git a/basegfx/Library_basegfx.mk b/basegfx/Library_basegfx.mk
index 76d06b777668..0e428631056c 100644
--- a/basegfx/Library_basegfx.mk
+++ b/basegfx/Library_basegfx.mk
@@ -72,6 +72,7 @@ $(eval $(call gb_Library_add_exception_objects,basegfx,\
     basegfx/source/tools/keystoplerp \
     basegfx/source/tools/numbertools \
     basegfx/source/tools/stringconversiontools \
+    basegfx/source/tools/systemdependentdata \
     basegfx/source/tools/tools \
     basegfx/source/tools/unopolypolygon \
     basegfx/source/tools/zoomtools \
diff --git a/basegfx/source/polygon/b2dpolygon.cxx b/basegfx/source/polygon/b2dpolygon.cxx
index 9372cb3d9038..c94f262d6600 100644
--- a/basegfx/source/polygon/b2dpolygon.cxx
+++ b/basegfx/source/polygon/b2dpolygon.cxx
@@ -24,6 +24,7 @@
 #include <basegfx/matrix/b2dhommatrix.hxx>
 #include <basegfx/curve/b2dcubicbezier.hxx>
 #include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/utils/systemdependentdata.hxx>
 #include <algorithm>
 #include <memory>
 #include <vector>
@@ -455,20 +456,21 @@ public:
     }
 };
 
-class ImplBufferedData
+class ImplBufferedData : public basegfx::SystemDependentDataHolder
 {
 private:
     // Possibility to hold the last subdivision
-    std::unique_ptr< basegfx::B2DPolygon >        mpDefaultSubdivision;
+    std::unique_ptr< basegfx::B2DPolygon >  mpDefaultSubdivision;
 
     // Possibility to hold the last B2DRange calculation
-    std::unique_ptr< basegfx::B2DRange >          mpB2DRange;
+    std::unique_ptr< basegfx::B2DRange >    mpB2DRange;
 
 public:
     ImplBufferedData()
     :   mpDefaultSubdivision(),
         mpB2DRange()
-    {}
+    {
+    }
 
     const basegfx::B2DPolygon& getDefaultAdaptiveSubdivision(const basegfx::B2DPolygon& rSource) const
     {
@@ -1100,6 +1102,26 @@ public:
             maPoints.transform(rMatrix);
         }
     }
+
+    void addOrReplaceSystemDependentData(basegfx::SystemDependentData_SharedPtr& rData)
+    {
+        if(!mpBufferedData)
+        {
+            mpBufferedData.reset(new ImplBufferedData);
+        }
+
+        mpBufferedData->addOrReplaceSystemDependentData(rData);
+    }
+
+    basegfx::SystemDependentData_SharedPtr getSystemDependentData(size_t hash_code) const
+    {
+        if(mpBufferedData)
+        {
+            return mpBufferedData->getSystemDependentData(hash_code);
+        }
+
+        return basegfx::SystemDependentData_SharedPtr();
+    }
 };
 
 namespace basegfx
@@ -1470,6 +1492,23 @@ namespace basegfx
         }
     }
 
+    void B2DPolygon::addOrReplaceSystemDependentDataInternal(SystemDependentData_SharedPtr& rData) const
+    {
+        // Need to get ImplB2DPolygon* from cow_wrapper *without*
+        // calling make_unique() here - we do not want to
+        // 'modify' the ImplB2DPolygon, but add buffered data that
+        // is valid for all referencing instances
+        const B2DPolygon* pMe(this);
+        const ImplB2DPolygon* pMyImpl(pMe->mpPolygon.get());
+
+        const_cast<ImplB2DPolygon*>(pMyImpl)->addOrReplaceSystemDependentData(rData);
+    }
+
+    SystemDependentData_SharedPtr B2DPolygon::getSystemDependantDataInternal(size_t hash_code) const
+    {
+        return mpPolygon->getSystemDependentData(hash_code);
+    }
+
 } // end of namespace basegfx
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basegfx/source/tools/systemdependentdata.cxx b/basegfx/source/tools/systemdependentdata.cxx
new file mode 100755
index 000000000000..45f2efba5012
--- /dev/null
+++ b/basegfx/source/tools/systemdependentdata.cxx
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <basegfx/utils/systemdependentdata.hxx>
+
+namespace basegfx
+{
+    SystemDependentDataManager::SystemDependentDataManager()
+    {
+    }
+
+    SystemDependentDataManager::~SystemDependentDataManager()
+    {
+    }
+} // namespace basegfx
+
+namespace basegfx
+{
+    MinimalSystemDependentDataManager::MinimalSystemDependentDataManager()
+    :   SystemDependentDataManager(),
+        maSystemDependentDataReferences()
+    {
+    }
+
+    MinimalSystemDependentDataManager::~MinimalSystemDependentDataManager()
+    {
+    }
+
+    void MinimalSystemDependentDataManager::startUsage(basegfx::SystemDependentData_SharedPtr& rData)
+    {
+        if(rData)
+        {
+            maSystemDependentDataReferences.insert(rData);
+        }
+    }
+
+    void MinimalSystemDependentDataManager::endUsage(basegfx::SystemDependentData_SharedPtr& rData)
+    {
+        if(rData)
+        {
+            maSystemDependentDataReferences.erase(rData);
+        }
+    }
+
+    void MinimalSystemDependentDataManager::touchUsage(basegfx::SystemDependentData_SharedPtr& /* rData */)
+    {
+    }
+
+    void MinimalSystemDependentDataManager::flushAll()
+    {
+        maSystemDependentDataReferences.clear();
+    }
+} // namespace basegfx
+
+namespace basegfx
+{
+    SystemDependentData::SystemDependentData(
+        SystemDependentDataManager& rSystemDependentDataManager,
+        sal_uInt32 nHoldCycles)
+    :   mrSystemDependentDataManager(rSystemDependentDataManager),
+        mnHoldCycles(nHoldCycles)
+    {
+    }
+
+    SystemDependentData::~SystemDependentData()
+    {
+    }
+} // namespace basegfx
+
+namespace basegfx
+{
+    SystemDependentDataHolder::SystemDependentDataHolder()
+    :   maSystemDependentReferences()
+    {
+    }
+
+    SystemDependentDataHolder::~SystemDependentDataHolder()
+    {
+        for(auto& candidate : maSystemDependentReferences)
+        {
+            basegfx::SystemDependentData_SharedPtr aData(candidate.second.lock());
+
+            if(aData)
+            {
+                aData->getSystemDependentDataManager().endUsage(aData);
+            }
+        }
+    }
+
+    void SystemDependentDataHolder::addOrReplaceSystemDependentData(basegfx::SystemDependentData_SharedPtr& rData)
+    {
+        const size_t hash_code(typeid(*rData.get()).hash_code());
+        auto result(maSystemDependentReferences.find(hash_code));
+
+        if(result != maSystemDependentReferences.end())
+        {
+            basegfx::SystemDependentData_SharedPtr aData(result->second.lock());
+
+            if(aData)
+            {
+                aData->getSystemDependentDataManager().endUsage(aData);
+            }
+
+            maSystemDependentReferences.erase(result);
+            result = maSystemDependentReferences.end();
+        }
+
+        maSystemDependentReferences[hash_code] = rData;
+        rData->getSystemDependentDataManager().startUsage(rData);
+    }
+
+    SystemDependentData_SharedPtr SystemDependentDataHolder::getSystemDependentData(size_t hash_code) const
+    {
+        basegfx::SystemDependentData_SharedPtr aRetval;
+        auto result(maSystemDependentReferences.find(hash_code));
+
+        if(result != maSystemDependentReferences.end())
+        {
+            aRetval = result->second.lock();
+
+            if(aRetval)
+            {
+                aRetval->getSystemDependentDataManager().touchUsage(aRetval);
+            }
+            else
+            {
+                const_cast< SystemDependentDataHolder* >(this)->maSystemDependentReferences.erase(result);
+            }
+        }
+
+        return aRetval;
+    }
+} // namespace basegfx
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cui/source/dialogs/screenshotannotationdlg.cxx b/cui/source/dialogs/screenshotannotationdlg.cxx
index 86e53c89e081..5805c1f1537e 100644
--- a/cui/source/dialogs/screenshotannotationdlg.cxx
+++ b/cui/source/dialogs/screenshotannotationdlg.cxx
@@ -40,6 +40,7 @@
 #include <vcl/vclmedit.hxx>
 #include <vcl/button.hxx>
 #include <svtools/optionsdrawinglayer.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
 
 using namespace com::sun::star;
 
@@ -454,6 +455,7 @@ void ScreenshotAnnotationDlg_Impl::PaintControlDataEntry(
 
         // try to use transparency
         if (!mpVirtualBufferDevice->DrawPolyLineDirect(
+            basegfx::B2DHomMatrix(),
             aPolygon,
             fLineWidth,
             fTransparency,
diff --git a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
index e296f397e01b..2350f28699fc 100644
--- a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
@@ -291,6 +291,9 @@ namespace drawinglayer
             maLineAttribute(rLineAttribute),
             maStrokeAttribute(rStrokeAttribute)
         {
+            // simplify curve segments: moved here to not need to use it
+            // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
+            maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon);
         }
 
         PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(
@@ -301,6 +304,9 @@ namespace drawinglayer
             maLineAttribute(rLineAttribute),
             maStrokeAttribute()
         {
+            // simplify curve segments: moved here to not need to use it
+            // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
+            maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon);
         }
 
         bool PolygonStrokePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
index 0845c3316643..3295a97129f3 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
@@ -127,9 +127,9 @@ namespace drawinglayer
 
         bool VclPixelProcessor2D::tryDrawPolygonHairlinePrimitive2DDirect(const drawinglayer::primitive2d::PolygonHairlinePrimitive2D& rSource, double fTransparency)
         {
-            basegfx::B2DPolygon aLocalPolygon(rSource.getB2DPolygon());
+            const basegfx::B2DPolygon& rLocalPolygon(rSource.getB2DPolygon());
 
-            if(!aLocalPolygon.count())
+            if(!rLocalPolygon.count())
             {
                 // no geometry, done
                 return true;
@@ -139,10 +139,14 @@ namespace drawinglayer
 
             mpOutputDevice->SetFillColor();
             mpOutputDevice->SetLineColor(Color(aLineColor));
-            aLocalPolygon.transform(maCurrentTransformation);
+            //aLocalPolygon.transform(maCurrentTransformation);
 
             // try drawing; if it did not work, use standard fallback
-            return mpOutputDevice->DrawPolyLineDirect( aLocalPolygon, 0.0, fTransparency);
+            return mpOutputDevice->DrawPolyLineDirect(
+                maCurrentTransformation,
+                rLocalPolygon,
+                0.0,
+                fTransparency);
         }
 
         bool VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect(const drawinglayer::primitive2d::PolygonStrokePrimitive2D& rSource, double fTransparency)
@@ -158,7 +162,8 @@ namespace drawinglayer
             basegfx::B2DPolyPolygon aHairLinePolyPolygon;
 
             // simplify curve segments
-            aLocalPolygon = basegfx::utils::simplifyCurveSegments(aLocalPolygon);
+            // moved to PolygonStrokePrimitive2D::PolygonStrokePrimitive2D
+            // aLocalPolygon = basegfx::utils::simplifyCurveSegments(aLocalPolygon);
 
             if(rSource.getStrokeAttribute().isDefault() || 0.0 == rSource.getStrokeAttribute().getFullDotDashLen())
             {
@@ -182,24 +187,24 @@ namespace drawinglayer
                 return true;
             }
 
+            // check if LineWidth can be simplified in world coordinates
             double fLineWidth(rSource.getLineAttribute().getWidth());
 
             if(basegfx::fTools::more(fLineWidth, 0.0))
             {
                 basegfx::B2DVector aLineWidth(fLineWidth, 0.0);
-
                 aLineWidth = maCurrentTransformation * aLineWidth;
-                fLineWidth = aLineWidth.getLength();
-            }
+                const double fWorldLineWidth(aLineWidth.getLength());
 
-            // draw simple hairline for small line widths
-            // see also RenderPolygonStrokePrimitive2D which is used if this try fails
-            bool bIsAntiAliasing = getOptionsDrawinglayer().IsAntiAliasing();
-            if (   (basegfx::fTools::lessOrEqual(fLineWidth, 1.0) && bIsAntiAliasing)
-                || (basegfx::fTools::lessOrEqual(fLineWidth, 1.5) && !bIsAntiAliasing))
-            {
-                // draw simple hairline
-                fLineWidth = 0.0;
+                // draw simple hairline for small line widths
+                // see also RenderPolygonStrokePrimitive2D which is used if this try fails
+                bool bIsAntiAliasing = getOptionsDrawinglayer().IsAntiAliasing();
+                if (   (basegfx::fTools::lessOrEqual(fWorldLineWidth, 1.0) && bIsAntiAliasing)
+                    || (basegfx::fTools::lessOrEqual(fWorldLineWidth, 1.5) && !bIsAntiAliasing))
+                {
+                    // draw simple hairline
+                    fLineWidth = 0.0;
+                }
             }
 
             const basegfx::BColor aLineColor(
@@ -208,7 +213,9 @@ namespace drawinglayer
 
             mpOutputDevice->SetFillColor();
             mpOutputDevice->SetLineColor(Color(aLineColor));
-            aHairLinePolyPolygon.transform(maCurrentTransformation);
+
+            // do not transform self
+            // aHairLinePolyPolygon.transform(maCurrentTransformation);
 
             bool bHasPoints(false);
             bool bTryWorked(false);
@@ -222,6 +229,7 @@ namespace drawinglayer
                     bHasPoints = true;
 
                     if(mpOutputDevice->DrawPolyLineDirect(
+                        maCurrentTransformation,
                         aSingle,
                         fLineWidth,
                         fTransparency,
diff --git a/include/basegfx/polygon/b2dpolygon.hxx b/include/basegfx/polygon/b2dpolygon.hxx
index 714d2a1fd10d..353e7130f5ed 100644
--- a/include/basegfx/polygon/b2dpolygon.hxx
+++ b/include/basegfx/polygon/b2dpolygon.hxx
@@ -29,6 +29,7 @@
 #include <basegfx/basegfxdllapi.h>
 
 class ImplB2DPolygon;
+class SalGraphicsImpl;
 
 namespace basegfx
 {
@@ -37,6 +38,9 @@ namespace basegfx
     class B2DVector;
     class B2DHomMatrix;
     class B2DCubicBezier;
+    class SystemDependentData;
+    class SystemDependentDataManager;
+    typedef std::shared_ptr<SystemDependentData> SystemDependentData_SharedPtr;
 }
 
 namespace basegfx
@@ -218,6 +222,26 @@ namespace basegfx
 
         /// apply transformation given in matrix form
         void transform(const basegfx::B2DHomMatrix& rMatrix);
+
+        // 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 B2DPolygons
diff --git a/include/basegfx/utils/systemdependentdata.hxx b/include/basegfx/utils/systemdependentdata.hxx
new file mode 100755
index 000000000000..17a0ce44f815
--- /dev/null
+++ b/include/basegfx/utils/systemdependentdata.hxx
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_BASEGFX_SYSTEMDEPENDENTDATA_HXX
+#define INCLUDED_BASEGFX_SYSTEMDEPENDENTDATA_HXX
+
+#include <sal/types.h>
+#include <basegfx/basegfxdllapi.h>
+#include <memory>
+#include <map>
+#include <set>
+
+namespace basegfx
+{
+    class SystemDependentData;
+    typedef std::shared_ptr<SystemDependentData> SystemDependentData_SharedPtr;
+    typedef std::weak_ptr<SystemDependentData> SystemDependentData_WeakPtr;
+} // end of namespace basegfx
+
+namespace basegfx
+{
+    class BASEGFX_DLLPUBLIC SystemDependentDataManager
+    {
+    private:
+        // noncopyable
+        SystemDependentDataManager(const SystemDependentDataManager&) = delete;
+        SystemDependentDataManager& operator=(const SystemDependentDataManager&) = delete;
+
+    public:
+        SystemDependentDataManager();
+        virtual ~SystemDependentDataManager();
+
+        // call from (and with) SystemDependentData objects when start/end/touch
+        // usage is needed
+        virtual void startUsage(basegfx::SystemDependentData_SharedPtr& rData) = 0;
+        virtual void endUsage(basegfx::SystemDependentData_SharedPtr& rData) = 0;
+        virtual void touchUsage(basegfx::SystemDependentData_SharedPtr& rData) = 0;
+
+        // flush all buffred data (e.g. cleanup/shutdown)
+        virtual void flushAll() = 0;
+    };
+} // end of namespace basegfx
+
+namespace basegfx
+{
+    class BASEGFX_DLLPUBLIC MinimalSystemDependentDataManager : public SystemDependentDataManager
+    {
+    private:
+        // example of a minimal SystemDependentDataManager. It *needs to hold*
+        // a SystemDependentData_SharedPtr while SystemDependentDataHolder's will
+        // use a SystemDependentData_WeakPtr. When the held SystemDependentData_SharedPtr
+        // is deleted, the corresponding SystemDependentData_WeakPtr will get void.
+        // To make this work, a minimal SystemDependentDataManager *has* to hold at
+        // least that one SystemDependentData_SharedPtr.
+        // That SystemDependentData_SharedPtr may be (e.g. Timer-based or ressource-based)
+        // be freed then. This minimal implementation does never free it, so all stay valid.
+        // The instances may still be removed by endUsage calls, but there is no
+        // caching/buffering mechanism involved here at all. It's an example, but
+        // not used - better use an advanced derivation of SystemDependentDataManager
+        std::set< SystemDependentData_SharedPtr >   maSystemDependentDataReferences;
+
+    public:
+        MinimalSystemDependentDataManager();
+        virtual ~MinimalSystemDependentDataManager() override;
+
+        virtual void startUsage(basegfx::SystemDependentData_SharedPtr& rData) override;
+        virtual void endUsage(basegfx::SystemDependentData_SharedPtr& rData) override;
+        virtual void touchUsage(basegfx::SystemDependentData_SharedPtr& rData) override;
+        virtual void flushAll() override;
+    };
+} // end of namespace basegfx
+
+namespace basegfx
+{
+    class BASEGFX_DLLPUBLIC SystemDependentData
+    {
+    private:
+        // noncopyable
+        SystemDependentData(const SystemDependentData&) = delete;
+        SystemDependentData& operator=(const SystemDependentData&) = delete;
+
+        // reference to a SystemDependentDataManager, probably
+        // a single, globally used one, but not necessarily
+        SystemDependentDataManager&     mrSystemDependentDataManager;
+
+        // number of cycles a SystemDependentDataManager should/might
+        // hold this instance - does not have to be used, but should be
+        sal_uInt32                      mnHoldCycles;
+
+    public:
+        SystemDependentData(
+            SystemDependentDataManager& rSystemDependentDataManager,
+            sal_uInt32 nHoldCycles = 60);
+
+        // CAUTION! It is VERY important to keep this base class
+        // virtual, else typeid(class).hash_code() from derived classes
+        // will NOT work what is ESSENTIAL for the SystemDependentData
+        // mechanism to work properly. So DO NOT REMOVE virtual here, please.
+        virtual ~SystemDependentData();
+
+        // allow access to call startUsage/endUsage/touchUsage
+        // using getSystemDependentDataManager()
+        SystemDependentDataManager& getSystemDependentDataManager() { return mrSystemDependentDataManager; }
+
+        // number of cycles to hold data
+        sal_uInt32 getHoldCycles() const { return mnHoldCycles; }
+    };
+} // end of namespace basegfx
+
+namespace basegfx
+{
+    class BASEGFX_DLLPUBLIC SystemDependentDataHolder
+    {
+    private:
+        // Possibility to hold System-Dependent B2DPolygon-Representations
+        std::map< size_t, SystemDependentData_WeakPtr > maSystemDependentReferences;
+
+        // noncopyable
+        SystemDependentDataHolder(const SystemDependentDataHolder&) = delete;
+        SystemDependentDataHolder& operator=(const SystemDependentDataHolder&) = delete;
+
+    public:
+        SystemDependentDataHolder();
+        virtual ~SystemDependentDataHolder();
+
+        void addOrReplaceSystemDependentData(SystemDependentData_SharedPtr& rData);
+        SystemDependentData_SharedPtr getSystemDependentData(size_t hash_code) const;
+    };
+} // end of namespace basegfx
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx
index 54c22e6cc7fd..57bedf5b0635 100644
--- a/include/vcl/outdev.hxx
+++ b/include/vcl/outdev.hxx
@@ -800,6 +800,7 @@ public:
     // #i101491#
     // Helper who tries to use SalGDI's DrawPolyLine direct and returns it's bool.
     bool                        DrawPolyLineDirect(
+                                    const basegfx::B2DHomMatrix& rObjectTransform,
                                     const basegfx::B2DPolygon& rB2DPolygon,
                                     double fLineWidth = 0.0,
                                     double fTransparency = 0.0,
diff --git a/solenv/clang-format/blacklist b/solenv/clang-format/blacklist
index cd832249bf2d..810ca66431fc 100644
--- a/solenv/clang-format/blacklist
+++ b/solenv/clang-format/blacklist
@@ -369,6 +369,7 @@ basegfx/source/tools/gradienttools.cxx
 basegfx/source/tools/keystoplerp.cxx
 basegfx/source/tools/numbertools.cxx
 basegfx/source/tools/stringconversiontools.cxx
+basegfx/source/tools/systemdependentdata.cxx
 basegfx/source/tools/tools.cxx
 basegfx/source/tools/unopolypolygon.cxx
 basegfx/source/tools/unotools.cxx
@@ -5838,6 +5839,7 @@ include/basegfx/utils/gradienttools.hxx
 include/basegfx/utils/keystoplerp.hxx
 include/basegfx/utils/lerp.hxx
 include/basegfx/utils/rectcliptools.hxx
+include/basegfx/utils/systemdependentdata.hxx
 include/basegfx/utils/tools.hxx
 include/basegfx/utils/unopolypolygon.hxx
 include/basegfx/utils/zoomtools.hxx
diff --git a/vcl/headless/svpgdi.cxx b/vcl/headless/svpgdi.cxx
index f8ada21f8acd..fdf2295f367f 100644
--- a/vcl/headless/svpgdi.cxx
+++ b/vcl/headless/svpgdi.cxx
@@ -35,6 +35,8 @@
 #include <basegfx/polygon/b2dpolypolygontools.hxx>
 #include <basegfx/polygon/b2dpolygon.hxx>
 #include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/utils/systemdependentdata.hxx>
 
 #if ENABLE_CAIRO_CANVAS
 #   if defined CAIRO_VERSION && CAIRO_VERSION < CAIRO_VERSION_ENCODE(1, 10, 0)
@@ -720,8 +722,15 @@ void SvpSalGraphics::drawPolyLine(sal_uInt32 nPoints, const SalPoint* pPtAry)
         aPoly.setB2DPoint(i, basegfx::B2DPoint(pPtAry[i].mnX, pPtAry[i].mnY));
     aPoly.setClosed(false);
 
-    drawPolyLine(aPoly, 0.0, basegfx::B2DVector(1.0, 1.0), basegfx::B2DLineJoin::Miter,
-                 css::drawing::LineCap_BUTT, basegfx::deg2rad(15.0) /*default*/);
+    drawPolyLine(
+        basegfx::B2DHomMatrix(),
+        aPoly,
+        0.0,
+        basegfx::B2DVector(1.0, 1.0),
+        basegfx::B2DLineJoin::Miter,
+        css::drawing::LineCap_BUTT,
+        basegfx::deg2rad(15.0) /*default*/,
+        false);
 }
 
 void SvpSalGraphics::drawPolygon(sal_uInt32 nPoints, const SalPoint* pPtAry)
@@ -879,18 +888,141 @@ void SvpSalGraphics::drawLine( long nX1, long nY1, long nX2, long nY2 )
     releaseCairoContext(cr, false, extents);
 }
 
-basegfx::B2DRange SvpSalGraphics::drawPolyLine(
+class SystemDependentData_CairoPath : public basegfx::SystemDependentData
+{
+private:
+    cairo_path_t*       mpCairoPath;
+
+public:
+    SystemDependentData_CairoPath(
+        basegfx::SystemDependentDataManager& rSystemDependentDataManager,
+        cairo_path_t* pCairoPath);
+    virtual ~SystemDependentData_CairoPath() override;
+
+    cairo_path_t* getCairoPath() { return mpCairoPath; }
+};
+
+SystemDependentData_CairoPath::SystemDependentData_CairoPath(
+    basegfx::SystemDependentDataManager& rSystemDependentDataManager,
+    cairo_path_t* pCairoPath)
+:   basegfx::SystemDependentData(rSystemDependentDataManager),
+    mpCairoPath(pCairoPath)
+{
+}
+
+SystemDependentData_CairoPath::~SystemDependentData_CairoPath()
+{
+    if(nullptr != mpCairoPath)
+    {
+        cairo_path_destroy(mpCairoPath);
+        mpCairoPath = nullptr;
+    }
+}
+
+bool SvpSalGraphics::drawPolyLine(
+    const basegfx::B2DHomMatrix& rObjectToDevice,
+    const basegfx::B2DPolygon& rPolyLine,
+    double fTransparency,
+    const basegfx::B2DVector& rLineWidths,
+    basegfx::B2DLineJoin eLineJoin,
+    css::drawing::LineCap eLineCap,
+    double fMiterMinimumAngle,
+    bool bPixelSnapHairline)
+{
+    // short circuit if there is nothing to do
+    if(0 == rPolyLine.count())
+    {
+        return true;
+    }
+
+    // Wrap call to static verion of ::drawPolyLine by
+    // preparing/getting some local data and parameters
+    // due to usage in vcl/unx/generic/gdi/salgdi.cxx.
+    // This is mainly about extended handling of extents
+    // and the way destruction of CairoContext is handled
+    // due to current XOR stuff
+    cairo_t* cr = getCairoContext(false);
+    basegfx::B2DRange aExtents;
+    clipRegion(cr);
+
+    bool bRetval(
+        drawPolyLine(
+            cr,
+            &aExtents,
+            m_aLineColor,
+            getAntiAliasB2DDraw(),
+            rObjectToDevice,
+            rPolyLine,
+            fTransparency,
+            rLineWidths,
+            eLineJoin,
+            eLineCap,
+            fMiterMinimumAngle,
+            bPixelSnapHairline));
+
+    releaseCairoContext(cr, false, aExtents);
+
+    return bRetval;
+}
+
+bool SvpSalGraphics::drawPolyLine(
     cairo_t* cr,
+    basegfx::B2DRange* pExtents,
     const Color& rLineColor,
     bool bAntiAliasB2DDraw,
+    const basegfx::B2DHomMatrix& rObjectToDevice,
     const basegfx::B2DPolygon& rPolyLine,
     double fTransparency,
     const basegfx::B2DVector& rLineWidths,
     basegfx::B2DLineJoin eLineJoin,
     css::drawing::LineCap eLineCap,
-    double fMiterMinimumAngle)
+    double fMiterMinimumAngle,
+    bool bPixelSnapHairline)
 {
-    const bool bNoJoin = (basegfx::B2DLineJoin::NONE == eLineJoin && basegfx::fTools::more(rLineWidths.getX(), 0.0));
+    // short circuit if there is nothing to do
+    if(0 == rPolyLine.count())
+    {
+        return true;
+    }
+
+    // need to check/handle LineWidth when ObjectToDevice transformation is used
+    basegfx::B2DVector aLineWidths(rLineWidths);
+    const bool bObjectToDeviceIsIdentity(rObjectToDevice.isIdentity());
+    const basegfx::B2DVector aDeviceLineWidths(bObjectToDeviceIsIdentity ? rLineWidths : rObjectToDevice * rLineWidths);
+    const bool bCorrectLineWidth(!bObjectToDeviceIsIdentity && aDeviceLineWidths.getX() < 1.0 && aLineWidths.getX() >= 1.0);
+
+    // on-demand inverse of ObjectToDevice transformation
+    basegfx::B2DHomMatrix aObjectToDeviceInv;
+
+    if(bCorrectLineWidth)
+    {
+        if(aObjectToDeviceInv.isIdentity())
+        {
+            aObjectToDeviceInv = rObjectToDevice;
+            aObjectToDeviceInv.invert();
+        }
+
+        // calculate-back logical LineWidth for a hairline
+        aLineWidths = aObjectToDeviceInv * basegfx::B2DVector(1.0, 1.0);
+    }
+
+    if(!bObjectToDeviceIsIdentity)
+    {
+        // set ObjectToDevice transformation
+        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);
+    }
+
+    const bool bNoJoin((basegfx::B2DLineJoin::NONE == eLineJoin && basegfx::fTools::more(aLineWidths.getX(), 0.0)));
 
     // setup line attributes
     cairo_line_join_t eCairoLineJoin = CAIRO_LINE_JOIN_MITER;
@@ -933,76 +1065,131 @@ basegfx::B2DRange SvpSalGraphics::drawPolyLine(
         }
     }
 
-    cairo_set_source_rgba(cr, rLineColor.GetRed()/255.0,
-                              rLineColor.GetGreen()/255.0,
-                              rLineColor.GetBlue()/255.0,
-                              1.0-fTransparency);
+    cairo_set_source_rgba(
+        cr,
+        rLineColor.GetRed()/255.0,
+        rLineColor.GetGreen()/255.0,
+        rLineColor.GetBlue()/255.0,
+        1.0-fTransparency);
 
     cairo_set_line_join(cr, eCairoLineJoin);
     cairo_set_line_cap(cr, eCairoLineCap);
-    cairo_set_line_width(cr, rLineWidths.getX());
+    cairo_set_line_width(cr, aLineWidths.getX());
     cairo_set_miter_limit(cr, fMiterLimit);
 
+    // try to access buffered data
+    std::shared_ptr<SystemDependentData_CairoPath> pSystemDependentData_CairoPath(
+        rPolyLine.getSystemDependentData<SystemDependentData_CairoPath>());
 
-    basegfx::B2DRange extents(0, 0, 0, 0);
+    if(pSystemDependentData_CairoPath)
+    {
+        // check data validity
+        if(nullptr == pSystemDependentData_CairoPath->getCairoPath())
+        {
+            // data invalid, forget
+            pSystemDependentData_CairoPath.reset();
+        }
+    }
 
-    if (!bNoJoin)
+    if(pSystemDependentData_CairoPath)
     {
-        AddPolygonToPath(cr, rPolyLine, rPolyLine.isClosed(), !bAntiAliasB2DDraw, true);
-        extents = getClippedStrokeDamage(cr);
-        cairo_stroke(cr);
+        // re-use data
+        cairo_append_path(cr, pSystemDependentData_CairoPath->getCairoPath());
     }
     else
     {
-        const int nPointCount = rPolyLine.count();
-        // emulate rendering::PathJoinType::NONE by painting single edges
-        const sal_uInt32 nEdgeCount(rPolyLine.isClosed() ? nPointCount : nPointCount - 1);
-        basegfx::B2DPolygon aEdge;
-        aEdge.append(rPolyLine.getB2DPoint(0));
-        aEdge.append(basegfx::B2DPoint(0.0, 0.0));
+        // create data
+        basegfx::B2DPolygon aPolyLine(rPolyLine);
 
-        for (sal_uInt32 i = 0; i < nEdgeCount; ++i)
+        if(bPixelSnapHairline)
         {
-            const sal_uInt32 nNextIndex((i + 1) % nPointCount);
-            aEdge.setB2DPoint(1, rPolyLine.getB2DPoint(nNextIndex));
-            aEdge.setNextControlPoint(0, rPolyLine.getNextControlPoint(i % nPointCount));
-            aEdge.setPrevControlPoint(1, rPolyLine.getPrevControlPoint(nNextIndex));
+            // Need to take care of PixelSnapHairline now. The 'short' version
+            // will manipulate the Polygon by using the known tooling at
+            // basegfx. To do this correct, this needs to be done in device
+            // coordinates, so when the transformation is used, transform
+            // to device first, execute, transform back using the inverse.
+            // The important part for buffering the result and not need to
+            // do this at each repaint (for now) is to change a copy of the
+            // Polygon to create the CairoData, but to buffer it at the original
+            // unmodified Polygon.
+            // The 'long' version would be to add this to AddPolygonToPath
+            // equal as done in Win version (see impPixelSnap), should be done
+            // later
+            if(!bObjectToDeviceIsIdentity)
+            {
+                aPolyLine.transform(rObjectToDevice);
+            }
 
-            AddPolygonToPath(cr, aEdge, false, !bAntiAliasB2DDraw, true);
+            aPolyLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyLine);
 
-            extents.expand(getStrokeDamage(cr));
+            if(!bObjectToDeviceIsIdentity)
+            {
+                if(aObjectToDeviceInv.isIdentity())
+                {
+                    aObjectToDeviceInv = rObjectToDevice;
+                    aObjectToDeviceInv.invert();
+                }
 
-            cairo_stroke(cr);
+                aPolyLine.transform(aObjectToDeviceInv);
+            }
+        }
 
-            // prepare next step
-            aEdge.setB2DPoint(0, aEdge.getB2DPoint(1));
+        if (!bNoJoin)
+        {
+            AddPolygonToPath(cr, rPolyLine, rPolyLine.isClosed(), !bAntiAliasB2DDraw, true);
         }
+        else
+        {
+            const sal_uInt32 nPointCount(rPolyLine.count());
+            const sal_uInt32 nEdgeCount(rPolyLine.isClosed() ? nPointCount : nPointCount - 1);
+            basegfx::B2DPolygon aEdge;
 
-        extents.intersect(getClipBox(cr));
-    }
+            aEdge.append(rPolyLine.getB2DPoint(0));
+            aEdge.append(basegfx::B2DPoint(0.0, 0.0));
 
-    return extents;
-}
+            for (sal_uInt32 i(0); i < nEdgeCount; i++)
+            {
+                const sal_uInt32 nNextIndex((i + 1) % nPointCount);
+                aEdge.setB2DPoint(1, rPolyLine.getB2DPoint(nNextIndex));
+                aEdge.setNextControlPoint(0, rPolyLine.getNextControlPoint(i));
+                aEdge.setPrevControlPoint(1, rPolyLine.getPrevControlPoint(nNextIndex));
 
-bool SvpSalGraphics::drawPolyLine(
-    const basegfx::B2DPolygon& rPolyLine,
-    double fTransparency,
-    const basegfx::B2DVector& rLineWidths,
-    basegfx::B2DLineJoin eLineJoin,
-    css::drawing::LineCap eLineCap,
-    double fMiterMinimumAngle)
-{
-    // short circuit if there is nothing to do
-    if (rPolyLine.count() <= 0)
-        return true;
+                AddPolygonToPath(cr, aEdge, false, !bAntiAliasB2DDraw, true);
 
-    cairo_t* cr = getCairoContext(false);
-    clipRegion(cr);
+                // prepare next step
+                aEdge.setB2DPoint(0, aEdge.getB2DPoint(1));
+            }
+        }
 
-    basegfx::B2DRange extents = drawPolyLine(cr, m_aLineColor, getAntiAliasB2DDraw(), rPolyLine,
-                                             fTransparency, rLineWidths, eLineJoin, eLineCap, fMiterMinimumAngle);
+        // copy and add to buffering mechanism
+        rPolyLine.addOrReplaceSystemDependentData<SystemDependentData_CairoPath>(
+            SalGraphics::getSystemDependentDataManager(),
+            cairo_copy_path(cr));
+    }
 
-    releaseCairoContext(cr, false, extents);
+    // extract extents
+    if(nullptr != pExtents)
+    {
+        *pExtents = getClippedStrokeDamage(cr);
+    }
+
+    // draw and consume
+    cairo_stroke(cr);
+
+    if(!bObjectToDeviceIsIdentity)
+    {
+        // reset ObjectToDevice transformation if was set (safe, may
+        // be better suited at ::getCairoContext)
+        cairo_identity_matrix(cr);
+    }
+
+    if(nullptr != pExtents && !pExtents->isEmpty() && !bObjectToDeviceIsIdentity)
+    {
+        // transform extents to DeviceCoordiinates if used. These
+        // were calculated with ObjectToDevice transformation actively set,
+        // but use DeviceCoordinates locally
+        pExtents->transform(rObjectToDevice);
+    }
 
     return true;
 }
diff --git a/vcl/inc/headless/svpgdi.hxx b/vcl/inc/headless/svpgdi.hxx
index 8877b2ebf201..158c331eded6 100644
--- a/vcl/inc/headless/svpgdi.hxx
+++ b/vcl/inc/headless/svpgdi.hxx
@@ -99,16 +99,23 @@ public:
     static cairo_user_data_key_t* getDamageKey();
 
     static void clipRegion(cairo_t* cr, const vcl::Region& rClipRegion);
-    static basegfx::B2DRange drawPolyLine(
+
+    // need this static version of ::drawPolyLine for usage from
+    // vcl/unx/generic/gdi/salgdi.cxx. It gets wrapped by
+    // ::drawPolyLine with some added parameters (see there)
+    static bool drawPolyLine(
         cairo_t* cr,
+        basegfx::B2DRange* pExtents,
         const Color& rLineColor,
         bool bAntiAliasB2DDraw,
+        const basegfx::B2DHomMatrix& rObjectToDevice,
         const basegfx::B2DPolygon& rPolyLine,
         double fTransparency,
         const basegfx::B2DVector& rLineWidths,
         basegfx::B2DLineJoin eLineJoin,
         css::drawing::LineCap eLineCap,
-        double fMiterMinimumAngle);
+        double fMiterMinimumAngle,
+        bool bPixelSnapHairline);
 
 private:
     void invert(const basegfx::B2DPolygon &rPoly, SalInvert nFlags);
@@ -194,12 +201,15 @@ public:
     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            drawPolyLine( const basegfx::B2DPolygon&,
-                                          double fTransparency,
-                                          const basegfx::B2DVector& rLineWidths,
-                                          basegfx::B2DLineJoin,
-                                          css::drawing::LineCap,
-                                          double fMiterMinimumAngle) override;
+    virtual bool            drawPolyLine(
+                                const basegfx::B2DHomMatrix& rObjectToDevice,
+                                const basegfx::B2DPolygon&,
+                                double fTransparency,
+                                const basegfx::B2DVector& rLineWidths,
+                                basegfx::B2DLineJoin,
+                                css::drawing::LineCap,
+                                double fMiterMinimumAngle,
+                                bool bPixelSnapHairline) override;
     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,
diff --git a/vcl/inc/openglgdiimpl.hxx b/vcl/inc/openglgdiimpl.hxx
index 952a91e481e2..f2fd9b7819dd 100644
--- a/vcl/inc/openglgdiimpl.hxx
+++ b/vcl/inc/openglgdiimpl.hxx
@@ -252,12 +252,14 @@ public:
     virtual bool drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) override;
 
     virtual bool drawPolyLine(
+                const basegfx::B2DHomMatrix& rObjectToDevice,
                 const basegfx::B2DPolygon&,
                 double fTransparency,
                 const basegfx::B2DVector& rLineWidths,
                 basegfx::B2DLineJoin,
                 css::drawing::LineCap,
-                double fMiterMinimumAngle) override;
+                double fMiterMinimumAngle,
+                bool bPixelSnapHairline) override;
 
     virtual bool drawPolyLineBezier(
                 sal_uInt32 nPoints,
diff --git a/vcl/inc/qt5/Qt5Graphics.hxx b/vcl/inc/qt5/Qt5Graphics.hxx
index fa9823b0e789..6bde41ffa27f 100644
--- a/vcl/inc/qt5/Qt5Graphics.hxx
+++ b/vcl/inc/qt5/Qt5Graphics.hxx
@@ -116,9 +116,11 @@ public:
     virtual bool drawPolyPolygonBezier(sal_uInt32 nPoly, const sal_uInt32* pPoints,
                                        const SalPoint* const* pPtAry,
                                        const PolyFlags* const* pFlgAry) override;
-    virtual bool drawPolyLine(const basegfx::B2DPolygon&, double fTransparency,
+    virtual bool drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
+                              const basegfx::B2DPolygon&, double fTransparency,
                               const basegfx::B2DVector& rLineWidths, basegfx::B2DLineJoin,
-                              css::drawing::LineCap eLineCap, double fMiterMinimumAngle) override;
+                              css::drawing::LineCap eLineCap, double fMiterMinimumAngle,
+                              bool bPixelSnapHairline) override;
     virtual bool drawGradient(const tools::PolyPolygon&, const Gradient&) override;
 
     virtual void copyArea(long nDestX, long nDestY, long nSrcX, long nSrcY, long nSrcWidth,
diff --git a/vcl/inc/quartz/salgdi.h b/vcl/inc/quartz/salgdi.h
index 31a8353a17ed..b03fa5b6b3b2 100644
--- a/vcl/inc/quartz/salgdi.h
+++ b/vcl/inc/quartz/salgdi.h
@@ -229,12 +229,14 @@ public:
     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;
     virtual bool            drawPolyLine(
+                                const basegfx::B2DHomMatrix& rObjectToDevice,
                                 const basegfx::B2DPolygon&,
                                 double fTransparency,
                                 const basegfx::B2DVector& rLineWidths,
                                 basegfx::B2DLineJoin,
                                 css::drawing::LineCap eLineCap,
-                                double fMiterMinimumAngle) override;
+                                double fMiterMinimumAngle,
+                                bool bPixelSnapHairline) override;
     virtual bool            drawGradient( const tools::PolyPolygon&, const Gradient& ) override { return false; };
 
     // CopyArea --> No RasterOp, but ClipRegion
diff --git a/vcl/inc/salgdi.hxx b/vcl/inc/salgdi.hxx
index 8744c8f77573..866a10b5beb4 100644
--- a/vcl/inc/salgdi.hxx
+++ b/vcl/inc/salgdi.hxx
@@ -54,6 +54,7 @@ namespace basegfx {
     class B2DVector;
     class B2DPolygon;
     class B2DPolyPolygon;
+    class SystemDependentDataManager;
 }
 
 typedef sal_Unicode sal_Ucs; // TODO: use sal_UCS4 instead of sal_Unicode
@@ -76,6 +77,10 @@ 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;
 
@@ -242,12 +247,14 @@ public:
                                     const OutputDevice *i_pOutDev);
 
     bool                        DrawPolyLine(
+                                    const basegfx::B2DHomMatrix& rObjectToDevice,
                                     const basegfx::B2DPolygon& i_rPolygon,
                                     double i_fTransparency,
                                     const basegfx::B2DVector& i_rLineWidth,
                                     basegfx::B2DLineJoin i_eLineJoin,
                                     css::drawing::LineCap i_eLineCap,
                                     double i_fMiterMinimumAngle,
+                                    bool bPixelSnapHairline,
                                     const OutputDevice* i_pOutDev);
 
     bool                        DrawPolyLineBezier(
@@ -458,12 +465,14 @@ protected:
     virtual bool                drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) = 0;
 
     virtual bool                drawPolyLine(
+                                    const basegfx::B2DHomMatrix& rObjectToDevice,
                                     const basegfx::B2DPolygon&,
                                     double fTransparency,
                                     const basegfx::B2DVector& rLineWidths,
                                     basegfx::B2DLineJoin,
                                     css::drawing::LineCap,
-                                    double fMiterMinimumAngle) = 0;
+                                    double fMiterMinimumAngle,
+                                    bool bPixelSnapHairline) = 0;
 
     virtual bool                drawPolyLineBezier(
                                     sal_uInt32 nPoints,
diff --git a/vcl/inc/salgdiimpl.hxx b/vcl/inc/salgdiimpl.hxx
index 3fb3a0bfd7f9..8e545d093d20 100644
--- a/vcl/inc/salgdiimpl.hxx
+++ b/vcl/inc/salgdiimpl.hxx
@@ -103,12 +103,14 @@ public:
     virtual bool drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) = 0;
 
     virtual bool drawPolyLine(
+                const basegfx::B2DHomMatrix& rObjectToDevice,
                 const basegfx::B2DPolygon&,
                 double fTransparency,
                 const basegfx::B2DVector& rLineWidths,
                 basegfx::B2DLineJoin,
                 css::drawing::LineCap,
-                double fMiterMinimumAngle) = 0;
+                double fMiterMinimumAngle,
+                bool bPixelSnapHairline) = 0;
 
     virtual bool drawPolyLineBezier(
                 sal_uInt32 nPoints,
diff --git a/vcl/inc/unx/genpspgraphics.h b/vcl/inc/unx/genpspgraphics.h
index fd06248ff19f..3f6bae6c0744 100644
--- a/vcl/inc/unx/genpspgraphics.h
+++ b/vcl/inc/unx/genpspgraphics.h
@@ -128,12 +128,15 @@ public:
                                              PCONSTSALPOINT* pPtAry ) override;
     virtual bool            drawPolyPolygon( const basegfx::B2DPolyPolygon&,
                                              double fTransparency ) override;
-    virtual bool            drawPolyLine( const basegfx::B2DPolygon&,
-                                          double fTransparency,
-                                          const basegfx::B2DVector& rLineWidths,
-                                          basegfx::B2DLineJoin,
-                                          css::drawing::LineCap,
-                                          double fMiterMinimumAngle) override;
+    virtual bool            drawPolyLine(
+                                const basegfx::B2DHomMatrix& rObjectToDevice,
+                                const basegfx::B2DPolygon&,
+                                double fTransparency,
+                                const basegfx::B2DVector& rLineWidths,
+                                basegfx::B2DLineJoin,
+                                css::drawing::LineCap,
+                                double fMiterMinimumAngle,
+                                bool bPixelSnapHairline) override;
     virtual bool            drawPolyLineBezier( sal_uInt32 nPoints,
                                                 const SalPoint* pPtAry,
                                                 const PolyFlags* pFlgAry ) override;
diff --git a/vcl/inc/unx/salgdi.h b/vcl/inc/unx/salgdi.h
index 408434f9062e..8b73c4583243 100644
--- a/vcl/inc/unx/salgdi.h
+++ b/vcl/inc/unx/salgdi.h
@@ -163,12 +163,14 @@ public:
     virtual bool                    drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) override;
 
     virtual bool                    drawPolyLine(
+                                        const basegfx::B2DHomMatrix& rObjectToDevice,
                                         const basegfx::B2DPolygon&,
                                         double fTransparency,
                                         const basegfx::B2DVector& rLineWidth,
                                         basegfx::B2DLineJoin,
                                         css::drawing::LineCap,
-                                        double fMiterMinimumAngle) override;
+                                        double fMiterMinimumAngle,
+                                        bool bPixelSnapHairline) override;
 
     virtual bool                    drawGradient( const tools::PolyPolygon&, const Gradient& ) override;
 
diff --git a/vcl/inc/win/salbmp.h b/vcl/inc/win/salbmp.h
index f139a8608b30..7f172d21af82 100644
--- a/vcl/inc/win/salbmp.h
+++ b/vcl/inc/win/salbmp.h
@@ -23,6 +23,7 @@
 #include <tools/gen.hxx>
 #include <win/wincomp.hxx>
 #include <salbmp.hxx>
+#include <basegfx/utils/systemdependentdata.hxx>
 #include <memory>
 
 
@@ -32,26 +33,13 @@ class   BitmapPalette;
 class   SalGraphics;
 namespace Gdiplus { class Bitmap; }
 
-class WinSalBitmap : public SalBitmap
+class WinSalBitmap : public SalBitmap, public basegfx::SystemDependentDataHolder
 {
 private:
-    friend class GdiPlusBuffer; // allow buffer to remove maGdiPlusBitmap and mpAssociatedAlpha eventually
-
     Size                maSize;
     HGLOBAL             mhDIB;
     HBITMAP             mhDDB;
 
-    // the buffered evtl. used Gdiplus::Bitmap instance. It is managed by
-    // GdiPlusBuffer. To make this safe, it is only handed out as shared
-    // pointer; the GdiPlusBuffer may delete the local instance.
-
-    // mpAssociatedAlpha holds the last WinSalBitmap used to construct an
-    // evtl. buffered GdiPlusBmp. This is needed since the GdiPlusBmp is a single
-    // instance and remembered only on the content-WinSalBitmap, not on the
-    // alpha-WinSalBitmap.
-    std::shared_ptr< Gdiplus::Bitmap >       maGdiPlusBitmap;
-    const WinSalBitmap* mpAssociatedAlpha;
-
     sal_uInt16          mnBitCount;
 
     Gdiplus::Bitmap*    ImplCreateGdiPlusBitmap(const WinSalBitmap& rAlphaSource);
@@ -98,6 +86,22 @@ public:
     virtual bool                ScalingSupported() const override;
     virtual bool                Scale( const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag ) override;
     virtual bool                Replace( const Color& rSearchColor, const Color& rReplaceColor, sal_uInt8 nTol ) override;
+
+    // exclusive management op's for SystemDependentData at WinSalBitmap
+    template<class T>
+    std::shared_ptr<T> getSystemDependentData() const
+    {
+        return std::static_pointer_cast<T>(basegfx::SystemDependentDataHolder::getSystemDependentData(typeid(T).hash_code()));
+    }
+
+    template<class T, class... Args>
+    std::shared_ptr<T> addOrReplaceSystemDependentData(basegfx::SystemDependentDataManager& manager, Args&&... args) const
+    {
+        std::shared_ptr<T> r = std::make_shared<T>(manager, std::forward<Args>(args)...);
+        basegfx::SystemDependentData_SharedPtr r2(r);
+        const_cast< WinSalBitmap* >(this)->basegfx::SystemDependentDataHolder::addOrReplaceSystemDependentData(r2);
+        return r;
+    }
 };
 
 #endif // INCLUDED_VCL_INC_WIN_SALBMP_H
diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h
index 0540b66b34f7..51d14cb7ddda 100644
--- a/vcl/inc/win/salgdi.h
+++ b/vcl/inc/win/salgdi.h
@@ -237,12 +237,14 @@ protected:
     virtual void        drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, PCONSTSALPOINT* pPtAry ) override;
     virtual bool        drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) override;
     virtual bool        drawPolyLine(
+        const basegfx::B2DHomMatrix& rObjectToDevice,
         const basegfx::B2DPolygon&,
         double fTransparency,
         const basegfx::B2DVector& rLineWidth,
         basegfx::B2DLineJoin,
         css::drawing::LineCap,
-        double fMiterMinimumAngle) override;
+        double fMiterMinimumAngle,
+        bool bPixelSnapHairline) 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/opengl/gdiimpl.cxx b/vcl/opengl/gdiimpl.cxx
index 5efecae3a34e..9fff1d50cc13 100644
--- a/vcl/opengl/gdiimpl.cxx
+++ b/vcl/opengl/gdiimpl.cxx
@@ -1549,8 +1549,15 @@ void OpenGLSalGraphicsImpl::drawPolyLine( sal_uInt32 nPoints, const SalPoint* pP
         aPoly.setB2DPoint(i, basegfx::B2DPoint(pPtAry[i].mnX, pPtAry[i].mnY));
     aPoly.setClosed(false);
 
-    drawPolyLine(aPoly, 0.0, basegfx::B2DVector(1.0, 1.0), basegfx::B2DLineJoin::Miter,
-                 css::drawing::LineCap_BUTT, basegfx::deg2rad(15.0) /*default*/);
+    drawPolyLine(
+        basegfx::B2DHomMatrix(),
+        aPoly,
+        0.0,
+        basegfx::B2DVector(1.0, 1.0),
+        basegfx::B2DLineJoin::Miter,
+        css::drawing::LineCap_BUTT,
+        basegfx::deg2rad(15.0) /*default*/,
+        false);
 }
 
 void OpenGLSalGraphicsImpl::drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry )
@@ -1594,19 +1601,39 @@ bool OpenGLSalGraphicsImpl::drawPolyPolygon(const basegfx::B2DPolyPolygon& rPoly
     return true;
 }
 
-bool OpenGLSalGraphicsImpl::drawPolyLine(const basegfx::B2DPolygon& rPolygon, double fTransparency,
-                                         const basegfx::B2DVector& rLineWidth, basegfx::B2DLineJoin eLineJoin,
-                                         css::drawing::LineCap eLineCap, double fMiterMinimumAngle)
+bool OpenGLSalGraphicsImpl::drawPolyLine(
+    const basegfx::B2DHomMatrix& rObjectToDevice,
+    const basegfx::B2DPolygon& rPolygon,
+    double fTransparency,
+    const basegfx::B2DVector& rLineWidth,
+    basegfx::B2DLineJoin eLineJoin,
+    css::drawing::LineCap eLineCap,
+    double fMiterMinimumAngle,
+    bool bPixelSnapHairline)
 {
     VCL_GL_INFO("::drawPolyLine " << rPolygon.getB2DRange());
 
+    // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
+    basegfx::B2DPolygon aPolyLine(rPolygon);
+    aPolyLine.transform(rObjectToDevice);
+    if(bPixelSnapHairline) { aPolyLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyLine); }
+    const basegfx::B2DVector aLineWidth(rObjectToDevice * rLineWidth);
+
     // addDrawPolyLine() assumes that there are no duplicate points in the
     // polygon.
-    basegfx::B2DPolygon aPolygon(rPolygon);
-    aPolygon.removeDoublePoints();
+    // basegfx::B2DPolygon aPolygon(rPolygon);
+    aPolyLine.removeDoublePoints();
+
+    mpRenderList->addDrawPolyLine(
+        aPolyLine,
+        fTransparency,
+        aLineWidth,
+        eLineJoin,
+        eLineCap,
+        fMiterMinimumAngle,
+        mnLineColor,
+        mrParent.getAntiAliasB2DDraw());
 
-    mpRenderList->addDrawPolyLine(aPolygon, fTransparency, rLineWidth, eLineJoin, eLineCap,
-                                  fMiterMinimumAngle, mnLineColor, mrParent.getAntiAliasB2DDraw());
     PostBatchDraw();
     return true;
 }
diff --git a/vcl/qt5/Qt5Graphics_GDI.cxx b/vcl/qt5/Qt5Graphics_GDI.cxx
index 1a61fe540e79..5470295cbf32 100644
--- a/vcl/qt5/Qt5Graphics_GDI.cxx
+++ b/vcl/qt5/Qt5Graphics_GDI.cxx
@@ -316,28 +316,39 @@ bool Qt5Graphics::drawPolyPolygonBezier(sal_uInt32 /*nPoly*/, const sal_uInt32*
     return false;
 }
 
-bool Qt5Graphics::drawPolyLine(const basegfx::B2DPolygon& rPolyLine, double fTransparency,
+bool Qt5Graphics::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
+                               const basegfx::B2DPolygon& rPolyLine, double fTransparency,
                                const basegfx::B2DVector& rLineWidths,
                                basegfx::B2DLineJoin eLineJoin, css::drawing::LineCap eLineCap,
-                               double fMiterMinimumAngle)
+                               double fMiterMinimumAngle, bool bPixelSnapHairline)
 {
     if (SALCOLOR_NONE == m_aFillColor && SALCOLOR_NONE == m_aLineColor)
         return true;
 
     // short circuit if there is nothing to do
-    const int nPointCount = rPolyLine.count();
-    if (nPointCount <= 0)
+    if (0 == rPolyLine.count())
+    {
         return true;
+    }
+
+    // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
+    basegfx::B2DPolygon aPolyLine(rPolyLine);
+    aPolyLine.transform(rObjectToDevice);
+    if (bPixelSnapHairline)
+    {
+        aPolyLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyLine);
+    }
+    const basegfx::B2DVector aLineWidths(rObjectToDevice * rLineWidths);
 
     // setup poly-polygon path
     QPainterPath aPath;
-    AddPolygonToPath(aPath, rPolyLine, rPolyLine.isClosed(), !getAntiAliasB2DDraw(), true);
+    AddPolygonToPath(aPath, aPolyLine, aPolyLine.isClosed(), !getAntiAliasB2DDraw(), true);
 
     Qt5Painter aPainter(*this, false, 255 * (1.0 - fTransparency));
 
     // setup line attributes
     QPen aPen = aPainter.pen();
-    aPen.setWidth(rLineWidths.getX());
+    aPen.setWidth(aLineWidths.getX());
 
     switch (eLineJoin)
     {
diff --git a/vcl/quartz/salgdicommon.cxx b/vcl/quartz/salgdicommon.cxx
index 77241c282ea4..09adf784d2ad 100644
--- a/vcl/quartz/salgdicommon.cxx
+++ b/vcl/quartz/salgdicommon.cxx
@@ -24,6 +24,7 @@
 #include <cstring>
 
 #include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
 #include <osl/endian.h>
 #include <osl/file.hxx>
 #include <sal/types.h>
@@ -943,18 +944,20 @@ void AquaSalGraphics::drawPixel( long nX, long nY, Color nColor )
     ImplDrawPixel( nX, nY, aPixelColor );
 }
 
-bool AquaSalGraphics::drawPolyLine( const basegfx::B2DPolygon& rPolyLine,
-                                    double fTransparency,
-                                    const basegfx::B2DVector& rLineWidths,
-                                    basegfx::B2DLineJoin eLineJoin,
-                                    css::drawing::LineCap eLineCap,
-                                    double fMiterMinimumAngle)
+bool AquaSalGraphics::drawPolyLine(
+    const basegfx::B2DHomMatrix& rObjectToDevice,
+    const basegfx::B2DPolygon& rPolyLine,
+    double fTransparency,
+    const basegfx::B2DVector& rLineWidths,
+    basegfx::B2DLineJoin eLineJoin,
+    css::drawing::LineCap eLineCap,
+    double fMiterMinimumAngle,
+    bool bPixelSnapHairline)
 {
     DBG_DRAW_OPERATION("drawPolyLine", true);
 
     // short circuit if there is nothing to do
-    const int nPointCount = rPolyLine.count();
-    if( nPointCount <= 0 )
+    if(0 == rPolyLine.count())
     {
         DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
         return true;
@@ -968,16 +971,23 @@ bool AquaSalGraphics::drawPolyLine( const basegfx::B2DPolygon& rPolyLine,
     }
 #endif
 
+    // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
+    const basegfx::B2DVector aLineWidths(rObjectToDevice * rLineWidths);
+
     // #i101491# Aqua does not support B2DLineJoin::NONE; return false to use
     // the fallback (own geometry preparation)
     // #i104886# linejoin-mode and thus the above only applies to "fat" lines
-    if( (basegfx::B2DLineJoin::NONE == eLineJoin) &&
-        (rLineWidths.getX() > 1.3) )
+    if( (basegfx::B2DLineJoin::NONE == eLineJoin) && (aLineWidths.getX() > 1.3) )
     {
         DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
         return false;
     }
 
+    // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
+    basegfx::B2DPolygon aPolyLine(rPolyLine);
+    aPolyLine.transform(rObjectToDevice);
+    if(bPixelSnapHairline) { aPolyLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyLine); }
+
     // setup line attributes
     CGLineJoin aCGLineJoin = kCGLineJoinMiter;
     switch( eLineJoin )
@@ -1014,7 +1024,12 @@ bool AquaSalGraphics::drawPolyLine( const basegfx::B2DPolygon& rPolyLine,
     // setup poly-polygon path
     CGMutablePathRef xPath = CGPathCreateMutable();
     SAL_INFO( "vcl.cg", "CGPathCreateMutable() = " << xPath );
-    AddPolygonToPath( xPath, rPolyLine, rPolyLine.isClosed(), !getAntiAliasB2DDraw(), true );
+    AddPolygonToPath(
+        xPath,
+        aPolyLine,
+        aPolyLine.isClosed(),
+        !getAntiAliasB2DDraw(),
+        true);
 
     const CGRect aRefreshRect = CGPathGetBoundingBox( xPath );
     SAL_INFO( "vcl.cg", "CGPathGetBoundingBox(" << xPath << ") = " << aRefreshRect );
@@ -1034,7 +1049,7 @@ bool AquaSalGraphics::drawPolyLine( const basegfx::B2DPolygon& rPolyLine,
         CGContextSetAlpha( mrContext, 1.0 - fTransparency );
         CGContextSetLineJoin( mrContext, aCGLineJoin );
         CGContextSetLineCap( mrContext, aCGLineCap );
-        CGContextSetLineWidth( mrContext, rLineWidths.getX() );
+        CGContextSetLineWidth( mrContext, aLineWidths.getX() );
         CGContextSetMiterLimit(mrContext, fCGMiterLimit);
         SAL_INFO( "vcl.cg", "CGContextDrawPath(" << mrContext << ",kCGPathStroke)" );
         CGContextDrawPath( mrContext, kCGPathStroke );
diff --git a/vcl/source/app/svmain.cxx b/vcl/source/app/svmain.cxx
index a91b5c6686db..9e4e3ca1a04a 100644
--- a/vcl/source/app/svmain.cxx
+++ b/vcl/source/app/svmain.cxx
@@ -101,6 +101,8 @@
 #include <opengl/zone.hxx>
 #include <opengl/watchdog.hxx>
 
+#include <basegfx/utils/systemdependentdata.hxx>
+
 #if OSL_DEBUG_LEVEL > 0
 #include <typeinfo>
 #include <rtl/strbuf.hxx>
@@ -426,6 +428,9 @@ 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();
diff --git a/vcl/source/gdi/salgdilayout.cxx b/vcl/source/gdi/salgdilayout.cxx
index 8a70deeb3a59..08fb2fae49f7 100644
--- a/vcl/source/gdi/salgdilayout.cxx
+++ b/vcl/source/gdi/salgdilayout.cxx
@@ -31,6 +31,10 @@
 #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 <o3tl/make_unique.hxx>
 
 // The only common SalFrame method
 
@@ -75,6 +79,131 @@ namespace
     }
 }
 
+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();
+}
+
 rtl::Reference<OpenGLContext> SalGraphics::GetOpenGLContext() const
 {
     OpenGLSalGraphicsImpl *pImpl = dynamic_cast<OpenGLSalGraphicsImpl*>(GetImpl());
@@ -512,22 +641,56 @@ bool SalGraphics::DrawPolyPolygonBezier( sal_uInt32 i_nPoly, const sal_uInt32* i
     return bRet;
 }
 
-bool SalGraphics::DrawPolyLine( const basegfx::B2DPolygon& i_rPolygon,
-                                double i_fTransparency,
-                                const basegfx::B2DVector& i_rLineWidth,
-                                basegfx::B2DLineJoin i_eLineJoin,
-                                css::drawing::LineCap i_eLineCap,
-                                double i_fMiterMinimumAngle,
-                                const OutputDevice* i_pOutDev )
+bool SalGraphics::DrawPolyLine(
+    const basegfx::B2DHomMatrix& rObjectToDevice,
+    const basegfx::B2DPolygon& i_rPolygon,
+    double i_fTransparency,
+    const basegfx::B2DVector& i_rLineWidth,
+    basegfx::B2DLineJoin i_eLineJoin,
+    css::drawing::LineCap i_eLineCap,
+    double i_fMiterMinimumAngle,
+    bool bPixelSnapHairline,
+    const OutputDevice* i_pOutDev)
 {
     bool bRet = false;
+
     if( (m_nLayout & SalLayoutFlags::BiDiRtl) || (i_pOutDev && i_pOutDev->IsRTLEnabled()) )
     {
-        basegfx::B2DPolygon aMirror( mirror( i_rPolygon, i_pOutDev ) );
-        bRet = drawPolyLine( aMirror, i_fTransparency, i_rLineWidth, i_eLineJoin, i_eLineCap, i_fMiterMinimumAngle );
+        // if mirrored, we need to apply transformation since it is
+        // not clear what 'mirror' does - might be changed when this
+        // happens often
+        basegfx::B2DPolygon aMirror(i_rPolygon);
+
+        aMirror.transform(rObjectToDevice);
+        aMirror = mirror(aMirror, i_pOutDev);
+        // basegfx::B2DPolygon aMirror( mirror( i_rPolygon, i_pOutDev ) );
+
+        // also need to transform LineWidth
+        const basegfx::B2DVector aLineWidth(rObjectToDevice * i_rLineWidth);
+
+        bRet = drawPolyLine(
+            basegfx::B2DHomMatrix(), // now empty transformation, already used
+            aMirror,
+            i_fTransparency,
+            aLineWidth,
+            i_eLineJoin,
+            i_eLineCap,
+            i_fMiterMinimumAngle,
+            bPixelSnapHairline);
     }
     else
-        bRet = drawPolyLine( i_rPolygon, i_fTransparency, i_rLineWidth, i_eLineJoin, i_eLineCap, i_fMiterMinimumAngle );
+    {
+        bRet = drawPolyLine(
+            rObjectToDevice,
+            i_rPolygon,
+            i_fTransparency,
+            i_rLineWidth,
+            i_eLineJoin,
+            i_eLineCap,
+            i_fMiterMinimumAngle,
+            bPixelSnapHairline);
+    }
+
     return bRet;
 }
 
diff --git a/vcl/source/outdev/line.cxx b/vcl/source/outdev/line.cxx
index 8c856a83cd7c..fe16d676c29f 100644
--- a/vcl/source/outdev/line.cxx
+++ b/vcl/source/outdev/line.cxx
@@ -132,18 +132,21 @@ void OutputDevice::DrawLine( const Point& rStartPt, const Point& rEndPt )
         aB2DPolyLine.append(basegfx::B2DPoint(rEndPt.X(), rEndPt.Y()));
         aB2DPolyLine.transform( aTransform );
 
-        if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
-        {
-            aB2DPolyLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine);
-        }
+        const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
+        // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
+        // {
+        //     aB2DPolyLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine);
+        // }
 
         if( mpGraphics->DrawPolyLine(
+            basegfx::B2DHomMatrix(),
             aB2DPolyLine,
             0.0,
             aB2DLineWidth,
             basegfx::B2DLineJoin::NONE,
             css::drawing::LineCap_BUTT,
             basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
+            bPixelSnapHairline,
             this))
         {
             return;
@@ -242,24 +245,30 @@ void OutputDevice::drawLine( basegfx::B2DPolyPolygon aLinePolyPolygon, const Lin
         for(sal_uInt32 a(0); a < aLinePolyPolygon.count(); a++)
         {
             const basegfx::B2DPolygon aCandidate(aLinePolyPolygon.getB2DPolygon(a));
+            const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
             bool bDone(false);
 
             if(bTryAA)
             {
                 bDone = mpGraphics->DrawPolyLine(
+                    basegfx::B2DHomMatrix(),
                     aCandidate,
                     0.0,
                     basegfx::B2DVector(1.0,1.0),
                     basegfx::B2DLineJoin::NONE,
                     css::drawing::LineCap_BUTT,
                     basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
+                    bPixelSnapHairline,
                     this);
             }
 
             if(!bDone)
             {
                 tools::Polygon aPolygon(aCandidate);
-                mpGraphics->DrawPolyLine(aPolygon.GetSize(), reinterpret_cast<SalPoint*>(aPolygon.GetPointAry()), this);
+                mpGraphics->DrawPolyLine(
+                    aPolygon.GetSize(),
+                    reinterpret_cast<SalPoint*>(aPolygon.GetPointAry()),
+                    this);
             }
         }
     }
diff --git a/vcl/source/outdev/polygon.cxx b/vcl/source/outdev/polygon.cxx
index bef902a2d273..94ad52a79dbd 100644
--- a/vcl/source/outdev/polygon.cxx
+++ b/vcl/source/outdev/polygon.cxx
@@ -86,21 +86,25 @@ void OutputDevice::DrawPolyPolygon( const tools::PolyPolygon& rPolyPoly )
         if(bSuccess && IsLineColor())
         {
             const basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
+            const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
 
-            if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
-            {
-                aB2DPolyPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyPolygon);
-            }
+            // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
+            // {
+            //     aB2DPolyPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyPolygon);
+            // }
 
             for(sal_uInt32 a(0); bSuccess && a < aB2DPolyPolygon.count(); a++)
             {
-                bSuccess = mpGraphics->DrawPolyLine( aB2DPolyPolygon.getB2DPolygon(a),
-                                                     0.0,
-                                                     aB2DLineWidth,
-                                                     basegfx::B2DLineJoin::NONE,
-                                                     css::drawing::LineCap_BUTT,
-                                                     basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
-                                                     this);
+                bSuccess = mpGraphics->DrawPolyLine(
+                    basegfx::B2DHomMatrix(),
+                    aB2DPolyPolygon.getB2DPolygon(a),
+                    0.0,
+                    aB2DLineWidth,
+                    basegfx::B2DLineJoin::NONE,
+                    css::drawing::LineCap_BUTT,
+                    basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
+                    bPixelSnapHairline,
+                    this);
             }
         }
 
@@ -199,19 +203,23 @@ void OutputDevice::DrawPolygon( const tools::Polygon& rPoly )
         if(bSuccess && IsLineColor())
         {
             const basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
-
-            if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
-            {
-                aB2DPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolygon);
-            }
-
-            bSuccess = mpGraphics->DrawPolyLine( aB2DPolygon,
-                                                 0.0,
-                                                 aB2DLineWidth,
-                                                 basegfx::B2DLineJoin::NONE,
-                                                 css::drawing::LineCap_BUTT,
-                                                 basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
-                                                 this);
+            const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
+
+            // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
+            // {
+            //     aB2DPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolygon);
+            // }
+
+            bSuccess = mpGraphics->DrawPolyLine(
+                basegfx::B2DHomMatrix(),
+                aB2DPolygon,
+                0.0,
+                aB2DLineWidth,
+                basegfx::B2DLineJoin::NONE,
+                css::drawing::LineCap_BUTT,
+                basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
+                bPixelSnapHairline,
+                this);
         }
 
         if(bSuccess)
@@ -302,21 +310,25 @@ void OutputDevice::ImplDrawPolyPolygonWithB2DPolyPolygon(const basegfx::B2DPolyP
         if(bSuccess && IsLineColor())
         {
             const basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
+            const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
 
-            if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
-            {
-                aB2DPolyPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyPolygon);
-            }
+            // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
+            // {
+            //     aB2DPolyPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyPolygon);
+            // }
 
             for(sal_uInt32 a(0);bSuccess && a < aB2DPolyPolygon.count(); a++)
             {
-                bSuccess = mpGraphics->DrawPolyLine( aB2DPolyPolygon.getB2DPolygon(a),
-                                                     0.0,
-                                                     aB2DLineWidth,
-                                                     basegfx::B2DLineJoin::NONE,
-                                                     css::drawing::LineCap_BUTT,
-                                                     basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
-                                                     this);
+                bSuccess = mpGraphics->DrawPolyLine(
+                    basegfx::B2DHomMatrix(),
+                    aB2DPolyPolygon.getB2DPolygon(a),
+                    0.0,
+                    aB2DLineWidth,
+                    basegfx::B2DLineJoin::NONE,
+                    css::drawing::LineCap_BUTT,
+                    basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
+                    bPixelSnapHairline,
+                    this);
             }
         }
 
diff --git a/vcl/source/outdev/polyline.cxx b/vcl/source/outdev/polyline.cxx
index bd5ca14b57f5..b5c06b9aa411 100644
--- a/vcl/source/outdev/polyline.cxx
+++ b/vcl/source/outdev/polyline.cxx
@@ -59,31 +59,38 @@ void OutputDevice::DrawPolyLine( const tools::Polygon& rPoly )
         InitLineColor();
 
     // use b2dpolygon drawing if possible
-    if ( DrawPolyLineDirect( rPoly.getB2DPolygon() ) )
+    if(DrawPolyLineDirect(
+        basegfx::B2DHomMatrix(),
+        rPoly.getB2DPolygon()))
     {
-        basegfx::B2DPolygon aB2DPolyLine(rPoly.getB2DPolygon());
-        const basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation();
-        const basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
-
-        // transform the polygon
-        aB2DPolyLine.transform( aTransform );
-
-        if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
-        {
-            aB2DPolyLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine);
-        }
+        return;
+    }
 
-        if(mpGraphics->DrawPolyLine(
-            aB2DPolyLine,
-            0.0,
-            aB2DLineWidth,
-            basegfx::B2DLineJoin::NONE,
-            css::drawing::LineCap_BUTT,
-            basegfx::deg2rad(15.0) /*default fMiterMinimumAngle, not used*/,
-            this))
-        {
-            return;
-        }
+    const basegfx::B2DPolygon aB2DPolyLine(rPoly.getB2DPolygon());
+    const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation());
+    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,
+        0.0,
+        aB2DLineWidth,
+        basegfx::B2DLineJoin::NONE,
+        css::drawing::LineCap_BUTT,
+        basegfx::deg2rad(15.0) /*default fMiterMinimumAngle, not used*/,
+        bPixelSnapHairline,
+        this))
+    {
+        return;
     }
 
     tools::Polygon aPoly = ImplLogicToDevicePixel( rPoly );
@@ -175,8 +182,17 @@ void OutputDevice::DrawPolyLine( const basegfx::B2DPolygon& rB2DPolygon,
         InitLineColor();
 
     // use b2dpolygon drawing if possible
-    if ( DrawPolyLineDirect(rB2DPolygon, fLineWidth, 0.0, eLineJoin, eLineCap, fMiterMinimumAngle) )
+    if(DrawPolyLineDirect(
+        basegfx::B2DHomMatrix(),
+        rB2DPolygon,
+        fLineWidth,
+        0.0,
+        eLineJoin,
+        eLineCap,
+        fMiterMinimumAngle))
+    {
         return;
+    }
 
     // #i101491#
     // no output yet; fallback to geometry decomposition and use filled polygon paint
@@ -222,9 +238,15 @@ void OutputDevice::DrawPolyLine( const basegfx::B2DPolygon& rB2DPolygon,
         // to avoid optical gaps
         for(sal_uInt32 a(0); a < aAreaPolyPolygon.count(); a++)
         {
-            (void)DrawPolyLineDirect(aAreaPolyPolygon.getB2DPolygon(a), 0.0, 0.0,
-                                     basegfx::B2DLineJoin::NONE, css::drawing::LineCap_BUTT,
-                                     basegfx::deg2rad(15.0) /*default, not used*/, bTryAA);
+            (void)DrawPolyLineDirect(
+                basegfx::B2DHomMatrix(),
+                aAreaPolyPolygon.getB2DPolygon(a),
+                0.0,
+                0.0,
+                basegfx::B2DLineJoin::NONE,
+                css::drawing::LineCap_BUTT,
+                basegfx::deg2rad(15.0) /*default, not used*/,
+                bTryAA);
         }
     }
     else
@@ -287,13 +309,15 @@ void OutputDevice::drawPolyLine(const tools::Polygon& rPoly, const LineInfo& rLi
         mpAlphaVDev->DrawPolyLine( rPoly, rLineInfo );
 }
 
-bool OutputDevice::DrawPolyLineDirect( const basegfx::B2DPolygon& rB2DPolygon,
-                                       double fLineWidth,
-                                       double fTransparency,
-                                       basegfx::B2DLineJoin eLineJoin,
-                                       css::drawing::LineCap eLineCap,
-                                       double fMiterMinimumAngle,
-                                       bool bBypassAACheck)
+bool OutputDevice::DrawPolyLineDirect(
+    const basegfx::B2DHomMatrix& rObjectTransform,
+    const basegfx::B2DPolygon& rB2DPolygon,
+    double fLineWidth,
+    double fTransparency,
+    basegfx::B2DLineJoin eLineJoin,
+    css::drawing::LineCap eLineCap,
+    double fMiterMinimumAngle,
+    bool bBypassAACheck)
 {
     assert(!is_double_buffered_window());
 
@@ -322,37 +346,43 @@ bool OutputDevice::DrawPolyLineDirect( const basegfx::B2DPolygon& rB2DPolygon,
 
     if(bTryAA)
     {
-        const basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation();
-        basegfx::B2DVector aB2DLineWidth(1.0, 1.0);
+        // combine rObjectTransform with WorldToDevice
+        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
-        basegfx::B2DPolygon aB2DPolygon(rB2DPolygon);
-        aB2DPolygon.transform(aTransform);
-
-        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);
-        }
+        // 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( aB2DPolygon,
-                                                      fTransparency,
-                                                      aB2DLineWidth,
-                                                      eLineJoin,
-                                                      eLineCap,
-                                                      fMiterMinimumAngle,
-                                                      this );
+        bool bDrawSuccess = mpGraphics->DrawPolyLine(
+            aTransform,
+            rB2DPolygon,
+            fTransparency,
+            aB2DLineWidth,
+            eLineJoin,
+            eLineCap,
+            fMiterMinimumAngle,
+            bPixelSnapHairline,
+            this);
 
         if( bDrawSuccess )
         {
diff --git a/vcl/source/outdev/transparent.cxx b/vcl/source/outdev/transparent.cxx
index 42874c7eba4e..ecd63b642b47 100644
--- a/vcl/source/outdev/transparent.cxx
+++ b/vcl/source/outdev/transparent.cxx
@@ -255,17 +255,22 @@ void OutputDevice::DrawTransparent( const basegfx::B2DPolyPolygon& rB2DPolyPoly,
         if( bDrawnOk && IsLineColor() )
         {
             const basegfx::B2DVector aHairlineWidth(1,1);
-            const sal_uInt32 nPolyCount = aB2DPolyPolygon.count();
+            const sal_uInt32 nPolyCount(aB2DPolyPolygon.count());
+            const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
+
             for( sal_uInt32 nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx )
             {
-                const basegfx::B2DPolygon aOnePoly = aB2DPolyPolygon.getB2DPolygon( nPolyIdx );
+                const basegfx::B2DPolygon aOnePoly(aB2DPolyPolygon.getB2DPolygon(nPolyIdx));
+
                 mpGraphics->DrawPolyLine(
+                    basegfx::B2DHomMatrix(),
                     aOnePoly,
                     fTransparency,
                     aHairlineWidth,
                     basegfx::B2DLineJoin::NONE,
                     css::drawing::LineCap_BUTT,
                     basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
+                    bPixelSnapHairline,
                     this );
             }
         }
@@ -358,17 +363,22 @@ bool OutputDevice::DrawTransparentNatively ( const tools::PolyPolygon& rPolyPoly
             mpGraphics->SetFillColor();
             // draw the border line
             const basegfx::B2DVector aLineWidths( 1, 1 );
-            const sal_uInt32 nPolyCount = aB2DPolyPolygon.count();
+            const sal_uInt32 nPolyCount(aB2DPolyPolygon.count());
+            const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
+
             for( sal_uInt32 nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx )
             {
-                const basegfx::B2DPolygon& rPolygon = aB2DPolyPolygon.getB2DPolygon( nPolyIdx );
+                const basegfx::B2DPolygon aPolygon(aB2DPolyPolygon.getB2DPolygon(nPolyIdx));
+
                 bDrawn = mpGraphics->DrawPolyLine(
-                    rPolygon,
+                    basegfx::B2DHomMatrix(),
+                    aPolygon,
                     fTransparency,
                     aLineWidths,
                     basegfx::B2DLineJoin::NONE,
                     css::drawing::LineCap_BUTT,
                     basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
+                    bPixelSnapHairline,
                     this );
             }
             // prepare to restore the fill color
diff --git a/vcl/unx/generic/gdi/gdiimpl.cxx b/vcl/unx/generic/gdi/gdiimpl.cxx
index 901e9fd3b0ce..c650f9dd4dad 100644
--- a/vcl/unx/generic/gdi/gdiimpl.cxx
+++ b/vcl/unx/generic/gdi/gdiimpl.cxx
@@ -1564,14 +1564,18 @@ bool X11SalGraphicsImpl::drawFilledTrapezoids( const basegfx::B2DTrapezoid* pB2D
 }
 
 bool X11SalGraphicsImpl::drawPolyLine(
+    const basegfx::B2DHomMatrix& rObjectToDevice,
     const basegfx::B2DPolygon& rPolygon,
     double fTransparency,
     const basegfx::B2DVector& rLineWidth,
     basegfx::B2DLineJoin eLineJoin,
     css::drawing::LineCap eLineCap,
-    double fMiterMinimumAngle)
+    double fMiterMinimumAngle,
+    bool bPixelSnapHairline)
 {
-    const bool bIsHairline = (rLineWidth.getX() == rLineWidth.getY()) && (rLineWidth.getX() <= 1.2);
+    // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
+    const basegfx::B2DVector aLineWidth(rObjectToDevice * rLineWidth);
+    const bool bIsHairline((aLineWidth.getX() == aLineWidth.getY()) && (aLineWidth.getX() <= 1.2));
 
     // #i101491#
     if( !bIsHairline && (rPolygon.count() > 1000) )
@@ -1585,18 +1589,23 @@ bool X11SalGraphicsImpl::drawPolyLine(
         return false;
     }
 
+    // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
+    basegfx::B2DPolygon aPolyLine(rPolygon);
+    aPolyLine.transform(rObjectToDevice);
+    if(bPixelSnapHairline) { aPolyLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyLine); }
+
     // temporarily adjust brush color to pen color
     // since the line is drawn as an area-polygon
     const Color aKeepBrushColor = mnBrushColor;
     mnBrushColor = mnPenColor;
 
     // #i11575#desc5#b adjust B2D tessellation result to raster positions
-    basegfx::B2DPolygon aPolygon = rPolygon;
-    const double fHalfWidth = 0.5 * rLineWidth.getX();
+    // basegfx::B2DPolygon aPolygon = rPolygon;
+    const double fHalfWidth = 0.5 * aLineWidth.getX();
 
     // #i122456# This is probably thought to happen to align hairlines to pixel positions, so
     // it should be a 0.5 translation, not more. It will definitely go wrong with fat lines
-    aPolygon.transform( basegfx::utils::createTranslateB2DHomMatrix(0.5, 0.5) );
+    aPolyLine.transform( basegfx::utils::createTranslateB2DHomMatrix(0.5, 0.5) );
 
     // shortcut for hairline drawing to improve performance
     bool bDrawnOk = true;
@@ -1605,7 +1614,7 @@ bool X11SalGraphicsImpl::drawPolyLine(
         // hairlines can benefit from a simplified tessellation
         // e.g. for hairlines the linejoin style can be ignored
         basegfx::B2DTrapezoidVector aB2DTrapVector;
-        basegfx::utils::createLineTrapezoidFromB2DPolygon( aB2DTrapVector, aPolygon, rLineWidth.getX() );
+        basegfx::utils::createLineTrapezoidFromB2DPolygon( aB2DTrapVector, aPolyLine, aLineWidth.getX() );
 
         // draw tessellation result
         const int nTrapCount = aB2DTrapVector.size();
@@ -1618,21 +1627,25 @@ bool X11SalGraphicsImpl::drawPolyLine(
     }
 
     // get the area polygon for the line polygon
-    if( (rLineWidth.getX() != rLineWidth.getY())
-    && !basegfx::fTools::equalZero( rLineWidth.getY() ) )
+    if( (aLineWidth.getX() != aLineWidth.getY()) && !basegfx::fTools::equalZero( aLineWidth.getY() ) )
     {
         // prepare for createAreaGeometry() with anisotropic linewidth
-        aPolygon.transform( basegfx::utils::createScaleB2DHomMatrix(1.0, rLineWidth.getX() / rLineWidth.getY()));
+        aPolyLine.transform( basegfx::utils::createScaleB2DHomMatrix(1.0, aLineWidth.getX() / aLineWidth.getY()));
     }
 
     // create the area-polygon for the line
-    const basegfx::B2DPolyPolygon aAreaPolyPoly( basegfx::utils::createAreaGeometry(aPolygon, fHalfWidth, eLineJoin, eLineCap, fMiterMinimumAngle) );
-
-    if( (rLineWidth.getX() != rLineWidth.getY())
-    && !basegfx::fTools::equalZero( rLineWidth.getX() ) )
+    const basegfx::B2DPolyPolygon aAreaPolyPoly(
+        basegfx::utils::createAreaGeometry(
+            aPolyLine,
+            fHalfWidth,
+            eLineJoin,
+            eLineCap,
+            fMiterMinimumAngle));
+
+    if( (aLineWidth.getX() != aLineWidth.getY()) && !basegfx::fTools::equalZero( aLineWidth.getX() ) )
     {
         // postprocess createAreaGeometry() for anisotropic linewidth
-        aPolygon.transform(basegfx::utils::createScaleB2DHomMatrix(1.0, rLineWidth.getY() / rLineWidth.getX()));
+        aPolyLine.transform(basegfx::utils::createScaleB2DHomMatrix(1.0, aLineWidth.getY() / aLineWidth.getX()));
     }
 
     // draw each area polypolygon component individually
diff --git a/vcl/unx/generic/gdi/gdiimpl.hxx b/vcl/unx/generic/gdi/gdiimpl.hxx
index 156700fcac8a..f738e1e996ea 100644
--- a/vcl/unx/generic/gdi/gdiimpl.hxx
+++ b/vcl/unx/generic/gdi/gdiimpl.hxx
@@ -160,12 +160,14 @@ public:
     virtual bool drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) override;
 
     virtual bool drawPolyLine(
+                const basegfx::B2DHomMatrix& rObjectToDevice,
                 const basegfx::B2DPolygon&,
                 double fTransparency,
                 const basegfx::B2DVector& rLineWidths,
                 basegfx::B2DLineJoin,
                 css::drawing::LineCap,
-                double fMiterMinimumAngle) override;
+                double fMiterMinimumAngle,
+                bool bPixelSnapHairline) override;
 
     virtual bool drawPolyLineBezier(
                 sal_uInt32 nPoints,
diff --git a/vcl/unx/generic/gdi/salgdi.cxx b/vcl/unx/generic/gdi/salgdi.cxx
index 622dc223dcc6..1d6d5bf657ac 100644
--- a/vcl/unx/generic/gdi/salgdi.cxx
+++ b/vcl/unx/generic/gdi/salgdi.cxx
@@ -696,16 +696,16 @@ void X11SalGraphics::clipRegion(cairo_t* cr)
 #endif // ENABLE_CAIRO_CANVAS
 
 bool X11SalGraphics::drawPolyLine(
+    const basegfx::B2DHomMatrix& rObjectToDevice,
     const basegfx::B2DPolygon& rPolygon,
     double fTransparency,
     const basegfx::B2DVector& rLineWidth,
     basegfx::B2DLineJoin eLineJoin,
     css::drawing::LineCap eLineCap,
-    double fMiterMinimumAngle)
+    double fMiterMinimumAngle,
+    bool bPixelSnapHairline)
 {
-    const int nPointCount(rPolygon.count());
-
-    if(nPointCount <= 0)
+    if(0 == rPolygon.count())
     {
         return true;
     }
@@ -723,17 +723,41 @@ bool X11SalGraphics::drawPolyLine(
         cairo_t* cr = getCairoContext();
         clipRegion(cr);
 
-        SvpSalGraphics::drawPolyLine(cr, mnPenColor, getAntiAliasB2DDraw(),
-                                     rPolygon, fTransparency, rLineWidth,
-                                     eLineJoin, eLineCap, fMiterMinimumAngle);
+        // Use the now available static drawPolyLine from the Cairo-Headless-Fallback
+        // that will take care of all needed stuff
+        const bool bRetval(
+            SvpSalGraphics::drawPolyLine(
+                cr,
+                nullptr,
+                mnPenColor,
+                getAntiAliasB2DDraw(),
+                rObjectToDevice,
+                rPolygon,
+                fTransparency,
+                rLineWidth,
+                eLineJoin,
+                eLineCap,
+                fMiterMinimumAngle,
+                bPixelSnapHairline));
 
         releaseCairoContext(cr);
-        return true;
+
+        if(bRetval)
+        {
+            return true;
+        }
     }
 #endif // ENABLE_CAIRO_CANVAS
 
-    return mxImpl->drawPolyLine( rPolygon, fTransparency, rLineWidth,
-            eLineJoin, eLineCap, fMiterMinimumAngle );
+    return mxImpl->drawPolyLine(
+        rObjectToDevice,
+        rPolygon,
+        fTransparency,
+        rLineWidth,
+        eLineJoin,
+        eLineCap,
+        fMiterMinimumAngle,
+        bPixelSnapHairline);
 }
 
 bool X11SalGraphics::drawGradient(const tools::PolyPolygon& rPoly, const Gradient& rGradient)
diff --git a/vcl/unx/generic/print/genpspgraphics.cxx b/vcl/unx/generic/print/genpspgraphics.cxx
index a9c9483890f0..4ee35c8b72eb 100644
--- a/vcl/unx/generic/print/genpspgraphics.cxx
+++ b/vcl/unx/generic/print/genpspgraphics.cxx
@@ -411,12 +411,14 @@ bool GenPspGraphics::drawPolyPolygon( const basegfx::B2DPolyPolygon&, double /*f
 }
 
 bool GenPspGraphics::drawPolyLine(
+    const basegfx::B2DHomMatrix& /* rObjectToDevice */,
     const basegfx::B2DPolygon&,
     double /*fTransparency*/,
     const basegfx::B2DVector& /*rLineWidths*/,
     basegfx::B2DLineJoin /*eJoin*/,
     css::drawing::LineCap /*eLineCap*/,
-    double /*fMiterMinimumAngle*/)
+    double /*fMiterMinimumAngle*/,
+    bool /* bPixelSnapHairline */)
 {
     // TODO: a PS printer can draw B2DPolyLines almost directly
     return false;
diff --git a/vcl/win/gdi/gdiimpl.cxx b/vcl/win/gdi/gdiimpl.cxx
index a66f1f5f9579..bf2dcc9197e0 100644
--- a/vcl/win/gdi/gdiimpl.cxx
+++ b/vcl/win/gdi/gdiimpl.cxx
@@ -35,6 +35,7 @@
 #include <vcl/salbtype.hxx>
 #include <win/salframe.h>
 #include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/utils/systemdependentdata.hxx>
 

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list