@@ -12,13 +12,24 @@ use rawkit_proc_macros::Tag;
1212use std:: io:: { Read , Seek } ;
1313use thiserror:: Error ;
1414use tiff:: file:: TiffRead ;
15- use tiff:: tags:: { Compression , ImageLength , ImageWidth , Orientation , StripByteCounts , SubIfd , Tag } ;
16- use tiff:: values:: Transform ;
15+ use tiff:: tags:: { Compression , ImageLength , ImageWidth , Orientation , StripByteCounts , SubIfd , Tag , ThumbnailLength , ThumbnailOffset } ;
16+ use tiff:: values:: { CompressionValue , OrientationValue } ;
1717use tiff:: { Ifd , TiffError } ;
1818
1919pub ( crate ) const CHANNELS_IN_RGB : usize = 3 ;
2020pub ( crate ) type Histogram = [ [ usize ; 0x2000 ] ; CHANNELS_IN_RGB ] ;
2121
22+ pub enum ThumbnailFormat {
23+ Jpeg ,
24+ Unsupported ,
25+ }
26+
27+ /// A thumbnail image extracted from the raw file. This is usually a JPEG image.
28+ pub struct ThumbnailImage {
29+ pub data : Vec < u8 > ,
30+ pub format : ThumbnailFormat ,
31+ }
32+
2233/// The amount of black level to be subtracted from Raw Image.
2334pub enum SubtractBlack {
2435 /// Don't subtract any value.
@@ -48,7 +59,7 @@ pub struct RawImage {
4859 pub cfa_pattern : [ u8 ; 4 ] ,
4960
5061 /// Transformation to be applied to negate the orientation of camera.
51- pub transform : Transform ,
62+ pub orientation : OrientationValue ,
5263
5364 /// The maximum possible value of pixel that the camera sensor could give.
5465 pub maximum : u16 ,
@@ -97,8 +108,8 @@ pub struct Image<T> {
97108
98109 /// The transformation required to orient the image correctly.
99110 ///
100- /// This will be [`Transform ::Horizontal`] after the transform step is applied.
101- pub transform : Transform ,
111+ /// This will be [`OrientationValue ::Horizontal`] after the orientation step is applied.
112+ pub orientation : OrientationValue ,
102113}
103114
104115#[ allow( dead_code) ]
@@ -119,15 +130,15 @@ impl RawImage {
119130 let ifd = Ifd :: new_first_ifd ( & mut file) ?;
120131
121132 let camera_model = metadata:: identify:: identify_camera_model ( & ifd, & mut file) . unwrap ( ) ;
122- let transform = ifd. get_value :: < Orientation , _ > ( & mut file) ?;
133+ let orientation = ifd. get_value :: < Orientation , _ > ( & mut file) ?;
123134
124135 let mut raw_image = if camera_model. model == "DSLR-A100" {
125136 decoder:: arw1:: decode_a100 ( ifd, & mut file)
126137 } else {
127138 let sub_ifd = ifd. get_value :: < SubIfd , _ > ( & mut file) ?;
128139 let arw_ifd = sub_ifd. get_value :: < ArwIfd , _ > ( & mut file) ?;
129140
130- if arw_ifd. compression == 1 {
141+ if arw_ifd. compression == CompressionValue :: Uncompressed {
131142 decoder:: uncompressed:: decode ( sub_ifd, & mut file)
132143 } else if arw_ifd. strip_byte_counts [ 0 ] == arw_ifd. image_width * arw_ifd. image_height {
133144 decoder:: arw2:: decode ( sub_ifd, & mut file)
@@ -138,13 +149,38 @@ impl RawImage {
138149 } ;
139150
140151 raw_image. camera_model = Some ( camera_model) ;
141- raw_image. transform = transform ;
152+ raw_image. orientation = orientation ;
142153
143154 raw_image. calculate_conversion_matrices ( ) ;
144155
145156 Ok ( raw_image)
146157 }
147158
159+ /// Extracts the thumbnail image from the raw file.
160+ pub fn extract_thumbnail < R : Read + Seek > ( reader : & mut R ) -> Result < ThumbnailImage , DecoderError > {
161+ let mut file = TiffRead :: new ( reader) ?;
162+ let ifd = Ifd :: new_first_ifd ( & mut file) ?;
163+
164+ // TODO: ARW files Store the thumbnail offset and length in the first IFD. Add support for other file types in the future.
165+ let thumbnail_offset = ifd. get_value :: < ThumbnailOffset , _ > ( & mut file) ?;
166+ let thumbnail_length = ifd. get_value :: < ThumbnailLength , _ > ( & mut file) ?;
167+ file. seek_from_start ( thumbnail_offset) ?;
168+
169+ let mut thumbnail_data = vec ! [ 0 ; thumbnail_length as usize ] ;
170+ file. read_exact ( & mut thumbnail_data) ?;
171+
172+ // Check the first two bytes to determine the format of the thumbnail.
173+ // JPEG format starts with 0xFF, 0xD8.
174+ if thumbnail_data[ 0 ..2 ] == [ 0xFF , 0xD8 ] {
175+ Ok ( ThumbnailImage {
176+ data : thumbnail_data,
177+ format : ThumbnailFormat :: Jpeg ,
178+ } )
179+ } else {
180+ Err ( DecoderError :: UnsupportedThumbnailFormat )
181+ }
182+ }
183+
148184 /// Converts the [`RawImage`] to an [`Image`] with 8 bit resolution for each channel.
149185 ///
150186 /// Applies all the processing steps to finally get RGB pixel data.
@@ -156,7 +192,7 @@ impl RawImage {
156192 data : image. data . iter ( ) . map ( |x| ( x >> 8 ) as u8 ) . collect ( ) ,
157193 width : image. width ,
158194 height : image. height ,
159- transform : image. transform ,
195+ orientation : image. orientation ,
160196 }
161197 }
162198
@@ -174,7 +210,7 @@ impl RawImage {
174210 let image = raw_image. demosaic_and_apply ( ( convert_to_rgb, & mut record_histogram) ) ;
175211
176212 let gamma_correction = image. gamma_correction_fn ( & record_histogram. histogram ) ;
177- if image. transform == Transform :: Horizontal {
213+ if image. orientation == OrientationValue :: Horizontal {
178214 image. apply ( gamma_correction)
179215 } else {
180216 image. transform_and_apply ( gamma_correction)
@@ -211,7 +247,7 @@ impl RawImage {
211247 data : image,
212248 width : self . width ,
213249 height : self . height ,
214- transform : self . transform ,
250+ orientation : self . orientation ,
215251 }
216252 }
217253}
@@ -232,7 +268,7 @@ impl Image<u16> {
232268
233269 pub fn transform_and_apply ( self , mut transform : impl PixelTransform ) -> Image < u16 > {
234270 let mut image = vec ! [ 0 ; self . width * self . height * 3 ] ;
235- let ( width, height, iter) = self . transform_iter ( ) ;
271+ let ( width, height, iter) = self . orientation_iter ( ) ;
236272 for Pixel { values, row, column } in iter. map ( |mut pixel| {
237273 pixel. values = transform. apply ( pixel) ;
238274 pixel
@@ -246,7 +282,7 @@ impl Image<u16> {
246282 data : image,
247283 width,
248284 height,
249- transform : Transform :: Horizontal ,
285+ orientation : OrientationValue :: Horizontal ,
250286 }
251287 }
252288}
@@ -259,4 +295,6 @@ pub enum DecoderError {
259295 ConversionError ( #[ from] std:: num:: TryFromIntError ) ,
260296 #[ error( "An IO Error ocurred" ) ]
261297 IoError ( #[ from] std:: io:: Error ) ,
298+ #[ error( "The thumbnail format is unsupported" ) ]
299+ UnsupportedThumbnailFormat ,
262300}
0 commit comments