LCOV - code coverage report
Current view: directory - frmts/png - pngdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 536 471 87.9 %
Date: 2012-12-26 Functions: 33 26 78.8 %

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

Generated by: LCOV version 1.7