[Libreoffice-commits] core.git: vcl/qa vcl/skia

Luboš Luňák (via logerrit) logerrit at kemper.freedesktop.org
Fri Oct 2 05:26:12 UTC 2020


 vcl/qa/cppunit/skia/skia.cxx |  111 ++++++++++++++++++++++++++++++++++++++++++-
 vcl/skia/gdiimpl.cxx         |   30 +++++------
 2 files changed, 125 insertions(+), 16 deletions(-)

New commits:
commit c1206e12eab237ffa7dde728da5bf1883a05ddb0
Author:     Luboš Luňák <l.lunak at collabora.com>
AuthorDate: Wed Sep 30 18:39:39 2020 +0200
Commit:     Luboš Luňák <l.lunak at collabora.com>
CommitDate: Fri Oct 2 07:25:26 2020 +0200

    SkCanvas::drawPaint() -> drawRect(), where applicable, and fix matrix
    
    It makes the code a bit simpler. Also fix the matrix used
    in SkiaSalGraphicsImpl::drawShader(), plus add asserts to verify
    it's correct.
    
    Change-Id: Ia6692ad90e822e6e44028b280dc2faaac06959a5
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/103743
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lunak at collabora.com>

diff --git a/vcl/qa/cppunit/skia/skia.cxx b/vcl/qa/cppunit/skia/skia.cxx
index 07f5a872ddbb..28591d807bb1 100644
--- a/vcl/qa/cppunit/skia/skia.cxx
+++ b/vcl/qa/cppunit/skia/skia.cxx
@@ -11,7 +11,11 @@
 
 #include <vcl/skia/SkiaHelper.hxx>
 #include <skia/salbmp.hxx>
-#include <vcl/bitmapaccess.hxx>
+#include <bitmapwriteaccess.hxx>
+#include <vcl/virdev.hxx>
+#include <tools/stream.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
 
 // This tests backends that use Skia (i.e. intentionally not the svp one, which is the default.)
 // Note that you still may need to actually set for Skia to be used (see vcl/README.vars).
@@ -27,10 +31,29 @@ public:
     }
 
     void testBitmapErase();
+    void testDrawShaders();
 
     CPPUNIT_TEST_SUITE(SkiaTest);
     CPPUNIT_TEST(testBitmapErase);
+    CPPUNIT_TEST(testDrawShaders);
     CPPUNIT_TEST_SUITE_END();
+
+private:
+#if 0
+    template <class BitmapT> // handle both Bitmap and BitmapEx
+    void savePNG(const OUString& sWhere, const BitmapT& rBmp)
+    {
+        SvFileStream aStream(sWhere, StreamMode::WRITE | StreamMode::TRUNC);
+        GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+        rFilter.compressAsPNG(BitmapEx(rBmp), aStream);
+    }
+    void savePNG(const OUString& sWhere, const ScopedVclPtr<VirtualDevice>& device)
+    {
+        SvFileStream aStream(sWhere, StreamMode::WRITE | StreamMode::TRUNC);
+        GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+        rFilter.compressAsPNG(device->GetBitmapEx(Point(), device->GetOutputSizePixel()), aStream);
+    }
+#endif
 };
 
 void SkiaTest::testBitmapErase()
@@ -62,6 +85,92 @@ void SkiaTest::testBitmapErase()
     CPPUNIT_ASSERT(!skiaBitmap->unittestHasEraseColor());
 }
 
+// Test that draw calls that internally result in SkShader calls work properly.
+void SkiaTest::testDrawShaders()
+{
+    if (!SkiaHelper::isVCLSkiaEnabled())
+        return;
+    ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT);
+    device->SetOutputSizePixel(Size(20, 20));
+    device->SetBackground(Wallpaper(COL_WHITE));
+    device->Erase();
+    Bitmap bitmap(Size(10, 10), 24);
+    bitmap.Erase(COL_RED);
+    SkiaSalBitmap* skiaBitmap = dynamic_cast<SkiaSalBitmap*>(bitmap.ImplGetSalBitmap().get());
+    CPPUNIT_ASSERT(skiaBitmap);
+    CPPUNIT_ASSERT(skiaBitmap->PreferSkShader());
+    AlphaMask alpha(Size(10, 10));
+    alpha.Erase(64);
+    SkiaSalBitmap* skiaAlpha = dynamic_cast<SkiaSalBitmap*>(alpha.ImplGetSalBitmap().get());
+    CPPUNIT_ASSERT(skiaAlpha);
+    CPPUNIT_ASSERT(skiaAlpha->PreferSkShader());
+
+    device->DrawBitmap(Point(5, 5), bitmap);
+    //savePNG("/tmp/a1.png", device);
+    // Check that the area is painted, but nothing else.
+    CPPUNIT_ASSERT_EQUAL(COL_WHITE, device->GetPixel(Point(0, 0)));
+    CPPUNIT_ASSERT_EQUAL(COL_RED, device->GetPixel(Point(5, 5)));
+    CPPUNIT_ASSERT_EQUAL(COL_RED, device->GetPixel(Point(14, 14)));
+    CPPUNIT_ASSERT_EQUAL(COL_WHITE, device->GetPixel(Point(15, 15)));
+    device->Erase();
+
+    device->DrawBitmapEx(Point(5, 5), BitmapEx(bitmap, alpha));
+    //savePNG("/tmp/a2.png", device);
+    Color resultRed(COL_RED.GetRed() * 3 / 4 + 64, 64, 64); // 3/4 red, 1/4 white
+    CPPUNIT_ASSERT_EQUAL(COL_WHITE, device->GetPixel(Point(0, 0)));
+    CPPUNIT_ASSERT_EQUAL(resultRed, device->GetPixel(Point(5, 5)));
+    CPPUNIT_ASSERT_EQUAL(resultRed, device->GetPixel(Point(14, 14)));
+    CPPUNIT_ASSERT_EQUAL(COL_WHITE, device->GetPixel(Point(15, 15)));
+    device->Erase();
+
+    basegfx::B2DHomMatrix matrix;
+    matrix.scale(10, 10);
+    matrix.rotate(M_PI / 4);
+    device->DrawTransformedBitmapEx(matrix, BitmapEx(bitmap, alpha));
+    //savePNG("/tmp/a3.png", device);
+    CPPUNIT_ASSERT_EQUAL(resultRed, device->GetPixel(Point(0, 1)));
+    CPPUNIT_ASSERT_EQUAL(COL_WHITE, device->GetPixel(Point(1, 0)));
+    CPPUNIT_ASSERT_EQUAL(resultRed, device->GetPixel(Point(0, 10)));
+    CPPUNIT_ASSERT_EQUAL(COL_WHITE, device->GetPixel(Point(10, 10)));
+    device->Erase();
+
+    // Test with scaling. Use everything 10x larger to reduce the impact of smoothscaling.
+    ScopedVclPtr<VirtualDevice> deviceLarge = VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT);
+    deviceLarge->SetOutputSizePixel(Size(200, 200));
+    deviceLarge->SetBackground(Wallpaper(COL_WHITE));
+    deviceLarge->Erase();
+    Bitmap bitmapLarge(Size(100, 100), 24);
+    bitmapLarge.Erase(COL_RED);
+    SkiaSalBitmap* skiaBitmapLarge
+        = dynamic_cast<SkiaSalBitmap*>(bitmapLarge.ImplGetSalBitmap().get());
+    CPPUNIT_ASSERT(skiaBitmapLarge);
+    CPPUNIT_ASSERT(skiaBitmapLarge->PreferSkShader());
+    AlphaMask alphaLarge(Size(100, 100));
+    alphaLarge.Erase(64);
+    {
+        BitmapWriteAccess access(bitmapLarge);
+        access.SetFillColor(COL_BLUE);
+        access.FillRect(tools::Rectangle(Point(20, 40), Size(10, 10)));
+    }
+    // Using alpha will still lead to shaders being used.
+    deviceLarge->DrawBitmapEx(Point(50, 50), Size(60, 60), Point(20, 20), Size(30, 30),
+                              BitmapEx(bitmapLarge, alphaLarge));
+    //savePNG("/tmp/a4.png", deviceLarge);
+    Color resultBlue(64, 64, COL_BLUE.GetBlue() * 3 / 4 + 64); // 3/4 blue, 1/4 white
+    CPPUNIT_ASSERT_EQUAL(COL_WHITE, deviceLarge->GetPixel(Point(40, 40)));
+    CPPUNIT_ASSERT_EQUAL(resultRed, deviceLarge->GetPixel(Point(50, 50)));
+    CPPUNIT_ASSERT_EQUAL(resultRed, deviceLarge->GetPixel(Point(100, 100)));
+    CPPUNIT_ASSERT_EQUAL(COL_WHITE, deviceLarge->GetPixel(Point(110, 110)));
+    // Don't test colors near the edge between the colors, smoothscaling affects them.
+    const int diff = 3;
+    CPPUNIT_ASSERT_EQUAL(resultRed, deviceLarge->GetPixel(Point(50, 89 - diff)));
+    CPPUNIT_ASSERT_EQUAL(resultBlue, deviceLarge->GetPixel(Point(50, 90 + diff)));
+    CPPUNIT_ASSERT_EQUAL(resultBlue, deviceLarge->GetPixel(Point(69 - diff, 100)));
+    CPPUNIT_ASSERT_EQUAL(resultRed, deviceLarge->GetPixel(Point(70 + diff, 100)));
+    CPPUNIT_ASSERT_EQUAL(resultBlue, deviceLarge->GetPixel(Point(50, 100)));
+    device->Erase();
+}
+
 } // namespace
 
 CPPUNIT_TEST_SUITE_REGISTRATION(SkiaTest);
diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx
index ade34a6e8b01..b6ffda221424 100644
--- a/vcl/skia/gdiimpl.cxx
+++ b/vcl/skia/gdiimpl.cxx
@@ -1655,17 +1655,21 @@ void SkiaSalGraphicsImpl::drawShader(const SalTwoRect& rPosAry, const sk_sp<SkSh
     if (rPosAry.mnSrcWidth != rPosAry.mnDestWidth || rPosAry.mnSrcHeight != rPosAry.mnDestHeight)
         paint.setFilterQuality(kHigh_SkFilterQuality);
     SkCanvas* canvas = getDrawCanvas();
-    // SkCanvas::drawShader() cannot do rectangles, so clip to destination and use a matrix
-    // to map from source.
-    SkMatrix matrix;
+    // Scaling needs to be done explicitly using a matrix.
     SkAutoCanvasRestore autoRestore(canvas, true);
-    canvas->clipRect(destinationRect);
-    matrix.set(SkMatrix::kMScaleX, 1.0 * rPosAry.mnDestWidth / rPosAry.mnSrcWidth);
-    matrix.set(SkMatrix::kMScaleY, 1.0 * rPosAry.mnDestHeight / rPosAry.mnSrcHeight);
-    matrix.set(SkMatrix::kMTransX, rPosAry.mnDestX - rPosAry.mnSrcX);
-    matrix.set(SkMatrix::kMTransY, rPosAry.mnDestY - rPosAry.mnSrcY);
+    SkMatrix matrix = SkMatrix::Translate(rPosAry.mnDestX, rPosAry.mnDestY)
+                      * SkMatrix::Scale(1.0 * rPosAry.mnDestWidth / rPosAry.mnSrcWidth,
+                                        1.0 * rPosAry.mnDestHeight / rPosAry.mnSrcHeight)
+                      * SkMatrix::Translate(-rPosAry.mnSrcX, -rPosAry.mnSrcY);
+    assert(matrix.mapXY(rPosAry.mnSrcX, rPosAry.mnSrcY)
+           == SkPoint::Make(rPosAry.mnDestX, rPosAry.mnDestY));
+    assert(matrix.mapXY(rPosAry.mnSrcX + rPosAry.mnSrcWidth, rPosAry.mnSrcY + rPosAry.mnSrcHeight)
+           == SkPoint::Make(rPosAry.mnDestX + rPosAry.mnDestWidth,
+                            rPosAry.mnDestY + rPosAry.mnDestHeight));
     canvas->concat(matrix);
-    canvas->drawPaint(paint);
+    SkRect sourceRect
+        = SkRect::MakeXYWH(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight);
+    canvas->drawRect(sourceRect, paint);
     postDraw();
 }
 
@@ -1737,19 +1741,15 @@ bool SkiaSalGraphicsImpl::drawTransformedBitmap(const basegfx::B2DPoint& rNull,
         paint.setFilterQuality(kHigh_SkFilterQuality);
         if (pSkiaAlphaBitmap)
         {
-            // SkCanvas::drawPaint() cannot do rectangles, so clip (is transformed by the matrix too).
-            canvas->clipRect(SkRect::MakeWH(aSize.Width(), aSize.Height()));
             paint.setShader(SkShaders::Blend(SkBlendMode::kDstOut, // VCL alpha is one-minus-alpha.
                                              rSkiaBitmap.GetSkShader(),
                                              pSkiaAlphaBitmap->GetAlphaSkShader()));
-            canvas->drawPaint(paint);
+            canvas->drawRect(SkRect::MakeWH(aSize.Width(), aSize.Height()), paint);
         }
         else if (rSkiaBitmap.PreferSkShader())
         {
-            // SkCanvas::drawPaint() cannot do rectangles, so clip (is transformed by the matrix too).
-            canvas->clipRect(SkRect::MakeWH(aSize.Width(), aSize.Height()));
             paint.setShader(rSkiaBitmap.GetSkShader());
-            canvas->drawPaint(paint);
+            canvas->drawRect(SkRect::MakeWH(aSize.Width(), aSize.Height()), paint);
         }
         else
         {


More information about the Libreoffice-commits mailing list