basegfx/source/tools/systemdependentdata.cxx            |   59 
 cui/source/dialogs/screenshotannotationdlg.cxx          |    1 
 drawinglayer/source/primitive2d/polygonprimitive2d.cxx  |    2 
 drawinglayer/source/processor2d/vclpixelprocessor2d.cxx |   92 -
 include/basegfx/polygon/b2dlinegeometry.hxx             |    3 
 include/basegfx/polygon/b2dpolygon.hxx                  |   10 
 include/basegfx/polygon/b2dpolypolygon.hxx              |   10 
 include/vcl/outdev.hxx                                  |    1 
 sc/source/ui/view/gridwin.cxx                           |   22 
 vcl/headless/svpgdi.cxx                                 |  278 ++-
 vcl/inc/headless/svpgdi.hxx                             |    2 
 vcl/inc/openglgdiimpl.hxx                               |    1 
 vcl/inc/qt5/Qt5Graphics.hxx                             |    7 
 vcl/inc/quartz/salgdi.h                                 |    1 
 vcl/inc/salgdi.hxx                                      |    2 
 vcl/inc/salgdiimpl.hxx                                  |    1 
 vcl/inc/skia/gdiimpl.hxx                                |  286 +++
 vcl/inc/unx/genpspgraphics.h                            |    1 
 vcl/inc/unx/salgdi.h                                    |    1 
 vcl/inc/win/salbmp.h                                    |   10 
 vcl/inc/win/salgdi.h                                    |    1 
 vcl/opengl/gdiimpl.cxx                                  |   70 
 vcl/qt5/Qt5Graphics_GDI.cxx                             |   50 
 vcl/quartz/salgdicommon.cxx                             |   72 
 vcl/skia/gdiimpl.cxx                                    | 1287 ++++++++++++++++
 vcl/source/gdi/FileDefinitionWidgetDraw.cxx             |    4 
 vcl/source/gdi/salgdilayout.cxx                         |    4 
 vcl/source/outdev/line.cxx                              |    2 
 vcl/source/outdev/polygon.cxx                           |    3 
 vcl/source/outdev/polyline.cxx                          |    5 
 vcl/source/outdev/textline.cxx                          |   13 
 vcl/source/outdev/transparent.cxx                       |    2 
 vcl/unx/generic/gdi/gdiimpl.cxx                         |   90 +
 vcl/unx/generic/gdi/gdiimpl.hxx                         |    1 
 vcl/unx/generic/gdi/salgdi.cxx                          |    3 
 vcl/unx/generic/print/genpspgraphics.cxx                |    1 
 vcl/win/gdi/gdiimpl.cxx                                 |  102 +
 vcl/win/gdi/gdiimpl.hxx                                 |    1 
 vcl/win/gdi/salgdi_gdiplus.cxx                          |    2 
 39 files changed, 2218 insertions(+), 285 deletions(-)

New commits:
commit 19ab4ebce6750792274c9b6e9bbf07b987f6037f
Author:     Dennis Francis <dennis.fran...@collabora.com>
AuthorDate: Sun Apr 26 05:38:01 2020 +0530
Commit:     Michael Meeks <michael.me...@collabora.com>
CommitDate: Sat May 23 17:28:21 2020 +0100

    lokit: trim validation dropdown height to content
    
    The validation dropdown looks ugly for lists with small number of
    items as its height is hardcoded to
    
    SC_FILTERLISTBOX_LINES(=12) * TextHeight
    
    Instead lets use the number of entries in the list to determine
    the height if this count is less than SC_FILTERLISTBOX_LINES
    
    Change-Id: If026140044e6665159cd616c13a2eb57356ae53f
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/92914
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Dennis Francis <dennis.fran...@collabora.com>
    (cherry picked from commit adf10bae8aecd8b765a21660b31056292276bdb2)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/93066
    Tested-by: Jenkins

diff --git a/sc/source/ui/view/gridwin.cxx b/sc/source/ui/view/gridwin.cxx
index a52f07ef43db..996e7c90b787 100644
--- a/sc/source/ui/view/gridwin.cxx
+++ b/sc/source/ui/view/gridwin.cxx
@@ -1129,9 +1129,13 @@ void ScGridWindow::LaunchDataSelectMenu( SCCOL nCol, 
SCROW nRow )
 
         // minimum width in pixel
         const long nMinLOKWinWidth = static_cast<long>(1.3 * STD_COL_WIDTH * 
pViewData->GetPPTX());
-        if (comphelper::LibreOfficeKit::isActive() && nSizeX < nMinLOKWinWidth)
+        bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+        if (bLOKActive && nSizeX < nMinLOKWinWidth)
             nSizeX = nMinLOKWinWidth;
 
+        if (bLOKActive && aStrings.size() < SC_FILTERLISTBOX_LINES)
+            nHeight = nHeight * (aStrings.size() + 1) / SC_FILTERLISTBOX_LINES;
+
         Size aParentSize = GetParent()->GetOutputSizePixel();
         Size aSize( nSizeX, nHeight );
 
commit 7738ef73fbffb806bc7eec77b825391ff066e86e
Author:     Dennis Francis <dennis.fran...@collabora.com>
AuthorDate: Sun Apr 26 04:56:11 2020 +0530
Commit:     Michael Meeks <michael.me...@collabora.com>
CommitDate: Sat May 23 17:27:58 2020 +0100

    lokit: fix validation dropdown's wrong position
    
    Reverse the zoom scale on the position of the dropdown before
    letting the vcl::FloatingWindow convert this to twips without
    any scale information. Without this reverse scaling, the
    dropdown will be incorrect due to double application of the same zoom
    (one in core and one in client).
    
    Change-Id: I73de12593b13e1bc9fb0514bec23c20d440d8923
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/92913
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Dennis Francis <dennis.fran...@collabora.com>
    (cherry picked from commit a68bfe87f6c720dd2b4721edf869c6801fa2d967)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/93008
    Tested-by: Jenkins

diff --git a/sc/source/ui/view/gridwin.cxx b/sc/source/ui/view/gridwin.cxx
index 37eb8853902c..a52f07ef43db 100644
--- a/sc/source/ui/view/gridwin.cxx
+++ b/sc/source/ui/view/gridwin.cxx
@@ -1063,6 +1063,22 @@ void ScGridWindow::LaunchDataSelectMenu( SCCOL nCol, 
SCROW nRow )
     long nHeight = 0;
     pViewData->GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
     Point aPos = pViewData->GetScrPos( nCol, nRow, eWhich );
+
+    if (comphelper::LibreOfficeKit::isActive())
+    {
+        // aPos is now view-zoom adjusted and in pixels an more importantly 
this is pixel aligned to the view-zoom,
+        // but once we use this to set the position of the floating window, it 
has no information of view-zoom level
+        // so if we don't reverse the zoom now, a simple PixelToLogic(aPos, 
MapMode(MapUnit::MapTwip)) employed in
+        // FloatingWindow::ImplCalcPos will produce a 'scaled' twips position 
which will again get zoom scaled in the
+        // client (effective double scaling) causing wrong positioning/size.
+        double fZoomX(pViewData->GetZoomX());
+        double fZoomY(pViewData->GetZoomY());
+        aPos.setX(aPos.getX() / fZoomX);
+        aPos.setY(aPos.getY() / fZoomY);
+        nSizeX = nSizeX / fZoomX;
+        nSizeY = nSizeY / fZoomY;
+    }
+
     if ( bLayoutRTL )
         aPos.AdjustX( -nSizeX );
 
commit 27f4f53c51f14e6afa1baaac40702eb9834189c2
Author:     Armin Le Grand <armin.le.gr...@me.com>
AuthorDate: Sat Feb 8 12:14:09 2020 +0100
Commit:     Michael Meeks <michael.me...@collabora.com>
CommitDate: Sat May 23 17:26:30 2020 +0100

    tdf#130478 add direct dash paint in cairo
    
    Make use of mentioned task's 1st commit now
    and implement direct dashing for cairo. All
    the data is now available (that was the work),
    make use of it.
    Keeping a local static bool to control it, so
    later if needed can be switched on/off e.g
    using some global var/setting.
    Cairo will now do buffering of complex polygon
    data in cairo path data (from some compexity
    on and timer/mem-controlled), but not dismantle
    to own, stroked data.
    With bDoDirectCairoStroke disabled, the dis-
    mantled data will be buffered - more mem, but
    should also help - at least it gets reused
    now that it is possible to keep it at the
    original geometry (incoming polygon) at all
    
    Change-Id: Ic08f659d7ccbedc910ec678b1509ee1a7f3f2112
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/88262
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>

diff --git a/vcl/headless/svpgdi.cxx b/vcl/headless/svpgdi.cxx
index e67a5f19c224..feaaa671e67e 100644
--- a/vcl/headless/svpgdi.cxx
+++ b/vcl/headless/svpgdi.cxx
@@ -1318,7 +1318,16 @@ bool SvpSalGraphics::drawPolyLine(
     const double fDotDashLength(nullptr != pStroke ? 
std::accumulate(pStroke->begin(), pStroke->end(), 0.0) : 0.0);
     const bool bStrokeUsed(0.0 != fDotDashLength);
 
-    if(pSystemDependentData_CairoPath)
+    // MM01 decide if to stroke direcly
+    static bool bDoDirectCairoStroke(true);
+
+    // MM01 activate to stroke direcly
+    if(bDoDirectCairoStroke && bStrokeUsed)
+    {
+        cairo_set_dash(cr, pStroke->data(), pStroke->size(), 0.0);
+    }
+
+    if(!bDoDirectCairoStroke && pSystemDependentData_CairoPath)
     {
         // MM01 - check on stroke change. Used against not used, or if both 
used,
         // equal or different?
@@ -1362,7 +1371,7 @@ bool SvpSalGraphics::drawPolyLine(
         // MM01 need to do line dashing as fallback stuff here now
         basegfx::B2DPolyPolygon aPolyPolygonLine;
 
-        if(bStrokeUsed)
+        if(!bDoDirectCairoStroke && bStrokeUsed)
         {
             // apply LineStyle
             basegfx::utils::applyLineDashing(
@@ -1374,7 +1383,7 @@ bool SvpSalGraphics::drawPolyLine(
         }
         else
         {
-            // no line dashing, just copy
+            // no line dashing or direct stroke, just copy
             aPolyPolygonLine.append(rPolyLine);
         }
 
commit 7bb7c4362fff2bb3ea58a372e5e4cb833fa54603
Author:     Armin Le Grand (Collabora) <armin.le.gr...@me.com>
AuthorDate: Thu Feb 6 18:53:12 2020 +0100
Commit:     Michael Meeks <michael.me...@collabora.com>
CommitDate: Sat May 23 17:26:28 2020 +0100

    tdf#130478 Enhance Dashed line drawing on all systems
    
    For more info and explanation including state of process
    information and discussion(s) see task please.
    
    Adding corrections for gerrit build
    
    Change-Id: Ie10fb8093a86459dee80db5ab4355b47e46c1f8c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/88130
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>

diff --git a/cui/source/dialogs/screenshotannotationdlg.cxx 
b/cui/source/dialogs/screenshotannotationdlg.cxx
index 8dd164f7c9e9..dce0c66fb5b7 100644
--- a/cui/source/dialogs/screenshotannotationdlg.cxx
+++ b/cui/source/dialogs/screenshotannotationdlg.cxx
@@ -407,6 +407,7 @@ void ScreenshotAnnotationDlg_Impl::PaintScreenShotEntry(
             aPolygon,
             fLineWidth,
             fTransparency,
+            nullptr, // MM01
             basegfx::B2DLineJoin::Round))
         {
             // no transparency, draw without
diff --git a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx 
b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
index bcf1fc6bbc3b..10f91b7ff4bb 100644
--- a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
@@ -290,6 +290,7 @@ namespace drawinglayer
             maLineAttribute(rLineAttribute),
             maStrokeAttribute(rStrokeAttribute)
         {
+            // MM01: keep these - these are no curve-decompposers but just 
checks
             // simplify curve segments: moved here to not need to use it
             // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
             maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon);
@@ -303,6 +304,7 @@ namespace drawinglayer
             maLineAttribute(rLineAttribute),
             maStrokeAttribute()
         {
+            // MM01: keep these - these are no curve-decompposers but just 
checks
             // simplify curve segments: moved here to not need to use it
             // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
             maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon);
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx 
b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
index f5ad3de9f417..0cc87e6eaa20 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
@@ -108,7 +108,7 @@ namespace drawinglayer
 
         void 
VclPixelProcessor2D::tryDrawPolyPolygonColorPrimitive2DDirect(const 
drawinglayer::primitive2d::PolyPolygonColorPrimitive2D& rSource, double 
fTransparency)
         {
-            if(!rSource.getB2DPolyPolygon().count())
+            if(!rSource.getB2DPolyPolygon().count() || fTransparency < 0.0 || 
fTransparency >= 1.0)
             {
                 // no geometry, done
                 return;
@@ -128,7 +128,7 @@ namespace drawinglayer
         {
             const basegfx::B2DPolygon& rLocalPolygon(rSource.getB2DPolygon());
 
-            if(!rLocalPolygon.count())
+            if(!rLocalPolygon.count() || fTransparency < 0.0 || fTransparency 
>= 1.0)
             {
                 // no geometry, done
                 return true;
@@ -150,41 +150,28 @@ namespace drawinglayer
 
         bool VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect(const 
drawinglayer::primitive2d::PolygonStrokePrimitive2D& rSource, double 
fTransparency)
         {
-            if(!rSource.getB2DPolygon().count())
+            const basegfx::B2DPolygon& rLocalPolygon(rSource.getB2DPolygon());
+
+            if(!rLocalPolygon.count() || fTransparency < 0.0 || fTransparency 
>= 1.0)
             {
                 // no geometry, done
                 return true;
             }
 
-            // get geometry data, prepare hairline data
-            const basegfx::B2DPolygon& aLocalPolygon(rSource.getB2DPolygon());
-            basegfx::B2DPolyPolygon aHairLinePolyPolygon;
-
-            // simplify curve segments
-            // moved to PolygonStrokePrimitive2D::PolygonStrokePrimitive2D
-            // aLocalPolygon = 
basegfx::utils::simplifyCurveSegments(aLocalPolygon);
-
-            if(rSource.getStrokeAttribute().isDefault() || 0.0 == 
rSource.getStrokeAttribute().getFullDotDashLen())
-            {
-                // no line dashing, just copy
-                aHairLinePolyPolygon.append(aLocalPolygon);
-            }
-            else
+            if (basegfx::B2DLineJoin::NONE == 
rSource.getLineAttribute().getLineJoin()
+                && css::drawing::LineCap_BUTT != 
rSource.getLineAttribute().getLineCap())
             {
-                // apply LineStyle
-                basegfx::utils::applyLineDashing(
-                    aLocalPolygon,
-                    rSource.getStrokeAttribute().getDotDashArray(),
-                    &aHairLinePolyPolygon,
-                    nullptr,
-                    rSource.getStrokeAttribute().getFullDotDashLen());
+                // better use decompose to get that combination done for now, 
see discussion
+                // at 
https://bugs.documentfoundation.org/show_bug.cgi?id=130478#c17 and ff
+                return false;
             }
 
-            if(!aHairLinePolyPolygon.count())
-            {
-                // no geometry, done
-                return true;
-            }
+            // MM01: Radically change here - no dismantle/applyLineDashing,
+            // let that happen low-level at DrawPolyLineDirect implementations
+            // to open up for buffering and evtl. direct draw with sys-dep
+            // graphic systems. Check for stroke is in use
+            const bool 
bStrokeAttributeNotUsed(rSource.getStrokeAttribute().isDefault()
+                || 0.0 == rSource.getStrokeAttribute().getFullDotDashLen());
 
             // check if LineWidth can be simplified in world coordinates
             double fLineWidth(rSource.getLineAttribute().getWidth());
@@ -213,42 +200,17 @@ namespace drawinglayer
             mpOutputDevice->SetFillColor();
             mpOutputDevice->SetLineColor(Color(aLineColor));
 
-            // do not transform self
-            // aHairLinePolyPolygon.transform(maCurrentTransformation);
-
-            bool bHasPoints(false);
-            bool bTryWorked(false);
-
-            for(sal_uInt32 a(0); a < aHairLinePolyPolygon.count(); a++)
-            {
-                const basegfx::B2DPolygon& 
aSingle(aHairLinePolyPolygon.getB2DPolygon(a));
-
-                if(aSingle.count())
-                {
-                    bHasPoints = true;
-
-                    if(mpOutputDevice->DrawPolyLineDirect(
-                        maCurrentTransformation,
-                        aSingle,
-                        fLineWidth,
-                        fTransparency,
-                        rSource.getLineAttribute().getLineJoin(),
-                        rSource.getLineAttribute().getLineCap(),
-                        rSource.getLineAttribute().getMiterMinimumAngle()
-                        /* false bBypassAACheck, default*/))
-                    {
-                        bTryWorked = true;
-                    }
-                }
-            }
-
-            if(!bTryWorked && !bHasPoints)
-            {
-                // no geometry despite try
-                bTryWorked = true;
-            }
-
-            return bTryWorked;
+            // MM01 draw direct, hand over dash data if available
+            return mpOutputDevice->DrawPolyLineDirect(
+                maCurrentTransformation,
+                rLocalPolygon,
+                fLineWidth,
+                fTransparency,
+                bStrokeAttributeNotUsed ? nullptr : 
&rSource.getStrokeAttribute().getDotDashArray(),
+                rSource.getLineAttribute().getLineJoin(),
+                rSource.getLineAttribute().getLineCap(),
+                rSource.getLineAttribute().getMiterMinimumAngle()
+                /* false bBypassAACheck, default*/);
         }
 
         void VclPixelProcessor2D::processBasePrimitive2D(const 
primitive2d::BasePrimitive2D& rCandidate)
diff --git a/include/basegfx/polygon/b2dlinegeometry.hxx 
b/include/basegfx/polygon/b2dlinegeometry.hxx
index d798fff18cd9..6a98d6189394 100644
--- a/include/basegfx/polygon/b2dlinegeometry.hxx
+++ b/include/basegfx/polygon/b2dlinegeometry.hxx
@@ -123,6 +123,9 @@ namespace basegfx
             the usual fallback to bevel is used. Allowed range is cropped
             to [F_PI .. 0.01 * F_PI].
 
+            Commit 51b5b93092d6231615de470c62494c24e54828a1 needs
+            revert, we need the triangulation for X11 fat line drawing
+
             @param pTriangles
             If given, the method will additionally add the created geometry as
             B2DTriangle's
diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx
index cd3c84d13305..d9b6651e0c9e 100644
--- a/include/vcl/outdev.hxx
+++ b/include/vcl/outdev.hxx
@@ -795,6 +795,7 @@ public:
                                     const basegfx::B2DPolygon& rB2DPolygon,
                                     double fLineWidth = 0.0,
                                     double fTransparency = 0.0,
+                                    const std::vector< double >* = nullptr, // 
MM01
                                     basegfx::B2DLineJoin eLineJoin = 
basegfx::B2DLineJoin::NONE,
                                     css::drawing::LineCap eLineCap = 
css::drawing::LineCap_BUTT,
                                     double fMiterMinimumAngle = 
basegfx::deg2rad(15.0),
diff --git a/vcl/headless/svpgdi.cxx b/vcl/headless/svpgdi.cxx
index 5492c1dda21c..e67a5f19c224 100644
--- a/vcl/headless/svpgdi.cxx
+++ b/vcl/headless/svpgdi.cxx
@@ -20,6 +20,8 @@
 #include <config_features.h>
 
 #include <memory>
+#include <numeric>
+
 #ifndef IOS
 #include <headless/svpgdi.hxx>
 #endif
@@ -783,6 +785,7 @@ void SvpSalGraphics::drawPolyLine(sal_uInt32 nPoints, const 
SalPoint* pPtAry)
         aPoly,
         0.0,
         basegfx::B2DVector(1.0, 1.0),
+        nullptr, // MM01
         basegfx::B2DLineJoin::Miter,
         css::drawing::LineCap_BUTT,
         basegfx::deg2rad(15.0) /*default*/,
@@ -1062,6 +1065,7 @@ private:
     // need to be compared with to check for data validity
     bool                mbNoJoin;
     bool                mbAntiAliasB2DDraw;
+    std::vector< double >                       maStroke;
 
 public:
     SystemDependentData_CairoPath(
@@ -1069,12 +1073,15 @@ public:
         size_t nSizeMeasure,
         cairo_t* cr,
         bool bNoJoin,
-        bool bAntiAliasB2DDraw);
+        bool bAntiAliasB2DDraw,
+        const std::vector< double >* pStroke); // MM01
     virtual ~SystemDependentData_CairoPath() override;
 
+    // read access
     cairo_path_t* getCairoPath() { return mpCairoPath; }
     bool getNoJoin() const { return mbNoJoin; }
     bool getAntiAliasB2DDraw() const { return mbAntiAliasB2DDraw; }
+    const std::vector< double >& getStroke() const { return maStroke; }
 
     virtual sal_Int64 estimateUsageInBytes() const override;
 };
@@ -1084,17 +1091,24 @@ 
SystemDependentData_CairoPath::SystemDependentData_CairoPath(
     size_t nSizeMeasure,
     cairo_t* cr,
     bool bNoJoin,
-    bool bAntiAliasB2DDraw)
+    bool bAntiAliasB2DDraw,
+    const std::vector< double >* pStroke)
 :   basegfx::SystemDependentData(rSystemDependentDataManager),
     mpCairoPath(nullptr),
     mbNoJoin(bNoJoin),
-    mbAntiAliasB2DDraw(bAntiAliasB2DDraw)
+    mbAntiAliasB2DDraw(bAntiAliasB2DDraw),
+    maStroke()
 {
     // tdf#129845 only create a copy of the path when nSizeMeasure is
     // bigger than some decent threshold
     if(nSizeMeasure > 50)
     {
         mpCairoPath = cairo_copy_path(cr);
+
+        if(nullptr != pStroke)
+        {
+            maStroke = *pStroke;
+        }
     }
 }
 
@@ -1130,7 +1144,8 @@ bool SvpSalGraphics::drawPolyLine(
     const basegfx::B2DHomMatrix& rObjectToDevice,
     const basegfx::B2DPolygon& rPolyLine,
     double fTransparency,
-    const basegfx::B2DVector& rLineWidths,
+    const basegfx::B2DVector& rLineWidth,
+    const std::vector< double >* pStroke, // MM01
     basegfx::B2DLineJoin eLineJoin,
     css::drawing::LineCap eLineCap,
     double fMiterMinimumAngle,
@@ -1161,7 +1176,8 @@ bool SvpSalGraphics::drawPolyLine(
             rObjectToDevice,
             rPolyLine,
             fTransparency,
-            rLineWidths,
+            rLineWidth,
+            pStroke, // MM01
             eLineJoin,
             eLineCap,
             fMiterMinimumAngle,
@@ -1180,7 +1196,8 @@ bool SvpSalGraphics::drawPolyLine(
     const basegfx::B2DHomMatrix& rObjectToDevice,
     const basegfx::B2DPolygon& rPolyLine,
     double fTransparency,
-    const basegfx::B2DVector& rLineWidths,
+    const basegfx::B2DVector& rLineWidth,
+    const std::vector< double >* pStroke, // MM01
     basegfx::B2DLineJoin eLineJoin,
     css::drawing::LineCap eLineCap,
     double fMiterMinimumAngle,
@@ -1193,10 +1210,10 @@ bool SvpSalGraphics::drawPolyLine(
     }
 
     // need to check/handle LineWidth when ObjectToDevice transformation is 
used
-    basegfx::B2DVector aLineWidths(rLineWidths);
+    basegfx::B2DVector aLineWidth(rLineWidth);
     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);
+    const basegfx::B2DVector aDeviceLineWidth(bObjectToDeviceIsIdentity ? 
rLineWidth : rObjectToDevice * rLineWidth);
+    const bool bCorrectLineWidth(!bObjectToDeviceIsIdentity && 
aDeviceLineWidth.getX() < 1.0 && aLineWidth.getX() >= 1.0);
 
     // on-demand inverse of ObjectToDevice transformation
     basegfx::B2DHomMatrix aObjectToDeviceInv;
@@ -1210,12 +1227,11 @@ bool SvpSalGraphics::drawPolyLine(
         }
 
         // calculate-back logical LineWidth for a hairline
-        aLineWidths = aObjectToDeviceInv * basegfx::B2DVector(1.0, 1.0);
+        aLineWidth = aObjectToDeviceInv * basegfx::B2DVector(1.0, 1.0);
     }
 
     // PixelOffset used: Need to reflect in linear transformation
     cairo_matrix_t aMatrix;
-
     basegfx::B2DHomMatrix 
aDamageMatrix(basegfx::utils::createTranslateB2DHomMatrix(0.5, 0.5));
 
     if (bObjectToDeviceIsIdentity)
@@ -1241,8 +1257,6 @@ bool SvpSalGraphics::drawPolyLine(
     // set linear transformation
     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;
     switch (eLineJoin)
@@ -1293,13 +1307,35 @@ bool SvpSalGraphics::drawPolyLine(
 
     cairo_set_line_join(cr, eCairoLineJoin);
     cairo_set_line_cap(cr, eCairoLineCap);
-    cairo_set_line_width(cr, aLineWidths.getX());
+    cairo_set_line_width(cr, aLineWidth.getX());
     cairo_set_miter_limit(cr, fMiterLimit);
 
     // try to access buffered data
     std::shared_ptr<SystemDependentData_CairoPath> 
pSystemDependentData_CairoPath(
         rPolyLine.getSystemDependentData<SystemDependentData_CairoPath>());
 
+    // MM01 need to do line dashing as fallback stuff here now
+    const double fDotDashLength(nullptr != pStroke ? 
std::accumulate(pStroke->begin(), pStroke->end(), 0.0) : 0.0);
+    const bool bStrokeUsed(0.0 != fDotDashLength);
+
+    if(pSystemDependentData_CairoPath)
+    {
+        // MM01 - check on stroke change. Used against not used, or if both 
used,
+        // equal or different?
+        const bool 
bStrokeWasUsed(!pSystemDependentData_CairoPath->getStroke().empty());
+
+        if(bStrokeWasUsed != bStrokeUsed
+        || (bStrokeUsed && *pStroke != 
pSystemDependentData_CairoPath->getStroke()))
+        {
+            // data invalid, forget
+            pSystemDependentData_CairoPath.reset();
+        }
+    }
+
+    // check for basegfx::B2DLineJoin::NONE to react accordingly
+    const bool bNoJoin((basegfx::B2DLineJoin::NONE == eLineJoin
+        && basegfx::fTools::more(aLineWidth.getX(), 0.0)));
+
     if(pSystemDependentData_CairoPath)
     {
         // check data validity
@@ -1323,42 +1359,67 @@ bool SvpSalGraphics::drawPolyLine(
         // create data
         size_t nSizeMeasure(0);
 
-        if (!bNoJoin)
+        // MM01 need to do line dashing as fallback stuff here now
+        basegfx::B2DPolyPolygon aPolyPolygonLine;
+
+        if(bStrokeUsed)
         {
-            // PixelOffset now reflected in linear transformation used
-            nSizeMeasure += AddPolygonToPath(
-                cr,
-                rPolyLine,
-                rObjectToDevice, // ObjectToDevice *without* LineDraw-Offset
-                !bAntiAliasB2DDraw,
-                bPixelSnapHairline);
+            // apply LineStyle
+            basegfx::utils::applyLineDashing(
+                rPolyLine, // source
+                *pStroke, // pattern
+                &aPolyPolygonLine, // traget for lines
+                nullptr, // target for gaps
+                fDotDashLength); // full length if available
         }
         else
         {
-            const sal_uInt32 nPointCount(rPolyLine.count());
-            const sal_uInt32 nEdgeCount(rPolyLine.isClosed() ? nPointCount : 
nPointCount - 1);
-            basegfx::B2DPolygon aEdge;
+            // no line dashing, just copy
+            aPolyPolygonLine.append(rPolyLine);
+        }
 
-            aEdge.append(rPolyLine.getB2DPoint(0));
-            aEdge.append(basegfx::B2DPoint(0.0, 0.0));
+        // MM01 checked/verified for Cairo
+        for(sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++)
+        {
+            const basegfx::B2DPolygon 
aPolyLine(aPolyPolygonLine.getB2DPolygon(a));
 
-            for (sal_uInt32 i(0); i < nEdgeCount; i++)
+            if (!bNoJoin)
             {
-                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));
-
                 // PixelOffset now reflected in linear transformation used
                 nSizeMeasure += AddPolygonToPath(
                     cr,
-                    aEdge,
+                    aPolyLine,
                     rObjectToDevice, // ObjectToDevice *without* 
LineDraw-Offset
                     !bAntiAliasB2DDraw,
                     bPixelSnapHairline);
+            }
+            else
+            {
+                const sal_uInt32 nPointCount(aPolyLine.count());
+                const sal_uInt32 nEdgeCount(aPolyLine.isClosed() ? nPointCount 
: nPointCount - 1);
+                basegfx::B2DPolygon aEdge;
+
+                aEdge.append(aPolyLine.getB2DPoint(0));
+                aEdge.append(basegfx::B2DPoint(0.0, 0.0));
 
-                // prepare next step
-                aEdge.setB2DPoint(0, aEdge.getB2DPoint(1));
+                for (sal_uInt32 i(0); i < nEdgeCount; i++)
+                {
+                    const sal_uInt32 nNextIndex((i + 1) % nPointCount);
+                    aEdge.setB2DPoint(1, aPolyLine.getB2DPoint(nNextIndex));
+                    aEdge.setNextControlPoint(0, 
aPolyLine.getNextControlPoint(i));
+                    aEdge.setPrevControlPoint(1, 
aPolyLine.getPrevControlPoint(nNextIndex));
+
+                    // PixelOffset now reflected in linear transformation used
+                    nSizeMeasure += AddPolygonToPath(
+                        cr,
+                        aEdge,
+                        rObjectToDevice, // ObjectToDevice *without* 
LineDraw-Offset
+                        !bAntiAliasB2DDraw,
+                        bPixelSnapHairline);
+
+                    // prepare next step
+                    aEdge.setB2DPoint(0, aEdge.getB2DPoint(1));
+                }
             }
         }
 
@@ -1370,7 +1431,8 @@ bool SvpSalGraphics::drawPolyLine(
                 nSizeMeasure,
                 cr,
                 bNoJoin,
-                bAntiAliasB2DDraw);
+                bAntiAliasB2DDraw,
+                pStroke);
         }
     }
 
@@ -1450,7 +1512,8 @@ namespace
                 nSizeMeasure,
                 cr,
                 false,
-                false);
+                false,
+                nullptr);
         }
     }
 }
diff --git a/vcl/inc/headless/svpgdi.hxx b/vcl/inc/headless/svpgdi.hxx
index 083ab54a30a9..ef64d5b241c9 100644
--- a/vcl/inc/headless/svpgdi.hxx
+++ b/vcl/inc/headless/svpgdi.hxx
@@ -116,6 +116,7 @@ public:
         const basegfx::B2DPolygon& rPolyLine,
         double fTransparency,
         const basegfx::B2DVector& rLineWidths,
+        const std::vector< double >* pStroke, // MM01
         basegfx::B2DLineJoin eLineJoin,
         css::drawing::LineCap eLineCap,
         double fMiterMinimumAngle,
@@ -213,6 +214,7 @@ public:
                                 const basegfx::B2DPolygon&,
                                 double fTransparency,
                                 const basegfx::B2DVector& rLineWidths,
+                                const std::vector< double >* pStroke, // MM01
                                 basegfx::B2DLineJoin,
                                 css::drawing::LineCap,
                                 double fMiterMinimumAngle,
diff --git a/vcl/inc/openglgdiimpl.hxx b/vcl/inc/openglgdiimpl.hxx
index b5be8792a982..83053fdd8cc8 100644
--- a/vcl/inc/openglgdiimpl.hxx
+++ b/vcl/inc/openglgdiimpl.hxx
@@ -259,6 +259,7 @@ public:
                 const basegfx::B2DPolygon&,
                 double fTransparency,
                 const basegfx::B2DVector& rLineWidths,
+                const std::vector< double >* pStroke, // MM01
                 basegfx::B2DLineJoin,
                 css::drawing::LineCap,
                 double fMiterMinimumAngle,
diff --git a/vcl/inc/qt5/Qt5Graphics.hxx b/vcl/inc/qt5/Qt5Graphics.hxx
old mode 100644
new mode 100755
index a7c7aaa727ac..29d176093e6a
--- a/vcl/inc/qt5/Qt5Graphics.hxx
+++ b/vcl/inc/qt5/Qt5Graphics.hxx
@@ -119,9 +119,10 @@ public:
                                        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,
-                              bool bPixelSnapHairline) override;
+                              const basegfx::B2DVector& rLineWidths,
+                              const std::vector<double>* pStroke, // MM01
+                              basegfx::B2DLineJoin, 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 338878e79529..433fce10f029 100644
--- a/vcl/inc/quartz/salgdi.h
+++ b/vcl/inc/quartz/salgdi.h
@@ -241,6 +241,7 @@ public:
                                 const basegfx::B2DPolygon&,
                                 double fTransparency,
                                 const basegfx::B2DVector& rLineWidths,
+                                const std::vector< double >* pStroke, // MM01
                                 basegfx::B2DLineJoin,
                                 css::drawing::LineCap eLineCap,
                                 double fMiterMinimumAngle,
diff --git a/vcl/inc/salgdi.hxx b/vcl/inc/salgdi.hxx
index 6b5899a4ce10..0ddd823668e3 100644
--- a/vcl/inc/salgdi.hxx
+++ b/vcl/inc/salgdi.hxx
@@ -247,6 +247,7 @@ public:
                                     const basegfx::B2DPolygon& i_rPolygon,
                                     double i_fTransparency,
                                     const basegfx::B2DVector& i_rLineWidth,
+                                    const std::vector< double >* i_pStroke, // 
MM01
                                     basegfx::B2DLineJoin i_eLineJoin,
                                     css::drawing::LineCap i_eLineCap,
                                     double i_fMiterMinimumAngle,
@@ -464,6 +465,7 @@ protected:
                                     const basegfx::B2DPolygon&,
                                     double fTransparency,
                                     const basegfx::B2DVector& rLineWidths,
+                                    const std::vector< double >* pStroke, // 
MM01
                                     basegfx::B2DLineJoin,
                                     css::drawing::LineCap,
                                     double fMiterMinimumAngle,
diff --git a/vcl/inc/salgdiimpl.hxx b/vcl/inc/salgdiimpl.hxx
index d58750f93ea9..b2eafe9de99f 100644
--- a/vcl/inc/salgdiimpl.hxx
+++ b/vcl/inc/salgdiimpl.hxx
@@ -107,6 +107,7 @@ public:
                 const basegfx::B2DPolygon&,
                 double fTransparency,
                 const basegfx::B2DVector& rLineWidths,
+                const std::vector< double >* pStroke, // MM01
                 basegfx::B2DLineJoin,
                 css::drawing::LineCap,
                 double fMiterMinimumAngle,
diff --git a/vcl/inc/skia/gdiimpl.hxx b/vcl/inc/skia/gdiimpl.hxx
new file mode 100755
index 000000000000..46b3f7bcd334
--- /dev/null
+++ b/vcl/inc/skia/gdiimpl.hxx
@@ -0,0 +1,286 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_SKIA_GDIIMPL_HXX
+#define INCLUDED_VCL_SKIA_GDIIMPL_HXX
+
+#include <vcl/dllapi.h>
+
+#include <salgdiimpl.hxx>
+#include <salgeom.hxx>
+
+#include <SkSurface.h>
+
+#include <prewin.h>
+#include <tools/sk_app/WindowContext.h>
+#include <postwin.h>
+
+class SkiaFlushIdle;
+
+class VCL_DLLPUBLIC SkiaSalGraphicsImpl : public SalGraphicsImpl
+{
+public:
+    SkiaSalGraphicsImpl(SalGraphics& pParent, SalGeometryProvider* pProvider);
+    virtual ~SkiaSalGraphicsImpl() override;
+
+    virtual void Init() override;
+
+    virtual void DeInit() override;
+
+    virtual OUString getRenderBackendName() const override { return "skia"; }
+
+    const vcl::Region& getClipRegion() const;
+    virtual bool setClipRegion(const vcl::Region&) override;
+
+    //
+    // get the depth of the device
+    virtual sal_uInt16 GetBitCount() const override;
+
+    // get the width of the device
+    virtual long GetGraphicsWidth() const override;
+
+    // set the clip region to empty
+    virtual void ResetClipRegion() override;
+
+    // set the line color to transparent (= don't draw lines)
+
+    virtual void SetLineColor() override;
+
+    // set the line color to a specific color
+    virtual void SetLineColor(Color nColor) override;
+
+    // set the fill color to transparent (= don't fill)
+    virtual void SetFillColor() override;
+
+    // set the fill color to a specific color, shapes will be
+    // filled accordingly
+    virtual void SetFillColor(Color nColor) override;
+
+    // enable/disable XOR drawing
+    virtual void SetXORMode(bool bSet, bool bInvertOnly) override;
+
+    // set line color for raster operations
+    virtual void SetROPLineColor(SalROPColor nROPColor) override;
+
+    // set fill color for raster operations
+    virtual void SetROPFillColor(SalROPColor nROPColor) override;
+
+    // draw --> LineColor and FillColor and RasterOp and ClipRegion
+    virtual void drawPixel(long nX, long nY) override;
+    virtual void drawPixel(long nX, long nY, Color nColor) override;
+
+    virtual void drawLine(long nX1, long nY1, long nX2, long nY2) override;
+
+    virtual void drawRect(long nX, long nY, long nWidth, long nHeight) 
override;
+
+    virtual void drawPolyLine(sal_uInt32 nPoints, const SalPoint* pPtAry) 
override;
+
+    virtual void drawPolygon(sal_uInt32 nPoints, const SalPoint* pPtAry) 
override;
+
+    virtual void drawPolyPolygon(sal_uInt32 nPoly, const sal_uInt32* pPoints,
+                                 PCONSTSALPOINT* pPtAry) override;
+
+    virtual bool drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectToDevice,
+                                 const basegfx::B2DPolyPolygon&, double 
fTransparency) override;
+
+    virtual bool drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
+                              const basegfx::B2DPolygon&, double fTransparency,
+                              const basegfx::B2DVector& rLineWidths,
+                              const std::vector<double>* pStroke, // MM01
+                              basegfx::B2DLineJoin, css::drawing::LineCap,
+                              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;
+
+    // CopyArea --> No RasterOp, but ClipRegion
+    virtual void copyArea(long nDestX, long nDestY, long nSrcX, long nSrcY, 
long nSrcWidth,
+                          long nSrcHeight, bool bWindowInvalidate) override;
+
+    virtual void copyBits(const SalTwoRect& rPosAry, SalGraphics* 
pSrcGraphics) override;
+
+    virtual bool blendBitmap(const SalTwoRect&, const SalBitmap& rBitmap) 
override;
+
+    virtual bool blendAlphaBitmap(const SalTwoRect&, const SalBitmap& 
rSrcBitmap,
+                                  const SalBitmap& rMaskBitmap,
+                                  const SalBitmap& rAlphaBitmap) override;
+
+    virtual void drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& 
rSalBitmap) override;
+
+    virtual void drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& 
rSalBitmap,
+                            const SalBitmap& rMaskBitmap) override;
+
+    virtual void drawMask(const SalTwoRect& rPosAry, const SalBitmap& 
rSalBitmap,
+                          Color nMaskColor) override;
+
+    virtual std::shared_ptr<SalBitmap> getBitmap(long nX, long nY, long nWidth,
+                                                 long nHeight) override;
+
+    virtual Color getPixel(long nX, long nY) override;
+
+    // invert --> ClipRegion (only Windows or VirDevs)
+    virtual void invert(long nX, long nY, long nWidth, long nHeight, SalInvert 
nFlags) override;
+
+    virtual void invert(sal_uInt32 nPoints, const SalPoint* pPtAry, SalInvert 
nFlags) override;
+
+    virtual bool drawEPS(long nX, long nY, long nWidth, long nHeight, void* 
pPtr,
+                         sal_uInt32 nSize) override;
+
+    /** Render bitmap with alpha channel
+
+        @param rSourceBitmap
+        Source bitmap to blit
+
+        @param rAlphaBitmap
+        Alpha channel to use for blitting
+
+        @return true, if the operation succeeded, and false
+        otherwise. In this case, clients should try to emulate alpha
+        compositing themselves
+     */
+    virtual bool drawAlphaBitmap(const SalTwoRect&, const SalBitmap& 
rSourceBitmap,
+                                 const SalBitmap& rAlphaBitmap) override;
+
+    /** draw transformed bitmap (maybe with alpha) where Null, X, Y define the 
coordinate system */
+    virtual bool drawTransformedBitmap(const basegfx::B2DPoint& rNull, const 
basegfx::B2DPoint& rX,
+                                       const basegfx::B2DPoint& rY, const 
SalBitmap& rSourceBitmap,
+                                       const SalBitmap* pAlphaBitmap) override;
+
+    /** Render solid rectangle with given transparency
+
+      @param nX             Top left coordinate of rectangle
+
+      @param nY             Bottom right coordinate of rectangle
+
+      @param nWidth         Width of rectangle
+
+      @param nHeight        Height of rectangle
+
+      @param nTransparency  Transparency value (0-255) to use. 0 blits and 
opaque, 255 a
+                            fully transparent rectangle
+
+      @returns true if successfully drawn, false if not able to draw rectangle
+     */
+    virtual bool drawAlphaRect(long nX, long nY, long nWidth, long nHeight,
+                               sal_uInt8 nTransparency) override;
+
+    virtual bool drawGradient(const tools::PolyPolygon& rPolygon,
+                              const Gradient& rGradient) override;
+
+    virtual bool supportsOperation(OutDevSupportType eType) const override;
+
+#ifdef DBG_UTIL
+    void dump(const char* file) const;
+#endif
+
+    // Default blend mode for SkPaint is SkBlendMode::kSrcOver
+    void drawImage(const SalTwoRect& rPosAry, const sk_sp<SkImage>& aImage,
+                   SkBlendMode eBlendMode = SkBlendMode::kSrcOver);
+    void drawBitmap(const SalTwoRect& rPosAry, const SkBitmap& aBitmap,
+                    SkBlendMode eBlendMode = SkBlendMode::kSrcOver);
+
+protected:
+    // To be called before any drawing.
+    void preDraw();
+    // To be called after any drawing.
+    void postDraw();
+    // The canvas to drawn to. Will be diverted to a temporary for Xor mode.
+    SkCanvas* getDrawCanvas() { return mXorMode ? getXorCanvas() : 
mSurface->getCanvas(); }
+
+    virtual void createSurface();
+    // Call to ensure that mSurface is valid. If mSurface is going to be 
modified,
+    // use preDraw() instead of this.
+    void checkSurface();
+    void recreateSurface();
+    void destroySurface();
+    // Reimplemented for X11.
+    virtual bool avoidRecreateByResize() const { return false; }
+    void createWindowSurface();
+    virtual void createWindowContext() = 0;
+    void createOffscreenSurface();
+
+    void privateDrawAlphaRect(long nX, long nY, long nWidth, long nHeight, 
double nTransparency,
+                              bool blockAA = false);
+
+    void setProvider(SalGeometryProvider* provider) { mProvider = provider; }
+
+    bool isOffscreen() const { return mProvider == nullptr || 
mProvider->IsOffScreen(); }
+    bool isGPU() const { return mIsGPU; }
+
+    void invert(basegfx::B2DPolygon const& rPoly, SalInvert eFlags);
+
+    // Called by SkiaFlushIdle.
+    virtual void performFlush() = 0;
+    friend class SkiaFlushIdle;
+
+    // get the width of the device
+    int GetWidth() const { return mProvider ? mProvider->GetWidth() : 1; }
+    // get the height of the device
+    int GetHeight() const { return mProvider ? mProvider->GetHeight() : 1; }
+
+    void drawMask(const SalTwoRect& rPosAry, const sk_sp<SkImage>& rImage, 
Color nMaskColor);
+
+    SkCanvas* getXorCanvas();
+    static void setCanvasClipRegion(SkCanvas* canvas, const vcl::Region& 
region);
+
+    // When drawing using GPU, rounding errors may result in off-by-one errors,
+    // see https://bugs.chromium.org/p/skia/issues/detail?id=9611 . Compensate 
for
+    // it by using centers of pixels (Skia uses float coordinates). In raster 
case
+    // it seems better to not do this though.
+    SkScalar toSkX(long x) const { return mIsGPU ? x + 0.5 : x; }
+    SkScalar toSkY(long y) const { return mIsGPU ? y + 0.5 : y; }
+
+    template <typename charT, typename traits>
+    friend inline std::basic_ostream<charT, traits>&
+    operator<<(std::basic_ostream<charT, traits>& stream, const 
SkiaSalGraphicsImpl* graphics)
+    { // O - offscreen, G - GPU-based, R - raster
+        return stream << static_cast<const void*>(graphics) << " "
+                      << Size(graphics->GetWidth(), graphics->GetHeight())
+                      << (graphics->isOffscreen() ? "O" : "") << 
(graphics->isGPU() ? "G" : "R");
+    }
+
+    SalGraphics& mParent;
+    /// Pointer to the SalFrame or SalVirtualDevice
+    SalGeometryProvider* mProvider;
+    std::unique_ptr<sk_app::WindowContext> mWindowContext;
+    // The Skia surface that is target of all the rendering.
+    sk_sp<SkSurface> mSurface;
+    bool mIsGPU; // whether the surface is GPU-backed
+    // Keep reference to shared GrContext.
+    vcl::Region mClipRegion;
+    Color mLineColor;
+    Color mFillColor;
+    bool mXorMode;
+    SkBitmap mXorBitmap;
+    std::unique_ptr<SkCanvas> mXorCanvas;
+    SkRect mXorExtents; // the area that needs updating for the xor operation 
(or empty for all)
+    std::unique_ptr<SkiaFlushIdle> mFlush;
+    int mPendingPixelsToFlush;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/unx/genpspgraphics.h b/vcl/inc/unx/genpspgraphics.h
index 73084a4f6a61..95d0a7464302 100644
--- a/vcl/inc/unx/genpspgraphics.h
+++ b/vcl/inc/unx/genpspgraphics.h
@@ -134,6 +134,7 @@ public:
                                 const basegfx::B2DPolygon&,
                                 double fTransparency,
                                 const basegfx::B2DVector& rLineWidths,
+                                const std::vector< double >* pStroke, // MM01
                                 basegfx::B2DLineJoin,
                                 css::drawing::LineCap,
                                 double fMiterMinimumAngle,
diff --git a/vcl/inc/unx/salgdi.h b/vcl/inc/unx/salgdi.h
index d2bbeeb128af..c0268f7a3da3 100644
--- a/vcl/inc/unx/salgdi.h
+++ b/vcl/inc/unx/salgdi.h
@@ -165,6 +165,7 @@ public:
                                         const basegfx::B2DPolygon&,
                                         double fTransparency,
                                         const basegfx::B2DVector& rLineWidth,
+                                        const std::vector< double >* pStroke, 
// MM01
                                         basegfx::B2DLineJoin,
                                         css::drawing::LineCap,
                                         double fMiterMinimumAngle,
diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h
index d6d8dea14c24..c193cb91ef79 100644
--- a/vcl/inc/win/salgdi.h
+++ b/vcl/inc/win/salgdi.h
@@ -230,6 +230,7 @@ protected:
         const basegfx::B2DPolygon&,
         double fTransparency,
         const basegfx::B2DVector& rLineWidth,
+        const std::vector< double >* pStroke, // MM01
         basegfx::B2DLineJoin,
         css::drawing::LineCap,
         double fMiterMinimumAngle,
diff --git a/vcl/opengl/gdiimpl.cxx b/vcl/opengl/gdiimpl.cxx
index b7443ffd6d26..a536c9f5651d 100644
--- a/vcl/opengl/gdiimpl.cxx
+++ b/vcl/opengl/gdiimpl.cxx
@@ -27,6 +27,7 @@
 #include <basegfx/matrix/b2dhommatrixtools.hxx>
 #include <basegfx/polygon/b2dlinegeometry.hxx>
 #include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
 #include <basegfx/polygon/b2dpolygontriangulator.hxx>
 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
 #include <basegfx/polygon/b2dtrapezoid.hxx>
@@ -43,6 +44,7 @@
 
 #include <cmath>
 #include <vector>
+#include <numeric>
 
 #include <glm/gtc/type_ptr.hpp>
 #include <glm/gtx/norm.hpp>
@@ -1565,6 +1567,7 @@ void OpenGLSalGraphicsImpl::drawPolyLine( sal_uInt32 
nPoints, const SalPoint* pP
         aPoly,
         0.0,
         basegfx::B2DVector(1.0, 1.0),
+        nullptr, // MM01
         basegfx::B2DLineJoin::Miter,
         css::drawing::LineCap_BUTT,
         basegfx::deg2rad(15.0) /*default*/,
@@ -1642,6 +1645,7 @@ bool OpenGLSalGraphicsImpl::drawPolyLine(
     const basegfx::B2DPolygon& rPolygon,
     double fTransparency,
     const basegfx::B2DVector& rLineWidth,
+    const std::vector< double >* pStroke, // MM01
     basegfx::B2DLineJoin eLineJoin,
     css::drawing::LineCap eLineCap,
     double fMiterMinimumAngle,
@@ -1649,28 +1653,60 @@ bool OpenGLSalGraphicsImpl::drawPolyLine(
 {
     VCL_GL_INFO("::drawPolyLine " << rPolygon.getB2DRange());
 
+    // MM01 check done for simple reasons
+    if(!rPolygon.count() || fTransparency < 0.0 || fTransparency > 1.0)
+    {
+        return true;
+    }
+
+    // MM01 need to do line dashing as fallback stuff here now
+    const double fDotDashLength(nullptr != pStroke ? 
std::accumulate(pStroke->begin(), pStroke->end(), 0.0) : 0.0);
+    const bool bStrokeUsed(0.0 != fDotDashLength);
+    basegfx::B2DPolyPolygon aPolyPolygonLine;
+
+    if(bStrokeUsed)
+    {
+        // apply LineStyle
+        basegfx::utils::applyLineDashing(
+            rPolygon, // source
+            *pStroke, // pattern
+            &aPolyPolygonLine, // traget for lines
+            nullptr, // target for gaps
+            fDotDashLength); // full length if available
+    }
+    else
+    {
+        // no line dashing, just copy
+        aPolyPolygonLine.append(rPolygon);
+    }
+
     // Transform to DeviceCoordinates, get DeviceLineWidth, execute 
PixelSnapHairline
-    basegfx::B2DPolygon aPolyLine(rPolygon);
-    aPolyLine.transform(rObjectToDevice);
-    if(bPixelSnapHairline) { aPolyLine = 
basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyLine); }
+    aPolyPolygonLine.transform(rObjectToDevice);
+    if(bPixelSnapHairline) { aPolyPolygonLine = 
basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygonLine); }
     const basegfx::B2DVector aLineWidth(rObjectToDevice * rLineWidth);
 
-    // addDrawPolyLine() assumes that there are no duplicate points in the
-    // polygon.
-    // basegfx::B2DPolygon aPolygon(rPolygon);
-    aPolyLine.removeDoublePoints();
+    for(sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++)
+    {
+        // addDrawPolyLine() assumes that there are no duplicate points in the 
polygon
+        basegfx::B2DPolygon aPolyLine(aPolyPolygonLine.getB2DPolygon(a));
+        basegfx::utils::simplifyCurveSegments(aPolyLine);
+        aPolyLine.removeDoublePoints();
 
-    mpRenderList->addDrawPolyLine(
-        aPolyLine,
-        fTransparency,
-        aLineWidth,
-        eLineJoin,
-        eLineCap,
-        fMiterMinimumAngle,
-        mnLineColor,
-        mrParent.getAntiAliasB2DDraw());
+        mpRenderList->addDrawPolyLine(
+            aPolyLine,
+            fTransparency,
+            aLineWidth,
+            eLineJoin,
+            eLineCap,
+            fMiterMinimumAngle,
+            mnLineColor,
+            mrParent.getAntiAliasB2DDraw());
+
+        // MM01: not sure - maybe this can be moved out of this loop, but to
+        // keep on the safe side for now, do not relly change something for now
+        PostBatchDraw();
+    }
 
-    PostBatchDraw();
     return true;
 }
 
diff --git a/vcl/qt5/Qt5Graphics_GDI.cxx b/vcl/qt5/Qt5Graphics_GDI.cxx
old mode 100644
new mode 100755
index 2aa70949dc32..102c84900c08
--- a/vcl/qt5/Qt5Graphics_GDI.cxx
+++ b/vcl/qt5/Qt5Graphics_GDI.cxx
@@ -29,7 +29,9 @@
 #include <QtGui/QWindow>
 #include <QtWidgets/QWidget>
 
+#include <numeric>
 #include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
 
 static const basegfx::B2DPoint aHalfPointOfs(0.5, 0.5);
 
@@ -323,37 +325,67 @@ bool Qt5Graphics::drawPolyPolygonBezier(sal_uInt32 
/*nPoly*/, const sal_uInt32*
 
 bool Qt5Graphics::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
                                const basegfx::B2DPolygon& rPolyLine, double 
fTransparency,
-                               const basegfx::B2DVector& rLineWidths,
+                               const basegfx::B2DVector& rLineWidth,
+                               const std::vector<double>* pStroke, // MM01
                                basegfx::B2DLineJoin eLineJoin, 
css::drawing::LineCap eLineCap,
                                double fMiterMinimumAngle, bool 
bPixelSnapHairline)
 {
     if (SALCOLOR_NONE == m_aFillColor && SALCOLOR_NONE == m_aLineColor)
+    {
         return true;
+    }
 
-    // short circuit if there is nothing to do
-    if (0 == rPolyLine.count())
+    // MM01 check done for simple reasons
+    if (!rPolyLine.count() || fTransparency < 0.0 || fTransparency > 1.0)
     {
         return true;
     }
 
+    // MM01 need to do line dashing as fallback stuff here now
+    const double fDotDashLength(
+        nullptr != pStroke ? std::accumulate(pStroke->begin(), pStroke->end(), 
0.0) : 0.0);
+    const bool bStrokeUsed(0.0 != fDotDashLength);
+    basegfx::B2DPolyPolygon aPolyPolygonLine;
+
+    if (bStrokeUsed)
+    {
+        // apply LineStyle
+        basegfx::utils::applyLineDashing(rPolyLine, // source
+                                         *pStroke, // pattern
+                                         &aPolyPolygonLine, // traget for lines
+                                         nullptr, // target for gaps
+                                         fDotDashLength); // full length if 
available
+    }
+    else
+    {
+        // no line dashing, just copy
+        aPolyPolygonLine.append(rPolyLine);
+    }
+
     // Transform to DeviceCoordinates, get DeviceLineWidth, execute 
PixelSnapHairline
-    basegfx::B2DPolygon aPolyLine(rPolyLine);
-    aPolyLine.transform(rObjectToDevice);
+    aPolyPolygonLine.transform(rObjectToDevice);
     if (bPixelSnapHairline)
     {
-        aPolyLine = 
basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyLine);
+        aPolyPolygonLine = 
basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygonLine);
     }
-    const basegfx::B2DVector aLineWidths(rObjectToDevice * rLineWidths);
+    const basegfx::B2DVector aLineWidth(rObjectToDevice * rLineWidth);
 
     // setup poly-polygon path
     QPainterPath aPath;
-    AddPolygonToPath(aPath, aPolyLine, aPolyLine.isClosed(), 
!getAntiAliasB2DDraw(), true);
+
+    // MM01 todo - I assume that this is OKAY to be done in one run for Qt5,
+    // but this NEEDS to be checked/verified
+    for (sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++)
+    {
+        const basegfx::B2DPolygon aPolyLine(aPolyPolygonLine.getB2DPolygon(a));
+        AddPolygonToPath(aPath, aPolyLine, aPolyLine.isClosed(), 
!getAntiAliasB2DDraw(), true);
+    }
 
     Qt5Painter aPainter(*this, false, 255 * (1.0 - fTransparency));
 
     // setup line attributes
     QPen aPen = aPainter.pen();
-    aPen.setWidth(aLineWidths.getX());
+    aPen.setWidth(aLineWidth.getX());
 
     switch (eLineJoin)
     {
diff --git a/vcl/quartz/salgdicommon.cxx b/vcl/quartz/salgdicommon.cxx
index 9317cc400f28..e926cf17c59e 100644
--- a/vcl/quartz/salgdicommon.cxx
+++ b/vcl/quartz/salgdicommon.cxx
@@ -22,9 +22,11 @@
 
 #include <cassert>
 #include <cstring>
+#include <numeric>
 
 #include <basegfx/polygon/b2dpolygon.hxx>
 #include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
 #include <osl/endian.h>
 #include <osl/file.hxx>
 #include <sal/types.h>
@@ -815,15 +817,18 @@ bool AquaSalGraphics::drawPolyLine(
     const basegfx::B2DHomMatrix& rObjectToDevice,
     const basegfx::B2DPolygon& rPolyLine,
     double fTransparency,
-    const basegfx::B2DVector& rLineWidths,
+    const basegfx::B2DVector& rLineWidth,
+    const std::vector< double >* pStroke, // MM01
     basegfx::B2DLineJoin eLineJoin,
     css::drawing::LineCap eLineCap,
     double fMiterMinimumAngle,
     bool bPixelSnapHairline)
 {
-    // short circuit if there is nothing to do
-    if(0 == rPolyLine.count())
+    // MM01 check done for simple reasons
+    if(!rPolyLine.count() || fTransparency < 0.0 || fTransparency > 1.0)
+    {
         return true;
+    }
 
 #ifdef IOS
     if( !CheckContext() )
@@ -831,21 +836,40 @@ bool AquaSalGraphics::drawPolyLine(
 #endif
 
     // need to check/handle LineWidth when ObjectToDevice transformation is 
used
-    const basegfx::B2DVector aDeviceLineWidths(rObjectToDevice * rLineWidths);
-    const bool bCorrectLineWidth(aDeviceLineWidths.getX() < 1.0 && 
rLineWidths.getX() >= 1.0);
-    const basegfx::B2DVector aLineWidths(bCorrectLineWidth ? rLineWidths : 
aDeviceLineWidths);
+    const basegfx::B2DVector aDeviceLineWidth(rObjectToDevice * rLineWidth);
+    const bool bCorrectLineWidth(aDeviceLineWidth.getX() < 1.0 && 
rLineWidth.getX() >= 1.0);
+    const basegfx::B2DVector aLineWidth(bCorrectLineWidth ? rLineWidth : 
aDeviceLineWidth);
 
     // #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) && (aLineWidths.getX() > 
1.3) )
+    if( (basegfx::B2DLineJoin::NONE == eLineJoin) && (aLineWidth.getX() > 1.3) 
)
         return false;
 
+    // MM01 need to do line dashing as fallback stuff here now
+    const double fDotDashLength(nullptr != pStroke ? 
std::accumulate(pStroke->begin(), pStroke->end(), 0.0) : 0.0);
+    const bool bStrokeUsed(0.0 != fDotDashLength);
+    basegfx::B2DPolyPolygon aPolyPolygonLine;
+
+    if(bStrokeUsed)
+    {
+        // apply LineStyle
+        basegfx::utils::applyLineDashing(
+            rPolyLine, // source
+            *pStroke, // pattern
+            &aPolyPolygonLine, // traget for lines
+            nullptr, // target for gaps
+            fDotDashLength); // full length if available
+    }
+    else
+    {
+        // no line dashing, just copy
+        aPolyPolygonLine.append(rPolyLine);
+    }
+
     // Transform to DeviceCoordinates, get DeviceLineWidth, execute 
PixelSnapHairline
-    basegfx::B2DPolygon aPolyLine(rPolyLine);
-    aPolyLine.transform(rObjectToDevice);
-    if(bPixelSnapHairline)
-        aPolyLine = 
basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyLine);
+    aPolyPolygonLine.transform(rObjectToDevice);
+    if(bPixelSnapHairline) { aPolyPolygonLine = 
basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygonLine); }
 
     // setup line attributes
     CGLineJoin aCGLineJoin = kCGLineJoinMiter;
@@ -882,12 +906,19 @@ bool AquaSalGraphics::drawPolyLine(
 
     // setup poly-polygon path
     CGMutablePathRef xPath = CGPathCreateMutable();
-    AddPolygonToPath(
-        xPath,
-        aPolyLine,
-        aPolyLine.isClosed(),
-        !getAntiAliasB2DDraw(),
-        true);
+
+    // MM01 todo - I assume that this is OKAY to be done in one run for quartz
+    // but this NEEDS to be checked/verified
+    for(sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++)
+    {
+        const basegfx::B2DPolygon aPolyLine(aPolyPolygonLine.getB2DPolygon(a));
+        AddPolygonToPath(
+            xPath,
+            aPolyLine,
+            aPolyLine.isClosed(),
+            !getAntiAliasB2DDraw(),
+            true);
+    }
 
     const CGRect aRefreshRect = CGPathGetBoundingBox( xPath );
     // #i97317# workaround for Quartz having problems with drawing small 
polygons
@@ -902,12 +933,7 @@ bool AquaSalGraphics::drawPolyLine(
         CGContextSetAlpha( maContextHolder.get(), 1.0 - fTransparency );
         CGContextSetLineJoin( maContextHolder.get(), aCGLineJoin );
         CGContextSetLineCap( maContextHolder.get(), aCGLineCap );
-
-        // aLineWidths.getX() can be negative here. That causes a warning that 
shows up in the debugger.
-        if (aLineWidths.getX() > 0)
-        {
-            CGContextSetLineWidth( maContextHolder.get(), aLineWidths.getX() );
-        }
+        CGContextSetLineWidth( maContextHolder.get(), aLineWidth.getX() );
         CGContextSetMiterLimit(maContextHolder.get(), fCGMiterLimit);
         CGContextDrawPath( maContextHolder.get(), kCGPathStroke );
         maContextHolder.restoreState();
diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx
new file mode 100755
index 000000000000..a4ca9f0aeb55
--- /dev/null
+++ b/vcl/skia/gdiimpl.cxx
@@ -0,0 +1,1287 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <skia/gdiimpl.hxx>
+
+#include <salgdi.hxx>
+#include <skia/salbmp.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/lazydelete.hxx>
+#include <vcl/skia/SkiaHelper.hxx>
+#include <skia/utils.hxx>
+#include <skia/zone.hxx>
+
+#include <SkCanvas.h>
+#include <SkPath.h>
+#include <SkRegion.h>
+#include <SkDashPathEffect.h>
+#include <GrBackendSurface.h>
+
+#include <numeric>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+
+namespace
+{
+// Create Skia Path from B2DPolygon
+// Note that polygons generally have the complication that when used
+// for area (fill) operations they usually miss the right-most and
+// bottom-most line of pixels of the bounding rectangle (see
+// 
https://lists.freedesktop.org/archives/libreoffice/2019-November/083709.html).
+// So be careful with rectangle->polygon conversions (generally avoid them).
+void addPolygonToPath(const basegfx::B2DPolygon& rPolygon, SkPath& rPath)
+{
+    const sal_uInt32 nPointCount(rPolygon.count());
+
+    if (nPointCount <= 1)
+        return;
+
+    const bool bClosePath(rPolygon.isClosed());
+    const bool bHasCurves(rPolygon.areControlPointsUsed());
+
+    bool bFirst = true;
+
+    sal_uInt32 nCurrentIndex = 0;
+    sal_uInt32 nPreviousIndex = nPointCount - 1;
+
+    basegfx::B2DPoint aCurrentPoint;
+    basegfx::B2DPoint aPreviousPoint;
+
+    for (sal_uInt32 nIndex = 0; nIndex <= nPointCount; nIndex++)
+    {
+        if (nIndex == nPointCount && !bClosePath)
+            continue;
+
+        // Make sure we loop the last point to first point
+        nCurrentIndex = nIndex % nPointCount;
+        aCurrentPoint = rPolygon.getB2DPoint(nCurrentIndex);
+
+        if (bFirst)
+        {
+            rPath.moveTo(aCurrentPoint.getX(), aCurrentPoint.getY());
+            bFirst = false;
+        }
+        else if (!bHasCurves)
+        {
+            rPath.lineTo(aCurrentPoint.getX(), aCurrentPoint.getY());
+        }
+        else
+        {
+            basegfx::B2DPoint aPreviousControlPoint = 
rPolygon.getNextControlPoint(nPreviousIndex);
+            basegfx::B2DPoint aCurrentControlPoint = 
rPolygon.getPrevControlPoint(nCurrentIndex);
+
+            if (aPreviousControlPoint.equal(aPreviousPoint))
+            {
+                aPreviousControlPoint
+                    = aPreviousPoint + ((aPreviousControlPoint - 
aCurrentPoint) * 0.0005);
+            }
+
+            if (aCurrentControlPoint.equal(aCurrentPoint))
+            {
+                aCurrentControlPoint
+                    = aCurrentPoint + ((aCurrentControlPoint - aPreviousPoint) 
* 0.0005);
+            }
+            rPath.cubicTo(aPreviousControlPoint.getX(), 
aPreviousControlPoint.getY(),
+                          aCurrentControlPoint.getX(), 
aCurrentControlPoint.getY(),
+                          aCurrentPoint.getX(), aCurrentPoint.getY());
+        }
+        aPreviousPoint = aCurrentPoint;
+        nPreviousIndex = nCurrentIndex;
+    }
+    if (bClosePath)
+    {
+        rPath.close();
+    }
+}
+
+void addPolyPolygonToPath(const basegfx::B2DPolyPolygon& rPolyPolygon, SkPath& 
rPath)
+{
+    const sal_uInt32 nPolygonCount(rPolyPolygon.count());
+
+    if (nPolygonCount == 0)
+        return;
+
+    for (const auto& rPolygon : rPolyPolygon)
+    {
+        addPolygonToPath(rPolygon, rPath);
+    }
+}
+
+SkColor toSkColor(Color color)
+{
+    return SkColorSetARGB(255 - color.GetTransparency(), color.GetRed(), 
color.GetGreen(),
+                          color.GetBlue());
+}
+
+SkColor toSkColorWithTransparency(Color aColor, double fTransparency)
+{
+    return SkColorSetA(toSkColor(aColor), 255 * (1.0 - fTransparency));
+}
+
+Color fromSkColor(SkColor color)
+{
+    return Color(255 - SkColorGetA(color), SkColorGetR(color), 
SkColorGetG(color),
+                 SkColorGetB(color));
+}
+
+// returns true if the source or destination rectangles are invalid
+bool checkInvalidSourceOrDestination(SalTwoRect const& rPosAry)
+{
+    return rPosAry.mnSrcWidth <= 0 || rPosAry.mnSrcHeight <= 0 || 
rPosAry.mnDestWidth <= 0
+           || rPosAry.mnDestHeight <= 0;
+}
+
+} // end anonymous namespace
+
+// Class that triggers flushing the backing buffer when idle.
+class SkiaFlushIdle : public Idle
+{
+    SkiaSalGraphicsImpl* mpGraphics;
+
+public:
+    explicit SkiaFlushIdle(SkiaSalGraphicsImpl* pGraphics)
+        : Idle("skia idle swap")
+        , mpGraphics(pGraphics)
+    {
+        // We don't want to be swapping before we've painted.
+        SetPriority(TaskPriority::POST_PAINT);
+    }
+
+    virtual void Invoke() override
+    {
+        mpGraphics->performFlush();
+        Stop();
+        SetPriority(TaskPriority::HIGHEST);
+    }
+};
+
+SkiaSalGraphicsImpl::SkiaSalGraphicsImpl(SalGraphics& rParent, 
SalGeometryProvider* pProvider)
+    : mParent(rParent)
+    , mProvider(pProvider)
+    , mIsGPU(false)
+    , mLineColor(SALCOLOR_NONE)
+    , mFillColor(SALCOLOR_NONE)
+    , mXorMode(false)
+    , mFlush(new SkiaFlushIdle(this))
+    , mPendingPixelsToFlush(0)
+{
+}
+
+SkiaSalGraphicsImpl::~SkiaSalGraphicsImpl()
+{
+    assert(!mSurface);
+    assert(!mWindowContext);
+}
+
+void SkiaSalGraphicsImpl::Init() {}
+
+void SkiaSalGraphicsImpl::recreateSurface()
+{
+    destroySurface();
+    createSurface();
+}
+
+void SkiaSalGraphicsImpl::createSurface()
+{
+    SkiaZone zone;
+    if (isOffscreen())
+        createOffscreenSurface();
+    else
+        createWindowSurface();
+    mSurface->getCanvas()->save(); // see SetClipRegion()
+    mClipRegion = vcl::Region(tools::Rectangle(0, 0, GetWidth(), GetHeight()));
+
+    // We don't want to be swapping before we've painted.
+    mFlush->Stop();
+    mFlush->SetPriority(TaskPriority::POST_PAINT);
+}
+
+void SkiaSalGraphicsImpl::createWindowSurface()
+{
+    SkiaZone zone;
+    assert(!isOffscreen());
+    assert(!mSurface);
+    assert(!mWindowContext);
+    createWindowContext();
+    if (mWindowContext)
+        mSurface = mWindowContext->getBackbufferSurface();
+    if (!mSurface)
+    {
+        switch (SkiaHelper::renderMethodToUse())
+        {
+            case SkiaHelper::RenderVulkan:
+                SAL_WARN("vcl.skia", "cannot create Vulkan GPU window surface, 
disabling Vulkan");
+                // fall back to raster
+                SkiaHelper::disableRenderMethod(SkiaHelper::RenderVulkan);
+                destroySurface(); // destroys also WindowContext
+                return createWindowSurface(); // try again
+            case SkiaHelper::RenderRaster:
+                abort(); // this should not really happen
+        }
+    }
+    assert((mSurface->getCanvas()->getGrContext() != nullptr) == mIsGPU);
+#ifdef DBG_UTIL
+    SkiaHelper::prefillSurface(mSurface);
+#endif
+}
+
+void SkiaSalGraphicsImpl::createOffscreenSurface()
+{
+    SkiaZone zone;
+    assert(isOffscreen());
+    assert(!mSurface);
+    assert(!mWindowContext);
+    switch (SkiaHelper::renderMethodToUse())
+    {
+        case SkiaHelper::RenderVulkan:
+        {
+            GrContext* grContext = SkiaHelper::getSharedGrContext();
+            // We may not get a GrContext if called before any onscreen window 
is created.
+            if (!grContext)
+            {
+                SAL_INFO("vcl.skia",
+                         "creating Vulkan offscreen GPU surface before any 
window exists");
+                // Create temporary WindowContext with no window. That will 
fail,
+                // but it will initialize the shared GrContext.
+                createWindowContext();
+                // This will use the temporarily created context.
+                grContext = SkiaHelper::getSharedGrContext();
+                // Destroy the temporary WindowContext.
+                destroySurface();
+            }
+            if (grContext)
+            {
+                mSurface = SkiaHelper::createSkSurface(GetWidth(), 
GetHeight());
+                assert(mSurface);
+                assert(mSurface->getCanvas()->getGrContext()); // is GPU-backed
+                mIsGPU = true;
+                return;
+            }
+            SAL_WARN("vcl.skia", "cannot create Vulkan offscreen GPU surface, 
disabling Vulkan");
+            SkiaHelper::disableRenderMethod(SkiaHelper::RenderVulkan);
+            break;
+        }
+        default:
+            break;
+    }
+    // Create raster surface as a fallback.
+    mSurface = SkiaHelper::createSkSurface(GetWidth(), GetHeight());
+    assert(mSurface);
+    assert(!mSurface->getCanvas()->getGrContext()); // is not GPU-backed
+    mIsGPU = false;
+}
+
+void SkiaSalGraphicsImpl::destroySurface()
+{
+    SkiaZone zone;
+    if (mSurface)
+    {
+        // check setClipRegion() invariant
+        assert(mSurface->getCanvas()->getSaveCount() == 2);
+        // if this fails, something forgot to use SkAutoCanvasRestore
+        assert(mSurface->getCanvas()->getTotalMatrix().isIdentity());
+    }
+    // If we use e.g. Vulkan, we must destroy the surface before the context,
+    // otherwise destroying the surface will reference the context. This is
+    // handled by calling destroySurface() before destroying the context.
+    // However we also need to flush the surface before destroying it,
+    // otherwise when destroying the context later there still could be queued
+    // commands referring to the surface data. This is probably a Skia bug,
+    // but work around it here.
+    if (mSurface)
+        mSurface->flush();
+    mSurface.reset();
+    mWindowContext.reset();
+    mIsGPU = false;
+}
+
+void SkiaSalGraphicsImpl::DeInit() { destroySurface(); }
+
+void SkiaSalGraphicsImpl::preDraw()
+{
+    SkiaZone::enter(); // matched in postDraw()
+    checkSurface();
+    assert(!mXorMode || mXorExtents.isEmpty()); // must be reset in postDraw()
+}
+
+void SkiaSalGraphicsImpl::postDraw()
+{
+    if (mXorMode)
+    {
+        // Apply the result from the temporary bitmap manually. This is indeed
+        // slow, but it doesn't seem to be needed often and can be optimized
+        // in each operation by setting mXorExtents to the area that should be
+        // updated.
+        if (mXorExtents.isEmpty())
+            mXorExtents = SkRect::MakeXYWH(0, 0, mSurface->width(), 
mSurface->height());
+        else
+        {
+            // Make slightly larger, just in case (rounding, antialiasing,...).
+            mXorExtents.outset(2, 2);
+            mXorExtents.intersect(SkRect::MakeXYWH(0, 0, mSurface->width(), 
mSurface->height()));
+        }
+        SAL_INFO("vcl.skia", "applyxor("
+                                 << this << "): "
+                                 << tools::Rectangle(mXorExtents.left(), 
mXorExtents.top(),
+                                                     mXorExtents.right(), 
mXorExtents.bottom()));
+        // Copy the surface contents to another pixmap.
+        SkBitmap surfaceBitmap;
+        // Use unpremultiplied alpha format, so that we do not have to do the 
conversions to get
+        // the RGB and back (Skia will do it when converting, but it'll be 
presumably faster at it).
+        if (!surfaceBitmap.tryAllocPixels(
+                mSurface->imageInfo().makeAlphaType(kUnpremul_SkAlphaType)))
+            abort();
+        SkPaint paint;
+        paint.setBlendMode(SkBlendMode::kSrc); // copy as is
+        SkCanvas canvas(surfaceBitmap);
+        canvas.drawImageRect(mSurface->makeImageSnapshot(), mXorExtents, 
mXorExtents, &paint);
+        // xor to surfaceBitmap
+        assert(surfaceBitmap.info().alphaType() == kUnpremul_SkAlphaType);
+        assert(mXorBitmap.info().alphaType() == kUnpremul_SkAlphaType);
+        assert(surfaceBitmap.bytesPerPixel() == 4);
+        assert(mXorBitmap.bytesPerPixel() == 4);
+        for (int y = mXorExtents.top(); y < mXorExtents.bottom(); ++y)
+        {
+            uint8_t* data = 
static_cast<uint8_t*>(surfaceBitmap.getAddr(mXorExtents.x(), y));
+            const uint8_t* xordata = 
static_cast<uint8_t*>(mXorBitmap.getAddr(mXorExtents.x(), y));
+            for (int x = 0; x < mXorExtents.width(); ++x)
+            {
+                *data++ ^= *xordata++;
+                *data++ ^= *xordata++;
+                *data++ ^= *xordata++;
+                // alpha is not xor-ed
+                data++;
+                xordata++;
+            }
+        }
+        surfaceBitmap.notifyPixelsChanged();
+        mSurface->getCanvas()->drawBitmapRect(surfaceBitmap, mXorExtents, 
mXorExtents, &paint);
+        mXorCanvas.reset();
+        mXorBitmap.reset();
+        mXorExtents.setEmpty();
+    }
+    if (!isOffscreen())
+    {
+        if (!Application::IsInExecute())
+            performFlush(); // otherwise nothing would trigger idle rendering
+        else if (!mFlush->IsActive())
+            mFlush->Start();
+    }
+    // Skia (at least when using Vulkan) queues drawing commands and executes 
them only later.
+    // But some operations may queue way too much data to draw, leading to 
Vulkan getting out of memory,
+    // which at least on Linux leads to driver problems affecting even the 
whole X11 session.
+    // One such problematic operation may be drawBitmap(SkBitmap), which is 
used by SkiaX11CairoTextRender
+    // to draw text, which is internally done by creating the SkBitmap from 
cairo surface data. Apparently
+    // the cairo surface's size matches the size of the destination (window), 
which may be large,
+    // and each text drawing allocates a new surface (and thus SkBitmap). So 
we may end up queueing up
+    // millions of pixels of bitmap data. So force a flush if such a possibly 
problematic operation
+    // has queued up too much data.
+    if (mPendingPixelsToFlush > 10 * 1024 * 1024)
+    {
+        mSurface->flush();
+        mPendingPixelsToFlush = 0;
+    }
+    SkiaZone::leave(); // matched in preDraw()
+}
+
+// VCL can sometimes resize us without telling us, update the surface if 
needed.
+// Also create the surface on demand if it has not been created yet (it is a 
waste
+// to create it in Init() if it gets recreated later anyway).
+void SkiaSalGraphicsImpl::checkSurface()
+{
+    if (!mSurface)
+    {
+        recreateSurface();
+        SAL_INFO("vcl.skia",
+                 "create(" << this << "): " << Size(mSurface->width(), 
mSurface->height()));
+    }
+    else if (GetWidth() != mSurface->width() || GetHeight() != 
mSurface->height())
+    {
+        if (!avoidRecreateByResize())
+        {
+            Size oldSize(mSurface->width(), mSurface->height());
+            recreateSurface();
+            SAL_INFO("vcl.skia", "recreate(" << this << "): old " << oldSize 
<< " new "
+                                             << Size(mSurface->width(), 
mSurface->height())
+                                             << " requested " << 
Size(GetWidth(), GetHeight()));
+        }
+    }
+}
+
+bool SkiaSalGraphicsImpl::setClipRegion(const vcl::Region& region)
+{
+    if (mClipRegion == region)
+        return true;
+    SkiaZone zone;
+    mClipRegion = region;
+    checkSurface();
+    SAL_INFO("vcl.skia", "setclipregion(" << this << "): " << region);
+    SkCanvas* canvas = mSurface->getCanvas();
+    // SkCanvas::clipRegion() can only further reduce the clip region,
+    // but we need to set the given region, which may extend it.
+    // So handle that by always having the full clip region saved on the stack
+    // and always go back to that. SkCanvas::restore() only affects the clip
+    // and the matrix.
+    assert(canvas->getSaveCount() == 2); // = there is just one save()
+    canvas->restore();
+    canvas->save();
+    setCanvasClipRegion(canvas, region);
+    return true;
+}
+
+void SkiaSalGraphicsImpl::setCanvasClipRegion(SkCanvas* canvas, const 
vcl::Region& region)
+{
+    SkiaZone zone;
+    SkPath path;
+    // Handle polygons last, since rectangle->polygon area conversions
+    // are problematic (see addPolygonToPath() comment).
+    if (region.getRegionBand())
+    {
+        RectangleVector rectangles;
+        region.GetRegionRectangles(rectangles);
+        for (const tools::Rectangle& rectangle : rectangles)
+            path.addRect(SkRect::MakeXYWH(rectangle.getX(), rectangle.getY(), 
rectangle.GetWidth(),
+                                          rectangle.GetHeight()));
+    }
+    else if (!region.IsEmpty())
+    {
+        addPolyPolygonToPath(region.GetAsB2DPolyPolygon(), path);
+    }
+    path.setFillType(SkPathFillType::kEvenOdd);
+    canvas->clipPath(path);
+}
+
+void SkiaSalGraphicsImpl::ResetClipRegion()
+{
+    setClipRegion(vcl::Region(tools::Rectangle(0, 0, GetWidth(), 
GetHeight())));
+}
+
+const vcl::Region& SkiaSalGraphicsImpl::getClipRegion() const { return 
mClipRegion; }
+
+sal_uInt16 SkiaSalGraphicsImpl::GetBitCount() const { return 32; }
+
+long SkiaSalGraphicsImpl::GetGraphicsWidth() const { return GetWidth(); }
+
+void SkiaSalGraphicsImpl::SetLineColor() { mLineColor = SALCOLOR_NONE; }
+
+void SkiaSalGraphicsImpl::SetLineColor(Color nColor) { mLineColor = nColor; }
+
+void SkiaSalGraphicsImpl::SetFillColor() { mFillColor = SALCOLOR_NONE; }
+
+void SkiaSalGraphicsImpl::SetFillColor(Color nColor) { mFillColor = nColor; }
+
+void SkiaSalGraphicsImpl::SetXORMode(bool set, bool)
+{
+    mXorMode = set;
+    if (mXorMode)
+        mXorExtents.setEmpty();
+}
+
+SkCanvas* SkiaSalGraphicsImpl::getXorCanvas()
+{
+    SkiaZone zone;
+    assert(mXorMode);
+    // Skia does not implement xor drawing, so we need to handle it manually 
by redirecting
+    // to a temporary SkBitmap and then doing the xor operation on the data 
ourselves.
+    // There's no point in using SkSurface for GPU, we'd immediately need to 
get the pixels back.
+    if (!mXorCanvas)
+    {
+        // Use unpremultiplied alpha (see xor applying in PostDraw()).
+        if 
(!mXorBitmap.tryAllocPixels(mSurface->imageInfo().makeAlphaType(kUnpremul_SkAlphaType)))
+            abort();
+        mXorBitmap.eraseARGB(0, 0, 0, 0);
+        mXorCanvas = std::make_unique<SkCanvas>(mXorBitmap);
+        setCanvasClipRegion(mXorCanvas.get(), mClipRegion);
+    }
+    return mXorCanvas.get();
+}
+
+void SkiaSalGraphicsImpl::SetROPLineColor(SalROPColor nROPColor)
+{
+    switch (nROPColor)
+    {
+        case SalROPColor::N0:
+            mLineColor = Color(0, 0, 0);
+            break;
+        case SalROPColor::N1:
+            mLineColor = Color(0xff, 0xff, 0xff);
+            break;
+        case SalROPColor::Invert:
+            mLineColor = Color(0xff, 0xff, 0xff);
+            break;
+    }
+}
+
+void SkiaSalGraphicsImpl::SetROPFillColor(SalROPColor nROPColor)
+{
+    switch (nROPColor)
+    {
+        case SalROPColor::N0:
+            mFillColor = Color(0, 0, 0);
+            break;
+        case SalROPColor::N1:
+            mFillColor = Color(0xff, 0xff, 0xff);
+            break;
+        case SalROPColor::Invert:
+            mFillColor = Color(0xff, 0xff, 0xff);
+            break;
+    }
+}
+
+void SkiaSalGraphicsImpl::drawPixel(long nX, long nY) { drawPixel(nX, nY, 
mLineColor); }
+
+void SkiaSalGraphicsImpl::drawPixel(long nX, long nY, Color nColor)
+{
+    if (nColor == SALCOLOR_NONE)
+        return;
+    preDraw();
+    SAL_INFO("vcl.skia", "drawpixel(" << this << "): " << Point(nX, nY) << ":" 
<< nColor);
+    SkPaint paint;
+    paint.setColor(toSkColor(nColor));
+    // Apparently drawPixel() is actually expected to set the pixel and not 
draw it.
+    paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
+    getDrawCanvas()->drawPoint(toSkX(nX), toSkY(nY), paint);
+    if (mXorMode) // limit xor area update
+        mXorExtents = SkRect::MakeXYWH(nX, nY, 1, 1);
+    postDraw();
+}
+
+void SkiaSalGraphicsImpl::drawLine(long nX1, long nY1, long nX2, long nY2)
+{
+    if (mLineColor == SALCOLOR_NONE)
+        return;
+    preDraw();
+    SAL_INFO("vcl.skia", "drawline(" << this << "): " << Point(nX1, nY1) << 
"->" << Point(nX2, nY2)
+                                     << ":" << mLineColor);
+    SkPaint paint;
+    paint.setColor(toSkColor(mLineColor));
+    paint.setAntiAlias(mParent.getAntiAliasB2DDraw());
+    // Raster has better results if shifted by 0.25 (unlike the 0.5 done by 
toSkX/toSkY).
+    if (!isGPU())
+        getDrawCanvas()->drawLine(nX1 + 0.25, nY1 + 0.25, nX2 + 0.25, nY2 + 
0.25, paint);
+    else
+        getDrawCanvas()->drawLine(toSkX(nX1), toSkY(nY1), toSkX(nX2), 
toSkY(nY2), paint);
+    if (mXorMode) // limit xor area update
+        mXorExtents = SkRect::MakeLTRB(nX1, nY1, nX2 + 1, nY2 + 1);
+    postDraw();
+}
+
+void SkiaSalGraphicsImpl::privateDrawAlphaRect(long nX, long nY, long nWidth, 
long nHeight,
+                                               double fTransparency, bool 
blockAA)
+{
+    preDraw();
+    SAL_INFO("vcl.skia", "privatedrawrect(" << this << "): " << Point(nX, nY) 
<< "/"
+                                            << Size(nWidth, nHeight) << ":" << 
mLineColor << ":"
+                                            << mFillColor << ":" << 
fTransparency);
+    SkCanvas* canvas = getDrawCanvas();
+    SkPaint paint;
+    paint.setAntiAlias(!blockAA && mParent.getAntiAliasB2DDraw());
+    if (mFillColor != SALCOLOR_NONE)
+    {
+        paint.setColor(toSkColorWithTransparency(mFillColor, fTransparency));
+        paint.setStyle(SkPaint::kFill_Style);
+        canvas->drawIRect(SkIRect::MakeXYWH(nX, nY, nWidth, nHeight), paint);
+    }
+    if (mLineColor != SALCOLOR_NONE)
+    {
+        paint.setColor(toSkColorWithTransparency(mLineColor, fTransparency));
+        paint.setStyle(SkPaint::kStroke_Style);
+        canvas->drawIRect(SkIRect::MakeXYWH(nX, nY, nWidth - 1, nHeight - 1), 
paint);
+    }
+    if (mXorMode) // limit xor area update
+        mXorExtents = SkRect::MakeXYWH(nX, nY, nWidth, nHeight);
+    postDraw();
+}
+
+void SkiaSalGraphicsImpl::drawRect(long nX, long nY, long nWidth, long nHeight)
+{
+    privateDrawAlphaRect(nX, nY, nWidth, nHeight, 0.0, true);
+}
+
+void SkiaSalGraphicsImpl::drawPolyLine(sal_uInt32 nPoints, const SalPoint* 
pPtAry)
+{
+    basegfx::B2DPolygon aPolygon;
+    aPolygon.append(basegfx::B2DPoint(pPtAry->mnX, pPtAry->mnY), nPoints);
+    for (sal_uInt32 i = 1; i < nPoints; ++i)
+        aPolygon.setB2DPoint(i, basegfx::B2DPoint(pPtAry[i].mnX, 
pPtAry[i].mnY));
+    aPolygon.setClosed(false);
+
+    drawPolyLine(basegfx::B2DHomMatrix(), aPolygon, 0.0, 
basegfx::B2DVector(1.0, 1.0),
+                 nullptr, // MM01
+                 basegfx::B2DLineJoin::Miter, css::drawing::LineCap_BUTT,
+                 basegfx::deg2rad(15.0) /*default*/, false);
+}
+
+void SkiaSalGraphicsImpl::drawPolygon(sal_uInt32 nPoints, const SalPoint* 
pPtAry)
+{
+    basegfx::B2DPolygon aPolygon;
+    aPolygon.append(basegfx::B2DPoint(pPtAry->mnX, pPtAry->mnY), nPoints);
+    for (sal_uInt32 i = 1; i < nPoints; ++i)
+        aPolygon.setB2DPoint(i, basegfx::B2DPoint(pPtAry[i].mnX, 
pPtAry[i].mnY));
+
+    drawPolyPolygon(basegfx::B2DHomMatrix(), 
basegfx::B2DPolyPolygon(aPolygon), 0.0);
+}
+
+void SkiaSalGraphicsImpl::drawPolyPolygon(sal_uInt32 nPoly, const sal_uInt32* 
pPoints,
+                                          PCONSTSALPOINT* pPtAry)
+{
+    basegfx::B2DPolyPolygon aPolyPolygon;
+    for (sal_uInt32 nPolygon = 0; nPolygon < nPoly; ++nPolygon)
+    {
+        sal_uInt32 nPoints = pPoints[nPolygon];
+        if (nPoints)
+        {
+            PCONSTSALPOINT pSalPoints = pPtAry[nPolygon];
+            basegfx::B2DPolygon aPolygon;
+            aPolygon.append(basegfx::B2DPoint(pSalPoints->mnX, 
pSalPoints->mnY), nPoints);
+            for (sal_uInt32 i = 1; i < nPoints; ++i)
+                aPolygon.setB2DPoint(i, basegfx::B2DPoint(pSalPoints[i].mnX, 
pSalPoints[i].mnY));
+
+            aPolyPolygon.append(aPolygon);
+        }
+    }
+
+    drawPolyPolygon(basegfx::B2DHomMatrix(), aPolyPolygon, 0.0);
+}
+
+bool SkiaSalGraphicsImpl::drawPolyPolygon(const basegfx::B2DHomMatrix& 
rObjectToDevice,
+                                          const basegfx::B2DPolyPolygon& 
rPolyPolygon,
+                                          double fTransparency)
+{
+    const bool bHasFill(mFillColor != SALCOLOR_NONE);
+    const bool bHasLine(mLineColor != SALCOLOR_NONE);
+
+    if (rPolyPolygon.count() == 0 || !(bHasFill || bHasLine) || fTransparency 
< 0.0
+        || fTransparency >= 1.0)
+        return true;
+
+    preDraw();
+
+    SkPath aPath;
+    basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon);
+    aPolyPolygon.transform(rObjectToDevice);
+    SAL_INFO("vcl.skia", "drawpolypolygon(" << this << "): " << aPolyPolygon 
<< ":" << mLineColor
+                                            << ":" << mFillColor);
+    addPolyPolygonToPath(aPolyPolygon, aPath);
+    aPath.setFillType(SkPathFillType::kEvenOdd);
+
+    SkPaint aPaint;
+    aPaint.setAntiAlias(mParent.getAntiAliasB2DDraw());
+    if (mFillColor != SALCOLOR_NONE)
+    {
+        aPaint.setColor(toSkColorWithTransparency(mFillColor, fTransparency));
+        aPaint.setStyle(SkPaint::kFill_Style);
+        getDrawCanvas()->drawPath(aPath, aPaint);
+    }
+    if (mLineColor != SALCOLOR_NONE)
+    {
+        // Raster has better results if shifted by 0.25 (unlike the 0.5 done 
by toSkX/toSkY).
+        if (!isGPU())
+            aPath.offset(0.25, 0.25, nullptr);
+        else // Apply the same adjustment as toSkX()/toSkY() do.
+            aPath.offset(0.5, 0.5, nullptr);
+        aPaint.setColor(toSkColorWithTransparency(mLineColor, fTransparency));
+        aPaint.setStyle(SkPaint::kStroke_Style);
+        getDrawCanvas()->drawPath(aPath, aPaint);
+    }
+    if (mXorMode) // limit xor area update
+        mXorExtents = aPath.getBounds();
+    postDraw();
+    return true;
+}
+
+bool SkiaSalGraphicsImpl::drawPolyLine(const basegfx::B2DHomMatrix& 
rObjectToDevice,
+                                       const basegfx::B2DPolygon& rPolyLine, 
double fTransparency,
+                                       const basegfx::B2DVector& rLineWidth,
+                                       const std::vector<double>* pStroke, // 
MM01
+                                       basegfx::B2DLineJoin eLineJoin,
+                                       css::drawing::LineCap eLineCap, double 
fMiterMinimumAngle,
+                                       bool bPixelSnapHairline)
+{
+    // MM01 check done for simple reasons
+    if (!rPolyLine.count() || fTransparency < 0.0 || fTransparency > 1.0
+        || mLineColor == SALCOLOR_NONE)
+    {
+        return true;
+    }
+
+    preDraw();
+    SAL_INFO("vcl.skia", "drawpolyline(" << this << "): " << rPolyLine << ":" 
<< mLineColor);
+
+    // need to check/handle LineWidth when ObjectToDevice transformation is 
used
+    const basegfx::B2DVector aDeviceLineWidth(rObjectToDevice * rLineWidth);
+    const bool bCorrectLineWidth(aDeviceLineWidth.getX() < 1.0 && 
rLineWidth.getX() >= 1.0);
+    const basegfx::B2DVector aLineWidth(bCorrectLineWidth ? rLineWidth : 
aDeviceLineWidth);
+
+    // Skia does not support B2DLineJoin::NONE; return false to use
+    // the fallback (own geometry preparation),
+    // linejoin-mode and thus the above only applies to "fat" lines.
+    if ((basegfx::B2DLineJoin::NONE == eLineJoin) && (aLineWidth.getX() > 1.3))
+        return false;
+
+    // MM01 need to do line dashing as fallback stuff here now
+    const double fDotDashLength(
+        nullptr != pStroke ? std::accumulate(pStroke->begin(), pStroke->end(), 
0.0) : 0.0);
+    const bool bStrokeUsed(0.0 != fDotDashLength);
+    basegfx::B2DPolyPolygon aPolyPolygonLine;
+
+    if (bStrokeUsed)
+    {
+        // apply LineStyle
+        basegfx::utils::applyLineDashing(rPolyLine, // source
+                                         *pStroke, // pattern
+                                         &aPolyPolygonLine, // traget for lines
+                                         nullptr, // target for gaps
+                                         fDotDashLength); // full length if 
available
+    }
+    else
+    {
+        // no line dashing, just copy
+        aPolyPolygonLine.append(rPolyLine);
+    }
+
+    // Transform to DeviceCoordinates, get DeviceLineWidth, execute 
PixelSnapHairline
+    aPolyPolygonLine.transform(rObjectToDevice);
+    if (bPixelSnapHairline)
+    {
+        aPolyPolygonLine = 
basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygonLine);
+    }
+
+    // Setup Line Join
+    SkPaint::Join eSkLineJoin = SkPaint::kMiter_Join;
+    switch (eLineJoin)
+    {
+        case basegfx::B2DLineJoin::Bevel:
+            eSkLineJoin = SkPaint::kBevel_Join;
+            break;
+        case basegfx::B2DLineJoin::Round:
+            eSkLineJoin = SkPaint::kRound_Join;
+            break;
+        case basegfx::B2DLineJoin::NONE:
+        case basegfx::B2DLineJoin::Miter:
+            eSkLineJoin = SkPaint::kMiter_Join;
+            break;
+    }
+
+    // convert miter minimum angle to miter limit
+    double fMiterLimit = 1.0 / std::sin(fMiterMinimumAngle / 2.0);
+
+    // Setup Line Cap
+    SkPaint::Cap eSkLineCap(SkPaint::kButt_Cap);
+
+    switch (eLineCap)
+    {
+        case css::drawing::LineCap_ROUND:
+            eSkLineCap = SkPaint::kRound_Cap;
+            break;
+        case css::drawing::LineCap_SQUARE:
+            eSkLineCap = SkPaint::kSquare_Cap;
+            break;
+        default: // css::drawing::LineCap_BUTT:
+            eSkLineCap = SkPaint::kButt_Cap;
+            break;
+    }
+
+    SkPaint aPaint;
+    aPaint.setStyle(SkPaint::kStroke_Style);
+    aPaint.setStrokeCap(eSkLineCap);
+    aPaint.setStrokeJoin(eSkLineJoin);
+    aPaint.setColor(toSkColorWithTransparency(mLineColor, fTransparency));
+    aPaint.setStrokeMiter(fMiterLimit);
+    aPaint.setStrokeWidth(aLineWidth.getX());
+    aPaint.setAntiAlias(mParent.getAntiAliasB2DDraw());
+
+    SkPath aPath;
+
+    // MM01 checked/verified for Skia (on Win)
+    for (sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++)
+    {
+        const basegfx::B2DPolygon aPolyLine(aPolyPolygonLine.getB2DPolygon(a));
+        addPolygonToPath(aPolyLine, aPath);
+    }
+
+    aPath.setFillType(SkPathFillType::kEvenOdd);
+    // Apply the same adjustment as toSkX()/toSkY() do. Do it here even in the 
non-GPU
+    // case as it seems to produce better results.
+    aPath.offset(0.5, 0.5, nullptr);
+    getDrawCanvas()->drawPath(aPath, aPaint);
+    if (mXorMode) // limit xor area update
+        mXorExtents = aPath.getBounds();
+    postDraw();
+
+    return true;
+}
+
+bool SkiaSalGraphicsImpl::drawPolyLineBezier(sal_uInt32, const SalPoint*, 
const PolyFlags*)
+{
+    // TODO?
+    return false;
+}
+
+bool SkiaSalGraphicsImpl::drawPolygonBezier(sal_uInt32, const SalPoint*, const 
PolyFlags*)
+{
+    // TODO?
+    return false;
+}
+
+bool SkiaSalGraphicsImpl::drawPolyPolygonBezier(sal_uInt32, const sal_uInt32*,
+                                                const SalPoint* const*, const 
PolyFlags* const*)
+{
+    // TODO?
+    return false;
+}
+
+void SkiaSalGraphicsImpl::copyArea(long nDestX, long nDestY, long nSrcX, long 
nSrcY, long nSrcWidth,
+                                   long nSrcHeight, bool /*bWindowInvalidate*/)
+{
+    if (nDestX == nSrcX && nDestY == nSrcY)
+        return;
+    preDraw();
+    SAL_INFO("vcl.skia", "copyarea(" << this << "): " << Point(nSrcX, nSrcY) 
<< "->"
+                                     << Point(nDestX, nDestY) << "/"
+                                     << Size(nSrcWidth, nSrcHeight));
+    // Do not use makeImageSnapshot(rect), as that one may make a needless 
data copy.
+    sk_sp<SkImage> image = mSurface->makeImageSnapshot();
+    SkPaint paint;
+    paint.setBlendMode(SkBlendMode::kSrc); // copy as is, including alpha
+    getDrawCanvas()->drawImageRect(image, SkIRect::MakeXYWH(nSrcX, nSrcY, 
nSrcWidth, nSrcHeight),
+                                   SkRect::MakeXYWH(nDestX, nDestY, nSrcWidth, 
nSrcHeight), &paint);
+    if (mXorMode) // limit xor area update
+        mXorExtents = SkRect::MakeXYWH(nDestX, nDestY, nSrcWidth, nSrcHeight);
+    postDraw();
+}
+
+void SkiaSalGraphicsImpl::copyBits(const SalTwoRect& rPosAry, SalGraphics* 
pSrcGraphics)
+{
+    preDraw();
+    SkiaSalGraphicsImpl* src;
+    if (pSrcGraphics)
+    {
+        assert(dynamic_cast<SkiaSalGraphicsImpl*>(pSrcGraphics->GetImpl()));
+        src = static_cast<SkiaSalGraphicsImpl*>(pSrcGraphics->GetImpl());
+        src->checkSurface();
+    }
+    else
+        src = this;
+    SAL_INFO("vcl.skia", "copybits(" << this << "): (" << src << "):" << 
rPosAry);
+    // Do not use makeImageSnapshot(rect), as that one may make a needless 
data copy.
+    sk_sp<SkImage> image = src->mSurface->makeImageSnapshot();
+    SkPaint paint;
+    paint.setBlendMode(SkBlendMode::kSrc); // copy as is, including alpha
+    getDrawCanvas()->drawImageRect(
+        image,
+        SkIRect::MakeXYWH(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, 
rPosAry.mnSrcHeight),
+        SkRect::MakeXYWH(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth,
+                         rPosAry.mnDestHeight),
+        &paint);
+    if (mXorMode) // limit xor area update
+        mXorExtents = SkRect::MakeXYWH(rPosAry.mnDestX, rPosAry.mnDestY, 
rPosAry.mnDestWidth,
+                                       rPosAry.mnDestHeight);
+    postDraw();
+}
+
+bool SkiaSalGraphicsImpl::blendBitmap(const SalTwoRect& rPosAry, const 
SalBitmap& rBitmap)
+{
+    if (checkInvalidSourceOrDestination(rPosAry))
+        return false;
+
+    assert(dynamic_cast<const SkiaSalBitmap*>(&rBitmap));
+    const SkiaSalBitmap& rSkiaBitmap = static_cast<const 
SkiaSalBitmap&>(rBitmap);
+    // This is used by VirtualDevice in the alpha mode for the "alpha" layer 
which
+    // is actually one-minus-alpha (opacity). Therefore white=0xff=transparent,
+    // black=0x00=opaque. So the result is transparent only if both the inputs
+    // are transparent. Since for blending operations white=1.0 and black=0.0,
+    // kMultiply should handle exactly that 
(transparent*transparent=transparent,
+    // opaque*transparent=opaque). And guessing from the "floor" in TYPE_BLEND 
in opengl's
+    // combinedTextureFragmentShader.glsl, the layer is not even alpha values 
but
+    // simply yes-or-no mask.
+    // See also blendAlphaBitmap().
+    drawImage(rPosAry, rSkiaBitmap.GetSkImage(), SkBlendMode::kMultiply);
+    return true;
+}
+
+bool SkiaSalGraphicsImpl::blendAlphaBitmap(const SalTwoRect& rPosAry,
+                                           const SalBitmap& rSourceBitmap,
+                                           const SalBitmap& rMaskBitmap,
+                                           const SalBitmap& rAlphaBitmap)
+{
+    if (checkInvalidSourceOrDestination(rPosAry))
+        return false;
+
+    assert(dynamic_cast<const SkiaSalBitmap*>(&rSourceBitmap));
+    assert(dynamic_cast<const SkiaSalBitmap*>(&rMaskBitmap));
+    assert(dynamic_cast<const SkiaSalBitmap*>(&rAlphaBitmap));
+
+    sk_sp<SkSurface> tmpSurface = 
SkiaHelper::createSkSurface(rSourceBitmap.GetSize());
+    if (!tmpSurface)
+        return false;
+
+    const SkiaSalBitmap& rSkiaSourceBitmap = static_cast<const 
SkiaSalBitmap&>(rSourceBitmap);
+    const SkiaSalBitmap& rSkiaMaskBitmap = static_cast<const 
SkiaSalBitmap&>(rMaskBitmap);
+    const SkiaSalBitmap& rSkiaAlphaBitmap = static_cast<const 
SkiaSalBitmap&>(rAlphaBitmap);
+
+    // This was originally implemented for the OpenGL drawing method and it is 
poorly documented.
+    // The source and mask bitmaps are the usual data and alpha bitmaps, and 
'alpha'
+    // is the "alpha" layer of the VirtualDevice (the alpha in VirtualDevice 
is also stored
+    // as a separate bitmap). Now I understand it correctly these two alpha 
masks first need
+    // to be combined into the actual alpha mask to be used. The formula for 
TYPE_BLEND
+    // in opengl's combinedTextureFragmentShader.glsl is
+    // "result_alpha = 1.0 - (1.0 - floor(alpha)) * mask".
+    // See also blendBitmap().
+    SkCanvas* aCanvas = tmpSurface->getCanvas();
+    aCanvas->clear(SK_ColorTRANSPARENT);
+    SkPaint aPaint;
+    // First copy the mask as is.
+    aPaint.setBlendMode(SkBlendMode::kSrc);
+    aCanvas->drawImage(rSkiaMaskBitmap.GetAlphaSkImage(), 0, 0, &aPaint);
+    // Do the "1 - alpha" (no idea how to do "floor", but hopefully not needed 
in practice).
+    aPaint.setBlendMode(SkBlendMode::kDstOut);
+    aCanvas->drawImage(rSkiaAlphaBitmap.GetAlphaSkImage(), 0, 0, &aPaint);
+    // And now draw the bitmap with "1 - x", where x is the "( 1 - alpha ) * 
mask".
+    aPaint.setBlendMode(SkBlendMode::kSrcOut);
+    aCanvas->drawImage(rSkiaSourceBitmap.GetSkImage(), 0, 0, &aPaint);
+
+    drawImage(rPosAry, tmpSurface->makeImageSnapshot());
+    return true;
+}
+
+void SkiaSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const 
SalBitmap& rSalBitmap)
+{
+    if (checkInvalidSourceOrDestination(rPosAry))
+        return;
+
+    assert(dynamic_cast<const SkiaSalBitmap*>(&rSalBitmap));
+    const SkiaSalBitmap& rSkiaSourceBitmap = static_cast<const 
SkiaSalBitmap&>(rSalBitmap);
+
+    drawImage(rPosAry, rSkiaSourceBitmap.GetSkImage());
+}
+
+void SkiaSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const 
SalBitmap& rSalBitmap,
+                                     const SalBitmap& rMaskBitmap)
+{
+    drawAlphaBitmap(rPosAry, rSalBitmap, rMaskBitmap);
+}
+
+void SkiaSalGraphicsImpl::drawMask(const SalTwoRect& rPosAry, const SalBitmap& 
rSalBitmap,
+                                   Color nMaskColor)
+{
+    assert(dynamic_cast<const SkiaSalBitmap*>(&rSalBitmap));
+    drawMask(rPosAry, static_cast<const 
SkiaSalBitmap&>(rSalBitmap).GetAlphaSkImage(), nMaskColor);
+}
+
+void SkiaSalGraphicsImpl::drawMask(const SalTwoRect& rPosAry, const 
sk_sp<SkImage>& rImage,
+                                   Color nMaskColor)
+{
+    SAL_INFO("vcl.skia", "drawmask(" << this << "): " << rPosAry << ":" << 
nMaskColor);
+    sk_sp<SkSurface> tmpSurface = SkiaHelper::createSkSurface(rImage->width(), 
rImage->height());
+    assert(tmpSurface);
+    SkCanvas* canvas = tmpSurface->getCanvas();
+    canvas->clear(toSkColor(nMaskColor));
+    SkPaint paint;
+    // Draw the color with the given mask.
+    // TODO figure out the right blend mode to avoid the temporary surface
+    paint.setBlendMode(SkBlendMode::kDstOut);
+    canvas->drawImage(rImage, 0, 0, &paint);
+
+    drawImage(rPosAry, tmpSurface->makeImageSnapshot());
+}
+
+std::shared_ptr<SalBitmap> SkiaSalGraphicsImpl::getBitmap(long nX, long nY, 
long nWidth,
+                                                          long nHeight)
+{
+    SkiaZone zone;
+    checkSurface();
+    SAL_INFO("vcl.skia",
+             "getbitmap(" << this << "): " << Point(nX, nY) << "/" << 
Size(nWidth, nHeight));
+    mSurface->getCanvas()->flush();
+    // TODO makeImageSnapshot(rect) may copy the data, which may be a waste if 
this is used
+    // e.g. for VirtualDevice's lame alpha blending, in which case the image 
will eventually end up
+    // in blendAlphaBitmap(), where we could simply use the proper rect of the 
image.

... etc. - the rest is truncated
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to