[Libreoffice-commits] core.git: solenv/clang-format vcl/inc vcl/opengl vcl/skia

Luboš Luňák (via logerrit) logerrit at kemper.freedesktop.org
Mon Jan 6 10:12:39 UTC 2020


 solenv/clang-format/blacklist |    1 
 vcl/inc/scanlinewriter.hxx    |   90 ++++++++++++++++++++++++++
 vcl/inc/skia/salbmp.hxx       |   27 ++++++-
 vcl/opengl/salbmp.cxx         |   59 -----------------
 vcl/skia/salbmp.cxx           |  145 ++++++++++++++++++++++++++++++++++--------
 5 files changed, 233 insertions(+), 89 deletions(-)

New commits:
commit aab9dafdae868b9e55c143cdc08598895f19f63d
Author:     Luboš Luňák <l.lunak at collabora.com>
AuthorDate: Fri Dec 13 17:07:44 2019 +0100
Commit:     Luboš Luňák <l.lunak at collabora.com>
CommitDate: Mon Jan 6 11:12:04 2020 +0100

    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 at collabora.com>

diff --git a/solenv/clang-format/blacklist b/solenv/clang-format/blacklist
index 84500af7c2ef..eae0f3714d25 100644
--- a/solenv/clang-format/blacklist
+++ b/solenv/clang-format/blacklist
@@ -17320,6 +17320,7 @@ vcl/inc/saltimer.hxx
 vcl/inc/salusereventlist.hxx
 vcl/inc/salvd.hxx
 vcl/inc/salwtype.hxx
+vcl/inc/scanlinewriter.hxx
 vcl/inc/schedulerimpl.hxx
 vcl/inc/scrptrun.h
 vcl/inc/scrwnd.hxx
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;


More information about the Libreoffice-commits mailing list