3333
3434 // We only use an Option here so we can call with_limits on the decoder without moving.
3535 inner : Option < Decoder < R > > ,
36+ buffer : DecodingResult ,
3637}
3738
3839impl < R > TiffDecoder < R >
5657 Err ( other) => return Err ( ImageError :: from_tiff_decode ( other) ) ,
5758 }
5859
59- let planar_config = inner
60- . find_tag ( Tag :: PlanarConfiguration )
61- . map ( |res| res. and_then ( |r| r. into_u16 ( ) . ok ( ) ) . unwrap_or_default ( ) )
62- . unwrap_or_default ( ) ;
63-
64- // Decode not supported for non Chunky Planar Configuration
65- if planar_config > 1 {
66- Err ( ImageError :: Unsupported (
67- UnsupportedError :: from_format_and_kind (
68- ImageFormat :: Tiff . into ( ) ,
69- UnsupportedErrorKind :: GenericFeature ( String :: from ( "PlanarConfiguration = 2" ) ) ,
70- ) ,
71- ) ) ?;
72- }
73-
7460 let color_type = match tiff_color_type {
7561 tiff:: ColorType :: Gray ( 1 ) => ColorType :: L8 ,
7662 tiff:: ColorType :: Gray ( 8 ) => ColorType :: L8 ,
@@ -118,6 +104,7 @@ where
118104 color_type,
119105 original_color_type,
120106 inner : Some ( inner) ,
107+ buffer : DecodingResult :: U8 ( vec ! [ ] ) ,
121108 } )
122109 }
123110
@@ -133,6 +120,66 @@ where
133120 } ;
134121 total_pixels. saturating_mul ( bytes_per_pixel)
135122 }
123+
124+ /// Interleave planes in our `buffer` into `output`.
125+ fn interleave_planes (
126+ & mut self ,
127+ layout : tiff:: decoder:: BufferLayoutPreference ,
128+ output : & mut [ u8 ] ,
129+ ) -> ImageResult < ( ) > {
130+ if self . original_color_type != self . color_type . into ( ) {
131+ return Err ( ImageError :: Unsupported (
132+ UnsupportedError :: from_format_and_kind (
133+ ImageFormat :: Tiff . into ( ) ,
134+ UnsupportedErrorKind :: GenericFeature (
135+ "Planar TIFF with CMYK color type is not supported" . to_string ( ) ,
136+ ) ,
137+ ) ,
138+ ) ) ;
139+ }
140+
141+ // This only works if we and `tiff` agree on the layout, including the color type, of
142+ // the sample matrix.
143+ //
144+ // TODO: triple buffer in the other case and fixup the planar layout independent of
145+ // sample type. Problem description follows:
146+ //
147+ // That will suck since we can't call `interleave_planes` with a `ColorType` argument,
148+ // Changing that parameter to `ExtendedColorType` is a can of worms, and exposing the
149+ // underlying generic function is an optimization killer (we may want to help LLVM
150+ // optimize this interleaving by SIMD). For LumaAlpha(1) colors we should do the bit
151+ // expansion at the same time as interleaving to avoid wasting the memory traversal but
152+ // expand-then-interleave is at least clear, albeit an extra buffer required. Meanwhile
153+ // for `Cmyk8`/`Cmyk16` our output is smaller than the tiff buffer (4 samples to 3, or
154+ // 5 to 4 if we had alpha) and not wanting multiple conversion function implementations
155+ // we should interleave-then-expand?
156+ //
157+ // The hard part of the solution will be managing complexity.
158+ let plane_stride = layout. plane_stride . map_or ( 0 , |n| n. get ( ) ) ;
159+ let bytes = self . buffer . as_buffer ( 0 ) ;
160+
161+ let planes = bytes
162+ . as_bytes ( )
163+ . chunks_exact ( plane_stride)
164+ . collect :: < Vec < _ > > ( ) ;
165+
166+ // Gracefully handle a mismatch of expectations. This should not occur in practice as we
167+ // check that all planes have been read (see note on `read_image_to_buffer` usage below).
168+ if planes. len ( ) < usize:: from ( self . color_type . channel_count ( ) ) {
169+ return Err ( ImageError :: Decoding ( DecodingError :: new (
170+ ImageFormat :: Tiff . into ( ) ,
171+ "Not enough planes read from TIFF image" . to_string ( ) ,
172+ ) ) ) ;
173+ }
174+
175+ utils:: interleave_planes (
176+ output,
177+ self . color_type ,
178+ & planes[ ..usize:: from ( self . color_type . channel_count ( ) ) ] ,
179+ ) ;
180+
181+ Ok ( ( ) )
182+ }
136183}
137184
138185fn check_sample_format ( sample_format : u16 , color_type : tiff:: ColorType ) -> Result < ( ) , ImageError > {
@@ -319,15 +366,30 @@ impl<R: BufRead + Seek> ImageDecoder for TiffDecoder<R> {
319366 Ok ( ( ) )
320367 }
321368
322- fn read_image ( self , buf : & mut [ u8 ] ) -> ImageResult < ( ) > {
369+ fn read_image ( mut self , buf : & mut [ u8 ] ) -> ImageResult < ( ) > {
323370 assert_eq ! ( u64 :: try_from( buf. len( ) ) , Ok ( self . total_bytes( ) ) ) ;
324371
325- match self
372+ let layout = self
326373 . inner
374+ . as_mut ( )
327375 . unwrap ( )
328- . read_image ( )
329- . map_err ( ImageError :: from_tiff_decode) ?
330- {
376+ . read_image_to_buffer ( & mut self . buffer )
377+ . map_err ( ImageError :: from_tiff_decode) ?;
378+
379+ // Check if we have all of the planes. Otherwise we ran into the allocation limit.
380+ if self . buffer . as_buffer ( 0 ) . as_bytes ( ) . len ( ) < layout. complete_len {
381+ return Err ( ImageError :: Limits ( LimitError :: from_kind (
382+ LimitErrorKind :: InsufficientMemory ,
383+ ) ) ) ;
384+ }
385+
386+ if layout. planes > 1 {
387+ // Note that we do not support planar layouts if we have to do conversion. Yet. See a
388+ // more detailed comment in the implementation.
389+ return self . interleave_planes ( layout, buf) ;
390+ }
391+
392+ match self . buffer {
331393 DecodingResult :: U8 ( v) if self . original_color_type == ExtendedColorType :: Cmyk8 => {
332394 let mut out_cur = Cursor :: new ( buf) ;
333395 for cmyk in v. chunks_exact ( 4 ) {
@@ -383,6 +445,7 @@ impl<R: BufRead + Seek> ImageDecoder for TiffDecoder<R> {
383445 }
384446 DecodingResult :: F16 ( _) => unreachable ! ( ) ,
385447 }
448+
386449 Ok ( ( ) )
387450 }
388451
0 commit comments