[Libreoffice-commits] core.git: 2 commits - external/skia vcl/inc vcl/skia
LuboÅ¡ LuÅák (via logerrit)
logerrit at kemper.freedesktop.org
Tue Apr 7 09:53:10 UTC 2020
external/skia/UnpackedTarball_skia.mk | 1
external/skia/extend-rgb-to-rgba.patch.0 | 23 +++
vcl/inc/skia/salbmp.hxx | 7 +
vcl/skia/salbmp.cxx | 198 ++++++++++++++++++++++++-------
4 files changed, 187 insertions(+), 42 deletions(-)
New commits:
commit c27d2e9145be8972e5d2174fb3f317dc08930074
Author: Luboš Luňák <l.lunak at collabora.com>
AuthorDate: Thu Apr 2 12:33:57 2020 +0200
Commit: Luboš Luňák <l.lunak at collabora.com>
CommitDate: Tue Apr 7 11:52:43 2020 +0200
optimize bit depth conversions to/from Skia where possible
Skia has an optimized function for RGB->RGBA conversion that
we are going to do often because 24bpp is the most common LO
image format. The function is private, so patch that.
Also optimize 32bpp->8bpp conversion with gray palette.
Change-Id: I48b06f80d5ca9e1c8e3ee51da61e87541bd83d5a
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/91768
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak at collabora.com>
diff --git a/external/skia/UnpackedTarball_skia.mk b/external/skia/UnpackedTarball_skia.mk
index 99344871cb9d..da0a2a7a0547 100644
--- a/external/skia/UnpackedTarball_skia.mk
+++ b/external/skia/UnpackedTarball_skia.mk
@@ -32,6 +32,7 @@ skia_patches := \
windows-force-unicode-api.patch.0 \
operator-eq-bool.patch.0 \
fix-without-gl.patch.0 \
+ extend-rgb-to-rgba.patch.0 \
$(eval $(call gb_UnpackedTarball_set_patchlevel,skia,1))
diff --git a/external/skia/extend-rgb-to-rgba.patch.0 b/external/skia/extend-rgb-to-rgba.patch.0
new file mode 100644
index 000000000000..f68dbab96336
--- /dev/null
+++ b/external/skia/extend-rgb-to-rgba.patch.0
@@ -0,0 +1,23 @@
+diff --git a/include/core/SkSwizzle.h b/include/core/SkSwizzle.h
+index 61e93b2da7..c19063bb91 100644
+--- ./include/core/SkSwizzle.h
++++ ./include/core/SkSwizzle.h
+@@ -16,4 +16,6 @@
+ */
+ SK_API void SkSwapRB(uint32_t* dest, const uint32_t* src, int count);
+
++SK_API void SkExtendRGBToRGBA(uint32_t* dest, const uint8_t* src, int count);
++
+ #endif
+diff --git a/src/core/SkSwizzle.cpp b/src/core/SkSwizzle.cpp
+index 301b0184f1..6e6dd27558 100644
+--- ./src/core/SkSwizzle.cpp
++++ ./src/core/SkSwizzle.cpp
+@@ -12,3 +12,7 @@
+ void SkSwapRB(uint32_t* dest, const uint32_t* src, int count) {
+ SkOpts::RGBA_to_BGRA(dest, src, count);
+ }
++
++void SkExtendRGBToRGBA(uint32_t* dest, const uint8_t* src, int count) {
++ SkOpts::RGB_to_RGB1(dest, src, count);
++}
diff --git a/vcl/skia/salbmp.cxx b/vcl/skia/salbmp.cxx
index f0bffb500194..c000b391eaa2 100644
--- a/vcl/skia/salbmp.cxx
+++ b/vcl/skia/salbmp.cxx
@@ -32,6 +32,7 @@
#include <SkImage.h>
#include <SkPixelRef.h>
#include <SkSurface.h>
+#include <SkSwizzle.h>
#include <skia/utils.hxx>
#include <skia/zone.hxx>
@@ -387,19 +388,16 @@ SkBitmap SkiaSalBitmap::GetAsSkBitmap() const
else if (mBitCount == 24)
{
// Convert 24bpp RGB/BGR to 32bpp RGBA/BGRA.
- std::unique_ptr<sal_uInt8[]> data(
- new sal_uInt8[mPixelsSize.Height() * mPixelsSize.Width() * 4]);
- sal_uInt8* dest = data.get();
+ std::unique_ptr<uint32_t[]> data(
+ new uint32_t[mPixelsSize.Height() * mPixelsSize.Width()]);
+ uint32_t* dest = data.get();
for (long y = 0; y < mPixelsSize.Height(); ++y)
{
const sal_uInt8* src = mBuffer.get() + mScanlineSize * y;
- for (long x = 0; x < mPixelsSize.Width(); ++x)
- {
- *dest++ = *src++;
- *dest++ = *src++;
- *dest++ = *src++;
- *dest++ = 0xff;
- }
+ // This also works as BGR to BGRA (the function extends 3 bytes to 4
+ // by adding 0xFF alpha, so position of B and R doesn't matter).
+ SkExtendRGBToRGBA(dest, src, mPixelsSize.Width());
+ dest += mPixelsSize.Width();
}
if (!bitmap.installPixels(
SkImageInfo::MakeS32(mPixelsSize.Width(), mPixelsSize.Height(),
@@ -647,6 +645,17 @@ void SkiaSalBitmap::EnsureBitmapData()
}
}
}
+ else if (mBitCount == 8 && mPalette.IsGreyPalette())
+ {
+ 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;
+ // no actual data conversion, use one color channel as the gray value
+ for (long x = 0; x < mSize.Width(); ++x)
+ dest[x] = src[x * 4];
+ }
+ }
else
{
std::unique_ptr<vcl::ScanlineWriter> pWriter
commit 912773d521fd6b8ff90d27f7bd3f5a46fd0df582
Author: Luboš Luňák <l.lunak at collabora.com>
AuthorDate: Tue Mar 24 16:21:44 2020 +0100
Commit: Luboš Luňák <l.lunak at collabora.com>
CommitDate: Tue Apr 7 11:52:32 2020 +0200
use delayed scaling in SalSkiaBitmap
This allows doing the scaling at least in some cases on the GPU.
Pending scaling is detected by mSize (logical size) != mPixelSize
(actual size of pixel data).
Change-Id: I8adef13750c195fdbe48c9167737a0c31cda66d5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/91767
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak at collabora.com>
diff --git a/vcl/inc/skia/salbmp.hxx b/vcl/inc/skia/salbmp.hxx
index 762e45c420b8..a5f264ae2ba1 100644
--- a/vcl/inc/skia/salbmp.hxx
+++ b/vcl/inc/skia/salbmp.hxx
@@ -73,6 +73,8 @@ private:
// Reset the cached images allocated in GetSkImage()/GetAlphaSkImage(),
// and also the SkBitmap allocated in GetAsSkBitmap().
void ResetCachedData();
+ // Sets the data only as SkImage (will be converted as needed).
+ void ResetToSkImage(sk_sp<SkImage> image);
// Call to ensure mBuffer has data (will convert from mImage if necessary).
void EnsureBitmapData();
void EnsureBitmapData() const { return const_cast<SkiaSalBitmap*>(this)->EnsureBitmapData(); }
@@ -117,6 +119,11 @@ private:
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
+ // Actual scaling triggered by scale() is done on-demand. This is the size of the pixel
+ // data in mBuffer, if it differs from mSize, then there is a scaling operation pending.
+ Size mPixelsSize;
+ SkFilterQuality mScaleQuality = kHigh_SkFilterQuality; // quality for on-demand scaling
+ bool mDisableScale = false; // used to block our scale()
#ifdef DBG_UTIL
int mWriteAccessCount = 0; // number of write AcquireAccess() that have not been released
#endif
diff --git a/vcl/skia/salbmp.cxx b/vcl/skia/salbmp.cxx
index 27de5fc82508..f0bffb500194 100644
--- a/vcl/skia/salbmp.cxx
+++ b/vcl/skia/salbmp.cxx
@@ -58,7 +58,7 @@ SkiaSalBitmap::SkiaSalBitmap(const sk_sp<SkImage>& image)
mImage = image;
mPalette = BitmapPalette();
mBitCount = 32;
- mSize = Size(image->width(), image->height());
+ mSize = mPixelsSize = Size(image->width(), image->height());
#ifdef DBG_UTIL
mWriteAccessCount = 0;
#endif
@@ -73,14 +73,14 @@ bool SkiaSalBitmap::Create(const Size& rSize, sal_uInt16 nBitCount, const Bitmap
return false;
mPalette = rPal;
mBitCount = nBitCount;
- mSize = rSize;
+ mSize = mPixelsSize = rSize;
#ifdef DBG_UTIL
mWriteAccessCount = 0;
#endif
if (!CreateBitmapData())
{
mBitCount = 0;
- mSize = Size();
+ mSize = mPixelsSize = Size();
mPalette = BitmapPalette();
return false;
}
@@ -146,6 +146,7 @@ bool SkiaSalBitmap::Create(const SalBitmap& rSalBmp, sal_uInt16 nNewBitCount)
mPalette = src.mPalette;
mBitCount = src.mBitCount;
mSize = src.mSize;
+ mPixelsSize = src.mPixelsSize;
mScanlineSize = src.mScanlineSize;
#ifdef DBG_UTIL
mWriteAccessCount = 0;
@@ -156,9 +157,7 @@ bool SkiaSalBitmap::Create(const SalBitmap& rSalBmp, sal_uInt16 nNewBitCount)
// about it and rely on EnsureBitmapData() doing the conversion from mImage
// if needed, even if that may need unnecessary to- and from- SkImage
// conversion.
- GetSkImage(); // create mImage
- mBitmap.reset();
- mBuffer.reset();
+ ResetToSkImage(GetSkImage());
}
SAL_INFO("vcl.skia.trace", "create(" << this << "): (" << &src << ")");
return true;
@@ -282,7 +281,7 @@ bool SkiaSalBitmap::GetSystemData(BitmapSystemData&)
return false;
}
-bool SkiaSalBitmap::ScalingSupported() const { return true; }
+bool SkiaSalBitmap::ScalingSupported() const { return !mDisableScale; }
bool SkiaSalBitmap::Scale(const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag)
{
@@ -297,32 +296,39 @@ bool SkiaSalBitmap::Scale(const double& rScaleX, const double& rScaleY, BmpScale
SAL_INFO("vcl.skia.trace", "scale(" << this << "): " << mSize << "->" << newSize << ":"
<< static_cast<int>(nScaleFlag));
- SkPaint paint;
+ // The idea here is that the actual scaling will be delayed until the result
+ // is actually needed. Usually the scaled bitmap will be drawn somewhere,
+ // so delaying will mean the scaling can be done as a part of GetSkImage().
+ // That means it can be GPU-accelerated, while done here directly it would need
+ // to be either done by CPU, or with the CPU->GPU->CPU roundtrip required
+ // by GPU-accelereated scaling.
+ // Pending scaling is detected by 'mSize != mPixelsSize'.
+ SkFilterQuality currentQuality;
switch (nScaleFlag)
{
case BmpScaleFlag::Fast:
- paint.setFilterQuality(kNone_SkFilterQuality);
+ currentQuality = kNone_SkFilterQuality;
break;
case BmpScaleFlag::Default:
- paint.setFilterQuality(kMedium_SkFilterQuality);
+ currentQuality = kMedium_SkFilterQuality;
break;
case BmpScaleFlag::BestQuality:
- paint.setFilterQuality(kHigh_SkFilterQuality);
+ currentQuality = kHigh_SkFilterQuality;
break;
default:
return false;
}
- sk_sp<SkSurface> surface = SkiaHelper::createSkSurface(newSize);
- assert(surface);
- paint.setBlendMode(SkBlendMode::kSrc); // draw as is, including alpha
- surface->getCanvas()->drawImageRect(
- 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.
- mBuffer.reset();
- ResetCachedData();
- mImage = surface->makeImageSnapshot();
+ // if there is already one scale() pending, use the lowest quality of all requested
+ static_assert(kMedium_SkFilterQuality < kHigh_SkFilterQuality);
+ mScaleQuality = std::min(mScaleQuality, currentQuality);
+ // scaling will be actually done on-demand when needed, the need will be recognized
+ // by mSize != mPixelsSize
mSize = newSize;
+ // Do not reset cached data if mImage is possibly the only data we have.
+ if (mBuffer)
+ ResetCachedData();
+ // The rest will be handled when the scaled bitmap is actually needed,
+ // such as in EnsureBitmapData() or GetSkImage().
return true;
}
@@ -354,6 +360,7 @@ SkBitmap SkiaSalBitmap::GetAsSkBitmap() const
if (!mBitmap.isNull())
return mBitmap;
EnsureBitmapData();
+ assert(mSize == mPixelsSize); // data has already been scaled if needed
SkiaZone zone;
SkBitmap bitmap;
if (mBuffer)
@@ -362,7 +369,7 @@ SkBitmap SkiaSalBitmap::GetAsSkBitmap() const
{
// 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;
+ const size_t bytes = mPixelsSize.Height() * mScanlineSize;
std::unique_ptr<sal_uInt8[]> data(new sal_uInt8[bytes]);
memcpy(data.get(), mBuffer.get(), bytes);
#if SKIA_USE_BITMAP32
@@ -371,8 +378,8 @@ SkBitmap SkiaSalBitmap::GetAsSkBitmap() const
SkAlphaType alphaType = kUnpremul_SkAlphaType;
#endif
if (!bitmap.installPixels(
- SkImageInfo::MakeS32(mSize.Width(), mSize.Height(), alphaType), data.release(),
- mSize.Width() * 4,
+ SkImageInfo::MakeS32(mPixelsSize.Width(), mPixelsSize.Height(), alphaType),
+ data.release(), mPixelsSize.Width() * 4,
[](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
abort();
bitmap.setImmutable();
@@ -380,12 +387,13 @@ SkBitmap SkiaSalBitmap::GetAsSkBitmap() const
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]);
+ std::unique_ptr<sal_uInt8[]> data(
+ new sal_uInt8[mPixelsSize.Height() * mPixelsSize.Width() * 4]);
sal_uInt8* dest = data.get();
- for (long y = 0; y < mSize.Height(); ++y)
+ for (long y = 0; y < mPixelsSize.Height(); ++y)
{
const sal_uInt8* src = mBuffer.get() + mScanlineSize * y;
- for (long x = 0; x < mSize.Width(); ++x)
+ for (long x = 0; x < mPixelsSize.Width(); ++x)
{
*dest++ = *src++;
*dest++ = *src++;
@@ -394,8 +402,9 @@ SkBitmap SkiaSalBitmap::GetAsSkBitmap() const
}
}
if (!bitmap.installPixels(
- SkImageInfo::MakeS32(mSize.Width(), mSize.Height(), kOpaque_SkAlphaType),
- data.release(), mSize.Width() * 4,
+ SkImageInfo::MakeS32(mPixelsSize.Width(), mPixelsSize.Height(),
+ kOpaque_SkAlphaType),
+ data.release(), mPixelsSize.Width() * 4,
[](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
abort();
bitmap.setImmutable();
@@ -408,12 +417,13 @@ SkBitmap SkiaSalBitmap::GetAsSkBitmap() const
#define GET_FORMAT \
(kN32_SkColorType == kBGRA_8888_SkColorType ? BitConvert::BGRA : BitConvert::RGBA)
std::unique_ptr<sal_uInt8[]> data
- = convertDataBitCount(mBuffer.get(), mSize.Width(), mSize.Height(), mBitCount,
- mScanlineSize, mPalette, GET_FORMAT);
+ = convertDataBitCount(mBuffer.get(), mPixelsSize.Width(), mPixelsSize.Height(),
+ mBitCount, mScanlineSize, mPalette, GET_FORMAT);
#undef GET_FORMAT
if (!bitmap.installPixels(
- SkImageInfo::MakeS32(mSize.Width(), mSize.Height(), kOpaque_SkAlphaType),
- data.release(), mSize.Width() * 4,
+ SkImageInfo::MakeS32(mPixelsSize.Width(), mPixelsSize.Height(),
+ kOpaque_SkAlphaType),
+ data.release(), mPixelsSize.Width() * 4,
[](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
abort();
bitmap.setImmutable();
@@ -429,7 +439,28 @@ const sk_sp<SkImage>& SkiaSalBitmap::GetSkImage() const
assert(mWriteAccessCount == 0);
#endif
if (mImage)
+ {
+ if (mImage->width() != mSize.Width() || mImage->height() != mSize.Height())
+ {
+ assert(!mBuffer); // This code should be only called if only mImage holds data.
+ SkiaZone zone;
+ sk_sp<SkSurface> surface = SkiaHelper::createSkSurface(mSize);
+ assert(surface);
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
+ paint.setFilterQuality(mScaleQuality);
+ surface->getCanvas()->drawImageRect(
+ mImage, SkRect::MakeWH(mImage->width(), mImage->height()),
+ SkRect::MakeWH(mSize.Width(), mSize.Height()), &paint);
+ SAL_INFO("vcl.skia.trace", "getskimage(" << this << "): image scaled "
+ << Size(mImage->width(), mImage->height())
+ << "->" << mSize << ":"
+ << static_cast<int>(mScaleQuality));
+ SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
+ thisPtr->mImage = surface->makeImageSnapshot();
+ }
return mImage;
+ }
SkiaZone zone;
sk_sp<SkSurface> surface = SkiaHelper::createSkSurface(mSize);
assert(surface);
@@ -447,10 +478,14 @@ const sk_sp<SkImage>& SkiaSalBitmap::GetAlphaSkImage() const
assert(mWriteAccessCount == 0);
#endif
if (mAlphaImage)
+ {
+ assert(mSize == mPixelsSize); // data has already been scaled if needed
return mAlphaImage;
+ }
SkiaZone zone;
// TODO can we convert directly mImage -> mAlphaImage?
EnsureBitmapData();
+ assert(mSize == mPixelsSize); // data has already been scaled if needed
SkBitmap alphaBitmap;
if (mBuffer && mBitCount <= 8)
{
@@ -512,7 +547,49 @@ const sk_sp<SkImage>& SkiaSalBitmap::GetAlphaSkImage() const
void SkiaSalBitmap::EnsureBitmapData()
{
if (mBuffer)
+ {
+ if (mSize != mPixelsSize) // pending scaling?
+ {
+ // This will be pixel->pixel scaling, use VCL algorithm, it should be faster than Skia
+ // (no need to possibly convert bpp, it's multithreaded,...).
+ std::shared_ptr<SkiaSalBitmap> src = std::make_shared<SkiaSalBitmap>();
+ if (!src->Create(*this))
+ abort();
+ // force 'src' to use VCL's scaling
+ src->mDisableScale = true;
+ src->mSize = src->mPixelsSize;
+ Bitmap bitmap(src);
+ BmpScaleFlag scaleFlag;
+ switch (mScaleQuality)
+ {
+ case kNone_SkFilterQuality:
+ scaleFlag = BmpScaleFlag::Fast;
+ break;
+ case kMedium_SkFilterQuality:
+ scaleFlag = BmpScaleFlag::Default;
+ break;
+ case kHigh_SkFilterQuality:
+ scaleFlag = BmpScaleFlag::BestQuality;
+ break;
+ default:
+ abort();
+ }
+ bitmap.Scale(mSize, scaleFlag);
+ assert(dynamic_cast<const SkiaSalBitmap*>(bitmap.ImplGetSalBitmap().get()));
+ const SkiaSalBitmap* dest
+ = static_cast<const SkiaSalBitmap*>(bitmap.ImplGetSalBitmap().get());
+ assert(dest->mSize == dest->mPixelsSize);
+ assert(dest->mSize == mSize);
+ SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): pixels scaled "
+ << mPixelsSize << "->" << mSize << ":"
+ << static_cast<int>(mScaleQuality));
+ Destroy();
+ Create(*dest);
+ mDisableScale = false;
+ }
return;
+ }
+ // Try to fill mBuffer from mImage.
if (!mImage)
return;
SkiaZone zone;
@@ -529,7 +606,20 @@ void SkiaSalBitmap::EnsureBitmapData()
SkCanvas canvas(mBitmap);
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
- canvas.drawImage(mImage, 0, 0, &paint);
+ if (mSize != mPixelsSize) // pending scaling?
+ {
+ paint.setFilterQuality(mScaleQuality);
+ canvas.drawImageRect(mImage,
+ SkRect::MakeWH(mPixelsSize.getWidth(), mPixelsSize.getHeight()),
+ SkRect::MakeWH(mSize.getWidth(), mSize.getHeight()), &paint);
+ SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): image scaled " << mPixelsSize
+ << "->" << mSize << ":"
+ << static_cast<int>(mScaleQuality));
+ mPixelsSize = mSize;
+ mScaleQuality = kNone_SkFilterQuality;
+ }
+ else
+ canvas.drawImage(mImage, 0, 0, &paint);
canvas.flush();
mBitmap.setImmutable();
assert(mBuffer != nullptr);
@@ -600,11 +690,23 @@ void SkiaSalBitmap::EnsureBitmapUniqueData()
void SkiaSalBitmap::ResetCachedData()
{
SkiaZone zone;
+ // There may be a case when only mImage is set and CreatBitmapData() will create
+ // mBuffer from it if needed, in that case ResetToSkImage() should be used.
+ assert(mBuffer.get() || !mImage);
mBitmap.reset();
mAlphaImage.reset();
mImage.reset();
}
+void SkiaSalBitmap::ResetToSkImage(sk_sp<SkImage> image)
+{
+ SkiaZone zone;
+ mBuffer.reset();
+ mBitmap.reset();
+ mAlphaImage.reset();
+ mImage = image;
+}
+
#ifdef DBG_UTIL
void SkiaSalBitmap::dump(const char* file) const
{
@@ -613,6 +715,7 @@ void SkiaSalBitmap::dump(const char* file) const
SkBitmap saveBitmap = mBitmap;
bool resetBuffer = !mBuffer;
int saveWriteAccessCount = mWriteAccessCount;
+ Size savePrescaleSize = mPixelsSize;
SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
// avoid possible assert
thisPtr->mWriteAccessCount = 0;
@@ -624,13 +727,15 @@ void SkiaSalBitmap::dump(const char* file) const
thisPtr->mImage = saveImage;
thisPtr->mAlphaImage = saveAlphaImage;
thisPtr->mWriteAccessCount = saveWriteAccessCount;
+ thisPtr->mPixelsSize = savePrescaleSize;
}
void SkiaSalBitmap::verify() const
{
if (!mBuffer)
return;
- size_t canary = mScanlineSize * mSize.Height();
+ // Use mPixelsSize, that describes the size of the actual data.
+ size_t canary = mScanlineSize * mPixelsSize.Height();
assert(memcmp(mBuffer.get() + canary, CANARY, sizeof(CANARY)) == 0);
}
More information about the Libreoffice-commits
mailing list