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.Grand@me.com>
diff --git a/basegfx/source/polygon/b2dlinegeometry.cxx b/basegfx/source/polygon/b2dlinegeometry.cxx
index 0f602eb6..72c25f0 100644
--- a/basegfx/source/polygon/b2dlinegeometry.cxx
+++ b/basegfx/source/polygon/b2dlinegeometry.cxx
@@ -848,7 +848,8 @@ namespace basegfx
            css::drawing::LineCap eCap,
            double fMaxAllowedAngle,
            double fMaxPartOfEdge,
            double fMiterMinimumAngle)
            double fMiterMinimumAngle,
            basegfx::triangulator::B2DTriangleVector* pTriangles)
        {
            if(fMaxAllowedAngle > F_PI2)
            {
@@ -958,7 +959,7 @@ namespace basegfx
                                        fHalfLineWidth,
                                        eJoin,
                                        fMiterMinimumAngle,
                                        nullptr));
                                        pTriangles));
                            }
                            else if(aOrientation == B2VectorOrientation::Negative)
                            {
@@ -975,7 +976,7 @@ namespace basegfx
                                        fHalfLineWidth,
                                        eJoin,
                                        fMiterMinimumAngle,
                                        nullptr));
                                        pTriangles));
                            }
                        }

@@ -994,7 +995,7 @@ namespace basegfx
                                    bLast && eCap == css::drawing::LineCap_ROUND,
                                    bFirst && eCap == css::drawing::LineCap_SQUARE,
                                    bLast && eCap == css::drawing::LineCap_SQUARE,
                                    nullptr));
                                    pTriangles));
                        }
                        else
                        {
@@ -1006,7 +1007,7 @@ namespace basegfx
                                    false,
                                    false,
                                    false,
                                    nullptr));
                                    pTriangles));
                        }

                        // prepare next step
@@ -1029,7 +1030,17 @@ namespace basegfx
                            aCandidate.getB2DPoint(0),
                            fHalfLineWidth));

                    aRetval.append(aCircle);
                    if(nullptr != pTriangles)
                    {
                        const basegfx::triangulator::B2DTriangleVector aResult(
                            basegfx::triangulator::triangulate(
                                aCircle));
                        pTriangles->insert(pTriangles->end(), aResult.begin(), aResult.end());
                    }
                    else
                    {
                        aRetval.append(aCircle);
                    }
                }

                return aRetval;
diff --git a/cui/source/dialogs/screenshotannotationdlg.cxx b/cui/source/dialogs/screenshotannotationdlg.cxx
index dca914c..c9a93d2 100644
--- a/cui/source/dialogs/screenshotannotationdlg.cxx
+++ b/cui/source/dialogs/screenshotannotationdlg.cxx
@@ -406,6 +406,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 2410f21..cdcc843 100644
--- a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
@@ -278,6 +278,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);
@@ -291,6 +292,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 e9642dd..2fa5a7d 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
@@ -96,7 +96,7 @@ namespace drawinglayer::processor2d

        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;
@@ -116,7 +116,7 @@ namespace drawinglayer::processor2d
        {
            const basegfx::B2DPolygon& rLocalPolygon(rSource.getB2DPolygon());

            if(!rLocalPolygon.count())
            if(!rLocalPolygon.count() || fTransparency < 0.0 || fTransparency >= 1.0)
            {
                // no geometry, done
                return true;
@@ -138,41 +138,28 @@ namespace drawinglayer::processor2d

        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())
            if (basegfx::B2DLineJoin::NONE == rSource.getLineAttribute().getLineJoin()
                && css::drawing::LineCap_BUTT != rSource.getLineAttribute().getLineCap())
            {
                // no line dashing, just copy
                aHairLinePolyPolygon.append(aLocalPolygon);
            }
            else
            {
                // 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());
@@ -201,42 +188,17 @@ namespace drawinglayer::processor2d
            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 c6973c6..6a98d61 100644
--- a/include/basegfx/polygon/b2dlinegeometry.hxx
+++ b/include/basegfx/polygon/b2dlinegeometry.hxx
@@ -123,6 +123,13 @@ 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

            @return
            The tools::PolyPolygon containing the geometry of the extended line by
            it's line width. Contains bezier segments and edge roundings as
@@ -135,7 +142,8 @@ namespace basegfx
            css::drawing::LineCap eCap,
            double fMaxAllowedAngle = basegfx::deg2rad(12.5),
            double fMaxPartOfEdge = 0.4,
            double fMiterMinimumAngle = basegfx::deg2rad(15.0));
            double fMiterMinimumAngle = basegfx::deg2rad(15.0),
            basegfx::triangulator::B2DTriangleVector* pTriangles = nullptr);

    } // end of namespace utils
} // end of namespace basegfx
diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx
index c991867..687b9a1 100644
--- a/include/vcl/outdev.hxx
+++ b/include/vcl/outdev.hxx
@@ -799,6 +799,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 0d4a366..80a24fa 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*/,
@@ -1064,6 +1067,7 @@ private:
    // need to be compared with to check for data validity
    bool                mbNoJoin;
    bool                mbAntiAliasB2DDraw;
    std::vector< double >                       maStroke;

public:
    SystemDependentData_CairoPath(
@@ -1071,12 +1075,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;
};
@@ -1088,17 +1095,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;
        }
    }
}

@@ -1134,7 +1148,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,
@@ -1165,7 +1180,8 @@ bool SvpSalGraphics::drawPolyLine(
            rObjectToDevice,
            rPolyLine,
            fTransparency,
            rLineWidths,
            rLineWidth,
            pStroke, // MM01
            eLineJoin,
            eLineCap,
            fMiterMinimumAngle,
@@ -1184,7 +1200,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,
@@ -1197,10 +1214,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;
@@ -1214,12 +1231,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)
@@ -1245,8 +1261,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)
@@ -1297,13 +1311,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
@@ -1327,42 +1363,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;

                // prepare next step
                aEdge.setB2DPoint(0, aEdge.getB2DPoint(1));
                aEdge.append(aPolyLine.getB2DPoint(0));
                aEdge.append(basegfx::B2DPoint(0.0, 0.0));

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

@@ -1374,7 +1435,8 @@ bool SvpSalGraphics::drawPolyLine(
                nSizeMeasure,
                cr,
                bNoJoin,
                bAntiAliasB2DDraw);
                bAntiAliasB2DDraw,
                pStroke);
        }
    }

@@ -1454,7 +1516,8 @@ namespace
                nSizeMeasure,
                cr,
                false,
                false);
                false,
                nullptr);
        }
    }
}
diff --git a/vcl/inc/headless/svpgdi.hxx b/vcl/inc/headless/svpgdi.hxx
index bb0d3d5..99300ed 100644
--- a/vcl/inc/headless/svpgdi.hxx
+++ b/vcl/inc/headless/svpgdi.hxx
@@ -114,6 +114,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,
@@ -211,6 +212,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/opengl/gdiimpl.hxx b/vcl/inc/opengl/gdiimpl.hxx
index 2deba40..ac488a2 100644
--- a/vcl/inc/opengl/gdiimpl.hxx
+++ b/vcl/inc/opengl/gdiimpl.hxx
@@ -260,6 +260,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 51a4a45..a17421b
--- 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 48ae50e..3fa3a0e 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 6fa87ec..8ae88aba1 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,
@@ -466,6 +467,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 432dbdb..3b179a7 100644
--- a/vcl/inc/salgdiimpl.hxx
+++ b/vcl/inc/salgdiimpl.hxx
@@ -110,6 +110,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
old mode 100644
new mode 100755
index 10b6371..46b3f7b
--- a/vcl/inc/skia/gdiimpl.hxx
+++ b/vcl/inc/skia/gdiimpl.hxx
@@ -101,9 +101,10 @@ public:

    virtual bool drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
                              const basegfx::B2DPolygon&, double fTransparency,
                              const basegfx::B2DVector& rLineWidths, basegfx::B2DLineJoin,
                              css::drawing::LineCap, double fMiterMinimumAngle,
                              bool bPixelSnapHairline) override;
                              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;
diff --git a/vcl/inc/unx/genpspgraphics.h b/vcl/inc/unx/genpspgraphics.h
index cce727b..6dfca75 100644
--- a/vcl/inc/unx/genpspgraphics.h
+++ b/vcl/inc/unx/genpspgraphics.h
@@ -132,6 +132,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 111702c..01e5fff 100644
--- a/vcl/inc/unx/salgdi.h
+++ b/vcl/inc/unx/salgdi.h
@@ -170,6 +170,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 1586680..06365cd 100644
--- a/vcl/inc/win/salgdi.h
+++ b/vcl/inc/win/salgdi.h
@@ -245,6 +245,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 5c26a6d..ab4b163 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>
@@ -1559,6 +1561,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*/,
@@ -1636,6 +1639,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,
@@ -1643,28 +1647,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());

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

    return true;
}

diff --git a/vcl/qt5/Qt5Graphics_GDI.cxx b/vcl/qt5/Qt5Graphics_GDI.cxx
old mode 100644
new mode 100755
index 2aa7094..102c849
--- 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())
    {
        return true;
    }

    // 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 f5f1aba..2f0ea1a 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,7 +933,7 @@ bool AquaSalGraphics::drawPolyLine(
        CGContextSetAlpha( maContextHolder.get(), 1.0 - fTransparency );
        CGContextSetLineJoin( maContextHolder.get(), aCGLineJoin );
        CGContextSetLineCap( maContextHolder.get(), aCGLineCap );
        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
old mode 100644
new mode 100755
index a1d8c00..a4ca9f0
--- a/vcl/skia/gdiimpl.cxx
+++ b/vcl/skia/gdiimpl.cxx
@@ -34,7 +34,9 @@
#include <SkDashPathEffect.h>
#include <GrBackendSurface.h>

#include <numeric>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>

namespace
{
@@ -622,6 +624,7 @@ void SkiaSalGraphicsImpl::drawPolyLine(sal_uInt32 nPoints, const SalPoint* pPtAr
    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);
}
@@ -706,34 +709,60 @@ bool SkiaSalGraphicsImpl::drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectTo

bool SkiaSalGraphicsImpl::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 (rPolyLine.count() == 0 || fTransparency < 0.0 || fTransparency >= 1.0
    // 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 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);

    // 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) && (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);
    aPolyPolygonLine.transform(rObjectToDevice);
    if (bPixelSnapHairline)
        aPolyLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyLine);
    {
        aPolyPolygonLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygonLine);
    }

    // Setup Line Join
    SkPaint::Join eSkLineJoin = SkPaint::kMiter_Join;
@@ -776,11 +805,18 @@ bool SkiaSalGraphicsImpl::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDev
    aPaint.setStrokeJoin(eSkLineJoin);
    aPaint.setColor(toSkColorWithTransparency(mLineColor, fTransparency));
    aPaint.setStrokeMiter(fMiterLimit);
    aPaint.setStrokeWidth(aLineWidths.getX());
    aPaint.setStrokeWidth(aLineWidth.getX());
    aPaint.setAntiAlias(mParent.getAntiAliasB2DDraw());

    SkPath aPath;
    addPolygonToPath(aPolyLine, 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.
diff --git a/vcl/source/gdi/FileDefinitionWidgetDraw.cxx b/vcl/source/gdi/FileDefinitionWidgetDraw.cxx
old mode 100644
new mode 100755
index 4133c3c..747c054
--- a/vcl/source/gdi/FileDefinitionWidgetDraw.cxx
+++ b/vcl/source/gdi/FileDefinitionWidgetDraw.cxx
@@ -298,6 +298,7 @@ void drawFromDrawCommands(gfx::DrawRoot const& rDrawRoot, SalGraphics& rGraphics
                    rGraphics.DrawPolyLine(
                        basegfx::B2DHomMatrix(), aB2DPolygon, 1.0 - rRectangle.mnOpacity,
                        basegfx::B2DVector(rRectangle.mnStrokeWidth, rRectangle.mnStrokeWidth),
                        nullptr, // MM01
                        basegfx::B2DLineJoin::Round, css::drawing::LineCap_ROUND, 0.0f, false,
                        nullptr);
                }
@@ -345,6 +346,7 @@ void drawFromDrawCommands(gfx::DrawRoot const& rDrawRoot, SalGraphics& rGraphics
                        rGraphics.DrawPolyLine(
                            basegfx::B2DHomMatrix(), rPolygon, 1.0 - rPath.mnOpacity,
                            basegfx::B2DVector(rPath.mnStrokeWidth, rPath.mnStrokeWidth),
                            nullptr, // MM01
                            basegfx::B2DLineJoin::Round, css::drawing::LineCap_ROUND, 0.0f, false,
                            nullptr);
                    }
@@ -387,6 +389,7 @@ void munchDrawCommands(std::vector<std::shared_ptr<WidgetDrawAction>> const& rDr
                rGraphics.DrawPolyLine(
                    basegfx::B2DHomMatrix(), aB2DPolygon, 0.0f,
                    basegfx::B2DVector(rWidgetDraw.mnStrokeWidth, rWidgetDraw.mnStrokeWidth),
                    nullptr, // MM01
                    basegfx::B2DLineJoin::Round, css::drawing::LineCap_ROUND, 0.0f, false, nullptr);
            }
            break;
@@ -410,6 +413,7 @@ void munchDrawCommands(std::vector<std::shared_ptr<WidgetDrawAction>> const& rDr
                rGraphics.DrawPolyLine(
                    basegfx::B2DHomMatrix(), aB2DPolygon, 0.0f,
                    basegfx::B2DVector(rWidgetDraw.mnStrokeWidth, rWidgetDraw.mnStrokeWidth),
                    nullptr, // MM01
                    basegfx::B2DLineJoin::Round, css::drawing::LineCap_ROUND, 0.0f, false, nullptr);
            }
            break;
diff --git a/vcl/source/gdi/salgdilayout.cxx b/vcl/source/gdi/salgdilayout.cxx
index cb8b8a8..fa5656b 100644
--- a/vcl/source/gdi/salgdilayout.cxx
+++ b/vcl/source/gdi/salgdilayout.cxx
@@ -573,6 +573,7 @@ bool SalGraphics::DrawPolyLine(
    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,
@@ -601,6 +602,7 @@ bool SalGraphics::DrawPolyLine(
                    i_rPolygon,
                    i_fTransparency,
                    i_rLineWidth,
                    i_pStroke, // MM01
                    i_eLineJoin,
                    i_eLineCap,
                    i_fMiterMinimumAngle,
@@ -628,6 +630,7 @@ bool SalGraphics::DrawPolyLine(
                    i_rPolygon,
                    i_fTransparency,
                    i_rLineWidth,
                    i_pStroke, // MM01
                    i_eLineJoin,
                    i_eLineCap,
                    i_fMiterMinimumAngle,
@@ -642,6 +645,7 @@ bool SalGraphics::DrawPolyLine(
        i_rPolygon,
        i_fTransparency,
        i_rLineWidth,
        i_pStroke, // MM01
        i_eLineJoin,
        i_eLineCap,
        i_fMiterMinimumAngle,
diff --git a/vcl/source/outdev/line.cxx b/vcl/source/outdev/line.cxx
index a9bdffb..3332ab7 100644
--- a/vcl/source/outdev/line.cxx
+++ b/vcl/source/outdev/line.cxx
@@ -130,6 +130,7 @@ void OutputDevice::DrawLine( const Point& rStartPt, const Point& rEndPt )
            aB2DPolyLine,
            0.0,
            aB2DLineWidth,
            nullptr, // MM01
            basegfx::B2DLineJoin::NONE,
            css::drawing::LineCap_BUTT,
            basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
@@ -241,6 +242,7 @@ void OutputDevice::drawLine( basegfx::B2DPolyPolygon aLinePolyPolygon, const Lin
                    rB2DPolygon,
                    0.0,
                    basegfx::B2DVector(1.0,1.0),
                    nullptr, // MM01
                    basegfx::B2DLineJoin::NONE,
                    css::drawing::LineCap_BUTT,
                    basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
diff --git a/vcl/source/outdev/polygon.cxx b/vcl/source/outdev/polygon.cxx
index 9b0cb38..c33374f 100644
--- a/vcl/source/outdev/polygon.cxx
+++ b/vcl/source/outdev/polygon.cxx
@@ -98,6 +98,7 @@ void OutputDevice::DrawPolyPolygon( const tools::PolyPolygon& rPolyPoly )
                    rPolygon,
                    0.0,
                    aB2DLineWidth,
                    nullptr, // MM01
                    basegfx::B2DLineJoin::NONE,
                    css::drawing::LineCap_BUTT,
                    basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
@@ -216,6 +217,7 @@ void OutputDevice::DrawPolygon( const tools::Polygon& rPoly )
                aB2DPolygon,
                0.0,
                aB2DLineWidth,
                nullptr, // MM01
                basegfx::B2DLineJoin::NONE,
                css::drawing::LineCap_BUTT,
                basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
@@ -326,6 +328,7 @@ void OutputDevice::ImplDrawPolyPolygonWithB2DPolyPolygon(const basegfx::B2DPolyP
                    rPolygon,
                    0.0,
                    aB2DLineWidth,
                    nullptr, // MM01
                    basegfx::B2DLineJoin::NONE,
                    css::drawing::LineCap_BUTT,
                    basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
diff --git a/vcl/source/outdev/polyline.cxx b/vcl/source/outdev/polyline.cxx
index f070ebe..161bf5f 100644
--- a/vcl/source/outdev/polyline.cxx
+++ b/vcl/source/outdev/polyline.cxx
@@ -73,6 +73,7 @@ void OutputDevice::DrawPolyLine( const tools::Polygon& rPoly )
        aB2DPolyLine,
        0.0,
        aB2DLineWidth,
        nullptr, // MM01
        basegfx::B2DLineJoin::NONE,
        css::drawing::LineCap_BUTT,
        basegfx::deg2rad(15.0) /*default fMiterMinimumAngle, not used*/,
@@ -176,6 +177,7 @@ void OutputDevice::DrawPolyLine( const basegfx::B2DPolygon& rB2DPolygon,
        rB2DPolygon,
        fLineWidth,
        0.0,
        nullptr, // MM01
        eLineJoin,
        eLineCap,
        fMiterMinimumAngle))
@@ -232,6 +234,7 @@ void OutputDevice::DrawPolyLine( const basegfx::B2DPolygon& rB2DPolygon,
                rPolygon,
                0.0,
                0.0,
                nullptr, // MM01
                basegfx::B2DLineJoin::NONE,
                css::drawing::LineCap_BUTT,
                basegfx::deg2rad(15.0) /*default, not used*/,
@@ -303,6 +306,7 @@ bool OutputDevice::DrawPolyLineDirect(
    const basegfx::B2DPolygon& rB2DPolygon,
    double fLineWidth,
    double fTransparency,
    const std::vector< double >* pStroke, // MM01
    basegfx::B2DLineJoin eLineJoin,
    css::drawing::LineCap eLineCap,
    double fMiterMinimumAngle,
@@ -347,6 +351,7 @@ bool OutputDevice::DrawPolyLineDirect(
            rB2DPolygon,
            fTransparency,
            aB2DLineWidth,
            pStroke, // MM01
            eLineJoin,
            eLineCap,
            fMiterMinimumAngle,
diff --git a/vcl/source/outdev/textline.cxx b/vcl/source/outdev/textline.cxx
index 571855d..e52b024 100644
--- a/vcl/source/outdev/textline.cxx
+++ b/vcl/source/outdev/textline.cxx
@@ -1008,9 +1008,16 @@ void OutputDevice::DrawWaveLine(const Point& rStartPos, const Point& rEndPos, lo

    mpGraphics->SetLineColor(GetLineColor());
    mpGraphics->DrawPolyLine(
            aRotationMatrix, aWaveLinePolygon, 0.0, aLineWidth,
            basegfx::B2DLineJoin::NONE, css::drawing::LineCap_BUTT,
            basegfx::deg2rad(15.0), bPixelSnapHairline, this);
            aRotationMatrix,
            aWaveLinePolygon,
            0.0,
            aLineWidth,
            nullptr, // MM01
            basegfx::B2DLineJoin::NONE,
            css::drawing::LineCap_BUTT,
            basegfx::deg2rad(15.0),
            bPixelSnapHairline,
            this);

    if( mpAlphaVDev )
        mpAlphaVDev->DrawWaveLine( rStartPos, rEndPos, nLineWidth );
diff --git a/vcl/source/outdev/transparent.cxx b/vcl/source/outdev/transparent.cxx
index 78ea40b..3c4a6fb 100644
--- a/vcl/source/outdev/transparent.cxx
+++ b/vcl/source/outdev/transparent.cxx
@@ -270,6 +270,7 @@ void OutputDevice::DrawTransparent(
                    rPolygon,
                    fTransparency,
                    aHairlineWidth,
                    nullptr, // MM01
                    basegfx::B2DLineJoin::NONE,
                    css::drawing::LineCap_BUTT,
                    basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
@@ -392,6 +393,7 @@ bool OutputDevice::DrawTransparentNatively ( const tools::PolyPolygon& rPolyPoly
                    rPolygon,
                    fTransparency,
                    aLineWidths,
                    nullptr, // MM01
                    basegfx::B2DLineJoin::NONE,
                    css::drawing::LineCap_BUTT,
                    basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
diff --git a/vcl/unx/generic/gdi/gdiimpl.cxx b/vcl/unx/generic/gdi/gdiimpl.cxx
index f738591..12ac598 100644
--- a/vcl/unx/generic/gdi/gdiimpl.cxx
+++ b/vcl/unx/generic/gdi/gdiimpl.cxx
@@ -18,6 +18,8 @@
 */

#include <memory>
#include <numeric>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xrender.h>
@@ -1584,6 +1586,7 @@ private:
    basegfx::B2DLineJoin                        meJoin;
    css::drawing::LineCap                       meCap;
    double                                      mfMiterMinimumAngle;
    std::vector< double >                       maStroke;

public:
    SystemDependentData_Triangulation(
@@ -1592,13 +1595,16 @@ public:
        const basegfx::B2DVector& rLineWidth,
        basegfx::B2DLineJoin eJoin,
        css::drawing::LineCap eCap,
        double fMiterMinimumAngle);
        double fMiterMinimumAngle,
        const std::vector< double >* pStroke); // MM01

    // read access
    const basegfx::triangulator::B2DTriangleVector& getTriangles() const { return maTriangles; }
    const basegfx::B2DVector& getLineWidth() const { return maLineWidth; }
    const basegfx::B2DLineJoin& getJoin() const { return meJoin; }
    const css::drawing::LineCap& getCap() const { return meCap; }
    double getMiterMinimumAngle() const { return mfMiterMinimumAngle; }
    const std::vector< double >& getStroke() const { return maStroke; }

    virtual sal_Int64 estimateUsageInBytes() const override;
};
@@ -1611,14 +1617,20 @@ SystemDependentData_Triangulation::SystemDependentData_Triangulation(
    const basegfx::B2DVector& rLineWidth,
    basegfx::B2DLineJoin eJoin,
    css::drawing::LineCap eCap,
    double fMiterMinimumAngle)
    double fMiterMinimumAngle,
    const std::vector< double >* pStroke)
:   basegfx::SystemDependentData(rSystemDependentDataManager),
    maTriangles(rTriangles),
    maLineWidth(rLineWidth),
    meJoin(eJoin),
    meCap(eCap),
    mfMiterMinimumAngle(fMiterMinimumAngle)
    mfMiterMinimumAngle(fMiterMinimumAngle),
    maStroke()
{
    if(nullptr != pStroke)
    {
        maStroke = *pStroke;
    }
}

sal_Int64 SystemDependentData_Triangulation::estimateUsageInBytes() const
@@ -1638,6 +1650,7 @@ bool X11SalGraphicsImpl::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,
@@ -1655,7 +1668,6 @@ bool X11SalGraphicsImpl::drawPolyLine(
    const basegfx::B2DVector aDeviceLineWidths(bObjectToDeviceIsIdentity ? rLineWidth : rObjectToDevice * rLineWidth);
    const bool bCorrectLineWidth(!bObjectToDeviceIsIdentity && aDeviceLineWidths.getX() < 1.0 && aLineWidth.getX() >= 1.0);
    basegfx::B2DHomMatrix aObjectToDeviceInv;
    basegfx::B2DPolygon aPolygon(rPolygon);

    if(bCorrectLineWidth)
    {
@@ -1673,6 +1685,25 @@ bool X11SalGraphicsImpl::drawPolyLine(
    std::shared_ptr<SystemDependentData_Triangulation> pSystemDependentData_Triangulation(
        rPolygon.getSystemDependentData<SystemDependentData_Triangulation>());

    // 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_Triangulation)
    {
        // MM01 - check on stroke change. Used against not used, or if oth used,
        // equal or different? Triangulation geometry creation depends heavily
        // on stroke, independent of being transformation independent
        const bool bStrokeWasUsed(!pSystemDependentData_Triangulation->getStroke().empty());

        if(bStrokeWasUsed != bStrokeUsed
        || (bStrokeUsed && *pStroke != pSystemDependentData_Triangulation->getStroke()))
        {
            // data invalid, forget
            pSystemDependentData_Triangulation.reset();
        }
    }

    if(pSystemDependentData_Triangulation)
    {
        // check data validity (I)
@@ -1709,15 +1740,36 @@ bool X11SalGraphicsImpl::drawPolyLine(

    if(!pSystemDependentData_Triangulation)
    {
        // MM01 need to do line dashing as fallback stuff here now
        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);
        }

        // try to create data
        if(bPixelSnapHairline)
        {
            // Do NOT transform, but keep device-independent. To
            // do so, transform to device for snap, but back again after
            if(!bObjectToDeviceIsIdentity)
            {
                aPolygon.transform(rObjectToDevice);
                aPolyPolygonLine.transform(rObjectToDevice);
            }

            aPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolygon);
            aPolyPolygonLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygonLine);

            if(!bObjectToDeviceIsIdentity)
            {
@@ -1727,12 +1779,31 @@ bool X11SalGraphicsImpl::drawPolyLine(
                    aObjectToDeviceInv.invert();
                }

                aPolygon.transform(aObjectToDeviceInv);
                aPolyPolygonLine.transform(aObjectToDeviceInv);
            }
        }

        basegfx::triangulator::B2DTriangleVector aTriangles;

        // MM01 checked/verified for X11 (linux)
        for(sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++)
        {
            const basegfx::B2DPolygon aPolyLine(aPolyPolygonLine.getB2DPolygon(a));
            // MM01 upps - commit 51b5b93092d6231615de470c62494c24e54828a1 removed
            // this *central* geometry-creating lines (!) probably due to aAreaPolyPoly
            // *not* being used - that's true, but the work is inside of filling
            // aTriangles data (!)
            basegfx::utils::createAreaGeometry(
                aPolyLine,
                0.5 * aLineWidth.getX(),
                eLineJoin,
                eLineCap,
                basegfx::deg2rad(12.5),
                0.4,
                fMiterMinimumAngle,
                &aTriangles); // CAUTION! This is *needed* since it creates the data!
        }

        if(!aTriangles.empty())
        {
            // Add to buffering mechanism
@@ -1744,7 +1815,8 @@ bool X11SalGraphicsImpl::drawPolyLine(
                aLineWidth,
                eLineJoin,
                eLineCap,
                fMiterMinimumAngle);
                fMiterMinimumAngle,
                pStroke);
        }
    }

diff --git a/vcl/unx/generic/gdi/gdiimpl.hxx b/vcl/unx/generic/gdi/gdiimpl.hxx
index 6660e19..9a4940e 100644
--- a/vcl/unx/generic/gdi/gdiimpl.hxx
+++ b/vcl/unx/generic/gdi/gdiimpl.hxx
@@ -172,6 +172,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/unx/generic/gdi/salgdi.cxx b/vcl/unx/generic/gdi/salgdi.cxx
index 864d3fd..d5b6281 100644
--- a/vcl/unx/generic/gdi/salgdi.cxx
+++ b/vcl/unx/generic/gdi/salgdi.cxx
@@ -699,6 +699,7 @@ bool X11SalGraphics::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,
@@ -735,6 +736,7 @@ bool X11SalGraphics::drawPolyLine(
                rPolygon,
                fTransparency,
                rLineWidth,
                pStroke, // MM01
                eLineJoin,
                eLineCap,
                fMiterMinimumAngle,
@@ -754,6 +756,7 @@ bool X11SalGraphics::drawPolyLine(
        rPolygon,
        fTransparency,
        rLineWidth,
        pStroke, // MM01
        eLineJoin,
        eLineCap,
        fMiterMinimumAngle,
diff --git a/vcl/unx/generic/print/genpspgraphics.cxx b/vcl/unx/generic/print/genpspgraphics.cxx
index 901edb5..e0a6778 100644
--- a/vcl/unx/generic/print/genpspgraphics.cxx
+++ b/vcl/unx/generic/print/genpspgraphics.cxx
@@ -433,6 +433,7 @@ bool GenPspGraphics::drawPolyLine(
    const basegfx::B2DPolygon&,
    double /*fTransparency*/,
    const basegfx::B2DVector& /*rLineWidths*/,
    const std::vector< double >* /*pStroke*/, // MM01
    basegfx::B2DLineJoin /*eJoin*/,
    css::drawing::LineCap /*eLineCap*/,
    double /*fMiterMinimumAngle*/,
diff --git a/vcl/win/gdi/gdiimpl.cxx b/vcl/win/gdi/gdiimpl.cxx
index 875f492..919395f 100644
--- a/vcl/win/gdi/gdiimpl.cxx
+++ b/vcl/win/gdi/gdiimpl.cxx
@@ -20,6 +20,7 @@
#include <sal/config.h>

#include <memory>
#include <numeric>

#include <svsys.h>

@@ -1987,18 +1988,19 @@ private:
    // all other values the triangulation is based on and
    // need to be compared with to check for data validity
    bool                                    mbNoLineJoin;
    std::vector< double >                       maStroke;

public:
    SystemDependentData_GraphicsPath(
        basegfx::SystemDependentDataManager& rSystemDependentDataManager,
        std::shared_ptr<Gdiplus::GraphicsPath>& rpGraphicsPath,
        bool bNoLineJoin);
        bool bNoLineJoin,
        const std::vector< double >* pStroke); // MM01

    // read access to Gdiplus::GraphicsPath
    // read access
    std::shared_ptr<Gdiplus::GraphicsPath>& getGraphicsPath() { return mpGraphicsPath; }

    // other data-validity access
    bool getNoLineJoin() const { return mbNoLineJoin; }
    const std::vector< double >& getStroke() const { return maStroke; }

    virtual sal_Int64 estimateUsageInBytes() const override;
};
@@ -2008,11 +2010,17 @@ public:
SystemDependentData_GraphicsPath::SystemDependentData_GraphicsPath(
    basegfx::SystemDependentDataManager& rSystemDependentDataManager,
    std::shared_ptr<Gdiplus::GraphicsPath>& rpGraphicsPath,
    bool bNoLineJoin)
    bool bNoLineJoin,
    const std::vector< double >* pStroke)
:   basegfx::SystemDependentData(rSystemDependentDataManager),
    mpGraphicsPath(rpGraphicsPath),
    mbNoLineJoin(bNoLineJoin)
    mbNoLineJoin(bNoLineJoin),
    maStroke()
{
    if(nullptr != pStroke)
    {
        maStroke = *pStroke;
    }
}

sal_Int64 SystemDependentData_GraphicsPath::estimateUsageInBytes() const
@@ -2136,7 +2144,8 @@ bool WinSalGraphicsImpl::drawPolyPolygon(
        rPolyPolygon.addOrReplaceSystemDependentData<SystemDependentData_GraphicsPath>(
            ImplGetSystemDependentDataManager(),
            pGraphicsPath,
            false);
            false,
            nullptr);
    }

    if(mrParent.getAntiAliasB2DDraw())
@@ -2196,12 +2205,14 @@ bool WinSalGraphicsImpl::drawPolyLine(
    const basegfx::B2DPolygon& rPolygon,
    double fTransparency,
    const basegfx::B2DVector& rLineWidths,
    const std::vector< double >* pStroke, // MM01
    basegfx::B2DLineJoin eLineJoin,
    css::drawing::LineCap eLineCap,
    double fMiterMinimumAngle,
    bool bPixelSnapHairline)
{
    if(!mbPen || 0 == rPolygon.count())
    // MM01 check done for simple reasons
    if(!mbPen || !rPolygon.count() || fTransparency < 0.0 || fTransparency > 1.0)
    {
        return true;
    }
@@ -2293,6 +2304,25 @@ bool WinSalGraphicsImpl::drawPolyLine(
    std::shared_ptr<SystemDependentData_GraphicsPath> pSystemDependentData_GraphicsPath(
        rPolygon.getSystemDependentData<SystemDependentData_GraphicsPath>());

    // 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_GraphicsPath)
    {
        // MM01 - check on stroke change. Used against not used, or if oth used,
        // equal or different? Triangulation geometry creation depends heavily
        // on stroke, independent of being transformation independent
        const bool bStrokeWasUsed(!pSystemDependentData_GraphicsPath->getStroke().empty());

        if(bStrokeWasUsed != bStrokeUsed
        || (bStrokeUsed && *pStroke != pSystemDependentData_GraphicsPath->getStroke()))
        {
            // data invalid, forget
            pSystemDependentData_GraphicsPath.reset();
        }
    }

    if(pSystemDependentData_GraphicsPath)
    {
        // check data validity
@@ -2314,17 +2344,47 @@ bool WinSalGraphicsImpl::drawPolyLine(
        // fill data of buffered data
        pGraphicsPath = std::make_shared<Gdiplus::GraphicsPath>();

        impAddB2DPolygonToGDIPlusGraphicsPathReal(
            *pGraphicsPath,
            rPolygon,
            rObjectToDevice,
            bNoLineJoin,
            bPixelSnapHairline);

        if(rPolygon.isClosed() && !bNoLineJoin)
        if(bStrokeUsed)
        {
            // #i101491# needed to create the correct line joins
            pGraphicsPath->CloseFigure();
            // MM01 need to do line dashing as fallback stuff here now
            basegfx::B2DPolyPolygon aPolyPolygonLine;

            // apply LineStyle
            basegfx::utils::applyLineDashing(
                rPolygon, // source
                *pStroke, // pattern
                &aPolyPolygonLine, // traget for lines
                nullptr, // target for gaps
                fDotDashLength); // full length if available

            // MM01 checked/verified, ok
            for(sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++)
            {
                const basegfx::B2DPolygon aPolyLine(aPolyPolygonLine.getB2DPolygon(a));
                pGraphicsPath->StartFigure();
                impAddB2DPolygonToGDIPlusGraphicsPathReal(
                    *pGraphicsPath,
                    aPolyLine,
                    rObjectToDevice,
                    bNoLineJoin,
                    bPixelSnapHairline);
            }
        }
        else
        {
            // no line dashing, just copy
            impAddB2DPolygonToGDIPlusGraphicsPathReal(
                *pGraphicsPath,
                rPolygon,
                rObjectToDevice,
                bNoLineJoin,
                bPixelSnapHairline);

            if(rPolygon.isClosed() && !bNoLineJoin)
            {
                // #i101491# needed to create the correct line joins
                pGraphicsPath->CloseFigure();
            }
        }

        // add to buffering mechanism
@@ -2333,7 +2393,8 @@ bool WinSalGraphicsImpl::drawPolyLine(
            rPolygon.addOrReplaceSystemDependentData<SystemDependentData_GraphicsPath>(
                ImplGetSystemDependentDataManager(),
                pGraphicsPath,
                bNoLineJoin);
                bNoLineJoin,
                pStroke);
        }
    }

diff --git a/vcl/win/gdi/gdiimpl.hxx b/vcl/win/gdi/gdiimpl.hxx
index 3b3b183..eb56388 100644
--- a/vcl/win/gdi/gdiimpl.hxx
+++ b/vcl/win/gdi/gdiimpl.hxx
@@ -130,6 +130,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/win/gdi/salgdi_gdiplus.cxx b/vcl/win/gdi/salgdi_gdiplus.cxx
index 99c7d0e..ce2f2cc 100644
--- a/vcl/win/gdi/salgdi_gdiplus.cxx
+++ b/vcl/win/gdi/salgdi_gdiplus.cxx
@@ -42,6 +42,7 @@ bool WinSalGraphics::drawPolyLine(
    const basegfx::B2DPolygon& rPolygon,
    double fTransparency,
    const basegfx::B2DVector& rLineWidths,
    const std::vector< double >* pStroke, // MM01
    basegfx::B2DLineJoin eLineJoin,
    css::drawing::LineCap eLineCap,
    double fMiterMinimumAngle,
@@ -52,6 +53,7 @@ bool WinSalGraphics::drawPolyLine(
        rPolygon,
        fTransparency,
        rLineWidths,
        pStroke, // MM01
        eLineJoin,
        eLineCap,
        fMiterMinimumAngle,