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