[Libreoffice-commits] core.git: vcl/inc vcl/qa vcl/skia
LuboÅ¡ LuÅák (via logerrit)
logerrit at kemper.freedesktop.org
Fri Sep 4 15:30:15 UTC 2020
vcl/inc/skia/salbmp.hxx | 4 +++
vcl/qa/cppunit/BackendTest.cxx | 53 +++++++++++++++++++++++++++++++++++++++++
vcl/skia/gdiimpl.cxx | 38 ++++++++++++++++++++++-------
vcl/skia/salbmp.cxx | 11 ++++++++
4 files changed, 97 insertions(+), 9 deletions(-)
New commits:
commit e6f54c46dcc0dd9b308d07bd25cdab231e5a335e
Author: Luboš Luňák <l.lunak at collabora.com>
AuthorDate: Tue Sep 1 22:21:43 2020 +0200
Commit: Luboš Luňák <l.lunak at collabora.com>
CommitDate: Fri Sep 4 17:29:27 2020 +0200
ignore fully opaque alpha bitmap for Skia
This avoids some more expensive operations (if using CPU) like
SkBlendMode::kDstOut for hacks like
9ff0cd1f6b2200940ac51e580809e2a17c4b9550 that just set a fully
opaque bitmap as the alpha.
Change-Id: I446efc482d7bb13e899bf8b352e13fce6b5f9176
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/101896
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak at collabora.com>
diff --git a/vcl/inc/skia/salbmp.hxx b/vcl/inc/skia/salbmp.hxx
index 6ce94aad1b01..f834478be51a 100644
--- a/vcl/inc/skia/salbmp.hxx
+++ b/vcl/inc/skia/salbmp.hxx
@@ -78,6 +78,10 @@ public:
OString GetImageKey() const;
OString GetAlphaImageKey() const;
+ // Returns true if it is known that this bitmap can be ignored if it's to be used
+ // as an alpha bitmap. An optimization, not guaranteed to return true for all such cases.
+ bool IsFullyOpaqueAsAlpha() const;
+
#ifdef DBG_UTIL
void dump(const char* file) const;
#endif
diff --git a/vcl/qa/cppunit/BackendTest.cxx b/vcl/qa/cppunit/BackendTest.cxx
index 0e3d9c54dd08..ba82e9d2ba9d 100644
--- a/vcl/qa/cppunit/BackendTest.cxx
+++ b/vcl/qa/cppunit/BackendTest.cxx
@@ -14,6 +14,7 @@
#include <tools/stream.hxx>
#include <vcl/graphicfilter.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <bitmapwriteaccess.hxx>
#include <test/outputdevice.hxx>
@@ -582,6 +583,56 @@ public:
}
}
+ // Test SalGraphics::blendBitmap() and blendAlphaBitmap() calls.
+ void testDrawBlendExtended()
+ {
+ // Create virtual device with alpha.
+ ScopedVclPtr<VirtualDevice> device
+ = VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT, DeviceFormat::DEFAULT);
+ device->SetOutputSizePixel(Size(10, 10));
+ device->SetBackground(Wallpaper(COL_WHITE));
+ device->Erase();
+ Bitmap bitmap(Size(5, 5), 24);
+ bitmap.Erase(COL_BLUE);
+ // No alpha, this will actually call SalGraphics::DrawBitmap(), but still check
+ // the alpha of the device is handled correctly.
+ device->DrawBitmapEx(Point(2, 2), BitmapEx(bitmap));
+ exportDevice("/tmp/blend_extended_01.png", device);
+ CPPUNIT_ASSERT_EQUAL(COL_BLUE, device->GetPixel(Point(2, 2)));
+ CPPUNIT_ASSERT_EQUAL(COL_BLUE, device->GetPixel(Point(6, 6)));
+ // Check pixels outside of the bitmap aren't affected.
+ CPPUNIT_ASSERT_EQUAL(COL_WHITE, device->GetPixel(Point(1, 1)));
+ CPPUNIT_ASSERT_EQUAL(COL_WHITE, device->GetPixel(Point(7, 7)));
+
+ device->Erase();
+ AlphaMask alpha(Size(5, 5));
+ alpha.Erase(0); // opaque
+ device->DrawBitmapEx(Point(2, 2), BitmapEx(bitmap, alpha));
+ exportDevice("/tmp/blend_extended_02.png", device);
+ CPPUNIT_ASSERT_EQUAL(COL_BLUE, device->GetPixel(Point(2, 2)));
+ CPPUNIT_ASSERT_EQUAL(COL_BLUE, device->GetPixel(Point(6, 6)));
+
+ device->Erase();
+ alpha.Erase(255); // transparent
+ device->DrawBitmapEx(Point(2, 2), BitmapEx(bitmap, alpha));
+ exportDevice("/tmp/blend_extended_03.png", device);
+ CPPUNIT_ASSERT_EQUAL(COL_WHITE, device->GetPixel(Point(2, 2)));
+ CPPUNIT_ASSERT_EQUAL(COL_WHITE, device->GetPixel(Point(6, 6)));
+
+ // Skia optimizes bitmaps that have just been Erase()-ed, so explicitly
+ // set some pixels in the alpha to avoid this and have an actual bitmap
+ // as the alpha mask.
+ device->Erase();
+ alpha.Erase(255); // transparent
+ BitmapWriteAccess* alphaWrite = alpha.AcquireAlphaWriteAccess();
+ alphaWrite->SetPixelIndex(0, 0, 0); // opaque
+ alpha.ReleaseAccess(alphaWrite);
+ device->DrawBitmapEx(Point(2, 2), BitmapEx(bitmap, alpha));
+ exportDevice("/tmp/blend_extended_04.png", device);
+ CPPUNIT_ASSERT_EQUAL(COL_BLUE, device->GetPixel(Point(2, 2)));
+ CPPUNIT_ASSERT_EQUAL(COL_WHITE, device->GetPixel(Point(6, 6)));
+ }
+
void testTdf124848()
{
ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT);
@@ -693,6 +744,8 @@ public:
CPPUNIT_TEST(testErase);
+ CPPUNIT_TEST(testDrawBlendExtended);
+
CPPUNIT_TEST(testTdf124848);
CPPUNIT_TEST(testTdf136171);
diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx
index 15c9937c26e7..8c8eee3e3f88 100644
--- a/vcl/skia/gdiimpl.cxx
+++ b/vcl/skia/gdiimpl.cxx
@@ -1159,7 +1159,14 @@ bool SkiaSalGraphicsImpl::blendBitmap(const SalTwoRect& rPosAry, const SalBitmap
// combinedTextureFragmentShader.glsl, the layer is not even alpha values but
// simply yes-or-no mask.
// See also blendAlphaBitmap().
- drawBitmap(rPosAry, rSkiaBitmap, SkBlendMode::kMultiply);
+ if (rSkiaBitmap.IsFullyOpaqueAsAlpha())
+ {
+ // Optimization. If the bitmap means fully opaque, it's all zero's. In CPU
+ // mode it should be faster to just copy instead of SkBlendMode::kMultiply.
+ drawBitmap(rPosAry, rSkiaBitmap);
+ }
+ else
+ drawBitmap(rPosAry, rSkiaBitmap, SkBlendMode::kMultiply);
return true;
}
@@ -1178,6 +1185,13 @@ bool SkiaSalGraphicsImpl::blendAlphaBitmap(const SalTwoRect& rPosAry,
const SkiaSalBitmap& rSkiaMaskBitmap = static_cast<const SkiaSalBitmap&>(rMaskBitmap);
const SkiaSalBitmap& rSkiaAlphaBitmap = static_cast<const SkiaSalBitmap&>(rAlphaBitmap);
+ if (rSkiaMaskBitmap.IsFullyOpaqueAsAlpha())
+ {
+ // Optimization. If the mask of the bitmap to be blended means it's actually opaque,
+ // just draw the bitmap directly (that's what the math below will result in).
+ drawBitmap(rPosAry, rSkiaSourceBitmap);
+ return true;
+ }
// This was originally implemented for the OpenGL drawing method and it is poorly documented.
// The source and mask bitmaps are the usual data and alpha bitmaps, and 'alpha'
// is the "alpha" layer of the VirtualDevice (the alpha in VirtualDevice is also stored
@@ -1412,6 +1426,8 @@ sk_sp<SkImage> SkiaSalGraphicsImpl::mergeCacheBitmaps(const SkiaSalBitmap& bitma
// GPU-accelerated drawing with SkShader should be fast enough to not need caching.
if (isGPU())
return image;
+ if (alphaBitmap && alphaBitmap->IsFullyOpaqueAsAlpha())
+ alphaBitmap = nullptr; // the alpha can be ignored
// Probably not much point in caching of just doing a copy.
if (alphaBitmap == nullptr && targetSize == bitmap.GetSize())
return image;
@@ -1517,6 +1533,8 @@ bool SkiaSalGraphicsImpl::drawAlphaBitmap(const SalTwoRect& rPosAry, const SalBi
{
assert(dynamic_cast<const SkiaSalBitmap*>(&rSourceBitmap));
assert(dynamic_cast<const SkiaSalBitmap*>(&rAlphaBitmap));
+ const SkiaSalBitmap& rSkiaSourceBitmap = static_cast<const SkiaSalBitmap&>(rSourceBitmap);
+ const SkiaSalBitmap& rSkiaAlphaBitmap = static_cast<const SkiaSalBitmap&>(rAlphaBitmap);
// In raster mode use mergeCacheBitmaps(), which will cache the result, avoiding repeated
// alpha blending or scaling. In GPU mode it is simpler to just use SkShader.
SalTwoRect imagePosAry(rPosAry);
@@ -1531,17 +1549,16 @@ bool SkiaSalGraphicsImpl::drawAlphaBitmap(const SalTwoRect& rPosAry, const SalBi
imagePosAry.mnSrcHeight = imagePosAry.mnDestHeight;
imageSize = Size(imagePosAry.mnSrcWidth, imagePosAry.mnSrcHeight);
}
- sk_sp<SkImage> image
- = mergeCacheBitmaps(static_cast<const SkiaSalBitmap&>(rSourceBitmap),
- static_cast<const SkiaSalBitmap*>(&rAlphaBitmap), imageSize);
+ sk_sp<SkImage> image = mergeCacheBitmaps(rSkiaSourceBitmap, &rSkiaAlphaBitmap, imageSize);
if (image)
drawImage(imagePosAry, image);
+ else if (rSkiaAlphaBitmap.IsFullyOpaqueAsAlpha()) // alpha can be ignored
+ drawShader(rPosAry, rSkiaSourceBitmap.GetSkShader());
else
- drawShader(
- rPosAry,
- SkShaders::Blend(SkBlendMode::kDstOut, // VCL alpha is one-minus-alpha.
- static_cast<const SkiaSalBitmap&>(rSourceBitmap).GetSkShader(),
- static_cast<const SkiaSalBitmap*>(&rAlphaBitmap)->GetAlphaSkShader()));
+ drawShader(rPosAry,
+ SkShaders::Blend(SkBlendMode::kDstOut, // VCL alpha is one-minus-alpha.
+ rSkiaSourceBitmap.GetSkShader(),
+ rSkiaAlphaBitmap.GetAlphaSkShader()));
return true;
}
@@ -1617,6 +1634,9 @@ bool SkiaSalGraphicsImpl::drawTransformedBitmap(const basegfx::B2DPoint& rNull,
const SkiaSalBitmap& rSkiaBitmap = static_cast<const SkiaSalBitmap&>(rSourceBitmap);
const SkiaSalBitmap* pSkiaAlphaBitmap = static_cast<const SkiaSalBitmap*>(pAlphaBitmap);
+ if (pSkiaAlphaBitmap && pSkiaAlphaBitmap->IsFullyOpaqueAsAlpha())
+ pSkiaAlphaBitmap = nullptr; // the alpha can be ignored
+
// Setup the image transformation,
// using the rNull, rX, rY points as destinations for the (0,0), (Width,0), (0,Height) source points.
const basegfx::B2DVector aXRel = rX - rNull;
diff --git a/vcl/skia/salbmp.cxx b/vcl/skia/salbmp.cxx
index 378f5557de4e..af9596a3ace7 100644
--- a/vcl/skia/salbmp.cxx
+++ b/vcl/skia/salbmp.cxx
@@ -773,6 +773,17 @@ sk_sp<SkShader> SkiaSalBitmap::GetAlphaSkShader() const
return GetAlphaSkImage()->makeShader();
}
+bool SkiaSalBitmap::IsFullyOpaqueAsAlpha() const
+{
+ if (!mEraseColorSet)
+ return false; // don't bother figuring it out from the pixels
+ // If the erase color is set so that this bitmap used as alpha would
+ // mean a fully opaque alpha mask (= noop), we can skip using it.
+ // Note that for alpha bitmaps we use the VCL "trasparency" convention,
+ // i.e. alpha 0 is opaque.
+ return SkColorGetA(fromEraseColorToAlphaImageColor(mEraseColor)) == 0;
+}
+
void SkiaSalBitmap::EraseInternal()
{
if (mPixelsSize.IsEmpty())
More information about the Libreoffice-commits
mailing list