use Skia linear+mipmap for quality large downscaling (tdf#140129)
This is what https://bugs.chromium.org/p/skia/issues/detail?id=11810#c1
suggests (although I consider it to be an annoyance having to do this
explicitly).
Change-Id: I3df80374492c7b208ebaf819c0b4794ba535aa53
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/113979
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
diff --git a/vcl/inc/skia/utils.hxx b/vcl/inc/skia/utils.hxx
index 716bd3e..e53a17c 100644
--- a/vcl/inc/skia/utils.hxx
+++ b/vcl/inc/skia/utils.hxx
@@ -85,11 +85,42 @@ VCL_DLLPUBLIC const SkSurfaceProps* surfaceProps();
// Set pixel geometry to be used by SkSurfaceProps.
VCL_DLLPUBLIC void setPixelGeometry(SkPixelGeometry pixelGeometry);
inline SkSamplingOptions makeSamplingOptions(BmpScaleFlag scaling)
// Normal scaling algorithms have a poor quality when downscaling a lot.
// https://bugs.chromium.org/p/skia/issues/detail?id=11810 suggests to use mipmaps
// in such a case, which is annoying to do explicitly instead of Skia deciding which
// algorithm would be the best, but now with Skia removing SkFilterQuality and requiring
// explicitly being told what algorithm to use this appears to be the best we can do.
// Anything scaled down at least this ratio will use linear+mipmaps.
constexpr int downscaleRatioThreshold = 4;
inline SkSamplingOptions makeSamplingOptions(BmpScaleFlag scaling, const SkMatrix& matrix)
{
switch (scaling)
{
case BmpScaleFlag::BestQuality:
if (matrix.getScaleX() <= 1.0 / downscaleRatioThreshold
|| matrix.getScaleY() <= 1.0 / downscaleRatioThreshold)
return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear);
return SkSamplingOptions(SkCubicResampler::Mitchell());
case BmpScaleFlag::Default:
return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone);
case BmpScaleFlag::Fast:
return SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone);
default:
assert(false);
return SkSamplingOptions();
}
}
inline SkSamplingOptions makeSamplingOptions(BmpScaleFlag scaling, const Size& srcSize,
const Size& destSize)
{
switch (scaling)
{
case BmpScaleFlag::BestQuality:
if (srcSize.Width() / destSize.Width() >= downscaleRatioThreshold
|| srcSize.Height() / destSize.Height() >= downscaleRatioThreshold)
return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear);
return SkSamplingOptions(SkCubicResampler::Mitchell());
case BmpScaleFlag::Default:
return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone);
@@ -104,7 +135,12 @@ inline SkSamplingOptions makeSamplingOptions(BmpScaleFlag scaling)
inline SkSamplingOptions makeSamplingOptions(const SalTwoRect& rPosAry)
{
if (rPosAry.mnSrcWidth != rPosAry.mnDestWidth || rPosAry.mnSrcHeight != rPosAry.mnDestHeight)
{
if (rPosAry.mnSrcWidth / rPosAry.mnDestWidth >= downscaleRatioThreshold
|| rPosAry.mnSrcHeight / rPosAry.mnDestHeight >= downscaleRatioThreshold)
return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear);
return SkSamplingOptions(SkCubicResampler::Mitchell()); // best
}
return SkSamplingOptions(); // none
}
diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx
index 828b795..fd4f64e 100644
--- a/vcl/skia/gdiimpl.cxx
+++ b/vcl/skia/gdiimpl.cxx
@@ -1652,7 +1652,7 @@ sk_sp<SkImage> SkiaSalGraphicsImpl::mergeCacheBitmaps(const SkiaSalBitmap& bitma
matrix.set(SkMatrix::kMScaleX, 1.0 * targetSize.Width() / bitmap.GetSize().Width());
matrix.set(SkMatrix::kMScaleY, 1.0 * targetSize.Height() / bitmap.GetSize().Height());
canvas->concat(matrix);
samplingOptions = makeSamplingOptions(BmpScaleFlag::BestQuality);
samplingOptions = makeSamplingOptions(BmpScaleFlag::BestQuality, matrix);
}
if (alphaBitmap != nullptr)
{
@@ -1884,7 +1884,7 @@ bool SkiaSalGraphicsImpl::drawTransformedBitmap(const basegfx::B2DPoint& rNull,
canvas->concat(matrix);
SkSamplingOptions samplingOptions;
if (matrixNeedsHighQuality(matrix))
samplingOptions = makeSamplingOptions(BmpScaleFlag::BestQuality);
samplingOptions = makeSamplingOptions(BmpScaleFlag::BestQuality, matrix);
if (fAlpha == 1.0)
canvas->drawImage(imageToDraw, 0, 0, samplingOptions);
else
@@ -1911,7 +1911,7 @@ bool SkiaSalGraphicsImpl::drawTransformedBitmap(const basegfx::B2DPoint& rNull,
canvas->concat(matrix);
SkSamplingOptions samplingOptions;
if (matrixNeedsHighQuality(matrix))
samplingOptions = makeSamplingOptions(BmpScaleFlag::BestQuality);
samplingOptions = makeSamplingOptions(BmpScaleFlag::BestQuality, matrix);
if (pSkiaAlphaBitmap)
{
SkPaint paint;
diff --git a/vcl/skia/salbmp.cxx b/vcl/skia/salbmp.cxx
index 1302a35..bb19f91 100644
--- a/vcl/skia/salbmp.cxx
+++ b/vcl/skia/salbmp.cxx
@@ -779,9 +779,9 @@ const sk_sp<SkImage>& SkiaSalBitmap::GetSkImage() const
assert(surface);
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
surface->getCanvas()->drawImageRect(mImage,
SkRect::MakeWH(mSize.Width(), mSize.Height()),
makeSamplingOptions(mScaleQuality), &paint);
surface->getCanvas()->drawImageRect(
mImage, SkRect::MakeWH(mSize.Width(), mSize.Height()),
makeSamplingOptions(mScaleQuality, imageSize(mImage), mSize), &paint);
SAL_INFO("vcl.skia.trace", "getskimage(" << this << "): image scaled "
<< Size(mImage->width(), mImage->height())
<< "->" << mSize << ":"
@@ -893,7 +893,9 @@ const sk_sp<SkImage>& SkiaSalBitmap::GetAlphaSkImage() const
paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
surface->getCanvas()->drawImageRect(
mImage, SkRect::MakeWH(mSize.Width(), mSize.Height()),
scaling ? makeSamplingOptions(mScaleQuality) : SkSamplingOptions(), &paint);
scaling ? makeSamplingOptions(mScaleQuality, imageSize(mImage), mSize)
: SkSamplingOptions(),
&paint);
if (scaling)
SAL_INFO("vcl.skia.trace", "getalphaskimage(" << this << "): image scaled "
<< Size(mImage->width(), mImage->height())
@@ -1147,7 +1149,8 @@ void SkiaSalBitmap::EnsureBitmapData()
if (imageSize(mImage) != mSize) // pending scaling?
{
canvas.drawImageRect(mImage, SkRect::MakeWH(mSize.getWidth(), mSize.getHeight()),
makeSamplingOptions(mScaleQuality), &paint);
makeSamplingOptions(mScaleQuality, imageSize(mImage), mSize),
&paint);
SAL_INFO("vcl.skia.trace",
"ensurebitmapdata(" << this << "): image scaled " << imageSize(mImage) << "->"
<< mSize << ":" << static_cast<int>(mScaleQuality));