diff options
Diffstat (limited to 'boost/gil/extension/io/jpeg/detail/reader_backend.hpp')
-rw-r--r-- | boost/gil/extension/io/jpeg/detail/reader_backend.hpp | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/boost/gil/extension/io/jpeg/detail/reader_backend.hpp b/boost/gil/extension/io/jpeg/detail/reader_backend.hpp new file mode 100644 index 0000000000..71c446af4f --- /dev/null +++ b/boost/gil/extension/io/jpeg/detail/reader_backend.hpp @@ -0,0 +1,329 @@ +/* + Copyright 2012 Christian Henning + Use, modification and distribution are subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +*/ + +/*************************************************************************************************/ + +#ifndef BOOST_GIL_EXTENSION_IO_JPEG_DETAIL_READER_BACKEND_HPP +#define BOOST_GIL_EXTENSION_IO_JPEG_DETAIL_READER_BACKEND_HPP + +//////////////////////////////////////////////////////////////////////////////////////// +/// \file +/// \brief +/// \author Christian Henning \n +/// +/// \date 2012 \n +/// +//////////////////////////////////////////////////////////////////////////////////////// + +#include <boost/gil/extension/io/jpeg/tags.hpp> + +#include <memory> + +namespace boost { namespace gil { + +#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) +#pragma warning(push) +#pragma warning(disable:4512) //assignment operator could not be generated +#pragma warning(disable:4611) //interaction between '_setjmp' and C++ object destruction is non-portable +#endif + +namespace detail { + +/// +/// Wrapper for libjpeg's decompress object. Implements value semantics. +/// +struct jpeg_decompress_wrapper +{ +protected: + + using jpeg_decompress_ptr_t = std::shared_ptr<jpeg_decompress_struct> ; + +protected: + + /// + /// Default Constructor + /// + jpeg_decompress_wrapper() + : _jpeg_decompress_ptr( new jpeg_decompress_struct() + , jpeg_decompress_deleter + ) + {} + + jpeg_decompress_struct* get() { return _jpeg_decompress_ptr.get(); } + const jpeg_decompress_struct* get() const { return _jpeg_decompress_ptr.get(); } + +private: + + static void jpeg_decompress_deleter( jpeg_decompress_struct* jpeg_decompress_ptr ) + { + if( jpeg_decompress_ptr ) + { + jpeg_destroy_decompress( jpeg_decompress_ptr ); + + delete jpeg_decompress_ptr; + jpeg_decompress_ptr = NULL; + } + } + +private: + + jpeg_decompress_ptr_t _jpeg_decompress_ptr; + +}; + +} // namespace detail + +/// +/// JPEG Backend +/// +template< typename Device > +struct reader_backend< Device + , jpeg_tag + > + : public jpeg_io_base + , public detail::jpeg_decompress_wrapper +{ +public: + + typedef jpeg_tag format_tag_t; + +public: + // + // Constructor + // + reader_backend( const Device& io_dev + , const image_read_settings< jpeg_tag >& settings + ) + : _io_dev( io_dev ) + , _settings( settings ) + , _info() + + , _scanline_length( 0 ) + { + get()->err = jpeg_std_error( &_jerr ); + get()->client_data = this; + + // Error exit handler: does not return to caller. + _jerr.error_exit = &reader_backend::error_exit; + + if( setjmp( _mark )) + { + raise_error(); + } + + _src._jsrc.bytes_in_buffer = 0; + _src._jsrc.next_input_byte = buffer; + _src._jsrc.init_source = reinterpret_cast< void(*) ( j_decompress_ptr )>( &reader_backend< Device, jpeg_tag >::init_device ); + _src._jsrc.fill_input_buffer = reinterpret_cast< boolean(*)( j_decompress_ptr )>( &reader_backend< Device, jpeg_tag >::fill_buffer ); + _src._jsrc.skip_input_data = reinterpret_cast< void(*) ( j_decompress_ptr + , long num_bytes + ) >( &reader_backend< Device, jpeg_tag >::skip_input_data ); + _src._jsrc.term_source = reinterpret_cast< void(*) ( j_decompress_ptr ) >( &reader_backend< Device, jpeg_tag >::close_device ); + _src._jsrc.resync_to_restart = jpeg_resync_to_restart; + _src._this = this; + + jpeg_create_decompress( get() ); + + get()->src = &_src._jsrc; + + jpeg_read_header( get() + , TRUE + ); + + io_error_if( get()->data_precision != 8 + , "Image file is not supported." + ); + + // + read_header(); + + // + if( _settings._dim.x == 0 ) + { + _settings._dim.x = _info._width; + } + + if( _settings._dim.y == 0 ) + { + _settings._dim.y = _info._height; + } + } + + /// Read image header. + void read_header() + { + _info._width = get()->image_width; + _info._height = get()->image_height; + _info._num_components = get()->num_components; + _info._color_space = get()->jpeg_color_space; + _info._data_precision = get()->data_precision; + + _info._density_unit = get()->density_unit; + _info._x_density = get()->X_density; + _info._y_density = get()->Y_density; + + // obtain real world dimensions + // taken from https://bitbucket.org/edd/jpegxx/src/ea2492a1a4a6/src/read.cpp#cl-62 + + jpeg_calc_output_dimensions( get() ); + + double units_conversion = 0; + if (get()->density_unit == 1) // dots per inch + { + units_conversion = 25.4; // millimeters in an inch + } + else if (get()->density_unit == 2) // dots per cm + { + units_conversion = 10; // millimeters in a centimeter + } + + _info._pixel_width_mm = get()->X_density ? (get()->output_width / double(get()->X_density)) * units_conversion : 0; + _info._pixel_height_mm = get()->Y_density ? (get()->output_height / double(get()->Y_density)) * units_conversion : 0; + } + + /// Return image read settings. + const image_read_settings< jpeg_tag >& get_settings() + { + return _settings; + } + + /// Return image header info. + const image_read_info< jpeg_tag >& get_info() + { + return _info; + } + + /// Check if image is large enough. + void check_image_size( const point_t& img_dim ) + { + if( _settings._dim.x > 0 ) + { + if( img_dim.x < _settings._dim.x ) { io_error( "Supplied image is too small" ); } + } + else + { + if( (jpeg_image_width::type) img_dim.x < _info._width ) { io_error( "Supplied image is too small" ); } + } + + + if( _settings._dim.y > 0 ) + { + if( img_dim.y < _settings._dim.y ) { io_error( "Supplied image is too small" ); } + } + else + { + if( (jpeg_image_height::type) img_dim.y < _info._height ) { io_error( "Supplied image is too small" ); } + } + } + +protected: + + // Taken from jerror.c + /* + * Error exit handler: must not return to caller. + * + * Applications may override this if they want to get control back after + * an error. Typically one would longjmp somewhere instead of exiting. + * The setjmp buffer can be made a private field within an expanded error + * handler object. Note that the info needed to generate an error message + * is stored in the error object, so you can generate the message now or + * later, at your convenience. + * You should make sure that the JPEG object is cleaned up (with jpeg_abort + * or jpeg_destroy) at some point. + */ + static void error_exit( j_common_ptr cinfo ) + { + reader_backend< Device, jpeg_tag >* mgr = reinterpret_cast< reader_backend< Device, jpeg_tag >* >( cinfo->client_data ); + + longjmp( mgr->_mark, 1 ); + } + + void raise_error() + { + // we clean up in the destructor + + io_error( "jpeg is invalid." ); + } + +private: + + // See jdatasrc.c for default implementation for the following static member functions. + + static void init_device( jpeg_decompress_struct* cinfo ) + { + gil_jpeg_source_mgr* src = reinterpret_cast< gil_jpeg_source_mgr* >( cinfo->src ); + src->_jsrc.bytes_in_buffer = 0; + src->_jsrc.next_input_byte = src->_this->buffer; + } + + static boolean fill_buffer( jpeg_decompress_struct* cinfo ) + { + gil_jpeg_source_mgr* src = reinterpret_cast< gil_jpeg_source_mgr* >( cinfo->src ); + size_t count = src->_this->_io_dev.read(src->_this->buffer, sizeof(src->_this->buffer) ); + + if( count <= 0 ) + { + // libjpeg does that: adding an EOF marker + src->_this->buffer[0] = (JOCTET) 0xFF; + src->_this->buffer[1] = (JOCTET) JPEG_EOI; + count = 2; + } + + src->_jsrc.next_input_byte = src->_this->buffer; + src->_jsrc.bytes_in_buffer = count; + + return TRUE; + } + + static void skip_input_data( jpeg_decompress_struct * cinfo, long num_bytes ) + { + gil_jpeg_source_mgr* src = reinterpret_cast< gil_jpeg_source_mgr* >( cinfo->src ); + + if( num_bytes > 0 ) + { + while( num_bytes > long( src->_jsrc.bytes_in_buffer )) + { + num_bytes -= (long) src->_jsrc.bytes_in_buffer; + fill_buffer( cinfo ); + } + + src->_jsrc.next_input_byte += num_bytes; + src->_jsrc.bytes_in_buffer -= num_bytes; + } + } + + static void close_device( jpeg_decompress_struct* ) {} + +public: + + Device _io_dev; + + image_read_settings< jpeg_tag > _settings; + image_read_info< jpeg_tag > _info; + + std::size_t _scanline_length; + + struct gil_jpeg_source_mgr + { + jpeg_source_mgr _jsrc; + reader_backend* _this; + }; + + gil_jpeg_source_mgr _src; + + // libjpeg default is 4096 - see jdatasrc.c + JOCTET buffer[4096]; +}; + +#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) +#pragma warning(pop) +#endif + +} // namespace gil +} // namespace boost + +#endif |