/rust/registry/src/index.crates.io-1949cf8c6b5b557f/tiff-0.10.3/src/decoder/image.rs
Line | Count | Source |
1 | | use super::ifd::Value; |
2 | | use super::stream::PackBitsReader; |
3 | | use super::tag_reader::TagReader; |
4 | | use super::ChunkType; |
5 | | use super::{predict_f16, predict_f32, predict_f64, ValueReader}; |
6 | | use crate::tags::{ |
7 | | CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, SampleFormat, Tag, |
8 | | }; |
9 | | use crate::{ |
10 | | ColorType, Directory, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError, |
11 | | }; |
12 | | |
13 | | use std::io::{self, Cursor, Read, Seek}; |
14 | | use std::num::NonZeroUsize; |
15 | | use std::sync::Arc; |
16 | | |
17 | | #[derive(Debug)] |
18 | | pub(crate) struct StripDecodeState { |
19 | | pub rows_per_strip: u32, |
20 | | } |
21 | | |
22 | | #[derive(Debug)] |
23 | | /// Computed values useful for tile decoding |
24 | | pub(crate) struct TileAttributes { |
25 | | pub image_width: usize, |
26 | | pub image_height: usize, |
27 | | |
28 | | pub tile_width: usize, |
29 | | pub tile_length: usize, |
30 | | } |
31 | | |
32 | | impl TileAttributes { |
33 | 36.3k | pub fn tiles_across(&self) -> usize { |
34 | 36.3k | self.image_width.div_ceil(self.tile_width) |
35 | 36.3k | } |
36 | 13.2k | pub fn tiles_down(&self) -> usize { |
37 | 13.2k | self.image_height.div_ceil(self.tile_length) |
38 | 13.2k | } |
39 | 1.81k | fn padding_right(&self) -> usize { |
40 | 1.81k | (self.tile_width - self.image_width % self.tile_width) % self.tile_width |
41 | 1.81k | } |
42 | 1.69k | fn padding_down(&self) -> usize { |
43 | 1.69k | (self.tile_length - self.image_height % self.tile_length) % self.tile_length |
44 | 1.69k | } |
45 | 11.5k | pub fn get_padding(&self, tile: usize) -> (usize, usize) { |
46 | 11.5k | let row = tile / self.tiles_across(); |
47 | 11.5k | let column = tile % self.tiles_across(); |
48 | | |
49 | 11.5k | let padding_right = if column == self.tiles_across() - 1 { |
50 | 1.81k | self.padding_right() |
51 | | } else { |
52 | 9.74k | 0 |
53 | | }; |
54 | | |
55 | 11.5k | let padding_down = if row == self.tiles_down() - 1 { |
56 | 1.69k | self.padding_down() |
57 | | } else { |
58 | 9.85k | 0 |
59 | | }; |
60 | | |
61 | 11.5k | (padding_right, padding_down) |
62 | 11.5k | } |
63 | | } |
64 | | |
65 | | #[derive(Debug)] |
66 | | pub(crate) struct Image { |
67 | | pub ifd: Option<Directory>, |
68 | | pub width: u32, |
69 | | pub height: u32, |
70 | | pub bits_per_sample: u8, |
71 | | pub samples: u16, |
72 | | pub sample_format: SampleFormat, |
73 | | pub photometric_interpretation: PhotometricInterpretation, |
74 | | pub compression_method: CompressionMethod, |
75 | | pub predictor: Predictor, |
76 | | pub jpeg_tables: Option<Arc<Vec<u8>>>, |
77 | | pub chunk_type: ChunkType, |
78 | | pub planar_config: PlanarConfiguration, |
79 | | pub strip_decoder: Option<StripDecodeState>, |
80 | | pub tile_attributes: Option<TileAttributes>, |
81 | | pub chunk_offsets: Vec<u64>, |
82 | | pub chunk_bytes: Vec<u64>, |
83 | | } |
84 | | |
85 | | impl Image { |
86 | 8.59k | pub fn from_reader<R: Read + Seek>( |
87 | 8.59k | decoder: &mut ValueReader<R>, |
88 | 8.59k | ifd: Directory, |
89 | 8.59k | ) -> TiffResult<Image> { |
90 | 8.59k | let mut tag_reader = TagReader { decoder, ifd: &ifd }; |
91 | | |
92 | 8.59k | let width = tag_reader.require_tag(Tag::ImageWidth)?.into_u32()?; |
93 | 8.43k | let height = tag_reader.require_tag(Tag::ImageLength)?.into_u32()?; |
94 | 8.40k | if width == 0 || height == 0 { |
95 | 0 | return Err(TiffError::FormatError(TiffFormatError::InvalidDimensions( |
96 | 0 | width, height, |
97 | 0 | ))); |
98 | 8.40k | } |
99 | | |
100 | 8.40k | let photometric_interpretation = tag_reader |
101 | 8.40k | .find_tag(Tag::PhotometricInterpretation)? |
102 | 8.39k | .map(Value::into_u16) |
103 | 8.39k | .transpose()? |
104 | 8.33k | .and_then(PhotometricInterpretation::from_u16) |
105 | 8.33k | .ok_or(TiffUnsupportedError::UnknownInterpretation)?; |
106 | | |
107 | | // Try to parse both the compression method and the number, format, and bits of the included samples. |
108 | | // If they are not explicitly specified, those tags are reset to their default values and not carried from previous images. |
109 | 8.32k | let compression_method = match tag_reader.find_tag(Tag::Compression)? { |
110 | 7.94k | Some(val) => CompressionMethod::from_u16_exhaustive(val.into_u16()?), |
111 | 379 | None => CompressionMethod::None, |
112 | | }; |
113 | | |
114 | 8.32k | let jpeg_tables = if compression_method == CompressionMethod::ModernJPEG |
115 | 5.17k | && ifd.contains(Tag::JPEGTables) |
116 | | { |
117 | 1.25k | let vec = tag_reader |
118 | 1.25k | .find_tag(Tag::JPEGTables)? |
119 | 1.24k | .unwrap() |
120 | 1.24k | .into_u8_vec()?; |
121 | 1.24k | if vec.len() < 2 { |
122 | 2 | return Err(TiffError::FormatError( |
123 | 2 | TiffFormatError::InvalidTagValueType(Tag::JPEGTables), |
124 | 2 | )); |
125 | 1.24k | } |
126 | | |
127 | 1.24k | Some(Arc::new(vec)) |
128 | | } else { |
129 | 7.06k | None |
130 | | }; |
131 | | |
132 | 8.31k | let samples: u16 = tag_reader |
133 | 8.31k | .find_tag(Tag::SamplesPerPixel)? |
134 | 8.30k | .map(Value::into_u16) |
135 | 8.30k | .transpose()? |
136 | 8.30k | .unwrap_or(1); |
137 | 8.30k | if samples == 0 { |
138 | 0 | return Err(TiffFormatError::SamplesPerPixelIsZero.into()); |
139 | 8.30k | } |
140 | | |
141 | 8.30k | let sample_format = match tag_reader.find_tag_uint_vec(Tag::SampleFormat)? { |
142 | 1.47k | Some(vals) => { |
143 | 1.47k | let sample_format: Vec<_> = vals |
144 | 1.47k | .into_iter() |
145 | 1.47k | .map(SampleFormat::from_u16_exhaustive) |
146 | 1.47k | .collect(); |
147 | | |
148 | | // TODO: for now, only homogenous formats across samples are supported. |
149 | 76.9k | if !sample_format.windows(2).all(|s| s[0] == s[1]) {Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<_>::{closure#0}<tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Line | Count | Source | 149 | 465 | if !sample_format.windows(2).all(|s| s[0] == s[1]) { |
Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#0}<tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Line | Count | Source | 149 | 76.5k | if !sample_format.windows(2).all(|s| s[0] == s[1]) { |
|
150 | 77 | return Err(TiffUnsupportedError::UnsupportedSampleFormat(sample_format).into()); |
151 | 1.39k | } |
152 | | |
153 | 1.39k | sample_format[0] |
154 | | } |
155 | 6.76k | None => SampleFormat::Uint, |
156 | | }; |
157 | | |
158 | 8.16k | let bits_per_sample: Vec<u8> = tag_reader |
159 | 8.16k | .find_tag_uint_vec(Tag::BitsPerSample)? |
160 | 8.06k | .unwrap_or_else(|| vec![1]); Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#1}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<_>::{closure#1}<tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#1}Line | Count | Source | 160 | 460 | .unwrap_or_else(|| vec![1]); |
Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#1}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#1}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#1}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#1}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#1}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#1}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#1}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#1}<tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#1}Line | Count | Source | 160 | 2.51k | .unwrap_or_else(|| vec![1]); |
|
161 | | |
162 | | // Technically bits_per_sample.len() should be *equal* to samples, but libtiff also allows |
163 | | // it to be a single value that applies to all samples. |
164 | 8.06k | if bits_per_sample.len() != samples.into() && bits_per_sample.len() != 1 { |
165 | 19 | return Err(TiffError::FormatError( |
166 | 19 | TiffFormatError::InconsistentSizesEncountered, |
167 | 19 | )); |
168 | 8.04k | } |
169 | | |
170 | | // This library (and libtiff) do not support mixed sample formats and zero bits per sample |
171 | | // doesn't make sense. |
172 | 149k | if bits_per_sample.iter().any(|&b| b != bits_per_sample[0]) || bits_per_sample[0] == 0 {Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#2}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<_>::{closure#2}<tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#2}Line | Count | Source | 172 | 4.08k | if bits_per_sample.iter().any(|&b| b != bits_per_sample[0]) || bits_per_sample[0] == 0 { |
Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#2}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#2}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#2}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#2}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#2}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#2}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#2}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#2}<tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#2}Line | Count | Source | 172 | 145k | if bits_per_sample.iter().any(|&b| b != bits_per_sample[0]) || bits_per_sample[0] == 0 { |
|
173 | 9 | return Err(TiffUnsupportedError::InconsistentBitsPerSample(bits_per_sample).into()); |
174 | 8.03k | } |
175 | | |
176 | 8.03k | let predictor = tag_reader |
177 | 8.03k | .find_tag(Tag::Predictor)? |
178 | 8.03k | .map(Value::into_u16) |
179 | 8.03k | .transpose()? |
180 | 8.03k | .map(|p| { |
181 | 1.25k | Predictor::from_u16(p) |
182 | 1.25k | .ok_or(TiffError::FormatError(TiffFormatError::UnknownPredictor(p))) |
183 | 1.25k | }) Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#3}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<_>::{closure#3}<tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#3}Line | Count | Source | 180 | 341 | .map(|p| { | 181 | 341 | Predictor::from_u16(p) | 182 | 341 | .ok_or(TiffError::FormatError(TiffFormatError::UnknownPredictor(p))) | 183 | 341 | }) |
Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#3}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#3}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#3}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#3}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#3}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#3}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#3}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#3}<tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#3}Line | Count | Source | 180 | 911 | .map(|p| { | 181 | 911 | Predictor::from_u16(p) | 182 | 911 | .ok_or(TiffError::FormatError(TiffFormatError::UnknownPredictor(p))) | 183 | 911 | }) |
|
184 | 8.03k | .transpose()? |
185 | 8.02k | .unwrap_or(Predictor::None); |
186 | | |
187 | 8.02k | let planar_config = tag_reader |
188 | 8.02k | .find_tag(Tag::PlanarConfiguration)? |
189 | 8.02k | .map(Value::into_u16) |
190 | 8.02k | .transpose()? |
191 | 8.02k | .map(|p| { |
192 | 1.71k | PlanarConfiguration::from_u16(p).ok_or(TiffError::FormatError( |
193 | 1.71k | TiffFormatError::UnknownPlanarConfiguration(p), |
194 | 1.71k | )) |
195 | 1.71k | }) Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#4}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<_>::{closure#4}<tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#4}Line | Count | Source | 191 | 478 | .map(|p| { | 192 | 478 | PlanarConfiguration::from_u16(p).ok_or(TiffError::FormatError( | 193 | 478 | TiffFormatError::UnknownPlanarConfiguration(p), | 194 | 478 | )) | 195 | 478 | }) |
Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#4}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#4}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#4}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#4}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#4}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#4}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#4}Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#4}<tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>>::{closure#4}Line | Count | Source | 191 | 1.23k | .map(|p| { | 192 | 1.23k | PlanarConfiguration::from_u16(p).ok_or(TiffError::FormatError( | 193 | 1.23k | TiffFormatError::UnknownPlanarConfiguration(p), | 194 | 1.23k | )) | 195 | 1.23k | }) |
|
196 | 8.02k | .transpose()? |
197 | 8.02k | .unwrap_or(PlanarConfiguration::Chunky); |
198 | | |
199 | 8.02k | let planes = match planar_config { |
200 | 8.01k | PlanarConfiguration::Chunky => 1, |
201 | 4 | PlanarConfiguration::Planar => samples, |
202 | | }; |
203 | | |
204 | | let chunk_type; |
205 | | let chunk_offsets; |
206 | | let chunk_bytes; |
207 | | let strip_decoder; |
208 | | let tile_attributes; |
209 | 8.02k | match ( |
210 | 8.02k | ifd.contains(Tag::StripByteCounts), |
211 | 8.02k | ifd.contains(Tag::StripOffsets), |
212 | 8.02k | ifd.contains(Tag::TileByteCounts), |
213 | 8.02k | ifd.contains(Tag::TileOffsets), |
214 | 8.02k | ) { |
215 | | (true, true, false, false) => { |
216 | 6.25k | chunk_type = ChunkType::Strip; |
217 | | |
218 | 6.25k | chunk_offsets = tag_reader |
219 | 6.25k | .find_tag(Tag::StripOffsets)? |
220 | 6.24k | .unwrap() |
221 | 6.24k | .into_u64_vec()?; |
222 | 6.24k | chunk_bytes = tag_reader |
223 | 6.24k | .find_tag(Tag::StripByteCounts)? |
224 | 6.20k | .unwrap() |
225 | 6.20k | .into_u64_vec()?; |
226 | 6.20k | let rows_per_strip = tag_reader |
227 | 6.20k | .find_tag(Tag::RowsPerStrip)? |
228 | 6.19k | .map(Value::into_u32) |
229 | 6.19k | .transpose()? |
230 | 6.19k | .unwrap_or(height); |
231 | 6.19k | strip_decoder = Some(StripDecodeState { rows_per_strip }); |
232 | 6.19k | tile_attributes = None; |
233 | | |
234 | 6.19k | if chunk_offsets.len() != chunk_bytes.len() |
235 | 6.17k | || rows_per_strip == 0 |
236 | 6.17k | || u32::try_from(chunk_offsets.len())? |
237 | 6.17k | != (height.saturating_sub(1) / rows_per_strip + 1) * planes as u32 |
238 | | { |
239 | 42 | return Err(TiffError::FormatError( |
240 | 42 | TiffFormatError::InconsistentSizesEncountered, |
241 | 42 | )); |
242 | 6.14k | } |
243 | | } |
244 | | (false, false, true, true) => { |
245 | 1.73k | chunk_type = ChunkType::Tile; |
246 | | |
247 | 1.73k | let tile_width = |
248 | 1.73k | usize::try_from(tag_reader.require_tag(Tag::TileWidth)?.into_u32()?)?; |
249 | 1.73k | let tile_length = |
250 | 1.73k | usize::try_from(tag_reader.require_tag(Tag::TileLength)?.into_u32()?)?; |
251 | | |
252 | 1.73k | if tile_width == 0 { |
253 | 1 | return Err(TiffFormatError::InvalidTagValueType(Tag::TileWidth).into()); |
254 | 1.73k | } else if tile_length == 0 { |
255 | 1 | return Err(TiffFormatError::InvalidTagValueType(Tag::TileLength).into()); |
256 | 1.72k | } |
257 | | |
258 | 1.72k | strip_decoder = None; |
259 | | tile_attributes = Some(TileAttributes { |
260 | 1.72k | image_width: usize::try_from(width)?, |
261 | 1.72k | image_height: usize::try_from(height)?, |
262 | 1.72k | tile_width, |
263 | 1.72k | tile_length, |
264 | | }); |
265 | 1.72k | chunk_offsets = tag_reader |
266 | 1.72k | .find_tag(Tag::TileOffsets)? |
267 | 1.72k | .unwrap() |
268 | 1.72k | .into_u64_vec()?; |
269 | 1.72k | chunk_bytes = tag_reader |
270 | 1.72k | .find_tag(Tag::TileByteCounts)? |
271 | 1.72k | .unwrap() |
272 | 1.72k | .into_u64_vec()?; |
273 | | |
274 | 1.72k | let tile = tile_attributes.as_ref().unwrap(); |
275 | 1.72k | if chunk_offsets.len() != chunk_bytes.len() |
276 | 1.71k | || chunk_offsets.len() |
277 | 1.71k | != tile.tiles_down() * tile.tiles_across() * planes as usize |
278 | | { |
279 | 4 | return Err(TiffError::FormatError( |
280 | 4 | TiffFormatError::InconsistentSizesEncountered, |
281 | 4 | )); |
282 | 1.71k | } |
283 | | } |
284 | | (_, _, _, _) => { |
285 | 27 | return Err(TiffError::FormatError( |
286 | 27 | TiffFormatError::StripTileTagConflict, |
287 | 27 | )) |
288 | | } |
289 | | }; |
290 | | |
291 | 7.86k | Ok(Image { |
292 | 7.86k | ifd: Some(ifd), |
293 | 7.86k | width, |
294 | 7.86k | height, |
295 | 7.86k | bits_per_sample: bits_per_sample[0], |
296 | 7.86k | samples, |
297 | 7.86k | sample_format, |
298 | 7.86k | photometric_interpretation, |
299 | 7.86k | compression_method, |
300 | 7.86k | jpeg_tables, |
301 | 7.86k | predictor, |
302 | 7.86k | chunk_type, |
303 | 7.86k | planar_config, |
304 | 7.86k | strip_decoder, |
305 | 7.86k | tile_attributes, |
306 | 7.86k | chunk_offsets, |
307 | 7.86k | chunk_bytes, |
308 | 7.86k | }) |
309 | 8.59k | } Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<_> <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>> Line | Count | Source | 86 | 3.83k | pub fn from_reader<R: Read + Seek>( | 87 | 3.83k | decoder: &mut ValueReader<R>, | 88 | 3.83k | ifd: Directory, | 89 | 3.83k | ) -> TiffResult<Image> { | 90 | 3.83k | let mut tag_reader = TagReader { decoder, ifd: &ifd }; | 91 | | | 92 | 3.83k | let width = tag_reader.require_tag(Tag::ImageWidth)?.into_u32()?; | 93 | 3.72k | let height = tag_reader.require_tag(Tag::ImageLength)?.into_u32()?; | 94 | 3.71k | if width == 0 || height == 0 { | 95 | 0 | return Err(TiffError::FormatError(TiffFormatError::InvalidDimensions( | 96 | 0 | width, height, | 97 | 0 | ))); | 98 | 3.71k | } | 99 | | | 100 | 3.71k | let photometric_interpretation = tag_reader | 101 | 3.71k | .find_tag(Tag::PhotometricInterpretation)? | 102 | 3.71k | .map(Value::into_u16) | 103 | 3.71k | .transpose()? | 104 | 3.71k | .and_then(PhotometricInterpretation::from_u16) | 105 | 3.71k | .ok_or(TiffUnsupportedError::UnknownInterpretation)?; | 106 | | | 107 | | // Try to parse both the compression method and the number, format, and bits of the included samples. | 108 | | // If they are not explicitly specified, those tags are reset to their default values and not carried from previous images. | 109 | 3.71k | let compression_method = match tag_reader.find_tag(Tag::Compression)? { | 110 | 3.64k | Some(val) => CompressionMethod::from_u16_exhaustive(val.into_u16()?), | 111 | 68 | None => CompressionMethod::None, | 112 | | }; | 113 | | | 114 | 3.71k | let jpeg_tables = if compression_method == CompressionMethod::ModernJPEG | 115 | 2.82k | && ifd.contains(Tag::JPEGTables) | 116 | | { | 117 | 948 | let vec = tag_reader | 118 | 948 | .find_tag(Tag::JPEGTables)? | 119 | 946 | .unwrap() | 120 | 946 | .into_u8_vec()?; | 121 | 946 | if vec.len() < 2 { | 122 | 0 | return Err(TiffError::FormatError( | 123 | 0 | TiffFormatError::InvalidTagValueType(Tag::JPEGTables), | 124 | 0 | )); | 125 | 946 | } | 126 | | | 127 | 946 | Some(Arc::new(vec)) | 128 | | } else { | 129 | 2.76k | None | 130 | | }; | 131 | | | 132 | 3.70k | let samples: u16 = tag_reader | 133 | 3.70k | .find_tag(Tag::SamplesPerPixel)? | 134 | 3.70k | .map(Value::into_u16) | 135 | 3.70k | .transpose()? | 136 | 3.70k | .unwrap_or(1); | 137 | 3.70k | if samples == 0 { | 138 | 0 | return Err(TiffFormatError::SamplesPerPixelIsZero.into()); | 139 | 3.70k | } | 140 | | | 141 | 3.70k | let sample_format = match tag_reader.find_tag_uint_vec(Tag::SampleFormat)? { | 142 | 272 | Some(vals) => { | 143 | 272 | let sample_format: Vec<_> = vals | 144 | 272 | .into_iter() | 145 | 272 | .map(SampleFormat::from_u16_exhaustive) | 146 | 272 | .collect(); | 147 | | | 148 | | // TODO: for now, only homogenous formats across samples are supported. | 149 | 272 | if !sample_format.windows(2).all(|s| s[0] == s[1]) { | 150 | 14 | return Err(TiffUnsupportedError::UnsupportedSampleFormat(sample_format).into()); | 151 | 258 | } | 152 | | | 153 | 258 | sample_format[0] | 154 | | } | 155 | 3.42k | None => SampleFormat::Uint, | 156 | | }; | 157 | | | 158 | 3.68k | let bits_per_sample: Vec<u8> = tag_reader | 159 | 3.68k | .find_tag_uint_vec(Tag::BitsPerSample)? | 160 | 3.66k | .unwrap_or_else(|| vec![1]); | 161 | | | 162 | | // Technically bits_per_sample.len() should be *equal* to samples, but libtiff also allows | 163 | | // it to be a single value that applies to all samples. | 164 | 3.66k | if bits_per_sample.len() != samples.into() && bits_per_sample.len() != 1 { | 165 | 3 | return Err(TiffError::FormatError( | 166 | 3 | TiffFormatError::InconsistentSizesEncountered, | 167 | 3 | )); | 168 | 3.66k | } | 169 | | | 170 | | // This library (and libtiff) do not support mixed sample formats and zero bits per sample | 171 | | // doesn't make sense. | 172 | 3.66k | if bits_per_sample.iter().any(|&b| b != bits_per_sample[0]) || bits_per_sample[0] == 0 { | 173 | 2 | return Err(TiffUnsupportedError::InconsistentBitsPerSample(bits_per_sample).into()); | 174 | 3.66k | } | 175 | | | 176 | 3.66k | let predictor = tag_reader | 177 | 3.66k | .find_tag(Tag::Predictor)? | 178 | 3.66k | .map(Value::into_u16) | 179 | 3.66k | .transpose()? | 180 | 3.66k | .map(|p| { | 181 | | Predictor::from_u16(p) | 182 | | .ok_or(TiffError::FormatError(TiffFormatError::UnknownPredictor(p))) | 183 | | }) | 184 | 3.66k | .transpose()? | 185 | 3.66k | .unwrap_or(Predictor::None); | 186 | | | 187 | 3.66k | let planar_config = tag_reader | 188 | 3.66k | .find_tag(Tag::PlanarConfiguration)? | 189 | 3.65k | .map(Value::into_u16) | 190 | 3.65k | .transpose()? | 191 | 3.65k | .map(|p| { | 192 | | PlanarConfiguration::from_u16(p).ok_or(TiffError::FormatError( | 193 | | TiffFormatError::UnknownPlanarConfiguration(p), | 194 | | )) | 195 | | }) | 196 | 3.65k | .transpose()? | 197 | 3.65k | .unwrap_or(PlanarConfiguration::Chunky); | 198 | | | 199 | 3.65k | let planes = match planar_config { | 200 | 3.65k | PlanarConfiguration::Chunky => 1, | 201 | 1 | PlanarConfiguration::Planar => samples, | 202 | | }; | 203 | | | 204 | | let chunk_type; | 205 | | let chunk_offsets; | 206 | | let chunk_bytes; | 207 | | let strip_decoder; | 208 | | let tile_attributes; | 209 | 3.65k | match ( | 210 | 3.65k | ifd.contains(Tag::StripByteCounts), | 211 | 3.65k | ifd.contains(Tag::StripOffsets), | 212 | 3.65k | ifd.contains(Tag::TileByteCounts), | 213 | 3.65k | ifd.contains(Tag::TileOffsets), | 214 | 3.65k | ) { | 215 | | (true, true, false, false) => { | 216 | 3.15k | chunk_type = ChunkType::Strip; | 217 | | | 218 | 3.15k | chunk_offsets = tag_reader | 219 | 3.15k | .find_tag(Tag::StripOffsets)? | 220 | 3.15k | .unwrap() | 221 | 3.15k | .into_u64_vec()?; | 222 | 3.15k | chunk_bytes = tag_reader | 223 | 3.15k | .find_tag(Tag::StripByteCounts)? | 224 | 3.14k | .unwrap() | 225 | 3.14k | .into_u64_vec()?; | 226 | 3.14k | let rows_per_strip = tag_reader | 227 | 3.14k | .find_tag(Tag::RowsPerStrip)? | 228 | 3.14k | .map(Value::into_u32) | 229 | 3.14k | .transpose()? | 230 | 3.14k | .unwrap_or(height); | 231 | 3.14k | strip_decoder = Some(StripDecodeState { rows_per_strip }); | 232 | 3.14k | tile_attributes = None; | 233 | | | 234 | 3.14k | if chunk_offsets.len() != chunk_bytes.len() | 235 | 3.13k | || rows_per_strip == 0 | 236 | 3.13k | || u32::try_from(chunk_offsets.len())? | 237 | 3.13k | != (height.saturating_sub(1) / rows_per_strip + 1) * planes as u32 | 238 | | { | 239 | 9 | return Err(TiffError::FormatError( | 240 | 9 | TiffFormatError::InconsistentSizesEncountered, | 241 | 9 | )); | 242 | 3.13k | } | 243 | | } | 244 | | (false, false, true, true) => { | 245 | 499 | chunk_type = ChunkType::Tile; | 246 | | | 247 | 499 | let tile_width = | 248 | 499 | usize::try_from(tag_reader.require_tag(Tag::TileWidth)?.into_u32()?)?; | 249 | 499 | let tile_length = | 250 | 499 | usize::try_from(tag_reader.require_tag(Tag::TileLength)?.into_u32()?)?; | 251 | | | 252 | 499 | if tile_width == 0 { | 253 | 0 | return Err(TiffFormatError::InvalidTagValueType(Tag::TileWidth).into()); | 254 | 499 | } else if tile_length == 0 { | 255 | 0 | return Err(TiffFormatError::InvalidTagValueType(Tag::TileLength).into()); | 256 | 499 | } | 257 | | | 258 | 499 | strip_decoder = None; | 259 | | tile_attributes = Some(TileAttributes { | 260 | 499 | image_width: usize::try_from(width)?, | 261 | 499 | image_height: usize::try_from(height)?, | 262 | 499 | tile_width, | 263 | 499 | tile_length, | 264 | | }); | 265 | 499 | chunk_offsets = tag_reader | 266 | 499 | .find_tag(Tag::TileOffsets)? | 267 | 499 | .unwrap() | 268 | 499 | .into_u64_vec()?; | 269 | 499 | chunk_bytes = tag_reader | 270 | 499 | .find_tag(Tag::TileByteCounts)? | 271 | 499 | .unwrap() | 272 | 499 | .into_u64_vec()?; | 273 | | | 274 | 499 | let tile = tile_attributes.as_ref().unwrap(); | 275 | 499 | if chunk_offsets.len() != chunk_bytes.len() | 276 | 498 | || chunk_offsets.len() | 277 | 498 | != tile.tiles_down() * tile.tiles_across() * planes as usize | 278 | | { | 279 | 1 | return Err(TiffError::FormatError( | 280 | 1 | TiffFormatError::InconsistentSizesEncountered, | 281 | 1 | )); | 282 | 498 | } | 283 | | } | 284 | | (_, _, _, _) => { | 285 | 2 | return Err(TiffError::FormatError( | 286 | 2 | TiffFormatError::StripTileTagConflict, | 287 | 2 | )) | 288 | | } | 289 | | }; | 290 | | | 291 | 3.62k | Ok(Image { | 292 | 3.62k | ifd: Some(ifd), | 293 | 3.62k | width, | 294 | 3.62k | height, | 295 | 3.62k | bits_per_sample: bits_per_sample[0], | 296 | 3.62k | samples, | 297 | 3.62k | sample_format, | 298 | 3.62k | photometric_interpretation, | 299 | 3.62k | compression_method, | 300 | 3.62k | jpeg_tables, | 301 | 3.62k | predictor, | 302 | 3.62k | chunk_type, | 303 | 3.62k | planar_config, | 304 | 3.62k | strip_decoder, | 305 | 3.62k | tile_attributes, | 306 | 3.62k | chunk_offsets, | 307 | 3.62k | chunk_bytes, | 308 | 3.62k | }) | 309 | 3.83k | } |
Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>> <tiff::decoder::image::Image>::from_reader::<std::io::cursor::Cursor<&[u8]>> Line | Count | Source | 86 | 4.75k | pub fn from_reader<R: Read + Seek>( | 87 | 4.75k | decoder: &mut ValueReader<R>, | 88 | 4.75k | ifd: Directory, | 89 | 4.75k | ) -> TiffResult<Image> { | 90 | 4.75k | let mut tag_reader = TagReader { decoder, ifd: &ifd }; | 91 | | | 92 | 4.75k | let width = tag_reader.require_tag(Tag::ImageWidth)?.into_u32()?; | 93 | 4.70k | let height = tag_reader.require_tag(Tag::ImageLength)?.into_u32()?; | 94 | 4.68k | if width == 0 || height == 0 { | 95 | 0 | return Err(TiffError::FormatError(TiffFormatError::InvalidDimensions( | 96 | 0 | width, height, | 97 | 0 | ))); | 98 | 4.68k | } | 99 | | | 100 | 4.68k | let photometric_interpretation = tag_reader | 101 | 4.68k | .find_tag(Tag::PhotometricInterpretation)? | 102 | 4.68k | .map(Value::into_u16) | 103 | 4.68k | .transpose()? | 104 | 4.62k | .and_then(PhotometricInterpretation::from_u16) | 105 | 4.62k | .ok_or(TiffUnsupportedError::UnknownInterpretation)?; | 106 | | | 107 | | // Try to parse both the compression method and the number, format, and bits of the included samples. | 108 | | // If they are not explicitly specified, those tags are reset to their default values and not carried from previous images. | 109 | 4.61k | let compression_method = match tag_reader.find_tag(Tag::Compression)? { | 110 | 4.30k | Some(val) => CompressionMethod::from_u16_exhaustive(val.into_u16()?), | 111 | 311 | None => CompressionMethod::None, | 112 | | }; | 113 | | | 114 | 4.61k | let jpeg_tables = if compression_method == CompressionMethod::ModernJPEG | 115 | 2.34k | && ifd.contains(Tag::JPEGTables) | 116 | | { | 117 | 307 | let vec = tag_reader | 118 | 307 | .find_tag(Tag::JPEGTables)? | 119 | 303 | .unwrap() | 120 | 303 | .into_u8_vec()?; | 121 | 300 | if vec.len() < 2 { | 122 | 2 | return Err(TiffError::FormatError( | 123 | 2 | TiffFormatError::InvalidTagValueType(Tag::JPEGTables), | 124 | 2 | )); | 125 | 298 | } | 126 | | | 127 | 298 | Some(Arc::new(vec)) | 128 | | } else { | 129 | 4.30k | None | 130 | | }; | 131 | | | 132 | 4.60k | let samples: u16 = tag_reader | 133 | 4.60k | .find_tag(Tag::SamplesPerPixel)? | 134 | 4.60k | .map(Value::into_u16) | 135 | 4.60k | .transpose()? | 136 | 4.59k | .unwrap_or(1); | 137 | 4.59k | if samples == 0 { | 138 | 0 | return Err(TiffFormatError::SamplesPerPixelIsZero.into()); | 139 | 4.59k | } | 140 | | | 141 | 4.59k | let sample_format = match tag_reader.find_tag_uint_vec(Tag::SampleFormat)? { | 142 | 1.20k | Some(vals) => { | 143 | 1.20k | let sample_format: Vec<_> = vals | 144 | 1.20k | .into_iter() | 145 | 1.20k | .map(SampleFormat::from_u16_exhaustive) | 146 | 1.20k | .collect(); | 147 | | | 148 | | // TODO: for now, only homogenous formats across samples are supported. | 149 | 1.20k | if !sample_format.windows(2).all(|s| s[0] == s[1]) { | 150 | 63 | return Err(TiffUnsupportedError::UnsupportedSampleFormat(sample_format).into()); | 151 | 1.14k | } | 152 | | | 153 | 1.14k | sample_format[0] | 154 | | } | 155 | 3.33k | None => SampleFormat::Uint, | 156 | | }; | 157 | | | 158 | 4.47k | let bits_per_sample: Vec<u8> = tag_reader | 159 | 4.47k | .find_tag_uint_vec(Tag::BitsPerSample)? | 160 | 4.39k | .unwrap_or_else(|| vec![1]); | 161 | | | 162 | | // Technically bits_per_sample.len() should be *equal* to samples, but libtiff also allows | 163 | | // it to be a single value that applies to all samples. | 164 | 4.39k | if bits_per_sample.len() != samples.into() && bits_per_sample.len() != 1 { | 165 | 16 | return Err(TiffError::FormatError( | 166 | 16 | TiffFormatError::InconsistentSizesEncountered, | 167 | 16 | )); | 168 | 4.38k | } | 169 | | | 170 | | // This library (and libtiff) do not support mixed sample formats and zero bits per sample | 171 | | // doesn't make sense. | 172 | 4.38k | if bits_per_sample.iter().any(|&b| b != bits_per_sample[0]) || bits_per_sample[0] == 0 { | 173 | 7 | return Err(TiffUnsupportedError::InconsistentBitsPerSample(bits_per_sample).into()); | 174 | 4.37k | } | 175 | | | 176 | 4.37k | let predictor = tag_reader | 177 | 4.37k | .find_tag(Tag::Predictor)? | 178 | 4.37k | .map(Value::into_u16) | 179 | 4.37k | .transpose()? | 180 | 4.37k | .map(|p| { | 181 | | Predictor::from_u16(p) | 182 | | .ok_or(TiffError::FormatError(TiffFormatError::UnknownPredictor(p))) | 183 | | }) | 184 | 4.37k | .transpose()? | 185 | 4.36k | .unwrap_or(Predictor::None); | 186 | | | 187 | 4.36k | let planar_config = tag_reader | 188 | 4.36k | .find_tag(Tag::PlanarConfiguration)? | 189 | 4.36k | .map(Value::into_u16) | 190 | 4.36k | .transpose()? | 191 | 4.36k | .map(|p| { | 192 | | PlanarConfiguration::from_u16(p).ok_or(TiffError::FormatError( | 193 | | TiffFormatError::UnknownPlanarConfiguration(p), | 194 | | )) | 195 | | }) | 196 | 4.36k | .transpose()? | 197 | 4.36k | .unwrap_or(PlanarConfiguration::Chunky); | 198 | | | 199 | 4.36k | let planes = match planar_config { | 200 | 4.36k | PlanarConfiguration::Chunky => 1, | 201 | 3 | PlanarConfiguration::Planar => samples, | 202 | | }; | 203 | | | 204 | | let chunk_type; | 205 | | let chunk_offsets; | 206 | | let chunk_bytes; | 207 | | let strip_decoder; | 208 | | let tile_attributes; | 209 | 4.36k | match ( | 210 | 4.36k | ifd.contains(Tag::StripByteCounts), | 211 | 4.36k | ifd.contains(Tag::StripOffsets), | 212 | 4.36k | ifd.contains(Tag::TileByteCounts), | 213 | 4.36k | ifd.contains(Tag::TileOffsets), | 214 | 4.36k | ) { | 215 | | (true, true, false, false) => { | 216 | 3.10k | chunk_type = ChunkType::Strip; | 217 | | | 218 | 3.10k | chunk_offsets = tag_reader | 219 | 3.10k | .find_tag(Tag::StripOffsets)? | 220 | 3.09k | .unwrap() | 221 | 3.09k | .into_u64_vec()?; | 222 | 3.09k | chunk_bytes = tag_reader | 223 | 3.09k | .find_tag(Tag::StripByteCounts)? | 224 | 3.06k | .unwrap() | 225 | 3.06k | .into_u64_vec()?; | 226 | 3.06k | let rows_per_strip = tag_reader | 227 | 3.06k | .find_tag(Tag::RowsPerStrip)? | 228 | 3.05k | .map(Value::into_u32) | 229 | 3.05k | .transpose()? | 230 | 3.05k | .unwrap_or(height); | 231 | 3.05k | strip_decoder = Some(StripDecodeState { rows_per_strip }); | 232 | 3.05k | tile_attributes = None; | 233 | | | 234 | 3.05k | if chunk_offsets.len() != chunk_bytes.len() | 235 | 3.03k | || rows_per_strip == 0 | 236 | 3.03k | || u32::try_from(chunk_offsets.len())? | 237 | 3.03k | != (height.saturating_sub(1) / rows_per_strip + 1) * planes as u32 | 238 | | { | 239 | 33 | return Err(TiffError::FormatError( | 240 | 33 | TiffFormatError::InconsistentSizesEncountered, | 241 | 33 | )); | 242 | 3.01k | } | 243 | | } | 244 | | (false, false, true, true) => { | 245 | 1.23k | chunk_type = ChunkType::Tile; | 246 | | | 247 | 1.23k | let tile_width = | 248 | 1.23k | usize::try_from(tag_reader.require_tag(Tag::TileWidth)?.into_u32()?)?; | 249 | 1.23k | let tile_length = | 250 | 1.23k | usize::try_from(tag_reader.require_tag(Tag::TileLength)?.into_u32()?)?; | 251 | | | 252 | 1.23k | if tile_width == 0 { | 253 | 1 | return Err(TiffFormatError::InvalidTagValueType(Tag::TileWidth).into()); | 254 | 1.23k | } else if tile_length == 0 { | 255 | 1 | return Err(TiffFormatError::InvalidTagValueType(Tag::TileLength).into()); | 256 | 1.23k | } | 257 | | | 258 | 1.23k | strip_decoder = None; | 259 | | tile_attributes = Some(TileAttributes { | 260 | 1.23k | image_width: usize::try_from(width)?, | 261 | 1.23k | image_height: usize::try_from(height)?, | 262 | 1.23k | tile_width, | 263 | 1.23k | tile_length, | 264 | | }); | 265 | 1.23k | chunk_offsets = tag_reader | 266 | 1.23k | .find_tag(Tag::TileOffsets)? | 267 | 1.22k | .unwrap() | 268 | 1.22k | .into_u64_vec()?; | 269 | 1.22k | chunk_bytes = tag_reader | 270 | 1.22k | .find_tag(Tag::TileByteCounts)? | 271 | 1.22k | .unwrap() | 272 | 1.22k | .into_u64_vec()?; | 273 | | | 274 | 1.22k | let tile = tile_attributes.as_ref().unwrap(); | 275 | 1.22k | if chunk_offsets.len() != chunk_bytes.len() | 276 | 1.22k | || chunk_offsets.len() | 277 | 1.22k | != tile.tiles_down() * tile.tiles_across() * planes as usize | 278 | | { | 279 | 3 | return Err(TiffError::FormatError( | 280 | 3 | TiffFormatError::InconsistentSizesEncountered, | 281 | 3 | )); | 282 | 1.22k | } | 283 | | } | 284 | | (_, _, _, _) => { | 285 | 25 | return Err(TiffError::FormatError( | 286 | 25 | TiffFormatError::StripTileTagConflict, | 287 | 25 | )) | 288 | | } | 289 | | }; | 290 | | | 291 | 4.23k | Ok(Image { | 292 | 4.23k | ifd: Some(ifd), | 293 | 4.23k | width, | 294 | 4.23k | height, | 295 | 4.23k | bits_per_sample: bits_per_sample[0], | 296 | 4.23k | samples, | 297 | 4.23k | sample_format, | 298 | 4.23k | photometric_interpretation, | 299 | 4.23k | compression_method, | 300 | 4.23k | jpeg_tables, | 301 | 4.23k | predictor, | 302 | 4.23k | chunk_type, | 303 | 4.23k | planar_config, | 304 | 4.23k | strip_decoder, | 305 | 4.23k | tile_attributes, | 306 | 4.23k | chunk_offsets, | 307 | 4.23k | chunk_bytes, | 308 | 4.23k | }) | 309 | 4.75k | } |
|
310 | | |
311 | 27.4k | pub(crate) fn colortype(&self) -> TiffResult<ColorType> { |
312 | 27.4k | match self.photometric_interpretation { |
313 | 2.72k | PhotometricInterpretation::RGB => match self.samples { |
314 | 2.32k | 3 => Ok(ColorType::RGB(self.bits_per_sample)), |
315 | 390 | 4 => Ok(ColorType::RGBA(self.bits_per_sample)), |
316 | | // FIXME: We should _ignore_ other components. In particular: |
317 | | // > Beware of extra components. Some TIFF files may have more components per pixel |
318 | | // than you think. A Baseline TIFF reader must skip over them gracefully,using the |
319 | | // values of the SamplesPerPixel and BitsPerSample fields. |
320 | | // > -- TIFF 6.0 Specification, Section 7, Additional Baseline requirements. |
321 | 2 | _ => Err(TiffError::UnsupportedError( |
322 | 2 | TiffUnsupportedError::InterpretationWithBits( |
323 | 2 | self.photometric_interpretation, |
324 | 2 | vec![self.bits_per_sample; self.samples as usize], |
325 | 2 | ), |
326 | 2 | )), |
327 | | }, |
328 | 133 | PhotometricInterpretation::CMYK => match self.samples { |
329 | 129 | 4 => Ok(ColorType::CMYK(self.bits_per_sample)), |
330 | 1 | 5 => Ok(ColorType::CMYKA(self.bits_per_sample)), |
331 | 3 | _ => Err(TiffError::UnsupportedError( |
332 | 3 | TiffUnsupportedError::InterpretationWithBits( |
333 | 3 | self.photometric_interpretation, |
334 | 3 | vec![self.bits_per_sample; self.samples as usize], |
335 | 3 | ), |
336 | 3 | )), |
337 | | }, |
338 | 3 | PhotometricInterpretation::YCbCr => match self.samples { |
339 | 2 | 3 => Ok(ColorType::YCbCr(self.bits_per_sample)), |
340 | 1 | _ => Err(TiffError::UnsupportedError( |
341 | 1 | TiffUnsupportedError::InterpretationWithBits( |
342 | 1 | self.photometric_interpretation, |
343 | 1 | vec![self.bits_per_sample; self.samples as usize], |
344 | 1 | ), |
345 | 1 | )), |
346 | | }, |
347 | | // TODO: treatment of WhiteIsZero is not quite consistent with `invert_colors` that is |
348 | | // later called when that interpretation is read. That function does not support |
349 | | // Multiband as a color type and will error. It's unclear how to resolve that exactly. |
350 | | PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero => { |
351 | 24.6k | match self.samples { |
352 | 24.6k | 1 => Ok(ColorType::Gray(self.bits_per_sample)), |
353 | 9 | _ => Ok(ColorType::Multiband { |
354 | 9 | bit_depth: self.bits_per_sample, |
355 | 9 | num_samples: self.samples, |
356 | 9 | }), |
357 | | } |
358 | | } |
359 | | // TODO: this is bad we should not fail at this point |
360 | | PhotometricInterpretation::RGBPalette |
361 | | | PhotometricInterpretation::TransparencyMask |
362 | 5 | | PhotometricInterpretation::CIELab => Err(TiffError::UnsupportedError( |
363 | 5 | TiffUnsupportedError::InterpretationWithBits( |
364 | 5 | self.photometric_interpretation, |
365 | 5 | vec![self.bits_per_sample; self.samples as usize], |
366 | 5 | ), |
367 | 5 | )), |
368 | | } |
369 | 27.4k | } |
370 | | |
371 | 0 | pub(crate) fn minimum_row_stride(&self, dims: (u32, u32)) -> Option<NonZeroUsize> { |
372 | 0 | let (width, height) = dims; |
373 | | |
374 | 0 | let row_stride = u64::from(width) |
375 | 0 | .saturating_mul(self.samples_per_pixel() as u64) |
376 | 0 | .saturating_mul(self.bits_per_sample as u64) |
377 | 0 | .div_ceil(8); |
378 | | |
379 | | // Note: row stride should be smaller than the len if we have an actual buffer. If there |
380 | | // are no pixels in the buffer (height _or_ width is 0) then the stride is not well defined |
381 | | // and we return `None`. |
382 | 0 | (height > 0) |
383 | 0 | .then_some(row_stride as usize) |
384 | 0 | .and_then(NonZeroUsize::new) |
385 | 0 | } |
386 | | |
387 | 19.5k | fn create_reader<'r, R: 'r + Read>( |
388 | 19.5k | reader: R, |
389 | 19.5k | compression_method: CompressionMethod, |
390 | 19.5k | compressed_length: u64, |
391 | 19.5k | // FIXME: these should be `expect` attributes or we choose another way of passing them. |
392 | 19.5k | #[cfg_attr(not(feature = "jpeg"), allow(unused_variables))] jpeg_tables: Option<&[u8]>, |
393 | 19.5k | #[cfg_attr(not(feature = "fax"), allow(unused_variables))] dimensions: (u32, u32), |
394 | 19.5k | ) -> TiffResult<Box<dyn Read + 'r>> { |
395 | 19.5k | Ok(match compression_method { |
396 | 9.51k | CompressionMethod::None => Box::new(reader), |
397 | | #[cfg(feature = "lzw")] |
398 | 1.40k | CompressionMethod::LZW => Box::new(super::stream::LZWReader::new( |
399 | 1.40k | reader, |
400 | 1.40k | usize::try_from(compressed_length)?, |
401 | | )), |
402 | | #[cfg(feature = "zstd")] |
403 | | CompressionMethod::ZSTD => Box::new(zstd::Decoder::new(reader)?), |
404 | 296 | CompressionMethod::PackBits => Box::new(PackBitsReader::new(reader, compressed_length)), |
405 | | #[cfg(feature = "deflate")] |
406 | | CompressionMethod::Deflate | CompressionMethod::OldDeflate => { |
407 | 2.47k | Box::new(super::stream::DeflateReader::new(reader)) |
408 | | } |
409 | | #[cfg(feature = "jpeg")] |
410 | | CompressionMethod::ModernJPEG => { |
411 | | use zune_jpeg::zune_core; |
412 | | |
413 | 5.55k | if jpeg_tables.is_some() && compressed_length < 2 { |
414 | 2 | return Err(TiffError::FormatError( |
415 | 2 | TiffFormatError::InvalidTagValueType(Tag::JPEGTables), |
416 | 2 | )); |
417 | 5.55k | } |
418 | | |
419 | | // Construct new jpeg_reader wrapping a SmartReader. |
420 | | // |
421 | | // JPEG compression in TIFF allows saving quantization and/or huffman tables in one |
422 | | // central location. These `jpeg_tables` are simply prepended to the remaining jpeg image data. |
423 | | // Because these `jpeg_tables` start with a `SOI` (HEX: `0xFFD8`) or __start of image__ marker |
424 | | // which is also at the beginning of the remaining JPEG image data and would |
425 | | // confuse the JPEG renderer, one of these has to be taken off. In this case the first two |
426 | | // bytes of the remaining JPEG data is removed because it follows `jpeg_tables`. |
427 | | // Similary, `jpeg_tables` ends with a `EOI` (HEX: `0xFFD9`) or __end of image__ marker, |
428 | | // this has to be removed as well (last two bytes of `jpeg_tables`). |
429 | 5.55k | let mut jpeg_reader = match jpeg_tables { |
430 | 1.72k | Some(jpeg_tables) => { |
431 | 1.72k | let mut reader = reader.take(compressed_length); |
432 | 1.72k | reader.read_exact(&mut [0; 2])?; |
433 | | |
434 | 1.50k | Box::new( |
435 | 1.50k | Cursor::new(&jpeg_tables[..jpeg_tables.len() - 2]) |
436 | 1.50k | .chain(reader.take(compressed_length)), |
437 | 1.50k | ) as Box<dyn Read> |
438 | | } |
439 | 3.83k | None => Box::new(reader.take(compressed_length)), |
440 | | }; |
441 | | |
442 | 5.34k | let mut jpeg_data = Vec::new(); |
443 | 5.34k | jpeg_reader.read_to_end(&mut jpeg_data)?; |
444 | | |
445 | 5.34k | let mut decoder = zune_jpeg::JpegDecoder::new(jpeg_data); |
446 | 5.34k | let mut options: zune_core::options::DecoderOptions = Default::default(); |
447 | | |
448 | | // Disable color conversion by setting the output colorspace to the input |
449 | | // colorspace. |
450 | 5.34k | decoder.decode_headers()?; |
451 | 4.56k | if let Some(colorspace) = decoder.get_input_colorspace() { |
452 | 4.56k | options = options.jpeg_set_out_colorspace(colorspace); |
453 | 4.56k | } |
454 | | |
455 | 4.56k | decoder.set_options(options); |
456 | | |
457 | 4.56k | let data = decoder.decode()?; |
458 | | |
459 | 1.04k | Box::new(Cursor::new(data)) |
460 | | } |
461 | | #[cfg(feature = "fax")] |
462 | 291 | CompressionMethod::Fax4 => Box::new(super::stream::Group4Reader::new( |
463 | 291 | dimensions, |
464 | 291 | reader, |
465 | 291 | compressed_length, |
466 | 1 | )?), |
467 | 9 | method => { |
468 | 9 | return Err(TiffError::UnsupportedError( |
469 | 9 | TiffUnsupportedError::UnsupportedCompressionMethod(method), |
470 | 9 | )) |
471 | | } |
472 | | }) |
473 | 19.5k | } Unexecuted instantiation: <tiff::decoder::image::Image>::create_reader::<&mut std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::create_reader::<_> <tiff::decoder::image::Image>::create_reader::<&mut std::io::cursor::Cursor<&[u8]>> Line | Count | Source | 387 | 13.5k | fn create_reader<'r, R: 'r + Read>( | 388 | 13.5k | reader: R, | 389 | 13.5k | compression_method: CompressionMethod, | 390 | 13.5k | compressed_length: u64, | 391 | 13.5k | // FIXME: these should be `expect` attributes or we choose another way of passing them. | 392 | 13.5k | #[cfg_attr(not(feature = "jpeg"), allow(unused_variables))] jpeg_tables: Option<&[u8]>, | 393 | 13.5k | #[cfg_attr(not(feature = "fax"), allow(unused_variables))] dimensions: (u32, u32), | 394 | 13.5k | ) -> TiffResult<Box<dyn Read + 'r>> { | 395 | 13.5k | Ok(match compression_method { | 396 | 9.20k | CompressionMethod::None => Box::new(reader), | 397 | | #[cfg(feature = "lzw")] | 398 | 564 | CompressionMethod::LZW => Box::new(super::stream::LZWReader::new( | 399 | 564 | reader, | 400 | 564 | usize::try_from(compressed_length)?, | 401 | | )), | 402 | | #[cfg(feature = "zstd")] | 403 | | CompressionMethod::ZSTD => Box::new(zstd::Decoder::new(reader)?), | 404 | 61 | CompressionMethod::PackBits => Box::new(PackBitsReader::new(reader, compressed_length)), | 405 | | #[cfg(feature = "deflate")] | 406 | | CompressionMethod::Deflate | CompressionMethod::OldDeflate => { | 407 | 613 | Box::new(super::stream::DeflateReader::new(reader)) | 408 | | } | 409 | | #[cfg(feature = "jpeg")] | 410 | | CompressionMethod::ModernJPEG => { | 411 | | use zune_jpeg::zune_core; | 412 | | | 413 | 3.08k | if jpeg_tables.is_some() && compressed_length < 2 { | 414 | 0 | return Err(TiffError::FormatError( | 415 | 0 | TiffFormatError::InvalidTagValueType(Tag::JPEGTables), | 416 | 0 | )); | 417 | 3.08k | } | 418 | | | 419 | | // Construct new jpeg_reader wrapping a SmartReader. | 420 | | // | 421 | | // JPEG compression in TIFF allows saving quantization and/or huffman tables in one | 422 | | // central location. These `jpeg_tables` are simply prepended to the remaining jpeg image data. | 423 | | // Because these `jpeg_tables` start with a `SOI` (HEX: `0xFFD8`) or __start of image__ marker | 424 | | // which is also at the beginning of the remaining JPEG image data and would | 425 | | // confuse the JPEG renderer, one of these has to be taken off. In this case the first two | 426 | | // bytes of the remaining JPEG data is removed because it follows `jpeg_tables`. | 427 | | // Similary, `jpeg_tables` ends with a `EOI` (HEX: `0xFFD9`) or __end of image__ marker, | 428 | | // this has to be removed as well (last two bytes of `jpeg_tables`). | 429 | 3.08k | let mut jpeg_reader = match jpeg_tables { | 430 | 1.22k | Some(jpeg_tables) => { | 431 | 1.22k | let mut reader = reader.take(compressed_length); | 432 | 1.22k | reader.read_exact(&mut [0; 2])?; | 433 | | | 434 | 1.04k | Box::new( | 435 | 1.04k | Cursor::new(&jpeg_tables[..jpeg_tables.len() - 2]) | 436 | 1.04k | .chain(reader.take(compressed_length)), | 437 | 1.04k | ) as Box<dyn Read> | 438 | | } | 439 | 1.85k | None => Box::new(reader.take(compressed_length)), | 440 | | }; | 441 | | | 442 | 2.89k | let mut jpeg_data = Vec::new(); | 443 | 2.89k | jpeg_reader.read_to_end(&mut jpeg_data)?; | 444 | | | 445 | 2.89k | let mut decoder = zune_jpeg::JpegDecoder::new(jpeg_data); | 446 | 2.89k | let mut options: zune_core::options::DecoderOptions = Default::default(); | 447 | | | 448 | | // Disable color conversion by setting the output colorspace to the input | 449 | | // colorspace. | 450 | 2.89k | decoder.decode_headers()?; | 451 | 2.54k | if let Some(colorspace) = decoder.get_input_colorspace() { | 452 | 2.54k | options = options.jpeg_set_out_colorspace(colorspace); | 453 | 2.54k | } | 454 | | | 455 | 2.54k | decoder.set_options(options); | 456 | | | 457 | 2.54k | let data = decoder.decode()?; | 458 | | | 459 | 509 | Box::new(Cursor::new(data)) | 460 | | } | 461 | | #[cfg(feature = "fax")] | 462 | 65 | CompressionMethod::Fax4 => Box::new(super::stream::Group4Reader::new( | 463 | 65 | dimensions, | 464 | 65 | reader, | 465 | 65 | compressed_length, | 466 | 0 | )?), | 467 | 1 | method => { | 468 | 1 | return Err(TiffError::UnsupportedError( | 469 | 1 | TiffUnsupportedError::UnsupportedCompressionMethod(method), | 470 | 1 | )) | 471 | | } | 472 | | }) | 473 | 13.5k | } |
Unexecuted instantiation: <tiff::decoder::image::Image>::create_reader::<&mut std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::create_reader::<&mut std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::create_reader::<&mut std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::create_reader::<&mut std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::create_reader::<&mut std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::create_reader::<&mut std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::create_reader::<&mut std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::create_reader::<&mut std::io::cursor::Cursor<&[u8]>> <tiff::decoder::image::Image>::create_reader::<&mut std::io::cursor::Cursor<&[u8]>> Line | Count | Source | 387 | 5.95k | fn create_reader<'r, R: 'r + Read>( | 388 | 5.95k | reader: R, | 389 | 5.95k | compression_method: CompressionMethod, | 390 | 5.95k | compressed_length: u64, | 391 | 5.95k | // FIXME: these should be `expect` attributes or we choose another way of passing them. | 392 | 5.95k | #[cfg_attr(not(feature = "jpeg"), allow(unused_variables))] jpeg_tables: Option<&[u8]>, | 393 | 5.95k | #[cfg_attr(not(feature = "fax"), allow(unused_variables))] dimensions: (u32, u32), | 394 | 5.95k | ) -> TiffResult<Box<dyn Read + 'r>> { | 395 | 5.95k | Ok(match compression_method { | 396 | 306 | CompressionMethod::None => Box::new(reader), | 397 | | #[cfg(feature = "lzw")] | 398 | 836 | CompressionMethod::LZW => Box::new(super::stream::LZWReader::new( | 399 | 836 | reader, | 400 | 836 | usize::try_from(compressed_length)?, | 401 | | )), | 402 | | #[cfg(feature = "zstd")] | 403 | | CompressionMethod::ZSTD => Box::new(zstd::Decoder::new(reader)?), | 404 | 235 | CompressionMethod::PackBits => Box::new(PackBitsReader::new(reader, compressed_length)), | 405 | | #[cfg(feature = "deflate")] | 406 | | CompressionMethod::Deflate | CompressionMethod::OldDeflate => { | 407 | 1.86k | Box::new(super::stream::DeflateReader::new(reader)) | 408 | | } | 409 | | #[cfg(feature = "jpeg")] | 410 | | CompressionMethod::ModernJPEG => { | 411 | | use zune_jpeg::zune_core; | 412 | | | 413 | 2.47k | if jpeg_tables.is_some() && compressed_length < 2 { | 414 | 2 | return Err(TiffError::FormatError( | 415 | 2 | TiffFormatError::InvalidTagValueType(Tag::JPEGTables), | 416 | 2 | )); | 417 | 2.47k | } | 418 | | | 419 | | // Construct new jpeg_reader wrapping a SmartReader. | 420 | | // | 421 | | // JPEG compression in TIFF allows saving quantization and/or huffman tables in one | 422 | | // central location. These `jpeg_tables` are simply prepended to the remaining jpeg image data. | 423 | | // Because these `jpeg_tables` start with a `SOI` (HEX: `0xFFD8`) or __start of image__ marker | 424 | | // which is also at the beginning of the remaining JPEG image data and would | 425 | | // confuse the JPEG renderer, one of these has to be taken off. In this case the first two | 426 | | // bytes of the remaining JPEG data is removed because it follows `jpeg_tables`. | 427 | | // Similary, `jpeg_tables` ends with a `EOI` (HEX: `0xFFD9`) or __end of image__ marker, | 428 | | // this has to be removed as well (last two bytes of `jpeg_tables`). | 429 | 2.47k | let mut jpeg_reader = match jpeg_tables { | 430 | 496 | Some(jpeg_tables) => { | 431 | 496 | let mut reader = reader.take(compressed_length); | 432 | 496 | reader.read_exact(&mut [0; 2])?; | 433 | | | 434 | 466 | Box::new( | 435 | 466 | Cursor::new(&jpeg_tables[..jpeg_tables.len() - 2]) | 436 | 466 | .chain(reader.take(compressed_length)), | 437 | 466 | ) as Box<dyn Read> | 438 | | } | 439 | 1.98k | None => Box::new(reader.take(compressed_length)), | 440 | | }; | 441 | | | 442 | 2.44k | let mut jpeg_data = Vec::new(); | 443 | 2.44k | jpeg_reader.read_to_end(&mut jpeg_data)?; | 444 | | | 445 | 2.44k | let mut decoder = zune_jpeg::JpegDecoder::new(jpeg_data); | 446 | 2.44k | let mut options: zune_core::options::DecoderOptions = Default::default(); | 447 | | | 448 | | // Disable color conversion by setting the output colorspace to the input | 449 | | // colorspace. | 450 | 2.44k | decoder.decode_headers()?; | 451 | 2.01k | if let Some(colorspace) = decoder.get_input_colorspace() { | 452 | 2.01k | options = options.jpeg_set_out_colorspace(colorspace); | 453 | 2.01k | } | 454 | | | 455 | 2.01k | decoder.set_options(options); | 456 | | | 457 | 2.01k | let data = decoder.decode()?; | 458 | | | 459 | 534 | Box::new(Cursor::new(data)) | 460 | | } | 461 | | #[cfg(feature = "fax")] | 462 | 226 | CompressionMethod::Fax4 => Box::new(super::stream::Group4Reader::new( | 463 | 226 | dimensions, | 464 | 226 | reader, | 465 | 226 | compressed_length, | 466 | 1 | )?), | 467 | 8 | method => { | 468 | 8 | return Err(TiffError::UnsupportedError( | 469 | 8 | TiffUnsupportedError::UnsupportedCompressionMethod(method), | 470 | 8 | )) | 471 | | } | 472 | | }) | 473 | 5.95k | } |
|
474 | | |
475 | | /// Samples per pixel within chunk. |
476 | | /// |
477 | | /// In planar config, samples are stored in separate strips/chunks, also called bands. |
478 | | /// |
479 | | /// Example with `bits_per_sample = [8, 8, 8]` and `PhotometricInterpretation::RGB`: |
480 | | /// * `PlanarConfiguration::Chunky` -> 3 (RGBRGBRGB...) |
481 | | /// * `PlanarConfiguration::Planar` -> 1 (RRR...) (GGG...) (BBB...) |
482 | 42.9k | pub(crate) fn samples_per_pixel(&self) -> usize { |
483 | 42.9k | match self.planar_config { |
484 | 42.9k | PlanarConfiguration::Chunky => self.samples.into(), |
485 | 0 | PlanarConfiguration::Planar => 1, |
486 | | } |
487 | 42.9k | } |
488 | | |
489 | | /// Number of strips per pixel. |
490 | 7.78k | pub(crate) fn strips_per_pixel(&self) -> usize { |
491 | 7.78k | match self.planar_config { |
492 | 7.78k | PlanarConfiguration::Chunky => 1, |
493 | 0 | PlanarConfiguration::Planar => self.samples.into(), |
494 | | } |
495 | 7.78k | } |
496 | | |
497 | 0 | pub(crate) fn chunk_file_range(&self, chunk: u32) -> TiffResult<(u64, u64)> { |
498 | 0 | let file_offset = self |
499 | 0 | .chunk_offsets |
500 | 0 | .get(chunk as usize) |
501 | 0 | .ok_or(TiffError::FormatError( |
502 | 0 | TiffFormatError::InconsistentSizesEncountered, |
503 | 0 | ))?; |
504 | | |
505 | 0 | let compressed_bytes = |
506 | 0 | self.chunk_bytes |
507 | 0 | .get(chunk as usize) |
508 | 0 | .ok_or(TiffError::FormatError( |
509 | 0 | TiffFormatError::InconsistentSizesEncountered, |
510 | 0 | ))?; |
511 | | |
512 | 0 | Ok((*file_offset, *compressed_bytes)) |
513 | 0 | } |
514 | | |
515 | 46.8k | pub(crate) fn chunk_dimensions(&self) -> TiffResult<(u32, u32)> { |
516 | 46.8k | match self.chunk_type { |
517 | | ChunkType::Strip => { |
518 | 22.0k | let strip_attrs = self.strip_decoder.as_ref().unwrap(); |
519 | 22.0k | Ok((self.width, strip_attrs.rows_per_strip)) |
520 | | } |
521 | | ChunkType::Tile => { |
522 | 24.8k | let tile_attrs = self.tile_attributes.as_ref().unwrap(); |
523 | | Ok(( |
524 | 24.8k | u32::try_from(tile_attrs.tile_width)?, |
525 | 24.8k | u32::try_from(tile_attrs.tile_length)?, |
526 | | )) |
527 | | } |
528 | | } |
529 | 46.8k | } |
530 | | |
531 | 19.5k | pub(crate) fn chunk_data_dimensions(&self, chunk_index: u32) -> TiffResult<(u32, u32)> { |
532 | 19.5k | let dims = self.chunk_dimensions()?; |
533 | | |
534 | 19.5k | match self.chunk_type { |
535 | | ChunkType::Strip => { |
536 | 7.98k | let strip_attrs = self.strip_decoder.as_ref().unwrap(); |
537 | 7.98k | let strips_per_band = |
538 | 7.98k | self.height.saturating_sub(1) / strip_attrs.rows_per_strip + 1; |
539 | 7.98k | let strip_height_without_padding = (chunk_index % strips_per_band) |
540 | 7.98k | .checked_mul(dims.1) |
541 | 7.98k | .and_then(|x| self.height.checked_sub(x)) |
542 | 7.98k | .ok_or(TiffError::UsageError(UsageError::InvalidChunkIndex( |
543 | 7.98k | chunk_index, |
544 | 7.98k | )))?; |
545 | | |
546 | | // Ignore potential vertical padding on the bottommost strip |
547 | 7.98k | let strip_height = dims.1.min(strip_height_without_padding); |
548 | | |
549 | 7.98k | Ok((dims.0, strip_height)) |
550 | | } |
551 | | ChunkType::Tile => { |
552 | 11.5k | let tile_attrs = self.tile_attributes.as_ref().unwrap(); |
553 | 11.5k | let (padding_right, padding_down) = tile_attrs.get_padding(chunk_index as usize); |
554 | | |
555 | 11.5k | let tile_width = tile_attrs.tile_width - padding_right; |
556 | 11.5k | let tile_length = tile_attrs.tile_length - padding_down; |
557 | | |
558 | 11.5k | Ok((u32::try_from(tile_width)?, u32::try_from(tile_length)?)) |
559 | | } |
560 | | } |
561 | 19.5k | } |
562 | | |
563 | 19.6k | pub(crate) fn expand_chunk( |
564 | 19.6k | &self, |
565 | 19.6k | reader: &mut ValueReader<impl Read>, |
566 | 19.6k | buf: &mut [u8], |
567 | 19.6k | output_row_stride: usize, |
568 | 19.6k | chunk_index: u32, |
569 | 19.6k | ) -> TiffResult<()> { |
570 | | let ValueReader { |
571 | 19.6k | reader, |
572 | | bigtiff: _, |
573 | 19.6k | limits, |
574 | 19.6k | } = reader; |
575 | | |
576 | 19.6k | let byte_order = reader.byte_order; |
577 | 19.6k | let reader = reader.inner(); |
578 | | |
579 | | // Validate that the color type is supported. |
580 | 19.6k | let color_type = self.colortype()?; |
581 | 12.0k | match color_type { |
582 | 1.46k | ColorType::RGB(n) |
583 | 341 | | ColorType::RGBA(n) |
584 | 104 | | ColorType::CMYK(n) |
585 | 0 | | ColorType::CMYKA(n) |
586 | 0 | | ColorType::YCbCr(n) |
587 | 5.67k | | ColorType::Gray(n) |
588 | | | ColorType::Multiband { |
589 | 0 | bit_depth: n, |
590 | | num_samples: _, |
591 | 17.7k | } if n == 8 || n == 16 || n == 32 || n == 64 => {} |
592 | 12.0k | ColorType::Gray(n) |
593 | | | ColorType::Multiband { |
594 | 0 | bit_depth: n, |
595 | | num_samples: _, |
596 | 12.0k | } if n < 8 => match self.predictor { |
597 | 12.0k | Predictor::None => {} |
598 | | Predictor::Horizontal => { |
599 | 1 | return Err(TiffError::UnsupportedError( |
600 | 1 | TiffUnsupportedError::HorizontalPredictor(color_type), |
601 | 1 | )); |
602 | | } |
603 | | Predictor::FloatingPoint => { |
604 | 1 | return Err(TiffError::UnsupportedError( |
605 | 1 | TiffUnsupportedError::FloatingPointPredictor(color_type), |
606 | 1 | )); |
607 | | } |
608 | | }, |
609 | 0 | type_ => { |
610 | 0 | return Err(TiffError::UnsupportedError( |
611 | 0 | TiffUnsupportedError::UnsupportedColorType(type_), |
612 | 0 | )); |
613 | | } |
614 | | } |
615 | | |
616 | | // Validate that the predictor is supported for the sample type. |
617 | 19.6k | match (self.predictor, self.sample_format) { |
618 | | ( |
619 | | Predictor::Horizontal, |
620 | | SampleFormat::Int | SampleFormat::Uint | SampleFormat::IEEEFP, |
621 | 1.08k | ) => {} |
622 | | (Predictor::Horizontal, _) => { |
623 | 0 | return Err(TiffError::UnsupportedError( |
624 | 0 | TiffUnsupportedError::HorizontalPredictor(color_type), |
625 | 0 | )); |
626 | | } |
627 | 6 | (Predictor::FloatingPoint, SampleFormat::IEEEFP) => {} |
628 | | (Predictor::FloatingPoint, _) => { |
629 | 4 | return Err(TiffError::UnsupportedError( |
630 | 4 | TiffUnsupportedError::FloatingPointPredictor(color_type), |
631 | 4 | )); |
632 | | } |
633 | 18.5k | _ => {} |
634 | | } |
635 | | |
636 | 19.6k | let compressed_bytes = |
637 | 19.6k | self.chunk_bytes |
638 | 19.6k | .get(chunk_index as usize) |
639 | 19.6k | .ok_or(TiffError::FormatError( |
640 | 19.6k | TiffFormatError::InconsistentSizesEncountered, |
641 | 19.6k | ))?; |
642 | 19.6k | if *compressed_bytes > limits.intermediate_buffer_size as u64 { |
643 | 77 | return Err(TiffError::LimitsExceeded); |
644 | 19.5k | } |
645 | | |
646 | 19.5k | let compression_method = self.compression_method; |
647 | 19.5k | let photometric_interpretation = self.photometric_interpretation; |
648 | 19.5k | let predictor = self.predictor; |
649 | 19.5k | let samples = self.samples_per_pixel(); |
650 | | |
651 | 19.5k | let chunk_dims = self.chunk_dimensions()?; |
652 | 19.5k | let data_dims = self.chunk_data_dimensions(chunk_index)?; |
653 | | |
654 | 19.5k | let chunk_row_bits = (u64::from(chunk_dims.0) * u64::from(self.bits_per_sample)) |
655 | 19.5k | .checked_mul(samples as u64) |
656 | 19.5k | .ok_or(TiffError::LimitsExceeded)?; |
657 | 19.5k | let chunk_row_bytes: usize = chunk_row_bits.div_ceil(8).try_into()?; |
658 | | |
659 | 19.5k | let data_row_bits = (u64::from(data_dims.0) * u64::from(self.bits_per_sample)) |
660 | 19.5k | .checked_mul(samples as u64) |
661 | 19.5k | .ok_or(TiffError::LimitsExceeded)?; |
662 | 19.5k | let data_row_bytes: usize = data_row_bits.div_ceil(8).try_into()?; |
663 | | |
664 | | // TODO: Should these return errors instead? |
665 | 19.5k | assert!(output_row_stride >= data_row_bytes); |
666 | 19.5k | assert!(buf.len() >= output_row_stride * (data_dims.1 as usize - 1) + data_row_bytes); |
667 | | |
668 | 19.5k | let mut reader = Self::create_reader( |
669 | 19.5k | reader, |
670 | 19.5k | compression_method, |
671 | 19.5k | *compressed_bytes, |
672 | 19.5k | self.jpeg_tables.as_deref().map(|a| &**a), Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<_>::{closure#0}<tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Line | Count | Source | 672 | 1.22k | self.jpeg_tables.as_deref().map(|a| &**a), |
Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>>::{closure#0}<tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>>::{closure#0}Line | Count | Source | 672 | 498 | self.jpeg_tables.as_deref().map(|a| &**a), |
|
673 | 19.5k | chunk_dims, |
674 | 4.52k | )?; |
675 | | |
676 | 15.0k | if output_row_stride == chunk_row_bytes { |
677 | 3.60k | let tile = &mut buf[..chunk_row_bytes * data_dims.1 as usize]; |
678 | 3.60k | reader.read_exact(tile)?; |
679 | | |
680 | 694k | for row in tile.chunks_mut(chunk_row_bytes) { |
681 | 694k | super::fix_endianness_and_predict( |
682 | 694k | row, |
683 | 694k | color_type.bit_depth(), |
684 | 694k | samples, |
685 | 694k | byte_order, |
686 | 694k | predictor, |
687 | 694k | ); |
688 | 694k | } |
689 | 2.44k | if photometric_interpretation == PhotometricInterpretation::WhiteIsZero { |
690 | 680 | super::invert_colors(tile, color_type, self.sample_format)?; |
691 | 1.76k | } |
692 | 11.4k | } else if chunk_row_bytes > data_row_bytes && self.predictor == Predictor::FloatingPoint { |
693 | | // The floating point predictor shuffles the padding bytes into the encoded output, so |
694 | | // this case is handled specially when needed. |
695 | 0 | let mut encoded = vec![0u8; chunk_row_bytes]; |
696 | 0 | for row in buf.chunks_mut(output_row_stride).take(data_dims.1 as usize) { |
697 | 0 | reader.read_exact(&mut encoded)?; |
698 | | |
699 | 0 | let row = &mut row[..data_row_bytes]; |
700 | 0 | match color_type.bit_depth() { |
701 | 0 | 16 => predict_f16(&mut encoded, row, samples), |
702 | 0 | 32 => predict_f32(&mut encoded, row, samples), |
703 | 0 | 64 => predict_f64(&mut encoded, row, samples), |
704 | 0 | _ => unreachable!(), |
705 | | } |
706 | 0 | if photometric_interpretation == PhotometricInterpretation::WhiteIsZero { |
707 | 0 | super::invert_colors(row, color_type, self.sample_format)?; |
708 | 0 | } |
709 | | } |
710 | | } else { |
711 | 2.58M | for row in buf.chunks_mut(output_row_stride).take(data_dims.1 as usize) { |
712 | 2.58M | let row = &mut row[..data_row_bytes]; |
713 | 2.58M | reader.read_exact(row)?; |
714 | | |
715 | | // Skip horizontal padding |
716 | 2.58M | if chunk_row_bytes > data_row_bytes { |
717 | 111k | let len = u64::try_from(chunk_row_bytes - data_row_bytes)?; |
718 | 111k | io::copy(&mut reader.by_ref().take(len), &mut io::sink())?; |
719 | 2.47M | } |
720 | | |
721 | 2.58M | super::fix_endianness_and_predict( |
722 | 2.58M | row, |
723 | 2.58M | color_type.bit_depth(), |
724 | 2.58M | samples, |
725 | 2.58M | byte_order, |
726 | 2.58M | predictor, |
727 | | ); |
728 | 2.58M | if photometric_interpretation == PhotometricInterpretation::WhiteIsZero { |
729 | 168k | super::invert_colors(row, color_type, self.sample_format)?; |
730 | 2.41M | } |
731 | | } |
732 | | } |
733 | | |
734 | 12.3k | Ok(()) |
735 | 19.6k | } Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<_> <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>> Line | Count | Source | 563 | 13.6k | pub(crate) fn expand_chunk( | 564 | 13.6k | &self, | 565 | 13.6k | reader: &mut ValueReader<impl Read>, | 566 | 13.6k | buf: &mut [u8], | 567 | 13.6k | output_row_stride: usize, | 568 | 13.6k | chunk_index: u32, | 569 | 13.6k | ) -> TiffResult<()> { | 570 | | let ValueReader { | 571 | 13.6k | reader, | 572 | | bigtiff: _, | 573 | 13.6k | limits, | 574 | 13.6k | } = reader; | 575 | | | 576 | 13.6k | let byte_order = reader.byte_order; | 577 | 13.6k | let reader = reader.inner(); | 578 | | | 579 | | // Validate that the color type is supported. | 580 | 13.6k | let color_type = self.colortype()?; | 581 | 9.21k | match color_type { | 582 | 406 | ColorType::RGB(n) | 583 | 127 | | ColorType::RGBA(n) | 584 | 84 | | ColorType::CMYK(n) | 585 | 0 | | ColorType::CMYKA(n) | 586 | 0 | | ColorType::YCbCr(n) | 587 | 3.77k | | ColorType::Gray(n) | 588 | | | ColorType::Multiband { | 589 | 0 | bit_depth: n, | 590 | | num_samples: _, | 591 | 12.9k | } if n == 8 || n == 16 || n == 32 || n == 64 => {} | 592 | 9.21k | ColorType::Gray(n) | 593 | | | ColorType::Multiband { | 594 | 0 | bit_depth: n, | 595 | | num_samples: _, | 596 | 9.21k | } if n < 8 => match self.predictor { | 597 | 9.21k | Predictor::None => {} | 598 | | Predictor::Horizontal => { | 599 | 0 | return Err(TiffError::UnsupportedError( | 600 | 0 | TiffUnsupportedError::HorizontalPredictor(color_type), | 601 | 0 | )); | 602 | | } | 603 | | Predictor::FloatingPoint => { | 604 | 0 | return Err(TiffError::UnsupportedError( | 605 | 0 | TiffUnsupportedError::FloatingPointPredictor(color_type), | 606 | 0 | )); | 607 | | } | 608 | | }, | 609 | 0 | type_ => { | 610 | 0 | return Err(TiffError::UnsupportedError( | 611 | 0 | TiffUnsupportedError::UnsupportedColorType(type_), | 612 | 0 | )); | 613 | | } | 614 | | } | 615 | | | 616 | | // Validate that the predictor is supported for the sample type. | 617 | 13.6k | match (self.predictor, self.sample_format) { | 618 | | ( | 619 | | Predictor::Horizontal, | 620 | | SampleFormat::Int | SampleFormat::Uint | SampleFormat::IEEEFP, | 621 | 396 | ) => {} | 622 | | (Predictor::Horizontal, _) => { | 623 | 0 | return Err(TiffError::UnsupportedError( | 624 | 0 | TiffUnsupportedError::HorizontalPredictor(color_type), | 625 | 0 | )); | 626 | | } | 627 | 0 | (Predictor::FloatingPoint, SampleFormat::IEEEFP) => {} | 628 | | (Predictor::FloatingPoint, _) => { | 629 | 1 | return Err(TiffError::UnsupportedError( | 630 | 1 | TiffUnsupportedError::FloatingPointPredictor(color_type), | 631 | 1 | )); | 632 | | } | 633 | 13.2k | _ => {} | 634 | | } | 635 | | | 636 | 13.6k | let compressed_bytes = | 637 | 13.6k | self.chunk_bytes | 638 | 13.6k | .get(chunk_index as usize) | 639 | 13.6k | .ok_or(TiffError::FormatError( | 640 | 13.6k | TiffFormatError::InconsistentSizesEncountered, | 641 | 13.6k | ))?; | 642 | 13.6k | if *compressed_bytes > limits.intermediate_buffer_size as u64 { | 643 | 11 | return Err(TiffError::LimitsExceeded); | 644 | 13.5k | } | 645 | | | 646 | 13.5k | let compression_method = self.compression_method; | 647 | 13.5k | let photometric_interpretation = self.photometric_interpretation; | 648 | 13.5k | let predictor = self.predictor; | 649 | 13.5k | let samples = self.samples_per_pixel(); | 650 | | | 651 | 13.5k | let chunk_dims = self.chunk_dimensions()?; | 652 | 13.5k | let data_dims = self.chunk_data_dimensions(chunk_index)?; | 653 | | | 654 | 13.5k | let chunk_row_bits = (u64::from(chunk_dims.0) * u64::from(self.bits_per_sample)) | 655 | 13.5k | .checked_mul(samples as u64) | 656 | 13.5k | .ok_or(TiffError::LimitsExceeded)?; | 657 | 13.5k | let chunk_row_bytes: usize = chunk_row_bits.div_ceil(8).try_into()?; | 658 | | | 659 | 13.5k | let data_row_bits = (u64::from(data_dims.0) * u64::from(self.bits_per_sample)) | 660 | 13.5k | .checked_mul(samples as u64) | 661 | 13.5k | .ok_or(TiffError::LimitsExceeded)?; | 662 | 13.5k | let data_row_bytes: usize = data_row_bits.div_ceil(8).try_into()?; | 663 | | | 664 | | // TODO: Should these return errors instead? | 665 | 13.5k | assert!(output_row_stride >= data_row_bytes); | 666 | 13.5k | assert!(buf.len() >= output_row_stride * (data_dims.1 as usize - 1) + data_row_bytes); | 667 | | | 668 | 13.5k | let mut reader = Self::create_reader( | 669 | 13.5k | reader, | 670 | 13.5k | compression_method, | 671 | 13.5k | *compressed_bytes, | 672 | 13.5k | self.jpeg_tables.as_deref().map(|a| &**a), | 673 | 13.5k | chunk_dims, | 674 | 2.57k | )?; | 675 | | | 676 | 11.0k | if output_row_stride == chunk_row_bytes { | 677 | 1.45k | let tile = &mut buf[..chunk_row_bytes * data_dims.1 as usize]; | 678 | 1.45k | reader.read_exact(tile)?; | 679 | | | 680 | 674k | for row in tile.chunks_mut(chunk_row_bytes) { | 681 | 674k | super::fix_endianness_and_predict( | 682 | 674k | row, | 683 | 674k | color_type.bit_depth(), | 684 | 674k | samples, | 685 | 674k | byte_order, | 686 | 674k | predictor, | 687 | 674k | ); | 688 | 674k | } | 689 | 1.00k | if photometric_interpretation == PhotometricInterpretation::WhiteIsZero { | 690 | 165 | super::invert_colors(tile, color_type, self.sample_format)?; | 691 | 835 | } | 692 | 9.56k | } else if chunk_row_bytes > data_row_bytes && self.predictor == Predictor::FloatingPoint { | 693 | | // The floating point predictor shuffles the padding bytes into the encoded output, so | 694 | | // this case is handled specially when needed. | 695 | 0 | let mut encoded = vec![0u8; chunk_row_bytes]; | 696 | 0 | for row in buf.chunks_mut(output_row_stride).take(data_dims.1 as usize) { | 697 | 0 | reader.read_exact(&mut encoded)?; | 698 | | | 699 | 0 | let row = &mut row[..data_row_bytes]; | 700 | 0 | match color_type.bit_depth() { | 701 | 0 | 16 => predict_f16(&mut encoded, row, samples), | 702 | 0 | 32 => predict_f32(&mut encoded, row, samples), | 703 | 0 | 64 => predict_f64(&mut encoded, row, samples), | 704 | 0 | _ => unreachable!(), | 705 | | } | 706 | 0 | if photometric_interpretation == PhotometricInterpretation::WhiteIsZero { | 707 | 0 | super::invert_colors(row, color_type, self.sample_format)?; | 708 | 0 | } | 709 | | } | 710 | | } else { | 711 | 2.29M | for row in buf.chunks_mut(output_row_stride).take(data_dims.1 as usize) { | 712 | 2.29M | let row = &mut row[..data_row_bytes]; | 713 | 2.29M | reader.read_exact(row)?; | 714 | | | 715 | | // Skip horizontal padding | 716 | 2.29M | if chunk_row_bytes > data_row_bytes { | 717 | 78.6k | let len = u64::try_from(chunk_row_bytes - data_row_bytes)?; | 718 | 78.6k | io::copy(&mut reader.by_ref().take(len), &mut io::sink())?; | 719 | 2.21M | } | 720 | | | 721 | 2.29M | super::fix_endianness_and_predict( | 722 | 2.29M | row, | 723 | 2.29M | color_type.bit_depth(), | 724 | 2.29M | samples, | 725 | 2.29M | byte_order, | 726 | 2.29M | predictor, | 727 | | ); | 728 | 2.29M | if photometric_interpretation == PhotometricInterpretation::WhiteIsZero { | 729 | 164k | super::invert_colors(row, color_type, self.sample_format)?; | 730 | 2.12M | } | 731 | | } | 732 | | } | 733 | | | 734 | 10.1k | Ok(()) | 735 | 13.6k | } |
Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>> Unexecuted instantiation: <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>> <tiff::decoder::image::Image>::expand_chunk::<std::io::cursor::Cursor<&[u8]>> Line | Count | Source | 563 | 6.02k | pub(crate) fn expand_chunk( | 564 | 6.02k | &self, | 565 | 6.02k | reader: &mut ValueReader<impl Read>, | 566 | 6.02k | buf: &mut [u8], | 567 | 6.02k | output_row_stride: usize, | 568 | 6.02k | chunk_index: u32, | 569 | 6.02k | ) -> TiffResult<()> { | 570 | | let ValueReader { | 571 | 6.02k | reader, | 572 | | bigtiff: _, | 573 | 6.02k | limits, | 574 | 6.02k | } = reader; | 575 | | | 576 | 6.02k | let byte_order = reader.byte_order; | 577 | 6.02k | let reader = reader.inner(); | 578 | | | 579 | | // Validate that the color type is supported. | 580 | 6.02k | let color_type = self.colortype()?; | 581 | 2.82k | match color_type { | 582 | 1.05k | ColorType::RGB(n) | 583 | 214 | | ColorType::RGBA(n) | 584 | 20 | | ColorType::CMYK(n) | 585 | 0 | | ColorType::CMYKA(n) | 586 | 0 | | ColorType::YCbCr(n) | 587 | 1.90k | | ColorType::Gray(n) | 588 | | | ColorType::Multiband { | 589 | 0 | bit_depth: n, | 590 | | num_samples: _, | 591 | 4.73k | } if n == 8 || n == 16 || n == 32 || n == 64 => {} | 592 | 2.82k | ColorType::Gray(n) | 593 | | | ColorType::Multiband { | 594 | 0 | bit_depth: n, | 595 | | num_samples: _, | 596 | 2.82k | } if n < 8 => match self.predictor { | 597 | 2.82k | Predictor::None => {} | 598 | | Predictor::Horizontal => { | 599 | 1 | return Err(TiffError::UnsupportedError( | 600 | 1 | TiffUnsupportedError::HorizontalPredictor(color_type), | 601 | 1 | )); | 602 | | } | 603 | | Predictor::FloatingPoint => { | 604 | 1 | return Err(TiffError::UnsupportedError( | 605 | 1 | TiffUnsupportedError::FloatingPointPredictor(color_type), | 606 | 1 | )); | 607 | | } | 608 | | }, | 609 | 0 | type_ => { | 610 | 0 | return Err(TiffError::UnsupportedError( | 611 | 0 | TiffUnsupportedError::UnsupportedColorType(type_), | 612 | 0 | )); | 613 | | } | 614 | | } | 615 | | | 616 | | // Validate that the predictor is supported for the sample type. | 617 | 6.02k | match (self.predictor, self.sample_format) { | 618 | | ( | 619 | | Predictor::Horizontal, | 620 | | SampleFormat::Int | SampleFormat::Uint | SampleFormat::IEEEFP, | 621 | 688 | ) => {} | 622 | | (Predictor::Horizontal, _) => { | 623 | 0 | return Err(TiffError::UnsupportedError( | 624 | 0 | TiffUnsupportedError::HorizontalPredictor(color_type), | 625 | 0 | )); | 626 | | } | 627 | 6 | (Predictor::FloatingPoint, SampleFormat::IEEEFP) => {} | 628 | | (Predictor::FloatingPoint, _) => { | 629 | 3 | return Err(TiffError::UnsupportedError( | 630 | 3 | TiffUnsupportedError::FloatingPointPredictor(color_type), | 631 | 3 | )); | 632 | | } | 633 | 5.32k | _ => {} | 634 | | } | 635 | | | 636 | 6.01k | let compressed_bytes = | 637 | 6.01k | self.chunk_bytes | 638 | 6.01k | .get(chunk_index as usize) | 639 | 6.01k | .ok_or(TiffError::FormatError( | 640 | 6.01k | TiffFormatError::InconsistentSizesEncountered, | 641 | 6.01k | ))?; | 642 | 6.01k | if *compressed_bytes > limits.intermediate_buffer_size as u64 { | 643 | 66 | return Err(TiffError::LimitsExceeded); | 644 | 5.95k | } | 645 | | | 646 | 5.95k | let compression_method = self.compression_method; | 647 | 5.95k | let photometric_interpretation = self.photometric_interpretation; | 648 | 5.95k | let predictor = self.predictor; | 649 | 5.95k | let samples = self.samples_per_pixel(); | 650 | | | 651 | 5.95k | let chunk_dims = self.chunk_dimensions()?; | 652 | 5.95k | let data_dims = self.chunk_data_dimensions(chunk_index)?; | 653 | | | 654 | 5.95k | let chunk_row_bits = (u64::from(chunk_dims.0) * u64::from(self.bits_per_sample)) | 655 | 5.95k | .checked_mul(samples as u64) | 656 | 5.95k | .ok_or(TiffError::LimitsExceeded)?; | 657 | 5.95k | let chunk_row_bytes: usize = chunk_row_bits.div_ceil(8).try_into()?; | 658 | | | 659 | 5.95k | let data_row_bits = (u64::from(data_dims.0) * u64::from(self.bits_per_sample)) | 660 | 5.95k | .checked_mul(samples as u64) | 661 | 5.95k | .ok_or(TiffError::LimitsExceeded)?; | 662 | 5.95k | let data_row_bytes: usize = data_row_bits.div_ceil(8).try_into()?; | 663 | | | 664 | | // TODO: Should these return errors instead? | 665 | 5.95k | assert!(output_row_stride >= data_row_bytes); | 666 | 5.95k | assert!(buf.len() >= output_row_stride * (data_dims.1 as usize - 1) + data_row_bytes); | 667 | | | 668 | 5.95k | let mut reader = Self::create_reader( | 669 | 5.95k | reader, | 670 | 5.95k | compression_method, | 671 | 5.95k | *compressed_bytes, | 672 | 5.95k | self.jpeg_tables.as_deref().map(|a| &**a), | 673 | 5.95k | chunk_dims, | 674 | 1.95k | )?; | 675 | | | 676 | 3.99k | if output_row_stride == chunk_row_bytes { | 677 | 2.15k | let tile = &mut buf[..chunk_row_bytes * data_dims.1 as usize]; | 678 | 2.15k | reader.read_exact(tile)?; | 679 | | | 680 | 19.7k | for row in tile.chunks_mut(chunk_row_bytes) { | 681 | 19.7k | super::fix_endianness_and_predict( | 682 | 19.7k | row, | 683 | 19.7k | color_type.bit_depth(), | 684 | 19.7k | samples, | 685 | 19.7k | byte_order, | 686 | 19.7k | predictor, | 687 | 19.7k | ); | 688 | 19.7k | } | 689 | 1.44k | if photometric_interpretation == PhotometricInterpretation::WhiteIsZero { | 690 | 515 | super::invert_colors(tile, color_type, self.sample_format)?; | 691 | 934 | } | 692 | 1.84k | } else if chunk_row_bytes > data_row_bytes && self.predictor == Predictor::FloatingPoint { | 693 | | // The floating point predictor shuffles the padding bytes into the encoded output, so | 694 | | // this case is handled specially when needed. | 695 | 0 | let mut encoded = vec![0u8; chunk_row_bytes]; | 696 | 0 | for row in buf.chunks_mut(output_row_stride).take(data_dims.1 as usize) { | 697 | 0 | reader.read_exact(&mut encoded)?; | 698 | | | 699 | 0 | let row = &mut row[..data_row_bytes]; | 700 | 0 | match color_type.bit_depth() { | 701 | 0 | 16 => predict_f16(&mut encoded, row, samples), | 702 | 0 | 32 => predict_f32(&mut encoded, row, samples), | 703 | 0 | 64 => predict_f64(&mut encoded, row, samples), | 704 | 0 | _ => unreachable!(), | 705 | | } | 706 | 0 | if photometric_interpretation == PhotometricInterpretation::WhiteIsZero { | 707 | 0 | super::invert_colors(row, color_type, self.sample_format)?; | 708 | 0 | } | 709 | | } | 710 | | } else { | 711 | 297k | for row in buf.chunks_mut(output_row_stride).take(data_dims.1 as usize) { | 712 | 297k | let row = &mut row[..data_row_bytes]; | 713 | 297k | reader.read_exact(row)?; | 714 | | | 715 | | // Skip horizontal padding | 716 | 296k | if chunk_row_bytes > data_row_bytes { | 717 | 32.9k | let len = u64::try_from(chunk_row_bytes - data_row_bytes)?; | 718 | 32.9k | io::copy(&mut reader.by_ref().take(len), &mut io::sink())?; | 719 | 263k | } | 720 | | | 721 | 296k | super::fix_endianness_and_predict( | 722 | 296k | row, | 723 | 296k | color_type.bit_depth(), | 724 | 296k | samples, | 725 | 296k | byte_order, | 726 | 296k | predictor, | 727 | | ); | 728 | 296k | if photometric_interpretation == PhotometricInterpretation::WhiteIsZero { | 729 | 3.72k | super::invert_colors(row, color_type, self.sample_format)?; | 730 | 292k | } | 731 | | } | 732 | | } | 733 | | | 734 | 2.25k | Ok(()) | 735 | 6.02k | } |
|
736 | | } |