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