vcl/qa/cppunit/png/PngFilterTest.cxx | 53 +++++++++++++++++++++++++++++++ vcl/source/filter/png/PngImageWriter.cxx | 35 +++++++++++++++----- 2 files changed, 79 insertions(+), 9 deletions(-)
New commits: commit c010b8cc712d1336ed79fb220764f1a850dc22e5 Author: Sarper Akdemir <sarper.akde...@collabora.com> AuthorDate: Wed Jan 25 15:36:59 2023 +0300 Commit: Tomaž Vajngerl <qui...@gmail.com> CommitDate: Sun Mar 5 10:55:44 2023 +0000 tdf#153180: PngImageWriter add support for gray alpha colortype 8 bit grayscale images can have alpha accompanying it, and PNG supports 2 channel gray-alpha images. libpng supports this with the use of colortype PNG_COLOR_TYPE_GRAY_ALPHA. This patch adds support for writing GRAY_ALPHA colorType PNG images. This is done by expanding what was in place for ScanlineFormat::N24BitTcRgb with alpha PNG_COLOR_TYPE_RGBA. Change-Id: I80d462d784f91529eb9371c6bdb029c78f32f81e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146138 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <qui...@gmail.com> diff --git a/vcl/qa/cppunit/png/PngFilterTest.cxx b/vcl/qa/cppunit/png/PngFilterTest.cxx index dae007eb7898..fc3963c356e8 100644 --- a/vcl/qa/cppunit/png/PngFilterTest.cxx +++ b/vcl/qa/cppunit/png/PngFilterTest.cxx @@ -26,6 +26,7 @@ #include <vcl/filter/PngImageReader.hxx> #include <vcl/filter/PngImageWriter.hxx> #include <vcl/BitmapReadAccess.hxx> +#include <vcl/BitmapMonochromeFilter.hxx> #include <bitmap/BitmapWriteAccess.hxx> #include <vcl/alpha.hxx> #include <vcl/graphicfilter.hxx> @@ -176,6 +177,7 @@ public: void testPngRoundtrip24_8(); void testPngRoundtrip32(); void testPngWrite8BitRGBPalette(); + void testTdf153180MonochromeFilterPngExport(); void testDump(); CPPUNIT_TEST_SUITE(PngFilterTest); @@ -188,6 +190,7 @@ public: CPPUNIT_TEST(testPngRoundtrip32); CPPUNIT_TEST(testPngWrite8BitRGBPalette); CPPUNIT_TEST(testDump); + CPPUNIT_TEST(testTdf153180MonochromeFilterPngExport); CPPUNIT_TEST_SUITE_END(); }; @@ -1958,6 +1961,56 @@ void PngFilterTest::testPngWrite8BitRGBPalette() } } +void PngFilterTest::testTdf153180MonochromeFilterPngExport() +{ + GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter(); + + Graphic aGraphicOriginal; + { + // 3 * 16 bits rgb color alpha, no background chunk + const OUString aURL(getFullUrl(u"bgan6a16.png")); + SvFileStream aFileStream(aURL, StreamMode::READ); + ErrCode aResult = rFilter.ImportGraphic(aGraphicOriginal, aURL, aFileStream); + CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, aResult); + CPPUNIT_ASSERT(aGraphicOriginal.IsAlpha()); + } + + // Apply the monochrome filter to the graphic but keep the alpha. + BitmapEx aBitmapEx(aGraphicOriginal.GetBitmapEx()); + AlphaMask aAlphaMask(aBitmapEx.GetAlphaMask()); + + BitmapEx aTmpBmpEx(aBitmapEx.GetBitmap()); + BitmapFilter::Filter(aTmpBmpEx, BitmapMonochromeFilter{ sal_uInt8{ 127 } }); + + Graphic aGraphicAfterFilter{ BitmapEx(aTmpBmpEx.GetBitmap(), aAlphaMask) }; + CPPUNIT_ASSERT(aGraphicAfterFilter.IsAlpha()); + + // export the resulting graphic + utl::TempFileNamed aTempFile(u"testPngExportTdf153180", true, u".png"); + if (!bKeepTemp) + aTempFile.EnableKillingFile(); + { + SvStream& rStream = *aTempFile.GetStream(StreamMode::WRITE); + vcl::PngImageWriter aPngWriter(rStream); + bool bWriteSuccess = aPngWriter.write(aGraphicAfterFilter.GetBitmapEx()); + CPPUNIT_ASSERT_EQUAL(true, bWriteSuccess); + aTempFile.CloseStream(); + } + { + SvStream& rStream = *aTempFile.GetStream(StreamMode::READ); + rStream.Seek(0); + // Import the png and check that it still has alpha + Graphic aGraphic; + ErrCode aResult = rFilter.ImportGraphic(aGraphic, aTempFile.GetURL(), rStream); + CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, aResult); + + // Without the accompanying patch would fail with: + // assertion failed + // -Expression : aGraphic.IsAlpha() + CPPUNIT_ASSERT(aGraphic.IsAlpha()); + } +} + void PngFilterTest::testDump() { utl::TempFileNamed aTempFile; diff --git a/vcl/source/filter/png/PngImageWriter.cxx b/vcl/source/filter/png/PngImageWriter.cxx index 35ccefaec2c7..914302223d6a 100644 --- a/vcl/source/filter/png/PngImageWriter.cxx +++ b/vcl/source/filter/png/PngImageWriter.cxx @@ -15,15 +15,25 @@ namespace { -void combineScanlineChannels(Scanline pRGBScanline, Scanline pAlphaScanline, +void combineScanlineChannels(Scanline pColorScanline, Scanline pAlphaScanline, std::vector<std::remove_pointer_t<Scanline>>& pResult, - sal_uInt32 nBitmapWidth) + sal_uInt32 nBitmapWidth, int colorType) { + if (colorType == PNG_COLOR_TYPE_GRAY_ALPHA) + { + for (sal_uInt32 i = 0; i < nBitmapWidth; ++i) + { + pResult[i * 2] = *pColorScanline++; // Gray + pResult[i * 2 + 1] = *pAlphaScanline++; // A + } + return; + } + for (sal_uInt32 i = 0; i < nBitmapWidth; ++i) { - pResult[i * 4] = *pRGBScanline++; // R - pResult[i * 4 + 1] = *pRGBScanline++; // G - pResult[i * 4 + 2] = *pRGBScanline++; // B + pResult[i * 4] = *pColorScanline++; // R + pResult[i * 4 + 1] = *pColorScanline++; // G + pResult[i * 4 + 2] = *pColorScanline++; // B pResult[i * 4 + 3] = *pAlphaScanline++; // A } } @@ -130,7 +140,14 @@ static bool pngWrite(SvStream& rStream, const BitmapEx& rBitmapEx, int nCompress if (!aBitmap.HasGreyPalette8Bit()) colorType = PNG_COLOR_TYPE_PALETTE; else + { colorType = PNG_COLOR_TYPE_GRAY; + if (pAlphaAccess) + { + colorType = PNG_COLOR_TYPE_GRAY_ALPHA; + bCombineChannels = true; + } + } bitDepth = 8; break; } @@ -222,14 +239,14 @@ static bool pngWrite(SvStream& rStream, const BitmapEx& rBitmapEx, int nCompress if (bCombineChannels) { auto nBitmapWidth = pAccess->Width(); - // Allocate enough size to fit all 4 channels - aCombinedChannels.resize(nBitmapWidth * 4); + // Allocate enough size to fit all channels + aCombinedChannels.resize(nBitmapWidth * png_get_channels(pPng, pInfo)); Scanline pAlphaPointer = pAlphaAccess->GetScanline(y); if (!pSourcePointer || !pAlphaPointer) return false; - // Combine RGB and alpha channels + // Combine color and alpha channels combineScanlineChannels(pSourcePointer, pAlphaPointer, aCombinedChannels, - nBitmapWidth); + nBitmapWidth, colorType); pFinalPointer = aCombinedChannels.data(); // Invert alpha channel (255 - a) png_set_invert_alpha(pPng);