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::cache::{BlockCache, BlockKey, BlockKind};
9use crate::error::{Error, Result};
10use crate::filters;
11use crate::header::ByteOrder;
12use crate::ifd::{Ifd, RasterLayout};
13use crate::source::TiffSource;
14use crate::{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 specs = collect_strip_specs(ifd, &layout)?;
37    let relevant_specs: Vec<_> = specs
38        .iter()
39        .copied()
40        .filter(|spec| {
41            let spec_row_end = spec.row_start + spec.rows_in_strip;
42            spec.row_start < window_row_end && spec_row_end > window.row_off
43        })
44        .collect();
45
46    #[cfg(not(feature = "rayon"))]
47    let decoded_blocks: Result<Vec<_>> = relevant_specs
48        .iter()
49        .map(|&spec| {
50            read_strip_block(
51                source,
52                ifd,
53                byte_order,
54                cache,
55                spec,
56                &layout,
57                gdal_structural_metadata,
58            )
59            .map(|block| (spec, block))
60        })
61        .collect();
62
63    #[cfg(feature = "rayon")]
64    let decoded_blocks: Result<Vec<_>> = relevant_specs
65        .par_iter()
66        .map(|&spec| {
67            read_strip_block(
68                source,
69                ifd,
70                byte_order,
71                cache,
72                spec,
73                &layout,
74                gdal_structural_metadata,
75            )
76            .map(|block| (spec, block))
77        })
78        .collect();
79
80    for (spec, block) in decoded_blocks? {
81        let block = &*block;
82        let block_row_end = spec.row_start + spec.rows_in_strip;
83        let copy_row_start = spec.row_start.max(window.row_off);
84        let copy_row_end = block_row_end.min(window_row_end);
85
86        if layout.planar_configuration == 1 {
87            let src_row_bytes = layout.row_bytes();
88            let copy_bytes_per_row = window.cols * layout.pixel_stride_bytes();
89            for row in copy_row_start..copy_row_end {
90                let src_row_index = row - spec.row_start;
91                let dest_row_index = row - window.row_off;
92                let src_offset =
93                    src_row_index * src_row_bytes + window.col_off * layout.pixel_stride_bytes();
94                let dest_offset = dest_row_index * output_row_bytes;
95                output[dest_offset..dest_offset + copy_bytes_per_row]
96                    .copy_from_slice(&block[src_offset..src_offset + copy_bytes_per_row]);
97            }
98        } else {
99            let src_row_bytes = layout.sample_plane_row_bytes();
100            for row in copy_row_start..copy_row_end {
101                let src_row_index = row - spec.row_start;
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 window.col_off..window.col_end() {
108                    let src = &src_row
109                        [col * layout.bytes_per_sample..(col + 1) * layout.bytes_per_sample];
110                    let dest_col_index = col - window.col_off;
111                    let pixel_base = dest_col_index * layout.pixel_stride_bytes()
112                        + spec.plane * layout.bytes_per_sample;
113                    dest_row[pixel_base..pixel_base + layout.bytes_per_sample].copy_from_slice(src);
114                }
115            }
116        }
117    }
118
119    Ok(output)
120}
121
122fn collect_strip_specs(ifd: &Ifd, layout: &RasterLayout) -> Result<Vec<StripBlockSpec>> {
123    let offsets = ifd
124        .strip_offsets()
125        .ok_or(Error::TagNotFound(crate::ifd::TAG_STRIP_OFFSETS))?;
126    let counts = ifd
127        .strip_byte_counts()
128        .ok_or(Error::TagNotFound(crate::ifd::TAG_STRIP_BYTE_COUNTS))?;
129    if offsets.len() != counts.len() {
130        return Err(Error::InvalidImageLayout(format!(
131            "StripOffsets has {} entries but StripByteCounts has {}",
132            offsets.len(),
133            counts.len()
134        )));
135    }
136
137    let rows_per_strip = ifd.rows_per_strip().unwrap_or(ifd.height());
138    if rows_per_strip == 0 {
139        return Err(Error::InvalidImageLayout(
140            "RowsPerStrip must be greater than zero".into(),
141        ));
142    }
143    let rows_per_strip = rows_per_strip as usize;
144    let strips_per_plane = layout.height.div_ceil(rows_per_strip);
145    let expected = match layout.planar_configuration {
146        1 => strips_per_plane,
147        2 => strips_per_plane * layout.samples_per_pixel,
148        planar => return Err(Error::UnsupportedPlanarConfiguration(planar)),
149    };
150    if offsets.len() != expected {
151        return Err(Error::InvalidImageLayout(format!(
152            "expected {expected} strips, found {}",
153            offsets.len()
154        )));
155    }
156
157    Ok((0..expected)
158        .map(|strip_index| {
159            let plane = if layout.planar_configuration == 1 {
160                0
161            } else {
162                strip_index / strips_per_plane
163            };
164            let plane_strip_index = if layout.planar_configuration == 1 {
165                strip_index
166            } else {
167                strip_index % strips_per_plane
168            };
169            let row_start = plane_strip_index * rows_per_strip;
170            let rows_in_strip = rows_per_strip.min(layout.height.saturating_sub(row_start));
171            StripBlockSpec {
172                index: strip_index,
173                plane,
174                row_start,
175                offset: offsets[strip_index],
176                byte_count: counts[strip_index],
177                rows_in_strip,
178            }
179        })
180        .collect())
181}
182
183#[derive(Clone, Copy)]
184struct StripBlockSpec {
185    index: usize,
186    plane: usize,
187    row_start: usize,
188    offset: u64,
189    byte_count: u64,
190    rows_in_strip: usize,
191}
192
193fn read_strip_block(
194    source: &dyn TiffSource,
195    ifd: &Ifd,
196    byte_order: ByteOrder,
197    cache: &BlockCache,
198    spec: StripBlockSpec,
199    layout: &RasterLayout,
200    gdal_structural_metadata: Option<&GdalStructuralMetadata>,
201) -> Result<Arc<Vec<u8>>> {
202    let cache_key = BlockKey {
203        ifd_index: ifd.index,
204        kind: BlockKind::Strip,
205        block_index: spec.index,
206    };
207    if let Some(cached) = cache.get(&cache_key) {
208        return Ok(cached);
209    }
210
211    let compressed = if let Some(bytes) = source.as_slice() {
212        let start = usize::try_from(spec.offset).map_err(|_| Error::OffsetOutOfBounds {
213            offset: spec.offset,
214            length: spec.byte_count,
215            data_len: bytes.len() as u64,
216        })?;
217        let len = usize::try_from(spec.byte_count).map_err(|_| Error::OffsetOutOfBounds {
218            offset: spec.offset,
219            length: spec.byte_count,
220            data_len: bytes.len() as u64,
221        })?;
222        let end = start.checked_add(len).ok_or(Error::OffsetOutOfBounds {
223            offset: spec.offset,
224            length: spec.byte_count,
225            data_len: bytes.len() as u64,
226        })?;
227        if end > bytes.len() {
228            return Err(Error::OffsetOutOfBounds {
229                offset: spec.offset,
230                length: spec.byte_count,
231                data_len: bytes.len() as u64,
232            });
233        }
234        bytes[start..end].to_vec()
235    } else {
236        let len = usize::try_from(spec.byte_count).map_err(|_| Error::OffsetOutOfBounds {
237            offset: spec.offset,
238            length: spec.byte_count,
239            data_len: source.len(),
240        })?;
241        source.read_exact_at(spec.offset, len)?
242    };
243
244    let compressed = match gdal_structural_metadata {
245        Some(metadata) => metadata
246            .unwrap_block(&compressed, byte_order, spec.offset)?
247            .to_vec(),
248        None => compressed,
249    };
250
251    let jpeg_tables = ifd
252        .tag(TAG_JPEG_TABLES)
253        .and_then(|tag| tag.value.as_bytes());
254    let samples = if layout.planar_configuration == 1 {
255        layout.samples_per_pixel
256    } else {
257        1
258    };
259    let expected_row_bytes = layout.width * samples * layout.bytes_per_sample;
260    let expected_len = spec
261        .rows_in_strip
262        .checked_mul(expected_row_bytes)
263        .ok_or_else(|| Error::InvalidImageLayout("strip size overflows usize".into()))?;
264    let mut decoded = filters::decompress(
265        ifd.compression(),
266        &compressed,
267        spec.index,
268        jpeg_tables,
269        expected_len,
270    )?;
271    if decoded.len() < expected_len {
272        return Err(Error::DecompressionFailed {
273            index: spec.index,
274            reason: format!(
275                "decoded strip is too small: expected at least {expected_len} bytes, found {}",
276                decoded.len()
277            ),
278        });
279    }
280    if decoded.len() > expected_len {
281        decoded.truncate(expected_len);
282    }
283
284    for row in decoded.chunks_exact_mut(expected_row_bytes) {
285        filters::fix_endianness_and_predict(
286            row,
287            layout.bits_per_sample,
288            samples as u16,
289            byte_order,
290            layout.predictor,
291        )?;
292    }
293
294    Ok(cache.insert(cache_key, decoded))
295}