diff options
author | offtkp <parisoplop@gmail.com> | 2022-07-16 12:34:47 +0300 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2022-07-19 10:00:10 +0200 |
commit | 089b101e5447aac42e6fc79345e60da3ec63893d (patch) | |
tree | 23a1a96092b6b77c07d3e968a622a81f35ca2df3 /vcl | |
parent | 3d1032cf6b67f3f6fa539d1d42d681080517d38c (diff) |
Add ms-gif PNG chunk export support in PngImageWriter
Added export support for msOG chunks in the new PngImageWriter
and unit test
Change-Id: I258c9ca23e41c1d5ce01fc6711bc55c13636db17
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137093
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/qa/cppunit/png/PngFilterTest.cxx | 67 | ||||
-rw-r--r-- | vcl/qa/cppunit/png/data/dummy.gif | bin | 0 -> 101 bytes | |||
-rw-r--r-- | vcl/source/filter/png/PngImageWriter.cxx | 59 |
3 files changed, 114 insertions, 12 deletions
diff --git a/vcl/qa/cppunit/png/PngFilterTest.cxx b/vcl/qa/cppunit/png/PngFilterTest.cxx index 64a99756aa89..33af620eb08a 100644 --- a/vcl/qa/cppunit/png/PngFilterTest.cxx +++ b/vcl/qa/cppunit/png/PngFilterTest.cxx @@ -1696,16 +1696,65 @@ void PngFilterTest::testPngSuite() void PngFilterTest::testMsGifInPng() { - Graphic aGraphic; - const OUString aURL(getFullUrl(u"ms-gif.png")); - SvFileStream aFileStream(aURL, StreamMode::READ); GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter(); - ErrCode aResult = rFilter.ImportGraphic(aGraphic, aURL, aFileStream); - CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, aResult); - CPPUNIT_ASSERT(aGraphic.IsGfxLink()); - // The image is technically a PNG, but it has an animated Gif as a chunk (Microsoft extension). - CPPUNIT_ASSERT_EQUAL(GfxLinkType::NativeGif, aGraphic.GetSharedGfxLink()->GetType()); - CPPUNIT_ASSERT(aGraphic.IsAnimated()); + { + Graphic aGraphic; + const OUString aURL(getFullUrl(u"ms-gif.png")); + SvFileStream aFileStream(aURL, StreamMode::READ); + ErrCode aResult = rFilter.ImportGraphic(aGraphic, aURL, aFileStream); + CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, aResult); + CPPUNIT_ASSERT(aGraphic.IsGfxLink()); + // The image is technically a PNG, but it has an animated Gif as a chunk (Microsoft extension). + CPPUNIT_ASSERT_EQUAL(GfxLinkType::NativeGif, aGraphic.GetSharedGfxLink()->GetType()); + CPPUNIT_ASSERT(aGraphic.IsAnimated()); + } + { + // Tests msOG chunk export support + const OUString aURL(getFullUrl(u"dummy.gif")); + SvFileStream aGIFStream(aURL, StreamMode::READ); + sal_uInt32 nGIFSize = aGIFStream.TellEnd(); + const char* const pHeader = "MSOFFICE9.0"; + auto nHeaderSize = strlen(pHeader); + uno::Sequence<sal_Int8> aGIFSequence(nHeaderSize + nGIFSize); + sal_Int8* pSequence = aGIFSequence.getArray(); + for (size_t i = 0; i < nHeaderSize; i++) + *pSequence++ = pHeader[i]; + aGIFStream.Seek(STREAM_SEEK_TO_BEGIN); + aGIFStream.ReadBytes(pSequence, nGIFSize); + // Create msOG chunk + beans::PropertyValue aChunkProperty, aFilterProperty; + aChunkProperty.Name = "msOG"; + aChunkProperty.Value <<= aGIFSequence; + uno::Sequence<beans::PropertyValue> aAdditionalChunkSequence{ aChunkProperty }; + aFilterProperty.Name = "AdditionalChunks"; + aFilterProperty.Value <<= aAdditionalChunkSequence; + uno::Sequence<beans::PropertyValue> aPNGParameters{ aFilterProperty }; + // Export the png with the chunk + OUString ext = u".png"; + utl::TempFile aTempFile(u"testPngExportMsGif", true, &ext); + if (!bKeepTemp) + aTempFile.EnableKillingFile(); + { + SvStream& rStream = *aTempFile.GetStream(StreamMode::WRITE); + BitmapEx aDummyBitmap(Size(8, 8), vcl::PixelFormat::N24_BPP); + vcl::PngImageWriter aPngWriter(rStream); + aPngWriter.setParameters(aPNGParameters); + bool bWriteSuccess = aPngWriter.write(aDummyBitmap); + CPPUNIT_ASSERT_EQUAL(true, bWriteSuccess); + aTempFile.CloseStream(); + } + { + SvStream& rStream = *aTempFile.GetStream(StreamMode::READ); + rStream.Seek(0); + // Import the png and check that it is a gif + Graphic aGraphic; + ErrCode aResult = rFilter.ImportGraphic(aGraphic, aTempFile.GetURL(), rStream); + CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, aResult); + CPPUNIT_ASSERT(aGraphic.IsGfxLink()); + CPPUNIT_ASSERT_EQUAL(GfxLinkType::NativeGif, aGraphic.GetSharedGfxLink()->GetType()); + CPPUNIT_ASSERT(aGraphic.IsAnimated()); + } + } } void PngFilterTest::testPngRoundtrip8BitGrey() diff --git a/vcl/qa/cppunit/png/data/dummy.gif b/vcl/qa/cppunit/png/data/dummy.gif Binary files differnew file mode 100644 index 000000000000..fd5c62dcdcb6 --- /dev/null +++ b/vcl/qa/cppunit/png/data/dummy.gif diff --git a/vcl/source/filter/png/PngImageWriter.cxx b/vcl/source/filter/png/PngImageWriter.cxx index 6a123e4eb547..7db4e4b6bc98 100644 --- a/vcl/source/filter/png/PngImageWriter.cxx +++ b/vcl/source/filter/png/PngImageWriter.cxx @@ -47,7 +47,8 @@ static void lclWriteStream(png_structp pPng, png_bytep pData, png_size_t pDataSi png_error(pPng, "Write Error"); } -static bool pngWrite(SvStream& rStream, BitmapEx& rBitmapEx, int nCompressionLevel) +static bool pngWrite(SvStream& rStream, const BitmapEx& rBitmapEx, int nCompressionLevel, + const std::vector<PngChunk>& aAdditionalChunks) { png_structp pPng = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); @@ -197,6 +198,14 @@ static bool pngWrite(SvStream& rStream, BitmapEx& rBitmapEx, int nCompressionLev } } + if (!aAdditionalChunks.empty()) + { + for (const auto& aChunk : aAdditionalChunks) + { + png_write_chunk(pPng, aChunk.name.data(), aChunk.data.data(), aChunk.size); + } + } + png_write_end(pPng, pInfo); png_destroy_write_struct(&pPng, &pInfo); @@ -204,6 +213,50 @@ static bool pngWrite(SvStream& rStream, BitmapEx& rBitmapEx, int nCompressionLev return true; } +void PngImageWriter::setParameters(css::uno::Sequence<css::beans::PropertyValue> const& rParameters) +{ + for (auto const& rValue : rParameters) + { + if (rValue.Name == "Compression") + rValue.Value >>= mnCompressionLevel; + else if (rValue.Name == "Interlaced") + rValue.Value >>= mbInterlaced; + else if (rValue.Name == "AdditionalChunks") + { + css::uno::Sequence<css::beans::PropertyValue> aAdditionalChunkSequence; + if (rValue.Value >>= aAdditionalChunkSequence) + { + for (const auto& rAdditionalChunk : std::as_const(aAdditionalChunkSequence)) + { + if (rAdditionalChunk.Name.getLength() == 4) + { + vcl::PngChunk aChunk; + for (sal_Int32 k = 0; k < 4; k++) + { + aChunk.name[k] = static_cast<sal_uInt8>(rAdditionalChunk.Name[k]); + } + aChunk.name[4] = '\0'; + + css::uno::Sequence<sal_Int8> aByteSeq; + if (rAdditionalChunk.Value >>= aByteSeq) + { + sal_uInt32 nChunkSize = aByteSeq.getLength(); + aChunk.size = nChunkSize; + if (nChunkSize) + { + const sal_Int8* pSource = aByteSeq.getConstArray(); + std::vector<sal_uInt8> aData(pSource, pSource + nChunkSize); + aChunk.data = std::move(aData); + maAdditionalChunks.push_back(aChunk); + } + } + } + } + } + } + } +} + PngImageWriter::PngImageWriter(SvStream& rStream) : mrStream(rStream) , mnCompressionLevel(6) @@ -211,9 +264,9 @@ PngImageWriter::PngImageWriter(SvStream& rStream) { } -bool PngImageWriter::write(BitmapEx& rBitmapEx) +bool PngImageWriter::write(const BitmapEx& rBitmapEx) { - return pngWrite(mrStream, rBitmapEx, mnCompressionLevel); + return pngWrite(mrStream, rBitmapEx, mnCompressionLevel, maAdditionalChunks); } } // namespace vcl |