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