summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuboš Luňák <l.lunak@collabora.com>2020-03-18 11:38:42 +0100
committerLuboš Luňák <l.lunak@collabora.com>2020-03-18 14:54:26 +0100
commit4020c402526c472b71729085eeed094449fe00ed (patch)
tree7d3d37c27666a918e81a956a57af4473d13b59fc
parenta5d01fba3c5e166c42f9b1be53a458b05640c0b8 (diff)
make SkiaSalBitmap always use internal buffer for pixels
The latest chrome/m82 branch of Skia now always does a deep copy when creating SkImage from SkBitmap unless the source is immutable, which means a copy would be done or way or another. This makes the handling consistent, the SkBitmap is now used only for caching. Change-Id: I8031ab7f639baee6ad9c9708b35afea384a19f65 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/90689 Tested-by: Jenkins Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
-rw-r--r--vcl/inc/skia/salbmp.hxx26
-rw-r--r--vcl/skia/salbmp.cxx259
2 files changed, 124 insertions, 161 deletions
diff --git a/vcl/inc/skia/salbmp.hxx b/vcl/inc/skia/salbmp.hxx
index 4550680400d7..762e45c420b8 100644
--- a/vcl/inc/skia/salbmp.hxx
+++ b/vcl/inc/skia/salbmp.hxx
@@ -70,16 +70,16 @@ public:
#endif
private:
- // Reset the cached images allocated in GetSkImage()/GetAlphaSkImage().
- void ResetSkImages();
- // Call to ensure mBitmap or mBuffer have data (will convert from mImage
- // if necessary).
+ // Reset the cached images allocated in GetSkImage()/GetAlphaSkImage(),
+ // and also the SkBitmap allocated in GetAsSkBitmap().
+ void ResetCachedData();
+ // Call to ensure mBuffer has data (will convert from mImage if necessary).
void EnsureBitmapData();
void EnsureBitmapData() const { return const_cast<SkiaSalBitmap*>(this)->EnsureBitmapData(); }
// Like EnsureBitmapData(), but will also make any shared data unique.
// Call before changing the data.
void EnsureBitmapUniqueData();
- // Allocate mBitmap or mBuffer (with uninitialized contents).
+ // Allocate mBuffer (with uninitialized contents).
bool CreateBitmapData();
SkBitmap GetAsSkBitmap() const;
#ifdef DBG_UTIL
@@ -92,11 +92,10 @@ private:
friend inline std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& stream, const SkiaSalBitmap* bitmap)
{
- // B - has SkBitmap, D - has data buffer, I/i - has SkImage (on GPU/CPU),
+ // B - has SkBitmap, 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.isNull() ? "B" : "")
- << (bitmap->mBuffer.get() ? "D" : "")
<< (bitmap->mImage ? (bitmap->mImage->isTextureBacked() ? "I" : "i") : "")
<< (bitmap->mAlphaImage ? (bitmap->mAlphaImage->isTextureBacked() ? "A" : "a")
: "");
@@ -106,18 +105,17 @@ private:
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 mBuffer buffer, which normally stores pixels in the given format.
// 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
+ // There is no "master" storage that the other would be derived from. The usual
+ // mode of operation is that mBuffer holds 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
+ // mBuffer must be filled from it on demand if necessary by EnsureBitmapData().
boost::shared_ptr<sal_uInt8[]> mBuffer;
int mScanlineSize; // size of one row in mBuffer
+ SkBitmap mBitmap; // cached mBuffer, if needed
+ sk_sp<SkImage> mImage; // possibly GPU-backed
sk_sp<SkImage> mAlphaImage; // cached contents as alpha image, possibly GPU-backed
#ifdef DBG_UTIL
int mWriteAccessCount = 0; // number of write AcquireAccess() that have not been released
diff --git a/vcl/skia/salbmp.cxx b/vcl/skia/salbmp.cxx
index 6522709a5ecd..bb173b564e5c 100644
--- a/vcl/skia/salbmp.cxx
+++ b/vcl/skia/salbmp.cxx
@@ -53,8 +53,7 @@ static bool isValidBitCount(sal_uInt16 nBitCount)
SkiaSalBitmap::SkiaSalBitmap(const sk_sp<SkImage>& image)
{
- ResetSkImages();
- mBitmap.reset();
+ ResetCachedData();
mBuffer.reset();
mImage = image;
mPalette = BitmapPalette();
@@ -68,8 +67,7 @@ SkiaSalBitmap::SkiaSalBitmap(const sk_sp<SkImage>& image)
bool SkiaSalBitmap::Create(const Size& rSize, sal_uInt16 nBitCount, const BitmapPalette& rPal)
{
- ResetSkImages();
- mBitmap.reset();
+ ResetCachedData();
mBuffer.reset();
if (!isValidBitCount(nBitCount))
return false;
@@ -94,74 +92,35 @@ 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.
- // And even 8bpp is problematic, since Skia does not support palettes and a VCL bitmap can change
- // its grayscale status simply by changing the palette.
- // 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 (mBitCount)
+ // The pixels could be stored in SkBitmap, but 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. And even 8bpp is problematic, since Skia does not support palettes
+ // and a VCL bitmap can change its grayscale status simply by changing the palette.
+ // Moreover creating SkImage from SkBitmap does a data copy unless the bitmap is immutable.
+ // So just always store pixels in our buffer and convert as necessary.
+ int bitScanlineWidth;
+ if (o3tl::checked_multiply<int>(mSize.Width(), mBitCount, bitScanlineWidth))
{
- case 32:
- colorType = kN32_SkColorType;
- break;
- default:
- break;
+ SAL_WARN("vcl.skia", "checked multiply failed");
+ return false;
}
- if (colorType != kUnknown_SkColorType)
+ mScanlineSize = AlignedWidth4Bytes(bitScanlineWidth);
+ if (mScanlineSize != 0 && mSize.Height() != 0)
{
- // If vcl::BackendCapabilities::mbSupportsBitmap32 is set,
- // BitmapReadAccess::ImplSetAccessPointers() uses functions that use premultiplied
- // alpha. If not set, it would use functions that would read just RGB, so using
- // premultiplied alpha here would change those values.
-#if SKIA_USE_BITMAP32
- assert(ImplGetSVData()->mpDefInst->GetBackendCapabilities()->mbSupportsBitmap32);
- if (!mBitmap.tryAllocPixels(
- SkImageInfo::Make(mSize.Width(), mSize.Height(), colorType, kPremul_SkAlphaType)))
-#else
- assert(!ImplGetSVData()->mpDefInst->GetBackendCapabilities()->mbSupportsBitmap32);
- if (!mBitmap.tryAllocPixels(
- SkImageInfo::Make(mSize.Width(), mSize.Height(), colorType, kUnpremul_SkAlphaType)))
+ size_t allocate = mScanlineSize * mSize.Height();
+#ifdef DBG_UTIL
+ allocate += sizeof(CANARY);
#endif
- {
- return false;
- }
+ mBuffer = boost::make_shared<sal_uInt8[]>(allocate);
#ifdef DBG_UTIL
// fill with random garbage
- sal_uInt8* buffer = static_cast<sal_uInt8*>(mBitmap.getPixels());
- size_t size = mBitmap.rowBytes() * mBitmap.height();
- for (size_t i = 0; i < size; i++)
+ sal_uInt8* buffer = mBuffer.get();
+ for (size_t i = 0; i < allocate; i++)
buffer[i] = (i & 0xFF);
+ memcpy(buffer + allocate - sizeof(CANARY), CANARY, sizeof(CANARY));
#endif
}
- else
- {
- // Image formats not supported by Skia are stored in a buffer and converted as necessary.
- int bitScanlineWidth;
- if (o3tl::checked_multiply<int>(mSize.Width(), mBitCount, bitScanlineWidth))
- {
- SAL_WARN("vcl.skia", "checked multiply failed");
- return false;
- }
- mScanlineSize = AlignedWidth4Bytes(bitScanlineWidth);
- if (mScanlineSize != 0 && mSize.Height() != 0)
- {
- size_t allocate = mScanlineSize * mSize.Height();
-#ifdef DBG_UTIL
- allocate += sizeof(CANARY);
-#endif
- mBuffer = boost::make_shared<sal_uInt8[]>(allocate);
-#ifdef DBG_UTIL
- // fill with random garbage
- sal_uInt8* buffer = mBuffer.get();
- for (size_t i = 0; i < allocate; i++)
- buffer[i] = (i & 0xFF);
- memcpy(buffer + allocate - sizeof(CANARY), CANARY, sizeof(CANARY));
-#endif
- }
- }
return true;
}
@@ -217,8 +176,7 @@ void SkiaSalBitmap::Destroy()
#ifdef DBG_UTIL
assert(mWriteAccessCount == 0);
#endif
- ResetSkImages();
- mBitmap.reset();
+ ResetCachedData();
mBuffer.reset();
}
@@ -232,12 +190,12 @@ BitmapBuffer* SkiaSalBitmap::AcquireBuffer(BitmapAccessMode nMode)
{
case BitmapAccessMode::Write:
EnsureBitmapUniqueData();
- if (mBitmap.isNull() && !mBuffer)
+ if (!mBuffer)
return nullptr;
break;
case BitmapAccessMode::Read:
EnsureBitmapData();
- if (mBitmap.isNull() && !mBuffer)
+ if (!mBuffer)
return nullptr;
break;
case BitmapAccessMode::Info:
@@ -253,16 +211,8 @@ BitmapBuffer* SkiaSalBitmap::AcquireBuffer(BitmapAccessMode nMode)
buffer->mnHeight = mSize.Height();
buffer->mnBitCount = mBitCount;
buffer->maPalette = mPalette;
- if (mBuffer)
- {
- buffer->mpBits = mBuffer.get();
- buffer->mnScanlineSize = mScanlineSize;
- }
- else
- {
- buffer->mpBits = static_cast<sal_uInt8*>(mBitmap.getPixels());
- buffer->mnScanlineSize = mBitmap.rowBytes();
- }
+ buffer->mpBits = mBuffer.get();
+ buffer->mnScanlineSize = mScanlineSize;
switch (mBitCount)
{
case 1:
@@ -287,9 +237,11 @@ BitmapBuffer* SkiaSalBitmap::AcquireBuffer(BitmapAccessMode nMode)
break;
}
case 32:
- buffer->mnFormat = mBitmap.colorType() == kRGBA_8888_SkColorType
- ? ScanlineFormat::N32BitTcRgba
- : ScanlineFormat::N32BitTcBgra;
+#define GET_FORMAT \
+ (kN32_SkColorType == kBGRA_8888_SkColorType ? ScanlineFormat::N32BitTcBgra \
+ : ScanlineFormat::N32BitTcRgba)
+ buffer->mnFormat = GET_FORMAT;
+#undef GET_FORMAT
break;
default:
abort();
@@ -311,7 +263,7 @@ void SkiaSalBitmap::ReleaseBuffer(BitmapBuffer* pBuffer, BitmapAccessMode nMode)
--mWriteAccessCount;
#endif
mPalette = pBuffer->maPalette;
- ResetSkImages();
+ ResetCachedData();
}
// Are there any more ground movements underneath us ?
assert(pBuffer->mnWidth == mSize.Width());
@@ -367,9 +319,8 @@ bool SkiaSalBitmap::Scale(const double& rScaleX, const double& rScaleY, BmpScale
GetSkImage(), SkRect::MakeXYWH(0, 0, mSize.Width(), mSize.Height()),
SkRect::MakeXYWH(0, 0, newSize.Width(), newSize.Height()), &paint);
// This will get generated from mImage if needed.
- mBitmap.reset();
mBuffer.reset();
- ResetSkImages();
+ ResetCachedData();
mImage = surface->makeImageSnapshot();
mSize = newSize;
return true;
@@ -400,14 +351,33 @@ SkBitmap SkiaSalBitmap::GetAsSkBitmap() const
#ifdef DBG_UTIL
assert(mWriteAccessCount == 0);
#endif
- EnsureBitmapData();
if (!mBitmap.isNull())
return mBitmap;
+ EnsureBitmapData();
SkiaZone zone;
SkBitmap bitmap;
if (mBuffer)
{
- if (mBitCount == 24)
+ if (mBitCount == 32)
+ {
+ // Make a copy, the bitmap should be immutable (otherwise converting it
+ // to SkImage will make a copy anyway).
+ const size_t bytes = mSize.Height() * mScanlineSize;
+ std::unique_ptr<sal_uInt8[]> data(new sal_uInt8[bytes]);
+ memcpy(data.get(), mBuffer.get(), bytes);
+#if SKIA_USE_BITMAP32
+ SkAlphaType alphaType = kPremul_SkAlphaType;
+#else
+ SkAlphaType alphaType = kUnpremul_SkAlphaType;
+#endif
+ if (!bitmap.installPixels(
+ SkImageInfo::MakeS32(mSize.Width(), mSize.Height(), alphaType), data.release(),
+ mSize.Width() * 4,
+ [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
+ abort();
+ bitmap.setImmutable();
+ }
+ else if (mBitCount == 24)
{
// Convert 24bpp RGB/BGR to 32bpp RGBA/BGRA.
std::unique_ptr<sal_uInt8[]> data(new sal_uInt8[mSize.Height() * mSize.Width() * 4]);
@@ -430,6 +400,8 @@ SkBitmap SkiaSalBitmap::GetAsSkBitmap() const
abort();
bitmap.setImmutable();
}
+ // Skia has a format for 8bit grayscale SkBitmap, but it seems to cause a problem
+ // with our PNG loader (tdf#121120), so convert it to RGBA below as well.
else
{
// Use a macro to hide an unreachable code warning.
@@ -447,7 +419,8 @@ SkBitmap SkiaSalBitmap::GetAsSkBitmap() const
bitmap.setImmutable();
}
}
- return bitmap;
+ const_cast<SkBitmap&>(mBitmap) = bitmap;
+ return mBitmap;
}
const sk_sp<SkImage>& SkiaSalBitmap::GetSkImage() const
@@ -513,10 +486,7 @@ const sk_sp<SkImage>& SkiaSalBitmap::GetAlphaSkImage() const
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.
+ // So create such SkBitmap and make it share bitmap8's data.
alphaBitmap.setInfo(
bitmap8->info().makeColorType(kAlpha_8_SkColorType).makeAlphaType(kPremul_SkAlphaType),
bitmap8->rowBytes());
@@ -541,85 +511,79 @@ const sk_sp<SkImage>& SkiaSalBitmap::GetAlphaSkImage() const
void SkiaSalBitmap::EnsureBitmapData()
{
- if (!mBitmap.isNull() || mBuffer)
+ if (mBuffer)
return;
if (!mImage)
return;
SkiaZone zone;
if (!CreateBitmapData())
abort();
- if (!mBitmap.isNull())
+ assert(mBitmap.isNull());
+ SkAlphaType alphaType = kUnpremul_SkAlphaType;
+#if SKIA_USE_BITMAP32
+ if (mBitCount == 32)
+ alphaType = kPremul_SkAlphaType;
+#endif
+ if (!mBitmap.tryAllocPixels(SkImageInfo::MakeS32(mSize.Width(), mSize.Height(), alphaType)))
+ abort();
+ SkCanvas canvas(mBitmap);
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
+ canvas.drawImage(mImage, 0, 0, &paint);
+ canvas.flush();
+ mBitmap.setImmutable();
+ assert(mBuffer != nullptr);
+ if (mBitCount == 32)
{
- 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 << ")");
+ for (long y = 0; y < mSize.Height(); ++y)
+ {
+ const uint8_t* src = static_cast<uint8_t*>(mBitmap.getAddr(0, y));
+ sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
+ memcpy(dest, src, mScanlineSize);
+ }
}
- else
+ else if (mBitCount == 24) // non-paletted
{
- 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)
{
- for (long y = 0; y < mSize.Height(); ++y)
+ const uint8_t* src = static_cast<uint8_t*>(mBitmap.getAddr(0, y));
+ sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
+ for (long x = 0; x < mSize.Width(); ++x)
{
- 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
- }
+ *dest++ = *src++;
+ *dest++ = *src++;
+ *dest++ = *src++;
+ ++src; // skip alpha
}
}
- else
+ }
+ else
+ {
+ std::unique_ptr<vcl::ScanlineWriter> pWriter
+ = vcl::ScanlineWriter::Create(mBitCount, mPalette);
+ for (long y = 0; y < mSize.Height(); ++y)
{
- 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*>(mBitmap.getAddr(0, y));
+ sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
+ pWriter->nextLine(dest);
+ for (long x = 0; x < mSize.Width(); ++x)
{
- 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);
- }
+ 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 << ")");
}
+ verify();
+ SAL_INFO("vcl.skia", "ensurebitmapdata(" << this << ")");
}
void SkiaSalBitmap::EnsureBitmapUniqueData()
{
EnsureBitmapData();
- // TODO thread safety?
- if (mBitmap.pixelRef() && !mBitmap.pixelRef()->unique())
- {
- // SkBitmap copies share pixels, so make a deep copy.
- SkBitmap newBitmap;
- if (!newBitmap.tryAllocPixels(mBitmap.info()))
- abort();
- newBitmap.writePixels(mBitmap.pixmap());
- assert(newBitmap.getPixels() != mBitmap.getPixels());
- mBitmap = newBitmap;
- }
+ mBitmap.reset(); // just reset, this function is called before modifying mBuffer
if (mBuffer.use_count() > 1)
{
sal_uInt32 allocate = mScanlineSize * mSize.Height();
@@ -633,9 +597,10 @@ void SkiaSalBitmap::EnsureBitmapUniqueData()
}
}
-void SkiaSalBitmap::ResetSkImages()
+void SkiaSalBitmap::ResetCachedData()
{
SkiaZone zone;
+ mBitmap.reset();
mAlphaImage.reset();
mImage.reset();
}