LCOV - code coverage report
Current view: top level - frmts/png - pngdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1083 1211 89.4 %
Date: 2024-05-02 22:57:13 Functions: 48 49 98.0 %

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

Generated by: LCOV version 1.14