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