aboutsummaryrefslogtreecommitdiff
path: root/vendor/exr/src/image/read/image.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/exr/src/image/read/image.rs')
-rw-r--r--vendor/exr/src/image/read/image.rs209
1 files changed, 209 insertions, 0 deletions
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;
+}
+