1use std::sync::Arc;
4
5#[cfg(feature = "rayon")]
6use rayon::prelude::*;
7
8use crate::block_decode;
9use crate::cache::{BlockCache, BlockKey, BlockKind};
10use crate::error::{Error, Result};
11use crate::header::ByteOrder;
12use crate::ifd::{Ifd, RasterLayout};
13use crate::source::TiffSource;
14use crate::{
15 allocate_decode_output, read_block_payload, read_gdal_block_payload, DecodeReadOptions,
16 GdalStructuralMetadata, Window,
17};
18
19const TAG_JPEG_TABLES: u16 = 347;
20
21pub(crate) fn read_window(
22 source: &dyn TiffSource,
23 ifd: &Ifd,
24 byte_order: ByteOrder,
25 cache: &BlockCache,
26 window: Window,
27 options: DecodeReadOptions<'_>,
28) -> Result<Vec<u8>> {
29 let layout = ifd.raster_layout()?;
30 if window.is_empty() {
31 return Ok(Vec::new());
32 }
33
34 let output_len = window.output_len(&layout)?;
35 let mut output = allocate_decode_output(output_len, options.decode_output_bytes)?;
36 let window_row_end = window.row_end();
37 let window_col_end = window.col_end();
38 let output_row_bytes = window.cols * layout.pixel_stride_bytes();
39
40 let relevant_specs = collect_tile_specs_for_window(ifd, &layout, window, None)?;
41
42 #[cfg(feature = "rayon")]
43 let decoded_blocks: Result<Vec<_>> = relevant_specs
44 .par_iter()
45 .map(|&spec| {
46 read_tile_block(
47 source,
48 ifd,
49 byte_order,
50 cache,
51 spec,
52 &layout,
53 options.gdal_structural_metadata,
54 )
55 .map(|block| (spec, block))
56 })
57 .collect();
58
59 #[cfg(not(feature = "rayon"))]
60 let decoded_blocks: Result<Vec<_>> = relevant_specs
61 .iter()
62 .map(|&spec| {
63 read_tile_block(
64 source,
65 ifd,
66 byte_order,
67 cache,
68 spec,
69 &layout,
70 options.gdal_structural_metadata,
71 )
72 .map(|block| (spec, block))
73 })
74 .collect();
75
76 for (spec, block) in decoded_blocks? {
77 let block = &*block;
78 let copy_row_start = spec.y.max(window.row_off);
79 let copy_row_end = (spec.y + spec.rows_in_tile).min(window_row_end);
80 let copy_col_start = spec.x.max(window.col_off);
81 let copy_col_end = (spec.x + spec.cols_in_tile).min(window_col_end);
82
83 let src_row_bytes = spec.tile_width
84 * if layout.planar_configuration == 1 {
85 layout.pixel_stride_bytes()
86 } else {
87 layout.bytes_per_sample
88 };
89
90 if layout.planar_configuration == 1 {
91 let copy_bytes_per_row = (copy_col_end - copy_col_start) * layout.pixel_stride_bytes();
92 for row in copy_row_start..copy_row_end {
93 let src_row_index = row - spec.y;
94 let dest_row_index = row - window.row_off;
95 let src_offset = src_row_index * src_row_bytes
96 + (copy_col_start - spec.x) * layout.pixel_stride_bytes();
97 let dest_offset = dest_row_index * output_row_bytes
98 + (copy_col_start - window.col_off) * layout.pixel_stride_bytes();
99 output[dest_offset..dest_offset + copy_bytes_per_row]
100 .copy_from_slice(&block[src_offset..src_offset + copy_bytes_per_row]);
101 }
102 } else {
103 for row in copy_row_start..copy_row_end {
104 let src_row_index = row - spec.y;
105 let dest_row_index = row - window.row_off;
106 let src_row =
107 &block[src_row_index * src_row_bytes..(src_row_index + 1) * src_row_bytes];
108 let dest_row = &mut output
109 [dest_row_index * output_row_bytes..(dest_row_index + 1) * output_row_bytes];
110 for col in copy_col_start..copy_col_end {
111 let src = &src_row[(col - spec.x) * layout.bytes_per_sample
112 ..(col - spec.x + 1) * layout.bytes_per_sample];
113 let pixel_base = (col - window.col_off) * layout.pixel_stride_bytes()
114 + spec.plane * layout.bytes_per_sample;
115 dest_row[pixel_base..pixel_base + layout.bytes_per_sample].copy_from_slice(src);
116 }
117 }
118 }
119 }
120
121 Ok(output)
122}
123
124pub(crate) fn read_window_band(
125 source: &dyn TiffSource,
126 ifd: &Ifd,
127 byte_order: ByteOrder,
128 cache: &BlockCache,
129 window: Window,
130 band_index: usize,
131 options: DecodeReadOptions<'_>,
132) -> Result<Vec<u8>> {
133 let layout = ifd.raster_layout()?;
134 if band_index >= layout.samples_per_pixel {
135 return Err(Error::BandIndexOutOfBounds {
136 index: band_index,
137 band_count: layout.samples_per_pixel,
138 });
139 }
140 if window.is_empty() {
141 return Ok(Vec::new());
142 }
143
144 let output_len = window.band_output_len(&layout)?;
145 let mut output = allocate_decode_output(output_len, options.decode_output_bytes)?;
146 let window_row_end = window.row_end();
147 let window_col_end = window.col_end();
148 let output_row_bytes = window.cols * layout.bytes_per_sample;
149
150 let relevant_specs = collect_tile_specs_for_window(ifd, &layout, window, Some(band_index))?;
151
152 #[cfg(feature = "rayon")]
153 let decoded_blocks: Result<Vec<_>> = relevant_specs
154 .par_iter()
155 .map(|&spec| {
156 read_tile_block(
157 source,
158 ifd,
159 byte_order,
160 cache,
161 spec,
162 &layout,
163 options.gdal_structural_metadata,
164 )
165 .map(|block| (spec, block))
166 })
167 .collect();
168
169 #[cfg(not(feature = "rayon"))]
170 let decoded_blocks: Result<Vec<_>> = relevant_specs
171 .iter()
172 .map(|&spec| {
173 read_tile_block(
174 source,
175 ifd,
176 byte_order,
177 cache,
178 spec,
179 &layout,
180 options.gdal_structural_metadata,
181 )
182 .map(|block| (spec, block))
183 })
184 .collect();
185
186 for (spec, block) in decoded_blocks? {
187 let block = &*block;
188 let copy_row_start = spec.y.max(window.row_off);
189 let copy_row_end = (spec.y + spec.rows_in_tile).min(window_row_end);
190 let copy_col_start = spec.x.max(window.col_off);
191 let copy_col_end = (spec.x + spec.cols_in_tile).min(window_col_end);
192
193 let src_row_bytes = spec.tile_width
194 * if layout.planar_configuration == 1 {
195 layout.pixel_stride_bytes()
196 } else {
197 layout.bytes_per_sample
198 };
199
200 if layout.planar_configuration == 1 {
201 let band_offset = band_index * layout.bytes_per_sample;
202 for row in copy_row_start..copy_row_end {
203 let src_row_index = row - spec.y;
204 let dest_row_index = row - window.row_off;
205 let src_row =
206 &block[src_row_index * src_row_bytes..(src_row_index + 1) * src_row_bytes];
207 let dest_row = &mut output
208 [dest_row_index * output_row_bytes..(dest_row_index + 1) * output_row_bytes];
209 for col in copy_col_start..copy_col_end {
210 let src_base = (col - spec.x) * layout.pixel_stride_bytes() + band_offset;
211 let dest_col_index = col - window.col_off;
212 let dest_base = dest_col_index * layout.bytes_per_sample;
213 dest_row[dest_base..dest_base + layout.bytes_per_sample]
214 .copy_from_slice(&src_row[src_base..src_base + layout.bytes_per_sample]);
215 }
216 }
217 } else {
218 let copy_bytes_per_row = (copy_col_end - copy_col_start) * layout.bytes_per_sample;
219 for row in copy_row_start..copy_row_end {
220 let src_row_index = row - spec.y;
221 let dest_row_index = row - window.row_off;
222 let src_offset = src_row_index * src_row_bytes
223 + (copy_col_start - spec.x) * layout.bytes_per_sample;
224 let dest_offset = dest_row_index * output_row_bytes
225 + (copy_col_start - window.col_off) * layout.bytes_per_sample;
226 output[dest_offset..dest_offset + copy_bytes_per_row]
227 .copy_from_slice(&block[src_offset..src_offset + copy_bytes_per_row]);
228 }
229 }
230 }
231
232 Ok(output)
233}
234
235fn collect_tile_specs_for_window(
236 ifd: &Ifd,
237 layout: &RasterLayout,
238 window: Window,
239 band_index: Option<usize>,
240) -> Result<Vec<TileBlockSpec>> {
241 let tile_width = ifd
242 .tile_width()
243 .ok_or(Error::TagNotFound(crate::ifd::TAG_TILE_WIDTH))? as usize;
244 let tile_height = ifd
245 .tile_height()
246 .ok_or(Error::TagNotFound(crate::ifd::TAG_TILE_LENGTH))? as usize;
247 if tile_width == 0 || tile_height == 0 {
248 return Err(Error::InvalidImageLayout(
249 "tile width and height must be greater than zero".into(),
250 ));
251 }
252
253 let offsets = ifd
254 .tile_offsets()
255 .ok_or(Error::TagNotFound(crate::ifd::TAG_TILE_OFFSETS))?;
256 let counts = ifd
257 .tile_byte_counts()
258 .ok_or(Error::TagNotFound(crate::ifd::TAG_TILE_BYTE_COUNTS))?;
259 if offsets.len() != counts.len() {
260 return Err(Error::InvalidImageLayout(format!(
261 "TileOffsets has {} entries but TileByteCounts has {}",
262 offsets.len(),
263 counts.len()
264 )));
265 }
266
267 let tiles_across = layout.width.div_ceil(tile_width);
268 let tiles_down = layout.height.div_ceil(tile_height);
269 let tiles_per_plane = tiles_across
270 .checked_mul(tiles_down)
271 .ok_or_else(tile_count_overflow)?;
272 let expected = match layout.planar_configuration {
273 1 => tiles_per_plane,
274 2 => tiles_per_plane
275 .checked_mul(layout.samples_per_pixel)
276 .ok_or_else(tile_count_overflow)?,
277 planar => return Err(Error::UnsupportedPlanarConfiguration(planar)),
278 };
279 if offsets.len() != expected {
280 return Err(Error::InvalidImageLayout(format!(
281 "expected {expected} tiles, found {}",
282 offsets.len()
283 )));
284 }
285
286 let first_tile_row = window.row_off / tile_height;
287 let last_tile_row = window.row_end().div_ceil(tile_height).min(tiles_down);
288 let first_tile_col = window.col_off / tile_width;
289 let last_tile_col = window.col_end().div_ceil(tile_width).min(tiles_across);
290 let plane_range = if layout.planar_configuration == 1 {
291 0..1
292 } else if let Some(band_index) = band_index {
293 band_index..band_index + 1
294 } else {
295 0..layout.samples_per_pixel
296 };
297 let spec_count = (last_tile_row - first_tile_row)
298 .saturating_mul(last_tile_col - first_tile_col)
299 .saturating_mul(plane_range.end - plane_range.start);
300 let mut specs = Vec::with_capacity(spec_count);
301
302 for plane in plane_range {
303 for tile_row in first_tile_row..last_tile_row {
304 for tile_col in first_tile_col..last_tile_col {
305 let plane_tile_index = tile_row
306 .checked_mul(tiles_across)
307 .and_then(|base| base.checked_add(tile_col))
308 .ok_or_else(tile_count_overflow)?;
309 let tile_index = if layout.planar_configuration == 1 {
310 plane_tile_index
311 } else {
312 plane
313 .checked_mul(tiles_per_plane)
314 .and_then(|base| base.checked_add(plane_tile_index))
315 .ok_or_else(tile_count_overflow)?
316 };
317 let x = tile_col * tile_width;
318 let y = tile_row * tile_height;
319 let cols_in_tile = tile_width.min(layout.width.saturating_sub(x));
320 let rows_in_tile = tile_height.min(layout.height.saturating_sub(y));
321 specs.push(TileBlockSpec {
322 index: tile_index,
323 plane,
324 x,
325 y,
326 cols_in_tile,
327 rows_in_tile,
328 offset: offsets[tile_index],
329 byte_count: counts[tile_index],
330 tile_width,
331 tile_height,
332 });
333 }
334 }
335 }
336
337 Ok(specs)
338}
339
340fn tile_count_overflow() -> Error {
341 Error::InvalidImageLayout("tile count overflows usize".into())
342}
343
344#[derive(Clone, Copy)]
345struct TileBlockSpec {
346 index: usize,
347 plane: usize,
348 x: usize,
349 y: usize,
350 cols_in_tile: usize,
351 rows_in_tile: usize,
352 offset: u64,
353 byte_count: u64,
354 tile_width: usize,
355 tile_height: usize,
356}
357
358fn read_tile_block(
359 source: &dyn TiffSource,
360 ifd: &Ifd,
361 byte_order: ByteOrder,
362 cache: &BlockCache,
363 spec: TileBlockSpec,
364 layout: &RasterLayout,
365 gdal_structural_metadata: Option<&GdalStructuralMetadata>,
366) -> Result<Arc<Vec<u8>>> {
367 let cache_key = BlockKey {
368 ifd_index: ifd.index,
369 kind: BlockKind::Tile,
370 block_index: spec.index,
371 };
372 if let Some(cached) = cache.get(&cache_key) {
373 return Ok(cached);
374 }
375
376 let jpeg_tables = ifd
377 .tag(TAG_JPEG_TABLES)
378 .and_then(|tag| tag.value.as_bytes());
379 let byte_count_limit =
380 block_decode::compressed_block_byte_count_limit(&block_decode::BlockDecodeRequest {
381 ifd,
382 layout: *layout,
383 byte_order,
384 compressed: &[],
385 index: spec.index,
386 jpeg_tables,
387 block_width: spec.tile_width,
388 block_height: spec.tile_height,
389 })?;
390 let compressed = match gdal_structural_metadata {
391 Some(metadata) => read_gdal_block_payload(
392 source,
393 metadata,
394 byte_order,
395 spec.offset,
396 spec.byte_count,
397 byte_count_limit,
398 spec.index,
399 )?,
400 None => read_block_payload(
401 source,
402 spec.offset,
403 spec.byte_count,
404 byte_count_limit,
405 spec.index,
406 )?,
407 };
408
409 let decoded = block_decode::decode_compressed_block(block_decode::BlockDecodeRequest {
410 ifd,
411 layout: *layout,
412 byte_order,
413 compressed: &compressed,
414 index: spec.index,
415 jpeg_tables,
416 block_width: spec.tile_width,
417 block_height: spec.tile_height,
418 })?;
419 Ok(cache.insert(cache_key, decoded))
420}