diff options
author | Luboš Luňák <l.lunak@collabora.com> | 2019-12-04 18:01:47 +0100 |
---|---|---|
committer | Luboš Luňák <l.lunak@collabora.com> | 2019-12-06 14:26:45 +0100 |
commit | 8fede4e7870579251c58823566c88476df2038b0 (patch) | |
tree | 888a42fd265447827dba661456d4b383a9f8de80 | |
parent | cd46c8710b2eb244572670d78fec1f0dcb4a577f (diff) |
make all Skia drawing GPU-backed, if possible
This primarily means using SkiaHelper::createSkSurface(), which will
create a GPU-backed SkSurface if Vulkan is used, and it is used in place
of temporary SkBitmap instances, which are always raster-based.
Change-Id: I3fe35866f962030f464d5c1d1c4bf518c20ee9af
Reviewed-on: https://gerrit.libreoffice.org/84562
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
-rw-r--r-- | vcl/inc/skia/gdiimpl.hxx | 9 | ||||
-rw-r--r-- | vcl/inc/skia/salbmp.hxx | 30 | ||||
-rw-r--r-- | vcl/inc/skia/utils.hxx | 18 | ||||
-rw-r--r-- | vcl/skia/SkiaHelper.cxx | 74 | ||||
-rw-r--r-- | vcl/skia/gdiimpl.cxx | 174 | ||||
-rw-r--r-- | vcl/skia/salbmp.cxx | 164 | ||||
-rw-r--r-- | vcl/skia/win/gdiimpl.cxx | 34 |
7 files changed, 296 insertions, 207 deletions
diff --git a/vcl/inc/skia/gdiimpl.hxx b/vcl/inc/skia/gdiimpl.hxx index 7365d58f9173..d54c09d67eb8 100644 --- a/vcl/inc/skia/gdiimpl.hxx +++ b/vcl/inc/skia/gdiimpl.hxx @@ -193,10 +193,11 @@ public: #ifdef DBG_UTIL void dump(const char* file) const; - static void dump(const SkBitmap& bitmap, const char* file); #endif // Default blend mode for SkPaint is SkBlendMode::kSrcOver + void drawImage(const SalTwoRect& rPosAry, const sk_sp<SkImage>& aImage, + SkBlendMode eBlendMode = SkBlendMode::kSrcOver); void drawBitmap(const SalTwoRect& rPosAry, const SkBitmap& aBitmap, SkBlendMode eBlendMode = SkBlendMode::kSrcOver); @@ -237,7 +238,7 @@ protected: // get the height of the device int GetHeight() const { return mProvider ? mProvider->GetHeight() : 1; } - void drawMask(const SalTwoRect& rPosAry, const SkImage& rImage, Color nMaskColor); + void drawMask(const SalTwoRect& rPosAry, const sk_sp<SkImage>& rImage, Color nMaskColor); // When drawing using GPU, rounding errors may result in off-by-one errors, // see https://bugs.chromium.org/p/skia/issues/detail?id=9611 . Compensate for @@ -246,10 +247,6 @@ protected: SkScalar toSkX(long x) const { return mIsGPU ? x + 0.5 : x; } SkScalar toSkY(long y) const { return mIsGPU ? y + 0.5 : y; } -#ifdef DBG_UTIL - void prefillSurface(); -#endif - template <typename charT, typename traits> friend inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& stream, const SkiaSalGraphicsImpl* graphics) diff --git a/vcl/inc/skia/salbmp.hxx b/vcl/inc/skia/salbmp.hxx index 0b0e1aa5439c..ea25d477c985 100644 --- a/vcl/inc/skia/salbmp.hxx +++ b/vcl/inc/skia/salbmp.hxx @@ -22,15 +22,13 @@ #include <salbmp.hxx> -#include <SkBitmap.h> - -class SkImage; +#include <SkImage.h> class VCL_PLUGIN_PUBLIC SkiaSalBitmap : public SalBitmap { public: SkiaSalBitmap(); - SkiaSalBitmap(const SkImage& image); + SkiaSalBitmap(const sk_sp<SkImage>& image); virtual ~SkiaSalBitmap() override; // SalBitmap methods @@ -59,12 +57,11 @@ public: sal_uInt8 nTol) override; virtual bool ConvertToGreyscale() override; - // Accesses the internal SkBitmap. If the bit count is one that Skia does - // not support natively, data from the internal buffer is converted - // to a 32bpp SkBitmap. - const SkBitmap& GetSkBitmap() const; + // Returns the contents as SkImage (possibly GPU-backed). + const sk_sp<SkImage>& GetSkImage() const; - const SkBitmap& GetAlphaSkBitmap() const; + // Returns the contents as alpha SkImage (possibly GPU-backed) + const sk_sp<SkImage>& GetAlphaSkImage() const; #ifdef DBG_UTIL void dump(const char* file) const; @@ -72,6 +69,7 @@ public: private: void ResetCachedBitmap(); + SkBitmap GetAsSkBitmap() const; #ifdef DBG_UTIL void verify() const; #else @@ -82,16 +80,18 @@ private: friend inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& stream, const SkiaSalBitmap* bitmap) { // TODO GPU-based, once it's done - // B - has SkBitmap, A - has alpha SkBitmap, D - has data buffer + // B - has SkBitmap, D - has data buffer, I/i - has SkImage (on GPU/CPU), + // A/a - has alpha SkImage (on GPU/CPU) return stream << static_cast<const void*>(bitmap) << " " << bitmap->GetSize() << "/" << bitmap->mBitCount << (!bitmap->mBitmap.drawsNothing() ? "B" : "") - << (!bitmap->mAlphaBitmap.drawsNothing() ? "A" : "") - << (bitmap->mBuffer.get() ? "D" : ""); + << (bitmap->mBuffer.get() ? "D" : "") + << (bitmap->mImage ? (bitmap->mImage->isTextureBacked() ? "I" : "i") : "") + << (bitmap->mAlphaImage ? (bitmap->mAlphaImage->isTextureBacked() ? "A" : "a") + : ""); } - // TODO use something GPU-backed, or at least cache it for when drawing it to something GPU-backed? + // Bitmap pixels, if format is supported by Skia. If not, mBuffer is used. SkBitmap mBitmap; - SkBitmap mAlphaBitmap; // TODO for use as an alpha channel or mask BitmapPalette mPalette; int mBitCount; // bpp Size mSize; @@ -99,6 +99,8 @@ private: // in a buffer (and converted to 32bpp SkBitmap on-demand using GetSkBitmap()). std::unique_ptr<sal_uInt8[]> mBuffer; int mScanlineSize; // size of one row in mBuffer + sk_sp<SkImage> mImage; // cached contents, possibly GPU-backed + sk_sp<SkImage> mAlphaImage; // cached contents as alpha image, possibly GPU-backed }; #endif // INCLUDED_VCL_INC_SKIA_SALBMP_H diff --git a/vcl/inc/skia/utils.hxx b/vcl/inc/skia/utils.hxx index 5d824400030a..27ca2b1f0b72 100644 --- a/vcl/inc/skia/utils.hxx +++ b/vcl/inc/skia/utils.hxx @@ -22,6 +22,8 @@ #include <vcl/skia/SkiaHelper.hxx> +#include <tools/gen.hxx> + #include <tools/sk_app/VulkanWindowContext.h> namespace SkiaHelper @@ -31,6 +33,22 @@ GrContext* getSharedGrContext(); void disableRenderMethod(RenderMethod method); +// Create SkSurface, GPU-backed if possible. +VCL_DLLPUBLIC sk_sp<SkSurface> createSkSurface(int width, int height, + SkColorType type = kN32_SkColorType); + +inline sk_sp<SkSurface> createSkSurface(const Size& size, SkColorType type = kN32_SkColorType) +{ + return createSkSurface(size.Width(), size.Height(), type); +} + +#ifdef DBG_UTIL +void prefillSurface(sk_sp<SkSurface>& surface); +void dump(const SkBitmap& bitmap, const char* file); +void dump(const sk_sp<SkImage>& image, const char* file); +void dump(const sk_sp<SkSurface>& surface, const char* file); +#endif + } // namespace #endif // INCLUDED_VCL_INC_SKIA_UTILS_H diff --git a/vcl/skia/SkiaHelper.cxx b/vcl/skia/SkiaHelper.cxx index 471d47f01f69..6949c2d9fe40 100644 --- a/vcl/skia/SkiaHelper.cxx +++ b/vcl/skia/SkiaHelper.cxx @@ -25,8 +25,13 @@ bool isVCLSkiaEnabled() { return false; } #include <skia/utils.hxx> +#include <SkSurface.h> #include <tools/sk_app/VulkanWindowContext.h> +#ifdef DBG_UTIL +#include <fstream> +#endif + namespace SkiaHelper { static bool supportsVCLSkia() @@ -151,12 +156,81 @@ GrContext* getSharedGrContext() return nullptr; } +sk_sp<SkSurface> createSkSurface(int width, int height, SkColorType type) +{ + assert(type == kN32_SkColorType || type == kAlpha_8_SkColorType); + sk_sp<SkSurface> surface; + switch (SkiaHelper::renderMethodToUse()) + { + case SkiaHelper::RenderVulkan: + { + if (GrContext* grContext = getSharedGrContext()) + { + surface = SkSurface::MakeRenderTarget( + grContext, SkBudgeted::kNo, + SkImageInfo::Make(width, height, type, kPremul_SkAlphaType)); + assert(surface); +#ifdef DBG_UTIL + prefillSurface(surface); +#endif + return surface; + } + break; + } + default: + break; + } + // Create raster surface as a fallback. + surface = SkSurface::MakeRaster(SkImageInfo::Make(width, height, type, kPremul_SkAlphaType)); + assert(surface); +#ifdef DBG_UTIL + prefillSurface(surface); +#endif + return surface; +} + void cleanup() { delete sharedGrContext; sharedGrContext = nullptr; } +#ifdef DBG_UTIL +void prefillSurface(sk_sp<SkSurface>& surface) +{ + // Pre-fill the surface with deterministic garbage. + SkBitmap bitmap; + bitmap.allocN32Pixels(2, 2); + SkPMColor* scanline; + scanline = bitmap.getAddr32(0, 0); + *scanline++ = SkPreMultiplyARGB(0xFF, 0xBF, 0x80, 0x40); + *scanline++ = SkPreMultiplyARGB(0xFF, 0x40, 0x80, 0xBF); + scanline = bitmap.getAddr32(0, 1); + *scanline++ = SkPreMultiplyARGB(0xFF, 0xE3, 0x5C, 0x13); + *scanline++ = SkPreMultiplyARGB(0xFF, 0x13, 0x5C, 0xE3); + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha + paint.setShader(bitmap.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat)); + surface->getCanvas()->drawPaint(paint); +} + +void dump(const SkBitmap& bitmap, const char* file) { dump(SkImage::MakeFromBitmap(bitmap), file); } + +void dump(const sk_sp<SkSurface>& surface, const char* file) +{ + surface->getCanvas()->flush(); + dump(surface->makeImageSnapshot(), file); +} + +void dump(const sk_sp<SkImage>& image, const char* file) +{ + sk_sp<SkData> data = image->encodeToData(); + std::ofstream ostream(file, std::ios::binary); + ostream.write(static_cast<const char*>(data->data()), data->size()); +} + +#endif + } // namespace #endif // HAVE_FEATURE_SKIA diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx index 6a977ab6b598..ddcd9b75d30f 100644 --- a/vcl/skia/gdiimpl.cxx +++ b/vcl/skia/gdiimpl.cxx @@ -35,10 +35,6 @@ #include <basegfx/polygon/b2dpolygontools.hxx> -#ifdef DBG_UTIL -#include <fstream> -#endif - namespace { // Create Skia Path from B2DPolygon @@ -199,9 +195,6 @@ void SkiaSalGraphicsImpl::createSurface() createOffscreenSurface(); else createWindowSurface(); -#ifdef DBG_UTIL - prefillSurface(); -#endif mSurface->getCanvas()->save(); // see SetClipRegion() mClipRegion = vcl::Region(tools::Rectangle(0, 0, GetWidth(), GetHeight())); @@ -232,6 +225,10 @@ void SkiaSalGraphicsImpl::createWindowSurface() abort(); // this should not really happen } } + assert((mSurface->getCanvas()->getGrContext() != nullptr) == mIsGPU); +#ifdef DBG_UTIL + SkiaHelper::prefillSurface(mSurface); +#endif } void SkiaSalGraphicsImpl::createOffscreenSurface() @@ -259,14 +256,10 @@ void SkiaSalGraphicsImpl::createOffscreenSurface() } if (grContext) { - mSurface = SkSurface::MakeRenderTarget( - grContext, SkBudgeted::kNo, - SkImageInfo::MakeN32Premul(GetWidth(), GetHeight())); - mIsGPU = true; + mSurface = SkiaHelper::createSkSurface(GetWidth(), GetHeight()); assert(mSurface); -#ifdef DBG_UTIL - prefillSurface(); -#endif + assert(mSurface->getCanvas()->getGrContext()); // is GPU-backed + mIsGPU = true; return; } SAL_WARN("vcl.skia", "cannot create Vulkan offscreen GPU surface, disabling Vulkan"); @@ -276,9 +269,10 @@ void SkiaSalGraphicsImpl::createOffscreenSurface() default: break; } - // Create raster surface. Subclasses will create GPU-backed surfaces as appropriate. - mSurface = SkSurface::MakeRasterN32Premul(GetWidth(), GetHeight()); + // Create raster surface as a fallback. + mSurface = SkiaHelper::createSkSurface(GetWidth(), GetHeight()); assert(mSurface); + assert(!mSurface->getCanvas()->getGrContext()); // is not GPU-backed mIsGPU = false; } @@ -768,10 +762,8 @@ bool SkiaSalGraphicsImpl::blendBitmap(const SalTwoRect& rPosAry, const SalBitmap return false; assert(dynamic_cast<const SkiaSalBitmap*>(&rBitmap)); - const SkiaSalBitmap& rSkiaBitmap = static_cast<const SkiaSalBitmap&>(rBitmap); - drawBitmap(rPosAry, rSkiaBitmap.GetSkBitmap(), SkBlendMode::kMultiply); - + drawImage(rPosAry, rSkiaBitmap.GetSkImage(), SkBlendMode::kMultiply); return true; } @@ -787,31 +779,27 @@ bool SkiaSalGraphicsImpl::blendAlphaBitmap(const SalTwoRect& rPosAry, assert(dynamic_cast<const SkiaSalBitmap*>(&rMaskBitmap)); assert(dynamic_cast<const SkiaSalBitmap*>(&rAlphaBitmap)); - SkBitmap aTempBitmap; - if (!aTempBitmap.tryAllocN32Pixels(rSourceBitmap.GetSize().Width(), - rSourceBitmap.GetSize().Height())) - { + sk_sp<SkSurface> tmpSurface = SkiaHelper::createSkSurface(rSourceBitmap.GetSize()); + if (!tmpSurface) return false; - } const SkiaSalBitmap& rSkiaSourceBitmap = static_cast<const SkiaSalBitmap&>(rSourceBitmap); const SkiaSalBitmap& rSkiaMaskBitmap = static_cast<const SkiaSalBitmap&>(rMaskBitmap); const SkiaSalBitmap& rSkiaAlphaBitmap = static_cast<const SkiaSalBitmap&>(rAlphaBitmap); - SkCanvas aCanvas(aTempBitmap); + SkCanvas* aCanvas = tmpSurface->getCanvas(); SkPaint aPaint; aPaint.setBlendMode(SkBlendMode::kSrc); - aCanvas.drawBitmap(rSkiaMaskBitmap.GetAlphaSkBitmap(), 0, 0, &aPaint); - - aPaint.setBlendMode(SkBlendMode::kSrcIn); - aCanvas.drawBitmap(rSkiaAlphaBitmap.GetAlphaSkBitmap(), 0, 0, &aPaint); + aCanvas->drawImage(rSkiaSourceBitmap.GetSkImage(), 0, 0, &aPaint); - aPaint.setBlendMode(SkBlendMode::kSrcOut); - aCanvas.drawBitmap(rSkiaSourceBitmap.GetSkBitmap(), 0, 0, &aPaint); - - drawBitmap(rPosAry, aTempBitmap); + // Apply cumulatively both the bitmap alpha and the device alpha. + aPaint.setBlendMode(SkBlendMode::kDstOut); // VCL alpha is one-minus-alpha + aCanvas->drawImage(rSkiaMaskBitmap.GetAlphaSkImage(), 0, 0, &aPaint); + aPaint.setBlendMode(SkBlendMode::kDstOut); // VCL alpha is one-minus-alpha + aCanvas->drawImage(rSkiaAlphaBitmap.GetAlphaSkImage(), 0, 0, &aPaint); + drawImage(rPosAry, tmpSurface->makeImageSnapshot()); return true; } @@ -823,7 +811,7 @@ void SkiaSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& assert(dynamic_cast<const SkiaSalBitmap*>(&rSalBitmap)); const SkiaSalBitmap& rSkiaSourceBitmap = static_cast<const SkiaSalBitmap&>(rSalBitmap); - drawBitmap(rPosAry, rSkiaSourceBitmap.GetSkBitmap()); + drawImage(rPosAry, rSkiaSourceBitmap.GetSkImage()); } void SkiaSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap, @@ -836,26 +824,24 @@ void SkiaSalGraphicsImpl::drawMask(const SalTwoRect& rPosAry, const SalBitmap& r Color nMaskColor) { assert(dynamic_cast<const SkiaSalBitmap*>(&rSalBitmap)); - sk_sp<SkImage> image - = SkImage::MakeFromBitmap(static_cast<const SkiaSalBitmap&>(rSalBitmap).GetAlphaSkBitmap()); - drawMask(rPosAry, *image, nMaskColor); + drawMask(rPosAry, static_cast<const SkiaSalBitmap&>(rSalBitmap).GetAlphaSkImage(), nMaskColor); } -void SkiaSalGraphicsImpl::drawMask(const SalTwoRect& rPosAry, const SkImage& rImage, +void SkiaSalGraphicsImpl::drawMask(const SalTwoRect& rPosAry, const sk_sp<SkImage>& rImage, Color nMaskColor) { - SkBitmap tmpBitmap; - if (!tmpBitmap.tryAllocN32Pixels(rImage.width(), rImage.height())) - abort(); - tmpBitmap.eraseColor(toSkColor(nMaskColor)); + SAL_INFO("vcl.skia", "drawmask(" << this << "): " << rPosAry << ":" << nMaskColor); + sk_sp<SkSurface> tmpSurface = SkiaHelper::createSkSurface(rImage->width(), rImage->height()); + assert(tmpSurface); + SkCanvas* canvas = tmpSurface->getCanvas(); + canvas->clear(toSkColor(nMaskColor)); SkPaint paint; // Draw the color with the given mask. - // TODO figure out the right blend mode to avoid the temporary bitmap + // TODO figure out the right blend mode to avoid the temporary surface paint.setBlendMode(SkBlendMode::kDstOut); - SkCanvas canvas(tmpBitmap); - canvas.drawImage(&rImage, 0, 0, &paint); + canvas->drawImage(rImage, 0, 0, &paint); - drawBitmap(rPosAry, tmpBitmap); + drawImage(rPosAry, tmpSurface->makeImageSnapshot()); } std::shared_ptr<SalBitmap> SkiaSalGraphicsImpl::getBitmap(long nX, long nY, long nWidth, @@ -866,7 +852,7 @@ std::shared_ptr<SalBitmap> SkiaSalGraphicsImpl::getBitmap(long nX, long nY, long "getbitmap(" << this << "): " << Point(nX, nY) << "/" << Size(nWidth, nHeight)); mSurface->getCanvas()->flush(); sk_sp<SkImage> image = mSurface->makeImageSnapshot(SkIRect::MakeXYWH(nX, nY, nWidth, nHeight)); - return std::make_shared<SkiaSalBitmap>(*image); + return std::make_shared<SkiaSalBitmap>(image); } Color SkiaSalGraphicsImpl::getPixel(long nX, long nY) @@ -922,7 +908,7 @@ void SkiaSalGraphicsImpl::invert(basegfx::B2DPolygon const& rPoly, SalInvert eFl if (eFlags == SalInvert::N50) { // This creates 4x4 checker pattern bitmap - // TODO Cache the bitmap + // TODO Use SkiaHelper::createSkSurface() and cache the image SkBitmap aBitmap; aBitmap.allocN32Pixels(4, 4); const SkPMColor white = SkPreMultiplyARGB(0xFF, 0xFF, 0xFF, 0xFF); @@ -989,21 +975,37 @@ bool SkiaSalGraphicsImpl::drawAlphaBitmap(const SalTwoRect& rPosAry, const SalBi { assert(dynamic_cast<const SkiaSalBitmap*>(&rSourceBitmap)); assert(dynamic_cast<const SkiaSalBitmap*>(&rAlphaBitmap)); - SkBitmap tmpBitmap; - if (!tmpBitmap.tryAllocN32Pixels(rSourceBitmap.GetSize().Width(), - rSourceBitmap.GetSize().Height())) + sk_sp<SkSurface> tmpSurface = SkiaHelper::createSkSurface(rSourceBitmap.GetSize()); + if (!tmpSurface) return false; - SkCanvas canvas(tmpBitmap); + SkCanvas* canvas = tmpSurface->getCanvas(); SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); // copy as is, including alpha - canvas.drawBitmap(static_cast<const SkiaSalBitmap&>(rSourceBitmap).GetSkBitmap(), 0, 0, &paint); - paint.setBlendMode(SkBlendMode::kDstOut); - canvas.drawBitmap(static_cast<const SkiaSalBitmap&>(rAlphaBitmap).GetAlphaSkBitmap(), 0, 0, + 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); - drawBitmap(rPosAry, tmpBitmap); + 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", "drawimage(" << this << "): " << rPosAry << ":" << int(eBlendMode)); + mSurface->getCanvas()->drawImageRect(aImage, aSourceRect, aDestinationRect, &aPaint); + postDraw(); +} + void SkiaSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const SkBitmap& aBitmap, SkBlendMode eBlendMode) { @@ -1033,26 +1035,19 @@ bool SkiaSalGraphicsImpl::drawTransformedBitmap(const basegfx::B2DPoint& rNull, const SkiaSalBitmap& rSkiaBitmap = static_cast<const SkiaSalBitmap&>(rSourceBitmap); const SkiaSalBitmap* pSkiaAlphaBitmap = static_cast<const SkiaSalBitmap*>(pAlphaBitmap); - long nSourceWidth = rSourceBitmap.GetSize().Width(); - long nSourceHeight = rSourceBitmap.GetSize().Height(); - - SkBitmap aTemporaryBitmap; - if (!aTemporaryBitmap.tryAllocN32Pixels(nSourceWidth, nSourceHeight)) - { + sk_sp<SkSurface> tmpSurface = SkiaHelper::createSkSurface(rSourceBitmap.GetSize()); + if (!tmpSurface) return false; - } + // Combine bitmap + alpha bitmap into one temporary bitmap with alpha + SkCanvas* aCanvas = tmpSurface->getCanvas(); + SkPaint aPaint; + aPaint.setBlendMode(SkBlendMode::kSrc); // copy as is, including alpha + aCanvas->drawImage(rSkiaBitmap.GetSkImage(), 0, 0, &aPaint); + if (pSkiaAlphaBitmap != nullptr) { - // Combine bitmap + alpha bitmap into one temporary bitmap with alpha - SkCanvas aCanvas(aTemporaryBitmap); - SkPaint aPaint; - aPaint.setBlendMode(SkBlendMode::kSrc); // copy as is, including alpha - aCanvas.drawBitmap(rSkiaBitmap.GetSkBitmap(), 0, 0, &aPaint); - if (pSkiaAlphaBitmap != nullptr) - { - aPaint.setBlendMode(SkBlendMode::kDstOut); - aCanvas.drawBitmap(pSkiaAlphaBitmap->GetAlphaSkBitmap(), 0, 0, &aPaint); - } + aPaint.setBlendMode(SkBlendMode::kDstOut); // VCL alpha is one-minus-alpha + aCanvas->drawImage(pSkiaAlphaBitmap->GetAlphaSkImage(), 0, 0, &aPaint); } // setup the image transformation // using the rNull, rX, rY points as destinations for the (0,0), (0,Width), (Height,0) source points @@ -1075,7 +1070,7 @@ bool SkiaSalGraphicsImpl::drawTransformedBitmap(const basegfx::B2DPoint& rNull, { SkAutoCanvasRestore autoRestore(mSurface->getCanvas(), true); mSurface->getCanvas()->concat(aMatrix); - mSurface->getCanvas()->drawBitmap(aTemporaryBitmap, 0, 0); + mSurface->getCanvas()->drawImage(tmpSurface->makeImageSnapshot(), 0, 0); } postDraw(); @@ -1111,36 +1106,7 @@ bool SkiaSalGraphicsImpl::supportsOperation(OutDevSupportType eType) const void SkiaSalGraphicsImpl::dump(const char* file) const { assert(mSurface.get()); - mSurface->getCanvas()->flush(); - sk_sp<SkImage> image = mSurface->makeImageSnapshot(); - sk_sp<SkData> data = image->encodeToData(); - std::ofstream ostream(file, std::ios::binary); - ostream.write(static_cast<const char*>(data->data()), data->size()); -} - -void SkiaSalGraphicsImpl::dump(const SkBitmap& bitmap, const char* file) -{ - sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap); - sk_sp<SkData> data = image->encodeToData(); - std::ofstream ostream(file, std::ios::binary); - ostream.write(static_cast<const char*>(data->data()), data->size()); -} - -void SkiaSalGraphicsImpl::prefillSurface() -{ - // Pre-fill the surface with deterministic garbage. - SkBitmap bitmap; - bitmap.allocN32Pixels(2, 2); - SkPMColor* scanline; - scanline = bitmap.getAddr32(0, 0); - *scanline++ = SkPreMultiplyARGB(0xFF, 0xBF, 0x80, 0x40); - *scanline++ = SkPreMultiplyARGB(0xFF, 0x40, 0x80, 0xBF); - scanline = bitmap.getAddr32(0, 1); - *scanline++ = SkPreMultiplyARGB(0xFF, 0xE3, 0x5C, 0x13); - *scanline++ = SkPreMultiplyARGB(0xFF, 0x13, 0x5C, 0xE3); - SkPaint paint; - paint.setShader(bitmap.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat)); - mSurface->getCanvas()->drawPaint(paint); + SkiaHelper::dump(mSurface, file); } #endif diff --git a/vcl/skia/salbmp.cxx b/vcl/skia/salbmp.cxx index fb0c3608028d..d7fc3f6905c6 100644 --- a/vcl/skia/salbmp.cxx +++ b/vcl/skia/salbmp.cxx @@ -27,6 +27,9 @@ #include <SkCanvas.h> #include <SkImage.h> #include <SkPixelRef.h> +#include <SkSurface.h> + +#include <skia/utils.hxx> #ifdef DBG_UTIL #include <fstream> @@ -43,15 +46,18 @@ static bool isValidBitCount(sal_uInt16 nBitCount) || (nBitCount == 32); } -SkiaSalBitmap::SkiaSalBitmap(const SkImage& image) +SkiaSalBitmap::SkiaSalBitmap(const sk_sp<SkImage>& image) { - if (Create(Size(image.width(), image.height()), 32, BitmapPalette())) + if (Create(Size(image->width(), image->height()), 32, BitmapPalette())) { SkCanvas canvas(mBitmap); // TODO makeNonTextureImage() ? SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha - canvas.drawImage(&image, 0, 0, &paint); + canvas.drawImage(image, 0, 0, &paint); + // Also set up the cached image, often it will be immediately read again. + // TODO Maybe do the image -> bitmap conversion on demand? + mImage = image; } } @@ -291,9 +297,12 @@ bool SkiaSalBitmap::ConvertToGreyscale() return false; } -const SkBitmap& SkiaSalBitmap::GetSkBitmap() const +SkBitmap SkiaSalBitmap::GetAsSkBitmap() const { - if (mBuffer && mBitmap.drawsNothing()) + if (!mBitmap.drawsNothing()) + return mBitmap; + SkBitmap bitmap; + if (mBuffer) { if (mBitCount == 24) { @@ -311,12 +320,12 @@ const SkBitmap& SkiaSalBitmap::GetSkBitmap() const *dest++ = 0xff; } } - if (!const_cast<SkBitmap&>(mBitmap).installPixels( + if (!bitmap.installPixels( SkImageInfo::MakeS32(mSize.Width(), mSize.Height(), kOpaque_SkAlphaType), data.release(), mSize.Width() * 4, [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr)) abort(); - SAL_INFO("vcl.skia", "skbitmap(" << this << ")"); + bitmap.setImmutable(); } else { @@ -327,90 +336,105 @@ const SkBitmap& SkiaSalBitmap::GetSkBitmap() const = convertDataBitCount(mBuffer.get(), mSize.Width(), mSize.Height(), mBitCount, mScanlineSize, mPalette, GET_FORMAT); #undef GET_FORMAT - if (!const_cast<SkBitmap&>(mBitmap).installPixels( + if (!bitmap.installPixels( SkImageInfo::MakeS32(mSize.Width(), mSize.Height(), kOpaque_SkAlphaType), data.release(), mSize.Width() * 4, [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr)) abort(); - SAL_INFO("vcl.skia", "skbitmap(" << this << ")"); + bitmap.setImmutable(); } } - return mBitmap; + return bitmap; +} + +const sk_sp<SkImage>& SkiaSalBitmap::GetSkImage() const +{ + if (mImage) + return mImage; + sk_sp<SkSurface> surface = SkiaHelper::createSkSurface(mSize); + assert(surface); + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha + surface->getCanvas()->drawBitmap(GetAsSkBitmap(), 0, 0, &paint); + const_cast<sk_sp<SkImage>&>(mImage) = surface->makeImageSnapshot(); + SAL_INFO("vcl.skia", "getskimage(" << this << ")"); + return mImage; } -const SkBitmap& SkiaSalBitmap::GetAlphaSkBitmap() const +const sk_sp<SkImage>& SkiaSalBitmap::GetAlphaSkImage() const { - if (mAlphaBitmap.drawsNothing()) + if (mAlphaImage) + return mAlphaImage; + SkBitmap alphaBitmap; + if (mBuffer && mBitCount <= 8) { - if (mBuffer && mBitCount <= 8) + assert(mBuffer.get()); + verify(); + std::unique_ptr<sal_uInt8[]> data + = convertDataBitCount(mBuffer.get(), mSize.Width(), mSize.Height(), mBitCount, + mScanlineSize, mPalette, BitConvert::A8); + if (!alphaBitmap.installPixels( + SkImageInfo::MakeA8(mSize.Width(), mSize.Height()), data.release(), mSize.Width(), + [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr)) + abort(); + alphaBitmap.setImmutable(); + } + else + { + SkBitmap originalBitmap = GetAsSkBitmap(); + // To make things more interesting, some LO code creates masks as 24bpp, + // so we first need to convert to 8bit to be able to convert that to 8bit alpha. + SkBitmap* convertedBitmap = nullptr; + const SkBitmap* bitmap8 = &originalBitmap; + if (originalBitmap.colorType() != kGray_8_SkColorType) { - assert(mBuffer.get()); - verify(); - std::unique_ptr<sal_uInt8[]> data - = convertDataBitCount(mBuffer.get(), mSize.Width(), mSize.Height(), mBitCount, - mScanlineSize, mPalette, BitConvert::A8); - if (!const_cast<SkBitmap&>(mAlphaBitmap) - .installPixels( - SkImageInfo::MakeA8(mSize.Width(), mSize.Height()), data.release(), - mSize.Width(), - [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, - nullptr)) + convertedBitmap = new SkBitmap; + if (!convertedBitmap->tryAllocPixels(SkImageInfo::Make( + mSize.Width(), mSize.Height(), kGray_8_SkColorType, kOpaque_SkAlphaType))) abort(); - SAL_INFO("vcl.skia", "skalphabitmap(" << this << ")"); - } - else - { - GetSkBitmap(); // make sure we have mBitmap, in case (mBuffer && mBitCount > 8) - // To make things more interesting, some LO code creates masks as 24bpp, - // so we first need to convert to 8bit to be able to convert that to 8bit alpha. - SkBitmap* convertedBitmap = nullptr; - const SkBitmap* bitmap8 = &mBitmap; - if (mBitmap.colorType() != kGray_8_SkColorType) - { - convertedBitmap = new SkBitmap; - if (!convertedBitmap->tryAllocPixels(SkImageInfo::Make( - mSize.Width(), mSize.Height(), kGray_8_SkColorType, kOpaque_SkAlphaType))) - abort(); - SkCanvas canvas(*convertedBitmap); - SkPaint paint; - paint.setBlendMode(SkBlendMode::kSrc); // copy and convert depth - canvas.drawBitmap(mBitmap, 0, 0, &paint); - bitmap8 = convertedBitmap; - } - // Skia uses a bitmap as an alpha channel only if it's set as kAlpha_8_SkColorType. - // But in SalBitmap::Create() it's not quite clear if the 8-bit image will be used - // as a mask or as a real bitmap. So mBitmap is always kGray_8_SkColorType for 8bpp - // and mAlphaBitmap is kAlpha_8_SkColorType that can be used as a mask. - // Make mAlphaBitmap share mBitmap's data. - const_cast<SkBitmap&>(mAlphaBitmap) - .setInfo(bitmap8->info().makeColorType(kAlpha_8_SkColorType), bitmap8->rowBytes()); - const_cast<SkBitmap&>(mAlphaBitmap) - .setPixelRef(sk_ref_sp(bitmap8->pixelRef()), bitmap8->pixelRefOrigin().x(), - bitmap8->pixelRefOrigin().y()); - delete convertedBitmap; - SAL_INFO("vcl.skia", "skalphabitmap(" << this << ")"); - return mAlphaBitmap; + SkCanvas canvas(*convertedBitmap); + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); // copy and convert depth + canvas.drawBitmap(originalBitmap, 0, 0, &paint); + convertedBitmap->setImmutable(); + bitmap8 = convertedBitmap; } + // Skia uses a bitmap as an alpha channel only if it's set as kAlpha_8_SkColorType. + // But in SalBitmap::Create() it's not quite clear if the 8-bit image will be used + // as a mask or as a real bitmap. So mBitmap is always kGray_8_SkColorType for 8bpp + // and alphaBitmap is kAlpha_8_SkColorType that can be used as a mask. + // Make alphaBitmap share bitmap8's data. + alphaBitmap.setInfo( + bitmap8->info().makeColorType(kAlpha_8_SkColorType).makeAlphaType(kPremul_SkAlphaType), + bitmap8->rowBytes()); + alphaBitmap.setPixelRef(sk_ref_sp(bitmap8->pixelRef()), bitmap8->pixelRefOrigin().x(), + bitmap8->pixelRefOrigin().y()); + delete convertedBitmap; + alphaBitmap.setImmutable(); } - return mAlphaBitmap; + sk_sp<SkSurface> surface = SkiaHelper::createSkSurface(mSize, kAlpha_8_SkColorType); + assert(surface); + // https://bugs.chromium.org/p/skia/issues/detail?id=9692 + // Raster kAlpha_8_SkColorType surfaces need empty contents for SkBlendMode::kSrc. + if (!surface->getCanvas()->getGrContext()) + surface->getCanvas()->clear(SkColorSetARGB(0x00, 0x00, 0x00, 0x00)); + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha + surface->getCanvas()->drawBitmap(alphaBitmap, 0, 0, &paint); + const_cast<sk_sp<SkImage>&>(mAlphaImage) = surface->makeImageSnapshot(); + SAL_INFO("vcl.skia", "getalphaskbitmap(" << this << ")"); + return mAlphaImage; } -// Reset the cached bitmap allocated in GetSkBitmap(). +// Reset the cached images allocated in GetSkImage(). void SkiaSalBitmap::ResetCachedBitmap() { - mAlphaBitmap.reset(); - if (mBuffer) - mBitmap.reset(); + mAlphaImage.reset(); + mImage.reset(); } #ifdef DBG_UTIL -void SkiaSalBitmap::dump(const char* file) const -{ - sk_sp<SkImage> image = SkImage::MakeFromBitmap(GetSkBitmap()); - sk_sp<SkData> data = image->encodeToData(); - std::ofstream ostream(file, std::ios::binary); - ostream.write(static_cast<const char*>(data->data()), data->size()); -} +void SkiaSalBitmap::dump(const char* file) const { SkiaHelper::dump(GetSkImage(), file); } void SkiaSalBitmap::verify() const { diff --git a/vcl/skia/win/gdiimpl.cxx b/vcl/skia/win/gdiimpl.cxx index 5270a7c3af18..c680d21dde70 100644 --- a/vcl/skia/win/gdiimpl.cxx +++ b/vcl/skia/win/gdiimpl.cxx @@ -11,6 +11,7 @@ #include <win/saldata.hxx> #include <vcl/skia/SkiaHelper.hxx> +#include <skia/utils.hxx> #include <SkColorFilter.h> #include <SkPixelRef.h> @@ -150,7 +151,7 @@ void WinSkiaSalGraphicsImpl::DrawTextMask(CompatibleDC::Texture* pTexture, Color const SalTwoRect& rPosAry) { assert(dynamic_cast<SkiaCompatibleDC::Texture*>(pTexture)); - drawMask(rPosAry, *static_cast<const SkiaCompatibleDC::Texture*>(pTexture)->image, nMaskColor); + drawMask(rPosAry, static_cast<const SkiaCompatibleDC::Texture*>(pTexture)->image, nMaskColor); } SkiaCompatibleDC::SkiaCompatibleDC(SalGraphics& rGraphics, int x, int y, int width, int height) @@ -192,8 +193,16 @@ sk_sp<SkImage> SkiaCompatibleDC::getAsMaskImage() alpha.setInfo(bitmap8.info().makeColorType(kAlpha_8_SkColorType), bitmap8.rowBytes()); alpha.setPixelRef(sk_ref_sp(bitmap8.pixelRef()), bitmap8.pixelRefOrigin().x(), bitmap8.pixelRefOrigin().y()); - // TODO GPU? - return SkImage::MakeFromBitmap(alpha); + sk_sp<SkSurface> surface + = SkiaHelper::createSkSurface(alpha.width(), alpha.height(), kAlpha_8_SkColorType); + // https://bugs.chromium.org/p/skia/issues/detail?id=9692 + // Raster kAlpha_8_SkColorType surfaces need empty contents for SkBlendMode::kSrc. + if (!surface->getCanvas()->getGrContext()) + surface->getCanvas()->clear(SkColorSetARGB(0x00, 0x00, 0x00, 0x00)); + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha + surface->getCanvas()->drawBitmap(alpha, 0, 0, &paint); + return surface->makeImageSnapshot(); } sk_sp<SkImage> SkiaCompatibleDC::getAsImage() @@ -203,22 +212,21 @@ sk_sp<SkImage> SkiaCompatibleDC::getAsImage() kBGRA_8888_SkColorType, kUnpremul_SkAlphaType), mpData, maRects.mnSrcWidth * 4)) abort(); - SkBitmap bitmap; - if (!bitmap.tryAllocPixels(tmpBitmap.info())) - abort(); + sk_sp<SkSurface> surface = SkiaHelper::createSkSurface(tmpBitmap.width(), tmpBitmap.height()); SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha - SkCanvas canvas(bitmap); + SkCanvas* canvas = surface->getCanvas(); + canvas->save(); // The data we got is upside-down. SkMatrix matrix; matrix.preTranslate(0, maRects.mnSrcHeight); matrix.setConcat(matrix, SkMatrix::MakeScale(1, -1)); - canvas.concat(matrix); - canvas.drawBitmapRect(tmpBitmap, - SkRect::MakeXYWH(0, 0, maRects.mnSrcWidth, maRects.mnSrcHeight), - SkRect::MakeXYWH(0, 0, maRects.mnSrcWidth, maRects.mnSrcHeight), &paint); - // TODO GPU - return SkImage::MakeFromBitmap(bitmap); + canvas->concat(matrix); + canvas->drawBitmapRect(tmpBitmap, + SkRect::MakeXYWH(0, 0, maRects.mnSrcWidth, maRects.mnSrcHeight), + SkRect::MakeXYWH(0, 0, maRects.mnSrcWidth, maRects.mnSrcHeight), &paint); + canvas->restore(); + return surface->makeImageSnapshot(); } SkiaControlsCache::SkiaControlsCache() |