2 // Copyright 2007-2012 Christian Henning, Lubomir Bourdev
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
8 #ifndef BOOST_GIL_EXTENSION_IO_TIFF_DETAIL_READER_HPP
9 #define BOOST_GIL_EXTENSION_IO_TIFF_DETAIL_READER_HPP
11 #include <boost/gil/extension/io/tiff/detail/device.hpp>
12 #include <boost/gil/extension/io/tiff/detail/is_allowed.hpp>
13 #include <boost/gil/extension/io/tiff/detail/reader_backend.hpp>
15 #include <boost/gil/io/base.hpp>
16 #include <boost/gil/io/bit_operations.hpp>
17 #include <boost/gil/io/conversion_policies.hpp>
18 #include <boost/gil/io/device.hpp>
19 #include <boost/gil/io/dynamic_io_new.hpp>
20 #include <boost/gil/io/reader_base.hpp>
21 #include <boost/gil/io/row_buffer_helper.hpp>
23 #include <boost/assert.hpp>
27 #include <type_traits>
30 // taken from jpegxx - https://bitbucket.org/edd/jpegxx/src/ea2492a1a4a6/src/ijg_headers.hpp
31 #ifndef BOOST_GIL_EXTENSION_IO_TIFF_C_LIB_COMPILED_AS_CPLUSPLUS
38 #ifndef BOOST_GIL_EXTENSION_IO_TIFF_C_LIB_COMPILED_AS_CPLUSPLUS
42 namespace boost { namespace gil {
44 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
46 #pragma warning(disable:4512) //assignment operator could not be generated
50 struct plane_recursion
52 template< typename View
54 , typename ConversionPolicy
57 void read_plane( const View& dst_view
60 , ConversionPolicy >* p
63 using plane_t = typename kth_channel_view_type<K, View>::type;
64 plane_t plane = kth_channel_view<K>( dst_view );
66 p->template read_data< detail::row_buffer_helper_view< plane_t > >( plane, K );
68 plane_recursion< K - 1 >::read_plane( dst_view, p );
73 struct plane_recursion< -1 >
75 template< typename View
77 , typename ConversionPolicy
80 void read_plane( const View& /* dst_view */
92 template< typename Device
93 , typename ConversionPolicy
99 : public reader_base< tiff_tag
102 , public reader_backend< Device
108 using this_t = reader<Device, tiff_tag, ConversionPolicy>;
109 using cc_t = typename ConversionPolicy::color_converter_type;
113 using backend_t = reader_backend<Device, tiff_tag>;
115 reader( const Device& io_dev
116 , const image_read_settings< tiff_tag >& settings
118 : reader_base< tiff_tag
126 reader( const Device& io_dev
127 , const typename ConversionPolicy::color_converter_type& cc
128 , const image_read_settings< tiff_tag >& settings
130 : reader_base< tiff_tag
138 // only works for homogeneous image types
139 template< typename View >
140 void apply( View& dst_view )
142 if( this->_info._photometric_interpretation == PHOTOMETRIC_PALETTE )
144 this->_scanline_length = this->_info._width
145 * num_channels< rgb16_view_t >::value
146 * sizeof( channel_type<rgb16_view_t>::type );
149 // 1. Read indices. It's an array of grayX_pixel_t.
150 // 2. Read palette. It's an array of rgb16_pixel_t.
151 // 3. ??? Create virtual image or transform the two arrays
152 // into a rgb16_image_t object. The latter might
153 // be a good first solution.
155 switch( this->_info._bits_per_sample )
157 case 1: { read_palette_image< gray1_image_t >( dst_view ); break; }
158 case 2: { read_palette_image< gray2_image_t >( dst_view ); break; }
159 case 4: { read_palette_image< gray4_image_t >( dst_view ); break; }
160 case 8: { read_palette_image< gray8_image_t >( dst_view ); break; }
161 case 16: { read_palette_image< gray16_image_t >( dst_view ); break; }
163 default: { io_error( "Not supported palette " ); }
171 this->_scanline_length = this->_io_dev.get_scanline_size();
173 // In case we only read the image the user's type and
174 // the tiff type need to compatible. Which means:
175 // color_spaces_are_compatible && channels_are_pairwise_compatible
177 using is_read_only = typename std::is_same
180 detail::read_and_no_convert
183 io_error_if( !detail::is_allowed< View >( this->_info
186 , "Image types aren't compatible."
189 if( this->_info._planar_configuration == PLANARCONFIG_SEPARATE )
191 plane_recursion< num_channels< View >::value - 1 >::read_plane( dst_view
195 else if( this->_info._planar_configuration == PLANARCONFIG_CONTIG )
198 , typename is_read_only::type()
203 io_error( "Wrong planar configuration setting." );
210 template< typename View >
212 , std::true_type // is_read_only
215 read_data< detail::row_buffer_helper_view< View > >( v, 0 );
218 template< typename View >
220 , std::false_type // is_read_only
223 // the read_data function needs to know what gil type the source image is
224 // to have the default color converter function correctly
226 switch( this->_info._photometric_interpretation )
228 case PHOTOMETRIC_MINISWHITE:
229 case PHOTOMETRIC_MINISBLACK:
231 switch( this->_info._bits_per_sample )
233 case 1: { read_data< detail::row_buffer_helper_view< gray1_image_t::view_t > >( v, 0 ); break; }
234 case 2: { read_data< detail::row_buffer_helper_view< gray2_image_t::view_t > >( v, 0 ); break; }
235 case 4: { read_data< detail::row_buffer_helper_view< gray4_image_t::view_t > >( v, 0 ); break; }
236 case 8: { read_data< detail::row_buffer_helper_view< gray8_view_t > >( v, 0 ); break; }
237 case 16: { read_data< detail::row_buffer_helper_view< gray16_view_t > >( v, 0 ); break; }
238 case 32: { read_data< detail::row_buffer_helper_view< gray32_view_t > >( v, 0 ); break; }
239 default: { io_error( "Image type is not supported." ); }
245 case PHOTOMETRIC_RGB:
247 switch( this->_info._samples_per_pixel )
251 switch( this->_info._bits_per_sample )
253 case 8: { read_data< detail::row_buffer_helper_view< rgb8_view_t > >( v, 0 ); break; }
254 case 16: { read_data< detail::row_buffer_helper_view< rgb16_view_t > >( v, 0 ); break; }
255 case 32: { read_data< detail::row_buffer_helper_view< rgb32_view_t > >( v, 0 ); break; }
256 default: { io_error( "Image type is not supported." ); }
264 switch( this->_info._bits_per_sample )
266 case 8: { read_data< detail::row_buffer_helper_view< rgba8_view_t > >( v, 0 ); break; }
267 case 16: { read_data< detail::row_buffer_helper_view< rgba16_view_t > >( v, 0 ); break; }
268 case 32: { read_data< detail::row_buffer_helper_view< rgba32_view_t > >( v, 0 ); break; }
269 default: { io_error( "Image type is not supported." ); }
275 default: { io_error( "Image type is not supported." ); }
280 case PHOTOMETRIC_SEPARATED: // CYMK
282 switch( this->_info._bits_per_sample )
284 case 8: { read_data< detail::row_buffer_helper_view< cmyk8_view_t > >( v, 0 ); break; }
285 case 16: { read_data< detail::row_buffer_helper_view< cmyk16_view_t > >( v, 0 ); break; }
286 case 32: { read_data< detail::row_buffer_helper_view< cmyk32_view_t > >( v, 0 ); break; }
287 default: { io_error( "Image type is not supported." ); }
293 default: { io_error( "Image type is not supported." ); }
297 template< typename PaletteImage
300 void read_palette_image( const View& dst_view )
302 PaletteImage indices( this->_info._width - this->_settings._top_left.x
303 , this->_info._height - this->_settings._top_left.y );
305 // read the palette first
306 read_data< detail::row_buffer_helper_view
308 typename PaletteImage::view_t>
311 read_palette_image(dst_view, view(indices),
312 typename std::is_same<View, rgb16_view_t>::type());
315 template< typename View
316 , typename Indices_View
318 void read_palette_image( const View& dst_view
319 , const Indices_View& indices_view
320 , std::true_type // is View rgb16_view_t
323 tiff_color_map::red_t red = nullptr;
324 tiff_color_map::green_t green = nullptr;
325 tiff_color_map::blue_t blue = nullptr;
327 this->_io_dev.get_field_defaulted( red, green, blue );
329 using channel_t = typename channel_traits<typename element_type<typename Indices_View::value_type>::type>::value_type;
331 int num_colors = channel_traits< channel_t >::max_value();
333 rgb16_planar_view_t palette = planar_rgb_view( num_colors
338 , sizeof(uint16_t) * num_colors );
340 for( typename rgb16_view_t::y_coord_t y = 0; y < dst_view.height(); ++y )
342 typename rgb16_view_t::x_iterator it = dst_view.row_begin( y );
343 typename rgb16_view_t::x_iterator end = dst_view.row_end( y );
345 typename Indices_View::x_iterator indices_it = indices_view.row_begin( y );
347 for( ; it != end; ++it, ++indices_it )
349 uint16_t i = gil::at_c<0>( *indices_it );
356 template< typename View
357 , typename Indices_View
360 void read_palette_image( const View& /* dst_view */
361 , const Indices_View& /* indices_view */
362 , std::false_type // is View rgb16_view_t
365 io_error( "User supplied image type must be rgb16_image_t." );
368 template< typename Buffer >
369 void skip_over_rows( Buffer& buffer
373 if( this->_info._compression != COMPRESSION_NONE )
375 // Skipping over rows is not possible for compressed images( no random access ). See man
376 // page ( diagnostics section ) for more information.
377 for( std::ptrdiff_t row = 0; row < this->_settings._top_left.y; ++row )
379 this->_io_dev.read_scanline( buffer
381 , static_cast< tsample_t >( plane ));
386 template< typename Buffer
389 void read_data( const View& dst_view
392 if( this->_io_dev.is_tiled() )
394 read_tiled_data< Buffer >( dst_view, 0 );
398 read_stripped_data< Buffer >( dst_view, 0 );
403 template< typename Buffer
406 void read_tiled_data( const View& dst_view
410 if( dst_view.width() != this->_info._width
411 || dst_view.height() != this->_info._height
415 read_tiled_data_subimage< Buffer >( dst_view, plane );
420 read_tiled_data_full< Buffer >( dst_view, plane );
424 template< typename Buffer
427 void read_tiled_data_subimage( const View& dst_view
432 /// using row_buffer_helper_t = Buffer;
433 /// not working? I get compiler error with MSVC10.
434 /// read_stripped_data IS working.
435 using row_buffer_helper_t = detail::row_buffer_helper_view<View>;
437 using it_t = typename row_buffer_helper_t::iterator_t;
439 tiff_image_width::type image_width = this->_info._width;
440 tiff_image_height::type image_height = this->_info._height;
442 tiff_tile_width::type tile_width = this->_info._tile_width;
443 tiff_tile_length::type tile_height = this->_info._tile_length;
445 std::ptrdiff_t subimage_x = this->_settings._top_left.x;
446 std::ptrdiff_t subimage_y = this->_settings._top_left.y;
448 std::ptrdiff_t subimage_width = this->_settings._dim.x;
449 std::ptrdiff_t subimage_height = this->_settings._dim.y;
451 row_buffer_helper_t row_buffer_helper(this->_io_dev.get_tile_size(), true );
453 for( unsigned int y = 0; y < image_height; y += tile_height )
455 for( unsigned int x = 0; x < image_width; x += tile_width )
457 uint32_t current_tile_width = ( x + tile_width < image_width ) ? tile_width : image_width - x;
458 uint32_t current_tile_length = ( y + tile_height < image_height ) ? tile_height : image_height - y;
460 this->_io_dev.read_tile( row_buffer_helper.buffer()
464 , static_cast< tsample_t >( plane )
467 // these are all whole image coordinates
468 point_t tile_top_left ( x, y );
469 point_t tile_lower_right( x + current_tile_width - 1, y + current_tile_length - 1 );
471 point_t view_top_left ( subimage_x, subimage_y );
472 point_t view_lower_right( subimage_x + subimage_width - 1
473 , subimage_y + subimage_height - 1 );
475 if( tile_top_left.x > view_lower_right.x
476 || tile_top_left.y > view_lower_right.y
477 || tile_lower_right.x < view_top_left.x
478 || tile_lower_right.y < view_top_left.y
481 // current tile and dst_view do not overlap
486 // dst_view is overlapping the current tile
488 // next is to define the portion in the tile that needs to be copied
490 // get the whole image coordinates
491 std::ptrdiff_t img_x0 = ( tile_top_left.x >= view_top_left.x ) ? tile_top_left.x : view_top_left.x;
492 std::ptrdiff_t img_y0 = ( tile_top_left.y >= view_top_left.y ) ? tile_top_left.y : view_top_left.y;
494 std::ptrdiff_t img_x1 = ( tile_lower_right.x <= view_lower_right.x ) ? tile_lower_right.x : view_lower_right.x;
495 std::ptrdiff_t img_y1 = ( tile_lower_right.y <= view_lower_right.y ) ? tile_lower_right.y : view_lower_right.y;
497 // convert to tile coordinates
498 std::ptrdiff_t tile_x0 = img_x0 - x;
499 std::ptrdiff_t tile_y0 = img_y0 - y;
500 std::ptrdiff_t tile_x1 = img_x1 - x;
501 std::ptrdiff_t tile_y1 = img_y1 - y;
503 BOOST_ASSERT(tile_x0 >= 0 && tile_y0 >= 0 && tile_x1 >= 0 && tile_y1 >= 0);
504 BOOST_ASSERT(tile_x0 <= img_x1 && tile_y0 <= img_y1);
505 BOOST_ASSERT(tile_x0 < tile_width && tile_y0 < tile_height && tile_x1 < tile_width && tile_y1 < tile_height);
507 std::ptrdiff_t tile_subimage_view_width = tile_x1 - tile_x0 + 1;
508 std::ptrdiff_t tile_subimage_view_height = tile_y1 - tile_y0 + 1;
510 // convert to dst_view coordinates
511 std::ptrdiff_t dst_x0 = img_x0 - subimage_x;
512 std::ptrdiff_t dst_y0 = img_y0 - subimage_y;
513 BOOST_ASSERT(dst_x0 >= 0 && dst_y0 >= 0);
515 View dst_subimage_view = subimage_view( dst_view
518 , (int) tile_subimage_view_width
519 , (int) tile_subimage_view_height
522 // the row_buffer is a 1D array which represents a 2D image. We cannot
523 // use interleaved_view here, since row_buffer could be bit_aligned.
524 // Interleaved_view's fourth parameter "rowsize_in_bytes" doesn't work
525 // for bit_aligned pixels.
527 for( std::ptrdiff_t dst_row = 0; dst_row < dst_subimage_view.height(); ++dst_row )
529 std::ptrdiff_t tile_row = dst_row + tile_y0;
531 // jump to the beginning of the current tile row
532 it_t begin = row_buffer_helper.begin() + tile_row * tile_width;
535 it_t end = begin + dst_subimage_view.width();
537 this->_cc_policy.read( begin
539 , dst_subimage_view.row_begin( dst_row )
547 template< typename Buffer
550 void read_tiled_data_full( const View& dst_view
555 /// using row_buffer_helper_t = Buffer;
556 /// not working? I get compiler error with MSVC10.
557 /// read_stripped_data IS working.
558 using row_buffer_helper_t = detail::row_buffer_helper_view<View>;
560 using it_t = typename row_buffer_helper_t::iterator_t;
562 tiff_image_width::type image_width = this->_info._width;
563 tiff_image_height::type image_height = this->_info._height;
565 tiff_tile_width::type tile_width = this->_info._tile_width;
566 tiff_tile_length::type tile_height = this->_info._tile_length;
568 row_buffer_helper_t row_buffer_helper(this->_io_dev.get_tile_size(), true );
570 for( unsigned int y = 0; y < image_height; y += tile_height )
572 for( unsigned int x = 0; x < image_width; x += tile_width )
574 uint32_t current_tile_width = ( x + tile_width < image_width ) ? tile_width : image_width - x;
575 uint32_t current_tile_length = ( y + tile_height < image_height ) ? tile_height : image_height - y;
577 this->_io_dev.read_tile( row_buffer_helper.buffer()
581 , static_cast< tsample_t >( plane )
584 View dst_subimage_view = subimage_view( dst_view
588 , current_tile_length
591 // the row_buffer is a 1D array which represents a 2D image. We cannot
592 // use interleaved_view here, since row_buffer could be bit_aligned.
593 // Interleaved_view's fourth parameter "rowsize_in_bytes" doesn't work
594 // for bit_aligned pixels.
596 for( int row = 0; row < dst_subimage_view.height(); ++row )
598 it_t begin = row_buffer_helper.begin() + row * tile_width;
599 it_t end = begin + dst_subimage_view.width();
601 this->_cc_policy.read( begin
603 , dst_subimage_view.row_begin( row )
610 template< typename Buffer
613 void read_stripped_data( const View& dst_view
616 using is_view_bit_aligned_t = typename is_bit_aligned<typename View::value_type>::type;
618 //using row_buffer_helper_t =detail::row_buffer_helper_view<View>;
619 using row_buffer_helper_t = Buffer;
620 using it_t = typename row_buffer_helper_t::iterator_t;
622 std::size_t size_to_allocate = buffer_size< typename View::value_type >( dst_view.width()
623 , is_view_bit_aligned_t() );
624 row_buffer_helper_t row_buffer_helper( size_to_allocate, true );
626 it_t begin = row_buffer_helper.begin();
628 it_t first = begin + this->_settings._top_left.x;
629 it_t last = first + this->_settings._dim.x; // one after last element
631 // I don't think tiff allows for random access of row, that's why we need
632 // to read and discard rows when reading subimages.
633 skip_over_rows( row_buffer_helper.buffer()
637 std::ptrdiff_t row = this->_settings._top_left.y;
638 std::ptrdiff_t row_end = row + this->_settings._dim.y;
639 std::ptrdiff_t dst_row = 0;
646 this->_io_dev.read_scanline( row_buffer_helper.buffer()
648 , static_cast< tsample_t >( plane )
651 this->_cc_policy.read( first
653 , dst_view.row_begin( dst_row ));
657 template< typename Pixel >
658 std::size_t buffer_size( std::size_t width
659 , std::false_type // is_bit_aligned
662 std::size_t scanline_size_in_bytes = this->_io_dev.get_scanline_size();
664 std::size_t element_size = sizeof( Pixel );
666 std::size_t ret = std::max( width
667 , (( scanline_size_in_bytes + element_size - 1 ) / element_size )
673 template< typename Pixel >
674 std::size_t buffer_size( std::size_t /* width */
675 , std::true_type // is_bit_aligned
678 return this->_io_dev.get_scanline_size();
683 template < int K > friend struct plane_recursion;
688 struct tiff_type_format_checker
690 tiff_type_format_checker( const image_read_info< tiff_tag >& info )
694 template< typename Image >
697 using view_t = typename Image::view_t;
699 return is_allowed< view_t >( _info
705 tiff_type_format_checker& operator=( const tiff_type_format_checker& ) { return *this; }
709 const image_read_info< tiff_tag > _info;
712 struct tiff_read_is_supported
714 template< typename View >
715 struct apply : public is_read_supported< typename get_pixel_type< View >::type
721 } // namespace detail
725 /// Tiff Dynamic Image Reader
727 template< typename Device >
728 class dynamic_image_reader< Device
731 : public reader< Device
733 , detail::read_and_no_convert
736 using parent_t = reader<Device, tiff_tag, detail::read_and_no_convert>;
740 dynamic_image_reader( const Device& io_dev
741 , const image_read_settings< tiff_tag >& settings
748 template< typename Images >
749 void apply( any_image< Images >& images )
751 detail::tiff_type_format_checker format_checker( this->_info );
753 if( !construct_matched( images
757 io_error( "No matching image type between those of the given any_image and that of the file" );
761 this->init_image( images
765 detail::dynamic_io_fnobj< detail::tiff_read_is_supported
769 apply_operation( view( images )
776 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)