| 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 |
|
| 4 | use crate::image::*;
|
| 5 | use crate::meta::header::{Header, ImageAttributes};
|
| 6 | use crate::error::{Result, UnitResult};
|
| 7 | use crate::block::{UncompressedBlock, BlockIndex};
|
| 8 | use crate::block::chunk::TileCoordinates;
|
| 9 | use std::path::Path;
|
| 10 | use std::io::{Read, BufReader};
|
| 11 | use std::io::Seek;
|
| 12 | use crate::meta::MetaData;
|
| 13 | use 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)]
|
| 19 | pub struct ReadImage<OnProgress, ReadLayers> {
|
| 20 | on_progress: OnProgress,
|
| 21 | read_layers: ReadLayers,
|
| 22 | pedantic: bool,
|
| 23 | parallel: bool,
|
| 24 | }
|
| 25 |
|
| 26 | impl<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)]
|
| 139 | pub struct ImageWithAttributesReader<L> {
|
| 140 | image_attributes: ImageAttributes,
|
| 141 | layers_reader: L,
|
| 142 | }
|
| 143 |
|
| 144 | impl<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.
|
| 176 | pub 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.
|
| 195 | pub 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 | |