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);

Reply via email to