1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 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;
}