diff options
author | Luboš Luňák <l.lunak@collabora.com> | 2019-12-13 17:07:44 +0100 |
---|---|---|
committer | Luboš Luňák <l.lunak@collabora.com> | 2020-01-06 11:12:04 +0100 |
commit | aab9dafdae868b9e55c143cdc08598895f19f63d (patch) | |
tree | 744be99a517b1854bff0ae8b7b85174e2601d13e /vcl | |
parent | c9253b9007059496521f24ef8348e80bb120e1e6 (diff) |
convert SkImage -> SkBitmap only on demand
This should possibly save some unneeded conversions.
Change-Id: Ice8a186f13a0e61bee260cf910f8a4d0538ef974
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/85542
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/inc/scanlinewriter.hxx | 90 | ||||
-rw-r--r-- | vcl/inc/skia/salbmp.hxx | 27 | ||||
-rw-r--r-- | vcl/opengl/salbmp.cxx | 59 | ||||
-rw-r--r-- | vcl/skia/salbmp.cxx | 145 |
4 files changed, 232 insertions, 89 deletions
diff --git a/vcl/inc/scanlinewriter.hxx b/vcl/inc/scanlinewriter.hxx new file mode 100644 index 000000000000..a5b892b02b6b --- /dev/null +++ b/vcl/inc/scanlinewriter.hxx @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_VCL_INC_SCANLINEWRITER_HXX +#define INCLUDED_VCL_INC_SCANLINEWRITER_HXX + +#include <vcl/bitmap.hxx> +#include <vcl/BitmapPalette.hxx> + +namespace vcl +{ + +// Write color information for 1, 4 and 8 bit palette bitmap scanlines. +class ScanlineWriter +{ + BitmapPalette& maPalette; + sal_uInt8 const mnColorsPerByte; // number of colors that are stored in one byte + sal_uInt8 const mnColorBitSize; // number of bits a color takes + sal_uInt8 const mnColorBitMask; // bit mask used to isolate the color + sal_uInt8* mpCurrentScanline; + long mnX; + +public: + + ScanlineWriter(BitmapPalette& aPalette, sal_Int8 nColorsPerByte) + : maPalette(aPalette) + , mnColorsPerByte(nColorsPerByte) + , mnColorBitSize(8 / mnColorsPerByte) // bit size is number of bit in a byte divided by number of colors per byte (8 / 2 = 4 for 4-bit) + , mnColorBitMask((1 << mnColorBitSize) - 1) // calculate the bit mask from the bit size + , mpCurrentScanline(nullptr) + , mnX(0) + {} + + static std::unique_ptr<ScanlineWriter> Create(sal_uInt16 nBits, BitmapPalette& aPalette) + { + switch(nBits) + { + case 1: + return std::make_unique<ScanlineWriter>(aPalette, 8); + case 4: + return std::make_unique<ScanlineWriter>(aPalette, 2); + case 8: + return std::make_unique<ScanlineWriter>(aPalette, 1); + default: + abort(); + } + } + + void writeRGB(sal_uInt8 nR, sal_uInt8 nG, sal_uInt8 nB) + { + // calculate to which index we will write + long nScanlineIndex = mnX / mnColorsPerByte; + + // calculate the number of shifts to get the color information to the right place + long nShift = (8 - mnColorBitSize) - ((mnX % mnColorsPerByte) * mnColorBitSize); + + sal_uInt16 nColorIndex = maPalette.GetBestIndex(BitmapColor(nR, nG, nB)); + mpCurrentScanline[nScanlineIndex] &= ~(mnColorBitMask << nShift); // clear + mpCurrentScanline[nScanlineIndex] |= (nColorIndex & mnColorBitMask) << nShift; // set + mnX++; + } + + void nextLine(sal_uInt8* pScanline) + { + mnX = 0; + mpCurrentScanline = pScanline; + } +}; + +} // namespace vcl + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/skia/salbmp.hxx b/vcl/inc/skia/salbmp.hxx index df3664f26f1c..a0fa2d900c78 100644 --- a/vcl/inc/skia/salbmp.hxx +++ b/vcl/inc/skia/salbmp.hxx @@ -68,7 +68,14 @@ public: #endif private: - void ResetCachedBitmap(); + // Reset the cached images allocated in GetSkImage()/GetAlphaSkImage(). + void ResetSkImages(); + // Call to ensure mBitmap or mBuffer have data (will convert from mImage + // if necessary). + void EnsureBitmapData(); + void EnsureBitmapData() const { return const_cast<SkiaSalBitmap*>(this)->EnsureBitmapData(); } + // Allocate mBitmap or mBuffer (with uninitialized contents). + bool CreateBitmapData(); SkBitmap GetAsSkBitmap() const; #ifdef DBG_UTIL void verify() const; @@ -90,17 +97,25 @@ private: : ""); } - // Bitmap pixels, if format is supported by Skia. If not, mBuffer is used. - SkBitmap mBitmap; BitmapPalette mPalette; - int mBitCount; // bpp + int mBitCount = 0; // bpp Size mSize; + // The contents of the bitmap may be stored in several different ways: + // As SkBitmap, if format is supported by Skia. + // As mBuffer buffer, if format is not supported by Skia. + // As SkImage, as cached GPU-backed data, but sometimes also a result of some operation. + // There is no "master" storage that the others would be derived from. The usual + // mode of operation is that mBitmap or mBuffer hold the data, mImage is created + // on demand as GPU-backed cached data by calling GetSkImage(), and the cached mImage + // is reset by ResetCachedImage(). But sometimes only mImage will be set and in that case + // mBitmap/mBuffer must be filled from it on demand if necessary by EnsureBitmapData(). + SkBitmap mBitmap; + sk_sp<SkImage> mImage; // possibly GPU-backed 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 #ifdef DBG_UTIL - int mWriteAccessCount; // number of write AcquireAccess() that have not been released + int mWriteAccessCount = 0; // number of write AcquireAccess() that have not been released #endif }; diff --git a/vcl/opengl/salbmp.cxx b/vcl/opengl/salbmp.cxx index 3a033c4afef0..3b500b0945dc 100644 --- a/vcl/opengl/salbmp.cxx +++ b/vcl/opengl/salbmp.cxx @@ -32,6 +32,7 @@ #include <salgdi.hxx> #include <vcleventlisteners.hxx> #include <vcl/lazydelete.hxx> +#include <scanlinewriter.hxx> #include <o3tl/make_shared.hxx> @@ -333,47 +334,6 @@ void lclInstantiateTexture(OpenGLTexture& rTexture, const int nWidth, const int rTexture = OpenGLTexture (nWidth, nHeight, nFormat, nType, pData); } -// Write color information for 1 and 4 bit palette bitmap scanlines. -class ScanlineWriter -{ - BitmapPalette& maPalette; - sal_uInt8 const mnColorsPerByte; // number of colors that are stored in one byte - sal_uInt8 const mnColorBitSize; // number of bits a color takes - sal_uInt8 const mnColorBitMask; // bit mask used to isolate the color - sal_uInt8* mpCurrentScanline; - long mnX; - -public: - ScanlineWriter(BitmapPalette& aPalette, sal_Int8 nColorsPerByte) - : maPalette(aPalette) - , mnColorsPerByte(nColorsPerByte) - , mnColorBitSize(8 / mnColorsPerByte) // bit size is number of bit in a byte divided by number of colors per byte (8 / 2 = 4 for 4-bit) - , mnColorBitMask((1 << mnColorBitSize) - 1) // calculate the bit mask from the bit size - , mpCurrentScanline(nullptr) - , mnX(0) - {} - - void writeRGB(sal_uInt8 nR, sal_uInt8 nG, sal_uInt8 nB) - { - // calculate to which index we will write - long nScanlineIndex = mnX / mnColorsPerByte; - - // calculate the number of shifts to get the color information to the right place - long nShift = (8 - mnColorBitSize) - ((mnX % mnColorsPerByte) * mnColorBitSize); - - sal_uInt16 nColorIndex = maPalette.GetBestIndex(BitmapColor(nR, nG, nB)); - mpCurrentScanline[nScanlineIndex] &= ~(mnColorBitMask << nShift); // clear - mpCurrentScanline[nScanlineIndex] |= (nColorIndex & mnColorBitMask) << nShift; // set - mnX++; - } - - void nextLine(sal_uInt8* pScanline) - { - mnX = 0; - mpCurrentScanline = pScanline; - } -}; - } // end anonymous namespace Size OpenGLSalBitmap::GetSize() const @@ -475,22 +435,7 @@ bool OpenGLSalBitmap::ReadTexture() maTexture.Read(nFormat, nType, pBuffer); sal_uInt32 nSourceBytesPerRow = lclBytesPerRow(24, mnWidth); - std::unique_ptr<ScanlineWriter> pWriter; - switch(mnBits) - { - case 1: - pWriter.reset(new ScanlineWriter(maPalette, 8)); - break; - case 4: - pWriter.reset(new ScanlineWriter(maPalette, 2)); - break; - case 8: - pWriter.reset(new ScanlineWriter(maPalette, 1)); - break; - default: - abort(); - } - + std::unique_ptr<vcl::ScanlineWriter> pWriter = vcl::ScanlineWriter::Create(mnBits, maPalette); for (int y = 0; y < mnHeight; ++y) { sal_uInt8* pSource = &pBuffer[y * nSourceBytesPerRow]; diff --git a/vcl/skia/salbmp.cxx b/vcl/skia/salbmp.cxx index b2edfb509c44..9bc2d0ecf217 100644 --- a/vcl/skia/salbmp.cxx +++ b/vcl/skia/salbmp.cxx @@ -23,6 +23,7 @@ #include <tools/helpers.hxx> #include <salgdi.hxx> +#include <scanlinewriter.hxx> #include <SkCanvas.h> #include <SkImage.h> @@ -48,25 +49,47 @@ static bool isValidBitCount(sal_uInt16 nBitCount) SkiaSalBitmap::SkiaSalBitmap(const sk_sp<SkImage>& image) { - 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); - // Also set up the cached image, often it will be immediately read again. - // TODO Maybe do the image -> bitmap conversion on demand? - mImage = image; - } + ResetSkImages(); + mBitmap.reset(); + mBuffer.reset(); + mImage = image; + mPalette = BitmapPalette(); + mBitCount = 32; + mSize = Size(image->width(), image->height()); +#ifdef DBG_UTIL + mWriteAccessCount = 0; +#endif + SAL_INFO("vcl.skia", "bitmapfromimage(" << this << ")"); } bool SkiaSalBitmap::Create(const Size& rSize, sal_uInt16 nBitCount, const BitmapPalette& rPal) { + ResetSkImages(); mBitmap.reset(); mBuffer.reset(); if (!isValidBitCount(nBitCount)) return false; + mPalette = rPal; + mBitCount = nBitCount; + mSize = rSize; +#ifdef DBG_UTIL + mWriteAccessCount = 0; +#endif + if (!CreateBitmapData()) + { + mBitCount = 0; + mSize = Size(); + mPalette = BitmapPalette(); + return false; + } + SAL_INFO("vcl.skia", "create(" << this << ")"); + return true; +} + +bool SkiaSalBitmap::CreateBitmapData() +{ + assert(mBitmap.isNull()); + assert(!mBuffer); // Skia only supports 8bit gray, 16bit and 32bit formats (e.g. 24bpp is actually stored as 32bpp). // But some of our code accessing the bitmap assumes that when it asked for 24bpp, the format // really will be 24bpp (e.g. the png loader), so we cannot use SkBitmap to store the data. @@ -75,7 +98,7 @@ bool SkiaSalBitmap::Create(const Size& rSize, sal_uInt16 nBitCount, const Bitmap // So basically use Skia only for 32bpp bitmaps. // TODO what is the performance impact of handling 24bpp ourselves instead of in Skia? SkColorType colorType = kUnknown_SkColorType; - switch (nBitCount) + switch (mBitCount) { case 32: colorType = kN32_SkColorType; @@ -94,7 +117,7 @@ bool SkiaSalBitmap::Create(const Size& rSize, sal_uInt16 nBitCount, const Bitmap // functions that merely read RGB without A, so the premultiplied values would // not be converted back to unpremultiplied values. if (!mBitmap.tryAllocPixels( - SkImageInfo::Make(rSize.Width(), rSize.Height(), colorType, kUnpremul_SkAlphaType))) + SkImageInfo::Make(mSize.Width(), mSize.Height(), colorType, kUnpremul_SkAlphaType))) { return false; } @@ -110,16 +133,16 @@ bool SkiaSalBitmap::Create(const Size& rSize, sal_uInt16 nBitCount, const Bitmap { // Image formats not supported by Skia are stored in a buffer and converted as necessary. int bitScanlineWidth; - if (o3tl::checked_multiply<int>(rSize.Width(), nBitCount, bitScanlineWidth)) + if (o3tl::checked_multiply<int>(mSize.Width(), mBitCount, bitScanlineWidth)) { SAL_WARN("vcl.skia", "checked multiply failed"); return false; } mScanlineSize = AlignedWidth4Bytes(bitScanlineWidth); sal_uInt8* buffer = nullptr; - if (mScanlineSize != 0 && rSize.Height() != 0) + if (mScanlineSize != 0 && mSize.Height() != 0) { - size_t allocate = mScanlineSize * rSize.Height(); + size_t allocate = mScanlineSize * mSize.Height(); #ifdef DBG_UTIL allocate += sizeof(CANARY); #endif @@ -133,13 +156,6 @@ bool SkiaSalBitmap::Create(const Size& rSize, sal_uInt16 nBitCount, const Bitmap } mBuffer.reset(buffer); } - mPalette = rPal; - mBitCount = nBitCount; - mSize = rSize; -#ifdef DBG_UTIL - mWriteAccessCount = 0; -#endif - SAL_INFO("vcl.skia", "create(" << this << ")"); return true; } @@ -159,6 +175,8 @@ bool SkiaSalBitmap::Create(const SalBitmap& rSalBmp, sal_uInt16 nNewBitCount) if (nNewBitCount == src.GetBitCount()) { mBitmap = src.mBitmap; // TODO unshare? + mImage = src.mImage; // TODO unshare? + mAlphaImage = src.mAlphaImage; // TODO unshare? mPalette = src.mPalette; mBitCount = src.mBitCount; mSize = src.mSize; @@ -199,6 +217,7 @@ void SkiaSalBitmap::Destroy() #ifdef DBG_UTIL assert(mWriteAccessCount == 0); #endif + ResetSkImages(); mBitmap.reset(); mBuffer.reset(); } @@ -212,6 +231,7 @@ BitmapBuffer* SkiaSalBitmap::AcquireBuffer(BitmapAccessMode nMode) #ifndef DBG_UTIL (void)nMode; // TODO #endif + EnsureBitmapData(); if (mBitmap.drawsNothing() && !mBuffer) return nullptr; #ifdef DBG_UTIL @@ -282,7 +302,7 @@ void SkiaSalBitmap::ReleaseBuffer(BitmapBuffer* pBuffer, BitmapAccessMode nMode) --mWriteAccessCount; #endif mPalette = pBuffer->maPalette; - ResetCachedBitmap(); + ResetSkImages(); } // Are there any more ground movements underneath us ? assert(pBuffer->mnWidth == mSize.Width()); @@ -337,6 +357,7 @@ SkBitmap SkiaSalBitmap::GetAsSkBitmap() const #ifdef DBG_UTIL assert(mWriteAccessCount == 0); #endif + EnsureBitmapData(); if (!mBitmap.drawsNothing()) return mBitmap; SkBitmap bitmap; @@ -409,6 +430,8 @@ const sk_sp<SkImage>& SkiaSalBitmap::GetAlphaSkImage() const #endif if (mAlphaImage) return mAlphaImage; + // TODO can we convert directly mImage -> mAlphaImage? + EnsureBitmapData(); SkBitmap alphaBitmap; if (mBuffer && mBitCount <= 8) { @@ -470,8 +493,73 @@ const sk_sp<SkImage>& SkiaSalBitmap::GetAlphaSkImage() const return mAlphaImage; } -// Reset the cached images allocated in GetSkImage(). -void SkiaSalBitmap::ResetCachedBitmap() +void SkiaSalBitmap::EnsureBitmapData() +{ + if (!mBitmap.drawsNothing() || mBuffer) + return; + if (!mImage) + return; + if (!CreateBitmapData()) + abort(); + if (!mBitmap.isNull()) + { + SkCanvas canvas(mBitmap); + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha + canvas.drawImage(mImage, 0, 0, &paint); + SAL_INFO("vcl.skia", "ensurebitmapdata1(" << this << ")"); + } + else + { + SkBitmap tmpBitmap; + if (!tmpBitmap.tryAllocPixels(SkImageInfo::Make(mSize.Width(), mSize.Height(), + kN32_SkColorType, kUnpremul_SkAlphaType))) + abort(); + SkCanvas canvas(tmpBitmap); + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha + canvas.drawImage(mImage, 0, 0, &paint); + assert(mBuffer != nullptr); + if (mBitCount == 24) // non-paletted + { + for (long y = 0; y < mSize.Height(); ++y) + { + const uint8_t* src = static_cast<uint8_t*>(tmpBitmap.getAddr(0, y)); + sal_uInt8* dest = mBuffer.get() + mScanlineSize * y; + for (long x = 0; x < mSize.Width(); ++x) + { + *dest++ = *src++; + *dest++ = *src++; + *dest++ = *src++; + ++src; // skip alpha + } + } + } + else + { + std::unique_ptr<vcl::ScanlineWriter> pWriter + = vcl::ScanlineWriter::Create(mBitCount, mPalette); + for (long y = 0; y < mSize.Height(); ++y) + { + const uint8_t* src = static_cast<uint8_t*>(tmpBitmap.getAddr(0, y)); + sal_uInt8* dest = mBuffer.get() + mScanlineSize * y; + pWriter->nextLine(dest); + for (long x = 0; x < mSize.Width(); ++x) + { + sal_uInt8 r = *src++; + sal_uInt8 g = *src++; + sal_uInt8 b = *src++; + ++src; // skip alpha + pWriter->writeRGB(r, g, b); + } + } + } + verify(); + SAL_INFO("vcl.skia", "ensurebitmapdata2(" << this << ")"); + } +} + +void SkiaSalBitmap::ResetSkImages() { mAlphaImage.reset(); mImage.reset(); @@ -482,12 +570,17 @@ void SkiaSalBitmap::dump(const char* file) const { sk_sp<SkImage> saveImage = mImage; sk_sp<SkImage> saveAlphaImage = mAlphaImage; + SkBitmap saveBitmap = mBitmap; + bool resetBuffer = !mBuffer; int saveWriteAccessCount = mWriteAccessCount; SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this); // avoid possible assert thisPtr->mWriteAccessCount = 0; SkiaHelper::dump(GetSkImage(), file); // restore old state, so that debugging doesn't affect it + thisPtr->mBitmap = saveBitmap; + if (resetBuffer) + thisPtr->mBuffer.reset(); thisPtr->mImage = saveImage; thisPtr->mAlphaImage = saveAlphaImage; thisPtr->mWriteAccessCount = saveWriteAccessCount; |