cache Skia' drawAlphaBitmap() in raster mode (tdf#104878)
Moving the mouse with the bug document triggers repeated calls
to drawAlphaBitmap(), which is somewhat costly if not GPU-accelerated.
Now the main cost in that bugreport is generating the frames
all over again.
Change-Id: Ic44811c713a745459f0af811c3d55038c944d89e
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/94152
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
diff --git a/vcl/skia/SkiaHelper.cxx b/vcl/skia/SkiaHelper.cxx
index d7004a7..aab6560 100644
--- a/vcl/skia/SkiaHelper.cxx
+++ b/vcl/skia/SkiaHelper.cxx
@@ -489,12 +489,13 @@ sk_sp<SkImage> findCachedImage(const OString& key)
if (it->key == key)
{
sk_sp<SkImage> ret = it->image;
SAL_INFO("vcl.skia.trace", "findcachedimage " << it->image);
SAL_INFO("vcl.skia.trace", "findcachedimage " << it->image << " found");
imageCache->splice(imageCache->begin(), *imageCache, it);
return ret;
}
}
}
SAL_INFO("vcl.skia.trace", "findcachedimage " << key << " not found");
return nullptr;
}
diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx
index 436a9d3..28183ec 100644
--- a/vcl/skia/gdiimpl.cxx
+++ b/vcl/skia/gdiimpl.cxx
@@ -1256,57 +1256,23 @@ bool SkiaSalGraphicsImpl::drawEPS(long, long, long, long, void*, sal_uInt32)
return false;
}
bool SkiaSalGraphicsImpl::drawAlphaBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSourceBitmap,
const SalBitmap& rAlphaBitmap)
{
assert(dynamic_cast<const SkiaSalBitmap*>(&rSourceBitmap));
assert(dynamic_cast<const SkiaSalBitmap*>(&rAlphaBitmap));
sk_sp<SkSurface> tmpSurface = SkiaHelper::createSkSurface(rSourceBitmap.GetSize());
if (!tmpSurface)
return false;
SkCanvas* canvas = tmpSurface->getCanvas();
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc); // copy as is, including alpha
canvas->drawImage(static_cast<const SkiaSalBitmap&>(rSourceBitmap).GetSkImage(), 0, 0, &paint);
paint.setBlendMode(SkBlendMode::kDstOut); // VCL alpha is one-minus-alpha
canvas->drawImage(static_cast<const SkiaSalBitmap&>(rAlphaBitmap).GetAlphaSkImage(), 0, 0,
&paint);
drawImage(rPosAry, tmpSurface->makeImageSnapshot());
return true;
}
void SkiaSalGraphicsImpl::drawImage(const SalTwoRect& rPosAry, const sk_sp<SkImage>& aImage,
SkBlendMode eBlendMode)
{
SkRect aSourceRect
= SkRect::MakeXYWH(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight);
SkRect aDestinationRect = SkRect::MakeXYWH(rPosAry.mnDestX, rPosAry.mnDestY,
rPosAry.mnDestWidth, rPosAry.mnDestHeight);
SkPaint aPaint;
aPaint.setBlendMode(eBlendMode);
preDraw();
SAL_INFO("vcl.skia.trace", "drawimage(" << this << "): " << rPosAry << ":" << int(eBlendMode));
getDrawCanvas()->drawImageRect(aImage, aSourceRect, aDestinationRect, &aPaint);
addXorRegion(aDestinationRect);
postDraw();
}
// Create SkImage from a bitmap and possibly an alpha mask (the usual VCL one-minus-alpha),
// with the given target size. Result will be possibly cached, unless disabled.
static sk_sp<SkImage> mergeBitmaps(const SkiaSalBitmap& bitmap, const SkiaSalBitmap* alphaBitmap,
const Size targetSize, bool blockCaching)
const Size targetSize, bool blockCaching = false)
{
sk_sp<SkImage> image;
OString key;
if (targetSize == bitmap.GetSize())
blockCaching = true; // probably not much point in caching if no scaling is involved
if (alphaBitmap == nullptr && targetSize == bitmap.GetSize())
blockCaching = true; // probably not much point in caching of just doing a copy
if (targetSize.Width() > bitmap.GetSize().Width()
|| targetSize.Height() > bitmap.GetSize().Height())
blockCaching = true; // caching enlarging is probably wasteful and not worth it
if (bitmap.GetSize().Width() < 100 && bitmap.GetSize().Height() < 100)
blockCaching = true; // image too small to be worth caching
if (SkiaHelper::renderMethodToUse() != SkiaHelper::RenderRaster
&& targetSize == bitmap.GetSize())
blockCaching = true; // GPU-accelerated shouldn't need caching of applying alpha
if (!blockCaching)
{
OStringBuffer keyBuf;
@@ -1377,6 +1343,36 @@ static sk_sp<SkImage> mergeBitmaps(const SkiaSalBitmap& bitmap, const SkiaSalBit
return image;
}
bool SkiaSalGraphicsImpl::drawAlphaBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSourceBitmap,
const SalBitmap& rAlphaBitmap)
{
assert(dynamic_cast<const SkiaSalBitmap*>(&rSourceBitmap));
assert(dynamic_cast<const SkiaSalBitmap*>(&rAlphaBitmap));
sk_sp<SkImage> image
= mergeBitmaps(static_cast<const SkiaSalBitmap&>(rSourceBitmap),
static_cast<const SkiaSalBitmap*>(&rAlphaBitmap), rSourceBitmap.GetSize());
drawImage(rPosAry, image);
return true;
}
void SkiaSalGraphicsImpl::drawImage(const SalTwoRect& rPosAry, const sk_sp<SkImage>& aImage,
SkBlendMode eBlendMode)
{
SkRect aSourceRect
= SkRect::MakeXYWH(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight);
SkRect aDestinationRect = SkRect::MakeXYWH(rPosAry.mnDestX, rPosAry.mnDestY,
rPosAry.mnDestWidth, rPosAry.mnDestHeight);
SkPaint aPaint;
aPaint.setBlendMode(eBlendMode);
preDraw();
SAL_INFO("vcl.skia.trace", "drawimage(" << this << "): " << rPosAry << ":" << int(eBlendMode));
getDrawCanvas()->drawImageRect(aImage, aSourceRect, aDestinationRect, &aPaint);
addXorRegion(aDestinationRect);
postDraw();
}
bool SkiaSalGraphicsImpl::drawTransformedBitmap(const basegfx::B2DPoint& rNull,
const basegfx::B2DPoint& rX,
const basegfx::B2DPoint& rY,
@@ -1389,10 +1385,12 @@ bool SkiaSalGraphicsImpl::drawTransformedBitmap(const basegfx::B2DPoint& rNull,
const SkiaSalBitmap& rSkiaBitmap = static_cast<const SkiaSalBitmap&>(rSourceBitmap);
const SkiaSalBitmap* pSkiaAlphaBitmap = static_cast<const SkiaSalBitmap*>(pAlphaBitmap);
// 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;
const basegfx::B2DVector aYRel = rY - rNull;
// Setup the image transformation,
// using the rNull, rX, rY points as destinations for the (0,0), (Width,0), (0,Height) source points.
// Round to pixels, otherwise kMScaleX/Y below could be slightly != 1, causing unnecessary uncached
// scaling.
const basegfx::B2IVector aXRel = basegfx::fround(rX - rNull);
const basegfx::B2IVector aYRel = basegfx::fround(rY - rNull);
const Size aSize = rSourceBitmap.GetSize();