From 1b6a04ca5504955c571d1c97504fb45ea0befee4 Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Mon, 8 Jan 2024 01:21:28 +0400 Subject: Initial vendor packages Signed-off-by: Valentin Popov --- vendor/exr/src/image/read/any_channels.rs | 128 +++++++ vendor/exr/src/image/read/image.rs | 209 +++++++++++ vendor/exr/src/image/read/layers.rs | 204 +++++++++++ vendor/exr/src/image/read/levels.rs | 219 ++++++++++++ vendor/exr/src/image/read/mod.rs | 207 +++++++++++ vendor/exr/src/image/read/samples.rs | 122 +++++++ vendor/exr/src/image/read/specific_channels.rs | 463 +++++++++++++++++++++++++ 7 files changed, 1552 insertions(+) create mode 100644 vendor/exr/src/image/read/any_channels.rs create mode 100644 vendor/exr/src/image/read/image.rs create mode 100644 vendor/exr/src/image/read/layers.rs create mode 100644 vendor/exr/src/image/read/levels.rs create mode 100644 vendor/exr/src/image/read/mod.rs create mode 100644 vendor/exr/src/image/read/samples.rs create mode 100644 vendor/exr/src/image/read/specific_channels.rs (limited to 'vendor/exr/src/image/read') 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]. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ReadAnyChannels { + + /// 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; +} + +/// 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 { + + /// Stores a separate sample reader per channel in the layer + sample_channels_reader: SmallVec<[AnyChannelReader; 4]>, +} + +/// Processes pixel blocks from a file and accumulates them into a single arbitrary channel. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct AnyChannelReader { + + /// 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, + + /// 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 { + type Reader = AnyChannelsReader; + + fn create_channels_reader(&self, header: &Header) -> Result { + 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 ChannelsReader for AnyChannelsReader { + type Channels = AnyChannels; + + 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 { + on_progress: OnProgress, + read_layers: ReadLayers, + pedantic: bool, + parallel: bool, +} + +impl ReadImage 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(self, on_progress: OnProgress) -> ReadImage + 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(self, path: impl AsRef) -> Result> + 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(self, unbuffered: impl Read + Seek) -> Result> + 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(self, buffered: impl Read + Seek) -> Result> + 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(mut self, chunks_reader: crate::block::reader::Reader) -> Result> + 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 { + image_attributes: ImageAttributes, + layers_reader: L, +} + +impl ImageWithAttributesReader where L: LayersReader { + + /// A new image reader with image attributes. + pub fn new(headers: &[Header], layers_reader: L) -> Result + { + 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 { + 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; + + /// Create a single reader for a single layer + fn create_layers_reader(&'s self, headers: &[Header]) -> Result; + + /// 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 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`]. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ReadAllLayers { + + /// 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 { + + /// 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; + + + /// 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 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 where Self:Sized { ReadAllLayers { read_channels: self } } + + // TODO pub fn all_valid_layers(self) -> ReadAllValidLayers { 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`]. +#[derive(Debug, Clone, PartialEq)] +pub struct AllLayersReader { + layer_readers: SmallVec<[LayerReader; 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`. +#[derive(Debug, Clone, PartialEq)] +pub struct FirstValidLayerReader { + layer_reader: LayerReader, + layer_index: usize, +} + +/// Processes pixel blocks from a file and accumulates them into a single layers. +/// For example, `ChannelsReader` can be +/// `SpecificChannelsReader` or `AnyChannelsReader`. +#[derive(Debug, Clone, PartialEq)] +pub struct LayerReader { + channels_reader: ChannelsReader, + attributes: LayerAttributes, + size: Vec2, + 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 LayerReader { + fn new(header: &Header, channels_reader: C) -> Result { + 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 where C: ReadChannels<'s> { + type Layers = Layers<::Channels>; + type Reader = AllLayersReader; + + fn create_layers_reader(&'s self, headers: &[Header]) -> Result { + let readers: Result<_> = headers.iter() + .map(|header| LayerReader::new(header, self.read_channels.create_channels_reader(header)?)) + .collect(); + + Ok(AllLayersReader { + layer_readers: readers? + }) + } +} + +impl LayersReader for AllLayersReader where C: ChannelsReader { + type Layers = Layers; + + 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 where C: ReadChannels<'s> { + type Layers = Layer<::Channels>; + type Reader = FirstValidLayerReader; + + fn create_layers_reader(&'s self, headers: &[Header]) -> Result { + 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 LayersReader for FirstValidLayerReader where C: ChannelsReader { + type Layers = Layer; + + 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 { + + /// The sample reading specification + pub read_samples: DeepOrFlatSamples +} + + +// FIXME rgba levels??? + +// Read the largest level, directly, without intermediate structs +impl ReadLargestLevel { + + /// Read all arbitrary channels in each layer. + pub fn all_channels(self) -> ReadAnyChannels { 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( + self, create_pixels: Create, set_pixel: Set + ) -> CollectPixels< + ReadOptionalChannel, G>, B>, A>, + (R, G, B, A), Pixels, Create, Set + > + where + R: FromNativeSample, G: FromNativeSample, B: FromNativeSample, A: FromNativeSample, + Create: Fn(Vec2, &RgbaChannels) -> Pixels, + Set: Fn(&mut Pixels, Vec2, (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( + self, create_pixels: Create, set_pixel: Set + ) -> CollectPixels< + ReadRequiredChannel, G>, B>, + (R, G, B), Pixels, Create, Set + > + where + R: FromNativeSample, G: FromNativeSample, B: FromNativeSample, + Create: Fn(Vec2, &RgbChannels) -> Pixels, + Set: Fn(&mut Pixels, Vec2, (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 { + + /// The sample reading specification + pub read_samples: DeepOrFlatSamples +} + +impl ReadAllLevels { + + /// Read all arbitrary channels in each layer. + pub fn all_channels(self) -> ReadAnyChannels { ReadAnyChannels { read_samples: self } } + + // TODO specific channels for multiple resolution levels + +} + +/*pub struct ReadLevels { + 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 { + levels: Levels, +} + +/// 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, resolution: Vec2) -> Result; +} + + +impl ReadSamples for ReadAllLevels { + type Reader = AllLevelsReader; + + fn create_sample_reader(&self, header: &Header, channel: &ChannelDescription) -> Result { + 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> = 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> = 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 SamplesReader for AllLevelsReader { + type Samples = Levels; + + 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) -> Result { + 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) -> Result { + 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) -> Result>>> { + 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( + path: impl AsRef, create: Create, set_pixel: Set +) + -> Result> + where + R: FromNativeSample, G: FromNativeSample, B: FromNativeSample, A: FromNativeSample, + Create: Fn(Vec2, &RgbaChannels) -> Pixels, // TODO type alias? CreateRgbaPixels, + Set: Fn(&mut Pixels, Vec2, (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( + path: impl AsRef, create: Create, set_pixel: Set +) + -> Result> + where + R: FromNativeSample, G: FromNativeSample, B: FromNativeSample, A: FromNativeSample, + Create: Fn(Vec2, &RgbaChannels) -> Pixels, // TODO type alias? CreateRgbaPixels, + Set: Fn(&mut Pixels, Vec2, (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 (self, storage: S) -> FlatSamplesWith { } + + // 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 (self, storage: S) -> { } + + /// Specify to read only the highest resolution level, skipping all smaller variations. + pub fn largest_resolution_level(self) -> ReadLargestLevel { ReadLargestLevel { read_samples: self } } + + /// Specify to read all contained resolution levels from the image, if any. + pub fn all_resolution_levels(self) -> ReadAllLevels { ReadAllLevels { read_samples: self } } + + // TODO pub fn specific_resolution_level])->usize >(self, select_level: F) -> ReadLevelBy { ReadAllLevels { read_samples: self } } +} + + +/*pub struct AnySamplesReader { TODO + resolution: Vec2, + 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, + resolution: Vec2, + 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.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, resolution: Vec2) -> Result { + 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; + + /// 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(self, channel_name: impl Into) -> ReadRequiredChannel { + 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(self, channel_name: impl Into, default_sample: Sample) + -> ReadOptionalChannel + { + 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( + self, create_pixels: CreatePixels, set_pixel: SetPixel + ) -> CollectPixels + where + ::RecursivePixel: IntoTuple, + ::RecursiveChannelDescriptions: IntoNonRecursive, + CreatePixels: Fn( + Vec2, + &<::RecursiveChannelDescriptions as IntoNonRecursive>::NonRecursive + ) -> PixelStorage, + SetPixel: Fn(&mut PixelStorage, Vec2, 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 { + 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 { + previous_channels: ReadChannels, + channel_name: Text, + px: PhantomData, +} + +/// Specifies how to collect all the specified channels into a number of individual pixels. +#[derive(Copy, Clone, Debug)] +pub struct CollectPixels { + read_channels: ReadChannels, + create_pixels: CreatePixels, + set_pixel: SetPixel, + px: PhantomData<(Pixel, PixelStorage)>, +} + +impl CheckDuplicates for ReadRequiredChannel { + fn already_contains(&self, name: &Text) -> bool { + &self.channel_name == name || self.previous_channels.already_contains(name) + } +} + +impl CheckDuplicates for ReadOptionalChannel { + 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 + where + InnerChannels: ReadSpecificChannel, + ::RecursivePixel: IntoTuple, + ::RecursiveChannelDescriptions: IntoNonRecursive, + CreatePixels: Fn(Vec2, &<::RecursiveChannelDescriptions as IntoNonRecursive>::NonRecursive) -> PixelStorage, + SetPixel: Fn(&mut PixelStorage, Vec2, Pixel), +{ + type Reader = SpecificChannelsReader< + PixelStorage, &'s SetPixel, + InnerChannels::RecursivePixelReader, + Pixel, + >; + + fn create_channels_reader(&'s self, header: &Header) -> Result { + 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 { + set_pixel: SetPixel, + pixel_storage: PixelStorage, + pixel_reader: PixelReader, + px: PhantomData +} + +impl +ChannelsReader for SpecificChannelsReader + where PxReader: RecursivePixelReader, + PxReader::RecursivePixel: IntoTuple, + PxReader::RecursiveChannelDescriptions: IntoNonRecursive, + SetPixel: Fn(&mut PixelStorage, Vec2, Pixel), +{ + type Channels = SpecificChannels::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 { Ok(NoneMore) } +} + +impl ReadSpecificChannel for ReadOptionalChannel + where ReadChannels: ReadSpecificChannel, DefaultSample: FromNativeSample + 'static, +{ + type RecursivePixelReader = Recursive>; + + fn create_recursive_reader(&self, channels: &ChannelList) -> Result { + 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 ReadSpecificChannel for ReadRequiredChannel + where ReadChannels: ReadSpecificChannel, Sample: FromNativeSample + 'static +{ + type RecursivePixelReader = Recursive>; + + fn create_recursive_reader(&self, channels: &ChannelList) -> Result { + 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 { + + /// to be multiplied with line width! + channel_byte_offset: usize, + + channel: ChannelDescription, + px: PhantomData +} + +/// 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 { + reader: Option>, + default_sample: DefaultSample, +} + +impl SampleReader { + 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, + 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 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::()).collect::>(); + let in_f32s_bytes = input_f32s.iter().cloned().flat_map(f32::to_le_bytes).collect::>(); + + let mut out_f16_samples_batched = vec![ + f16::from_f32(rand::random::()); + 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 + RecursivePixelReader + for Recursive> + where Sample: FromNativeSample + 'static +{ + type RecursiveChannelDescriptions = Recursive; + fn get_descriptions(&self) -> Self::RecursiveChannelDescriptions { Recursive::new(self.inner.get_descriptions(), self.value.channel.clone()) } + + type RecursivePixel = Recursive; + + 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 +RecursivePixelReader +for Recursive> + where Sample: FromNativeSample + 'static +{ + type RecursiveChannelDescriptions = Recursive>; + 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; + + 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); + } +} + + -- cgit v1.2.3