fix line width in DrawPolyLine() with matrix (tdf#132498)

For backends that do the object-to-device coordinates transformation
directly, it's necessary to also convert the size of line width.
But simply multiplying it with the matrix can also rotate the line
width "vector", making it e.g. negative. So don't use just the X
coordinate, use vector length for the transformation, which is ok.
In fact it doesn't even make sense to treat width as a vector, because
a width simply is not a vector (and for this reason it's also not
actually used).

Change-Id: I1241c9cb29155df105170d568a879ebc32b11a5f
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/93203
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Tested-by: Jenkins
diff --git a/vcl/CppunitTest_vcl_backend_test.mk b/vcl/CppunitTest_vcl_backend_test.mk
index f146cb62..5a88622 100644
--- a/vcl/CppunitTest_vcl_backend_test.mk
+++ b/vcl/CppunitTest_vcl_backend_test.mk
@@ -14,6 +14,7 @@ $(eval $(call gb_CppunitTest_add_exception_objects,vcl_backend_test, \
))

$(eval $(call gb_CppunitTest_use_libraries,vcl_backend_test, \
	basegfx \
	comphelper \
	cppu \
	cppuhelper \
diff --git a/vcl/headless/svpgdi.cxx b/vcl/headless/svpgdi.cxx
index b5649b0..fadf641 100644
--- a/vcl/headless/svpgdi.cxx
+++ b/vcl/headless/svpgdi.cxx
@@ -1092,7 +1092,7 @@ void SvpSalGraphics::drawPolyLine(sal_uInt32 nPoints, const SalPoint* pPtAry)
        basegfx::B2DHomMatrix(),
        aPoly,
        0.0,
        basegfx::B2DVector(1.0, 1.0),
        1.0,
        nullptr, // MM01
        basegfx::B2DLineJoin::Miter,
        css::drawing::LineCap_BUTT,
@@ -1456,7 +1456,7 @@ bool SvpSalGraphics::drawPolyLine(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolygon& rPolyLine,
    double fTransparency,
    const basegfx::B2DVector& rLineWidth,
    double fLineWidth,
    const std::vector< double >* pStroke, // MM01
    basegfx::B2DLineJoin eLineJoin,
    css::drawing::LineCap eLineCap,
@@ -1488,7 +1488,7 @@ bool SvpSalGraphics::drawPolyLine(
            rObjectToDevice,
            rPolyLine,
            fTransparency,
            rLineWidth,
            fLineWidth,
            pStroke, // MM01
            eLineJoin,
            eLineCap,
@@ -1508,7 +1508,7 @@ bool SvpSalGraphics::drawPolyLine(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolygon& rPolyLine,
    double fTransparency,
    const basegfx::B2DVector& rLineWidth,
    double fLineWidth,
    const std::vector< double >* pStroke, // MM01
    basegfx::B2DLineJoin eLineJoin,
    css::drawing::LineCap eLineCap,
@@ -1522,21 +1522,20 @@ bool SvpSalGraphics::drawPolyLine(
    }

    // need to check/handle LineWidth when ObjectToDevice transformation is used
    basegfx::B2DVector aLineWidth(rLineWidth);
    const bool bObjectToDeviceIsIdentity(rObjectToDevice.isIdentity());

    // tdf#124848 calculate-back logical LineWidth for a hairline
    // since this implementation hands over the transformation to
    // the graphic sub-system
    if(aLineWidth.equalZero())
    if(fLineWidth == 0)
    {
        aLineWidth = basegfx::B2DVector(1.0, 1.0);
        fLineWidth = 1.0;

        if(!bObjectToDeviceIsIdentity)
        {
            basegfx::B2DHomMatrix aObjectToDeviceInv(rObjectToDevice);
            aObjectToDeviceInv.invert();
            aLineWidth = aObjectToDeviceInv * aLineWidth;
            fLineWidth = (aObjectToDeviceInv * basegfx::B2DVector(fLineWidth, 0)).getLength();
        }
    }

@@ -1617,7 +1616,7 @@ bool SvpSalGraphics::drawPolyLine(

    cairo_set_line_join(cr, eCairoLineJoin);
    cairo_set_line_cap(cr, eCairoLineCap);
    cairo_set_line_width(cr, aLineWidth.getX());
    cairo_set_line_width(cr, fLineWidth);
    cairo_set_miter_limit(cr, fMiterLimit);

    // try to access buffered data
@@ -1654,7 +1653,7 @@ bool SvpSalGraphics::drawPolyLine(

    // check for basegfx::B2DLineJoin::NONE to react accordingly
    const bool bNoJoin((basegfx::B2DLineJoin::NONE == eLineJoin
        && basegfx::fTools::more(aLineWidth.getX(), 0.0)));
        && basegfx::fTools::more(fLineWidth, 0.0)));

    if(pSystemDependentData_CairoPath)
    {
diff --git a/vcl/inc/headless/svpgdi.hxx b/vcl/inc/headless/svpgdi.hxx
index 99300ed..3d1617d 100644
--- a/vcl/inc/headless/svpgdi.hxx
+++ b/vcl/inc/headless/svpgdi.hxx
@@ -113,7 +113,7 @@ public:
        const basegfx::B2DHomMatrix& rObjectToDevice,
        const basegfx::B2DPolygon& rPolyLine,
        double fTransparency,
        const basegfx::B2DVector& rLineWidths,
        double fLineWidth,
        const std::vector< double >* pStroke, // MM01
        basegfx::B2DLineJoin eLineJoin,
        css::drawing::LineCap eLineCap,
@@ -211,7 +211,7 @@ public:
                                const basegfx::B2DHomMatrix& rObjectToDevice,
                                const basegfx::B2DPolygon&,
                                double fTransparency,
                                const basegfx::B2DVector& rLineWidths,
                                double fLineWidth,
                                const std::vector< double >* pStroke, // MM01
                                basegfx::B2DLineJoin,
                                css::drawing::LineCap,
diff --git a/vcl/inc/opengl/RenderList.hxx b/vcl/inc/opengl/RenderList.hxx
index 06dd03b..213a2f4 100644
--- a/vcl/inc/opengl/RenderList.hxx
+++ b/vcl/inc/opengl/RenderList.hxx
@@ -163,7 +163,7 @@ public:
                            Color nLineColor, Color nFillColor, bool bUseAA);

    void addDrawPolyLine(const basegfx::B2DPolygon& rPolygon, double fTransparency,
                         const basegfx::B2DVector& rLineWidth, basegfx::B2DLineJoin eLineJoin,
                         double fLineWidth, basegfx::B2DLineJoin eLineJoin,
                         css::drawing::LineCap eLineCap, double fMiterMinimumAngle,
                         Color nLineColor, bool bUseAA);
};
diff --git a/vcl/inc/opengl/gdiimpl.hxx b/vcl/inc/opengl/gdiimpl.hxx
index ac488a2..a6de106 100644
--- a/vcl/inc/opengl/gdiimpl.hxx
+++ b/vcl/inc/opengl/gdiimpl.hxx
@@ -259,7 +259,7 @@ public:
                const basegfx::B2DHomMatrix& rObjectToDevice,
                const basegfx::B2DPolygon&,
                double fTransparency,
                const basegfx::B2DVector& rLineWidths,
                double fLineWidth,
                const std::vector< double >* pStroke, // MM01
                basegfx::B2DLineJoin,
                css::drawing::LineCap,
diff --git a/vcl/inc/qt5/Qt5Graphics.hxx b/vcl/inc/qt5/Qt5Graphics.hxx
index 0a66271..bc4870ee 100644
--- a/vcl/inc/qt5/Qt5Graphics.hxx
+++ b/vcl/inc/qt5/Qt5Graphics.hxx
@@ -118,8 +118,7 @@ public:
                                       const SalPoint* const* pPtAry,
                                       const PolyFlags* const* pFlgAry) override;
    virtual bool drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
                              const basegfx::B2DPolygon&, double fTransparency,
                              const basegfx::B2DVector& rLineWidths,
                              const basegfx::B2DPolygon&, double fTransparency, double fLineWidths,
                              const std::vector<double>* pStroke, // MM01
                              basegfx::B2DLineJoin, css::drawing::LineCap eLineCap,
                              double fMiterMinimumAngle, bool bPixelSnapHairline) override;
diff --git a/vcl/inc/quartz/salgdi.h b/vcl/inc/quartz/salgdi.h
index 3fa3a0e..5e0222b 100644
--- a/vcl/inc/quartz/salgdi.h
+++ b/vcl/inc/quartz/salgdi.h
@@ -240,7 +240,7 @@ public:
                                const basegfx::B2DHomMatrix& rObjectToDevice,
                                const basegfx::B2DPolygon&,
                                double fTransparency,
                                const basegfx::B2DVector& rLineWidths,
                                double rLineWidth,
                                const std::vector< double >* pStroke, // MM01
                                basegfx::B2DLineJoin,
                                css::drawing::LineCap eLineCap,
diff --git a/vcl/inc/salgdi.hxx b/vcl/inc/salgdi.hxx
index 8ae88aba1..2161ebe 100644
--- a/vcl/inc/salgdi.hxx
+++ b/vcl/inc/salgdi.hxx
@@ -246,7 +246,7 @@ public:
                                    const basegfx::B2DHomMatrix& rObjectToDevice,
                                    const basegfx::B2DPolygon& i_rPolygon,
                                    double i_fTransparency,
                                    const basegfx::B2DVector& i_rLineWidth,
                                    double i_fLineWidth,
                                    const std::vector< double >* i_pStroke, // MM01
                                    basegfx::B2DLineJoin i_eLineJoin,
                                    css::drawing::LineCap i_eLineCap,
@@ -466,7 +466,7 @@ protected:
                                    const basegfx::B2DHomMatrix& rObjectToDevice,
                                    const basegfx::B2DPolygon&,
                                    double fTransparency,
                                    const basegfx::B2DVector& rLineWidths,
                                    double fLineWidth,
                                    const std::vector< double >* pStroke, // MM01
                                    basegfx::B2DLineJoin,
                                    css::drawing::LineCap,
diff --git a/vcl/inc/salgdiimpl.hxx b/vcl/inc/salgdiimpl.hxx
index 3b179a7..0e62669 100644
--- a/vcl/inc/salgdiimpl.hxx
+++ b/vcl/inc/salgdiimpl.hxx
@@ -109,7 +109,7 @@ public:
                const basegfx::B2DHomMatrix& rObjectToDevice,
                const basegfx::B2DPolygon&,
                double fTransparency,
                const basegfx::B2DVector& rLineWidths,
                double fLineWidth,
                const std::vector< double >* pStroke, // MM01
                basegfx::B2DLineJoin,
                css::drawing::LineCap,
diff --git a/vcl/inc/skia/gdiimpl.hxx b/vcl/inc/skia/gdiimpl.hxx
index 68652ca..c78845f 100644
--- a/vcl/inc/skia/gdiimpl.hxx
+++ b/vcl/inc/skia/gdiimpl.hxx
@@ -103,8 +103,7 @@ public:
                                 const basegfx::B2DPolyPolygon&, double fTransparency) override;

    virtual bool drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
                              const basegfx::B2DPolygon&, double fTransparency,
                              const basegfx::B2DVector& rLineWidths,
                              const basegfx::B2DPolygon&, double fTransparency, double fLineWidth,
                              const std::vector<double>* pStroke, // MM01
                              basegfx::B2DLineJoin, css::drawing::LineCap,
                              double fMiterMinimumAngle, bool bPixelSnapHairline) override;
diff --git a/vcl/inc/unx/genpspgraphics.h b/vcl/inc/unx/genpspgraphics.h
index 6dfca75..6e68c96 100644
--- a/vcl/inc/unx/genpspgraphics.h
+++ b/vcl/inc/unx/genpspgraphics.h
@@ -131,7 +131,7 @@ public:
                                const basegfx::B2DHomMatrix& rObjectToDevice,
                                const basegfx::B2DPolygon&,
                                double fTransparency,
                                const basegfx::B2DVector& rLineWidths,
                                double fLineWidth,
                                const std::vector< double >* pStroke, // MM01
                                basegfx::B2DLineJoin,
                                css::drawing::LineCap,
diff --git a/vcl/inc/unx/salgdi.h b/vcl/inc/unx/salgdi.h
index 19abfa0..4216b70 100644
--- a/vcl/inc/unx/salgdi.h
+++ b/vcl/inc/unx/salgdi.h
@@ -169,7 +169,7 @@ public:
                                        const basegfx::B2DHomMatrix& rObjectToDevice,
                                        const basegfx::B2DPolygon&,
                                        double fTransparency,
                                        const basegfx::B2DVector& rLineWidth,
                                        double fLineWidth,
                                        const std::vector< double >* pStroke, // MM01
                                        basegfx::B2DLineJoin,
                                        css::drawing::LineCap,
diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h
index c45611a..4d10291 100644
--- a/vcl/inc/win/salgdi.h
+++ b/vcl/inc/win/salgdi.h
@@ -241,7 +241,7 @@ protected:
        const basegfx::B2DHomMatrix& rObjectToDevice,
        const basegfx::B2DPolygon&,
        double fTransparency,
        const basegfx::B2DVector& rLineWidth,
        double fLineWidth,
        const std::vector< double >* pStroke, // MM01
        basegfx::B2DLineJoin,
        css::drawing::LineCap,
diff --git a/vcl/opengl/RenderList.cxx b/vcl/opengl/RenderList.cxx
index 8181795..4830f10 100644
--- a/vcl/opengl/RenderList.cxx
+++ b/vcl/opengl/RenderList.cxx
@@ -372,7 +372,7 @@ void RenderList::addDrawTextureWithMaskColor(OpenGLTexture const & rTexture, Col
}

void RenderList::addDrawPolyLine(const basegfx::B2DPolygon& rPolygon, double fTransparency,
                                 const basegfx::B2DVector& rLineWidth, basegfx::B2DLineJoin eLineJoin,
                                 double fLineWidth, basegfx::B2DLineJoin eLineJoin,
                                 css::drawing::LineCap eLineCap, double fMiterMinimumAngle,
                                 Color nLineColor, bool bUseAA)
{
@@ -383,8 +383,8 @@ void RenderList::addDrawPolyLine(const basegfx::B2DPolygon& rPolygon, double fTr
    if (fTransparency == 1.0)
        return;

    const bool bIsHairline = (rLineWidth.getX() == rLineWidth.getY()) && (rLineWidth.getX() <= 1.2);
    const float fLineWidth = bIsHairline ? 1.0f : rLineWidth.getX();
    const bool bIsHairline = fLineWidth <= 1.2;
    fLineWidth = bIsHairline ? 1.0f : fLineWidth;

    basegfx::B2DPolygon aPolygon(rPolygon);
    if (rPolygon.areControlPointsUsed())
diff --git a/vcl/opengl/gdiimpl.cxx b/vcl/opengl/gdiimpl.cxx
index 25cac157..6c76154e 100644
--- a/vcl/opengl/gdiimpl.cxx
+++ b/vcl/opengl/gdiimpl.cxx
@@ -1560,7 +1560,7 @@ void OpenGLSalGraphicsImpl::drawPolyLine( sal_uInt32 nPoints, const SalPoint* pP
        basegfx::B2DHomMatrix(),
        aPoly,
        0.0,
        basegfx::B2DVector(1.0, 1.0),
        1.0,
        nullptr, // MM01
        basegfx::B2DLineJoin::Miter,
        css::drawing::LineCap_BUTT,
@@ -1638,7 +1638,7 @@ bool OpenGLSalGraphicsImpl::drawPolyLine(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolygon& rPolygon,
    double fTransparency,
    const basegfx::B2DVector& rLineWidth,
    double fLineWidth,
    const std::vector< double >* pStroke, // MM01
    basegfx::B2DLineJoin eLineJoin,
    css::drawing::LineCap eLineCap,
@@ -1680,10 +1680,10 @@ bool OpenGLSalGraphicsImpl::drawPolyLine(
    if(bPixelSnapHairline) { aPolyPolygonLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygonLine); }

    // tdf#124848 get correct LineWidth in discrete coordinates,
    // take hairline case into account
    const basegfx::B2DVector aLineWidth(rLineWidth.equalZero()
        ? basegfx::B2DVector(1.0, 1.0)
        : rObjectToDevice * rLineWidth);
    if(fLineWidth == 0) // hairline
        fLineWidth = 1.0;
    else // Adjust line width for object-to-device scale.
        fLineWidth = (rObjectToDevice * basegfx::B2DVector(fLineWidth, 0)).getLength();

    for(sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++)
    {
@@ -1695,7 +1695,7 @@ bool OpenGLSalGraphicsImpl::drawPolyLine(
        mpRenderList->addDrawPolyLine(
            aPolyLine,
            fTransparency,
            aLineWidth,
            fLineWidth,
            eLineJoin,
            eLineCap,
            fMiterMinimumAngle,
diff --git a/vcl/qa/cppunit/BackendTest.cxx b/vcl/qa/cppunit/BackendTest.cxx
index 802e4eb..f06d8fc 100644
--- a/vcl/qa/cppunit/BackendTest.cxx
+++ b/vcl/qa/cppunit/BackendTest.cxx
@@ -13,6 +13,7 @@
#include <vcl/bitmap.hxx>
#include <tools/stream.hxx>
#include <vcl/graphicfilter.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>

#include <test/outputdevice.hxx>

@@ -44,6 +45,16 @@ class BackendTest : public test::BootstrapFixture
        }
    }

    void exportDevice(const OUString& filename, const VclPtr<VirtualDevice>& device)
    {
        if (mbExportBitmap)
        {
            BitmapEx aBitmapEx(device->GetBitmap(Point(0, 0), device->GetOutputSizePixel()));
            SvFileStream aStream(filename, StreamMode::WRITE | StreamMode::TRUNC);
            GraphicFilter::GetGraphicFilter().compressAsPNG(aBitmapEx, aStream);
        }
    }

public:
    BackendTest()
        : BootstrapFixture(true, false)
@@ -444,6 +455,36 @@ public:
            CPPUNIT_ASSERT(eResult != vcl::test::TestResult::Failed);
    }

    void testTdf124848()
    {
        ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT);
        device->SetOutputSizePixel(Size(100, 100));
        device->SetBackground(Wallpaper(COL_WHITE));
        device->Erase();
        device->SetAntialiasing(AntialiasingFlags::EnableB2dDraw);
        device->SetLineColor(COL_BLACK);
        basegfx::B2DHomMatrix matrix;
        // DrawPolyLine() would apply the whole matrix to the line width, making it negative
        // in case of a larger rotation.
        matrix.rotate(M_PI); //180 degrees
        matrix.translate(100, 100);
        CPPUNIT_ASSERT(device->DrawPolyLineDirect(
            matrix, basegfx::B2DPolygon{ { 50, 50 }, { 50, 100 } }, 100));
        exportDevice("/tmp/tdf124848-1.png", device);
        // 100px wide line should fill the entire width of the upper half
        CPPUNIT_ASSERT_EQUAL(COL_BLACK, device->GetPixel(Point(2, 2)));

        // Also check hairline.
        device->Erase();
        CPPUNIT_ASSERT(
            device->DrawPolyLineDirect(matrix, basegfx::B2DPolygon{ { 50, 50 }, { 50, 100 } }, 0));
        exportDevice("/tmp/tdf124848-2.png", device);
        // 1px wide
        CPPUNIT_ASSERT_EQUAL(COL_BLACK, device->GetPixel(Point(50, 20)));
        CPPUNIT_ASSERT_EQUAL(COL_WHITE, device->GetPixel(Point(49, 20)));
        CPPUNIT_ASSERT_EQUAL(COL_WHITE, device->GetPixel(Point(51, 20)));
    }

    CPPUNIT_TEST_SUITE(BackendTest);
    CPPUNIT_TEST(testDrawRectWithRectangle);
    CPPUNIT_TEST(testDrawRectWithPixel);
@@ -487,6 +528,8 @@ public:
    CPPUNIT_TEST(testDrawBlend);
    CPPUNIT_TEST(testDrawXor);

    CPPUNIT_TEST(testTdf124848);

    CPPUNIT_TEST_SUITE_END();
};

diff --git a/vcl/qt5/Qt5Graphics_GDI.cxx b/vcl/qt5/Qt5Graphics_GDI.cxx
index eb43811..cfebca7 100644
--- a/vcl/qt5/Qt5Graphics_GDI.cxx
+++ b/vcl/qt5/Qt5Graphics_GDI.cxx
@@ -325,7 +325,7 @@ 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& rLineWidth,
                               double fLineWidth,
                               const std::vector<double>* pStroke, // MM01
                               basegfx::B2DLineJoin eLineJoin, css::drawing::LineCap eLineCap,
                               double fMiterMinimumAngle, bool bPixelSnapHairline)
@@ -371,9 +371,10 @@ bool Qt5Graphics::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
    }

    // tdf#124848 get correct LineWidth in discrete coordinates,
    // take hairline case into account
    const basegfx::B2DVector aLineWidth(rLineWidth.equalZero() ? basegfx::B2DVector(1.0, 1.0)
                                                               : rObjectToDevice * rLineWidth);
    if (fLineWidth == 0) // hairline
        fLineWidth = 1.0;
    else // Adjust line width for object-to-device scale.
        fLineWidth = (rObjectToDevice * basegfx::B2DVector(fLineWidth, 0)).getLength();

    // setup poly-polygon path
    QPainterPath aPath;
@@ -390,7 +391,7 @@ bool Qt5Graphics::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,

    // setup line attributes
    QPen aPen = aPainter.pen();
    aPen.setWidth(aLineWidth.getX());
    aPen.setWidth(fLineWidth);

    switch (eLineJoin)
    {
diff --git a/vcl/quartz/salgdicommon.cxx b/vcl/quartz/salgdicommon.cxx
index 51de382..3126400 100644
--- a/vcl/quartz/salgdicommon.cxx
+++ b/vcl/quartz/salgdicommon.cxx
@@ -817,7 +817,7 @@ bool AquaSalGraphics::drawPolyLine(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolygon& rPolyLine,
    double fTransparency,
    const basegfx::B2DVector& rLineWidth,
    double fLineWidth,
    const std::vector< double >* pStroke, // MM01
    basegfx::B2DLineJoin eLineJoin,
    css::drawing::LineCap eLineCap,
@@ -836,15 +836,15 @@ bool AquaSalGraphics::drawPolyLine(
#endif

    // tdf#124848 get correct LineWidth in discrete coordinates,
    // take hairline case into account
    const basegfx::B2DVector aLineWidth(rLineWidth.equalZero()
        ? basegfx::B2DVector(1.0, 1.0)
        : rObjectToDevice * rLineWidth);
    if(fLineWidth == 0) // hairline
        fLineWidth = 1.0;
    else // Adjust line width for object-to-device scale.
        fLineWidth = (rObjectToDevice * basegfx::B2DVector(fLineWidth, 0)).getLength();

    // #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) && (aLineWidth.getX() > 1.3) )
    if( (basegfx::B2DLineJoin::NONE == eLineJoin) && (fLineWidth > 1.3) )
        return false;

    // MM01 need to do line dashing as fallback stuff here now
@@ -935,12 +935,7 @@ bool AquaSalGraphics::drawPolyLine(
        CGContextSetAlpha( maContextHolder.get(), 1.0 - fTransparency );
        CGContextSetLineJoin( maContextHolder.get(), aCGLineJoin );
        CGContextSetLineCap( maContextHolder.get(), aCGLineCap );

        // aLineWidth.getX() can be negative here. That causes a warning that shows up in the debugger.
        if (aLineWidth.getX() > 0)
        {
            CGContextSetLineWidth( maContextHolder.get(), aLineWidth.getX() );
        }
        CGContextSetLineWidth( maContextHolder.get(), fLineWidth );
        CGContextSetMiterLimit(maContextHolder.get(), fCGMiterLimit);
        CGContextDrawPath( maContextHolder.get(), kCGPathStroke );
        maContextHolder.restoreState();
diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx
index bd55aaf..00cf34d 100644
--- a/vcl/skia/gdiimpl.cxx
+++ b/vcl/skia/gdiimpl.cxx
@@ -650,7 +650,7 @@ void SkiaSalGraphicsImpl::drawPolyLine(sal_uInt32 nPoints, const SalPoint* pPtAr
        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),
    drawPolyLine(basegfx::B2DHomMatrix(), aPolygon, 0.0, 1.0,
                 nullptr, // MM01
                 basegfx::B2DLineJoin::Miter, css::drawing::LineCap_BUTT,
                 basegfx::deg2rad(15.0) /*default*/, false);
@@ -742,7 +742,7 @@ bool SkiaSalGraphicsImpl::drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectTo

bool SkiaSalGraphicsImpl::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
                                       const basegfx::B2DPolygon& rPolyLine, double fTransparency,
                                       const basegfx::B2DVector& rLineWidth,
                                       double fLineWidth,
                                       const std::vector<double>* pStroke, // MM01
                                       basegfx::B2DLineJoin eLineJoin,
                                       css::drawing::LineCap eLineCap, double fMiterMinimumAngle,
@@ -759,9 +759,10 @@ bool SkiaSalGraphicsImpl::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDev
    SAL_INFO("vcl.skia.trace", "drawpolyline(" << this << "): " << rPolyLine << ":" << mLineColor);

    // tdf#124848 get correct LineWidth in discrete coordinates,
    // take hairline case into account
    const basegfx::B2DVector aLineWidth(rLineWidth.equalZero() ? basegfx::B2DVector(1.0, 1.0)
                                                               : rObjectToDevice * rLineWidth);
    if (fLineWidth == 0) // hairline
        fLineWidth = 1.0;
    else // Adjust line width for object-to-device scale.
        fLineWidth = (rObjectToDevice * basegfx::B2DVector(fLineWidth, 0)).getLength();

    // MM01 need to do line dashing as fallback stuff here now
    const double fDotDashLength(
@@ -833,7 +834,7 @@ bool SkiaSalGraphicsImpl::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDev
    aPaint.setStrokeJoin(eSkLineJoin);
    aPaint.setColor(toSkColorWithTransparency(mLineColor, fTransparency));
    aPaint.setStrokeMiter(fMiterLimit);
    aPaint.setStrokeWidth(aLineWidth.getX());
    aPaint.setStrokeWidth(fLineWidth);
    aPaint.setAntiAlias(mParent.getAntiAliasB2DDraw());

    if (eLineJoin != basegfx::B2DLineJoin::NONE)
diff --git a/vcl/source/gdi/FileDefinitionWidgetDraw.cxx b/vcl/source/gdi/FileDefinitionWidgetDraw.cxx
index 80bcda5..01698c4 100644
--- a/vcl/source/gdi/FileDefinitionWidgetDraw.cxx
+++ b/vcl/source/gdi/FileDefinitionWidgetDraw.cxx
@@ -295,12 +295,11 @@ void drawFromDrawCommands(gfx::DrawRoot const& rDrawRoot, SalGraphics& rGraphics
                {
                    rGraphics.SetLineColor(Color(*rRectangle.mpStrokeColor));
                    rGraphics.SetFillColor();
                    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);
                    rGraphics.DrawPolyLine(basegfx::B2DHomMatrix(), aB2DPolygon,
                                           1.0 - rRectangle.mnOpacity, rRectangle.mnStrokeWidth,
                                           nullptr, // MM01
                                           basegfx::B2DLineJoin::Round, css::drawing::LineCap_ROUND,
                                           0.0f, false, nullptr);
                }
            }
            break;
@@ -343,12 +342,11 @@ void drawFromDrawCommands(gfx::DrawRoot const& rDrawRoot, SalGraphics& rGraphics
                    rGraphics.SetFillColor();
                    for (auto const& rPolygon : aPolyPolygon)
                    {
                        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);
                        rGraphics.DrawPolyLine(basegfx::B2DHomMatrix(), rPolygon,
                                               1.0 - rPath.mnOpacity, rPath.mnStrokeWidth,
                                               nullptr, // MM01
                                               basegfx::B2DLineJoin::Round,
                                               css::drawing::LineCap_ROUND, 0.0f, false, nullptr);
                    }
                }
            }
@@ -387,8 +385,7 @@ void munchDrawCommands(std::vector<std::shared_ptr<WidgetDrawAction>> const& rDr
                rGraphics.SetLineColor(rWidgetDraw.maStrokeColor);
                rGraphics.SetFillColor();
                rGraphics.DrawPolyLine(
                    basegfx::B2DHomMatrix(), aB2DPolygon, 0.0f,
                    basegfx::B2DVector(rWidgetDraw.mnStrokeWidth, rWidgetDraw.mnStrokeWidth),
                    basegfx::B2DHomMatrix(), aB2DPolygon, 0.0f, rWidgetDraw.mnStrokeWidth,
                    nullptr, // MM01
                    basegfx::B2DLineJoin::Round, css::drawing::LineCap_ROUND, 0.0f, false, nullptr);
            }
@@ -411,8 +408,7 @@ void munchDrawCommands(std::vector<std::shared_ptr<WidgetDrawAction>> const& rDr
                };

                rGraphics.DrawPolyLine(
                    basegfx::B2DHomMatrix(), aB2DPolygon, 0.0f,
                    basegfx::B2DVector(rWidgetDraw.mnStrokeWidth, rWidgetDraw.mnStrokeWidth),
                    basegfx::B2DHomMatrix(), aB2DPolygon, 0.0f, rWidgetDraw.mnStrokeWidth,
                    nullptr, // MM01
                    basegfx::B2DLineJoin::Round, css::drawing::LineCap_ROUND, 0.0f, false, nullptr);
            }
diff --git a/vcl/source/gdi/salgdilayout.cxx b/vcl/source/gdi/salgdilayout.cxx
index 16a3080..0a50329f 100644
--- a/vcl/source/gdi/salgdilayout.cxx
+++ b/vcl/source/gdi/salgdilayout.cxx
@@ -572,7 +572,7 @@ bool SalGraphics::DrawPolyLine(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolygon& i_rPolygon,
    double i_fTransparency,
    const basegfx::B2DVector& i_rLineWidth,
    double i_rLineWidth,
    const std::vector< double >* i_pStroke, // MM01
    basegfx::B2DLineJoin i_eLineJoin,
    css::drawing::LineCap i_eLineCap,
diff --git a/vcl/source/outdev/line.cxx b/vcl/source/outdev/line.cxx
index e88dd38..f451b6e 100644
--- a/vcl/source/outdev/line.cxx
+++ b/vcl/source/outdev/line.cxx
@@ -128,8 +128,7 @@ void OutputDevice::DrawLine( const Point& rStartPt, const Point& rEndPt )
            basegfx::B2DHomMatrix(),
            aB2DPolyLine,
            0.0,
            // tdf#124848 hairline
            basegfx::B2DVector::getEmptyVector(),
            0.0, // tdf#124848 hairline
            nullptr, // MM01
            basegfx::B2DLineJoin::NONE,
            css::drawing::LineCap_BUTT,
@@ -241,8 +240,7 @@ void OutputDevice::drawLine( basegfx::B2DPolyPolygon aLinePolyPolygon, const Lin
                    basegfx::B2DHomMatrix(),
                    rB2DPolygon,
                    0.0,
                    // tdf#124848 hairline
                    basegfx::B2DVector::getEmptyVector(),
                    0.0, // tdf#124848 hairline
                    nullptr, // MM01
                    basegfx::B2DLineJoin::NONE,
                    css::drawing::LineCap_BUTT,
diff --git a/vcl/source/outdev/polygon.cxx b/vcl/source/outdev/polygon.cxx
index c742379..60deace 100644
--- a/vcl/source/outdev/polygon.cxx
+++ b/vcl/source/outdev/polygon.cxx
@@ -96,8 +96,7 @@ void OutputDevice::DrawPolyPolygon( const tools::PolyPolygon& rPolyPoly )
                    aTransform,
                    rPolygon,
                    0.0,
                    // tdf#124848 hairline
                    basegfx::B2DVector::getEmptyVector(),
                    0.0, // tdf#124848 hairline
                    nullptr, // MM01
                    basegfx::B2DLineJoin::NONE,
                    css::drawing::LineCap_BUTT,
@@ -215,8 +214,7 @@ void OutputDevice::DrawPolygon( const tools::Polygon& rPoly )
                aTransform,
                aB2DPolygon,
                0.0,
                // tdf#124848 hairline
                basegfx::B2DVector::getEmptyVector(),
                0.0, // tdf#124848 hairline
                nullptr, // MM01
                basegfx::B2DLineJoin::NONE,
                css::drawing::LineCap_BUTT,
@@ -326,8 +324,7 @@ void OutputDevice::ImplDrawPolyPolygonWithB2DPolyPolygon(const basegfx::B2DPolyP
                    aTransform,
                    rPolygon,
                    0.0,
                    // tdf#124848 hairline
                    basegfx::B2DVector::getEmptyVector(),
                    0.0, // tdf#124848 hairline
                    nullptr, // MM01
                    basegfx::B2DLineJoin::NONE,
                    css::drawing::LineCap_BUTT,
diff --git a/vcl/source/outdev/polyline.cxx b/vcl/source/outdev/polyline.cxx
index 3e2ed37..d17056d 100644
--- a/vcl/source/outdev/polyline.cxx
+++ b/vcl/source/outdev/polyline.cxx
@@ -71,8 +71,7 @@ void OutputDevice::DrawPolyLine( const tools::Polygon& rPoly )
        aTransform,
        aB2DPolyLine,
        0.0,
        // tdf#124848 hairline
        basegfx::B2DVector::getEmptyVector(),
        0.0, // tdf#124848 hairline
        nullptr, // MM01
        basegfx::B2DLineJoin::NONE,
        css::drawing::LineCap_BUTT,
@@ -348,8 +347,7 @@ bool OutputDevice::DrawPolyLineDirect(
            aTransform,
            rB2DPolygon,
            fTransparency,
            // tdf#124848 use LineWidth direct, do not try to solve for zero-case (aka hairline)
            basegfx::B2DVector(fLineWidth, fLineWidth),
            fLineWidth, // tdf#124848 use LineWidth direct, do not try to solve for zero-case (aka hairline)
            pStroke, // MM01
            eLineJoin,
            eLineCap,
diff --git a/vcl/source/outdev/textline.cxx b/vcl/source/outdev/textline.cxx
index a6eaa6b..6ade611 100644
--- a/vcl/source/outdev/textline.cxx
+++ b/vcl/source/outdev/textline.cxx
@@ -1004,7 +1004,6 @@ void OutputDevice::DrawWaveLine(const Point& rStartPos, const Point& rEndPos, lo
    const basegfx::B2DRectangle aWaveLineRectangle(nStartX, nStartY, nEndX, nEndY + nWaveHeight);
    const basegfx::B2DPolygon aWaveLinePolygon = basegfx::createWaveLinePolygon(aWaveLineRectangle);
    const basegfx::B2DHomMatrix aRotationMatrix = basegfx::utils::createRotateAroundPoint(nStartX, nStartY, basegfx::deg2rad(-fOrientation));
    const basegfx::B2DVector aLineWidth(nLineWidth, nLineWidth);
    const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);

    mpGraphics->SetLineColor(GetLineColor());
@@ -1012,7 +1011,7 @@ void OutputDevice::DrawWaveLine(const Point& rStartPos, const Point& rEndPos, lo
            aRotationMatrix,
            aWaveLinePolygon,
            0.0,
            aLineWidth,
            nLineWidth,
            nullptr, // MM01
            basegfx::B2DLineJoin::NONE,
            css::drawing::LineCap_BUTT,
diff --git a/vcl/source/outdev/transparent.cxx b/vcl/source/outdev/transparent.cxx
index 0cf325d..e35d4df 100644
--- a/vcl/source/outdev/transparent.cxx
+++ b/vcl/source/outdev/transparent.cxx
@@ -269,8 +269,7 @@ void OutputDevice::DrawTransparent(
                    aFullTransform,
                    rPolygon,
                    fTransparency,
                    // tdf#124848 hairline
                    basegfx::B2DVector::getEmptyVector(),
                    0.0, // tdf#124848 hairline
                    nullptr, // MM01
                    basegfx::B2DLineJoin::NONE,
                    css::drawing::LineCap_BUTT,
@@ -392,8 +391,7 @@ bool OutputDevice::DrawTransparentNatively ( const tools::PolyPolygon& rPolyPoly
                    aTransform,
                    rPolygon,
                    fTransparency,
                    // tdf#124848 hairline
                    basegfx::B2DVector::getEmptyVector(),
                    0.0, // tdf#124848 hairline
                    nullptr, // MM01
                    basegfx::B2DLineJoin::NONE,
                    css::drawing::LineCap_BUTT,
diff --git a/vcl/unx/generic/gdi/gdiimpl.cxx b/vcl/unx/generic/gdi/gdiimpl.cxx
index 99884a4..ee45815 100644
--- a/vcl/unx/generic/gdi/gdiimpl.cxx
+++ b/vcl/unx/generic/gdi/gdiimpl.cxx
@@ -1582,7 +1582,7 @@ private:

    // all other values the triangulation is based on and
    // need to be compared with to check for data validity
    basegfx::B2DVector                          maLineWidth;
    double                                      mfLineWidth;
    basegfx::B2DLineJoin                        meJoin;
    css::drawing::LineCap                       meCap;
    double                                      mfMiterMinimumAngle;
@@ -1592,7 +1592,7 @@ public:
    SystemDependentData_Triangulation(
        basegfx::SystemDependentDataManager& rSystemDependentDataManager,
        const basegfx::triangulator::B2DTriangleVector& rTriangles,
        const basegfx::B2DVector& rLineWidth,
        double fLineWidth,
        basegfx::B2DLineJoin eJoin,
        css::drawing::LineCap eCap,
        double fMiterMinimumAngle,
@@ -1600,7 +1600,7 @@ public:

    // read access
    const basegfx::triangulator::B2DTriangleVector& getTriangles() const { return maTriangles; }
    const basegfx::B2DVector& getLineWidth() const { return maLineWidth; }
    double getLineWidth() const { return mfLineWidth; }
    const basegfx::B2DLineJoin& getJoin() const { return meJoin; }
    const css::drawing::LineCap& getCap() const { return meCap; }
    double getMiterMinimumAngle() const { return mfMiterMinimumAngle; }
@@ -1614,14 +1614,14 @@ public:
SystemDependentData_Triangulation::SystemDependentData_Triangulation(
    basegfx::SystemDependentDataManager& rSystemDependentDataManager,
    const basegfx::triangulator::B2DTriangleVector& rTriangles,
    const basegfx::B2DVector& rLineWidth,
    double fLineWidth,
    basegfx::B2DLineJoin eJoin,
    css::drawing::LineCap eCap,
    double fMiterMinimumAngle,
    const std::vector< double >* pStroke)
:   basegfx::SystemDependentData(rSystemDependentDataManager),
    maTriangles(rTriangles),
    maLineWidth(rLineWidth),
    mfLineWidth(fLineWidth),
    meJoin(eJoin),
    meCap(eCap),
    mfMiterMinimumAngle(fMiterMinimumAngle),
@@ -1649,7 +1649,7 @@ bool X11SalGraphicsImpl::drawPolyLine(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolygon& rPolygon,
    double fTransparency,
    const basegfx::B2DVector& rLineWidth,
    double fLineWidth,
    const std::vector< double >* pStroke, // MM01
    basegfx::B2DLineJoin eLineJoin,
    css::drawing::LineCap eLineCap,
@@ -1663,7 +1663,6 @@ bool X11SalGraphicsImpl::drawPolyLine(
    }

    // need to check/handle LineWidth when ObjectToDevice transformation is used
    basegfx::B2DVector aLineWidth(rLineWidth);
    const bool bObjectToDeviceIsIdentity(rObjectToDevice.isIdentity());
    basegfx::B2DHomMatrix aObjectToDeviceInv;

@@ -1672,9 +1671,9 @@ bool X11SalGraphicsImpl::drawPolyLine(
    // the graphic sub-system, but the triangulation data is prepared
    // view-independent based on the logic LineWidth, so we need to
    // know it
    if(aLineWidth.equalZero())
    if(fLineWidth == 0)
    {
        aLineWidth = basegfx::B2DVector(1.0, 1.0);
        fLineWidth = 1.0;

        if(!bObjectToDeviceIsIdentity)
        {
@@ -1684,7 +1683,7 @@ bool X11SalGraphicsImpl::drawPolyLine(
                aObjectToDeviceInv.invert();
            }

            aLineWidth = aObjectToDeviceInv * aLineWidth;
            fLineWidth = (aObjectToDeviceInv * basegfx::B2DVector(fLineWidth, 0)).getLength();
        }
    }

@@ -1727,18 +1726,14 @@ bool X11SalGraphicsImpl::drawPolyLine(
    if(pSystemDependentData_Triangulation)
    {
        // check data validity (II)
        if(pSystemDependentData_Triangulation->getLineWidth() != aLineWidth)
        if(pSystemDependentData_Triangulation->getLineWidth() != fLineWidth)
        {
            // sometimes small inconsistencies, use a percentage tolerance
            const double fFactorX(basegfx::fTools::equalZero(aLineWidth.getX())
            const double fFactor(basegfx::fTools::equalZero(fLineWidth)
                ? 0.0
                : fabs(1.0 - (pSystemDependentData_Triangulation->getLineWidth().getX() / aLineWidth.getX())));
            const double fFactorY(basegfx::fTools::equalZero(aLineWidth.getY())
                ? 0.0
                : fabs(1.0 - (pSystemDependentData_Triangulation->getLineWidth().getY() / aLineWidth.getY())));

                : fabs(1.0 - (pSystemDependentData_Triangulation->getLineWidth() / fLineWidth)));
            // compare with 5.0% tolerance
            if(basegfx::fTools::more(fFactorX, 0.05) || basegfx::fTools::more(fFactorY, 0.05))
            if(basegfx::fTools::more(fFactor, 0.05))
            {
                // data invalid, forget
                pSystemDependentData_Triangulation.reset();
@@ -1803,7 +1798,7 @@ bool X11SalGraphicsImpl::drawPolyLine(
            // aTriangles data (!)
            basegfx::utils::createAreaGeometry(
                aPolyLine,
                0.5 * aLineWidth.getX(),
                0.5 * fLineWidth,
                eLineJoin,
                eLineCap,
                basegfx::deg2rad(12.5),
@@ -1820,7 +1815,7 @@ bool X11SalGraphicsImpl::drawPolyLine(
            pSystemDependentData_Triangulation = rPolygon.addOrReplaceSystemDependentData<SystemDependentData_Triangulation>(
                ImplGetSystemDependentDataManager(),
                aTriangles,
                aLineWidth,
                fLineWidth,
                eLineJoin,
                eLineCap,
                fMiterMinimumAngle,
diff --git a/vcl/unx/generic/gdi/gdiimpl.hxx b/vcl/unx/generic/gdi/gdiimpl.hxx
index 9a4940e..061993b 100644
--- a/vcl/unx/generic/gdi/gdiimpl.hxx
+++ b/vcl/unx/generic/gdi/gdiimpl.hxx
@@ -171,7 +171,7 @@ public:
                const basegfx::B2DHomMatrix& rObjectToDevice,
                const basegfx::B2DPolygon&,
                double fTransparency,
                const basegfx::B2DVector& rLineWidths,
                double fLineWidth,
                const std::vector< double >* pStroke, // MM01
                basegfx::B2DLineJoin,
                css::drawing::LineCap,
diff --git a/vcl/unx/generic/gdi/salgdi.cxx b/vcl/unx/generic/gdi/salgdi.cxx
index eeb6719..3ecbe01 100644
--- a/vcl/unx/generic/gdi/salgdi.cxx
+++ b/vcl/unx/generic/gdi/salgdi.cxx
@@ -702,7 +702,7 @@ bool X11SalGraphics::drawPolyLine(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolygon& rPolygon,
    double fTransparency,
    const basegfx::B2DVector& rLineWidth,
    double fLineWidth,
    const std::vector< double >* pStroke, // MM01
    basegfx::B2DLineJoin eLineJoin,
    css::drawing::LineCap eLineCap,
@@ -739,7 +739,7 @@ bool X11SalGraphics::drawPolyLine(
                rObjectToDevice,
                rPolygon,
                fTransparency,
                rLineWidth,
                fLineWidth,
                pStroke, // MM01
                eLineJoin,
                eLineCap,
@@ -759,7 +759,7 @@ bool X11SalGraphics::drawPolyLine(
        rObjectToDevice,
        rPolygon,
        fTransparency,
        rLineWidth,
        fLineWidth,
        pStroke, // MM01
        eLineJoin,
        eLineCap,
diff --git a/vcl/unx/generic/print/genpspgraphics.cxx b/vcl/unx/generic/print/genpspgraphics.cxx
index f9bb9880..8604462 100644
--- a/vcl/unx/generic/print/genpspgraphics.cxx
+++ b/vcl/unx/generic/print/genpspgraphics.cxx
@@ -432,7 +432,7 @@ bool GenPspGraphics::drawPolyLine(
    const basegfx::B2DHomMatrix& /* rObjectToDevice */,
    const basegfx::B2DPolygon&,
    double /*fTransparency*/,
    const basegfx::B2DVector& /*rLineWidths*/,
    double /*fLineWidth*/,
    const std::vector< double >* /*pStroke*/, // MM01
    basegfx::B2DLineJoin /*eJoin*/,
    css::drawing::LineCap /*eLineCap*/,
diff --git a/vcl/win/gdi/gdiimpl.cxx b/vcl/win/gdi/gdiimpl.cxx
index 08c5f78..b019692 100644
--- a/vcl/win/gdi/gdiimpl.cxx
+++ b/vcl/win/gdi/gdiimpl.cxx
@@ -2204,7 +2204,7 @@ bool WinSalGraphicsImpl::drawPolyLine(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolygon& rPolygon,
    double fTransparency,
    const basegfx::B2DVector& rLineWidth,
    double fLineWidth,
    const std::vector< double >* pStroke, // MM01
    basegfx::B2DLineJoin eLineJoin,
    css::drawing::LineCap eLineCap,
@@ -2218,29 +2218,28 @@ bool WinSalGraphicsImpl::drawPolyLine(
    }

    // need to check/handle LineWidth when ObjectToDevice transformation is used
    basegfx::B2DVector aLineWidth(rLineWidth);
    const bool bObjectToDeviceIsIdentity(rObjectToDevice.isIdentity());
    const bool bIsHairline(aLineWidth.equalZero());
    const bool bIsHairline(fLineWidth == 0);

    // tdf#124848 calculate-back logical LineWidth for a hairline
    // since this implementation hands over the transformation to
    // the graphic sub-system
    if(bIsHairline)
    {
        aLineWidth = basegfx::B2DVector(1.0, 1.0);
        fLineWidth = 1.0;

        if(!bObjectToDeviceIsIdentity)
        {
            basegfx::B2DHomMatrix aObjectToDeviceInv(rObjectToDevice);
            aObjectToDeviceInv.invert();
            aLineWidth = aObjectToDeviceInv * aLineWidth;
            fLineWidth = (aObjectToDeviceInv * basegfx::B2DVector(fLineWidth, 0)).getLength();
        }
    }

    Gdiplus::Graphics aGraphics(mrParent.getHDC());
    const sal_uInt8 aTrans = static_cast<sal_uInt8>(basegfx::fround( 255 * (1.0 - fTransparency) ));
    const Gdiplus::Color aTestColor(aTrans, maLineColor.GetRed(), maLineColor.GetGreen(), maLineColor.GetBlue());
    Gdiplus::Pen aPen(aTestColor.GetValue(), Gdiplus::REAL(aLineWidth.getX()));
    Gdiplus::Pen aPen(aTestColor.GetValue(), Gdiplus::REAL(fLineWidth));
    bool bNoLineJoin(false);

    // Set full (Object-to-Device) transformation - if used
@@ -2340,7 +2339,7 @@ bool WinSalGraphicsImpl::drawPolyLine(
        // the back-calculated logical linewidth is already here, just use it.
        // Still be careful - a zero LineWidth *should* not happen, but...
        std::vector<Gdiplus::REAL> aDashArray(pStroke->size());
        const double fFactor(aLineWidth.equalZero() ? 1.0 : 1.0 / aLineWidth.getX());
        const double fFactor(fLineWidth == 0 ? 1.0 : 1.0 / fLineWidth);

        for(size_t a(0); a < pStroke->size(); a++)
        {
diff --git a/vcl/win/gdi/gdiimpl.hxx b/vcl/win/gdi/gdiimpl.hxx
index eb56388..748afbc 100644
--- a/vcl/win/gdi/gdiimpl.hxx
+++ b/vcl/win/gdi/gdiimpl.hxx
@@ -129,7 +129,7 @@ public:
                const basegfx::B2DHomMatrix& rObjectToDevice,
                const basegfx::B2DPolygon&,
                double fTransparency,
                const basegfx::B2DVector& rLineWidths,
                double fLineWidth,
                const std::vector< double >* pStroke, // MM01
                basegfx::B2DLineJoin,
                css::drawing::LineCap,
diff --git a/vcl/win/gdi/salgdi_gdiplus.cxx b/vcl/win/gdi/salgdi_gdiplus.cxx
index ce2f2cc..f56a227 100644
--- a/vcl/win/gdi/salgdi_gdiplus.cxx
+++ b/vcl/win/gdi/salgdi_gdiplus.cxx
@@ -41,7 +41,7 @@ bool WinSalGraphics::drawPolyLine(
    const basegfx::B2DHomMatrix& rObjectToDevice,
    const basegfx::B2DPolygon& rPolygon,
    double fTransparency,
    const basegfx::B2DVector& rLineWidths,
    double fLineWidth,
    const std::vector< double >* pStroke, // MM01
    basegfx::B2DLineJoin eLineJoin,
    css::drawing::LineCap eLineCap,
@@ -52,7 +52,7 @@ bool WinSalGraphics::drawPolyLine(
        rObjectToDevice,
        rPolygon,
        fTransparency,
        rLineWidths,
        fLineWidth,
        pStroke, // MM01
        eLineJoin,
        eLineCap,