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 26412 : bool PNGDataset::IsCompatibleOfSingleBlock() const
78 : {
79 26212 : return nBitDepth == 8 && !bInterlaced && nRasterXSize <= 512 &&
80 25867 : nRasterYSize <= 512 &&
81 25865 : CPLTestBool(
82 52624 : CPLGetConfigOption("GDAL_PNG_WHOLE_IMAGE_OPTIM", "YES")) &&
83 52188 : CPLTestBool(CPLGetConfigOption("GDAL_PNG_SINGLE_BLOCK", "YES"));
84 : }
85 : #endif
86 :
87 : /************************************************************************/
88 : /* PNGRasterBand() */
89 : /************************************************************************/
90 :
91 26412 : PNGRasterBand::PNGRasterBand(PNGDataset *poDSIn, int nBandIn)
92 26412 : : bHaveNoData(FALSE), dfNoDataValue(-1)
93 : {
94 26412 : poDS = poDSIn;
95 26412 : nBand = nBandIn;
96 :
97 26412 : if (poDSIn->nBitDepth == 16)
98 182 : eDataType = GDT_UInt16;
99 : else
100 26230 : eDataType = GDT_UInt8;
101 :
102 26412 : nBlockXSize = poDSIn->nRasterXSize;
103 : #ifdef ENABLE_WHOLE_IMAGE_OPTIMIZATION
104 26412 : if (poDSIn->IsCompatibleOfSingleBlock())
105 : {
106 25687 : nBlockYSize = poDSIn->nRasterYSize;
107 : }
108 : else
109 : #endif
110 : {
111 725 : nBlockYSize = 1;
112 : }
113 26412 : }
114 :
115 : /************************************************************************/
116 : /* IReadBlock() */
117 : /************************************************************************/
118 :
119 13186 : CPLErr PNGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
120 :
121 : {
122 : #ifdef ENABLE_WHOLE_IMAGE_OPTIMIZATION
123 13186 : if (nBlockYSize > 1)
124 : {
125 : GDALRasterIOExtraArg sExtraArg;
126 193 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
127 193 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
128 386 : return IRasterIO(GF_Read, 0, 0, nRasterXSize, nRasterYSize, pImage,
129 : nRasterXSize, nRasterYSize, eDataType, nDTSize,
130 193 : static_cast<GSpacing>(nDTSize) * nRasterXSize,
131 193 : &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 472 : GDALColorInterp PNGRasterBand::GetColorInterpretation()
221 :
222 : {
223 472 : PNGDataset *poGDS = cpl::down_cast<PNGDataset *>(poDS);
224 :
225 472 : if (poGDS->nColorType == PNG_COLOR_TYPE_GRAY)
226 27 : return GCI_GrayIndex;
227 :
228 445 : else if (poGDS->nColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
229 : {
230 51 : if (nBand == 1)
231 17 : return GCI_GrayIndex;
232 : else
233 34 : return GCI_AlphaBand;
234 : }
235 :
236 394 : else if (poGDS->nColorType == PNG_COLOR_TYPE_PALETTE)
237 47 : return GCI_PaletteIndex;
238 :
239 347 : else if (poGDS->nColorType == PNG_COLOR_TYPE_RGB ||
240 275 : poGDS->nColorType == PNG_COLOR_TYPE_RGB_ALPHA)
241 : {
242 347 : if (nBand == 1)
243 68 : return GCI_RedBand;
244 279 : else if (nBand == 2)
245 72 : return GCI_GreenBand;
246 207 : else if (nBand == 3)
247 63 : return GCI_BlueBand;
248 : else
249 144 : return GCI_AlphaBand;
250 : }
251 : else
252 0 : return GCI_GrayIndex;
253 : }
254 :
255 : /************************************************************************/
256 : /* GetColorTable() */
257 : /************************************************************************/
258 :
259 621 : GDALColorTable *PNGRasterBand::GetColorTable()
260 :
261 : {
262 621 : PNGDataset *poGDS = cpl::down_cast<PNGDataset *>(poDS);
263 :
264 621 : if (nBand == 1)
265 593 : return poGDS->poColorTable.get();
266 :
267 28 : 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 248 : double PNGRasterBand::GetNoDataValue(int *pbSuccess)
288 :
289 : {
290 248 : if (bHaveNoData)
291 : {
292 36 : if (pbSuccess != nullptr)
293 32 : *pbSuccess = bHaveNoData;
294 36 : return dfNoDataValue;
295 : }
296 :
297 212 : return GDALPamRasterBand::GetNoDataValue(pbSuccess);
298 : }
299 :
300 : /************************************************************************/
301 : /* ==================================================================== */
302 : /* PNGDataset */
303 : /* ==================================================================== */
304 : /************************************************************************/
305 :
306 : /************************************************************************/
307 : /* PNGDataset() */
308 : /************************************************************************/
309 :
310 10056 : PNGDataset::PNGDataset()
311 : {
312 10056 : memset(&sSetJmpContext, 0, sizeof(sSetJmpContext));
313 10056 : }
314 :
315 : /************************************************************************/
316 : /* ~PNGDataset() */
317 : /************************************************************************/
318 :
319 20112 : PNGDataset::~PNGDataset()
320 :
321 : {
322 10056 : PNGDataset::Close();
323 :
324 10056 : if (hPNG != nullptr)
325 9854 : png_destroy_read_struct(&hPNG, &psPNGInfo, nullptr);
326 20112 : }
327 :
328 : /************************************************************************/
329 : /* Close() */
330 : /************************************************************************/
331 :
332 14944 : CPLErr PNGDataset::Close(GDALProgressFunc, void *)
333 : {
334 14944 : CPLErr eErr = CE_None;
335 :
336 14944 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
337 : {
338 10056 : eErr = PNGDataset::FlushCache(true);
339 :
340 10056 : if (fpImage != nullptr && VSIFCloseL(fpImage) != 0)
341 0 : eErr = CE_Failure;
342 10056 : fpImage = nullptr;
343 :
344 10056 : eErr = GDAL::Combine(eErr, GDALPamDataset::Close());
345 : }
346 14944 : 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 4208 : CPLErr PNGDataset::LoadWholeImage(void *pSingleBuffer, GSpacing nPixelSpace,
381 : GSpacing nLineSpace, GSpacing nBandSpace,
382 : void *apabyBuffers[4])
383 : {
384 4208 : 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 4207 : const bool bCanUseDeinterleave =
415 5841 : (nBands == 3 || nBands == 4) &&
416 1634 : (apabyBuffers != nullptr ||
417 1470 : (nPixelSpace == 1 &&
418 1470 : 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 4207 : char szChunkName[5] = {0};
427 4207 : bool bError = false;
428 :
429 : // We try to read the zlib compressed data into pData, if there is
430 : // enough room for that
431 4207 : size_t nDataSize = 0;
432 8414 : std::vector<GByte> abyCompressedData; // keep in this scope
433 4207 : GByte *pabyCompressedData = static_cast<GByte *>(pSingleBuffer);
434 4207 : size_t nCompressedDataSize = 0;
435 4207 : if (pSingleBuffer)
436 : {
437 2675 : if (nPixelSpace == nBands && nLineSpace == nPixelSpace * nRasterXSize &&
438 418 : (nBands == 1 || nBandSpace == 1))
439 : {
440 418 : nDataSize =
441 418 : static_cast<size_t>(nRasterXSize) * nRasterYSize * nBands;
442 : }
443 2257 : else if (nPixelSpace == 1 && nLineSpace == nRasterXSize &&
444 : nBandSpace ==
445 1986 : static_cast<GSpacing>(nRasterXSize) * nRasterYSize)
446 : {
447 1986 : nDataSize =
448 1986 : static_cast<size_t>(nRasterXSize) * nRasterYSize * nBands;
449 : }
450 : }
451 :
452 4207 : const auto nPosBefore = VSIFTellL(fpImage);
453 4207 : bError = VSIFSeekL(fpImage, 8, SEEK_SET) != 0;
454 : // Iterate over PNG chunks
455 16213 : while (!bError)
456 : {
457 : uint32_t nChunkSize;
458 16213 : if (VSIFReadL(&nChunkSize, sizeof(nChunkSize), 1, fpImage) == 0)
459 : {
460 0 : bError = true;
461 4207 : break;
462 : }
463 16213 : CPL_MSBPTR32(&nChunkSize);
464 16213 : if (VSIFReadL(szChunkName, 4, 1, fpImage) == 0)
465 : {
466 0 : bError = true;
467 0 : break;
468 : }
469 16213 : 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 13560 : if (nChunkSize >
476 6780 : 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 6780 : 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 6780 : if (nCompressedDataSize + nChunkSize > nDataSize)
510 : {
511 4007 : 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 4007 : abyCompressedData.resize(nCompressedDataSize + nChunkSize);
517 4007 : 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 4007 : if (bVectorEmptyBefore && pSingleBuffer &&
527 : nCompressedDataSize > 0)
528 : {
529 0 : memcpy(pabyCompressedData, pSingleBuffer,
530 : nCompressedDataSize);
531 : }
532 : }
533 6780 : VSIFReadL(pabyCompressedData + nCompressedDataSize, nChunkSize, 1,
534 : fpImage);
535 6780 : nCompressedDataSize += nChunkSize;
536 : }
537 9433 : else if (strcmp(szChunkName, "IEND") == 0)
538 4207 : break;
539 : else
540 : {
541 : // CPLDebug("PNG", "Skipping chunk %s of size %u", szChunkName,
542 : // nChunkSize);
543 5226 : bError = VSIFSeekL(fpImage, static_cast<vsi_l_offset>(nChunkSize),
544 : SEEK_CUR) != 0;
545 : }
546 12006 : bError = bError || VSIFSeekL(fpImage, 4, SEEK_CUR) != 0; // CRC
547 : }
548 4207 : bError = VSIFSeekL(fpImage, nPosBefore, SEEK_SET) != 0 || bError;
549 4207 : if (bError)
550 0 : return CE_Failure;
551 :
552 4207 : const int nSamplesPerLine = nRasterXSize * nBands;
553 : size_t nOutBytes;
554 4207 : constexpr int FILTER_TYPE_BYTE = 1;
555 4207 : const size_t nZlibDecompressedSize = static_cast<size_t>(nRasterYSize) *
556 4207 : (FILTER_TYPE_BYTE + nSamplesPerLine);
557 : GByte *pabyZlibDecompressed =
558 4207 : static_cast<GByte *>(VSI_MALLOC_VERBOSE(nZlibDecompressedSize));
559 4207 : if (pabyZlibDecompressed == nullptr)
560 : {
561 0 : return CE_Failure;
562 : }
563 :
564 4207 : if (CPLZLibInflate(pabyCompressedData, nCompressedDataSize,
565 : pabyZlibDecompressed, nZlibDecompressedSize,
566 4207 : &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 8414 : std::vector<GByte> abyTemp;
575 8414 : std::vector<GByte> abyLineUp;
576 :
577 4207 : if (pSingleBuffer != nullptr && nPixelSpace == nBands &&
578 428 : nLineSpace == nPixelSpace * nRasterXSize &&
579 418 : (nBands == 1 || nBandSpace == 1))
580 : {
581 418 : pabyOutputBuffer = static_cast<GByte *>(pSingleBuffer);
582 : }
583 : else
584 : {
585 3789 : abyTemp.resize(nSamplesPerLine);
586 3789 : pabyOutputBuffer = abyTemp.data();
587 : }
588 :
589 633546 : 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 629339 : const GByte *CPL_RESTRICT pabyInputLine =
594 : pabyZlibDecompressed +
595 629339 : static_cast<size_t>(iY) * (FILTER_TYPE_BYTE + nSamplesPerLine);
596 629339 : const GByte nFilterType = pabyInputLine[0];
597 629339 : pabyInputLine++;
598 : GByte *const CPL_RESTRICT pabyOutputLine =
599 629339 : abyTemp.empty()
600 629339 : ? pabyOutputBuffer + static_cast<size_t>(iY) * nSamplesPerLine
601 541158 : : abyTemp.data();
602 629339 : if (nFilterType == 0)
603 : {
604 : // Filter type 0: None
605 133137 : memcpy(pabyOutputLine, pabyInputLine, nSamplesPerLine);
606 : }
607 496202 : else if (nFilterType == 1)
608 : {
609 : // Filter type 1: Sub (horizontal differencing)
610 : #ifdef HAVE_SSE2
611 13899 : if (nBands == 3)
612 : {
613 : png_row_info row_info;
614 5581 : memset(&row_info, 0, sizeof(row_info));
615 5581 : row_info.rowbytes = nSamplesPerLine;
616 :
617 5581 : gdal_png_read_filter_row_sub3_sse2(&row_info, pabyInputLine,
618 : pabyOutputLine);
619 : }
620 8318 : else if (nBands == 4)
621 : {
622 : png_row_info row_info;
623 6689 : memset(&row_info, 0, sizeof(row_info));
624 6689 : row_info.rowbytes = nSamplesPerLine;
625 :
626 6689 : gdal_png_read_filter_row_sub4_sse2(&row_info, pabyInputLine,
627 : pabyOutputLine);
628 : }
629 : else
630 : #endif
631 : {
632 : int iX;
633 4474 : for (iX = 0; iX < nBands; ++iX)
634 2845 : 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 215189 : for (; iX < nSamplesPerLine; ++iX)
699 213560 : pabyOutputLine[iX] = static_cast<GByte>(
700 213560 : pabyInputLine[iX] + pabyOutputLine[iX - nBands]);
701 : }
702 : }
703 482303 : else if (nFilterType == 2)
704 : {
705 : // Filter type 2: Up (vertical differencing)
706 82542 : if (iY == 0)
707 : {
708 0 : memcpy(pabyOutputLine, pabyInputLine, nSamplesPerLine);
709 : }
710 : else
711 : {
712 82542 : if (abyTemp.empty())
713 : {
714 22083 : const GByte *CPL_RESTRICT pabyOutputLineUp =
715 : pabyOutputBuffer +
716 22083 : (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 198407 : for (iX = 0; iX + 31 < nSamplesPerLine; iX += 32)
724 : {
725 : auto in =
726 176324 : _mm_loadu_si128(reinterpret_cast<const __m128i *>(
727 176324 : pabyInputLine + iX));
728 : auto in2 =
729 176324 : _mm_loadu_si128(reinterpret_cast<const __m128i *>(
730 176324 : pabyInputLine + iX + 16));
731 : auto up =
732 176324 : _mm_loadu_si128(reinterpret_cast<const __m128i *>(
733 176324 : pabyOutputLineUp + iX));
734 : auto up2 =
735 176324 : _mm_loadu_si128(reinterpret_cast<const __m128i *>(
736 176324 : pabyOutputLineUp + iX + 16));
737 176324 : in = _mm_add_epi8(in, up);
738 176324 : in2 = _mm_add_epi8(in2, up2);
739 176324 : _mm_storeu_si128(
740 176324 : reinterpret_cast<__m128i *>(pabyOutputLine + iX),
741 : in);
742 176324 : _mm_storeu_si128(reinterpret_cast<__m128i *>(
743 176324 : pabyOutputLine + iX + 16),
744 : in2);
745 : }
746 : #endif
747 27137 : 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 1367960 : for (iX = 0; iX + 31 < nSamplesPerLine; iX += 32)
760 : {
761 : auto in =
762 1307500 : _mm_loadu_si128(reinterpret_cast<const __m128i *>(
763 1307500 : pabyInputLine + iX));
764 : auto in2 =
765 1307500 : _mm_loadu_si128(reinterpret_cast<const __m128i *>(
766 1307500 : pabyInputLine + iX + 16));
767 : auto out =
768 2615010 : _mm_loadu_si128(reinterpret_cast<const __m128i *>(
769 1307500 : pabyOutputLine + iX));
770 : auto out2 =
771 1307500 : _mm_loadu_si128(reinterpret_cast<const __m128i *>(
772 1307500 : pabyOutputLine + iX + 16));
773 1307500 : out = _mm_add_epi8(out, in);
774 1307500 : out2 = _mm_add_epi8(out2, in2);
775 1307500 : _mm_storeu_si128(
776 1307500 : reinterpret_cast<__m128i *>(pabyOutputLine + iX),
777 : out);
778 1307500 : _mm_storeu_si128(reinterpret_cast<__m128i *>(
779 1307500 : pabyOutputLine + iX + 16),
780 : out2);
781 : }
782 : #endif
783 229355 : for (; iX < nSamplesPerLine; ++iX)
784 168896 : pabyOutputLine[iX] = static_cast<GByte>(
785 168896 : pabyOutputLine[iX] + pabyInputLine[iX]);
786 : #endif
787 : }
788 : }
789 : }
790 399761 : else if (nFilterType == 3)
791 : {
792 : // Filter type 3: Average
793 90028 : 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 89976 : if (nBands == 3)
809 : {
810 : png_row_info row_info;
811 66514 : memset(&row_info, 0, sizeof(row_info));
812 66514 : row_info.rowbytes = nSamplesPerLine;
813 66514 : if (!abyTemp.empty())
814 66167 : abyLineUp = abyTemp;
815 : const GByte *const pabyOutputLineUp =
816 66514 : abyTemp.empty()
817 66514 : ? pabyOutputBuffer + (static_cast<size_t>(iY) - 1) *
818 347 : nSamplesPerLine
819 66167 : : abyLineUp.data();
820 :
821 66514 : gdal_png_read_filter_row_avg3_sse2(&row_info, pabyInputLine,
822 : pabyOutputLine,
823 : pabyOutputLineUp);
824 : }
825 23462 : else if (nBands == 4)
826 : {
827 : png_row_info row_info;
828 16198 : memset(&row_info, 0, sizeof(row_info));
829 16198 : row_info.rowbytes = nSamplesPerLine;
830 16198 : if (!abyTemp.empty())
831 16198 : abyLineUp = abyTemp;
832 : const GByte *const pabyOutputLineUp =
833 16198 : abyTemp.empty()
834 16198 : ? pabyOutputBuffer + (static_cast<size_t>(iY) - 1) *
835 0 : nSamplesPerLine
836 16198 : : abyLineUp.data();
837 :
838 16198 : gdal_png_read_filter_row_avg4_sse2(&row_info, pabyInputLine,
839 : pabyOutputLine,
840 : pabyOutputLineUp);
841 : }
842 : else
843 : #endif
844 7264 : if (abyTemp.empty())
845 : {
846 2896 : const GByte *CPL_RESTRICT pabyOutputLineUp =
847 : pabyOutputBuffer +
848 2896 : (static_cast<size_t>(iY) - 1) * nSamplesPerLine;
849 5792 : for (int iX = 0; iX < nBands; ++iX)
850 : {
851 2896 : pabyOutputLine[iX] = static_cast<GByte>(
852 2896 : pabyInputLine[iX] + pabyOutputLineUp[iX] / 2);
853 : }
854 720152 : for (int iX = nBands; iX < nSamplesPerLine; ++iX)
855 : {
856 717256 : pabyOutputLine[iX] = static_cast<GByte>(
857 717256 : pabyInputLine[iX] + (pabyOutputLine[iX - nBands] +
858 717256 : pabyOutputLineUp[iX]) /
859 : 2);
860 : }
861 : }
862 : else
863 : {
864 13101 : for (int iX = 0; iX < nBands; ++iX)
865 : {
866 8733 : pabyOutputLine[iX] = static_cast<GByte>(
867 8733 : pabyInputLine[iX] + pabyOutputLine[iX] / 2);
868 : }
869 2248310 : for (int iX = nBands; iX < nSamplesPerLine; ++iX)
870 : {
871 2243940 : pabyOutputLine[iX] = static_cast<GByte>(
872 2243940 : pabyInputLine[iX] +
873 2243940 : (pabyOutputLine[iX - nBands] + pabyOutputLine[iX]) /
874 : 2);
875 : }
876 : }
877 : }
878 : }
879 309733 : else if (nFilterType == 4)
880 : {
881 : // Filter type 4: Paeth
882 309733 : 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 309577 : if (!abyTemp.empty())
894 267448 : abyLineUp = abyTemp;
895 : const GByte *const pabyOutputLineUp =
896 309577 : abyTemp.empty()
897 309577 : ? pabyOutputBuffer +
898 42129 : (static_cast<size_t>(iY) - 1) * nSamplesPerLine
899 267448 : : abyLineUp.data();
900 : #ifdef HAVE_SSE2
901 309577 : if (nBands == 3)
902 : {
903 : png_row_info row_info;
904 244312 : memset(&row_info, 0, sizeof(row_info));
905 244312 : row_info.rowbytes = nSamplesPerLine;
906 244312 : gdal_png_read_filter_row_paeth3_sse2(
907 : &row_info, pabyInputLine, pabyOutputLine,
908 : pabyOutputLineUp);
909 : }
910 65265 : else if (nBands == 4)
911 : {
912 : png_row_info row_info;
913 54759 : memset(&row_info, 0, sizeof(row_info));
914 54759 : row_info.rowbytes = nSamplesPerLine;
915 54759 : gdal_png_read_filter_row_paeth4_sse2(
916 : &row_info, pabyInputLine, pabyOutputLine,
917 : pabyOutputLineUp);
918 : }
919 : else
920 : #endif
921 : {
922 10506 : int iX = 0;
923 28241 : for (; iX < nBands; ++iX)
924 : {
925 17735 : GByte b = pabyOutputLineUp[iX];
926 17735 : pabyOutputLine[iX] =
927 17735 : static_cast<GByte>(pabyInputLine[iX] + b);
928 : }
929 3310100 : for (; iX < nSamplesPerLine; ++iX)
930 : {
931 3299590 : GByte a = pabyOutputLine[iX - nBands];
932 3299590 : GByte b = pabyOutputLineUp[iX];
933 3299590 : GByte c = pabyOutputLineUp[iX - nBands];
934 3299590 : int p_minus_a = b - c;
935 3299590 : int p_minus_b = a - c;
936 3299590 : int p_minus_c = p_minus_a + p_minus_b;
937 3299590 : int pa = std::abs(p_minus_a);
938 3299590 : int pb = std::abs(p_minus_b);
939 3299590 : int pc = std::abs(p_minus_c);
940 3299590 : if (pa <= pb && pa <= pc)
941 3124300 : pabyOutputLine[iX] =
942 3124300 : static_cast<GByte>(pabyInputLine[iX] + a);
943 175292 : else if (pb <= pc)
944 127641 : pabyOutputLine[iX] =
945 127641 : static_cast<GByte>(pabyInputLine[iX] + b);
946 : else
947 47651 : pabyOutputLine[iX] =
948 47651 : 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 629339 : if (!abyTemp.empty())
962 : {
963 541158 : if (pSingleBuffer)
964 : {
965 158385 : GByte *pabyDest =
966 158385 : static_cast<GByte *>(pSingleBuffer) + iY * nLineSpace;
967 158385 : if (bCanUseDeinterleave)
968 : {
969 : // Cache friendly way for typical band interleaved case.
970 : void *apDestBuffers[4];
971 50639 : apDestBuffers[0] = pabyDest;
972 50639 : apDestBuffers[1] = pabyDest + nBandSpace;
973 50639 : apDestBuffers[2] = pabyDest + 2 * nBandSpace;
974 50639 : apDestBuffers[3] = pabyDest + 3 * nBandSpace;
975 50639 : GDALDeinterleave(pabyOutputLine, GDT_UInt8, nBands,
976 50639 : apDestBuffers, GDT_UInt8, nRasterXSize);
977 : }
978 107746 : else if (nPixelSpace <= nBands && nBandSpace > nBands)
979 : {
980 : // Cache friendly way for typical band interleaved case.
981 106758 : for (int iBand = 0; iBand < nBands; iBand++)
982 : {
983 71172 : GByte *pabyDest2 = pabyDest + iBand * nBandSpace;
984 71172 : const GByte *pabyScanline2 = pabyOutputLine + iBand;
985 71172 : GDALCopyWords(pabyScanline2, GDT_UInt8, nBands,
986 : pabyDest2, GDT_UInt8,
987 : static_cast<int>(nPixelSpace),
988 : nRasterXSize);
989 35586 : }
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 1574510 : for (int iBand = 0; iBand < nBands; iBand++)
1008 : {
1009 1191740 : apabyDestBuffers[iBand] =
1010 1191740 : static_cast<GByte *>(apabyBuffers[iBand]) +
1011 1191740 : iY * nRasterXSize;
1012 : }
1013 382773 : if (bCanUseDeinterleave)
1014 : {
1015 : // Cache friendly way for typical band interleaved case.
1016 370925 : GDALDeinterleave(
1017 : pabyOutputLine, GDT_UInt8, nBands,
1018 : reinterpret_cast<void **>(apabyDestBuffers), GDT_UInt8,
1019 370925 : nRasterXSize);
1020 : }
1021 : else
1022 : {
1023 : // Generic method
1024 3092540 : for (int x = 0; x < nRasterXSize; ++x)
1025 : {
1026 9242090 : for (int iBand = 0; iBand < nBands; iBand++)
1027 : {
1028 6161390 : apabyDestBuffers[iBand][x] =
1029 6161390 : pabyOutputLine[x * nBands + iBand];
1030 : }
1031 : }
1032 : }
1033 : }
1034 : }
1035 : }
1036 :
1037 4207 : CPLFree(pabyZlibDecompressed);
1038 :
1039 4207 : return CE_None;
1040 : }
1041 :
1042 : #endif // ENABLE_WHOLE_IMAGE_OPTIMIZATION
1043 :
1044 : /************************************************************************/
1045 : /* IRasterIO() */
1046 : /************************************************************************/
1047 :
1048 4071 : 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 4071 : if (panBandMap == nullptr)
1059 : {
1060 0 : return CE_Failure;
1061 : }
1062 :
1063 4071 : if ((eRWFlag == GF_Read) && (nBandCount == nBands) && (nXOff == 0) &&
1064 2730 : (nYOff == 0) && (nXSize == nBufXSize) && (nXSize == nRasterXSize) &&
1065 2726 : (nYSize == nBufYSize) && (nYSize == nRasterYSize) &&
1066 2652 : (eBufType == GDT_UInt8) &&
1067 2652 : (eBufType == GetRasterBand(1)->GetRasterDataType()) &&
1068 8142 : (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 5272 : if (!bInterlaced && nBitDepth == 8 &&
1076 2620 : CPLTestBool(
1077 : CPLGetConfigOption("GDAL_PNG_WHOLE_IMAGE_OPTIM", "YES")))
1078 : {
1079 2612 : return LoadWholeImage(pData, nPixelSpace, nLineSpace, nBandSpace,
1080 2612 : nullptr);
1081 : }
1082 40 : 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 40 : 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 38 : const bool bCanUseDeinterleave =
1122 43 : (nBands == 3 || nBands == 4) && nPixelSpace == 1 &&
1123 : nBandSpace ==
1124 5 : static_cast<GSpacing>(nRasterXSize) * nRasterYSize;
1125 :
1126 7134 : for (int y = 0; y < nYSize; ++y)
1127 : {
1128 7096 : CPLErr tmpError = LoadScanline(y);
1129 7096 : if (tmpError != CE_None)
1130 0 : return tmpError;
1131 7096 : const GByte *pabyScanline =
1132 7096 : pabyBuffer + (y - nBufferStartLine) * nBands * nXSize;
1133 7096 : GByte *pabyDest =
1134 7096 : static_cast<GByte *>(pData) + y * nLineSpace;
1135 7096 : 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 6920 : 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 1703890 : for (int x = 0; x < nXSize; ++x)
1164 : {
1165 3393970 : for (int iBand = 0; iBand < nBands; iBand++)
1166 : {
1167 1696980 : pabyDest[(x * nPixelSpace) +
1168 1696980 : iBand * nBandSpace] =
1169 1696980 : pabyScanline[x * nBands + iBand];
1170 : }
1171 : }
1172 : }
1173 : }
1174 38 : return CE_None;
1175 : }
1176 : }
1177 :
1178 1419 : return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1179 : pData, nBufXSize, nBufYSize, eBufType,
1180 : nBandCount, panBandMap, nPixelSpace,
1181 1419 : nLineSpace, nBandSpace, psExtraArg);
1182 : }
1183 :
1184 : /************************************************************************/
1185 : /* IRasterIO() */
1186 : /************************************************************************/
1187 :
1188 8632 : 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 8632 : auto poGDS = cpl::down_cast<PNGDataset *>(poDS);
1198 8632 : if ((eRWFlag == GF_Read) && (nXOff == 0) && (nYOff == 0) &&
1199 4962 : (nXSize == nBufXSize) && (nXSize == nRasterXSize) &&
1200 4925 : (nYSize == nBufYSize) && (nYSize == nRasterYSize) &&
1201 4464 : (eBufType == GDT_UInt8) && (eBufType == eDataType))
1202 : {
1203 4464 : bool bBlockAlreadyLoaded = false;
1204 4464 : if (nBlockYSize > 1)
1205 : {
1206 4326 : auto poBlock = TryGetLockedBlockRef(0, 0);
1207 4326 : if (poBlock != nullptr)
1208 : {
1209 2932 : bBlockAlreadyLoaded = poBlock->GetDataRef() != pData;
1210 2932 : poBlock->DropLock();
1211 : }
1212 : }
1213 :
1214 4464 : if (bBlockAlreadyLoaded)
1215 : {
1216 : // will got to general case
1217 : }
1218 70 : else if (poGDS->nBands == 1 && !poGDS->bInterlaced &&
1219 1861 : poGDS->nBitDepth == 8 &&
1220 70 : CPLTestBool(
1221 : CPLGetConfigOption("GDAL_PNG_WHOLE_IMAGE_OPTIM", "YES")))
1222 : {
1223 64 : return poGDS->LoadWholeImage(pData, nPixelSpace, nLineSpace, 0,
1224 64 : nullptr);
1225 : }
1226 1657 : else if (nBlockYSize > 1)
1227 : {
1228 : void *apabyBuffers[4];
1229 1532 : GDALRasterBlock *apoBlocks[4] = {nullptr, nullptr, nullptr,
1230 : nullptr};
1231 1532 : CPLErr eErr = CE_None;
1232 1532 : bool bNeedToUseDefaultCase = true;
1233 6301 : for (int i = 0; i < poGDS->nBands; ++i)
1234 : {
1235 4769 : if (i + 1 == nBand && nPixelSpace == 1 &&
1236 1532 : nLineSpace == nRasterXSize)
1237 : {
1238 1530 : bNeedToUseDefaultCase = false;
1239 1530 : apabyBuffers[i] = pData;
1240 : }
1241 : else
1242 : {
1243 3239 : apoBlocks[i] =
1244 3239 : poGDS->GetRasterBand(i + 1)->GetLockedBlockRef(0, 0,
1245 3239 : TRUE);
1246 3239 : apabyBuffers[i] =
1247 3239 : apoBlocks[i] ? apoBlocks[i]->GetDataRef() : nullptr;
1248 3239 : if (apabyBuffers[i] == nullptr)
1249 0 : eErr = CE_Failure;
1250 : }
1251 : }
1252 1532 : if (eErr == CE_None)
1253 : {
1254 1532 : eErr = poGDS->LoadWholeImage(nullptr, 0, 0, 0, apabyBuffers);
1255 : }
1256 6301 : for (int i = 0; i < poGDS->nBands; ++i)
1257 : {
1258 4769 : if (apoBlocks[i])
1259 3239 : apoBlocks[i]->DropLock();
1260 : }
1261 1532 : if (eErr != CE_None || !bNeedToUseDefaultCase)
1262 1530 : return eErr;
1263 : }
1264 : }
1265 : #endif
1266 7038 : return GDALPamRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1267 : pData, nBufXSize, nBufYSize, eBufType,
1268 7038 : nPixelSpace, nLineSpace, psExtraArg);
1269 : }
1270 :
1271 : /************************************************************************/
1272 : /* GetGeoTransform() */
1273 : /************************************************************************/
1274 :
1275 157 : CPLErr PNGDataset::GetGeoTransform(GDALGeoTransform >) const
1276 :
1277 : {
1278 157 : const_cast<PNGDataset *>(this)->LoadWorldFile();
1279 :
1280 157 : if (bGeoTransformValid)
1281 : {
1282 3 : gt = m_gt;
1283 3 : return CE_None;
1284 : }
1285 :
1286 154 : 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 10099 : CPLErr PNGDataset::FlushCache(bool bAtClosing)
1297 :
1298 : {
1299 10099 : const CPLErr eErr = GDALPamDataset::FlushCache(bAtClosing);
1300 :
1301 10099 : if (pabyBuffer != nullptr)
1302 : {
1303 238 : CPLFree(pabyBuffer);
1304 238 : pabyBuffer = nullptr;
1305 238 : nBufferStartLine = 0;
1306 238 : nBufferLines = 0;
1307 : }
1308 10099 : 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 16 : void PNGDataset::Restart()
1333 :
1334 : {
1335 16 : if (!m_bHasRewind)
1336 : {
1337 8 : m_bHasRewind = true;
1338 8 : CPLDebug("PNG", "Restart decompression from top (emitted once)");
1339 : }
1340 :
1341 16 : png_destroy_read_struct(&hPNG, &psPNGInfo, nullptr);
1342 :
1343 16 : hPNG =
1344 16 : png_create_read_struct(PNG_LIBPNG_VER_STRING, this, nullptr, nullptr);
1345 :
1346 : #ifdef DISABLE_CRC_CHECK
1347 : PNGDatasetDisableCRCCheck(hPNG);
1348 : #endif
1349 :
1350 16 : png_set_error_fn(hPNG, &sSetJmpContext, png_gdal_error, png_gdal_warning);
1351 16 : if (setjmp(sSetJmpContext) != 0)
1352 0 : return;
1353 :
1354 16 : psPNGInfo = png_create_info_struct(hPNG);
1355 :
1356 16 : CPL_IGNORE_RET_VAL(VSIFSeekL(fpImage, 0, SEEK_SET));
1357 16 : png_set_read_fn(hPNG, fpImage, png_vsi_read_data);
1358 16 : png_read_info(hPNG, psPNGInfo);
1359 :
1360 16 : if (nBitDepth < 8)
1361 16 : png_set_packing(hPNG);
1362 :
1363 16 : 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 18239 : static bool safe_png_read_rows(png_structp hPNG, png_bytep row,
1483 : jmp_buf sSetJmpContext)
1484 : {
1485 18239 : if (setjmp(sSetJmpContext) != 0)
1486 2 : return false;
1487 18239 : png_read_rows(hPNG, &row, nullptr, 1);
1488 18237 : return true;
1489 : }
1490 :
1491 : /************************************************************************/
1492 : /* LoadScanline() */
1493 : /************************************************************************/
1494 :
1495 20389 : CPLErr PNGDataset::LoadScanline(int nLine)
1496 :
1497 : {
1498 20389 : CPLAssert(nLine >= 0 && nLine < GetRasterYSize());
1499 :
1500 20389 : if (nLine >= nBufferStartLine && nLine < nBufferStartLine + nBufferLines)
1501 2257 : return CE_None;
1502 :
1503 : const int nPixelOffset =
1504 18132 : (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 18132 : if (bInterlaced)
1509 13 : return LoadInterlacedChunk(nLine);
1510 :
1511 : // Ensure we have space allocated for one scanline.
1512 18119 : if (pabyBuffer == nullptr)
1513 225 : pabyBuffer = reinterpret_cast<GByte *>(
1514 225 : 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 18119 : if (nLine <= nLastLineRead)
1519 : {
1520 16 : Restart();
1521 : }
1522 :
1523 : // Read till we get the desired row.
1524 18119 : png_bytep row = pabyBuffer;
1525 18119 : const GUInt32 nErrorCounter = CPLGetErrorCounter();
1526 36356 : while (nLine > nLastLineRead)
1527 : {
1528 18239 : 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 18237 : nLastLineRead++;
1538 : }
1539 :
1540 18117 : nBufferStartLine = nLine;
1541 18117 : nBufferLines = 1;
1542 :
1543 : // Do swap on LSB machines. 16-bit PNG data is stored in MSB format.
1544 18117 : 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 18117 : 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 9853 : void PNGDataset::CollectMetadata()
1570 :
1571 : {
1572 9853 : if (nBitDepth < 8)
1573 : {
1574 36 : for (int iBand = 0; iBand < nBands; iBand++)
1575 : {
1576 36 : GetRasterBand(iBand + 1)->SetMetadataItem(
1577 36 : GDALMD_NBITS, CPLString().Printf("%d", nBitDepth),
1578 18 : GDAL_MDD_IMAGE_STRUCTURE);
1579 : }
1580 : }
1581 :
1582 : int nTextCount;
1583 : png_textp text_ptr;
1584 9853 : if (png_get_text(hPNG, psPNGInfo, &text_ptr, &nTextCount) == 0)
1585 9847 : 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 16 : void PNGDataset::CollectXMPMetadata()
1610 :
1611 : {
1612 16 : if (fpImage == nullptr || bHasReadXMPMetadata)
1613 0 : return;
1614 :
1615 : // Save current position to avoid disturbing PNG stream decoding.
1616 16 : const vsi_l_offset nCurOffset = VSIFTellL(fpImage);
1617 :
1618 16 : vsi_l_offset nOffset = 8;
1619 16 : bool bError = VSIFSeekL(fpImage, nOffset, SEEK_SET) != 0;
1620 :
1621 : // Loop over chunks.
1622 83 : while (!bError)
1623 : {
1624 : int nLength;
1625 :
1626 83 : if (VSIFReadL(&nLength, 4, 1, fpImage) != 1)
1627 16 : break;
1628 83 : nOffset += 4;
1629 83 : CPL_MSBPTR32(&nLength);
1630 83 : if (nLength <= 0)
1631 15 : break;
1632 :
1633 : char pszChunkType[5];
1634 68 : if (VSIFReadL(pszChunkType, 4, 1, fpImage) != 1)
1635 0 : break;
1636 68 : nOffset += 4;
1637 68 : pszChunkType[4] = 0;
1638 :
1639 68 : 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 67 : nOffset += nLength;
1677 67 : bError = VSIFSeekL(fpImage, nOffset, SEEK_SET) != 0;
1678 : }
1679 :
1680 67 : nOffset += 4;
1681 : int nCRC;
1682 67 : if (VSIFReadL(&nCRC, 4, 1, fpImage) != 1)
1683 0 : break;
1684 : }
1685 :
1686 16 : CPL_IGNORE_RET_VAL(VSIFSeekL(fpImage, nCurOffset, SEEK_SET));
1687 :
1688 16 : 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 124 : CSLConstList PNGDataset::GetMetadata(const char *pszDomain)
1800 : {
1801 124 : if (fpImage == nullptr)
1802 0 : return nullptr;
1803 124 : if (eAccess == GA_ReadOnly && !bHasReadXMPMetadata &&
1804 88 : pszDomain != nullptr && EQUAL(pszDomain, "xml:XMP"))
1805 16 : CollectXMPMetadata();
1806 124 : if (eAccess == GA_ReadOnly && !bHasReadICCMetadata &&
1807 64 : pszDomain != nullptr && EQUAL(pszDomain, "COLOR_PROFILE"))
1808 13 : LoadICCProfile();
1809 124 : return GDALPamDataset::GetMetadata(pszDomain);
1810 : }
1811 :
1812 : /************************************************************************/
1813 : /* GetMetadataItem() */
1814 : /************************************************************************/
1815 770 : const char *PNGDataset::GetMetadataItem(const char *pszName,
1816 : const char *pszDomain)
1817 : {
1818 770 : if (eAccess == GA_ReadOnly && !bHasReadICCMetadata &&
1819 246 : pszDomain != nullptr && EQUAL(pszDomain, "COLOR_PROFILE"))
1820 11 : LoadICCProfile();
1821 770 : return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
1822 : }
1823 :
1824 : /************************************************************************/
1825 : /* Open() */
1826 : /************************************************************************/
1827 :
1828 9855 : 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 9855 : if (!PNGDriverIdentify(poOpenInfo))
1834 1 : return nullptr;
1835 : #else
1836 : if (poOpenInfo->fpL == nullptr)
1837 : return nullptr;
1838 : #endif
1839 :
1840 9854 : if (poOpenInfo->eAccess == GA_Update)
1841 : {
1842 0 : ReportUpdateNotSupportedByDriver("PNG");
1843 0 : return nullptr;
1844 : }
1845 :
1846 : // Create a corresponding GDALDataset.
1847 9854 : PNGDataset *poDS = new PNGDataset();
1848 9854 : return OpenStage2(poOpenInfo, poDS);
1849 : }
1850 :
1851 9854 : GDALDataset *PNGDataset::OpenStage2(GDALOpenInfo *poOpenInfo, PNGDataset *&poDS)
1852 :
1853 : {
1854 9854 : poDS->fpImage = poOpenInfo->fpL;
1855 9854 : poOpenInfo->fpL = nullptr;
1856 9854 : poDS->eAccess = poOpenInfo->eAccess;
1857 :
1858 19708 : poDS->hPNG =
1859 9854 : png_create_read_struct(PNG_LIBPNG_VER_STRING, poDS, nullptr, nullptr);
1860 9854 : 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 9854 : poDS->psPNGInfo = png_create_info_struct(poDS->hPNG);
1876 :
1877 : // Set up error handling.
1878 9854 : png_set_error_fn(poDS->hPNG, &poDS->sSetJmpContext, png_gdal_error,
1879 : png_gdal_warning);
1880 :
1881 9854 : 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 9854 : png_set_read_fn(poDS->hPNG, poDS->fpImage, png_vsi_read_data);
1891 9854 : png_read_info(poDS->hPNG, poDS->psPNGInfo);
1892 :
1893 : // Capture some information from the file that is of interest.
1894 9853 : poDS->nRasterXSize =
1895 9853 : static_cast<int>(png_get_image_width(poDS->hPNG, poDS->psPNGInfo));
1896 9853 : poDS->nRasterYSize =
1897 9853 : static_cast<int>(png_get_image_height(poDS->hPNG, poDS->psPNGInfo));
1898 :
1899 9853 : poDS->nBands = png_get_channels(poDS->hPNG, poDS->psPNGInfo);
1900 9853 : poDS->nBitDepth = png_get_bit_depth(poDS->hPNG, poDS->psPNGInfo);
1901 9853 : poDS->bInterlaced = png_get_interlace_type(poDS->hPNG, poDS->psPNGInfo) !=
1902 : PNG_INTERLACE_NONE;
1903 :
1904 9853 : poDS->nColorType = png_get_color_type(poDS->hPNG, poDS->psPNGInfo);
1905 :
1906 9853 : 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 9853 : if (poDS->nBitDepth < 8)
1919 18 : png_set_packing(poDS->hPNG);
1920 :
1921 : // Create band information objects.
1922 35645 : for (int iBand = 0; iBand < poDS->nBands; iBand++)
1923 25792 : 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 9853 : if (poDS->nColorType == PNG_COLOR_TYPE_PALETTE)
1928 : {
1929 392 : png_color *pasPNGPalette = nullptr;
1930 392 : int nColorCount = 0;
1931 :
1932 392 : if (png_get_PLTE(poDS->hPNG, poDS->psPNGInfo, &pasPNGPalette,
1933 392 : &nColorCount) == 0)
1934 0 : nColorCount = 0;
1935 :
1936 392 : unsigned char *trans = nullptr;
1937 392 : png_color_16 *trans_values = nullptr;
1938 392 : int num_trans = 0;
1939 392 : png_get_tRNS(poDS->hPNG, poDS->psPNGInfo, &trans, &num_trans,
1940 : &trans_values);
1941 :
1942 392 : poDS->poColorTable = std::make_unique<GDALColorTable>();
1943 :
1944 : GDALColorEntry oEntry;
1945 392 : int nNoDataIndex = -1;
1946 25487 : for (int iColor = nColorCount - 1; iColor >= 0; iColor--)
1947 : {
1948 25095 : oEntry.c1 = pasPNGPalette[iColor].red;
1949 25095 : oEntry.c2 = pasPNGPalette[iColor].green;
1950 25095 : oEntry.c3 = pasPNGPalette[iColor].blue;
1951 :
1952 25095 : 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 21606 : oEntry.c4 = 255;
1965 :
1966 25095 : 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 392 : if (nNoDataIndex > -1)
1972 : {
1973 269 : poDS->GetRasterBand(1)->SetNoDataValue(nNoDataIndex);
1974 : }
1975 : }
1976 :
1977 : // Check for transparency values in greyscale images.
1978 9853 : if (poDS->nColorType == PNG_COLOR_TYPE_GRAY)
1979 : {
1980 1416 : png_color_16 *trans_values = nullptr;
1981 : unsigned char *trans;
1982 : int num_trans;
1983 :
1984 1416 : if (png_get_tRNS(poDS->hPNG, poDS->psPNGInfo, &trans, &num_trans,
1985 1421 : &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 9853 : if (poDS->nColorType == PNG_COLOR_TYPE_RGB)
1994 : {
1995 6418 : png_color_16 *trans_values = nullptr;
1996 : unsigned char *trans;
1997 : int num_trans;
1998 :
1999 6418 : if (png_get_tRNS(poDS->hPNG, poDS->psPNGInfo, &trans, &num_trans,
2000 6422 : &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 9853 : png_color_16 *backgroundColor = nullptr;
2016 9853 : if (png_get_bKGD(poDS->hPNG, poDS->psPNGInfo, &backgroundColor) ==
2017 9868 : 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 9853 : poDS->CollectMetadata();
2043 :
2044 : // More metadata.
2045 9853 : if (poDS->nBands > 1)
2046 : {
2047 8045 : poDS->SetMetadataItem(GDALMD_INTERLEAVE, "PIXEL",
2048 8045 : GDAL_MDD_IMAGE_STRUCTURE);
2049 : }
2050 :
2051 : // Initialize any PAM information.
2052 9853 : poDS->SetDescription(poOpenInfo->pszFilename);
2053 :
2054 : const char *pszPhysicalFileName =
2055 9853 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "PHYSICAL_FILENAME");
2056 9853 : if (pszPhysicalFileName)
2057 : {
2058 : // Code path used by JPGDatasetCommon::OpenRawThermalImage()
2059 6 : poDS->SetPhysicalFilename(pszPhysicalFileName);
2060 : const char *pszSubdatasetName =
2061 6 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "SUBDATASET_NAME");
2062 6 : if (pszSubdatasetName)
2063 : {
2064 6 : poDS->SetSubdatasetName(pszSubdatasetName);
2065 : }
2066 6 : poDS->TryLoadXML();
2067 :
2068 6 : poDS->oOvManager.Initialize(poDS, ":::VIRTUAL:::");
2069 : }
2070 : else
2071 : {
2072 9847 : poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
2073 :
2074 : // Open overviews.
2075 9847 : poDS->oOvManager.Initialize(poDS, poOpenInfo);
2076 : }
2077 :
2078 : // Used by JPEG FLIR
2079 9853 : poDS->m_bByteOrderIsLittleEndian = CPLTestBool(CSLFetchNameValueDef(
2080 9853 : poOpenInfo->papszOpenOptions, "BYTE_ORDER_LITTLE_ENDIAN", "NO"));
2081 :
2082 9853 : return poDS;
2083 : }
2084 :
2085 : /************************************************************************/
2086 : /* LoadWorldFile() */
2087 : /************************************************************************/
2088 :
2089 196 : void PNGDataset::LoadWorldFile()
2090 : {
2091 196 : if (bHasTriedLoadWorldFile)
2092 32 : return;
2093 164 : bHasTriedLoadWorldFile = TRUE;
2094 :
2095 164 : char *pszWldFilename = nullptr;
2096 164 : bGeoTransformValid =
2097 164 : GDALReadWorldFile2(GetDescription(), nullptr, m_gt,
2098 : oOvManager.GetSiblingFiles(), &pszWldFilename);
2099 :
2100 164 : if (!bGeoTransformValid)
2101 164 : bGeoTransformValid =
2102 164 : GDALReadWorldFile2(GetDescription(), ".wld", m_gt,
2103 : oOvManager.GetSiblingFiles(), &pszWldFilename);
2104 :
2105 164 : if (pszWldFilename)
2106 : {
2107 3 : osWldFilename = pszWldFilename;
2108 3 : CPLFree(pszWldFilename);
2109 : }
2110 : }
2111 :
2112 : /************************************************************************/
2113 : /* GetFileList() */
2114 : /************************************************************************/
2115 :
2116 39 : char **PNGDataset::GetFileList()
2117 :
2118 : {
2119 39 : char **papszFileList = GDALPamDataset::GetFileList();
2120 :
2121 39 : LoadWorldFile();
2122 :
2123 40 : if (!osWldFilename.empty() &&
2124 1 : CSLFindString(papszFileList, osWldFilename) == -1)
2125 : {
2126 1 : papszFileList = CSLAddString(papszFileList, osWldFilename);
2127 : }
2128 :
2129 39 : return papszFileList;
2130 : }
2131 :
2132 : /************************************************************************/
2133 : /* WriteMetadataAsText() */
2134 : /************************************************************************/
2135 :
2136 3 : static bool IsASCII(const char *pszStr)
2137 : {
2138 28 : for (int i = 0; pszStr[i] != '\0'; i++)
2139 : {
2140 25 : if (reinterpret_cast<GByte *>(const_cast<char *>(pszStr))[i] >= 128)
2141 0 : return false;
2142 : }
2143 3 : return true;
2144 : }
2145 :
2146 3 : static bool safe_png_set_text(jmp_buf sSetJmpContext, png_structp png_ptr,
2147 : png_infop info_ptr, png_const_textp text_ptr,
2148 : int num_text)
2149 : {
2150 3 : if (setjmp(sSetJmpContext) != 0)
2151 : {
2152 0 : return false;
2153 : }
2154 3 : png_set_text(png_ptr, info_ptr, text_ptr, num_text);
2155 3 : return true;
2156 : }
2157 :
2158 3 : void PNGDataset::WriteMetadataAsText(jmp_buf sSetJmpContext, png_structp hPNG,
2159 : png_infop psPNGInfo, const char *pszKey,
2160 : const char *pszValue)
2161 : {
2162 : png_text sText;
2163 3 : memset(&sText, 0, sizeof(png_text));
2164 3 : sText.compression = PNG_TEXT_COMPRESSION_NONE;
2165 3 : sText.key = const_cast<png_charp>(pszKey);
2166 3 : sText.text = const_cast<png_charp>(pszValue);
2167 :
2168 : // UTF-8 values should be written in iTXt, whereas TEXT should be LATIN-1.
2169 3 : if (!IsASCII(pszValue) && CPLIsUTF8(pszValue, -1))
2170 0 : sText.compression = PNG_ITXT_COMPRESSION_NONE;
2171 :
2172 3 : safe_png_set_text(sSetJmpContext, hPNG, psPNGInfo, &sText, 1);
2173 3 : }
2174 :
2175 4433 : static bool safe_png_set_IHDR(jmp_buf sSetJmpContext, png_structp png_ptr,
2176 : png_infop info_ptr, png_uint_32 width,
2177 : png_uint_32 height, int bit_depth, int color_type,
2178 : int interlace_type, int compression_type,
2179 : int filter_type)
2180 : {
2181 4433 : if (setjmp(sSetJmpContext) != 0)
2182 : {
2183 0 : return false;
2184 : }
2185 4433 : png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type,
2186 : interlace_type, compression_type, filter_type);
2187 4433 : return true;
2188 : }
2189 :
2190 4034 : static bool safe_png_set_compression_level(jmp_buf sSetJmpContext,
2191 : png_structp png_ptr, int level)
2192 : {
2193 4034 : if (setjmp(sSetJmpContext) != 0)
2194 : {
2195 0 : return false;
2196 : }
2197 4034 : png_set_compression_level(png_ptr, level);
2198 4034 : return true;
2199 : }
2200 :
2201 9 : static bool safe_png_set_tRNS(jmp_buf sSetJmpContext, png_structp png_ptr,
2202 : png_infop info_ptr, png_const_bytep trans,
2203 : int num_trans, png_color_16p trans_values)
2204 : {
2205 9 : if (setjmp(sSetJmpContext) != 0)
2206 : {
2207 0 : return false;
2208 : }
2209 9 : png_set_tRNS(png_ptr, info_ptr, trans, num_trans, trans_values);
2210 9 : return true;
2211 : }
2212 :
2213 6 : static bool safe_png_set_bKGD(jmp_buf sSetJmpContext, png_structp png_ptr,
2214 : png_infop info_ptr,
2215 : png_const_color_16p background)
2216 : {
2217 6 : if (setjmp(sSetJmpContext) != 0)
2218 : {
2219 0 : return false;
2220 : }
2221 6 : png_set_bKGD(png_ptr, info_ptr, background);
2222 6 : return true;
2223 : }
2224 :
2225 2 : static bool safe_png_set_iCCP(jmp_buf sSetJmpContext, png_structp png_ptr,
2226 : png_infop info_ptr, png_const_charp name,
2227 : int compression_type, png_const_bytep profile,
2228 : png_uint_32 proflen)
2229 : {
2230 2 : if (setjmp(sSetJmpContext) != 0)
2231 : {
2232 0 : return false;
2233 : }
2234 2 : png_set_iCCP(png_ptr, info_ptr, name, compression_type, profile, proflen);
2235 2 : return true;
2236 : }
2237 :
2238 36 : static bool safe_png_set_PLTE(jmp_buf sSetJmpContext, png_structp png_ptr,
2239 : png_infop info_ptr, png_const_colorp palette,
2240 : int num_palette)
2241 : {
2242 36 : if (setjmp(sSetJmpContext) != 0)
2243 : {
2244 0 : return false;
2245 : }
2246 36 : png_set_PLTE(png_ptr, info_ptr, palette, num_palette);
2247 36 : return true;
2248 : }
2249 :
2250 4431 : static bool safe_png_write_info(jmp_buf sSetJmpContext, png_structp png_ptr,
2251 : png_infop info_ptr)
2252 : {
2253 4431 : if (setjmp(sSetJmpContext) != 0)
2254 : {
2255 5 : return false;
2256 : }
2257 4431 : png_write_info(png_ptr, info_ptr);
2258 4426 : return true;
2259 : }
2260 :
2261 190856 : static bool safe_png_write_rows(jmp_buf sSetJmpContext, png_structp png_ptr,
2262 : png_bytepp row, png_uint_32 num_rows)
2263 : {
2264 190856 : if (setjmp(sSetJmpContext) != 0)
2265 : {
2266 4 : return false;
2267 : }
2268 190856 : png_write_rows(png_ptr, row, num_rows);
2269 190852 : return true;
2270 : }
2271 :
2272 4426 : static bool safe_png_write_end(jmp_buf sSetJmpContext, png_structp png_ptr,
2273 : png_infop info_ptr)
2274 : {
2275 4426 : if (setjmp(sSetJmpContext) != 0)
2276 : {
2277 7 : return false;
2278 : }
2279 4426 : png_write_end(png_ptr, info_ptr);
2280 4419 : return true;
2281 : }
2282 :
2283 : /************************************************************************/
2284 : /* CreateCopy() */
2285 : /************************************************************************/
2286 :
2287 4447 : GDALDataset *PNGDataset::CreateCopy(const char *pszFilename,
2288 : GDALDataset *poSrcDS, int bStrict,
2289 : CSLConstList papszOptions,
2290 : GDALProgressFunc pfnProgress,
2291 : void *pProgressData)
2292 :
2293 : {
2294 : // Perform some rudimentary checks.
2295 4447 : const int nBands = poSrcDS->GetRasterCount();
2296 4447 : if (nBands != 1 && nBands != 2 && nBands != 3 && nBands != 4)
2297 : {
2298 2 : CPLError(CE_Failure, CPLE_NotSupported,
2299 : "PNG driver doesn't support %d bands. Must be 1 (grey),\n"
2300 : "2 (grey+alpha), 3 (rgb) or 4 (rgba) bands.\n",
2301 : nBands);
2302 :
2303 2 : return nullptr;
2304 : }
2305 :
2306 4513 : if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt8 &&
2307 68 : poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16)
2308 : {
2309 9 : CPLError(
2310 : (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
2311 : "PNG driver doesn't support data type %s. "
2312 : "Only eight bit (Byte) and sixteen bit (UInt16) bands supported. "
2313 : "%s\n",
2314 : GDALGetDataTypeName(poSrcDS->GetRasterBand(1)->GetRasterDataType()),
2315 : (bStrict) ? "" : "Defaulting to Byte");
2316 :
2317 9 : if (bStrict)
2318 9 : return nullptr;
2319 : }
2320 :
2321 : // Create the dataset.
2322 : auto fpImage(
2323 4436 : CPLTestBool(CSLFetchNameValueDef(
2324 : papszOptions, "@CREATE_ONLY_VISIBLE_AT_CLOSE_TIME", "NO"))
2325 234 : ? VSIFileManager::GetHandler(pszFilename)
2326 234 : ->CreateOnlyVisibleAtCloseTime(pszFilename, true, nullptr)
2327 9106 : : VSIFilesystemHandler::OpenStatic(pszFilename, "wb"));
2328 4436 : if (fpImage == nullptr)
2329 : {
2330 3 : CPLError(CE_Failure, CPLE_OpenFailed,
2331 : "Unable to create png file %s: %s\n", pszFilename,
2332 3 : VSIStrerror(errno));
2333 3 : return nullptr;
2334 : }
2335 :
2336 : // Initialize PNG access to the file.
2337 : jmp_buf sSetJmpContext;
2338 :
2339 : png_structp hPNG =
2340 4433 : png_create_write_struct(PNG_LIBPNG_VER_STRING, &sSetJmpContext,
2341 4433 : png_gdal_error, png_gdal_warning);
2342 4433 : png_infop psPNGInfo = png_create_info_struct(hPNG);
2343 :
2344 : // Set up some parameters.
2345 4433 : int nColorType = 0;
2346 :
2347 4433 : if (nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() == nullptr)
2348 157 : nColorType = PNG_COLOR_TYPE_GRAY;
2349 4276 : else if (nBands == 1)
2350 36 : nColorType = PNG_COLOR_TYPE_PALETTE;
2351 4240 : else if (nBands == 2)
2352 264 : nColorType = PNG_COLOR_TYPE_GRAY_ALPHA;
2353 3976 : else if (nBands == 3)
2354 3764 : nColorType = PNG_COLOR_TYPE_RGB;
2355 212 : else if (nBands == 4)
2356 212 : nColorType = PNG_COLOR_TYPE_RGB_ALPHA;
2357 :
2358 : int nBitDepth;
2359 : GDALDataType eType;
2360 4433 : if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16)
2361 : {
2362 4374 : eType = GDT_UInt8;
2363 4374 : nBitDepth = 8;
2364 4374 : if (nBands == 1)
2365 : {
2366 138 : const char *pszNbits = poSrcDS->GetRasterBand(1)->GetMetadataItem(
2367 138 : GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE);
2368 138 : if (pszNbits != nullptr)
2369 : {
2370 3 : nBitDepth = atoi(pszNbits);
2371 3 : if (!(nBitDepth == 1 || nBitDepth == 2 || nBitDepth == 4))
2372 0 : nBitDepth = 8;
2373 : }
2374 : }
2375 : }
2376 : else
2377 : {
2378 59 : eType = GDT_UInt16;
2379 59 : nBitDepth = 16;
2380 : }
2381 :
2382 4433 : const char *pszNbits = CSLFetchNameValue(papszOptions, GDALMD_NBITS);
2383 4433 : if (eType == GDT_UInt8 && pszNbits != nullptr)
2384 : {
2385 13 : nBitDepth = atoi(pszNbits);
2386 13 : if (!(nBitDepth == 1 || nBitDepth == 2 || nBitDepth == 4 ||
2387 : nBitDepth == 8))
2388 : {
2389 1 : CPLError(CE_Warning, CPLE_NotSupported,
2390 : "Invalid bit depth. Using 8");
2391 1 : nBitDepth = 8;
2392 : }
2393 : }
2394 :
2395 4433 : png_set_write_fn(hPNG, fpImage.get(), png_vsi_write_data, png_vsi_flush);
2396 :
2397 4433 : const int nXSize = poSrcDS->GetRasterXSize();
2398 4433 : const int nYSize = poSrcDS->GetRasterYSize();
2399 :
2400 4433 : if (!safe_png_set_IHDR(sSetJmpContext, hPNG, psPNGInfo, nXSize, nYSize,
2401 : nBitDepth, nColorType, PNG_INTERLACE_NONE,
2402 : PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE))
2403 : {
2404 0 : fpImage->CancelCreation();
2405 0 : png_destroy_write_struct(&hPNG, &psPNGInfo);
2406 0 : return nullptr;
2407 : }
2408 :
2409 : // Do we want to control the compression level?
2410 4433 : const char *pszLevel = CSLFetchNameValue(papszOptions, "ZLEVEL");
2411 :
2412 4433 : if (pszLevel)
2413 : {
2414 4036 : const int nLevel = atoi(pszLevel);
2415 4036 : if (nLevel < 0 || nLevel > 9)
2416 : {
2417 2 : CPLError(CE_Failure, CPLE_AppDefined,
2418 : "Illegal ZLEVEL value '%s', should be 0-9.", pszLevel);
2419 2 : fpImage->CancelCreation();
2420 2 : png_destroy_write_struct(&hPNG, &psPNGInfo);
2421 2 : return nullptr;
2422 : }
2423 :
2424 4034 : if (!safe_png_set_compression_level(sSetJmpContext, hPNG, nLevel))
2425 : {
2426 0 : fpImage->CancelCreation();
2427 0 : png_destroy_write_struct(&hPNG, &psPNGInfo);
2428 0 : return nullptr;
2429 : }
2430 : }
2431 :
2432 : // Try to handle nodata values as a tRNS block (note that for paletted
2433 : // images, we save the effect to apply as part of palette).
2434 : png_color_16 sTRNSColor;
2435 :
2436 : // Gray nodata.
2437 4431 : if (nColorType == PNG_COLOR_TYPE_GRAY)
2438 : {
2439 155 : int bHaveNoData = FALSE;
2440 : const double dfNoDataValue =
2441 155 : poSrcDS->GetRasterBand(1)->GetNoDataValue(&bHaveNoData);
2442 :
2443 155 : if (bHaveNoData && dfNoDataValue >= 0 && dfNoDataValue < 65536)
2444 : {
2445 3 : sTRNSColor.gray = static_cast<png_uint_16>(dfNoDataValue);
2446 3 : if (!safe_png_set_tRNS(sSetJmpContext, hPNG, psPNGInfo, nullptr, 0,
2447 : &sTRNSColor))
2448 : {
2449 0 : fpImage->CancelCreation();
2450 0 : png_destroy_write_struct(&hPNG, &psPNGInfo);
2451 0 : return nullptr;
2452 : }
2453 : }
2454 : }
2455 :
2456 : // RGB nodata.
2457 4431 : if (nColorType == PNG_COLOR_TYPE_RGB)
2458 : {
2459 : // First try to use the NODATA_VALUES metadata item.
2460 3764 : if (poSrcDS->GetMetadataItem("NODATA_VALUES") != nullptr)
2461 : {
2462 : char **papszValues =
2463 1 : CSLTokenizeString(poSrcDS->GetMetadataItem("NODATA_VALUES"));
2464 :
2465 1 : if (CSLCount(papszValues) >= 3)
2466 : {
2467 1 : sTRNSColor.red = static_cast<png_uint_16>(atoi(papszValues[0]));
2468 1 : sTRNSColor.green =
2469 1 : static_cast<png_uint_16>(atoi(papszValues[1]));
2470 1 : sTRNSColor.blue =
2471 1 : static_cast<png_uint_16>(atoi(papszValues[2]));
2472 1 : if (!safe_png_set_tRNS(sSetJmpContext, hPNG, psPNGInfo, nullptr,
2473 : 0, &sTRNSColor))
2474 : {
2475 0 : fpImage->CancelCreation();
2476 0 : png_destroy_write_struct(&hPNG, &psPNGInfo);
2477 0 : CSLDestroy(papszValues);
2478 0 : return nullptr;
2479 : }
2480 : }
2481 :
2482 1 : CSLDestroy(papszValues);
2483 : }
2484 : // Otherwise, get the nodata value from the bands.
2485 : else
2486 : {
2487 3763 : int bHaveNoDataRed = FALSE;
2488 : const double dfNoDataValueRed =
2489 3763 : poSrcDS->GetRasterBand(1)->GetNoDataValue(&bHaveNoDataRed);
2490 :
2491 3763 : int bHaveNoDataGreen = FALSE;
2492 : const double dfNoDataValueGreen =
2493 3763 : poSrcDS->GetRasterBand(2)->GetNoDataValue(&bHaveNoDataGreen);
2494 :
2495 3763 : int bHaveNoDataBlue = FALSE;
2496 : const double dfNoDataValueBlue =
2497 3763 : poSrcDS->GetRasterBand(3)->GetNoDataValue(&bHaveNoDataBlue);
2498 :
2499 3763 : if ((bHaveNoDataRed && dfNoDataValueRed >= 0 &&
2500 0 : dfNoDataValueRed < 65536) &&
2501 0 : (bHaveNoDataGreen && dfNoDataValueGreen >= 0 &&
2502 0 : dfNoDataValueGreen < 65536) &&
2503 0 : (bHaveNoDataBlue && dfNoDataValueBlue >= 0 &&
2504 : dfNoDataValueBlue < 65536))
2505 : {
2506 0 : sTRNSColor.red = static_cast<png_uint_16>(dfNoDataValueRed);
2507 0 : sTRNSColor.green = static_cast<png_uint_16>(dfNoDataValueGreen);
2508 0 : sTRNSColor.blue = static_cast<png_uint_16>(dfNoDataValueBlue);
2509 0 : if (!safe_png_set_tRNS(sSetJmpContext, hPNG, psPNGInfo, nullptr,
2510 : 0, &sTRNSColor))
2511 : {
2512 0 : fpImage->CancelCreation();
2513 0 : png_destroy_write_struct(&hPNG, &psPNGInfo);
2514 0 : return nullptr;
2515 : }
2516 : }
2517 : }
2518 : }
2519 :
2520 4431 : if (const char *pszBackgroundColor =
2521 4431 : poSrcDS->GetMetadataItem("BACKGROUND_COLOR"))
2522 : {
2523 6 : bool ret_set_bKGD = true;
2524 6 : png_color_16 backgroundColor = {0, 0, 0, 0, 0};
2525 6 : if (nColorType == PNG_COLOR_TYPE_GRAY ||
2526 : nColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
2527 : {
2528 2 : backgroundColor.gray =
2529 2 : static_cast<png_uint_16>(atoi(pszBackgroundColor));
2530 2 : ret_set_bKGD = safe_png_set_bKGD(sSetJmpContext, hPNG, psPNGInfo,
2531 : &backgroundColor);
2532 : }
2533 4 : else if (nColorType == PNG_COLOR_TYPE_PALETTE)
2534 : {
2535 1 : backgroundColor.index =
2536 1 : static_cast<png_byte>(atoi(pszBackgroundColor));
2537 1 : ret_set_bKGD = safe_png_set_bKGD(sSetJmpContext, hPNG, psPNGInfo,
2538 : &backgroundColor);
2539 : }
2540 3 : else if (nColorType == PNG_COLOR_TYPE_RGB ||
2541 : nColorType == PNG_COLOR_TYPE_RGB_ALPHA)
2542 : {
2543 : const CPLStringList aosTokens(
2544 6 : CSLTokenizeString2(pszBackgroundColor, " ,", 3));
2545 3 : if (aosTokens.size() == 3)
2546 : {
2547 3 : backgroundColor.red =
2548 3 : static_cast<png_uint_16>(atoi(aosTokens[0]));
2549 3 : backgroundColor.green =
2550 3 : static_cast<png_uint_16>(atoi(aosTokens[1]));
2551 3 : backgroundColor.blue =
2552 3 : static_cast<png_uint_16>(atoi(aosTokens[2]));
2553 3 : ret_set_bKGD = safe_png_set_bKGD(sSetJmpContext, hPNG,
2554 : psPNGInfo, &backgroundColor);
2555 : }
2556 : }
2557 6 : if (!ret_set_bKGD)
2558 : {
2559 0 : fpImage->CancelCreation();
2560 0 : png_destroy_write_struct(&hPNG, &psPNGInfo);
2561 0 : return nullptr;
2562 : }
2563 : }
2564 :
2565 : // Copy color profile data.
2566 : const char *pszICCProfile =
2567 4431 : CSLFetchNameValue(papszOptions, "SOURCE_ICC_PROFILE");
2568 : const char *pszICCProfileName =
2569 4431 : CSLFetchNameValue(papszOptions, "SOURCE_ICC_PROFILE_NAME");
2570 4431 : if (pszICCProfileName == nullptr)
2571 4430 : pszICCProfileName = poSrcDS->GetMetadataItem("SOURCE_ICC_PROFILE_NAME",
2572 4430 : "COLOR_PROFILE");
2573 :
2574 4431 : if (pszICCProfile == nullptr)
2575 : pszICCProfile =
2576 4430 : poSrcDS->GetMetadataItem("SOURCE_ICC_PROFILE", "COLOR_PROFILE");
2577 :
2578 4431 : if ((pszICCProfileName != nullptr) && EQUAL(pszICCProfileName, "sRGB"))
2579 : {
2580 1 : pszICCProfile = nullptr;
2581 :
2582 : // assumes this can't fail ?
2583 1 : png_set_sRGB(hPNG, psPNGInfo, PNG_sRGB_INTENT_PERCEPTUAL);
2584 : }
2585 :
2586 4431 : if (pszICCProfile != nullptr)
2587 : {
2588 2 : char *pEmbedBuffer = CPLStrdup(pszICCProfile);
2589 : png_uint_32 nEmbedLen =
2590 2 : CPLBase64DecodeInPlace(reinterpret_cast<GByte *>(pEmbedBuffer));
2591 2 : const char *pszLocalICCProfileName =
2592 2 : (pszICCProfileName != nullptr) ? pszICCProfileName : "ICC Profile";
2593 :
2594 2 : if (!safe_png_set_iCCP(
2595 : sSetJmpContext, hPNG, psPNGInfo, pszLocalICCProfileName, 0,
2596 : reinterpret_cast<png_const_bytep>(pEmbedBuffer), nEmbedLen))
2597 : {
2598 0 : CPLFree(pEmbedBuffer);
2599 0 : fpImage->CancelCreation();
2600 0 : png_destroy_write_struct(&hPNG, &psPNGInfo);
2601 0 : return nullptr;
2602 : }
2603 :
2604 2 : CPLFree(pEmbedBuffer);
2605 : }
2606 4429 : else if ((pszICCProfileName == nullptr) ||
2607 1 : !EQUAL(pszICCProfileName, "sRGB"))
2608 : {
2609 : // Output gamma, primaries and whitepoint.
2610 4428 : const char *pszGamma = CSLFetchNameValue(papszOptions, "PNG_GAMMA");
2611 4428 : if (pszGamma == nullptr)
2612 4426 : pszGamma = poSrcDS->GetMetadataItem("PNG_GAMMA", "COLOR_PROFILE");
2613 :
2614 4428 : if (pszGamma != nullptr)
2615 : {
2616 4 : double dfGamma = CPLAtof(pszGamma);
2617 : // assumes this can't fail ?
2618 4 : png_set_gAMA(hPNG, psPNGInfo, dfGamma);
2619 : }
2620 :
2621 : const char *pszPrimariesRed =
2622 4428 : CSLFetchNameValue(papszOptions, "SOURCE_PRIMARIES_RED");
2623 4428 : if (pszPrimariesRed == nullptr)
2624 4427 : pszPrimariesRed = poSrcDS->GetMetadataItem("SOURCE_PRIMARIES_RED",
2625 4427 : "COLOR_PROFILE");
2626 : const char *pszPrimariesGreen =
2627 4428 : CSLFetchNameValue(papszOptions, "SOURCE_PRIMARIES_GREEN");
2628 4428 : if (pszPrimariesGreen == nullptr)
2629 4427 : pszPrimariesGreen = poSrcDS->GetMetadataItem(
2630 4427 : "SOURCE_PRIMARIES_GREEN", "COLOR_PROFILE");
2631 : const char *pszPrimariesBlue =
2632 4428 : CSLFetchNameValue(papszOptions, "SOURCE_PRIMARIES_BLUE");
2633 4428 : if (pszPrimariesBlue == nullptr)
2634 4427 : pszPrimariesBlue = poSrcDS->GetMetadataItem("SOURCE_PRIMARIES_BLUE",
2635 4427 : "COLOR_PROFILE");
2636 : const char *pszWhitepoint =
2637 4428 : CSLFetchNameValue(papszOptions, "SOURCE_WHITEPOINT");
2638 4428 : if (pszWhitepoint == nullptr)
2639 : pszWhitepoint =
2640 4427 : poSrcDS->GetMetadataItem("SOURCE_WHITEPOINT", "COLOR_PROFILE");
2641 :
2642 4428 : if ((pszPrimariesRed != nullptr) && (pszPrimariesGreen != nullptr) &&
2643 2 : (pszPrimariesBlue != nullptr) && (pszWhitepoint != nullptr))
2644 : {
2645 2 : bool bOk = true;
2646 2 : double faColour[8] = {0.0};
2647 2 : char **apapszTokenList[4] = {nullptr};
2648 :
2649 2 : apapszTokenList[0] = CSLTokenizeString2(pszWhitepoint, ",",
2650 : CSLT_ALLOWEMPTYTOKENS |
2651 : CSLT_STRIPLEADSPACES |
2652 : CSLT_STRIPENDSPACES);
2653 2 : apapszTokenList[1] = CSLTokenizeString2(pszPrimariesRed, ",",
2654 : CSLT_ALLOWEMPTYTOKENS |
2655 : CSLT_STRIPLEADSPACES |
2656 : CSLT_STRIPENDSPACES);
2657 2 : apapszTokenList[2] = CSLTokenizeString2(pszPrimariesGreen, ",",
2658 : CSLT_ALLOWEMPTYTOKENS |
2659 : CSLT_STRIPLEADSPACES |
2660 : CSLT_STRIPENDSPACES);
2661 2 : apapszTokenList[3] = CSLTokenizeString2(pszPrimariesBlue, ",",
2662 : CSLT_ALLOWEMPTYTOKENS |
2663 : CSLT_STRIPLEADSPACES |
2664 : CSLT_STRIPENDSPACES);
2665 :
2666 2 : if ((CSLCount(apapszTokenList[0]) == 3) &&
2667 2 : (CSLCount(apapszTokenList[1]) == 3) &&
2668 6 : (CSLCount(apapszTokenList[2]) == 3) &&
2669 2 : (CSLCount(apapszTokenList[3]) == 3))
2670 : {
2671 10 : for (int i = 0; i < 4; i++)
2672 : {
2673 32 : for (int j = 0; j < 3; j++)
2674 : {
2675 24 : const double v = CPLAtof(apapszTokenList[i][j]);
2676 :
2677 24 : if (j == 2)
2678 : {
2679 : /* Last term of xyY colour must be 1.0 */
2680 8 : if (v != 1.0)
2681 : {
2682 0 : bOk = false;
2683 0 : break;
2684 : }
2685 : }
2686 : else
2687 : {
2688 16 : faColour[i * 2 + j] = v;
2689 : }
2690 : }
2691 8 : if (!bOk)
2692 0 : break;
2693 : }
2694 :
2695 2 : if (bOk)
2696 : {
2697 : // assumes this can't fail ?
2698 2 : png_set_cHRM(hPNG, psPNGInfo, faColour[0], faColour[1],
2699 : faColour[2], faColour[3], faColour[4],
2700 : faColour[5], faColour[6], faColour[7]);
2701 : }
2702 : }
2703 :
2704 2 : CSLDestroy(apapszTokenList[0]);
2705 2 : CSLDestroy(apapszTokenList[1]);
2706 2 : CSLDestroy(apapszTokenList[2]);
2707 2 : CSLDestroy(apapszTokenList[3]);
2708 : }
2709 : }
2710 :
2711 : // Write the palette if there is one. Technically, it may be possible to
2712 : // write 16-bit palettes for PNG, but for now, this is omitted.
2713 4431 : if (nColorType == PNG_COLOR_TYPE_PALETTE)
2714 : {
2715 36 : int bHaveNoData = FALSE;
2716 : double dfNoDataValue =
2717 36 : poSrcDS->GetRasterBand(1)->GetNoDataValue(&bHaveNoData);
2718 :
2719 36 : GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
2720 :
2721 36 : int nEntryCount = poCT->GetColorEntryCount();
2722 36 : int nMaxEntryCount = 1 << nBitDepth;
2723 36 : if (nEntryCount > nMaxEntryCount)
2724 0 : nEntryCount = nMaxEntryCount;
2725 :
2726 : png_color *pasPNGColors = reinterpret_cast<png_color *>(
2727 36 : CPLMalloc(sizeof(png_color) * nEntryCount));
2728 :
2729 : GDALColorEntry sEntry;
2730 36 : bool bFoundTrans = false;
2731 7028 : for (int iColor = 0; iColor < nEntryCount; iColor++)
2732 : {
2733 6992 : poCT->GetColorEntryAsRGB(iColor, &sEntry);
2734 6992 : if (sEntry.c4 != 255)
2735 267 : bFoundTrans = true;
2736 :
2737 6992 : pasPNGColors[iColor].red = static_cast<png_byte>(sEntry.c1);
2738 6992 : pasPNGColors[iColor].green = static_cast<png_byte>(sEntry.c2);
2739 6992 : pasPNGColors[iColor].blue = static_cast<png_byte>(sEntry.c3);
2740 : }
2741 :
2742 36 : if (!safe_png_set_PLTE(sSetJmpContext, hPNG, psPNGInfo, pasPNGColors,
2743 : nEntryCount))
2744 : {
2745 0 : CPLFree(pasPNGColors);
2746 0 : fpImage->CancelCreation();
2747 0 : png_destroy_write_struct(&hPNG, &psPNGInfo);
2748 0 : return nullptr;
2749 : }
2750 :
2751 36 : CPLFree(pasPNGColors);
2752 :
2753 : // If we have transparent elements in the palette, we need to write a
2754 : // transparency block.
2755 36 : if (bFoundTrans || bHaveNoData)
2756 : {
2757 : unsigned char *pabyAlpha =
2758 5 : static_cast<unsigned char *>(CPLMalloc(nEntryCount));
2759 :
2760 588 : for (int iColor = 0; iColor < nEntryCount; iColor++)
2761 : {
2762 583 : poCT->GetColorEntryAsRGB(iColor, &sEntry);
2763 583 : pabyAlpha[iColor] = static_cast<unsigned char>(sEntry.c4);
2764 :
2765 583 : if (bHaveNoData && iColor == static_cast<int>(dfNoDataValue))
2766 3 : pabyAlpha[iColor] = 0;
2767 : }
2768 :
2769 5 : if (!safe_png_set_tRNS(sSetJmpContext, hPNG, psPNGInfo, pabyAlpha,
2770 : nEntryCount, nullptr))
2771 : {
2772 0 : CPLFree(pabyAlpha);
2773 0 : fpImage->CancelCreation();
2774 0 : png_destroy_write_struct(&hPNG, &psPNGInfo);
2775 0 : return nullptr;
2776 : }
2777 :
2778 5 : CPLFree(pabyAlpha);
2779 : }
2780 : }
2781 :
2782 : // Add text info.
2783 : // These are predefined keywords. See "4.2.7 tEXt Textual data" of
2784 : // http://www.w3.org/TR/PNG-Chunks.html for more information.
2785 4431 : const char *apszKeywords[] = {"Title", "Author", "Description",
2786 : "Copyright", "Creation Time", "Software",
2787 : "Disclaimer", "Warning", "Source",
2788 : "Comment", nullptr};
2789 4431 : const bool bWriteMetadataAsText = CPLTestBool(
2790 : CSLFetchNameValueDef(papszOptions, "WRITE_METADATA_AS_TEXT", "FALSE"));
2791 48741 : for (int i = 0; apszKeywords[i] != nullptr; i++)
2792 : {
2793 44310 : const char *pszKey = apszKeywords[i];
2794 44310 : const char *pszValue = CSLFetchNameValue(papszOptions, pszKey);
2795 44310 : if (pszValue == nullptr && bWriteMetadataAsText)
2796 9 : pszValue = poSrcDS->GetMetadataItem(pszKey);
2797 44310 : if (pszValue != nullptr)
2798 : {
2799 2 : WriteMetadataAsText(sSetJmpContext, hPNG, psPNGInfo, pszKey,
2800 : pszValue);
2801 : }
2802 : }
2803 4431 : if (bWriteMetadataAsText)
2804 : {
2805 1 : CSLConstList papszSrcMD = poSrcDS->GetMetadata();
2806 4 : for (; papszSrcMD && *papszSrcMD; papszSrcMD++)
2807 : {
2808 3 : char *pszKey = nullptr;
2809 3 : const char *pszValue = CPLParseNameValue(*papszSrcMD, &pszKey);
2810 3 : if (pszKey && pszValue)
2811 : {
2812 3 : if (CSLFindString(const_cast<char **>(apszKeywords), pszKey) <
2813 1 : 0 &&
2814 4 : !EQUAL(pszKey, "AREA_OR_POINT") &&
2815 1 : !EQUAL(pszKey, "NODATA_VALUES"))
2816 : {
2817 1 : WriteMetadataAsText(sSetJmpContext, hPNG, psPNGInfo, pszKey,
2818 : pszValue);
2819 : }
2820 3 : CPLFree(pszKey);
2821 : }
2822 : }
2823 : }
2824 :
2825 : // Write the PNG info.
2826 4431 : if (!safe_png_write_info(sSetJmpContext, hPNG, psPNGInfo))
2827 : {
2828 5 : fpImage->CancelCreation();
2829 5 : png_destroy_write_struct(&hPNG, &psPNGInfo);
2830 5 : return nullptr;
2831 : }
2832 :
2833 4426 : if (nBitDepth < 8)
2834 : {
2835 : // Assumes this can't fail
2836 6 : png_set_packing(hPNG);
2837 : }
2838 :
2839 : // Loop over the image, copying image data.
2840 4426 : CPLErr eErr = CE_None;
2841 4426 : const int nWordSize = GDALGetDataTypeSizeBytes(eType);
2842 :
2843 : GByte *pabyScanline = reinterpret_cast<GByte *>(
2844 4426 : CPLMalloc(cpl::fits_on<int>(nBands * nXSize * nWordSize)));
2845 :
2846 195283 : for (int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++)
2847 : {
2848 190857 : png_bytep row = pabyScanline;
2849 :
2850 381714 : eErr = poSrcDS->RasterIO(
2851 : GF_Read, 0, iLine, nXSize, 1, pabyScanline, nXSize, 1, eType,
2852 190857 : nBands, nullptr, static_cast<GSpacing>(nBands) * nWordSize,
2853 190857 : static_cast<GSpacing>(nBands) * nXSize * nWordSize, nWordSize,
2854 : nullptr);
2855 :
2856 : #ifdef CPL_LSB
2857 190857 : if (nBitDepth == 16)
2858 13653 : GDALSwapWords(row, 2, nXSize * nBands, 2);
2859 : #endif
2860 190857 : if (eErr == CE_None)
2861 : {
2862 190856 : if (!safe_png_write_rows(sSetJmpContext, hPNG, &row, 1))
2863 : {
2864 4 : eErr = CE_Failure;
2865 : }
2866 : }
2867 :
2868 381709 : if (eErr == CE_None &&
2869 190852 : !pfnProgress((iLine + 1) / static_cast<double>(nYSize), nullptr,
2870 : pProgressData))
2871 : {
2872 1 : eErr = CE_Failure;
2873 1 : CPLError(CE_Failure, CPLE_UserInterrupt,
2874 : "User terminated CreateCopy()");
2875 : }
2876 : }
2877 :
2878 4426 : CPLFree(pabyScanline);
2879 :
2880 4426 : if (!safe_png_write_end(sSetJmpContext, hPNG, psPNGInfo))
2881 : {
2882 7 : eErr = CE_Failure;
2883 : }
2884 4426 : png_destroy_write_struct(&hPNG, &psPNGInfo);
2885 :
2886 4426 : if (eErr == CE_None)
2887 : {
2888 4419 : if (fpImage->Close() != 0)
2889 : {
2890 0 : CPLError(CE_Failure, CPLE_FileIO,
2891 : "Error at file closing of '%s': %s", pszFilename,
2892 0 : VSIStrerror(errno));
2893 0 : eErr = CE_Failure;
2894 : }
2895 : }
2896 : else
2897 : {
2898 7 : fpImage->CancelCreation();
2899 7 : fpImage.reset();
2900 : }
2901 :
2902 4426 : if (eErr != CE_None)
2903 7 : return nullptr;
2904 :
2905 : // Do we need a world file?
2906 4419 : if (CPLFetchBool(papszOptions, "WORLDFILE", false))
2907 : {
2908 0 : GDALGeoTransform gt;
2909 0 : if (poSrcDS->GetGeoTransform(gt) == CE_None)
2910 0 : GDALWriteWorldFile(pszFilename, "wld", gt.data());
2911 : }
2912 :
2913 : // Re-open dataset and copy any auxiliary PAM information.
2914 :
2915 : /* If writing to stdout, we can't reopen it, so return */
2916 : /* a fake dataset to make the caller happy */
2917 4419 : if (CPLTestBool(CPLGetConfigOption("GDAL_OPEN_AFTER_COPY", "YES")))
2918 : {
2919 4218 : CPLPushErrorHandler(CPLQuietErrorHandler);
2920 4218 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
2921 : PNGDataset *poDS =
2922 4218 : cpl::down_cast<PNGDataset *>(PNGDataset::Open(&oOpenInfo));
2923 4218 : CPLPopErrorHandler();
2924 4218 : if (poDS)
2925 : {
2926 4217 : int nFlags = GCIF_PAM_DEFAULT & ~GCIF_METADATA;
2927 4217 : poDS->CloneInfo(poSrcDS, nFlags);
2928 :
2929 : char **papszExcludedDomains =
2930 4217 : CSLAddString(nullptr, "COLOR_PROFILE");
2931 4217 : if (bWriteMetadataAsText)
2932 1 : papszExcludedDomains = CSLAddString(papszExcludedDomains, "");
2933 4217 : GDALDriver::DefaultCopyMetadata(poSrcDS, poDS, papszOptions,
2934 : papszExcludedDomains);
2935 4217 : CSLDestroy(papszExcludedDomains);
2936 :
2937 4217 : return poDS;
2938 : }
2939 1 : CPLErrorReset();
2940 : }
2941 :
2942 202 : PNGDataset *poPNG_DS = new PNGDataset();
2943 202 : poPNG_DS->nRasterXSize = nXSize;
2944 202 : poPNG_DS->nRasterYSize = nYSize;
2945 202 : poPNG_DS->nBitDepth = nBitDepth;
2946 822 : for (int i = 0; i < nBands; i++)
2947 620 : poPNG_DS->SetBand(i + 1, new PNGRasterBand(poPNG_DS, i + 1));
2948 202 : return poPNG_DS;
2949 : }
2950 :
2951 : /************************************************************************/
2952 : /* png_vsi_read_data() */
2953 : /* */
2954 : /* Read data callback through VSI. */
2955 : /************************************************************************/
2956 78303 : static void png_vsi_read_data(png_structp png_ptr, png_bytep data,
2957 : png_size_t length)
2958 :
2959 : {
2960 : // fread() returns 0 on error, so it is OK to store this in a png_size_t
2961 : // instead of an int, which is what fread() actually returns.
2962 : const png_size_t check = static_cast<png_size_t>(
2963 156606 : VSIFReadL(data, 1, length,
2964 78303 : reinterpret_cast<VSILFILE *>(png_get_io_ptr(png_ptr))));
2965 :
2966 78303 : if (check != length)
2967 3 : png_error(png_ptr, "Read Error");
2968 78300 : }
2969 :
2970 : /************************************************************************/
2971 : /* png_vsi_write_data() */
2972 : /************************************************************************/
2973 :
2974 51097 : static void png_vsi_write_data(png_structp png_ptr, png_bytep data,
2975 : png_size_t length)
2976 : {
2977 102194 : const size_t check = VSIFWriteL(
2978 51097 : data, 1, length, reinterpret_cast<VSILFILE *>(png_get_io_ptr(png_ptr)));
2979 :
2980 51097 : if (check != length)
2981 10 : png_error(png_ptr, "Write Error");
2982 51087 : }
2983 :
2984 : /************************************************************************/
2985 : /* png_vsi_flush() */
2986 : /************************************************************************/
2987 0 : static void png_vsi_flush(png_structp png_ptr)
2988 : {
2989 0 : VSIFFlushL(reinterpret_cast<VSILFILE *>(png_get_io_ptr(png_ptr)));
2990 0 : }
2991 :
2992 : /************************************************************************/
2993 : /* png_gdal_error() */
2994 : /************************************************************************/
2995 :
2996 19 : static void png_gdal_error(png_structp png_ptr, const char *error_message)
2997 : {
2998 19 : CPLError(CE_Failure, CPLE_AppDefined, "libpng: %s", error_message);
2999 :
3000 : // Use longjmp instead of a C++ exception, because libpng is generally not
3001 : // built as C++ and so will not honor unwind semantics.
3002 :
3003 : jmp_buf *psSetJmpContext =
3004 19 : reinterpret_cast<jmp_buf *>(png_get_error_ptr(png_ptr));
3005 19 : if (psSetJmpContext)
3006 : {
3007 19 : longjmp(*psSetJmpContext, 1);
3008 : }
3009 0 : }
3010 :
3011 : /************************************************************************/
3012 : /* png_gdal_warning() */
3013 : /************************************************************************/
3014 :
3015 0 : static void png_gdal_warning(CPL_UNUSED png_structp png_ptr,
3016 : const char *error_message)
3017 : {
3018 0 : CPLError(CE_Warning, CPLE_AppDefined, "libpng: %s", error_message);
3019 0 : }
3020 :
3021 : /************************************************************************/
3022 : /* GDALRegister_PNG() */
3023 : /************************************************************************/
3024 :
3025 72 : void GDALRegister_PNG()
3026 :
3027 : {
3028 72 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
3029 0 : return;
3030 :
3031 72 : GDALDriver *poDriver = new GDALDriver();
3032 72 : PNGDriverSetCommonMetadata(poDriver);
3033 :
3034 72 : poDriver->pfnOpen = PNGDataset::Open;
3035 72 : poDriver->pfnCreateCopy = PNGDataset::CreateCopy;
3036 :
3037 72 : GetGDALDriverManager()->RegisterDriver(poDriver);
3038 : }
|