summaryrefslogtreecommitdiff
path: root/vcl
diff options
context:
space:
mode:
authorLuboš Luňák <l.lunak@collabora.com>2021-04-15 17:39:48 +0200
committerLuboš Luňák <l.lunak@collabora.com>2021-04-15 21:14:49 +0200
commitb94a2dc95dce8c67ddb9f01f7bd91da2a5759d9c (patch)
tree1e936d3cc90651c521ae34e56f9766a396c0e162 /vcl
parent272a639bd8ea7c85d6f12dc2e217e79280a1a196 (diff)
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@collabora.com>
Diffstat (limited to 'vcl')
-rw-r--r--vcl/qa/cppunit/GraphicTest.cxx28
-rw-r--r--vcl/source/filter/graphicfilter.cxx73
-rw-r--r--vcl/source/filter/png/PngImageReader.cxx164
-rw-r--r--vcl/source/filter/png/png.hxx33
4 files changed, 230 insertions, 68 deletions
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: */