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