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 <[email protected]> AuthorDate: Wed Sep 30 18:39:39 2020 +0200 Commit: Luboš Luňák <[email protected]> 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 <[email protected]> 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 { _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
