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 316 : {
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 316 : PNGRasterBand::PNGRasterBand( PNGDataset *poDS, int nBand )
220 :
221 : {
222 316 : this->poDS = poDS;
223 316 : this->nBand = nBand;
224 :
225 316 : if( poDS->nBitDepth == 16 )
226 118 : eDataType = GDT_UInt16;
227 : else
228 198 : eDataType = GDT_Byte;
229 :
230 316 : nBlockXSize = poDS->nRasterXSize;;
231 316 : nBlockYSize = 1;
232 :
233 316 : bHaveNoData = FALSE;
234 316 : dfNoDataValue = -1;
235 :
236 : #ifdef SUPPORT_CREATE
237 : this->reset_band_provision_flags();
238 : #endif
239 316 : }
240 :
241 : /************************************************************************/
242 : /* IReadBlock() */
243 : /************************************************************************/
244 :
245 24286 : CPLErr PNGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
246 : void * pImage )
247 :
248 : {
249 24286 : PNGDataset *poGDS = (PNGDataset *) poDS;
250 : CPLErr eErr;
251 : GByte *pabyScanline;
252 24286 : int i, nPixelSize, nPixelOffset, nXSize = GetXSize();
253 :
254 24286 : CPLAssert( nBlockXOff == 0 );
255 :
256 24286 : if( poGDS->nBitDepth == 16 )
257 2522 : nPixelSize = 2;
258 : else
259 21764 : nPixelSize = 1;
260 :
261 :
262 24286 : if (poGDS->fpImage == NULL)
263 : {
264 40 : memset( pImage, 0, nPixelSize * nXSize );
265 40 : return CE_None;
266 : }
267 :
268 24246 : nPixelOffset = poGDS->nBands * nPixelSize;
269 :
270 : /* -------------------------------------------------------------------- */
271 : /* Load the desired scanline into the working buffer. */
272 : /* -------------------------------------------------------------------- */
273 24246 : eErr = poGDS->LoadScanline( nBlockYOff );
274 24246 : if( eErr != CE_None )
275 10 : return eErr;
276 :
277 : pabyScanline = poGDS->pabyBuffer
278 : + (nBlockYOff - poGDS->nBufferStartLine) * nPixelOffset * nXSize
279 24236 : + nPixelSize * (nBand - 1);
280 :
281 : /* -------------------------------------------------------------------- */
282 : /* Transfer between the working buffer the the callers buffer. */
283 : /* -------------------------------------------------------------------- */
284 24236 : if( nPixelSize == nPixelOffset )
285 5084 : memcpy( pImage, pabyScanline, nPixelSize * nXSize );
286 19152 : else if( nPixelSize == 1 )
287 : {
288 4339460 : for( i = 0; i < nXSize; i++ )
289 4321620 : ((GByte *) pImage)[i] = pabyScanline[i*nPixelOffset];
290 : }
291 : else
292 : {
293 1312 : CPLAssert( nPixelSize == 2 );
294 29856 : for( i = 0; i < nXSize; i++ )
295 : {
296 28544 : ((GUInt16 *) pImage)[i] =
297 28544 : *((GUInt16 *) (pabyScanline+i*nPixelOffset));
298 : }
299 : }
300 :
301 : /* -------------------------------------------------------------------- */
302 : /* Forceably load the other bands associated with this scanline. */
303 : /* -------------------------------------------------------------------- */
304 : int iBand;
305 79648 : for(iBand = 1; iBand < poGDS->GetRasterCount(); iBand++)
306 : {
307 : GDALRasterBlock *poBlock;
308 :
309 : poBlock =
310 55412 : poGDS->GetRasterBand(iBand+1)->GetLockedBlockRef(nBlockXOff,nBlockYOff);
311 55412 : if( poBlock != NULL )
312 55412 : poBlock->DropLock();
313 : }
314 :
315 24236 : return CE_None;
316 : }
317 :
318 : /************************************************************************/
319 : /* GetColorInterpretation() */
320 : /************************************************************************/
321 :
322 104 : GDALColorInterp PNGRasterBand::GetColorInterpretation()
323 :
324 : {
325 104 : PNGDataset *poGDS = (PNGDataset *) poDS;
326 :
327 104 : if( poGDS->nColorType == PNG_COLOR_TYPE_GRAY )
328 10 : return GCI_GrayIndex;
329 :
330 94 : else if( poGDS->nColorType == PNG_COLOR_TYPE_GRAY_ALPHA )
331 : {
332 2 : if( nBand == 1 )
333 2 : return GCI_GrayIndex;
334 : else
335 0 : return GCI_AlphaBand;
336 : }
337 :
338 92 : else if( poGDS->nColorType == PNG_COLOR_TYPE_PALETTE )
339 6 : return GCI_PaletteIndex;
340 :
341 86 : else if( poGDS->nColorType == PNG_COLOR_TYPE_RGB
342 : || poGDS->nColorType == PNG_COLOR_TYPE_RGB_ALPHA )
343 : {
344 86 : if( nBand == 1 )
345 22 : return GCI_RedBand;
346 64 : else if( nBand == 2 )
347 22 : return GCI_GreenBand;
348 42 : else if( nBand == 3 )
349 22 : return GCI_BlueBand;
350 : else
351 20 : return GCI_AlphaBand;
352 : }
353 : else
354 0 : return GCI_GrayIndex;
355 : }
356 :
357 : /************************************************************************/
358 : /* GetColorTable() */
359 : /************************************************************************/
360 :
361 36 : GDALColorTable *PNGRasterBand::GetColorTable()
362 :
363 : {
364 36 : PNGDataset *poGDS = (PNGDataset *) poDS;
365 :
366 36 : if( nBand == 1 )
367 20 : return poGDS->poColorTable;
368 : else
369 16 : return NULL;
370 : }
371 :
372 : /************************************************************************/
373 : /* SetNoDataValue() */
374 : /************************************************************************/
375 :
376 44 : CPLErr PNGRasterBand::SetNoDataValue( double dfNewValue )
377 :
378 : {
379 44 : bHaveNoData = TRUE;
380 44 : dfNoDataValue = dfNewValue;
381 :
382 44 : return CE_None;
383 : }
384 :
385 : /************************************************************************/
386 : /* GetNoDataValue() */
387 : /************************************************************************/
388 :
389 106 : double PNGRasterBand::GetNoDataValue( int *pbSuccess )
390 :
391 : {
392 106 : if( bHaveNoData )
393 : {
394 34 : if( pbSuccess != NULL )
395 32 : *pbSuccess = bHaveNoData;
396 34 : return dfNoDataValue;
397 : }
398 : else
399 : {
400 72 : return GDALPamRasterBand::GetNoDataValue( pbSuccess );
401 : }
402 : }
403 :
404 : /************************************************************************/
405 : /* ==================================================================== */
406 : /* PNGDataset */
407 : /* ==================================================================== */
408 : /************************************************************************/
409 :
410 :
411 : /************************************************************************/
412 : /* PNGDataset() */
413 : /************************************************************************/
414 :
415 128 : PNGDataset::PNGDataset()
416 :
417 : {
418 128 : fpImage = NULL;
419 128 : hPNG = NULL;
420 128 : psPNGInfo = NULL;
421 128 : pabyBuffer = NULL;
422 128 : nBufferStartLine = 0;
423 128 : nBufferLines = 0;
424 128 : nLastLineRead = -1;
425 128 : poColorTable = NULL;
426 128 : nBitDepth = 8;
427 :
428 128 : bGeoTransformValid = FALSE;
429 128 : adfGeoTransform[0] = 0.0;
430 128 : adfGeoTransform[1] = 1.0;
431 128 : adfGeoTransform[2] = 0.0;
432 128 : adfGeoTransform[3] = 0.0;
433 128 : adfGeoTransform[4] = 0.0;
434 128 : adfGeoTransform[5] = 1.0;
435 :
436 128 : bHasTriedLoadWorldFile = FALSE;
437 128 : bHasReadXMPMetadata = FALSE;
438 128 : }
439 :
440 : /************************************************************************/
441 : /* ~PNGDataset() */
442 : /************************************************************************/
443 :
444 128 : PNGDataset::~PNGDataset()
445 :
446 : {
447 128 : FlushCache();
448 :
449 128 : if( hPNG != NULL )
450 126 : png_destroy_read_struct( &hPNG, &psPNGInfo, NULL );
451 :
452 128 : if( fpImage )
453 126 : VSIFCloseL( fpImage );
454 :
455 128 : if( poColorTable != NULL )
456 12 : delete poColorTable;
457 128 : }
458 :
459 : /************************************************************************/
460 : /* GetGeoTransform() */
461 : /************************************************************************/
462 :
463 40 : CPLErr PNGDataset::GetGeoTransform( double * padfTransform )
464 :
465 : {
466 40 : LoadWorldFile();
467 :
468 40 : if( bGeoTransformValid )
469 : {
470 6 : memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
471 6 : return CE_None;
472 : }
473 : else
474 34 : 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 128 : void PNGDataset::FlushCache()
485 :
486 : {
487 128 : GDALPamDataset::FlushCache();
488 :
489 128 : if( pabyBuffer != NULL )
490 : {
491 74 : CPLFree( pabyBuffer );
492 74 : pabyBuffer = NULL;
493 74 : nBufferStartLine = 0;
494 74 : nBufferLines = 0;
495 : }
496 128 : }
497 :
498 : /************************************************************************/
499 : /* Restart() */
500 : /* */
501 : /* Restart reading from the beginning of the file. */
502 : /************************************************************************/
503 :
504 2 : void PNGDataset::Restart()
505 :
506 : {
507 2 : png_destroy_read_struct( &hPNG, &psPNGInfo, NULL );
508 :
509 2 : hPNG = png_create_read_struct( PNG_LIBPNG_VER_STRING, this, NULL, NULL );
510 :
511 2 : png_set_error_fn( hPNG, &sSetJmpContext, png_gdal_error, png_gdal_warning );
512 2 : if( setjmp( sSetJmpContext ) != 0 )
513 0 : return;
514 :
515 2 : psPNGInfo = png_create_info_struct( hPNG );
516 :
517 2 : VSIFSeekL( fpImage, 0, SEEK_SET );
518 2 : png_set_read_fn( hPNG, fpImage, png_vsi_read_data );
519 2 : png_read_info( hPNG, psPNGInfo );
520 :
521 2 : if( nBitDepth < 8 )
522 0 : png_set_packing( hPNG );
523 :
524 2 : nLastLineRead = -1;
525 : }
526 :
527 : /************************************************************************/
528 : /* LoadInterlacedChunk() */
529 : /************************************************************************/
530 :
531 4 : CPLErr PNGDataset::LoadInterlacedChunk( int iLine )
532 :
533 : {
534 : int nPixelOffset;
535 :
536 4 : if( nBitDepth == 16 )
537 0 : nPixelOffset = 2 * GetRasterCount();
538 : else
539 4 : 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 4 : MAX(1,MAX_PNG_CHUNK_BYTES / (nPixelOffset * GetRasterXSize()));
548 : png_bytep *png_rows;
549 :
550 4 : if( nMaxChunkLines > GetRasterYSize() )
551 4 : nMaxChunkLines = GetRasterYSize();
552 :
553 : /* -------------------------------------------------------------------- */
554 : /* Allocate chunk buffer, if we don't already have it from a */
555 : /* previous request. */
556 : /* -------------------------------------------------------------------- */
557 4 : nBufferLines = nMaxChunkLines;
558 4 : if( nMaxChunkLines + iLine > GetRasterYSize() )
559 0 : nBufferStartLine = GetRasterYSize() - nMaxChunkLines;
560 : else
561 4 : nBufferStartLine = iLine;
562 :
563 4 : if( pabyBuffer == NULL )
564 : {
565 : pabyBuffer = (GByte *)
566 4 : VSIMalloc(nPixelOffset*GetRasterXSize()*nMaxChunkLines);
567 :
568 4 : 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 4 : 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 4 : png_bytep dummy_row = (png_bytep)CPLMalloc(nPixelOffset*GetRasterXSize());
603 4 : png_rows = (png_bytep*)CPLMalloc(sizeof(png_bytep) * GetRasterYSize());
604 :
605 604 : for( i = 0; i < GetRasterYSize(); i++ )
606 : {
607 1200 : if( i >= nBufferStartLine && i < nBufferStartLine + nBufferLines )
608 600 : png_rows[i] = pabyBuffer
609 600 : + (i-nBufferStartLine) * nPixelOffset * GetRasterXSize();
610 : else
611 0 : png_rows[i] = dummy_row;
612 : }
613 :
614 4 : png_read_image( hPNG, png_rows );
615 :
616 4 : CPLFree( png_rows );
617 4 : CPLFree( dummy_row );
618 :
619 4 : nLastLineRead = nBufferStartLine + nBufferLines - 1;
620 :
621 4 : return CE_None;
622 : }
623 :
624 : /************************************************************************/
625 : /* LoadScanline() */
626 : /************************************************************************/
627 :
628 24246 : CPLErr PNGDataset::LoadScanline( int nLine )
629 :
630 : {
631 : int nPixelOffset;
632 :
633 24246 : CPLAssert( nLine >= 0 && nLine < GetRasterYSize() );
634 :
635 24246 : if( nLine >= nBufferStartLine && nLine < nBufferStartLine + nBufferLines)
636 14528 : return CE_None;
637 :
638 9718 : if( nBitDepth == 16 )
639 1674 : nPixelOffset = 2 * GetRasterCount();
640 : else
641 8044 : nPixelOffset = 1 * GetRasterCount();
642 :
643 9718 : if( setjmp( sSetJmpContext ) != 0 )
644 10 : 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 9718 : if( bInterlaced )
651 4 : return LoadInterlacedChunk( nLine );
652 :
653 : /* -------------------------------------------------------------------- */
654 : /* Ensure we have space allocated for one scanline */
655 : /* -------------------------------------------------------------------- */
656 9714 : if( pabyBuffer == NULL )
657 70 : 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 9714 : if( nLine <= nLastLineRead )
664 : {
665 2 : Restart();
666 2 : 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 9714 : row = pabyBuffer;
676 29132 : while( nLine > nLastLineRead )
677 : {
678 9714 : png_read_rows( hPNG, &row, NULL, 1 );
679 9704 : nLastLineRead++;
680 : }
681 :
682 9704 : nBufferStartLine = nLine;
683 9704 : 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 9704 : if( nBitDepth == 16 )
691 1664 : GDALSwapWords( row, 2, GetRasterXSize() * GetRasterCount(), 2 );
692 : #endif
693 :
694 9704 : 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 126 : void PNGDataset::CollectMetadata()
709 :
710 : {
711 : int nTextCount;
712 : png_textp text_ptr;
713 :
714 126 : 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 126 : if( png_get_text( hPNG, psPNGInfo, &text_ptr, &nTextCount ) == 0 )
725 126 : 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 126 : void PNGDataset::CollectXMPMetadata()
749 :
750 : {
751 126 : if (fpImage == NULL || bHasReadXMPMetadata)
752 0 : return;
753 :
754 : /* Save current position to avoid disturbing PNG stream decoding */
755 126 : vsi_l_offset nCurOffset = VSIFTellL(fpImage);
756 :
757 126 : vsi_l_offset nOffset = 8;
758 126 : VSIFSeekL( fpImage, nOffset, SEEK_SET );
759 :
760 : /* Loop over chunks */
761 396 : while(TRUE)
762 : {
763 : int nLength;
764 : char pszChunkType[5];
765 : int nCRC;
766 :
767 522 : if (VSIFReadL( &nLength, 4, 1, fpImage ) != 1)
768 0 : break;
769 522 : nOffset += 4;
770 522 : CPL_MSBPTR32(&nLength);
771 522 : if (nLength <= 0)
772 122 : break;
773 400 : if (VSIFReadL( pszChunkType, 4, 1, fpImage ) != 1)
774 0 : break;
775 400 : nOffset += 4;
776 400 : pszChunkType[4] = 0;
777 :
778 400 : if (strcmp(pszChunkType, "iTXt") == 0 && nLength > 22)
779 : {
780 2 : char* pszContent = (char*)VSIMalloc(nLength + 1);
781 2 : if (pszContent == NULL)
782 0 : break;
783 2 : if (VSIFReadL( pszContent, nLength, 1, fpImage) != 1)
784 : {
785 0 : VSIFree(pszContent);
786 0 : break;
787 : }
788 2 : nOffset += nLength;
789 2 : pszContent[nLength] = '\0';
790 2 : 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 2 : int nOldPamFlags = nPamFlags;
794 :
795 : char *apszMDList[2];
796 2 : apszMDList[0] = pszContent + 22;
797 2 : apszMDList[1] = NULL;
798 2 : SetMetadata(apszMDList, "xml:XMP");
799 :
800 2 : nPamFlags = nOldPamFlags;
801 :
802 2 : VSIFree(pszContent);
803 :
804 2 : break;
805 : }
806 : else
807 : {
808 0 : VSIFree(pszContent);
809 : }
810 : }
811 : else
812 : {
813 398 : nOffset += nLength;
814 398 : VSIFSeekL( fpImage, nOffset, SEEK_SET );
815 : }
816 :
817 398 : nOffset += 4;
818 398 : if (VSIFReadL( &nCRC, 4, 1, fpImage ) != 1)
819 2 : break;
820 : }
821 :
822 126 : VSIFSeekL( fpImage, nCurOffset, SEEK_SET );
823 :
824 126 : bHasReadXMPMetadata = TRUE;
825 : }
826 :
827 : /************************************************************************/
828 : /* GetMetadata() */
829 : /************************************************************************/
830 :
831 40 : char **PNGDataset::GetMetadata( const char * pszDomain )
832 : {
833 40 : if (fpImage == NULL)
834 0 : return NULL;
835 40 : if (eAccess == GA_ReadOnly && !bHasReadXMPMetadata &&
836 : (pszDomain != NULL && EQUAL(pszDomain, "xml:XMP")))
837 0 : CollectXMPMetadata();
838 40 : return GDALPamDataset::GetMetadata(pszDomain);
839 : }
840 :
841 : /************************************************************************/
842 : /* GetMetadataItem() */
843 : /************************************************************************/
844 :
845 118 : const char *PNGDataset::GetMetadataItem( const char * pszName,
846 : const char * pszDomain )
847 : {
848 118 : if (fpImage == NULL)
849 0 : return NULL;
850 118 : if (eAccess == GA_ReadOnly && !bHasReadXMPMetadata &&
851 : (pszDomain != NULL && EQUAL(pszDomain, "xml:XMP")))
852 0 : CollectXMPMetadata();
853 118 : return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
854 : }
855 :
856 : /************************************************************************/
857 : /* Identify() */
858 : /************************************************************************/
859 :
860 26576 : int PNGDataset::Identify( GDALOpenInfo * poOpenInfo )
861 :
862 : {
863 26576 : if( poOpenInfo->nHeaderBytes < 4 )
864 22468 : return FALSE;
865 :
866 4108 : if( png_sig_cmp(poOpenInfo->pabyHeader, (png_size_t)0,
867 : poOpenInfo->nHeaderBytes) != 0 )
868 3982 : return FALSE;
869 :
870 126 : return TRUE;
871 : }
872 :
873 : /************************************************************************/
874 : /* Open() */
875 : /************************************************************************/
876 :
877 7552 : GDALDataset *PNGDataset::Open( GDALOpenInfo * poOpenInfo )
878 :
879 : {
880 7552 : if( !Identify( poOpenInfo ) )
881 7426 : return NULL;
882 :
883 126 : 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 126 : VSILFILE *fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
895 126 : 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 126 : poDS = new PNGDataset();
909 :
910 126 : poDS->fpImage = fp;
911 126 : poDS->eAccess = poOpenInfo->eAccess;
912 :
913 : poDS->hPNG = png_create_read_struct( PNG_LIBPNG_VER_STRING, poDS,
914 126 : NULL, NULL );
915 126 : 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 126 : poDS->psPNGInfo = png_create_info_struct( poDS->hPNG );
933 :
934 : /* -------------------------------------------------------------------- */
935 : /* Setup error handling. */
936 : /* -------------------------------------------------------------------- */
937 126 : png_set_error_fn( poDS->hPNG, &poDS->sSetJmpContext, png_gdal_error, png_gdal_warning );
938 :
939 126 : 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 126 : png_set_read_fn( poDS->hPNG, poDS->fpImage, png_vsi_read_data );
948 126 : png_read_info( poDS->hPNG, poDS->psPNGInfo );
949 :
950 : /* -------------------------------------------------------------------- */
951 : /* Capture some information from the file that is of interest. */
952 : /* -------------------------------------------------------------------- */
953 126 : poDS->nRasterXSize = png_get_image_width( poDS->hPNG, poDS->psPNGInfo);
954 126 : poDS->nRasterYSize = png_get_image_height( poDS->hPNG,poDS->psPNGInfo);
955 :
956 126 : poDS->nBands = png_get_channels( poDS->hPNG, poDS->psPNGInfo );
957 126 : poDS->nBitDepth = png_get_bit_depth( poDS->hPNG, poDS->psPNGInfo );
958 : poDS->bInterlaced = png_get_interlace_type( poDS->hPNG, poDS->psPNGInfo )
959 126 : != PNG_INTERLACE_NONE;
960 :
961 126 : poDS->nColorType = png_get_color_type( poDS->hPNG, poDS->psPNGInfo );
962 :
963 126 : 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 126 : if( poDS->nBitDepth < 8 )
978 0 : png_set_packing( poDS->hPNG );
979 :
980 : /* -------------------------------------------------------------------- */
981 : /* Create band information objects. */
982 : /* -------------------------------------------------------------------- */
983 880 : for( int iBand = 0; iBand < poDS->nBands; iBand++ )
984 314 : 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 126 : if( poDS->nColorType == PNG_COLOR_TYPE_PALETTE )
991 : {
992 : png_color *pasPNGPalette;
993 : int nColorCount;
994 : GDALColorEntry oEntry;
995 12 : unsigned char *trans = NULL;
996 12 : png_color_16 *trans_values = NULL;
997 12 : int num_trans = 0;
998 12 : int nNoDataIndex = -1;
999 :
1000 12 : 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 12 : &trans, &num_trans, &trans_values );
1006 :
1007 12 : poDS->poColorTable = new GDALColorTable();
1008 :
1009 204 : for( int iColor = nColorCount - 1; iColor >= 0; iColor-- )
1010 : {
1011 192 : oEntry.c1 = pasPNGPalette[iColor].red;
1012 192 : oEntry.c2 = pasPNGPalette[iColor].green;
1013 192 : oEntry.c3 = pasPNGPalette[iColor].blue;
1014 :
1015 192 : if( iColor < num_trans )
1016 : {
1017 192 : oEntry.c4 = trans[iColor];
1018 192 : if( oEntry.c4 == 0 )
1019 : {
1020 12 : if( nNoDataIndex == -1 )
1021 12 : nNoDataIndex = iColor;
1022 : else
1023 0 : nNoDataIndex = -2;
1024 : }
1025 : }
1026 : else
1027 0 : oEntry.c4 = 255;
1028 :
1029 192 : 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 12 : if( nNoDataIndex > -1 )
1037 : {
1038 12 : poDS->GetRasterBand(1)->SetNoDataValue(nNoDataIndex);
1039 : }
1040 : }
1041 :
1042 : /* -------------------------------------------------------------------- */
1043 : /* Check for transparency values in greyscale images. */
1044 : /* -------------------------------------------------------------------- */
1045 126 : if( poDS->nColorType == PNG_COLOR_TYPE_GRAY )
1046 : {
1047 42 : png_color_16 *trans_values = NULL;
1048 : unsigned char *trans;
1049 : int num_trans;
1050 :
1051 42 : if( png_get_tRNS( poDS->hPNG, poDS->psPNGInfo,
1052 : &trans, &num_trans, &trans_values ) != 0
1053 : && trans_values != NULL )
1054 : {
1055 8 : poDS->GetRasterBand(1)->SetNoDataValue(trans_values->gray);
1056 : }
1057 : }
1058 :
1059 : /* -------------------------------------------------------------------- */
1060 : /* Check for nodata color for RGB images. */
1061 : /* -------------------------------------------------------------------- */
1062 126 : if( poDS->nColorType == PNG_COLOR_TYPE_RGB )
1063 : {
1064 24 : png_color_16 *trans_values = NULL;
1065 : unsigned char *trans;
1066 : int num_trans;
1067 :
1068 24 : if( png_get_tRNS( poDS->hPNG, poDS->psPNGInfo,
1069 : &trans, &num_trans, &trans_values ) != 0
1070 : && trans_values != NULL )
1071 : {
1072 8 : CPLString oNDValue;
1073 :
1074 : oNDValue.Printf( "%d %d %d",
1075 : trans_values->red,
1076 : trans_values->green,
1077 8 : trans_values->blue );
1078 8 : poDS->SetMetadataItem( "NODATA_VALUES", oNDValue.c_str() );
1079 :
1080 8 : poDS->GetRasterBand(1)->SetNoDataValue(trans_values->red);
1081 8 : poDS->GetRasterBand(2)->SetNoDataValue(trans_values->green);
1082 8 : poDS->GetRasterBand(3)->SetNoDataValue(trans_values->blue);
1083 : }
1084 : }
1085 :
1086 : /* -------------------------------------------------------------------- */
1087 : /* Extract any text chunks as "metadata". */
1088 : /* -------------------------------------------------------------------- */
1089 126 : poDS->CollectMetadata();
1090 :
1091 126 : poDS->CollectXMPMetadata();
1092 :
1093 : /* -------------------------------------------------------------------- */
1094 : /* More metadata. */
1095 : /* -------------------------------------------------------------------- */
1096 126 : if( poDS->nBands > 1 )
1097 : {
1098 72 : poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" );
1099 : }
1100 :
1101 : /* -------------------------------------------------------------------- */
1102 : /* Initialize any PAM information. */
1103 : /* -------------------------------------------------------------------- */
1104 126 : poDS->SetDescription( poOpenInfo->pszFilename );
1105 126 : poDS->TryLoadXML( poOpenInfo->papszSiblingFiles );
1106 :
1107 : /* -------------------------------------------------------------------- */
1108 : /* Open overviews. */
1109 : /* -------------------------------------------------------------------- */
1110 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename,
1111 126 : poOpenInfo->papszSiblingFiles );
1112 :
1113 126 : return poDS;
1114 : }
1115 :
1116 : /************************************************************************/
1117 : /* LoadWorldFile() */
1118 : /************************************************************************/
1119 :
1120 54 : void PNGDataset::LoadWorldFile()
1121 : {
1122 54 : if (bHasTriedLoadWorldFile)
1123 4 : return;
1124 50 : bHasTriedLoadWorldFile = TRUE;
1125 :
1126 50 : char* pszWldFilename = NULL;
1127 : bGeoTransformValid =
1128 50 : GDALReadWorldFile2( GetDescription(), NULL,
1129 : adfGeoTransform, oOvManager.GetSiblingFiles(),
1130 100 : &pszWldFilename);
1131 :
1132 50 : if( !bGeoTransformValid )
1133 : bGeoTransformValid =
1134 50 : GDALReadWorldFile2( GetDescription(), ".wld",
1135 : adfGeoTransform, oOvManager.GetSiblingFiles(),
1136 100 : &pszWldFilename);
1137 :
1138 50 : if (pszWldFilename)
1139 : {
1140 4 : osWldFilename = pszWldFilename;
1141 4 : CPLFree(pszWldFilename);
1142 : }
1143 : }
1144 :
1145 : /************************************************************************/
1146 : /* GetFileList() */
1147 : /************************************************************************/
1148 :
1149 14 : char **PNGDataset::GetFileList()
1150 :
1151 : {
1152 14 : char **papszFileList = GDALPamDataset::GetFileList();
1153 :
1154 14 : LoadWorldFile();
1155 :
1156 14 : if (osWldFilename.size() != 0 &&
1157 : CSLFindString(papszFileList, osWldFilename) == -1)
1158 : {
1159 0 : papszFileList = CSLAddString( papszFileList, osWldFilename );
1160 : }
1161 :
1162 14 : return papszFileList;
1163 : }
1164 :
1165 : /************************************************************************/
1166 : /* CreateCopy() */
1167 : /************************************************************************/
1168 :
1169 : GDALDataset *
1170 64 : PNGDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
1171 : int bStrict, char ** papszOptions,
1172 : GDALProgressFunc pfnProgress, void * pProgressData )
1173 :
1174 : {
1175 64 : int nBands = poSrcDS->GetRasterCount();
1176 64 : int nXSize = poSrcDS->GetRasterXSize();
1177 64 : int nYSize = poSrcDS->GetRasterYSize();
1178 :
1179 : /* -------------------------------------------------------------------- */
1180 : /* Some some rudimentary checks */
1181 : /* -------------------------------------------------------------------- */
1182 64 : 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 4 : nBands );
1188 :
1189 4 : return NULL;
1190 : }
1191 :
1192 60 : 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 18 : (bStrict) ? "" : "Defaulting to Byte" );
1201 :
1202 18 : if (bStrict)
1203 18 : return NULL;
1204 : }
1205 :
1206 : /* -------------------------------------------------------------------- */
1207 : /* Setup some parameters. */
1208 : /* -------------------------------------------------------------------- */
1209 42 : int nColorType=0, nBitDepth;
1210 : GDALDataType eType;
1211 :
1212 42 : if( nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() == NULL )
1213 20 : nColorType = PNG_COLOR_TYPE_GRAY;
1214 22 : else if( nBands == 1 )
1215 2 : nColorType = PNG_COLOR_TYPE_PALETTE;
1216 20 : else if( nBands == 2 )
1217 2 : nColorType = PNG_COLOR_TYPE_GRAY_ALPHA;
1218 18 : else if( nBands == 3 )
1219 10 : nColorType = PNG_COLOR_TYPE_RGB;
1220 8 : else if( nBands == 4 )
1221 8 : nColorType = PNG_COLOR_TYPE_RGB_ALPHA;
1222 :
1223 42 : if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16 )
1224 : {
1225 34 : eType = GDT_Byte;
1226 34 : nBitDepth = 8;
1227 : }
1228 : else
1229 : {
1230 8 : eType = GDT_UInt16;
1231 8 : nBitDepth = 16;
1232 : }
1233 :
1234 : /* -------------------------------------------------------------------- */
1235 : /* Create the dataset. */
1236 : /* -------------------------------------------------------------------- */
1237 : VSILFILE *fpImage;
1238 :
1239 42 : fpImage = VSIFOpenL( pszFilename, "wb" );
1240 42 : if( fpImage == NULL )
1241 : {
1242 : CPLError( CE_Failure, CPLE_OpenFailed,
1243 : "Unable to create png file %s.\n",
1244 4 : pszFilename );
1245 4 : 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 38 : &sSetJmpContext, png_gdal_error, png_gdal_warning );
1257 38 : psPNGInfo = png_create_info_struct( hPNG );
1258 :
1259 38 : if( setjmp( sSetJmpContext ) != 0 )
1260 : {
1261 2 : VSIFCloseL( fpImage );
1262 2 : png_destroy_write_struct( &hPNG, &psPNGInfo );
1263 2 : return NULL;
1264 : }
1265 :
1266 38 : 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 38 : PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE );
1271 :
1272 : /* -------------------------------------------------------------------- */
1273 : /* Do we want to control the compression level? */
1274 : /* -------------------------------------------------------------------- */
1275 38 : const char *pszLevel = CSLFetchNameValue( papszOptions, "ZLEVEL" );
1276 :
1277 38 : 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 38 : if( nColorType == PNG_COLOR_TYPE_GRAY )
1300 : {
1301 16 : int bHaveNoData = FALSE;
1302 16 : double dfNoDataValue = -1;
1303 :
1304 16 : dfNoDataValue = poSrcDS->GetRasterBand(1)->GetNoDataValue( &bHaveNoData );
1305 :
1306 16 : if ( bHaveNoData && dfNoDataValue >= 0 && dfNoDataValue < 65536 )
1307 : {
1308 4 : sTRNSColor.gray = (png_uint_16) dfNoDataValue;
1309 4 : png_set_tRNS( hPNG, psPNGInfo, NULL, 0, &sTRNSColor );
1310 : }
1311 : }
1312 :
1313 : // RGB nodata.
1314 38 : if( nColorType == PNG_COLOR_TYPE_RGB )
1315 : {
1316 : // First try to use the NODATA_VALUES metadata item.
1317 10 : if ( poSrcDS->GetMetadataItem( "NODATA_VALUES" ) != NULL )
1318 : {
1319 : char **papszValues = CSLTokenizeString(
1320 2 : poSrcDS->GetMetadataItem( "NODATA_VALUES" ) );
1321 :
1322 2 : if( CSLCount(papszValues) >= 3 )
1323 : {
1324 2 : sTRNSColor.red = (png_uint_16) atoi(papszValues[0]);
1325 2 : sTRNSColor.green = (png_uint_16) atoi(papszValues[1]);
1326 2 : sTRNSColor.blue = (png_uint_16) atoi(papszValues[2]);
1327 2 : png_set_tRNS( hPNG, psPNGInfo, NULL, 0, &sTRNSColor );
1328 : }
1329 :
1330 2 : CSLDestroy( papszValues );
1331 : }
1332 : // Otherwise, get the nodata value from the bands.
1333 : else
1334 : {
1335 8 : int bHaveNoDataRed = FALSE;
1336 8 : int bHaveNoDataGreen = FALSE;
1337 8 : int bHaveNoDataBlue = FALSE;
1338 8 : double dfNoDataValueRed = -1;
1339 8 : double dfNoDataValueGreen = -1;
1340 8 : double dfNoDataValueBlue = -1;
1341 :
1342 8 : dfNoDataValueRed = poSrcDS->GetRasterBand(1)->GetNoDataValue( &bHaveNoDataRed );
1343 8 : dfNoDataValueGreen= poSrcDS->GetRasterBand(2)->GetNoDataValue( &bHaveNoDataGreen );
1344 8 : dfNoDataValueBlue = poSrcDS->GetRasterBand(3)->GetNoDataValue( &bHaveNoDataBlue );
1345 :
1346 8 : 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 38 : png_color *pasPNGColors = NULL;
1364 38 : unsigned char *pabyAlpha = NULL;
1365 :
1366 38 : if( nColorType == PNG_COLOR_TYPE_PALETTE )
1367 : {
1368 : GDALColorTable *poCT;
1369 : GDALColorEntry sEntry;
1370 2 : int iColor, bFoundTrans = FALSE;
1371 2 : int bHaveNoData = FALSE;
1372 2 : double dfNoDataValue = -1;
1373 :
1374 2 : dfNoDataValue = poSrcDS->GetRasterBand(1)->GetNoDataValue( &bHaveNoData );
1375 :
1376 2 : poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
1377 :
1378 : pasPNGColors = (png_color *) CPLMalloc(sizeof(png_color) *
1379 2 : poCT->GetColorEntryCount());
1380 :
1381 34 : for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ )
1382 : {
1383 32 : poCT->GetColorEntryAsRGB( iColor, &sEntry );
1384 32 : if( sEntry.c4 != 255 )
1385 2 : bFoundTrans = TRUE;
1386 :
1387 32 : pasPNGColors[iColor].red = (png_byte) sEntry.c1;
1388 32 : pasPNGColors[iColor].green = (png_byte) sEntry.c2;
1389 32 : pasPNGColors[iColor].blue = (png_byte) sEntry.c3;
1390 : }
1391 :
1392 : png_set_PLTE( hPNG, psPNGInfo, pasPNGColors,
1393 2 : poCT->GetColorEntryCount() );
1394 :
1395 : /* -------------------------------------------------------------------- */
1396 : /* If we have transparent elements in the palette we need to */
1397 : /* write a transparency block. */
1398 : /* -------------------------------------------------------------------- */
1399 2 : if( bFoundTrans || bHaveNoData )
1400 : {
1401 2 : pabyAlpha = (unsigned char *)CPLMalloc(poCT->GetColorEntryCount());
1402 :
1403 34 : for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ )
1404 : {
1405 32 : poCT->GetColorEntryAsRGB( iColor, &sEntry );
1406 32 : pabyAlpha[iColor] = (unsigned char) sEntry.c4;
1407 :
1408 32 : if( bHaveNoData && iColor == (int) dfNoDataValue )
1409 2 : pabyAlpha[iColor] = 0;
1410 : }
1411 :
1412 : png_set_tRNS( hPNG, psPNGInfo, pabyAlpha,
1413 2 : poCT->GetColorEntryCount(), NULL );
1414 : }
1415 : }
1416 :
1417 38 : png_write_info( hPNG, psPNGInfo );
1418 :
1419 : /* -------------------------------------------------------------------- */
1420 : /* Loop over image, copying image data. */
1421 : /* -------------------------------------------------------------------- */
1422 : GByte *pabyScanline;
1423 38 : CPLErr eErr = CE_None;
1424 38 : int nWordSize = nBitDepth/8;
1425 :
1426 38 : pabyScanline = (GByte *) CPLMalloc( nBands * nXSize * nWordSize );
1427 :
1428 2672 : for( int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++ )
1429 : {
1430 2634 : png_bytep row = pabyScanline;
1431 :
1432 8010 : for( int iBand = 0; iBand < nBands; iBand++ )
1433 : {
1434 5376 : 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 5376 : nBands * nXSize * nWordSize );
1440 : }
1441 :
1442 : #ifdef CPL_LSB
1443 2634 : if( nBitDepth == 16 )
1444 126 : GDALSwapWords( row, 2, nXSize * nBands, 2 );
1445 : #endif
1446 2634 : if( eErr == CE_None )
1447 2632 : png_write_rows( hPNG, &row, 1 );
1448 :
1449 2634 : 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 38 : CPLFree( pabyScanline );
1460 :
1461 38 : png_write_end( hPNG, psPNGInfo );
1462 36 : png_destroy_write_struct( &hPNG, &psPNGInfo );
1463 :
1464 36 : VSIFCloseL( fpImage );
1465 :
1466 36 : CPLFree( pabyAlpha );
1467 36 : CPLFree( pasPNGColors );
1468 :
1469 36 : if( eErr != CE_None )
1470 0 : return NULL;
1471 :
1472 : /* -------------------------------------------------------------------- */
1473 : /* Do we need a world file? */
1474 : /* -------------------------------------------------------------------- */
1475 36 : 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 36 : 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 36 : CPLPushErrorHandler(CPLQuietErrorHandler);
1491 36 : PNGDataset *poDS = (PNGDataset*) PNGDataset::Open( &oOpenInfo );
1492 36 : CPLPopErrorHandler();
1493 36 : if( poDS )
1494 : {
1495 34 : poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
1496 34 : return poDS;
1497 : }
1498 :
1499 2 : CPLErrorReset();
1500 :
1501 2 : PNGDataset* poPNG_DS = new PNGDataset();
1502 2 : poPNG_DS->nRasterXSize = nXSize;
1503 2 : poPNG_DS->nRasterYSize = nYSize;
1504 2 : poPNG_DS->nBitDepth = nBitDepth;
1505 4 : for(int i=0;i<nBands;i++)
1506 2 : poPNG_DS->SetBand( i+1, new PNGRasterBand( poPNG_DS, i+1) );
1507 2 : return poPNG_DS;
1508 : }
1509 :
1510 : /************************************************************************/
1511 : /* png_vsi_read_data() */
1512 : /* */
1513 : /* Read data callback through VSI. */
1514 : /************************************************************************/
1515 : static void
1516 1208 : 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 1208 : (VSILFILE*)png_get_io_ptr(png_ptr));
1526 :
1527 1208 : if (check != length)
1528 2 : png_error(png_ptr, "Read Error");
1529 1206 : }
1530 :
1531 : /************************************************************************/
1532 : /* png_vsi_write_data() */
1533 : /************************************************************************/
1534 :
1535 : static void
1536 482 : png_vsi_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
1537 : {
1538 : png_uint_32 check;
1539 :
1540 482 : check = VSIFWriteL(data, 1, length, (VSILFILE*)png_get_io_ptr(png_ptr));
1541 :
1542 482 : if (check != length)
1543 0 : png_error(png_ptr, "Write Error");
1544 482 : }
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 12 : static void png_gdal_error( png_structp png_ptr, const char *error_message )
1559 : {
1560 : CPLError( CE_Failure, CPLE_AppDefined,
1561 12 : "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 12 : jmp_buf* psSetJmpContext = (jmp_buf*) png_get_error_ptr(png_ptr);
1568 12 : if (psSetJmpContext)
1569 : {
1570 12 : 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 1135 : void GDALRegister_PNG()
1589 :
1590 : {
1591 : GDALDriver *poDriver;
1592 :
1593 1135 : if( GDALGetDriverByName( "PNG" ) == NULL )
1594 : {
1595 1093 : poDriver = new GDALDriver();
1596 :
1597 1093 : poDriver->SetDescription( "PNG" );
1598 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
1599 1093 : "Portable Network Graphics" );
1600 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
1601 1093 : "frmt_various.html#PNG" );
1602 1093 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "png" );
1603 1093 : poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/png" );
1604 :
1605 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
1606 1093 : "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 1093 : "</CreationOptionList>\n" );
1612 :
1613 1093 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
1614 :
1615 1093 : poDriver->pfnOpen = PNGDataset::Open;
1616 1093 : poDriver->pfnCreateCopy = PNGDataset::CreateCopy;
1617 1093 : poDriver->pfnIdentify = PNGDataset::Identify;
1618 : #ifdef SUPPORT_CREATE
1619 : poDriver->pfnCreate = PNGDataset::Create;
1620 : #endif
1621 :
1622 1093 : GetGDALDriverManager()->RegisterDriver( poDriver );
1623 : }
1624 1135 : }
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
|