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