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