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

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

Generated by: LCOV version 1.14