LCOV - code coverage report
Current view: top level - frmts/png - pngdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1081 1215 89.0 %
Date: 2025-05-31 00:00:17 Functions: 46 48 95.8 %

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

Generated by: LCOV version 1.14