// Copyright (C) 2008 Davis E. King (davis@dlib.net), Nils Labugt // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_PNG_LOADER_CPp_ #define DLIB_PNG_LOADER_CPp_ // only do anything with this file if DLIB_PNG_SUPPORT is defined #ifdef DLIB_PNG_SUPPORT #include "../array2d.h" #include "../pixel.h" #include "../dir_nav.h" #include "png_loader.h" #include <png.h> #include "../string.h" #include "../byte_orderer.h" #include <sstream> #include <cstring> namespace dlib { // ---------------------------------------------------------------------------------------- // 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_reader_callback(png_structp png, png_bytep data, png_size_t length) { 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"); } // ---------------------------------------------------------------------------------------- void png_loader::load(std::function<std::size_t(char*,std::size_t)> clb) { // 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_infop info_ptr = png_create_info_struct( png_ptr ); if ( info_ptr == NULL ) { 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_infop end_info = png_create_info_struct( png_ptr ); if ( end_info == NULL ) { png_destroy_read_struct(&png_ptr, &info_ptr, ( png_infopp )NULL ); throw image_load_error("Error while reading PNG file : png_create_info_struct()"); } if (setjmp(png_jmpbuf(png_ptr))) { // 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); // 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 ); 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_)); if (rows == NULL) throw image_load_error("png_loader: parse error"); } // ---------------------------------------------------------------------------------------- void png_loader::load(std::istream& in) { load([&](char* data, std::size_t ndata) { in.read(data, ndata); return in.gcount(); }); } // ---------------------------------------------------------------------------------------- png_loader::png_loader(const unsigned char* image_buffer, std::size_t buffer_size) { 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; }); } // ---------------------------------------------------------------------------------------- png_loader::png_loader(std::istream& in) { load(in); } png_loader::png_loader( const char* filename ) { std::ifstream in(filename, std::ios::binary); load(in); } 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()) {} // ---------------------------------------------------------------------------------------- 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_;} long png_loader::nr() const { return height; } long png_loader::nc() const { return width; } // ---------------------------------------------------------------------------------------- } #endif // DLIB_PNG_SUPPORT #endif // DLIB_PNG_LOADER_CPp_