Coverage Report

Created: 2025-11-15 08:43

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