/* pngread.c - read a PNG file
*
* Last changed in libpng 1.6 .1 [ March 28 , 2013 ]
* Copyright ( c ) 1998 - 2013 Glenn Randers - Pehrson
* ( Version 0.96 Copyright ( c ) 1996 , 1997 Andreas Dilger )
* ( Version 0.88 Copyright ( c ) 1995 , 1996 Guy Eric Schalnat , Group 42 , Inc . )
*
* This code is released under the libpng license .
* For conditions of distribution and use , see the disclaimer
* and license in png . h
*
* This file contains routines that an application calls directly to
* read a PNG file or stream .
*/
# include "pngpriv.h"
# if defined(PNG_SIMPLIFIED_READ_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)
# include <errno.h>
# endif
# ifdef PNG_READ_SUPPORTED
/* Create a PNG structure for reading, and allocate any memory needed. */
PNG_FUNCTION ( png_structp , PNGAPI
png_create_read_struct , ( png_const_charp user_png_ver , png_voidp error_ptr ,
png_error_ptr error_fn , png_error_ptr warn_fn ) , PNG_ALLOCATED )
{
# ifndef PNG_USER_MEM_SUPPORTED
png_structp png_ptr = png_create_png_struct ( user_png_ver , error_ptr ,
error_fn , warn_fn , NULL , NULL , NULL ) ;
# else
return png_create_read_struct_2 ( user_png_ver , error_ptr , error_fn ,
warn_fn , NULL , NULL , NULL ) ;
}
/* Alternate create PNG structure for reading, and allocate any memory
* needed .
*/
PNG_FUNCTION ( png_structp , PNGAPI
png_create_read_struct_2 , ( png_const_charp user_png_ver , png_voidp error_ptr ,
png_error_ptr error_fn , png_error_ptr warn_fn , png_voidp mem_ptr ,
png_malloc_ptr malloc_fn , png_free_ptr free_fn ) , PNG_ALLOCATED )
{
png_structp png_ptr = png_create_png_struct ( user_png_ver , error_ptr ,
error_fn , warn_fn , mem_ptr , malloc_fn , free_fn ) ;
# endif /* PNG_USER_MEM_SUPPORTED */
if ( png_ptr ! = NULL )
{
png_ptr - > mode = PNG_IS_READ_STRUCT ;
/* Added in libpng-1.6.0; this can be used to detect a read structure if
* required ( it will be zero in a write structure . )
*/
# ifdef PNG_SEQUENTIAL_READ_SUPPORTED
png_ptr - > IDAT_read_size = PNG_IDAT_READ_SIZE ;
# endif
# ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED
png_ptr - > flags | = PNG_FLAG_BENIGN_ERRORS_WARN ;
/* In stable builds only warn if an application error can be completely
* handled .
*/
# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC
png_ptr - > flags | = PNG_FLAG_APP_WARNINGS_WARN ;
# endif
# endif
/* TODO: delay this, it can be done in png_init_io (if the app doesn't
* do it itself ) avoiding setting the default function if it is not
* required .
*/
png_set_read_fn ( png_ptr , NULL , NULL ) ;
}
return png_ptr ;
}
# ifdef PNG_SEQUENTIAL_READ_SUPPORTED
/* Read the information before the actual image data. This has been
* changed in v0 .90 to allow reading a file that already has the magic
* bytes read from the stream . You can tell libpng how many bytes have
* been read from the beginning of the stream ( up to the maximum of 8 )
* via png_set_sig_bytes ( ) , and we will only check the remaining bytes
* here . The application can then have access to the signature bytes we
* read if it is determined that this isn ' t a valid PNG file .
*/
void PNGAPI
png_read_info ( png_structrp png_ptr , png_inforp info_ptr )
{
# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
int keep ;
# endif
png_debug ( 1 , " in png_read_info " ) ;
if ( png_ptr = = NULL | | info_ptr = = NULL )
return ;
/* Read and check the PNG file signature. */
png_read_sig ( png_ptr , info_ptr ) ;
for ( ; ; )
{
png_uint_32 length = png_read_chunk_header ( png_ptr ) ;
png_uint_32 chunk_name = png_ptr - > chunk_name ;
/* IDAT logic needs to happen here to simplify getting the two flags
* right .
*/
if ( chunk_name = = png_IDAT )
{
if ( ! ( png_ptr - > mode & PNG_HAVE_IHDR ) )
png_chunk_error ( png_ptr , " Missing IHDR before IDAT " ) ;
else if ( png_ptr - > color_type = = PNG_COLOR_TYPE_PALETTE & &
! ( png_ptr - > mode & PNG_HAVE_PLTE ) )
png_chunk_error ( png_ptr , " Missing PLTE before IDAT " ) ;
else if ( png_ptr - > mode & PNG_AFTER_IDAT )
png_chunk_benign_error ( png_ptr , " Too many IDATs found " ) ;
png_ptr - > mode | = PNG_HAVE_IDAT ;
}
else if ( png_ptr - > mode & PNG_HAVE_IDAT )
png_ptr - > mode | = PNG_AFTER_IDAT ;
/* This should be a binary subdivision search or a hash for
* matching the chunk name rather than a linear search .
*/
if ( chunk_name = = png_IHDR )
png_handle_IHDR ( png_ptr , info_ptr , length ) ;
else if ( chunk_name = = png_IEND )
png_handle_IEND ( png_ptr , info_ptr , length ) ;
# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
else if ( ( keep = png_chunk_unknown_handling ( png_ptr , chunk_name ) ) ! = 0 )
{
png_handle_unknown ( png_ptr , info_ptr , length , keep ) ;
if ( chunk_name = = png_PLTE )
png_ptr - > mode | = PNG_HAVE_PLTE ;
else if ( chunk_name = = png_IDAT )
{
png_ptr - > idat_size = 0 ; /* It has been consumed */
break ;
}
}
# endif
else if ( chunk_name = = png_PLTE )
png_handle_PLTE ( png_ptr , info_ptr , length ) ;
else if ( chunk_name = = png_IDAT )
{
png_ptr - > idat_size = length ;
break ;
}
# ifdef PNG_READ_bKGD_SUPPORTED
else if ( chunk_name = = png_bKGD )
png_handle_bKGD ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_cHRM_SUPPORTED
else if ( chunk_name = = png_cHRM )
png_handle_cHRM ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_gAMA_SUPPORTED
else if ( chunk_name = = png_gAMA )
png_handle_gAMA ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_hIST_SUPPORTED
else if ( chunk_name = = png_hIST )
png_handle_hIST ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_oFFs_SUPPORTED
else if ( chunk_name = = png_oFFs )
png_handle_oFFs ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_pCAL_SUPPORTED
else if ( chunk_name = = png_pCAL )
png_handle_pCAL ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_sCAL_SUPPORTED
else if ( chunk_name = = png_sCAL )
png_handle_sCAL ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_pHYs_SUPPORTED
else if ( chunk_name = = png_pHYs )
png_handle_pHYs ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_sBIT_SUPPORTED
else if ( chunk_name = = png_sBIT )
png_handle_sBIT ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_sRGB_SUPPORTED
else if ( chunk_name = = png_sRGB )
png_handle_sRGB ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_iCCP_SUPPORTED
else if ( chunk_name = = png_iCCP )
png_handle_iCCP ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_sPLT_SUPPORTED
else if ( chunk_name = = png_sPLT )
png_handle_sPLT ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_tEXt_SUPPORTED
else if ( chunk_name = = png_tEXt )
png_handle_tEXt ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_tIME_SUPPORTED
else if ( chunk_name = = png_tIME )
png_handle_tIME ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_tRNS_SUPPORTED
else if ( chunk_name = = png_tRNS )
png_handle_tRNS ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_zTXt_SUPPORTED
else if ( chunk_name = = png_zTXt )
png_handle_zTXt ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_iTXt_SUPPORTED
else if ( chunk_name = = png_iTXt )
png_handle_iTXt ( png_ptr , info_ptr , length ) ;
# endif
else
png_handle_unknown ( png_ptr , info_ptr , length ,
PNG_HANDLE_CHUNK_AS_DEFAULT ) ;
}
}
# endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
/* Optional call to update the users info_ptr structure */
void PNGAPI
png_read_update_info ( png_structrp png_ptr , png_inforp info_ptr )
{
png_debug ( 1 , " in png_read_update_info " ) ;
if ( png_ptr ! = NULL )
{
if ( ( png_ptr - > flags & PNG_FLAG_ROW_INIT ) = = 0 )
{
png_read_start_row ( png_ptr ) ;
# ifdef PNG_READ_TRANSFORMS_SUPPORTED
png_read_transform_info ( png_ptr , info_ptr ) ;
# else
PNG_UNUSED ( info_ptr )
# endif
}
/* New in 1.6.0 this avoids the bug of doing the initializations twice */
else
png_app_error ( png_ptr ,
" png_read_update_info/png_start_read_image: duplicate call " ) ;
}
}
# ifdef PNG_SEQUENTIAL_READ_SUPPORTED
/* Initialize palette, background, etc, after transformations
* are set , but before any reading takes place . This allows
* the user to obtain a gamma - corrected palette , for example .
* If the user doesn ' t call this , we will do it ourselves .
*/
void PNGAPI
png_start_read_image ( png_structrp png_ptr )
{
png_debug ( 1 , " in png_start_read_image " ) ;
if ( png_ptr ! = NULL )
{
if ( ( png_ptr - > flags & PNG_FLAG_ROW_INIT ) = = 0 )
png_read_start_row ( png_ptr ) ;
/* New in 1.6.0 this avoids the bug of doing the initializations twice */
else
png_app_error ( png_ptr ,
" png_start_read_image/png_read_update_info: duplicate call " ) ;
}
}
# endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
# ifdef PNG_SEQUENTIAL_READ_SUPPORTED
void PNGAPI
png_read_row ( png_structrp png_ptr , png_bytep row , png_bytep dsp_row )
{
png_row_info row_info ;
if ( png_ptr = = NULL )
return ;
png_debug2 ( 1 , " in png_read_row (row %lu, pass %d) " ,
( unsigned long ) png_ptr - > row_number , png_ptr - > pass ) ;
/* png_read_start_row sets the information (in particular iwidth) for this
* interlace pass .
*/
if ( ! ( png_ptr - > flags & PNG_FLAG_ROW_INIT ) )
png_read_start_row ( png_ptr ) ;
/* 1.5.6: row_info moved out of png_struct to a local here. */
row_info . width = png_ptr - > iwidth ; /* NOTE: width of current interlaced row */
row_info . color_type = png_ptr - > color_type ;
row_info . bit_depth = png_ptr - > bit_depth ;
row_info . channels = png_ptr - > channels ;
row_info . pixel_depth = png_ptr - > pixel_depth ;
row_info . rowbytes = PNG_ROWBYTES ( row_info . pixel_depth , row_info . width ) ;
if ( png_ptr - > row_number = = 0 & & png_ptr - > pass = = 0 )
{
/* Check for transforms that have been set but were defined out */
# if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED)
if ( png_ptr - > transformations & PNG_INVERT_MONO )
png_warning ( png_ptr , " PNG_READ_INVERT_SUPPORTED is not defined " ) ;
# endif
# if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED)
if ( png_ptr - > transformations & PNG_FILLER )
png_warning ( png_ptr , " PNG_READ_FILLER_SUPPORTED is not defined " ) ;
# endif
# if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \
! defined ( PNG_READ_PACKSWAP_SUPPORTED )
if ( png_ptr - > transformations & PNG_PACKSWAP )
png_warning ( png_ptr , " PNG_READ_PACKSWAP_SUPPORTED is not defined " ) ;
# endif
# if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED)
if ( png_ptr - > transformations & PNG_PACK )
png_warning ( png_ptr , " PNG_READ_PACK_SUPPORTED is not defined " ) ;
# endif
# if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED)
if ( png_ptr - > transformations & PNG_SHIFT )
png_warning ( png_ptr , " PNG_READ_SHIFT_SUPPORTED is not defined " ) ;
# endif
# if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED)
if ( png_ptr - > transformations & PNG_BGR )
png_warning ( png_ptr , " PNG_READ_BGR_SUPPORTED is not defined " ) ;
# endif
# if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED)
if ( png_ptr - > transformations & PNG_SWAP_BYTES )
png_warning ( png_ptr , " PNG_READ_SWAP_SUPPORTED is not defined " ) ;
# endif
}
# ifdef PNG_READ_INTERLACING_SUPPORTED
/* If interlaced and we do not need a new row, combine row and return.
* Notice that the pixels we have from previous rows have been transformed
* already ; we can only combine like with like ( transformed or
* untransformed ) and , because of the libpng API for interlaced images , this
* means we must transform before de - interlacing .
*/
if ( png_ptr - > interlaced & & ( png_ptr - > transformations & PNG_INTERLACE ) )
{
switch ( png_ptr - > pass )
{
case 0 :
if ( png_ptr - > row_number & 0x07 )
{
if ( dsp_row ! = NULL )
png_combine_row ( png_ptr , dsp_row , 1 /*display*/ ) ;
png_read_finish_row ( png_ptr ) ;
return ;
}
break ;
case 1 :
if ( ( png_ptr - > row_number & 0x07 ) | | png_ptr - > width < 5 )
{
if ( dsp_row ! = NULL )
png_combine_row ( png_ptr , dsp_row , 1 /*display*/ ) ;
png_read_finish_row ( png_ptr ) ;
return ;
}
break ;
case 2 :
if ( ( png_ptr - > row_number & 0x07 ) ! = 4 )
{
if ( dsp_row ! = NULL & & ( png_ptr - > row_number & 4 ) )
png_combine_row ( png_ptr , dsp_row , 1 /*display*/ ) ;
png_read_finish_row ( png_ptr ) ;
return ;
}
break ;
case 3 :
if ( ( png_ptr - > row_number & 3 ) | | png_ptr - > width < 3 )
{
if ( dsp_row ! = NULL )
png_combine_row ( png_ptr , dsp_row , 1 /*display*/ ) ;
png_read_finish_row ( png_ptr ) ;
return ;
}
break ;
case 4 :
if ( ( png_ptr - > row_number & 3 ) ! = 2 )
{
if ( dsp_row ! = NULL & & ( png_ptr - > row_number & 2 ) )
png_combine_row ( png_ptr , dsp_row , 1 /*display*/ ) ;
png_read_finish_row ( png_ptr ) ;
return ;
}
break ;
case 5 :
if ( ( png_ptr - > row_number & 1 ) | | png_ptr - > width < 2 )
{
if ( dsp_row ! = NULL )
png_combine_row ( png_ptr , dsp_row , 1 /*display*/ ) ;
png_read_finish_row ( png_ptr ) ;
return ;
}
break ;
default :
case 6 :
if ( ! ( png_ptr - > row_number & 1 ) )
{
png_read_finish_row ( png_ptr ) ;
return ;
}
break ;
}
}
# endif
if ( ! ( png_ptr - > mode & PNG_HAVE_IDAT ) )
png_error ( png_ptr , " Invalid attempt to read row data " ) ;
/* Fill the row with IDAT data: */
png_read_IDAT_data ( png_ptr , png_ptr - > row_buf , row_info . rowbytes + 1 ) ;
if ( png_ptr - > row_buf [ 0 ] > PNG_FILTER_VALUE_NONE )
{
if ( png_ptr - > row_buf [ 0 ] < PNG_FILTER_VALUE_LAST )
png_read_filter_row ( png_ptr , & row_info , png_ptr - > row_buf + 1 ,
png_ptr - > prev_row + 1 , png_ptr - > row_buf [ 0 ] ) ;
else
png_error ( png_ptr , " bad adaptive filter value " ) ;
}
/* libpng 1.5.6: the following line was copying png_ptr->rowbytes before
* 1.5 .6 , while the buffer really is this big in current versions of libpng
* it may not be in the future , so this was changed just to copy the
* interlaced count :
*/
memcpy ( png_ptr - > prev_row , png_ptr - > row_buf , row_info . rowbytes + 1 ) ;
# ifdef PNG_MNG_FEATURES_SUPPORTED
if ( ( png_ptr - > mng_features_permitted & PNG_FLAG_MNG_FILTER_64 ) & &
( png_ptr - > filter_type = = PNG_INTRAPIXEL_DIFFERENCING ) )
{
/* Intrapixel differencing */
png_do_read_intrapixel ( & row_info , png_ptr - > row_buf + 1 ) ;
}
# endif
# ifdef PNG_READ_TRANSFORMS_SUPPORTED
if ( png_ptr - > transformations )
png_do_read_transformations ( png_ptr , & row_info ) ;
# endif
/* The transformed pixel depth should match the depth now in row_info. */
if ( png_ptr - > transformed_pixel_depth = = 0 )
{
png_ptr - > transformed_pixel_depth = row_info . pixel_depth ;
if ( row_info . pixel_depth > png_ptr - > maximum_pixel_depth )
png_error ( png_ptr , " sequential row overflow " ) ;
}
else if ( png_ptr - > transformed_pixel_depth ! = row_info . pixel_depth )
png_error ( png_ptr , " internal sequential row size calculation error " ) ;
# ifdef PNG_READ_INTERLACING_SUPPORTED
/* Blow up interlaced rows to full size */
if ( png_ptr - > interlaced & &
( png_ptr - > transformations & PNG_INTERLACE ) )
{
if ( png_ptr - > pass < 6 )
png_do_read_interlace ( & row_info , png_ptr - > row_buf + 1 , png_ptr - > pass ,
png_ptr - > transformations ) ;
if ( dsp_row ! = NULL )
png_combine_row ( png_ptr , dsp_row , 1 /*display*/ ) ;
if ( row ! = NULL )
png_combine_row ( png_ptr , row , 0 /*row*/ ) ;
}
else
# endif
{
if ( row ! = NULL )
png_combine_row ( png_ptr , row , - 1 /*ignored*/ ) ;
if ( dsp_row ! = NULL )
png_combine_row ( png_ptr , dsp_row , - 1 /*ignored*/ ) ;
}
png_read_finish_row ( png_ptr ) ;
if ( png_ptr - > read_row_fn ! = NULL )
( * ( png_ptr - > read_row_fn ) ) ( png_ptr , png_ptr - > row_number , png_ptr - > pass ) ;
}
# endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
# ifdef PNG_SEQUENTIAL_READ_SUPPORTED
/* Read one or more rows of image data. If the image is interlaced,
* and png_set_interlace_handling ( ) has been called , the rows need to
* contain the contents of the rows from the previous pass . If the
* image has alpha or transparency , and png_handle_alpha ( ) [ * ] has been
* called , the rows contents must be initialized to the contents of the
* screen .
*
* " row " holds the actual image , and pixels are placed in it
* as they arrive . If the image is displayed after each pass , it will
* appear to " sparkle " in . " display_row " can be used to display a
* " chunky " progressive image , with finer detail added as it becomes
* available . If you do not want this " chunky " display , you may pass
* NULL for display_row . If you do not want the sparkle display , and
* you have not called png_handle_alpha ( ) , you may pass NULL for rows .
* If you have called png_handle_alpha ( ) , and the image has either an
* alpha channel or a transparency chunk , you must provide a buffer for
* rows . In this case , you do not have to provide a display_row buffer
* also , but you may . If the image is not interlaced , or if you have
* not called png_set_interlace_handling ( ) , the display_row buffer will
* be ignored , so pass NULL to it .
*
* [ * ] png_handle_alpha ( ) does not exist yet , as of this version of libpng
*/
void PNGAPI
png_read_rows ( png_structrp png_ptr , png_bytepp row ,
png_bytepp display_row , png_uint_32 num_rows )
{
png_uint_32 i ;
png_bytepp rp ;
png_bytepp dp ;
png_debug ( 1 , " in png_read_rows " ) ;
if ( png_ptr = = NULL )
return ;
rp = row ;
dp = display_row ;
if ( rp ! = NULL & & dp ! = NULL )
for ( i = 0 ; i < num_rows ; i + + )
{
png_bytep rptr = * rp + + ;
png_bytep dptr = * dp + + ;
png_read_row ( png_ptr , rptr , dptr ) ;
}
else if ( rp ! = NULL )
for ( i = 0 ; i < num_rows ; i + + )
{
png_bytep rptr = * rp ;
png_read_row ( png_ptr , rptr , NULL ) ;
rp + + ;
}
else if ( dp ! = NULL )
for ( i = 0 ; i < num_rows ; i + + )
{
png_bytep dptr = * dp ;
png_read_row ( png_ptr , NULL , dptr ) ;
dp + + ;
}
}
# endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
# ifdef PNG_SEQUENTIAL_READ_SUPPORTED
/* Read the entire image. If the image has an alpha channel or a tRNS
* chunk , and you have called png_handle_alpha ( ) [ * ] , you will need to
* initialize the image to the current image that PNG will be overlaying .
* We set the num_rows again here , in case it was incorrectly set in
* png_read_start_row ( ) by a call to png_read_update_info ( ) or
* png_start_read_image ( ) if png_set_interlace_handling ( ) wasn ' t called
* prior to either of these functions like it should have been . You can
* only call this function once . If you desire to have an image for
* each pass of a interlaced image , use png_read_rows ( ) instead .
*
* [ * ] png_handle_alpha ( ) does not exist yet , as of this version of libpng
*/
void PNGAPI
png_read_image ( png_structrp png_ptr , png_bytepp image )
{
png_uint_32 i , image_height ;
int pass , j ;
png_bytepp rp ;
png_debug ( 1 , " in png_read_image " ) ;
if ( png_ptr = = NULL )
return ;
# ifdef PNG_READ_INTERLACING_SUPPORTED
if ( ! ( png_ptr - > flags & PNG_FLAG_ROW_INIT ) )
{
pass = png_set_interlace_handling ( png_ptr ) ;
/* And make sure transforms are initialized. */
png_start_read_image ( png_ptr ) ;
}
else
{
if ( png_ptr - > interlaced & & ! ( png_ptr - > transformations & PNG_INTERLACE ) )
{
/* Caller called png_start_read_image or png_read_update_info without
* first turning on the PNG_INTERLACE transform . We can fix this here ,
* but the caller should do it !
*/
png_warning ( png_ptr , " Interlace handling should be turned on when "
" using png_read_image " ) ;
/* Make sure this is set correctly */
png_ptr - > num_rows = png_ptr - > height ;
}
/* Obtain the pass number, which also turns on the PNG_INTERLACE flag in
* the above error case .
*/
pass = png_set_interlace_handling ( png_ptr ) ;
}
# else
if ( png_ptr - > interlaced )
png_error ( png_ptr ,
" Cannot read interlaced image -- interlace handler disabled " ) ;
pass = 1 ;
# endif
image_height = png_ptr - > height ;
for ( j = 0 ; j < pass ; j + + )
{
rp = image ;
for ( i = 0 ; i < image_height ; i + + )
{
png_read_row ( png_ptr , * rp , NULL ) ;
rp + + ;
}
}
}
# endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
# ifdef PNG_SEQUENTIAL_READ_SUPPORTED
/* Read the end of the PNG file. Will not read past the end of the
* file , will verify the end is accurate , and will read any comments
* or time information at the end of the file , if info is not NULL .
*/
void PNGAPI
png_read_end ( png_structrp png_ptr , png_inforp info_ptr )
{
# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
int keep ;
# endif
png_debug ( 1 , " in png_read_end " ) ;
if ( png_ptr = = NULL )
return ;
/* If png_read_end is called in the middle of reading the rows there may
* still be pending IDAT data and an owned zstream . Deal with this here .
*/
# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
if ( ! png_chunk_unknown_handling ( png_ptr , png_IDAT ) )
# endif
png_read_finish_IDAT ( png_ptr ) ;
# ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
/* Report invalid palette index; added at libng-1.5.10 */
if ( png_ptr - > color_type = = PNG_COLOR_TYPE_PALETTE & &
png_ptr - > num_palette_max > png_ptr - > num_palette )
png_benign_error ( png_ptr , " Read palette index exceeding num_palette " ) ;
# endif
do
{
png_uint_32 length = png_read_chunk_header ( png_ptr ) ;
png_uint_32 chunk_name = png_ptr - > chunk_name ;
if ( chunk_name = = png_IHDR )
png_handle_IHDR ( png_ptr , info_ptr , length ) ;
else if ( chunk_name = = png_IEND )
png_handle_IEND ( png_ptr , info_ptr , length ) ;
# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
else if ( ( keep = png_chunk_unknown_handling ( png_ptr , chunk_name ) ) ! = 0 )
{
if ( chunk_name = = png_IDAT )
{
if ( ( length > 0 ) | | ( png_ptr - > mode & PNG_HAVE_CHUNK_AFTER_IDAT ) )
png_benign_error ( png_ptr , " Too many IDATs found " ) ;
}
png_handle_unknown ( png_ptr , info_ptr , length , keep ) ;
if ( chunk_name = = png_PLTE )
png_ptr - > mode | = PNG_HAVE_PLTE ;
}
# endif
else if ( chunk_name = = png_IDAT )
{
/* Zero length IDATs are legal after the last IDAT has been
* read , but not after other chunks have been read .
*/
if ( ( length > 0 ) | | ( png_ptr - > mode & PNG_HAVE_CHUNK_AFTER_IDAT ) )
png_benign_error ( png_ptr , " Too many IDATs found " ) ;
png_crc_finish ( png_ptr , length ) ;
}
else if ( chunk_name = = png_PLTE )
png_handle_PLTE ( png_ptr , info_ptr , length ) ;
# ifdef PNG_READ_bKGD_SUPPORTED
else if ( chunk_name = = png_bKGD )
png_handle_bKGD ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_cHRM_SUPPORTED
else if ( chunk_name = = png_cHRM )
png_handle_cHRM ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_gAMA_SUPPORTED
else if ( chunk_name = = png_gAMA )
png_handle_gAMA ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_hIST_SUPPORTED
else if ( chunk_name = = png_hIST )
png_handle_hIST ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_oFFs_SUPPORTED
else if ( chunk_name = = png_oFFs )
png_handle_oFFs ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_pCAL_SUPPORTED
else if ( chunk_name = = png_pCAL )
png_handle_pCAL ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_sCAL_SUPPORTED
else if ( chunk_name = = png_sCAL )
png_handle_sCAL ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_pHYs_SUPPORTED
else if ( chunk_name = = png_pHYs )
png_handle_pHYs ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_sBIT_SUPPORTED
else if ( chunk_name = = png_sBIT )
png_handle_sBIT ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_sRGB_SUPPORTED
else if ( chunk_name = = png_sRGB )
png_handle_sRGB ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_iCCP_SUPPORTED
else if ( chunk_name = = png_iCCP )
png_handle_iCCP ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_sPLT_SUPPORTED
else if ( chunk_name = = png_sPLT )
png_handle_sPLT ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_tEXt_SUPPORTED
else if ( chunk_name = = png_tEXt )
png_handle_tEXt ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_tIME_SUPPORTED
else if ( chunk_name = = png_tIME )
png_handle_tIME ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_tRNS_SUPPORTED
else if ( chunk_name = = png_tRNS )
png_handle_tRNS ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_zTXt_SUPPORTED
else if ( chunk_name = = png_zTXt )
png_handle_zTXt ( png_ptr , info_ptr , length ) ;
# endif
# ifdef PNG_READ_iTXt_SUPPORTED
else if ( chunk_name = = png_iTXt )
png_handle_iTXt ( png_ptr , info_ptr , length ) ;
# endif
else
png_handle_unknown ( png_ptr , info_ptr , length ,
PNG_HANDLE_CHUNK_AS_DEFAULT ) ;
} while ( ! ( png_ptr - > mode & PNG_HAVE_IEND ) ) ;
}
# endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
/* Free all memory used in the read struct */
static void
png_read_destroy ( png_structrp png_ptr )
{
png_debug ( 1 , " in png_read_destroy " ) ;
# ifdef PNG_READ_GAMMA_SUPPORTED
png_destroy_gamma_table ( png_ptr ) ;
# endif
png_free ( png_ptr , png_ptr - > big_row_buf ) ;
png_free ( png_ptr , png_ptr - > big_prev_row ) ;
png_free ( png_ptr , png_ptr - > read_buffer ) ;
# ifdef PNG_READ_QUANTIZE_SUPPORTED
png_free ( png_ptr , png_ptr - > palette_lookup ) ;
png_free ( png_ptr , png_ptr - > quantize_index ) ;
# endif
if ( png_ptr - > free_me & PNG_FREE_PLTE )
png_zfree ( png_ptr , png_ptr - > palette ) ;
png_ptr - > free_me & = ~ PNG_FREE_PLTE ;
# if defined(PNG_tRNS_SUPPORTED) || \
defined ( PNG_READ_EXPAND_SUPPORTED ) | | defined ( PNG_READ_BACKGROUND_SUPPORTED )
if ( png_ptr - > free_me & PNG_FREE_TRNS )
png_free ( png_ptr , png_ptr - > trans_alpha ) ;
png_ptr - > free_me & = ~ PNG_FREE_TRNS ;
# endif
inflateEnd ( & png_ptr - > zstream ) ;
# ifdef PNG_PROGRESSIVE_READ_SUPPORTED
png_free ( png_ptr , png_ptr - > save_buffer ) ;
# endif
# if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) &&\
defined ( PNG_READ_UNKNOWN_CHUNKS_SUPPORTED )
png_free ( png_ptr , png_ptr - > unknown_chunk . data ) ;
# endif
# ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
png_free ( png_ptr , png_ptr - > chunk_list ) ;
# endif
/* NOTE: the 'setjmp' buffer may still be allocated and the memory and error
* callbacks are still set at this point . They are required to complete the
* destruction of the png_struct itself .
*/
}
/* Free all memory used by the read */
void PNGAPI
png_destroy_read_struct ( png_structpp png_ptr_ptr , png_infopp info_ptr_ptr ,
png_infopp end_info_ptr_ptr )
{
png_structrp png_ptr = NULL ;
png_debug ( 1 , " in png_destroy_read_struct " ) ;
if ( png_ptr_ptr ! = NULL )
png_ptr = * png_ptr_ptr ;
if ( png_ptr = = NULL )
return ;
/* libpng 1.6.0: use the API to destroy info structs to ensure consistent
* behavior . Prior to 1.6 .0 libpng did extra ' info ' destruction in this API .
* The extra was , apparently , unnecessary yet this hides memory leak bugs .
*/
png_destroy_info_struct ( png_ptr , end_info_ptr_ptr ) ;
png_destroy_info_struct ( png_ptr , info_ptr_ptr ) ;
* png_ptr_ptr = NULL ;
png_read_destroy ( png_ptr ) ;
png_destroy_png_struct ( png_ptr ) ;
}
void PNGAPI
png_set_read_status_fn ( png_structrp png_ptr , png_read_status_ptr read_row_fn )
{
if ( png_ptr = = NULL )
return ;
png_ptr - > read_row_fn = read_row_fn ;
}
# ifdef PNG_SEQUENTIAL_READ_SUPPORTED
# ifdef PNG_INFO_IMAGE_SUPPORTED
void PNGAPI
png_read_png ( png_structrp png_ptr , png_inforp info_ptr ,
int transforms ,
voidp params )
{
int row ;
if ( png_ptr = = NULL | | info_ptr = = NULL )
return ;
/* png_read_info() gives us all of the information from the
* PNG file before the first IDAT ( image data chunk ) .
*/
png_read_info ( png_ptr , info_ptr ) ;
if ( info_ptr - > height > PNG_UINT_32_MAX / ( sizeof ( png_bytep ) ) )
png_error ( png_ptr , " Image is too high to process with png_read_png() " ) ;
/* -------------- image transformations start here ------------------- */
# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
/* Tell libpng to strip 16-bit/color files down to 8 bits per color.
*/
if ( transforms & PNG_TRANSFORM_SCALE_16 )
{
/* Added at libpng-1.5.4. "strip_16" produces the same result that it
* did in earlier versions , while " scale_16 " is now more accurate .
*/
png_set_scale_16 ( png_ptr ) ;
}
# endif
# ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
/* If both SCALE and STRIP are required pngrtran will effectively cancel the
* latter by doing SCALE first . This is ok and allows apps not to check for
* which is supported to get the right answer .
*/
if ( transforms & PNG_TRANSFORM_STRIP_16 )
png_set_strip_16 ( png_ptr ) ;
# endif
# ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
/* Strip alpha bytes from the input data without combining with
* the background ( not recommended ) .
*/
if ( transforms & PNG_TRANSFORM_STRIP_ALPHA )
png_set_strip_alpha ( png_ptr ) ;
# endif
# if defined(PNG_READ_PACK_SUPPORTED) && !defined(PNG_READ_EXPAND_SUPPORTED)
/* Extract multiple pixels with bit depths of 1, 2, or 4 from a single
* byte into separate bytes ( useful for paletted and grayscale images ) .
*/
if ( transforms & PNG_TRANSFORM_PACKING )
png_set_packing ( png_ptr ) ;
# endif
# ifdef PNG_READ_PACKSWAP_SUPPORTED
/* Change the order of packed pixels to least significant bit first
* ( not useful if you are using png_set_packing ) .
*/
if ( transforms & PNG_TRANSFORM_PACKSWAP )
png_set_packswap ( png_ptr ) ;
# endif
# ifdef PNG_READ_EXPAND_SUPPORTED
/* Expand paletted colors into true RGB triplets
* Expand grayscale images to full 8 bits from 1 , 2 , or 4 bits / pixel
* Expand paletted or RGB images with transparency to full alpha
* channels so the data will be available as RGBA quartets .
*/
if ( transforms & PNG_TRANSFORM_EXPAND )
if ( ( png_ptr - > bit_depth < 8 ) | |
( png_ptr - > color_type = = PNG_COLOR_TYPE_PALETTE ) | |
( png_get_valid ( png_ptr , info_ptr , PNG_INFO_tRNS ) ) )
png_set_expand ( png_ptr ) ;
# endif
/* We don't handle background color or gamma transformation or quantizing.
*/
# ifdef PNG_READ_INVERT_SUPPORTED
/* Invert monochrome files to have 0 as white and 1 as black
*/
if ( transforms & PNG_TRANSFORM_INVERT_MONO )
png_set_invert_mono ( png_ptr ) ;
# endif
# ifdef PNG_READ_SHIFT_SUPPORTED
/* If you want to shift the pixel values from the range [0,255] or
* [ 0 , 65535 ] to the original [ 0 , 7 ] or [ 0 , 31 ] , or whatever range the
* colors were originally in :
*/
if ( ( transforms & PNG_TRANSFORM_SHIFT )
& & png_get_valid ( png_ptr , info_ptr , PNG_INFO_sBIT ) )
{
png_color_8p sig_bit = 0 ;
png_get_sBIT ( png_ptr , info_ptr , & sig_bit ) ;
png_set_shift ( png_ptr , sig_bit ) ;
}
# endif
# ifdef PNG_READ_BGR_SUPPORTED
/* Flip the RGB pixels to BGR (or RGBA to BGRA) */
if ( transforms & PNG_TRANSFORM_BGR )
png_set_bgr ( png_ptr ) ;
# endif
# ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
/* Swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */
if ( transforms & PNG_TRANSFORM_SWAP_ALPHA )
png_set_swap_alpha ( png_ptr ) ;
# endif
# ifdef PNG_READ_SWAP_SUPPORTED
/* Swap bytes of 16-bit files to least significant byte first */
if ( transforms & PNG_TRANSFORM_SWAP_ENDIAN )
png_set_swap ( png_ptr ) ;
# endif
/* Added at libpng-1.2.41 */
# ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
/* Invert the alpha channel from opacity to transparency */
if ( transforms & PNG_TRANSFORM_INVERT_ALPHA )
png_set_invert_alpha ( png_ptr ) ;
# endif
/* Added at libpng-1.2.41 */
# ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
/* Expand grayscale image to RGB */
if ( transforms & PNG_TRANSFORM_GRAY_TO_RGB )
png_set_gray_to_rgb ( png_ptr ) ;
# endif
/* Added at libpng-1.5.4 */
# ifdef PNG_READ_EXPAND_16_SUPPORTED
if ( transforms & PNG_TRANSFORM_EXPAND_16 )
png_set_expand_16 ( png_ptr ) ;
# endif
/* We don't handle adding filler bytes */
/* We use png_read_image and rely on that for interlace handling, but we also
* call png_read_update_info therefore must turn on interlace handling now :
*/
( void ) png_set_interlace_handling ( png_ptr ) ;
/* Optional call to gamma correct and add the background to the palette
* and update info structure . REQUIRED if you are expecting libpng to
* update the palette for you ( i . e . , you selected such a transform above ) .
*/
png_read_update_info ( png_ptr , info_ptr ) ;
/* -------------- image transformations end here ------------------- */
png_free_data ( png_ptr , info_ptr , PNG_FREE_ROWS , 0 ) ;
if ( info_ptr - > row_pointers = = NULL )
{
png_uint_32 iptr ;
info_ptr - > row_pointers = ( png_bytepp ) png_malloc ( png_ptr ,
info_ptr - > height * ( sizeof ( png_bytep ) ) ) ;
for ( iptr = 0 ; iptr < info_ptr - > height ; iptr + + )
info_ptr - > row_pointers [ iptr ] = NULL ;
info_ptr - > free_me | = PNG_FREE_ROWS ;
for ( row = 0 ; row < ( int ) info_ptr - > height ; row + + )
info_ptr - > row_pointers [ row ] = ( png_bytep ) png_malloc ( png_ptr ,
png_get_rowbytes ( png_ptr , info_ptr ) ) ;
}
png_read_image ( png_ptr , info_ptr - > row_pointers ) ;
info_ptr - > valid | = PNG_INFO_IDAT ;
/* Read rest of file, and get additional chunks in info_ptr - REQUIRED */
png_read_end ( png_ptr , info_ptr ) ;
PNG_UNUSED ( transforms ) /* Quiet compiler warnings */
PNG_UNUSED ( params )
}
# endif /* PNG_INFO_IMAGE_SUPPORTED */
# endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
# ifdef PNG_SIMPLIFIED_READ_SUPPORTED
/* SIMPLIFIED READ
*
* This code currently relies on the sequential reader , though it could easily
* be made to work with the progressive one .
*/
/* Arguments to png_image_finish_read: */
/* Encoding of PNG data (used by the color-map code) */
/* TODO: change these, dang, ANSI-C reserves the 'E' namespace. */
# define E_NOTSET 0 /* File encoding not yet known */
# define E_sRGB 1 /* 8-bit encoded to sRGB gamma */
# define E_LINEAR 2 /* 16-bit linear: not encoded, NOT pre-multiplied! */
# define E_FILE 3 /* 8-bit encoded to file gamma, not sRGB or linear */
# define E_LINEAR8 4 /* 8-bit linear: only from a file value */
/* Color-map processing: after libpng has run on the PNG image further
* processing may be needed to conver the data to color - map indicies .
*/
# define PNG_CMAP_NONE 0
# define PNG_CMAP_GA 1 /* Process GA data to a color-map with alpha */
# define PNG_CMAP_TRANS 2 /* Process GA data to a background index */
# define PNG_CMAP_RGB 3 /* Process RGB data */
# define PNG_CMAP_RGB_ALPHA 4 /* Process RGBA data */
/* The following document where the background is for each processing case. */
# define PNG_CMAP_NONE_BACKGROUND 256
# define PNG_CMAP_GA_BACKGROUND 231
# define PNG_CMAP_TRANS_BACKGROUND 254
# define PNG_CMAP_RGB_BACKGROUND 256
# define PNG_CMAP_RGB_ALPHA_BACKGROUND 216
typedef struct
{
/* Arguments: */
png_imagep image ;
png_voidp buffer ;
png_int_32 row_stride ;
png_voidp colormap ;
png_const_colorp background ;
/* Local variables: */
png_voidp local_row ;
png_voidp first_row ;
ptrdiff_t row_bytes ; /* step between rows */
int file_encoding ; /* E_ values above */
png_fixed_point gamma_to_linear ; /* For E_FILE, reciprocal of gamma */
int colormap_processing ; /* PNG_CMAP_ values above */
} png_image_read_control ;
/* Do all the *safe* initialization - 'safe' means that png_error won't be
* called , so setting up the jmp_buf is not required . This means that anything
* called from here must * not * call png_malloc - it has to call png_malloc_warn
* instead so that control is returned safely back to this routine .
*/
static int
png_image_read_init ( png_imagep image )
{
if ( image - > opaque = = NULL )
{
png_structp png_ptr = png_create_read_struct ( PNG_LIBPNG_VER_STRING , image ,
png_safe_error , png_safe_warning ) ;
/* And set the rest of the structure to NULL to ensure that the various
* fields are consistent .
*/
memset ( image , 0 , ( sizeof * image ) ) ;
image - > version = PNG_IMAGE_VERSION ;
if ( png_ptr ! = NULL )
{
png_infop info_ptr = png_create_info_struct ( png_ptr ) ;
if ( info_ptr ! = NULL )
{
png_controlp control = png_voidcast ( png_controlp ,
png_malloc_warn ( png_ptr , ( sizeof * control ) ) ) ;
if ( control ! = NULL )
{
memset ( control , 0 , ( sizeof * control ) ) ;
control - > png_ptr = png_ptr ;
control - > info_ptr = info_ptr ;
control - > for_write = 0 ;
image - > opaque = control ;
return 1 ;
}
/* Error clean up */
png_destroy_info_struct ( png_ptr , & info_ptr ) ;
}
png_destroy_read_struct ( & png_ptr , NULL , NULL ) ;
}
return png_image_error ( image , " png_image_read: out of memory " ) ;
}
return png_image_error ( image , " png_image_read: opaque pointer not NULL " ) ;
}
/* Utility to find the base format of a PNG file from a png_struct. */
static png_uint_32
png_image_format ( png_structrp png_ptr )
{
png_uint_32 format = 0 ;
if ( png_ptr - > color_type & PNG_COLOR_MASK_COLOR )
format | = PNG_FORMAT_FLAG_COLOR ;
if ( png_ptr - > color_type & PNG_COLOR_MASK_ALPHA )
format | = PNG_FORMAT_FLAG_ALPHA ;
/* Use png_ptr here, not info_ptr, because by examination png_handle_tRNS
* sets the png_struct fields ; that ' s all we are interested in here . The
* precise interaction with an app call to png_set_tRNS and PNG file reading
* is unclear .
*/
else if ( png_ptr - > num_trans > 0 )
format | = PNG_FORMAT_FLAG_ALPHA ;
if ( png_ptr - > bit_depth = = 16 )
format | = PNG_FORMAT_FLAG_LINEAR ;
if ( png_ptr - > color_type & PNG_COLOR_MASK_PALETTE )
format | = PNG_FORMAT_FLAG_COLORMAP ;
return format ;
}
/* Is the given gamma significantly different from sRGB? The test is the same
* one used in pngrtran . c when deciding whether to do gamma correction . The
* arithmetic optimizes the division by using the fact that the inverse of the
* file sRGB gamma is 2.2
*/
static int
png_gamma_not_sRGB ( png_fixed_point g )
{
if ( g < PNG_FP_1 )
{
/* An uninitialized gamma is assumed to be sRGB for the simplified API. */
if ( g = = 0 )
return 0 ;
return png_gamma_significant ( ( g * 11 + 2 ) / 5 /* i.e. *2.2, rounded */ ) ;
}
return 1 ;
}
/* Do the main body of a 'png_image_begin_read' function; read the PNG file
* header and fill in all the information . This is executed in a safe context ,
* unlike the init routine above .
*/
static int
png_image_read_header ( png_voidp argument )
{
png_imagep image = png_voidcast ( png_imagep , argument ) ;
png_structrp png_ptr = image - > opaque - > png_ptr ;
png_inforp info_ptr = image - > opaque - > info_ptr ;
png_set_benign_errors ( png_ptr , 1 /*warn*/ ) ;
png_read_info ( png_ptr , info_ptr ) ;
/* Do this the fast way; just read directly out of png_struct. */
image - > width = png_ptr - > width ;
image - > height = png_ptr - > height ;
{
png_uint_32 format = png_image_format ( png_ptr ) ;
image - > format = format ;
# ifdef PNG_COLORSPACE_SUPPORTED
/* Does the colorspace match sRGB? If there is no color endpoint
* ( colorant ) information assume yes , otherwise require the
* ' ENDPOINTS_MATCHE_sRGB ' colorspace flag to have been set . If the
* colorspace has been determined to be invalid ignore it .
*/
if ( ( format & PNG_FORMAT_FLAG_COLOR ) ! = 0 & & ( ( png_ptr - > colorspace . flags
& ( PNG_COLORSPACE_HAVE_ENDPOINTS | PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB |
PNG_COLORSPACE_INVALID ) ) = = PNG_COLORSPACE_HAVE_ENDPOINTS ) )
image - > flags | = PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB ;
# endif
}
/* We need the maximum number of entries regardless of the format the
* application sets here .
*/
{
png_uint_32 cmap_entries ;
switch ( png_ptr - > color_type )
{
case PNG_COLOR_TYPE_GRAY :
cmap_entries = 1U < < png_ptr - > bit_depth ;
break ;
case PNG_COLOR_TYPE_PALETTE :
cmap_entries = png_ptr - > num_palette ;
break ;
default :
cmap_entries = 256 ;
break ;
}
if ( cmap_entries > 256 )
cmap_entries = 256 ;
image - > colormap_entries = cmap_entries ;
}
return 1 ;
}
# ifdef PNG_STDIO_SUPPORTED
int PNGAPI
png_image_begin_read_from_stdio ( png_imagep image , FILE * file )
{
if ( image ! = NULL & & image - > version = = PNG_IMAGE_VERSION )
{
if ( file ! = NULL )
{
if ( png_image_read_init ( image ) )
{
/* This is slightly evil, but png_init_io doesn't do anything other
* than this and we haven ' t changed the standard IO functions so
* this saves a ' safe ' function .
*/
image - > opaque - > png_ptr - > io_ptr = file ;
return png_safe_execute ( image , png_image_read_header , image ) ;
}
}
else
return png_image_error ( image ,
" png_image_begin_read_from_stdio: invalid argument " ) ;
}
else if ( image ! = NULL )
return png_image_error ( image ,
" png_image_begin_read_from_stdio: incorrect PNG_IMAGE_VERSION " ) ;
return 0 ;
}
int PNGAPI
png_image_begin_read_from_file ( png_imagep image , const char * file_name )
{
if ( image ! = NULL & & image - > version = = PNG_IMAGE_VERSION )
{
if ( file_name ! = NULL )
{
FILE * fp = fopen ( file_name , " rb " ) ;
if ( fp ! = NULL )
{
if ( png_image_read_init ( image ) )
{
image - > opaque - > png_ptr - > io_ptr = fp ;
image - > opaque - > owned_file = 1 ;
return png_safe_execute ( image , png_image_read_header , image ) ;
}
/* Clean up: just the opened file. */
( void ) fclose ( fp ) ;
}
else
return png_image_error ( image , strerror ( errno ) ) ;
}
else
return png_image_error ( image ,
" png_image_begin_read_from_file: invalid argument " ) ;
}
else if ( image ! = NULL )
return png_image_error ( image ,
" png_image_begin_read_from_file: incorrect PNG_IMAGE_VERSION " ) ;
return 0 ;
}
# endif /* PNG_STDIO_SUPPORTED */
static void PNGCBAPI
png_image_memory_read ( png_structp png_ptr , png_bytep out , png_size_t need )
{
if ( png_ptr ! = NULL )
{
png_imagep image = png_voidcast ( png_imagep , png_ptr - > io_ptr ) ;
if ( image ! = NULL )
{
png_controlp cp = image - > opaque ;
if ( cp ! = NULL )
{
png_const_bytep memory = cp - > memory ;
png_size_t size = cp - > size ;
if ( memory ! = NULL & & size > = need )
{
memcpy ( out , memory , need ) ;
cp - > memory = memory + need ;
cp - > size = size - need ;
return ;
}
png_error ( png_ptr , " read beyond end of data " ) ;
}
}
png_error ( png_ptr , " invalid memory read " ) ;
}
}
int PNGAPI png_image_begin_read_from_memory ( png_imagep image ,
png_const_voidp memory , png_size_t size )
{
if ( image ! = NULL & & image - > version = = PNG_IMAGE_VERSION )
{
if ( memory ! = NULL & & size > 0 )
{
if ( png_image_read_init ( image ) )
{
/* Now set the IO functions to read from the memory buffer and
* store it into io_ptr . Again do this in - place to avoid calling a
* libpng function that requires error handling .
*/
image - > opaque - > memory = png_voidcast ( png_const_bytep , memory ) ;
image - > opaque - > size = size ;
image - > opaque - > png_ptr - > io_ptr = image ;
image - > opaque - > png_ptr - > read_data_fn = png_image_memory_read ;
return png_safe_execute ( image , png_image_read_header , image ) ;
}
}
else
return png_image_error ( image ,
" png_image_begin_read_from_memory: invalid argument " ) ;
}
else if ( image ! = NULL )
return png_image_error ( image ,
" png_image_begin_read_from_memory: incorrect PNG_IMAGE_VERSION " ) ;
return 0 ;
}
/* Utility function to skip chunks that are not used by the simplified image
* read functions and an appropriate macro to call it .
*/
# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
static void
png_image_skip_unused_chunks ( png_structrp png_ptr )
{
/* Prepare the reader to ignore all recognized chunks whose data will not
* be used , i . e . , all chunks recognized by libpng except for those
* involved in basic image reading :
*
* IHDR , PLTE , IDAT , IEND
*
* Or image data handling :
*
* tRNS , bKGD , gAMA , cHRM , sRGB , iCCP and sBIT .
*
* This provides a small performance improvement and eliminates any
* potential vulnerability to security problems in the unused chunks .
*/
{
static PNG_CONST png_byte chunks_to_process [ ] = {
98 , 75 , 71 , 68 , ' \0 ' , /* bKGD */
99 , 72 , 82 , 77 , ' \0 ' , /* cHRM */
103 , 65 , 77 , 65 , ' \0 ' , /* gAMA */
105 , 67 , 67 , 80 , ' \0 ' , /* iCCP */
115 , 66 , 73 , 84 , ' \0 ' , /* sBIT */
115 , 82 , 71 , 66 , ' \0 ' , /* sRGB */
} ;
/* Ignore unknown chunks and all other chunks except for the
* IHDR , PLTE , tRNS , IDAT , and IEND chunks .
*/
png_set_keep_unknown_chunks ( png_ptr , PNG_HANDLE_CHUNK_NEVER ,
NULL , - 1 ) ;
/* But do not ignore image data handling chunks */
png_set_keep_unknown_chunks ( png_ptr , PNG_HANDLE_CHUNK_AS_DEFAULT ,
chunks_to_process , ( sizeof chunks_to_process ) / 5 ) ;
}
}
# define PNG_SKIP_CHUNKS(p) png_image_skip_unused_chunks(p)
# else
# define PNG_SKIP_CHUNKS(p) ((void)0)
# endif /* PNG_HANDLE_AS_UNKNOWN_SUPPORTED */
/* The following macro gives the exact rounded answer for all values in the
* range 0. .255 ( it actually divides by 51.2 , but the rounding still generates
* the correct numbers 0. .5
*/
# define PNG_DIV51(v8) (((v8) * 5 + 130) >> 8)
/* Utility functions to make particular color-maps */
static void
set_file_encoding ( png_image_read_control * display )
{
png_fixed_point g = display - > image - > opaque - > png_ptr - > colorspace . gamma ;
if ( png_gamma_significant ( g ) )
{
if ( png_gamma_not_sRGB ( g ) )
{
display - > file_encoding = E_FILE ;
display - > gamma_to_linear = png_reciprocal ( g ) ;
}
else
display - > file_encoding = E_sRGB ;
}
else
display - > file_encoding = E_LINEAR8 ;
}
static unsigned int
decode_gamma ( png_image_read_control * display , png_uint_32 value , int encoding )
{
if ( encoding = = E_FILE ) /* double check */
encoding = display - > file_encoding ;
if ( encoding = = E_NOTSET ) /* must be the file encoding */
{
set_file_encoding ( display ) ;
encoding = display - > file_encoding ;
}
switch ( encoding )
{
case E_FILE :
value = png_gamma_16bit_correct ( value * 257 , display - > gamma_to_linear ) ;
break ;
case E_sRGB :
value = png_sRGB_table [ value ] ;
break ;
case E_LINEAR :
break ;
case E_LINEAR8 :
value * = 257 ;
break ;
default :
png_error ( display - > image - > opaque - > png_ptr ,
" unexpected encoding (internal error) " ) ;
break ;
}
return value ;
}
static png_uint_32
png_colormap_compose ( png_image_read_control * display ,
png_uint_32 foreground , int foreground_encoding , png_uint_32 alpha ,
png_uint_32 background , int encoding )
{
/* The file value is composed on the background, the background has the given
* encoding and so does the result , the file is encoded with E_FILE and the
* file and alpha are 8 - bit values . The ( output ) encoding will always be
* E_LINEAR or E_sRGB .
*/
png_uint_32 f = decode_gamma ( display , foreground , foreground_encoding ) ;
png_uint_32 b = decode_gamma ( display , background , encoding ) ;
/* The alpha is always an 8-bit value (it comes from the palette), the value
* scaled by 255 is what PNG_sRGB_FROM_LINEAR requires .
*/
f = f * alpha + b * ( 255 - alpha ) ;
if ( encoding = = E_LINEAR )
{
/* Scale to 65535; divide by 255, approximately (in fact this is extremely
* accurate , it divides by 255.00000005937181414556 , with no overflow . )
*/
f * = 257 ; /* Now scaled by 65535 */
f + = f > > 16 ;
f = ( f + 32768 ) > > 16 ;
}
else /* E_sRGB */
f = PNG_sRGB_FROM_LINEAR ( f ) ;
return f ;
}
/* NOTE: E_LINEAR values to this routine must be 16-bit, but E_FILE values must
* be 8 - bit .
*/
static void
png_create_colormap_entry ( png_image_read_control * display ,
png_uint_32 ip , png_uint_32 red , png_uint_32 green , png_uint_32 blue ,
png_uint_32 alpha , int encoding )
{
png_imagep image = display - > image ;
const int output_encoding = ( image - > format & PNG_FORMAT_FLAG_LINEAR ) ?
E_LINEAR : E_sRGB ;
const int convert_to_Y = ( image - > format & PNG_FORMAT_FLAG_COLOR ) = = 0 & &
( red ! = green | | green ! = blue ) ;
if ( ip > 255 )
png_error ( image - > opaque - > png_ptr , " color-map index out of range " ) ;
/* Update the cache with whether the file gamma is significantly different
* from sRGB .
*/
if ( encoding = = E_FILE )
{
if ( display - > file_encoding = = E_NOTSET )
set_file_encoding ( display ) ;
/* Note that the cached value may be E_FILE too, but if it is then the
* gamma_to_linear member has been set .
*/
encoding = display - > file_encoding ;
}
if ( encoding = = E_FILE )
{
png_fixed_point g = display - > gamma_to_linear ;
red = png_gamma_16bit_correct ( red * 257 , g ) ;
green = png_gamma_16bit_correct ( green * 257 , g ) ;
blue = png_gamma_16bit_correct ( blue * 257 , g ) ;
if ( convert_to_Y | | output_encoding = = E_LINEAR )
{
alpha * = 257 ;
encoding = E_LINEAR ;
}
else
{
red = PNG_sRGB_FROM_LINEAR ( red * 255 ) ;
green = PNG_sRGB_FROM_LINEAR ( green * 255 ) ;
blue = PNG_sRGB_FROM_LINEAR ( blue * 255 ) ;
encoding = E_sRGB ;
}
}
else if ( encoding = = E_LINEAR8 )
{
/* This encoding occurs quite frequently in test cases because PngSuite
* includes a gAMA 1.0 chunk with most images .
*/
red * = 257 ;
green * = 257 ;
blue * = 257 ;
alpha * = 257 ;
encoding = E_LINEAR ;
}
else if ( encoding = = E_sRGB & & ( convert_to_Y | | output_encoding = = E_LINEAR ) )
{
/* The values are 8-bit sRGB values, but must be converted to 16-bit
* linear .
*/
red = png_sRGB_table [ red ] ;
green = png_sRGB_table [ green ] ;
blue = png_sRGB_table [ blue ] ;
alpha * = 257 ;
encoding = E_LINEAR ;
}
/* This is set if the color isn't gray but the output is. */
if ( encoding = = E_LINEAR )
{
if ( convert_to_Y )
{
/* NOTE: these values are copied from png_do_rgb_to_gray */
png_uint_32 y = ( png_uint_32 ) 6968 * red + ( png_uint_32 ) 23434 * green +
( png_uint_32 ) 2366 * blue ;
if ( output_encoding = = E_LINEAR )
y = ( y + 16384 ) > > 15 ;
else
{
/* y is scaled by 32768, we need it scaled by 255: */
y = ( y + 128 ) > > 8 ;
y * = 255 ;
y = PNG_sRGB_FROM_LINEAR ( ( y + 64 ) > > 7 ) ;
encoding = E_sRGB ;
}
blue = red = green = y ;
}
else if ( output_encoding = = E_sRGB )
{
red = PNG_sRGB_FROM_LINEAR ( red * 255 ) ;
green = PNG_sRGB_FROM_LINEAR ( green * 255 ) ;
blue = PNG_sRGB_FROM_LINEAR ( blue * 255 ) ;
alpha = PNG_DIV257 ( alpha ) ;
encoding = E_sRGB ;
}
}
if ( encoding ! = output_encoding )
png_error ( image - > opaque - > png_ptr , " bad encoding (internal error) " ) ;
/* Store the value. */
{
# ifdef PNG_FORMAT_BGR_SUPPORTED
const int afirst = ( image - > format & PNG_FORMAT_FLAG_AFIRST ) ! = 0 & &
( image - > format & PNG_FORMAT_FLAG_ALPHA ) ! = 0 ;
# else
# define afirst 0
# endif
# ifdef PNG_FORMAT_BGR_SUPPORTED
const int bgr = ( image - > format & PNG_FORMAT_FLAG_BGR ) ? 2 : 0 ;
# else
# define bgr 0
# endif
if ( output_encoding = = E_LINEAR )
{
png_uint_16p entry = png_voidcast ( png_uint_16p , display - > colormap ) ;
entry + = ip * PNG_IMAGE_SAMPLE_CHANNELS ( image - > format ) ;
/* The linear 16-bit values must be pre-multiplied by the alpha channel
* value , if less than 65535 ( this is , effectively , composite on black
* if the alpha channel is removed . )
*/
switch ( PNG_IMAGE_SAMPLE_CHANNELS ( image - > format ) )
{
case 4 :
entry [ afirst ? 0 : 3 ] = ( png_uint_16 ) alpha ;
/* FALL THROUGH */
case 3 :
if ( alpha < 65535 )
{
if ( alpha > 0 )
{
blue = ( blue * alpha + 32767U ) / 65535U ;
green = ( green * alpha + 32767U ) / 65535U ;
red = ( red * alpha + 32767U ) / 65535U ;
}
else
red = green = blue = 0 ;
}
entry [ afirst + ( 2 ^ bgr ) ] = ( png_uint_16 ) blue ;
entry [ afirst + 1 ] = ( png_uint_16 ) green ;
entry [ afirst + bgr ] = ( png_uint_16 ) red ;
break ;
case 2 :
entry [ 1 ^ afirst ] = ( png_uint_16 ) alpha ;
/* FALL THROUGH */
case 1 :
if ( alpha < 65535 )
{
if ( alpha > 0 )
green = ( green * alpha + 32767U ) / 65535U ;
else
green = 0 ;
}
entry [ afirst ] = ( png_uint_16 ) green ;
break ;
default :
break ;
}
}
else /* output encoding is E_sRGB */
{
png_bytep entry = png_voidcast ( png_bytep , display - > colormap ) ;
entry + = ip * PNG_IMAGE_SAMPLE_CHANNELS ( image - > format ) ;
switch ( PNG_IMAGE_SAMPLE_CHANNELS ( image - > format ) )
{
case 4 :
entry [ afirst ? 0 : 3 ] = ( png_byte ) alpha ;
case 3 :
entry [ afirst + ( 2 ^ bgr ) ] = ( png_byte ) blue ;
entry [ afirst + 1 ] = ( png_byte ) green ;
entry [ afirst + bgr ] = ( png_byte ) red ;
break ;
case 2 :
entry [ 1 ^ afirst ] = ( png_byte ) alpha ;
case 1 :
entry [ afirst ] = ( png_byte ) green ;
break ;
default :
break ;
}
}
# ifdef afirst
# undef afirst
# endif
# ifdef bgr
# undef bgr
# endif
}
}
static int
make_gray_file_colormap ( png_image_read_control * display )
{
unsigned int i ;
for ( i = 0 ; i < 256 ; + + i )
png_create_colormap_entry ( display , i , i , i , i , 255 , E_FILE ) ;
return i ;
}
static int
make_gray_colormap ( png_image_read_control * display )
{
unsigned int i ;
for ( i = 0 ; i < 256 ; + + i )
png_create_colormap_entry ( display , i , i , i , i , 255 , E_sRGB ) ;
return i ;
}
# define PNG_GRAY_COLORMAP_ENTRIES 256
static int
make_ga_colormap ( png_image_read_control * display )
{
unsigned int i , a ;
/* Alpha is retained, the output will be a color-map with entries
* selected by six levels of alpha . One transparent entry , 6 gray
* levels for all the intermediate alpha values , leaving 230 entries
* for the opaque grays . The color - map entries are the six values
* [ 0. .5 ] * 51 , the GA processing uses PNG_DIV51 ( value ) to find the
* relevant entry .
*
* if ( alpha > 229 ) // opaque
* {
* // The 231 entries are selected to make the math below work:
* base = 0 ;
* entry = ( 231 * gray + 128 ) > > 8 ;
* }
* else if ( alpha < 26 ) // transparent
* {
* base = 231 ;
* entry = 0 ;
* }
* else // partially opaque
* {
* base = 226 + 6 * PNG_DIV51 ( alpha ) ;
* entry = PNG_DIV51 ( gray ) ;
* }
*/
i = 0 ;
while ( i < 231 )
{
unsigned int gray = ( i * 256 + 115 ) / 231 ;
png_create_colormap_entry ( display , i + + , gray , gray , gray , 255 , E_sRGB ) ;
}
/* 255 is used here for the component values for consistency with the code
* that undoes premultiplication in pngwrite . c .
*/
png_create_colormap_entry ( display , i + + , 255 , 255 , 255 , 0 , E_sRGB ) ;
for ( a = 1 ; a < 5 ; + + a )
{
unsigned int g ;
for ( g = 0 ; g < 6 ; + + g )
png_create_colormap_entry ( display , i + + , g * 51 , g * 51 , g * 51 , a * 51 ,
E_sRGB ) ;
}
return i ;
}
# define PNG_GA_COLORMAP_ENTRIES 256
static int
make_rgb_colormap ( png_image_read_control * display )
{
unsigned int i , r ;
/* Build a 6x6x6 opaque RGB cube */
for ( i = r = 0 ; r < 6 ; + + r )
{
unsigned int g ;
for ( g = 0 ; g < 6 ; + + g )
{
unsigned int b ;
for ( b = 0 ; b < 6 ; + + b )
png_create_colormap_entry ( display , i + + , r * 51 , g * 51 , b * 51 , 255 ,
E_sRGB ) ;
}
}
return i ;
}
# define PNG_RGB_COLORMAP_ENTRIES 216
/* Return a palette index to the above palette given three 8-bit sRGB values. */
# define PNG_RGB_INDEX(r,g,b) \
( ( png_byte ) ( 6 * ( 6 * PNG_DIV51 ( r ) + PNG_DIV51 ( g ) ) + PNG_DIV51 ( b ) ) )
static int
png_image_read_colormap ( png_voidp argument )
{
png_image_read_control * display =
png_voidcast ( png_image_read_control * , argument ) ;
const png_imagep image = display - > image ;
const png_structrp png_ptr = image - > opaque - > png_ptr ;
const png_uint_32 output_format = image - > format ;
const int output_encoding = ( output_format & PNG_FORMAT_FLAG_LINEAR ) ?
E_LINEAR : E_sRGB ;
unsigned int cmap_entries ;
unsigned int output_processing ; /* Output processing option */
unsigned int data_encoding = E_NOTSET ; /* Encoding libpng must produce */
/* Background information; the background color and the index of this color
* in the color - map if it exists ( else 256 ) .
*/
unsigned int background_index = 256 ;
png_uint_32 back_r , back_g , back_b ;
/* Flags to accumulate things that need to be done to the input. */
int expand_tRNS = 0 ;
/* Exclude the NYI feature of compositing onto a color-mapped buffer; it is
* very difficult to do , the results look awful , and it is difficult to see
* what possible use it is because the application can ' t control the
* color - map .
*/
if ( ( ( png_ptr - > color_type & PNG_COLOR_MASK_ALPHA ) ! = 0 | |
png_ptr - > num_trans > 0 ) /* alpha in input */ & &
( ( output_format & PNG_FORMAT_FLAG_ALPHA ) = = 0 ) /* no alpha in output */ )
{
if ( output_encoding = = E_LINEAR ) /* compose on black */
back_b = back_g = back_r = 0 ;
else if ( display - > background = = NULL /* no way to remove it */ )
png_error ( png_ptr ,
" a background color must be supplied to remove alpha/transparency " ) ;
/* Get a copy of the background color (this avoids repeating the checks
* below . ) The encoding is 8 - bit sRGB or 16 - bit linear , depending on the
* output format .
*/
else
{
back_g = display - > background - > green ;
if ( output_format & PNG_FORMAT_FLAG_COLOR )
{
back_r = display - > background - > red ;
back_b = display - > background - > blue ;
}
else
back_b = back_r = back_g ;
}
}
else if ( output_encoding = = E_LINEAR )
back_b = back_r = back_g = 65535 ;
else
back_b = back_r = back_g = 255 ;
/* Default the input file gamma if required - this is necessary because
* libpng assumes that if no gamma information is present the data is in the
* output format , but the simplified API deduces the gamma from the input
* format .
*/
if ( ( png_ptr - > colorspace . flags & PNG_COLORSPACE_HAVE_GAMMA ) = = 0 )
{
/* Do this directly, not using the png_colorspace functions, to ensure
* that it happens even if the colorspace is invalid ( though probably if
* it is the setting will be ignored ) Note that the same thing can be
* achieved at the application interface with png_set_gAMA .
*/
if ( png_ptr - > bit_depth = = 16 & &
( image - > flags & PNG_IMAGE_FLAG_16BIT_sRGB ) = = 0 )
png_ptr - > colorspace . gamma = PNG_GAMMA_LINEAR ;
else
png_ptr - > colorspace . gamma = PNG_GAMMA_sRGB_INVERSE ;
png_ptr - > colorspace . flags | = PNG_COLORSPACE_HAVE_GAMMA ;
}
/* Decide what to do based on the PNG color type of the input data. The
* utility function png_create_colormap_entry deals with most aspects of the
* output transformations ; this code works out how to produce bytes of
* color - map entries from the original format .
*/
switch ( png_ptr - > color_type )
{
case PNG_COLOR_TYPE_GRAY :
if ( png_ptr - > bit_depth < = 8 )
{
/* There at most 256 colors in the output, regardless of
* transparency .
*/
unsigned int step , i , val , trans = 256 /*ignore*/ , back_alpha = 0 ;
cmap_entries = 1U < < png_ptr - > bit_depth ;
if ( cmap_entries > image - > colormap_entries )
png_error ( png_ptr , " gray[8] color-map: too few entries " ) ;
step = 255 / ( cmap_entries - 1 ) ;
output_processing = PNG_CMAP_NONE ;
/* If there is a tRNS chunk then this either selects a transparent
* value or , if the output has no alpha , the background color .
*/
if ( png_ptr - > num_trans > 0 )
{
trans = png_ptr - > trans_color . gray ;
if ( ( output_format & PNG_FORMAT_FLAG_ALPHA ) = = 0 )
back_alpha = output_encoding = = E_LINEAR ? 65535 : 255 ;
}
/* png_create_colormap_entry just takes an RGBA and writes the
* corresponding color - map entry using the format from ' image ' ,
* including the required conversion to sRGB or linear as
* appropriate . The input values are always either sRGB ( if the
* gamma correction flag is 0 ) or 0. .255 scaled file encoded values
* ( if the function must gamma correct them ) .
*/
for ( i = val = 0 ; i < cmap_entries ; + + i , val + = step )
{
/* 'i' is a file value. While this will result in duplicated
* entries for 8 - bit non - sRGB encoded files it is necessary to
* have non - gamma corrected values to do tRNS handling .
*/
if ( i ! = trans )
png_create_colormap_entry ( display , i , val , val , val , 255 ,
E_FILE /*8-bit with file gamma*/ ) ;
/* Else this entry is transparent. The colors don't matter if
* there is an alpha channel ( back_alpha = = 0 ) , but it does no
* harm to pass them in ; the values are not set above so this
* passes in white .
*
* NOTE : this preserves the full precision of the application
* supplied background color when it is used .
*/
else
png_create_colormap_entry ( display , i , back_r , back_g , back_b ,
back_alpha , output_encoding ) ;
}
/* We need libpng to preserve the original encoding. */
data_encoding = E_FILE ;
/* The rows from libpng, while technically gray values, are now also
* color - map indicies ; however , they may need to be expanded to 1
* byte per pixel . This is what png_set_packing does ( i . e . , it
* unpacks the bit values into bytes . )
*/
if ( png_ptr - > bit_depth < 8 )
png_set_packing ( png_ptr ) ;
}
else /* bit depth is 16 */
{
/* The 16-bit input values can be converted directly to 8-bit gamma
* encoded values ; however , if a tRNS chunk is present 257 color - map
* entries are required . This means that the extra entry requires
* special processing ; add an alpha channel , sacrifice gray level
* 254 and convert transparent ( alpha = = 0 ) entries to that .
*
* Use libpng to chop the data to 8 bits . Convert it to sRGB at the
* same time to minimize quality loss . If a tRNS chunk is present
* this means libpng must handle it too ; otherwise it is impossible
* to do the exact match on the 16 - bit value .
*
* If the output has no alpha channel * and * the background color is
* gray then it is possible to let libpng handle the substitution by
* ensuring that the corresponding gray level matches the background
* color exactly .
*/
data_encoding = E_sRGB ;
if ( PNG_GRAY_COLORMAP_ENTRIES > image - > colormap_entries )
png_error ( png_ptr , " gray[16] color-map: too few entries " ) ;
cmap_entries = make_gray_colormap ( display ) ;
if ( png_ptr - > num_trans > 0 )
{
unsigned int back_alpha ;
if ( output_format & PNG_FORMAT_FLAG_ALPHA )
back_alpha = 0 ;
else
{
if ( back_r = = back_g & & back_g = = back_b )
{
/* Background is gray; no special processing will be
* required .
*/
png_color_16 c ;
png_uint_32 gray = back_g ;
if ( output_encoding = = E_LINEAR )
{
gray = PNG_sRGB_FROM_LINEAR ( gray * 255 ) ;
/* And make sure the corresponding palette entry
* matches .
*/
png_create_colormap_entry ( display , gray , back_g , back_g ,
back_g , 65535 , E_LINEAR ) ;
}
/* The background passed to libpng, however, must be the
* sRGB value .
*/
c . index = 0 ; /*unused*/
c . gray = c . red = c . green = c . blue = ( png_uint_16 ) gray ;
/* NOTE: does this work without expanding tRNS to alpha?
* It should be the color - > gray case below apparently
* doesn ' t .
*/
png_set_background_fixed ( png_ptr , & c ,
PNG_BACKGROUND_GAMMA_SCREEN , 0 /*need_expand*/ ,
0 /*gamma: not used*/ ) ;
output_processing = PNG_CMAP_NONE ;
break ;
}
back_alpha = output_encoding = = E_LINEAR ? 65535 : 255 ;
}
/* output_processing means that the libpng-processed row will be
* 8 - bit GA and it has to be processing to single byte color - map
* values . Entry 254 is replaced by either a completely
* transparent entry or by the background color at full
* precision ( and the background color is not a simple gray leve
* in this case . )
*/
expand_tRNS = 1 ;
output_processing = PNG_CMAP_TRANS ;
background_index = 254 ;
/* And set (overwrite) color-map entry 254 to the actual
* background color at full precision .
*/
png_create_colormap_entry ( display , 254 , back_r , back_g , back_b ,
back_alpha , output_encoding ) ;
}
else
output_processing = PNG_CMAP_NONE ;
}
break ;
case PNG_COLOR_TYPE_GRAY_ALPHA :
/* 8-bit or 16-bit PNG with two channels - gray and alpha. A minimum
* of 65536 combinations . If , however , the alpha channel is to be
* removed there are only 256 possibilities if the background is gray .
* ( Otherwise there is a subset of the 65536 possibilities defined by
* the triangle between black , white and the background color . )
*
* Reduce 16 - bit files to 8 - bit and sRGB encode the result . No need to
* worry about tRNS matching - tRNS is ignored if there is an alpha
* channel .
*/
data_encoding = E_sRGB ;
if ( output_format & PNG_FORMAT_FLAG_ALPHA )
{
if ( PNG_GA_COLORMAP_ENTRIES > image - > colormap_entries )
png_error ( png_ptr , " gray+alpha color-map: too few entries " ) ;
cmap_entries = make_ga_colormap ( display ) ;
background_index = PNG_CMAP_GA_BACKGROUND ;
output_processing = PNG_CMAP_GA ;
}
else /* alpha is removed */
{
/* Alpha must be removed as the PNG data is processed when the
* background is a color because the G and A channels are
* independent and the vector addition ( non - parallel vectors ) is a
* 2 - D problem .
*
* This can be reduced to the same algorithm as above by making a
* colormap containing gray levels ( for the opaque grays ) , a
* background entry ( for a transparent pixel ) and a set of four six
* level color values , one set for each intermediate alpha value .
* See the comments in make_ga_colormap for how this works in the
* per - pixel processing .
*
* If the background is gray , however , we only need a 256 entry gray
* level color map . It is sufficient to make the entry generated
* for the background color be exactly the color specified .
*/
if ( ( output_format & PNG_FORMAT_FLAG_COLOR ) = = 0 | |
( back_r = = back_g & & back_g = = back_b ) )
{
/* Background is gray; no special processing will be required. */
png_color_16 c ;
png_uint_32 gray = back_g ;
if ( PNG_GRAY_COLORMAP_ENTRIES > image - > colormap_entries )
png_error ( png_ptr , " gray-alpha color-map: too few entries " ) ;
cmap_entries = make_gray_colormap ( display ) ;
if ( output_encoding = = E_LINEAR )
{
gray = PNG_sRGB_FROM_LINEAR ( gray * 255 ) ;
/* And make sure the corresponding palette entry matches. */
png_create_colormap_entry ( display , gray , back_g , back_g ,
back_g , 65535 , E_LINEAR ) ;
}
/* The background passed to libpng, however, must be the sRGB
* value .
*/
c . index = 0 ; /*unused*/
c . gray = c . red = c . green = c . blue = ( png_uint_16 ) gray ;
png_set_background_fixed ( png_ptr , & c ,
PNG_BACKGROUND_GAMMA_SCREEN , 0 /*need_expand*/ ,
0 /*gamma: not used*/ ) ;
output_processing = PNG_CMAP_NONE ;
}
else
{
png_uint_32 i , a ;
/* This is the same as png_make_ga_colormap, above, except that
* the entries are all opaque .
*/
if ( PNG_GA_COLORMAP_ENTRIES > image - > colormap_entries )
png_error ( png_ptr , " ga-alpha color-map: too few entries " ) ;
i = 0 ;
while ( i < 231 )
{
png_uint_32 gray = ( i * 256 + 115 ) / 231 ;
png_create_colormap_entry ( display , i + + , gray , gray , gray ,
255 , E_sRGB ) ;
}
/* NOTE: this preserves the full precision of the application
* background color .
*/
background_index = i ;
png_create_colormap_entry ( display , i + + , back_r , back_g , back_b ,
output_encoding = = E_LINEAR ? 65535U : 255U , output_encoding ) ;
/* For non-opaque input composite on the sRGB background - this
* requires inverting the encoding for each component . The input
* is still converted to the sRGB encoding because this is a
* reasonable approximate to the logarithmic curve of human
* visual sensitivity , at least over the narrow range which PNG
* represents . Consequently ' G ' is always sRGB encoded , while
* ' A ' is linear . We need the linear background colors .
*/
if ( output_encoding = = E_sRGB ) /* else already linear */
{
/* This may produce a value not exactly matching the
* background , but that ' s ok because these numbers are only
* used when alpha ! = 0
*/
back_r = png_sRGB_table [ back_r ] ;
back_g = png_sRGB_table [ back_g ] ;
back_b = png_sRGB_table [ back_b ] ;
}
for ( a = 1 ; a < 5 ; + + a )
{
unsigned int g ;
/* PNG_sRGB_FROM_LINEAR expects a 16-bit linear value scaled
* by an 8 - bit alpha value ( 0. .255 ) .
*/
png_uint_32 alpha = 51 * a ;
png_uint_32 back_rx = ( 255 - alpha ) * back_r ;
png_uint_32 back_gx = ( 255 - alpha ) * back_g ;
png_uint_32 back_bx = ( 255 - alpha ) * back_b ;
for ( g = 0 ; g < 6 ; + + g )
{
png_uint_32 gray = png_sRGB_table [ g * 51 ] * alpha ;
png_create_colormap_entry ( display , i + + ,
PNG_sRGB_FROM_LINEAR ( gray + back_rx ) ,
PNG_sRGB_FROM_LINEAR ( gray + back_gx ) ,
PNG_sRGB_FROM_LINEAR ( gray + back_bx ) , 255 , E_sRGB ) ;
}
}
cmap_entries = i ;
output_processing = PNG_CMAP_GA ;
}
}
break ;
case PNG_COLOR_TYPE_RGB :
case PNG_COLOR_TYPE_RGB_ALPHA :
/* Exclude the case where the output is gray; we can always handle this
* with the cases above .
*/
if ( ( output_format & PNG_FORMAT_FLAG_COLOR ) = = 0 )
{
/* The color-map will be grayscale, so we may as well convert the
* input RGB values to a simple grayscale and use the grayscale
* code above .
*
* NOTE : calling this apparently damages the recognition of the
* transparent color in background color handling ; call
* png_set_tRNS_to_alpha before png_set_background_fixed .
*/
png_set_rgb_to_gray_fixed ( png_ptr , PNG_ERROR_ACTION_NONE , - 1 ,
- 1 ) ;
data_encoding = E_sRGB ;
/* The output will now be one or two 8-bit gray or gray+alpha
* channels . The more complex case arises when the input has alpha .
*/
if ( ( png_ptr - > color_type = = PNG_COLOR_TYPE_RGB_ALPHA | |
png_ptr - > num_trans > 0 ) & &
( output_format & PNG_FORMAT_FLAG_ALPHA ) ! = 0 )
{
/* Both input and output have an alpha channel, so no background
* processing is required ; just map the GA bytes to the right
* color - map entry .
*/
expand_tRNS = 1 ;
if ( PNG_GA_COLORMAP_ENTRIES > image - > colormap_entries )
png_error ( png_ptr , " rgb[ga] color-map: too few entries " ) ;
cmap_entries = make_ga_colormap ( display ) ;
background_index = PNG_CMAP_GA_BACKGROUND ;
output_processing = PNG_CMAP_GA ;
}
else
{
/* Either the input or the output has no alpha channel, so there
* will be no non - opaque pixels in the color - map ; it will just be
* grayscale .
*/
if ( PNG_GRAY_COLORMAP_ENTRIES > image - > colormap_entries )
png_error ( png_ptr , " rgb[gray] color-map: too few entries " ) ;
/* Ideally this code would use libpng to do the gamma correction,
* but if an input alpha channel is to be removed we will hit the
* libpng bug in gamma + compose + rgb - to - gray ( the double gamma
* correction bug ) . Fix this by dropping the gamma correction in
* this case and doing it in the palette ; this will result in
* duplicate palette entries , but that ' s better than the
* alternative of double gamma correction .
*/
if ( ( png_ptr - > color_type = = PNG_COLOR_TYPE_RGB_ALPHA | |
png_ptr - > num_trans > 0 ) & &
png_gamma_not_sRGB ( png_ptr - > colorspace . gamma ) )
{
cmap_entries = make_gray_file_colormap ( display ) ;
data_encoding = E_FILE ;
}
else
cmap_entries = make_gray_colormap ( display ) ;
/* But if the input has alpha or transparency it must be removed
*/
if ( png_ptr - > color_type = = PNG_COLOR_TYPE_RGB_ALPHA | |
png_ptr - > num_trans > 0 )
{
png_color_16 c ;
png_uint_32 gray = back_g ;
/* We need to ensure that the application background exists in
* the colormap and that completely transparent pixels map to
* it . Achieve this simply by ensuring that the entry
* selected for the background really is the background color .
*/
if ( data_encoding = = E_FILE ) /* from the fixup above */
{
/* The app supplied a gray which is in output_encoding, we
* need to convert it to a value of the input ( E_FILE )
* encoding then set this palette entry to the required
* output encoding .
*/
if ( output_encoding = = E_sRGB )
gray = png_sRGB_table [ gray ] ; /* now E_LINEAR */
gray = PNG_DIV257 ( png_gamma_16bit_correct ( gray ,
png_ptr - > colorspace . gamma ) ) ; /* now E_FILE */
/* And make sure the corresponding palette entry contains
* exactly the required sRGB value .
*/
png_create_colormap_entry ( display , gray , back_g , back_g ,
back_g , 0 /*unused*/ , output_encoding ) ;
}
else if ( output_encoding = = E_LINEAR )
{
gray = PNG_sRGB_FROM_LINEAR ( gray * 255 ) ;
/* And make sure the corresponding palette entry matches.
*/
png_create_colormap_entry ( display , gray , back_g , back_g ,
back_g , 0 /*unused*/ , E_LINEAR ) ;
}
/* The background passed to libpng, however, must be the
* output ( normally sRGB ) value .
*/
c . index = 0 ; /*unused*/
c . gray = c . red = c . green = c . blue = ( png_uint_16 ) gray ;
/* NOTE: the following is apparently a bug in libpng. Without
* it the transparent color recognition in
* png_set_background_fixed seems to go wrong .
*/
expand_tRNS = 1 ;
png_set_background_fixed ( png_ptr , & c ,
PNG_BACKGROUND_GAMMA_SCREEN , 0 /*need_expand*/ ,
0 /*gamma: not used*/ ) ;
}
output_processing = PNG_CMAP_NONE ;
}
}
else /* output is color */
{
/* We could use png_quantize here so long as there is no transparent
* color or alpha ; png_quantize ignores alpha . Easier overall just
* to do it once and using PNG_DIV51 on the 6 x6x6 reduced RGB cube .
* Consequently we always want libpng to produce sRGB data .
*/
data_encoding = E_sRGB ;
/* Is there any transparency or alpha? */
if ( png_ptr - > color_type = = PNG_COLOR_TYPE_RGB_ALPHA | |
png_ptr - > num_trans > 0 )
{
/* Is there alpha in the output too? If so all four channels are
* processed into a special RGB cube with alpha support .
*/
if ( output_format & PNG_FORMAT_FLAG_ALPHA )
{
png_uint_32 r ;
if ( PNG_RGB_COLORMAP_ENTRIES + 1 + 27 > image - > colormap_entries )
png_error ( png_ptr , " rgb+alpha color-map: too few entries " ) ;
cmap_entries = make_rgb_colormap ( display ) ;
/* Add a transparent entry. */
png_create_colormap_entry ( display , cmap_entries , 255 , 255 ,
255 , 0 , E_sRGB ) ;
/* This is stored as the background index for the processing
* algorithm .
*/
background_index = cmap_entries + + ;
/* Add 27 r,g,b entries each with alpha 0.5. */
for ( r = 0 ; r < 256 ; r = ( r < < 1 ) | 0x7f )
{
png_uint_32 g ;
for ( g = 0 ; g < 256 ; g = ( g < < 1 ) | 0x7f )
{
png_uint_32 b ;
/* This generates components with the values 0, 127 and
* 255
*/
for ( b = 0 ; b < 256 ; b = ( b < < 1 ) | 0x7f )
png_create_colormap_entry ( display , cmap_entries + + ,
r , g , b , 128 , E_sRGB ) ;
}
}
expand_tRNS = 1 ;
output_processing = PNG_CMAP_RGB_ALPHA ;
}
else
{
/* Alpha/transparency must be removed. The background must
* exist in the color map ( achieved by setting adding it after
* the 666 color - map ) . If the standard processing code will
* pick up this entry automatically that ' s all that is
* required ; libpng can be called to do the background
* processing .
*/
unsigned int sample_size =
PNG_IMAGE_SAMPLE_SIZE ( output_format ) ;
png_uint_32 r , g , b ; /* sRGB background */
if ( PNG_RGB_COLORMAP_ENTRIES + 1 + 27 > image - > colormap_entries )
png_error ( png_ptr , " rgb-alpha color-map: too few entries " ) ;
cmap_entries = make_rgb_colormap ( display ) ;
png_create_colormap_entry ( display , cmap_entries , back_r ,
back_g , back_b , 0 /*unused*/ , output_encoding ) ;
if ( output_encoding = = E_LINEAR )
{
r = PNG_sRGB_FROM_LINEAR ( back_r * 255 ) ;
g = PNG_sRGB_FROM_LINEAR ( back_g * 255 ) ;
b = PNG_sRGB_FROM_LINEAR ( back_b * 255 ) ;
}
else
{
r = back_r ;
g = back_g ;
b = back_g ;
}
/* Compare the newly-created color-map entry with the one the
* PNG_CMAP_RGB algorithm will use . If the two entries don ' t
* match , add the new one and set this as the background
* index .
*/
if ( memcmp ( ( png_const_bytep ) display - > colormap +
sample_size * cmap_entries ,
( png_const_bytep ) display - > colormap +
sample_size * PNG_RGB_INDEX ( r , g , b ) ,
sample_size ) ! = 0 )
{
/* The background color must be added. */
background_index = cmap_entries + + ;
/* Add 27 r,g,b entries each with created by composing with
* the background at alpha 0.5 .
*/
for ( r = 0 ; r < 256 ; r = ( r < < 1 ) | 0x7f )
{
for ( g = 0 ; g < 256 ; g = ( g < < 1 ) | 0x7f )
{
/* This generates components with the values 0, 127
* and 255
*/
for ( b = 0 ; b < 256 ; b = ( b < < 1 ) | 0x7f )
png_create_colormap_entry ( display , cmap_entries + + ,
png_colormap_compose ( display , r , E_sRGB , 128 ,
back_r , output_encoding ) ,
png_colormap_compose ( display , g , E_sRGB , 128 ,
back_g , output_encoding ) ,
png_colormap_compose ( display , b , E_sRGB , 128 ,
back_b , output_encoding ) ,
0 /*unused*/ , output_encoding ) ;
}
}
expand_tRNS = 1 ;
output_processing = PNG_CMAP_RGB_ALPHA ;
}
else /* background color is in the standard color-map */
{
png_color_16 c ;
c . index = 0 ; /*unused*/
c . red = ( png_uint_16 ) back_r ;
c . gray = c . green = ( png_uint_16 ) back_g ;
c . blue = ( png_uint_16 ) back_b ;
png_set_background_fixed ( png_ptr , & c ,
PNG_BACKGROUND_GAMMA_SCREEN , 0 /*need_expand*/ ,
0 /*gamma: not used*/ ) ;
output_processing = PNG_CMAP_RGB ;
}
}
}
else /* no alpha or transparency in the input */
{
/* Alpha in the output is irrelevant, simply map the opaque input
* pixels to the 6 x6x6 color - map .
*/
if ( PNG_RGB_COLORMAP_ENTRIES > image - > colormap_entries )
png_error ( png_ptr , " rgb color-map: too few entries " ) ;
cmap_entries = make_rgb_colormap ( display ) ;
output_processing = PNG_CMAP_RGB ;
}
}
break ;
case PNG_COLOR_TYPE_PALETTE :
/* It's already got a color-map. It may be necessary to eliminate the
* tRNS entries though .
*/
{
unsigned int num_trans = png_ptr - > num_trans ;
png_const_bytep trans = num_trans > 0 ? png_ptr - > trans_alpha : NULL ;
png_const_colorp colormap = png_ptr - > palette ;
const int do_background = trans ! = NULL & &
( output_format & PNG_FORMAT_FLAG_ALPHA ) = = 0 ;
unsigned int i ;
/* Just in case: */
if ( trans = = NULL )
num_trans = 0 ;
output_processing = PNG_CMAP_NONE ;
data_encoding = E_FILE ; /* Don't change from color-map indicies */
cmap_entries = png_ptr - > num_palette ;
if ( cmap_entries > 256 )
cmap_entries = 256 ;
if ( cmap_entries > image - > colormap_entries )
png_error ( png_ptr , " palette color-map: too few entries " ) ;
for ( i = 0 ; i < cmap_entries ; + + i )
{
if ( do_background & & i < num_trans & & trans [ i ] < 255 )
{
if ( trans [ i ] = = 0 )
png_create_colormap_entry ( display , i , back_r , back_g ,
back_b , 0 , output_encoding ) ;
else
{
/* Must compose the PNG file color in the color-map entry
* on the sRGB color in ' back ' .
*/
png_create_colormap_entry ( display , i ,
png_colormap_compose ( display , colormap [ i ] . red , E_FILE ,
trans [ i ] , back_r , output_encoding ) ,
png_colormap_compose ( display , colormap [ i ] . green , E_FILE ,
trans [ i ] , back_g , output_encoding ) ,
png_colormap_compose ( display , colormap [ i ] . blue , E_FILE ,
trans [ i ] , back_b , output_encoding ) ,
output_encoding = = E_LINEAR ? trans [ i ] * 257U :
trans [ i ] ,
output_encoding ) ;
}
}
else
png_create_colormap_entry ( display , i , colormap [ i ] . red ,
colormap [ i ] . green , colormap [ i ] . blue ,
i < num_trans ? trans [ i ] : 255U , E_FILE /*8-bit*/ ) ;
}
/* The PNG data may have indicies packed in fewer than 8 bits, it
* must be expanded if so .
*/
if ( png_ptr - > bit_depth < 8 )
png_set_packing ( png_ptr ) ;
}
break ;
default :
png_error ( png_ptr , " invalid PNG color type " ) ;
/*NOT REACHED*/
break ;
}
/* Now deal with the output processing */
if ( expand_tRNS & & png_ptr - > num_trans > 0 & &
( png_ptr - > color_type & PNG_COLOR_MASK_ALPHA ) = = 0 )
png_set_tRNS_to_alpha ( png_ptr ) ;
switch ( data_encoding )
{
default :
png_error ( png_ptr , " bad data option (internal error) " ) ;
break ;
case E_sRGB :
/* Change to 8-bit sRGB */
png_set_alpha_mode_fixed ( png_ptr , PNG_ALPHA_PNG , PNG_GAMMA_sRGB ) ;
/* FALL THROUGH */
case E_FILE :
if ( png_ptr - > bit_depth > 8 )
png_set_scale_16 ( png_ptr ) ;
break ;
}
if ( cmap_entries > 256 | | cmap_entries > image - > colormap_entries )
png_error ( png_ptr , " color map overflow (BAD internal error) " ) ;
image - > colormap_entries = cmap_entries ;
/* Double check using the recorded background index */
switch ( output_processing )
{
case PNG_CMAP_NONE :
if ( background_index ! = PNG_CMAP_NONE_BACKGROUND )
goto bad_background ;
break ;
case PNG_CMAP_GA :
if ( background_index ! = PNG_CMAP_GA_BACKGROUND )
goto bad_background ;
break ;
case PNG_CMAP_TRANS :
if ( background_index > = cmap_entries | |
background_index ! = PNG_CMAP_TRANS_BACKGROUND )
goto bad_background ;
break ;
case PNG_CMAP_RGB :
if ( background_index ! = PNG_CMAP_RGB_BACKGROUND )
goto bad_background ;
break ;
case PNG_CMAP_RGB_ALPHA :
if ( background_index ! = PNG_CMAP_RGB_ALPHA_BACKGROUND )
goto bad_background ;
break ;
default :
png_error ( png_ptr , " bad processing option (internal error) " ) ;
bad_background :
png_error ( png_ptr , " bad background index (internal error) " ) ;
}
display - > colormap_processing = output_processing ;
return 1 /*ok*/ ;
}
/* The final part of the color-map read called from png_image_finish_read. */
static int
png_image_read_and_map ( png_voidp argument )
{
png_image_read_control * display = png_voidcast ( png_image_read_control * ,
argument ) ;
png_imagep image = display - > image ;
png_structrp png_ptr = image - > opaque - > png_ptr ;
int passes ;
/* Called when the libpng data must be transformed into the color-mapped
* form . There is a local row buffer in display - > local and this routine must
* do the interlace handling .
*/
switch ( png_ptr - > interlaced )
{
case PNG_INTERLACE_NONE :
passes = 1 ;
break ;
case PNG_INTERLACE_ADAM7 :
passes = PNG_INTERLACE_ADAM7_PASSES ;
break ;
default :
passes = 0 ;
png_error ( png_ptr , " unknown interlace type " ) ;
}
{
png_uint_32 height = image - > height ;
png_uint_32 width = image - > width ;
int proc = display - > colormap_processing ;
png_bytep first_row = png_voidcast ( png_bytep , display - > first_row ) ;
ptrdiff_t step_row = display - > row_bytes ;
int pass ;
for ( pass = 0 ; pass < passes ; + + pass )
{
unsigned int startx , stepx , stepy ;
png_uint_32 y ;
if ( png_ptr - > interlaced = = PNG_INTERLACE_ADAM7 )
{
/* The row may be empty for a short image: */
if ( PNG_PASS_COLS ( width , pass ) = = 0 )
continue ;
startx = PNG_PASS_START_COL ( pass ) ;
stepx = PNG_PASS_COL_OFFSET ( pass ) ;
y = PNG_PASS_START_ROW ( pass ) ;
stepy = PNG_PASS_ROW_OFFSET ( pass ) ;
}
else
{
y = 0 ;
startx = 0 ;
stepx = stepy = 1 ;
}
for ( ; y < height ; y + = stepy )
{
png_bytep inrow = png_voidcast ( png_bytep , display - > local_row ) ;
png_bytep outrow = first_row + y * step_row ;
png_const_bytep end_row = outrow + width ;
/* Read read the libpng data into the temporary buffer. */
png_read_row ( png_ptr , inrow , NULL ) ;
/* Now process the row according to the processing option, note
* that the caller verifies that the format of the libpng output
* data is as required .
*/
outrow + = startx ;
switch ( proc )
{
case PNG_CMAP_GA :
for ( ; outrow < end_row ; outrow + = stepx )
{
/* The data is always in the PNG order */
unsigned int gray = * inrow + + ;
unsigned int alpha = * inrow + + ;
unsigned int entry ;
/* NOTE: this code is copied as a comment in
* make_ga_colormap above . Please update the
* comment if you change this code !
*/
if ( alpha > 229 ) /* opaque */
{
entry = ( 231 * gray + 128 ) > > 8 ;
}
else if ( alpha < 26 ) /* transparent */
{
entry = 231 ;
}
else /* partially opaque */
{
entry = 226 + 6 * PNG_DIV51 ( alpha ) + PNG_DIV51 ( gray ) ;
}
* outrow = ( png_byte ) entry ;
}
break ;
case PNG_CMAP_TRANS :
for ( ; outrow < end_row ; outrow + = stepx )
{
png_byte gray = * inrow + + ;
png_byte alpha = * inrow + + ;
if ( alpha = = 0 )
* outrow = PNG_CMAP_TRANS_BACKGROUND ;
else if ( gray ! = PNG_CMAP_TRANS_BACKGROUND )
* outrow = gray ;
else
* outrow = ( png_byte ) ( PNG_CMAP_TRANS_BACKGROUND + 1 ) ;
}
break ;
case PNG_CMAP_RGB :
for ( ; outrow < end_row ; outrow + = stepx )
{
* outrow = PNG_RGB_INDEX ( inrow [ 0 ] , inrow [ 1 ] , inrow [ 2 ] ) ;
inrow + = 3 ;
}
break ;
case PNG_CMAP_RGB_ALPHA :
for ( ; outrow < end_row ; outrow + = stepx )
{
unsigned int alpha = inrow [ 3 ] ;
/* Because the alpha entries only hold alpha==0.5 values
* split the processing at alpha = = 0.25 ( 64 ) and 0.75
* ( 196 ) .
*/
if ( alpha > = 196 )
* outrow = PNG_RGB_INDEX ( inrow [ 0 ] , inrow [ 1 ] ,
inrow [ 2 ] ) ;
else if ( alpha < 64 )
* outrow = PNG_CMAP_RGB_ALPHA_BACKGROUND ;
else
{
/* Likewise there are three entries for each of r, g
* and b . We could select the entry by popcount on
* the top two bits on those architectures that
* support it , this is what the code below does ,
* crudely .
*/
unsigned int back_i = PNG_CMAP_RGB_ALPHA_BACKGROUND + 1 ;
/* Here are how the values map:
*
* 0x00 . . 0x3f - > 0
* 0x40 . . 0xbf - > 1
* 0xc0 . . 0xff - > 2
*
* So , as above with the explicit alpha checks , the
* breakpoints are at 64 and 196.
*/
if ( inrow [ 0 ] & 0x80 ) back_i + = 9 ; /* red */
if ( inrow [ 0 ] & 0x40 ) back_i + = 9 ;
if ( inrow [ 0 ] & 0x80 ) back_i + = 3 ; /* green */
if ( inrow [ 0 ] & 0x40 ) back_i + = 3 ;
if ( inrow [ 0 ] & 0x80 ) back_i + = 1 ; /* blue */
if ( inrow [ 0 ] & 0x40 ) back_i + = 1 ;
* outrow = ( png_byte ) back_i ;
}
inrow + = 4 ;
}
break ;
default :
break ;
}
}
}
}
return 1 ;
}
static int
png_image_read_colormapped ( png_voidp argument )
{
png_image_read_control * display = png_voidcast ( png_image_read_control * ,
argument ) ;
png_imagep image = display - > image ;
png_controlp control = image - > opaque ;
png_structrp png_ptr = control - > png_ptr ;
png_inforp info_ptr = control - > info_ptr ;
int passes = 0 ; /* As a flag */
PNG_SKIP_CHUNKS ( png_ptr ) ;
/* Update the 'info' structure and make sure the result is as required; first
* make sure to turn on the interlace handling if it will be required
* ( because it can ' t be turned on * after * the call to png_read_update_info ! )
*/
if ( display - > colormap_processing = = PNG_CMAP_NONE )
passes = png_set_interlace_handling ( png_ptr ) ;
png_read_update_info ( png_ptr , info_ptr ) ;
/* The expected output can be deduced from the colormap_processing option. */
switch ( display - > colormap_processing )
{
case PNG_CMAP_NONE :
/* Output must be one channel and one byte per pixel, the output
* encoding can be anything .
*/
if ( ( info_ptr - > color_type = = PNG_COLOR_TYPE_PALETTE | |
info_ptr - > color_type = = PNG_COLOR_TYPE_GRAY ) & &
info_ptr - > bit_depth = = 8 )
break ;
goto bad_output ;
case PNG_CMAP_TRANS :
case PNG_CMAP_GA :
/* Output must be two channels and the 'G' one must be sRGB, the latter
* can be checked with an exact number because it should have been set
* to this number above !
*/
if ( info_ptr - > color_type = = PNG_COLOR_TYPE_GRAY_ALPHA & &
info_ptr - > bit_depth = = 8 & &
png_ptr - > screen_gamma = = PNG_GAMMA_sRGB & &
image - > colormap_entries = = 256 )
break ;
goto bad_output ;
case PNG_CMAP_RGB :
/* Output must be 8-bit sRGB encoded RGB */
if ( info_ptr - > color_type = = PNG_COLOR_TYPE_RGB & &
info_ptr - > bit_depth = = 8 & &
png_ptr - > screen_gamma = = PNG_GAMMA_sRGB & &
image - > colormap_entries = = 216 )
break ;
goto bad_output ;
case PNG_CMAP_RGB_ALPHA :
/* Output must be 8-bit sRGB encoded RGBA */
if ( info_ptr - > color_type = = PNG_COLOR_TYPE_RGB_ALPHA & &
info_ptr - > bit_depth = = 8 & &
png_ptr - > screen_gamma = = PNG_GAMMA_sRGB & &
image - > colormap_entries = = 244 /* 216 + 1 + 27 */ )
break ;
/* goto bad_output; */
/* FALL THROUGH */
default :
bad_output :
png_error ( png_ptr , " bad color-map processing (internal error) " ) ;
}
/* Now read the rows. Do this here if it is possible to read directly into
* the output buffer , otherwise allocate a local row buffer of the maximum
* size libpng requires and call the relevant processing routine safely .
*/
{
png_voidp first_row = display - > buffer ;
ptrdiff_t row_bytes = display - > row_stride ;
/* The following expression is designed to work correctly whether it gives
* a signed or an unsigned result .
*/
if ( row_bytes < 0 )
{
char * ptr = png_voidcast ( char * , first_row ) ;
ptr + = ( image - > height - 1 ) * ( - row_bytes ) ;
first_row = png_voidcast ( png_voidp , ptr ) ;
}
display - > first_row = first_row ;
display - > row_bytes = row_bytes ;
}
if ( passes = = 0 )
{
int result ;
png_voidp row = png_malloc ( png_ptr , png_get_rowbytes ( png_ptr , info_ptr ) ) ;
display - > local_row = row ;
result = png_safe_execute ( image , png_image_read_and_map , display ) ;
display - > local_row = NULL ;
png_free ( png_ptr , row ) ;
return result ;
}
else
{
png_alloc_size_t row_bytes = display - > row_bytes ;
while ( - - passes > = 0 )
{
png_uint_32 y = image - > height ;
png_bytep row = png_voidcast ( png_bytep , display - > first_row ) ;
while ( y - - > 0 )
{
png_read_row ( png_ptr , row , NULL ) ;
row + = row_bytes ;
}
}
return 1 ;
}
}
/* Just the row reading part of png_image_read. */
static int
png_image_read_composite ( png_voidp argument )
{
png_image_read_control * display = png_voidcast ( png_image_read_control * ,
argument ) ;
png_imagep image = display - > image ;
png_structrp png_ptr = image - > opaque - > png_ptr ;
int passes ;
switch ( png_ptr - > interlaced )
{
case PNG_INTERLACE_NONE :
passes = 1 ;
break ;
case PNG_INTERLACE_ADAM7 :
passes = PNG_INTERLACE_ADAM7_PASSES ;
break ;
default :
passes = 0 ;
png_error ( png_ptr , " unknown interlace type " ) ;
}
{
png_uint_32 height = image - > height ;
png_uint_32 width = image - > width ;
ptrdiff_t step_row = display - > row_bytes ;
unsigned int channels = ( image - > format & PNG_FORMAT_FLAG_COLOR ) ? 3 : 1 ;
int pass ;
for ( pass = 0 ; pass < passes ; + + pass )
{
unsigned int startx , stepx , stepy ;
png_uint_32 y ;
if ( png_ptr - > interlaced = = PNG_INTERLACE_ADAM7 )
{
/* The row may be empty for a short image: */
if ( PNG_PASS_COLS ( width , pass ) = = 0 )
continue ;
startx = PNG_PASS_START_COL ( pass ) * channels ;
stepx = PNG_PASS_COL_OFFSET ( pass ) * channels ;
y = PNG_PASS_START_ROW ( pass ) ;
stepy = PNG_PASS_ROW_OFFSET ( pass ) ;
}
else
{
y = 0 ;
startx = 0 ;
stepx = channels ;
stepy = 1 ;
}
for ( ; y < height ; y + = stepy )
{
png_bytep inrow = png_voidcast ( png_bytep , display - > local_row ) ;
png_bytep outrow ;
png_const_bytep end_row ;
/* Read the row, which is packed: */
png_read_row ( png_ptr , inrow , NULL ) ;
outrow = png_voidcast ( png_bytep , display - > first_row ) ;
outrow + = y * step_row ;
end_row = outrow + width * channels ;
/* Now do the composition on each pixel in this row. */
outrow + = startx ;
for ( ; outrow < end_row ; outrow + = stepx )
{
png_byte alpha = inrow [ channels ] ;
if ( alpha > 0 ) /* else no change to the output */
{
unsigned int c ;
for ( c = 0 ; c < channels ; + + c )
{
png_uint_32 component = inrow [ c ] ;
if ( alpha < 255 ) /* else just use component */
{
/* This is PNG_OPTIMIZED_ALPHA, the component value
* is a linear 8 - bit value . Combine this with the
* current outrow [ c ] value which is sRGB encoded .
* Arithmetic here is 16 - bits to preserve the output
* values correctly .
*/
component * = 257 * 255 ; /* =65535 */
component + = ( 255 - alpha ) * png_sRGB_table [ outrow [ c ] ] ;
/* So 'component' is scaled by 255*65535 and is
* therefore appropriate for the sRGB to linear
* conversion table .
*/
component = PNG_sRGB_FROM_LINEAR ( component ) ;
}
outrow [ c ] = ( png_byte ) component ;
}
}
inrow + = channels + 1 ; /* components and alpha channel */
}
}
}
}
return 1 ;
}
/* The do_local_background case; called when all the following transforms are to
* be done :
*
* PNG_RGB_TO_GRAY
* PNG_COMPOSITE
* PNG_GAMMA
*
* This is a work - round for the fact that both the PNG_RGB_TO_GRAY and
* PNG_COMPOSITE code performs gamma correction , so we get double gamma
* correction . The fix - up is to prevent the PNG_COMPOSITE operation happening
* inside libpng , so this routine sees an 8 or 16 - bit gray + alpha row and handles
* the removal or pre - multiplication of the alpha channel .
*/
static int
png_image_read_background ( png_voidp argument )
{
png_image_read_control * display = png_voidcast ( png_image_read_control * ,
argument ) ;
png_imagep image = display - > image ;
png_structrp png_ptr = image - > opaque - > png_ptr ;
png_inforp info_ptr = image - > opaque - > info_ptr ;
png_uint_32 height = image - > height ;
png_uint_32 width = image - > width ;
int pass , passes ;
/* Double check the convoluted logic below. We expect to get here with
* libpng doing rgb to gray and gamma correction but background processing
* left to the png_image_read_background function . The rows libpng produce
* might be 8 or 16 - bit but should always have two channels ; gray plus alpha .
*/
if ( ( png_ptr - > transformations & PNG_RGB_TO_GRAY ) = = 0 )
png_error ( png_ptr , " lost rgb to gray " ) ;
if ( ( png_ptr - > transformations & PNG_COMPOSE ) ! = 0 )
png_error ( png_ptr , " unexpected compose " ) ;
if ( png_get_channels ( png_ptr , info_ptr ) ! = 2 )
png_error ( png_ptr , " lost/gained channels " ) ;
/* Expect the 8-bit case to always remove the alpha channel */
if ( ( image - > format & PNG_FORMAT_FLAG_LINEAR ) = = 0 & &
( image - > format & PNG_FORMAT_FLAG_ALPHA ) ! = 0 )
png_error ( png_ptr , " unexpected 8-bit transformation " ) ;
switch ( png_ptr - > interlaced )
{
case PNG_INTERLACE_NONE :
passes = 1 ;
break ;
case PNG_INTERLACE_ADAM7 :
passes = PNG_INTERLACE_ADAM7_PASSES ;
break ;
default :
passes = 0 ;
png_error ( png_ptr , " unknown interlace type " ) ;
}
switch ( png_get_bit_depth ( png_ptr , info_ptr ) )
{
default :
png_error ( png_ptr , " unexpected bit depth " ) ;
break ;
case 8 :
/* 8-bit sRGB gray values with an alpha channel; the alpha channel is
* to be removed by composing on a backgroundi : either the row if
* display - > background is NULL or display - > background - > green if not .
* Unlike the code above ALPHA_OPTIMIZED has * not * been done .
*/
{
png_bytep first_row = png_voidcast ( png_bytep , display - > first_row ) ;
ptrdiff_t step_row = display - > row_bytes ;
for ( pass = 0 ; pass < passes ; + + pass )
{
png_bytep row = png_voidcast ( png_bytep ,
display - > first_row ) ;
unsigned int startx , stepx , stepy ;
png_uint_32 y ;
if ( png_ptr - > interlaced = = PNG_INTERLACE_ADAM7 )
{
/* The row may be empty for a short image: */
if ( PNG_PASS_COLS ( width , pass ) = = 0 )
continue ;
startx = PNG_PASS_START_COL ( pass ) ;
stepx = PNG_PASS_COL_OFFSET ( pass ) ;
y = PNG_PASS_START_ROW ( pass ) ;
stepy = PNG_PASS_ROW_OFFSET ( pass ) ;
}
else
{
y = 0 ;
startx = 0 ;
stepx = stepy = 1 ;
}
if ( display - > background = = NULL )
{
for ( ; y < height ; y + = stepy )
{
png_bytep inrow = png_voidcast ( png_bytep ,
display - > local_row ) ;
png_bytep outrow = first_row + y * step_row ;
png_const_bytep end_row = outrow + width ;
/* Read the row, which is packed: */
png_read_row ( png_ptr , inrow , NULL ) ;
/* Now do the composition on each pixel in this row. */
outrow + = startx ;
for ( ; outrow < end_row ; outrow + = stepx )
{
png_byte alpha = inrow [ 1 ] ;
if ( alpha > 0 ) /* else no change to the output */
{
png_uint_32 component = inrow [ 0 ] ;
if ( alpha < 255 ) /* else just use component */
{
/* Since PNG_OPTIMIZED_ALPHA was not set it is
* necessary to invert the sRGB transfer
* function and multiply the alpha out .
*/
component = png_sRGB_table [ component ] * alpha ;
component + = png_sRGB_table [ outrow [ 0 ] ] *
( 255 - alpha ) ;
component = PNG_sRGB_FROM_LINEAR ( component ) ;
}
outrow [ 0 ] = ( png_byte ) component ;
}
inrow + = 2 ; /* gray and alpha channel */
}
}
}
else /* constant background value */
{
png_byte background8 = display - > background - > green ;
png_uint_16 background = png_sRGB_table [ background8 ] ;
for ( ; y < height ; y + = stepy )
{
png_bytep inrow = png_voidcast ( png_bytep ,
display - > local_row ) ;
png_bytep outrow = first_row + y * step_row ;
png_const_bytep end_row = outrow + width ;
/* Read the row, which is packed: */
png_read_row ( png_ptr , inrow , NULL ) ;
/* Now do the composition on each pixel in this row. */
outrow + = startx ;
for ( ; outrow < end_row ; outrow + = stepx )
{
png_byte alpha = inrow [ 1 ] ;
if ( alpha > 0 ) /* else use background */
{
png_uint_32 component = inrow [ 0 ] ;
if ( alpha < 255 ) /* else just use component */
{
component = png_sRGB_table [ component ] * alpha ;
component + = background * ( 255 - alpha ) ;
component = PNG_sRGB_FROM_LINEAR ( component ) ;
}
outrow [ 0 ] = ( png_byte ) component ;
}
else
outrow [ 0 ] = background8 ;
inrow + = 2 ; /* gray and alpha channel */
}
row + = display - > row_bytes ;
}
}
}
}
break ;
case 16 :
/* 16-bit linear with pre-multiplied alpha; the pre-multiplication must
* still be done and , maybe , the alpha channel removed . This code also
* handles the alpha - first option .
*/
{
png_uint_16p first_row = png_voidcast ( png_uint_16p ,
display - > first_row ) ;
/* The division by two is safe because the caller passed in a
* stride which was multiplied by 2 ( below ) to get row_bytes .
*/
ptrdiff_t step_row = display - > row_bytes / 2 ;
int preserve_alpha = ( image - > format & PNG_FORMAT_FLAG_ALPHA ) ! = 0 ;
unsigned int outchannels = 1 + preserve_alpha ;
int swap_alpha = 0 ;
if ( preserve_alpha & & ( image - > format & PNG_FORMAT_FLAG_AFIRST ) )
swap_alpha = 1 ;
for ( pass = 0 ; pass < passes ; + + pass )
{
unsigned int startx , stepx , stepy ;
png_uint_32 y ;
/* The 'x' start and step are adjusted to output components here.
*/
if ( png_ptr - > interlaced = = PNG_INTERLACE_ADAM7 )
{
/* The row may be empty for a short image: */
if ( PNG_PASS_COLS ( width , pass ) = = 0 )
continue ;
startx = PNG_PASS_START_COL ( pass ) * outchannels ;
stepx = PNG_PASS_COL_OFFSET ( pass ) * outchannels ;
y = PNG_PASS_START_ROW ( pass ) ;
stepy = PNG_PASS_ROW_OFFSET ( pass ) ;
}
else
{
y = 0 ;
startx = 0 ;
stepx = outchannels ;
stepy = 1 ;
}
for ( ; y < height ; y + = stepy )
{
png_const_uint_16p inrow ;
png_uint_16p outrow = first_row + y * step_row ;
png_uint_16p end_row = outrow + width * outchannels ;
/* Read the row, which is packed: */
png_read_row ( png_ptr , png_voidcast ( png_bytep ,
display - > local_row ) , NULL ) ;
inrow = png_voidcast ( png_const_uint_16p , display - > local_row ) ;
/* Now do the pre-multiplication on each pixel in this row.
*/
outrow + = startx ;
for ( ; outrow < end_row ; outrow + = stepx )
{
png_uint_32 component = inrow [ 0 ] ;
png_uint_16 alpha = inrow [ 1 ] ;
if ( alpha > 0 ) /* else 0 */
{
if ( alpha < 65535 ) /* else just use component */
{
component * = alpha ;
component + = 32767 ;
component / = 65535 ;
}
}
else
component = 0 ;
outrow [ swap_alpha ] = ( png_uint_16 ) component ;
if ( preserve_alpha )
outrow [ 1 ^ swap_alpha ] = alpha ;
inrow + = 2 ; /* components and alpha channel */
}
}
}
}
break ;
}
return 1 ;
}
/* The guts of png_image_finish_read as a png_safe_execute callback. */
static int
png_image_read_direct ( png_voidp argument )
{
png_image_read_control * display = png_voidcast ( png_image_read_control * ,
argument ) ;
png_imagep image = display - > image ;
png_structrp png_ptr = image - > opaque - > png_ptr ;
png_inforp info_ptr = image - > opaque - > info_ptr ;
png_uint_32 format = image - > format ;
int linear = ( format & PNG_FORMAT_FLAG_LINEAR ) ! = 0 ;
int do_local_compose = 0 ;
int do_local_background = 0 ; /* to avoid double gamma correction bug */
int passes = 0 ;
/* Add transforms to ensure the correct output format is produced then check
* that the required implementation support is there . Always expand ; always
* need 8 bits minimum , no palette and expanded tRNS .
*/
png_set_expand ( png_ptr ) ;
/* Now check the format to see if it was modified. */
{
png_uint_32 base_format = png_image_format ( png_ptr ) &
~ PNG_FORMAT_FLAG_COLORMAP /* removed by png_set_expand */ ;
png_uint_32 change = format ^ base_format ;
png_fixed_point output_gamma ;
int mode ; /* alpha mode */
/* Do this first so that we have a record if rgb to gray is happening. */
if ( change & PNG_FORMAT_FLAG_COLOR )
{
/* gray<->color transformation required. */
if ( format & PNG_FORMAT_FLAG_COLOR )
png_set_gray_to_rgb ( png_ptr ) ;
else
{
/* libpng can't do both rgb to gray and
* background / pre - multiplication if there is also significant gamma
* correction , because both operations require linear colors and
* the code only supports one transform doing the gamma correction .
* Handle this by doing the pre - multiplication or background
* operation in this code , if necessary .
*
* TODO : fix this by rewriting pngrtran . c ( ! )
*
* For the moment ( given that fixing this in pngrtran . c is an
* enormous change ) ' do_local_background ' is used to indicate that
* the problem exists .
*/
if ( base_format & PNG_FORMAT_FLAG_ALPHA )
do_local_background = 1 /*maybe*/ ;
png_set_rgb_to_gray_fixed ( png_ptr , PNG_ERROR_ACTION_NONE ,
PNG_RGB_TO_GRAY_DEFAULT , PNG_RGB_TO_GRAY_DEFAULT ) ;
}
change & = ~ PNG_FORMAT_FLAG_COLOR ;
}
/* Set the gamma appropriately, linear for 16-bit input, sRGB otherwise.
*/
{
png_fixed_point input_gamma_default ;
if ( ( base_format & PNG_FORMAT_FLAG_LINEAR ) & &
( image - > flags & PNG_IMAGE_FLAG_16BIT_sRGB ) = = 0 )
input_gamma_default = PNG_GAMMA_LINEAR ;
else
input_gamma_default = PNG_DEFAULT_sRGB ;
/* Call png_set_alpha_mode to set the default for the input gamma; the
* output gamma is set by a second call below .
*/
png_set_alpha_mode_fixed ( png_ptr , PNG_ALPHA_PNG , input_gamma_default ) ;
}
if ( linear )
{
/* If there *is* an alpha channel in the input it must be multiplied
* out ; use PNG_ALPHA_STANDARD , otherwise just use PNG_ALPHA_PNG .
*/
if ( base_format & PNG_FORMAT_FLAG_ALPHA )
mode = PNG_ALPHA_STANDARD ; /* associated alpha */
else
mode = PNG_ALPHA_PNG ;
output_gamma = PNG_GAMMA_LINEAR ;
}
else
{
mode = PNG_ALPHA_PNG ;
output_gamma = PNG_DEFAULT_sRGB ;
}
/* If 'do_local_background' is set check for the presence of gamma
* correction ; this is part of the work - round for the libpng bug
* described above .
*
* TODO : fix libpng and remove this .
*/
if ( do_local_background )
{
png_fixed_point gtest ;
/* This is 'png_gamma_threshold' from pngrtran.c; the test used for
* gamma correction , the screen gamma hasn ' t been set on png_struct
* yet ; it ' s set below . png_struct : : gamma , however , is set to the
* final value .
*/
if ( png_muldiv ( & gtest , output_gamma , png_ptr - > colorspace . gamma ,
PNG_FP_1 ) & & ! png_gamma_significant ( gtest ) )
do_local_background = 0 ;
else if ( mode = = PNG_ALPHA_STANDARD )
{
do_local_background = 2 /*required*/ ;
mode = PNG_ALPHA_PNG ; /* prevent libpng doing it */
}
/* else leave as 1 for the checks below */
}
/* If the bit-depth changes then handle that here. */
if ( change & PNG_FORMAT_FLAG_LINEAR )
{
if ( linear /*16-bit output*/ )
png_set_expand_16 ( png_ptr ) ;
else /* 8-bit output */
png_set_scale_16 ( png_ptr ) ;
change & = ~ PNG_FORMAT_FLAG_LINEAR ;
}
/* Now the background/alpha channel changes. */
if ( change & PNG_FORMAT_FLAG_ALPHA )
{
/* Removing an alpha channel requires composition for the 8-bit
* formats ; for the 16 - bit it is already done , above , by the
* pre - multiplication and the channel just needs to be stripped .
*/
if ( base_format & PNG_FORMAT_FLAG_ALPHA )
{
/* If RGB->gray is happening the alpha channel must be left and the
* operation completed locally .
*
* TODO : fix libpng and remove this .
*/
if ( do_local_background )
do_local_background = 2 /*required*/ ;
/* 16-bit output: just remove the channel */
else if ( linear ) /* compose on black (well, pre-multiply) */
png_set_strip_alpha ( png_ptr ) ;
/* 8-bit output: do an appropriate compose */
else if ( display - > background ! = NULL )
{
png_color_16 c ;
c . index = 0 ; /*unused*/
c . red = display - > background - > red ;
c . green = display - > background - > green ;
c . blue = display - > background - > blue ;
c . gray = display - > background - > green ;
/* This is always an 8-bit sRGB value, using the 'green' channel
* for gray is much better than calculating the luminance here ;
* we can get off - by - one errors in that calculation relative to
* the app expectations and that will show up in transparent
* pixels .
*/
png_set_background_fixed ( png_ptr , & c ,
PNG_BACKGROUND_GAMMA_SCREEN , 0 /*need_expand*/ ,
0 /*gamma: not used*/ ) ;
}
else /* compose on row: implemented below. */
{
do_local_compose = 1 ;
/* This leaves the alpha channel in the output, so it has to be
* removed by the code below . Set the encoding to the ' OPTIMIZE '
* one so the code only has to hack on the pixels that require
* composition .
*/
mode = PNG_ALPHA_OPTIMIZED ;
}
}
else /* output needs an alpha channel */
{
/* This is tricky because it happens before the swap operation has
* been accomplished ; however , the swap does * not * swap the added
* alpha channel ( weird API ) , so it must be added in the correct
* place .
*/
png_uint_32 filler ; /* opaque filler */
int where ;
if ( linear )
filler = 65535 ;
else
filler = 255 ;
# ifdef PNG_FORMAT_AFIRST_SUPPORTED
if ( format & PNG_FORMAT_FLAG_AFIRST )
{
where = PNG_FILLER_BEFORE ;
change & = ~ PNG_FORMAT_FLAG_AFIRST ;
}
else
# endif
where = PNG_FILLER_AFTER ;
png_set_add_alpha ( png_ptr , filler , where ) ;
}
/* This stops the (irrelevant) call to swap_alpha below. */
change & = ~ PNG_FORMAT_FLAG_ALPHA ;
}
/* Now set the alpha mode correctly; this is always done, even if there is
* no alpha channel in either the input or the output because it correctly
* sets the output gamma .
*/
png_set_alpha_mode_fixed ( png_ptr , mode , output_gamma ) ;
# ifdef PNG_FORMAT_BGR_SUPPORTED
if ( change & PNG_FORMAT_FLAG_BGR )
{
/* Check only the output format; PNG is never BGR; don't do this if
* the output is gray , but fix up the ' format ' value in that case .
*/
if ( format & PNG_FORMAT_FLAG_COLOR )
png_set_bgr ( png_ptr ) ;
else
format & = ~ PNG_FORMAT_FLAG_BGR ;
change & = ~ PNG_FORMAT_FLAG_BGR ;
}
# endif
# ifdef PNG_FORMAT_AFIRST_SUPPORTED
if ( change & PNG_FORMAT_FLAG_AFIRST )
{
/* Only relevant if there is an alpha channel - it's particularly
* important to handle this correctly because do_local_compose may
* be set above and then libpng will keep the alpha channel for this
* code to remove .
*/
if ( format & PNG_FORMAT_FLAG_ALPHA )
{
/* Disable this if doing a local background,
* TODO : remove this when local background is no longer required .
*/
if ( do_local_background ! = 2 )
png_set_swap_alpha ( png_ptr ) ;
}
else
format & = ~ PNG_FORMAT_FLAG_AFIRST ;
change & = ~ PNG_FORMAT_FLAG_AFIRST ;
}
# endif
/* If the *output* is 16-bit then we need to check for a byte-swap on this
* architecture .
*/
if ( linear )
{
PNG_CONST png_uint_16 le = 0x0001 ;
if ( * ( png_const_bytep ) & le )
png_set_swap ( png_ptr ) ;
}
/* If change is not now 0 some transformation is missing - error out. */
if ( change )
png_error ( png_ptr , " png_read_image: unsupported transformation " ) ;
}
PNG_SKIP_CHUNKS ( png_ptr ) ;
/* Update the 'info' structure and make sure the result is as required; first
* make sure to turn on the interlace handling if it will be required
* ( because it can ' t be turned on * after * the call to png_read_update_info ! )
*
* TODO : remove the do_local_background fixup below .
*/
if ( ! do_local_compose & & do_local_background ! = 2 )
passes = png_set_interlace_handling ( png_ptr ) ;
png_read_update_info ( png_ptr , info_ptr ) ;
{
png_uint_32 info_format = 0 ;
if ( info_ptr - > color_type & PNG_COLOR_MASK_COLOR )
info_format | = PNG_FORMAT_FLAG_COLOR ;
if ( info_ptr - > color_type & PNG_COLOR_MASK_ALPHA )
{
/* do_local_compose removes this channel below. */
if ( ! do_local_compose )
{
/* do_local_background does the same if required. */
if ( do_local_background ! = 2 | |
( format & PNG_FORMAT_FLAG_ALPHA ) ! = 0 )
info_format | = PNG_FORMAT_FLAG_ALPHA ;
}
}
else if ( do_local_compose ) /* internal error */
png_error ( png_ptr , " png_image_read: alpha channel lost " ) ;
if ( info_ptr - > bit_depth = = 16 )
info_format | = PNG_FORMAT_FLAG_LINEAR ;
# ifdef PNG_FORMAT_BGR_SUPPORTED
if ( png_ptr - > transformations & PNG_BGR )
info_format | = PNG_FORMAT_FLAG_BGR ;
# endif
# ifdef PNG_FORMAT_AFIRST_SUPPORTED
if ( do_local_background = = 2 )
{
if ( format & PNG_FORMAT_FLAG_AFIRST )
info_format | = PNG_FORMAT_FLAG_AFIRST ;
}
if ( ( png_ptr - > transformations & PNG_SWAP_ALPHA ) ! = 0 | |
( ( png_ptr - > transformations & PNG_ADD_ALPHA ) ! = 0 & &
( png_ptr - > flags & PNG_FLAG_FILLER_AFTER ) = = 0 ) )
{
if ( do_local_background = = 2 )
png_error ( png_ptr , " unexpected alpha swap transformation " ) ;
info_format | = PNG_FORMAT_FLAG_AFIRST ;
}
# endif
/* This is actually an internal error. */
if ( info_format ! = format )
png_error ( png_ptr , " png_read_image: invalid transformations " ) ;
}
/* Now read the rows. If do_local_compose is set then it is necessary to use
* a local row buffer . The output will be GA , RGBA or BGRA and must be
* converted to G , RGB or BGR as appropriate . The ' local_row ' member of the
* display acts as a flag .
*/
{
png_voidp first_row = display - > buffer ;
ptrdiff_t row_bytes = display - > row_stride ;
if ( linear )
row_bytes * = 2 ;
/* The following expression is designed to work correctly whether it gives
* a signed or an unsigned result .
*/
if ( row_bytes < 0 )
{
char * ptr = png_voidcast ( char * , first_row ) ;
ptr + = ( image - > height - 1 ) * ( - row_bytes ) ;
first_row = png_voidcast ( png_voidp , ptr ) ;
}
display - > first_row = first_row ;
display - > row_bytes = row_bytes ;
}
if ( do_local_compose )
{
int result ;
png_voidp row = png_malloc ( png_ptr , png_get_rowbytes ( png_ptr , info_ptr ) ) ;
display - > local_row = row ;
result = png_safe_execute ( image , png_image_read_composite , display ) ;
display - > local_row = NULL ;
png_free ( png_ptr , row ) ;
return result ;
}
else if ( do_local_background = = 2 )
{
int result ;
png_voidp row = png_malloc ( png_ptr , png_get_rowbytes ( png_ptr , info_ptr ) ) ;
display - > local_row = row ;
result = png_safe_execute ( image , png_image_read_background , display ) ;
display - > local_row = NULL ;
png_free ( png_ptr , row ) ;
return result ;
}
else
{
png_alloc_size_t row_bytes = display - > row_bytes ;
while ( - - passes > = 0 )
{
png_uint_32 y = image - > height ;
png_bytep row = png_voidcast ( png_bytep , display - > first_row ) ;
while ( y - - > 0 )
{
png_read_row ( png_ptr , row , NULL ) ;
row + = row_bytes ;
}
}
return 1 ;
}
}
int PNGAPI
png_image_finish_read ( png_imagep image , png_const_colorp background ,
void * buffer , png_int_32 row_stride , void * colormap )
{
if ( image ! = NULL & & image - > version = = PNG_IMAGE_VERSION )
{
png_uint_32 check ;
if ( row_stride = = 0 )
row_stride = PNG_IMAGE_ROW_STRIDE ( * image ) ;
if ( row_stride < 0 )
check = - row_stride ;
else
check = row_stride ;
if ( image - > opaque ! = NULL & & buffer ! = NULL & &
check > = PNG_IMAGE_ROW_STRIDE ( * image ) )
{
if ( ( image - > format & PNG_FORMAT_FLAG_COLORMAP ) = = 0 | |
( image - > colormap_entries > 0 & & colormap ! = NULL ) )
{
int result ;
png_image_read_control display ;
memset ( & display , 0 , ( sizeof display ) ) ;
display . image = image ;
display . buffer = buffer ;
display . row_stride = row_stride ;
display . colormap = colormap ;
display . background = background ;
display . local_row = NULL ;
/* Choose the correct 'end' routine; for the color-map case all the
* setup has already been done .
*/
if ( image - > format & PNG_FORMAT_FLAG_COLORMAP )
result =
png_safe_execute ( image , png_image_read_colormap , & display ) & &
png_safe_execute ( image , png_image_read_colormapped , & display ) ;
else
result =
png_safe_execute ( image , png_image_read_direct , & display ) ;
png_image_free ( image ) ;
return result ;
}
else
return png_image_error ( image ,
" png_image_finish_read[color-map]: no color-map " ) ;
}
else
return png_image_error ( image ,
" png_image_finish_read: invalid argument " ) ;
}
else if ( image ! = NULL )
return png_image_error ( image ,
" png_image_finish_read: damaged PNG_IMAGE_VERSION " ) ;
return 0 ;
}
# endif /* PNG_SIMPLIFIED_READ_SUPPORTED */
# endif /* PNG_READ_SUPPORTED */