exr/block/
lines.rs

1//! Extract lines from a block of pixel bytes.
2
3use crate::math::*;
4use std::io::{Cursor};
5use crate::error::{Result, UnitResult};
6use smallvec::SmallVec;
7use std::ops::Range;
8use crate::block::{BlockIndex};
9use crate::meta::attribute::ChannelList;
10
11
12/// A single line of pixels.
13/// Use [LineRef] or [LineRefMut] for easier type names.
14#[derive(Clone, Copy, Eq, PartialEq, Debug)]
15pub struct LineSlice<T> {
16
17    // TODO also store enum SampleType, as it would always be matched in every place it is used
18
19    /// Where this line is located inside the image.
20    pub location: LineIndex,
21
22    /// The raw bytes of the pixel line, either `&[u8]` or `&mut [u8]`.
23    /// Must be re-interpreted as slice of f16, f32, or u32,
24    /// according to the channel data type.
25    pub value: T,
26}
27
28
29/// An reference to a single line of pixels.
30/// May go across the whole image or just a tile section of it.
31///
32/// This line contains an immutable slice that all samples will be read from.
33pub type LineRef<'s> = LineSlice<&'s [u8]>;
34
35/// A reference to a single mutable line of pixels.
36/// May go across the whole image or just a tile section of it.
37///
38/// This line contains a mutable slice that all samples will be written to.
39pub type LineRefMut<'s> = LineSlice<&'s mut [u8]>;
40
41
42/// Specifies where a row of pixels lies inside an image.
43/// This is a globally unique identifier which includes
44/// the layer, channel index, and pixel location.
45#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
46pub struct LineIndex {
47
48    /// Index of the layer.
49    pub layer: usize,
50
51    /// The channel index of the layer.
52    pub channel: usize,
53
54    /// Index of the mip or rip level in the image.
55    pub level: Vec2<usize>,
56
57    /// Position of the most left pixel of the row.
58    pub position: Vec2<usize>,
59
60    /// The width of the line; the number of samples in this row,
61    /// that is, the number of f16, f32, or u32 values.
62    pub sample_count: usize,
63}
64
65
66impl LineIndex {
67
68    /// Iterates the lines of this block index in interleaved fashion:
69    /// For each line in this block, this iterator steps once through each channel.
70    /// This is how lines are stored in a pixel data block.
71    ///
72    /// Does not check whether `self.layer_index`, `self.level`, `self.size` and `self.position` are valid indices.__
73    // TODO be sure this cannot produce incorrect data, as this is not further checked but only handled with panics
74    #[inline]
75    #[must_use]
76    pub fn lines_in_block(block: BlockIndex, channels: &ChannelList) -> impl Iterator<Item=(Range<usize>, LineIndex)> {
77        struct LineIter {
78            layer: usize, level: Vec2<usize>, width: usize,
79            end_y: usize, x: usize, channel_sizes: SmallVec<[usize; 8]>,
80            byte: usize, channel: usize, y: usize,
81        }
82
83        // FIXME what about sub sampling??
84
85        impl Iterator for LineIter {
86            type Item = (Range<usize>, LineIndex);
87            // TODO size hint?
88
89            fn next(&mut self) -> Option<Self::Item> {
90                if self.y < self.end_y {
91
92                    // compute return value before incrementing
93                    let byte_len = self.channel_sizes[self.channel];
94                    let return_value = (
95                        (self.byte .. self.byte + byte_len),
96                        LineIndex {
97                            channel: self.channel,
98                            layer: self.layer,
99                            level: self.level,
100                            position: Vec2(self.x, self.y),
101                            sample_count: self.width,
102                        }
103                    );
104
105                    { // increment indices
106                        self.byte += byte_len;
107                        self.channel += 1;
108
109                        if self.channel == self.channel_sizes.len() {
110                            self.channel = 0;
111                            self.y += 1;
112                        }
113                    }
114
115                    Some(return_value)
116                }
117
118                else {
119                    None
120                }
121            }
122        }
123
124        let channel_line_sizes: SmallVec<[usize; 8]> = channels.list.iter()
125            .map(move |channel| block.pixel_size.0 * channel.sample_type.bytes_per_sample()) // FIXME is it fewer samples per tile or just fewer tiles for sampled images???
126            .collect();
127
128        LineIter {
129            layer: block.layer,
130            level: block.level,
131            width: block.pixel_size.0,
132            x: block.pixel_position.0,
133            end_y: block.pixel_position.y() + block.pixel_size.height(),
134            channel_sizes: channel_line_sizes,
135
136            byte: 0,
137            channel: 0,
138            y: block.pixel_position.y()
139        }
140    }
141}
142
143
144
145impl<'s> LineRefMut<'s> {
146
147    /// Writes the samples (f16, f32, u32 values) into this line value reference.
148    /// Use `write_samples` if there is not slice available.
149    #[inline]
150    #[must_use]
151    pub fn write_samples_from_slice<T: crate::io::Data>(self, slice: &[T]) -> UnitResult {
152        debug_assert_eq!(slice.len(), self.location.sample_count, "slice size does not match the line width");
153        debug_assert_eq!(self.value.len(), self.location.sample_count * T::BYTE_SIZE, "sample type size does not match line byte size");
154
155        T::write_slice(&mut Cursor::new(self.value), slice)
156    }
157
158    /// Iterate over all samples in this line, from left to right.
159    /// The supplied `get_line` function returns the sample value
160    /// for a given sample index within the line,
161    /// which starts at zero for each individual line.
162    /// Use `write_samples_from_slice` if you already have a slice of samples.
163    #[inline]
164    #[must_use]
165    pub fn write_samples<T: crate::io::Data>(self, mut get_sample: impl FnMut(usize) -> T) -> UnitResult {
166        debug_assert_eq!(self.value.len(), self.location.sample_count * T::BYTE_SIZE, "sample type size does not match line byte size");
167
168        let mut write = Cursor::new(self.value);
169
170        for index in 0..self.location.sample_count {
171            T::write(get_sample(index), &mut write)?;
172        }
173
174        Ok(())
175    }
176}
177
178impl LineRef<'_> {
179
180    /// Read the samples (f16, f32, u32 values) from this line value reference.
181    /// Use `read_samples` if there is not slice available.
182    pub fn read_samples_into_slice<T: crate::io::Data>(self, slice: &mut [T]) -> UnitResult {
183        debug_assert_eq!(slice.len(), self.location.sample_count, "slice size does not match the line width");
184        debug_assert_eq!(self.value.len(), self.location.sample_count * T::BYTE_SIZE, "sample type size does not match line byte size");
185
186        T::read_slice(&mut Cursor::new(self.value), slice)
187    }
188
189    /// Iterate over all samples in this line, from left to right.
190    /// Use `read_sample_into_slice` if you already have a slice of samples.
191    pub fn read_samples<T: crate::io::Data>(&self) -> impl Iterator<Item = Result<T>> + '_ {
192        debug_assert_eq!(self.value.len(), self.location.sample_count * T::BYTE_SIZE, "sample type size does not match line byte size");
193
194        let mut read = self.value; // FIXME deep data
195        (0..self.location.sample_count).map(move |_| T::read(&mut read))
196    }
197}