31 typedef int static_assert_png_byte_is_char[
sizeof(png_byte)==1?1:-1] ;
34 #ifndef PNG_TRANSFORM_GRAY_TO_RGB
35 #define PNG_TRANSFORM_GRAY_TO_RGB 0
38 #if GCONFIG_HAVE_FMEMOPEN
41 FILE * fmemopen_(
void * p ,
size_t n ,
const char * mode )
43 return fmemopen( p , n , mode ) ;
49 FILE * fmemopen_(
void * p ,
size_t n ,
const char * )
51 FILE * fp = std::tmpfile() ;
54 size_t rc = std::fwrite( p , n , 1U , fp ) ;
60 std::fseek( fp , 0 , SEEK_SET ) ;
83 ~
FileCloser() {
if( m_fp ) std::fclose( m_fp ) ; }
87 explicit PngImp(
bool reader ) ;
95 png_struct * m_png_struct ;
96 png_info * m_png_info ;
100 void operator=(
const PngImp & ) ;
103 static void error_fn( png_struct * ,
const char * ) ;
104 static void warning_fn( png_struct * ,
const char * ) ;
113 typedef PngReader::Map Map ;
116 void decode(
ImageData & ,
const G::Path & path ,
int scale ,
bool monochrome_out ) ;
117 void decode(
ImageData & ,
const char * data_p ,
size_t data_size ,
int scale ,
bool monochrome_out ) ;
124 void decodeImp(
ImageData & , FILE * ,
const ImageBuffer * ,
int scale ,
bool monochrome_out ) ;
125 void init_io( png_struct * , FILE * ) ;
126 static void read( png_struct * ,
unsigned char * p ,
size_t ) ;
127 void reset( FILE * fp ) ;
133 unique_ptr<imagebuf> m_read_state ;
142 typedef std::multimap<std::string,std::string> Map ;
146 png_text * p()
const ;
153 std::vector<std::string> m_strings ;
154 std::vector<png_text> m_text ;
163 typedef PngWriter::Map Map ;
166 void write(
const G::Path & path ) ;
167 void write( Output & out ) ;
170 static void flush_imp( png_struct * ) ;
171 static void write_imp( png_struct * p , png_byte * data , png_size_t n ) ;
180 Gr::PngImp::PngImp(
bool reader ) :
182 m_png_struct(nullptr) ,
188 void Gr::PngImp::init_(
bool reader )
190 m_png_struct = reader ?
191 png_create_read_struct( PNG_LIBPNG_VER_STRING , reinterpret_cast<png_voidp>(
this) , error_fn , warning_fn ) :
192 png_create_write_struct( PNG_LIBPNG_VER_STRING , reinterpret_cast<png_voidp>(this) , error_fn , warning_fn ) ;
194 if( m_png_struct ==
nullptr )
195 throw Png::Error(
"png_create_struct failed" ) ;
197 m_png_info = png_create_info_struct( m_png_struct ) ;
198 if( m_png_info ==
nullptr )
201 throw Png::Error(
"png_create_info_struct failed" ) ;
205 void Gr::PngImp::pngReset()
211 Gr::PngImp::~PngImp()
216 void Gr::PngImp::cleanup_()
219 png_destroy_read_struct( &m_png_struct , &m_png_info ,
nullptr ) ;
221 png_destroy_write_struct( &m_png_struct , &m_png_info ) ;
223 m_png_struct = nullptr ;
224 m_png_info = nullptr ;
227 void Gr::PngImp::error_fn( png_struct * p ,
const char * what )
229 G_DEBUG(
"Gr::PngImp::error: error callback from libpng: " << what ) ;
230 #if PNG_LIBPNG_VER_MAJOR > 1 || PNG_LIBPNG_VER_MINOR >= 5
231 png_longjmp( p , 99 ) ;
233 longjmp( p->jmpbuf , 99 ) ;
237 void Gr::PngImp::warning_fn( png_struct * ,
const char * what )
239 G_DEBUG(
"Gr::PngImp::warning: warning callback from libpng: " << what ) ;
244 void Gr::PngInfo::init( std::istream & stream )
246 unsigned char buffer[31] = { 0 } ;
247 stream.read( reinterpret_cast<char*>(buffer) ,
sizeof(buffer) ) ;
248 std::pair<int,int> pair = parse( buffer , stream.gcount() ) ;
253 void Gr::PngInfo::init(
const unsigned char * p_in ,
size_t n )
255 std::pair<int,int> pair = parse( p_in , n ) ;
264 m_monochrome_out(monochrome_out)
271 m_monochrome_out = monochrome_out ;
277 imp.decode( out , path , m_scale , m_monochrome_out ) ;
278 m_tags = imp.tags() ;
284 imp.decode( out , p , n , m_scale , m_monochrome_out ) ;
285 m_tags = imp.tags() ;
290 decode( out , reinterpret_cast<const char*>(p) , n ) ;
296 imp.decode( out , b , m_scale , m_monochrome_out ) ;
297 m_tags = imp.tags() ;
311 Gr::PngReaderImp::PngReaderImp() :
316 Gr::PngReaderImp::~PngReaderImp()
320 void Gr::PngReaderImp::decode( ImageData & out ,
const char * p ,
size_t n ,
int scale ,
bool monochrome_out )
322 FILE * fp = fmemopen_( const_cast<char*>(p) , n ,
"rb" ) ;
324 throw Png::Error(
"fmemopen failed" ) ;
325 PngImp::FileCloser closer( fp ) ;
326 decodeImp( out , fp ,
nullptr , scale , monochrome_out ) ;
329 void Gr::PngReaderImp::decode( ImageData & out ,
const G::Path & path ,
int scale ,
bool monochrome_out )
331 FILE * fp = nullptr ;
334 fp = std::fopen( path.
str().c_str() ,
"rb" ) ;
337 throw Png::Error(
"cannot open png file" , path.
str() ) ;
338 PngImp::FileCloser closer( fp ) ;
339 decodeImp( out , fp ,
nullptr , scale , monochrome_out ) ;
342 void Gr::PngReaderImp::decode( ImageData & out ,
const ImageBuffer & image_buffer ,
int scale ,
bool monochrome_out )
344 decodeImp( out ,
nullptr , &image_buffer , scale , monochrome_out ) ;
347 void Gr::PngReaderImp::init_io( png_struct * png , FILE * fp )
350 png_init_io( png , fp ) ;
352 png_set_read_fn( png ,
this , &PngReaderImp::read ) ;
355 void Gr::PngReaderImp::decodeImp( ImageData & out , FILE * fp ,
const ImageBuffer * bp ,
int scale ,
bool monochrome_out )
357 if( fp ==
nullptr && bp ==
nullptr )
361 m_read_state.reset(
new imagebuf(*bp) ) ;
363 const int transforms =
364 PNG_TRANSFORM_STRIP_16 |
365 PNG_TRANSFORM_STRIP_ALPHA |
366 PNG_TRANSFORM_PACKING |
367 PNG_TRANSFORM_EXPAND |
368 PNG_TRANSFORM_GRAY_TO_RGB ;
375 if( ! setjmp( png_jmpbuf(m_png_struct) ) )
379 init_io( m_png_struct , fp ) ;
380 png_read_info( m_png_struct , m_png_info ) ;
381 dx = png_get_image_width( m_png_struct , m_png_info ) ;
382 dy = png_get_image_height( m_png_struct , m_png_info ) ;
389 if( !ok || dx <= 0 || dy <= 0 )
390 throw Png::Error(
"png_read_info failed" ) ;
391 out.resize( dx , dy , 3 ) ;
401 png_text * text_p = nullptr ;
403 png_byte ** out_pp = out.rowPointers() ;
404 if( ! setjmp( png_jmpbuf(m_png_struct) ) )
408 init_io( m_png_struct , fp ) ;
409 png_set_rows( m_png_struct , m_png_info , out_pp ) ;
410 png_read_png( m_png_struct , m_png_info , transforms ,
nullptr ) ;
411 png_get_text( m_png_struct , m_png_info , &text_p , &text_n ) ;
412 int depth = png_get_bit_depth( m_png_struct , m_png_info ) ;
420 throw Png::Error(
"png_read_png failed" ) ;
421 for(
int i = 0 ; i < text_n ; i++ )
423 if( text_p && text_p[i].compression == PNG_TEXT_COMPRESSION_NONE )
424 m_tags.insert( Map::value_type(text_p[i].key,text_p[i].text) ) ;
431 out.scale( scale , monochrome_out ,
true ) ;
434 void Gr::PngReaderImp::read( png_struct * png ,
unsigned char * p ,
size_t n )
436 PngReaderImp * imp =
static_cast<PngReaderImp*
>( png_get_io_ptr( png ) ) ;
437 if( imp ==
nullptr || imp->m_read_state.get() ==
nullptr || p == nullptr )
438 png_error( png ,
"read error" ) ;
440 size_t rc =
static_cast<size_t>( imp->m_read_state->sgetn( reinterpret_cast<char*>(p) , n ) ) ;
442 png_error( png ,
"read error" ) ;
445 Gr::PngReader::Map Gr::PngReaderImp::tags()
const
450 void Gr::PngReaderImp::reset( FILE * fp )
452 int rc = fp ==
nullptr ? 0 : std::fseek( fp , 0 , SEEK_SET ) ;
454 throw Png::Error(
"fseek error" ) ;
457 void Gr::PngReaderImp::reset(
const ImageBuffer * bp )
460 m_read_state.reset(
new imagebuf(*bp) ) ;
465 Gr::PngTagsImp::PngTagsImp()
469 Gr::PngTagsImp::PngTagsImp(
const Map & tags )
471 m_text.reserve( tags.size() ) ;
472 Map::const_iterator
const end = tags.end() ;
473 for( Map::const_iterator p = tags.begin() ; p != end ; ++p )
475 if( (*p).first.length() == 0U || (*p).first.length() > 78 )
476 throw Png::Error(
"invalid tag key" ) ;
477 static png_text zero ;
478 png_text text( zero ) ;
479 text.compression = PNG_TEXT_COMPRESSION_NONE ;
480 m_strings.push_back( (*p).first +
'\0' ) ;
481 text.key =
const_cast<char*
>(m_strings.back().data()) ;
482 m_strings.push_back( (*p).second +
'\0' ) ;
483 text.text =
const_cast<char*
>(m_strings.back().data()) ;
484 m_text.push_back( text ) ;
488 int Gr::PngTagsImp::n()
const
490 return m_text.size() ;
493 png_text * Gr::PngTagsImp::p()
const
495 typedef std::vector<png_text> Vector ;
496 Vector & text =
const_cast<Vector&
>(m_text) ;
497 return m_text.size() ? &text[0] : nullptr ;
503 m_imp(new PngWriterImp(data,tags))
514 m_imp->write( path ) ;
519 m_imp->write( out ) ;
524 Gr::PngWriterImp::PngWriterImp(
const ImageData & data , Map tags_in ) :
531 png_uint_32 dx =
static_cast<png_uint_32
>( m_data.dx() ) ;
532 png_uint_32 dy =
static_cast<png_uint_32
>( m_data.dy() ) ;
533 png_byte ** row_pointers =
const_cast<ImageData&
>(m_data).rowPointers() ;
534 png_text * tags_p = m_tags.p() ;
535 int tags_n = m_tags.n() ;
537 if( ! setjmp( png_jmpbuf(m_png_struct) ) )
541 png_set_IHDR( m_png_struct , m_png_info , dx , dy , 8 ,
542 data.channels() == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_GRAY ,
543 PNG_INTERLACE_NONE , PNG_COMPRESSION_TYPE_DEFAULT , PNG_FILTER_TYPE_DEFAULT ) ;
544 png_set_rows( m_png_struct , m_png_info , row_pointers ) ;
546 png_set_text( m_png_struct , m_png_info , tags_p , tags_n ) ;
555 throw Png::Error(
"png_set_rows failed" ) ;
558 void Gr::PngWriterImp::write(
const G::Path & path )
560 FILE * fp = nullptr ;
563 fp = std::fopen( path.
str().c_str() ,
"wb" ) ;
566 throw Png::Error(
"cannot create output" , path.
str() ) ;
567 PngImp::FileCloser closer( fp ) ;
571 if( ! setjmp( png_jmpbuf(m_png_struct) ) )
575 png_init_io( m_png_struct , fp ) ;
576 png_write_png( m_png_struct , m_png_info , PNG_TRANSFORM_PACKING ,
nullptr ) ;
585 throw Png::Error(
"png_write_png failed" ) ;
592 if( ! setjmp( png_jmpbuf(m_png_struct) ) )
596 png_set_write_fn( m_png_struct , reinterpret_cast<void*>(&out) , write_imp , flush_imp ) ;
597 png_write_png( m_png_struct , m_png_info , PNG_TRANSFORM_PACKING ,
nullptr ) ;
606 throw Png::Error(
"png_write_png failed" ) ;
609 void Gr::PngWriterImp::write_imp( png_struct * p , png_byte * data , png_size_t n )
611 Output * out =
reinterpret_cast<Output*
>( png_get_io_ptr(p) ) ;
615 void Gr::PngWriterImp::flush_imp( png_struct * )
std::string str() const
Returns the path string.
Map tags() const
Returns the text tags from the last decode().
A traits class that can be specialised for Gr::ImageBuffer candidates.
A holder for image data, having eight bits per sample and one or three channels.
A private base class that manages png_struct and png_info structures, and handles error callbacks...
A class which acquires the process's special privileges on construction and releases them on destruct...
Vectors ImageBuffer
An ImageBuffer is used to hold raw image data, typically in more than one chunk.
RAII class to do std::fclose().
PngWriter(const ImageData &, Map tags=Map())
Constructor with the raw image data prepared in a ImageData object.
A private pimple-pattern class for Gr::PngWriter.
void decode(ImageData &out, const G::Path &in)
Decodes a png file into an image. Throws on error.
A private pimple-pattern class for Gr::PngReader.
PngReader(int scale=1, bool monochrome_out=false)
Constructor.
void setup(int scale, bool monochrome_out=false)
Sets the decoding scale factor.
static bool available()
Returns true if the png library is available.
A Path object represents a file system path.
Abstract interface for Gr::PngWriter::write().
void write(const G::Path &path)
Writes to file.