File indexing completed on 2025-01-18 09:36:57
0001
0002
0003
0004
0005
0006
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)
0022 #pragma warning(disable:4611)
0023 #endif
0024
0025 namespace detail {
0026
0027
0028
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
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 }
0070
0071
0072
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
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
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
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
0161
0162
0163 jpeg_calc_output_dimensions( get() );
0164
0165 double units_conversion = 0;
0166 if (get()->density_unit == 1)
0167 {
0168 units_conversion = 25.4;
0169 }
0170 else if (get()->density_unit == 2)
0171 {
0172 units_conversion = 10;
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
0180 const image_read_settings< jpeg_tag >& get_settings()
0181 {
0182 return _settings;
0183 }
0184
0185
0186 const image_read_info< jpeg_tag >& get_info()
0187 {
0188 return _info;
0189 }
0190
0191
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
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226
0227
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
0239
0240 io_error( "jpeg is invalid." );
0241 }
0242
0243 private:
0244
0245
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
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
0310 JOCTET buffer_[4096];
0311 };
0312
0313 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
0314 #pragma warning(pop)
0315 #endif
0316
0317 }
0318 }
0319
0320 #endif