    /* -*- 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 .
     */

    #include <vcl/pngwrite.hxx>

    #include <cmath>
    #include <limits>
    #include <rtl/crc.h>
    #include <rtl/alloc.h>
    #include <tools/zcodec.hxx>
    #include <tools/stream.hxx>
    #include <vcl/bitmapaccess.hxx>
    #include <vcl/svapp.hxx>
    #include <vcl/alpha.hxx>
    #include <osl/endian.h>
    #include <memory>

    #define PNG_DEF_COMPRESSION 6

    #define PNGCHUNK_IHDR 0x49484452
    #define PNGCHUNK_PLTE 0x504c5445
    #define PNGCHUNK_IDAT 0x49444154
    #define PNGCHUNK_IEND 0x49454e44
    #define PNGCHUNK_pHYs 0x70485973
    #define PNGCHUNK_tRNS 0x74524e53

    namespace vcl
    {

    class PNGWriterImpl
    {
    public:

        PNGWriterImpl(const BitmapEx& BmpEx,
                      const css::uno::Sequence<css::beans::PropertyValue>* pFilterData);

        bool Write(SvStream& rOutStream);

        std::vector<vcl::PNGWriter::ChunkData>& GetChunks()
        {
            return maChunkSeq;
        }

    private:

        std::vector<vcl::PNGWriter::ChunkData> maChunkSeq;

        sal_Int32 mnCompLevel;
        sal_Int32 mnInterlaced;
        sal_uInt32 mnMaxChunkSize;
        bool mbStatus;

        Bitmap::ScopedReadAccess mpAccess;
        BitmapReadAccess* mpMaskAccess;
        ZCodec mpZCodec;

        std::unique_ptr<sal_uInt8[]> mpDeflateInBuf;         // as big as the size of a scanline + alphachannel + 1
        std::unique_ptr<sal_uInt8[]> mpPreviousScan;         // as big as mpDeflateInBuf
        std::unique_ptr<sal_uInt8[]> mpCurrentScan;
        sal_uLong mnDeflateInSize;

        sal_uLong mnWidth;
        sal_uLong mnHeight;
        sal_uInt8 mnBitsPerPixel;
        sal_uInt8 mnFilterType;  // 0 or 4;
        sal_uLong mnBBP;         // bytes per pixel ( needed for filtering )
        bool mbTrueAlpha;
        sal_uLong mnCRC;

        void ImplWritepHYs(const BitmapEx& rBitmapEx);
        void ImplWriteIDAT();
        sal_uLong ImplGetFilter(sal_uLong nY, sal_uLong nXStart = 0, sal_uLong nXAdd = 1);
        void ImplClearFirstScanline();
        void ImplWriteTransparent();
        bool ImplWriteHeader();
        void ImplWritePalette();
        void ImplOpenChunk(sal_uLong nChunkType);
        void ImplWriteChunk(sal_uInt8 nNumb);
        void ImplWriteChunk(sal_uInt32 nNumb);
        void ImplWriteChunk(unsigned char const * pSource, sal_uInt32 nDatSize);
    };

    PNGWriterImpl::PNGWriterImpl( const BitmapEx& rBmpEx,
        const css::uno::Sequence<css::beans::PropertyValue>* pFilterData )
        : mnCompLevel(PNG_DEF_COMPRESSION)
        , mnInterlaced(0)
        , mnMaxChunkSize(0)
        , mbStatus(true)
        , mpMaskAccess(nullptr)
        , mnDeflateInSize(0)
        , mnWidth(0)
        , mnHeight(0)
        , mnBitsPerPixel(0)
        , mnFilterType(0)
        , mnBBP(0)
        , mbTrueAlpha(false)
        , mnCRC(0)
    {
        if (!rBmpEx.IsEmpty())
        {
            Bitmap aBmp(rBmpEx.GetBitmap());

            mnInterlaced = 0; // ( aBmp.GetSizePixel().Width() > 128 ) || ( aBmp.GetSizePixel().Height() > 128 ) ? 1 : 0; #i67236#

            // #i67234# defaulting max chunk size to 256kb when using interlace mode
            mnMaxChunkSize = mnInterlaced == 0 ? std::numeric_limits<sal_uInt32>::max() : 0x40000;

            if (pFilterData)
            {
                sal_Int32 i = 0;
                for (i = 0; i < pFilterData->getLength(); i++)
                {
                    if ((*pFilterData)[i].Name == "Compression")
                        (*pFilterData)[i].Value >>= mnCompLevel;
                    else if ((*pFilterData)[i].Name == "Interlaced")
                        (*pFilterData)[i].Value >>= mnInterlaced;
                    else if ((*pFilterData)[i].Name == "MaxChunkSize")
                    {
                        sal_Int32 nVal = 0;
                        if ((*pFilterData)[i].Value >>= nVal)
                            mnMaxChunkSize = static_cast<sal_uInt32>(nVal);
                    }
                }
            }
            mnBitsPerPixel = static_cast<sal_uInt8>(aBmp.GetBitCount());

            if (rBmpEx.IsTransparent())
            {
                if (mnBitsPerPixel <= 8 && rBmpEx.IsAlpha())
                {
                    aBmp.Convert( BmpConversion::N24Bit );
                    mnBitsPerPixel = 24;
                }

                if (mnBitsPerPixel <= 8) // transparent palette
                {
                    aBmp.Convert(BmpConversion::N8BitTrans);
                    aBmp.Replace(rBmpEx.GetMask(), BMP_COL_TRANS);
                    mnBitsPerPixel = 8;
                    mpAccess = Bitmap::ScopedReadAccess(aBmp);
                    if (mpAccess)
                    {
                        if (ImplWriteHeader())
                        {
                            ImplWritepHYs(rBmpEx);
                            ImplWritePalette();
                            ImplWriteTransparent();
                            ImplWriteIDAT();
                        }
                        mpAccess.reset();
                    }
                    else
                    {
                        mbStatus = false;
                    }
                }
                else
                {
                    mpAccess = Bitmap::ScopedReadAccess(aBmp); // true RGB with alphachannel
                    if (mpAccess)
                    {
                        mbTrueAlpha = rBmpEx.IsAlpha();
                        if (mbTrueAlpha)
                        {
                            AlphaMask aMask(rBmpEx.GetAlpha());
                            mpMaskAccess = aMask.AcquireReadAccess();
                            if (mpMaskAccess)
                            {
                                if (ImplWriteHeader())
                                {
                                    ImplWritepHYs(rBmpEx);
                                    ImplWriteIDAT();
                                }
                                aMask.ReleaseAccess(mpMaskAccess);
                                mpMaskAccess = nullptr;
                            }
                            else
                            {
                                mbStatus = false;
                            }
                        }
                        else
                        {
                            Bitmap aMask(rBmpEx.GetMask());
                            mpMaskAccess = aMask.AcquireReadAccess();
                            if (mpMaskAccess)
                            {
                                if (ImplWriteHeader())
                                {
                                    ImplWritepHYs(rBmpEx);
                                    ImplWriteIDAT();
                                }
                                Bitmap::ReleaseAccess(mpMaskAccess);
                                mpMaskAccess = nullptr;
                            }
                            else
                            {
                                mbStatus = false;
                            }
                        }
                        mpAccess.reset();
                    }
                    else
                    {
                        mbStatus = false;
                    }
                }
            }
            else
            {
                mpAccess = Bitmap::ScopedReadAccess(aBmp); // palette + RGB without alphachannel
                if (mpAccess)
                {
                    if (ImplWriteHeader())
                    {
                        ImplWritepHYs(rBmpEx);
                        if (mpAccess->HasPalette())
                            ImplWritePalette();

                        ImplWriteIDAT();
                    }
                    mpAccess.reset();
                }
                else
                {
                    mbStatus = false;
                }
            }

            if (mbStatus)
            {
                ImplOpenChunk(PNGCHUNK_IEND); // create an IEND chunk
            }
        }
    }

    bool PNGWriterImpl::Write(SvStream& rOStm)
    {
       /* png signature is always an array of 8 bytes */
        SvStreamEndian nOldMode = rOStm.GetEndian();
        rOStm.SetEndian(SvStreamEndian::BIG);
        rOStm.WriteUInt32(0x89504e47);
        rOStm.WriteUInt32(0x0d0a1a0a);

        for (auto const& chunk : maChunkSeq)
        {
            sal_uInt32 nType = chunk.nType;
        #if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN)
            nType = OSL_SWAPDWORD(nType);
        #endif
            sal_uInt32 nCRC = rtl_crc32(0, &nType, 4);
            sal_uInt32 nDataSize = chunk.aData.size();
            if (nDataSize)
                nCRC = rtl_crc32(nCRC, &chunk.aData[0], nDataSize);
            rOStm.WriteUInt32(nDataSize);
            rOStm.WriteUInt32(chunk.nType);
            if (nDataSize)
                rOStm.WriteBytes(&chunk.aData[0], nDataSize);
            rOStm.WriteUInt32(nCRC);
        }
        rOStm.SetEndian(nOldMode);
        return mbStatus;
    }

    bool PNGWriterImpl::ImplWriteHeader()
    {
        ImplOpenChunk(PNGCHUNK_IHDR);
        ImplWriteChunk(sal_uInt32(mnWidth =  mpAccess->Width()));
        ImplWriteChunk(sal_uInt32(mnHeight = mpAccess->Height()));

        if (mnWidth && mnHeight && mnBitsPerPixel && mbStatus)
        {
            sal_uInt8 nBitDepth = mnBitsPerPixel;
            if (mnBitsPerPixel <= 8)
                mnFilterType = 0;
            else
                mnFilterType = 4;

            sal_uInt8 nColorType = 2; // colortype:

                                        // bit 0 -> palette is used
            if (mpAccess->HasPalette()) // bit 1 -> color is used
                nColorType |= 1;        // bit 2 -> alpha channel is used
            else
                nBitDepth /= 3;

            if (mpMaskAccess)
                nColorType |= 4;

            ImplWriteChunk(nBitDepth);
            ImplWriteChunk(nColorType); // colortype
            ImplWriteChunk(static_cast<sal_uInt8>(0)); // compression type
            ImplWriteChunk(static_cast<sal_uInt8>(0)); // filter type - is not supported in this version
            ImplWriteChunk(static_cast<sal_uInt8>(mnInterlaced)); // interlace type
        }
        else
        {
            mbStatus = false;
        }
        return mbStatus;
    }

    void PNGWriterImpl::ImplWritePalette()
    {
        const sal_uLong nCount = mpAccess->GetPaletteEntryCount();
        std::unique_ptr<sal_uInt8[]> pTempBuf(new sal_uInt8[nCount * 3]);
        sal_uInt8* pTmp = pTempBuf.get();

        ImplOpenChunk(PNGCHUNK_PLTE);

        for ( sal_uLong i = 0; i < nCount; i++ )
        {
            const BitmapColor& rColor = mpAccess->GetPaletteColor(i);
            *pTmp++ = rColor.GetRed();
            *pTmp++ = rColor.GetGreen();
            *pTmp++ = rColor.GetBlue();
        }
        ImplWriteChunk(pTempBuf.get(), nCount * 3);
    }

    void PNGWriterImpl::ImplWriteTransparent()
    {
        const sal_uLong nTransIndex = mpAccess->GetBestPaletteIndex(BMP_COL_TRANS);

        ImplOpenChunk(PNGCHUNK_tRNS);

        for (sal_uLong n = 0; n <= nTransIndex; n++)
        {
            ImplWriteChunk((nTransIndex == n) ? static_cast<sal_uInt8>(0x0) : static_cast<sal_uInt8>(0xff));
        }
    }

    void PNGWriterImpl::ImplWritepHYs(const BitmapEx& rBmpEx)
    {
        if (rBmpEx.GetPrefMapMode().GetMapUnit() == MapUnit::Map100thMM)
        {
            Size aPrefSize(rBmpEx.GetPrefSize());

            if (aPrefSize.Width() && aPrefSize.Height() && mnWidth && mnHeight)
            {
                ImplOpenChunk(PNGCHUNK_pHYs);
                sal_uInt32 nPrefSizeX = static_cast<sal_uInt32>(100000.0 / (static_cast<double>(aPrefSize.Width()) / mnWidth) + 0.5);
                sal_uInt32 nPrefSizeY = static_cast<sal_uInt32>(100000.0 / (static_cast<double>(aPrefSize.Height()) / mnHeight) + 0.5);
                ImplWriteChunk(nPrefSizeX);
                ImplWriteChunk(nPrefSizeY);
                ImplWriteChunk(sal_uInt8(1)); // nMapUnit
            }
        }
    }

    void PNGWriterImpl::ImplWriteIDAT()
    {
        mnDeflateInSize = mnBitsPerPixel;

        if (mpMaskAccess)
            mnDeflateInSize += 8;

        mnBBP = (mnDeflateInSize + 7) >> 3;

        mnDeflateInSize = mnBBP * mnWidth + 1;

        mpDeflateInBuf.reset(new sal_uInt8[mnDeflateInSize]);

        if (mnFilterType) // using filter type 4 we need memory for the scanline 3 times
        {
            mpPreviousScan.reset(new sal_uInt8[mnDeflateInSize]);
            mpCurrentScan.reset(new sal_uInt8[mnDeflateInSize]);
            ImplClearFirstScanline();
        }
        mpZCodec.BeginCompression(mnCompLevel, true);
        mpZCodec.SetCRC(mnCRC);
        SvMemoryStream aOStm;
        if (mnInterlaced == 0)
        {
            for (sal_uLong nY = 0; nY < mnHeight; nY++)
            {
                mpZCodec.Write(aOStm, mpDeflateInBuf.get(), ImplGetFilter(nY));
            }
        }
        else
        {
            // interlace mode
            sal_uLong nY;
            for (nY = 0; nY < mnHeight; nY += 8) // pass 1
            {
                mpZCodec.Write(aOStm, mpDeflateInBuf.get(), ImplGetFilter(nY, 0, 8));
            }
            ImplClearFirstScanline();

            for (nY = 0; nY < mnHeight; nY += 8) // pass 2
            {
                mpZCodec.Write(aOStm, mpDeflateInBuf.get(), ImplGetFilter(nY, 4, 8));
            }
            ImplClearFirstScanline();

            if (mnHeight >= 5) // pass 3
            {
                for (nY = 4; nY < mnHeight; nY += 8)
                {
                    mpZCodec.Write(aOStm, mpDeflateInBuf.get(), ImplGetFilter(nY, 0, 4));
                }
                ImplClearFirstScanline();
            }

            for (nY = 0; nY < mnHeight; nY += 4) // pass 4
            {
                mpZCodec.Write(aOStm, mpDeflateInBuf.get(), ImplGetFilter(nY, 2, 4));
            }
            ImplClearFirstScanline();

            if (mnHeight >= 3) // pass 5
            {
                for (nY = 2; nY < mnHeight; nY += 4)
                {
                    mpZCodec.Write(aOStm, mpDeflateInBuf.get(), ImplGetFilter(nY, 0, 2));
                }
                ImplClearFirstScanline();
            }

            for (nY = 0; nY < mnHeight; nY += 2) // pass 6
            {
                mpZCodec.Write(aOStm, mpDeflateInBuf.get(), ImplGetFilter(nY, 1, 2));
            }
            ImplClearFirstScanline();

            if (mnHeight >= 2) // pass 7
            {
                for (nY = 1; nY < mnHeight; nY += 2)
                {
                    mpZCodec.Write(aOStm, mpDeflateInBuf.get(), ImplGetFilter (nY));
                }
            }
        }
        mpZCodec.EndCompression();
        mnCRC = mpZCodec.GetCRC();

        if (mnFilterType) // using filter type 4 we need memory for the scanline 3 times
        {
            mpCurrentScan.reset();
            mpPreviousScan.reset();
        }
        mpDeflateInBuf.reset();

        sal_uInt32 nIDATSize = aOStm.Tell();
        sal_uInt32 nBytes, nBytesToWrite = nIDATSize;
        while(nBytesToWrite)
        {
            nBytes = nBytesToWrite <= mnMaxChunkSize ? nBytesToWrite : mnMaxChunkSize;
            ImplOpenChunk(PNGCHUNK_IDAT);
            ImplWriteChunk(const_cast<unsigned char *>(static_cast<unsigned char const *>(aOStm.GetData())) + (nIDATSize - nBytesToWrite), nBytes);
            nBytesToWrite -= nBytes;
        }
    }

    // ImplGetFilter writes the complete Scanline (nY) - in interlace mode the parameter nXStart and nXAdd
    // appends to the currently used pass
    // the complete size of scanline will be returned - in interlace mode zero is possible!

    sal_uLong PNGWriterImpl::ImplGetFilter (sal_uLong nY, sal_uLong nXStart, sal_uLong nXAdd)
    {
        sal_uInt8* pDest;

        if (mnFilterType)
            pDest = mpCurrentScan.get();
        else
            pDest = mpDeflateInBuf.get();

        if (nXStart < mnWidth)
        {
            *pDest++ = mnFilterType; // in this version the filter type is either 0 or 4

            if (mpAccess->HasPalette()) // alphachannel is not allowed by pictures including palette entries
            {
                switch (mnBitsPerPixel)
                {
                    case 1:
                    {
                        Scanline pScanline = mpAccess->GetScanline( nY );
                        sal_uLong nX, nXIndex;
                        for (nX = nXStart, nXIndex = 0; nX < mnWidth; nX += nXAdd, nXIndex++)
                        {
                            sal_uLong nShift = (nXIndex & 7) ^ 7;
                            if (nShift == 7)
                                *pDest = mpAccess->GetIndexFromData(pScanline, nX) << nShift;
                            else if  (nShift == 0)
                                *pDest++ |= mpAccess->GetIndexFromData(pScanline, nX) << nShift;
                            else
                                *pDest |= mpAccess->GetIndexFromData(pScanline, nX) << nShift;
                        }
                        if ( (nXIndex & 7) != 0 )
                            pDest++;    // byte is not completely used, so the bufferpointer is to correct
                    }
                    break;

                    case 4:
                    {
                        Scanline pScanline = mpAccess->GetScanline( nY );
                        sal_uLong nX, nXIndex;
                        for (nX = nXStart, nXIndex = 0; nX < mnWidth; nX += nXAdd, nXIndex++)
                        {
                            if(nXIndex & 1)
                                *pDest++ |= mpAccess->GetIndexFromData(pScanline, nX);
                            else
                                *pDest = mpAccess->GetIndexFromData(pScanline, nX) << 4;
                        }
                        if (nXIndex & 1)
                            pDest++;
                    }
                    break;

                    case 8:
                    {
                        Scanline pScanline = mpAccess->GetScanline( nY );
                        for (sal_uLong nX = nXStart; nX < mnWidth; nX += nXAdd)
                        {
                            *pDest++ = mpAccess->GetIndexFromData( pScanline, nX );
                        }
                    }
                    break;

                    default :
                        mbStatus = false;
                    break;
                }
            }
            else
            {
                if (mpMaskAccess) // mpMaskAccess != NULL -> alphachannel is to create
                {
                    if (mbTrueAlpha)
                    {
                        Scanline pScanline = mpAccess->GetScanline( nY );
                        Scanline pScanlineMask = mpMaskAccess->GetScanline( nY );
                        for (sal_uLong nX = nXStart; nX < mnWidth; nX += nXAdd)
                        {
                            const BitmapColor& rColor = mpAccess->GetPixelFromData(pScanline, nX);
                            *pDest++ = rColor.GetRed();
                            *pDest++ = rColor.GetGreen();
                            *pDest++ = rColor.GetBlue();
                            *pDest++ = 255 - mpMaskAccess->GetIndexFromData(pScanlineMask, nX);
                        }
                    }
                    else
                    {
                        const BitmapColor aTrans(mpMaskAccess->GetBestMatchingColor(COL_WHITE));
                        Scanline pScanline = mpAccess->GetScanline( nY );
                        Scanline pScanlineMask = mpMaskAccess->GetScanline( nY );

                        for (sal_uLong nX = nXStart; nX < mnWidth; nX += nXAdd)
                        {
                            const BitmapColor& rColor = mpAccess->GetPixelFromData(pScanline, nX);
                            *pDest++ = rColor.GetRed();
                            *pDest++ = rColor.GetGreen();
                            *pDest++ = rColor.GetBlue();

                            if(mpMaskAccess->GetPixelFromData(pScanlineMask, nX) == aTrans)
                                *pDest++ = 0;
                            else
                                *pDest++ = 0xff;
                        }
                    }
                }
                else
                {
                    Scanline pScanline = mpAccess->GetScanline( nY );
                    for (sal_uLong nX = nXStart; nX < mnWidth; nX += nXAdd)
                    {
                        const BitmapColor& rColor = mpAccess->GetPixelFromData(pScanline, nX);
                        *pDest++ = rColor.GetRed();
                        *pDest++ = rColor.GetGreen();
                        *pDest++ = rColor.GetBlue();
                    }
                }
            }
        }
        // filter type4 ( PAETH ) will be used only for 24bit graphics
        if (mnFilterType)
        {
            mnDeflateInSize = pDest - mpCurrentScan.get();
            pDest = mpDeflateInBuf.get();
            *pDest++ = 4; // filter type

            sal_uInt8* p1 = mpCurrentScan.get() + 1; // Current Pixel
            sal_uInt8* p2 = p1 - mnBBP;        // left pixel
            sal_uInt8* p3 = mpPreviousScan.get();    // upper pixel
            sal_uInt8* p4 = p3 - mnBBP;        // upperleft Pixel;

            while (pDest < mpDeflateInBuf.get() + mnDeflateInSize)
            {
                sal_uLong nb = *p3++;
                sal_uLong na, nc;
                if (p2 >= mpCurrentScan.get() + 1)
                {
                    na = *p2;
                    nc = *p4;
                }
                else
                {
                    na = nc = 0;
                }

                long np = na + nb - nc;
                long npa = np - na;
                long npb = np - nb;
                long npc = np - nc;

                if (npa < 0)
                    npa =-npa;
                if (npb < 0)
                    npb =-npb;
                if (npc < 0)
                    npc =-npc;

                if (npa <= npb && npa <= npc)
                    *pDest++ = *p1++ - static_cast<sal_uInt8>(na);
                else if ( npb <= npc )
                    *pDest++ = *p1++ - static_cast<sal_uInt8>(nb);
                else
                    *pDest++ = *p1++ - static_cast<sal_uInt8>(nc);

                p4++;
                p2++;
            }
            for (long i = 0; i < static_cast<long>(mnDeflateInSize - 1); i++)
            {
                mpPreviousScan[i] = mpCurrentScan[i + 1];
            }
        }
        else
        {
            mnDeflateInSize = pDest - mpDeflateInBuf.get();
        }
        return mnDeflateInSize;
    }

    void PNGWriterImpl::ImplClearFirstScanline()
    {
        if (mnFilterType)
            memset(mpPreviousScan.get(), 0, mnDeflateInSize);
    }

    void PNGWriterImpl::ImplOpenChunk (sal_uLong nChunkType)
    {
        maChunkSeq.emplace_back();
        maChunkSeq.back().nType = nChunkType;
    }

    void PNGWriterImpl::ImplWriteChunk (sal_uInt8 nSource)
    {
        maChunkSeq.back().aData.push_back(nSource);
    }

    void PNGWriterImpl::ImplWriteChunk (sal_uInt32 nSource)
    {
        vcl::PNGWriter::ChunkData& rChunkData = maChunkSeq.back();
        rChunkData.aData.push_back(static_cast<sal_uInt8>(nSource >> 24));
        rChunkData.aData.push_back(static_cast<sal_uInt8>(nSource >> 16));
        rChunkData.aData.push_back(static_cast<sal_uInt8>(nSource >> 8));
        rChunkData.aData.push_back(static_cast<sal_uInt8>(nSource));
    }

    void PNGWriterImpl::ImplWriteChunk (unsigned char const * pSource, sal_uInt32 nDatSize)
    {
        if (nDatSize)
        {
            vcl::PNGWriter::ChunkData& rChunkData = maChunkSeq.back();
            sal_uInt32 nSize = rChunkData.aData.size();
            rChunkData.aData.resize(nSize + nDatSize);
            memcpy(&rChunkData.aData[nSize], pSource, nDatSize);
        }
    }

    PNGWriter::PNGWriter(const BitmapEx& rBmpEx,
                         const css::uno::Sequence<css::beans::PropertyValue>* pFilterData)
        : mpImpl(new vcl::PNGWriterImpl(rBmpEx, pFilterData))
    {
    }

    PNGWriter::~PNGWriter()
    {
    }

    bool PNGWriter::Write(SvStream& rStream)
    {
        return mpImpl->Write(rStream);
    }

    std::vector<vcl::PNGWriter::ChunkData>& PNGWriter::GetChunks()
    {
        return mpImpl->GetChunks();
    }

    } // namespace vcl

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V547 Expression 'mnInterlaced == 0' is always true.