vcl/qa/cppunit/skia/skia.cxx |   23 +++++++++++++++++
 vcl/skia/gdiimpl.cxx         |   57 ++++++++++++++++++++++++++++++-------------
 2 files changed, 64 insertions(+), 16 deletions(-)

New commits:
commit 5b292e878703ebc32e875406f4116cba145a9042
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Wed Nov 18 16:30:52 2020 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Nov 19 21:45:14 2020 +0100

    avoid Skia floating point position fixups for rectangles (tdf#137329)
    
    Avoid to toSkX()/toSkY() tweaks for rectangular areas, so with AA
    enabled it leads to fuzzy edges, and rectangles should line up
    perfectly with all pixels even without tweaks.
    
    Change-Id: I45bf5a57a9f2d941eb7ec224992fc452481a2f98
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/106060
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/vcl/qa/cppunit/skia/skia.cxx b/vcl/qa/cppunit/skia/skia.cxx
index d13e1530f95e..1f04c5825584 100644
--- a/vcl/qa/cppunit/skia/skia.cxx
+++ b/vcl/qa/cppunit/skia/skia.cxx
@@ -35,6 +35,7 @@ public:
     void testInterpretAs8Bit();
     void testAlphaBlendWith();
     void testBitmapCopyOnWrite();
+    void testTdf137329();
 
     CPPUNIT_TEST_SUITE(SkiaTest);
     CPPUNIT_TEST(testBitmapErase);
@@ -42,6 +43,7 @@ public:
     CPPUNIT_TEST(testInterpretAs8Bit);
     CPPUNIT_TEST(testAlphaBlendWith);
     CPPUNIT_TEST(testBitmapCopyOnWrite);
+    CPPUNIT_TEST(testTdf137329);
     CPPUNIT_TEST_SUITE_END();
 
 private:
@@ -303,6 +305,27 @@ void SkiaTest::testBitmapCopyOnWrite()
     CPPUNIT_ASSERT(bitmap.unittestGetAlphaImage() != oldAlphaImage);
 }
 
+void SkiaTest::testTdf137329()
+{
+    // Draw a filled polygon in the entire device, with AA enabled.
+    // All pixels in the device should be black, even those at edges (i.e. not 
affected by AA).
+    ScopedVclPtr<VirtualDevice> device = 
VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT);
+    device->SetOutputSizePixel(Size(10, 10));
+    device->SetBackground(Wallpaper(COL_WHITE));
+    device->SetAntialiasing(AntialiasingFlags::Enable);
+    device->Erase();
+    device->SetLineColor();
+    device->SetFillColor(COL_BLACK);
+    device->DrawPolyPolygon(
+        basegfx::B2DPolyPolygon(basegfx::B2DPolygon{ { 0, 0 }, { 10, 0 }, { 
10, 10 }, { 0, 10 } }));
+    // savePNG("/tmp/tdf137329.png", device);
+    CPPUNIT_ASSERT_EQUAL(COL_BLACK, device->GetPixel(Point(0, 0)));
+    CPPUNIT_ASSERT_EQUAL(COL_BLACK, device->GetPixel(Point(9, 0)));
+    CPPUNIT_ASSERT_EQUAL(COL_BLACK, device->GetPixel(Point(9, 9)));
+    CPPUNIT_ASSERT_EQUAL(COL_BLACK, device->GetPixel(Point(0, 9)));
+    CPPUNIT_ASSERT_EQUAL(COL_BLACK, device->GetPixel(Point(4, 4)));
+}
+
 } // namespace
 
 CPPUNIT_TEST_SUITE_REGISTRATION(SkiaTest);
diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx
index 1f1c4002f94d..c7035e7d3de7 100644
--- a/vcl/skia/gdiimpl.cxx
+++ b/vcl/skia/gdiimpl.cxx
@@ -53,7 +53,8 @@ namespace
 // bottom-most line of pixels of the bounding rectangle (see
 // 
https://lists.freedesktop.org/archives/libreoffice/2019-November/083709.html).
 // So be careful with rectangle->polygon conversions (generally avoid them).
-void addPolygonToPath(const basegfx::B2DPolygon& rPolygon, SkPath& rPath)
+void addPolygonToPath(const basegfx::B2DPolygon& rPolygon, SkPath& rPath,
+                      bool* hasOnlyOrthogonal = nullptr)
 {
     const sal_uInt32 nPointCount(rPolygon.count());
 
@@ -88,6 +89,11 @@ void addPolygonToPath(const basegfx::B2DPolygon& rPolygon, 
SkPath& rPath)
         else if (!bHasCurves)
         {
             rPath.lineTo(aCurrentPoint.getX(), aCurrentPoint.getY());
+            // If asked for, check whether the polygon has a line that is not
+            // strictly horizontal or vertical.
+            if (hasOnlyOrthogonal != nullptr && aCurrentPoint.getX() != 
aPreviousPoint.getX()
+                && aCurrentPoint.getY() != aPreviousPoint.getY())
+                *hasOnlyOrthogonal = false;
         }
         else
         {
@@ -96,7 +102,12 @@ void addPolygonToPath(const basegfx::B2DPolygon& rPolygon, 
SkPath& rPath)
 
             if (aPreviousControlPoint.equal(aPreviousPoint)
                 && aCurrentControlPoint.equal(aCurrentPoint))
+            {
                 rPath.lineTo(aCurrentPoint.getX(), aCurrentPoint.getY()); // a 
straight line
+                if (hasOnlyOrthogonal != nullptr && aCurrentPoint.getX() != 
aPreviousPoint.getX()
+                    && aCurrentPoint.getY() != aPreviousPoint.getY())
+                    *hasOnlyOrthogonal = false;
+            }
             else
             {
                 if (aPreviousControlPoint.equal(aPreviousPoint))
@@ -112,6 +123,8 @@ void addPolygonToPath(const basegfx::B2DPolygon& rPolygon, 
SkPath& rPath)
                 rPath.cubicTo(aPreviousControlPoint.getX(), 
aPreviousControlPoint.getY(),
                               aCurrentControlPoint.getX(), 
aCurrentControlPoint.getY(),
                               aCurrentPoint.getX(), aCurrentPoint.getY());
+                if (hasOnlyOrthogonal != nullptr)
+                    *hasOnlyOrthogonal = false;
             }
         }
         aPreviousPoint = aCurrentPoint;
@@ -123,7 +136,8 @@ void addPolygonToPath(const basegfx::B2DPolygon& rPolygon, 
SkPath& rPath)
     }
 }
 
-void addPolyPolygonToPath(const basegfx::B2DPolyPolygon& rPolyPolygon, SkPath& 
rPath)
+void addPolyPolygonToPath(const basegfx::B2DPolyPolygon& rPolyPolygon, SkPath& 
rPath,
+                          bool* hasOnlyOrthogonal = nullptr)
 {
     const sal_uInt32 nPolygonCount(rPolyPolygon.count());
 
@@ -132,7 +146,7 @@ void addPolyPolygonToPath(const basegfx::B2DPolyPolygon& 
rPolyPolygon, SkPath& r
 
     for (const auto& rPolygon : rPolyPolygon)
     {
-        addPolygonToPath(rPolygon, rPath);
+        addPolygonToPath(rPolygon, rPath, hasOnlyOrthogonal);
     }
 }
 
@@ -852,36 +866,47 @@ void SkiaSalGraphicsImpl::performDrawPolyPolygon(const 
basegfx::B2DPolyPolygon&
     preDraw();
 
     SkPath polygonPath;
-    addPolyPolygonToPath(aPolyPolygon, polygonPath);
+    bool hasOnlyOrthogonal = true;
+    addPolyPolygonToPath(aPolyPolygon, polygonPath, &hasOnlyOrthogonal);
     polygonPath.setFillType(SkPathFillType::kEvenOdd);
     addUpdateRegion(polygonPath.getBounds());
 
     SkPaint aPaint;
     aPaint.setAntiAlias(useAA);
-    // We normally use pixel at their center positions, but slightly off (see 
toSkX/Y()).
-    // With AA lines that "slightly off" causes tiny changes of color, making 
some tests
-    // fail. Since moving AA-ed line slightly to a side doesn't cause any real 
visual
-    // difference, just place exactly at the center. tdf#134346
-    const SkScalar posFix = useAA ? toSkXYFix : 0;
+
+    // For lines we use toSkX()/toSkY() in order to pass centers of pixels to 
Skia,
+    // as that leads to better results with floating-point coordinates
+    // (e.g. https://bugs.chromium.org/p/skia/issues/detail?id=9611).
+    // But that means that we generally need to use it also for areas, so that 
they
+    // line up properly if used together (tdf#134346).
+    // On the other hand, with AA enabled and rectangular areas, this leads to 
fuzzy
+    // edges (tdf#137329). But since rectangular areas line up perfectly to 
pixels
+    // everywhere, it shouldn't be necessary to do this for them.
+    // So if AA is enabled, avoid this fixup for rectangular areas.
+    if (!useAA || !hasOnlyOrthogonal)
+    {
+        // We normally use pixel at their center positions, but slightly off 
(see toSkX/Y()).
+        // With AA lines that "slightly off" causes tiny changes of color, 
making some tests
+        // fail. Since moving AA-ed line slightly to a side doesn't cause any 
real visual
+        // difference, just place exactly at the center. tdf#134346
+        const SkScalar posFix = useAA ? toSkXYFix : 0;
+        polygonPath.offset(toSkX(0) + posFix, toSkY(0) + posFix, nullptr);
+    }
     if (mFillColor != SALCOLOR_NONE)
     {
-        SkPath path;
-        polygonPath.offset(toSkX(0) + posFix, toSkY(0) + posFix, &path);
         aPaint.setColor(toSkColorWithTransparency(mFillColor, fTransparency));
         aPaint.setStyle(SkPaint::kFill_Style);
         // HACK: If the polygon is just a line, it still should be drawn. But 
when filling
         // Skia doesn't draw empty polygons, so in that case ensure the line 
is drawn.
-        if (mLineColor == SALCOLOR_NONE && path.getBounds().isEmpty())
+        if (mLineColor == SALCOLOR_NONE && polygonPath.getBounds().isEmpty())
             aPaint.setStyle(SkPaint::kStroke_Style);
-        getDrawCanvas()->drawPath(path, aPaint);
+        getDrawCanvas()->drawPath(polygonPath, aPaint);
     }
     if (mLineColor != SALCOLOR_NONE)
     {
-        SkPath path;
-        polygonPath.offset(toSkX(0) + posFix, toSkY(0) + posFix, &path);
         aPaint.setColor(toSkColorWithTransparency(mLineColor, fTransparency));
         aPaint.setStyle(SkPaint::kStroke_Style);
-        getDrawCanvas()->drawPath(path, aPaint);
+        getDrawCanvas()->drawPath(polygonPath, aPaint);
     }
     postDraw();
 #if defined LINUX
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to