summaryrefslogtreecommitdiff
path: root/boost/gil/extension/io/jpeg/detail/reader_backend.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/gil/extension/io/jpeg/detail/reader_backend.hpp')
-rw-r--r--boost/gil/extension/io/jpeg/detail/reader_backend.hpp329
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