1 : /******************************************************************************
2 : * $Id: pngdataset.cpp 25571 2013-01-27 00:28:04Z rouault $
3 : *
4 : * Project: PNG Driver
5 : * Purpose: Implement GDAL PNG Support
6 : * Author: Frank Warmerdam, warmerda@home.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2000, Frank Warmerdam
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ******************************************************************************
29 : *
30 : * ISSUES:
31 : * o CollectMetadata() will only capture TEXT chunks before the image
32 : * data as the code is currently structured.
33 : * o Interlaced images are read entirely into memory for use. This is
34 : * bad for large images.
35 : * o Image reading is always strictly sequential. Reading backwards will
36 : * cause the file to be rewound, and access started again from the
37 : * beginning.
38 : * o 1, 2 and 4 bit data promoted to 8 bit.
39 : * o Transparency values not currently read and applied to palette.
40 : * o 16 bit alpha values are not scaled by to eight bit.
41 : * o I should install setjmp()/longjmp() based error trapping for PNG calls.
42 : * Currently a failure in png libraries will result in a complete
43 : * application termination.
44 : *
45 : */
46 :
47 : #include "gdal_pam.h"
48 : #include "png.h"
49 : #include "cpl_string.h"
50 : #include <setjmp.h>
51 :
52 : CPL_CVSID("$Id: pngdataset.cpp 25571 2013-01-27 00:28:04Z rouault $");
53 :
54 : CPL_C_START
55 : void GDALRegister_PNG(void);
56 : CPL_C_END
57 :
58 : // Define SUPPORT_CREATE if you want Create() call supported.
59 : // Note: callers must provide blocks in increasing Y order.
60 :
61 : // Disclaimer (E. Rouault) : this code is NOT production ready at all.
62 : // A lot of issues remains : uninitialized variables, unclosed file,
63 : // inability to handle properly multiband case, inability to read&write
64 : // at the same time. Do NOT use it unless you're ready to fix it
65 : //#define SUPPORT_CREATE
66 :
67 : // we believe it is ok to use setjmp() in this situation.
68 : #ifdef _MSC_VER
69 : # pragma warning(disable:4611)
70 : #endif
71 :
72 : static void
73 : png_vsi_read_data(png_structp png_ptr, png_bytep data, png_size_t length);
74 :
75 : static void
76 : png_vsi_write_data(png_structp png_ptr, png_bytep data, png_size_t length);
77 :
78 : static void png_vsi_flush(png_structp png_ptr);
79 :
80 : static void png_gdal_error( png_structp png_ptr, const char *error_message );
81 : static void png_gdal_warning( png_structp png_ptr, const char *error_message );
82 :
83 : /************************************************************************/
84 : /* ==================================================================== */
85 : /* PNGDataset */
86 : /* ==================================================================== */
87 : /************************************************************************/
88 :
89 : class PNGRasterBand;
90 :
91 : class PNGDataset : public GDALPamDataset
92 : {
93 : friend class PNGRasterBand;
94 :
95 : VSILFILE *fpImage;
96 : png_structp hPNG;
97 : png_infop psPNGInfo;
98 : int nBitDepth;
99 : int nColorType; /* PNG_COLOR_TYPE_* */
100 : int bInterlaced;
101 :
102 : int nBufferStartLine;
103 : int nBufferLines;
104 : int nLastLineRead;
105 : GByte *pabyBuffer;
106 :
107 : GDALColorTable *poColorTable;
108 :
109 : int bGeoTransformValid;
110 : double adfGeoTransform[6];
111 :
112 :
113 : void CollectMetadata();
114 :
115 : int bHasReadXMPMetadata;
116 : void CollectXMPMetadata();
117 :
118 : CPLErr LoadScanline( int );
119 : CPLErr LoadInterlacedChunk( int );
120 : void Restart();
121 :
122 : int bHasTriedLoadWorldFile;
123 : void LoadWorldFile();
124 : CPLString osWldFilename;
125 :
126 : public:
127 : PNGDataset();
128 : ~PNGDataset();
129 :
130 : static GDALDataset *Open( GDALOpenInfo * );
131 : static int Identify( GDALOpenInfo * );
132 : static GDALDataset* CreateCopy( const char * pszFilename,
133 : GDALDataset *poSrcDS,
134 : int bStrict, char ** papszOptions,
135 : GDALProgressFunc pfnProgress,
136 : void * pProgressData );
137 :
138 : virtual char **GetFileList(void);
139 :
140 : virtual CPLErr GetGeoTransform( double * );
141 : virtual void FlushCache( void );
142 :
143 : virtual char **GetMetadata( const char * pszDomain = "" );
144 :
145 : // semi-private.
146 : jmp_buf sSetJmpContext;
147 :
148 : #ifdef SUPPORT_CREATE
149 : int m_nBitDepth;
150 : GByte *m_pabyBuffer;
151 : png_byte *m_pabyAlpha;
152 : png_structp m_hPNG;
153 : png_infop m_psPNGInfo;
154 : png_color *m_pasPNGColors;
155 : VSILFILE *m_fpImage;
156 : int m_bGeoTransformValid;
157 : double m_adfGeoTransform[6];
158 : char *m_pszFilename;
159 : int m_nColorType; /* PNG_COLOR_TYPE_* */
160 :
161 : virtual CPLErr SetGeoTransform( double * );
162 : static GDALDataset *Create( const char* pszFilename,
163 : int nXSize, int nYSize, int nBands,
164 : GDALDataType, char** papszParmList );
165 : protected:
166 : CPLErr write_png_header();
167 :
168 : #endif
169 : };
170 :
171 : /************************************************************************/
172 : /* ==================================================================== */
173 : /* PNGRasterBand */
174 : /* ==================================================================== */
175 : /************************************************************************/
176 :
177 : class PNGRasterBand : public GDALPamRasterBand
178 215 : {
179 : friend class PNGDataset;
180 :
181 : public:
182 :
183 : PNGRasterBand( PNGDataset *, int );
184 :
185 : virtual CPLErr IReadBlock( int, int, void * );
186 :
187 : virtual GDALColorInterp GetColorInterpretation();
188 : virtual GDALColorTable *GetColorTable();
189 : CPLErr SetNoDataValue( double dfNewValue );
190 : virtual double GetNoDataValue( int *pbSuccess = NULL );
191 :
192 : int bHaveNoData;
193 : double dfNoDataValue;
194 :
195 :
196 : #ifdef SUPPORT_CREATE
197 : virtual CPLErr SetColorTable(GDALColorTable*);
198 : virtual CPLErr IWriteBlock( int, int, void * );
199 :
200 : protected:
201 : int m_bBandProvided[5];
202 : void reset_band_provision_flags()
203 : {
204 : PNGDataset& ds = *(PNGDataset*)poDS;
205 :
206 : for(size_t i = 0; i < ds.nBands; i++)
207 : m_bBandProvided[i] = FALSE;
208 : }
209 : #endif
210 : };
211 :
212 :
213 : /************************************************************************/
214 : /* PNGRasterBand() */
215 : /************************************************************************/
216 :
217 215 : PNGRasterBand::PNGRasterBand( PNGDataset *poDS, int nBand )
218 :
219 : {
220 215 : this->poDS = poDS;
221 215 : this->nBand = nBand;
222 :
223 215 : if( poDS->nBitDepth == 16 )
224 59 : eDataType = GDT_UInt16;
225 : else
226 156 : eDataType = GDT_Byte;
227 :
228 215 : nBlockXSize = poDS->nRasterXSize;;
229 215 : nBlockYSize = 1;
230 :
231 215 : bHaveNoData = FALSE;
232 215 : dfNoDataValue = -1;
233 :
234 : #ifdef SUPPORT_CREATE
235 : this->reset_band_provision_flags();
236 : #endif
237 215 : }
238 :
239 : /************************************************************************/
240 : /* IReadBlock() */
241 : /************************************************************************/
242 :
243 18543 : CPLErr PNGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
244 : void * pImage )
245 :
246 : {
247 18543 : PNGDataset *poGDS = (PNGDataset *) poDS;
248 : CPLErr eErr;
249 : GByte *pabyScanline;
250 18543 : int i, nPixelSize, nPixelOffset, nXSize = GetXSize();
251 :
252 18543 : CPLAssert( nBlockXOff == 0 );
253 :
254 18543 : if( poGDS->nBitDepth == 16 )
255 1261 : nPixelSize = 2;
256 : else
257 17282 : nPixelSize = 1;
258 :
259 :
260 18543 : if (poGDS->fpImage == NULL)
261 : {
262 20 : memset( pImage, 0, nPixelSize * nXSize );
263 20 : return CE_None;
264 : }
265 :
266 18523 : nPixelOffset = poGDS->nBands * nPixelSize;
267 :
268 : /* -------------------------------------------------------------------- */
269 : /* Load the desired scanline into the working buffer. */
270 : /* -------------------------------------------------------------------- */
271 18523 : eErr = poGDS->LoadScanline( nBlockYOff );
272 18523 : if( eErr != CE_None )
273 5 : return eErr;
274 :
275 : pabyScanline = poGDS->pabyBuffer
276 : + (nBlockYOff - poGDS->nBufferStartLine) * nPixelOffset * nXSize
277 18518 : + nPixelSize * (nBand - 1);
278 :
279 : /* -------------------------------------------------------------------- */
280 : /* Transfer between the working buffer the the callers buffer. */
281 : /* -------------------------------------------------------------------- */
282 18518 : if( nPixelSize == nPixelOffset )
283 2542 : memcpy( pImage, pabyScanline, nPixelSize * nXSize );
284 15976 : else if( nPixelSize == 1 )
285 : {
286 4736130 : for( i = 0; i < nXSize; i++ )
287 4720810 : ((GByte *) pImage)[i] = pabyScanline[i*nPixelOffset];
288 : }
289 : else
290 : {
291 656 : CPLAssert( nPixelSize == 2 );
292 14928 : for( i = 0; i < nXSize; i++ )
293 : {
294 14272 : ((GUInt16 *) pImage)[i] =
295 14272 : *((GUInt16 *) (pabyScanline+i*nPixelOffset));
296 : }
297 : }
298 :
299 : /* -------------------------------------------------------------------- */
300 : /* Forceably load the other bands associated with this scanline. */
301 : /* -------------------------------------------------------------------- */
302 : int iBand;
303 65424 : for(iBand = 1; iBand < poGDS->GetRasterCount(); iBand++)
304 : {
305 : GDALRasterBlock *poBlock;
306 :
307 : poBlock =
308 46906 : poGDS->GetRasterBand(iBand+1)->GetLockedBlockRef(nBlockXOff,nBlockYOff);
309 46906 : if( poBlock != NULL )
310 46906 : poBlock->DropLock();
311 : }
312 :
313 18518 : return CE_None;
314 : }
315 :
316 : /************************************************************************/
317 : /* GetColorInterpretation() */
318 : /************************************************************************/
319 :
320 52 : GDALColorInterp PNGRasterBand::GetColorInterpretation()
321 :
322 : {
323 52 : PNGDataset *poGDS = (PNGDataset *) poDS;
324 :
325 52 : if( poGDS->nColorType == PNG_COLOR_TYPE_GRAY )
326 5 : return GCI_GrayIndex;
327 :
328 47 : else if( poGDS->nColorType == PNG_COLOR_TYPE_GRAY_ALPHA )
329 : {
330 1 : if( nBand == 1 )
331 1 : return GCI_GrayIndex;
332 : else
333 0 : return GCI_AlphaBand;
334 : }
335 :
336 46 : else if( poGDS->nColorType == PNG_COLOR_TYPE_PALETTE )
337 3 : return GCI_PaletteIndex;
338 :
339 43 : else if( poGDS->nColorType == PNG_COLOR_TYPE_RGB
340 : || poGDS->nColorType == PNG_COLOR_TYPE_RGB_ALPHA )
341 : {
342 43 : if( nBand == 1 )
343 11 : return GCI_RedBand;
344 32 : else if( nBand == 2 )
345 11 : return GCI_GreenBand;
346 21 : else if( nBand == 3 )
347 11 : return GCI_BlueBand;
348 : else
349 10 : return GCI_AlphaBand;
350 : }
351 : else
352 0 : return GCI_GrayIndex;
353 : }
354 :
355 : /************************************************************************/
356 : /* GetColorTable() */
357 : /************************************************************************/
358 :
359 18 : GDALColorTable *PNGRasterBand::GetColorTable()
360 :
361 : {
362 18 : PNGDataset *poGDS = (PNGDataset *) poDS;
363 :
364 18 : if( nBand == 1 )
365 10 : return poGDS->poColorTable;
366 : else
367 8 : return NULL;
368 : }
369 :
370 : /************************************************************************/
371 : /* SetNoDataValue() */
372 : /************************************************************************/
373 :
374 23 : CPLErr PNGRasterBand::SetNoDataValue( double dfNewValue )
375 :
376 : {
377 23 : bHaveNoData = TRUE;
378 23 : dfNoDataValue = dfNewValue;
379 :
380 23 : return CE_None;
381 : }
382 :
383 : /************************************************************************/
384 : /* GetNoDataValue() */
385 : /************************************************************************/
386 :
387 59 : double PNGRasterBand::GetNoDataValue( int *pbSuccess )
388 :
389 : {
390 59 : if( bHaveNoData )
391 : {
392 17 : if( pbSuccess != NULL )
393 16 : *pbSuccess = bHaveNoData;
394 17 : return dfNoDataValue;
395 : }
396 : else
397 : {
398 42 : return GDALPamRasterBand::GetNoDataValue( pbSuccess );
399 : }
400 : }
401 :
402 : /************************************************************************/
403 : /* ==================================================================== */
404 : /* PNGDataset */
405 : /* ==================================================================== */
406 : /************************************************************************/
407 :
408 :
409 : /************************************************************************/
410 : /* PNGDataset() */
411 : /************************************************************************/
412 :
413 81 : PNGDataset::PNGDataset()
414 :
415 : {
416 81 : fpImage = NULL;
417 81 : hPNG = NULL;
418 81 : psPNGInfo = NULL;
419 81 : pabyBuffer = NULL;
420 81 : nBufferStartLine = 0;
421 81 : nBufferLines = 0;
422 81 : nLastLineRead = -1;
423 81 : poColorTable = NULL;
424 81 : nBitDepth = 8;
425 :
426 81 : bGeoTransformValid = FALSE;
427 81 : adfGeoTransform[0] = 0.0;
428 81 : adfGeoTransform[1] = 1.0;
429 81 : adfGeoTransform[2] = 0.0;
430 81 : adfGeoTransform[3] = 0.0;
431 81 : adfGeoTransform[4] = 0.0;
432 81 : adfGeoTransform[5] = 1.0;
433 :
434 81 : bHasTriedLoadWorldFile = FALSE;
435 81 : bHasReadXMPMetadata = FALSE;
436 81 : }
437 :
438 : /************************************************************************/
439 : /* ~PNGDataset() */
440 : /************************************************************************/
441 :
442 81 : PNGDataset::~PNGDataset()
443 :
444 : {
445 81 : FlushCache();
446 :
447 81 : if( hPNG != NULL )
448 80 : png_destroy_read_struct( &hPNG, &psPNGInfo, NULL );
449 :
450 81 : if( fpImage )
451 80 : VSIFCloseL( fpImage );
452 :
453 81 : if( poColorTable != NULL )
454 7 : delete poColorTable;
455 81 : }
456 :
457 : /************************************************************************/
458 : /* GetGeoTransform() */
459 : /************************************************************************/
460 :
461 20 : CPLErr PNGDataset::GetGeoTransform( double * padfTransform )
462 :
463 : {
464 20 : LoadWorldFile();
465 :
466 20 : if( bGeoTransformValid )
467 : {
468 3 : memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
469 3 : return CE_None;
470 : }
471 : else
472 17 : return GDALPamDataset::GetGeoTransform( padfTransform );
473 : }
474 :
475 : /************************************************************************/
476 : /* FlushCache() */
477 : /* */
478 : /* We override this so we can also flush out local tiff strip */
479 : /* cache if need be. */
480 : /************************************************************************/
481 :
482 81 : void PNGDataset::FlushCache()
483 :
484 : {
485 81 : GDALPamDataset::FlushCache();
486 :
487 81 : if( pabyBuffer != NULL )
488 : {
489 45 : CPLFree( pabyBuffer );
490 45 : pabyBuffer = NULL;
491 45 : nBufferStartLine = 0;
492 45 : nBufferLines = 0;
493 : }
494 81 : }
495 :
496 : /************************************************************************/
497 : /* Restart() */
498 : /* */
499 : /* Restart reading from the beginning of the file. */
500 : /************************************************************************/
501 :
502 1 : void PNGDataset::Restart()
503 :
504 : {
505 1 : png_destroy_read_struct( &hPNG, &psPNGInfo, NULL );
506 :
507 1 : hPNG = png_create_read_struct( PNG_LIBPNG_VER_STRING, this, NULL, NULL );
508 :
509 1 : png_set_error_fn( hPNG, &sSetJmpContext, png_gdal_error, png_gdal_warning );
510 1 : if( setjmp( sSetJmpContext ) != 0 )
511 0 : return;
512 :
513 1 : psPNGInfo = png_create_info_struct( hPNG );
514 :
515 1 : VSIFSeekL( fpImage, 0, SEEK_SET );
516 1 : png_set_read_fn( hPNG, fpImage, png_vsi_read_data );
517 1 : png_read_info( hPNG, psPNGInfo );
518 :
519 1 : if( nBitDepth < 8 )
520 0 : png_set_packing( hPNG );
521 :
522 1 : nLastLineRead = -1;
523 : }
524 :
525 : /************************************************************************/
526 : /* LoadInterlacedChunk() */
527 : /************************************************************************/
528 :
529 2 : CPLErr PNGDataset::LoadInterlacedChunk( int iLine )
530 :
531 : {
532 : int nPixelOffset;
533 :
534 2 : if( nBitDepth == 16 )
535 0 : nPixelOffset = 2 * GetRasterCount();
536 : else
537 2 : nPixelOffset = 1 * GetRasterCount();
538 :
539 : /* -------------------------------------------------------------------- */
540 : /* Was is the biggest chunk we can safely operate on? */
541 : /* -------------------------------------------------------------------- */
542 : #define MAX_PNG_CHUNK_BYTES 100000000
543 :
544 : int nMaxChunkLines =
545 2 : MAX(1,MAX_PNG_CHUNK_BYTES / (nPixelOffset * GetRasterXSize()));
546 : png_bytep *png_rows;
547 :
548 2 : if( nMaxChunkLines > GetRasterYSize() )
549 2 : nMaxChunkLines = GetRasterYSize();
550 :
551 : /* -------------------------------------------------------------------- */
552 : /* Allocate chunk buffer, if we don't already have it from a */
553 : /* previous request. */
554 : /* -------------------------------------------------------------------- */
555 2 : nBufferLines = nMaxChunkLines;
556 2 : if( nMaxChunkLines + iLine > GetRasterYSize() )
557 0 : nBufferStartLine = GetRasterYSize() - nMaxChunkLines;
558 : else
559 2 : nBufferStartLine = iLine;
560 :
561 2 : if( pabyBuffer == NULL )
562 : {
563 : pabyBuffer = (GByte *)
564 2 : VSIMalloc(nPixelOffset*GetRasterXSize()*nMaxChunkLines);
565 :
566 2 : if( pabyBuffer == NULL )
567 : {
568 : CPLError( CE_Failure, CPLE_OutOfMemory,
569 : "Unable to allocate buffer for whole interlaced PNG"
570 : "image of size %dx%d.\n",
571 0 : GetRasterXSize(), GetRasterYSize() );
572 0 : return CE_Failure;
573 : }
574 : #ifdef notdef
575 : if( nMaxChunkLines < GetRasterYSize() )
576 : CPLDebug( "PNG",
577 : "Interlaced file being handled in %d line chunks.\n"
578 : "Performance is likely to be quite poor.",
579 : nMaxChunkLines );
580 : #endif
581 : }
582 :
583 : /* -------------------------------------------------------------------- */
584 : /* Do we need to restart reading? We do this if we aren't on */
585 : /* the first attempt to read the image. */
586 : /* -------------------------------------------------------------------- */
587 2 : if( nLastLineRead != -1 )
588 : {
589 0 : Restart();
590 0 : if( setjmp( sSetJmpContext ) != 0 )
591 0 : return CE_Failure;
592 : }
593 :
594 : /* -------------------------------------------------------------------- */
595 : /* Allocate and populate rows array. We create a row for each */
596 : /* row in the image, but use our dummy line for rows not in the */
597 : /* target window. */
598 : /* -------------------------------------------------------------------- */
599 : int i;
600 2 : png_bytep dummy_row = (png_bytep)CPLMalloc(nPixelOffset*GetRasterXSize());
601 2 : png_rows = (png_bytep*)CPLMalloc(sizeof(png_bytep) * GetRasterYSize());
602 :
603 302 : for( i = 0; i < GetRasterYSize(); i++ )
604 : {
605 600 : if( i >= nBufferStartLine && i < nBufferStartLine + nBufferLines )
606 300 : png_rows[i] = pabyBuffer
607 300 : + (i-nBufferStartLine) * nPixelOffset * GetRasterXSize();
608 : else
609 0 : png_rows[i] = dummy_row;
610 : }
611 :
612 2 : png_read_image( hPNG, png_rows );
613 :
614 2 : CPLFree( png_rows );
615 2 : CPLFree( dummy_row );
616 :
617 2 : nLastLineRead = nBufferStartLine + nBufferLines - 1;
618 :
619 2 : return CE_None;
620 : }
621 :
622 : /************************************************************************/
623 : /* LoadScanline() */
624 : /************************************************************************/
625 :
626 18523 : CPLErr PNGDataset::LoadScanline( int nLine )
627 :
628 : {
629 : int nPixelOffset;
630 :
631 18523 : CPLAssert( nLine >= 0 && nLine < GetRasterYSize() );
632 :
633 18523 : if( nLine >= nBufferStartLine && nLine < nBufferStartLine + nBufferLines)
634 12064 : return CE_None;
635 :
636 6459 : if( nBitDepth == 16 )
637 837 : nPixelOffset = 2 * GetRasterCount();
638 : else
639 5622 : nPixelOffset = 1 * GetRasterCount();
640 :
641 6459 : if( setjmp( sSetJmpContext ) != 0 )
642 5 : return CE_Failure;
643 :
644 : /* -------------------------------------------------------------------- */
645 : /* If the file is interlaced, we will load the entire image */
646 : /* into memory using the high level API. */
647 : /* -------------------------------------------------------------------- */
648 6459 : if( bInterlaced )
649 2 : return LoadInterlacedChunk( nLine );
650 :
651 : /* -------------------------------------------------------------------- */
652 : /* Ensure we have space allocated for one scanline */
653 : /* -------------------------------------------------------------------- */
654 6457 : if( pabyBuffer == NULL )
655 43 : pabyBuffer = (GByte *) CPLMalloc(nPixelOffset * GetRasterXSize());
656 :
657 : /* -------------------------------------------------------------------- */
658 : /* Otherwise we just try to read the requested row. Do we need */
659 : /* to rewind and start over? */
660 : /* -------------------------------------------------------------------- */
661 6457 : if( nLine <= nLastLineRead )
662 : {
663 1 : Restart();
664 1 : if( setjmp( sSetJmpContext ) != 0 )
665 0 : return CE_Failure;
666 : }
667 :
668 : /* -------------------------------------------------------------------- */
669 : /* Read till we get the desired row. */
670 : /* -------------------------------------------------------------------- */
671 : png_bytep row;
672 :
673 6457 : row = pabyBuffer;
674 19366 : while( nLine > nLastLineRead )
675 : {
676 6457 : png_read_rows( hPNG, &row, NULL, 1 );
677 6452 : nLastLineRead++;
678 : }
679 :
680 6452 : nBufferStartLine = nLine;
681 6452 : nBufferLines = 1;
682 :
683 : /* -------------------------------------------------------------------- */
684 : /* Do swap on LSB machines. 16bit PNG data is stored in MSB */
685 : /* format. */
686 : /* -------------------------------------------------------------------- */
687 : #ifdef CPL_LSB
688 6452 : if( nBitDepth == 16 )
689 832 : GDALSwapWords( row, 2, GetRasterXSize() * GetRasterCount(), 2 );
690 : #endif
691 :
692 6452 : return CE_None;
693 : }
694 :
695 : /************************************************************************/
696 : /* CollectMetadata() */
697 : /* */
698 : /* We normally do this after reading up to the image, but be */
699 : /* forwarned ... we can missing text chunks this way. */
700 : /* */
701 : /* We turn each PNG text chunk into one metadata item. It */
702 : /* might be nice to preserve language information though we */
703 : /* don't try to now. */
704 : /************************************************************************/
705 :
706 78 : void PNGDataset::CollectMetadata()
707 :
708 : {
709 : int nTextCount;
710 : png_textp text_ptr;
711 :
712 78 : if( nBitDepth < 8 )
713 : {
714 0 : for( int iBand = 0; iBand < nBands; iBand++ )
715 : {
716 : GetRasterBand(iBand+1)->SetMetadataItem(
717 : "NBITS", CPLString().Printf( "%d", nBitDepth ),
718 0 : "IMAGE_STRUCTURE" );
719 : }
720 : }
721 :
722 78 : if( png_get_text( hPNG, psPNGInfo, &text_ptr, &nTextCount ) == 0 )
723 78 : return;
724 :
725 0 : for( int iText = 0; iText < nTextCount; iText++ )
726 : {
727 0 : char *pszTag = CPLStrdup(text_ptr[iText].key);
728 :
729 0 : for( int i = 0; pszTag[i] != '\0'; i++ )
730 : {
731 0 : if( pszTag[i] == ' ' || pszTag[i] == '=' || pszTag[i] == ':' )
732 0 : pszTag[i] = '_';
733 : }
734 :
735 0 : SetMetadataItem( pszTag, text_ptr[iText].text );
736 0 : CPLFree( pszTag );
737 : }
738 : }
739 :
740 : /************************************************************************/
741 : /* CollectXMPMetadata() */
742 : /************************************************************************/
743 :
744 : /* See §2.1.5 of http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf */
745 :
746 2 : void PNGDataset::CollectXMPMetadata()
747 :
748 : {
749 2 : if (fpImage == NULL || bHasReadXMPMetadata)
750 0 : return;
751 :
752 : /* Save current position to avoid disturbing PNG stream decoding */
753 2 : vsi_l_offset nCurOffset = VSIFTellL(fpImage);
754 :
755 2 : vsi_l_offset nOffset = 8;
756 2 : VSIFSeekL( fpImage, nOffset, SEEK_SET );
757 :
758 : /* Loop over chunks */
759 9 : while(TRUE)
760 : {
761 : int nLength;
762 : char pszChunkType[5];
763 : int nCRC;
764 :
765 11 : if (VSIFReadL( &nLength, 4, 1, fpImage ) != 1)
766 0 : break;
767 11 : nOffset += 4;
768 11 : CPL_MSBPTR32(&nLength);
769 11 : if (nLength <= 0)
770 1 : break;
771 10 : if (VSIFReadL( pszChunkType, 4, 1, fpImage ) != 1)
772 0 : break;
773 10 : nOffset += 4;
774 10 : pszChunkType[4] = 0;
775 :
776 10 : if (strcmp(pszChunkType, "iTXt") == 0 && nLength > 22)
777 : {
778 1 : char* pszContent = (char*)VSIMalloc(nLength + 1);
779 1 : if (pszContent == NULL)
780 0 : break;
781 1 : if (VSIFReadL( pszContent, nLength, 1, fpImage) != 1)
782 : {
783 0 : VSIFree(pszContent);
784 0 : break;
785 : }
786 1 : nOffset += nLength;
787 1 : pszContent[nLength] = '\0';
788 1 : if (memcmp(pszContent, "XML:com.adobe.xmp\0\0\0\0\0", 22) == 0)
789 : {
790 : /* Avoid setting the PAM dirty bit just for that */
791 1 : int nOldPamFlags = nPamFlags;
792 :
793 : char *apszMDList[2];
794 1 : apszMDList[0] = pszContent + 22;
795 1 : apszMDList[1] = NULL;
796 1 : SetMetadata(apszMDList, "xml:XMP");
797 :
798 1 : nPamFlags = nOldPamFlags;
799 :
800 1 : VSIFree(pszContent);
801 :
802 1 : break;
803 : }
804 : else
805 : {
806 0 : VSIFree(pszContent);
807 : }
808 : }
809 : else
810 : {
811 9 : nOffset += nLength;
812 9 : VSIFSeekL( fpImage, nOffset, SEEK_SET );
813 : }
814 :
815 9 : nOffset += 4;
816 9 : if (VSIFReadL( &nCRC, 4, 1, fpImage ) != 1)
817 0 : break;
818 : }
819 :
820 2 : VSIFSeekL( fpImage, nCurOffset, SEEK_SET );
821 :
822 2 : bHasReadXMPMetadata = TRUE;
823 : }
824 :
825 : /************************************************************************/
826 : /* GetMetadata() */
827 : /************************************************************************/
828 :
829 21 : char **PNGDataset::GetMetadata( const char * pszDomain )
830 : {
831 21 : if (fpImage == NULL)
832 0 : return NULL;
833 21 : if (eAccess == GA_ReadOnly && !bHasReadXMPMetadata &&
834 : (pszDomain != NULL && EQUAL(pszDomain, "xml:XMP")))
835 2 : CollectXMPMetadata();
836 21 : return GDALPamDataset::GetMetadata(pszDomain);
837 : }
838 :
839 : /************************************************************************/
840 : /* Identify() */
841 : /************************************************************************/
842 :
843 14217 : int PNGDataset::Identify( GDALOpenInfo * poOpenInfo )
844 :
845 : {
846 14217 : if( poOpenInfo->nHeaderBytes < 4 )
847 12024 : return FALSE;
848 :
849 2193 : if( png_sig_cmp(poOpenInfo->pabyHeader, (png_size_t)0,
850 : poOpenInfo->nHeaderBytes) != 0 )
851 2112 : return FALSE;
852 :
853 81 : return TRUE;
854 : }
855 :
856 : /************************************************************************/
857 : /* Open() */
858 : /************************************************************************/
859 :
860 3961 : GDALDataset *PNGDataset::Open( GDALOpenInfo * poOpenInfo )
861 :
862 : {
863 3961 : if( !Identify( poOpenInfo ) )
864 3881 : return NULL;
865 :
866 80 : if( poOpenInfo->eAccess == GA_Update )
867 : {
868 : CPLError( CE_Failure, CPLE_NotSupported,
869 : "The PNG driver does not support update access to existing"
870 0 : " datasets.\n" );
871 0 : return NULL;
872 : }
873 :
874 : /* -------------------------------------------------------------------- */
875 : /* Open a file handle using large file API. */
876 : /* -------------------------------------------------------------------- */
877 80 : VSILFILE *fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
878 80 : if( fp == NULL )
879 : {
880 : CPLError( CE_Failure, CPLE_OpenFailed,
881 : "Unexpected failure of VSIFOpenL(%s) in PNG Open()",
882 0 : poOpenInfo->pszFilename );
883 0 : return NULL;
884 : }
885 :
886 : /* -------------------------------------------------------------------- */
887 : /* Create a corresponding GDALDataset. */
888 : /* -------------------------------------------------------------------- */
889 : PNGDataset *poDS;
890 :
891 80 : poDS = new PNGDataset();
892 :
893 80 : poDS->fpImage = fp;
894 80 : poDS->eAccess = poOpenInfo->eAccess;
895 :
896 : poDS->hPNG = png_create_read_struct( PNG_LIBPNG_VER_STRING, poDS,
897 80 : NULL, NULL );
898 80 : if (poDS->hPNG == NULL)
899 : {
900 : #if LIBPNG_VER_MINOR >= 2 || LIBPNG_VER_MAJOR > 1
901 : int version = png_access_version_number();
902 : CPLError( CE_Failure, CPLE_NotSupported,
903 : "The PNG driver failed to access libpng with version '%s',"
904 : " library is actually version '%d'.\n",
905 : PNG_LIBPNG_VER_STRING, version);
906 : #else
907 : CPLError( CE_Failure, CPLE_NotSupported,
908 : "The PNG driver failed to in png_create_read_struct().\n"
909 0 : "This may be due to version compatibility problems." );
910 : #endif
911 0 : delete poDS;
912 0 : return NULL;
913 : }
914 :
915 80 : poDS->psPNGInfo = png_create_info_struct( poDS->hPNG );
916 :
917 : /* -------------------------------------------------------------------- */
918 : /* Setup error handling. */
919 : /* -------------------------------------------------------------------- */
920 80 : png_set_error_fn( poDS->hPNG, &poDS->sSetJmpContext, png_gdal_error, png_gdal_warning );
921 :
922 80 : if( setjmp( poDS->sSetJmpContext ) != 0 )
923 : {
924 2 : delete poDS;
925 2 : return NULL;
926 : }
927 :
928 : /* -------------------------------------------------------------------- */
929 : /* Read pre-image data after ensuring the file is rewound. */
930 : /* -------------------------------------------------------------------- */
931 : /* we should likely do a setjmp() here */
932 :
933 80 : png_set_read_fn( poDS->hPNG, poDS->fpImage, png_vsi_read_data );
934 80 : png_read_info( poDS->hPNG, poDS->psPNGInfo );
935 :
936 : /* -------------------------------------------------------------------- */
937 : /* Capture some information from the file that is of interest. */
938 : /* -------------------------------------------------------------------- */
939 78 : poDS->nRasterXSize = png_get_image_width( poDS->hPNG, poDS->psPNGInfo);
940 78 : poDS->nRasterYSize = png_get_image_height( poDS->hPNG,poDS->psPNGInfo);
941 :
942 78 : poDS->nBands = png_get_channels( poDS->hPNG, poDS->psPNGInfo );
943 78 : poDS->nBitDepth = png_get_bit_depth( poDS->hPNG, poDS->psPNGInfo );
944 : poDS->bInterlaced = png_get_interlace_type( poDS->hPNG, poDS->psPNGInfo )
945 78 : != PNG_INTERLACE_NONE;
946 :
947 78 : poDS->nColorType = png_get_color_type( poDS->hPNG, poDS->psPNGInfo );
948 :
949 78 : if( poDS->nColorType == PNG_COLOR_TYPE_PALETTE
950 : && poDS->nBands > 1 )
951 : {
952 : CPLDebug( "GDAL", "PNG Driver got %d from png_get_channels(),\n"
953 : "but this kind of image (paletted) can only have one band.\n"
954 : "Correcting and continuing, but this may indicate a bug!",
955 0 : poDS->nBands );
956 0 : poDS->nBands = 1;
957 : }
958 :
959 : /* -------------------------------------------------------------------- */
960 : /* We want to treat 1,2,4 bit images as eight bit. This call */
961 : /* causes libpng to unpack the image. */
962 : /* -------------------------------------------------------------------- */
963 78 : if( poDS->nBitDepth < 8 )
964 0 : png_set_packing( poDS->hPNG );
965 :
966 : /* -------------------------------------------------------------------- */
967 : /* Create band information objects. */
968 : /* -------------------------------------------------------------------- */
969 584 : for( int iBand = 0; iBand < poDS->nBands; iBand++ )
970 214 : poDS->SetBand( iBand+1, new PNGRasterBand( poDS, iBand+1 ) );
971 :
972 : /* -------------------------------------------------------------------- */
973 : /* Is there a palette? Note: we should also read back and */
974 : /* apply transparency values if available. */
975 : /* -------------------------------------------------------------------- */
976 78 : if( poDS->nColorType == PNG_COLOR_TYPE_PALETTE )
977 : {
978 : png_color *pasPNGPalette;
979 : int nColorCount;
980 : GDALColorEntry oEntry;
981 7 : unsigned char *trans = NULL;
982 7 : png_color_16 *trans_values = NULL;
983 7 : int num_trans = 0;
984 7 : int nNoDataIndex = -1;
985 :
986 7 : if( png_get_PLTE( poDS->hPNG, poDS->psPNGInfo,
987 : &pasPNGPalette, &nColorCount ) == 0 )
988 0 : nColorCount = 0;
989 :
990 : png_get_tRNS( poDS->hPNG, poDS->psPNGInfo,
991 7 : &trans, &num_trans, &trans_values );
992 :
993 7 : poDS->poColorTable = new GDALColorTable();
994 :
995 119 : for( int iColor = nColorCount - 1; iColor >= 0; iColor-- )
996 : {
997 112 : oEntry.c1 = pasPNGPalette[iColor].red;
998 112 : oEntry.c2 = pasPNGPalette[iColor].green;
999 112 : oEntry.c3 = pasPNGPalette[iColor].blue;
1000 :
1001 112 : if( iColor < num_trans )
1002 : {
1003 112 : oEntry.c4 = trans[iColor];
1004 112 : if( oEntry.c4 == 0 )
1005 : {
1006 7 : if( nNoDataIndex == -1 )
1007 7 : nNoDataIndex = iColor;
1008 : else
1009 0 : nNoDataIndex = -2;
1010 : }
1011 : }
1012 : else
1013 0 : oEntry.c4 = 255;
1014 :
1015 112 : poDS->poColorTable->SetColorEntry( iColor, &oEntry );
1016 : }
1017 :
1018 : /*
1019 : ** Special hack to an index as the no data value, as long as it
1020 : ** is the _only_ transparent color in the palette.
1021 : */
1022 7 : if( nNoDataIndex > -1 )
1023 : {
1024 7 : poDS->GetRasterBand(1)->SetNoDataValue(nNoDataIndex);
1025 : }
1026 : }
1027 :
1028 : /* -------------------------------------------------------------------- */
1029 : /* Check for transparency values in greyscale images. */
1030 : /* -------------------------------------------------------------------- */
1031 78 : if( poDS->nColorType == PNG_COLOR_TYPE_GRAY )
1032 : {
1033 21 : png_color_16 *trans_values = NULL;
1034 : unsigned char *trans;
1035 : int num_trans;
1036 :
1037 21 : if( png_get_tRNS( poDS->hPNG, poDS->psPNGInfo,
1038 : &trans, &num_trans, &trans_values ) != 0
1039 : && trans_values != NULL )
1040 : {
1041 4 : poDS->GetRasterBand(1)->SetNoDataValue(trans_values->gray);
1042 : }
1043 : }
1044 :
1045 : /* -------------------------------------------------------------------- */
1046 : /* Check for nodata color for RGB images. */
1047 : /* -------------------------------------------------------------------- */
1048 78 : if( poDS->nColorType == PNG_COLOR_TYPE_RGB )
1049 : {
1050 12 : png_color_16 *trans_values = NULL;
1051 : unsigned char *trans;
1052 : int num_trans;
1053 :
1054 12 : if( png_get_tRNS( poDS->hPNG, poDS->psPNGInfo,
1055 : &trans, &num_trans, &trans_values ) != 0
1056 : && trans_values != NULL )
1057 : {
1058 4 : CPLString oNDValue;
1059 :
1060 : oNDValue.Printf( "%d %d %d",
1061 : trans_values->red,
1062 : trans_values->green,
1063 4 : trans_values->blue );
1064 4 : poDS->SetMetadataItem( "NODATA_VALUES", oNDValue.c_str() );
1065 :
1066 4 : poDS->GetRasterBand(1)->SetNoDataValue(trans_values->red);
1067 4 : poDS->GetRasterBand(2)->SetNoDataValue(trans_values->green);
1068 4 : poDS->GetRasterBand(3)->SetNoDataValue(trans_values->blue);
1069 : }
1070 : }
1071 :
1072 : /* -------------------------------------------------------------------- */
1073 : /* Extract any text chunks as "metadata". */
1074 : /* -------------------------------------------------------------------- */
1075 78 : poDS->CollectMetadata();
1076 :
1077 : /* -------------------------------------------------------------------- */
1078 : /* More metadata. */
1079 : /* -------------------------------------------------------------------- */
1080 78 : if( poDS->nBands > 1 )
1081 : {
1082 50 : poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" );
1083 : }
1084 :
1085 : /* -------------------------------------------------------------------- */
1086 : /* Initialize any PAM information. */
1087 : /* -------------------------------------------------------------------- */
1088 78 : poDS->SetDescription( poOpenInfo->pszFilename );
1089 78 : poDS->TryLoadXML( poOpenInfo->papszSiblingFiles );
1090 :
1091 : /* -------------------------------------------------------------------- */
1092 : /* Open overviews. */
1093 : /* -------------------------------------------------------------------- */
1094 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename,
1095 78 : poOpenInfo->papszSiblingFiles );
1096 :
1097 78 : return poDS;
1098 : }
1099 :
1100 : /************************************************************************/
1101 : /* LoadWorldFile() */
1102 : /************************************************************************/
1103 :
1104 27 : void PNGDataset::LoadWorldFile()
1105 : {
1106 27 : if (bHasTriedLoadWorldFile)
1107 2 : return;
1108 25 : bHasTriedLoadWorldFile = TRUE;
1109 :
1110 25 : char* pszWldFilename = NULL;
1111 : bGeoTransformValid =
1112 25 : GDALReadWorldFile2( GetDescription(), NULL,
1113 : adfGeoTransform, oOvManager.GetSiblingFiles(),
1114 50 : &pszWldFilename);
1115 :
1116 25 : if( !bGeoTransformValid )
1117 : bGeoTransformValid =
1118 25 : GDALReadWorldFile2( GetDescription(), ".wld",
1119 : adfGeoTransform, oOvManager.GetSiblingFiles(),
1120 50 : &pszWldFilename);
1121 :
1122 25 : if (pszWldFilename)
1123 : {
1124 2 : osWldFilename = pszWldFilename;
1125 2 : CPLFree(pszWldFilename);
1126 : }
1127 : }
1128 :
1129 : /************************************************************************/
1130 : /* GetFileList() */
1131 : /************************************************************************/
1132 :
1133 7 : char **PNGDataset::GetFileList()
1134 :
1135 : {
1136 7 : char **papszFileList = GDALPamDataset::GetFileList();
1137 :
1138 7 : LoadWorldFile();
1139 :
1140 7 : if (osWldFilename.size() != 0 &&
1141 : CSLFindString(papszFileList, osWldFilename) == -1)
1142 : {
1143 0 : papszFileList = CSLAddString( papszFileList, osWldFilename );
1144 : }
1145 :
1146 7 : return papszFileList;
1147 : }
1148 :
1149 : /************************************************************************/
1150 : /* CreateCopy() */
1151 : /************************************************************************/
1152 :
1153 : GDALDataset *
1154 44 : PNGDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
1155 : int bStrict, char ** papszOptions,
1156 : GDALProgressFunc pfnProgress, void * pProgressData )
1157 :
1158 : {
1159 44 : int nBands = poSrcDS->GetRasterCount();
1160 44 : int nXSize = poSrcDS->GetRasterXSize();
1161 44 : int nYSize = poSrcDS->GetRasterYSize();
1162 :
1163 : /* -------------------------------------------------------------------- */
1164 : /* Some some rudimentary checks */
1165 : /* -------------------------------------------------------------------- */
1166 44 : if( nBands != 1 && nBands != 2 && nBands != 3 && nBands != 4 )
1167 : {
1168 : CPLError( CE_Failure, CPLE_NotSupported,
1169 : "PNG driver doesn't support %d bands. Must be 1 (grey),\n"
1170 : "2 (grey+alpha), 3 (rgb) or 4 (rgba) bands.\n",
1171 2 : nBands );
1172 :
1173 2 : return NULL;
1174 : }
1175 :
1176 42 : if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte
1177 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16 )
1178 : {
1179 : CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
1180 : "PNG driver doesn't support data type %s. "
1181 : "Only eight bit (Byte) and sixteen bit (UInt16) bands supported. %s\n",
1182 : GDALGetDataTypeName(
1183 : poSrcDS->GetRasterBand(1)->GetRasterDataType()),
1184 9 : (bStrict) ? "" : "Defaulting to Byte" );
1185 :
1186 9 : if (bStrict)
1187 9 : return NULL;
1188 : }
1189 :
1190 : /* -------------------------------------------------------------------- */
1191 : /* Setup some parameters. */
1192 : /* -------------------------------------------------------------------- */
1193 33 : int nColorType=0, nBitDepth;
1194 : GDALDataType eType;
1195 :
1196 33 : if( nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() == NULL )
1197 13 : nColorType = PNG_COLOR_TYPE_GRAY;
1198 20 : else if( nBands == 1 )
1199 1 : nColorType = PNG_COLOR_TYPE_PALETTE;
1200 19 : else if( nBands == 2 )
1201 2 : nColorType = PNG_COLOR_TYPE_GRAY_ALPHA;
1202 17 : else if( nBands == 3 )
1203 6 : nColorType = PNG_COLOR_TYPE_RGB;
1204 11 : else if( nBands == 4 )
1205 11 : nColorType = PNG_COLOR_TYPE_RGB_ALPHA;
1206 :
1207 33 : if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16 )
1208 : {
1209 28 : eType = GDT_Byte;
1210 28 : nBitDepth = 8;
1211 : }
1212 : else
1213 : {
1214 5 : eType = GDT_UInt16;
1215 5 : nBitDepth = 16;
1216 : }
1217 :
1218 : /* -------------------------------------------------------------------- */
1219 : /* Create the dataset. */
1220 : /* -------------------------------------------------------------------- */
1221 : VSILFILE *fpImage;
1222 :
1223 33 : fpImage = VSIFOpenL( pszFilename, "wb" );
1224 33 : if( fpImage == NULL )
1225 : {
1226 : CPLError( CE_Failure, CPLE_OpenFailed,
1227 : "Unable to create png file %s.\n",
1228 7 : pszFilename );
1229 7 : return NULL;
1230 : }
1231 :
1232 : /* -------------------------------------------------------------------- */
1233 : /* Initialize PNG access to the file. */
1234 : /* -------------------------------------------------------------------- */
1235 : png_structp hPNG;
1236 : png_infop psPNGInfo;
1237 :
1238 : jmp_buf sSetJmpContext;
1239 : hPNG = png_create_write_struct( PNG_LIBPNG_VER_STRING,
1240 26 : &sSetJmpContext, png_gdal_error, png_gdal_warning );
1241 26 : psPNGInfo = png_create_info_struct( hPNG );
1242 :
1243 26 : if( setjmp( sSetJmpContext ) != 0 )
1244 : {
1245 2 : VSIFCloseL( fpImage );
1246 2 : png_destroy_write_struct( &hPNG, &psPNGInfo );
1247 2 : return NULL;
1248 : }
1249 :
1250 26 : png_set_write_fn( hPNG, fpImage, png_vsi_write_data, png_vsi_flush );
1251 :
1252 : png_set_IHDR( hPNG, psPNGInfo, nXSize, nYSize,
1253 : nBitDepth, nColorType, PNG_INTERLACE_NONE,
1254 26 : PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE );
1255 :
1256 : /* -------------------------------------------------------------------- */
1257 : /* Do we want to control the compression level? */
1258 : /* -------------------------------------------------------------------- */
1259 26 : const char *pszLevel = CSLFetchNameValue( papszOptions, "ZLEVEL" );
1260 :
1261 26 : if( pszLevel )
1262 : {
1263 0 : int nLevel = atoi(pszLevel);
1264 0 : if( nLevel < 1 || nLevel > 9 )
1265 : {
1266 : CPLError( CE_Failure, CPLE_AppDefined,
1267 : "Illegal ZLEVEL value '%s', should be 1-9.",
1268 0 : pszLevel );
1269 0 : return NULL;
1270 : }
1271 :
1272 0 : png_set_compression_level( hPNG, nLevel );
1273 : }
1274 :
1275 : /* -------------------------------------------------------------------- */
1276 : /* Try to handle nodata values as a tRNS block (note for */
1277 : /* paletted images, we save the effect to apply as part of */
1278 : /* palette). */
1279 : /* -------------------------------------------------------------------- */
1280 : png_color_16 sTRNSColor;
1281 :
1282 : // Gray nodata.
1283 26 : if( nColorType == PNG_COLOR_TYPE_GRAY )
1284 : {
1285 9 : int bHaveNoData = FALSE;
1286 9 : double dfNoDataValue = -1;
1287 :
1288 9 : dfNoDataValue = poSrcDS->GetRasterBand(1)->GetNoDataValue( &bHaveNoData );
1289 :
1290 9 : if ( bHaveNoData && dfNoDataValue >= 0 && dfNoDataValue < 65536 )
1291 : {
1292 2 : sTRNSColor.gray = (png_uint_16) dfNoDataValue;
1293 2 : png_set_tRNS( hPNG, psPNGInfo, NULL, 0, &sTRNSColor );
1294 : }
1295 : }
1296 :
1297 : // RGB nodata.
1298 26 : if( nColorType == PNG_COLOR_TYPE_RGB )
1299 : {
1300 : // First try to use the NODATA_VALUES metadata item.
1301 5 : if ( poSrcDS->GetMetadataItem( "NODATA_VALUES" ) != NULL )
1302 : {
1303 : char **papszValues = CSLTokenizeString(
1304 1 : poSrcDS->GetMetadataItem( "NODATA_VALUES" ) );
1305 :
1306 1 : if( CSLCount(papszValues) >= 3 )
1307 : {
1308 1 : sTRNSColor.red = (png_uint_16) atoi(papszValues[0]);
1309 1 : sTRNSColor.green = (png_uint_16) atoi(papszValues[1]);
1310 1 : sTRNSColor.blue = (png_uint_16) atoi(papszValues[2]);
1311 1 : png_set_tRNS( hPNG, psPNGInfo, NULL, 0, &sTRNSColor );
1312 : }
1313 :
1314 1 : CSLDestroy( papszValues );
1315 : }
1316 : // Otherwise, get the nodata value from the bands.
1317 : else
1318 : {
1319 4 : int bHaveNoDataRed = FALSE;
1320 4 : int bHaveNoDataGreen = FALSE;
1321 4 : int bHaveNoDataBlue = FALSE;
1322 4 : double dfNoDataValueRed = -1;
1323 4 : double dfNoDataValueGreen = -1;
1324 4 : double dfNoDataValueBlue = -1;
1325 :
1326 4 : dfNoDataValueRed = poSrcDS->GetRasterBand(1)->GetNoDataValue( &bHaveNoDataRed );
1327 4 : dfNoDataValueGreen= poSrcDS->GetRasterBand(2)->GetNoDataValue( &bHaveNoDataGreen );
1328 4 : dfNoDataValueBlue = poSrcDS->GetRasterBand(3)->GetNoDataValue( &bHaveNoDataBlue );
1329 :
1330 4 : if ( ( bHaveNoDataRed && dfNoDataValueRed >= 0 && dfNoDataValueRed < 65536 ) &&
1331 : ( bHaveNoDataGreen && dfNoDataValueGreen >= 0 && dfNoDataValueGreen < 65536 ) &&
1332 : ( bHaveNoDataBlue && dfNoDataValueBlue >= 0 && dfNoDataValueBlue < 65536 ) )
1333 : {
1334 0 : sTRNSColor.red = (png_uint_16) dfNoDataValueRed;
1335 0 : sTRNSColor.green = (png_uint_16) dfNoDataValueGreen;
1336 0 : sTRNSColor.blue = (png_uint_16) dfNoDataValueBlue;
1337 0 : png_set_tRNS( hPNG, psPNGInfo, NULL, 0, &sTRNSColor );
1338 : }
1339 : }
1340 : }
1341 :
1342 : /* -------------------------------------------------------------------- */
1343 : /* Write palette if there is one. Technically, I think it is */
1344 : /* possible to write 16bit palettes for PNG, but we will omit */
1345 : /* this for now. */
1346 : /* -------------------------------------------------------------------- */
1347 26 : png_color *pasPNGColors = NULL;
1348 26 : unsigned char *pabyAlpha = NULL;
1349 :
1350 26 : if( nColorType == PNG_COLOR_TYPE_PALETTE )
1351 : {
1352 : GDALColorTable *poCT;
1353 : GDALColorEntry sEntry;
1354 1 : int iColor, bFoundTrans = FALSE;
1355 1 : int bHaveNoData = FALSE;
1356 1 : double dfNoDataValue = -1;
1357 :
1358 1 : dfNoDataValue = poSrcDS->GetRasterBand(1)->GetNoDataValue( &bHaveNoData );
1359 :
1360 1 : poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
1361 :
1362 : pasPNGColors = (png_color *) CPLMalloc(sizeof(png_color) *
1363 1 : poCT->GetColorEntryCount());
1364 :
1365 17 : for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ )
1366 : {
1367 16 : poCT->GetColorEntryAsRGB( iColor, &sEntry );
1368 16 : if( sEntry.c4 != 255 )
1369 1 : bFoundTrans = TRUE;
1370 :
1371 16 : pasPNGColors[iColor].red = (png_byte) sEntry.c1;
1372 16 : pasPNGColors[iColor].green = (png_byte) sEntry.c2;
1373 16 : pasPNGColors[iColor].blue = (png_byte) sEntry.c3;
1374 : }
1375 :
1376 : png_set_PLTE( hPNG, psPNGInfo, pasPNGColors,
1377 1 : poCT->GetColorEntryCount() );
1378 :
1379 : /* -------------------------------------------------------------------- */
1380 : /* If we have transparent elements in the palette we need to */
1381 : /* write a transparency block. */
1382 : /* -------------------------------------------------------------------- */
1383 1 : if( bFoundTrans || bHaveNoData )
1384 : {
1385 1 : pabyAlpha = (unsigned char *)CPLMalloc(poCT->GetColorEntryCount());
1386 :
1387 17 : for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ )
1388 : {
1389 16 : poCT->GetColorEntryAsRGB( iColor, &sEntry );
1390 16 : pabyAlpha[iColor] = (unsigned char) sEntry.c4;
1391 :
1392 16 : if( bHaveNoData && iColor == (int) dfNoDataValue )
1393 1 : pabyAlpha[iColor] = 0;
1394 : }
1395 :
1396 : png_set_tRNS( hPNG, psPNGInfo, pabyAlpha,
1397 1 : poCT->GetColorEntryCount(), NULL );
1398 : }
1399 : }
1400 :
1401 26 : png_write_info( hPNG, psPNGInfo );
1402 :
1403 : /* -------------------------------------------------------------------- */
1404 : /* Loop over image, copying image data. */
1405 : /* -------------------------------------------------------------------- */
1406 : GByte *pabyScanline;
1407 26 : CPLErr eErr = CE_None;
1408 26 : int nWordSize = nBitDepth/8;
1409 :
1410 26 : pabyScanline = (GByte *) CPLMalloc( nBands * nXSize * nWordSize );
1411 :
1412 2554 : for( int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++ )
1413 : {
1414 2528 : png_bytep row = pabyScanline;
1415 :
1416 10027 : for( int iBand = 0; iBand < nBands; iBand++ )
1417 : {
1418 7499 : GDALRasterBand * poBand = poSrcDS->GetRasterBand( iBand+1 );
1419 : eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1,
1420 : pabyScanline + iBand*nWordSize,
1421 : nXSize, 1, eType,
1422 : nBands * nWordSize,
1423 7499 : nBands * nXSize * nWordSize );
1424 : }
1425 :
1426 : #ifdef CPL_LSB
1427 2528 : if( nBitDepth == 16 )
1428 63 : GDALSwapWords( row, 2, nXSize * nBands, 2 );
1429 : #endif
1430 2528 : if( eErr == CE_None )
1431 2527 : png_write_rows( hPNG, &row, 1 );
1432 :
1433 2528 : if( eErr == CE_None
1434 : && !pfnProgress( (iLine+1) / (double) nYSize,
1435 : NULL, pProgressData ) )
1436 : {
1437 1 : eErr = CE_Failure;
1438 : CPLError( CE_Failure, CPLE_UserInterrupt,
1439 1 : "User terminated CreateCopy()" );
1440 : }
1441 : }
1442 :
1443 26 : CPLFree( pabyScanline );
1444 :
1445 26 : png_write_end( hPNG, psPNGInfo );
1446 24 : png_destroy_write_struct( &hPNG, &psPNGInfo );
1447 :
1448 24 : VSIFCloseL( fpImage );
1449 :
1450 24 : CPLFree( pabyAlpha );
1451 24 : CPLFree( pasPNGColors );
1452 :
1453 24 : if( eErr != CE_None )
1454 0 : return NULL;
1455 :
1456 : /* -------------------------------------------------------------------- */
1457 : /* Do we need a world file? */
1458 : /* -------------------------------------------------------------------- */
1459 24 : if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) )
1460 : {
1461 : double adfGeoTransform[6];
1462 :
1463 0 : if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None )
1464 0 : GDALWriteWorldFile( pszFilename, "wld", adfGeoTransform );
1465 : }
1466 :
1467 : /* -------------------------------------------------------------------- */
1468 : /* Re-open dataset, and copy any auxilary pam information. */
1469 : /* -------------------------------------------------------------------- */
1470 24 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
1471 :
1472 : /* If outputing to stdout, we can't reopen it, so we'll return */
1473 : /* a fake dataset to make the caller happy */
1474 24 : CPLPushErrorHandler(CPLQuietErrorHandler);
1475 24 : PNGDataset *poDS = (PNGDataset*) PNGDataset::Open( &oOpenInfo );
1476 24 : CPLPopErrorHandler();
1477 24 : if( poDS )
1478 : {
1479 23 : poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
1480 23 : return poDS;
1481 : }
1482 :
1483 1 : CPLErrorReset();
1484 :
1485 1 : PNGDataset* poPNG_DS = new PNGDataset();
1486 1 : poPNG_DS->nRasterXSize = nXSize;
1487 1 : poPNG_DS->nRasterYSize = nYSize;
1488 1 : poPNG_DS->nBitDepth = nBitDepth;
1489 2 : for(int i=0;i<nBands;i++)
1490 1 : poPNG_DS->SetBand( i+1, new PNGRasterBand( poPNG_DS, i+1) );
1491 1 : return poPNG_DS;
1492 : }
1493 :
1494 : /************************************************************************/
1495 : /* png_vsi_read_data() */
1496 : /* */
1497 : /* Read data callback through VSI. */
1498 : /************************************************************************/
1499 : static void
1500 889 : png_vsi_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
1501 :
1502 : {
1503 : png_size_t check;
1504 :
1505 : /* fread() returns 0 on error, so it is OK to store this in a png_size_t
1506 : * instead of an int, which is what fread() actually returns.
1507 : */
1508 : check = (png_size_t)VSIFReadL(data, (png_size_t)1, length,
1509 889 : (VSILFILE*)png_get_io_ptr(png_ptr));
1510 :
1511 889 : if (check != length)
1512 3 : png_error(png_ptr, "Read Error");
1513 886 : }
1514 :
1515 : /************************************************************************/
1516 : /* png_vsi_write_data() */
1517 : /************************************************************************/
1518 :
1519 : static void
1520 404 : png_vsi_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
1521 : {
1522 : png_uint_32 check;
1523 :
1524 404 : check = VSIFWriteL(data, 1, length, (VSILFILE*)png_get_io_ptr(png_ptr));
1525 :
1526 404 : if (check != length)
1527 0 : png_error(png_ptr, "Write Error");
1528 404 : }
1529 :
1530 : /************************************************************************/
1531 : /* png_vsi_flush() */
1532 : /************************************************************************/
1533 0 : static void png_vsi_flush(png_structp png_ptr)
1534 : {
1535 0 : VSIFFlushL( (VSILFILE*)png_get_io_ptr(png_ptr) );
1536 0 : }
1537 :
1538 : /************************************************************************/
1539 : /* png_gdal_error() */
1540 : /************************************************************************/
1541 :
1542 9 : static void png_gdal_error( png_structp png_ptr, const char *error_message )
1543 : {
1544 : CPLError( CE_Failure, CPLE_AppDefined,
1545 9 : "libpng: %s", error_message );
1546 :
1547 : // We have to use longjmp instead of a C++ exception because
1548 : // libpng is generally not built as C++ and so won't honour unwind
1549 : // semantics. Ugg.
1550 :
1551 9 : jmp_buf* psSetJmpContext = (jmp_buf*) png_get_error_ptr(png_ptr);
1552 9 : if (psSetJmpContext)
1553 : {
1554 9 : longjmp( *psSetJmpContext, 1 );
1555 : }
1556 0 : }
1557 :
1558 : /************************************************************************/
1559 : /* png_gdal_warning() */
1560 : /************************************************************************/
1561 :
1562 0 : static void png_gdal_warning( png_structp png_ptr, const char *error_message )
1563 : {
1564 : CPLError( CE_Warning, CPLE_AppDefined,
1565 0 : "libpng: %s", error_message );
1566 0 : }
1567 :
1568 : /************************************************************************/
1569 : /* GDALRegister_PNG() */
1570 : /************************************************************************/
1571 :
1572 610 : void GDALRegister_PNG()
1573 :
1574 : {
1575 : GDALDriver *poDriver;
1576 :
1577 610 : if( GDALGetDriverByName( "PNG" ) == NULL )
1578 : {
1579 588 : poDriver = new GDALDriver();
1580 :
1581 588 : poDriver->SetDescription( "PNG" );
1582 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
1583 588 : "Portable Network Graphics" );
1584 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
1585 588 : "frmt_various.html#PNG" );
1586 588 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "png" );
1587 588 : poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/png" );
1588 :
1589 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
1590 588 : "Byte UInt16" );
1591 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
1592 : "<CreationOptionList>\n"
1593 : " <Option name='WORLDFILE' type='boolean' description='Create world file'/>\n"
1594 : " <Option name='ZLEVEL' type='int' description='DEFLATE compression level 1-9' default='6'/>"
1595 588 : "</CreationOptionList>\n" );
1596 :
1597 588 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
1598 :
1599 588 : poDriver->pfnOpen = PNGDataset::Open;
1600 588 : poDriver->pfnCreateCopy = PNGDataset::CreateCopy;
1601 588 : poDriver->pfnIdentify = PNGDataset::Identify;
1602 : #ifdef SUPPORT_CREATE
1603 : poDriver->pfnCreate = PNGDataset::Create;
1604 : #endif
1605 :
1606 588 : GetGDALDriverManager()->RegisterDriver( poDriver );
1607 : }
1608 610 : }
1609 :
1610 : #ifdef SUPPORT_CREATE
1611 : /************************************************************************/
1612 : /* IWriteBlock() */
1613 : /************************************************************************/
1614 :
1615 : CPLErr PNGRasterBand::IWriteBlock(int x, int y, void* pvData)
1616 : {
1617 : // rcg, added to support Create().
1618 :
1619 : PNGDataset& ds = *(PNGDataset*)poDS;
1620 :
1621 :
1622 : // Write the block (or consolidate into multichannel block)
1623 : // and then write.
1624 :
1625 : const GDALDataType dt = this->GetRasterDataType();
1626 : const size_t wordsize = ds.m_nBitDepth / 8;
1627 : GDALCopyWords( pvData, dt, wordsize,
1628 : ds.m_pabyBuffer + (nBand-1) * wordsize,
1629 : dt, ds.nBands * wordsize,
1630 : nBlockXSize );
1631 :
1632 : // See if we got all the bands.
1633 : size_t i;
1634 : m_bBandProvided[nBand - 1] = TRUE;
1635 : for(i = 0; i < ds.nBands; i++)
1636 : {
1637 : if(!m_bBandProvided[i])
1638 : return CE_None;
1639 : }
1640 :
1641 : // We received all the bands, so
1642 : // reset band flags and write pixels out.
1643 : this->reset_band_provision_flags();
1644 :
1645 :
1646 : // If first block, write out file header.
1647 : if(x == 0 && y == 0)
1648 : {
1649 : CPLErr err = ds.write_png_header();
1650 : if(err != CE_None)
1651 : return err;
1652 : }
1653 :
1654 : #ifdef CPL_LSB
1655 : if( ds.m_nBitDepth == 16 )
1656 : GDALSwapWords( ds.m_pabyBuffer, 2, nBlockXSize * ds.nBands, 2 );
1657 : #endif
1658 : png_write_rows( ds.m_hPNG, &ds.m_pabyBuffer, 1 );
1659 :
1660 : return CE_None;
1661 : }
1662 :
1663 :
1664 : /************************************************************************/
1665 : /* SetGeoTransform() */
1666 : /************************************************************************/
1667 :
1668 : CPLErr PNGDataset::SetGeoTransform( double * padfTransform )
1669 : {
1670 : // rcg, added to support Create().
1671 :
1672 : CPLErr eErr = CE_None;
1673 :
1674 : memcpy( m_adfGeoTransform, padfTransform, sizeof(double) * 6 );
1675 :
1676 : if ( m_pszFilename )
1677 : {
1678 : if ( GDALWriteWorldFile( m_pszFilename, "wld", m_adfGeoTransform )
1679 : == FALSE )
1680 : {
1681 : CPLError( CE_Failure, CPLE_FileIO, "Can't write world file." );
1682 : eErr = CE_Failure;
1683 : }
1684 : }
1685 :
1686 : return eErr;
1687 : }
1688 :
1689 :
1690 : /************************************************************************/
1691 : /* SetColorTable() */
1692 : /************************************************************************/
1693 :
1694 : CPLErr PNGRasterBand::SetColorTable(GDALColorTable* poCT)
1695 : {
1696 : if( poCT == NULL )
1697 : return CE_Failure;
1698 :
1699 : // rcg, added to support Create().
1700 : // We get called even for grayscale files, since some
1701 : // formats need a palette even then. PNG doesn't, so
1702 : // if a gray palette is given, just ignore it.
1703 :
1704 : GDALColorEntry sEntry;
1705 : for( size_t i = 0; i < poCT->GetColorEntryCount(); i++ )
1706 : {
1707 : poCT->GetColorEntryAsRGB( i, &sEntry );
1708 : if( sEntry.c1 != sEntry.c2 || sEntry.c1 != sEntry.c3)
1709 : {
1710 : CPLErr err = GDALPamRasterBand::SetColorTable(poCT);
1711 : if(err != CE_None)
1712 : return err;
1713 :
1714 : PNGDataset& ds = *(PNGDataset*)poDS;
1715 : ds.m_nColorType = PNG_COLOR_TYPE_PALETTE;
1716 : break;
1717 : // band::IWriteBlock will emit color table as part of
1718 : // header preceding first block write.
1719 : }
1720 : }
1721 :
1722 : return CE_None;
1723 : }
1724 :
1725 :
1726 : /************************************************************************/
1727 : /* PNGDataset::write_png_header() */
1728 : /************************************************************************/
1729 :
1730 : CPLErr PNGDataset::write_png_header()
1731 : {
1732 : /* -------------------------------------------------------------------- */
1733 : /* Initialize PNG access to the file. */
1734 : /* -------------------------------------------------------------------- */
1735 :
1736 : m_hPNG = png_create_write_struct(
1737 : PNG_LIBPNG_VER_STRING, NULL,
1738 : png_gdal_error, png_gdal_warning );
1739 :
1740 : m_psPNGInfo = png_create_info_struct( m_hPNG );
1741 :
1742 : png_set_write_fn( m_hPNG, m_fpImage, png_vsi_write_data, png_vsi_flush );
1743 :
1744 : png_set_IHDR( m_hPNG, m_psPNGInfo, nRasterXSize, nRasterYSize,
1745 : m_nBitDepth, m_nColorType, PNG_INTERLACE_NONE,
1746 : PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT );
1747 :
1748 : png_set_compression_level(m_hPNG, Z_BEST_COMPRESSION);
1749 :
1750 : //png_set_swap_alpha(m_hPNG); // Use RGBA order, not ARGB.
1751 :
1752 : /* -------------------------------------------------------------------- */
1753 : /* Try to handle nodata values as a tRNS block (note for */
1754 : /* paletted images, we save the effect to apply as part of */
1755 : /* palette). */
1756 : /* -------------------------------------------------------------------- */
1757 : //m_bHaveNoData = FALSE;
1758 : //m_dfNoDataValue = -1;
1759 : png_color_16 sTRNSColor;
1760 :
1761 :
1762 : int bHaveNoData = FALSE;
1763 : double dfNoDataValue = -1;
1764 :
1765 : if( m_nColorType == PNG_COLOR_TYPE_GRAY )
1766 : {
1767 : dfNoDataValue = this->GetRasterBand(1)->GetNoDataValue( &bHaveNoData );
1768 :
1769 : if ( bHaveNoData && dfNoDataValue >= 0 && dfNoDataValue < 65536 )
1770 : {
1771 : sTRNSColor.gray = (png_uint_16) dfNoDataValue;
1772 : png_set_tRNS( m_hPNG, m_psPNGInfo, NULL, 0, &sTRNSColor );
1773 : }
1774 : }
1775 :
1776 : // RGB nodata.
1777 : if( nColorType == PNG_COLOR_TYPE_RGB )
1778 : {
1779 : // First try to use the NODATA_VALUES metadata item.
1780 : if ( this->GetMetadataItem( "NODATA_VALUES" ) != NULL )
1781 : {
1782 : char **papszValues = CSLTokenizeString(
1783 : this->GetMetadataItem( "NODATA_VALUES" ) );
1784 :
1785 : if( CSLCount(papszValues) >= 3 )
1786 : {
1787 : sTRNSColor.red = (png_uint_16) atoi(papszValues[0]);
1788 : sTRNSColor.green = (png_uint_16) atoi(papszValues[1]);
1789 : sTRNSColor.blue = (png_uint_16) atoi(papszValues[2]);
1790 : png_set_tRNS( m_hPNG, m_psPNGInfo, NULL, 0, &sTRNSColor );
1791 : }
1792 :
1793 : CSLDestroy( papszValues );
1794 : }
1795 : // Otherwise, get the nodata value from the bands.
1796 : else
1797 : {
1798 : int bHaveNoDataRed = FALSE;
1799 : int bHaveNoDataGreen = FALSE;
1800 : int bHaveNoDataBlue = FALSE;
1801 : double dfNoDataValueRed = -1;
1802 : double dfNoDataValueGreen = -1;
1803 : double dfNoDataValueBlue = -1;
1804 :
1805 : dfNoDataValueRed = this->GetRasterBand(1)->GetNoDataValue( &bHaveNoDataRed );
1806 : dfNoDataValueGreen= this->GetRasterBand(2)->GetNoDataValue( &bHaveNoDataGreen );
1807 : dfNoDataValueBlue = this->GetRasterBand(3)->GetNoDataValue( &bHaveNoDataBlue );
1808 :
1809 : if ( ( bHaveNoDataRed && dfNoDataValueRed >= 0 && dfNoDataValueRed < 65536 ) &&
1810 : ( bHaveNoDataGreen && dfNoDataValueGreen >= 0 && dfNoDataValueGreen < 65536 ) &&
1811 : ( bHaveNoDataBlue && dfNoDataValueBlue >= 0 && dfNoDataValueBlue < 65536 ) )
1812 : {
1813 : sTRNSColor.red = (png_uint_16) dfNoDataValueRed;
1814 : sTRNSColor.green = (png_uint_16) dfNoDataValueGreen;
1815 : sTRNSColor.blue = (png_uint_16) dfNoDataValueBlue;
1816 : png_set_tRNS( m_hPNG, m_psPNGInfo, NULL, 0, &sTRNSColor );
1817 : }
1818 : }
1819 : }
1820 :
1821 : /* -------------------------------------------------------------------- */
1822 : /* Write palette if there is one. Technically, I think it is */
1823 : /* possible to write 16bit palettes for PNG, but we will omit */
1824 : /* this for now. */
1825 : /* -------------------------------------------------------------------- */
1826 :
1827 : if( nColorType == PNG_COLOR_TYPE_PALETTE )
1828 : {
1829 : GDALColorTable *poCT;
1830 : GDALColorEntry sEntry;
1831 : int iColor, bFoundTrans = FALSE;
1832 : int bHaveNoData = FALSE;
1833 : double dfNoDataValue = -1;
1834 :
1835 : dfNoDataValue = this->GetRasterBand(1)->GetNoDataValue( &bHaveNoData );
1836 :
1837 : poCT = this->GetRasterBand(1)->GetColorTable();
1838 :
1839 : m_pasPNGColors = (png_color *) CPLMalloc(sizeof(png_color) *
1840 : poCT->GetColorEntryCount());
1841 :
1842 : for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ )
1843 : {
1844 : poCT->GetColorEntryAsRGB( iColor, &sEntry );
1845 : if( sEntry.c4 != 255 )
1846 : bFoundTrans = TRUE;
1847 :
1848 : m_pasPNGColors[iColor].red = (png_byte) sEntry.c1;
1849 : m_pasPNGColors[iColor].green = (png_byte) sEntry.c2;
1850 : m_pasPNGColors[iColor].blue = (png_byte) sEntry.c3;
1851 : }
1852 :
1853 : png_set_PLTE( m_hPNG, m_psPNGInfo, m_pasPNGColors,
1854 : poCT->GetColorEntryCount() );
1855 :
1856 : /* -------------------------------------------------------------------- */
1857 : /* If we have transparent elements in the palette we need to */
1858 : /* write a transparency block. */
1859 : /* -------------------------------------------------------------------- */
1860 : if( bFoundTrans || bHaveNoData )
1861 : {
1862 : m_pabyAlpha = (unsigned char *)CPLMalloc(poCT->GetColorEntryCount());
1863 :
1864 : for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ )
1865 : {
1866 : poCT->GetColorEntryAsRGB( iColor, &sEntry );
1867 : m_pabyAlpha[iColor] = (unsigned char) sEntry.c4;
1868 :
1869 : if( bHaveNoData && iColor == (int) dfNoDataValue )
1870 : m_pabyAlpha[iColor] = 0;
1871 : }
1872 :
1873 : png_set_tRNS( m_hPNG, m_psPNGInfo, m_pabyAlpha,
1874 : poCT->GetColorEntryCount(), NULL );
1875 : }
1876 : }
1877 :
1878 : png_write_info( m_hPNG, m_psPNGInfo );
1879 : return CE_None;
1880 : }
1881 :
1882 :
1883 : /************************************************************************/
1884 : /* Create() */
1885 : /************************************************************************/
1886 :
1887 : // static
1888 : GDALDataset *PNGDataset::Create
1889 : (
1890 : const char* pszFilename,
1891 : int nXSize, int nYSize,
1892 : int nBands,
1893 : GDALDataType eType,
1894 : char **papszOptions
1895 : )
1896 : {
1897 : if( eType != GDT_Byte && eType != GDT_UInt16)
1898 : {
1899 : CPLError( CE_Failure, CPLE_AppDefined,
1900 : "Attempt to create PNG dataset with an illegal\n"
1901 : "data type (%s), only Byte and UInt16 supported by the format.\n",
1902 : GDALGetDataTypeName(eType) );
1903 :
1904 : return NULL;
1905 : }
1906 :
1907 : if( nBands < 1 || nBands > 4 )
1908 : {
1909 : CPLError( CE_Failure, CPLE_NotSupported,
1910 : "PNG driver doesn't support %d bands. "
1911 : "Must be 1 (gray/indexed color),\n"
1912 : "2 (gray+alpha), 3 (rgb) or 4 (rgba) bands.\n",
1913 : nBands );
1914 :
1915 : return NULL;
1916 : }
1917 :
1918 :
1919 : // Bands are:
1920 : // 1: grayscale or indexed color
1921 : // 2: gray plus alpha
1922 : // 3: rgb
1923 : // 4: rgb plus alpha
1924 :
1925 : if(nXSize < 1 || nYSize < 1)
1926 : {
1927 : CPLError( CE_Failure, CPLE_NotSupported,
1928 : "Specified pixel dimensions (% d x %d) are bad.\n",
1929 : nXSize, nYSize );
1930 : }
1931 :
1932 : /* -------------------------------------------------------------------- */
1933 : /* Setup some parameters. */
1934 : /* -------------------------------------------------------------------- */
1935 :
1936 : PNGDataset* poDS = new PNGDataset();
1937 :
1938 : poDS->nRasterXSize = nXSize;
1939 : poDS->nRasterYSize = nYSize;
1940 : poDS->eAccess = GA_Update;
1941 : poDS->nBands = nBands;
1942 :
1943 :
1944 : switch(nBands)
1945 : {
1946 : case 1:
1947 : poDS->m_nColorType = PNG_COLOR_TYPE_GRAY;
1948 : break;// if a non-gray palette is set, we'll change this.
1949 :
1950 : case 2:
1951 : poDS->m_nColorType = PNG_COLOR_TYPE_GRAY_ALPHA;
1952 : break;
1953 :
1954 : case 3:
1955 : poDS->m_nColorType = PNG_COLOR_TYPE_RGB;
1956 : break;
1957 :
1958 : case 4:
1959 : poDS->m_nColorType = PNG_COLOR_TYPE_RGB_ALPHA;
1960 : break;
1961 : }
1962 :
1963 : poDS->m_nBitDepth = (eType == GDT_Byte ? 8 : 16);
1964 :
1965 : poDS->m_pabyBuffer = (GByte *) CPLMalloc(
1966 : nBands * nXSize * poDS->m_nBitDepth / 8 );
1967 :
1968 : /* -------------------------------------------------------------------- */
1969 : /* Create band information objects. */
1970 : /* -------------------------------------------------------------------- */
1971 : int iBand;
1972 :
1973 : for( iBand = 1; iBand <= poDS->nBands; iBand++ )
1974 : poDS->SetBand( iBand, new PNGRasterBand( poDS, iBand ) );
1975 :
1976 : /* -------------------------------------------------------------------- */
1977 : /* Do we need a world file? */
1978 : /* -------------------------------------------------------------------- */
1979 : if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) )
1980 : poDS->m_bGeoTransformValid = TRUE;
1981 :
1982 : /* -------------------------------------------------------------------- */
1983 : /* Create the file. */
1984 : /* -------------------------------------------------------------------- */
1985 :
1986 : poDS->m_fpImage = VSIFOpenL( pszFilename, "wb" );
1987 : if( poDS->m_fpImage == NULL )
1988 : {
1989 : CPLError( CE_Failure, CPLE_OpenFailed,
1990 : "Unable to create PNG file %s.\n",
1991 : pszFilename );
1992 : delete poDS;
1993 : return NULL;
1994 : }
1995 :
1996 : poDS->m_pszFilename = CPLStrdup(pszFilename);
1997 :
1998 : return poDS;
1999 : }
2000 :
2001 : #endif
|