1 // 2 // Copyright 2012 Christian Henning 3 // 4 // Distributed under the Boost Software License, Version 1.0 5 // See accompanying file LICENSE_1_0.txt or copy at 6 // http://www.boost.org/LICENSE_1_0.txt 7 // 8 #ifndef BOOST_GIL_EXTENSION_IO_JPEG_DETAIL_READER_BACKEND_HPP 9 #define BOOST_GIL_EXTENSION_IO_JPEG_DETAIL_READER_BACKEND_HPP 10 11 #include <boost/gil/extension/io/jpeg/tags.hpp> 12 #include <boost/gil/extension/io/jpeg/detail/base.hpp> 13 14 #include <csetjmp> 15 #include <memory> 16 17 namespace boost { namespace gil { 18 19 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) 20 #pragma warning(push) 21 #pragma warning(disable:4512) //assignment operator could not be generated 22 #pragma warning(disable:4611) //interaction between '_setjmp' and C++ object destruction is non-portable 23 #endif 24 25 namespace detail { 26 27 /// 28 /// Wrapper for libjpeg's decompress object. Implements value semantics. 29 /// 30 struct jpeg_decompress_wrapper 31 { 32 protected: 33 34 using jpeg_decompress_ptr_t = std::shared_ptr<jpeg_decompress_struct> ; 35 36 protected: 37 38 /// 39 /// Default Constructor 40 /// jpeg_decompress_wrapperboost::gil::detail::jpeg_decompress_wrapper41 jpeg_decompress_wrapper() 42 : _jpeg_decompress_ptr( new jpeg_decompress_struct() 43 , jpeg_decompress_deleter 44 ) 45 {} 46 getboost::gil::detail::jpeg_decompress_wrapper47 jpeg_decompress_struct* get() { return _jpeg_decompress_ptr.get(); } getboost::gil::detail::jpeg_decompress_wrapper48 const jpeg_decompress_struct* get() const { return _jpeg_decompress_ptr.get(); } 49 50 private: 51 jpeg_decompress_deleterboost::gil::detail::jpeg_decompress_wrapper52 static void jpeg_decompress_deleter( jpeg_decompress_struct* jpeg_decompress_ptr ) 53 { 54 if( jpeg_decompress_ptr ) 55 { 56 jpeg_destroy_decompress( jpeg_decompress_ptr ); 57 58 delete jpeg_decompress_ptr; 59 jpeg_decompress_ptr = nullptr; 60 } 61 } 62 63 private: 64 65 jpeg_decompress_ptr_t _jpeg_decompress_ptr; 66 67 }; 68 69 } // namespace detail 70 71 /// 72 /// JPEG Backend 73 /// 74 template< typename Device > 75 struct reader_backend< Device 76 , jpeg_tag 77 > 78 : public jpeg_io_base 79 , public detail::jpeg_decompress_wrapper 80 { 81 public: 82 83 using format_tag_t = jpeg_tag; 84 85 public: 86 // 87 // Constructor 88 // reader_backendboost::gil::reader_backend89 reader_backend( const Device& io_dev 90 , const image_read_settings< jpeg_tag >& settings 91 ) 92 : _io_dev( io_dev ) 93 , _settings( settings ) 94 , _info() 95 96 , _scanline_length( 0 ) 97 { 98 get()->err = jpeg_std_error( &_jerr ); 99 get()->client_data = this; 100 101 // Error exit handler: does not return to caller. 102 _jerr.error_exit = &reader_backend::error_exit; 103 104 if( setjmp( _mark )) 105 { 106 raise_error(); 107 } 108 109 _src._jsrc.bytes_in_buffer = 0; 110 _src._jsrc.next_input_byte = buffer_; 111 _src._jsrc.init_source = reinterpret_cast< void(*) ( j_decompress_ptr )>( &reader_backend< Device, jpeg_tag >::init_device ); 112 _src._jsrc.fill_input_buffer = reinterpret_cast< boolean(*)( j_decompress_ptr )>( &reader_backend< Device, jpeg_tag >::fill_buffer ); 113 _src._jsrc.skip_input_data = reinterpret_cast< void(*) ( j_decompress_ptr 114 , long num_bytes 115 ) >( &reader_backend< Device, jpeg_tag >::skip_input_data ); 116 _src._jsrc.term_source = reinterpret_cast< void(*) ( j_decompress_ptr ) >( &reader_backend< Device, jpeg_tag >::close_device ); 117 _src._jsrc.resync_to_restart = jpeg_resync_to_restart; 118 _src._this = this; 119 120 jpeg_create_decompress( get() ); 121 122 get()->src = &_src._jsrc; 123 124 jpeg_read_header( get() 125 , TRUE 126 ); 127 128 io_error_if( get()->data_precision != 8 129 , "Image file is not supported." 130 ); 131 132 // 133 read_header(); 134 135 // 136 if( _settings._dim.x == 0 ) 137 { 138 _settings._dim.x = _info._width; 139 } 140 141 if( _settings._dim.y == 0 ) 142 { 143 _settings._dim.y = _info._height; 144 } 145 } 146 147 /// Read image header. read_headerboost::gil::reader_backend148 void read_header() 149 { 150 _info._width = get()->image_width; 151 _info._height = get()->image_height; 152 _info._num_components = get()->num_components; 153 _info._color_space = get()->jpeg_color_space; 154 _info._data_precision = get()->data_precision; 155 156 _info._density_unit = get()->density_unit; 157 _info._x_density = get()->X_density; 158 _info._y_density = get()->Y_density; 159 160 // obtain real world dimensions 161 // taken from https://bitbucket.org/edd/jpegxx/src/ea2492a1a4a6/src/read.cpp#cl-62 162 163 jpeg_calc_output_dimensions( get() ); 164 165 double units_conversion = 0; 166 if (get()->density_unit == 1) // dots per inch 167 { 168 units_conversion = 25.4; // millimeters in an inch 169 } 170 else if (get()->density_unit == 2) // dots per cm 171 { 172 units_conversion = 10; // millimeters in a centimeter 173 } 174 175 _info._pixel_width_mm = get()->X_density ? (get()->output_width / double(get()->X_density)) * units_conversion : 0; 176 _info._pixel_height_mm = get()->Y_density ? (get()->output_height / double(get()->Y_density)) * units_conversion : 0; 177 } 178 179 /// Return image read settings. get_settingsboost::gil::reader_backend180 const image_read_settings< jpeg_tag >& get_settings() 181 { 182 return _settings; 183 } 184 185 /// Return image header info. get_infoboost::gil::reader_backend186 const image_read_info< jpeg_tag >& get_info() 187 { 188 return _info; 189 } 190 191 /// Check if image is large enough. check_image_sizeboost::gil::reader_backend192 void check_image_size( const point_t& img_dim ) 193 { 194 if( _settings._dim.x > 0 ) 195 { 196 if( img_dim.x < _settings._dim.x ) { io_error( "Supplied image is too small" ); } 197 } 198 else 199 { 200 if( (jpeg_image_width::type) img_dim.x < _info._width ) { io_error( "Supplied image is too small" ); } 201 } 202 203 204 if( _settings._dim.y > 0 ) 205 { 206 if( img_dim.y < _settings._dim.y ) { io_error( "Supplied image is too small" ); } 207 } 208 else 209 { 210 if( (jpeg_image_height::type) img_dim.y < _info._height ) { io_error( "Supplied image is too small" ); } 211 } 212 } 213 214 protected: 215 216 // Taken from jerror.c 217 /* 218 * Error exit handler: must not return to caller. 219 * 220 * Applications may override this if they want to get control back after 221 * an error. Typically one would longjmp somewhere instead of exiting. 222 * The setjmp buffer can be made a private field within an expanded error 223 * handler object. Note that the info needed to generate an error message 224 * is stored in the error object, so you can generate the message now or 225 * later, at your convenience. 226 * You should make sure that the JPEG object is cleaned up (with jpeg_abort 227 * or jpeg_destroy) at some point. 228 */ error_exitboost::gil::reader_backend229 static void error_exit( j_common_ptr cinfo ) 230 { 231 reader_backend< Device, jpeg_tag >* mgr = reinterpret_cast< reader_backend< Device, jpeg_tag >* >( cinfo->client_data ); 232 233 longjmp( mgr->_mark, 1 ); 234 } 235 raise_errorboost::gil::reader_backend236 void raise_error() 237 { 238 // we clean up in the destructor 239 240 io_error( "jpeg is invalid." ); 241 } 242 243 private: 244 245 // See jdatasrc.c for default implementation for the following static member functions. 246 init_deviceboost::gil::reader_backend247 static void init_device( jpeg_decompress_struct* cinfo ) 248 { 249 gil_jpeg_source_mgr* src = reinterpret_cast< gil_jpeg_source_mgr* >( cinfo->src ); 250 src->_jsrc.bytes_in_buffer = 0; 251 src->_jsrc.next_input_byte = src->_this->buffer_; 252 } 253 fill_bufferboost::gil::reader_backend254 static boolean fill_buffer( jpeg_decompress_struct* cinfo ) 255 { 256 gil_jpeg_source_mgr* src = reinterpret_cast< gil_jpeg_source_mgr* >( cinfo->src ); 257 size_t count = src->_this->_io_dev.read(src->_this->buffer_, sizeof(src->_this->buffer_) ); 258 259 if( count <= 0 ) 260 { 261 // libjpeg does that: adding an EOF marker 262 src->_this->buffer_[0] = (JOCTET) 0xFF; 263 src->_this->buffer_[1] = (JOCTET) JPEG_EOI; 264 count = 2; 265 } 266 267 src->_jsrc.next_input_byte = src->_this->buffer_; 268 src->_jsrc.bytes_in_buffer = count; 269 270 return TRUE; 271 } 272 skip_input_databoost::gil::reader_backend273 static void skip_input_data( jpeg_decompress_struct * cinfo, long num_bytes ) 274 { 275 gil_jpeg_source_mgr* src = reinterpret_cast< gil_jpeg_source_mgr* >( cinfo->src ); 276 277 if( num_bytes > 0 ) 278 { 279 while( num_bytes > long( src->_jsrc.bytes_in_buffer )) 280 { 281 num_bytes -= (long) src->_jsrc.bytes_in_buffer; 282 fill_buffer( cinfo ); 283 } 284 285 src->_jsrc.next_input_byte += num_bytes; 286 src->_jsrc.bytes_in_buffer -= num_bytes; 287 } 288 } 289 close_deviceboost::gil::reader_backend290 static void close_device( jpeg_decompress_struct* ) {} 291 292 public: 293 294 Device _io_dev; 295 296 image_read_settings< jpeg_tag > _settings; 297 image_read_info< jpeg_tag > _info; 298 299 std::size_t _scanline_length; 300 301 struct gil_jpeg_source_mgr 302 { 303 jpeg_source_mgr _jsrc; 304 reader_backend* _this; 305 }; 306 307 gil_jpeg_source_mgr _src; 308 309 // libjpeg default is 4096 - see jdatasrc.c 310 JOCTET buffer_[4096]; 311 }; 312 313 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) 314 #pragma warning(pop) 315 #endif 316 317 } // namespace gil 318 } // namespace boost 319 320 #endif 321