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