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;
}