LTP GCOV extension - code coverage report
Current view: directory - frmts/png - pngdataset.cpp
Test: gdal_filtered.info
Date: 2010-07-12 Instrumented lines: 443
Code covered: 87.6 % Executed lines: 388

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

Generated by: LTP GCOV extension version 1.5