Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:36:57

0001 //
0002 // Copyright 2012 Christian Henning
0003 //
0004 // Distributed under the Boost Software License, Version 1.0
0005 // See accompanying file LICENSE_1_0.txt or copy at
0006 // http://www.boost.org/LICENSE_1_0.txt
0007 //
0008 #ifndef BOOST_GIL_EXTENSION_IO_JPEG_DETAIL_READER_BACKEND_HPP
0009 #define BOOST_GIL_EXTENSION_IO_JPEG_DETAIL_READER_BACKEND_HPP
0010 
0011 #include <boost/gil/extension/io/jpeg/tags.hpp>
0012 #include <boost/gil/extension/io/jpeg/detail/base.hpp>
0013 
0014 #include <csetjmp>
0015 #include <memory>
0016 
0017 namespace boost { namespace gil {
0018 
0019 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
0020 #pragma warning(push)
0021 #pragma warning(disable:4512) //assignment operator could not be generated
0022 #pragma warning(disable:4611) //interaction between '_setjmp' and C++ object destruction is non-portable
0023 #endif
0024 
0025 namespace detail {
0026 
0027 ///
0028 /// Wrapper for libjpeg's decompress object. Implements value semantics.
0029 ///
0030 struct jpeg_decompress_wrapper
0031 {
0032 protected:
0033 
0034     using jpeg_decompress_ptr_t = std::shared_ptr<jpeg_decompress_struct> ;
0035 
0036 protected:
0037 
0038     ///
0039     /// Default Constructor
0040     ///
0041     jpeg_decompress_wrapper()
0042     : _jpeg_decompress_ptr( new jpeg_decompress_struct()
0043                           , jpeg_decompress_deleter
0044                           )
0045     {}
0046 
0047     jpeg_decompress_struct*       get()       { return _jpeg_decompress_ptr.get(); }
0048     const jpeg_decompress_struct* get() const { return _jpeg_decompress_ptr.get(); }
0049 
0050 private:
0051 
0052     static void jpeg_decompress_deleter( jpeg_decompress_struct* jpeg_decompress_ptr )
0053     {
0054         if( jpeg_decompress_ptr )
0055         {
0056             jpeg_destroy_decompress( jpeg_decompress_ptr );
0057 
0058             delete jpeg_decompress_ptr;
0059             jpeg_decompress_ptr = nullptr;
0060         }
0061     }
0062 
0063 private:
0064 
0065    jpeg_decompress_ptr_t _jpeg_decompress_ptr;
0066 
0067 };
0068 
0069 } // namespace detail
0070 
0071 ///
0072 /// JPEG Backend
0073 ///
0074 template< typename Device >
0075 struct reader_backend< Device
0076                      , jpeg_tag
0077                      >
0078     : public jpeg_io_base
0079     , public detail::jpeg_decompress_wrapper
0080 {
0081 public:
0082 
0083     using format_tag_t = jpeg_tag;
0084 
0085 public:
0086     //
0087     // Constructor
0088     //
0089     reader_backend( const Device&                          io_dev
0090                   , const image_read_settings< jpeg_tag >& settings
0091                   )
0092     : _io_dev( io_dev )
0093     , _settings( settings )
0094     , _info()
0095 
0096     , _scanline_length( 0 )
0097     {
0098         get()->err         = jpeg_std_error( &_jerr );
0099         get()->client_data = this;
0100 
0101         // Error exit handler: does not return to caller.
0102         _jerr.error_exit = &reader_backend::error_exit;
0103 
0104         if( setjmp( _mark ))
0105         {
0106             raise_error();
0107         }
0108 
0109         _src._jsrc.bytes_in_buffer   = 0;
0110         _src._jsrc.next_input_byte   = buffer_;
0111         _src._jsrc.init_source       = reinterpret_cast< void(*)   ( j_decompress_ptr )>( &reader_backend< Device, jpeg_tag >::init_device );
0112         _src._jsrc.fill_input_buffer = reinterpret_cast< boolean(*)( j_decompress_ptr )>( &reader_backend< Device, jpeg_tag >::fill_buffer );
0113         _src._jsrc.skip_input_data   = reinterpret_cast< void(*)   ( j_decompress_ptr
0114                                                                    , long num_bytes
0115                                                                    ) >( &reader_backend< Device, jpeg_tag >::skip_input_data );
0116         _src._jsrc.term_source       = reinterpret_cast< void(*)   ( j_decompress_ptr ) >( &reader_backend< Device, jpeg_tag >::close_device );
0117         _src._jsrc.resync_to_restart = jpeg_resync_to_restart;
0118         _src._this = this;
0119 
0120         jpeg_create_decompress( get() );
0121 
0122         get()->src = &_src._jsrc;
0123 
0124         jpeg_read_header( get()
0125                         , TRUE
0126                         );
0127 
0128         io_error_if( get()->data_precision != 8
0129                    , "Image file is not supported."
0130                    );
0131 
0132         //
0133         read_header();
0134 
0135         //
0136         if( _settings._dim.x == 0 )
0137         {
0138             _settings._dim.x = _info._width;
0139         }
0140 
0141         if( _settings._dim.y == 0 )
0142         {
0143             _settings._dim.y = _info._height;
0144         }
0145     }
0146 
0147     /// Read image header.
0148     void read_header()
0149     {
0150         _info._width          = get()->image_width;
0151         _info._height         = get()->image_height;
0152         _info._num_components = get()->num_components;
0153         _info._color_space    = get()->jpeg_color_space;
0154         _info._data_precision = get()->data_precision;
0155 
0156         _info._density_unit = get()->density_unit;
0157         _info._x_density    = get()->X_density;
0158         _info._y_density    = get()->Y_density;
0159 
0160         // obtain real world dimensions
0161         // taken from https://bitbucket.org/edd/jpegxx/src/ea2492a1a4a6/src/read.cpp#cl-62
0162 
0163         jpeg_calc_output_dimensions( get() );
0164 
0165         double units_conversion = 0;
0166         if (get()->density_unit == 1) // dots per inch
0167         {
0168             units_conversion = 25.4; // millimeters in an inch
0169         }
0170         else if (get()->density_unit == 2) // dots per cm
0171         {
0172             units_conversion = 10; // millimeters in a centimeter
0173         }
0174 
0175         _info._pixel_width_mm  = get()->X_density ? (get()->output_width  / double(get()->X_density)) * units_conversion : 0;
0176         _info._pixel_height_mm = get()->Y_density ? (get()->output_height / double(get()->Y_density)) * units_conversion : 0;
0177     }
0178 
0179     /// Return image read settings.
0180     const image_read_settings< jpeg_tag >& get_settings()
0181     {
0182         return _settings;
0183     }
0184 
0185     /// Return image header info.
0186     const image_read_info< jpeg_tag >& get_info()
0187     {
0188         return _info;
0189     }
0190 
0191     /// Check if image is large enough.
0192     void check_image_size( point_t const& img_dim )
0193     {
0194         if( _settings._dim.x > 0 )
0195         {
0196             if( img_dim.x < _settings._dim.x ) { io_error( "Supplied image is too small" ); }
0197         }
0198         else
0199         {
0200             if( (jpeg_image_width::type) img_dim.x < _info._width ) { io_error( "Supplied image is too small" ); }
0201         }
0202 
0203 
0204         if( _settings._dim.y > 0 )
0205         {
0206             if( img_dim.y < _settings._dim.y ) { io_error( "Supplied image is too small" ); }
0207         }
0208         else
0209         {
0210             if( (jpeg_image_height::type) img_dim.y < _info._height ) { io_error( "Supplied image is too small" ); }
0211         }
0212     }
0213 
0214 protected:
0215 
0216     // Taken from jerror.c
0217     /*
0218      * Error exit handler: must not return to caller.
0219      *
0220      * Applications may override this if they want to get control back after
0221      * an error.  Typically one would longjmp somewhere instead of exiting.
0222      * The setjmp buffer can be made a private field within an expanded error
0223      * handler object.  Note that the info needed to generate an error message
0224      * is stored in the error object, so you can generate the message now or
0225      * later, at your convenience.
0226      * You should make sure that the JPEG object is cleaned up (with jpeg_abort
0227      * or jpeg_destroy) at some point.
0228      */
0229     static void error_exit( j_common_ptr cinfo )
0230     {
0231         reader_backend< Device, jpeg_tag >* mgr = reinterpret_cast< reader_backend< Device, jpeg_tag >* >( cinfo->client_data );
0232 
0233         longjmp( mgr->_mark, 1 );
0234     }
0235 
0236     void raise_error()
0237     {
0238         // we clean up in the destructor
0239 
0240         io_error( "jpeg is invalid." );
0241     }
0242 
0243 private:
0244 
0245     // See jdatasrc.c for default implementation for the following static member functions.
0246 
0247     static void init_device( jpeg_decompress_struct* cinfo )
0248     {
0249         gil_jpeg_source_mgr* src = reinterpret_cast< gil_jpeg_source_mgr* >( cinfo->src );
0250         src->_jsrc.bytes_in_buffer = 0;
0251         src->_jsrc.next_input_byte = src->_this->buffer_;
0252     }
0253 
0254     static boolean fill_buffer( jpeg_decompress_struct* cinfo )
0255     {
0256         gil_jpeg_source_mgr* src = reinterpret_cast< gil_jpeg_source_mgr* >( cinfo->src );
0257         size_t count = src->_this->_io_dev.read(src->_this->buffer_, sizeof(src->_this->buffer_) );
0258 
0259         if( count <= 0 )
0260         {
0261             // libjpeg does that: adding an EOF marker
0262             src->_this->buffer_[0] = (JOCTET) 0xFF;
0263             src->_this->buffer_[1] = (JOCTET) JPEG_EOI;
0264             count = 2;
0265         }
0266 
0267         src->_jsrc.next_input_byte = src->_this->buffer_;
0268         src->_jsrc.bytes_in_buffer = count;
0269 
0270         return TRUE;
0271     }
0272 
0273     static void skip_input_data( jpeg_decompress_struct * cinfo, long num_bytes  )
0274     {
0275         gil_jpeg_source_mgr* src = reinterpret_cast< gil_jpeg_source_mgr* >( cinfo->src );
0276 
0277         if( num_bytes > 0 )
0278         {
0279             while( num_bytes > long( src->_jsrc.bytes_in_buffer ))
0280             {
0281                 num_bytes -= (long) src->_jsrc.bytes_in_buffer;
0282                 fill_buffer( cinfo );
0283             }
0284 
0285             src->_jsrc.next_input_byte += num_bytes;
0286             src->_jsrc.bytes_in_buffer -= num_bytes;
0287         }
0288     }
0289 
0290     static void close_device( jpeg_decompress_struct* ) {}
0291 
0292 public:
0293 
0294     Device _io_dev;
0295 
0296     image_read_settings< jpeg_tag > _settings;
0297     image_read_info< jpeg_tag >     _info;
0298 
0299     std::size_t _scanline_length;
0300 
0301     struct gil_jpeg_source_mgr
0302     {
0303         jpeg_source_mgr _jsrc;
0304         reader_backend* _this;
0305     };
0306 
0307     gil_jpeg_source_mgr _src;
0308 
0309     // libjpeg default is 4096 - see jdatasrc.c
0310     JOCTET buffer_[4096];
0311 };
0312 
0313 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
0314 #pragma warning(pop)
0315 #endif
0316 
0317 } // namespace gil
0318 } // namespace boost
0319 
0320 #endif