Revision: 70273
          http://sourceforge.net/p/brlcad/code/70273
Author:   starseeker
Date:     2017-09-13 15:31:23 +0000 (Wed, 13 Sep 2017)
Log Message:
-----------
We can safely add png to the formats list since we guarantee libpng

Modified Paths:
--------------
    brlcad/trunk/src/other/gdal/CMakeLists.txt

Added Paths:
-----------
    brlcad/trunk/src/other/gdal/frmts/png/
    brlcad/trunk/src/other/gdal/frmts/png/CMakeLists.txt
    brlcad/trunk/src/other/gdal/frmts/png/pngdataset.cpp

Modified: brlcad/trunk/src/other/gdal/CMakeLists.txt
===================================================================
--- brlcad/trunk/src/other/gdal/CMakeLists.txt  2017-09-13 14:14:46 UTC (rev 
70272)
+++ brlcad/trunk/src/other/gdal/CMakeLists.txt  2017-09-13 15:31:23 UTC (rev 
70273)
@@ -106,6 +106,7 @@
 find_package(PNG)
 if(PNG_LIBRARY)
   include_directories(${PNG_INCLUDE_DIR})
+  add_definitions(-DUSE_PNG)
 endif(PNG_LIBRARY)
 
 
@@ -210,7 +211,7 @@
   map
   ngsgeoid nitf northwood
   ozi
-  pds prf
+  pds png prf
   r rik rmf rs2
   safe saga
   sentinel2 sgi srtmhgt

Added: brlcad/trunk/src/other/gdal/frmts/png/CMakeLists.txt
===================================================================
--- brlcad/trunk/src/other/gdal/frmts/png/CMakeLists.txt                        
        (rev 0)
+++ brlcad/trunk/src/other/gdal/frmts/png/CMakeLists.txt        2017-09-13 
15:31:23 UTC (rev 70273)
@@ -0,0 +1,2 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+add_library(frmt_png OBJECT pngdataset.cpp)


Property changes on: brlcad/trunk/src/other/gdal/frmts/png/CMakeLists.txt
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/trunk/src/other/gdal/frmts/png/pngdataset.cpp
===================================================================
--- brlcad/trunk/src/other/gdal/frmts/png/pngdataset.cpp                        
        (rev 0)
+++ brlcad/trunk/src/other/gdal/frmts/png/pngdataset.cpp        2017-09-13 
15:31:23 UTC (rev 70273)
@@ -0,0 +1,2377 @@
+/******************************************************************************
+ *
+ * Project:  PNG Driver
+ * Purpose:  Implement GDAL PNG Support
+ * Author:   Frank Warmerdam, warme...@home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ * Copyright (c) 2007-2014, Even Rouault <even dot rouault at mines-paris dot 
org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * ISSUES:
+ *  o CollectMetadata() will only capture TEXT chunks before the image
+ *    data as the code is currently structured.
+ *  o Interlaced images are read entirely into memory for use.  This is
+ *    bad for large images.
+ *  o Image reading is always strictly sequential.  Reading backwards will
+ *    cause the file to be rewound, and access started again from the
+ *    beginning.
+ *  o 16 bit alpha values are not scaled by to eight bit.
+ *
+ */
+
+#include "cpl_string.h"
+#include "gdal_frmts.h"
+#include "gdal_pam.h"
+#include "png.h"
+
+#include <csetjmp>
+
+#include <algorithm>
+
+CPL_CVSID("$Id: pngdataset.cpp 37784 2017-03-18 23:29:45Z rouault $");
+
+// Note: Callers must provide blocks in increasing Y order.
+// Disclaimer (E. Rouault): this code is not production ready at all. A lot of
+// issues remain: uninitialized variables, unclosed files, lack of proper
+// multiband handling, and an inability to read and write at the same time. Do
+// not use it unless you're ready to fix it.
+
+// Define SUPPORT_CREATE to enable use of the Create() call.
+// #define SUPPORT_CREATE
+
+#ifdef _MSC_VER
+#  pragma warning(disable:4611)
+#endif
+
+static void
+png_vsi_read_data(png_structp png_ptr, png_bytep data, png_size_t length);
+
+static void
+png_vsi_write_data(png_structp png_ptr, png_bytep data, png_size_t length);
+
+static void png_vsi_flush(png_structp png_ptr);
+
+static void png_gdal_error( png_structp png_ptr, const char *error_message );
+static void png_gdal_warning( png_structp png_ptr, const char *error_message );
+
+/************************************************************************/
+/* ==================================================================== */
+/*                              PNGDataset                              */
+/* ==================================================================== */
+/************************************************************************/
+
+class PNGRasterBand;
+
+#ifdef _MSC_VER
+#pragma warning( push )
+// 'PNGDataset': structure was padded due to __declspec(align()) at line where
+// we use `jmp_buf`.
+#pragma warning( disable : 4324 )
+#endif
+
+class PNGDataset : public GDALPamDataset
+{
+    friend class PNGRasterBand;
+
+    VSILFILE        *fpImage;
+    png_structp hPNG;
+    png_infop   psPNGInfo;
+    int         nBitDepth;
+    int         nColorType;  // PNG_COLOR_TYPE_*
+    int         bInterlaced;
+
+    int         nBufferStartLine;
+    int         nBufferLines;
+    int         nLastLineRead;
+    GByte      *pabyBuffer;
+
+    GDALColorTable *poColorTable;
+
+    int    bGeoTransformValid;
+    double adfGeoTransform[6];
+
+    void        CollectMetadata();
+
+    int         bHasReadXMPMetadata;
+    void        CollectXMPMetadata();
+
+    CPLErr      LoadScanline( int );
+    CPLErr      LoadInterlacedChunk( int );
+    void        Restart();
+
+    int         bHasTriedLoadWorldFile;
+    void        LoadWorldFile();
+    CPLString   osWldFilename;
+
+    int         bHasReadICCMetadata;
+    void        LoadICCProfile();
+
+    static void WriteMetadataAsText(png_structp hPNG, png_infop psPNGInfo,
+                                    const char* pszKey, const char* pszValue);
+    static GDALDataset *OpenStage2( GDALOpenInfo *, PNGDataset*& );
+
+  public:
+                 PNGDataset();
+    virtual ~PNGDataset();
+
+    static GDALDataset *Open( GDALOpenInfo * );
+    static int          Identify( GDALOpenInfo * );
+    static GDALDataset* CreateCopy( const char * pszFilename,
+                                    GDALDataset *poSrcDS,
+                                    int bStrict, char ** papszOptions,
+                                    GDALProgressFunc pfnProgress,
+                                    void * pProgressData );
+
+    virtual char **GetFileList(void) override;
+
+    virtual CPLErr GetGeoTransform( double * ) override;
+    virtual void FlushCache( void ) override;
+
+    virtual char      **GetMetadataDomainList() override;
+
+    virtual char  **GetMetadata( const char * pszDomain = "" ) override;
+    virtual const char *GetMetadataItem( const char * pszName,
+                                         const char * pszDomain = NULL ) 
override;
+
+    virtual CPLErr      IRasterIO( GDALRWFlag, int, int, int, int,
+                                   void *, int, int, GDALDataType,
+                                   int, int *,
+                                   GSpacing, GSpacing,
+                                   GSpacing,
+                                   GDALRasterIOExtraArg* psExtraArg ) override;
+
+    jmp_buf     sSetJmpContext;  // Semi-private.
+
+#ifdef SUPPORT_CREATE
+    int        m_nBitDepth;
+    GByte      *m_pabyBuffer;
+    png_byte    *m_pabyAlpha;
+    png_structp m_hPNG;
+    png_infop   m_psPNGInfo;
+    png_color   *m_pasPNGColors;
+    VSILFILE        *m_fpImage;
+    int    m_bGeoTransformValid;
+    double m_adfGeoTransform[6];
+    char        *m_pszFilename;
+    int         m_nColorType;  // PNG_COLOR_TYPE_*
+
+    virtual CPLErr SetGeoTransform( double * );
+    static GDALDataset  *Create( const char* pszFilename,
+                                int nXSize, int nYSize, int nBands,
+                                GDALDataType, char** papszParmList );
+  protected:
+        CPLErr write_png_header();
+
+#endif
+};
+
+#ifdef _MSC_VER
+#pragma warning( pop )
+#endif
+
+/************************************************************************/
+/* ==================================================================== */
+/*                            PNGRasterBand                             */
+/* ==================================================================== */
+/************************************************************************/
+
+class PNGRasterBand : public GDALPamRasterBand
+{
+    friend class PNGDataset;
+
+  public:
+
+                   PNGRasterBand( PNGDataset *, int );
+    virtual ~PNGRasterBand() {}
+
+    virtual CPLErr IReadBlock( int, int, void * ) override;
+
+    virtual GDALColorInterp GetColorInterpretation() override;
+    virtual GDALColorTable *GetColorTable() override;
+    CPLErr SetNoDataValue( double dfNewValue ) override;
+    virtual double GetNoDataValue( int *pbSuccess = NULL ) override;
+
+    int         bHaveNoData;
+    double      dfNoDataValue;
+
+#ifdef SUPPORT_CREATE
+    virtual CPLErr SetColorTable(GDALColorTable*);
+    virtual CPLErr IWriteBlock( int, int, void * ) override;
+
+  protected:
+        int m_bBandProvided[5];
+        void reset_band_provision_flags()
+        {
+            PNGDataset& ds = *reinterpret_cast<PNGDataset *>( poDS );
+
+            for(size_t i = 0; i < static_cast<size_t>( ds.nBands ); i++)
+                m_bBandProvided[i] = FALSE;
+        }
+#endif
+};
+
+/************************************************************************/
+/*                           PNGRasterBand()                            */
+/************************************************************************/
+
+PNGRasterBand::PNGRasterBand( PNGDataset *poDSIn, int nBandIn ) :
+    bHaveNoData(FALSE),
+    dfNoDataValue(-1)
+{
+    poDS = poDSIn;
+    nBand = nBandIn;
+
+    if( poDSIn->nBitDepth == 16 )
+        eDataType = GDT_UInt16;
+    else
+        eDataType = GDT_Byte;
+
+    nBlockXSize = poDSIn->nRasterXSize;
+    nBlockYSize = 1;
+
+#ifdef SUPPORT_CREATE
+    reset_band_provision_flags();
+#endif
+}
+
+/************************************************************************/
+/*                             IReadBlock()                             */
+/************************************************************************/
+
+CPLErr PNGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
+                                  void * pImage )
+
+{
+    PNGDataset *poGDS = reinterpret_cast<PNGDataset *>( poDS );
+    int nPixelSize;
+
+    CPLAssert( nBlockXOff == 0 );
+
+    if( poGDS->nBitDepth == 16 )
+        nPixelSize = 2;
+    else
+        nPixelSize = 1;
+
+    const int nXSize = GetXSize();
+    if (poGDS->fpImage == NULL)
+    {
+        memset( pImage, 0, nPixelSize * nXSize );
+        return CE_None;
+    }
+
+    // Load the desired scanline into the working buffer.
+    CPLErr eErr = poGDS->LoadScanline( nBlockYOff );
+    if( eErr != CE_None )
+        return eErr;
+
+    const int nPixelOffset = poGDS->nBands * nPixelSize;
+
+    GByte *pabyScanline = poGDS->pabyBuffer
+        + (nBlockYOff - poGDS->nBufferStartLine) * nPixelOffset * nXSize
+        + nPixelSize * (nBand - 1);
+
+    // Transfer between the working buffer and the caller's buffer.
+    if( nPixelSize == nPixelOffset )
+        memcpy( pImage, pabyScanline, nPixelSize * nXSize );
+    else if( nPixelSize == 1 )
+    {
+        for( int i = 0; i < nXSize; i++ )
+            reinterpret_cast<GByte *>( pImage )[i] = 
pabyScanline[i*nPixelOffset];
+    }
+    else
+    {
+        CPLAssert( nPixelSize == 2 );
+        for( int i = 0; i < nXSize; i++ )
+        {
+            reinterpret_cast<GUInt16 *>( pImage )[i] =
+                *reinterpret_cast<GUInt16 *>( pabyScanline+i*nPixelOffset );
+        }
+    }
+
+    // Forcibly load the other bands associated with this scanline.
+    for(int iBand = 1; iBand < poGDS->GetRasterCount(); iBand++)
+    {
+        GDALRasterBlock *poBlock =
+            
poGDS->GetRasterBand(iBand+1)->GetLockedBlockRef(nBlockXOff,nBlockYOff);
+        if( poBlock != NULL )
+            poBlock->DropLock();
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                       GetColorInterpretation()                       */
+/************************************************************************/
+
+GDALColorInterp PNGRasterBand::GetColorInterpretation()
+
+{
+    PNGDataset *poGDS = reinterpret_cast<PNGDataset *>( poDS );
+
+    if( poGDS->nColorType == PNG_COLOR_TYPE_GRAY )
+        return GCI_GrayIndex;
+
+    else if( poGDS->nColorType == PNG_COLOR_TYPE_GRAY_ALPHA )
+    {
+        if( nBand == 1 )
+            return GCI_GrayIndex;
+        else
+            return GCI_AlphaBand;
+    }
+
+    else  if( poGDS->nColorType == PNG_COLOR_TYPE_PALETTE )
+        return GCI_PaletteIndex;
+
+    else  if( poGDS->nColorType == PNG_COLOR_TYPE_RGB
+              || poGDS->nColorType == PNG_COLOR_TYPE_RGB_ALPHA )
+    {
+        if( nBand == 1 )
+            return GCI_RedBand;
+        else if( nBand == 2 )
+            return GCI_GreenBand;
+        else if( nBand == 3 )
+            return GCI_BlueBand;
+        else
+            return GCI_AlphaBand;
+    }
+    else
+        return GCI_GrayIndex;
+}
+
+/************************************************************************/
+/*                           GetColorTable()                            */
+/************************************************************************/
+
+GDALColorTable *PNGRasterBand::GetColorTable()
+
+{
+    PNGDataset  *poGDS = reinterpret_cast<PNGDataset *>( poDS );
+
+    if( nBand == 1 )
+        return poGDS->poColorTable;
+
+    return NULL;
+}
+
+/************************************************************************/
+/*                           SetNoDataValue()                           */
+/************************************************************************/
+
+CPLErr PNGRasterBand::SetNoDataValue( double dfNewValue )
+
+{
+   bHaveNoData = TRUE;
+   dfNoDataValue = dfNewValue;
+
+   return CE_None;
+}
+
+/************************************************************************/
+/*                           GetNoDataValue()                           */
+/************************************************************************/
+
+double PNGRasterBand::GetNoDataValue( int *pbSuccess )
+
+{
+    if( bHaveNoData )
+    {
+        if( pbSuccess != NULL )
+            *pbSuccess = bHaveNoData;
+        return dfNoDataValue;
+    }
+
+    return GDALPamRasterBand::GetNoDataValue( pbSuccess );
+}
+
+/************************************************************************/
+/* ==================================================================== */
+/*                             PNGDataset                               */
+/* ==================================================================== */
+/************************************************************************/
+
+/************************************************************************/
+/*                             PNGDataset()                             */
+/************************************************************************/
+
+PNGDataset::PNGDataset() :
+    fpImage(NULL),
+    hPNG(NULL),
+    psPNGInfo(NULL),
+    nBitDepth(8),
+    nColorType(0),
+    bInterlaced(FALSE),
+    nBufferStartLine(0),
+    nBufferLines(0),
+    nLastLineRead(-1),
+    pabyBuffer(NULL),
+    poColorTable(NULL),
+    bGeoTransformValid(FALSE),
+    bHasReadXMPMetadata(FALSE),
+    bHasTriedLoadWorldFile(FALSE),
+    bHasReadICCMetadata(FALSE)
+{
+    adfGeoTransform[0] = 0.0;
+    adfGeoTransform[1] = 1.0;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = 0.0;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = 1.0;
+
+    memset(&sSetJmpContext, 0, sizeof(sSetJmpContext));
+}
+
+/************************************************************************/
+/*                            ~PNGDataset()                             */
+/************************************************************************/
+
+PNGDataset::~PNGDataset()
+
+{
+    FlushCache();
+
+    if( hPNG != NULL )
+        png_destroy_read_struct( &hPNG, &psPNGInfo, NULL );
+
+    if( fpImage )
+        VSIFCloseL( fpImage );
+
+    if( poColorTable != NULL )
+        delete poColorTable;
+}
+
+/************************************************************************/
+/*                            IsFullBandMap()                           */
+/************************************************************************/
+
+static int IsFullBandMap(int *panBandMap, int nBands)
+{
+    for(int i=0;i<nBands;i++)
+    {
+        if( panBandMap[i] != i + 1 )
+            return FALSE;
+    }
+    return TRUE;
+}
+
+/************************************************************************/
+/*                             IRasterIO()                              */
+/************************************************************************/
+
+CPLErr PNGDataset::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 )
+
+{
+    // Coverity says that we cannot pass a nullptr to IRasterIO.
+    if (panBandMap == NULL)
+    {
+      return CE_Failure;
+    }
+
+    if((eRWFlag == GF_Read) &&
+       (nBandCount == nBands) &&
+       (nXOff == 0) && (nYOff == 0) &&
+       (nXSize == nBufXSize) && (nXSize == nRasterXSize) &&
+       (nYSize == nBufYSize) && (nYSize == nRasterYSize) &&
+       (eBufType == GDT_Byte) &&
+       (eBufType == GetRasterBand(1)->GetRasterDataType()) &&
+       (pData != NULL) &&
+       (panBandMap != NULL) && IsFullBandMap(panBandMap, nBands))
+    {
+        // Pixel interleaved case.
+        if( nBandSpace == 1 )
+        {
+            for(int y = 0; y < nYSize; ++y)
+            {
+                CPLErr tmpError = LoadScanline(y);
+                if(tmpError != CE_None) return tmpError;
+                const GByte* pabyScanline = pabyBuffer
+                    + (y - nBufferStartLine) * nBands * nXSize;
+                if( nPixelSpace == nBandSpace * nBandCount )
+                {
+                    memcpy(&(reinterpret_cast<GByte*>( pData 
)[(y*nLineSpace)]),
+                           pabyScanline, nBandCount * nXSize);
+                }
+                else
+                {
+                    for(int x = 0; x < nXSize; ++x)
+                    {
+                        
memcpy(&(reinterpret_cast<GByte*>(pData)[(y*nLineSpace) + (x*nPixelSpace)]),
+                               (const GByte*)&(pabyScanline[x* nBandCount]), 
nBandCount);
+                    }
+                }
+            }
+        }
+        else
+        {
+            for(int y = 0; y < nYSize; ++y)
+            {
+                CPLErr tmpError = LoadScanline(y);
+                if(tmpError != CE_None) return tmpError;
+                const GByte* pabyScanline = pabyBuffer
+                    + (y - nBufferStartLine) * nBands * nXSize;
+                GByte* pabyDest = reinterpret_cast<GByte *>( pData ) +
+                                                            y*nLineSpace;
+                if( nPixelSpace <= nBands && nBandSpace > nBands )
+                {
+                    // Cache friendly way for typical band interleaved case.
+                    for(int iBand=0;iBand<nBands;iBand++)
+                    {
+                        GByte* pabyDest2 = pabyDest + iBand * nBandSpace;
+                        const GByte* pabyScanline2 = pabyScanline + iBand;
+                        GDALCopyWords( pabyScanline2, GDT_Byte, nBands,
+                                       pabyDest2, GDT_Byte,
+                                       static_cast<int>(nPixelSpace),
+                                       nXSize );
+                    }
+                }
+                else
+                {
+                    // Generic method
+                    for(int x = 0; x < nXSize; ++x)
+                    {
+                        for(int iBand=0;iBand<nBands;iBand++)
+                        {
+                            pabyDest[(x*nPixelSpace) + iBand * nBandSpace] =
+                                pabyScanline[x*nBands+iBand];
+                        }
+                    }
+                }
+            }
+        }
+
+        return CE_None;
+    }
+
+    return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
+                                     pData, nBufXSize, nBufYSize, eBufType,
+                                     nBandCount, panBandMap,
+                                     nPixelSpace, nLineSpace, nBandSpace,
+                                     psExtraArg);
+}
+
+/************************************************************************/
+/*                          GetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr PNGDataset::GetGeoTransform( double * padfTransform )
+
+{
+    LoadWorldFile();
+
+    if( bGeoTransformValid )
+    {
+        memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
+        return CE_None;
+    }
+
+    return GDALPamDataset::GetGeoTransform( padfTransform );
+}
+
+/************************************************************************/
+/*                             FlushCache()                             */
+/*                                                                      */
+/*      We override this so we can also flush out local TIFF strip      */
+/*      cache if need be.                                               */
+/************************************************************************/
+
+void PNGDataset::FlushCache()
+
+{
+    GDALPamDataset::FlushCache();
+
+    if( pabyBuffer != NULL )
+    {
+        CPLFree( pabyBuffer );
+        pabyBuffer = NULL;
+        nBufferStartLine = 0;
+        nBufferLines = 0;
+    }
+}
+
+/************************************************************************/
+/*                              Restart()                               */
+/*                                                                      */
+/*      Restart reading from the beginning of the file.                 */
+/************************************************************************/
+
+void PNGDataset::Restart()
+
+{
+    png_destroy_read_struct( &hPNG, &psPNGInfo, NULL );
+
+    hPNG = png_create_read_struct( PNG_LIBPNG_VER_STRING, this, NULL, NULL );
+
+    png_set_error_fn( hPNG, &sSetJmpContext, png_gdal_error, png_gdal_warning 
);
+    if( setjmp( sSetJmpContext ) != 0 )
+        return;
+
+    psPNGInfo = png_create_info_struct( hPNG );
+
+    VSIFSeekL( fpImage, 0, SEEK_SET );
+    png_set_read_fn( hPNG, fpImage, png_vsi_read_data );
+    png_read_info( hPNG, psPNGInfo );
+
+    if( nBitDepth < 8 )
+        png_set_packing( hPNG );
+
+    nLastLineRead = -1;
+}
+
+/************************************************************************/
+/*                        safe_png_read_image()                         */
+/************************************************************************/
+
+static bool safe_png_read_image(png_structp hPNG,
+                                png_bytep *png_rows,
+                                jmp_buf     sSetJmpContext)
+{
+    if( setjmp( sSetJmpContext ) != 0 )
+        return false;
+    png_read_image( hPNG, png_rows );
+    return true;
+}
+
+/************************************************************************/
+/*                        LoadInterlacedChunk()                         */
+/************************************************************************/
+
+CPLErr PNGDataset::LoadInterlacedChunk( int iLine )
+
+{
+    int nPixelOffset;
+
+    if( nBitDepth == 16 )
+        nPixelOffset = 2 * GetRasterCount();
+    else
+        nPixelOffset = 1 * GetRasterCount();
+
+    // What is the biggest chunk we can safely operate on?
+    static const int MAX_PNG_CHUNK_BYTES = 100000000;
+
+    int nMaxChunkLines =
+        std::max(1, MAX_PNG_CHUNK_BYTES / (nPixelOffset * GetRasterXSize()));
+
+    if( nMaxChunkLines > GetRasterYSize() )
+        nMaxChunkLines = GetRasterYSize();
+
+    // Allocate chunk buffer if we don't already have it from a previous
+    // request.
+    nBufferLines = nMaxChunkLines;
+    if( nMaxChunkLines + iLine > GetRasterYSize() )
+        nBufferStartLine = GetRasterYSize() - nMaxChunkLines;
+    else
+        nBufferStartLine = iLine;
+
+    if( pabyBuffer == NULL )
+    {
+      pabyBuffer = reinterpret_cast<GByte *>(
+          VSI_MALLOC_VERBOSE(nPixelOffset*GetRasterXSize()*nMaxChunkLines) );
+
+        if( pabyBuffer == NULL )
+        {
+            return CE_Failure;
+        }
+#ifdef notdef
+        if( nMaxChunkLines < GetRasterYSize() )
+            CPLDebug( "PNG",
+                      "Interlaced file being handled in %d line chunks.\n"
+                      "Performance is likely to be quite poor.",
+                      nMaxChunkLines );
+#endif
+    }
+
+    // Do we need to restart reading? We do this if we aren't on the first
+    // attempt to read the image.
+    if( nLastLineRead != -1 )
+    {
+        Restart();
+    }
+
+    // Allocate and populate rows array. We create a row for each row in the
+    // image but use our dummy line for rows not in the target window.
+    png_bytep dummy_row = reinterpret_cast<png_bytep>(
+        CPLMalloc(nPixelOffset*GetRasterXSize()) );
+    png_bytep *png_rows
+        = reinterpret_cast<png_bytep *>(
+            CPLMalloc(sizeof(png_bytep) * GetRasterYSize()) );
+
+    for( int i = 0; i < GetRasterYSize(); i++ )
+    {
+        if( i >= nBufferStartLine && i < nBufferStartLine + nBufferLines )
+            png_rows[i] = pabyBuffer
+                + (i-nBufferStartLine) * nPixelOffset * GetRasterXSize();
+        else
+            png_rows[i] = dummy_row;
+    }
+
+    bool bRet = safe_png_read_image( hPNG, png_rows, sSetJmpContext );
+
+    CPLFree( png_rows );
+    CPLFree( dummy_row );
+    if( !bRet )
+        return CE_Failure;
+
+    nLastLineRead = nBufferStartLine + nBufferLines - 1;
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                        safe_png_read_rows()                          */
+/************************************************************************/
+
+static bool safe_png_read_rows(png_structp hPNG,
+                                png_bytep  row,
+                                jmp_buf    sSetJmpContext)
+{
+    if( setjmp( sSetJmpContext ) != 0 )
+        return false;
+    png_read_rows( hPNG, &row, NULL, 1 );
+    return true;
+}
+
+/************************************************************************/
+/*                            LoadScanline()                            */
+/************************************************************************/
+
+CPLErr PNGDataset::LoadScanline( int nLine )
+
+{
+    CPLAssert( nLine >= 0 && nLine < GetRasterYSize() );
+
+    if( nLine >= nBufferStartLine && nLine < nBufferStartLine + nBufferLines)
+        return CE_None;
+
+    int nPixelOffset;
+    if( nBitDepth == 16 )
+        nPixelOffset = 2 * GetRasterCount();
+    else
+        nPixelOffset = 1 * GetRasterCount();
+
+    // If the file is interlaced, we load the entire image into memory using 
the
+    // high-level API.
+    if( bInterlaced )
+        return LoadInterlacedChunk( nLine );
+
+    // Ensure we have space allocated for one scanline.
+    if( pabyBuffer == NULL )
+        pabyBuffer = reinterpret_cast<GByte *>(
+            CPLMalloc(nPixelOffset * GetRasterXSize() ) );
+
+    // Otherwise we just try to read the requested row. Do we need to rewind 
and
+    // start over?
+    if( nLine <= nLastLineRead )
+    {
+        Restart();
+    }
+
+    // Read till we get the desired row.
+    png_bytep row = pabyBuffer;
+    while( nLine > nLastLineRead )
+    {
+        if( !safe_png_read_rows( hPNG, row, sSetJmpContext ) )
+            return CE_Failure;
+        nLastLineRead++;
+    }
+
+    nBufferStartLine = nLine;
+    nBufferLines = 1;
+
+     // Do swap on LSB machines. 16-bit PNG data is stored in MSB format.
+#ifdef CPL_LSB
+    if( nBitDepth == 16 )
+        GDALSwapWords( row, 2, GetRasterXSize() * GetRasterCount(), 2 );
+#endif
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          CollectMetadata()                           */
+/*                                                                      */
+/*      We normally do this after reading up to the image, but be       */
+/*      forewarned: we can miss text chunks this way.                   */
+/*                                                                      */
+/*      We turn each PNG text chunk into one metadata item.  It         */
+/*      might be nice to preserve language information though we        */
+/*      don't try to now.                                               */
+/************************************************************************/
+
+void PNGDataset::CollectMetadata()
+
+{
+    if( nBitDepth < 8 )
+    {
+        for( int iBand = 0; iBand < nBands; iBand++ )
+        {
+            GetRasterBand(iBand+1)->SetMetadataItem(
+                "NBITS", CPLString().Printf( "%d", nBitDepth ),
+                "IMAGE_STRUCTURE" );
+        }
+    }
+
+    int nTextCount;
+    png_textp text_ptr;
+    if( png_get_text( hPNG, psPNGInfo, &text_ptr, &nTextCount ) == 0 )
+        return;
+
+    for( int iText = 0; iText < nTextCount; iText++ )
+    {
+        char *pszTag = CPLStrdup(text_ptr[iText].key);
+
+        for( int i = 0; pszTag[i] != '\0'; i++ )
+        {
+            if( pszTag[i] == ' ' || pszTag[i] == '=' || pszTag[i] == ':' )
+                pszTag[i] = '_';
+        }
+
+        GDALDataset::SetMetadataItem( pszTag, text_ptr[iText].text );
+        CPLFree( pszTag );
+    }
+}
+
+/************************************************************************/
+/*                       CollectXMPMetadata()                           */
+/************************************************************************/
+
+// See ยง2.1.5 of
+// 
http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf.
+
+void PNGDataset::CollectXMPMetadata()
+
+{
+    if (fpImage == NULL || bHasReadXMPMetadata)
+        return;
+
+    // Save current position to avoid disturbing PNG stream decoding.
+    const vsi_l_offset nCurOffset = VSIFTellL(fpImage);
+
+    vsi_l_offset nOffset = 8;
+    VSIFSeekL( fpImage, nOffset, SEEK_SET );
+
+    // Loop over chunks.
+    while( true )
+    {
+        int nLength;
+
+        if (VSIFReadL( &nLength, 4, 1, fpImage ) != 1)
+            break;
+        nOffset += 4;
+        CPL_MSBPTR32(&nLength);
+        if (nLength <= 0)
+            break;
+
+        char pszChunkType[5];
+        if (VSIFReadL( pszChunkType, 4, 1, fpImage ) != 1)
+            break;
+        nOffset += 4;
+        pszChunkType[4] = 0;
+
+        if (strcmp(pszChunkType, "iTXt") == 0 && nLength > 22)
+        {
+            char* pszContent = reinterpret_cast<char *>(
+                VSIMalloc(nLength + 1) );
+            if (pszContent == NULL)
+                break;
+            if (VSIFReadL( pszContent, nLength, 1, fpImage) != 1)
+            {
+                VSIFree(pszContent);
+                break;
+            }
+            nOffset += nLength;
+            pszContent[nLength] = '\0';
+            if (memcmp(pszContent, "XML:com.adobe.xmp\0\0\0\0\0", 22) == 0)
+            {
+                // Avoid setting the PAM dirty bit just for that.
+                int nOldPamFlags = nPamFlags;
+
+                char *apszMDList[2] = { pszContent + 22, NULL };
+                SetMetadata(apszMDList, "xml:XMP");
+
+                nPamFlags = nOldPamFlags;
+
+                VSIFree(pszContent);
+
+                break;
+            }
+            else
+            {
+                VSIFree(pszContent);
+            }
+        }
+        else
+        {
+            nOffset += nLength;
+            VSIFSeekL( fpImage, nOffset, SEEK_SET );
+        }
+
+        nOffset += 4;
+        int nCRC;
+        if (VSIFReadL( &nCRC, 4, 1, fpImage ) != 1)
+            break;
+    }
+
+    VSIFSeekL( fpImage, nCurOffset, SEEK_SET );
+
+    bHasReadXMPMetadata = TRUE;
+}
+
+/************************************************************************/
+/*                           LoadICCProfile()                           */
+/************************************************************************/
+
+void PNGDataset::LoadICCProfile()
+{
+    if (hPNG == NULL || bHasReadICCMetadata)
+        return;
+    bHasReadICCMetadata = TRUE;
+
+    png_charp pszProfileName;
+    png_uint_32 nProfileLength;
+#if (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR > 4) || 
PNG_LIBPNG_VER_MAJOR > 1
+    png_bytep pProfileData;
+#else
+    png_charp pProfileData;
+#endif
+    int nCompressionType;
+
+    // Avoid setting the PAM dirty bit just for that.
+    int nOldPamFlags = nPamFlags;
+
+    if (png_get_iCCP(hPNG, psPNGInfo, &pszProfileName,
+       &nCompressionType, &pProfileData, &nProfileLength) != 0)
+    {
+        // Escape the profile.
+        char *pszBase64Profile = CPLBase64Encode(
+            static_cast<int>(nProfileLength), reinterpret_cast<const GByte *>( 
pProfileData ) );
+
+        // Set ICC profile metadata.
+        SetMetadataItem( "SOURCE_ICC_PROFILE", pszBase64Profile, 
"COLOR_PROFILE" );
+        SetMetadataItem( "SOURCE_ICC_PROFILE_NAME", pszProfileName, 
"COLOR_PROFILE" );
+
+        nPamFlags = nOldPamFlags;
+
+        CPLFree(pszBase64Profile);
+
+        return;
+    }
+
+    int nsRGBIntent;
+    if (png_get_sRGB(hPNG, psPNGInfo, &nsRGBIntent) != 0)
+    {
+        SetMetadataItem( "SOURCE_ICC_PROFILE_NAME", "sRGB", "COLOR_PROFILE" );
+
+        nPamFlags = nOldPamFlags;
+
+        return;
+    }
+
+    double dfGamma;
+    bool bGammaAvailable = false;
+    if (png_get_valid(hPNG, psPNGInfo, PNG_INFO_gAMA))
+    {
+        bGammaAvailable = true;
+
+        png_get_gAMA(hPNG,psPNGInfo, &dfGamma);
+
+        SetMetadataItem( "PNG_GAMMA",
+            CPLString().Printf( "%.9f", dfGamma ) , "COLOR_PROFILE" );
+    }
+
+    // Check that both cHRM and gAMA are available.
+    if (bGammaAvailable && png_get_valid(hPNG, psPNGInfo, PNG_INFO_cHRM))
+    {
+        double dfaWhitepoint[2];
+        double dfaCHR[6];
+
+        png_get_cHRM(hPNG, psPNGInfo,
+                    &dfaWhitepoint[0], &dfaWhitepoint[1],
+                    &dfaCHR[0], &dfaCHR[1],
+                    &dfaCHR[2], &dfaCHR[3],
+                    &dfaCHR[4], &dfaCHR[5]);
+
+        // Set all the colorimetric metadata.
+        SetMetadataItem( "SOURCE_PRIMARIES_RED",
+            CPLString().Printf( "%.9f, %.9f, 1.0", dfaCHR[0], dfaCHR[1] ) , 
"COLOR_PROFILE" );
+        SetMetadataItem( "SOURCE_PRIMARIES_GREEN",
+            CPLString().Printf( "%.9f, %.9f, 1.0", dfaCHR[2], dfaCHR[3] ) , 
"COLOR_PROFILE" );
+        SetMetadataItem( "SOURCE_PRIMARIES_BLUE",
+            CPLString().Printf( "%.9f, %.9f, 1.0", dfaCHR[4], dfaCHR[5] ) , 
"COLOR_PROFILE" );
+
+        SetMetadataItem( "SOURCE_WHITEPOINT",
+            CPLString().Printf( "%.9f, %.9f, 1.0", dfaWhitepoint[0], 
dfaWhitepoint[1] ) , "COLOR_PROFILE" );
+    }
+
+    nPamFlags = nOldPamFlags;
+}
+
+/************************************************************************/
+/*                      GetMetadataDomainList()                         */
+/************************************************************************/
+
+char **PNGDataset::GetMetadataDomainList()
+{
+    return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
+                                   TRUE,
+                                   "xml:XMP", "COLOR_PROFILE", NULL);
+}
+
+/************************************************************************/
+/*                           GetMetadata()                              */
+/************************************************************************/
+
+char  **PNGDataset::GetMetadata( const char * pszDomain )
+{
+    if (fpImage == NULL)
+        return NULL;
+    if (eAccess == GA_ReadOnly && !bHasReadXMPMetadata &&
+        pszDomain != NULL && EQUAL(pszDomain, "xml:XMP"))
+        CollectXMPMetadata();
+    if (eAccess == GA_ReadOnly && !bHasReadICCMetadata &&
+        pszDomain != NULL && EQUAL(pszDomain, "COLOR_PROFILE"))
+        LoadICCProfile();
+    return GDALPamDataset::GetMetadata(pszDomain);
+}
+
+/************************************************************************/
+/*                       GetMetadataItem()                              */
+/************************************************************************/
+const char *PNGDataset::GetMetadataItem( const char * pszName,
+                                         const char * pszDomain )
+{
+    if (eAccess == GA_ReadOnly && !bHasReadICCMetadata &&
+        pszDomain != NULL && EQUAL(pszDomain, "COLOR_PROFILE"))
+        LoadICCProfile();
+    return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
+}
+
+/************************************************************************/
+/*                              Identify()                              */
+/************************************************************************/
+
+int PNGDataset::Identify( GDALOpenInfo * poOpenInfo )
+
+{
+    if( poOpenInfo->nHeaderBytes < 4 )
+        return FALSE;
+
+    if( png_sig_cmp(poOpenInfo->pabyHeader, static_cast<png_size_t>( 0 ),
+                    poOpenInfo->nHeaderBytes) != 0 )
+        return FALSE;
+
+    return TRUE;
+}
+
+/************************************************************************/
+/*                                Open()                                */
+/************************************************************************/
+
+GDALDataset *PNGDataset::Open( GDALOpenInfo * poOpenInfo )
+
+{
+    if( !Identify( poOpenInfo ) )
+        return NULL;
+
+    if( poOpenInfo->eAccess == GA_Update )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported,
+                  "The PNG driver does not support update access to existing"
+                  " datasets.\n" );
+        return NULL;
+    }
+
+    // Create a corresponding GDALDataset.
+    PNGDataset *poDS = new PNGDataset();
+    return OpenStage2( poOpenInfo, poDS );
+}
+
+GDALDataset *PNGDataset::OpenStage2( GDALOpenInfo * poOpenInfo, PNGDataset*& 
poDS )
+
+{
+    poDS->fpImage = poOpenInfo->fpL;
+    poOpenInfo->fpL = NULL;
+    poDS->eAccess = poOpenInfo->eAccess;
+
+    poDS->hPNG = png_create_read_struct( PNG_LIBPNG_VER_STRING, poDS,
+                                         NULL, NULL );
+    if (poDS->hPNG == NULL)
+    {
+#if (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 2) || 
PNG_LIBPNG_VER_MAJOR > 1
+        int version = static_cast<int>(png_access_version_number());
+        CPLError( CE_Failure, CPLE_NotSupported,
+                  "The PNG driver failed to access libpng with version '%s',"
+                  " library is actually version '%d'.\n",
+                  PNG_LIBPNG_VER_STRING, version);
+#else
+        CPLError( CE_Failure, CPLE_NotSupported,
+                  "The PNG driver failed to in png_create_read_struct().\n"
+                  "This may be due to version compatibility problems." );
+#endif
+        delete poDS;
+        return NULL;
+    }
+
+    poDS->psPNGInfo = png_create_info_struct( poDS->hPNG );
+
+    // Set up error handling.
+    png_set_error_fn( poDS->hPNG, &poDS->sSetJmpContext, png_gdal_error, 
png_gdal_warning );
+
+    if( setjmp( poDS->sSetJmpContext ) != 0 )
+    {
+        delete poDS;
+        return NULL;
+    }
+
+    // Read pre-image data after ensuring the file is rewound.
+    // We should likely do a setjmp() here.
+
+    png_set_read_fn( poDS->hPNG, poDS->fpImage, png_vsi_read_data );
+    png_read_info( poDS->hPNG, poDS->psPNGInfo );
+
+    // Capture some information from the file that is of interest.
+    poDS->nRasterXSize = static_cast<int>(png_get_image_width( poDS->hPNG, 
poDS->psPNGInfo));
+    poDS->nRasterYSize = static_cast<int>(png_get_image_height( 
poDS->hPNG,poDS->psPNGInfo));
+
+    poDS->nBands = png_get_channels( poDS->hPNG, poDS->psPNGInfo );
+    poDS->nBitDepth = png_get_bit_depth( poDS->hPNG, poDS->psPNGInfo );
+    poDS->bInterlaced = png_get_interlace_type( poDS->hPNG, poDS->psPNGInfo )
+        != PNG_INTERLACE_NONE;
+
+    poDS->nColorType = png_get_color_type( poDS->hPNG, poDS->psPNGInfo );
+
+    if( poDS->nColorType == PNG_COLOR_TYPE_PALETTE
+        && poDS->nBands > 1 )
+    {
+        CPLDebug( "GDAL", "PNG Driver got %d from png_get_channels(),\n"
+                  "but this kind of image (paletted) can only have one band.\n"
+                  "Correcting and continuing, but this may indicate a bug!",
+                  poDS->nBands );
+        poDS->nBands = 1;
+    }
+
+    // We want to treat 1-, 2-, and 4-bit images as eight bit. This call causes
+    // libpng to unpack the image.
+    if( poDS->nBitDepth < 8 )
+        png_set_packing( poDS->hPNG );
+
+    // Create band information objects.
+    for( int iBand = 0; iBand < poDS->nBands; iBand++ )
+        poDS->SetBand( iBand+1, new PNGRasterBand( poDS, iBand+1 ) );
+
+    // Is there a palette?  Note: we should also read back and apply
+    // transparency values if available.
+    if( poDS->nColorType == PNG_COLOR_TYPE_PALETTE )
+    {
+        png_color *pasPNGPalette = NULL;
+        int nColorCount = 0;
+
+        if( png_get_PLTE( poDS->hPNG, poDS->psPNGInfo,
+                          &pasPNGPalette, &nColorCount ) == 0 )
+            nColorCount = 0;
+
+        unsigned char *trans = NULL;
+        png_color_16 *trans_values = NULL;
+        int num_trans = 0;
+        png_get_tRNS( poDS->hPNG, poDS->psPNGInfo,
+                      &trans, &num_trans, &trans_values );
+
+        poDS->poColorTable = new GDALColorTable();
+
+        GDALColorEntry oEntry;
+        int nNoDataIndex = -1;
+        for( int iColor = nColorCount - 1; iColor >= 0; iColor-- )
+        {
+            oEntry.c1 = pasPNGPalette[iColor].red;
+            oEntry.c2 = pasPNGPalette[iColor].green;
+            oEntry.c3 = pasPNGPalette[iColor].blue;
+
+            if( iColor < num_trans )
+            {
+                oEntry.c4 = trans[iColor];
+                if( oEntry.c4 == 0 )
+                {
+                    if( nNoDataIndex == -1 )
+                        nNoDataIndex = iColor;
+                    else
+                        nNoDataIndex = -2;
+                }
+            }
+            else
+                oEntry.c4 = 255;
+
+            poDS->poColorTable->SetColorEntry( iColor, &oEntry );
+        }
+
+        // Special hack to use an index as the no data value, as long as it is
+        // the only transparent color in the palette.
+        if( nNoDataIndex > -1 )
+        {
+            poDS->GetRasterBand(1)->SetNoDataValue(nNoDataIndex);
+        }
+    }
+
+    // Check for transparency values in greyscale images.
+    if( poDS->nColorType == PNG_COLOR_TYPE_GRAY )
+    {
+        png_color_16 *trans_values = NULL;
+        unsigned char *trans;
+        int num_trans;
+
+        if( png_get_tRNS( poDS->hPNG, poDS->psPNGInfo,
+                          &trans, &num_trans, &trans_values ) != 0
+            && trans_values != NULL )
+        {
+            poDS->GetRasterBand(1)->SetNoDataValue(trans_values->gray);
+        }
+    }
+
+    // Check for nodata color for RGB images.
+    if( poDS->nColorType == PNG_COLOR_TYPE_RGB )
+    {
+        png_color_16 *trans_values = NULL;
+        unsigned char *trans;
+        int num_trans;
+
+        if( png_get_tRNS( poDS->hPNG, poDS->psPNGInfo,
+                          &trans, &num_trans, &trans_values ) != 0
+            && trans_values != NULL )
+        {
+            CPLString oNDValue;
+
+            oNDValue.Printf( "%d %d %d",
+                    trans_values->red,
+                    trans_values->green,
+                    trans_values->blue );
+            poDS->SetMetadataItem( "NODATA_VALUES", oNDValue.c_str() );
+
+            poDS->GetRasterBand(1)->SetNoDataValue(trans_values->red);
+            poDS->GetRasterBand(2)->SetNoDataValue(trans_values->green);
+            poDS->GetRasterBand(3)->SetNoDataValue(trans_values->blue);
+        }
+    }
+
+    // Extract any text chunks as "metadata."
+    poDS->CollectMetadata();
+
+    // More metadata.
+    if( poDS->nBands > 1 )
+    {
+        poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" );
+    }
+
+    // Initialize any PAM information.
+    poDS->SetDescription( poOpenInfo->pszFilename );
+    poDS->TryLoadXML( poOpenInfo->GetSiblingFiles() );
+
+    // Open overviews.
+    poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename,
+                                 poOpenInfo->GetSiblingFiles() );
+
+    return poDS;
+}
+
+/************************************************************************/
+/*                        LoadWorldFile()                               */
+/************************************************************************/
+
+void PNGDataset::LoadWorldFile()
+{
+    if (bHasTriedLoadWorldFile)
+        return;
+    bHasTriedLoadWorldFile = TRUE;
+
+    char* pszWldFilename = NULL;
+    bGeoTransformValid =
+        GDALReadWorldFile2( GetDescription(), NULL,
+                            adfGeoTransform, oOvManager.GetSiblingFiles(),
+                            &pszWldFilename);
+
+    if( !bGeoTransformValid )
+        bGeoTransformValid =
+            GDALReadWorldFile2( GetDescription(), ".wld",
+                                adfGeoTransform, oOvManager.GetSiblingFiles(),
+                                &pszWldFilename);
+
+    if (pszWldFilename)
+    {
+        osWldFilename = pszWldFilename;
+        CPLFree(pszWldFilename);
+    }
+}
+
+/************************************************************************/
+/*                            GetFileList()                             */
+/************************************************************************/
+
+char **PNGDataset::GetFileList()
+
+{
+    char **papszFileList = GDALPamDataset::GetFileList();
+
+    LoadWorldFile();
+
+    if (!osWldFilename.empty() &&
+        CSLFindString(papszFileList, osWldFilename) == -1)
+    {
+        papszFileList = CSLAddString( papszFileList, osWldFilename );
+    }
+
+    return papszFileList;
+}
+
+/************************************************************************/
+/*                          WriteMetadataAsText()                       */
+/************************************************************************/
+
+#if defined(PNG_iTXt_SUPPORTED) || ((PNG_LIBPNG_VER_MAJOR == 1 && 
PNG_LIBPNG_VER_MINOR >= 4) || PNG_LIBPNG_VER_MAJOR > 1)
+#define HAVE_ITXT_SUPPORT
+#endif
+
+#ifdef HAVE_ITXT_SUPPORT
+static bool IsASCII(const char* pszStr)
+{
+    for(int i=0;pszStr[i]!='\0';i++)
+    {
+        if( reinterpret_cast<GByte *>(
+            const_cast<char *>( pszStr ) )[i] >= 128 )
+            return false;
+    }
+    return true;
+}
+#endif
+
+void PNGDataset::WriteMetadataAsText(png_structp hPNG, png_infop psPNGInfo,
+                                     const char* pszKey, const char* pszValue)
+{
+    png_text sText;
+    memset(&sText, 0, sizeof(png_text));
+    sText.compression = PNG_TEXT_COMPRESSION_NONE;
+    sText.key = (png_charp) pszKey;
+    sText.text = (png_charp) pszValue;
+#ifdef HAVE_ITXT_SUPPORT
+    // UTF-8 values should be written in iTXt, whereas TEXT should be LATIN-1.
+    if( !IsASCII(pszValue) && CPLIsUTF8(pszValue, -1) )
+        sText.compression = PNG_ITXT_COMPRESSION_NONE;
+#endif
+    png_set_text(hPNG, psPNGInfo, &sText, 1);
+}
+
+/************************************************************************/
+/*                             CreateCopy()                             */
+/************************************************************************/
+
+GDALDataset *
+PNGDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
+               int bStrict, char ** papszOptions,
+               GDALProgressFunc pfnProgress, void * pProgressData )
+
+{
+    // Perform some rudimentary checks.
+    const int nBands = poSrcDS->GetRasterCount();
+    if( nBands != 1 && nBands != 2 && nBands != 3 && nBands != 4 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported,
+                  "PNG driver doesn't support %d bands.  Must be 1 (grey),\n"
+                  "2 (grey+alpha), 3 (rgb) or 4 (rgba) bands.\n",
+                  nBands );
+
+        return NULL;
+    }
+
+    if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte
+        && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16 )
+    {
+        CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
+                  "PNG driver doesn't support data type %s. "
+                  "Only eight bit (Byte) and sixteen bit (UInt16) bands 
supported. %s\n",
+                  GDALGetDataTypeName(
+                      poSrcDS->GetRasterBand(1)->GetRasterDataType()),
+                  (bStrict) ? "" : "Defaulting to Byte" );
+
+        if (bStrict)
+            return NULL;
+    }
+
+    // Create the dataset.
+    VSILFILE *fpImage = VSIFOpenL( pszFilename, "wb" );
+    if( fpImage == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed,
+                  "Unable to create png file %s.\n",
+                  pszFilename );
+        return NULL;
+    }
+
+    // Initialize PNG access to the file.
+    jmp_buf     sSetJmpContext;
+
+    png_structp hPNG = png_create_write_struct(
+        PNG_LIBPNG_VER_STRING, &sSetJmpContext, png_gdal_error, 
png_gdal_warning );
+    png_infop  psPNGInfo = png_create_info_struct( hPNG );
+
+    if( setjmp( sSetJmpContext ) != 0 )
+    {
+        VSIFCloseL( fpImage );
+        png_destroy_write_struct( &hPNG, &psPNGInfo );
+        return NULL;
+    }
+
+    // Set up some parameters.
+    int  nColorType=0;
+
+    if( nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() == NULL )
+        nColorType = PNG_COLOR_TYPE_GRAY;
+    else if( nBands == 1 )
+        nColorType = PNG_COLOR_TYPE_PALETTE;
+    else if( nBands == 2 )
+        nColorType = PNG_COLOR_TYPE_GRAY_ALPHA;
+    else if( nBands == 3 )
+        nColorType = PNG_COLOR_TYPE_RGB;
+    else if( nBands == 4 )
+        nColorType = PNG_COLOR_TYPE_RGB_ALPHA;
+
+    int nBitDepth;
+    GDALDataType eType;
+    if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16 )
+    {
+        eType = GDT_Byte;
+        nBitDepth = 8;
+        if( nBands == 1 )
+        {
+            const char* pszNbits = poSrcDS->GetRasterBand(1)->GetMetadataItem(
+                                                    "NBITS", 
"IMAGE_STRUCTURE");
+            if( pszNbits != NULL )
+            {
+                nBitDepth = atoi(pszNbits);
+                if( !(nBitDepth == 1 || nBitDepth == 2 || nBitDepth == 4) )
+                    nBitDepth = 8;
+            }
+        }
+    }
+    else
+    {
+        eType = GDT_UInt16;
+        nBitDepth = 16;
+    }
+
+    const char* pszNbits = CSLFetchNameValue(papszOptions, "NBITS");
+    if( eType == GDT_Byte && pszNbits != NULL )
+    {
+        nBitDepth = atoi(pszNbits);
+        if( !(nBitDepth == 1 || nBitDepth == 2 || nBitDepth == 4 || nBitDepth 
== 8) )
+        {
+            CPLError(CE_Warning, CPLE_NotSupported, "Invalid bit depth. Using 
8");
+            nBitDepth = 8;
+        }
+    }
+
+    png_set_write_fn( hPNG, fpImage, png_vsi_write_data, png_vsi_flush );
+
+    const int nXSize = poSrcDS->GetRasterXSize();
+    const int nYSize = poSrcDS->GetRasterYSize();
+
+    png_set_IHDR( hPNG, psPNGInfo, nXSize, nYSize,
+                  nBitDepth, nColorType, PNG_INTERLACE_NONE,
+                  PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE );
+
+    // Do we want to control the compression level?
+    const char *pszLevel = CSLFetchNameValue( papszOptions, "ZLEVEL" );
+
+    if( pszLevel )
+    {
+        const int nLevel = atoi(pszLevel);
+        if( nLevel < 1 || nLevel > 9 )
+        {
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "Illegal ZLEVEL value '%s', should be 1-9.",
+                      pszLevel );
+            return NULL;
+        }
+
+        png_set_compression_level( hPNG, nLevel );
+    }
+
+    // Try to handle nodata values as a tRNS block (note that for paletted
+    // images, we save the effect to apply as part of palette).
+    png_color_16 sTRNSColor;
+
+    // Gray nodata.
+    if( nColorType == PNG_COLOR_TYPE_GRAY )
+    {
+       int bHaveNoData = FALSE;
+       const double dfNoDataValue
+           = poSrcDS->GetRasterBand(1)->GetNoDataValue( &bHaveNoData );
+
+       if ( bHaveNoData && dfNoDataValue >= 0 && dfNoDataValue < 65536 )
+       {
+          sTRNSColor.gray = (png_uint_16) dfNoDataValue;
+          png_set_tRNS( hPNG, psPNGInfo, NULL, 0, &sTRNSColor );
+       }
+    }
+
+    // RGB nodata.
+    if( nColorType == PNG_COLOR_TYPE_RGB )
+    {
+       // First try to use the NODATA_VALUES metadata item.
+       if ( poSrcDS->GetMetadataItem( "NODATA_VALUES" ) != NULL )
+       {
+           char **papszValues = CSLTokenizeString(
+               poSrcDS->GetMetadataItem( "NODATA_VALUES" ) );
+
+           if( CSLCount(papszValues) >= 3 )
+           {
+               sTRNSColor.red   = (png_uint_16) atoi(papszValues[0]);
+               sTRNSColor.green = (png_uint_16) atoi(papszValues[1]);
+               sTRNSColor.blue  = (png_uint_16) atoi(papszValues[2]);
+               png_set_tRNS( hPNG, psPNGInfo, NULL, 0, &sTRNSColor );
+           }
+
+           CSLDestroy( papszValues );
+       }
+       // Otherwise, get the nodata value from the bands.
+       else
+       {
+          int bHaveNoDataRed = FALSE;
+          const double dfNoDataValueRed
+              = poSrcDS->GetRasterBand(1)->GetNoDataValue( &bHaveNoDataRed );
+
+          int bHaveNoDataGreen = FALSE;
+          const double dfNoDataValueGreen
+              = poSrcDS->GetRasterBand(2)->GetNoDataValue( &bHaveNoDataGreen );
+
+          int bHaveNoDataBlue = FALSE;
+          const double dfNoDataValueBlue
+              = poSrcDS->GetRasterBand(3)->GetNoDataValue( &bHaveNoDataBlue );
+
+          if ( ( bHaveNoDataRed && dfNoDataValueRed >= 0 && dfNoDataValueRed < 
65536 ) &&
+               ( bHaveNoDataGreen && dfNoDataValueGreen >= 0 && 
dfNoDataValueGreen < 65536 ) &&
+               ( bHaveNoDataBlue && dfNoDataValueBlue >= 0 && 
dfNoDataValueBlue < 65536 ) )
+          {
+             sTRNSColor.red   = static_cast<png_uint_16>( dfNoDataValueRed );
+             sTRNSColor.green = static_cast<png_uint_16>( dfNoDataValueGreen );
+             sTRNSColor.blue  = static_cast<png_uint_16>( dfNoDataValueBlue );
+             png_set_tRNS( hPNG, psPNGInfo, NULL, 0, &sTRNSColor );
+          }
+       }
+    }
+
+    // Copy color profile data.
+    const char *pszICCProfile = CSLFetchNameValue(papszOptions, 
"SOURCE_ICC_PROFILE");
+    const char *pszICCProfileName = CSLFetchNameValue(papszOptions, 
"SOURCE_ICC_PROFILE_NAME");
+    if (pszICCProfileName == NULL)
+        pszICCProfileName = poSrcDS->GetMetadataItem( 
"SOURCE_ICC_PROFILE_NAME", "COLOR_PROFILE" );
+
+    if (pszICCProfile == NULL)
+        pszICCProfile = poSrcDS->GetMetadataItem( "SOURCE_ICC_PROFILE", 
"COLOR_PROFILE" );
+
+    if ((pszICCProfileName != NULL) && EQUAL(pszICCProfileName, "sRGB"))
+    {
+        pszICCProfile = NULL;
+
+        png_set_sRGB(hPNG, psPNGInfo, PNG_sRGB_INTENT_PERCEPTUAL);
+    }
+
+    if (pszICCProfile != NULL)
+    {
+        char *pEmbedBuffer = CPLStrdup(pszICCProfile);
+        png_uint_32 nEmbedLen
+            = CPLBase64DecodeInPlace(reinterpret_cast<GByte *>( pEmbedBuffer ) 
);
+        const char* pszLocalICCProfileName = 
(pszICCProfileName!=NULL)?pszICCProfileName:"ICC Profile";
+
+        png_set_iCCP(hPNG, psPNGInfo,
+#if (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR > 4) || 
PNG_LIBPNG_VER_MAJOR > 1
+            pszLocalICCProfileName,
+#else
+            (png_charp)pszLocalICCProfileName,
+#endif
+            0,
+#if (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR > 4) || 
PNG_LIBPNG_VER_MAJOR > 1
+            (png_const_bytep)pEmbedBuffer,
+#else
+            (png_charp)pEmbedBuffer,
+#endif
+            nEmbedLen);
+
+        CPLFree(pEmbedBuffer);
+    }
+    else if ((pszICCProfileName == NULL) || !EQUAL(pszICCProfileName, "sRGB"))
+    {
+        // Output gamma, primaries and whitepoint.
+        const char *pszGamma = CSLFetchNameValue(papszOptions, "PNG_GAMMA");
+        if (pszGamma == NULL)
+            pszGamma = poSrcDS->GetMetadataItem( "PNG_GAMMA", "COLOR_PROFILE" 
);
+
+        if (pszGamma != NULL)
+        {
+            double dfGamma = CPLAtof(pszGamma);
+            png_set_gAMA(hPNG, psPNGInfo, dfGamma);
+        }
+
+        const char *pszPrimariesRed = CSLFetchNameValue(papszOptions, 
"SOURCE_PRIMARIES_RED");
+        if (pszPrimariesRed == NULL)
+            pszPrimariesRed = poSrcDS->GetMetadataItem( 
"SOURCE_PRIMARIES_RED", "COLOR_PROFILE" );
+        const char *pszPrimariesGreen = CSLFetchNameValue(papszOptions, 
"SOURCE_PRIMARIES_GREEN");
+        if (pszPrimariesGreen == NULL)
+            pszPrimariesGreen = poSrcDS->GetMetadataItem( 
"SOURCE_PRIMARIES_GREEN", "COLOR_PROFILE" );
+        const char *pszPrimariesBlue = CSLFetchNameValue(papszOptions, 
"SOURCE_PRIMARIES_BLUE");
+        if (pszPrimariesBlue == NULL)
+            pszPrimariesBlue = poSrcDS->GetMetadataItem( 
"SOURCE_PRIMARIES_BLUE", "COLOR_PROFILE" );
+        const char *pszWhitepoint = CSLFetchNameValue(papszOptions, 
"SOURCE_WHITEPOINT");
+        if (pszWhitepoint == NULL)
+            pszWhitepoint = poSrcDS->GetMetadataItem( "SOURCE_WHITEPOINT", 
"COLOR_PROFILE" );
+
+        if ((pszPrimariesRed != NULL) && (pszPrimariesGreen != NULL) && 
(pszPrimariesBlue != NULL) &&
+            (pszWhitepoint != NULL))
+        {
+            bool bOk = true;
+            double faColour[8] = { 0.0 };
+            char** apapszTokenList[4] = { NULL };
+
+            apapszTokenList[0] = CSLTokenizeString2( pszWhitepoint, ",",
+                CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | 
CSLT_STRIPENDSPACES );
+            apapszTokenList[1] = CSLTokenizeString2( pszPrimariesRed, ",",
+                CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | 
CSLT_STRIPENDSPACES );
+            apapszTokenList[2] = CSLTokenizeString2( pszPrimariesGreen, ",",
+                CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | 
CSLT_STRIPENDSPACES );
+            apapszTokenList[3] = CSLTokenizeString2( pszPrimariesBlue, ",",
+                CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | 
CSLT_STRIPENDSPACES );
+
+            if ((CSLCount( apapszTokenList[0] ) == 3) &&
+                (CSLCount( apapszTokenList[1] ) == 3) &&
+                (CSLCount( apapszTokenList[2] ) == 3) &&
+                (CSLCount( apapszTokenList[3] ) == 3))
+            {
+                for( int i = 0; i < 4; i++ )
+                {
+                    for( int j = 0; j < 3; j++ )
+                    {
+                        const double v = CPLAtof(apapszTokenList[i][j]);
+
+                        if (j == 2)
+                        {
+                            /* Last term of xyY colour must be 1.0 */
+                            if (v != 1.0)
+                            {
+                                bOk = false;
+                                break;
+                            }
+                        }
+                        else
+                        {
+                            faColour[i*2 + j] = v;
+                        }
+                    }
+                    if (!bOk)
+                        break;
+                }
+
+                if (bOk)
+                {
+                    png_set_cHRM(hPNG, psPNGInfo,
+                        faColour[0], faColour[1],
+                        faColour[2], faColour[3],
+                        faColour[4], faColour[5],
+                        faColour[6], faColour[7]);
+                }
+            }
+
+            CSLDestroy( apapszTokenList[0] );
+            CSLDestroy( apapszTokenList[1] );
+            CSLDestroy( apapszTokenList[2] );
+            CSLDestroy( apapszTokenList[3] );
+        }
+    }
+
+    // Write the palette if there is one. Technically, it may be possible to
+    // write 16-bit palettes for PNG, but for now, this is omitted.
+    if( nColorType == PNG_COLOR_TYPE_PALETTE )
+    {
+        int bHaveNoData = FALSE;
+        double dfNoDataValue
+            = poSrcDS->GetRasterBand(1)->GetNoDataValue( &bHaveNoData );
+
+        GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
+
+        int nEntryCount = poCT->GetColorEntryCount();
+        int nMaxEntryCount = 1 << nBitDepth;
+        if( nEntryCount > nMaxEntryCount )
+            nEntryCount = nMaxEntryCount;
+
+        png_color *pasPNGColors = reinterpret_cast<png_color *>(
+            CPLMalloc( sizeof(png_color) * nEntryCount ) );
+
+        GDALColorEntry sEntry;
+        bool bFoundTrans = false;
+        for( int iColor = 0; iColor < nEntryCount; iColor++ )
+        {
+            poCT->GetColorEntryAsRGB( iColor, &sEntry );
+            if( sEntry.c4 != 255 )
+                bFoundTrans = true;
+
+            pasPNGColors[iColor].red = static_cast<png_byte>( sEntry.c1 );
+            pasPNGColors[iColor].green = static_cast<png_byte>( sEntry.c2 );
+            pasPNGColors[iColor].blue = static_cast<png_byte>( sEntry.c3 );
+        }
+
+        png_set_PLTE( hPNG, psPNGInfo, pasPNGColors,
+                      nEntryCount );
+
+        CPLFree( pasPNGColors );
+
+        // If we have transparent elements in the palette, we need to write a
+        // transparency block.
+        if( bFoundTrans || bHaveNoData )
+        {
+            unsigned char *pabyAlpha
+                = reinterpret_cast<unsigned char *>(
+                    CPLMalloc(nEntryCount) );
+
+            for( int iColor = 0; iColor < nEntryCount; iColor++ )
+            {
+                poCT->GetColorEntryAsRGB( iColor, &sEntry );
+                pabyAlpha[iColor] = static_cast<unsigned char>( sEntry.c4 );
+
+                if( bHaveNoData && iColor == static_cast<int>( dfNoDataValue ) 
)
+                    pabyAlpha[iColor] = 0;
+            }
+
+            png_set_tRNS( hPNG, psPNGInfo, pabyAlpha,
+                          nEntryCount, NULL );
+
+            CPLFree( pabyAlpha );
+        }
+    }
+
+    // Add text info.
+    // These are predefined keywords. See "4.2.7 tEXt Textual data" of
+    // http://www.w3.org/TR/PNG-Chunks.html for more information.
+    const char* apszKeywords[] = { "Title", "Author", "Description", 
"Copyright",
+                                   "Creation Time", "Software", "Disclaimer",
+                                   "Warning", "Source", "Comment", NULL };
+    const bool bWriteMetadataAsText = CPLTestBool(
+        CSLFetchNameValueDef(papszOptions, "WRITE_METADATA_AS_TEXT", "FALSE"));
+    for(int i=0;apszKeywords[i]!=NULL;i++)
+    {
+        const char* pszKey = apszKeywords[i];
+        const char* pszValue = CSLFetchNameValue(papszOptions, pszKey);
+        if( pszValue == NULL && bWriteMetadataAsText )
+            pszValue = poSrcDS->GetMetadataItem(pszKey);
+        if( pszValue != NULL )
+        {
+            WriteMetadataAsText(hPNG, psPNGInfo, pszKey, pszValue);
+        }
+    }
+    if( bWriteMetadataAsText )
+    {
+        char** papszSrcMD = poSrcDS->GetMetadata();
+        for( ; papszSrcMD && *papszSrcMD; papszSrcMD++ )
+        {
+            char* pszKey = NULL;
+            const char* pszValue = CPLParseNameValue(*papszSrcMD, &pszKey );
+            if( pszKey && pszValue )
+            {
+                if( CSLFindString(const_cast<char**>( apszKeywords ), pszKey) 
< 0 &&
+                    !EQUAL(pszKey, "AREA_OR_POINT") && !EQUAL(pszKey, 
"NODATA_VALUES") )
+                {
+                    WriteMetadataAsText(hPNG, psPNGInfo, pszKey, pszValue);
+                }
+                CPLFree(pszKey);
+            }
+        }
+    }
+
+    // Write the PNG info.
+    png_write_info( hPNG, psPNGInfo );
+
+    if( nBitDepth < 8 )
+        png_set_packing( hPNG );
+
+    // Loop over the image, copying image data.
+    CPLErr      eErr = CE_None;
+    const int nWordSize = GDALGetDataTypeSize(eType) / 8;
+
+    GByte *pabyScanline = reinterpret_cast<GByte *>(
+        CPLMalloc( nBands * nXSize * nWordSize ) );
+
+    for( int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++ )
+    {
+        png_bytep       row = pabyScanline;
+
+        eErr = poSrcDS->RasterIO( GF_Read, 0, iLine, nXSize, 1,
+                                  pabyScanline,
+                                  nXSize, 1, eType,
+                                  nBands, NULL,
+                                  nBands * nWordSize,
+                                  nBands * nXSize * nWordSize,
+                                  nWordSize,
+                                  NULL );
+
+#ifdef CPL_LSB
+        if( nBitDepth == 16 )
+            GDALSwapWords( row, 2, nXSize * nBands, 2 );
+#endif
+        if( eErr == CE_None )
+            png_write_rows( hPNG, &row, 1 );
+
+        if( eErr == CE_None
+            && !pfnProgress( (iLine+1) / static_cast<double>( nYSize ),
+                             NULL, pProgressData ) )
+        {
+            eErr = CE_Failure;
+            CPLError( CE_Failure, CPLE_UserInterrupt,
+                      "User terminated CreateCopy()" );
+        }
+    }
+
+    CPLFree( pabyScanline );
+
+    png_write_end( hPNG, psPNGInfo );
+    png_destroy_write_struct( &hPNG, &psPNGInfo );
+
+    VSIFCloseL( fpImage );
+
+    if( eErr != CE_None )
+        return NULL;
+
+    // Do we need a world file?
+    if( CPLFetchBool( papszOptions, "WORLDFILE", false ) )
+    {
+      double adfGeoTransform[6];
+
+      if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None )
+        GDALWriteWorldFile( pszFilename, "wld", adfGeoTransform );
+    }
+
+    // Re-open dataset and copy any auxiliary PAM information.
+
+    /* If writing to stdout, we can't reopen it, so return */
+    /* a fake dataset to make the caller happy */
+    if( CPLTestBool(CPLGetConfigOption("GDAL_OPEN_AFTER_COPY", "YES")) )
+    {
+        CPLPushErrorHandler(CPLQuietErrorHandler);
+        GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
+        PNGDataset *poDS = reinterpret_cast<PNGDataset *>(
+            PNGDataset::Open( &oOpenInfo ) );
+        CPLPopErrorHandler();
+        if( poDS )
+        {
+            int nFlags = GCIF_PAM_DEFAULT;
+            if( bWriteMetadataAsText )
+                nFlags &= ~GCIF_METADATA;
+            poDS->CloneInfo( poSrcDS, nFlags );
+            return poDS;
+        }
+        CPLErrorReset();
+    }
+
+    PNGDataset* poPNG_DS = new PNGDataset();
+    poPNG_DS->nRasterXSize = nXSize;
+    poPNG_DS->nRasterYSize = nYSize;
+    poPNG_DS->nBitDepth = nBitDepth;
+    for(int i=0;i<nBands;i++)
+        poPNG_DS->SetBand( i+1, new PNGRasterBand( poPNG_DS, i+1) );
+    return poPNG_DS;
+}
+
+/************************************************************************/
+/*                         png_vsi_read_data()                          */
+/*                                                                      */
+/*      Read data callback through VSI.                                 */
+/************************************************************************/
+static void
+png_vsi_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+
+{
+    // fread() returns 0 on error, so it is OK to store this in a png_size_t
+    // instead of an int, which is what fread() actually returns.
+    const png_size_t check
+        = static_cast<png_size_t>(
+            VSIFReadL(data, (png_size_t)1, length,
+                      reinterpret_cast<VSILFILE *>( png_get_io_ptr(png_ptr) ) 
) );
+
+    if (check != length)
+        png_error(png_ptr, "Read Error");
+}
+
+/************************************************************************/
+/*                         png_vsi_write_data()                         */
+/************************************************************************/
+
+static void
+png_vsi_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+    const size_t check
+        = VSIFWriteL(data, 1, length, reinterpret_cast<VSILFILE *>(
+            png_get_io_ptr(png_ptr) ) );
+
+    if (check != length)
+      png_error(png_ptr, "Write Error");
+}
+
+/************************************************************************/
+/*                           png_vsi_flush()                            */
+/************************************************************************/
+static void png_vsi_flush(png_structp png_ptr)
+{
+    VSIFFlushL( reinterpret_cast<VSILFILE *>( png_get_io_ptr(png_ptr) ) );
+}
+
+/************************************************************************/
+/*                           png_gdal_error()                           */
+/************************************************************************/
+
+static void png_gdal_error( png_structp png_ptr, const char *error_message )
+{
+    CPLError( CE_Failure, CPLE_AppDefined,
+              "libpng: %s", error_message );
+
+    // Use longjmp instead of a C++ exception, because libpng is generally not
+    // built as C++ and so will not honor unwind semantics.
+
+    jmp_buf* psSetJmpContext = reinterpret_cast<jmp_buf *>(
+        png_get_error_ptr( png_ptr ) );
+    if (psSetJmpContext)
+    {
+        longjmp( *psSetJmpContext, 1 );
+    }
+}
+
+/************************************************************************/
+/*                          png_gdal_warning()                          */
+/************************************************************************/
+
+static void png_gdal_warning( CPL_UNUSED png_structp png_ptr,
+                              const char *error_message )
+{
+    CPLError( CE_Warning, CPLE_AppDefined,
+              "libpng: %s", error_message );
+}
+
+/************************************************************************/
+/*                          GDALRegister_PNG()                          */
+/************************************************************************/
+
+void GDALRegister_PNG()
+
+{
+    if( GDALGetDriverByName( "PNG" ) != NULL )
+        return;
+
+    GDALDriver *poDriver = new GDALDriver();
+
+    poDriver->SetDescription( "PNG" );
+    poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
+    poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
+                               "Portable Network Graphics" );
+    poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
+                               "frmt_various.html#PNG" );
+    poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "png" );
+    poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/png" );
+
+    poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
+                               "Byte UInt16" );
+    poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
+"<CreationOptionList>\n"
+"   <Option name='WORLDFILE' type='boolean' description='Create world file' 
default='FALSE'/>\n"
+"   <Option name='ZLEVEL' type='int' description='DEFLATE compression level 
1-9' default='6'/>\n"
+"   <Option name='SOURCE_ICC_PROFILE' type='string' description='ICC 
Profile'/>\n"
+"   <Option name='SOURCE_ICC_PROFILE_NAME' type='string' description='ICC 
Profile name'/>\n"
+"   <Option name='SOURCE_PRIMARIES_RED' type='string' description='x,y,1.0 
(xyY) red chromaticity'/>\n"
+"   <Option name='SOURCE_PRIMARIES_GREEN' type='string' description='x,y,1.0 
(xyY) green chromaticity'/>\n"
+"   <Option name='SOURCE_PRIMARIES_BLUE' type='string' description='x,y,1.0 
(xyY) blue chromaticity'/>\n"
+"   <Option name='SOURCE_WHITEPOINT' type='string' description='x,y,1.0 (xyY) 
whitepoint'/>\n"
+"   <Option name='PNG_GAMMA' type='string' description='Gamma'/>\n"
+"   <Option name='TITLE' type='string' description='Title'/>\n"
+"   <Option name='DESCRIPTION' type='string' description='Description'/>\n"
+"   <Option name='COPYRIGHT' type='string' description='Copyright'/>\n"
+"   <Option name='COMMENT' type='string' description='Comment'/>\n"
+"   <Option name='WRITE_METADATA_AS_TEXT' type='boolean' description='Whether 
to write source dataset metadata in TEXT chunks' default='FALSE'/>\n"
+"   <Option name='NBITS' type='int' description='Force output bit depth: 1, 2 
or 4'/>\n"
+"</CreationOptionList>\n" );
+
+    poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
+
+    poDriver->pfnOpen = PNGDataset::Open;
+    poDriver->pfnCreateCopy = PNGDataset::CreateCopy;
+    poDriver->pfnIdentify = PNGDataset::Identify;
+#ifdef SUPPORT_CREATE
+    poDriver->pfnCreate = PNGDataset::Create;
+#endif
+
+    GetGDALDriverManager()->RegisterDriver( poDriver );
+}
+
+#ifdef SUPPORT_CREATE
+/************************************************************************/
+/*                         IWriteBlock()                                */
+/************************************************************************/
+
+CPLErr PNGRasterBand::IWriteBlock(int x, int y, void* pvData)
+{
+    PNGDataset& ds = *reinterpret_cast<PNGDataset*>( poDS );
+
+    // Write the block (or consolidate into multichannel block) and then write.
+
+    const GDALDataType dt = GetRasterDataType();
+    const size_t wordsize = ds.m_nBitDepth / 8;
+    GDALCopyWords( pvData, dt, wordsize,
+                   ds.m_pabyBuffer + (nBand-1) * wordsize,
+                   dt, ds.nBands * wordsize,
+                   nBlockXSize );
+
+    // See if we have all the bands.
+    m_bBandProvided[nBand - 1] = TRUE;
+    for( size_t i = 0; i < static_cast<size_t>( ds.nBands ); i++ )
+    {
+        if(!m_bBandProvided[i])
+            return CE_None;
+    }
+
+    // We received all the bands, so reset band flags and write pixels out.
+    this->reset_band_provision_flags();
+
+    // If it's the first block, write out the file header.
+    if(x == 0 && y == 0)
+    {
+        CPLErr err = ds.write_png_header();
+        if(err != CE_None)
+            return err;
+    }
+
+#ifdef CPL_LSB
+    if( ds.m_nBitDepth == 16 )
+        GDALSwapWords( ds.m_pabyBuffer, 2, nBlockXSize * ds.nBands, 2 );
+#endif
+    png_write_rows( ds.m_hPNG, &ds.m_pabyBuffer, 1 );
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                          SetGeoTransform()                           */
+/************************************************************************/
+
+CPLErr PNGDataset::SetGeoTransform( double * padfTransform )
+{
+    memcpy( m_adfGeoTransform, padfTransform, sizeof(double) * 6 );
+
+    if ( m_pszFilename )
+    {
+        if ( GDALWriteWorldFile( m_pszFilename, "wld", m_adfGeoTransform )
+             == FALSE )
+        {
+            CPLError( CE_Failure, CPLE_FileIO, "Can't write world file." );
+            return CE_Failure;
+        }
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                           SetColorTable()                            */
+/************************************************************************/
+
+CPLErr PNGRasterBand::SetColorTable(GDALColorTable* poCT)
+{
+    if( poCT == NULL )
+        return CE_Failure;
+
+    // We get called even for grayscale files, since some formats need a 
palette
+    // even then. PNG doesn't, so if a gray palette is given, just ignore it.
+
+    GDALColorEntry sEntry;
+    for( size_t i = 0; i < static_cast<size_t>( poCT->GetColorEntryCount() ); 
i++ )
+    {
+        poCT->GetColorEntryAsRGB( i, &sEntry );
+        if( sEntry.c1 != sEntry.c2 || sEntry.c1 != sEntry.c3)
+        {
+            CPLErr err = GDALPamRasterBand::SetColorTable(poCT);
+            if(err != CE_None)
+                return err;
+
+            PNGDataset& ds = *reinterpret_cast<PNGDataset *>( poDS );
+            ds.m_nColorType = PNG_COLOR_TYPE_PALETTE;
+            break;
+            // band::IWriteBlock will emit color table as part of the header
+            // preceding the first block write.
+        }
+    }
+
+    return CE_None;
+}
+
+/************************************************************************/
+/*                  PNGDataset::write_png_header()                      */
+/************************************************************************/
+
+CPLErr PNGDataset::write_png_header()
+{
+    // Initialize PNG access to the file.
+    m_hPNG = png_create_write_struct(
+        PNG_LIBPNG_VER_STRING, NULL,
+        png_gdal_error, png_gdal_warning );
+
+    m_psPNGInfo = png_create_info_struct( m_hPNG );
+
+    png_set_write_fn( m_hPNG, m_fpImage, png_vsi_write_data, png_vsi_flush );
+
+    png_set_IHDR( m_hPNG, m_psPNGInfo, nRasterXSize, nRasterYSize,
+                  m_nBitDepth, m_nColorType, PNG_INTERLACE_NONE,
+                  PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT );
+
+    png_set_compression_level(m_hPNG, Z_BEST_COMPRESSION);
+
+    // png_set_swap_alpha(m_hPNG); // Use RGBA order, not ARGB.
+
+    // Try to handle nodata values as a tRNS block (note that for paletted
+    // images, we save the effect to apply as part of the palette).
+    //m_bHaveNoData = FALSE;
+    //m_dfNoDataValue = -1;
+    png_color_16 sTRNSColor;
+
+    int bHaveNoData = FALSE;
+    double dfNoDataValue = -1;
+
+    if( m_nColorType == PNG_COLOR_TYPE_GRAY )
+    {
+        dfNoDataValue = GetRasterBand(1)->GetNoDataValue( &bHaveNoData );
+
+        if ( bHaveNoData && dfNoDataValue >= 0 && dfNoDataValue < 65536 )
+        {
+            sTRNSColor.gray = static_cast<png_uint_16>( dfNoDataValue );
+            png_set_tRNS( m_hPNG, m_psPNGInfo, NULL, 0, &sTRNSColor );
+        }
+    }
+
+    // RGB nodata.
+    if( nColorType == PNG_COLOR_TYPE_RGB )
+    {
+        // First, try to use the NODATA_VALUES metadata item.
+        if ( GetMetadataItem( "NODATA_VALUES" ) != NULL )
+        {
+            char **papszValues = CSLTokenizeString(
+                GetMetadataItem( "NODATA_VALUES" ) );
+
+            if( CSLCount(papszValues) >= 3 )
+            {
+                sTRNSColor.red   = static_cast<png_uint_16>( 
atoi(papszValues[0] ) );
+                sTRNSColor.green = static_cast<png_uint_16>( 
atoi(papszValues[1] ) );
+                sTRNSColor.blue  = static_cast<png_uint_16>( 
atoi(papszValues[2] ) );
+                png_set_tRNS( m_hPNG, m_psPNGInfo, NULL, 0, &sTRNSColor );
+            }
+
+            CSLDestroy( papszValues );
+        }
+        // Otherwise, get the nodata value from the bands.
+        else
+        {
+            int bHaveNoDataRed = FALSE;
+            const double dfNoDataValueRed
+                = GetRasterBand(1)->GetNoDataValue( &bHaveNoDataRed );
+
+            int bHaveNoDataGreen = FALSE;
+            const double dfNoDataValueGreen
+                = GetRasterBand(2)->GetNoDataValue( &bHaveNoDataGreen );
+
+            int bHaveNoDataBlue = FALSE;
+            const double dfNoDataValueBlue
+                = GetRasterBand(3)->GetNoDataValue( &bHaveNoDataBlue );
+
+            if ( ( bHaveNoDataRed && dfNoDataValueRed >= 0 && dfNoDataValueRed 
< 65536 ) &&
+                 ( bHaveNoDataGreen && dfNoDataValueGreen >= 0 && 
dfNoDataValueGreen < 65536 ) &&
+                 ( bHaveNoDataBlue && dfNoDataValueBlue >= 0 && 
dfNoDataValueBlue < 65536 ) )
+            {
+                sTRNSColor.red   = static_cast<png_uint_16>( dfNoDataValueRed 
);
+                sTRNSColor.green = static_cast<png_uint_16>( 
dfNoDataValueGreen );
+                sTRNSColor.blue  = static_cast<png_uint_16>( dfNoDataValueBlue 
);
+                png_set_tRNS( m_hPNG, m_psPNGInfo, NULL, 0, &sTRNSColor );
+            }
+        }
+    }
+
+    // Write the palette if there is one. Technically, it may be possible
+    // to write 16-bit palettes for PNG, but for now, doing so is omitted.
+    if( nColorType == PNG_COLOR_TYPE_PALETTE )
+    {
+        GDALColorTable *poCT = GetRasterBand(1)->GetColorTable();
+
+        int bHaveNoData = FALSE;
+        double dfNoDataValue = GetRasterBand(1)->GetNoDataValue( &bHaveNoData 
);
+
+        m_pasPNGColors = reinterpret_cast<png_color *>(
+            CPLMalloc( sizeof(png_color) * poCT->GetColorEntryCount() ) );
+
+        GDALColorEntry sEntry;
+        bool bFoundTrans = false;
+        for( int iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ )
+        {
+            poCT->GetColorEntryAsRGB( iColor, &sEntry );
+            if( sEntry.c4 != 255 )
+                bFoundTrans = true;
+
+            m_pasPNGColors[iColor].red   = static_cast<png_byte>( sEntry.c1 );
+            m_pasPNGColors[iColor].green = static_cast<png_byte>( sEntry.c2 );
+            m_pasPNGColors[iColor].blue  = static_cast<png_byte>( sEntry.c3 );
+        }
+
+        png_set_PLTE( m_hPNG, m_psPNGInfo, m_pasPNGColors,
+                      poCT->GetColorEntryCount() );
+
+        // If we have transparent elements in the palette, we need to write a
+        // transparency block.
+        if( bFoundTrans || bHaveNoData )
+        {
+            m_pabyAlpha = reinterpret_cast<unsigned char *>(
+                CPLMalloc(poCT->GetColorEntryCount() ) );
+
+            for( int iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ 
)
+            {
+                poCT->GetColorEntryAsRGB( iColor, &sEntry );
+                m_pabyAlpha[iColor] = static_cast<unsigned char>( sEntry.c4 );
+
+                if( bHaveNoData && iColor == static_cast<int>( dfNoDataValue ) 
)
+                    m_pabyAlpha[iColor] = 0;
+            }
+
+            png_set_tRNS( m_hPNG, m_psPNGInfo, m_pabyAlpha,
+                          poCT->GetColorEntryCount(), NULL );
+        }
+    }
+
+    png_write_info( m_hPNG, m_psPNGInfo );
+    return CE_None;
+}
+
+/************************************************************************/
+/*                               Create()                               */
+/************************************************************************/
+
+GDALDataset *PNGDataset::Create
+(
+    const char* pszFilename,
+    int nXSize, int nYSize,
+    int nBands,
+    GDALDataType eType,
+    char **papszOptions
+)
+{
+    if( eType != GDT_Byte && eType != GDT_UInt16)
+    {
+        CPLError( CE_Failure, CPLE_AppDefined,
+                  "Attempt to create PNG dataset with an illegal\n"
+                  "data type (%s), only Byte and UInt16 supported by the 
format.\n",
+                  GDALGetDataTypeName(eType) );
+
+        return NULL;
+    }
+
+    if( nBands < 1 || nBands > 4 )
+    {
+        CPLError( CE_Failure, CPLE_NotSupported,
+                  "PNG driver doesn't support %d bands. "
+                  "Must be 1 (gray/indexed color),\n"
+                  "2 (gray+alpha), 3 (rgb) or 4 (rgba) bands.\n",
+                  nBands );
+
+        return NULL;
+    }
+
+    // Bands are:
+    // 1: Grayscale or indexed color.
+    // 2: Gray plus alpha.
+    // 3: RGB.
+    // 4: RGB plus alpha.
+
+    if(nXSize < 1 || nYSize < 1)
+    {
+        CPLError( CE_Failure, CPLE_NotSupported,
+                  "Specified pixel dimensions (% d x %d) are bad.\n",
+                  nXSize, nYSize );
+    }
+
+    // Set up some parameters.
+    PNGDataset* poDS = new PNGDataset();
+
+    poDS->nRasterXSize = nXSize;
+    poDS->nRasterYSize = nYSize;
+    poDS->eAccess = GA_Update;
+    poDS->nBands = nBands;
+
+    switch(nBands)
+    {
+      case 1:
+        poDS->m_nColorType = PNG_COLOR_TYPE_GRAY;
+        break;  // If a non-gray palette is set, we'll change this.
+
+      case 2:
+        poDS->m_nColorType = PNG_COLOR_TYPE_GRAY_ALPHA;
+        break;
+
+      case 3:
+        poDS->m_nColorType = PNG_COLOR_TYPE_RGB;
+        break;
+
+      case 4:
+        poDS->m_nColorType = PNG_COLOR_TYPE_RGB_ALPHA;
+        break;
+    }
+
+    poDS->m_nBitDepth = (eType == GDT_Byte ? 8 : 16);
+
+    poDS->m_pabyBuffer = reinterpret_cast<GByte *>(
+        CPLMalloc( nBands * nXSize * poDS->m_nBitDepth / 8 ) );
+
+    // Create band information objects.
+    for( int iBand = 1; iBand <= poDS->nBands; iBand++ )
+        poDS->SetBand( iBand, new PNGRasterBand( poDS, iBand ) );
+
+    // Do we need a world file?
+    if( CPLFetchBool( papszOptions, "WORLDFILE", false ) )
+        poDS->m_bGeoTransformValid = TRUE;
+
+    // Create the file.
+
+    poDS->m_fpImage = VSIFOpenL( pszFilename, "wb" );
+    if( poDS->m_fpImage == NULL )
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed,
+                  "Unable to create PNG file %s.\n",
+                  pszFilename );
+        delete poDS;
+        return NULL;
+    }
+
+    poDS->m_pszFilename = CPLStrdup(pszFilename);
+
+    return poDS;
+}
+
+#endif


Property changes on: brlcad/trunk/src/other/gdal/frmts/png/pngdataset.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
BRL-CAD Source Commits mailing list
brlcad-commits@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/brlcad-commits

Reply via email to