@@ -12,7 +12,7 @@ use crate::tiff::tags::{
1212 SampleFormat , Tag ,
1313} ;
1414use crate :: tiff:: { TiffError , Value } ;
15- use crate :: tile:: { PredictorInfo , Tile } ;
15+ use crate :: tile:: Tile ;
1616
1717const DOCUMENT_NAME : u16 = 269 ;
1818
@@ -190,15 +190,18 @@ impl ImageFileDirectory {
190190
191191 let mut other_tags = HashMap :: new ( ) ;
192192
193- // for x in tag_data.into_iter() {
194-
195- // }
196193 tag_data. into_iter ( ) . try_for_each ( |( tag, value) | {
197194 match tag {
198195 Tag :: NewSubfileType => new_subfile_type = Some ( value. into_u32 ( ) ?) ,
199196 Tag :: ImageWidth => image_width = Some ( value. into_u32 ( ) ?) ,
200197 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+ } ) ,
202205 Tag :: Compression => {
203206 compression = Some ( CompressionMethod :: from_u16_exhaustive ( value. into_u16 ( ) ?) )
204207 }
@@ -241,12 +244,11 @@ impl ImageFileDirectory {
241244 Tag :: ExtraSamples => extra_samples = Some ( value. into_u16_vec ( ) ?) ,
242245 Tag :: SampleFormat => {
243246 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 ( ) ) ;
250252 }
251253 Tag :: JPEGTables => jpeg_tables = Some ( value. into_u8_vec ( ) ?. into ( ) ) ,
252254 Tag :: Copyright => copyright = Some ( value. into_string ( ) ?) ,
@@ -681,30 +683,6 @@ impl ImageFileDirectory {
681683 Some ( offset as _ ..( offset + byte_count) as _ )
682684 }
683685
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-
708686 /// Fetch the tile located at `x` column and `y` row using the provided reader.
709687 pub async fn fetch_tile (
710688 & self ,
@@ -719,12 +697,7 @@ impl ImageFileDirectory {
719697 Ok ( Tile {
720698 x,
721699 y,
722- predictor : self . predictor . unwrap_or ( Predictor :: None ) ,
723- predictor_info : self . get_predictor_info ( ) ,
724700 compressed_bytes,
725- compression_method : self . compression ,
726- photometric_interpretation : self . photometric_interpretation ,
727- jpeg_tables : self . jpeg_tables . clone ( ) ,
728701 } )
729702 }
730703
@@ -756,12 +729,7 @@ impl ImageFileDirectory {
756729 let tile = Tile {
757730 x,
758731 y,
759- predictor : self . predictor . unwrap_or ( Predictor :: None ) ,
760- predictor_info : self . get_predictor_info ( ) ,
761732 compressed_bytes,
762- compression_method : self . compression ,
763- photometric_interpretation : self . photometric_interpretation ,
764- jpeg_tables : self . jpeg_tables . clone ( ) ,
765733 } ;
766734 tiles. push ( tile) ;
767735 }
@@ -775,4 +743,132 @@ impl ImageFileDirectory {
775743 let y_count = ( self . image_height as f64 / self . tile_height ? as f64 ) . ceil ( ) ;
776744 Some ( ( x_count as usize , y_count as usize ) )
777745 }
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+ }
778874}
0 commit comments