exr/image/read/
image.rs

1//! The last wrapper of image readers, finally containing the [`from_file(path)`] method.
2//! This completes the builder and reads a complete image.
3
4use crate::image::*;
5use crate::meta::header::{Header, ImageAttributes};
6use crate::error::{Result, UnitResult};
7use crate::block::{UncompressedBlock, BlockIndex};
8use crate::block::chunk::TileCoordinates;
9use std::path::Path;
10use std::io::{Read, BufReader};
11use std::io::Seek;
12use crate::meta::MetaData;
13use crate::block::reader::ChunksReader;
14
15/// Specify whether to read the image in parallel,
16/// whether to use pedantic error handling,
17/// and a callback for the reading progress.
18#[derive(Debug, Clone)]
19pub struct ReadImage<OnProgress, ReadLayers> {
20    on_progress: OnProgress,
21    read_layers: ReadLayers,
22    pedantic: bool,
23    parallel: bool,
24}
25
26impl<F, L> ReadImage<F, L> where F: FnMut(f64)
27{
28    /// Uses relaxed error handling and parallel decompression.
29    pub fn new(read_layers: L, on_progress: F) -> Self {
30        Self {
31            on_progress, read_layers,
32            pedantic: false,
33            #[cfg(not(feature = "rayon"))]
34            parallel: false,
35            #[cfg(feature = "rayon")]
36            parallel: true,
37        }
38    }
39
40    /// Specify that any missing or unusual information should result in an error.
41    /// Otherwise, `exrs` will try to compute or ignore missing information.
42    ///
43    /// If pedantic is true, then an error will be returned as soon as anything is missing in the file,
44    /// or two values in the image contradict each other. If pedantic is false,
45    /// then only fatal errors will be thrown. By default, reading an image is not pedantic,
46    /// which means that slightly invalid files might still be readable.
47    /// For example, if some attribute is missing but can be recomputed, this flag decides whether an error is thrown.
48    /// Or if the pedantic flag is true and there are still bytes left after the decompression algorithm finished,
49    /// an error is thrown, because this should not happen and something might be wrong with the file.
50    /// Or if your application is a target of attacks, or if you want to emulate the original C++ library,
51    /// you might want to switch to pedantic reading.
52    pub fn pedantic(self) -> Self { Self { pedantic: true, ..self } }
53
54    /// Specify that multiple pixel blocks should never be decompressed using multiple threads at once.
55    /// This might be slower but uses less memory and less synchronization.
56    pub fn non_parallel(self) -> Self { Self { parallel: false, ..self } }
57
58    /// Specify a function to be called regularly throughout the loading process.
59    /// Replaces all previously specified progress functions in this reader.
60    pub fn on_progress<OnProgress>(self, on_progress: OnProgress) -> ReadImage<OnProgress, L>
61        where OnProgress: FnMut(f64)
62    {
63        ReadImage {
64            on_progress,
65            read_layers: self.read_layers,
66            pedantic: self.pedantic,
67            parallel: self.parallel
68        }
69    }
70
71
72    /// Read the exr image from a file.
73    /// Use [`ReadImage::read_from_unbuffered`] instead, if you do not have a file.
74    #[inline]
75    #[must_use]
76    pub fn from_file<Layers>(self, path: impl AsRef<Path>) -> Result<Image<Layers>>
77        where for<'s> L: ReadLayers<'s, Layers = Layers>
78    {
79        self.from_unbuffered(std::fs::File::open(path)?)
80    }
81
82    /// Buffer the reader and then read the exr image from it.
83    /// Use [`ReadImage::read_from_buffered`] instead, if your reader is an in-memory reader.
84    /// Use [`ReadImage::read_from_file`] instead, if you have a file path.
85    #[inline]
86    #[must_use]
87    pub fn from_unbuffered<Layers>(self, unbuffered: impl Read + Seek) -> Result<Image<Layers>>
88        where for<'s> L: ReadLayers<'s, Layers = Layers>
89    {
90        self.from_buffered(BufReader::new(unbuffered))
91    }
92
93    /// Read the exr image from a buffered reader.
94    /// Use [`ReadImage::read_from_file`] instead, if you have a file path.
95    /// Use [`ReadImage::read_from_unbuffered`] instead, if this is not an in-memory reader.
96    // TODO Use Parallel<> Wrapper to only require sendable byte source where parallel decompression is required
97    #[must_use]
98    pub fn from_buffered<Layers>(self, buffered: impl Read + Seek) -> Result<Image<Layers>>
99        where for<'s> L: ReadLayers<'s, Layers = Layers>
100    {
101        let chunks = crate::block::read(buffered, self.pedantic)?;
102        self.from_chunks(chunks)
103    }
104
105    /// Read the exr image from an initialized chunks reader
106    /// that has already extracted the meta data from the file.
107    /// Use [`ReadImage::read_from_file`] instead, if you have a file path.
108    /// Use [`ReadImage::read_from_buffered`] instead, if this is an in-memory reader.
109    // TODO Use Parallel<> Wrapper to only require sendable byte source where parallel decompression is required
110    #[must_use]
111    pub fn from_chunks<Layers>(mut self, chunks_reader: crate::block::reader::Reader<impl Read + Seek>) -> Result<Image<Layers>>
112        where for<'s> L: ReadLayers<'s, Layers = Layers>
113    {
114        let Self { pedantic, parallel, ref mut on_progress, ref mut read_layers } = self;
115
116        let layers_reader = read_layers.create_layers_reader(chunks_reader.headers())?;
117        let mut image_collector = ImageWithAttributesReader::new(chunks_reader.headers(), layers_reader)?;
118
119        let block_reader = chunks_reader
120            .filter_chunks(pedantic, |meta, tile, block| {
121                image_collector.filter_block(meta, tile, block)
122            })?
123            .on_progress(on_progress);
124
125        // TODO propagate send requirement further upwards
126        if parallel {
127            #[cfg(not(feature = "rayon"))]
128            return Err(crate::error::Error::unsupported("parallel decompression requires the rayon feature"));
129
130            #[cfg(feature = "rayon")]
131            block_reader.decompress_parallel(pedantic, |meta_data, block|{
132                image_collector.read_block(&meta_data.headers, block)
133            })?;
134        }
135        else {
136            block_reader.decompress_sequential(pedantic, |meta_data, block|{
137                image_collector.read_block(&meta_data.headers, block)
138            })?;
139        }
140
141        Ok(image_collector.into_image())
142    }
143}
144
145/// Processes blocks from a file and collects them into a complete `Image`.
146#[derive(Debug, Clone, PartialEq)]
147pub struct ImageWithAttributesReader<L> {
148    image_attributes: ImageAttributes,
149    layers_reader: L,
150}
151
152impl<L> ImageWithAttributesReader<L> where L: LayersReader {
153
154    /// A new image reader with image attributes.
155    pub fn new(headers: &[Header], layers_reader: L) -> Result<Self>
156    {
157        Ok(ImageWithAttributesReader {
158            image_attributes: headers.first().as_ref().expect("invalid headers").shared_attributes.clone(),
159            layers_reader,
160        })
161    }
162
163    /// Specify whether a single block of pixels should be loaded from the file
164    fn filter_block(&self, meta: &MetaData, tile: TileCoordinates, block: BlockIndex) -> bool {
165        self.layers_reader.filter_block(meta, tile, block)
166    }
167
168    /// Load a single pixel block, which has not been filtered, into the reader, accumulating the image
169    fn read_block(&mut self, headers: &[Header], block: UncompressedBlock) -> UnitResult {
170        self.layers_reader.read_block(headers, block)
171    }
172
173    /// Deliver the complete accumulated image
174    fn into_image(self) -> Image<L::Layers> {
175        Image {
176            attributes: self.image_attributes,
177            layer_data: self.layers_reader.into_layers()
178        }
179    }
180}
181
182
183/// A template that creates a `LayerReader` for each layer in the file.
184pub trait ReadLayers<'s> {
185
186    /// The type of the resulting Layers
187    type Layers;
188
189    /// The type of the temporary layer reader
190    type Reader: LayersReader<Layers = Self::Layers>;
191
192    /// Create a single reader for a single layer
193    fn create_layers_reader(&'s self, headers: &[Header]) -> Result<Self::Reader>;
194
195    /// Specify that all attributes should be read from an image.
196    /// Use `from_file(path)` on the return value of this method to actually decode an image.
197    fn all_attributes(self) -> ReadImage<fn(f64), Self> where Self: Sized {
198        ReadImage::new(self, ignore_progress)
199    }
200}
201
202/// Processes pixel blocks from a file and accumulates them into a single image layer.
203pub trait LayersReader {
204
205    /// The type of resulting layers
206    type Layers;
207
208    /// Specify whether a single block of pixels should be loaded from the file
209    fn filter_block(&self, meta: &MetaData, tile: TileCoordinates, block: BlockIndex) -> bool;
210
211    /// Load a single pixel block, which has not been filtered, into the reader, accumulating the layer
212    fn read_block(&mut self, headers: &[Header], block: UncompressedBlock) -> UnitResult;
213
214    /// Deliver the final accumulated layers for the image
215    fn into_layers(self) -> Self::Layers;
216}
217