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