diff options
author | Luboš Luňák <l.lunak@collabora.com> | 2021-11-11 14:01:55 +0100 |
---|---|---|
committer | Luboš Luňák <l.lunak@collabora.com> | 2021-11-16 10:38:54 +0100 |
commit | b5983dbe2c41f38e653201574cf20cd4bd76e950 (patch) | |
tree | a8254c2dc2b9ef2c7d91aa9dbf2b6994b3295645 /vcl/inc/skia | |
parent | 67669707e0c6c8a390d352e7060ad5862d727433 (diff) |
implement HiDPI support for Skia/Mac (tdf#144214)
The basic idea is the same as the 'aqua' backend, simply set up
a scaling matrix for all drawing. That will take care of the basic
drawing everything twice as large, which is twice the resolution.
And then blit this data to the window, which expects data this way.
Converting back from backing surface needs explicit coordinate
conversions, and when converting to a bitmap the bitmap needs
to be scaled down in order to appear normally sized. Fortunately
I've already implemented delayed scaling, which means that if
the bitmap is drawn later again without any modifications, no
data would be lost (to be done in a follow-up commit).
Unittests occassionally need special handling, as such scaling
down to bitmap not being smoothed, because they expect exact
color values.
Change-Id: Ieadf2c3693f7c9676c31c7394d46299addf7880c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/125060
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
Diffstat (limited to 'vcl/inc/skia')
-rw-r--r-- | vcl/inc/skia/gdiimpl.hxx | 20 | ||||
-rw-r--r-- | vcl/inc/skia/osx/gdiimpl.hxx | 3 | ||||
-rw-r--r-- | vcl/inc/skia/utils.hxx | 61 |
3 files changed, 70 insertions, 14 deletions
diff --git a/vcl/inc/skia/gdiimpl.hxx b/vcl/inc/skia/gdiimpl.hxx index 03a4d5cf0413..70bbcf5c4dcc 100644 --- a/vcl/inc/skia/gdiimpl.hxx +++ b/vcl/inc/skia/gdiimpl.hxx @@ -239,6 +239,7 @@ protected: void privateDrawAlphaRect(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, double nTransparency, bool blockAA = false); + void privateCopyBits(const SalTwoRect& rPosAry, SkiaSalGraphicsImpl* src); void setProvider(SalGeometryProvider* provider) { mProvider = provider; } @@ -256,6 +257,8 @@ protected: int GetWidth() const { return mProvider ? mProvider->GetWidth() : 1; } // get the height of the device int GetHeight() const { return mProvider ? mProvider->GetHeight() : 1; } + // Get the global HiDPI scaling factor. + virtual int getWindowScaling() const; SkCanvas* getXorCanvas(); void applyXor(); @@ -277,6 +280,8 @@ protected: // and swapping to the screen is not _that_slow. mDirtyRect.join(addedRect); } + void setCanvasScalingAndClipping(); + void resetCanvasScalingAndClipping(); static void setCanvasClipRegion(SkCanvas* canvas, const vcl::Region& region); sk_sp<SkImage> mergeCacheBitmaps(const SkiaSalBitmap& bitmap, const SkiaSalBitmap* alphaBitmap, const Size targetSize); @@ -305,9 +310,12 @@ protected: if (graphics == nullptr) return stream << "(null)"; // O - offscreen, G - GPU-based, R - raster - return stream << static_cast<const void*>(graphics) << " " - << Size(graphics->GetWidth(), graphics->GetHeight()) - << (graphics->isGPU() ? "G" : "R") << (graphics->isOffscreen() ? "O" : ""); + stream << static_cast<const void*>(graphics) << " " + << Size(graphics->GetWidth(), graphics->GetHeight()); + if (graphics->mScaling != 1) + stream << "*" << graphics->mScaling; + stream << (graphics->isGPU() ? "G" : "R") << (graphics->isOffscreen() ? "O" : ""); + return stream; } SalGraphics& mParent; @@ -318,14 +326,15 @@ protected: // Note that mSurface may be a proxy surface and not the one from the window context. std::unique_ptr<sk_app::WindowContext> mWindowContext; bool mIsGPU; // whether the surface is GPU-backed - SkIRect mDirtyRect; // the area that has been changed since the last performFlush() + // Note that we generally use VCL coordinates, which is not mSurface coordinates if mScaling!=1. + SkIRect mDirtyRect; // The area that has been changed since the last performFlush(). vcl::Region mClipRegion; + SkRegion mXorRegion; // The area that needs updating for the xor operation. Color mLineColor; Color mFillColor; bool mXorMode; SkBitmap mXorBitmap; std::unique_ptr<SkCanvas> mXorCanvas; - SkRegion mXorRegion; // the area that needs updating for the xor operation std::unique_ptr<SkiaFlushIdle> mFlush; // Info about pending polygons to draw (we try to merge adjacent polygons into one). struct LastPolyPolygonInfo @@ -336,6 +345,7 @@ protected: }; LastPolyPolygonInfo mLastPolyPolygonInfo; int mPendingOperationsToFlush; + int mScaling; // The scale factor for HiDPI screens. }; #endif diff --git a/vcl/inc/skia/osx/gdiimpl.hxx b/vcl/inc/skia/osx/gdiimpl.hxx index c4892ab45b43..42a8257f8b8f 100644 --- a/vcl/inc/skia/osx/gdiimpl.hxx +++ b/vcl/inc/skia/osx/gdiimpl.hxx @@ -43,6 +43,9 @@ public: virtual void Flush() override; virtual void Flush(const tools::Rectangle&) override; +protected: + virtual int getWindowScaling() const override; + private: virtual void createWindowSurfaceInternal(bool forceRaster = false) override; virtual void flushSurfaceToWindowContext() override; diff --git a/vcl/inc/skia/utils.hxx b/vcl/inc/skia/utils.hxx index ba479c58f234..ed404f7cc3eb 100644 --- a/vcl/inc/skia/utils.hxx +++ b/vcl/inc/skia/utils.hxx @@ -33,6 +33,8 @@ #include <tools/sk_app/WindowContext.h> #include <postmac.h> +#include <string_view> + namespace SkiaHelper { // Get the one shared GrDirectContext instance. @@ -90,6 +92,17 @@ VCL_DLLPUBLIC const SkSurfaceProps* surfaceProps(); // Set pixel geometry to be used by SkSurfaceProps. VCL_DLLPUBLIC void setPixelGeometry(SkPixelGeometry pixelGeometry); +inline bool isUnitTestRunning(const char* name = nullptr) +{ + if (name == nullptr) + { + static const char* const testname = getenv("LO_TESTNAME"); + return testname != nullptr; + } + const char* const testname = getenv("LO_TESTNAME"); + return testname != nullptr && std::string_view(name) == testname; +} + // 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 @@ -98,11 +111,14 @@ VCL_DLLPUBLIC void setPixelGeometry(SkPixelGeometry pixelGeometry); // Anything scaled down at least this ratio will use linear+mipmaps. constexpr int downscaleRatioThreshold = 4; -inline SkSamplingOptions makeSamplingOptions(BmpScaleFlag scaling, const SkMatrix& matrix) +inline SkSamplingOptions makeSamplingOptions(BmpScaleFlag scalingType, SkMatrix matrix, + int scalingFactor) { - switch (scaling) + switch (scalingType) { case BmpScaleFlag::BestQuality: + if (scalingFactor != 1) + matrix.postScale(scalingFactor, scalingFactor); if (matrix.getScaleX() <= 1.0 / downscaleRatioThreshold || matrix.getScaleY() <= 1.0 / downscaleRatioThreshold) return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear); @@ -110,6 +126,7 @@ inline SkSamplingOptions makeSamplingOptions(BmpScaleFlag scaling, const SkMatri case BmpScaleFlag::Default: return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone); case BmpScaleFlag::Fast: + case BmpScaleFlag::NearestNeighbor: return SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone); default: assert(false); @@ -117,12 +134,14 @@ inline SkSamplingOptions makeSamplingOptions(BmpScaleFlag scaling, const SkMatri } } -inline SkSamplingOptions makeSamplingOptions(BmpScaleFlag scaling, const Size& srcSize, - const Size& destSize) +inline SkSamplingOptions makeSamplingOptions(BmpScaleFlag scalingType, const Size& srcSize, + Size destSize, int scalingFactor) { - switch (scaling) + switch (scalingType) { case BmpScaleFlag::BestQuality: + if (scalingFactor != 1) + destSize *= scalingFactor; if (srcSize.Width() / destSize.Width() >= downscaleRatioThreshold || srcSize.Height() / destSize.Height() >= downscaleRatioThreshold) return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear); @@ -130,6 +149,7 @@ inline SkSamplingOptions makeSamplingOptions(BmpScaleFlag scaling, const Size& s case BmpScaleFlag::Default: return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone); case BmpScaleFlag::Fast: + case BmpScaleFlag::NearestNeighbor: return SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone); default: assert(false); @@ -137,18 +157,41 @@ inline SkSamplingOptions makeSamplingOptions(BmpScaleFlag scaling, const Size& s } } -inline SkSamplingOptions makeSamplingOptions(const SalTwoRect& rPosAry) +inline SkSamplingOptions makeSamplingOptions(const SalTwoRect& rPosAry, int scalingFactor, + int srcScalingFactor = 1) { - if (rPosAry.mnSrcWidth != rPosAry.mnDestWidth || rPosAry.mnSrcHeight != rPosAry.mnDestHeight) + // 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. + if (isUnitTestRunning()) + return SkSamplingOptions(); // none + Size srcSize(rPosAry.mnSrcWidth, rPosAry.mnSrcHeight); + Size destSize(rPosAry.mnDestWidth, rPosAry.mnDestHeight); + if (scalingFactor != 1) + destSize *= scalingFactor; + if (srcScalingFactor != 1) + srcSize *= srcScalingFactor; + if (srcSize != destSize) { - if (rPosAry.mnSrcWidth / rPosAry.mnDestWidth >= downscaleRatioThreshold - || rPosAry.mnSrcHeight / rPosAry.mnDestHeight >= downscaleRatioThreshold) + if (srcSize.Width() / destSize.Width() >= downscaleRatioThreshold + || srcSize.Height() / destSize.Height() >= downscaleRatioThreshold) return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear); return SkSamplingOptions(SkCubicResampler::Mitchell()); // best } return SkSamplingOptions(); // none } +inline SkRect scaleRect(const SkRect& rect, int scaling) +{ + return SkRect::MakeXYWH(rect.x() * scaling, rect.y() * scaling, rect.width() * scaling, + rect.height() * scaling); +} + +inline SkIRect scaleRect(const SkIRect& rect, int scaling) +{ + return SkIRect::MakeXYWH(rect.x() * scaling, rect.y() * scaling, rect.width() * scaling, + rect.height() * scaling); +} + #ifdef DBG_UTIL void prefillSurface(const sk_sp<SkSurface>& surface); VCL_DLLPUBLIC void dump(const SkBitmap& bitmap, const char* file); |