Coverage Report

Created: 2025-08-11 09:23

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