@@ -12,7 +12,7 @@ use crate::tiff::tags::{
12
12
SampleFormat , Tag ,
13
13
} ;
14
14
use crate :: tiff:: { TiffError , Value } ;
15
- use crate :: tile:: { PredictorInfo , Tile } ;
15
+ use crate :: tile:: Tile ;
16
16
17
17
const DOCUMENT_NAME : u16 = 269 ;
18
18
@@ -190,15 +190,18 @@ impl ImageFileDirectory {
190
190
191
191
let mut other_tags = HashMap :: new ( ) ;
192
192
193
- // for x in tag_data.into_iter() {
194
-
195
- // }
196
193
tag_data. into_iter ( ) . try_for_each ( |( tag, value) | {
197
194
match tag {
198
195
Tag :: NewSubfileType => new_subfile_type = Some ( value. into_u32 ( ) ?) ,
199
196
Tag :: ImageWidth => image_width = Some ( value. into_u32 ( ) ?) ,
200
197
Tag :: ImageLength => image_height = Some ( value. into_u32 ( ) ?) ,
201
- Tag :: BitsPerSample => bits_per_sample = Some ( value. into_u16_vec ( ) ?) ,
198
+ Tag :: BitsPerSample => bits_per_sample = Some ( {
199
+ let values = value. into_u16_vec ( ) ?;
200
+ if values. iter ( ) . any ( |v| * v != values[ 0 ] ) {
201
+ return Err ( TiffError :: UnsupportedError ( crate :: tiff:: TiffUnsupportedError :: InconsistentBitsPerSample ( values) ) ) ;
202
+ }
203
+ values
204
+ } ) ,
202
205
Tag :: Compression => {
203
206
compression = Some ( CompressionMethod :: from_u16_exhaustive ( value. into_u16 ( ) ?) )
204
207
}
@@ -241,12 +244,11 @@ impl ImageFileDirectory {
241
244
Tag :: ExtraSamples => extra_samples = Some ( value. into_u16_vec ( ) ?) ,
242
245
Tag :: SampleFormat => {
243
246
let values = value. into_u16_vec ( ) ?;
244
- sample_format = Some (
245
- values
246
- . into_iter ( )
247
- . map ( SampleFormat :: from_u16_exhaustive)
248
- . collect ( ) ,
249
- ) ;
247
+ // we don't support mixed sample formats
248
+ if values. iter ( ) . any ( |v| * v!= values[ 0 ] ) {
249
+ return Err ( TiffError :: UnsupportedError ( crate :: tiff:: TiffUnsupportedError :: UnsupportedSampleFormat ( values. iter ( ) . map ( |v| SampleFormat :: from_u16_exhaustive ( * v) ) . collect ( ) ) ) ) ;
250
+ }
251
+ sample_format = Some ( values. iter ( ) . map ( |v| SampleFormat :: from_u16_exhaustive ( * v) ) . collect ( ) ) ;
250
252
}
251
253
Tag :: JPEGTables => jpeg_tables = Some ( value. into_u8_vec ( ) ?. into ( ) ) ,
252
254
Tag :: Copyright => copyright = Some ( value. into_string ( ) ?) ,
@@ -681,30 +683,6 @@ impl ImageFileDirectory {
681
683
Some ( offset as _ ..( offset + byte_count) as _ )
682
684
}
683
685
684
- fn get_predictor_info ( & self ) -> PredictorInfo {
685
- PredictorInfo {
686
- endianness : self . endianness ,
687
- image_width : self . image_width ,
688
- image_height : self . image_height ,
689
- chunk_width : if self . tile_width . is_none ( ) {
690
- // we are stripped => image_width
691
- self . image_width
692
- } else {
693
- self . tile_width . unwrap ( )
694
- } ,
695
- chunk_height : if self . tile_height . is_none ( ) {
696
- self . rows_per_strip
697
- . expect ( "no tile height and no rows_per_strip" )
698
- } else {
699
- self . tile_height . unwrap ( )
700
- } ,
701
- bits_per_sample : & self . bits_per_sample ,
702
- samples_per_pixel : self . samples_per_pixel ,
703
- sample_format : & self . sample_format ,
704
- planar_configuration : self . planar_configuration ,
705
- }
706
- }
707
-
708
686
/// Fetch the tile located at `x` column and `y` row using the provided reader.
709
687
pub async fn fetch_tile (
710
688
& self ,
@@ -719,12 +697,7 @@ impl ImageFileDirectory {
719
697
Ok ( Tile {
720
698
x,
721
699
y,
722
- predictor : self . predictor . unwrap_or ( Predictor :: None ) ,
723
- predictor_info : self . get_predictor_info ( ) ,
724
700
compressed_bytes,
725
- compression_method : self . compression ,
726
- photometric_interpretation : self . photometric_interpretation ,
727
- jpeg_tables : self . jpeg_tables . clone ( ) ,
728
701
} )
729
702
}
730
703
@@ -756,12 +729,7 @@ impl ImageFileDirectory {
756
729
let tile = Tile {
757
730
x,
758
731
y,
759
- predictor : self . predictor . unwrap_or ( Predictor :: None ) ,
760
- predictor_info : self . get_predictor_info ( ) ,
761
732
compressed_bytes,
762
- compression_method : self . compression ,
763
- photometric_interpretation : self . photometric_interpretation ,
764
- jpeg_tables : self . jpeg_tables . clone ( ) ,
765
733
} ;
766
734
tiles. push ( tile) ;
767
735
}
@@ -775,4 +743,132 @@ impl ImageFileDirectory {
775
743
let y_count = ( self . image_height as f64 / self . tile_height ? as f64 ) . ceil ( ) ;
776
744
Some ( ( x_count as usize , y_count as usize ) )
777
745
}
746
+
747
+
748
+ /// width of a chunk (strip or tile)
749
+ ///
750
+ /// In case of tile, this is [`Tag::TileWidth`], otherwise [`Tag::ImageWidth`]
751
+ pub fn chunk_width ( & self ) -> u32 {
752
+ if self . tile_width . is_none ( ) {
753
+ // we are stripped => image_width
754
+ self . image_width
755
+ } else {
756
+ self . tile_width . unwrap ( )
757
+ }
758
+ }
759
+
760
+ /// Height of a chunk (strip or tile)
761
+ ///
762
+ /// in case of tile, this is [`Tag::TileLength`], otherwise [`Tag::RowsPerStrip`]
763
+ ///
764
+ /// # Panics
765
+ ///
766
+ /// if neither `tile_height` or `rows_per_strip` is found
767
+ pub fn chunk_height ( & self ) -> u32 {
768
+ if self . tile_height . is_none ( ) {
769
+ self . rows_per_strip
770
+ . expect ( "no tile height and no rows_per_strip" )
771
+ } else {
772
+ self . tile_height . unwrap ( )
773
+ }
774
+ }
775
+
776
+ /// chunk width in pixels, taking padding into account
777
+ ///
778
+ /// strips are considered image-width chunks
779
+ pub fn chunk_width_pixels ( & self , x : u32 ) -> AsyncTiffResult < u32 > {
780
+ if x >= self . chunks_across ( ) {
781
+ Err ( crate :: error:: AsyncTiffError :: TileIndexError (
782
+ x,
783
+ self . chunks_across ( ) ,
784
+ ) )
785
+ } else if x == self . chunks_across ( ) - 1 {
786
+ // last chunk
787
+ Ok ( self . image_width - self . chunk_width ( ) * x)
788
+ } else {
789
+ Ok ( self . chunk_width ( ) )
790
+ }
791
+ }
792
+
793
+ /// chunk height in pixels, taking padding into account
794
+ pub fn chunk_height_pixels ( & self , y : u32 ) -> AsyncTiffResult < u32 > {
795
+ if y >= self . chunks_down ( ) {
796
+ Err ( crate :: error:: AsyncTiffError :: TileIndexError (
797
+ y,
798
+ self . chunks_down ( ) ,
799
+ ) )
800
+ } else if y == self . chunks_down ( ) - 1 {
801
+ // last chunk
802
+ Ok ( self . image_height - self . chunk_height ( ) * y)
803
+ } else {
804
+ Ok ( self . chunk_height ( ) )
805
+ }
806
+ }
807
+
808
+ /// get the output row stride in bytes, taking padding into account
809
+ pub fn output_row_stride ( & self , x : u32 ) -> AsyncTiffResult < usize > {
810
+ Ok ( ( self . chunk_width_pixels ( x) ? as usize ) . saturating_mul ( self . bits_per_pixel ( ) ) / 8 )
811
+ }
812
+
813
+ /// The total number of bits per pixel.
814
+ ///
815
+ /// Technically bits_per_sample.len() should be *equal* to samples, but libtiff also allows
816
+ /// it to be a single value that applies to all samples.
817
+ pub fn bits_per_pixel ( & self ) -> usize {
818
+ match self . planar_configuration {
819
+ PlanarConfiguration :: Chunky => {
820
+ self . samples_per_pixel as usize * self . bits_per_sample [ 0 ] as usize
821
+ }
822
+ PlanarConfiguration :: Planar => self . bits_per_sample [ 0 ] as usize ,
823
+ }
824
+ }
825
+
826
+ /// The number of chunks in the horizontal (x) direction
827
+ pub fn chunks_across ( & self ) -> u32 {
828
+ self . image_width . div_ceil ( self . chunk_width ( ) )
829
+ }
830
+
831
+ /// The number of chunks in the vertical (y) direction
832
+ pub fn chunks_down ( & self ) -> u32 {
833
+ self . image_height . div_ceil ( self . chunk_height ( ) )
834
+ }
835
+ }
836
+
837
+
838
+ #[ cfg( test) ]
839
+ mod test {
840
+ use std:: collections:: HashMap ;
841
+
842
+ use crate :: {
843
+ reader:: Endianness ,
844
+ tiff:: { tags:: { PlanarConfiguration , Tag , PhotometricInterpretation } , Value } , ImageFileDirectory ,
845
+ } ;
846
+
847
+ // use super::PredictorInfo;
848
+
849
+ #[ test]
850
+ fn test_chunk_width_pixels ( ) {
851
+ let mut hmap = HashMap :: new ( ) ;
852
+ hmap. insert ( Tag :: ImageWidth , Value :: Unsigned ( 15 ) ) ;
853
+ hmap. insert ( Tag :: ImageLength , Value :: Unsigned ( 17 ) ) ;
854
+ hmap. insert ( Tag :: TileWidth , Value :: Unsigned ( 8 ) ) ;
855
+ hmap. insert ( Tag :: TileLength , Value :: Unsigned ( 8 ) ) ;
856
+ hmap. insert ( Tag :: BitsPerSample , Value :: Short ( 8 ) ) ;
857
+ hmap. insert ( Tag :: SamplesPerPixel , Value :: Short ( 1 ) ) ;
858
+ hmap. insert ( Tag :: PlanarConfiguration , Value :: Short ( PlanarConfiguration :: Chunky . to_u16 ( ) ) ) ;
859
+ hmap. insert ( Tag :: PhotometricInterpretation , Value :: Short ( PhotometricInterpretation :: BlackIsZero . to_u16 ( ) ) ) ;
860
+ let info = ImageFileDirectory :: from_tags ( hmap, Endianness :: LittleEndian ) . unwrap ( ) ;
861
+ assert_eq ! ( info. bits_per_pixel( ) , 8 ) ;
862
+ assert_eq ! ( info. chunks_across( ) , 2 ) ;
863
+ assert_eq ! ( info. chunks_down( ) , 3 ) ;
864
+ assert_eq ! ( info. chunk_width_pixels( 0 ) . unwrap( ) , info. chunk_width( ) ) ;
865
+ assert_eq ! ( info. chunk_width_pixels( 1 ) . unwrap( ) , 7 ) ;
866
+ info. chunk_width_pixels ( 2 ) . unwrap_err ( ) ;
867
+ assert_eq ! ( info. chunk_height_pixels( 0 ) . unwrap( ) , info. chunk_height( ) ) ;
868
+ assert_eq ! ( info. chunk_height_pixels( 2 ) . unwrap( ) , 1 ) ;
869
+ info. chunk_height_pixels ( 3 ) . unwrap_err ( ) ;
870
+ assert_eq ! ( info. output_row_stride( 0 ) . unwrap( ) , info. chunk_width( ) as _) ; //single-byte samples
871
+ assert_eq ! ( info. output_row_stride( 1 ) . unwrap( ) , 7 ) ;
872
+ info. output_row_stride ( 2 ) . unwrap_err ( ) ;
873
+ }
778
874
}
0 commit comments