1use std::sync::Arc;
4
5#[cfg(feature = "rayon")]
6use parking_lot::Mutex;
7#[cfg(feature = "rayon")]
8use rayon::prelude::*;
9
10use crate::block_decode;
11use crate::cache::{BlockCache, BlockKey, BlockKind};
12use crate::error::{Error, Result};
13use crate::header::ByteOrder;
14use crate::ifd::{Ifd, RasterLayout};
15use crate::source::TiffSource;
16use crate::{
17 allocate_decode_output, checked_layout_add, checked_layout_mul, read_block_payload,
18 read_gdal_block_payload, validate_decode_output_len, DecodeReadOptions, Window,
19};
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 let ifd_offset = ifd.offset();
34 let context = block_decode::BlockDecodeContext::new(ifd, layout, byte_order)?;
35
36 let output_len = window.output_len(&layout)?;
37 let mut output = allocate_decode_output(output_len, options.decode_output_bytes)?;
38
39 let relevant_specs = collect_tile_specs_for_window(ifd, &layout, window, None)?;
40
41 #[cfg(feature = "rayon")]
42 {
43 let output = Mutex::new(output.as_mut_slice());
44 relevant_specs.par_iter().try_for_each(|&spec| {
45 let block = read_tile_block(source, ifd_offset, cache, spec, &context, options)?;
46 copy_tile_window_block(&mut output.lock(), block.as_slice(), spec, &layout, window)?;
47 Ok::<(), Error>(())
48 })?;
49 }
50
51 #[cfg(not(feature = "rayon"))]
52 for spec in relevant_specs {
53 let block = read_tile_block(source, ifd_offset, cache, spec, &context, options)?;
54 copy_tile_window_block(&mut output, block.as_slice(), spec, &layout, window)?;
55 }
56
57 Ok(output)
58}
59
60pub(crate) fn read_window_band(
61 source: &dyn TiffSource,
62 ifd: &Ifd,
63 byte_order: ByteOrder,
64 cache: &BlockCache,
65 window: Window,
66 band_index: usize,
67 options: DecodeReadOptions<'_>,
68) -> Result<Vec<u8>> {
69 let layout = ifd.raster_layout()?;
70 if band_index >= layout.samples_per_pixel {
71 return Err(Error::BandIndexOutOfBounds {
72 index: band_index,
73 band_count: layout.samples_per_pixel,
74 });
75 }
76 if window.is_empty() {
77 return Ok(Vec::new());
78 }
79 let ifd_offset = ifd.offset();
80 let context = block_decode::BlockDecodeContext::new(ifd, layout, byte_order)?;
81
82 let output_len = window.band_output_len(&layout)?;
83 let mut output = allocate_decode_output(output_len, options.decode_output_bytes)?;
84
85 let relevant_specs = collect_tile_specs_for_window(ifd, &layout, window, Some(band_index))?;
86
87 #[cfg(feature = "rayon")]
88 {
89 let output = Mutex::new(output.as_mut_slice());
90 relevant_specs.par_iter().try_for_each(|&spec| {
91 let block = read_tile_block(source, ifd_offset, cache, spec, &context, options)?;
92 copy_tile_band_window_block(
93 &mut output.lock(),
94 block.as_slice(),
95 spec,
96 &layout,
97 window,
98 band_index,
99 )?;
100 Ok::<(), Error>(())
101 })?;
102 }
103
104 #[cfg(not(feature = "rayon"))]
105 for spec in relevant_specs {
106 let block = read_tile_block(source, ifd_offset, cache, spec, &context, options)?;
107 copy_tile_band_window_block(
108 &mut output,
109 block.as_slice(),
110 spec,
111 &layout,
112 window,
113 band_index,
114 )?;
115 }
116
117 Ok(output)
118}
119
120fn copy_tile_window_block(
121 output: &mut [u8],
122 block: &[u8],
123 spec: TileBlockSpec,
124 layout: &RasterLayout,
125 window: Window,
126) -> Result<()> {
127 let pixel_stride = layout.checked_pixel_stride_bytes()?;
128 let window_row_end = window.row_end();
129 let window_col_end = window.col_end();
130 let output_row_bytes = checked_layout_mul(window.cols, pixel_stride, "window row byte count")?;
131 let copy_row_start = spec.y.max(window.row_off);
132 let copy_row_end =
133 checked_layout_add(spec.y, spec.rows_in_tile, "tile row range")?.min(window_row_end);
134 let copy_col_start = spec.x.max(window.col_off);
135 let copy_col_end =
136 checked_layout_add(spec.x, spec.cols_in_tile, "tile column range")?.min(window_col_end);
137
138 let src_row_bytes = checked_layout_mul(
139 spec.tile_width,
140 if layout.planar_configuration == 1 {
141 pixel_stride
142 } else {
143 layout.bytes_per_sample
144 },
145 "tile source row byte count",
146 )?;
147
148 if layout.planar_configuration == 1 {
149 let copy_bytes_per_row = checked_layout_mul(
150 copy_col_end - copy_col_start,
151 pixel_stride,
152 "tile copy row byte count",
153 )?;
154 let src_col_offset = checked_layout_mul(
155 copy_col_start - spec.x,
156 pixel_stride,
157 "tile source column offset",
158 )?;
159 let dest_col_offset = checked_layout_mul(
160 copy_col_start - window.col_off,
161 pixel_stride,
162 "tile output column offset",
163 )?;
164 for row in copy_row_start..copy_row_end {
165 let src_row_index = row - spec.y;
166 let dest_row_index = row - window.row_off;
167 let src_offset = checked_layout_add(
168 checked_layout_mul(src_row_index, src_row_bytes, "tile source row offset")?,
169 src_col_offset,
170 "tile source offset",
171 )?;
172 let dest_offset = checked_layout_add(
173 checked_layout_mul(dest_row_index, output_row_bytes, "tile output row offset")?,
174 dest_col_offset,
175 "tile output offset",
176 )?;
177 let src_end =
178 checked_layout_add(src_offset, copy_bytes_per_row, "tile source copy range")?;
179 let dest_end =
180 checked_layout_add(dest_offset, copy_bytes_per_row, "tile output copy range")?;
181 output[dest_offset..dest_end].copy_from_slice(&block[src_offset..src_end]);
182 }
183 } else {
184 let plane_offset = checked_layout_mul(
185 spec.plane,
186 layout.bytes_per_sample,
187 "tile plane byte offset",
188 )?;
189 for row in copy_row_start..copy_row_end {
190 let src_row_index = row - spec.y;
191 let dest_row_index = row - window.row_off;
192 let src_row_offset =
193 checked_layout_mul(src_row_index, src_row_bytes, "tile source row offset")?;
194 let src_row_end =
195 checked_layout_add(src_row_offset, src_row_bytes, "tile source row range")?;
196 let dest_row_offset =
197 checked_layout_mul(dest_row_index, output_row_bytes, "tile output row offset")?;
198 let dest_row_end =
199 checked_layout_add(dest_row_offset, output_row_bytes, "tile output row range")?;
200 let src_row = &block[src_row_offset..src_row_end];
201 let dest_row = &mut output[dest_row_offset..dest_row_end];
202 for col in copy_col_start..copy_col_end {
203 let src_offset = checked_layout_mul(
204 col - spec.x,
205 layout.bytes_per_sample,
206 "tile source column offset",
207 )?;
208 let src_end = checked_layout_add(
209 src_offset,
210 layout.bytes_per_sample,
211 "tile source sample range",
212 )?;
213 let src = &src_row[src_offset..src_end];
214 let pixel_base = checked_layout_add(
215 checked_layout_mul(
216 col - window.col_off,
217 pixel_stride,
218 "tile output pixel offset",
219 )?,
220 plane_offset,
221 "tile output sample offset",
222 )?;
223 let pixel_end = checked_layout_add(
224 pixel_base,
225 layout.bytes_per_sample,
226 "tile output sample range",
227 )?;
228 dest_row[pixel_base..pixel_end].copy_from_slice(src);
229 }
230 }
231 }
232 Ok(())
233}
234
235fn copy_tile_band_window_block(
236 output: &mut [u8],
237 block: &[u8],
238 spec: TileBlockSpec,
239 layout: &RasterLayout,
240 window: Window,
241 band_index: usize,
242) -> Result<()> {
243 let pixel_stride = layout.checked_pixel_stride_bytes()?;
244 let window_row_end = window.row_end();
245 let window_col_end = window.col_end();
246 let output_row_bytes = checked_layout_mul(
247 window.cols,
248 layout.bytes_per_sample,
249 "window band row byte count",
250 )?;
251 let copy_row_start = spec.y.max(window.row_off);
252 let copy_row_end =
253 checked_layout_add(spec.y, spec.rows_in_tile, "tile row range")?.min(window_row_end);
254 let copy_col_start = spec.x.max(window.col_off);
255 let copy_col_end =
256 checked_layout_add(spec.x, spec.cols_in_tile, "tile column range")?.min(window_col_end);
257
258 let src_row_bytes = checked_layout_mul(
259 spec.tile_width,
260 if layout.planar_configuration == 1 {
261 pixel_stride
262 } else {
263 layout.bytes_per_sample
264 },
265 "tile source row byte count",
266 )?;
267
268 if layout.planar_configuration == 1 {
269 let band_offset =
270 checked_layout_mul(band_index, layout.bytes_per_sample, "band byte offset")?;
271 for row in copy_row_start..copy_row_end {
272 let src_row_index = row - spec.y;
273 let dest_row_index = row - window.row_off;
274 let src_row_offset =
275 checked_layout_mul(src_row_index, src_row_bytes, "tile source row offset")?;
276 let src_row_end =
277 checked_layout_add(src_row_offset, src_row_bytes, "tile source row range")?;
278 let dest_row_offset =
279 checked_layout_mul(dest_row_index, output_row_bytes, "tile output row offset")?;
280 let dest_row_end =
281 checked_layout_add(dest_row_offset, output_row_bytes, "tile output row range")?;
282 let src_row = &block[src_row_offset..src_row_end];
283 let dest_row = &mut output[dest_row_offset..dest_row_end];
284 for col in copy_col_start..copy_col_end {
285 let src_base = checked_layout_add(
286 checked_layout_mul(col - spec.x, pixel_stride, "tile source column offset")?,
287 band_offset,
288 "tile source band offset",
289 )?;
290 let dest_col_index = col - window.col_off;
291 let dest_base = checked_layout_mul(
292 dest_col_index,
293 layout.bytes_per_sample,
294 "tile output sample offset",
295 )?;
296 let src_end = checked_layout_add(
297 src_base,
298 layout.bytes_per_sample,
299 "tile source sample range",
300 )?;
301 let dest_end = checked_layout_add(
302 dest_base,
303 layout.bytes_per_sample,
304 "tile output sample range",
305 )?;
306 dest_row[dest_base..dest_end].copy_from_slice(&src_row[src_base..src_end]);
307 }
308 }
309 } else {
310 let copy_bytes_per_row = checked_layout_mul(
311 copy_col_end - copy_col_start,
312 layout.bytes_per_sample,
313 "tile copy row byte count",
314 )?;
315 let src_col_offset = checked_layout_mul(
316 copy_col_start - spec.x,
317 layout.bytes_per_sample,
318 "tile source column offset",
319 )?;
320 let dest_col_offset = checked_layout_mul(
321 copy_col_start - window.col_off,
322 layout.bytes_per_sample,
323 "tile output column offset",
324 )?;
325 for row in copy_row_start..copy_row_end {
326 let src_row_index = row - spec.y;
327 let dest_row_index = row - window.row_off;
328 let src_offset = checked_layout_add(
329 checked_layout_mul(src_row_index, src_row_bytes, "tile source row offset")?,
330 src_col_offset,
331 "tile source offset",
332 )?;
333 let dest_offset = checked_layout_add(
334 checked_layout_mul(dest_row_index, output_row_bytes, "tile output row offset")?,
335 dest_col_offset,
336 "tile output offset",
337 )?;
338 let src_end =
339 checked_layout_add(src_offset, copy_bytes_per_row, "tile source copy range")?;
340 let dest_end =
341 checked_layout_add(dest_offset, copy_bytes_per_row, "tile output copy range")?;
342 output[dest_offset..dest_end].copy_from_slice(&block[src_offset..src_end]);
343 }
344 }
345 Ok(())
346}
347
348fn collect_tile_specs_for_window(
349 ifd: &Ifd,
350 layout: &RasterLayout,
351 window: Window,
352 band_index: Option<usize>,
353) -> Result<Vec<TileBlockSpec>> {
354 let tile_width = ifd
355 .tile_width()
356 .ok_or(Error::TagNotFound(crate::ifd::TAG_TILE_WIDTH))? as usize;
357 let tile_height = ifd
358 .tile_height()
359 .ok_or(Error::TagNotFound(crate::ifd::TAG_TILE_LENGTH))? as usize;
360 if tile_width == 0 || tile_height == 0 {
361 return Err(Error::InvalidImageLayout(
362 "tile width and height must be greater than zero".into(),
363 ));
364 }
365
366 let offsets = ifd
367 .tile_offsets()
368 .ok_or(Error::TagNotFound(crate::ifd::TAG_TILE_OFFSETS))?;
369 let counts = ifd
370 .tile_byte_counts()
371 .ok_or(Error::TagNotFound(crate::ifd::TAG_TILE_BYTE_COUNTS))?;
372 if offsets.len() != counts.len() {
373 return Err(Error::InvalidImageLayout(format!(
374 "TileOffsets has {} entries but TileByteCounts has {}",
375 offsets.len(),
376 counts.len()
377 )));
378 }
379
380 let tiles_across = layout.width.div_ceil(tile_width);
381 let tiles_down = layout.height.div_ceil(tile_height);
382 let tiles_per_plane = tiles_across
383 .checked_mul(tiles_down)
384 .ok_or_else(tile_count_overflow)?;
385 let expected = match layout.planar_configuration {
386 1 => tiles_per_plane,
387 2 => tiles_per_plane
388 .checked_mul(layout.samples_per_pixel)
389 .ok_or_else(tile_count_overflow)?,
390 planar => return Err(Error::UnsupportedPlanarConfiguration(planar)),
391 };
392 if offsets.len() != expected {
393 return Err(Error::InvalidImageLayout(format!(
394 "expected {expected} tiles, found {}",
395 offsets.len()
396 )));
397 }
398
399 let first_tile_row = window.row_off / tile_height;
400 let last_tile_row = window.row_end().div_ceil(tile_height).min(tiles_down);
401 let first_tile_col = window.col_off / tile_width;
402 let last_tile_col = window.col_end().div_ceil(tile_width).min(tiles_across);
403 let plane_range = if layout.planar_configuration == 1 {
404 0..1
405 } else if let Some(band_index) = band_index {
406 band_index..band_index + 1
407 } else {
408 0..layout.samples_per_pixel
409 };
410 let spec_count = (last_tile_row - first_tile_row)
411 .saturating_mul(last_tile_col - first_tile_col)
412 .saturating_mul(plane_range.end - plane_range.start);
413 let mut specs = Vec::with_capacity(spec_count);
414
415 for plane in plane_range {
416 for tile_row in first_tile_row..last_tile_row {
417 for tile_col in first_tile_col..last_tile_col {
418 let plane_tile_index = tile_row
419 .checked_mul(tiles_across)
420 .and_then(|base| base.checked_add(tile_col))
421 .ok_or_else(tile_count_overflow)?;
422 let tile_index = if layout.planar_configuration == 1 {
423 plane_tile_index
424 } else {
425 plane
426 .checked_mul(tiles_per_plane)
427 .and_then(|base| base.checked_add(plane_tile_index))
428 .ok_or_else(tile_count_overflow)?
429 };
430 let x = tile_col * tile_width;
431 let y = tile_row * tile_height;
432 let cols_in_tile = tile_width.min(layout.width.saturating_sub(x));
433 let rows_in_tile = tile_height.min(layout.height.saturating_sub(y));
434 specs.push(TileBlockSpec {
435 index: tile_index,
436 plane,
437 x,
438 y,
439 cols_in_tile,
440 rows_in_tile,
441 offset: offsets[tile_index],
442 byte_count: counts[tile_index],
443 tile_width,
444 tile_height,
445 });
446 }
447 }
448 }
449
450 Ok(specs)
451}
452
453fn tile_count_overflow() -> Error {
454 Error::InvalidImageLayout("tile count overflows usize".into())
455}
456
457#[derive(Clone, Copy)]
458struct TileBlockSpec {
459 index: usize,
460 plane: usize,
461 x: usize,
462 y: usize,
463 cols_in_tile: usize,
464 rows_in_tile: usize,
465 offset: u64,
466 byte_count: u64,
467 tile_width: usize,
468 tile_height: usize,
469}
470
471fn read_tile_block(
472 source: &dyn TiffSource,
473 ifd_offset: u64,
474 cache: &BlockCache,
475 spec: TileBlockSpec,
476 context: &block_decode::BlockDecodeContext<'_>,
477 options: DecodeReadOptions<'_>,
478) -> Result<Arc<Vec<u8>>> {
479 let decode_request = block_decode::BlockDecodeRequest {
480 context,
481 compressed: &[],
482 index: spec.index,
483 block_width: spec.tile_width,
484 block_height: spec.tile_height,
485 };
486 let decoded_len = block_decode::decoded_block_len(&decode_request)?;
487 validate_decode_output_len(decoded_len, options.decode_output_bytes)?;
488
489 let cache_key = BlockKey {
490 ifd_offset,
491 kind: BlockKind::Tile,
492 block_index: spec.index,
493 };
494 if let Some(cached) = cache.get(&cache_key) {
495 return Ok(cached);
496 }
497
498 if spec.offset == 0 || spec.byte_count == 0 {
501 let decoded = allocate_decode_output(decoded_len, options.decode_output_bytes)?;
502 return Ok(cache.insert(cache_key, decoded));
503 }
504
505 let byte_count_limit = block_decode::compressed_block_byte_count_limit(&decode_request)?;
506 let compressed = match options.gdal_structural_metadata {
507 Some(metadata) => read_gdal_block_payload(
508 source,
509 metadata,
510 context.byte_order,
511 spec.offset,
512 spec.byte_count,
513 byte_count_limit,
514 spec.index,
515 )?,
516 None => read_block_payload(
517 source,
518 spec.offset,
519 spec.byte_count,
520 byte_count_limit,
521 spec.index,
522 )?,
523 };
524
525 let decoded = block_decode::decode_compressed_block(block_decode::BlockDecodeRequest {
526 context,
527 compressed: &compressed,
528 index: spec.index,
529 block_width: spec.tile_width,
530 block_height: spec.tile_height,
531 })?;
532 Ok(cache.insert(cache_key, decoded))
533}