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