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