LCOV - code coverage report
Current view: top level - frmts/png - pngdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1141 1301 87.7 %
Date: 2026-02-11 08:43:47 Functions: 48 51 94.1 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  PNG Driver
       4             :  * Purpose:  Implement GDAL PNG Support
       5             :  * Author:   Frank Warmerdam, warmerda@home.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2000, Frank Warmerdam
       9             :  * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ******************************************************************************
      13             :  *
      14             :  * ISSUES:
      15             :  *  o CollectMetadata() will only capture TEXT chunks before the image
      16             :  *    data as the code is currently structured.
      17             :  *  o Interlaced images are read entirely into memory for use.  This is
      18             :  *    bad for large images.
      19             :  *  o Image reading is always strictly sequential.  Reading backwards will
      20             :  *    cause the file to be rewound, and access started again from the
      21             :  *    beginning.
      22             :  *  o 16 bit alpha values are not scaled by to eight bit.
      23             :  *
      24             :  */
      25             : 
      26             : #include "pngdataset.h"
      27             : #include "pngdrivercore.h"
      28             : 
      29             : #include "cpl_string.h"
      30             : #include "cpl_vsi_virtual.h"
      31             : #include "gdal_frmts.h"
      32             : #include "gdal_pam.h"
      33             : 
      34             : #if defined(__clang__)
      35             : #pragma clang diagnostic push
      36             : #pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
      37             : #endif
      38             : 
      39             : #include "png.h"
      40             : 
      41             : #if defined(__clang__)
      42             : #pragma clang diagnostic pop
      43             : #endif
      44             : 
      45             : #include <csetjmp>
      46             : 
      47             : #include <algorithm>
      48             : #include <limits>
      49             : 
      50             : // Note: Callers must provide blocks in increasing Y order.
      51             : // Disclaimer (E. Rouault): this code is not production ready at all. A lot of
      52             : // issues remain: uninitialized variables, unclosed files, lack of proper
      53             : // multiband handling, and an inability to read and write at the same time. Do
      54             : // not use it unless you're ready to fix it.
      55             : 
      56             : #ifdef _MSC_VER
      57             : #pragma warning(disable : 4611)
      58             : #endif
      59             : 
      60             : static void png_vsi_read_data(png_structp png_ptr, png_bytep data,
      61             :                               png_size_t length);
      62             : 
      63             : static void png_vsi_write_data(png_structp png_ptr, png_bytep data,
      64             :                                png_size_t length);
      65             : 
      66             : static void png_vsi_flush(png_structp png_ptr);
      67             : 
      68             : static void png_gdal_error(png_structp png_ptr, const char *error_message);
      69             : static void png_gdal_warning(png_structp png_ptr, const char *error_message);
      70             : 
      71             : #ifdef ENABLE_WHOLE_IMAGE_OPTIMIZATION
      72             : 
      73             : /************************************************************************/
      74             : /*                     IsCompatibleOfSingleBlock()                      */
      75             : /************************************************************************/
      76             : 
      77       25770 : bool PNGDataset::IsCompatibleOfSingleBlock() const
      78             : {
      79       25582 :     return nBitDepth == 8 && !bInterlaced && nRasterXSize <= 512 &&
      80       25237 :            nRasterYSize <= 512 &&
      81       25235 :            CPLTestBool(
      82       51352 :                CPLGetConfigOption("GDAL_PNG_WHOLE_IMAGE_OPTIM", "YES")) &&
      83       50916 :            CPLTestBool(CPLGetConfigOption("GDAL_PNG_SINGLE_BLOCK", "YES"));
      84             : }
      85             : #endif
      86             : 
      87             : /************************************************************************/
      88             : /*                           PNGRasterBand()                            */
      89             : /************************************************************************/
      90             : 
      91       25770 : PNGRasterBand::PNGRasterBand(PNGDataset *poDSIn, int nBandIn)
      92       25770 :     : bHaveNoData(FALSE), dfNoDataValue(-1)
      93             : {
      94       25770 :     poDS = poDSIn;
      95       25770 :     nBand = nBandIn;
      96             : 
      97       25770 :     if (poDSIn->nBitDepth == 16)
      98         180 :         eDataType = GDT_UInt16;
      99             :     else
     100       25590 :         eDataType = GDT_UInt8;
     101             : 
     102       25770 :     nBlockXSize = poDSIn->nRasterXSize;
     103             : #ifdef ENABLE_WHOLE_IMAGE_OPTIMIZATION
     104       25770 :     if (poDSIn->IsCompatibleOfSingleBlock())
     105             :     {
     106       25057 :         nBlockYSize = poDSIn->nRasterYSize;
     107             :     }
     108             :     else
     109             : #endif
     110             :     {
     111         713 :         nBlockYSize = 1;
     112             :     }
     113       25770 : }
     114             : 
     115             : /************************************************************************/
     116             : /*                             IReadBlock()                             */
     117             : /************************************************************************/
     118             : 
     119       13136 : CPLErr PNGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     120             : 
     121             : {
     122             : #ifdef ENABLE_WHOLE_IMAGE_OPTIMIZATION
     123       13136 :     if (nBlockYSize > 1)
     124             :     {
     125             :         GDALRasterIOExtraArg sExtraArg;
     126         143 :         INIT_RASTERIO_EXTRA_ARG(sExtraArg);
     127         143 :         const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
     128         286 :         return IRasterIO(GF_Read, 0, 0, nRasterXSize, nRasterYSize, pImage,
     129             :                          nRasterXSize, nRasterYSize, eDataType, nDTSize,
     130         143 :                          static_cast<GSpacing>(nDTSize) * nRasterXSize,
     131         143 :                          &sExtraArg);
     132             :     }
     133             : #endif
     134             : 
     135       12993 :     PNGDataset *poGDS = cpl::down_cast<PNGDataset *>(poDS);
     136       12993 :     CPLAssert(nBlockXOff == 0);
     137             : 
     138       12993 :     const int nPixelSize = (poGDS->nBitDepth == 16) ? 2 : 1;
     139             : 
     140       12993 :     if (poGDS->fpImage == nullptr)
     141             :     {
     142           0 :         memset(pImage, 0, cpl::fits_on<int>(nPixelSize * nRasterXSize));
     143           0 :         return CE_None;
     144             :     }
     145             : 
     146             :     // Load the desired scanline into the working buffer.
     147       12993 :     CPLErr eErr = poGDS->LoadScanline(nBlockYOff);
     148       12993 :     if (eErr != CE_None)
     149           2 :         return eErr;
     150             : 
     151       12991 :     const int nPixelOffset = poGDS->nBands * nPixelSize;
     152             : 
     153             :     const auto CopyToDstBuffer =
     154     7608490 :         [this, nPixelOffset, nPixelSize](const GByte *pabyScanline, void *pDest)
     155             :     {
     156             :         // Transfer between the working buffer and the caller's buffer.
     157       19331 :         if (nPixelSize == nPixelOffset)
     158       10687 :             memcpy(pDest, pabyScanline,
     159       10687 :                    cpl::fits_on<int>(nPixelSize * nRasterXSize));
     160        8644 :         else if (nPixelSize == 1)
     161             :         {
     162     3761940 :             for (int i = 0; i < nRasterXSize; i++)
     163     3754510 :                 reinterpret_cast<GByte *>(pDest)[i] =
     164     3754510 :                     pabyScanline[i * nPixelOffset];
     165             :         }
     166             :         else
     167             :         {
     168        1216 :             CPLAssert(nPixelSize == 2);
     169       26688 :             for (int i = 0; i < nRasterXSize; i++)
     170             :             {
     171       25472 :                 reinterpret_cast<GUInt16 *>(pDest)[i] =
     172       25472 :                     *reinterpret_cast<const GUInt16 *>(pabyScanline +
     173       25472 :                                                        i * nPixelOffset);
     174             :             }
     175             :         }
     176       32322 :     };
     177             : 
     178       12991 :     const GByte *const pabySrcBufferFirstBand =
     179       12991 :         poGDS->pabyBuffer +
     180       12991 :         (nBlockYOff - poGDS->nBufferStartLine) * nPixelOffset * nRasterXSize;
     181       12991 :     CopyToDstBuffer(pabySrcBufferFirstBand + nPixelSize * (nBand - 1), pImage);
     182             : 
     183             :     // Forcibly load the other bands associated with this scanline.
     184       32322 :     for (int iBand = 1; iBand <= poGDS->GetRasterCount(); iBand++)
     185             :     {
     186       19331 :         if (iBand != nBand)
     187             :         {
     188        6340 :             auto poIterBand = poGDS->GetRasterBand(iBand);
     189             :             GDALRasterBlock *poBlock =
     190        6340 :                 poIterBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
     191        6340 :             if (poBlock != nullptr)
     192             :             {
     193             :                 // Block already cached
     194           0 :                 poBlock->DropLock();
     195           0 :                 continue;
     196             :             }
     197             : 
     198             :             // Instantiate the block
     199             :             poBlock =
     200        6340 :                 poIterBand->GetLockedBlockRef(nBlockXOff, nBlockYOff, TRUE);
     201        6340 :             if (poBlock == nullptr)
     202             :             {
     203           0 :                 continue;
     204             :             }
     205             : 
     206        6340 :             CopyToDstBuffer(pabySrcBufferFirstBand + nPixelSize * (iBand - 1),
     207             :                             poBlock->GetDataRef());
     208             : 
     209        6340 :             poBlock->DropLock();
     210             :         }
     211             :     }
     212             : 
     213       12991 :     return CE_None;
     214             : }
     215             : 
     216             : /************************************************************************/
     217             : /*                       GetColorInterpretation()                       */
     218             : /************************************************************************/
     219             : 
     220         345 : GDALColorInterp PNGRasterBand::GetColorInterpretation()
     221             : 
     222             : {
     223         345 :     PNGDataset *poGDS = cpl::down_cast<PNGDataset *>(poDS);
     224             : 
     225         345 :     if (poGDS->nColorType == PNG_COLOR_TYPE_GRAY)
     226          27 :         return GCI_GrayIndex;
     227             : 
     228         318 :     else if (poGDS->nColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
     229             :     {
     230          29 :         if (nBand == 1)
     231           8 :             return GCI_GrayIndex;
     232             :         else
     233          21 :             return GCI_AlphaBand;
     234             :     }
     235             : 
     236         289 :     else if (poGDS->nColorType == PNG_COLOR_TYPE_PALETTE)
     237          45 :         return GCI_PaletteIndex;
     238             : 
     239         244 :     else if (poGDS->nColorType == PNG_COLOR_TYPE_RGB ||
     240         202 :              poGDS->nColorType == PNG_COLOR_TYPE_RGB_ALPHA)
     241             :     {
     242         244 :         if (nBand == 1)
     243          37 :             return GCI_RedBand;
     244         207 :         else if (nBand == 2)
     245          44 :             return GCI_GreenBand;
     246         163 :         else if (nBand == 3)
     247          35 :             return GCI_BlueBand;
     248             :         else
     249         128 :             return GCI_AlphaBand;
     250             :     }
     251             :     else
     252           0 :         return GCI_GrayIndex;
     253             : }
     254             : 
     255             : /************************************************************************/
     256             : /*                           GetColorTable()                            */
     257             : /************************************************************************/
     258             : 
     259         578 : GDALColorTable *PNGRasterBand::GetColorTable()
     260             : 
     261             : {
     262         578 :     PNGDataset *poGDS = cpl::down_cast<PNGDataset *>(poDS);
     263             : 
     264         578 :     if (nBand == 1)
     265         554 :         return poGDS->poColorTable.get();
     266             : 
     267          24 :     return nullptr;
     268             : }
     269             : 
     270             : /************************************************************************/
     271             : /*                           SetNoDataValue()                           */
     272             : /************************************************************************/
     273             : 
     274         286 : CPLErr PNGRasterBand::SetNoDataValue(double dfNewValue)
     275             : 
     276             : {
     277         286 :     bHaveNoData = TRUE;
     278         286 :     dfNoDataValue = dfNewValue;
     279             : 
     280         286 :     return CE_None;
     281             : }
     282             : 
     283             : /************************************************************************/
     284             : /*                           GetNoDataValue()                           */
     285             : /************************************************************************/
     286             : 
     287         177 : double PNGRasterBand::GetNoDataValue(int *pbSuccess)
     288             : 
     289             : {
     290         177 :     if (bHaveNoData)
     291             :     {
     292          35 :         if (pbSuccess != nullptr)
     293          31 :             *pbSuccess = bHaveNoData;
     294          35 :         return dfNoDataValue;
     295             :     }
     296             : 
     297         142 :     return GDALPamRasterBand::GetNoDataValue(pbSuccess);
     298             : }
     299             : 
     300             : /************************************************************************/
     301             : /* ==================================================================== */
     302             : /*                             PNGDataset                               */
     303             : /* ==================================================================== */
     304             : /************************************************************************/
     305             : 
     306             : /************************************************************************/
     307             : /*                             PNGDataset()                             */
     308             : /************************************************************************/
     309             : 
     310        9860 : PNGDataset::PNGDataset()
     311             : {
     312        9860 :     memset(&sSetJmpContext, 0, sizeof(sSetJmpContext));
     313        9860 : }
     314             : 
     315             : /************************************************************************/
     316             : /*                            ~PNGDataset()                             */
     317             : /************************************************************************/
     318             : 
     319       19720 : PNGDataset::~PNGDataset()
     320             : 
     321             : {
     322        9860 :     PNGDataset::Close();
     323             : 
     324        9860 :     if (hPNG != nullptr)
     325        9672 :         png_destroy_read_struct(&hPNG, &psPNGInfo, nullptr);
     326       19720 : }
     327             : 
     328             : /************************************************************************/
     329             : /*                               Close()                                */
     330             : /************************************************************************/
     331             : 
     332       15127 : CPLErr PNGDataset::Close(GDALProgressFunc, void *)
     333             : {
     334       15127 :     CPLErr eErr = CE_None;
     335             : 
     336       15127 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     337             :     {
     338        9860 :         eErr = PNGDataset::FlushCache(true);
     339             : 
     340        9860 :         if (fpImage != nullptr && VSIFCloseL(fpImage) != 0)
     341           0 :             eErr = CE_Failure;
     342        9860 :         fpImage = nullptr;
     343             : 
     344        9860 :         eErr = GDAL::Combine(eErr, GDALPamDataset::Close());
     345             :     }
     346       15127 :     return eErr;
     347             : }
     348             : 
     349             : /************************************************************************/
     350             : /*                           LoadWholeImage()                           */
     351             : /************************************************************************/
     352             : 
     353             : #ifdef ENABLE_WHOLE_IMAGE_OPTIMIZATION
     354             : 
     355             : #ifdef HAVE_SSE2
     356             : #include "filter_sse2_intrinsics.c"
     357             : #endif
     358             : 
     359             : #if defined(__GNUC__) && !defined(__SSE2__) && !defined(USE_NEON_OPTIMIZATIONS)
     360             : __attribute__((optimize("tree-vectorize"))) static inline void
     361             : AddVectors(const GByte *CPL_RESTRICT pabyInputLine,
     362             :            GByte *CPL_RESTRICT pabyOutputLine, int nSize)
     363             : {
     364             :     for (int iX = 0; iX < nSize; ++iX)
     365             :         pabyOutputLine[iX] =
     366             :             static_cast<GByte>(pabyInputLine[iX] + pabyOutputLine[iX]);
     367             : }
     368             : 
     369             : __attribute__((optimize("tree-vectorize"))) static inline void
     370             : AddVectors(const GByte *CPL_RESTRICT pabyInputLine1,
     371             :            const GByte *CPL_RESTRICT pabyInputLine2,
     372             :            GByte *CPL_RESTRICT pabyOutputLine, int nSize)
     373             : {
     374             :     for (int iX = 0; iX < nSize; ++iX)
     375             :         pabyOutputLine[iX] =
     376             :             static_cast<GByte>(pabyInputLine1[iX] + pabyInputLine2[iX]);
     377             : }
     378             : #endif  //  defined(__GNUC__) && !defined(__SSE2__)
     379             : 
     380        4005 : CPLErr PNGDataset::LoadWholeImage(void *pSingleBuffer, GSpacing nPixelSpace,
     381             :                                   GSpacing nLineSpace, GSpacing nBandSpace,
     382             :                                   void *apabyBuffers[4])
     383             : {
     384        4005 :     if (fpImage == nullptr)
     385             :     {
     386          21 :         for (int iY = 0; iY < nRasterYSize; ++iY)
     387             :         {
     388          20 :             if (pSingleBuffer)
     389             :             {
     390          20 :                 GByte *pabyDest =
     391          20 :                     static_cast<GByte *>(pSingleBuffer) + iY * nLineSpace;
     392         420 :                 for (int x = 0; x < nRasterXSize; ++x)
     393             :                 {
     394         800 :                     for (int iBand = 0; iBand < nBands; iBand++)
     395             :                     {
     396         400 :                         pabyDest[(x * nPixelSpace) + iBand * nBandSpace] = 0;
     397             :                     }
     398             :                 }
     399             :             }
     400             :             else
     401             :             {
     402           0 :                 for (int iBand = 0; iBand < nBands; iBand++)
     403             :                 {
     404           0 :                     GByte *l_pabyBuffer =
     405           0 :                         static_cast<GByte *>(apabyBuffers[iBand]) +
     406           0 :                         iY * nRasterXSize;
     407           0 :                     memset(l_pabyBuffer, 0, nRasterXSize);
     408             :                 }
     409             :             }
     410             :         }
     411           1 :         return CE_None;
     412             :     }
     413             : 
     414        4004 :     const bool bCanUseDeinterleave =
     415        5616 :         (nBands == 3 || nBands == 4) &&
     416        1612 :         (apabyBuffers != nullptr ||
     417        1448 :          (nPixelSpace == 1 &&
     418        1448 :           nBandSpace == static_cast<GSpacing>(nRasterXSize) * nRasterYSize));
     419             : 
     420             :     // Below should work without SSE2, but the lack of optimized
     421             :     // filters can sometimes make it slower than regular optimized libpng,
     422             :     // so restrict to when SSE2 is available.
     423             : 
     424             :     // CPLDebug("PNG", "Using libdeflate optimization");
     425             : 
     426        4004 :     char szChunkName[5] = {0};
     427        4004 :     bool bError = false;
     428             : 
     429             :     // We try to read the zlib compressed data into pData, if there is
     430             :     // enough room for that
     431        4004 :     size_t nDataSize = 0;
     432        8008 :     std::vector<GByte> abyCompressedData;  // keep in this scope
     433        4004 :     GByte *pabyCompressedData = static_cast<GByte *>(pSingleBuffer);
     434        4004 :     size_t nCompressedDataSize = 0;
     435        4004 :     if (pSingleBuffer)
     436             :     {
     437        2666 :         if (nPixelSpace == nBands && nLineSpace == nPixelSpace * nRasterXSize &&
     438         430 :             (nBands == 1 || nBandSpace == 1))
     439             :         {
     440         430 :             nDataSize =
     441         430 :                 static_cast<size_t>(nRasterXSize) * nRasterYSize * nBands;
     442             :         }
     443        2236 :         else if (nPixelSpace == 1 && nLineSpace == nRasterXSize &&
     444             :                  nBandSpace ==
     445        1965 :                      static_cast<GSpacing>(nRasterXSize) * nRasterYSize)
     446             :         {
     447        1965 :             nDataSize =
     448        1965 :                 static_cast<size_t>(nRasterXSize) * nRasterYSize * nBands;
     449             :         }
     450             :     }
     451             : 
     452        4004 :     const auto nPosBefore = VSIFTellL(fpImage);
     453        4004 :     bError = VSIFSeekL(fpImage, 8, SEEK_SET) != 0;
     454             :     // Iterate over PNG chunks
     455       15544 :     while (!bError)
     456             :     {
     457             :         uint32_t nChunkSize;
     458       15544 :         if (VSIFReadL(&nChunkSize, sizeof(nChunkSize), 1, fpImage) == 0)
     459             :         {
     460           0 :             bError = true;
     461        4004 :             break;
     462             :         }
     463       15544 :         CPL_MSBPTR32(&nChunkSize);
     464       15544 :         if (VSIFReadL(szChunkName, 4, 1, fpImage) == 0)
     465             :         {
     466           0 :             bError = true;
     467           0 :             break;
     468             :         }
     469       15544 :         if (strcmp(szChunkName, "IDAT") == 0)
     470             :         {
     471             :             // CPLDebug("PNG", "IDAT %u %u", unsigned(nCompressedDataSize),
     472             :             // unsigned(nChunkSize));
     473             : 
     474             :             // There can be several IDAT chunks: concatenate ZLib stream
     475       12994 :             if (nChunkSize >
     476        6497 :                 std::numeric_limits<size_t>::max() - nCompressedDataSize)
     477             :             {
     478           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
     479             :                          "Out of memory when reading compressed stream");
     480           0 :                 bError = true;
     481           0 :                 break;
     482             :             }
     483             : 
     484             :             // Sanity check to avoid allocating too much memory
     485        6497 :             if (nCompressedDataSize + nChunkSize > 100 * 1024 * 1024)
     486             :             {
     487           0 :                 const auto nCurPos = VSIFTellL(fpImage);
     488           0 :                 if (VSIFSeekL(fpImage, 0, SEEK_END) != 0)
     489             :                 {
     490           0 :                     bError = true;
     491           0 :                     break;
     492             :                 }
     493           0 :                 const auto nSize = VSIFTellL(fpImage);
     494           0 :                 if (VSIFSeekL(fpImage, nCurPos, SEEK_SET) != 0)
     495             :                 {
     496           0 :                     bError = true;
     497           0 :                     break;
     498             :                 }
     499           0 :                 if (nSize < 100 * 1024 * 1024)
     500             :                 {
     501           0 :                     CPLError(CE_Failure, CPLE_OutOfMemory,
     502             :                              "Attempt at reading more data than available in "
     503             :                              "compressed stream");
     504           0 :                     bError = true;
     505           0 :                     break;
     506             :                 }
     507             :             }
     508             : 
     509        6497 :             if (nCompressedDataSize + nChunkSize > nDataSize)
     510             :             {
     511        3708 :                 const bool bVectorEmptyBefore = abyCompressedData.empty();
     512             :                 // unlikely situation: would mean that the zlib compressed
     513             :                 // data is longer than the decompressed image
     514             :                 try
     515             :                 {
     516        3708 :                     abyCompressedData.resize(nCompressedDataSize + nChunkSize);
     517        3708 :                     pabyCompressedData = abyCompressedData.data();
     518             :                 }
     519           0 :                 catch (const std::exception &)
     520             :                 {
     521           0 :                     CPLError(CE_Failure, CPLE_OutOfMemory,
     522             :                              "Out of memory when allocating compressed stream");
     523           0 :                     bError = true;
     524           0 :                     break;
     525             :                 }
     526        3708 :                 if (bVectorEmptyBefore && pSingleBuffer &&
     527             :                     nCompressedDataSize > 0)
     528             :                 {
     529           0 :                     memcpy(pabyCompressedData, pSingleBuffer,
     530             :                            nCompressedDataSize);
     531             :                 }
     532             :             }
     533        6497 :             VSIFReadL(pabyCompressedData + nCompressedDataSize, nChunkSize, 1,
     534             :                       fpImage);
     535        6497 :             nCompressedDataSize += nChunkSize;
     536             :         }
     537        9047 :         else if (strcmp(szChunkName, "IEND") == 0)
     538        4004 :             break;
     539             :         else
     540             :         {
     541             :             // CPLDebug("PNG", "Skipping chunk %s of size %u", szChunkName,
     542             :             // nChunkSize);
     543        5043 :             bError = VSIFSeekL(fpImage, static_cast<vsi_l_offset>(nChunkSize),
     544             :                                SEEK_CUR) != 0;
     545             :         }
     546       11540 :         bError = bError || VSIFSeekL(fpImage, 4, SEEK_CUR) != 0;  // CRC
     547             :     }
     548        4004 :     bError = VSIFSeekL(fpImage, nPosBefore, SEEK_SET) != 0 || bError;
     549        4004 :     if (bError)
     550           0 :         return CE_Failure;
     551             : 
     552        4004 :     const int nSamplesPerLine = nRasterXSize * nBands;
     553             :     size_t nOutBytes;
     554        4004 :     constexpr int FILTER_TYPE_BYTE = 1;
     555        4004 :     const size_t nZlibDecompressedSize = static_cast<size_t>(nRasterYSize) *
     556        4004 :                                          (FILTER_TYPE_BYTE + nSamplesPerLine);
     557             :     GByte *pabyZlibDecompressed =
     558        4004 :         static_cast<GByte *>(VSI_MALLOC_VERBOSE(nZlibDecompressedSize));
     559        4004 :     if (pabyZlibDecompressed == nullptr)
     560             :     {
     561           0 :         return CE_Failure;
     562             :     }
     563             : 
     564        4004 :     if (CPLZLibInflate(pabyCompressedData, nCompressedDataSize,
     565             :                        pabyZlibDecompressed, nZlibDecompressedSize,
     566        4004 :                        &nOutBytes) == nullptr)
     567             :     {
     568           0 :         CPLError(CE_Failure, CPLE_AppDefined, "CPLZLibInflate() failed");
     569           0 :         CPLFree(pabyZlibDecompressed);
     570           0 :         return CE_Failure;
     571             :     }
     572             : 
     573             :     GByte *pabyOutputBuffer;
     574        8008 :     std::vector<GByte> abyTemp;
     575        8008 :     std::vector<GByte> abyLineUp;
     576             : 
     577        4004 :     if (pSingleBuffer != nullptr && nPixelSpace == nBands &&
     578         440 :         nLineSpace == nPixelSpace * nRasterXSize &&
     579         430 :         (nBands == 1 || nBandSpace == 1))
     580             :     {
     581         430 :         pabyOutputBuffer = static_cast<GByte *>(pSingleBuffer);
     582             :     }
     583             :     else
     584             :     {
     585        3574 :         abyTemp.resize(nSamplesPerLine);
     586        3574 :         pabyOutputBuffer = abyTemp.data();
     587             :     }
     588             : 
     589      581375 :     for (int iY = 0; iY < nRasterYSize; ++iY)
     590             :     {
     591             :         // Cf http://www.libpng.org/pub/png/spec/1.2/PNG-Filters.html
     592             :         // CPLDebug("PNG", "Line %d, filter type = %d", iY, nFilterType);
     593      577371 :         const GByte *CPL_RESTRICT pabyInputLine =
     594             :             pabyZlibDecompressed +
     595      577371 :             static_cast<size_t>(iY) * (FILTER_TYPE_BYTE + nSamplesPerLine);
     596      577371 :         const GByte nFilterType = pabyInputLine[0];
     597      577371 :         pabyInputLine++;
     598             :         GByte *const CPL_RESTRICT pabyOutputLine =
     599      577371 :             abyTemp.empty()
     600      577371 :                 ? pabyOutputBuffer + static_cast<size_t>(iY) * nSamplesPerLine
     601      486118 :                 : abyTemp.data();
     602      577371 :         if (nFilterType == 0)
     603             :         {
     604             :             // Filter type 0: None
     605      125846 :             memcpy(pabyOutputLine, pabyInputLine, nSamplesPerLine);
     606             :         }
     607      451525 :         else if (nFilterType == 1)
     608             :         {
     609             :             // Filter type 1: Sub (horizontal differencing)
     610             : #ifdef HAVE_SSE2
     611       13024 :             if (nBands == 3)
     612             :             {
     613             :                 png_row_info row_info;
     614        5108 :                 memset(&row_info, 0, sizeof(row_info));
     615        5108 :                 row_info.rowbytes = nSamplesPerLine;
     616             : 
     617        5108 :                 gdal_png_read_filter_row_sub3_sse2(&row_info, pabyInputLine,
     618             :                                                    pabyOutputLine);
     619             :             }
     620        7916 :             else if (nBands == 4)
     621             :             {
     622             :                 png_row_info row_info;
     623        6320 :                 memset(&row_info, 0, sizeof(row_info));
     624        6320 :                 row_info.rowbytes = nSamplesPerLine;
     625             : 
     626        6320 :                 gdal_png_read_filter_row_sub4_sse2(&row_info, pabyInputLine,
     627             :                                                    pabyOutputLine);
     628             :             }
     629             :             else
     630             : #endif
     631             :             {
     632             :                 int iX;
     633        4383 :                 for (iX = 0; iX < nBands; ++iX)
     634        2787 :                     pabyOutputLine[iX] = pabyInputLine[iX];
     635             : #if !defined(HAVE_SSE2)
     636             :                 if (nBands == 3)
     637             :                 {
     638             :                     GByte nLast0 = pabyOutputLine[0];
     639             :                     GByte nLast1 = pabyOutputLine[1];
     640             :                     GByte nLast2 = pabyOutputLine[2];
     641             :                     for (; iX + 5 < nSamplesPerLine; iX += 6)
     642             :                     {
     643             :                         nLast0 =
     644             :                             static_cast<GByte>(nLast0 + pabyInputLine[iX + 0]);
     645             :                         nLast1 =
     646             :                             static_cast<GByte>(nLast1 + pabyInputLine[iX + 1]);
     647             :                         nLast2 =
     648             :                             static_cast<GByte>(nLast2 + pabyInputLine[iX + 2]);
     649             :                         pabyOutputLine[iX + 0] = nLast0;
     650             :                         pabyOutputLine[iX + 1] = nLast1;
     651             :                         pabyOutputLine[iX + 2] = nLast2;
     652             :                         nLast0 =
     653             :                             static_cast<GByte>(nLast0 + pabyInputLine[iX + 3]);
     654             :                         nLast1 =
     655             :                             static_cast<GByte>(nLast1 + pabyInputLine[iX + 4]);
     656             :                         nLast2 =
     657             :                             static_cast<GByte>(nLast2 + pabyInputLine[iX + 5]);
     658             :                         pabyOutputLine[iX + 3] = nLast0;
     659             :                         pabyOutputLine[iX + 4] = nLast1;
     660             :                         pabyOutputLine[iX + 5] = nLast2;
     661             :                     }
     662             :                 }
     663             :                 else if (nBands == 4)
     664             :                 {
     665             :                     GByte nLast0 = pabyOutputLine[0];
     666             :                     GByte nLast1 = pabyOutputLine[1];
     667             :                     GByte nLast2 = pabyOutputLine[2];
     668             :                     GByte nLast3 = pabyOutputLine[3];
     669             :                     for (; iX + 7 < nSamplesPerLine; iX += 8)
     670             :                     {
     671             :                         nLast0 =
     672             :                             static_cast<GByte>(nLast0 + pabyInputLine[iX + 0]);
     673             :                         nLast1 =
     674             :                             static_cast<GByte>(nLast1 + pabyInputLine[iX + 1]);
     675             :                         nLast2 =
     676             :                             static_cast<GByte>(nLast2 + pabyInputLine[iX + 2]);
     677             :                         nLast3 =
     678             :                             static_cast<GByte>(nLast3 + pabyInputLine[iX + 3]);
     679             :                         pabyOutputLine[iX + 0] = nLast0;
     680             :                         pabyOutputLine[iX + 1] = nLast1;
     681             :                         pabyOutputLine[iX + 2] = nLast2;
     682             :                         pabyOutputLine[iX + 3] = nLast3;
     683             :                         nLast0 =
     684             :                             static_cast<GByte>(nLast0 + pabyInputLine[iX + 4]);
     685             :                         nLast1 =
     686             :                             static_cast<GByte>(nLast1 + pabyInputLine[iX + 5]);
     687             :                         nLast2 =
     688             :                             static_cast<GByte>(nLast2 + pabyInputLine[iX + 6]);
     689             :                         nLast3 =
     690             :                             static_cast<GByte>(nLast3 + pabyInputLine[iX + 7]);
     691             :                         pabyOutputLine[iX + 4] = nLast0;
     692             :                         pabyOutputLine[iX + 5] = nLast1;
     693             :                         pabyOutputLine[iX + 6] = nLast2;
     694             :                         pabyOutputLine[iX + 7] = nLast3;
     695             :                     }
     696             :                 }
     697             : #endif
     698      200366 :                 for (; iX < nSamplesPerLine; ++iX)
     699      198770 :                     pabyOutputLine[iX] = static_cast<GByte>(
     700      198770 :                         pabyInputLine[iX] + pabyOutputLine[iX - nBands]);
     701             :             }
     702             :         }
     703      438501 :         else if (nFilterType == 2)
     704             :         {
     705             :             // Filter type 2: Up (vertical differencing)
     706       74420 :             if (iY == 0)
     707             :             {
     708           0 :                 memcpy(pabyOutputLine, pabyInputLine, nSamplesPerLine);
     709             :             }
     710             :             else
     711             :             {
     712       74420 :                 if (abyTemp.empty())
     713             :                 {
     714       22594 :                     const GByte *CPL_RESTRICT pabyOutputLineUp =
     715             :                         pabyOutputBuffer +
     716       22594 :                         (static_cast<size_t>(iY) - 1) * nSamplesPerLine;
     717             : #if defined(__GNUC__) && !defined(__SSE2__) && !defined(USE_NEON_OPTIMIZATIONS)
     718             :                     AddVectors(pabyInputLine, pabyOutputLineUp, pabyOutputLine,
     719             :                                nSamplesPerLine);
     720             : #else
     721             :                     int iX;
     722             : #ifdef HAVE_SSE2
     723      215814 :                     for (iX = 0; iX + 31 < nSamplesPerLine; iX += 32)
     724             :                     {
     725             :                         auto in =
     726      193220 :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
     727      193220 :                                 pabyInputLine + iX));
     728             :                         auto in2 =
     729      193220 :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
     730      193220 :                                 pabyInputLine + iX + 16));
     731             :                         auto up =
     732      193220 :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
     733      193220 :                                 pabyOutputLineUp + iX));
     734             :                         auto up2 =
     735      193220 :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
     736      193220 :                                 pabyOutputLineUp + iX + 16));
     737      193220 :                         in = _mm_add_epi8(in, up);
     738      193220 :                         in2 = _mm_add_epi8(in2, up2);
     739      193220 :                         _mm_storeu_si128(
     740      193220 :                             reinterpret_cast<__m128i *>(pabyOutputLine + iX),
     741             :                             in);
     742      193220 :                         _mm_storeu_si128(reinterpret_cast<__m128i *>(
     743      193220 :                                              pabyOutputLine + iX + 16),
     744             :                                          in2);
     745             :                     }
     746             : #endif
     747       27648 :                     for (; iX < nSamplesPerLine; ++iX)
     748        5054 :                         pabyOutputLine[iX] = static_cast<GByte>(
     749        5054 :                             pabyInputLine[iX] + pabyOutputLineUp[iX]);
     750             : #endif
     751             :                 }
     752             :                 else
     753             :                 {
     754             : #if defined(__GNUC__) && !defined(__SSE2__) && !defined(USE_NEON_OPTIMIZATIONS)
     755             :                     AddVectors(pabyInputLine, pabyOutputLine, nSamplesPerLine);
     756             : #else
     757             :                     int iX;
     758             : #ifdef HAVE_SSE2
     759     1096500 :                     for (iX = 0; iX + 31 < nSamplesPerLine; iX += 32)
     760             :                     {
     761             :                         auto in =
     762     1044670 :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
     763     1044670 :                                 pabyInputLine + iX));
     764             :                         auto in2 =
     765     1044670 :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
     766     1044670 :                                 pabyInputLine + iX + 16));
     767             :                         auto out =
     768     2089340 :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
     769     1044670 :                                 pabyOutputLine + iX));
     770             :                         auto out2 =
     771     1044670 :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
     772     1044670 :                                 pabyOutputLine + iX + 16));
     773     1044670 :                         out = _mm_add_epi8(out, in);
     774     1044670 :                         out2 = _mm_add_epi8(out2, in2);
     775     1044670 :                         _mm_storeu_si128(
     776     1044670 :                             reinterpret_cast<__m128i *>(pabyOutputLine + iX),
     777             :                             out);
     778     1044670 :                         _mm_storeu_si128(reinterpret_cast<__m128i *>(
     779     1044670 :                                              pabyOutputLine + iX + 16),
     780             :                                          out2);
     781             :                     }
     782             : #endif
     783      220722 :                     for (; iX < nSamplesPerLine; ++iX)
     784      168896 :                         pabyOutputLine[iX] = static_cast<GByte>(
     785      168896 :                             pabyOutputLine[iX] + pabyInputLine[iX]);
     786             : #endif
     787             :                 }
     788             :             }
     789             :         }
     790      364081 :         else if (nFilterType == 3)
     791             :         {
     792             :             // Filter type 3: Average
     793       71692 :             if (iY == 0)
     794             :             {
     795         166 :                 for (int iX = 0; iX < nBands; ++iX)
     796             :                 {
     797         114 :                     pabyOutputLine[iX] = pabyInputLine[iX];
     798             :                 }
     799        1522 :                 for (int iX = nBands; iX < nSamplesPerLine; ++iX)
     800             :                 {
     801        1470 :                     pabyOutputLine[iX] = static_cast<GByte>(
     802        1470 :                         pabyInputLine[iX] + pabyOutputLine[iX - nBands] / 2);
     803             :                 }
     804             :             }
     805             :             else
     806             :             {
     807             : #ifdef HAVE_SSE2
     808       71640 :                 if (nBands == 3)
     809             :                 {
     810             :                     png_row_info row_info;
     811       61686 :                     memset(&row_info, 0, sizeof(row_info));
     812       61686 :                     row_info.rowbytes = nSamplesPerLine;
     813       61686 :                     if (!abyTemp.empty())
     814       61686 :                         abyLineUp = abyTemp;
     815             :                     const GByte *const pabyOutputLineUp =
     816       61686 :                         abyTemp.empty()
     817       61686 :                             ? pabyOutputBuffer + (static_cast<size_t>(iY) - 1) *
     818           0 :                                                      nSamplesPerLine
     819       61686 :                             : abyLineUp.data();
     820             : 
     821       61686 :                     gdal_png_read_filter_row_avg3_sse2(&row_info, pabyInputLine,
     822             :                                                        pabyOutputLine,
     823             :                                                        pabyOutputLineUp);
     824             :                 }
     825        9954 :                 else if (nBands == 4)
     826             :                 {
     827             :                     png_row_info row_info;
     828        6597 :                     memset(&row_info, 0, sizeof(row_info));
     829        6597 :                     row_info.rowbytes = nSamplesPerLine;
     830        6597 :                     if (!abyTemp.empty())
     831        6475 :                         abyLineUp = abyTemp;
     832             :                     const GByte *const pabyOutputLineUp =
     833        6597 :                         abyTemp.empty()
     834        6597 :                             ? pabyOutputBuffer + (static_cast<size_t>(iY) - 1) *
     835         122 :                                                      nSamplesPerLine
     836        6475 :                             : abyLineUp.data();
     837             : 
     838        6597 :                     gdal_png_read_filter_row_avg4_sse2(&row_info, pabyInputLine,
     839             :                                                        pabyOutputLine,
     840             :                                                        pabyOutputLineUp);
     841             :                 }
     842             :                 else
     843             : #endif
     844        3357 :                     if (abyTemp.empty())
     845             :                 {
     846         856 :                     const GByte *CPL_RESTRICT pabyOutputLineUp =
     847             :                         pabyOutputBuffer +
     848         856 :                         (static_cast<size_t>(iY) - 1) * nSamplesPerLine;
     849        1712 :                     for (int iX = 0; iX < nBands; ++iX)
     850             :                     {
     851         856 :                         pabyOutputLine[iX] = static_cast<GByte>(
     852         856 :                             pabyInputLine[iX] + pabyOutputLineUp[iX] / 2);
     853             :                     }
     854      197912 :                     for (int iX = nBands; iX < nSamplesPerLine; ++iX)
     855             :                     {
     856      197056 :                         pabyOutputLine[iX] = static_cast<GByte>(
     857      197056 :                             pabyInputLine[iX] + (pabyOutputLine[iX - nBands] +
     858      197056 :                                                  pabyOutputLineUp[iX]) /
     859             :                                                     2);
     860             :                     }
     861             :                 }
     862             :                 else
     863             :                 {
     864        7500 :                     for (int iX = 0; iX < nBands; ++iX)
     865             :                     {
     866        4999 :                         pabyOutputLine[iX] = static_cast<GByte>(
     867        4999 :                             pabyInputLine[iX] + pabyOutputLine[iX] / 2);
     868             :                     }
     869     1294270 :                     for (int iX = nBands; iX < nSamplesPerLine; ++iX)
     870             :                     {
     871     1291770 :                         pabyOutputLine[iX] = static_cast<GByte>(
     872     1291770 :                             pabyInputLine[iX] +
     873     1291770 :                             (pabyOutputLine[iX - nBands] + pabyOutputLine[iX]) /
     874             :                                 2);
     875             :                     }
     876             :                 }
     877             :             }
     878             :         }
     879      292389 :         else if (nFilterType == 4)
     880             :         {
     881             :             // Filter type 4: Paeth
     882      292389 :             if (iY == 0)
     883             :             {
     884      158620 :                 for (int iX = 0; iX < nSamplesPerLine; ++iX)
     885             :                 {
     886      158464 :                     GByte a = iX < nBands ? 0 : pabyOutputLine[iX - nBands];
     887      158464 :                     pabyOutputLine[iX] =
     888      158464 :                         static_cast<GByte>(pabyInputLine[iX] + a);
     889             :                 }
     890             :             }
     891             :             else
     892             :             {
     893      292233 :                 if (!abyTemp.empty())
     894      250475 :                     abyLineUp = abyTemp;
     895             :                 const GByte *const pabyOutputLineUp =
     896      292233 :                     abyTemp.empty()
     897      292233 :                         ? pabyOutputBuffer +
     898       41758 :                               (static_cast<size_t>(iY) - 1) * nSamplesPerLine
     899      250475 :                         : abyLineUp.data();
     900             : #ifdef HAVE_SSE2
     901      292233 :                 if (nBands == 3)
     902             :                 {
     903             :                     png_row_info row_info;
     904      236379 :                     memset(&row_info, 0, sizeof(row_info));
     905      236379 :                     row_info.rowbytes = nSamplesPerLine;
     906      236379 :                     gdal_png_read_filter_row_paeth3_sse2(
     907             :                         &row_info, pabyInputLine, pabyOutputLine,
     908             :                         pabyOutputLineUp);
     909             :                 }
     910       55854 :                 else if (nBands == 4)
     911             :                 {
     912             :                     png_row_info row_info;
     913       48578 :                     memset(&row_info, 0, sizeof(row_info));
     914       48578 :                     row_info.rowbytes = nSamplesPerLine;
     915       48578 :                     gdal_png_read_filter_row_paeth4_sse2(
     916             :                         &row_info, pabyInputLine, pabyOutputLine,
     917             :                         pabyOutputLineUp);
     918             :                 }
     919             :                 else
     920             : #endif
     921             :                 {
     922        7276 :                     int iX = 0;
     923       18551 :                     for (; iX < nBands; ++iX)
     924             :                     {
     925       11275 :                         GByte b = pabyOutputLineUp[iX];
     926       11275 :                         pabyOutputLine[iX] =
     927       11275 :                             static_cast<GByte>(pabyInputLine[iX] + b);
     928             :                     }
     929     1659560 :                     for (; iX < nSamplesPerLine; ++iX)
     930             :                     {
     931     1652290 :                         GByte a = pabyOutputLine[iX - nBands];
     932     1652290 :                         GByte b = pabyOutputLineUp[iX];
     933     1652290 :                         GByte c = pabyOutputLineUp[iX - nBands];
     934     1652290 :                         int p_minus_a = b - c;
     935     1652290 :                         int p_minus_b = a - c;
     936     1652290 :                         int p_minus_c = p_minus_a + p_minus_b;
     937     1652290 :                         int pa = std::abs(p_minus_a);
     938     1652290 :                         int pb = std::abs(p_minus_b);
     939     1652290 :                         int pc = std::abs(p_minus_c);
     940     1652290 :                         if (pa <= pb && pa <= pc)
     941     1480940 :                             pabyOutputLine[iX] =
     942     1480940 :                                 static_cast<GByte>(pabyInputLine[iX] + a);
     943      171349 :                         else if (pb <= pc)
     944      123738 :                             pabyOutputLine[iX] =
     945      123738 :                                 static_cast<GByte>(pabyInputLine[iX] + b);
     946             :                         else
     947       47611 :                             pabyOutputLine[iX] =
     948       47611 :                                 static_cast<GByte>(pabyInputLine[iX] + c);
     949             :                     }
     950             :                 }
     951             :             }
     952             :         }
     953             :         else
     954             :         {
     955           0 :             CPLError(CE_Failure, CPLE_NotSupported, "Invalid filter type %d",
     956             :                      nFilterType);
     957           0 :             CPLFree(pabyZlibDecompressed);
     958           0 :             return CE_Failure;
     959             :         }
     960             : 
     961      577371 :         if (!abyTemp.empty())
     962             :         {
     963      486118 :             if (pSingleBuffer)
     964             :             {
     965      153009 :                 GByte *pabyDest =
     966      153009 :                     static_cast<GByte *>(pSingleBuffer) + iY * nLineSpace;
     967      153009 :                 if (bCanUseDeinterleave)
     968             :                 {
     969             :                     // Cache friendly way for typical band interleaved case.
     970             :                     void *apDestBuffers[4];
     971       45007 :                     apDestBuffers[0] = pabyDest;
     972       45007 :                     apDestBuffers[1] = pabyDest + nBandSpace;
     973       45007 :                     apDestBuffers[2] = pabyDest + 2 * nBandSpace;
     974       45007 :                     apDestBuffers[3] = pabyDest + 3 * nBandSpace;
     975       45007 :                     GDALDeinterleave(pabyOutputLine, GDT_UInt8, nBands,
     976       45007 :                                      apDestBuffers, GDT_UInt8, nRasterXSize);
     977             :                 }
     978      108002 :                 else if (nPixelSpace <= nBands && nBandSpace > nBands)
     979             :                 {
     980             :                     // Cache friendly way for typical band interleaved case.
     981      107526 :                     for (int iBand = 0; iBand < nBands; iBand++)
     982             :                     {
     983       71684 :                         GByte *pabyDest2 = pabyDest + iBand * nBandSpace;
     984       71684 :                         const GByte *pabyScanline2 = pabyOutputLine + iBand;
     985       71684 :                         GDALCopyWords(pabyScanline2, GDT_UInt8, nBands,
     986             :                                       pabyDest2, GDT_UInt8,
     987             :                                       static_cast<int>(nPixelSpace),
     988             :                                       nRasterXSize);
     989       35842 :                     }
     990             :                 }
     991             :                 else
     992             :                 {
     993             :                     // Generic method
     994    18895300 :                     for (int x = 0; x < nRasterXSize; ++x)
     995             :                     {
     996    38694900 :                         for (int iBand = 0; iBand < nBands; iBand++)
     997             :                         {
     998    19871700 :                             pabyDest[(x * nPixelSpace) + iBand * nBandSpace] =
     999    19871700 :                                 pabyOutputLine[x * nBands + iBand];
    1000             :                         }
    1001             :                     }
    1002             :                 }
    1003             :             }
    1004             :             else
    1005             :             {
    1006             :                 GByte *apabyDestBuffers[4];
    1007     1353840 :                 for (int iBand = 0; iBand < nBands; iBand++)
    1008             :                 {
    1009     1020730 :                     apabyDestBuffers[iBand] =
    1010     1020730 :                         static_cast<GByte *>(apabyBuffers[iBand]) +
    1011     1020730 :                         iY * nRasterXSize;
    1012             :                 }
    1013      333109 :                 if (bCanUseDeinterleave)
    1014             :                 {
    1015             :                     // Cache friendly way for typical band interleaved case.
    1016      328941 :                     GDALDeinterleave(
    1017             :                         pabyOutputLine, GDT_UInt8, nBands,
    1018             :                         reinterpret_cast<void **>(apabyDestBuffers), GDT_UInt8,
    1019      328941 :                         nRasterXSize);
    1020             :                 }
    1021             :                 else
    1022             :                 {
    1023             :                     // Generic method
    1024     1118780 :                     for (int x = 0; x < nRasterXSize; ++x)
    1025             :                     {
    1026     3343850 :                         for (int iBand = 0; iBand < nBands; iBand++)
    1027             :                         {
    1028     2229230 :                             apabyDestBuffers[iBand][x] =
    1029     2229230 :                                 pabyOutputLine[x * nBands + iBand];
    1030             :                         }
    1031             :                     }
    1032             :                 }
    1033             :             }
    1034             :         }
    1035             :     }
    1036             : 
    1037        4004 :     CPLFree(pabyZlibDecompressed);
    1038             : 
    1039        4004 :     return CE_None;
    1040             : }
    1041             : 
    1042             : #endif  // ENABLE_WHOLE_IMAGE_OPTIMIZATION
    1043             : 
    1044             : /************************************************************************/
    1045             : /*                             IRasterIO()                              */
    1046             : /************************************************************************/
    1047             : 
    1048        4033 : CPLErr PNGDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
    1049             :                              int nXSize, int nYSize, void *pData, int nBufXSize,
    1050             :                              int nBufYSize, GDALDataType eBufType,
    1051             :                              int nBandCount, BANDMAP_TYPE panBandMap,
    1052             :                              GSpacing nPixelSpace, GSpacing nLineSpace,
    1053             :                              GSpacing nBandSpace,
    1054             :                              GDALRasterIOExtraArg *psExtraArg)
    1055             : 
    1056             : {
    1057             :     // Coverity says that we cannot pass a nullptr to IRasterIO.
    1058        4033 :     if (panBandMap == nullptr)
    1059             :     {
    1060           0 :         return CE_Failure;
    1061             :     }
    1062             : 
    1063        4033 :     if ((eRWFlag == GF_Read) && (nBandCount == nBands) && (nXOff == 0) &&
    1064        2705 :         (nYOff == 0) && (nXSize == nBufXSize) && (nXSize == nRasterXSize) &&
    1065        2701 :         (nYSize == nBufYSize) && (nYSize == nRasterYSize) &&
    1066        2627 :         (eBufType == GDT_UInt8) &&
    1067        2627 :         (eBufType == GetRasterBand(1)->GetRasterDataType()) &&
    1068        8066 :         (pData != nullptr) && IsAllBands(nBands, panBandMap))
    1069             :     {
    1070             : #ifdef ENABLE_WHOLE_IMAGE_OPTIMIZATION
    1071             :         // Below should work without SSE2, but the lack of optimized
    1072             :         // filters can sometimes make it slower than regular optimized libpng,
    1073             :         // so restrict to when SSE2 is available.
    1074             : 
    1075        5246 :         if (!bInterlaced && nBitDepth == 8 &&
    1076        2619 :             CPLTestBool(
    1077             :                 CPLGetConfigOption("GDAL_PNG_WHOLE_IMAGE_OPTIM", "YES")))
    1078             :         {
    1079        2611 :             return LoadWholeImage(pData, nPixelSpace, nLineSpace, nBandSpace,
    1080        2611 :                                   nullptr);
    1081             :         }
    1082          16 :         else if (cpl::down_cast<PNGRasterBand *>(papoBands[0])->nBlockYSize > 1)
    1083             :         {
    1084             :             // Below code requires scanline access in
    1085             :             // PNGRasterBand::IReadBlock()
    1086             :         }
    1087             :         else
    1088             : #endif  // ENABLE_WHOLE_IMAGE_OPTIMIZATION
    1089             : 
    1090             :             // Pixel interleaved case.
    1091          16 :             if (nBandSpace == 1)
    1092             :             {
    1093         302 :                 for (int y = 0; y < nYSize; ++y)
    1094             :                 {
    1095         300 :                     CPLErr tmpError = LoadScanline(y);
    1096         300 :                     if (tmpError != CE_None)
    1097           0 :                         return tmpError;
    1098         300 :                     const GByte *pabyScanline =
    1099         300 :                         pabyBuffer + (y - nBufferStartLine) * nBands * nXSize;
    1100         300 :                     if (nPixelSpace == nBandSpace * nBandCount)
    1101             :                     {
    1102         150 :                         memcpy(&(static_cast<GByte *>(pData)[(y * nLineSpace)]),
    1103             :                                pabyScanline,
    1104         150 :                                cpl::fits_on<int>(nBandCount * nXSize));
    1105             :                     }
    1106             :                     else
    1107             :                     {
    1108       24450 :                         for (int x = 0; x < nXSize; ++x)
    1109             :                         {
    1110       24300 :                             memcpy(&(static_cast<GByte *>(
    1111       24300 :                                        pData)[(y * nLineSpace) +
    1112       24300 :                                               (x * nPixelSpace)]),
    1113       24300 :                                    &(pabyScanline[x * nBandCount]), nBandCount);
    1114             :                         }
    1115             :                     }
    1116             :                 }
    1117           2 :                 return CE_None;
    1118             :             }
    1119             :             else
    1120             :             {
    1121          14 :                 const bool bCanUseDeinterleave =
    1122          19 :                     (nBands == 3 || nBands == 4) && nPixelSpace == 1 &&
    1123             :                     nBandSpace ==
    1124           5 :                         static_cast<GSpacing>(nRasterXSize) * nRasterYSize;
    1125             : 
    1126         966 :                 for (int y = 0; y < nYSize; ++y)
    1127             :                 {
    1128         952 :                     CPLErr tmpError = LoadScanline(y);
    1129         952 :                     if (tmpError != CE_None)
    1130           0 :                         return tmpError;
    1131         952 :                     const GByte *pabyScanline =
    1132         952 :                         pabyBuffer + (y - nBufferStartLine) * nBands * nXSize;
    1133         952 :                     GByte *pabyDest =
    1134         952 :                         static_cast<GByte *>(pData) + y * nLineSpace;
    1135         952 :                     if (bCanUseDeinterleave)
    1136             :                     {
    1137             :                         // Cache friendly way for typical band interleaved case.
    1138             :                         void *apDestBuffers[4];
    1139         176 :                         apDestBuffers[0] = pabyDest;
    1140         176 :                         apDestBuffers[1] = pabyDest + nBandSpace;
    1141         176 :                         apDestBuffers[2] = pabyDest + 2 * nBandSpace;
    1142         176 :                         apDestBuffers[3] = pabyDest + 3 * nBandSpace;
    1143         176 :                         GDALDeinterleave(pabyScanline, GDT_UInt8, nBands,
    1144             :                                          apDestBuffers, GDT_UInt8,
    1145         176 :                                          nRasterXSize);
    1146             :                     }
    1147         776 :                     else if (nPixelSpace <= nBands && nBandSpace > nBands)
    1148             :                     {
    1149             :                         // Cache friendly way for typical band interleaved case.
    1150          39 :                         for (int iBand = 0; iBand < nBands; iBand++)
    1151             :                         {
    1152          26 :                             GByte *pabyDest2 = pabyDest + iBand * nBandSpace;
    1153          26 :                             const GByte *pabyScanline2 = pabyScanline + iBand;
    1154          26 :                             GDALCopyWords(pabyScanline2, GDT_UInt8, nBands,
    1155             :                                           pabyDest2, GDT_UInt8,
    1156             :                                           static_cast<int>(nPixelSpace),
    1157             :                                           nXSize);
    1158          13 :                         }
    1159             :                     }
    1160             :                     else
    1161             :                     {
    1162             :                         // Generic method
    1163      124884 :                         for (int x = 0; x < nXSize; ++x)
    1164             :                         {
    1165      248242 :                             for (int iBand = 0; iBand < nBands; iBand++)
    1166             :                             {
    1167      124121 :                                 pabyDest[(x * nPixelSpace) +
    1168      124121 :                                          iBand * nBandSpace] =
    1169      124121 :                                     pabyScanline[x * nBands + iBand];
    1170             :                             }
    1171             :                         }
    1172             :                     }
    1173             :                 }
    1174          14 :                 return CE_None;
    1175             :             }
    1176             :     }
    1177             : 
    1178        1406 :     return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    1179             :                                      pData, nBufXSize, nBufYSize, eBufType,
    1180             :                                      nBandCount, panBandMap, nPixelSpace,
    1181        1406 :                                      nLineSpace, nBandSpace, psExtraArg);
    1182             : }
    1183             : 
    1184             : /************************************************************************/
    1185             : /*                             IRasterIO()                              */
    1186             : /************************************************************************/
    1187             : 
    1188        8038 : CPLErr PNGRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
    1189             :                                 int nXSize, int nYSize, void *pData,
    1190             :                                 int nBufXSize, int nBufYSize,
    1191             :                                 GDALDataType eBufType, GSpacing nPixelSpace,
    1192             :                                 GSpacing nLineSpace,
    1193             :                                 GDALRasterIOExtraArg *psExtraArg)
    1194             : 
    1195             : {
    1196             : #ifdef ENABLE_WHOLE_IMAGE_OPTIMIZATION
    1197        8038 :     auto poGDS = cpl::down_cast<PNGDataset *>(poDS);
    1198        8038 :     if ((eRWFlag == GF_Read) && (nXOff == 0) && (nYOff == 0) &&
    1199        4405 :         (nXSize == nBufXSize) && (nXSize == nRasterXSize) &&
    1200        4364 :         (nYSize == nBufYSize) && (nYSize == nRasterYSize) &&
    1201        3965 :         (eBufType == GDT_UInt8) && (eBufType == eDataType))
    1202             :     {
    1203        3965 :         bool bBlockAlreadyLoaded = false;
    1204        3965 :         if (nBlockYSize > 1)
    1205             :         {
    1206        3827 :             auto poBlock = TryGetLockedBlockRef(0, 0);
    1207        3827 :             if (poBlock != nullptr)
    1208             :             {
    1209        2585 :                 bBlockAlreadyLoaded = poBlock->GetDataRef() != pData;
    1210        2585 :                 poBlock->DropLock();
    1211             :             }
    1212             :         }
    1213             : 
    1214        3965 :         if (bBlockAlreadyLoaded)
    1215             :         {
    1216             :             // will got to general case
    1217             :         }
    1218          62 :         else if (poGDS->nBands == 1 && !poGDS->bInterlaced &&
    1219        1643 :                  poGDS->nBitDepth == 8 &&
    1220          62 :                  CPLTestBool(
    1221             :                      CPLGetConfigOption("GDAL_PNG_WHOLE_IMAGE_OPTIM", "YES")))
    1222             :         {
    1223          56 :             return poGDS->LoadWholeImage(pData, nPixelSpace, nLineSpace, 0,
    1224          56 :                                          nullptr);
    1225             :         }
    1226        1463 :         else if (nBlockYSize > 1)
    1227             :         {
    1228             :             void *apabyBuffers[4];
    1229        1338 :             GDALRasterBlock *apoBlocks[4] = {nullptr, nullptr, nullptr,
    1230             :                                              nullptr};
    1231        1338 :             CPLErr eErr = CE_None;
    1232        1338 :             bool bNeedToUseDefaultCase = true;
    1233        5439 :             for (int i = 0; i < poGDS->nBands; ++i)
    1234             :             {
    1235        4101 :                 if (i + 1 == nBand && nPixelSpace == 1 &&
    1236        1338 :                     nLineSpace == nRasterXSize)
    1237             :                 {
    1238        1336 :                     bNeedToUseDefaultCase = false;
    1239        1336 :                     apabyBuffers[i] = pData;
    1240             :                 }
    1241             :                 else
    1242             :                 {
    1243        2765 :                     apoBlocks[i] =
    1244        2765 :                         poGDS->GetRasterBand(i + 1)->GetLockedBlockRef(0, 0,
    1245        2765 :                                                                        TRUE);
    1246        2765 :                     apabyBuffers[i] =
    1247        2765 :                         apoBlocks[i] ? apoBlocks[i]->GetDataRef() : nullptr;
    1248        2765 :                     if (apabyBuffers[i] == nullptr)
    1249           0 :                         eErr = CE_Failure;
    1250             :                 }
    1251             :             }
    1252        1338 :             if (eErr == CE_None)
    1253             :             {
    1254        1338 :                 eErr = poGDS->LoadWholeImage(nullptr, 0, 0, 0, apabyBuffers);
    1255             :             }
    1256        5439 :             for (int i = 0; i < poGDS->nBands; ++i)
    1257             :             {
    1258        4101 :                 if (apoBlocks[i])
    1259        2765 :                     apoBlocks[i]->DropLock();
    1260             :             }
    1261        1338 :             if (eErr != CE_None || !bNeedToUseDefaultCase)
    1262        1336 :                 return eErr;
    1263             :         }
    1264             :     }
    1265             : #endif
    1266        6646 :     return GDALPamRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    1267             :                                         pData, nBufXSize, nBufYSize, eBufType,
    1268        6646 :                                         nPixelSpace, nLineSpace, psExtraArg);
    1269             : }
    1270             : 
    1271             : /************************************************************************/
    1272             : /*                          GetGeoTransform()                           */
    1273             : /************************************************************************/
    1274             : 
    1275         135 : CPLErr PNGDataset::GetGeoTransform(GDALGeoTransform &gt) const
    1276             : 
    1277             : {
    1278         135 :     const_cast<PNGDataset *>(this)->LoadWorldFile();
    1279             : 
    1280         135 :     if (bGeoTransformValid)
    1281             :     {
    1282           3 :         gt = m_gt;
    1283           3 :         return CE_None;
    1284             :     }
    1285             : 
    1286         132 :     return GDALPamDataset::GetGeoTransform(gt);
    1287             : }
    1288             : 
    1289             : /************************************************************************/
    1290             : /*                             FlushCache()                             */
    1291             : /*                                                                      */
    1292             : /*      We override this so we can also flush out local TIFF strip      */
    1293             : /*      cache if need be.                                               */
    1294             : /************************************************************************/
    1295             : 
    1296        9873 : CPLErr PNGDataset::FlushCache(bool bAtClosing)
    1297             : 
    1298             : {
    1299        9873 :     const CPLErr eErr = GDALPamDataset::FlushCache(bAtClosing);
    1300             : 
    1301        9873 :     if (pabyBuffer != nullptr)
    1302             :     {
    1303         230 :         CPLFree(pabyBuffer);
    1304         230 :         pabyBuffer = nullptr;
    1305         230 :         nBufferStartLine = 0;
    1306         230 :         nBufferLines = 0;
    1307             :     }
    1308        9873 :     return eErr;
    1309             : }
    1310             : 
    1311             : #ifdef DISABLE_CRC_CHECK
    1312             : /************************************************************************/
    1313             : /*                     PNGDatasetDisableCRCCheck()                      */
    1314             : /************************************************************************/
    1315             : 
    1316             : static void PNGDatasetDisableCRCCheck(png_structp hPNG)
    1317             : {
    1318             :     hPNG->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
    1319             :     hPNG->flags |= PNG_FLAG_CRC_CRITICAL_IGNORE;
    1320             : 
    1321             :     hPNG->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
    1322             :     hPNG->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN;
    1323             : }
    1324             : #endif
    1325             : 
    1326             : /************************************************************************/
    1327             : /*                              Restart()                               */
    1328             : /*                                                                      */
    1329             : /*      Restart reading from the beginning of the file.                 */
    1330             : /************************************************************************/
    1331             : 
    1332           0 : void PNGDataset::Restart()
    1333             : 
    1334             : {
    1335           0 :     if (!m_bHasRewind)
    1336             :     {
    1337           0 :         m_bHasRewind = true;
    1338           0 :         CPLDebug("PNG", "Restart decompression from top (emitted once)");
    1339             :     }
    1340             : 
    1341           0 :     png_destroy_read_struct(&hPNG, &psPNGInfo, nullptr);
    1342             : 
    1343           0 :     hPNG =
    1344           0 :         png_create_read_struct(PNG_LIBPNG_VER_STRING, this, nullptr, nullptr);
    1345             : 
    1346             : #ifdef DISABLE_CRC_CHECK
    1347             :     PNGDatasetDisableCRCCheck(hPNG);
    1348             : #endif
    1349             : 
    1350           0 :     png_set_error_fn(hPNG, &sSetJmpContext, png_gdal_error, png_gdal_warning);
    1351           0 :     if (setjmp(sSetJmpContext) != 0)
    1352           0 :         return;
    1353             : 
    1354           0 :     psPNGInfo = png_create_info_struct(hPNG);
    1355             : 
    1356           0 :     CPL_IGNORE_RET_VAL(VSIFSeekL(fpImage, 0, SEEK_SET));
    1357           0 :     png_set_read_fn(hPNG, fpImage, png_vsi_read_data);
    1358           0 :     png_read_info(hPNG, psPNGInfo);
    1359             : 
    1360           0 :     if (nBitDepth < 8)
    1361           0 :         png_set_packing(hPNG);
    1362             : 
    1363           0 :     nLastLineRead = -1;
    1364             : }
    1365             : 
    1366             : /************************************************************************/
    1367             : /*                        safe_png_read_image()                         */
    1368             : /************************************************************************/
    1369             : 
    1370          13 : static bool safe_png_read_image(png_structp hPNG, png_bytep *png_rows,
    1371             :                                 jmp_buf sSetJmpContext)
    1372             : {
    1373          13 :     if (setjmp(sSetJmpContext) != 0)
    1374           0 :         return false;
    1375          13 :     png_read_image(hPNG, png_rows);
    1376          13 :     return true;
    1377             : }
    1378             : 
    1379             : /************************************************************************/
    1380             : /*                        LoadInterlacedChunk()                         */
    1381             : /************************************************************************/
    1382             : 
    1383          13 : CPLErr PNGDataset::LoadInterlacedChunk(int iLine)
    1384             : 
    1385             : {
    1386             :     const int nPixelOffset =
    1387          13 :         (nBitDepth == 16) ? 2 * GetRasterCount() : GetRasterCount();
    1388             : 
    1389             :     // What is the biggest chunk we can safely operate on?
    1390          13 :     constexpr int MAX_PNG_CHUNK_BYTES = 100000000;
    1391             : 
    1392             :     int nMaxChunkLines =
    1393          13 :         std::max(1, MAX_PNG_CHUNK_BYTES / (nPixelOffset * GetRasterXSize()));
    1394             : 
    1395          13 :     if (nMaxChunkLines > GetRasterYSize())
    1396          13 :         nMaxChunkLines = GetRasterYSize();
    1397             : 
    1398             :     // Allocate chunk buffer if we don't already have it from a previous
    1399             :     // request.
    1400          13 :     nBufferLines = nMaxChunkLines;
    1401          13 :     if (nMaxChunkLines + iLine > GetRasterYSize())
    1402           0 :         nBufferStartLine = GetRasterYSize() - nMaxChunkLines;
    1403             :     else
    1404          13 :         nBufferStartLine = iLine;
    1405             : 
    1406          13 :     if (pabyBuffer == nullptr)
    1407             :     {
    1408          13 :         pabyBuffer = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
    1409             :             nPixelOffset, GetRasterXSize(), nMaxChunkLines));
    1410             : 
    1411          13 :         if (pabyBuffer == nullptr)
    1412             :         {
    1413           0 :             return CE_Failure;
    1414             :         }
    1415             : #ifdef notdef
    1416             :         if (nMaxChunkLines < GetRasterYSize())
    1417             :             CPLDebug("PNG",
    1418             :                      "Interlaced file being handled in %d line chunks.\n"
    1419             :                      "Performance is likely to be quite poor.",
    1420             :                      nMaxChunkLines);
    1421             : #endif
    1422             :     }
    1423             : 
    1424             :     // Do we need to restart reading? We do this if we aren't on the first
    1425             :     // attempt to read the image.
    1426          13 :     if (nLastLineRead != -1)
    1427             :     {
    1428           0 :         Restart();
    1429             :     }
    1430             : 
    1431             :     // Allocate and populate rows array. We create a row for each row in the
    1432             :     // image but use our dummy line for rows not in the target window.
    1433             :     png_bytep dummy_row = reinterpret_cast<png_bytep>(
    1434          13 :         CPLMalloc(cpl::fits_on<int>(nPixelOffset * GetRasterXSize())));
    1435             :     png_bytep *png_rows = reinterpret_cast<png_bytep *>(
    1436          13 :         CPLMalloc(sizeof(png_bytep) * GetRasterYSize()));
    1437             : 
    1438        1833 :     for (int i = 0; i < GetRasterYSize(); i++)
    1439             :     {
    1440        1820 :         if (i >= nBufferStartLine && i < nBufferStartLine + nBufferLines)
    1441        3640 :             png_rows[i] = pabyBuffer + (i - nBufferStartLine) * nPixelOffset *
    1442        1820 :                                            GetRasterXSize();
    1443             :         else
    1444           0 :             png_rows[i] = dummy_row;
    1445             :     }
    1446             : 
    1447          13 :     bool bRet = safe_png_read_image(hPNG, png_rows, sSetJmpContext);
    1448             : 
    1449             :     // Do swap on LSB machines. 16-bit PNG data is stored in MSB format.
    1450          13 :     if (bRet && nBitDepth == 16
    1451             : #ifdef CPL_LSB
    1452           1 :         && !m_bByteOrderIsLittleEndian
    1453             : #else
    1454             :         && m_bByteOrderIsLittleEndian
    1455             : #endif
    1456             :     )
    1457             :     {
    1458          21 :         for (int i = 0; i < GetRasterYSize(); i++)
    1459             :         {
    1460          20 :             if (i >= nBufferStartLine && i < nBufferStartLine + nBufferLines)
    1461             :             {
    1462          20 :                 GDALSwapWords(png_rows[i], 2,
    1463          20 :                               GetRasterXSize() * GetRasterCount(), 2);
    1464             :             }
    1465             :         }
    1466             :     }
    1467             : 
    1468          13 :     CPLFree(png_rows);
    1469          13 :     CPLFree(dummy_row);
    1470          13 :     if (!bRet)
    1471           0 :         return CE_Failure;
    1472             : 
    1473          13 :     nLastLineRead = nBufferStartLine + nBufferLines - 1;
    1474             : 
    1475          13 :     return CE_None;
    1476             : }
    1477             : 
    1478             : /************************************************************************/
    1479             : /*                         safe_png_read_rows()                         */
    1480             : /************************************************************************/
    1481             : 
    1482       12095 : static bool safe_png_read_rows(png_structp hPNG, png_bytep row,
    1483             :                                jmp_buf sSetJmpContext)
    1484             : {
    1485       12095 :     if (setjmp(sSetJmpContext) != 0)
    1486           2 :         return false;
    1487       12095 :     png_read_rows(hPNG, &row, nullptr, 1);
    1488       12093 :     return true;
    1489             : }
    1490             : 
    1491             : /************************************************************************/
    1492             : /*                            LoadScanline()                            */
    1493             : /************************************************************************/
    1494             : 
    1495       14245 : CPLErr PNGDataset::LoadScanline(int nLine)
    1496             : 
    1497             : {
    1498       14245 :     CPLAssert(nLine >= 0 && nLine < GetRasterYSize());
    1499             : 
    1500       14245 :     if (nLine >= nBufferStartLine && nLine < nBufferStartLine + nBufferLines)
    1501        2257 :         return CE_None;
    1502             : 
    1503             :     const int nPixelOffset =
    1504       11988 :         (nBitDepth == 16) ? 2 * GetRasterCount() : GetRasterCount();
    1505             : 
    1506             :     // If the file is interlaced, we load the entire image into memory using the
    1507             :     // high-level API.
    1508       11988 :     if (bInterlaced)
    1509          13 :         return LoadInterlacedChunk(nLine);
    1510             : 
    1511             :     // Ensure we have space allocated for one scanline.
    1512       11975 :     if (pabyBuffer == nullptr)
    1513         217 :         pabyBuffer = reinterpret_cast<GByte *>(
    1514         217 :             CPLMalloc(cpl::fits_on<int>(nPixelOffset * GetRasterXSize())));
    1515             : 
    1516             :     // Otherwise we just try to read the requested row. Do we need to rewind and
    1517             :     // start over?
    1518       11975 :     if (nLine <= nLastLineRead)
    1519             :     {
    1520           0 :         Restart();
    1521             :     }
    1522             : 
    1523             :     // Read till we get the desired row.
    1524       11975 :     png_bytep row = pabyBuffer;
    1525       11975 :     const GUInt32 nErrorCounter = CPLGetErrorCounter();
    1526       24068 :     while (nLine > nLastLineRead)
    1527             :     {
    1528       12095 :         if (!safe_png_read_rows(hPNG, row, sSetJmpContext))
    1529             :         {
    1530           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    1531             :                      "Error while reading row %d%s", nLine,
    1532           2 :                      (nErrorCounter != CPLGetErrorCounter())
    1533           2 :                          ? CPLSPrintf(": %s", CPLGetLastErrorMsg())
    1534             :                          : "");
    1535           2 :             return CE_Failure;
    1536             :         }
    1537       12093 :         nLastLineRead++;
    1538             :     }
    1539             : 
    1540       11973 :     nBufferStartLine = nLine;
    1541       11973 :     nBufferLines = 1;
    1542             : 
    1543             :     // Do swap on LSB machines. 16-bit PNG data is stored in MSB format.
    1544       11973 :     if (nBitDepth == 16
    1545             : #ifdef CPL_LSB
    1546       10317 :         && !m_bByteOrderIsLittleEndian
    1547             : #else
    1548             :         && m_bByteOrderIsLittleEndian
    1549             : #endif
    1550             :     )
    1551             :     {
    1552       10316 :         GDALSwapWords(row, 2, GetRasterXSize() * GetRasterCount(), 2);
    1553             :     }
    1554             : 
    1555       11973 :     return CE_None;
    1556             : }
    1557             : 
    1558             : /************************************************************************/
    1559             : /*                          CollectMetadata()                           */
    1560             : /*                                                                      */
    1561             : /*      We normally do this after reading up to the image, but be       */
    1562             : /*      forewarned: we can miss text chunks this way.                   */
    1563             : /*                                                                      */
    1564             : /*      We turn each PNG text chunk into one metadata item.  It         */
    1565             : /*      might be nice to preserve language information though we        */
    1566             : /*      don't try to now.                                               */
    1567             : /************************************************************************/
    1568             : 
    1569        9671 : void PNGDataset::CollectMetadata()
    1570             : 
    1571             : {
    1572        9671 :     if (nBitDepth < 8)
    1573             :     {
    1574          16 :         for (int iBand = 0; iBand < nBands; iBand++)
    1575             :         {
    1576          16 :             GetRasterBand(iBand + 1)->SetMetadataItem(
    1577          16 :                 "NBITS", CPLString().Printf("%d", nBitDepth),
    1578           8 :                 "IMAGE_STRUCTURE");
    1579             :         }
    1580             :     }
    1581             : 
    1582             :     int nTextCount;
    1583             :     png_textp text_ptr;
    1584        9671 :     if (png_get_text(hPNG, psPNGInfo, &text_ptr, &nTextCount) == 0)
    1585        9665 :         return;
    1586             : 
    1587          14 :     for (int iText = 0; iText < nTextCount; iText++)
    1588             :     {
    1589           8 :         char *pszTag = CPLStrdup(text_ptr[iText].key);
    1590             : 
    1591          66 :         for (int i = 0; pszTag[i] != '\0'; i++)
    1592             :         {
    1593          58 :             if (pszTag[i] == ' ' || pszTag[i] == '=' || pszTag[i] == ':')
    1594           0 :                 pszTag[i] = '_';
    1595             :         }
    1596             : 
    1597           8 :         GDALDataset::SetMetadataItem(pszTag, text_ptr[iText].text);
    1598           8 :         CPLFree(pszTag);
    1599             :     }
    1600             : }
    1601             : 
    1602             : /************************************************************************/
    1603             : /*                         CollectXMPMetadata()                         */
    1604             : /************************************************************************/
    1605             : 
    1606             : // See §2.1.5 of
    1607             : // http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf.
    1608             : 
    1609          13 : void PNGDataset::CollectXMPMetadata()
    1610             : 
    1611             : {
    1612          13 :     if (fpImage == nullptr || bHasReadXMPMetadata)
    1613           0 :         return;
    1614             : 
    1615             :     // Save current position to avoid disturbing PNG stream decoding.
    1616          13 :     const vsi_l_offset nCurOffset = VSIFTellL(fpImage);
    1617             : 
    1618          13 :     vsi_l_offset nOffset = 8;
    1619          13 :     bool bError = VSIFSeekL(fpImage, nOffset, SEEK_SET) != 0;
    1620             : 
    1621             :     // Loop over chunks.
    1622          74 :     while (!bError)
    1623             :     {
    1624             :         int nLength;
    1625             : 
    1626          74 :         if (VSIFReadL(&nLength, 4, 1, fpImage) != 1)
    1627          13 :             break;
    1628          74 :         nOffset += 4;
    1629          74 :         CPL_MSBPTR32(&nLength);
    1630          74 :         if (nLength <= 0)
    1631          12 :             break;
    1632             : 
    1633             :         char pszChunkType[5];
    1634          62 :         if (VSIFReadL(pszChunkType, 4, 1, fpImage) != 1)
    1635           0 :             break;
    1636          62 :         nOffset += 4;
    1637          62 :         pszChunkType[4] = 0;
    1638             : 
    1639          62 :         if (strcmp(pszChunkType, "iTXt") == 0 && nLength > 22 &&
    1640             :             // Does not make sense to have a XMP content larger than 10 MB
    1641             :             // (XMP in JPEG must fit in 65 KB...)
    1642           1 :             nLength < 10 * 1024 * 1024)
    1643             :         {
    1644           1 :             char *pszContent = reinterpret_cast<char *>(VSIMalloc(nLength + 1));
    1645           1 :             if (pszContent == nullptr)
    1646           0 :                 break;
    1647           1 :             if (VSIFReadL(pszContent, nLength, 1, fpImage) != 1)
    1648             :             {
    1649           0 :                 VSIFree(pszContent);
    1650           0 :                 break;
    1651             :             }
    1652           1 :             nOffset += nLength;
    1653           1 :             pszContent[nLength] = '\0';
    1654           1 :             if (memcmp(pszContent, "XML:com.adobe.xmp\0\0\0\0\0", 22) == 0)
    1655             :             {
    1656             :                 // Avoid setting the PAM dirty bit just for that.
    1657           1 :                 const int nOldPamFlags = nPamFlags;
    1658             : 
    1659           1 :                 char *apszMDList[2] = {pszContent + 22, nullptr};
    1660           1 :                 SetMetadata(apszMDList, "xml:XMP");
    1661             : 
    1662             :                 // cppcheck-suppress redundantAssignment
    1663           1 :                 nPamFlags = nOldPamFlags;
    1664             : 
    1665           1 :                 VSIFree(pszContent);
    1666             : 
    1667           1 :                 break;
    1668             :             }
    1669             :             else
    1670             :             {
    1671           0 :                 VSIFree(pszContent);
    1672           0 :             }
    1673             :         }
    1674             :         else
    1675             :         {
    1676          61 :             nOffset += nLength;
    1677          61 :             bError = VSIFSeekL(fpImage, nOffset, SEEK_SET) != 0;
    1678             :         }
    1679             : 
    1680          61 :         nOffset += 4;
    1681             :         int nCRC;
    1682          61 :         if (VSIFReadL(&nCRC, 4, 1, fpImage) != 1)
    1683           0 :             break;
    1684             :     }
    1685             : 
    1686          13 :     CPL_IGNORE_RET_VAL(VSIFSeekL(fpImage, nCurOffset, SEEK_SET));
    1687             : 
    1688          13 :     bHasReadXMPMetadata = TRUE;
    1689             : }
    1690             : 
    1691             : /************************************************************************/
    1692             : /*                           LoadICCProfile()                           */
    1693             : /************************************************************************/
    1694             : 
    1695          24 : void PNGDataset::LoadICCProfile()
    1696             : {
    1697          24 :     if (hPNG == nullptr || bHasReadICCMetadata)
    1698           7 :         return;
    1699          24 :     bHasReadICCMetadata = TRUE;
    1700             : 
    1701             :     png_charp pszProfileName;
    1702             :     png_uint_32 nProfileLength;
    1703             :     png_bytep pProfileData;
    1704             :     int nCompressionType;
    1705             : 
    1706             :     // Avoid setting the PAM dirty bit just for that.
    1707          24 :     int nOldPamFlags = nPamFlags;
    1708             : 
    1709          24 :     if (png_get_iCCP(hPNG, psPNGInfo, &pszProfileName, &nCompressionType,
    1710          24 :                      &pProfileData, &nProfileLength) != 0)
    1711             :     {
    1712             :         // Escape the profile.
    1713             :         char *pszBase64Profile =
    1714           5 :             CPLBase64Encode(static_cast<int>(nProfileLength),
    1715             :                             reinterpret_cast<const GByte *>(pProfileData));
    1716             : 
    1717             :         // Set ICC profile metadata.
    1718           5 :         SetMetadataItem("SOURCE_ICC_PROFILE", pszBase64Profile,
    1719           5 :                         "COLOR_PROFILE");
    1720           5 :         SetMetadataItem("SOURCE_ICC_PROFILE_NAME", pszProfileName,
    1721           5 :                         "COLOR_PROFILE");
    1722             : 
    1723           5 :         nPamFlags = nOldPamFlags;
    1724             : 
    1725           5 :         CPLFree(pszBase64Profile);
    1726             : 
    1727           5 :         return;
    1728             :     }
    1729             : 
    1730             :     int nsRGBIntent;
    1731          19 :     if (png_get_sRGB(hPNG, psPNGInfo, &nsRGBIntent) != 0)
    1732             :     {
    1733           2 :         SetMetadataItem("SOURCE_ICC_PROFILE_NAME", "sRGB", "COLOR_PROFILE");
    1734             : 
    1735           2 :         nPamFlags = nOldPamFlags;
    1736             : 
    1737           2 :         return;
    1738             :     }
    1739             : 
    1740             :     double dfGamma;
    1741          17 :     bool bGammaAvailable = false;
    1742          17 :     if (png_get_valid(hPNG, psPNGInfo, PNG_INFO_gAMA))
    1743             :     {
    1744           6 :         bGammaAvailable = true;
    1745             : 
    1746           6 :         png_get_gAMA(hPNG, psPNGInfo, &dfGamma);
    1747             : 
    1748           6 :         SetMetadataItem("PNG_GAMMA", CPLString().Printf("%.9f", dfGamma),
    1749           6 :                         "COLOR_PROFILE");
    1750             :     }
    1751             : 
    1752             :     // Check that both cHRM and gAMA are available.
    1753          17 :     if (bGammaAvailable && png_get_valid(hPNG, psPNGInfo, PNG_INFO_cHRM))
    1754             :     {
    1755             :         double dfaWhitepoint[2];
    1756             :         double dfaCHR[6];
    1757             : 
    1758           4 :         png_get_cHRM(hPNG, psPNGInfo, &dfaWhitepoint[0], &dfaWhitepoint[1],
    1759             :                      &dfaCHR[0], &dfaCHR[1], &dfaCHR[2], &dfaCHR[3], &dfaCHR[4],
    1760             :                      &dfaCHR[5]);
    1761             : 
    1762             :         // Set all the colorimetric metadata.
    1763           4 :         SetMetadataItem(
    1764             :             "SOURCE_PRIMARIES_RED",
    1765           8 :             CPLString().Printf("%.9f, %.9f, 1.0", dfaCHR[0], dfaCHR[1]),
    1766           4 :             "COLOR_PROFILE");
    1767           4 :         SetMetadataItem(
    1768             :             "SOURCE_PRIMARIES_GREEN",
    1769           8 :             CPLString().Printf("%.9f, %.9f, 1.0", dfaCHR[2], dfaCHR[3]),
    1770           4 :             "COLOR_PROFILE");
    1771           4 :         SetMetadataItem(
    1772             :             "SOURCE_PRIMARIES_BLUE",
    1773           8 :             CPLString().Printf("%.9f, %.9f, 1.0", dfaCHR[4], dfaCHR[5]),
    1774           4 :             "COLOR_PROFILE");
    1775             : 
    1776           4 :         SetMetadataItem("SOURCE_WHITEPOINT",
    1777           4 :                         CPLString().Printf("%.9f, %.9f, 1.0", dfaWhitepoint[0],
    1778           4 :                                            dfaWhitepoint[1]),
    1779           4 :                         "COLOR_PROFILE");
    1780             :     }
    1781             : 
    1782          17 :     nPamFlags = nOldPamFlags;
    1783             : }
    1784             : 
    1785             : /************************************************************************/
    1786             : /*                       GetMetadataDomainList()                        */
    1787             : /************************************************************************/
    1788             : 
    1789           3 : char **PNGDataset::GetMetadataDomainList()
    1790             : {
    1791           3 :     return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
    1792           3 :                                    TRUE, "xml:XMP", "COLOR_PROFILE", nullptr);
    1793             : }
    1794             : 
    1795             : /************************************************************************/
    1796             : /*                            GetMetadata()                             */
    1797             : /************************************************************************/
    1798             : 
    1799         109 : CSLConstList PNGDataset::GetMetadata(const char *pszDomain)
    1800             : {
    1801         109 :     if (fpImage == nullptr)
    1802           0 :         return nullptr;
    1803         109 :     if (eAccess == GA_ReadOnly && !bHasReadXMPMetadata &&
    1804          79 :         pszDomain != nullptr && EQUAL(pszDomain, "xml:XMP"))
    1805          13 :         CollectXMPMetadata();
    1806         109 :     if (eAccess == GA_ReadOnly && !bHasReadICCMetadata &&
    1807          49 :         pszDomain != nullptr && EQUAL(pszDomain, "COLOR_PROFILE"))
    1808          13 :         LoadICCProfile();
    1809         109 :     return GDALPamDataset::GetMetadata(pszDomain);
    1810             : }
    1811             : 
    1812             : /************************************************************************/
    1813             : /*                          GetMetadataItem()                           */
    1814             : /************************************************************************/
    1815         741 : const char *PNGDataset::GetMetadataItem(const char *pszName,
    1816             :                                         const char *pszDomain)
    1817             : {
    1818         741 :     if (eAccess == GA_ReadOnly && !bHasReadICCMetadata &&
    1819         217 :         pszDomain != nullptr && EQUAL(pszDomain, "COLOR_PROFILE"))
    1820          11 :         LoadICCProfile();
    1821         741 :     return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
    1822             : }
    1823             : 
    1824             : /************************************************************************/
    1825             : /*                                Open()                                */
    1826             : /************************************************************************/
    1827             : 
    1828        9673 : GDALDataset *PNGDataset::Open(GDALOpenInfo *poOpenInfo)
    1829             : 
    1830             : {
    1831             : #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
    1832             :     // During fuzzing, do not use Identify to reject crazy content.
    1833        9673 :     if (!PNGDriverIdentify(poOpenInfo))
    1834           1 :         return nullptr;
    1835             : #else
    1836             :     if (poOpenInfo->fpL == nullptr)
    1837             :         return nullptr;
    1838             : #endif
    1839             : 
    1840        9672 :     if (poOpenInfo->eAccess == GA_Update)
    1841             :     {
    1842           0 :         ReportUpdateNotSupportedByDriver("PNG");
    1843           0 :         return nullptr;
    1844             :     }
    1845             : 
    1846             :     // Create a corresponding GDALDataset.
    1847        9672 :     PNGDataset *poDS = new PNGDataset();
    1848        9672 :     return OpenStage2(poOpenInfo, poDS);
    1849             : }
    1850             : 
    1851        9672 : GDALDataset *PNGDataset::OpenStage2(GDALOpenInfo *poOpenInfo, PNGDataset *&poDS)
    1852             : 
    1853             : {
    1854        9672 :     poDS->fpImage = poOpenInfo->fpL;
    1855        9672 :     poOpenInfo->fpL = nullptr;
    1856        9672 :     poDS->eAccess = poOpenInfo->eAccess;
    1857             : 
    1858       19344 :     poDS->hPNG =
    1859        9672 :         png_create_read_struct(PNG_LIBPNG_VER_STRING, poDS, nullptr, nullptr);
    1860        9672 :     if (poDS->hPNG == nullptr)
    1861             :     {
    1862           0 :         int version = static_cast<int>(png_access_version_number());
    1863           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1864             :                  "The PNG driver failed to access libpng with version '%s',"
    1865             :                  " library is actually version '%d'.\n",
    1866             :                  PNG_LIBPNG_VER_STRING, version);
    1867           0 :         delete poDS;
    1868           0 :         return nullptr;
    1869             :     }
    1870             : 
    1871             : #ifdef DISABLE_CRC_CHECK
    1872             :     PNGDatasetDisableCRCCheck(poDS->hPNG);
    1873             : #endif
    1874             : 
    1875        9672 :     poDS->psPNGInfo = png_create_info_struct(poDS->hPNG);
    1876             : 
    1877             :     // Set up error handling.
    1878        9672 :     png_set_error_fn(poDS->hPNG, &poDS->sSetJmpContext, png_gdal_error,
    1879             :                      png_gdal_warning);
    1880             : 
    1881        9672 :     if (setjmp(poDS->sSetJmpContext) != 0)
    1882             :     {
    1883           1 :         delete poDS;
    1884           1 :         return nullptr;
    1885             :     }
    1886             : 
    1887             :     // Read pre-image data after ensuring the file is rewound.
    1888             :     // We should likely do a setjmp() here.
    1889             : 
    1890        9672 :     png_set_read_fn(poDS->hPNG, poDS->fpImage, png_vsi_read_data);
    1891        9672 :     png_read_info(poDS->hPNG, poDS->psPNGInfo);
    1892             : 
    1893             :     // Capture some information from the file that is of interest.
    1894        9671 :     poDS->nRasterXSize =
    1895        9671 :         static_cast<int>(png_get_image_width(poDS->hPNG, poDS->psPNGInfo));
    1896        9671 :     poDS->nRasterYSize =
    1897        9671 :         static_cast<int>(png_get_image_height(poDS->hPNG, poDS->psPNGInfo));
    1898             : 
    1899        9671 :     poDS->nBands = png_get_channels(poDS->hPNG, poDS->psPNGInfo);
    1900        9671 :     poDS->nBitDepth = png_get_bit_depth(poDS->hPNG, poDS->psPNGInfo);
    1901        9671 :     poDS->bInterlaced = png_get_interlace_type(poDS->hPNG, poDS->psPNGInfo) !=
    1902             :                         PNG_INTERLACE_NONE;
    1903             : 
    1904        9671 :     poDS->nColorType = png_get_color_type(poDS->hPNG, poDS->psPNGInfo);
    1905             : 
    1906        9671 :     if (poDS->nColorType == PNG_COLOR_TYPE_PALETTE && poDS->nBands > 1)
    1907             :     {
    1908           0 :         CPLDebug("GDAL",
    1909             :                  "PNG Driver got %d from png_get_channels(),\n"
    1910             :                  "but this kind of image (paletted) can only have one band.\n"
    1911             :                  "Correcting and continuing, but this may indicate a bug!",
    1912             :                  poDS->nBands);
    1913           0 :         poDS->nBands = 1;
    1914             :     }
    1915             : 
    1916             :     // We want to treat 1-, 2-, and 4-bit images as eight bit. This call causes
    1917             :     // libpng to unpack the image.
    1918        9671 :     if (poDS->nBitDepth < 8)
    1919           8 :         png_set_packing(poDS->hPNG);
    1920             : 
    1921             :     // Create band information objects.
    1922       34866 :     for (int iBand = 0; iBand < poDS->nBands; iBand++)
    1923       25195 :         poDS->SetBand(iBand + 1, new PNGRasterBand(poDS, iBand + 1));
    1924             : 
    1925             :     // Is there a palette?  Note: we should also read back and apply
    1926             :     // transparency values if available.
    1927        9671 :     if (poDS->nColorType == PNG_COLOR_TYPE_PALETTE)
    1928             :     {
    1929         386 :         png_color *pasPNGPalette = nullptr;
    1930         386 :         int nColorCount = 0;
    1931             : 
    1932         386 :         if (png_get_PLTE(poDS->hPNG, poDS->psPNGInfo, &pasPNGPalette,
    1933         386 :                          &nColorCount) == 0)
    1934           0 :             nColorCount = 0;
    1935             : 
    1936         386 :         unsigned char *trans = nullptr;
    1937         386 :         png_color_16 *trans_values = nullptr;
    1938         386 :         int num_trans = 0;
    1939         386 :         png_get_tRNS(poDS->hPNG, poDS->psPNGInfo, &trans, &num_trans,
    1940             :                      &trans_values);
    1941             : 
    1942         386 :         poDS->poColorTable = std::make_unique<GDALColorTable>();
    1943             : 
    1944             :         GDALColorEntry oEntry;
    1945         386 :         int nNoDataIndex = -1;
    1946       26060 :         for (int iColor = nColorCount - 1; iColor >= 0; iColor--)
    1947             :         {
    1948       25674 :             oEntry.c1 = pasPNGPalette[iColor].red;
    1949       25674 :             oEntry.c2 = pasPNGPalette[iColor].green;
    1950       25674 :             oEntry.c3 = pasPNGPalette[iColor].blue;
    1951             : 
    1952       25674 :             if (iColor < num_trans)
    1953             :             {
    1954        3489 :                 oEntry.c4 = trans[iColor];
    1955        3489 :                 if (oEntry.c4 == 0)
    1956             :                 {
    1957         269 :                     if (nNoDataIndex == -1)
    1958         269 :                         nNoDataIndex = iColor;
    1959             :                     else
    1960           0 :                         nNoDataIndex = -2;
    1961             :                 }
    1962             :             }
    1963             :             else
    1964       22185 :                 oEntry.c4 = 255;
    1965             : 
    1966       25674 :             poDS->poColorTable->SetColorEntry(iColor, &oEntry);
    1967             :         }
    1968             : 
    1969             :         // Special hack to use an index as the no data value, as long as it is
    1970             :         // the only transparent color in the palette.
    1971         386 :         if (nNoDataIndex > -1)
    1972             :         {
    1973         269 :             poDS->GetRasterBand(1)->SetNoDataValue(nNoDataIndex);
    1974             :         }
    1975             :     }
    1976             : 
    1977             :     // Check for transparency values in greyscale images.
    1978        9671 :     if (poDS->nColorType == PNG_COLOR_TYPE_GRAY)
    1979             :     {
    1980        1412 :         png_color_16 *trans_values = nullptr;
    1981             :         unsigned char *trans;
    1982             :         int num_trans;
    1983             : 
    1984        1412 :         if (png_get_tRNS(poDS->hPNG, poDS->psPNGInfo, &trans, &num_trans,
    1985        1417 :                          &trans_values) != 0 &&
    1986           5 :             trans_values != nullptr)
    1987             :         {
    1988           5 :             poDS->GetRasterBand(1)->SetNoDataValue(trans_values->gray);
    1989             :         }
    1990             :     }
    1991             : 
    1992             :     // Check for nodata color for RGB images.
    1993        9671 :     if (poDS->nColorType == PNG_COLOR_TYPE_RGB)
    1994             :     {
    1995        6363 :         png_color_16 *trans_values = nullptr;
    1996             :         unsigned char *trans;
    1997             :         int num_trans;
    1998             : 
    1999        6363 :         if (png_get_tRNS(poDS->hPNG, poDS->psPNGInfo, &trans, &num_trans,
    2000        6367 :                          &trans_values) != 0 &&
    2001           4 :             trans_values != nullptr)
    2002             :         {
    2003           8 :             CPLString oNDValue;
    2004             : 
    2005           4 :             oNDValue.Printf("%d %d %d", trans_values->red, trans_values->green,
    2006           4 :                             trans_values->blue);
    2007           4 :             poDS->SetMetadataItem("NODATA_VALUES", oNDValue.c_str());
    2008             : 
    2009           4 :             poDS->GetRasterBand(1)->SetNoDataValue(trans_values->red);
    2010           4 :             poDS->GetRasterBand(2)->SetNoDataValue(trans_values->green);
    2011           4 :             poDS->GetRasterBand(3)->SetNoDataValue(trans_values->blue);
    2012             :         }
    2013             :     }
    2014             : 
    2015        9671 :     png_color_16 *backgroundColor = nullptr;
    2016        9671 :     if (png_get_bKGD(poDS->hPNG, poDS->psPNGInfo, &backgroundColor) ==
    2017        9686 :             PNG_INFO_bKGD &&
    2018          15 :         backgroundColor)
    2019             :     {
    2020          15 :         if (poDS->nColorType == PNG_COLOR_TYPE_GRAY ||
    2021          12 :             poDS->nColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
    2022             :         {
    2023           5 :             poDS->SetMetadataItem("BACKGROUND_COLOR",
    2024           5 :                                   CPLSPrintf("%d", backgroundColor->gray));
    2025             :         }
    2026          10 :         else if (poDS->nColorType == PNG_COLOR_TYPE_PALETTE)
    2027             :         {
    2028           2 :             poDS->SetMetadataItem("BACKGROUND_COLOR",
    2029           2 :                                   CPLSPrintf("%d", backgroundColor->index));
    2030             :         }
    2031           8 :         else if (poDS->nColorType == PNG_COLOR_TYPE_RGB ||
    2032           2 :                  poDS->nColorType == PNG_COLOR_TYPE_RGB_ALPHA)
    2033             :         {
    2034           8 :             poDS->SetMetadataItem("BACKGROUND_COLOR",
    2035           8 :                                   CPLSPrintf("%d,%d,%d", backgroundColor->red,
    2036           8 :                                              backgroundColor->green,
    2037           8 :                                              backgroundColor->blue));
    2038             :         }
    2039             :     }
    2040             : 
    2041             :     // Extract any text chunks as "metadata."
    2042        9671 :     poDS->CollectMetadata();
    2043             : 
    2044             :     // More metadata.
    2045        9671 :     if (poDS->nBands > 1)
    2046             :     {
    2047        7873 :         poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
    2048             :     }
    2049             : 
    2050             :     // Initialize any PAM information.
    2051        9671 :     poDS->SetDescription(poOpenInfo->pszFilename);
    2052             : 
    2053             :     const char *pszPhysicalFileName =
    2054        9671 :         CSLFetchNameValue(poOpenInfo->papszOpenOptions, "PHYSICAL_FILENAME");
    2055        9671 :     if (pszPhysicalFileName)
    2056             :     {
    2057             :         // Code path used by JPGDatasetCommon::OpenRawThermalImage()
    2058           6 :         poDS->SetPhysicalFilename(pszPhysicalFileName);
    2059             :         const char *pszSubdatasetName =
    2060           6 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, "SUBDATASET_NAME");
    2061           6 :         if (pszSubdatasetName)
    2062             :         {
    2063           6 :             poDS->SetSubdatasetName(pszSubdatasetName);
    2064             :         }
    2065           6 :         poDS->TryLoadXML();
    2066             : 
    2067           6 :         poDS->oOvManager.Initialize(poDS, ":::VIRTUAL:::");
    2068             :     }
    2069             :     else
    2070             :     {
    2071        9665 :         poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
    2072             : 
    2073             :         // Open overviews.
    2074        9665 :         poDS->oOvManager.Initialize(poDS, poOpenInfo);
    2075             :     }
    2076             : 
    2077             :     // Used by JPEG FLIR
    2078        9671 :     poDS->m_bByteOrderIsLittleEndian = CPLTestBool(CSLFetchNameValueDef(
    2079        9671 :         poOpenInfo->papszOpenOptions, "BYTE_ORDER_LITTLE_ENDIAN", "NO"));
    2080             : 
    2081        9671 :     return poDS;
    2082             : }
    2083             : 
    2084             : /************************************************************************/
    2085             : /*                           LoadWorldFile()                            */
    2086             : /************************************************************************/
    2087             : 
    2088         186 : void PNGDataset::LoadWorldFile()
    2089             : {
    2090         186 :     if (bHasTriedLoadWorldFile)
    2091           5 :         return;
    2092         181 :     bHasTriedLoadWorldFile = TRUE;
    2093             : 
    2094         181 :     char *pszWldFilename = nullptr;
    2095         181 :     bGeoTransformValid =
    2096         181 :         GDALReadWorldFile2(GetDescription(), nullptr, m_gt,
    2097             :                            oOvManager.GetSiblingFiles(), &pszWldFilename);
    2098             : 
    2099         181 :     if (!bGeoTransformValid)
    2100         181 :         bGeoTransformValid =
    2101         181 :             GDALReadWorldFile2(GetDescription(), ".wld", m_gt,
    2102             :                                oOvManager.GetSiblingFiles(), &pszWldFilename);
    2103             : 
    2104         181 :     if (pszWldFilename)
    2105             :     {
    2106           3 :         osWldFilename = pszWldFilename;
    2107           3 :         CPLFree(pszWldFilename);
    2108             :     }
    2109             : }
    2110             : 
    2111             : /************************************************************************/
    2112             : /*                            GetFileList()                             */
    2113             : /************************************************************************/
    2114             : 
    2115          51 : char **PNGDataset::GetFileList()
    2116             : 
    2117             : {
    2118          51 :     char **papszFileList = GDALPamDataset::GetFileList();
    2119             : 
    2120          51 :     LoadWorldFile();
    2121             : 
    2122          52 :     if (!osWldFilename.empty() &&
    2123           1 :         CSLFindString(papszFileList, osWldFilename) == -1)
    2124             :     {
    2125           1 :         papszFileList = CSLAddString(papszFileList, osWldFilename);
    2126             :     }
    2127             : 
    2128          51 :     return papszFileList;
    2129             : }
    2130             : 
    2131             : /************************************************************************/
    2132             : /*                        WriteMetadataAsText()                         */
    2133             : /************************************************************************/
    2134             : 
    2135           3 : static bool IsASCII(const char *pszStr)
    2136             : {
    2137          28 :     for (int i = 0; pszStr[i] != '\0'; i++)
    2138             :     {
    2139          25 :         if (reinterpret_cast<GByte *>(const_cast<char *>(pszStr))[i] >= 128)
    2140           0 :             return false;
    2141             :     }
    2142           3 :     return true;
    2143             : }
    2144             : 
    2145           3 : static bool safe_png_set_text(jmp_buf sSetJmpContext, png_structp png_ptr,
    2146             :                               png_infop info_ptr, png_const_textp text_ptr,
    2147             :                               int num_text)
    2148             : {
    2149           3 :     if (setjmp(sSetJmpContext) != 0)
    2150             :     {
    2151           0 :         return false;
    2152             :     }
    2153           3 :     png_set_text(png_ptr, info_ptr, text_ptr, num_text);
    2154           3 :     return true;
    2155             : }
    2156             : 
    2157           3 : void PNGDataset::WriteMetadataAsText(jmp_buf sSetJmpContext, png_structp hPNG,
    2158             :                                      png_infop psPNGInfo, const char *pszKey,
    2159             :                                      const char *pszValue)
    2160             : {
    2161             :     png_text sText;
    2162           3 :     memset(&sText, 0, sizeof(png_text));
    2163           3 :     sText.compression = PNG_TEXT_COMPRESSION_NONE;
    2164           3 :     sText.key = const_cast<png_charp>(pszKey);
    2165           3 :     sText.text = const_cast<png_charp>(pszValue);
    2166             : 
    2167             :     // UTF-8 values should be written in iTXt, whereas TEXT should be LATIN-1.
    2168           3 :     if (!IsASCII(pszValue) && CPLIsUTF8(pszValue, -1))
    2169           0 :         sText.compression = PNG_ITXT_COMPRESSION_NONE;
    2170             : 
    2171           3 :     safe_png_set_text(sSetJmpContext, hPNG, psPNGInfo, &sText, 1);
    2172           3 : }
    2173             : 
    2174        4392 : static bool safe_png_set_IHDR(jmp_buf sSetJmpContext, png_structp png_ptr,
    2175             :                               png_infop info_ptr, png_uint_32 width,
    2176             :                               png_uint_32 height, int bit_depth, int color_type,
    2177             :                               int interlace_type, int compression_type,
    2178             :                               int filter_type)
    2179             : {
    2180        4392 :     if (setjmp(sSetJmpContext) != 0)
    2181             :     {
    2182           0 :         return false;
    2183             :     }
    2184        4392 :     png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type,
    2185             :                  interlace_type, compression_type, filter_type);
    2186        4392 :     return true;
    2187             : }
    2188             : 
    2189        3990 : static bool safe_png_set_compression_level(jmp_buf sSetJmpContext,
    2190             :                                            png_structp png_ptr, int level)
    2191             : {
    2192        3990 :     if (setjmp(sSetJmpContext) != 0)
    2193             :     {
    2194           0 :         return false;
    2195             :     }
    2196        3990 :     png_set_compression_level(png_ptr, level);
    2197        3990 :     return true;
    2198             : }
    2199             : 
    2200           9 : static bool safe_png_set_tRNS(jmp_buf sSetJmpContext, png_structp png_ptr,
    2201             :                               png_infop info_ptr, png_const_bytep trans,
    2202             :                               int num_trans, png_color_16p trans_values)
    2203             : {
    2204           9 :     if (setjmp(sSetJmpContext) != 0)
    2205             :     {
    2206           0 :         return false;
    2207             :     }
    2208           9 :     png_set_tRNS(png_ptr, info_ptr, trans, num_trans, trans_values);
    2209           9 :     return true;
    2210             : }
    2211             : 
    2212           6 : static bool safe_png_set_bKGD(jmp_buf sSetJmpContext, png_structp png_ptr,
    2213             :                               png_infop info_ptr,
    2214             :                               png_const_color_16p background)
    2215             : {
    2216           6 :     if (setjmp(sSetJmpContext) != 0)
    2217             :     {
    2218           0 :         return false;
    2219             :     }
    2220           6 :     png_set_bKGD(png_ptr, info_ptr, background);
    2221           6 :     return true;
    2222             : }
    2223             : 
    2224           2 : static bool safe_png_set_iCCP(jmp_buf sSetJmpContext, png_structp png_ptr,
    2225             :                               png_infop info_ptr, png_const_charp name,
    2226             :                               int compression_type, png_const_bytep profile,
    2227             :                               png_uint_32 proflen)
    2228             : {
    2229           2 :     if (setjmp(sSetJmpContext) != 0)
    2230             :     {
    2231           0 :         return false;
    2232             :     }
    2233           2 :     png_set_iCCP(png_ptr, info_ptr, name, compression_type, profile, proflen);
    2234           2 :     return true;
    2235             : }
    2236             : 
    2237          34 : static bool safe_png_set_PLTE(jmp_buf sSetJmpContext, png_structp png_ptr,
    2238             :                               png_infop info_ptr, png_const_colorp palette,
    2239             :                               int num_palette)
    2240             : {
    2241          34 :     if (setjmp(sSetJmpContext) != 0)
    2242             :     {
    2243           0 :         return false;
    2244             :     }
    2245          34 :     png_set_PLTE(png_ptr, info_ptr, palette, num_palette);
    2246          34 :     return true;
    2247             : }
    2248             : 
    2249        4390 : static bool safe_png_write_info(jmp_buf sSetJmpContext, png_structp png_ptr,
    2250             :                                 png_infop info_ptr)
    2251             : {
    2252        4390 :     if (setjmp(sSetJmpContext) != 0)
    2253             :     {
    2254           5 :         return false;
    2255             :     }
    2256        4390 :     png_write_info(png_ptr, info_ptr);
    2257        4385 :     return true;
    2258             : }
    2259             : 
    2260      180360 : static bool safe_png_write_rows(jmp_buf sSetJmpContext, png_structp png_ptr,
    2261             :                                 png_bytepp row, png_uint_32 num_rows)
    2262             : {
    2263      180360 :     if (setjmp(sSetJmpContext) != 0)
    2264             :     {
    2265           4 :         return false;
    2266             :     }
    2267      180360 :     png_write_rows(png_ptr, row, num_rows);
    2268      180356 :     return true;
    2269             : }
    2270             : 
    2271        4385 : static bool safe_png_write_end(jmp_buf sSetJmpContext, png_structp png_ptr,
    2272             :                                png_infop info_ptr)
    2273             : {
    2274        4385 :     if (setjmp(sSetJmpContext) != 0)
    2275             :     {
    2276           7 :         return false;
    2277             :     }
    2278        4385 :     png_write_end(png_ptr, info_ptr);
    2279        4378 :     return true;
    2280             : }
    2281             : 
    2282             : /************************************************************************/
    2283             : /*                             CreateCopy()                             */
    2284             : /************************************************************************/
    2285             : 
    2286        4406 : GDALDataset *PNGDataset::CreateCopy(const char *pszFilename,
    2287             :                                     GDALDataset *poSrcDS, int bStrict,
    2288             :                                     CSLConstList papszOptions,
    2289             :                                     GDALProgressFunc pfnProgress,
    2290             :                                     void *pProgressData)
    2291             : 
    2292             : {
    2293             :     // Perform some rudimentary checks.
    2294        4406 :     const int nBands = poSrcDS->GetRasterCount();
    2295        4406 :     if (nBands != 1 && nBands != 2 && nBands != 3 && nBands != 4)
    2296             :     {
    2297           2 :         CPLError(CE_Failure, CPLE_NotSupported,
    2298             :                  "PNG driver doesn't support %d bands.  Must be 1 (grey),\n"
    2299             :                  "2 (grey+alpha), 3 (rgb) or 4 (rgba) bands.\n",
    2300             :                  nBands);
    2301             : 
    2302           2 :         return nullptr;
    2303             :     }
    2304             : 
    2305        4471 :     if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt8 &&
    2306          67 :         poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16)
    2307             :     {
    2308           9 :         CPLError(
    2309             :             (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
    2310             :             "PNG driver doesn't support data type %s. "
    2311             :             "Only eight bit (Byte) and sixteen bit (UInt16) bands supported. "
    2312             :             "%s\n",
    2313             :             GDALGetDataTypeName(poSrcDS->GetRasterBand(1)->GetRasterDataType()),
    2314             :             (bStrict) ? "" : "Defaulting to Byte");
    2315             : 
    2316           9 :         if (bStrict)
    2317           9 :             return nullptr;
    2318             :     }
    2319             : 
    2320             :     // Create the dataset.
    2321             :     auto fpImage(
    2322        4395 :         CPLTestBool(CSLFetchNameValueDef(
    2323             :             papszOptions, "@CREATE_ONLY_VISIBLE_AT_CLOSE_TIME", "NO"))
    2324         187 :             ? VSIFileManager::GetHandler(pszFilename)
    2325         187 :                   ->CreateOnlyVisibleAtCloseTime(pszFilename, true, nullptr)
    2326        8977 :             : VSIFilesystemHandler::OpenStatic(pszFilename, "wb"));
    2327        4395 :     if (fpImage == nullptr)
    2328             :     {
    2329           3 :         CPLError(CE_Failure, CPLE_OpenFailed,
    2330             :                  "Unable to create png file %s: %s\n", pszFilename,
    2331           3 :                  VSIStrerror(errno));
    2332           3 :         return nullptr;
    2333             :     }
    2334             : 
    2335             :     // Initialize PNG access to the file.
    2336             :     jmp_buf sSetJmpContext;
    2337             : 
    2338             :     png_structp hPNG =
    2339        4392 :         png_create_write_struct(PNG_LIBPNG_VER_STRING, &sSetJmpContext,
    2340        4392 :                                 png_gdal_error, png_gdal_warning);
    2341        4392 :     png_infop psPNGInfo = png_create_info_struct(hPNG);
    2342             : 
    2343             :     // Set up some parameters.
    2344        4392 :     int nColorType = 0;
    2345             : 
    2346        4392 :     if (nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() == nullptr)
    2347         157 :         nColorType = PNG_COLOR_TYPE_GRAY;
    2348        4235 :     else if (nBands == 1)
    2349          34 :         nColorType = PNG_COLOR_TYPE_PALETTE;
    2350        4201 :     else if (nBands == 2)
    2351         256 :         nColorType = PNG_COLOR_TYPE_GRAY_ALPHA;
    2352        3945 :     else if (nBands == 3)
    2353        3759 :         nColorType = PNG_COLOR_TYPE_RGB;
    2354         186 :     else if (nBands == 4)
    2355         186 :         nColorType = PNG_COLOR_TYPE_RGB_ALPHA;
    2356             : 
    2357             :     int nBitDepth;
    2358             :     GDALDataType eType;
    2359        4392 :     if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16)
    2360             :     {
    2361        4334 :         eType = GDT_UInt8;
    2362        4334 :         nBitDepth = 8;
    2363        4334 :         if (nBands == 1)
    2364             :         {
    2365         136 :             const char *pszNbits = poSrcDS->GetRasterBand(1)->GetMetadataItem(
    2366         136 :                 "NBITS", "IMAGE_STRUCTURE");
    2367         136 :             if (pszNbits != nullptr)
    2368             :             {
    2369           3 :                 nBitDepth = atoi(pszNbits);
    2370           3 :                 if (!(nBitDepth == 1 || nBitDepth == 2 || nBitDepth == 4))
    2371           0 :                     nBitDepth = 8;
    2372             :             }
    2373             :         }
    2374             :     }
    2375             :     else
    2376             :     {
    2377          58 :         eType = GDT_UInt16;
    2378          58 :         nBitDepth = 16;
    2379             :     }
    2380             : 
    2381        4392 :     const char *pszNbits = CSLFetchNameValue(papszOptions, "NBITS");
    2382        4392 :     if (eType == GDT_UInt8 && pszNbits != nullptr)
    2383             :     {
    2384          13 :         nBitDepth = atoi(pszNbits);
    2385          13 :         if (!(nBitDepth == 1 || nBitDepth == 2 || nBitDepth == 4 ||
    2386             :               nBitDepth == 8))
    2387             :         {
    2388           1 :             CPLError(CE_Warning, CPLE_NotSupported,
    2389             :                      "Invalid bit depth. Using 8");
    2390           1 :             nBitDepth = 8;
    2391             :         }
    2392             :     }
    2393             : 
    2394        4392 :     png_set_write_fn(hPNG, fpImage.get(), png_vsi_write_data, png_vsi_flush);
    2395             : 
    2396        4392 :     const int nXSize = poSrcDS->GetRasterXSize();
    2397        4392 :     const int nYSize = poSrcDS->GetRasterYSize();
    2398             : 
    2399        4392 :     if (!safe_png_set_IHDR(sSetJmpContext, hPNG, psPNGInfo, nXSize, nYSize,
    2400             :                            nBitDepth, nColorType, PNG_INTERLACE_NONE,
    2401             :                            PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE))
    2402             :     {
    2403           0 :         fpImage->CancelCreation();
    2404           0 :         png_destroy_write_struct(&hPNG, &psPNGInfo);
    2405           0 :         return nullptr;
    2406             :     }
    2407             : 
    2408             :     // Do we want to control the compression level?
    2409        4392 :     const char *pszLevel = CSLFetchNameValue(papszOptions, "ZLEVEL");
    2410             : 
    2411        4392 :     if (pszLevel)
    2412             :     {
    2413        3992 :         const int nLevel = atoi(pszLevel);
    2414        3992 :         if (nLevel < 0 || nLevel > 9)
    2415             :         {
    2416           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    2417             :                      "Illegal ZLEVEL value '%s', should be 0-9.", pszLevel);
    2418           2 :             fpImage->CancelCreation();
    2419           2 :             png_destroy_write_struct(&hPNG, &psPNGInfo);
    2420           2 :             return nullptr;
    2421             :         }
    2422             : 
    2423        3990 :         if (!safe_png_set_compression_level(sSetJmpContext, hPNG, nLevel))
    2424             :         {
    2425           0 :             fpImage->CancelCreation();
    2426           0 :             png_destroy_write_struct(&hPNG, &psPNGInfo);
    2427           0 :             return nullptr;
    2428             :         }
    2429             :     }
    2430             : 
    2431             :     // Try to handle nodata values as a tRNS block (note that for paletted
    2432             :     // images, we save the effect to apply as part of palette).
    2433             :     png_color_16 sTRNSColor;
    2434             : 
    2435             :     // Gray nodata.
    2436        4390 :     if (nColorType == PNG_COLOR_TYPE_GRAY)
    2437             :     {
    2438         155 :         int bHaveNoData = FALSE;
    2439             :         const double dfNoDataValue =
    2440         155 :             poSrcDS->GetRasterBand(1)->GetNoDataValue(&bHaveNoData);
    2441             : 
    2442         155 :         if (bHaveNoData && dfNoDataValue >= 0 && dfNoDataValue < 65536)
    2443             :         {
    2444           3 :             sTRNSColor.gray = static_cast<png_uint_16>(dfNoDataValue);
    2445           3 :             if (!safe_png_set_tRNS(sSetJmpContext, hPNG, psPNGInfo, nullptr, 0,
    2446             :                                    &sTRNSColor))
    2447             :             {
    2448           0 :                 fpImage->CancelCreation();
    2449           0 :                 png_destroy_write_struct(&hPNG, &psPNGInfo);
    2450           0 :                 return nullptr;
    2451             :             }
    2452             :         }
    2453             :     }
    2454             : 
    2455             :     // RGB nodata.
    2456        4390 :     if (nColorType == PNG_COLOR_TYPE_RGB)
    2457             :     {
    2458             :         // First try to use the NODATA_VALUES metadata item.
    2459        3759 :         if (poSrcDS->GetMetadataItem("NODATA_VALUES") != nullptr)
    2460             :         {
    2461             :             char **papszValues =
    2462           1 :                 CSLTokenizeString(poSrcDS->GetMetadataItem("NODATA_VALUES"));
    2463             : 
    2464           1 :             if (CSLCount(papszValues) >= 3)
    2465             :             {
    2466           1 :                 sTRNSColor.red = static_cast<png_uint_16>(atoi(papszValues[0]));
    2467           1 :                 sTRNSColor.green =
    2468           1 :                     static_cast<png_uint_16>(atoi(papszValues[1]));
    2469           1 :                 sTRNSColor.blue =
    2470           1 :                     static_cast<png_uint_16>(atoi(papszValues[2]));
    2471           1 :                 if (!safe_png_set_tRNS(sSetJmpContext, hPNG, psPNGInfo, nullptr,
    2472             :                                        0, &sTRNSColor))
    2473             :                 {
    2474           0 :                     fpImage->CancelCreation();
    2475           0 :                     png_destroy_write_struct(&hPNG, &psPNGInfo);
    2476           0 :                     CSLDestroy(papszValues);
    2477           0 :                     return nullptr;
    2478             :                 }
    2479             :             }
    2480             : 
    2481           1 :             CSLDestroy(papszValues);
    2482             :         }
    2483             :         // Otherwise, get the nodata value from the bands.
    2484             :         else
    2485             :         {
    2486        3758 :             int bHaveNoDataRed = FALSE;
    2487             :             const double dfNoDataValueRed =
    2488        3758 :                 poSrcDS->GetRasterBand(1)->GetNoDataValue(&bHaveNoDataRed);
    2489             : 
    2490        3758 :             int bHaveNoDataGreen = FALSE;
    2491             :             const double dfNoDataValueGreen =
    2492        3758 :                 poSrcDS->GetRasterBand(2)->GetNoDataValue(&bHaveNoDataGreen);
    2493             : 
    2494        3758 :             int bHaveNoDataBlue = FALSE;
    2495             :             const double dfNoDataValueBlue =
    2496        3758 :                 poSrcDS->GetRasterBand(3)->GetNoDataValue(&bHaveNoDataBlue);
    2497             : 
    2498        3758 :             if ((bHaveNoDataRed && dfNoDataValueRed >= 0 &&
    2499           0 :                  dfNoDataValueRed < 65536) &&
    2500           0 :                 (bHaveNoDataGreen && dfNoDataValueGreen >= 0 &&
    2501           0 :                  dfNoDataValueGreen < 65536) &&
    2502           0 :                 (bHaveNoDataBlue && dfNoDataValueBlue >= 0 &&
    2503             :                  dfNoDataValueBlue < 65536))
    2504             :             {
    2505           0 :                 sTRNSColor.red = static_cast<png_uint_16>(dfNoDataValueRed);
    2506           0 :                 sTRNSColor.green = static_cast<png_uint_16>(dfNoDataValueGreen);
    2507           0 :                 sTRNSColor.blue = static_cast<png_uint_16>(dfNoDataValueBlue);
    2508           0 :                 if (!safe_png_set_tRNS(sSetJmpContext, hPNG, psPNGInfo, nullptr,
    2509             :                                        0, &sTRNSColor))
    2510             :                 {
    2511           0 :                     fpImage->CancelCreation();
    2512           0 :                     png_destroy_write_struct(&hPNG, &psPNGInfo);
    2513           0 :                     return nullptr;
    2514             :                 }
    2515             :             }
    2516             :         }
    2517             :     }
    2518             : 
    2519        4390 :     if (const char *pszBackgroundColor =
    2520        4390 :             poSrcDS->GetMetadataItem("BACKGROUND_COLOR"))
    2521             :     {
    2522           6 :         bool ret_set_bKGD = true;
    2523           6 :         png_color_16 backgroundColor = {0, 0, 0, 0, 0};
    2524           6 :         if (nColorType == PNG_COLOR_TYPE_GRAY ||
    2525             :             nColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
    2526             :         {
    2527           2 :             backgroundColor.gray =
    2528           2 :                 static_cast<png_uint_16>(atoi(pszBackgroundColor));
    2529           2 :             ret_set_bKGD = safe_png_set_bKGD(sSetJmpContext, hPNG, psPNGInfo,
    2530             :                                              &backgroundColor);
    2531             :         }
    2532           4 :         else if (nColorType == PNG_COLOR_TYPE_PALETTE)
    2533             :         {
    2534           1 :             backgroundColor.index =
    2535           1 :                 static_cast<png_byte>(atoi(pszBackgroundColor));
    2536           1 :             ret_set_bKGD = safe_png_set_bKGD(sSetJmpContext, hPNG, psPNGInfo,
    2537             :                                              &backgroundColor);
    2538             :         }
    2539           3 :         else if (nColorType == PNG_COLOR_TYPE_RGB ||
    2540             :                  nColorType == PNG_COLOR_TYPE_RGB_ALPHA)
    2541             :         {
    2542             :             const CPLStringList aosTokens(
    2543           6 :                 CSLTokenizeString2(pszBackgroundColor, " ,", 3));
    2544           3 :             if (aosTokens.size() == 3)
    2545             :             {
    2546           3 :                 backgroundColor.red =
    2547           3 :                     static_cast<png_uint_16>(atoi(aosTokens[0]));
    2548           3 :                 backgroundColor.green =
    2549           3 :                     static_cast<png_uint_16>(atoi(aosTokens[1]));
    2550           3 :                 backgroundColor.blue =
    2551           3 :                     static_cast<png_uint_16>(atoi(aosTokens[2]));
    2552           3 :                 ret_set_bKGD = safe_png_set_bKGD(sSetJmpContext, hPNG,
    2553             :                                                  psPNGInfo, &backgroundColor);
    2554             :             }
    2555             :         }
    2556           6 :         if (!ret_set_bKGD)
    2557             :         {
    2558           0 :             fpImage->CancelCreation();
    2559           0 :             png_destroy_write_struct(&hPNG, &psPNGInfo);
    2560           0 :             return nullptr;
    2561             :         }
    2562             :     }
    2563             : 
    2564             :     // Copy color profile data.
    2565             :     const char *pszICCProfile =
    2566        4390 :         CSLFetchNameValue(papszOptions, "SOURCE_ICC_PROFILE");
    2567             :     const char *pszICCProfileName =
    2568        4390 :         CSLFetchNameValue(papszOptions, "SOURCE_ICC_PROFILE_NAME");
    2569        4390 :     if (pszICCProfileName == nullptr)
    2570        4389 :         pszICCProfileName = poSrcDS->GetMetadataItem("SOURCE_ICC_PROFILE_NAME",
    2571        4389 :                                                      "COLOR_PROFILE");
    2572             : 
    2573        4390 :     if (pszICCProfile == nullptr)
    2574             :         pszICCProfile =
    2575        4389 :             poSrcDS->GetMetadataItem("SOURCE_ICC_PROFILE", "COLOR_PROFILE");
    2576             : 
    2577        4390 :     if ((pszICCProfileName != nullptr) && EQUAL(pszICCProfileName, "sRGB"))
    2578             :     {
    2579           1 :         pszICCProfile = nullptr;
    2580             : 
    2581             :         // assumes this can't fail ?
    2582           1 :         png_set_sRGB(hPNG, psPNGInfo, PNG_sRGB_INTENT_PERCEPTUAL);
    2583             :     }
    2584             : 
    2585        4390 :     if (pszICCProfile != nullptr)
    2586             :     {
    2587           2 :         char *pEmbedBuffer = CPLStrdup(pszICCProfile);
    2588             :         png_uint_32 nEmbedLen =
    2589           2 :             CPLBase64DecodeInPlace(reinterpret_cast<GByte *>(pEmbedBuffer));
    2590           2 :         const char *pszLocalICCProfileName =
    2591           2 :             (pszICCProfileName != nullptr) ? pszICCProfileName : "ICC Profile";
    2592             : 
    2593           2 :         if (!safe_png_set_iCCP(
    2594             :                 sSetJmpContext, hPNG, psPNGInfo, pszLocalICCProfileName, 0,
    2595             :                 reinterpret_cast<png_const_bytep>(pEmbedBuffer), nEmbedLen))
    2596             :         {
    2597           0 :             CPLFree(pEmbedBuffer);
    2598           0 :             fpImage->CancelCreation();
    2599           0 :             png_destroy_write_struct(&hPNG, &psPNGInfo);
    2600           0 :             return nullptr;
    2601             :         }
    2602             : 
    2603           2 :         CPLFree(pEmbedBuffer);
    2604             :     }
    2605        4388 :     else if ((pszICCProfileName == nullptr) ||
    2606           1 :              !EQUAL(pszICCProfileName, "sRGB"))
    2607             :     {
    2608             :         // Output gamma, primaries and whitepoint.
    2609        4387 :         const char *pszGamma = CSLFetchNameValue(papszOptions, "PNG_GAMMA");
    2610        4387 :         if (pszGamma == nullptr)
    2611        4385 :             pszGamma = poSrcDS->GetMetadataItem("PNG_GAMMA", "COLOR_PROFILE");
    2612             : 
    2613        4387 :         if (pszGamma != nullptr)
    2614             :         {
    2615           4 :             double dfGamma = CPLAtof(pszGamma);
    2616             :             // assumes this can't fail ?
    2617           4 :             png_set_gAMA(hPNG, psPNGInfo, dfGamma);
    2618             :         }
    2619             : 
    2620             :         const char *pszPrimariesRed =
    2621        4387 :             CSLFetchNameValue(papszOptions, "SOURCE_PRIMARIES_RED");
    2622        4387 :         if (pszPrimariesRed == nullptr)
    2623        4386 :             pszPrimariesRed = poSrcDS->GetMetadataItem("SOURCE_PRIMARIES_RED",
    2624        4386 :                                                        "COLOR_PROFILE");
    2625             :         const char *pszPrimariesGreen =
    2626        4387 :             CSLFetchNameValue(papszOptions, "SOURCE_PRIMARIES_GREEN");
    2627        4387 :         if (pszPrimariesGreen == nullptr)
    2628        4386 :             pszPrimariesGreen = poSrcDS->GetMetadataItem(
    2629        4386 :                 "SOURCE_PRIMARIES_GREEN", "COLOR_PROFILE");
    2630             :         const char *pszPrimariesBlue =
    2631        4387 :             CSLFetchNameValue(papszOptions, "SOURCE_PRIMARIES_BLUE");
    2632        4387 :         if (pszPrimariesBlue == nullptr)
    2633        4386 :             pszPrimariesBlue = poSrcDS->GetMetadataItem("SOURCE_PRIMARIES_BLUE",
    2634        4386 :                                                         "COLOR_PROFILE");
    2635             :         const char *pszWhitepoint =
    2636        4387 :             CSLFetchNameValue(papszOptions, "SOURCE_WHITEPOINT");
    2637        4387 :         if (pszWhitepoint == nullptr)
    2638             :             pszWhitepoint =
    2639        4386 :                 poSrcDS->GetMetadataItem("SOURCE_WHITEPOINT", "COLOR_PROFILE");
    2640             : 
    2641        4387 :         if ((pszPrimariesRed != nullptr) && (pszPrimariesGreen != nullptr) &&
    2642           2 :             (pszPrimariesBlue != nullptr) && (pszWhitepoint != nullptr))
    2643             :         {
    2644           2 :             bool bOk = true;
    2645           2 :             double faColour[8] = {0.0};
    2646           2 :             char **apapszTokenList[4] = {nullptr};
    2647             : 
    2648           2 :             apapszTokenList[0] = CSLTokenizeString2(pszWhitepoint, ",",
    2649             :                                                     CSLT_ALLOWEMPTYTOKENS |
    2650             :                                                         CSLT_STRIPLEADSPACES |
    2651             :                                                         CSLT_STRIPENDSPACES);
    2652           2 :             apapszTokenList[1] = CSLTokenizeString2(pszPrimariesRed, ",",
    2653             :                                                     CSLT_ALLOWEMPTYTOKENS |
    2654             :                                                         CSLT_STRIPLEADSPACES |
    2655             :                                                         CSLT_STRIPENDSPACES);
    2656           2 :             apapszTokenList[2] = CSLTokenizeString2(pszPrimariesGreen, ",",
    2657             :                                                     CSLT_ALLOWEMPTYTOKENS |
    2658             :                                                         CSLT_STRIPLEADSPACES |
    2659             :                                                         CSLT_STRIPENDSPACES);
    2660           2 :             apapszTokenList[3] = CSLTokenizeString2(pszPrimariesBlue, ",",
    2661             :                                                     CSLT_ALLOWEMPTYTOKENS |
    2662             :                                                         CSLT_STRIPLEADSPACES |
    2663             :                                                         CSLT_STRIPENDSPACES);
    2664             : 
    2665           2 :             if ((CSLCount(apapszTokenList[0]) == 3) &&
    2666           2 :                 (CSLCount(apapszTokenList[1]) == 3) &&
    2667           6 :                 (CSLCount(apapszTokenList[2]) == 3) &&
    2668           2 :                 (CSLCount(apapszTokenList[3]) == 3))
    2669             :             {
    2670          10 :                 for (int i = 0; i < 4; i++)
    2671             :                 {
    2672          32 :                     for (int j = 0; j < 3; j++)
    2673             :                     {
    2674          24 :                         const double v = CPLAtof(apapszTokenList[i][j]);
    2675             : 
    2676          24 :                         if (j == 2)
    2677             :                         {
    2678             :                             /* Last term of xyY colour must be 1.0 */
    2679           8 :                             if (v != 1.0)
    2680             :                             {
    2681           0 :                                 bOk = false;
    2682           0 :                                 break;
    2683             :                             }
    2684             :                         }
    2685             :                         else
    2686             :                         {
    2687          16 :                             faColour[i * 2 + j] = v;
    2688             :                         }
    2689             :                     }
    2690           8 :                     if (!bOk)
    2691           0 :                         break;
    2692             :                 }
    2693             : 
    2694           2 :                 if (bOk)
    2695             :                 {
    2696             :                     // assumes this can't fail ?
    2697           2 :                     png_set_cHRM(hPNG, psPNGInfo, faColour[0], faColour[1],
    2698             :                                  faColour[2], faColour[3], faColour[4],
    2699             :                                  faColour[5], faColour[6], faColour[7]);
    2700             :                 }
    2701             :             }
    2702             : 
    2703           2 :             CSLDestroy(apapszTokenList[0]);
    2704           2 :             CSLDestroy(apapszTokenList[1]);
    2705           2 :             CSLDestroy(apapszTokenList[2]);
    2706           2 :             CSLDestroy(apapszTokenList[3]);
    2707             :         }
    2708             :     }
    2709             : 
    2710             :     // Write the palette if there is one. Technically, it may be possible to
    2711             :     // write 16-bit palettes for PNG, but for now, this is omitted.
    2712        4390 :     if (nColorType == PNG_COLOR_TYPE_PALETTE)
    2713             :     {
    2714          34 :         int bHaveNoData = FALSE;
    2715             :         double dfNoDataValue =
    2716          34 :             poSrcDS->GetRasterBand(1)->GetNoDataValue(&bHaveNoData);
    2717             : 
    2718          34 :         GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
    2719             : 
    2720          34 :         int nEntryCount = poCT->GetColorEntryCount();
    2721          34 :         int nMaxEntryCount = 1 << nBitDepth;
    2722          34 :         if (nEntryCount > nMaxEntryCount)
    2723           0 :             nEntryCount = nMaxEntryCount;
    2724             : 
    2725             :         png_color *pasPNGColors = reinterpret_cast<png_color *>(
    2726          34 :             CPLMalloc(sizeof(png_color) * nEntryCount));
    2727             : 
    2728             :         GDALColorEntry sEntry;
    2729          34 :         bool bFoundTrans = false;
    2730        7024 :         for (int iColor = 0; iColor < nEntryCount; iColor++)
    2731             :         {
    2732        6990 :             poCT->GetColorEntryAsRGB(iColor, &sEntry);
    2733        6990 :             if (sEntry.c4 != 255)
    2734         267 :                 bFoundTrans = true;
    2735             : 
    2736        6990 :             pasPNGColors[iColor].red = static_cast<png_byte>(sEntry.c1);
    2737        6990 :             pasPNGColors[iColor].green = static_cast<png_byte>(sEntry.c2);
    2738        6990 :             pasPNGColors[iColor].blue = static_cast<png_byte>(sEntry.c3);
    2739             :         }
    2740             : 
    2741          34 :         if (!safe_png_set_PLTE(sSetJmpContext, hPNG, psPNGInfo, pasPNGColors,
    2742             :                                nEntryCount))
    2743             :         {
    2744           0 :             CPLFree(pasPNGColors);
    2745           0 :             fpImage->CancelCreation();
    2746           0 :             png_destroy_write_struct(&hPNG, &psPNGInfo);
    2747           0 :             return nullptr;
    2748             :         }
    2749             : 
    2750          34 :         CPLFree(pasPNGColors);
    2751             : 
    2752             :         // If we have transparent elements in the palette, we need to write a
    2753             :         // transparency block.
    2754          34 :         if (bFoundTrans || bHaveNoData)
    2755             :         {
    2756             :             unsigned char *pabyAlpha =
    2757           5 :                 static_cast<unsigned char *>(CPLMalloc(nEntryCount));
    2758             : 
    2759         588 :             for (int iColor = 0; iColor < nEntryCount; iColor++)
    2760             :             {
    2761         583 :                 poCT->GetColorEntryAsRGB(iColor, &sEntry);
    2762         583 :                 pabyAlpha[iColor] = static_cast<unsigned char>(sEntry.c4);
    2763             : 
    2764         583 :                 if (bHaveNoData && iColor == static_cast<int>(dfNoDataValue))
    2765           3 :                     pabyAlpha[iColor] = 0;
    2766             :             }
    2767             : 
    2768           5 :             if (!safe_png_set_tRNS(sSetJmpContext, hPNG, psPNGInfo, pabyAlpha,
    2769             :                                    nEntryCount, nullptr))
    2770             :             {
    2771           0 :                 CPLFree(pabyAlpha);
    2772           0 :                 fpImage->CancelCreation();
    2773           0 :                 png_destroy_write_struct(&hPNG, &psPNGInfo);
    2774           0 :                 return nullptr;
    2775             :             }
    2776             : 
    2777           5 :             CPLFree(pabyAlpha);
    2778             :         }
    2779             :     }
    2780             : 
    2781             :     // Add text info.
    2782             :     // These are predefined keywords. See "4.2.7 tEXt Textual data" of
    2783             :     // http://www.w3.org/TR/PNG-Chunks.html for more information.
    2784        4390 :     const char *apszKeywords[] = {"Title",      "Author",        "Description",
    2785             :                                   "Copyright",  "Creation Time", "Software",
    2786             :                                   "Disclaimer", "Warning",       "Source",
    2787             :                                   "Comment",    nullptr};
    2788        4390 :     const bool bWriteMetadataAsText = CPLTestBool(
    2789             :         CSLFetchNameValueDef(papszOptions, "WRITE_METADATA_AS_TEXT", "FALSE"));
    2790       48290 :     for (int i = 0; apszKeywords[i] != nullptr; i++)
    2791             :     {
    2792       43900 :         const char *pszKey = apszKeywords[i];
    2793       43900 :         const char *pszValue = CSLFetchNameValue(papszOptions, pszKey);
    2794       43900 :         if (pszValue == nullptr && bWriteMetadataAsText)
    2795           9 :             pszValue = poSrcDS->GetMetadataItem(pszKey);
    2796       43900 :         if (pszValue != nullptr)
    2797             :         {
    2798           2 :             WriteMetadataAsText(sSetJmpContext, hPNG, psPNGInfo, pszKey,
    2799             :                                 pszValue);
    2800             :         }
    2801             :     }
    2802        4390 :     if (bWriteMetadataAsText)
    2803             :     {
    2804           1 :         CSLConstList papszSrcMD = poSrcDS->GetMetadata();
    2805           4 :         for (; papszSrcMD && *papszSrcMD; papszSrcMD++)
    2806             :         {
    2807           3 :             char *pszKey = nullptr;
    2808           3 :             const char *pszValue = CPLParseNameValue(*papszSrcMD, &pszKey);
    2809           3 :             if (pszKey && pszValue)
    2810             :             {
    2811           3 :                 if (CSLFindString(const_cast<char **>(apszKeywords), pszKey) <
    2812           1 :                         0 &&
    2813           4 :                     !EQUAL(pszKey, "AREA_OR_POINT") &&
    2814           1 :                     !EQUAL(pszKey, "NODATA_VALUES"))
    2815             :                 {
    2816           1 :                     WriteMetadataAsText(sSetJmpContext, hPNG, psPNGInfo, pszKey,
    2817             :                                         pszValue);
    2818             :                 }
    2819           3 :                 CPLFree(pszKey);
    2820             :             }
    2821             :         }
    2822             :     }
    2823             : 
    2824             :     // Write the PNG info.
    2825        4390 :     if (!safe_png_write_info(sSetJmpContext, hPNG, psPNGInfo))
    2826             :     {
    2827           5 :         fpImage->CancelCreation();
    2828           5 :         png_destroy_write_struct(&hPNG, &psPNGInfo);
    2829           5 :         return nullptr;
    2830             :     }
    2831             : 
    2832        4385 :     if (nBitDepth < 8)
    2833             :     {
    2834             :         // Assumes this can't fail
    2835           6 :         png_set_packing(hPNG);
    2836             :     }
    2837             : 
    2838             :     // Loop over the image, copying image data.
    2839        4385 :     CPLErr eErr = CE_None;
    2840        4385 :     const int nWordSize = GDALGetDataTypeSizeBytes(eType);
    2841             : 
    2842             :     GByte *pabyScanline = reinterpret_cast<GByte *>(
    2843        4385 :         CPLMalloc(cpl::fits_on<int>(nBands * nXSize * nWordSize)));
    2844             : 
    2845      184746 :     for (int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++)
    2846             :     {
    2847      180361 :         png_bytep row = pabyScanline;
    2848             : 
    2849      360722 :         eErr = poSrcDS->RasterIO(
    2850             :             GF_Read, 0, iLine, nXSize, 1, pabyScanline, nXSize, 1, eType,
    2851      180361 :             nBands, nullptr, static_cast<GSpacing>(nBands) * nWordSize,
    2852      180361 :             static_cast<GSpacing>(nBands) * nXSize * nWordSize, nWordSize,
    2853             :             nullptr);
    2854             : 
    2855             : #ifdef CPL_LSB
    2856      180361 :         if (nBitDepth == 16)
    2857       13397 :             GDALSwapWords(row, 2, nXSize * nBands, 2);
    2858             : #endif
    2859      180361 :         if (eErr == CE_None)
    2860             :         {
    2861      180360 :             if (!safe_png_write_rows(sSetJmpContext, hPNG, &row, 1))
    2862             :             {
    2863           4 :                 eErr = CE_Failure;
    2864             :             }
    2865             :         }
    2866             : 
    2867      360717 :         if (eErr == CE_None &&
    2868      180356 :             !pfnProgress((iLine + 1) / static_cast<double>(nYSize), nullptr,
    2869             :                          pProgressData))
    2870             :         {
    2871           1 :             eErr = CE_Failure;
    2872           1 :             CPLError(CE_Failure, CPLE_UserInterrupt,
    2873             :                      "User terminated CreateCopy()");
    2874             :         }
    2875             :     }
    2876             : 
    2877        4385 :     CPLFree(pabyScanline);
    2878             : 
    2879        4385 :     if (!safe_png_write_end(sSetJmpContext, hPNG, psPNGInfo))
    2880             :     {
    2881           7 :         eErr = CE_Failure;
    2882             :     }
    2883        4385 :     png_destroy_write_struct(&hPNG, &psPNGInfo);
    2884             : 
    2885        4385 :     if (eErr == CE_None)
    2886             :     {
    2887        4378 :         if (fpImage->Close() != 0)
    2888             :         {
    2889           0 :             CPLError(CE_Failure, CPLE_FileIO,
    2890             :                      "Error at file closing of '%s': %s", pszFilename,
    2891           0 :                      VSIStrerror(errno));
    2892           0 :             eErr = CE_Failure;
    2893             :         }
    2894             :     }
    2895             :     else
    2896             :     {
    2897           7 :         fpImage->CancelCreation();
    2898           7 :         fpImage.reset();
    2899             :     }
    2900             : 
    2901        4385 :     if (eErr != CE_None)
    2902           7 :         return nullptr;
    2903             : 
    2904             :     // Do we need a world file?
    2905        4378 :     if (CPLFetchBool(papszOptions, "WORLDFILE", false))
    2906             :     {
    2907           0 :         GDALGeoTransform gt;
    2908           0 :         if (poSrcDS->GetGeoTransform(gt) == CE_None)
    2909           0 :             GDALWriteWorldFile(pszFilename, "wld", gt.data());
    2910             :     }
    2911             : 
    2912             :     // Re-open dataset and copy any auxiliary PAM information.
    2913             : 
    2914             :     /* If writing to stdout, we can't reopen it, so return */
    2915             :     /* a fake dataset to make the caller happy */
    2916        4378 :     if (CPLTestBool(CPLGetConfigOption("GDAL_OPEN_AFTER_COPY", "YES")))
    2917             :     {
    2918        4191 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    2919        4191 :         GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
    2920             :         PNGDataset *poDS =
    2921        4191 :             cpl::down_cast<PNGDataset *>(PNGDataset::Open(&oOpenInfo));
    2922        4191 :         CPLPopErrorHandler();
    2923        4191 :         if (poDS)
    2924             :         {
    2925        4190 :             int nFlags = GCIF_PAM_DEFAULT & ~GCIF_METADATA;
    2926        4190 :             poDS->CloneInfo(poSrcDS, nFlags);
    2927             : 
    2928             :             char **papszExcludedDomains =
    2929        4190 :                 CSLAddString(nullptr, "COLOR_PROFILE");
    2930        4190 :             if (bWriteMetadataAsText)
    2931           1 :                 papszExcludedDomains = CSLAddString(papszExcludedDomains, "");
    2932        4190 :             GDALDriver::DefaultCopyMetadata(poSrcDS, poDS, papszOptions,
    2933             :                                             papszExcludedDomains);
    2934        4190 :             CSLDestroy(papszExcludedDomains);
    2935             : 
    2936        4190 :             return poDS;
    2937             :         }
    2938           1 :         CPLErrorReset();
    2939             :     }
    2940             : 
    2941         188 :     PNGDataset *poPNG_DS = new PNGDataset();
    2942         188 :     poPNG_DS->nRasterXSize = nXSize;
    2943         188 :     poPNG_DS->nRasterYSize = nYSize;
    2944         188 :     poPNG_DS->nBitDepth = nBitDepth;
    2945         763 :     for (int i = 0; i < nBands; i++)
    2946         575 :         poPNG_DS->SetBand(i + 1, new PNGRasterBand(poPNG_DS, i + 1));
    2947         188 :     return poPNG_DS;
    2948             : }
    2949             : 
    2950             : /************************************************************************/
    2951             : /*                         png_vsi_read_data()                          */
    2952             : /*                                                                      */
    2953             : /*      Read data callback through VSI.                                 */
    2954             : /************************************************************************/
    2955       77592 : static void png_vsi_read_data(png_structp png_ptr, png_bytep data,
    2956             :                               png_size_t length)
    2957             : 
    2958             : {
    2959             :     // fread() returns 0 on error, so it is OK to store this in a png_size_t
    2960             :     // instead of an int, which is what fread() actually returns.
    2961             :     const png_size_t check = static_cast<png_size_t>(
    2962      155184 :         VSIFReadL(data, 1, length,
    2963       77592 :                   reinterpret_cast<VSILFILE *>(png_get_io_ptr(png_ptr))));
    2964             : 
    2965       77592 :     if (check != length)
    2966           3 :         png_error(png_ptr, "Read Error");
    2967       77589 : }
    2968             : 
    2969             : /************************************************************************/
    2970             : /*                         png_vsi_write_data()                         */
    2971             : /************************************************************************/
    2972             : 
    2973       50860 : static void png_vsi_write_data(png_structp png_ptr, png_bytep data,
    2974             :                                png_size_t length)
    2975             : {
    2976      101720 :     const size_t check = VSIFWriteL(
    2977       50860 :         data, 1, length, reinterpret_cast<VSILFILE *>(png_get_io_ptr(png_ptr)));
    2978             : 
    2979       50860 :     if (check != length)
    2980          10 :         png_error(png_ptr, "Write Error");
    2981       50850 : }
    2982             : 
    2983             : /************************************************************************/
    2984             : /*                           png_vsi_flush()                            */
    2985             : /************************************************************************/
    2986           0 : static void png_vsi_flush(png_structp png_ptr)
    2987             : {
    2988           0 :     VSIFFlushL(reinterpret_cast<VSILFILE *>(png_get_io_ptr(png_ptr)));
    2989           0 : }
    2990             : 
    2991             : /************************************************************************/
    2992             : /*                           png_gdal_error()                           */
    2993             : /************************************************************************/
    2994             : 
    2995          19 : static void png_gdal_error(png_structp png_ptr, const char *error_message)
    2996             : {
    2997          19 :     CPLError(CE_Failure, CPLE_AppDefined, "libpng: %s", error_message);
    2998             : 
    2999             :     // Use longjmp instead of a C++ exception, because libpng is generally not
    3000             :     // built as C++ and so will not honor unwind semantics.
    3001             : 
    3002             :     jmp_buf *psSetJmpContext =
    3003          19 :         reinterpret_cast<jmp_buf *>(png_get_error_ptr(png_ptr));
    3004          19 :     if (psSetJmpContext)
    3005             :     {
    3006          19 :         longjmp(*psSetJmpContext, 1);
    3007             :     }
    3008           0 : }
    3009             : 
    3010             : /************************************************************************/
    3011             : /*                          png_gdal_warning()                          */
    3012             : /************************************************************************/
    3013             : 
    3014           0 : static void png_gdal_warning(CPL_UNUSED png_structp png_ptr,
    3015             :                              const char *error_message)
    3016             : {
    3017           0 :     CPLError(CE_Warning, CPLE_AppDefined, "libpng: %s", error_message);
    3018           0 : }
    3019             : 
    3020             : /************************************************************************/
    3021             : /*                          GDALRegister_PNG()                          */
    3022             : /************************************************************************/
    3023             : 
    3024          79 : void GDALRegister_PNG()
    3025             : 
    3026             : {
    3027          79 :     if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
    3028           0 :         return;
    3029             : 
    3030          79 :     GDALDriver *poDriver = new GDALDriver();
    3031          79 :     PNGDriverSetCommonMetadata(poDriver);
    3032             : 
    3033          79 :     poDriver->pfnOpen = PNGDataset::Open;
    3034          79 :     poDriver->pfnCreateCopy = PNGDataset::CreateCopy;
    3035             : 
    3036          79 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    3037             : }

Generated by: LCOV version 1.14