// pngstream.cc -- image streams producing PNG files // Copyright (C) 2008, 2009, 2016 SEIKO EPSON CORPORATION // // This file is part of the 'iscan' program. // // The 'iscan' program is free-ish software. // You can redistribute it and/or modify it under the terms of the GNU // General Public License as published by the Free Software Foundation; // either version 2 of the License or at your option any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS // FOR A PARTICULAR PURPOSE or MERCHANTABILITY. // See the GNU General Public License for more details. // // You should have received a verbatim copy of the GNU General Public // License along with this program; if not, write to: // // Free Software Foundation, Inc. // 59 Temple Place, Suite 330 // Boston, MA 02111-1307 USA // // As a special exception, the copyright holders give permission // to link the code of this program with the esmod library and // distribute linked combinations including the two. You must obey // the GNU General Public License in all respects for all of the // code used other then esmod. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pngstream.hh" #include namespace iscan { pngstream::pngstream (FILE *fp, const string& name) : _stream (fp), _header (false), _footer (false) { if (!_stream) throw std::invalid_argument ("invalid file handle"); #if HAVE_PNG_H init (); #endif } pngstream::~pngstream (void) { try { flush (); } catch (const std::exception& oops) { std::cerr << oops.what (); } #if HAVE_PNG_H lib->destroy_write_struct (&_png, &_info); #endif } basic_imgstream& pngstream::write (const byte_type *line, size_type n) { if (!line || 0 == n) return *this; #if HAVE_PNG_H if (!_header) { write_header (); } set_error_handler (_png, _info); lib->write_row (_png, (png_byte *) line); #endif return *this; } basic_imgstream& pngstream::flush (void) { #if HAVE_PNG_H set_error_handler (_png, _info); lib->write_flush (_png); if (_header && !_footer) { lib->write_end (_png, _info); _footer = true; } #endif fflush (_stream); return *this; } bool pngstream::is_usable (void) { if (lib) { return lib->is_usable; } lib = new (std::nothrow) png_lib_handle (); if (!lib) { return false; } lib->is_usable = false; lib->message = string (); lib->lib = NULL; #if HAVE_PNG_H try { // Some PNG libraries are *not* linked with libz which leads // to a "file not found" error when calling dlopen. We just // dlopen the libz library explicitly to work around this. basic_imgstream::dlopen ("libz"); basic_imgstream::dlopen ("libpng16", validate); } catch (std::runtime_error& e) { lib->message = e.what (); return lib->is_usable; } #endif /* HAVE_PNG_H */ return lib->is_usable; } #if HAVE_PNG_H #define funcsym(name) \ lib->name \ = ((pngstream::png_lib_handle::name##_f) \ basic_imgstream::dlsym (lib->lib, "png_"#name)); #endif bool pngstream::validate (lt_dlhandle h) { if (!h) return false; #if HAVE_PNG_H lib->lib = h; funcsym (access_version_number); funcsym (create_write_struct); funcsym (create_info_struct); funcsym (destroy_write_struct); funcsym (init_io); funcsym (set_IHDR); funcsym (set_pHYs); funcsym (set_invert_mono); funcsym (write_info); funcsym (write_row); funcsym (write_flush); funcsym (write_end); if (lib->access_version_number && lib->create_write_struct && lib->create_info_struct && lib->destroy_write_struct && lib->init_io && lib->set_IHDR && lib->set_pHYs && lib->set_invert_mono && lib->write_info && lib->write_row && lib->write_flush && lib->write_end) { lib->is_usable = (PNG_LIBPNG_VER <= lib->access_version_number ()); } #endif /* HAVE_PNG_H */ return lib->is_usable; } #if HAVE_PNG_H #undef funcsym #endif basic_imgstream& pngstream::write_header (void) { check_consistency (); #if HAVE_PNG_H set_error_handler (_png, _info); if (mono == _cspc) { _bits = 1; lib->set_invert_mono (_png); } lib->init_io (_png, _stream); lib->set_IHDR (_png, _info, _h_sz, _v_sz, _bits, (RGB == _cspc ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_GRAY), PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); size_type hres = size_type (100 / 2.54 * _hres + 0.5); size_type vres = size_type (100 / 2.54 * _vres + 0.5); lib->set_pHYs (_png, _info, hres, vres, PNG_RESOLUTION_METER); lib->write_info (_png, _info); #endif /* HAVE_PNG_H */ _header = true; return *this; } void pngstream::check_consistency (void) const { if (!(mono == _cspc || grey == _cspc || RGB == _cspc)) { throw std::logic_error ("unsupported colour space"); } } void pngstream::init (void) { if (!is_usable ()) { throw std::runtime_error (lib->message); } #if HAVE_PNG_H _png = lib->create_write_struct (version_string, NULL, NULL, NULL); _info = NULL; if (!_png) throw std::bad_alloc (); _info = lib->create_info_struct (_png); if (!_info) { lib->destroy_write_struct (&_png, NULL); throw std::bad_alloc (); } #endif /* HAVE_PNG_H */ } pngstream::png_lib_handle *pngstream::lib = NULL; #if HAVE_PNG_H png_const_charp pngstream::version_string = PNG_LIBPNG_VER_STRING; #endif } // namespace iscan