[Libreoffice-commits] core.git: vcl/qa vcl/source

Luboš Luňák (via logerrit) logerrit at kemper.freedesktop.org
Thu Apr 15 19:15:36 UTC 2021


 vcl/qa/cppunit/GraphicTest.cxx           |   28 +++++
 vcl/source/filter/graphicfilter.cxx      |   73 +++++++++++--
 vcl/source/filter/png/PngImageReader.cxx |  164 ++++++++++++++++++++-----------
 vcl/source/filter/png/png.hxx            |   33 ++++++
 4 files changed, 230 insertions(+), 68 deletions(-)

New commits:
commit b94a2dc95dce8c67ddb9f01f7bd91da2a5759d9c
Author:     Luboš Luňák <l.lunak at collabora.com>
AuthorDate: Thu Apr 15 17:39:48 2021 +0200
Commit:     Luboš Luňák <l.lunak at collabora.com>
CommitDate: Thu Apr 15 21:14:49 2021 +0200

    threaded loading also for png loader
    
    Similarly how the jpg loader can load in a thread. This somewhat
    complicates the loader, since bitmaps cannot be created in threads
    that do not hold the SolarMutex, and SolarMutex is held by
    GraphicFilter::MakeGraphicsAvailableThreaded(), which cannot
    release it (otherwise something else might grab it and change
    things in the background).
    
    Change-Id: Idd711ac379b64a49ef8b8386964507b446d16944
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/114165
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lunak at collabora.com>

diff --git a/vcl/qa/cppunit/GraphicTest.cxx b/vcl/qa/cppunit/GraphicTest.cxx
index a64ea67cdb78..d323a6cf5869 100644
--- a/vcl/qa/cppunit/GraphicTest.cxx
+++ b/vcl/qa/cppunit/GraphicTest.cxx
@@ -80,6 +80,8 @@ private:
     void testLoadPCX();
     void testLoadEPS();
 
+    void testAvailableThreaded();
+
     CPPUNIT_TEST_SUITE(GraphicTest);
     CPPUNIT_TEST(testUnloadedGraphic);
     CPPUNIT_TEST(testUnloadedGraphicLoading);
@@ -114,6 +116,8 @@ private:
     CPPUNIT_TEST(testLoadPCX);
     CPPUNIT_TEST(testLoadEPS);
 
+    CPPUNIT_TEST(testAvailableThreaded);
+
     CPPUNIT_TEST_SUITE_END();
 };
 
@@ -247,6 +251,15 @@ Graphic loadGraphic(std::u16string_view const& rFilename)
     return aGraphic;
 }
 
+Graphic importUnloadedGraphic(std::u16string_view const& rFilename)
+{
+    test::Directories aDirectories;
+    OUString aFilename = aDirectories.getURLFromSrc(DATA_DIRECTORY) + rFilename;
+    SvFileStream aFileStream(aFilename, StreamMode::READ);
+    GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+    return rGraphicFilter.ImportUnloadedGraphic(aFileStream);
+}
+
 void GraphicTest::testUnloadedGraphic()
 {
     // make unloaded test graphic
@@ -1219,6 +1232,21 @@ void GraphicTest::testLoadEPS()
     CPPUNIT_ASSERT_EQUAL(GraphicType::GdiMetafile, aGraphic.GetType());
 }
 
+void GraphicTest::testAvailableThreaded()
+{
+    Graphic jpgGraphic1 = importUnloadedGraphic(u"TypeDetectionExample.jpg");
+    Graphic jpgGraphic2 = importUnloadedGraphic(u"Exif1.jpg");
+    Graphic pngGraphic1 = importUnloadedGraphic(u"TypeDetectionExample.png");
+    Graphic pngGraphic2 = importUnloadedGraphic(u"testBasicMorphology.png");
+    std::vector<Graphic*> graphics = { &jpgGraphic1, &jpgGraphic2, &pngGraphic1, &pngGraphic2 };
+    for (auto& graphic : graphics)
+        CPPUNIT_ASSERT(!graphic->isAvailable());
+    GraphicFilter& graphicFilter = GraphicFilter::GetGraphicFilter();
+    graphicFilter.MakeGraphicsAvailableThreaded(graphics);
+    for (auto& graphic : graphics)
+        CPPUNIT_ASSERT(graphic->isAvailable());
+}
+
 } // namespace
 
 CPPUNIT_TEST_SUITE_REGISTRATION(GraphicTest);
diff --git a/vcl/source/filter/graphicfilter.cxx b/vcl/source/filter/graphicfilter.cxx
index cd29c5088a0e..9de2f801c790 100644
--- a/vcl/source/filter/graphicfilter.cxx
+++ b/vcl/source/filter/graphicfilter.cxx
@@ -44,6 +44,7 @@
 #include "igif/gifread.hxx"
 #include <vcl/pdfread.hxx>
 #include "jpeg/jpeg.hxx"
+#include "png/png.hxx"
 #include "ixbm/xbmread.hxx"
 #include <filter/XpmReader.hxx>
 #include <filter/TiffReader.hxx>
@@ -545,6 +546,9 @@ struct GraphicImportContext
     std::shared_ptr<Graphic> m_pGraphic;
     /// Write pixel data using this access.
     std::unique_ptr<BitmapScopedWriteAccess> m_pAccess;
+    std::unique_ptr<AlphaScopedWriteAccess> m_pAlphaAccess;
+    // Need to have an AlphaMask instance to keep its lifetime.
+    AlphaMask mAlphaMask;
     /// Signals if import finished correctly.
     ErrCode m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
     /// Original graphic format.
@@ -581,10 +585,20 @@ void GraphicImportTask::doWork()
 
 void GraphicImportTask::doImport(GraphicImportContext& rContext)
 {
-    if (!ImportJPEG(*rContext.m_pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::UseExistingBitmap, rContext.m_pAccess.get()))
-        rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
-    else
-        rContext.m_eLinkType = GfxLinkType::NativeJpg;
+    if(rContext.m_eLinkType == GfxLinkType::NativeJpg)
+    {
+        if (!ImportJPEG(*rContext.m_pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::UseExistingBitmap, rContext.m_pAccess.get()))
+            rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
+    }
+    else if(rContext.m_eLinkType == GfxLinkType::NativePng)
+    {
+        if (!vcl::ImportPNG(*rContext.m_pStream, *rContext.m_pGraphic,
+            rContext.m_nImportFlags | GraphicFilterImportFlags::UseExistingBitmap,
+            rContext.m_pAccess.get(), rContext.m_pAlphaAccess.get()))
+        {
+            rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
+        }
+    }
 }
 
 void GraphicFilter::ImportGraphics(std::vector< std::shared_ptr<Graphic> >& rGraphics, std::vector< std::unique_ptr<SvStream> > vStreams)
@@ -620,11 +634,10 @@ void GraphicFilter::ImportGraphics(std::vector< std::shared_ptr<Graphic> >& rGra
 
                 if (aFilterName.equalsIgnoreAsciiCase(IMP_JPEG))
                 {
+                    rContext.m_eLinkType = GfxLinkType::NativeJpg;
                     rContext.m_nImportFlags = GraphicFilterImportFlags::SetLogsizeForJpeg;
 
-                    if (!ImportJPEG( *rContext.m_pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::OnlyCreateBitmap, nullptr))
-                        rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
-                    else
+                    if (ImportJPEG( *rContext.m_pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::OnlyCreateBitmap, nullptr))
                     {
                         Bitmap& rBitmap = const_cast<Bitmap&>(rContext.m_pGraphic->GetBitmapExRef().GetBitmap());
                         rContext.m_pAccess = std::make_unique<BitmapScopedWriteAccess>(rBitmap);
@@ -634,6 +647,40 @@ void GraphicFilter::ImportGraphics(std::vector< std::shared_ptr<Graphic> >& rGra
                         else
                             GraphicImportTask::doImport(rContext);
                     }
+                    else
+                        rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
+                }
+                else if (aFilterName.equalsIgnoreAsciiCase(IMP_PNG))
+                {
+                    rContext.m_eLinkType = GfxLinkType::NativePng;
+
+                    if (vcl::ImportPNG( *rContext.m_pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::OnlyCreateBitmap, nullptr, nullptr))
+                    {
+                        const BitmapEx& rBitmapEx = rContext.m_pGraphic->GetBitmapExRef();
+                        Bitmap& rBitmap = const_cast<Bitmap&>(rBitmapEx.GetBitmap());
+                        rContext.m_pAccess = std::make_unique<BitmapScopedWriteAccess>(rBitmap);
+                        // The png reader either uses only Bitmap or Bitmap+AlphaMask.
+                        assert(rBitmapEx.IsAlpha() || !rBitmapEx.IsTransparent());
+                        if(rBitmapEx.IsAlpha())
+                        {
+                            // The separate alpha bitmap causes a number of complications. Not only
+                            // we need to have an extra bitmap access for it, but we also need
+                            // to keep an AlphaMask instance in the context. This is because
+                            // BitmapEx internally keeps Bitmap and not AlphaMask (because the Bitmap
+                            // may be also a mask, not alpha). So BitmapEx::GetAlpha() returns
+                            // a temporary, and direct access to the Bitmap wouldn't work
+                            // with AlphaScopedBitmapAccess. *sigh*
+                            rContext.mAlphaMask = rBitmapEx.GetAlpha();
+                            rContext.m_pAlphaAccess = std::make_unique<AlphaScopedWriteAccess>(rContext.mAlphaMask);
+                        }
+                        rContext.m_pStream->Seek(rContext.m_nStreamBegin);
+                        if (bThreads)
+                            rSharedPool.pushTask(std::make_unique<GraphicImportTask>(pTag, rContext));
+                        else
+                            GraphicImportTask::doImport(rContext);
+                    }
+                    else
+                        rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
                 }
                 else
                     rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
@@ -646,7 +693,10 @@ void GraphicFilter::ImportGraphics(std::vector< std::shared_ptr<Graphic> >& rGra
     // Process data after import.
     for (auto& rContext : aContexts)
     {
+        if(rContext.m_pAlphaAccess) // Need to move the AlphaMask back to the BitmapEx.
+            *rContext.m_pGraphic = BitmapEx( rContext.m_pGraphic->GetBitmapExRef().GetBitmap(), rContext.mAlphaMask );
         rContext.m_pAccess.reset();
+        rContext.m_pAlphaAccess.reset();
 
         if (rContext.m_nStatus == ERRCODE_NONE && (rContext.m_eLinkType != GfxLinkType::NONE) && !rContext.m_pGraphic->GetReaderContext())
         {
@@ -686,16 +736,17 @@ void GraphicFilter::ImportGraphics(std::vector< std::shared_ptr<Graphic> >& rGra
 
 void GraphicFilter::MakeGraphicsAvailableThreaded(std::vector<Graphic*>& graphics)
 {
-    // Graphic::makeAvailable() is not thread-safe. Only the jpeg loader is, so here
-    // we process only jpeg images that also have their stream data, load new Graphic's
+    // Graphic::makeAvailable() is not thread-safe. Only the jpeg and png loaders are, so here
+    // we process only jpeg and png images that also have their stream data, load new Graphic's
     // from them and then update the passed objects using them.
     std::vector< Graphic* > toLoad;
     for(auto graphic : graphics)
     {
         // Need to use GetSharedGfxLink, to access the pointer without copying.
         if(!graphic->isAvailable() && graphic->IsGfxLink()
-            && graphic->GetSharedGfxLink()->GetType() == GfxLinkType::NativeJpg
-            && graphic->GetSharedGfxLink()->GetDataSize() != 0 )
+            && graphic->GetSharedGfxLink()->GetDataSize() != 0
+            && (graphic->GetSharedGfxLink()->GetType() == GfxLinkType::NativeJpg
+                || graphic->GetSharedGfxLink()->GetType() == GfxLinkType::NativePng))
         {
             // Graphic objects share internal ImpGraphic, do not process any of those twice.
             const auto predicate = [graphic](Graphic* item) { return item->ImplGetImpGraphic() == graphic->ImplGetImpGraphic(); };
diff --git a/vcl/source/filter/png/PngImageReader.cxx b/vcl/source/filter/png/PngImageReader.cxx
index b3a2265c23bf..898ef74d5836 100644
--- a/vcl/source/filter/png/PngImageReader.cxx
+++ b/vcl/source/filter/png/PngImageReader.cxx
@@ -21,6 +21,8 @@
 #include <svdata.hxx>
 #include <salinst.hxx>
 
+#include "png.hxx"
+
 namespace
 {
 void lclReadStream(png_structp pPng, png_bytep pOutBytes, png_size_t nBytesToRead)
@@ -65,7 +67,10 @@ struct PngDestructor
     png_infop pInfo;
 };
 
-bool reader(SvStream& rStream, BitmapEx& rBitmapEx, bool bUseBitmap32)
+bool reader(SvStream& rStream, BitmapEx& rBitmapEx,
+            GraphicFilterImportFlags nImportFlags = GraphicFilterImportFlags::NONE,
+            BitmapScopedWriteAccess* pAccess = nullptr,
+            AlphaScopedWriteAccess* pAlphaAccess = nullptr)
 {
     if (!isPng(rStream))
         return false;
@@ -89,24 +94,33 @@ bool reader(SvStream& rStream, BitmapEx& rBitmapEx, bool bUseBitmap32)
     Bitmap aBitmap;
     AlphaMask aBitmapAlpha;
     Size prefSize;
-    BitmapScopedWriteAccess pWriteAccess;
-    AlphaScopedWriteAccess pWriteAccessAlpha;
+    BitmapScopedWriteAccess pWriteAccessInstance;
+    AlphaScopedWriteAccess pWriteAccessAlphaInstance;
     std::vector<std::vector<png_byte>> aRows;
+    auto pBackendCapabilities = ImplGetSVData()->mpDefInst->GetBackendCapabilities();
+    const bool bSupportsBitmap32 = pBackendCapabilities->mbSupportsBitmap32;
+    const bool bOnlyCreateBitmap
+        = static_cast<bool>(nImportFlags & GraphicFilterImportFlags::OnlyCreateBitmap);
+    const bool bUseExistingBitmap
+        = static_cast<bool>(nImportFlags & GraphicFilterImportFlags::UseExistingBitmap);
 
     if (setjmp(png_jmpbuf(pPng)))
     {
-        // Set the bitmap if it contains something, even on failure. This allows
-        // reading images that are only partially broken.
-        pWriteAccess.reset();
-        pWriteAccessAlpha.reset();
-        if (!aBitmap.IsEmpty() && !aBitmapAlpha.IsEmpty())
-            rBitmapEx = BitmapEx(aBitmap, aBitmapAlpha);
-        else if (!aBitmap.IsEmpty())
-            rBitmapEx = BitmapEx(aBitmap);
-        if (!rBitmapEx.IsEmpty() && !prefSize.IsEmpty())
+        if (!bUseExistingBitmap)
         {
-            rBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
-            rBitmapEx.SetPrefSize(prefSize);
+            // Set the bitmap if it contains something, even on failure. This allows
+            // reading images that are only partially broken.
+            pWriteAccessInstance.reset();
+            pWriteAccessAlphaInstance.reset();
+            if (!aBitmap.IsEmpty() && !aBitmapAlpha.IsEmpty())
+                rBitmapEx = BitmapEx(aBitmap, aBitmapAlpha);
+            else if (!aBitmap.IsEmpty())
+                rBitmapEx = BitmapEx(aBitmap);
+            if (!rBitmapEx.IsEmpty() && !prefSize.IsEmpty())
+            {
+                rBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+                rBitmapEx.SetPrefSize(prefSize);
+            }
         }
         return false;
     }
@@ -188,12 +202,60 @@ bool reader(SvStream& rStream, BitmapEx& rBitmapEx, bool bUseBitmap32)
                         static_cast<sal_Int32>((100000.0 * height) / res_y));
     }
 
-    if (colorType == PNG_COLOR_TYPE_RGB)
+    if (!bUseExistingBitmap)
     {
-        aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N24_BPP);
-        pWriteAccess = BitmapScopedWriteAccess(aBitmap);
-        if (!pWriteAccess)
+        switch (colorType)
+        {
+            case PNG_COLOR_TYPE_RGB:
+                aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N24_BPP);
+                break;
+            case PNG_COLOR_TYPE_RGBA:
+                if (bSupportsBitmap32)
+                    aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N32_BPP);
+                else
+                {
+                    aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N24_BPP);
+                    aBitmapAlpha = AlphaMask(Size(width, height), nullptr);
+                }
+                break;
+            case PNG_COLOR_TYPE_GRAY:
+                aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N8_BPP,
+                                 &Bitmap::GetGreyPalette(256));
+                break;
+            default:
+                abort();
+        }
+
+        if (bOnlyCreateBitmap)
+        {
+            if (!aBitmapAlpha.IsEmpty())
+                rBitmapEx = BitmapEx(aBitmap, aBitmapAlpha);
+            else
+                rBitmapEx = BitmapEx(aBitmap);
+            if (!prefSize.IsEmpty())
+            {
+                rBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+                rBitmapEx.SetPrefSize(prefSize);
+            }
+            return true;
+        }
+
+        pWriteAccessInstance = BitmapScopedWriteAccess(aBitmap);
+        if (!pWriteAccessInstance)
             return false;
+        if (!aBitmapAlpha.IsEmpty())
+        {
+            pWriteAccessAlphaInstance = AlphaScopedWriteAccess(aBitmapAlpha);
+            if (!pWriteAccessAlphaInstance)
+                return false;
+        }
+    }
+    BitmapScopedWriteAccess& pWriteAccess = pAccess ? *pAccess : pWriteAccessInstance;
+    AlphaScopedWriteAccess& pWriteAccessAlpha
+        = pAlphaAccess ? *pAlphaAccess : pWriteAccessAlphaInstance;
+
+    if (colorType == PNG_COLOR_TYPE_RGB)
+    {
         ScanlineFormat eFormat = pWriteAccess->GetScanlineFormat();
         if (eFormat == ScanlineFormat::N24BitTcBgr)
             png_set_bgr(pPng);
@@ -206,24 +268,16 @@ bool reader(SvStream& rStream, BitmapEx& rBitmapEx, bool bUseBitmap32)
                 png_read_row(pPng, pScanline, nullptr);
             }
         }
-        pWriteAccess.reset();
-        rBitmapEx = BitmapEx(aBitmap);
     }
     else if (colorType == PNG_COLOR_TYPE_RGB_ALPHA)
     {
         size_t aRowSizeBytes = png_get_rowbytes(pPng, pInfo);
 
-        if (bUseBitmap32)
+        if (bSupportsBitmap32)
         {
-            aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N32_BPP);
-            pWriteAccess = BitmapScopedWriteAccess(aBitmap);
-            if (!pWriteAccess)
-                return false;
             ScanlineFormat eFormat = pWriteAccess->GetScanlineFormat();
             if (eFormat == ScanlineFormat::N32BitTcAbgr || eFormat == ScanlineFormat::N32BitTcBgra)
-            {
                 png_set_bgr(pPng);
-            }
 
             for (int pass = 0; pass < nNumberOfPasses; pass++)
             {
@@ -263,22 +317,13 @@ bool reader(SvStream& rStream, BitmapEx& rBitmapEx, bool bUseBitmap32)
                     }
                 }
             }
-            pWriteAccess.reset();
-            rBitmapEx = BitmapEx(aBitmap);
         }
         else
         {
-            aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N24_BPP);
-            aBitmapAlpha = AlphaMask(Size(width, height), nullptr);
-            pWriteAccess = BitmapScopedWriteAccess(aBitmap);
-            if (!pWriteAccess)
-                return false;
             ScanlineFormat eFormat = pWriteAccess->GetScanlineFormat();
             if (eFormat == ScanlineFormat::N24BitTcBgr)
                 png_set_bgr(pPng);
 
-            pWriteAccessAlpha = AlphaScopedWriteAccess(aBitmapAlpha);
-
             aRows = std::vector<std::vector<png_byte>>(height);
             for (auto& rRow : aRows)
                 rRow.resize(aRowSizeBytes, 0);
@@ -302,20 +347,11 @@ bool reader(SvStream& rStream, BitmapEx& rBitmapEx, bool bUseBitmap32)
                     }
                 }
             }
-            pWriteAccess.reset();
-            pWriteAccessAlpha.reset();
-            rBitmapEx = BitmapEx(aBitmap, aBitmapAlpha);
         }
     }
     else if (colorType == PNG_COLOR_TYPE_GRAY)
     {
-        aBitmap
-            = Bitmap(Size(width, height), vcl::PixelFormat::N8_BPP, &Bitmap::GetGreyPalette(256));
         aBitmap.Erase(COL_WHITE);
-        pWriteAccess = BitmapScopedWriteAccess(aBitmap);
-        if (!pWriteAccess)
-            return false;
-
         for (int pass = 0; pass < nNumberOfPasses; pass++)
         {
             for (png_uint_32 y = 0; y < height; y++)
@@ -324,16 +360,23 @@ bool reader(SvStream& rStream, BitmapEx& rBitmapEx, bool bUseBitmap32)
                 png_read_row(pPng, pScanline, nullptr);
             }
         }
-        pWriteAccess.reset();
-        rBitmapEx = BitmapEx(aBitmap);
     }
 
     png_read_end(pPng, pInfo);
 
-    if (!prefSize.IsEmpty())
+    if (!bUseExistingBitmap)
     {
-        rBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
-        rBitmapEx.SetPrefSize(prefSize);
+        pWriteAccess.reset();
+        pWriteAccessAlpha.reset();
+        if (!aBitmapAlpha.IsEmpty())
+            rBitmapEx = BitmapEx(aBitmap, aBitmapAlpha);
+        else
+            rBitmapEx = BitmapEx(aBitmap);
+        if (!prefSize.IsEmpty())
+        {
+            rBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+            rBitmapEx.SetPrefSize(prefSize);
+        }
     }
 
     return true;
@@ -407,13 +450,7 @@ PngImageReader::PngImageReader(SvStream& rStream)
 {
 }
 
-bool PngImageReader::read(BitmapEx& rBitmapEx)
-{
-    auto pBackendCapabilities = ImplGetSVData()->mpDefInst->GetBackendCapabilities();
-    bool bSupportsBitmap32 = pBackendCapabilities->mbSupportsBitmap32;
-
-    return reader(mrStream, rBitmapEx, bSupportsBitmap32);
-}
+bool PngImageReader::read(BitmapEx& rBitmapEx) { return reader(mrStream, rBitmapEx); }
 
 BitmapEx PngImageReader::read()
 {
@@ -434,6 +471,19 @@ std::unique_ptr<sal_uInt8[]> PngImageReader::getMicrosoftGifChunk(SvStream& rStr
     return chunk;
 }
 
+bool ImportPNG(SvStream& rInputStream, Graphic& rGraphic, GraphicFilterImportFlags nImportFlags,
+               BitmapScopedWriteAccess* pAccess, AlphaScopedWriteAccess* pAlphaAccess)
+{
+    // Creating empty bitmaps should be practically a no-op, and thus thread-safe.
+    BitmapEx bitmap;
+    if (reader(rInputStream, bitmap, nImportFlags, pAccess, pAlphaAccess))
+    {
+        rGraphic = bitmap;
+        return true;
+    }
+    return false;
+}
+
 } // namespace vcl
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/png/png.hxx b/vcl/source/filter/png/png.hxx
new file mode 100644
index 000000000000..b3a1b7b17b1a
--- /dev/null
+++ b/vcl/source/filter/png/png.hxx
@@ -0,0 +1,33 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <vcl/graph.hxx>
+#include <vcl/graphicfilter.hxx>
+
+#include <bitmap/BitmapWriteAccess.hxx>
+
+namespace vcl
+{
+bool ImportPNG(SvStream& rInputStream, Graphic& rGraphic, GraphicFilterImportFlags nImportFlags,
+               BitmapScopedWriteAccess* pAccess, AlphaScopedWriteAccess* pAlphaAccess);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */


More information about the Libreoffice-commits mailing list