LCOV - code coverage report
Current view: top level - frmts/png - pngdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1076 1229 87.6 %
Date: 2025-07-11 10:11:13 Functions: 46 49 93.9 %

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

Generated by: LCOV version 1.14