vcl/inc/skia/gdiimpl.hxx | 11 +++++++++++ vcl/inc/skia/utils.hxx | 26 +++++++++++++++++--------- vcl/skia/gdiimpl.cxx | 10 +++++----- 3 files changed, 33 insertions(+), 14 deletions(-)
New commits: commit 0a596fbd94e1750e9d1f5cdab624c719b33c3ab4 Author: Luboš Luňák <l.lu...@collabora.com> AuthorDate: Fri Dec 3 16:48:47 2021 +0100 Commit: Luboš Luňák <l.lu...@collabora.com> CommitDate: Sat Dec 4 11:30:52 2021 +0100 only bilinear+mipmap for Skia/raster to-screen drawing (tdf#146024) The code already tries to hide the cost of the high-quality bicubic scaling by caching, but there are still cases where there's too much scaling done. Since this is only drawing to screen, use only bilinear+mipmap scaling in raster mode, which should be good enough (it's what the "super" scaling VCL algorithm for BmpScaleFlag::Default does as well). Change-Id: I75c86932e097411422dc1ef5e0534059dbf11ff8 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/126326 Tested-by: Jenkins Reviewed-by: Luboš Luňák <l.lu...@collabora.com> diff --git a/vcl/inc/skia/gdiimpl.hxx b/vcl/inc/skia/gdiimpl.hxx index 2473d0918284..b5144a249207 100644 --- a/vcl/inc/skia/gdiimpl.hxx +++ b/vcl/inc/skia/gdiimpl.hxx @@ -296,6 +296,17 @@ protected: void performDrawPolyPolygon(const basegfx::B2DPolyPolygon& polygon, double transparency, bool useAA); + BmpScaleFlag goodScalingQuality() const { return SkiaHelper::goodScalingQuality(isGPU()); } + SkSamplingOptions makeSamplingOptions(const SalTwoRect& rPosAry, int scalingFactor, + int srcScalingFactor = 1) + { + return SkiaHelper::makeSamplingOptions(rPosAry, scalingFactor, srcScalingFactor, isGPU()); + } + SkSamplingOptions makeSamplingOptions(const SkMatrix& matrix, int scalingFactor) + { + return SkiaHelper::makeSamplingOptions(goodScalingQuality(), matrix, scalingFactor); + } + // Create SkPaint to use when drawing to the surface. It is not to be used // when doing internal drawing such as when merging two bitmaps together. // This may apply some default settings to the paint as necessary. diff --git a/vcl/inc/skia/utils.hxx b/vcl/inc/skia/utils.hxx index c4d671ed6b7e..35180c016e24 100644 --- a/vcl/inc/skia/utils.hxx +++ b/vcl/inc/skia/utils.hxx @@ -135,6 +135,16 @@ inline bool isUnitTestRunning(const char* name = nullptr) return testname != nullptr && std::string_view(name) == testname; } +// Scaling done on the GPU is fast, but bicubic done in raster mode can be slow +// if done too much, and it generally shouldn't be needed for to-screen drawing. +// In that case use only BmpScaleFlag::Default, which is bilinear+mipmap, +// which should be good enough (and that's what the "super" bitmap scaling +// algorithm done by VCL does as well). +inline BmpScaleFlag goodScalingQuality(bool isGPU) +{ + return isGPU ? BmpScaleFlag::BestQuality : BmpScaleFlag::Default; +} + // 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 @@ -156,7 +166,9 @@ inline SkSamplingOptions makeSamplingOptions(BmpScaleFlag scalingType, SkMatrix return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear); return SkSamplingOptions(SkCubicResampler::Mitchell()); case BmpScaleFlag::Default: - return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone); + // Use SkMipmapMode::kNearest for better quality when downscaling. SkMipmapMode::kLinear + // would be even better, but it is not specially optimized in raster mode. + return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNearest); case BmpScaleFlag::Fast: case BmpScaleFlag::NearestNeighbor: return SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone); @@ -179,7 +191,8 @@ inline SkSamplingOptions makeSamplingOptions(BmpScaleFlag scalingType, const Siz return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear); return SkSamplingOptions(SkCubicResampler::Mitchell()); case BmpScaleFlag::Default: - return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone); + // As in the first overload, use kNearest. + return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNearest); case BmpScaleFlag::Fast: case BmpScaleFlag::NearestNeighbor: return SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone); @@ -190,7 +203,7 @@ inline SkSamplingOptions makeSamplingOptions(BmpScaleFlag scalingType, const Siz } inline SkSamplingOptions makeSamplingOptions(const SalTwoRect& rPosAry, int scalingFactor, - int srcScalingFactor = 1) + int srcScalingFactor, bool isGPU) { // If there will be scaling, make it smooth, but not in unittests, as those often // require exact color values and would be confused by this. @@ -203,12 +216,7 @@ inline SkSamplingOptions makeSamplingOptions(const SalTwoRect& rPosAry, int scal if (srcScalingFactor != 1) srcSize *= srcScalingFactor; if (srcSize != destSize) - { - if (srcSize.Width() / destSize.Width() >= downscaleRatioThreshold - || srcSize.Height() / destSize.Height() >= downscaleRatioThreshold) - return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear); - return SkSamplingOptions(SkCubicResampler::Mitchell()); // best - } + return makeSamplingOptions(goodScalingQuality(isGPU), srcSize, destSize, 1); return SkSamplingOptions(); // none } diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx index 5c014b918ded..f6620dfaafc0 100644 --- a/vcl/skia/gdiimpl.cxx +++ b/vcl/skia/gdiimpl.cxx @@ -1389,7 +1389,7 @@ std::shared_ptr<SalBitmap> SkiaSalGraphicsImpl::getBitmap(tools::Long nX, tools: if (mScaling != 1) { if (!isUnitTestRunning()) - bitmap->Scale(1.0 / mScaling, 1.0 / mScaling, BmpScaleFlag::BestQuality); + bitmap->Scale(1.0 / mScaling, 1.0 / mScaling, goodScalingQuality()); else { // Some tests require exact pixel values and would be confused by smooth-scaling. @@ -1397,7 +1397,7 @@ std::shared_ptr<SalBitmap> SkiaSalGraphicsImpl::getBitmap(tools::Long nX, tools: if (isUnitTestRunning("BackendTest__testDrawHaflEllipseAAWithPolyLineB2D_") || isUnitTestRunning("BackendTest__testDrawRectAAWithLine_")) { - bitmap->Scale(1.0 / mScaling, 1.0 / mScaling, BmpScaleFlag::BestQuality); + bitmap->Scale(1.0 / mScaling, 1.0 / mScaling, goodScalingQuality()); } else bitmap->Scale(1.0 / mScaling, 1.0 / mScaling, BmpScaleFlag::NearestNeighbor); @@ -1663,7 +1663,7 @@ sk_sp<SkImage> SkiaSalGraphicsImpl::mergeCacheBitmaps(const SkiaSalBitmap& bitma matrix.set(SkMatrix::kMScaleY, 1.0 * targetSize.Height() / sourceSize.Height()); canvas->concat(matrix); if (!isUnitTestRunning()) // unittests want exact pixel values - samplingOptions = makeSamplingOptions(BmpScaleFlag::BestQuality, matrix, 1); + samplingOptions = makeSamplingOptions(matrix, 1); } if (alphaBitmap != nullptr) { @@ -1914,7 +1914,7 @@ bool SkiaSalGraphicsImpl::drawTransformedBitmap(const basegfx::B2DPoint& rNull, // If the matrix changes geometry, we need to smooth-scale. If there's mScaling, // that's already been handled by mergeCacheBitmaps(). if (matrixNeedsHighQuality(matrix)) - samplingOptions = makeSamplingOptions(BmpScaleFlag::BestQuality, matrix, 1); + samplingOptions = makeSamplingOptions(matrix, 1); if (fAlpha == 1.0) { // Specify sizes to scale the image size back if needed (because of mScaling). @@ -1950,7 +1950,7 @@ bool SkiaSalGraphicsImpl::drawTransformedBitmap(const basegfx::B2DPoint& rNull, canvas->concat(matrix); SkSamplingOptions samplingOptions; if (matrixNeedsHighQuality(matrix) || (mScaling != 1 && !isUnitTestRunning())) - samplingOptions = makeSamplingOptions(BmpScaleFlag::BestQuality, matrix, mScaling); + samplingOptions = makeSamplingOptions(matrix, mScaling); if (pSkiaAlphaBitmap) { SkPaint paint = makeBitmapPaint();