// // Copyright 2012 Kenneth Riddile, Christian Henning // // Distributed under 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_TARGA_DETAIL_READ_HPP #define BOOST_GIL_EXTENSION_IO_TARGA_DETAIL_READ_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace gil { #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) #pragma warning(push) #pragma warning(disable:4512) //assignment operator could not be generated #endif /// /// Targa Reader /// template< typename Device , typename ConversionPolicy > class reader< Device , targa_tag , ConversionPolicy > : public reader_base< targa_tag , ConversionPolicy > , public reader_backend< Device , targa_tag > { private: using this_t = reader; using cc_t = typename ConversionPolicy::color_converter_type; public: using backend_t = reader_backend; reader( const Device& io_dev , const image_read_settings< targa_tag >& settings ) : reader_base< targa_tag , ConversionPolicy >() , backend_t( io_dev , settings ) {} reader( const Device& io_dev , const cc_t& cc , const image_read_settings< targa_tag >& settings ) : reader_base< targa_tag , ConversionPolicy >( cc ) , backend_t( io_dev , settings ) {} template< typename View > void apply( const View& dst_view ) { using is_read_and_convert_t = typename std::is_same < ConversionPolicy, detail::read_and_no_convert >::type; io_error_if( !detail::is_allowed< View >( this->_info, is_read_and_convert_t() ) , "Image types aren't compatible." ); switch( this->_info._image_type ) { case targa_image_type::_rgb: { if( this->_info._color_map_type != targa_color_map_type::_rgb ) { io_error( "Inconsistent color map type and image type in targa file." ); } if( this->_info._color_map_length != 0 ) { io_error( "Non-indexed targa files containing a palette are not supported." ); } switch( this->_info._bits_per_pixel ) { case 24: { this->_scanline_length = this->_info._width * ( this->_info._bits_per_pixel / 8 ); if( this->_info._screen_origin_bit ) { read_data< bgr8_view_t >( flipped_up_down_view( dst_view ) ); } else { read_data< bgr8_view_t >( dst_view ); } break; } case 32: { this->_scanline_length = this->_info._width * ( this->_info._bits_per_pixel / 8 ); if( this->_info._screen_origin_bit ) { read_data< bgra8_view_t >( flipped_up_down_view( dst_view ) ); } else { read_data< bgra8_view_t >( dst_view ); } break; } default: { io_error( "Unsupported bit depth in targa file." ); break; } } break; } case targa_image_type::_rle_rgb: { if( this->_info._color_map_type != targa_color_map_type::_rgb ) { io_error( "Inconsistent color map type and image type in targa file." ); } if( this->_info._color_map_length != 0 ) { io_error( "Non-indexed targa files containing a palette are not supported." ); } switch( this->_info._bits_per_pixel ) { case 24: { if( this->_info._screen_origin_bit ) { read_rle_data< bgr8_view_t >( flipped_up_down_view( dst_view ) ); } else { read_rle_data< bgr8_view_t >( dst_view ); } break; } case 32: { if( this->_info._screen_origin_bit ) { read_rle_data< bgra8_view_t >( flipped_up_down_view( dst_view ) ); } else { read_rle_data< bgra8_view_t >( dst_view ); } break; } default: { io_error( "Unsupported bit depth in targa file." ); break; } } break; } default: { io_error( "Unsupported image type in targa file." ); break; } } } private: // 8-8-8 BGR // 8-8-8-8 BGRA template< typename View_Src, typename View_Dst > void read_data( const View_Dst& view ) { byte_vector_t row( this->_info._width * (this->_info._bits_per_pixel / 8) ); // jump to first scanline this->_io_dev.seek( static_cast< long >( this->_info._offset )); View_Src v = interleaved_view( this->_info._width, 1, reinterpret_cast( &row.front() ), this->_info._width * num_channels< View_Src >::value ); typename View_Src::x_iterator beg = v.row_begin( 0 ) + this->_settings._top_left.x; typename View_Src::x_iterator end = beg + this->_settings._dim.x; // read bottom up since targa origin is bottom left for( std::ptrdiff_t y = this->_settings._dim.y - 1; y > -1; --y ) { // @todo: For now we're reading the whole scanline which is // slightly inefficient. Later versions should try to read // only the bytes which are necessary. this->_io_dev.read( &row.front(), row.size() ); this->_cc_policy.read( beg, end, view.row_begin(y) ); } } // 8-8-8 BGR // 8-8-8-8 BGRA template< typename View_Src, typename View_Dst > void read_rle_data( const View_Dst& view ) { targa_depth::type bytes_per_pixel = this->_info._bits_per_pixel / 8; size_t image_size = this->_info._width * this->_info._height * bytes_per_pixel; byte_vector_t image_data( image_size ); this->_io_dev.seek( static_cast< long >( this->_info._offset )); for( size_t pixel = 0; pixel < image_size; ) { targa_offset::type current_byte = this->_io_dev.read_uint8(); if( current_byte & 0x80 ) // run length chunk (high bit = 1) { uint8_t chunk_length = current_byte - 127; uint8_t pixel_data[4]; for( size_t channel = 0; channel < bytes_per_pixel; ++channel ) { pixel_data[channel] = this->_io_dev.read_uint8(); } // Repeat the next pixel chunk_length times for( uint8_t i = 0; i < chunk_length; ++i, pixel += bytes_per_pixel ) { memcpy( &image_data[pixel], pixel_data, bytes_per_pixel ); } } else // raw chunk { uint8_t chunk_length = current_byte + 1; // Write the next chunk_length pixels directly size_t pixels_written = chunk_length * bytes_per_pixel; this->_io_dev.read( &image_data[pixel], pixels_written ); pixel += pixels_written; } } View_Src v = flipped_up_down_view( interleaved_view( this->_info._width, this->_info._height, reinterpret_cast( &image_data.front() ), this->_info._width * num_channels< View_Src >::value ) ); for( std::ptrdiff_t y = 0; y != this->_settings._dim.y; ++y ) { typename View_Src::x_iterator beg = v.row_begin( y ) + this->_settings._top_left.x; typename View_Src::x_iterator end = beg + this->_settings._dim.x; this->_cc_policy.read( beg, end, view.row_begin(y) ); } } }; namespace detail { class targa_type_format_checker { public: targa_type_format_checker( const targa_depth::type& bpp ) : _bpp( bpp ) {} template< typename Image > bool apply() { if( _bpp < 32 ) { return pixels_are_compatible< typename Image::value_type, rgb8_pixel_t >::value ? true : false; } else { return pixels_are_compatible< typename Image::value_type, rgba8_pixel_t >::value ? true : false; } } private: // to avoid C4512 targa_type_format_checker& operator=( const targa_type_format_checker& ) { return *this; } private: const targa_depth::type _bpp; }; struct targa_read_is_supported { template< typename View > struct apply : public is_read_supported< typename get_pixel_type< View >::type , targa_tag > {}; }; } // namespace detail /// /// Targa Dynamic Image Reader /// template< typename Device > class dynamic_image_reader< Device , targa_tag > : public reader< Device , targa_tag , detail::read_and_no_convert > { using parent_t = reader; public: dynamic_image_reader( const Device& io_dev , const image_read_settings< targa_tag >& settings ) : parent_t( io_dev , settings ) {} template< typename Images > void apply( any_image< Images >& images ) { detail::targa_type_format_checker format_checker( this->_info._bits_per_pixel ); if( !construct_matched( images , format_checker )) { io_error( "No matching image type between those of the given any_image and that of the file" ); } else { this->init_image( images , this->_settings ); detail::dynamic_io_fnobj< detail::targa_read_is_supported , parent_t > op( this ); apply_operation( view( images ) , op ); } } }; #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) #pragma warning(pop) #endif } // namespace gil } // namespace boost #endif