diff options
Diffstat (limited to 'vendor/exr/src/block/lines.rs')
-rw-r--r-- | vendor/exr/src/block/lines.rs | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/vendor/exr/src/block/lines.rs b/vendor/exr/src/block/lines.rs new file mode 100644 index 0000000..1cdf8ee --- /dev/null +++ b/vendor/exr/src/block/lines.rs @@ -0,0 +1,197 @@ +//! Extract lines from a block of pixel bytes. + +use crate::math::*; +use std::io::{Cursor}; +use crate::error::{Result, UnitResult}; +use smallvec::SmallVec; +use std::ops::Range; +use crate::block::{BlockIndex}; +use crate::meta::attribute::ChannelList; + + +/// A single line of pixels. +/// Use [LineRef] or [LineRefMut] for easier type names. +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub struct LineSlice<T> { + + // TODO also store enum SampleType, as it would always be matched in every place it is used + + /// Where this line is located inside the image. + pub location: LineIndex, + + /// The raw bytes of the pixel line, either `&[u8]` or `&mut [u8]`. + /// Must be re-interpreted as slice of f16, f32, or u32, + /// according to the channel data type. + pub value: T, +} + + +/// An reference to a single line of pixels. +/// May go across the whole image or just a tile section of it. +/// +/// This line contains an immutable slice that all samples will be read from. +pub type LineRef<'s> = LineSlice<&'s [u8]>; + +/// A reference to a single mutable line of pixels. +/// May go across the whole image or just a tile section of it. +/// +/// This line contains a mutable slice that all samples will be written to. +pub type LineRefMut<'s> = LineSlice<&'s mut [u8]>; + + +/// Specifies where a row of pixels lies inside an image. +/// This is a globally unique identifier which includes +/// the layer, channel index, and pixel location. +#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)] +pub struct LineIndex { + + /// Index of the layer. + pub layer: usize, + + /// The channel index of the layer. + pub channel: usize, + + /// Index of the mip or rip level in the image. + pub level: Vec2<usize>, + + /// Position of the most left pixel of the row. + pub position: Vec2<usize>, + + /// The width of the line; the number of samples in this row, + /// that is, the number of f16, f32, or u32 values. + pub sample_count: usize, +} + + +impl LineIndex { + + /// Iterates the lines of this block index in interleaved fashion: + /// For each line in this block, this iterator steps once through each channel. + /// This is how lines are stored in a pixel data block. + /// + /// Does not check whether `self.layer_index`, `self.level`, `self.size` and `self.position` are valid indices.__ + // TODO be sure this cannot produce incorrect data, as this is not further checked but only handled with panics + #[inline] + #[must_use] + pub fn lines_in_block(block: BlockIndex, channels: &ChannelList) -> impl Iterator<Item=(Range<usize>, LineIndex)> { + struct LineIter { + layer: usize, level: Vec2<usize>, width: usize, + end_y: usize, x: usize, channel_sizes: SmallVec<[usize; 8]>, + byte: usize, channel: usize, y: usize, + } + + // FIXME what about sub sampling?? + + impl Iterator for LineIter { + type Item = (Range<usize>, LineIndex); + // TODO size hint? + + fn next(&mut self) -> Option<Self::Item> { + if self.y < self.end_y { + + // compute return value before incrementing + let byte_len = self.channel_sizes[self.channel]; + let return_value = ( + (self.byte .. self.byte + byte_len), + LineIndex { + channel: self.channel, + layer: self.layer, + level: self.level, + position: Vec2(self.x, self.y), + sample_count: self.width, + } + ); + + { // increment indices + self.byte += byte_len; + self.channel += 1; + + if self.channel == self.channel_sizes.len() { + self.channel = 0; + self.y += 1; + } + } + + Some(return_value) + } + + else { + None + } + } + } + + let channel_line_sizes: SmallVec<[usize; 8]> = channels.list.iter() + .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??? + .collect(); + + LineIter { + layer: block.layer, + level: block.level, + width: block.pixel_size.0, + x: block.pixel_position.0, + end_y: block.pixel_position.y() + block.pixel_size.height(), + channel_sizes: channel_line_sizes, + + byte: 0, + channel: 0, + y: block.pixel_position.y() + } + } +} + + + +impl<'s> LineRefMut<'s> { + + /// Writes the samples (f16, f32, u32 values) into this line value reference. + /// Use `write_samples` if there is not slice available. + #[inline] + #[must_use] + pub fn write_samples_from_slice<T: crate::io::Data>(self, slice: &[T]) -> UnitResult { + debug_assert_eq!(slice.len(), self.location.sample_count, "slice size does not match the line width"); + debug_assert_eq!(self.value.len(), self.location.sample_count * T::BYTE_SIZE, "sample type size does not match line byte size"); + + T::write_slice(&mut Cursor::new(self.value), slice) + } + + /// Iterate over all samples in this line, from left to right. + /// The supplied `get_line` function returns the sample value + /// for a given sample index within the line, + /// which starts at zero for each individual line. + /// Use `write_samples_from_slice` if you already have a slice of samples. + #[inline] + #[must_use] + pub fn write_samples<T: crate::io::Data>(self, mut get_sample: impl FnMut(usize) -> T) -> UnitResult { + debug_assert_eq!(self.value.len(), self.location.sample_count * T::BYTE_SIZE, "sample type size does not match line byte size"); + + let mut write = Cursor::new(self.value); + + for index in 0..self.location.sample_count { + T::write(get_sample(index), &mut write)?; + } + + Ok(()) + } +} + +impl LineRef<'_> { + + /// Read the samples (f16, f32, u32 values) from this line value reference. + /// Use `read_samples` if there is not slice available. + pub fn read_samples_into_slice<T: crate::io::Data>(self, slice: &mut [T]) -> UnitResult { + debug_assert_eq!(slice.len(), self.location.sample_count, "slice size does not match the line width"); + debug_assert_eq!(self.value.len(), self.location.sample_count * T::BYTE_SIZE, "sample type size does not match line byte size"); + + T::read_slice(&mut Cursor::new(self.value), slice) + } + + /// Iterate over all samples in this line, from left to right. + /// Use `read_sample_into_slice` if you already have a slice of samples. + pub fn read_samples<T: crate::io::Data>(&self) -> impl Iterator<Item = Result<T>> + '_ { + debug_assert_eq!(self.value.len(), self.location.sample_count * T::BYTE_SIZE, "sample type size does not match line byte size"); + + let mut read = self.value.clone(); // FIXME deep data + (0..self.location.sample_count).map(move |_| T::read(&mut read)) + } +}
\ No newline at end of file |