summaryrefslogtreecommitdiff
path: root/vendor/exr/src/image/read
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
committerValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
commit1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch)
tree7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/exr/src/image/read
parent5ecd8cf2cba827454317368b68571df0d13d7842 (diff)
downloadfparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz
fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/exr/src/image/read')
-rw-r--r--vendor/exr/src/image/read/any_channels.rs128
-rw-r--r--vendor/exr/src/image/read/image.rs209
-rw-r--r--vendor/exr/src/image/read/layers.rs204
-rw-r--r--vendor/exr/src/image/read/levels.rs219
-rw-r--r--vendor/exr/src/image/read/mod.rs207
-rw-r--r--vendor/exr/src/image/read/samples.rs122
-rw-r--r--vendor/exr/src/image/read/specific_channels.rs463
7 files changed, 1552 insertions, 0 deletions
diff --git a/vendor/exr/src/image/read/any_channels.rs b/vendor/exr/src/image/read/any_channels.rs
new file mode 100644
index 0000000..054a7c3
--- /dev/null
+++ b/vendor/exr/src/image/read/any_channels.rs
@@ -0,0 +1,128 @@
+//! How to read arbitrary channels.
+
+use crate::image::*;
+use crate::meta::header::{Header};
+use crate::error::{Result, UnitResult};
+use crate::block::UncompressedBlock;
+use crate::block::lines::{LineRef};
+use crate::math::Vec2;
+use crate::meta::attribute::{Text, ChannelDescription};
+use crate::image::read::layers::{ReadChannels, ChannelsReader};
+use crate::block::chunk::TileCoordinates;
+
+/// A template that creates an [AnyChannelsReader] for each layer in the image.
+/// This loads all channels for each layer.
+/// The `ReadSamples` can, for example, be [ReadFlatSamples] or [ReadAllLevels<ReadFlatSamples>].
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct ReadAnyChannels<ReadSamples> {
+
+ /// The sample reading specification
+ pub read_samples: ReadSamples
+}
+
+/// A template that creates a new [`SampleReader`] for each channel in each layer.
+pub trait ReadSamples {
+
+ /// The type of the temporary samples reader
+ type Reader: SamplesReader;
+
+ /// Create a single reader for a single channel of a layer
+ fn create_sample_reader(&self, header: &Header, channel: &ChannelDescription) -> Result<Self::Reader>;
+}
+
+/// Processes pixel blocks from a file and accumulates them into a collection of arbitrary channels.
+/// Loads all channels for each layer.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct AnyChannelsReader<SamplesReader> {
+
+ /// Stores a separate sample reader per channel in the layer
+ sample_channels_reader: SmallVec<[AnyChannelReader<SamplesReader>; 4]>,
+}
+
+/// Processes pixel blocks from a file and accumulates them into a single arbitrary channel.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct AnyChannelReader<SamplesReader> {
+
+ /// The custom reader that accumulates the pixel data for a single channel
+ samples: SamplesReader,
+
+ /// Temporarily accumulated meta data.
+ name: Text,
+
+ /// Temporarily accumulated meta data.
+ sampling_rate: Vec2<usize>,
+
+ /// Temporarily accumulated meta data.
+ quantize_linearly: bool,
+}
+
+/// Processes pixel blocks from a file and accumulates them into a single pixel channel.
+/// For example, stores thousands of "Red" pixel values for a single layer.
+pub trait SamplesReader {
+
+ /// The type of resulting sample storage
+ type Samples;
+
+ /// Specify whether a single block of pixels should be loaded from the file
+ fn filter_block(&self, tile: TileCoordinates) -> bool;
+
+ /// Load a single pixel line, which has not been filtered, into the reader, accumulating the sample data
+ fn read_line(&mut self, line: LineRef<'_>) -> UnitResult;
+
+ /// Deliver the final accumulated sample storage for the image
+ fn into_samples(self) -> Self::Samples;
+}
+
+
+impl<'s, S: 's + ReadSamples> ReadChannels<'s> for ReadAnyChannels<S> {
+ type Reader = AnyChannelsReader<S::Reader>;
+
+ fn create_channels_reader(&self, header: &Header) -> Result<Self::Reader> {
+ let samples: Result<_> = header.channels.list.iter()
+ .map(|channel: &ChannelDescription| Ok(AnyChannelReader {
+ samples: self.read_samples.create_sample_reader(header, channel)?,
+ name: channel.name.clone(),
+ sampling_rate: channel.sampling,
+ quantize_linearly: channel.quantize_linearly
+ }))
+ .collect();
+
+ Ok(AnyChannelsReader { sample_channels_reader: samples? })
+ }
+}
+
+impl<S: SamplesReader> ChannelsReader for AnyChannelsReader<S> {
+ type Channels = AnyChannels<S::Samples>;
+
+ fn filter_block(&self, tile: TileCoordinates) -> bool {
+ self.sample_channels_reader.iter().any(|channel| channel.samples.filter_block(tile))
+ }
+
+ fn read_block(&mut self, header: &Header, decompressed: UncompressedBlock) -> UnitResult {
+ /*for (bytes, line) in LineIndex::lines_in_block(decompressed.index, header) {
+ let channel = self.sample_channels_reader.get_mut(line.channel).unwrap();
+ channel.samples.read_line(LineSlice { location: line, value: &decompressed.data[bytes] })?;
+ }
+
+ Ok(())*/
+ for line in decompressed.lines(&header.channels) {
+ self.sample_channels_reader[line.location.channel].samples.read_line(line)?;
+ }
+
+ Ok(())
+ }
+
+ fn into_channels(self) -> Self::Channels {
+ AnyChannels { // not using `new()` as the channels are already sorted
+ list: self.sample_channels_reader.into_iter()
+ .map(|channel| AnyChannel {
+ sample_data: channel.samples.into_samples(),
+
+ name: channel.name,
+ quantize_linearly: channel.quantize_linearly,
+ sampling: channel.sampling_rate
+ })
+ .collect()
+ }
+ }
+}
diff --git a/vendor/exr/src/image/read/image.rs b/vendor/exr/src/image/read/image.rs
new file mode 100644
index 0000000..fce2f52
--- /dev/null
+++ b/vendor/exr/src/image/read/image.rs
@@ -0,0 +1,209 @@
+//! The last wrapper of image readers, finally containing the [`from_file(path)`] method.
+//! This completes the builder and reads a complete image.
+
+use crate::image::*;
+use crate::meta::header::{Header, ImageAttributes};
+use crate::error::{Result, UnitResult};
+use crate::block::{UncompressedBlock, BlockIndex};
+use crate::block::chunk::TileCoordinates;
+use std::path::Path;
+use std::io::{Read, BufReader};
+use std::io::Seek;
+use crate::meta::MetaData;
+use crate::block::reader::ChunksReader;
+
+/// Specify whether to read the image in parallel,
+/// whether to use pedantic error handling,
+/// and a callback for the reading progress.
+#[derive(Debug, Clone)]
+pub struct ReadImage<OnProgress, ReadLayers> {
+ on_progress: OnProgress,
+ read_layers: ReadLayers,
+ pedantic: bool,
+ parallel: bool,
+}
+
+impl<F, L> ReadImage<F, L> where F: FnMut(f64)
+{
+ /// Uses relaxed error handling and parallel decompression.
+ pub fn new(read_layers: L, on_progress: F) -> Self {
+ Self {
+ on_progress, read_layers,
+ pedantic: false, parallel: true,
+ }
+ }
+
+ /// Specify that any missing or unusual information should result in an error.
+ /// Otherwise, `exrs` will try to compute or ignore missing information.
+ ///
+ /// If pedantic is true, then an error will be returned as soon as anything is missing in the file,
+ /// or two values in the image contradict each other. If pedantic is false,
+ /// then only fatal errors will be thrown. By default, reading an image is not pedantic,
+ /// which means that slightly invalid files might still be readable.
+ /// For example, if some attribute is missing but can be recomputed, this flag decides whether an error is thrown.
+ /// Or if the pedantic flag is true and there are still bytes left after the decompression algorithm finished,
+ /// an error is thrown, because this should not happen and something might be wrong with the file.
+ /// Or if your application is a target of attacks, or if you want to emulate the original C++ library,
+ /// you might want to switch to pedantic reading.
+ pub fn pedantic(self) -> Self { Self { pedantic: true, ..self } }
+
+ /// Specify that multiple pixel blocks should never be decompressed using multiple threads at once.
+ /// This might be slower but uses less memory and less synchronization.
+ pub fn non_parallel(self) -> Self { Self { parallel: false, ..self } }
+
+ /// Specify a function to be called regularly throughout the loading process.
+ /// Replaces all previously specified progress functions in this reader.
+ pub fn on_progress<OnProgress>(self, on_progress: OnProgress) -> ReadImage<OnProgress, L>
+ where OnProgress: FnMut(f64)
+ {
+ ReadImage {
+ on_progress,
+ read_layers: self.read_layers,
+ pedantic: self.pedantic,
+ parallel: self.parallel
+ }
+ }
+
+
+ /// Read the exr image from a file.
+ /// Use [`ReadImage::read_from_unbuffered`] instead, if you do not have a file.
+ #[inline]
+ #[must_use]
+ pub fn from_file<Layers>(self, path: impl AsRef<Path>) -> Result<Image<Layers>>
+ where for<'s> L: ReadLayers<'s, Layers = Layers>
+ {
+ self.from_unbuffered(std::fs::File::open(path)?)
+ }
+
+ /// Buffer the reader and then read the exr image from it.
+ /// Use [`ReadImage::read_from_buffered`] instead, if your reader is an in-memory reader.
+ /// Use [`ReadImage::read_from_file`] instead, if you have a file path.
+ #[inline]
+ #[must_use]
+ pub fn from_unbuffered<Layers>(self, unbuffered: impl Read + Seek) -> Result<Image<Layers>>
+ where for<'s> L: ReadLayers<'s, Layers = Layers>
+ {
+ self.from_buffered(BufReader::new(unbuffered))
+ }
+
+ /// Read the exr image from a buffered reader.
+ /// Use [`ReadImage::read_from_file`] instead, if you have a file path.
+ /// Use [`ReadImage::read_from_unbuffered`] instead, if this is not an in-memory reader.
+ // TODO Use Parallel<> Wrapper to only require sendable byte source where parallel decompression is required
+ #[must_use]
+ pub fn from_buffered<Layers>(self, buffered: impl Read + Seek) -> Result<Image<Layers>>
+ where for<'s> L: ReadLayers<'s, Layers = Layers>
+ {
+ let chunks = crate::block::read(buffered, self.pedantic)?;
+ self.from_chunks(chunks)
+ }
+
+ /// Read the exr image from an initialized chunks reader
+ /// that has already extracted the meta data from the file.
+ /// Use [`ReadImage::read_from_file`] instead, if you have a file path.
+ /// Use [`ReadImage::read_from_buffered`] instead, if this is an in-memory reader.
+ // TODO Use Parallel<> Wrapper to only require sendable byte source where parallel decompression is required
+ #[must_use]
+ pub fn from_chunks<Layers>(mut self, chunks_reader: crate::block::reader::Reader<impl Read + Seek>) -> Result<Image<Layers>>
+ where for<'s> L: ReadLayers<'s, Layers = Layers>
+ {
+ let Self { pedantic, parallel, ref mut on_progress, ref mut read_layers } = self;
+
+ let layers_reader = read_layers.create_layers_reader(chunks_reader.headers())?;
+ let mut image_collector = ImageWithAttributesReader::new(chunks_reader.headers(), layers_reader)?;
+
+ let block_reader = chunks_reader
+ .filter_chunks(pedantic, |meta, tile, block| {
+ image_collector.filter_block(meta, tile, block)
+ })?
+ .on_progress(on_progress);
+
+ // TODO propagate send requirement further upwards
+ if parallel {
+ block_reader.decompress_parallel(pedantic, |meta_data, block|{
+ image_collector.read_block(&meta_data.headers, block)
+ })?;
+ }
+ else {
+ block_reader.decompress_sequential(pedantic, |meta_data, block|{
+ image_collector.read_block(&meta_data.headers, block)
+ })?;
+ }
+
+ Ok(image_collector.into_image())
+ }
+}
+
+/// Processes blocks from a file and collects them into a complete `Image`.
+#[derive(Debug, Clone, PartialEq)]
+pub struct ImageWithAttributesReader<L> {
+ image_attributes: ImageAttributes,
+ layers_reader: L,
+}
+
+impl<L> ImageWithAttributesReader<L> where L: LayersReader {
+
+ /// A new image reader with image attributes.
+ pub fn new(headers: &[Header], layers_reader: L) -> Result<Self>
+ {
+ Ok(ImageWithAttributesReader {
+ image_attributes: headers.first().as_ref().expect("invalid headers").shared_attributes.clone(),
+ layers_reader,
+ })
+ }
+
+ /// Specify whether a single block of pixels should be loaded from the file
+ fn filter_block(&self, meta: &MetaData, tile: TileCoordinates, block: BlockIndex) -> bool {
+ self.layers_reader.filter_block(meta, tile, block)
+ }
+
+ /// Load a single pixel block, which has not been filtered, into the reader, accumulating the image
+ fn read_block(&mut self, headers: &[Header], block: UncompressedBlock) -> UnitResult {
+ self.layers_reader.read_block(headers, block)
+ }
+
+ /// Deliver the complete accumulated image
+ fn into_image(self) -> Image<L::Layers> {
+ Image {
+ attributes: self.image_attributes,
+ layer_data: self.layers_reader.into_layers()
+ }
+ }
+}
+
+
+/// A template that creates a `LayerReader` for each layer in the file.
+pub trait ReadLayers<'s> {
+
+ /// The type of the resulting Layers
+ type Layers;
+
+ /// The type of the temporary layer reader
+ type Reader: LayersReader<Layers = Self::Layers>;
+
+ /// Create a single reader for a single layer
+ fn create_layers_reader(&'s self, headers: &[Header]) -> Result<Self::Reader>;
+
+ /// Specify that all attributes should be read from an image.
+ /// Use `from_file(path)` on the return value of this method to actually decode an image.
+ fn all_attributes(self) -> ReadImage<fn(f64), Self> where Self: Sized {
+ ReadImage::new(self, ignore_progress)
+ }
+}
+
+/// Processes pixel blocks from a file and accumulates them into a single image layer.
+pub trait LayersReader {
+
+ /// The type of resulting layers
+ type Layers;
+
+ /// Specify whether a single block of pixels should be loaded from the file
+ fn filter_block(&self, meta: &MetaData, tile: TileCoordinates, block: BlockIndex) -> bool;
+
+ /// Load a single pixel block, which has not been filtered, into the reader, accumulating the layer
+ fn read_block(&mut self, headers: &[Header], block: UncompressedBlock) -> UnitResult;
+
+ /// Deliver the final accumulated layers for the image
+ fn into_layers(self) -> Self::Layers;
+}
+
diff --git a/vendor/exr/src/image/read/layers.rs b/vendor/exr/src/image/read/layers.rs
new file mode 100644
index 0000000..75159c2
--- /dev/null
+++ b/vendor/exr/src/image/read/layers.rs
@@ -0,0 +1,204 @@
+//! How to read either a single or a list of layers.
+
+use crate::image::*;
+use crate::meta::header::{Header, LayerAttributes};
+use crate::error::{Result, UnitResult, Error};
+use crate::block::{UncompressedBlock, BlockIndex};
+use crate::math::Vec2;
+use crate::image::read::image::{ReadLayers, LayersReader};
+use crate::block::chunk::TileCoordinates;
+use crate::meta::MetaData;
+
+/// Specify to read all channels, aborting if any one is invalid.
+/// [`ReadRgbaChannels`] or [`ReadAnyChannels<ReadFlatSamples>`].
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct ReadAllLayers<ReadChannels> {
+
+ /// The channel reading specification
+ pub read_channels: ReadChannels,
+}
+
+/// Specify to read only the first layer which meets the previously specified requirements
+// FIXME do not throw error on deep data but just skip it!
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct ReadFirstValidLayer<ReadChannels> {
+
+ /// The channel reading specification
+ pub read_channels: ReadChannels,
+}
+
+/// A template that creates a [`ChannelsReader`] once for all channels per layer.
+pub trait ReadChannels<'s> {
+
+ /// The type of the temporary channels reader
+ type Reader: ChannelsReader;
+
+ /// Create a single reader for all channels of a specific layer
+ fn create_channels_reader(&'s self, header: &Header) -> Result<Self::Reader>;
+
+
+ /// Read only the first layer which meets the previously specified requirements
+ /// For example, skips layers with deep data, if specified earlier.
+ /// Aborts if the image contains no layers.
+ // TODO test if this filters non-deep layers while ignoring deep data layers!
+ fn first_valid_layer(self) -> ReadFirstValidLayer<Self> where Self:Sized { ReadFirstValidLayer { read_channels: self } }
+
+// FIXME do not throw error on deep data but just skip it!
+
+
+ /// Reads all layers, including an empty list. Aborts if any of the layers are invalid,
+ /// even if only one of the layers contains unexpected data.
+ fn all_layers(self) -> ReadAllLayers<Self> where Self:Sized { ReadAllLayers { read_channels: self } }
+
+ // TODO pub fn all_valid_layers(self) -> ReadAllValidLayers<Self> { ReadAllValidLayers { read_channels: self } }
+}
+
+
+/// Processes pixel blocks from a file and accumulates them into a list of layers.
+/// For example, `ChannelsReader` can be
+/// [`SpecificChannelsReader`] or [`AnyChannelsReader<FlatSamplesReader>`].
+#[derive(Debug, Clone, PartialEq)]
+pub struct AllLayersReader<ChannelsReader> {
+ layer_readers: SmallVec<[LayerReader<ChannelsReader>; 2]>, // TODO unpack struct?
+}
+
+/// Processes pixel blocks from a file and accumulates them into a single layers, using only the first.
+/// For example, `ChannelsReader` can be
+/// `SpecificChannelsReader` or `AnyChannelsReader<FlatSamplesReader>`.
+#[derive(Debug, Clone, PartialEq)]
+pub struct FirstValidLayerReader<ChannelsReader> {
+ layer_reader: LayerReader<ChannelsReader>,
+ layer_index: usize,
+}
+
+/// Processes pixel blocks from a file and accumulates them into a single layers.
+/// For example, `ChannelsReader` can be
+/// `SpecificChannelsReader` or `AnyChannelsReader<FlatSamplesReader>`.
+#[derive(Debug, Clone, PartialEq)]
+pub struct LayerReader<ChannelsReader> {
+ channels_reader: ChannelsReader,
+ attributes: LayerAttributes,
+ size: Vec2<usize>,
+ encoding: Encoding
+}
+
+/// Processes pixel blocks from a file and accumulates them into multiple channels per layer.
+pub trait ChannelsReader {
+
+ /// The type of the resulting channel collection
+ type Channels;
+
+ /// Specify whether a single block of pixels should be loaded from the file
+ fn filter_block(&self, tile: TileCoordinates) -> bool;
+
+ /// Load a single pixel block, which has not been filtered, into the reader, accumulating the channel data
+ fn read_block(&mut self, header: &Header, block: UncompressedBlock) -> UnitResult;
+
+ /// Deliver the final accumulated channel collection for the image
+ fn into_channels(self) -> Self::Channels;
+}
+
+
+impl<C> LayerReader<C> {
+ fn new(header: &Header, channels_reader: C) -> Result<Self> {
+ Ok(LayerReader {
+ channels_reader,
+ attributes: header.own_attributes.clone(),
+ size: header.layer_size,
+ encoding: Encoding {
+ compression: header.compression,
+ line_order: header.line_order,
+ blocks: match header.blocks {
+ crate::meta::BlockDescription::ScanLines => Blocks::ScanLines,
+ crate::meta::BlockDescription::Tiles(TileDescription { tile_size, .. }) => Blocks::Tiles(tile_size)
+ },
+ },
+ })
+ }
+}
+
+impl<'s, C> ReadLayers<'s> for ReadAllLayers<C> where C: ReadChannels<'s> {
+ type Layers = Layers<<C::Reader as ChannelsReader>::Channels>;
+ type Reader = AllLayersReader<C::Reader>;
+
+ fn create_layers_reader(&'s self, headers: &[Header]) -> Result<Self::Reader> {
+ let readers: Result<_> = headers.iter()
+ .map(|header| LayerReader::new(header, self.read_channels.create_channels_reader(header)?))
+ .collect();
+
+ Ok(AllLayersReader {
+ layer_readers: readers?
+ })
+ }
+}
+
+impl<C> LayersReader for AllLayersReader<C> where C: ChannelsReader {
+ type Layers = Layers<C::Channels>;
+
+ fn filter_block(&self, _: &MetaData, tile: TileCoordinates, block: BlockIndex) -> bool {
+ let layer = self.layer_readers.get(block.layer).expect("invalid layer index argument");
+ layer.channels_reader.filter_block(tile)
+ }
+
+ fn read_block(&mut self, headers: &[Header], block: UncompressedBlock) -> UnitResult {
+ self.layer_readers
+ .get_mut(block.index.layer).expect("invalid layer index argument")
+ .channels_reader.read_block(headers.get(block.index.layer).expect("invalid header index in block"), block)
+ }
+
+ fn into_layers(self) -> Self::Layers {
+ self.layer_readers
+ .into_iter()
+ .map(|layer| Layer {
+ channel_data: layer.channels_reader.into_channels(),
+ attributes: layer.attributes,
+ size: layer.size,
+ encoding: layer.encoding
+ })
+ .collect()
+ }
+}
+
+
+impl<'s, C> ReadLayers<'s> for ReadFirstValidLayer<C> where C: ReadChannels<'s> {
+ type Layers = Layer<<C::Reader as ChannelsReader>::Channels>;
+ type Reader = FirstValidLayerReader<C::Reader>;
+
+ fn create_layers_reader(&'s self, headers: &[Header]) -> Result<Self::Reader> {
+ headers.iter().enumerate()
+ .flat_map(|(index, header)|
+ self.read_channels.create_channels_reader(header)
+ .and_then(|reader| Ok(FirstValidLayerReader {
+ layer_reader: LayerReader::new(header, reader)?,
+ layer_index: index
+ }))
+ .ok()
+ )
+ .next()
+ .ok_or(Error::invalid("no layer in the image matched your specified requirements"))
+ }
+}
+
+
+impl<C> LayersReader for FirstValidLayerReader<C> where C: ChannelsReader {
+ type Layers = Layer<C::Channels>;
+
+ fn filter_block(&self, _: &MetaData, tile: TileCoordinates, block: BlockIndex) -> bool {
+ block.layer == self.layer_index && self.layer_reader.channels_reader.filter_block(tile)
+ }
+
+ fn read_block(&mut self, headers: &[Header], block: UncompressedBlock) -> UnitResult {
+ debug_assert_eq!(block.index.layer, self.layer_index, "block should have been filtered out");
+ self.layer_reader.channels_reader.read_block(&headers[self.layer_index], block)
+ }
+
+ fn into_layers(self) -> Self::Layers {
+ Layer {
+ channel_data: self.layer_reader.channels_reader.into_channels(),
+ attributes: self.layer_reader.attributes,
+ size: self.layer_reader.size,
+ encoding: self.layer_reader.encoding
+ }
+ }
+}
+
diff --git a/vendor/exr/src/image/read/levels.rs b/vendor/exr/src/image/read/levels.rs
new file mode 100644
index 0000000..5705903
--- /dev/null
+++ b/vendor/exr/src/image/read/levels.rs
@@ -0,0 +1,219 @@
+//! How to read a set of resolution levels.
+
+use crate::meta::*;
+use crate::image::*;
+use crate::error::*;
+use crate::meta::attribute::*;
+use crate::image::read::any_channels::*;
+use crate::block::chunk::TileCoordinates;
+use crate::image::read::specific_channels::*;
+use crate::image::recursive::*;
+use crate::math::Vec2;
+use crate::block::lines::LineRef;
+use crate::block::samples::*;
+use crate::meta::header::{Header};
+
+
+// Note: In the resulting image, the `FlatSamples` are placed
+// directly inside the channels, without `LargestLevel<>` indirection
+/// Specify to read only the highest resolution level, skipping all smaller variations.
+/// The sample storage can be [`ReadFlatSamples`].
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct ReadLargestLevel<DeepOrFlatSamples> {
+
+ /// The sample reading specification
+ pub read_samples: DeepOrFlatSamples
+}
+
+
+// FIXME rgba levels???
+
+// Read the largest level, directly, without intermediate structs
+impl<DeepOrFlatSamples> ReadLargestLevel<DeepOrFlatSamples> {
+
+ /// Read all arbitrary channels in each layer.
+ pub fn all_channels(self) -> ReadAnyChannels<DeepOrFlatSamples> { ReadAnyChannels { read_samples: self.read_samples } } // Instead of Self, the `FlatSamples` are used directly
+
+ /// Read only layers that contain rgba channels. Skips any other channels in the layer.
+ /// The alpha channel will contain the value `1.0` if no alpha channel can be found in the image.
+ ///
+ /// Using two closures, define how to store the pixels.
+ /// The first closure creates an image, and the second closure inserts a single pixel.
+ /// The type of the pixel can be defined by the second closure;
+ /// it must be a tuple containing four values, each being either `f16`, `f32`, `u32` or `Sample`.
+ ///
+ /// Throws an error for images with deep data or subsampling.
+ /// Use `specific_channels` or `all_channels` if you want to read something other than rgba.
+ pub fn rgba_channels<R,G,B,A, Create, Set, Pixels>(
+ self, create_pixels: Create, set_pixel: Set
+ ) -> CollectPixels<
+ ReadOptionalChannel<ReadRequiredChannel<ReadRequiredChannel<ReadRequiredChannel<NoneMore, R>, G>, B>, A>,
+ (R, G, B, A), Pixels, Create, Set
+ >
+ where
+ R: FromNativeSample, G: FromNativeSample, B: FromNativeSample, A: FromNativeSample,
+ Create: Fn(Vec2<usize>, &RgbaChannels) -> Pixels,
+ Set: Fn(&mut Pixels, Vec2<usize>, (R,G,B,A)),
+ {
+ self.specific_channels()
+ .required("R").required("G").required("B")
+ .optional("A", A::from_f32(1.0))
+ .collect_pixels(create_pixels, set_pixel)
+ }
+
+ /// Read only layers that contain rgb channels. Skips any other channels in the layer.
+ ///
+ /// Using two closures, define how to store the pixels.
+ /// The first closure creates an image, and the second closure inserts a single pixel.
+ /// The type of the pixel can be defined by the second closure;
+ /// it must be a tuple containing three values, each being either `f16`, `f32`, `u32` or `Sample`.
+ ///
+ /// Throws an error for images with deep data or subsampling.
+ /// Use `specific_channels` or `all_channels` if you want to read something other than rgb.
+ pub fn rgb_channels<R,G,B, Create, Set, Pixels>(
+ self, create_pixels: Create, set_pixel: Set
+ ) -> CollectPixels<
+ ReadRequiredChannel<ReadRequiredChannel<ReadRequiredChannel<NoneMore, R>, G>, B>,
+ (R, G, B), Pixels, Create, Set
+ >
+ where
+ R: FromNativeSample, G: FromNativeSample, B: FromNativeSample,
+ Create: Fn(Vec2<usize>, &RgbChannels) -> Pixels,
+ Set: Fn(&mut Pixels, Vec2<usize>, (R,G,B)),
+ {
+ self.specific_channels()
+ .required("R").required("G").required("B")
+ .collect_pixels(create_pixels, set_pixel)
+ }
+
+ /// Read only layers that contain the specified channels, skipping any other channels in the layer.
+ /// Further specify which channels should be included by calling `.required("ChannelName")`
+ /// or `.optional("ChannelName", default_value)` on the result of this function.
+ /// Call `collect_pixels` afterwards to define the pixel container for your set of channels.
+ ///
+ /// Throws an error for images with deep data or subsampling.
+ pub fn specific_channels(self) -> ReadZeroChannels {
+ ReadZeroChannels { }
+ }
+}
+
+/// Specify to read all contained resolution levels from the image, if any.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct ReadAllLevels<DeepOrFlatSamples> {
+
+ /// The sample reading specification
+ pub read_samples: DeepOrFlatSamples
+}
+
+impl<ReadDeepOrFlatSamples> ReadAllLevels<ReadDeepOrFlatSamples> {
+
+ /// Read all arbitrary channels in each layer.
+ pub fn all_channels(self) -> ReadAnyChannels<Self> { ReadAnyChannels { read_samples: self } }
+
+ // TODO specific channels for multiple resolution levels
+
+}
+
+/*pub struct ReadLevels<S> {
+ read_samples: S,
+}*/
+
+/// Processes pixel blocks from a file and accumulates them into multiple levels per channel.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct AllLevelsReader<SamplesReader> {
+ levels: Levels<SamplesReader>,
+}
+
+/// A template that creates a [`SamplesReader`] once for each resolution level.
+pub trait ReadSamplesLevel {
+
+ /// The type of the temporary level reader
+ type Reader: SamplesReader;
+
+ /// Create a single reader for a single resolution level
+ fn create_samples_level_reader(&self, header: &Header, channel: &ChannelDescription, level: Vec2<usize>, resolution: Vec2<usize>) -> Result<Self::Reader>;
+}
+
+
+impl<S: ReadSamplesLevel> ReadSamples for ReadAllLevels<S> {
+ type Reader = AllLevelsReader<S::Reader>;
+
+ fn create_sample_reader(&self, header: &Header, channel: &ChannelDescription) -> Result<Self::Reader> {
+ let data_size = header.layer_size / channel.sampling;
+
+ let levels = {
+ if let crate::meta::BlockDescription::Tiles(tiles) = &header.blocks {
+ match tiles.level_mode {
+ LevelMode::Singular => Levels::Singular(self.read_samples.create_samples_level_reader(header, channel, Vec2(0,0), header.layer_size)?),
+
+ LevelMode::MipMap => Levels::Mip {
+ rounding_mode: tiles.rounding_mode,
+ level_data: {
+ let round = tiles.rounding_mode;
+ let maps: Result<LevelMaps<S::Reader>> = mip_map_levels(round, data_size)
+ .map(|(index, level_size)| self.read_samples.create_samples_level_reader(header, channel, Vec2(index, index), level_size))
+ .collect();
+
+ maps?
+ },
+ },
+
+ // TODO put this into Levels::new(..) ?
+ LevelMode::RipMap => Levels::Rip {
+ rounding_mode: tiles.rounding_mode,
+ level_data: {
+ let round = tiles.rounding_mode;
+ let level_count_x = compute_level_count(round, data_size.width());
+ let level_count_y = compute_level_count(round, data_size.height());
+ let maps: Result<LevelMaps<S::Reader>> = rip_map_levels(round, data_size)
+ .map(|(index, level_size)| self.read_samples.create_samples_level_reader(header, channel, index, level_size))
+ .collect();
+
+ RipMaps {
+ map_data: maps?,
+ level_count: Vec2(level_count_x, level_count_y)
+ }
+ },
+ },
+ }
+ }
+
+ // scan line blocks never have mip maps
+ else {
+ Levels::Singular(self.read_samples.create_samples_level_reader(header, channel, Vec2(0, 0), data_size)?)
+ }
+ };
+
+ Ok(AllLevelsReader { levels })
+ }
+}
+
+
+impl<S: SamplesReader> SamplesReader for AllLevelsReader<S> {
+ type Samples = Levels<S::Samples>;
+
+ fn filter_block(&self, _: TileCoordinates) -> bool {
+ true
+ }
+
+ fn read_line(&mut self, line: LineRef<'_>) -> UnitResult {
+ self.levels.get_level_mut(line.location.level)?.read_line(line)
+ }
+
+ fn into_samples(self) -> Self::Samples {
+ match self.levels {
+ Levels::Singular(level) => Levels::Singular(level.into_samples()),
+ Levels::Mip { rounding_mode, level_data } => Levels::Mip {
+ rounding_mode, level_data: level_data.into_iter().map(|s| s.into_samples()).collect(),
+ },
+
+ Levels::Rip { rounding_mode, level_data } => Levels::Rip {
+ rounding_mode,
+ level_data: RipMaps {
+ level_count: level_data.level_count,
+ map_data: level_data.map_data.into_iter().map(|s| s.into_samples()).collect(),
+ }
+ },
+ }
+ }
+}
diff --git a/vendor/exr/src/image/read/mod.rs b/vendor/exr/src/image/read/mod.rs
new file mode 100644
index 0000000..c03fc90
--- /dev/null
+++ b/vendor/exr/src/image/read/mod.rs
@@ -0,0 +1,207 @@
+
+//! Read an exr image.
+//!
+//! For great flexibility and customization, use the `read()` function.
+//! The return value of the `read()` function must be further customized before reading a file.
+
+//!
+//! For very simple applications, you can alternatively use one of these functions:
+//!
+//! 1. `read_first_rgba_layer_from_file(path, your_constructor, your_pixel_setter)`:
+//! You specify how to store the pixels.
+//! The first layer containing rgba channels is then loaded from the file.
+//! Fails if no rgba layer can be found.
+//!
+//! 1. `read_all_rgba_layers_from_file(path, your_constructor, your_pixel_setter)`:
+//! You specify how to store the pixels.
+//! All layers containing rgba channels are then loaded from the file.
+//! Fails if any layer in the image does not contain rgba channels.
+//!
+//! 1. `read_first_flat_layer_from_file(path)`:
+//! The first layer containing non-deep data with arbitrary channels is loaded from the file.
+//! Fails if no non-deep layer can be found.
+//!
+//! 1. `read_all_flat_layers_from_file(path)`:
+//! All layers containing non-deep data with arbitrary channels are loaded from the file.
+//! Fails if any layer in the image contains deep data.
+//!
+//! 1. `read_all_data_from_file(path)`:
+//! All layers with arbitrary channels and all resolution levels are extracted from the file.
+//!
+//! Note: Currently does not support deep data, and currently fails
+//! if any layer in the image contains deep data.
+//!
+
+// The following three stages are internally used to read an image.
+// 1. `ReadImage` - The specification. Contains everything the user wants to tell us about loading an image.
+// The data in this structure will be instantiated and might be borrowed.
+// 2. `ImageReader` - The temporary reader. Based on the specification of the blueprint,
+// a reader is instantiated, once for each layer.
+// This data structure accumulates the image data from the file.
+// It also owns temporary data and references the blueprint.
+// 3. `Image` - The clean image. The accumulated data from the Reader
+// is converted to the clean image structure, without temporary data.
+
+pub mod image;
+pub mod layers;
+pub mod any_channels;
+pub mod levels;
+pub mod samples;
+pub mod specific_channels;
+
+use crate::error::{Result};
+use crate::image::read::samples::{ReadFlatSamples};
+use std::path::Path;
+use crate::image::{AnyImage, AnyChannels, FlatSamples, Image, Layer, FlatImage, PixelLayersImage, RgbaChannels};
+use crate::image::read::image::ReadLayers;
+use crate::image::read::layers::ReadChannels;
+use crate::math::Vec2;
+use crate::prelude::{PixelImage};
+use crate::block::samples::FromNativeSample;
+
+
+/// All resolution levels, all channels, all layers.
+/// Does not support deep data yet. Uses parallel decompression and relaxed error handling.
+/// Inspect the source code of this function if you need customization.
+pub fn read_all_data_from_file(path: impl AsRef<Path>) -> Result<AnyImage> {
+ read()
+ .no_deep_data() // TODO deep data
+ .all_resolution_levels()
+ .all_channels()
+ .all_layers()
+ .all_attributes()
+ .from_file(path)
+}
+
+// FIXME do not throw error on deep data but just skip it!
+/// No deep data, no resolution levels, all channels, all layers.
+/// Uses parallel decompression and relaxed error handling.
+/// Inspect the source code of this function if you need customization.
+pub fn read_all_flat_layers_from_file(path: impl AsRef<Path>) -> Result<FlatImage> {
+ read()
+ .no_deep_data()
+ .largest_resolution_level()
+ .all_channels()
+ .all_layers()
+ .all_attributes()
+ .from_file(path)
+}
+
+/// No deep data, no resolution levels, all channels, first layer.
+/// Uses parallel decompression and relaxed error handling.
+/// Inspect the source code of this function if you need customization.
+pub fn read_first_flat_layer_from_file(path: impl AsRef<Path>) -> Result<Image<Layer<AnyChannels<FlatSamples>>>> {
+ read()
+ .no_deep_data()
+ .largest_resolution_level()
+ .all_channels()
+ .first_valid_layer()
+ .all_attributes()
+ .from_file(path)
+}
+
+/// No deep data, no resolution levels, rgba channels, all layers.
+/// If a single layer does not contain rgba data, this method returns an error.
+/// Uses parallel decompression and relaxed error handling.
+/// `Create` and `Set` can be closures, see the examples for more information.
+/// Inspect the source code of this function if you need customization.
+/// The alpha channel will contain the value `1.0` if no alpha channel can be found in the image.
+///
+/// Using two closures, define how to store the pixels.
+/// The first closure creates an image, and the second closure inserts a single pixel.
+/// The type of the pixel can be defined by the second closure;
+/// it must be a tuple containing four values, each being either `f16`, `f32`, `u32` or `Sample`.
+// FIXME Set and Create should not need to be static
+pub fn read_all_rgba_layers_from_file<R,G,B,A, Set:'static, Create:'static, Pixels: 'static>(
+ path: impl AsRef<Path>, create: Create, set_pixel: Set
+)
+ -> Result<PixelLayersImage<Pixels, RgbaChannels>>
+ where
+ R: FromNativeSample, G: FromNativeSample, B: FromNativeSample, A: FromNativeSample,
+ Create: Fn(Vec2<usize>, &RgbaChannels) -> Pixels, // TODO type alias? CreateRgbaPixels<Pixels=Pixels>,
+ Set: Fn(&mut Pixels, Vec2<usize>, (R,G,B,A)),
+{
+ read()
+ .no_deep_data()
+ .largest_resolution_level()
+ .rgba_channels(create, set_pixel)
+ .all_layers()
+ .all_attributes()
+ .from_file(path)
+}
+
+/// No deep data, no resolution levels, rgba channels, choosing the first layer with rgba channels.
+/// Uses parallel decompression and relaxed error handling.
+/// `Create` and `Set` can be closures, see the examples for more information.
+/// Inspect the source code of this function if you need customization.
+/// The alpha channel will contain the value `1.0` if no alpha channel can be found in the image.
+///
+/// Using two closures, define how to store the pixels.
+/// The first closure creates an image, and the second closure inserts a single pixel.
+/// The type of the pixel can be defined by the second closure;
+/// it must be a tuple containing four values, each being either `f16`, `f32`, `u32` or `Sample`.
+// FIXME Set and Create should not need to be static
+pub fn read_first_rgba_layer_from_file<R,G,B,A, Set:'static, Create:'static, Pixels: 'static>(
+ path: impl AsRef<Path>, create: Create, set_pixel: Set
+)
+ -> Result<PixelImage<Pixels, RgbaChannels>>
+ where
+ R: FromNativeSample, G: FromNativeSample, B: FromNativeSample, A: FromNativeSample,
+ Create: Fn(Vec2<usize>, &RgbaChannels) -> Pixels, // TODO type alias? CreateRgbaPixels<Pixels=Pixels>,
+ Set: Fn(&mut Pixels, Vec2<usize>, (R,G,B,A)),
+{
+ read()
+ .no_deep_data()
+ .largest_resolution_level()
+ .rgba_channels(create, set_pixel)
+ .first_valid_layer()
+ .all_attributes()
+ .from_file(path)
+}
+
+
+/// Utilizes the builder pattern to configure an image reader. This is the initial struct.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct ReadBuilder;
+
+/// Create a reader which can be used to load an exr image.
+/// Allows you to exactly specify how to load the image, for example:
+///
+/// ```no_run
+/// use exr::prelude::*;
+///
+/// // the type of the this image depends on the chosen options
+/// let image = read()
+/// .no_deep_data() // (currently required)
+/// .largest_resolution_level() // or `all_resolution_levels()`
+/// .all_channels() // or `rgba_channels(constructor, setter)`
+/// .all_layers() // or `first_valid_layer()`
+/// .all_attributes() // (currently required)
+/// .on_progress(|progress| println!("progress: {:.1}", progress*100.0)) // optional
+/// .from_file("image.exr").unwrap(); // or `from_buffered(my_byte_slice)`
+/// ```
+///
+/// You can alternatively use one of the following simpler functions:
+/// 1. `read_first_flat_layer_from_file`
+/// 1. `read_all_rgba_layers_from_file`
+/// 1. `read_all_flat_layers_from_file`
+/// 1. `read_all_data_from_file`
+///
+// TODO not panic but skip deep layers!
+pub fn read() -> ReadBuilder { ReadBuilder }
+
+impl ReadBuilder {
+
+ /// Specify to handle only one sample per channel, disabling "deep data".
+ // TODO not panic but skip deep layers!
+ pub fn no_deep_data(self) -> ReadFlatSamples { ReadFlatSamples }
+
+ // pub fn any_resolution_levels() -> ReadBuilder<> {}
+
+ // TODO
+ // e. g. `let sum = reader.any_channels_with(|sample, sum| sum += sample)`
+ // e. g. `let floats = reader.any_channels_with(|sample, f32_samples| f32_samples[index] = sample as f32)`
+ // pub fn no_deep_data_with <S> (self, storage: S) -> FlatSamplesWith<S> { }
+
+ // pub fn flat_and_deep_data(self) -> ReadAnySamples { ReadAnySamples }
+}
diff --git a/vendor/exr/src/image/read/samples.rs b/vendor/exr/src/image/read/samples.rs
new file mode 100644
index 0000000..e03c3cc
--- /dev/null
+++ b/vendor/exr/src/image/read/samples.rs
@@ -0,0 +1,122 @@
+//! How to read samples (a grid of `f32`, `f16` or `u32` values).
+
+use crate::image::*;
+use crate::meta::header::{Header};
+use crate::error::{Result, UnitResult};
+use crate::block::lines::LineRef;
+use crate::math::Vec2;
+use crate::meta::attribute::{ChannelDescription, SampleType};
+use crate::image::read::any_channels::{SamplesReader, ReadSamples};
+use crate::image::read::levels::{ReadSamplesLevel, ReadAllLevels, ReadLargestLevel};
+use crate::block::chunk::TileCoordinates;
+// use crate::image::read::layers::ReadChannels;
+
+/// Specify to read only flat samples and no "deep data"
+// FIXME do not throw error on deep data but just skip it!
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub struct ReadFlatSamples;
+// pub struct ReadAnySamples;
+
+impl ReadFlatSamples {
+
+ // TODO
+ // e. g. `let sum = reader.any_channels_with(|sample, sum| sum += sample)`
+ // pub fn any_channels_with <S> (self, storage: S) -> { }
+
+ /// Specify to read only the highest resolution level, skipping all smaller variations.
+ pub fn largest_resolution_level(self) -> ReadLargestLevel<Self> { ReadLargestLevel { read_samples: self } }
+
+ /// Specify to read all contained resolution levels from the image, if any.
+ pub fn all_resolution_levels(self) -> ReadAllLevels<Self> { ReadAllLevels { read_samples: self } }
+
+ // TODO pub fn specific_resolution_level<F: Fn(&[Vec2<usize>])->usize >(self, select_level: F) -> ReadLevelBy<Self> { ReadAllLevels { read_samples: self } }
+}
+
+
+/*pub struct AnySamplesReader { TODO
+ resolution: Vec2<usize>,
+ samples: DeepAndFlatSamples
+}*/
+
+/// Processes pixel blocks from a file and accumulates them into a grid of samples, for example "Red" or "Alpha".
+#[derive(Debug, Clone, PartialEq)]
+pub struct FlatSamplesReader {
+ level: Vec2<usize>,
+ resolution: Vec2<usize>,
+ samples: FlatSamples
+}
+
+
+// only used when samples is directly inside a channel, without levels
+impl ReadSamples for ReadFlatSamples {
+ type Reader = FlatSamplesReader;
+
+ fn create_sample_reader(&self, header: &Header, channel: &ChannelDescription) -> Result<Self::Reader> {
+ self.create_samples_level_reader(header, channel, Vec2(0, 0), header.layer_size)
+ }
+}
+
+impl ReadSamplesLevel for ReadFlatSamples {
+ type Reader = FlatSamplesReader;
+
+ fn create_samples_level_reader(&self, _header: &Header, channel: &ChannelDescription, level: Vec2<usize>, resolution: Vec2<usize>) -> Result<Self::Reader> {
+ Ok(FlatSamplesReader {
+ level, resolution, // TODO sampling
+ samples: match channel.sample_type {
+ SampleType::F16 => FlatSamples::F16(vec![f16::ZERO; resolution.area()]),
+ SampleType::F32 => FlatSamples::F32(vec![0.0; resolution.area()]),
+ SampleType::U32 => FlatSamples::U32(vec![0; resolution.area()]),
+ }
+ })
+ }
+}
+
+
+impl SamplesReader for FlatSamplesReader {
+ type Samples = FlatSamples;
+
+ fn filter_block(&self, tile: TileCoordinates) -> bool {
+ tile.level_index == self.level
+ }
+
+ fn read_line(&mut self, line: LineRef<'_>) -> UnitResult {
+ let index = line.location;
+ let resolution = self.resolution;
+
+ // the index is generated by ourselves and must always be correct
+ debug_assert_eq!(index.level, self.level, "line should have been filtered");
+ debug_assert!(index.position.x() + index.sample_count <= resolution.width(), "line index calculation bug");
+ debug_assert!(index.position.y() < resolution.height(), "line index calculation bug");
+ debug_assert_ne!(resolution.0, 0, "sample size bug");
+
+ let start_index = index.position.y() * resolution.width() + index.position.x();
+ let end_index = start_index + index.sample_count;
+
+ debug_assert!(
+ start_index < end_index && end_index <= self.samples.len(),
+ "for resolution {:?}, this is an invalid line: {:?}",
+ self.resolution, line.location
+ );
+
+ match &mut self.samples {
+ FlatSamples::F16(samples) =>
+ line.read_samples_into_slice(&mut samples[start_index .. end_index])
+ .expect("writing line bytes failed"),
+
+ FlatSamples::F32(samples) =>
+ line.read_samples_into_slice(&mut samples[start_index .. end_index])
+ .expect("writing line bytes failed"),
+
+ FlatSamples::U32(samples) =>
+ line.read_samples_into_slice(&mut samples[start_index .. end_index])
+ .expect("writing line bytes failed"),
+ }
+
+ Ok(())
+ }
+
+ fn into_samples(self) -> FlatSamples {
+ self.samples
+ }
+}
+
diff --git a/vendor/exr/src/image/read/specific_channels.rs b/vendor/exr/src/image/read/specific_channels.rs
new file mode 100644
index 0000000..375691c
--- /dev/null
+++ b/vendor/exr/src/image/read/specific_channels.rs
@@ -0,0 +1,463 @@
+//! How to read arbitrary but specific selection of arbitrary channels.
+//! This is not a zero-cost abstraction.
+
+use crate::image::recursive::*;
+use crate::block::samples::*;
+use crate::image::*;
+use crate::math::*;
+use crate::meta::header::*;
+use crate::error::*;
+use crate::block::UncompressedBlock;
+use crate::image::read::layers::{ChannelsReader, ReadChannels};
+use crate::block::chunk::TileCoordinates;
+
+use std::marker::PhantomData;
+use crate::io::Read;
+
+
+/// Can be attached one more channel reader.
+/// Call `required` or `optional` on this object to declare another channel to be read from the file.
+/// Call `collect_pixels` at last to define how the previously declared pixels should be stored.
+pub trait ReadSpecificChannel: Sized + CheckDuplicates {
+
+ /// A separate internal reader for the pixels. Will be of type `Recursive<_, SampleReader<_>>`,
+ /// depending on the pixels of the specific channel combination.
+ type RecursivePixelReader: RecursivePixelReader;
+
+ /// Create a separate internal reader for the pixels of the specific channel combination.
+ fn create_recursive_reader(&self, channels: &ChannelList) -> Result<Self::RecursivePixelReader>;
+
+ /// Plan to read an additional channel from the image, with the specified name.
+ /// If the channel cannot be found in the image when the image is read, the image will not be loaded.
+ /// The generic parameter can usually be inferred from the closure in `collect_pixels`.
+ fn required<Sample>(self, channel_name: impl Into<Text>) -> ReadRequiredChannel<Self, Sample> {
+ let channel_name = channel_name.into();
+ assert!(self.already_contains(&channel_name).not(), "a channel with the name `{}` is already defined", channel_name);
+ ReadRequiredChannel { channel_name, previous_channels: self, px: Default::default() }
+ }
+
+ /// Plan to read an additional channel from the image, with the specified name.
+ /// If the file does not contain this channel, the specified default sample will be returned instead.
+ /// You can check whether the channel has been loaded by
+ /// checking the presence of the optional channel description before instantiating your own image.
+ /// The generic parameter can usually be inferred from the closure in `collect_pixels`.
+ fn optional<Sample>(self, channel_name: impl Into<Text>, default_sample: Sample)
+ -> ReadOptionalChannel<Self, Sample>
+ {
+ let channel_name = channel_name.into();
+ assert!(self.already_contains(&channel_name).not(), "a channel with the name `{}` is already defined", channel_name);
+ ReadOptionalChannel { channel_name, previous_channels: self, default_sample }
+ }
+
+ /// Using two closures, define how to store the pixels.
+ /// The first closure creates an image, and the second closure inserts a single pixel.
+ /// The type of the pixel can be defined by the second closure;
+ /// it must be a tuple containing `f16`, `f32`, `u32` or `Sample` values.
+ /// See the examples for more information.
+ fn collect_pixels<Pixel, PixelStorage, CreatePixels, SetPixel>(
+ self, create_pixels: CreatePixels, set_pixel: SetPixel
+ ) -> CollectPixels<Self, Pixel, PixelStorage, CreatePixels, SetPixel>
+ where
+ <Self::RecursivePixelReader as RecursivePixelReader>::RecursivePixel: IntoTuple<Pixel>,
+ <Self::RecursivePixelReader as RecursivePixelReader>::RecursiveChannelDescriptions: IntoNonRecursive,
+ CreatePixels: Fn(
+ Vec2<usize>,
+ &<<Self::RecursivePixelReader as RecursivePixelReader>::RecursiveChannelDescriptions as IntoNonRecursive>::NonRecursive
+ ) -> PixelStorage,
+ SetPixel: Fn(&mut PixelStorage, Vec2<usize>, Pixel),
+ {
+ CollectPixels { read_channels: self, set_pixel, create_pixels, px: Default::default() }
+ }
+}
+
+/// A reader containing sub-readers for reading the pixel content of an image.
+pub trait RecursivePixelReader {
+
+ /// The channel descriptions from the image.
+ /// Will be converted to a tuple before being stored in `SpecificChannels<_, ChannelDescriptions>`.
+ type RecursiveChannelDescriptions;
+
+ /// Returns the channel descriptions based on the channels in the file.
+ fn get_descriptions(&self) -> Self::RecursiveChannelDescriptions;
+
+ /// The pixel type. Will be converted to a tuple at the end of the process.
+ type RecursivePixel: Copy + Default + 'static;
+
+ /// Read the line of pixels.
+ fn read_pixels<'s, FullPixel>(
+ &self, bytes: &'s[u8], pixels: &mut [FullPixel],
+ get_pixel: impl Fn(&mut FullPixel) -> &mut Self::RecursivePixel
+ );
+}
+
+// does not use the generic `Recursive` struct to reduce the number of angle brackets in the public api
+/// Used to read another specific channel from an image.
+/// Contains the previous `ReadChannels` objects.
+#[derive(Clone, Debug)]
+pub struct ReadOptionalChannel<ReadChannels, Sample> {
+ previous_channels: ReadChannels,
+ channel_name: Text,
+ default_sample: Sample,
+}
+
+// does not use the generic `Recursive` struct to reduce the number of angle brackets in the public api
+/// Used to read another specific channel from an image.
+/// Contains the previous `ReadChannels` objects.
+#[derive(Clone, Debug)]
+pub struct ReadRequiredChannel<ReadChannels, Sample> {
+ previous_channels: ReadChannels,
+ channel_name: Text,
+ px: PhantomData<Sample>,
+}
+
+/// Specifies how to collect all the specified channels into a number of individual pixels.
+#[derive(Copy, Clone, Debug)]
+pub struct CollectPixels<ReadChannels, Pixel, PixelStorage, CreatePixels, SetPixel> {
+ read_channels: ReadChannels,
+ create_pixels: CreatePixels,
+ set_pixel: SetPixel,
+ px: PhantomData<(Pixel, PixelStorage)>,
+}
+
+impl<Inner: CheckDuplicates, Sample> CheckDuplicates for ReadRequiredChannel<Inner, Sample> {
+ fn already_contains(&self, name: &Text) -> bool {
+ &self.channel_name == name || self.previous_channels.already_contains(name)
+ }
+}
+
+impl<Inner: CheckDuplicates, Sample> CheckDuplicates for ReadOptionalChannel<Inner, Sample> {
+ fn already_contains(&self, name: &Text) -> bool {
+ &self.channel_name == name || self.previous_channels.already_contains(name)
+ }
+}
+
+impl<'s, InnerChannels, Pixel, PixelStorage, CreatePixels, SetPixel: 's>
+ReadChannels<'s> for CollectPixels<InnerChannels, Pixel, PixelStorage, CreatePixels, SetPixel>
+ where
+ InnerChannels: ReadSpecificChannel,
+ <InnerChannels::RecursivePixelReader as RecursivePixelReader>::RecursivePixel: IntoTuple<Pixel>,
+ <InnerChannels::RecursivePixelReader as RecursivePixelReader>::RecursiveChannelDescriptions: IntoNonRecursive,
+ CreatePixels: Fn(Vec2<usize>, &<<InnerChannels::RecursivePixelReader as RecursivePixelReader>::RecursiveChannelDescriptions as IntoNonRecursive>::NonRecursive) -> PixelStorage,
+ SetPixel: Fn(&mut PixelStorage, Vec2<usize>, Pixel),
+{
+ type Reader = SpecificChannelsReader<
+ PixelStorage, &'s SetPixel,
+ InnerChannels::RecursivePixelReader,
+ Pixel,
+ >;
+
+ fn create_channels_reader(&'s self, header: &Header) -> Result<Self::Reader> {
+ if header.deep { return Err(Error::invalid("`SpecificChannels` does not support deep data yet")) }
+
+ let pixel_reader = self.read_channels.create_recursive_reader(&header.channels)?;
+ let channel_descriptions = pixel_reader.get_descriptions().into_non_recursive();// TODO not call this twice
+
+ let create = &self.create_pixels;
+ let pixel_storage = create(header.layer_size, &channel_descriptions);
+
+ Ok(SpecificChannelsReader {
+ set_pixel: &self.set_pixel,
+ pixel_storage,
+ pixel_reader,
+ px: Default::default()
+ })
+ }
+}
+
+/// The reader that holds the temporary data that is required to read some specified channels.
+#[derive(Copy, Clone, Debug)]
+pub struct SpecificChannelsReader<PixelStorage, SetPixel, PixelReader, Pixel> {
+ set_pixel: SetPixel,
+ pixel_storage: PixelStorage,
+ pixel_reader: PixelReader,
+ px: PhantomData<Pixel>
+}
+
+impl<PixelStorage, SetPixel, PxReader, Pixel>
+ChannelsReader for SpecificChannelsReader<PixelStorage, SetPixel, PxReader, Pixel>
+ where PxReader: RecursivePixelReader,
+ PxReader::RecursivePixel: IntoTuple<Pixel>,
+ PxReader::RecursiveChannelDescriptions: IntoNonRecursive,
+ SetPixel: Fn(&mut PixelStorage, Vec2<usize>, Pixel),
+{
+ type Channels = SpecificChannels<PixelStorage, <PxReader::RecursiveChannelDescriptions as IntoNonRecursive>::NonRecursive>;
+
+ fn filter_block(&self, tile: TileCoordinates) -> bool { tile.is_largest_resolution_level() } // TODO all levels
+
+ fn read_block(&mut self, header: &Header, block: UncompressedBlock) -> UnitResult {
+ let mut pixels = vec![PxReader::RecursivePixel::default(); block.index.pixel_size.width()]; // TODO allocate once in self
+
+ let byte_lines = block.data.chunks_exact(header.channels.bytes_per_pixel * block.index.pixel_size.width());
+ debug_assert_eq!(byte_lines.len(), block.index.pixel_size.height(), "invalid block lines split");
+
+ for (y_offset, line_bytes) in byte_lines.enumerate() { // TODO sampling
+ // this two-step copy method should be very cache friendly in theory, and also reduce sample_type lookup count
+ self.pixel_reader.read_pixels(line_bytes, &mut pixels, |px| px);
+
+ for (x_offset, pixel) in pixels.iter().enumerate() {
+ let set_pixel = &self.set_pixel;
+ set_pixel(&mut self.pixel_storage, block.index.pixel_position + Vec2(x_offset, y_offset), pixel.into_tuple());
+ }
+ }
+
+ Ok(())
+ }
+
+ fn into_channels(self) -> Self::Channels {
+ SpecificChannels { channels: self.pixel_reader.get_descriptions().into_non_recursive(), pixels: self.pixel_storage }
+ }
+}
+
+
+/// Read zero channels from an image. Call `with_named_channel` on this object
+/// to read as many channels as desired.
+pub type ReadZeroChannels = NoneMore;
+
+impl ReadSpecificChannel for NoneMore {
+ type RecursivePixelReader = NoneMore;
+ fn create_recursive_reader(&self, _: &ChannelList) -> Result<Self::RecursivePixelReader> { Ok(NoneMore) }
+}
+
+impl<DefaultSample, ReadChannels> ReadSpecificChannel for ReadOptionalChannel<ReadChannels, DefaultSample>
+ where ReadChannels: ReadSpecificChannel, DefaultSample: FromNativeSample + 'static,
+{
+ type RecursivePixelReader = Recursive<ReadChannels::RecursivePixelReader, OptionalSampleReader<DefaultSample>>;
+
+ fn create_recursive_reader(&self, channels: &ChannelList) -> Result<Self::RecursivePixelReader> {
+ debug_assert!(self.previous_channels.already_contains(&self.channel_name).not(), "duplicate channel name: {}", self.channel_name);
+
+ let inner_samples_reader = self.previous_channels.create_recursive_reader(channels)?;
+ let reader = channels.channels_with_byte_offset()
+ .find(|(_, channel)| channel.name == self.channel_name)
+ .map(|(channel_byte_offset, channel)| SampleReader {
+ channel_byte_offset, channel: channel.clone(),
+ px: Default::default()
+ });
+
+ Ok(Recursive::new(inner_samples_reader, OptionalSampleReader {
+ reader, default_sample: self.default_sample,
+ }))
+ }
+}
+
+impl<Sample, ReadChannels> ReadSpecificChannel for ReadRequiredChannel<ReadChannels, Sample>
+ where ReadChannels: ReadSpecificChannel, Sample: FromNativeSample + 'static
+{
+ type RecursivePixelReader = Recursive<ReadChannels::RecursivePixelReader, SampleReader<Sample>>;
+
+ fn create_recursive_reader(&self, channels: &ChannelList) -> Result<Self::RecursivePixelReader> {
+ let previous_samples_reader = self.previous_channels.create_recursive_reader(channels)?;
+ let (channel_byte_offset, channel) = channels.channels_with_byte_offset()
+ .find(|(_, channel)| channel.name == self.channel_name)
+ .ok_or_else(|| Error::invalid(format!(
+ "layer does not contain all of your specified channels (`{}` is missing)",
+ self.channel_name
+ )))?;
+
+ Ok(Recursive::new(previous_samples_reader, SampleReader { channel_byte_offset, channel: channel.clone(), px: Default::default() }))
+ }
+}
+
+/// Reader for a single channel. Generic over the concrete sample type (f16, f32, u32).
+#[derive(Clone, Debug)]
+pub struct SampleReader<Sample> {
+
+ /// to be multiplied with line width!
+ channel_byte_offset: usize,
+
+ channel: ChannelDescription,
+ px: PhantomData<Sample>
+}
+
+/// Reader for a single channel. Generic over the concrete sample type (f16, f32, u32).
+/// Can also skip reading a channel if it could not be found in the image.
+#[derive(Clone, Debug)]
+pub struct OptionalSampleReader<DefaultSample> {
+ reader: Option<SampleReader<DefaultSample>>,
+ default_sample: DefaultSample,
+}
+
+impl<Sample: FromNativeSample> SampleReader<Sample> {
+ fn read_own_samples<'s, FullPixel>(
+ &self, bytes: &'s[u8], pixels: &mut [FullPixel],
+ get_sample: impl Fn(&mut FullPixel) -> &mut Sample
+ ){
+ let start_index = pixels.len() * self.channel_byte_offset;
+ let byte_count = pixels.len() * self.channel.sample_type.bytes_per_sample();
+ let mut own_bytes_reader = &mut &bytes[start_index .. start_index + byte_count]; // TODO check block size somewhere
+ let mut samples_out = pixels.iter_mut().map(|pixel| get_sample(pixel));
+
+ // match the type once for the whole line, not on every single sample
+ match self.channel.sample_type {
+ SampleType::F16 => read_and_convert_all_samples_batched(
+ &mut own_bytes_reader, &mut samples_out,
+ Sample::from_f16s
+ ),
+
+ SampleType::F32 => read_and_convert_all_samples_batched(
+ &mut own_bytes_reader, &mut samples_out,
+ Sample::from_f32s
+ ),
+
+ SampleType::U32 => read_and_convert_all_samples_batched(
+ &mut own_bytes_reader, &mut samples_out,
+ Sample::from_u32s
+ ),
+ }
+
+ debug_assert!(samples_out.next().is_none(), "not all samples have been converted");
+ debug_assert!(own_bytes_reader.is_empty(), "bytes left after reading all samples");
+ }
+}
+
+
+/// Does the same as `convert_batch(in_bytes.chunks().map(From::from_bytes))`, but vectorized.
+/// Reads the samples for one line, using the sample type specified in the file,
+/// and then converts those to the desired sample types.
+/// Uses batches to allow vectorization, converting multiple values with one instruction.
+fn read_and_convert_all_samples_batched<'t, From, To>(
+ mut in_bytes: impl Read,
+ out_samples: &mut impl ExactSizeIterator<Item=&'t mut To>,
+ convert_batch: fn(&[From], &mut [To])
+) where From: Data + Default + Copy, To: 't + Default + Copy
+{
+ // this is not a global! why is this warning triggered?
+ #[allow(non_upper_case_globals)]
+ const batch_size: usize = 16;
+
+ let total_sample_count = out_samples.len();
+ let batch_count = total_sample_count / batch_size;
+ let remaining_samples_count = total_sample_count % batch_size;
+
+ let len_error_msg = "sample count was miscalculated";
+ let byte_error_msg = "error when reading from in-memory slice";
+
+ // write samples from a given slice to the output iterator. should be inlined.
+ let output_n_samples = &mut move |samples: &[To]| {
+ for converted_sample in samples {
+ *out_samples.next().expect(len_error_msg) = *converted_sample;
+ }
+ };
+
+ // read samples from the byte source into a given slice. should be inlined.
+ // todo: use #[inline] when available
+ // error[E0658]: attributes on expressions are experimental,
+ // see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+ let read_n_samples = &mut move |samples: &mut [From]| {
+ Data::read_slice(&mut in_bytes, samples).expect(byte_error_msg);
+ };
+
+ // temporary arrays with fixed size, operations should be vectorized within these arrays
+ let mut source_samples_batch: [From; batch_size] = Default::default();
+ let mut desired_samples_batch: [To; batch_size] = Default::default();
+
+ // first convert all whole batches, size statically known to be 16 element arrays
+ for _ in 0 .. batch_count {
+ read_n_samples(&mut source_samples_batch);
+ convert_batch(source_samples_batch.as_slice(), desired_samples_batch.as_mut_slice());
+ output_n_samples(&desired_samples_batch);
+ }
+
+ // then convert a partial remaining batch, size known only at runtime
+ if remaining_samples_count != 0 {
+ let source_samples_batch = &mut source_samples_batch[..remaining_samples_count];
+ let desired_samples_batch = &mut desired_samples_batch[..remaining_samples_count];
+
+ read_n_samples(source_samples_batch);
+ convert_batch(source_samples_batch, desired_samples_batch);
+ output_n_samples(desired_samples_batch);
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn equals_naive_f32(){
+ for total_array_size in [3, 7, 30, 41, 120, 10_423] {
+ let input_f32s = (0..total_array_size).map(|_| rand::random::<f32>()).collect::<Vec<f32>>();
+ let in_f32s_bytes = input_f32s.iter().cloned().flat_map(f32::to_le_bytes).collect::<Vec<u8>>();
+
+ let mut out_f16_samples_batched = vec![
+ f16::from_f32(rand::random::<f32>());
+ total_array_size
+ ];
+
+ read_and_convert_all_samples_batched(
+ &mut in_f32s_bytes.as_slice(),
+ &mut out_f16_samples_batched.iter_mut(),
+ f16::from_f32s
+ );
+
+ let out_f16_samples_naive = input_f32s.iter()
+ .cloned().map(f16::from_f32);
+
+ assert!(out_f16_samples_naive.eq(out_f16_samples_batched));
+ }
+ }
+}
+
+
+impl RecursivePixelReader for NoneMore {
+ type RecursiveChannelDescriptions = NoneMore;
+ fn get_descriptions(&self) -> Self::RecursiveChannelDescriptions { NoneMore }
+
+ type RecursivePixel = NoneMore;
+
+ fn read_pixels<'s, FullPixel>(
+ &self, _: &'s[u8], _: &mut [FullPixel],
+ _: impl Fn(&mut FullPixel) -> &mut NoneMore
+ ){}
+}
+
+impl<Sample, InnerReader: RecursivePixelReader>
+ RecursivePixelReader
+ for Recursive<InnerReader, SampleReader<Sample>>
+ where Sample: FromNativeSample + 'static
+{
+ type RecursiveChannelDescriptions = Recursive<InnerReader::RecursiveChannelDescriptions, ChannelDescription>;
+ fn get_descriptions(&self) -> Self::RecursiveChannelDescriptions { Recursive::new(self.inner.get_descriptions(), self.value.channel.clone()) }
+
+ type RecursivePixel = Recursive<InnerReader::RecursivePixel, Sample>;
+
+ fn read_pixels<'s, FullPixel>(
+ &self, bytes: &'s[u8], pixels: &mut [FullPixel],
+ get_pixel: impl Fn(&mut FullPixel) -> &mut Self::RecursivePixel
+ ) {
+ self.value.read_own_samples(bytes, pixels, |px| &mut get_pixel(px).value);
+ self.inner.read_pixels(bytes, pixels, |px| &mut get_pixel(px).inner);
+ }
+}
+
+impl<Sample, InnerReader: RecursivePixelReader>
+RecursivePixelReader
+for Recursive<InnerReader, OptionalSampleReader<Sample>>
+ where Sample: FromNativeSample + 'static
+{
+ type RecursiveChannelDescriptions = Recursive<InnerReader::RecursiveChannelDescriptions, Option<ChannelDescription>>;
+ fn get_descriptions(&self) -> Self::RecursiveChannelDescriptions { Recursive::new(
+ self.inner.get_descriptions(), self.value.reader.as_ref().map(|reader| reader.channel.clone())
+ ) }
+
+ type RecursivePixel = Recursive<InnerReader::RecursivePixel, Sample>;
+
+ fn read_pixels<'s, FullPixel>(
+ &self, bytes: &'s[u8], pixels: &mut [FullPixel],
+ get_pixel: impl Fn(&mut FullPixel) -> &mut Self::RecursivePixel
+ ) {
+ if let Some(reader) = &self.value.reader {
+ reader.read_own_samples(bytes, pixels, |px| &mut get_pixel(px).value);
+ }
+ else {
+ // if this channel is optional and was not found in the file, fill the default sample
+ for pixel in pixels.iter_mut() {
+ get_pixel(pixel).value = self.value.default_sample;
+ }
+ }
+
+ self.inner.read_pixels(bytes, pixels, |px| &mut get_pixel(px).inner);
+ }
+}
+
+