1use std::sync::Arc;
4
5#[cfg(feature = "rayon")]
6use rayon::prelude::*;
7
8use crate::cache::{BlockCache, BlockKey, BlockKind};
9use crate::error::{Error, Result};
10use crate::filters;
11use crate::header::ByteOrder;
12use crate::ifd::{Ifd, RasterLayout};
13use crate::source::TiffSource;
14use crate::{GdalStructuralMetadata, Window};
15
16const TAG_JPEG_TABLES: u16 = 347;
17
18pub(crate) fn read_window(
19 source: &dyn TiffSource,
20 ifd: &Ifd,
21 byte_order: ByteOrder,
22 cache: &BlockCache,
23 window: Window,
24 gdal_structural_metadata: Option<&GdalStructuralMetadata>,
25) -> Result<Vec<u8>> {
26 let layout = ifd.raster_layout()?;
27 if window.is_empty() {
28 return Ok(Vec::new());
29 }
30
31 let output_len = window.output_len(&layout)?;
32 let mut output = vec![0u8; output_len];
33 let window_row_end = window.row_end();
34 let output_row_bytes = window.cols * layout.pixel_stride_bytes();
35
36 let specs = collect_strip_specs(ifd, &layout)?;
37 let relevant_specs: Vec<_> = specs
38 .iter()
39 .copied()
40 .filter(|spec| {
41 let spec_row_end = spec.row_start + spec.rows_in_strip;
42 spec.row_start < window_row_end && spec_row_end > window.row_off
43 })
44 .collect();
45
46 #[cfg(not(feature = "rayon"))]
47 let decoded_blocks: Result<Vec<_>> = relevant_specs
48 .iter()
49 .map(|&spec| {
50 read_strip_block(
51 source,
52 ifd,
53 byte_order,
54 cache,
55 spec,
56 &layout,
57 gdal_structural_metadata,
58 )
59 .map(|block| (spec, block))
60 })
61 .collect();
62
63 #[cfg(feature = "rayon")]
64 let decoded_blocks: Result<Vec<_>> = relevant_specs
65 .par_iter()
66 .map(|&spec| {
67 read_strip_block(
68 source,
69 ifd,
70 byte_order,
71 cache,
72 spec,
73 &layout,
74 gdal_structural_metadata,
75 )
76 .map(|block| (spec, block))
77 })
78 .collect();
79
80 for (spec, block) in decoded_blocks? {
81 let block = &*block;
82 let block_row_end = spec.row_start + spec.rows_in_strip;
83 let copy_row_start = spec.row_start.max(window.row_off);
84 let copy_row_end = block_row_end.min(window_row_end);
85
86 if layout.planar_configuration == 1 {
87 let src_row_bytes = layout.row_bytes();
88 let copy_bytes_per_row = window.cols * layout.pixel_stride_bytes();
89 for row in copy_row_start..copy_row_end {
90 let src_row_index = row - spec.row_start;
91 let dest_row_index = row - window.row_off;
92 let src_offset =
93 src_row_index * src_row_bytes + window.col_off * layout.pixel_stride_bytes();
94 let dest_offset = dest_row_index * output_row_bytes;
95 output[dest_offset..dest_offset + copy_bytes_per_row]
96 .copy_from_slice(&block[src_offset..src_offset + copy_bytes_per_row]);
97 }
98 } else {
99 let src_row_bytes = layout.sample_plane_row_bytes();
100 for row in copy_row_start..copy_row_end {
101 let src_row_index = row - spec.row_start;
102 let dest_row_index = row - window.row_off;
103 let src_row =
104 &block[src_row_index * src_row_bytes..(src_row_index + 1) * src_row_bytes];
105 let dest_row = &mut output
106 [dest_row_index * output_row_bytes..(dest_row_index + 1) * output_row_bytes];
107 for col in window.col_off..window.col_end() {
108 let src = &src_row
109 [col * layout.bytes_per_sample..(col + 1) * layout.bytes_per_sample];
110 let dest_col_index = col - window.col_off;
111 let pixel_base = dest_col_index * layout.pixel_stride_bytes()
112 + spec.plane * layout.bytes_per_sample;
113 dest_row[pixel_base..pixel_base + layout.bytes_per_sample].copy_from_slice(src);
114 }
115 }
116 }
117 }
118
119 Ok(output)
120}
121
122fn collect_strip_specs(ifd: &Ifd, layout: &RasterLayout) -> Result<Vec<StripBlockSpec>> {
123 let offsets = ifd
124 .strip_offsets()
125 .ok_or(Error::TagNotFound(crate::ifd::TAG_STRIP_OFFSETS))?;
126 let counts = ifd
127 .strip_byte_counts()
128 .ok_or(Error::TagNotFound(crate::ifd::TAG_STRIP_BYTE_COUNTS))?;
129 if offsets.len() != counts.len() {
130 return Err(Error::InvalidImageLayout(format!(
131 "StripOffsets has {} entries but StripByteCounts has {}",
132 offsets.len(),
133 counts.len()
134 )));
135 }
136
137 let rows_per_strip = ifd.rows_per_strip().unwrap_or(ifd.height());
138 if rows_per_strip == 0 {
139 return Err(Error::InvalidImageLayout(
140 "RowsPerStrip must be greater than zero".into(),
141 ));
142 }
143 let rows_per_strip = rows_per_strip as usize;
144 let strips_per_plane = layout.height.div_ceil(rows_per_strip);
145 let expected = match layout.planar_configuration {
146 1 => strips_per_plane,
147 2 => strips_per_plane * layout.samples_per_pixel,
148 planar => return Err(Error::UnsupportedPlanarConfiguration(planar)),
149 };
150 if offsets.len() != expected {
151 return Err(Error::InvalidImageLayout(format!(
152 "expected {expected} strips, found {}",
153 offsets.len()
154 )));
155 }
156
157 Ok((0..expected)
158 .map(|strip_index| {
159 let plane = if layout.planar_configuration == 1 {
160 0
161 } else {
162 strip_index / strips_per_plane
163 };
164 let plane_strip_index = if layout.planar_configuration == 1 {
165 strip_index
166 } else {
167 strip_index % strips_per_plane
168 };
169 let row_start = plane_strip_index * rows_per_strip;
170 let rows_in_strip = rows_per_strip.min(layout.height.saturating_sub(row_start));
171 StripBlockSpec {
172 index: strip_index,
173 plane,
174 row_start,
175 offset: offsets[strip_index],
176 byte_count: counts[strip_index],
177 rows_in_strip,
178 }
179 })
180 .collect())
181}
182
183#[derive(Clone, Copy)]
184struct StripBlockSpec {
185 index: usize,
186 plane: usize,
187 row_start: usize,
188 offset: u64,
189 byte_count: u64,
190 rows_in_strip: usize,
191}
192
193fn read_strip_block(
194 source: &dyn TiffSource,
195 ifd: &Ifd,
196 byte_order: ByteOrder,
197 cache: &BlockCache,
198 spec: StripBlockSpec,
199 layout: &RasterLayout,
200 gdal_structural_metadata: Option<&GdalStructuralMetadata>,
201) -> Result<Arc<Vec<u8>>> {
202 let cache_key = BlockKey {
203 ifd_index: ifd.index,
204 kind: BlockKind::Strip,
205 block_index: spec.index,
206 };
207 if let Some(cached) = cache.get(&cache_key) {
208 return Ok(cached);
209 }
210
211 let compressed = if let Some(bytes) = source.as_slice() {
212 let start = usize::try_from(spec.offset).map_err(|_| Error::OffsetOutOfBounds {
213 offset: spec.offset,
214 length: spec.byte_count,
215 data_len: bytes.len() as u64,
216 })?;
217 let len = usize::try_from(spec.byte_count).map_err(|_| Error::OffsetOutOfBounds {
218 offset: spec.offset,
219 length: spec.byte_count,
220 data_len: bytes.len() as u64,
221 })?;
222 let end = start.checked_add(len).ok_or(Error::OffsetOutOfBounds {
223 offset: spec.offset,
224 length: spec.byte_count,
225 data_len: bytes.len() as u64,
226 })?;
227 if end > bytes.len() {
228 return Err(Error::OffsetOutOfBounds {
229 offset: spec.offset,
230 length: spec.byte_count,
231 data_len: bytes.len() as u64,
232 });
233 }
234 bytes[start..end].to_vec()
235 } else {
236 let len = usize::try_from(spec.byte_count).map_err(|_| Error::OffsetOutOfBounds {
237 offset: spec.offset,
238 length: spec.byte_count,
239 data_len: source.len(),
240 })?;
241 source.read_exact_at(spec.offset, len)?
242 };
243
244 let compressed = match gdal_structural_metadata {
245 Some(metadata) => metadata
246 .unwrap_block(&compressed, byte_order, spec.offset)?
247 .to_vec(),
248 None => compressed,
249 };
250
251 let jpeg_tables = ifd
252 .tag(TAG_JPEG_TABLES)
253 .and_then(|tag| tag.value.as_bytes());
254 let samples = if layout.planar_configuration == 1 {
255 layout.samples_per_pixel
256 } else {
257 1
258 };
259 let expected_row_bytes = layout.width * samples * layout.bytes_per_sample;
260 let expected_len = spec
261 .rows_in_strip
262 .checked_mul(expected_row_bytes)
263 .ok_or_else(|| Error::InvalidImageLayout("strip size overflows usize".into()))?;
264 let mut decoded = filters::decompress(
265 ifd.compression(),
266 &compressed,
267 spec.index,
268 jpeg_tables,
269 expected_len,
270 )?;
271 if decoded.len() < expected_len {
272 return Err(Error::DecompressionFailed {
273 index: spec.index,
274 reason: format!(
275 "decoded strip is too small: expected at least {expected_len} bytes, found {}",
276 decoded.len()
277 ),
278 });
279 }
280 if decoded.len() > expected_len {
281 decoded.truncate(expected_len);
282 }
283
284 for row in decoded.chunks_exact_mut(expected_row_bytes) {
285 filters::fix_endianness_and_predict(
286 row,
287 layout.bits_per_sample,
288 samples as u16,
289 byte_order,
290 layout.predictor,
291 )?;
292 }
293
294 Ok(cache.insert(cache_key, decoded))
295}