@ -21,287 +21,141 @@ namespace dlib
// ----------------------------------------------------------------------------------------
struct LibpngData
{
png_bytep * row_pointers_ ;
png_structp png_ptr_ ;
png_infop info_ptr_ ;
png_infop end_info_ ;
} ;
struct PngBufferReaderState
{
const unsigned char * buffer_ ;
size_t buffer_size_ ;
size_t current_pos_ ;
} ;
struct FileInfo
{
FileInfo ( FILE * fp , const char * filename ) : fp_ ( fp ) , filename_ ( filename )
// Don't do anything when libpng calls us to tell us about an error. Just return to
// our own code and throw an exception (at the long jump target).
void png_loader_user_error_fn_silent ( png_structp png_struct , png_const_charp )
{
longjmp ( png_jmpbuf ( png_struct ) , 1 ) ;
}
FileInfo ( const unsigned char * buffer , size_t buffer_size ) : buffer_ ( buffer ) , buffer_size_ ( buffer_size )
void png_loader_user_warning_fn_silent ( png_structp , png_const_charp )
{
}
// no copying this object.
FileInfo ( const FileInfo & ) = delete ;
FileInfo & operator = ( const FileInfo & ) = delete ;
~ FileInfo ( )
void png_reader_callback ( png_structp png , png_bytep data , png_size_t length )
{
if ( fp_ ! = nullptr ) fclose ( fp_ ) ;
using callback_t = std : : function < std : : size_t ( char * , std : : size_t ) > ;
callback_t * clb = static_cast < callback_t * > ( png_get_io_ptr ( png ) ) ;
const auto ret = ( * clb ) ( ( char * ) data , length ) ;
if ( ret ! = length )
png_error ( png , " png_loader: read error in png_reader_callback " ) ;
}
FILE * fp_ { nullptr } ;
const char * filename_ { nullptr } ;
const unsigned char * buffer_ { nullptr } ;
size_t buffer_size_ { 0 } ;
} ;
// ----------------------------------------------------------------------------------------
png_loader : :
png_loader ( const char * filename ) : height_ ( 0 ) , width_ ( 0 )
void png_loader : : load ( std : : function < std : : size_t ( char * , std : : size_t ) > clb )
{
read_image ( check_file ( filename ) ) ;
}
// Read header
png_byte sig [ 8 ] ;
if ( clb ( ( char * ) sig , 8 ) ! = 8 )
throw image_load_error ( " png_loader: error reading file stream " ) ;
if ( png_sig_cmp ( sig , 0 , 8 ) ! = 0 )
throw image_load_error ( " png_loader: format error " ) ;
// ----------------------------------------------------------------------------------------
// Create structs
png_structp png_ptr = png_create_read_struct ( PNG_LIBPNG_VER_STRING , NULL , & png_loader_user_error_fn_silent , & png_loader_user_warning_fn_silent ) ;
if ( png_ptr = = NULL )
throw image_load_error ( " Error while reading PNG file : png_create_read_struct() " ) ;
png_loader : :
png_loader ( const std : : string & filename ) : height_ ( 0 ) , width_ ( 0 )
png_infop info_ptr = png_create_info_struct ( png_ptr ) ;
if ( info_ptr = = NULL )
{
read_image ( check_file ( filename . c_str ( ) ) ) ;
png_destroy_read_struct ( & png_ptr , ( png_infopp ) NULL , ( png_infopp ) NULL ) ;
throw image_load_error ( " Error while reading PNG file : png_create_info_struct() " ) ;
}
// ----------------------------------------------------------------------------------------
png_loader : :
png_loader ( const dlib : : file & f ) : height_ ( 0 ) , width_ ( 0 )
png_infop end_info = png_create_info_struct ( png_ptr ) ;
if ( end_info = = NULL )
{
read_image ( check_file ( f . full_name ( ) . c_str ( ) ) ) ;
png_destroy_read_struct ( & png_ptr , & info_ptr , ( png_infopp ) NULL ) ;
throw image_load_error ( " Error while reading PNG file : png_create_info_struct() " ) ;
}
// ----------------------------------------------------------------------------------------
png_loader : :
png_loader ( const unsigned char * image_buffer , size_t buffer_size ) : height_ ( 0 ) , width_ ( 0 )
if ( setjmp ( png_jmpbuf ( png_ptr ) ) )
{
read_image ( std : : unique_ptr < FileInfo > ( new FileInfo ( image_buffer , buffer_size ) ) ) ;
// If you get here, then there was an error while parsing.
png_destroy_read_struct ( & png_ptr , & info_ptr , & end_info ) ;
throw image_load_error ( " png_loader: parse error " ) ;
}
// ----------------------------------------------------------------------------------------
png_set_palette_to_rgb ( png_ptr ) ;
png_set_read_fn ( png_ptr , & clb , png_reader_callback ) ;
png_set_sig_bytes ( png_ptr , 8 ) ;
// flags force one byte per channel output
byte_orderer bo ;
int png_transforms = PNG_TRANSFORM_PACKING ;
if ( bo . host_is_little_endian ( ) )
png_transforms | = PNG_TRANSFORM_SWAP_ENDIAN ;
png_read_png ( png_ptr , info_ptr , png_transforms , NULL ) ;
const unsigned char * png_loader : : get_row ( unsigned i ) const
{
return ld_ - > row_pointers_ [ i ] ;
}
// If you get here, you are no longer affected by C's crazy longjmp
finalizer = std : : shared_ptr < char > ( new char , [ = ] ( char * ptr ) mutable {
delete ptr ;
png_destroy_read_struct ( & png_ptr , & info_ptr , & end_info ) ;
} ) ;
// ----------------------------------------------------------------------------------------
color_type = png_get_color_type ( png_ptr , info_ptr ) ;
height = png_get_image_height ( png_ptr , info_ptr ) ;
width = png_get_image_width ( png_ptr , info_ptr ) ;
bit_depth_ = png_get_bit_depth ( png_ptr , info_ptr ) ;
rows = ( unsigned char * * ) png_get_rows ( png_ptr , info_ptr ) ;
png_loader : : ~ png_loader ( )
{
if ( ld_ & & ld_ - > row_pointers_ ! = NULL )
png_destroy_read_struct ( & ( ld_ - > png_ptr_ ) , & ( ld_ - > info_ptr_ ) , & ( ld_ - > end_info_ ) ) ;
}
if ( ! is_gray ( ) & & ! is_graya ( ) & & ! is_rgb ( ) & & ! is_rgba ( ) )
throw image_load_error ( " png_loader: unsupported color type " ) ;
// ----------------------------------------------------------------------------------------
if ( bit_depth_ ! = 8 & & bit_depth_ ! = 16 )
throw image_load_error ( " png_loader: unsupported bit depth of " + std : : to_string ( bit_depth_ ) ) ;
bool png_loader : : is_gray ( ) const
{
return ( color_type_ = = PNG_COLOR_TYPE_GRAY ) ;
if ( rows = = NULL )
throw image_load_error ( " png_loader: parse error " ) ;
}
// ----------------------------------------------------------------------------------------
bool png_loader : : is_graya ( ) const
void png_loader : : load ( std : : istream & in )
{
return ( color_type_ = = PNG_COLOR_TYPE_GRAY_ALPHA ) ;
load ( [ & ] ( char * data , std : : size_t ndata ) {
in . read ( data , ndata ) ;
return in . gcount ( ) ;
} ) ;
}
// ----------------------------------------------------------------------------------------
bool png_loader : : is_rgb ( ) const
png_loader : : png_loader ( const unsigned char * image_buffer , std : : size_t buffer_size )
{
return ( color_type_ = = PNG_COLOR_TYPE_RGB ) ;
std : : size_t counter { 0 } ;
load ( [ & ] ( char * data , std : : size_t ndata ) {
ndata = std : : min ( ndata , buffer_size - counter ) ;
std : : memcpy ( data , image_buffer + counter , ndata ) ;
counter + = ndata ;
return ndata ;
} ) ;
}
// ----------------------------------------------------------------------------------------
bool png_loader : : is_rgba ( ) const
png_loader : : png_loader ( std : : istream & in )
{
return ( color_type_ = = PNG_COLOR_TYPE_RGB_ALPHA ) ;
load ( in ) ;
}
// ----------------------------------------------------------------------------------------
std : : unique_ptr < FileInfo > png_loader : : check_file ( const char * filename )
{
if ( filename = = NULL )
png_loader : : png_loader ( const char * filename )
{
throw image_load_error ( " png_loader: invalid filename, it is NULL " ) ;
}
FILE * fp = fopen ( filename , " rb " ) ;
if ( ! fp )
{
throw image_load_error ( std : : string ( " png_loader: unable to open file " ) + filename ) ;
std : : ifstream in ( filename , std : : ios : : binary ) ;
load ( in ) ;
}
return std : : unique_ptr < FileInfo > ( new FileInfo ( fp , filename ) ) ;
}
png_loader : : png_loader ( const std : : string & filename ) : png_loader ( filename . c_str ( ) ) { }
png_loader : : png_loader ( const dlib : : file & f ) : png_loader ( f . full_name ( ) ) { }
// ----------------------------------------------------------------------------------------
// Don't do anything when libpng calls us to tell us about an error. Just return to
// our own code and throw an exception (at the long jump target).
void png_loader_user_error_fn_silent ( png_structp png_struct , png_const_charp )
{
longjmp ( png_jmpbuf ( png_struct ) , 1 ) ;
}
void png_loader_user_warning_fn_silent ( png_structp , png_const_charp )
{
}
void png_buffer_reader ( png_structp png_ptr , png_bytep data , size_t length )
{
PngBufferReaderState * state = static_cast < PngBufferReaderState * > ( png_get_io_ptr ( png_ptr ) ) ;
if ( length > ( state - > buffer_size_ - state - > current_pos_ ) )
{
png_error ( png_ptr , " png_loader: read error in png_buffer_reader " ) ;
}
memcpy ( data , state - > buffer_ + state - > current_pos_ , length ) ;
state - > current_pos_ + = length ;
}
void png_loader : : read_image ( std : : unique_ptr < FileInfo > file_info )
{
DLIB_CASSERT ( file_info ) ;
ld_ . reset ( new LibpngData ) ;
constexpr png_size_t png_header_size = 8 ;
std : : string load_error_info ;
if ( file_info - > fp_ ! = NULL )
{
png_byte sig [ png_header_size ] ;
if ( fread ( sig , 1 , png_header_size , file_info - > fp_ ) ! = png_header_size )
{
throw image_load_error ( std : : string ( " png_loader: error reading file " ) + file_info - > filename_ ) ;
}
load_error_info = std : : string ( " in file " ) + file_info - > filename_ ;
if ( png_sig_cmp ( sig , 0 , png_header_size ) ! = 0 )
{
throw image_load_error ( std : : string ( " png_loader: format error " ) + load_error_info ) ;
}
}
else
{
if ( file_info - > buffer_ = = NULL )
{
throw image_load_error ( std : : string ( " png_loader: invalid image buffer, it is NULL " ) ) ;
}
if ( file_info - > buffer_size_ = = 0 )
{
throw image_load_error ( std : : string ( " png_loader: invalid image buffer size, it is 0 " ) ) ;
}
if ( file_info - > buffer_size_ < png_header_size | |
png_sig_cmp ( ( png_bytep ) file_info - > buffer_ , 0 , png_header_size ) ! = 0 )
{
throw image_load_error ( std : : string ( " png_loader: format error in image buffer " ) ) ;
}
buffer_reader_state_ . reset ( new PngBufferReaderState ) ;
}
ld_ - > png_ptr_ = png_create_read_struct ( PNG_LIBPNG_VER_STRING , NULL , & png_loader_user_error_fn_silent , & png_loader_user_warning_fn_silent ) ;
if ( ld_ - > png_ptr_ = = NULL )
{
std : : ostringstream sout ;
sout < < " Error, unable to allocate png structure " < < std : : endl ;
const char * runtime_version = png_get_header_ver ( NULL ) ;
if ( runtime_version & & std : : strcmp ( PNG_LIBPNG_VER_STRING , runtime_version ) ! = 0 )
{
sout < < " This is happening because you compiled against one version of libpng, but then linked to another. " < < std : : endl ;
sout < < " Compiled against libpng version: " < < PNG_LIBPNG_VER_STRING < < std : : endl ;
sout < < " Linking to this version of libpng: " < < runtime_version < < std : : endl ;
}
throw image_load_error ( sout . str ( ) ) ;
}
ld_ - > info_ptr_ = png_create_info_struct ( ld_ - > png_ptr_ ) ;
if ( ld_ - > info_ptr_ = = NULL )
{
png_destroy_read_struct ( & ( ld_ - > png_ptr_ ) , ( png_infopp ) NULL , ( png_infopp ) NULL ) ;
throw image_load_error ( std : : string ( " png_loader: unable to allocate png info structure " ) + load_error_info ) ;
}
ld_ - > end_info_ = png_create_info_struct ( ld_ - > png_ptr_ ) ;
if ( ld_ - > end_info_ = = NULL )
{
png_destroy_read_struct ( & ( ld_ - > png_ptr_ ) , & ( ld_ - > info_ptr_ ) , ( png_infopp ) NULL ) ;
throw image_load_error ( std : : string ( " png_loader: unable to allocate png info structure " ) + load_error_info ) ;
}
if ( setjmp ( png_jmpbuf ( ld_ - > png_ptr_ ) ) )
{
// If we get here, we had a problem writing the file
png_destroy_read_struct ( & ( ld_ - > png_ptr_ ) , & ( ld_ - > info_ptr_ ) , & ( ld_ - > end_info_ ) ) ;
throw image_load_error ( std : : string ( " png_loader: parse error " ) + load_error_info ) ;
}
png_set_palette_to_rgb ( ld_ - > png_ptr_ ) ;
if ( file_info - > fp_ ! = NULL )
{
png_init_io ( ld_ - > png_ptr_ , file_info - > fp_ ) ;
}
else
{
buffer_reader_state_ - > buffer_ = file_info - > buffer_ ;
buffer_reader_state_ - > buffer_size_ = file_info - > buffer_size_ ;
// skipping header
buffer_reader_state_ - > current_pos_ = png_header_size ;
png_set_read_fn ( ld_ - > png_ptr_ , buffer_reader_state_ . get ( ) , png_buffer_reader ) ;
}
png_set_sig_bytes ( ld_ - > png_ptr_ , png_header_size ) ;
// flags force one byte per channel output
byte_orderer bo ;
int png_transforms = PNG_TRANSFORM_PACKING ;
if ( bo . host_is_little_endian ( ) )
png_transforms | = PNG_TRANSFORM_SWAP_ENDIAN ;
png_read_png ( ld_ - > png_ptr_ , ld_ - > info_ptr_ , png_transforms , NULL ) ;
height_ = png_get_image_height ( ld_ - > png_ptr_ , ld_ - > info_ptr_ ) ;
width_ = png_get_image_width ( ld_ - > png_ptr_ , ld_ - > info_ptr_ ) ;
bit_depth_ = png_get_bit_depth ( ld_ - > png_ptr_ , ld_ - > info_ptr_ ) ;
color_type_ = png_get_color_type ( ld_ - > png_ptr_ , ld_ - > info_ptr_ ) ;
if ( color_type_ ! = PNG_COLOR_TYPE_GRAY & &
color_type_ ! = PNG_COLOR_TYPE_RGB & &
color_type_ ! = PNG_COLOR_TYPE_RGB_ALPHA & &
color_type_ ! = PNG_COLOR_TYPE_GRAY_ALPHA )
{
png_destroy_read_struct ( & ( ld_ - > png_ptr_ ) , & ( ld_ - > info_ptr_ ) , & ( ld_ - > end_info_ ) ) ;
throw image_load_error ( std : : string ( " png_loader: unsupported color type " ) + load_error_info ) ;
}
if ( bit_depth_ ! = 8 & & bit_depth_ ! = 16 )
{
png_destroy_read_struct ( & ( ld_ - > png_ptr_ ) , & ( ld_ - > info_ptr_ ) , & ( ld_ - > end_info_ ) ) ;
throw image_load_error ( " png_loader: unsupported bit depth of " + cast_to_string ( bit_depth_ ) + load_error_info ) ;
}
ld_ - > row_pointers_ = png_get_rows ( ld_ - > png_ptr_ , ld_ - > info_ptr_ ) ;
if ( ld_ - > row_pointers_ = = NULL )
{
png_destroy_read_struct ( & ( ld_ - > png_ptr_ ) , & ( ld_ - > info_ptr_ ) , & ( ld_ - > end_info_ ) ) ;
throw image_load_error ( std : : string ( " png_loader: parse error " ) + load_error_info ) ;
}
}
bool png_loader : : is_gray ( ) const { return color_type = = PNG_COLOR_TYPE_GRAY ; }
bool png_loader : : is_graya ( ) const { return color_type = = PNG_COLOR_TYPE_GRAY_ALPHA ; }
bool png_loader : : is_rgb ( ) const { return color_type = = PNG_COLOR_TYPE_RGB ; }
bool png_loader : : is_rgba ( ) const { return color_type = = PNG_COLOR_TYPE_RGB_ALPHA ; }
unsigned int png_loader : : bit_depth ( ) const { return bit_depth_ ; }
// ----------------------------------------------------------------------------------------
@ -310,4 +164,3 @@ namespace dlib
# endif // DLIB_PNG_SUPPORT
# endif // DLIB_PNG_LOADER_CPp_