LCOV - code coverage report
Current view: directory - frmts/png - pngdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 543 473 87.1 %
Date: 2011-12-18 Functions: 34 27 79.4 %

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

Generated by: LCOV version 1.7