Imported Upstream version 1.72.0
[platform/upstream/boost.git] / boost / gil / extension / io / targa / detail / read.hpp
1 //
2 // Copyright 2012 Kenneth Riddile, 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_TARGA_DETAIL_READ_HPP
9 #define BOOST_GIL_EXTENSION_IO_TARGA_DETAIL_READ_HPP
10
11 #include <boost/gil/extension/io/targa/tags.hpp>
12 #include <boost/gil/extension/io/targa/detail/reader_backend.hpp>
13 #include <boost/gil/extension/io/targa/detail/is_allowed.hpp>
14
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>
22 #include <boost/gil/io/typedefs.hpp>
23
24 #include <type_traits>
25 #include <vector>
26
27 namespace boost { namespace gil {
28
29 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
30 #pragma warning(push)
31 #pragma warning(disable:4512) //assignment operator could not be generated
32 #endif
33
34 ///
35 /// Targa Reader
36 ///
37 template< typename Device
38         , typename ConversionPolicy
39         >
40 class reader< Device
41             , targa_tag
42             , ConversionPolicy
43             >
44     : public reader_base< targa_tag
45                         , ConversionPolicy
46                         >
47     , public reader_backend< Device
48                            , targa_tag
49                            >
50 {
51 private:
52
53     using this_t = reader<Device, targa_tag, ConversionPolicy>;
54     using cc_t = typename ConversionPolicy::color_converter_type;
55
56 public:
57
58     using backend_t = reader_backend<Device, targa_tag>;
59
60     reader( const Device&                           io_dev
61           , const image_read_settings< targa_tag >& settings
62           )
63     : reader_base< targa_tag
64                  , ConversionPolicy
65                  >()
66     , backend_t( io_dev
67                , settings
68                )
69     {}
70
71     reader( const Device&                         io_dev
72           , const cc_t&                           cc
73           , const image_read_settings< targa_tag >& settings
74           )
75     : reader_base< targa_tag
76                  , ConversionPolicy
77                  >( cc )
78     , backend_t( io_dev
79                , settings
80                )
81     {}
82
83     template< typename View >
84     void apply( const View& dst_view )
85     {
86         using is_read_and_convert_t = typename std::is_same
87             <
88                 ConversionPolicy,
89                 detail::read_and_no_convert
90             >::type;
91
92         io_error_if( !detail::is_allowed< View >( this->_info, is_read_and_convert_t() )
93                    , "Image types aren't compatible."
94                    );
95
96         switch( this->_info._image_type )
97         {
98             case targa_image_type::_rgb:
99             {
100                 if( this->_info._color_map_type != targa_color_map_type::_rgb )
101                 {
102                     io_error( "Inconsistent color map type and image type in targa file." );
103                 }
104
105                 if( this->_info._color_map_length != 0 )
106                 {
107                     io_error( "Non-indexed targa files containing a palette are not supported." );
108                 }
109
110                 switch( this->_info._bits_per_pixel )
111                 {
112                     case 24:
113                     {
114                         this->_scanline_length = this->_info._width * ( this->_info._bits_per_pixel / 8 );
115
116                         if( this->_info._screen_origin_bit )
117                         {
118                             read_data< bgr8_view_t >( flipped_up_down_view( dst_view ) );
119                         }
120                         else
121                         {
122                             read_data< bgr8_view_t >( dst_view );
123                         }
124
125                         break;
126                     }
127                     case 32:
128                     {
129                         this->_scanline_length = this->_info._width * ( this->_info._bits_per_pixel / 8 );
130
131                         if( this->_info._screen_origin_bit )
132                         {
133                             read_data< bgra8_view_t >( flipped_up_down_view( dst_view ) );
134                         }
135                         else
136                         {
137                             read_data< bgra8_view_t >( dst_view );
138                         }
139
140                         break;
141                     }
142                     default:
143                     {
144                         io_error( "Unsupported bit depth in targa file." );
145                         break;
146                     }
147                 }
148
149                 break;
150             }
151             case targa_image_type::_rle_rgb:
152             {
153                 if( this->_info._color_map_type != targa_color_map_type::_rgb )
154                 {
155                     io_error( "Inconsistent color map type and image type in targa file." );
156                 }
157
158                 if( this->_info._color_map_length != 0 )
159                 {
160                     io_error( "Non-indexed targa files containing a palette are not supported." );
161                 }
162
163                 switch( this->_info._bits_per_pixel )
164                 {
165                     case 24:
166                     {
167                         if( this->_info._screen_origin_bit )
168                         {
169                             read_rle_data< bgr8_view_t >( flipped_up_down_view( dst_view ) );
170                         }
171                         else
172                         {
173                             read_rle_data< bgr8_view_t >( dst_view );
174                         }
175                         break;
176                     }
177                     case 32:
178                     {
179                         if( this->_info._screen_origin_bit )
180                         {
181                             read_rle_data< bgra8_view_t >( flipped_up_down_view( dst_view ) );
182                         }
183                         else
184                         {
185                             read_rle_data< bgra8_view_t >( dst_view );
186                         }
187                         break;
188                     }
189                     default:
190                     {
191                         io_error( "Unsupported bit depth in targa file." );
192                         break;
193                     }
194                 }
195
196                 break;
197             }
198             default:
199             {
200                 io_error( "Unsupported image type in targa file." );
201                 break;
202             }
203         }
204     }
205
206 private:
207
208     // 8-8-8 BGR
209     // 8-8-8-8 BGRA
210     template< typename View_Src, typename View_Dst >
211     void read_data( const View_Dst& view )
212     {
213         byte_vector_t row( this->_info._width * (this->_info._bits_per_pixel / 8) );
214
215         // jump to first scanline
216         this->_io_dev.seek( static_cast< long >( this->_info._offset ));
217
218         View_Src v = interleaved_view( this->_info._width,
219                                        1,
220                                        reinterpret_cast<typename View_Src::value_type*>( &row.front() ),
221                                        this->_info._width * num_channels< View_Src >::value
222                                      );
223
224         typename View_Src::x_iterator beg = v.row_begin( 0 ) + this->_settings._top_left.x;
225         typename View_Src::x_iterator end = beg + this->_settings._dim.x;
226
227         // read bottom up since targa origin is bottom left
228         for( std::ptrdiff_t y = this->_settings._dim.y - 1; y > -1; --y )
229         {
230             // @todo: For now we're reading the whole scanline which is
231             // slightly inefficient. Later versions should try to read
232             // only the bytes which are necessary.
233             this->_io_dev.read( &row.front(), row.size() );
234             this->_cc_policy.read( beg, end, view.row_begin(y) );
235         }
236     }
237
238     // 8-8-8 BGR
239     // 8-8-8-8 BGRA
240     template< typename View_Src, typename View_Dst >
241     void read_rle_data( const View_Dst& view )
242     {
243         targa_depth::type bytes_per_pixel = this->_info._bits_per_pixel / 8;
244         size_t image_size = this->_info._width * this->_info._height * bytes_per_pixel;
245         byte_vector_t image_data( image_size );
246
247         this->_io_dev.seek( static_cast< long >( this->_info._offset ));
248
249         for( size_t pixel = 0; pixel < image_size; )
250         {
251             targa_offset::type current_byte = this->_io_dev.read_uint8();
252
253             if( current_byte & 0x80 ) // run length chunk (high bit = 1)
254             {
255                 uint8_t chunk_length = current_byte - 127;
256                 uint8_t pixel_data[4];
257                 for( size_t channel = 0; channel < bytes_per_pixel; ++channel )
258                 {
259                     pixel_data[channel] = this->_io_dev.read_uint8();
260                 }
261
262                 // Repeat the next pixel chunk_length times
263                 for( uint8_t i = 0; i < chunk_length; ++i, pixel += bytes_per_pixel )
264                 {
265                     memcpy( &image_data[pixel], pixel_data, bytes_per_pixel );
266                 }
267             }
268             else // raw chunk
269             {
270                 uint8_t chunk_length = current_byte + 1;
271
272                 // Write the next chunk_length pixels directly
273                 size_t pixels_written = chunk_length * bytes_per_pixel;
274                 this->_io_dev.read( &image_data[pixel], pixels_written );
275                 pixel += pixels_written;
276             }
277         }
278
279         View_Src v = flipped_up_down_view( interleaved_view( this->_info._width,
280                                                              this->_info._height,
281                                                              reinterpret_cast<typename View_Src::value_type*>( &image_data.front() ),
282                                                              this->_info._width * num_channels< View_Src >::value ) );
283
284         for( std::ptrdiff_t y = 0; y != this->_settings._dim.y; ++y )
285         {
286             typename View_Src::x_iterator beg = v.row_begin( y ) + this->_settings._top_left.x;
287             typename View_Src::x_iterator end = beg + this->_settings._dim.x;
288             this->_cc_policy.read( beg, end, view.row_begin(y) );
289         }
290     }
291 };
292
293 namespace detail {
294
295 class targa_type_format_checker
296 {
297 public:
298
299     targa_type_format_checker( const targa_depth::type& bpp )
300     : _bpp( bpp )
301     {}
302
303     template< typename Image >
304     bool apply()
305     {
306         if( _bpp < 32 )
307         {
308             return pixels_are_compatible< typename Image::value_type, rgb8_pixel_t >::value
309                    ? true
310                    : false;
311         }
312         else
313         {
314             return pixels_are_compatible< typename Image::value_type, rgba8_pixel_t >::value
315                    ? true
316                    : false;
317         }
318     }
319
320 private:
321
322     // to avoid C4512
323     targa_type_format_checker& operator=( const targa_type_format_checker& ) { return *this; }
324
325 private:
326
327     const targa_depth::type _bpp;
328 };
329
330 struct targa_read_is_supported
331 {
332     template< typename View >
333     struct apply : public is_read_supported< typename get_pixel_type< View >::type
334                                            , targa_tag
335                                            >
336     {};
337 };
338
339 } // namespace detail
340
341 ///
342 /// Targa Dynamic Image Reader
343 ///
344 template< typename Device >
345 class dynamic_image_reader< Device
346                           , targa_tag
347                           >
348     : public reader< Device
349                    , targa_tag
350                    , detail::read_and_no_convert
351                    >
352 {
353     using parent_t = reader<Device, targa_tag, detail::read_and_no_convert>;
354
355 public:
356
357     dynamic_image_reader( const Device&                           io_dev
358                         , const image_read_settings< targa_tag >& settings
359                         )
360     : parent_t( io_dev
361               , settings
362               )
363     {}
364
365     template< typename Images >
366     void apply( any_image< Images >& images )
367     {
368         detail::targa_type_format_checker format_checker( this->_info._bits_per_pixel );
369
370         if( !construct_matched( images
371                               , format_checker
372                               ))
373         {
374             io_error( "No matching image type between those of the given any_image and that of the file" );
375         }
376         else
377         {
378             this->init_image( images
379                             , this->_settings
380                             );
381
382             detail::dynamic_io_fnobj< detail::targa_read_is_supported
383                                     , parent_t
384                                     > op( this );
385
386             apply_operation( view( images )
387                            , op
388                            );
389         }
390     }
391 };
392
393 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
394 #pragma warning(pop)
395 #endif
396
397 } // namespace gil
398 } // namespace boost
399
400 #endif