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 window_col_end = window.col_end();
35 let output_row_bytes = window.cols * layout.pixel_stride_bytes();
36
37 let specs = collect_tile_specs(ifd, &layout)?;
38 let relevant_specs: Vec<_> = specs
39 .iter()
40 .copied()
41 .filter(|spec| {
42 let spec_row_end = spec.y + spec.rows_in_tile;
43 let spec_col_end = spec.x + spec.cols_in_tile;
44 spec.y < window_row_end
45 && spec_row_end > window.row_off
46 && spec.x < window_col_end
47 && spec_col_end > window.col_off
48 })
49 .collect();
50
51 #[cfg(feature = "rayon")]
52 let decoded_blocks: Result<Vec<_>> = relevant_specs
53 .par_iter()
54 .map(|&spec| {
55 read_tile_block(
56 source,
57 ifd,
58 byte_order,
59 cache,
60 spec,
61 &layout,
62 gdal_structural_metadata,
63 )
64 .map(|block| (spec, block))
65 })
66 .collect();
67
68 #[cfg(not(feature = "rayon"))]
69 let decoded_blocks: Result<Vec<_>> = relevant_specs
70 .iter()
71 .map(|&spec| {
72 read_tile_block(
73 source,
74 ifd,
75 byte_order,
76 cache,
77 spec,
78 &layout,
79 gdal_structural_metadata,
80 )
81 .map(|block| (spec, block))
82 })
83 .collect();
84
85 for (spec, block) in decoded_blocks? {
86 let block = &*block;
87 let copy_row_start = spec.y.max(window.row_off);
88 let copy_row_end = (spec.y + spec.rows_in_tile).min(window_row_end);
89 let copy_col_start = spec.x.max(window.col_off);
90 let copy_col_end = (spec.x + spec.cols_in_tile).min(window_col_end);
91
92 let src_row_bytes = spec.tile_width
93 * if layout.planar_configuration == 1 {
94 layout.pixel_stride_bytes()
95 } else {
96 layout.bytes_per_sample
97 };
98
99 if layout.planar_configuration == 1 {
100 let copy_bytes_per_row = (copy_col_end - copy_col_start) * layout.pixel_stride_bytes();
101 for row in copy_row_start..copy_row_end {
102 let src_row_index = row - spec.y;
103 let dest_row_index = row - window.row_off;
104 let src_offset = src_row_index * src_row_bytes
105 + (copy_col_start - spec.x) * layout.pixel_stride_bytes();
106 let dest_offset = dest_row_index * output_row_bytes
107 + (copy_col_start - window.col_off) * layout.pixel_stride_bytes();
108 output[dest_offset..dest_offset + copy_bytes_per_row]
109 .copy_from_slice(&block[src_offset..src_offset + copy_bytes_per_row]);
110 }
111 } else {
112 for row in copy_row_start..copy_row_end {
113 let src_row_index = row - spec.y;
114 let dest_row_index = row - window.row_off;
115 let src_row =
116 &block[src_row_index * src_row_bytes..(src_row_index + 1) * src_row_bytes];
117 let dest_row = &mut output
118 [dest_row_index * output_row_bytes..(dest_row_index + 1) * output_row_bytes];
119 for col in copy_col_start..copy_col_end {
120 let src = &src_row[(col - spec.x) * layout.bytes_per_sample
121 ..(col - spec.x + 1) * layout.bytes_per_sample];
122 let pixel_base = (col - window.col_off) * layout.pixel_stride_bytes()
123 + spec.plane * layout.bytes_per_sample;
124 dest_row[pixel_base..pixel_base + layout.bytes_per_sample].copy_from_slice(src);
125 }
126 }
127 }
128 }
129
130 Ok(output)
131}
132
133fn collect_tile_specs(ifd: &Ifd, layout: &RasterLayout) -> Result<Vec<TileBlockSpec>> {
134 let tile_width = ifd
135 .tile_width()
136 .ok_or(Error::TagNotFound(crate::ifd::TAG_TILE_WIDTH))? as usize;
137 let tile_height = ifd
138 .tile_height()
139 .ok_or(Error::TagNotFound(crate::ifd::TAG_TILE_LENGTH))? as usize;
140 if tile_width == 0 || tile_height == 0 {
141 return Err(Error::InvalidImageLayout(
142 "tile width and height must be greater than zero".into(),
143 ));
144 }
145
146 let offsets = ifd
147 .tile_offsets()
148 .ok_or(Error::TagNotFound(crate::ifd::TAG_TILE_OFFSETS))?;
149 let counts = ifd
150 .tile_byte_counts()
151 .ok_or(Error::TagNotFound(crate::ifd::TAG_TILE_BYTE_COUNTS))?;
152 if offsets.len() != counts.len() {
153 return Err(Error::InvalidImageLayout(format!(
154 "TileOffsets has {} entries but TileByteCounts has {}",
155 offsets.len(),
156 counts.len()
157 )));
158 }
159
160 let tiles_across = layout.width.div_ceil(tile_width);
161 let tiles_down = layout.height.div_ceil(tile_height);
162 let tiles_per_plane = tiles_across * tiles_down;
163 let expected = match layout.planar_configuration {
164 1 => tiles_per_plane,
165 2 => tiles_per_plane * layout.samples_per_pixel,
166 planar => return Err(Error::UnsupportedPlanarConfiguration(planar)),
167 };
168 if offsets.len() != expected {
169 return Err(Error::InvalidImageLayout(format!(
170 "expected {expected} tiles, found {}",
171 offsets.len()
172 )));
173 }
174
175 Ok((0..expected)
176 .map(|tile_index| {
177 let plane = if layout.planar_configuration == 1 {
178 0
179 } else {
180 tile_index / tiles_per_plane
181 };
182 let plane_tile_index = if layout.planar_configuration == 1 {
183 tile_index
184 } else {
185 tile_index % tiles_per_plane
186 };
187 let tile_row = plane_tile_index / tiles_across;
188 let tile_col = plane_tile_index % tiles_across;
189 let x = tile_col * tile_width;
190 let y = tile_row * tile_height;
191 let cols_in_tile = tile_width.min(layout.width.saturating_sub(x));
192 let rows_in_tile = tile_height.min(layout.height.saturating_sub(y));
193 TileBlockSpec {
194 index: tile_index,
195 plane,
196 x,
197 y,
198 cols_in_tile,
199 rows_in_tile,
200 offset: offsets[tile_index],
201 byte_count: counts[tile_index],
202 tile_width,
203 tile_height,
204 }
205 })
206 .collect())
207}
208
209#[derive(Clone, Copy)]
210struct TileBlockSpec {
211 index: usize,
212 plane: usize,
213 x: usize,
214 y: usize,
215 cols_in_tile: usize,
216 rows_in_tile: usize,
217 offset: u64,
218 byte_count: u64,
219 tile_width: usize,
220 tile_height: usize,
221}
222
223fn read_tile_block(
224 source: &dyn TiffSource,
225 ifd: &Ifd,
226 byte_order: ByteOrder,
227 cache: &BlockCache,
228 spec: TileBlockSpec,
229 layout: &RasterLayout,
230 gdal_structural_metadata: Option<&GdalStructuralMetadata>,
231) -> Result<Arc<Vec<u8>>> {
232 let cache_key = BlockKey {
233 ifd_index: ifd.index,
234 kind: BlockKind::Tile,
235 block_index: spec.index,
236 };
237 if let Some(cached) = cache.get(&cache_key) {
238 return Ok(cached);
239 }
240
241 let compressed = if let Some(bytes) = source.as_slice() {
242 let start = usize::try_from(spec.offset).map_err(|_| Error::OffsetOutOfBounds {
243 offset: spec.offset,
244 length: spec.byte_count,
245 data_len: bytes.len() as u64,
246 })?;
247 let len = usize::try_from(spec.byte_count).map_err(|_| Error::OffsetOutOfBounds {
248 offset: spec.offset,
249 length: spec.byte_count,
250 data_len: bytes.len() as u64,
251 })?;
252 let end = start.checked_add(len).ok_or(Error::OffsetOutOfBounds {
253 offset: spec.offset,
254 length: spec.byte_count,
255 data_len: bytes.len() as u64,
256 })?;
257 if end > bytes.len() {
258 return Err(Error::OffsetOutOfBounds {
259 offset: spec.offset,
260 length: spec.byte_count,
261 data_len: bytes.len() as u64,
262 });
263 }
264 bytes[start..end].to_vec()
265 } else {
266 let len = usize::try_from(spec.byte_count).map_err(|_| Error::OffsetOutOfBounds {
267 offset: spec.offset,
268 length: spec.byte_count,
269 data_len: source.len(),
270 })?;
271 source.read_exact_at(spec.offset, len)?
272 };
273
274 let compressed = match gdal_structural_metadata {
275 Some(metadata) => metadata
276 .unwrap_block(&compressed, byte_order, spec.offset)?
277 .to_vec(),
278 None => compressed,
279 };
280
281 let jpeg_tables = ifd
282 .tag(TAG_JPEG_TABLES)
283 .and_then(|tag| tag.value.as_bytes());
284 let samples = if layout.planar_configuration == 1 {
285 layout.samples_per_pixel
286 } else {
287 1
288 };
289 let row_bytes = spec.tile_width * samples * layout.bytes_per_sample;
290 let expected_len = spec
291 .tile_height
292 .checked_mul(row_bytes)
293 .ok_or_else(|| Error::InvalidImageLayout("tile size overflows usize".into()))?;
294 let mut decoded = filters::decompress(
295 ifd.compression(),
296 &compressed,
297 spec.index,
298 jpeg_tables,
299 expected_len,
300 )?;
301 if decoded.len() < expected_len {
302 return Err(Error::DecompressionFailed {
303 index: spec.index,
304 reason: format!(
305 "decoded tile is too small: expected at least {expected_len} bytes, found {}",
306 decoded.len()
307 ),
308 });
309 }
310 if decoded.len() > expected_len {
311 decoded.truncate(expected_len);
312 }
313
314 for row in decoded.chunks_exact_mut(row_bytes) {
315 filters::fix_endianness_and_predict(
316 row,
317 layout.bits_per_sample,
318 samples as u16,
319 byte_order,
320 layout.predictor,
321 )?;
322 }
323
324 Ok(cache.insert(cache_key, decoded))
325}