vcl/headless/CairoCommon.cxx |    5 ++--
 vcl/qa/cppunit/outdev.cxx    |   53 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 56 insertions(+), 2 deletions(-)

New commits:
commit 99d7bbf367eeb85055853d0d03ec46b2d6bbebe3
Author:     Noel Grandin <[email protected]>
AuthorDate: Thu Oct 16 11:01:47 2025 +0200
Commit:     Noel Grandin <[email protected]>
CommitDate: Sat Oct 18 08:37:12 2025 +0200

    tdf#168730 fix DrawRect with alpha
    
    Where we were merging the fill color with the background
    instead of overwriting it.
    
    regression from
      commit 088a7c7c451321a800ca8d3523a18b6bb93239b7
      Author: Noel Grandin <[email protected]>
      Date:   Tue Sep 24 16:18:11 2024 +0200
      remove alpha device from OutputDevice
    
    And add some unit tests for this and for DrawPolyPolygon.
    Verified both the new unit tests on a build from
    before the regression commit.
    
    Change-Id: Ia27e075d8be71fd35a2d6ef6b08e8f42b36a7776
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192482
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <[email protected]>

diff --git a/vcl/headless/CairoCommon.cxx b/vcl/headless/CairoCommon.cxx
index a9bdff539014..b2a6b13d31f8 100644
--- a/vcl/headless/CairoCommon.cxx
+++ b/vcl/headless/CairoCommon.cxx
@@ -629,6 +629,7 @@ cairo_t* CairoCommon::createTmpCompatibleCairoContext() 
const
 
 void CairoCommon::applyColor(cairo_t* cr, Color aColor, double fTransparency)
 {
+    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
     if (cairo_surface_get_content(cairo_get_target(cr)) != CAIRO_CONTENT_ALPHA)
     {
         cairo_set_source_rgba(cr, aColor.GetRed() / 255.0, aColor.GetGreen() / 
255.0,
@@ -638,7 +639,6 @@ void CairoCommon::applyColor(cairo_t* cr, Color aColor, 
double fTransparency)
     {
         double fSet = aColor == COL_BLACK ? 1.0 : 0.0;
         cairo_set_source_rgba(cr, 1, 1, 1, fSet);
-        cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
     }
 }
 
@@ -880,7 +880,8 @@ void CairoCommon::drawRect(double nX, double nY, double 
nWidth, double nHeight,
             basegfx::B2DRectangle(nX, nY, nX + nWidth, nY + nHeight));
 
         m_oFillColor = aOrigFillColor;
-        drawPolyPolygon(basegfx::B2DHomMatrix(), 
basegfx::B2DPolyPolygon(aRect), 0.0, bAntiAlias);
+        drawPolyPolygon(basegfx::B2DHomMatrix(), 
basegfx::B2DPolyPolygon(aRect),
+                        (255 - aOrigFillColor->GetAlpha()) / 255.0, 
bAntiAlias);
         m_oFillColor = std::nullopt;
     }
 
diff --git a/vcl/qa/cppunit/outdev.cxx b/vcl/qa/cppunit/outdev.cxx
index 1f1652174580..45a2a100e70a 100644
--- a/vcl/qa/cppunit/outdev.cxx
+++ b/vcl/qa/cppunit/outdev.cxx
@@ -1847,6 +1847,59 @@ CPPUNIT_TEST_FIXTURE(VclOutdevTest, testDrawPolyPolygon)
     }
 }
 
+// Test and document existing behaviour
+CPPUNIT_TEST_FIXTURE(VclOutdevTest, testDrawPolyPolygonAlpha)
+{
+    Size aSize(100, 100);
+    ScopedVclPtrInstance<VirtualDevice> pVDev(DeviceFormat::WITH_ALPHA);
+    pVDev->SetOutputSizePixel(aSize);
+
+    // create a square polypolygon
+    tools::Polygon aPolygon(4);
+    aPolygon.SetPoint(Point(0, 0), 0);
+    aPolygon.SetPoint(Point(0, 100), 1);
+    aPolygon.SetPoint(Point(100, 100), 2);
+    aPolygon.SetPoint(Point(0, 0), 3);
+    tools::PolyPolygon aPolyPolygon(aPolygon);
+    aPolyPolygon.Optimize(PolyOptimizeFlags::CLOSE);
+    basegfx::B2DPolyPolygon aB2DPolyPolygon(aPolyPolygon.getB2DPolyPolygon());
+
+    Color aLineColor(ColorAlpha, 255, 255, 0, 0); // opaque red
+    Color aFillColor(ColorAlpha, 127, 255, 0, 0); // 50% transparent red
+    pVDev->SetLineColor(aLineColor);
+    pVDev->SetFillColor(aFillColor);
+    pVDev->DrawPolyPolygon(aB2DPolyPolygon);
+
+    CPPUNIT_ASSERT_EQUAL(aLineColor, pVDev->GetPixel(Point(0, 0)));
+    // existing behaviour - we drop the alpha when we fill a polypolygon
+    CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 255, 255, 0, 0), 
pVDev->GetPixel(Point(1, 1)));
+}
+
+// Test and document existing behaviour
+CPPUNIT_TEST_FIXTURE(VclOutdevTest, testDrawRectAlpha)
+{
+    Size aSize(100, 100);
+    ScopedVclPtrInstance<VirtualDevice> pVDev(DeviceFormat::WITH_ALPHA);
+    pVDev->SetOutputSizePixel(aSize);
+
+    const Color RED_OPAQUE(ColorAlpha, 255, 255, 0, 0); // opaque red
+    const Color RED_TRANSPARENT(ColorAlpha, 127, 255, 0, 0); // 50% 
transparent red
+    pVDev->SetLineColor(RED_OPAQUE);
+    pVDev->SetFillColor(RED_TRANSPARENT);
+    pVDev->DrawRect(tools::Rectangle(0, 0, 100, 100));
+
+    CPPUNIT_ASSERT_EQUAL(RED_OPAQUE, pVDev->GetPixel(Point(0, 0)));
+    CPPUNIT_ASSERT_EQUAL(RED_TRANSPARENT, pVDev->GetPixel(Point(1, 1)));
+
+    pVDev->SetLineColor(RED_TRANSPARENT);
+    pVDev->SetFillColor(RED_OPAQUE);
+    pVDev->DrawRect(tools::Rectangle(0, 0, 100, 100));
+
+    // existing behaviour - we drop the alpha when we draw the line for a rect
+    CPPUNIT_ASSERT_EQUAL(RED_OPAQUE, pVDev->GetPixel(Point(0, 0)));
+    CPPUNIT_ASSERT_EQUAL(RED_OPAQUE, pVDev->GetPixel(Point(1, 1)));
+}
+
 static size_t ClipGradientTest(GDIMetaFile& rMtf, size_t nIndex)
 {
     MetaAction* pAction = rMtf.GetAction(nIndex);

Reply via email to