Skip to main content

tiff_reader/
strip.rs

1//! Strip-based data access for TIFF images.
2
3use 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}