@@ -4,7 +4,185 @@ use std::fmt::Debug;
4
4
use bytes:: { Bytes , BytesMut } ;
5
5
// use tiff::decoder::DecodingResult;
6
6
7
- use crate :: { error:: AsyncTiffResult , reader:: Endianness , tile:: PredictorInfo } ;
7
+ use crate :: ImageFileDirectory ;
8
+ use crate :: { error:: AsyncTiffResult , reader:: Endianness } ;
9
+
10
+ /// All info that may be used by a predictor
11
+ ///
12
+ /// Most of this is used by the floating point predictor
13
+ /// since that intermixes padding into the decompressed output
14
+ ///
15
+ /// Also provides convenience functions
16
+ ///
17
+ #[ derive( Debug , Clone , Copy ) ]
18
+ pub ( crate ) struct PredictorInfo {
19
+ /// endianness
20
+ endianness : Endianness ,
21
+ /// width of the image in pixels
22
+ image_width : u32 ,
23
+ /// height of the image in pixels
24
+ image_height : u32 ,
25
+ /// chunk width in pixels
26
+ ///
27
+ /// If this is a stripped tiff, `chunk_width=image_width`
28
+ chunk_width : u32 ,
29
+ /// chunk height in pixels
30
+ chunk_height : u32 ,
31
+ /// bits per sample
32
+ ///
33
+ /// We only support a single bits_per_sample across all samples
34
+ bits_per_sample : u16 ,
35
+ /// number of samples per pixel
36
+ samples_per_pixel : u16 ,
37
+ }
38
+
39
+ impl PredictorInfo {
40
+ pub ( crate ) fn from_ifd ( ifd : & ImageFileDirectory ) -> Self {
41
+ if !ifd. bits_per_sample . windows ( 2 ) . all ( |w| w[ 0 ] == w[ 1 ] ) {
42
+ panic ! ( "bits_per_sample should be the same for all channels" ) ;
43
+ }
44
+
45
+ let chunk_width = if let Some ( tile_width) = ifd. tile_width {
46
+ tile_width
47
+ } else {
48
+ ifd. image_width
49
+ } ;
50
+ let chunk_height = if let Some ( tile_height) = ifd. tile_height {
51
+ tile_height
52
+ } else {
53
+ ifd. rows_per_strip
54
+ . expect ( "no tile height and no rows_per_strip" )
55
+ } ;
56
+
57
+ PredictorInfo {
58
+ endianness : ifd. endianness ,
59
+ image_width : ifd. image_width ,
60
+ image_height : ifd. image_height ,
61
+ chunk_width,
62
+ chunk_height,
63
+ // TODO: validate this? Restore handling for different PlanarConfiguration?
64
+ bits_per_sample : ifd. bits_per_sample [ 0 ] ,
65
+ samples_per_pixel : ifd. samples_per_pixel ,
66
+ }
67
+ }
68
+
69
+ /// chunk width in pixels, taking padding into account
70
+ ///
71
+ /// strips are considered image-width chunks
72
+ ///
73
+ /// # Example
74
+ ///
75
+ /// ```rust
76
+ /// # use async_tiff::tiff::tags::{SampleFormat, PlanarConfiguration};
77
+ /// # use async_tiff::reader::Endianness;
78
+ /// # use async_tiff::PredictorInfo;
79
+ /// let info = PredictorInfo {
80
+ /// # endianness: Endianness::LittleEndian,
81
+ /// image_width: 15,
82
+ /// image_height: 15,
83
+ /// chunk_width: 8,
84
+ /// chunk_height: 8,
85
+ /// # bits_per_sample: &[32],
86
+ /// # samples_per_pixel: 1,
87
+ /// # sample_format: &[SampleFormat::IEEEFP],
88
+ /// # planar_configuration: PlanarConfiguration::Chunky,
89
+ /// };
90
+ ///
91
+ /// assert_eq!(info.chunk_width_pixels(0).unwrap(), (8));
92
+ /// assert_eq!(info.chunk_width_pixels(1).unwrap(), (7));
93
+ /// info.chunk_width_pixels(2).unwrap_err();
94
+ /// ```
95
+ fn chunk_width_pixels ( & self , x : u32 ) -> AsyncTiffResult < u32 > {
96
+ if x >= self . chunks_across ( ) {
97
+ Err ( crate :: error:: AsyncTiffError :: TileIndexError (
98
+ x,
99
+ self . chunks_across ( ) ,
100
+ ) )
101
+ } else if x == self . chunks_across ( ) - 1 {
102
+ // last chunk
103
+ Ok ( self . image_width - self . chunk_width * x)
104
+ } else {
105
+ Ok ( self . chunk_width )
106
+ }
107
+ }
108
+
109
+ /// chunk height in pixels, taking padding into account
110
+ ///
111
+ /// strips are considered image-width chunks
112
+ ///
113
+ /// # Example
114
+ ///
115
+ /// ```rust
116
+ /// # use async_tiff::tiff::tags::{SampleFormat, PlanarConfiguration};
117
+ /// # use async_tiff::reader::Endianness;
118
+ /// # use async_tiff::PredictorInfo;
119
+ /// let info = PredictorInfo {
120
+ /// # endianness: Endianness::LittleEndian,
121
+ /// image_width: 15,
122
+ /// image_height: 15,
123
+ /// chunk_width: 8,
124
+ /// chunk_height: 8,
125
+ /// # bits_per_sample: &[32],
126
+ /// # samples_per_pixel: 1,
127
+ /// # sample_format: &[SampleFormat::IEEEFP],
128
+ /// # planar_configuration: PlanarConfiguration::Chunky,
129
+ /// };
130
+ ///
131
+ /// assert_eq!(info.chunk_height_pixels(0).unwrap(), (8));
132
+ /// assert_eq!(info.chunk_height_pixels(1).unwrap(), (7));
133
+ /// info.chunk_height_pixels(2).unwrap_err();
134
+ /// ```
135
+ pub fn chunk_height_pixels ( & self , y : u32 ) -> AsyncTiffResult < u32 > {
136
+ if y >= self . chunks_down ( ) {
137
+ Err ( crate :: error:: AsyncTiffError :: TileIndexError (
138
+ y,
139
+ self . chunks_down ( ) ,
140
+ ) )
141
+ } else if y == self . chunks_down ( ) - 1 {
142
+ // last chunk
143
+ Ok ( self . image_height - self . chunk_height * y)
144
+ } else {
145
+ Ok ( self . chunk_height )
146
+ }
147
+ }
148
+
149
+ /// get the output row stride in bytes, taking padding into account
150
+ pub fn output_row_stride ( & self , x : u32 ) -> AsyncTiffResult < usize > {
151
+ Ok ( ( self . chunk_width_pixels ( x) ? as usize ) . saturating_mul ( self . bits_per_sample as _ ) / 8 )
152
+ }
153
+
154
+ // /// The total number of bits per pixel, taking into account possible different sample sizes
155
+ // ///
156
+ // /// Technically bits_per_sample.len() should be *equal* to samples, but libtiff also allows
157
+ // /// it to be a single value that applies to all samples.
158
+ // ///
159
+ // /// Libtiff and image-tiff do not support mixed bits per sample, but we give the possibility
160
+ // /// unless you also have PlanarConfiguration::Planar, at which point the first is taken
161
+ // pub fn bits_per_pixel(&self) -> usize {
162
+ // self.bits_per_sample
163
+ // match self.planar_configuration {
164
+ // PlanarConfiguration::Chunky => {
165
+ // if self.bits_per_sample.len() == 1 {
166
+ // self.samples_per_pixel as usize * self.bits_per_sample[0] as usize
167
+ // } else {
168
+ // assert_eq!(self.samples_per_pixel as usize, self.bits_per_sample.len());
169
+ // self.bits_per_sample.iter().map(|v| *v as usize).product()
170
+ // }
171
+ // }
172
+ // PlanarConfiguration::Planar => self.bits_per_sample[0] as usize,
173
+ // }
174
+ // }
175
+
176
+ /// The number of chunks in the horizontal (x) direction
177
+ pub fn chunks_across ( & self ) -> u32 {
178
+ self . image_width . div_ceil ( self . chunk_width )
179
+ }
180
+
181
+ /// The number of chunks in the vertical (y) direction
182
+ pub fn chunks_down ( & self ) -> u32 {
183
+ self . image_height . div_ceil ( self . chunk_height )
184
+ }
185
+ }
8
186
9
187
/// Trait for reverse predictors to implement
10
188
pub ( crate ) trait Unpredict : Debug + Send + Sync {
@@ -284,9 +462,9 @@ mod test {
284
462
285
463
use bytes:: Bytes ;
286
464
287
- use crate :: { predictor:: FloatingPointPredictor , reader:: Endianness , tile :: PredictorInfo } ;
465
+ use crate :: { predictor:: FloatingPointPredictor , reader:: Endianness } ;
288
466
289
- use super :: { HorizontalPredictor , NoPredictor , Unpredict } ;
467
+ use super :: * ;
290
468
291
469
const PRED_INFO : PredictorInfo = PredictorInfo {
292
470
endianness : Endianness :: LittleEndian ,
@@ -328,6 +506,27 @@ mod test {
328
506
2 , 1 , 0 ,
329
507
] ;
330
508
509
+ #[ test]
510
+ fn test_chunk_width_pixels ( ) {
511
+ let info = PredictorInfo {
512
+ endianness : Endianness :: LittleEndian ,
513
+ image_width : 15 ,
514
+ image_height : 17 ,
515
+ chunk_width : 8 ,
516
+ chunk_height : 8 ,
517
+ bits_per_sample : 8 ,
518
+ samples_per_pixel : 1 ,
519
+ } ;
520
+ assert_eq ! ( info. chunks_across( ) , 2 ) ;
521
+ assert_eq ! ( info. chunks_down( ) , 3 ) ;
522
+ assert_eq ! ( info. chunk_width_pixels( 0 ) . unwrap( ) , info. chunk_width) ;
523
+ assert_eq ! ( info. chunk_width_pixels( 1 ) . unwrap( ) , 7 ) ;
524
+ info. chunk_width_pixels ( 2 ) . unwrap_err ( ) ;
525
+ assert_eq ! ( info. chunk_height_pixels( 0 ) . unwrap( ) , info. chunk_height) ;
526
+ assert_eq ! ( info. chunk_height_pixels( 2 ) . unwrap( ) , 1 ) ;
527
+ info. chunk_height_pixels ( 3 ) . unwrap_err ( ) ;
528
+ }
529
+
331
530
#[ rustfmt:: skip]
332
531
#[ test]
333
532
fn test_no_predict ( ) {
0 commit comments