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