/rust/registry/src/index.crates.io-6f17d22bba15001f/exr-1.73.0/src/meta/mod.rs
Line | Count | Source (jump to first uncovered line) |
1 | | |
2 | | //! Describes all meta data possible in an exr file. |
3 | | //! Contains functionality to read and write meta data from bytes. |
4 | | //! Browse the `exr::image` module to get started with the high-level interface. |
5 | | |
6 | | pub mod attribute; |
7 | | pub mod header; |
8 | | |
9 | | |
10 | | use crate::io::*; |
11 | | use ::smallvec::SmallVec; |
12 | | use self::attribute::*; |
13 | | use crate::block::chunk::{TileCoordinates, CompressedBlock}; |
14 | | use crate::error::*; |
15 | | use std::fs::File; |
16 | | use std::io::{BufReader}; |
17 | | use crate::math::*; |
18 | | use std::collections::{HashSet}; |
19 | | use std::convert::TryFrom; |
20 | | use crate::meta::header::{Header}; |
21 | | use crate::block::{BlockIndex, UncompressedBlock}; |
22 | | |
23 | | |
24 | | // TODO rename MetaData to ImageInfo? |
25 | | |
26 | | /// Contains the complete meta data of an exr image. |
27 | | /// Defines how the image is split up in the file, |
28 | | /// the number and type of images and channels, |
29 | | /// and various other attributes. |
30 | | /// The usage of custom attributes is encouraged. |
31 | | #[derive(Debug, Clone, PartialEq)] |
32 | | pub struct MetaData { |
33 | | |
34 | | /// Some flags summarizing the features that must be supported to decode the file. |
35 | | pub requirements: Requirements, |
36 | | |
37 | | /// One header to describe each layer in this file. |
38 | | // TODO rename to layer descriptions? |
39 | | pub headers: Headers, |
40 | | } |
41 | | |
42 | | |
43 | | /// List of `Header`s. |
44 | | pub type Headers = SmallVec<[Header; 3]>; |
45 | | |
46 | | /// List of `OffsetTable`s. |
47 | | pub type OffsetTables = SmallVec<[OffsetTable; 3]>; |
48 | | |
49 | | |
50 | | /// The offset table is an ordered list of indices referencing pixel data in the exr file. |
51 | | /// For each pixel tile in the image, an index exists, which points to the byte-location |
52 | | /// of the corresponding pixel data in the file. That index can be used to load specific |
53 | | /// portions of an image without processing all bytes in a file. For each header, |
54 | | /// an offset table exists with its indices ordered by `LineOrder::Increasing`. |
55 | | // If the multipart bit is unset and the chunkCount attribute is not present, |
56 | | // the number of entries in the chunk table is computed using the |
57 | | // dataWindow, tileDesc, and compression attribute. |
58 | | // |
59 | | // If the multipart bit is set, the header must contain a |
60 | | // chunkCount attribute, that contains the length of the offset table. |
61 | | pub type OffsetTable = Vec<u64>; |
62 | | |
63 | | |
64 | | /// A summary of requirements that must be met to read this exr file. |
65 | | /// Used to determine whether this file can be read by a given reader. |
66 | | /// It includes the OpenEXR version number. This library aims to support version `2.0`. |
67 | | #[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)] |
68 | | pub struct Requirements { |
69 | | |
70 | | /// This library supports reading version 1 and 2, and writing version 2. |
71 | | // TODO write version 1 for simple images |
72 | | pub file_format_version: u8, |
73 | | |
74 | | /// If true, this image has tiled blocks and contains only a single layer. |
75 | | /// If false and not deep and not multilayer, this image is a single layer image with scan line blocks. |
76 | | pub is_single_layer_and_tiled: bool, |
77 | | |
78 | | // in c or bad c++ this might have been relevant (omg is he allowed to say that) |
79 | | /// Whether this file has strings with a length greater than 31. |
80 | | /// Strings can never be longer than 255. |
81 | | pub has_long_names: bool, |
82 | | |
83 | | /// This image contains at least one layer with deep data. |
84 | | pub has_deep_data: bool, |
85 | | |
86 | | /// Whether this file contains multiple layers. |
87 | | pub has_multiple_layers: bool, |
88 | | } |
89 | | |
90 | | |
91 | | /// Locates a rectangular section of pixels in an image. |
92 | | #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] |
93 | | pub struct TileIndices { |
94 | | |
95 | | /// Index of the tile. |
96 | | pub location: TileCoordinates, |
97 | | |
98 | | /// Pixel size of the tile. |
99 | | pub size: Vec2<usize>, |
100 | | } |
101 | | |
102 | | /// How the image pixels are split up into separate blocks. |
103 | | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] |
104 | | pub enum BlockDescription { |
105 | | |
106 | | /// The image is divided into scan line blocks. |
107 | | /// The number of scan lines in a block depends on the compression method. |
108 | | ScanLines, |
109 | | |
110 | | /// The image is divided into tile blocks. |
111 | | /// Also specifies the size of each tile in the image |
112 | | /// and whether this image contains multiple resolution levels. |
113 | | Tiles(TileDescription) |
114 | | } |
115 | | |
116 | | |
117 | | /*impl TileIndices { |
118 | | pub fn cmp(&self, other: &Self) -> Ordering { |
119 | | match self.location.level_index.1.cmp(&other.location.level_index.1) { |
120 | | Ordering::Equal => { |
121 | | match self.location.level_index.0.cmp(&other.location.level_index.0) { |
122 | | Ordering::Equal => { |
123 | | match self.location.tile_index.1.cmp(&other.location.tile_index.1) { |
124 | | Ordering::Equal => { |
125 | | self.location.tile_index.0.cmp(&other.location.tile_index.0) |
126 | | }, |
127 | | |
128 | | other => other, |
129 | | } |
130 | | }, |
131 | | |
132 | | other => other |
133 | | } |
134 | | }, |
135 | | |
136 | | other => other |
137 | | } |
138 | | } |
139 | | }*/ |
140 | | |
141 | | impl BlockDescription { |
142 | | |
143 | | /// Whether this image is tiled. If false, this image is divided into scan line blocks. |
144 | 1.71k | pub fn has_tiles(&self) -> bool { |
145 | 1.71k | match self { |
146 | 1.41k | BlockDescription::Tiles { .. } => true, |
147 | 298 | _ => false |
148 | | } |
149 | 1.71k | } |
150 | | } |
151 | | |
152 | | |
153 | | |
154 | | |
155 | | |
156 | | /// The first four bytes of each exr file. |
157 | | /// Used to abort reading non-exr files. |
158 | | pub mod magic_number { |
159 | | use super::*; |
160 | | |
161 | | /// The first four bytes of each exr file. |
162 | | pub const BYTES: [u8; 4] = [0x76, 0x2f, 0x31, 0x01]; |
163 | | |
164 | | /// Without validation, write this instance to the byte stream. |
165 | 14 | pub fn write(write: &mut impl Write) -> Result<()> { |
166 | 14 | u8::write_slice(write, &self::BYTES) |
167 | 14 | } Unexecuted instantiation: exr::meta::magic_number::write::<_> Unexecuted instantiation: exr::meta::magic_number::write::<exr::io::Tracking<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>> exr::meta::magic_number::write::<exr::io::Tracking<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>> Line | Count | Source | 165 | 14 | pub fn write(write: &mut impl Write) -> Result<()> { | 166 | 14 | u8::write_slice(write, &self::BYTES) | 167 | 14 | } |
|
168 | | |
169 | | /// Consumes four bytes from the reader and returns whether the file may be an exr file. |
170 | | // TODO check if exr before allocating BufRead |
171 | 4.36k | pub fn is_exr(read: &mut impl Read) -> Result<bool> { |
172 | 4.36k | let mut magic_num = [0; 4]; |
173 | 4.36k | u8::read_slice(read, &mut magic_num)?; |
174 | 4.36k | Ok(magic_num == self::BYTES) |
175 | 4.36k | } Unexecuted instantiation: exr::meta::magic_number::is_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::is_exr::<_> Unexecuted instantiation: exr::meta::magic_number::is_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::is_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::is_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::is_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::is_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::is_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::is_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::is_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::is_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> exr::meta::magic_number::is_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Line | Count | Source | 171 | 4.31k | pub fn is_exr(read: &mut impl Read) -> Result<bool> { | 172 | 4.31k | let mut magic_num = [0; 4]; | 173 | 4.31k | u8::read_slice(read, &mut magic_num)?; | 174 | 4.31k | Ok(magic_num == self::BYTES) | 175 | 4.31k | } |
exr::meta::magic_number::is_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>> Line | Count | Source | 171 | 14 | pub fn is_exr(read: &mut impl Read) -> Result<bool> { | 172 | 14 | let mut magic_num = [0; 4]; | 173 | 14 | u8::read_slice(read, &mut magic_num)?; | 174 | 14 | Ok(magic_num == self::BYTES) | 175 | 14 | } |
exr::meta::magic_number::is_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Line | Count | Source | 171 | 29 | pub fn is_exr(read: &mut impl Read) -> Result<bool> { | 172 | 29 | let mut magic_num = [0; 4]; | 173 | 29 | u8::read_slice(read, &mut magic_num)?; | 174 | 29 | Ok(magic_num == self::BYTES) | 175 | 29 | } |
|
176 | | |
177 | | /// Validate this image. If it is an exr file, return `Ok(())`. |
178 | 4.36k | pub fn validate_exr(read: &mut impl Read) -> UnitResult { |
179 | 4.36k | if self::is_exr(read)? { |
180 | 4.36k | Ok(()) |
181 | | |
182 | | } else { |
183 | 0 | Err(Error::invalid("file identifier missing")) |
184 | | } |
185 | 4.36k | } Unexecuted instantiation: exr::meta::magic_number::validate_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::validate_exr::<_> Unexecuted instantiation: exr::meta::magic_number::validate_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::validate_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::validate_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::validate_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::validate_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::validate_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::validate_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::validate_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: exr::meta::magic_number::validate_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> exr::meta::magic_number::validate_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Line | Count | Source | 178 | 4.31k | pub fn validate_exr(read: &mut impl Read) -> UnitResult { | 179 | 4.31k | if self::is_exr(read)? { | 180 | 4.31k | Ok(()) | 181 | | | 182 | | } else { | 183 | 0 | Err(Error::invalid("file identifier missing")) | 184 | | } | 185 | 4.31k | } |
exr::meta::magic_number::validate_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>> Line | Count | Source | 178 | 14 | pub fn validate_exr(read: &mut impl Read) -> UnitResult { | 179 | 14 | if self::is_exr(read)? { | 180 | 14 | Ok(()) | 181 | | | 182 | | } else { | 183 | 0 | Err(Error::invalid("file identifier missing")) | 184 | | } | 185 | 14 | } |
exr::meta::magic_number::validate_exr::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Line | Count | Source | 178 | 29 | pub fn validate_exr(read: &mut impl Read) -> UnitResult { | 179 | 29 | if self::is_exr(read)? { | 180 | 29 | Ok(()) | 181 | | | 182 | | } else { | 183 | 0 | Err(Error::invalid("file identifier missing")) | 184 | | } | 185 | 29 | } |
|
186 | | } |
187 | | |
188 | | /// A `0_u8` at the end of a sequence. |
189 | | pub mod sequence_end { |
190 | | use super::*; |
191 | | |
192 | | /// Number of bytes this would consume in an exr file. |
193 | 70 | pub fn byte_size() -> usize { |
194 | 70 | 1 |
195 | 70 | } |
196 | | |
197 | | /// Without validation, write this instance to the byte stream. |
198 | 392 | pub fn write<W: Write>(write: &mut W) -> UnitResult { |
199 | 392 | 0_u8.write(write) |
200 | 392 | } Unexecuted instantiation: exr::meta::sequence_end::write::<_> Unexecuted instantiation: exr::meta::sequence_end::write::<exr::io::Tracking<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>> exr::meta::sequence_end::write::<exr::io::Tracking<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>> Line | Count | Source | 198 | 392 | pub fn write<W: Write>(write: &mut W) -> UnitResult { | 199 | 392 | 0_u8.write(write) | 200 | 392 | } |
|
201 | | |
202 | | /// Peeks the next byte. If it is zero, consumes the byte and returns true. |
203 | 637k | pub fn has_come(read: &mut PeekRead<impl Read>) -> Result<bool> { |
204 | 637k | Ok(read.skip_if_eq(0)?) |
205 | 637k | } Unexecuted instantiation: exr::meta::sequence_end::has_come::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: exr::meta::sequence_end::has_come::<&[u8]> Unexecuted instantiation: exr::meta::sequence_end::has_come::<_> Unexecuted instantiation: exr::meta::sequence_end::has_come::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: exr::meta::sequence_end::has_come::<&[u8]> Unexecuted instantiation: exr::meta::sequence_end::has_come::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: exr::meta::sequence_end::has_come::<&[u8]> Unexecuted instantiation: exr::meta::sequence_end::has_come::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: exr::meta::sequence_end::has_come::<&[u8]> Unexecuted instantiation: exr::meta::sequence_end::has_come::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: exr::meta::sequence_end::has_come::<&[u8]> Unexecuted instantiation: exr::meta::sequence_end::has_come::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: exr::meta::sequence_end::has_come::<&[u8]> Unexecuted instantiation: exr::meta::sequence_end::has_come::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: exr::meta::sequence_end::has_come::<&[u8]> Unexecuted instantiation: exr::meta::sequence_end::has_come::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: exr::meta::sequence_end::has_come::<&[u8]> Unexecuted instantiation: exr::meta::sequence_end::has_come::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: exr::meta::sequence_end::has_come::<&[u8]> Unexecuted instantiation: exr::meta::sequence_end::has_come::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: exr::meta::sequence_end::has_come::<&[u8]> exr::meta::sequence_end::has_come::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Line | Count | Source | 203 | 579k | pub fn has_come(read: &mut PeekRead<impl Read>) -> Result<bool> { | 204 | 579k | Ok(read.skip_if_eq(0)?) | 205 | 579k | } |
exr::meta::sequence_end::has_come::<&[u8]> Line | Count | Source | 203 | 18.8k | pub fn has_come(read: &mut PeekRead<impl Read>) -> Result<bool> { | 204 | 18.8k | Ok(read.skip_if_eq(0)?) | 205 | 18.8k | } |
exr::meta::sequence_end::has_come::<exr::io::Tracking<std::io::cursor::Cursor<alloc::vec::Vec<u8>>>> Line | Count | Source | 203 | 168 | pub fn has_come(read: &mut PeekRead<impl Read>) -> Result<bool> { | 204 | 168 | Ok(read.skip_if_eq(0)?) | 205 | 168 | } |
exr::meta::sequence_end::has_come::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Line | Count | Source | 203 | 35.9k | pub fn has_come(read: &mut PeekRead<impl Read>) -> Result<bool> { | 204 | 35.9k | Ok(read.skip_if_eq(0)?) | 205 | 35.9k | } |
exr::meta::sequence_end::has_come::<&[u8]> Line | Count | Source | 203 | 3.64k | pub fn has_come(read: &mut PeekRead<impl Read>) -> Result<bool> { | 204 | 3.64k | Ok(read.skip_if_eq(0)?) | 205 | 3.64k | } |
|
206 | | } |
207 | | |
208 | 11.5k | fn missing_attribute(name: &str) -> Error { |
209 | 11.5k | Error::invalid(format!("missing or invalid {} attribute", name)) |
210 | 11.5k | } |
211 | | |
212 | | |
213 | | /// Compute the number of tiles required to contain all values. |
214 | 592k | pub fn compute_block_count(full_res: usize, tile_size: usize) -> usize { |
215 | 592k | // round up, because if the image is not evenly divisible by the tiles, |
216 | 592k | // we add another tile at the end (which is only partially used) |
217 | 592k | RoundingMode::Up.divide(full_res, tile_size) |
218 | 592k | } |
219 | | |
220 | | /// Compute the start position and size of a block inside a dimension. |
221 | | #[inline] |
222 | 242k | pub fn calculate_block_position_and_size(total_size: usize, block_size: usize, block_index: usize) -> Result<(usize, usize)> { |
223 | 242k | let block_position = block_size * block_index; |
224 | 242k | |
225 | 242k | Ok(( |
226 | 242k | block_position, |
227 | 242k | calculate_block_size(total_size, block_size, block_position)? |
228 | | )) |
229 | 242k | } |
230 | | |
231 | | /// Calculate the size of a single block. If this is the last block, |
232 | | /// this only returns the required size, which is always smaller than the default block size. |
233 | | // TODO use this method everywhere instead of convoluted formulas |
234 | | #[inline] |
235 | 2.22M | pub fn calculate_block_size(total_size: usize, block_size: usize, block_position: usize) -> Result<usize> { |
236 | 2.22M | if block_position >= total_size { |
237 | 906 | return Err(Error::invalid("block index")) |
238 | 2.22M | } |
239 | 2.22M | |
240 | 2.22M | if block_position + block_size <= total_size { |
241 | 1.57M | Ok(block_size) |
242 | | } |
243 | | else { |
244 | 657k | Ok(total_size - block_position) |
245 | | } |
246 | 2.22M | } |
247 | | |
248 | | |
249 | | /// Calculate number of mip levels in a given resolution. |
250 | | // TODO this should be cached? log2 may be very expensive |
251 | 566 | pub fn compute_level_count(round: RoundingMode, full_res: usize) -> usize { |
252 | 566 | usize::try_from(round.log2(u32::try_from(full_res).unwrap())).unwrap() + 1 |
253 | 566 | } |
254 | | |
255 | | /// Calculate the size of a single mip level by index. |
256 | | // TODO this should be cached? log2 may be very expensive |
257 | 927k | pub fn compute_level_size(round: RoundingMode, full_res: usize, level_index: usize) -> usize { |
258 | 927k | assert!(level_index < std::mem::size_of::<usize>() * 8, "largest level size exceeds maximum integer value"); |
259 | 927k | round.divide(full_res, 1 << level_index).max(1) |
260 | 927k | } |
261 | | |
262 | | /// Iterates over all rip map level resolutions of a given size, including the indices of each level. |
263 | | /// The order of iteration conforms to `LineOrder::Increasing`. |
264 | | // TODO cache these? |
265 | | // TODO compute these directly instead of summing up an iterator? |
266 | 214 | pub fn rip_map_levels(round: RoundingMode, max_resolution: Vec2<usize>) -> impl Iterator<Item=(Vec2<usize>, Vec2<usize>)> { |
267 | 44.3k | rip_map_indices(round, max_resolution).map(move |level_indices|{ |
268 | 44.3k | // TODO progressively divide instead?? |
269 | 44.3k | let width = compute_level_size(round, max_resolution.width(), level_indices.x()); |
270 | 44.3k | let height = compute_level_size(round, max_resolution.height(), level_indices.y()); |
271 | 44.3k | (level_indices, Vec2(width, height)) |
272 | 44.3k | }) |
273 | 214 | } |
274 | | |
275 | | /// Iterates over all mip map level resolutions of a given size, including the indices of each level. |
276 | | /// The order of iteration conforms to `LineOrder::Increasing`. |
277 | | // TODO cache all these level values when computing table offset size?? |
278 | | // TODO compute these directly instead of summing up an iterator? |
279 | 138 | pub fn mip_map_levels(round: RoundingMode, max_resolution: Vec2<usize>) -> impl Iterator<Item=(usize, Vec2<usize>)> { |
280 | 138 | mip_map_indices(round, max_resolution) |
281 | 2.99k | .map(move |level_index|{ |
282 | 2.99k | // TODO progressively divide instead?? |
283 | 2.99k | let width = compute_level_size(round, max_resolution.width(), level_index); |
284 | 2.99k | let height = compute_level_size(round, max_resolution.height(), level_index); |
285 | 2.99k | (level_index, Vec2(width, height)) |
286 | 2.99k | }) |
287 | 138 | } |
288 | | |
289 | | /// Iterates over all rip map level indices of a given size. |
290 | | /// The order of iteration conforms to `LineOrder::Increasing`. |
291 | 214 | pub fn rip_map_indices(round: RoundingMode, max_resolution: Vec2<usize>) -> impl Iterator<Item=Vec2<usize>> { |
292 | 214 | let (width, height) = ( |
293 | 214 | compute_level_count(round, max_resolution.width()), |
294 | 214 | compute_level_count(round, max_resolution.height()) |
295 | 214 | ); |
296 | 214 | |
297 | 1.60k | (0..height).flat_map(move |y_level|{ |
298 | 44.3k | (0..width).map(move |x_level|{ |
299 | 44.3k | Vec2(x_level, y_level) |
300 | 44.3k | }) |
301 | 1.60k | }) |
302 | 214 | } |
303 | | |
304 | | /// Iterates over all mip map level indices of a given size. |
305 | | /// The order of iteration conforms to `LineOrder::Increasing`. |
306 | 138 | pub fn mip_map_indices(round: RoundingMode, max_resolution: Vec2<usize>) -> impl Iterator<Item=usize> { |
307 | 138 | 0..compute_level_count(round, max_resolution.width().max(max_resolution.height())) |
308 | 138 | } |
309 | | |
310 | | /// Compute the number of chunks that an image is divided into. May be an expensive operation. |
311 | | // If not multilayer and chunkCount not present, |
312 | | // the number of entries in the chunk table is computed |
313 | | // using the dataWindow and tileDesc attributes and the compression format |
314 | 4.17k | pub fn compute_chunk_count(compression: Compression, data_size: Vec2<usize>, blocks: BlockDescription) -> usize { |
315 | | |
316 | 4.17k | if let BlockDescription::Tiles(tiles) = blocks { |
317 | 3.14k | let round = tiles.rounding_mode; |
318 | 3.14k | let Vec2(tile_width, tile_height) = tiles.tile_size; |
319 | | |
320 | | // TODO cache all these level values?? |
321 | | use crate::meta::attribute::LevelMode::*; |
322 | 3.14k | match tiles.level_mode { |
323 | | Singular => { |
324 | 2.88k | let tiles_x = compute_block_count(data_size.width(), tile_width); |
325 | 2.88k | let tiles_y = compute_block_count(data_size.height(), tile_height); |
326 | 2.88k | tiles_x * tiles_y |
327 | | } |
328 | | |
329 | | MipMap => { |
330 | 2.25k | mip_map_levels(round, data_size).map(|(_, Vec2(level_width, level_height))| { |
331 | 2.25k | compute_block_count(level_width, tile_width) * compute_block_count(level_height, tile_height) |
332 | 2.25k | }).sum() |
333 | | }, |
334 | | |
335 | | RipMap => { |
336 | 34.6k | rip_map_levels(round, data_size).map(|(_, Vec2(level_width, level_height))| { |
337 | 34.6k | compute_block_count(level_width, tile_width) * compute_block_count(level_height, tile_height) |
338 | 34.6k | }).sum() |
339 | | } |
340 | | } |
341 | | } |
342 | | |
343 | | // scan line blocks never have mip maps |
344 | | else { |
345 | 1.03k | compute_block_count(data_size.height(), compression.scan_lines_per_block()) |
346 | | } |
347 | 4.17k | } |
348 | | |
349 | | |
350 | | |
351 | | impl MetaData { |
352 | | |
353 | | /// Read the exr meta data from a file. |
354 | | /// Use `read_from_unbuffered` instead if you do not have a file. |
355 | | /// Does not validate the meta data. |
356 | | #[must_use] |
357 | 0 | pub fn read_from_file(path: impl AsRef<::std::path::Path>, pedantic: bool) -> Result<Self> { |
358 | 0 | Self::read_from_unbuffered(File::open(path)?, pedantic) |
359 | 0 | } |
360 | | |
361 | | /// Buffer the reader and then read the exr meta data from it. |
362 | | /// Use `read_from_buffered` if your reader is an in-memory reader. |
363 | | /// Use `read_from_file` if you have a file path. |
364 | | /// Does not validate the meta data. |
365 | | #[must_use] |
366 | 0 | pub fn read_from_unbuffered(unbuffered: impl Read, pedantic: bool) -> Result<Self> { |
367 | 0 | Self::read_from_buffered(BufReader::new(unbuffered), pedantic) |
368 | 0 | } |
369 | | |
370 | | /// Read the exr meta data from a reader. |
371 | | /// Use `read_from_file` if you have a file path. |
372 | | /// Use `read_from_unbuffered` if this is not an in-memory reader. |
373 | | /// Does not validate the meta data. |
374 | | #[must_use] |
375 | 0 | pub fn read_from_buffered(buffered: impl Read, pedantic: bool) -> Result<Self> { |
376 | 0 | let mut read = PeekRead::new(buffered); |
377 | 0 | MetaData::read_unvalidated_from_buffered_peekable(&mut read, pedantic) |
378 | 0 | } |
379 | | |
380 | | /// Does __not validate__ the meta data completely. |
381 | | #[must_use] |
382 | 4.36k | pub(crate) fn read_unvalidated_from_buffered_peekable(read: &mut PeekRead<impl Read>, pedantic: bool) -> Result<Self> { |
383 | 4.36k | magic_number::validate_exr(read)?; |
384 | | |
385 | 4.36k | let requirements = Requirements::read(read)?; |
386 | | |
387 | | // do this check now in order to fast-fail for newer versions and features than version 2 |
388 | 4.36k | requirements.validate()?; |
389 | | |
390 | 4.36k | let headers = Header::read_all(read, &requirements, pedantic)?; |
391 | | |
392 | | // TODO check if supporting requirements 2 always implies supporting requirements 1 |
393 | 1.69k | Ok(MetaData { requirements, headers }) |
394 | 4.36k | } Unexecuted instantiation: <exr::meta::MetaData>::read_unvalidated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_unvalidated_from_buffered_peekable::<_> Unexecuted instantiation: <exr::meta::MetaData>::read_unvalidated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_unvalidated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_unvalidated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_unvalidated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_unvalidated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_unvalidated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_unvalidated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_unvalidated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_unvalidated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> <exr::meta::MetaData>::read_unvalidated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Line | Count | Source | 382 | 4.31k | pub(crate) fn read_unvalidated_from_buffered_peekable(read: &mut PeekRead<impl Read>, pedantic: bool) -> Result<Self> { | 383 | 4.31k | magic_number::validate_exr(read)?; | 384 | | | 385 | 4.31k | let requirements = Requirements::read(read)?; | 386 | | | 387 | | // do this check now in order to fast-fail for newer versions and features than version 2 | 388 | 4.31k | requirements.validate()?; | 389 | | | 390 | 4.31k | let headers = Header::read_all(read, &requirements, pedantic)?; | 391 | | | 392 | | // TODO check if supporting requirements 2 always implies supporting requirements 1 | 393 | 1.66k | Ok(MetaData { requirements, headers }) | 394 | 4.31k | } |
<exr::meta::MetaData>::read_unvalidated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<alloc::vec::Vec<u8>>>> Line | Count | Source | 382 | 14 | pub(crate) fn read_unvalidated_from_buffered_peekable(read: &mut PeekRead<impl Read>, pedantic: bool) -> Result<Self> { | 383 | 14 | magic_number::validate_exr(read)?; | 384 | | | 385 | 14 | let requirements = Requirements::read(read)?; | 386 | | | 387 | | // do this check now in order to fast-fail for newer versions and features than version 2 | 388 | 14 | requirements.validate()?; | 389 | | | 390 | 14 | let headers = Header::read_all(read, &requirements, pedantic)?; | 391 | | | 392 | | // TODO check if supporting requirements 2 always implies supporting requirements 1 | 393 | 14 | Ok(MetaData { requirements, headers }) | 394 | 14 | } |
<exr::meta::MetaData>::read_unvalidated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Line | Count | Source | 382 | 29 | pub(crate) fn read_unvalidated_from_buffered_peekable(read: &mut PeekRead<impl Read>, pedantic: bool) -> Result<Self> { | 383 | 29 | magic_number::validate_exr(read)?; | 384 | | | 385 | 29 | let requirements = Requirements::read(read)?; | 386 | | | 387 | | // do this check now in order to fast-fail for newer versions and features than version 2 | 388 | 29 | requirements.validate()?; | 389 | | | 390 | 29 | let headers = Header::read_all(read, &requirements, pedantic)?; | 391 | | | 392 | | // TODO check if supporting requirements 2 always implies supporting requirements 1 | 393 | 23 | Ok(MetaData { requirements, headers }) | 394 | 29 | } |
|
395 | | |
396 | | /// Validates the meta data. |
397 | | #[must_use] |
398 | 4.36k | pub(crate) fn read_validated_from_buffered_peekable( |
399 | 4.36k | read: &mut PeekRead<impl Read>, pedantic: bool |
400 | 4.36k | ) -> Result<Self> { |
401 | 4.36k | let meta_data = Self::read_unvalidated_from_buffered_peekable(read, !pedantic)?; |
402 | 1.69k | MetaData::validate(meta_data.headers.as_slice(), pedantic)?; |
403 | 1.61k | Ok(meta_data) |
404 | 4.36k | } Unexecuted instantiation: <exr::meta::MetaData>::read_validated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_validated_from_buffered_peekable::<_> Unexecuted instantiation: <exr::meta::MetaData>::read_validated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_validated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_validated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_validated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_validated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_validated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_validated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_validated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_validated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> <exr::meta::MetaData>::read_validated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Line | Count | Source | 398 | 4.31k | pub(crate) fn read_validated_from_buffered_peekable( | 399 | 4.31k | read: &mut PeekRead<impl Read>, pedantic: bool | 400 | 4.31k | ) -> Result<Self> { | 401 | 4.31k | let meta_data = Self::read_unvalidated_from_buffered_peekable(read, !pedantic)?; | 402 | 1.66k | MetaData::validate(meta_data.headers.as_slice(), pedantic)?; | 403 | 1.57k | Ok(meta_data) | 404 | 4.31k | } |
<exr::meta::MetaData>::read_validated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<alloc::vec::Vec<u8>>>> Line | Count | Source | 398 | 14 | pub(crate) fn read_validated_from_buffered_peekable( | 399 | 14 | read: &mut PeekRead<impl Read>, pedantic: bool | 400 | 14 | ) -> Result<Self> { | 401 | 14 | let meta_data = Self::read_unvalidated_from_buffered_peekable(read, !pedantic)?; | 402 | 14 | MetaData::validate(meta_data.headers.as_slice(), pedantic)?; | 403 | 14 | Ok(meta_data) | 404 | 14 | } |
<exr::meta::MetaData>::read_validated_from_buffered_peekable::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Line | Count | Source | 398 | 29 | pub(crate) fn read_validated_from_buffered_peekable( | 399 | 29 | read: &mut PeekRead<impl Read>, pedantic: bool | 400 | 29 | ) -> Result<Self> { | 401 | 29 | let meta_data = Self::read_unvalidated_from_buffered_peekable(read, !pedantic)?; | 402 | 23 | MetaData::validate(meta_data.headers.as_slice(), pedantic)?; | 403 | 22 | Ok(meta_data) | 404 | 29 | } |
|
405 | | |
406 | | /// Validates the meta data and writes it to the stream. |
407 | | /// If pedantic, throws errors for files that may produce errors in other exr readers. |
408 | | /// Returns the automatically detected minimum requirement flags. |
409 | 14 | pub(crate) fn write_validating_to_buffered(write: &mut impl Write, headers: &[Header], pedantic: bool) -> Result<Requirements> { |
410 | | // pedantic validation to not allow slightly invalid files |
411 | | // that still could be read correctly in theory |
412 | 14 | let minimal_requirements = Self::validate(headers, pedantic)?; |
413 | | |
414 | 14 | magic_number::write(write)?; |
415 | 14 | minimal_requirements.write(write)?; |
416 | 14 | Header::write_all(headers, write, minimal_requirements.has_multiple_layers)?; |
417 | 14 | Ok(minimal_requirements) |
418 | 14 | } Unexecuted instantiation: <exr::meta::MetaData>::write_validating_to_buffered::<_> Unexecuted instantiation: <exr::meta::MetaData>::write_validating_to_buffered::<exr::io::Tracking<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>> <exr::meta::MetaData>::write_validating_to_buffered::<exr::io::Tracking<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>> Line | Count | Source | 409 | 14 | pub(crate) fn write_validating_to_buffered(write: &mut impl Write, headers: &[Header], pedantic: bool) -> Result<Requirements> { | 410 | | // pedantic validation to not allow slightly invalid files | 411 | | // that still could be read correctly in theory | 412 | 14 | let minimal_requirements = Self::validate(headers, pedantic)?; | 413 | | | 414 | 14 | magic_number::write(write)?; | 415 | 14 | minimal_requirements.write(write)?; | 416 | 14 | Header::write_all(headers, write, minimal_requirements.has_multiple_layers)?; | 417 | 14 | Ok(minimal_requirements) | 418 | 14 | } |
|
419 | | |
420 | | /// Read one offset table from the reader for each header. |
421 | 1.56k | pub fn read_offset_tables(read: &mut PeekRead<impl Read>, headers: &Headers) -> Result<OffsetTables> { |
422 | 1.56k | headers.iter() |
423 | 1.56k | .map(|header| u64::read_vec(read, header.chunk_count, u16::MAX as usize, None, "offset table size")) Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>::{closure#0} Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<_>::{closure#0} Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>::{closure#0} Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>::{closure#0} Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>::{closure#0} Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>::{closure#0} Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>::{closure#0} Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>::{closure#0} Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>::{closure#0} Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>::{closure#0} Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>::{closure#0} <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>::{closure#0} Line | Count | Source | 423 | 1.53k | .map(|header| u64::read_vec(read, header.chunk_count, u16::MAX as usize, None, "offset table size")) |
<exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::{closure#0} Line | Count | Source | 423 | 14 | .map(|header| u64::read_vec(read, header.chunk_count, u16::MAX as usize, None, "offset table size")) |
<exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>::{closure#0} Line | Count | Source | 423 | 22 | .map(|header| u64::read_vec(read, header.chunk_count, u16::MAX as usize, None, "offset table size")) |
|
424 | 1.56k | .collect() |
425 | 1.56k | } Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<_> Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> <exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Line | Count | Source | 421 | 1.53k | pub fn read_offset_tables(read: &mut PeekRead<impl Read>, headers: &Headers) -> Result<OffsetTables> { | 422 | 1.53k | headers.iter() | 423 | 1.53k | .map(|header| u64::read_vec(read, header.chunk_count, u16::MAX as usize, None, "offset table size")) | 424 | 1.53k | .collect() | 425 | 1.53k | } |
<exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<alloc::vec::Vec<u8>>>> Line | Count | Source | 421 | 14 | pub fn read_offset_tables(read: &mut PeekRead<impl Read>, headers: &Headers) -> Result<OffsetTables> { | 422 | 14 | headers.iter() | 423 | 14 | .map(|header| u64::read_vec(read, header.chunk_count, u16::MAX as usize, None, "offset table size")) | 424 | 14 | .collect() | 425 | 14 | } |
<exr::meta::MetaData>::read_offset_tables::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Line | Count | Source | 421 | 22 | pub fn read_offset_tables(read: &mut PeekRead<impl Read>, headers: &Headers) -> Result<OffsetTables> { | 422 | 22 | headers.iter() | 423 | 22 | .map(|header| u64::read_vec(read, header.chunk_count, u16::MAX as usize, None, "offset table size")) | 424 | 22 | .collect() | 425 | 22 | } |
|
426 | | |
427 | | /// Skip the offset tables by advancing the reader by the required byte count. |
428 | | // TODO use seek for large (probably all) tables! |
429 | 0 | pub fn skip_offset_tables(read: &mut PeekRead<impl Read>, headers: &Headers) -> Result<usize> { |
430 | 0 | let chunk_count: usize = headers.iter().map(|header| header.chunk_count).sum(); |
431 | 0 | crate::io::skip_bytes(read, chunk_count * u64::BYTE_SIZE)?; // TODO this should seek for large tables |
432 | 0 | Ok(chunk_count) |
433 | 0 | } |
434 | | |
435 | | /// This iterator tells you the block indices of all blocks that must be in the image. |
436 | | /// The order of the blocks depends on the `LineOrder` attribute |
437 | | /// (unspecified line order is treated the same as increasing line order). |
438 | | /// The blocks written to the file must be exactly in this order, |
439 | | /// except for when the `LineOrder` is unspecified. |
440 | | /// The index represents the block index, in increasing line order, within the header. |
441 | 14 | pub fn enumerate_ordered_header_block_indices(&self) -> impl '_ + Iterator<Item=(usize, BlockIndex)> { |
442 | 14 | crate::block::enumerate_ordered_header_block_indices(&self.headers) |
443 | 14 | } |
444 | | |
445 | | /// Go through all the block indices in the correct order and call the specified closure for each of these blocks. |
446 | | /// That way, the blocks indices are filled with real block data and returned as an iterator. |
447 | | /// The closure returns the an `UncompressedBlock` for each block index. |
448 | 14 | pub fn collect_ordered_blocks<'s>(&'s self, mut get_block: impl 's + FnMut(BlockIndex) -> UncompressedBlock) |
449 | 14 | -> impl 's + Iterator<Item=(usize, UncompressedBlock)> |
450 | 14 | { |
451 | 913 | self.enumerate_ordered_header_block_indices().map(move |(index_in_header, block_index)|{ |
452 | 913 | (index_in_header, get_block(block_index)) |
453 | 913 | }) Unexecuted instantiation: <exr::meta::MetaData>::collect_ordered_blocks::<_>::{closure#0} Unexecuted instantiation: <exr::meta::MetaData>::collect_ordered_blocks::<<exr::meta::MetaData>::collect_ordered_block_data<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0}>::{closure#0} Unexecuted instantiation: <exr::meta::MetaData>::collect_ordered_blocks::<<exr::meta::MetaData>::collect_ordered_block_data<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0}>::{closure#0} Unexecuted instantiation: <exr::meta::MetaData>::collect_ordered_blocks::<<exr::meta::MetaData>::collect_ordered_block_data<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0}>::{closure#0} <exr::meta::MetaData>::collect_ordered_blocks::<<exr::meta::MetaData>::collect_ordered_block_data<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0}>::{closure#0} Line | Count | Source | 451 | 913 | self.enumerate_ordered_header_block_indices().map(move |(index_in_header, block_index)|{ | 452 | 913 | (index_in_header, get_block(block_index)) | 453 | 913 | }) |
|
454 | 14 | } Unexecuted instantiation: <exr::meta::MetaData>::collect_ordered_blocks::<_> Unexecuted instantiation: <exr::meta::MetaData>::collect_ordered_blocks::<<exr::meta::MetaData>::collect_ordered_block_data<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0}> Unexecuted instantiation: <exr::meta::MetaData>::collect_ordered_blocks::<<exr::meta::MetaData>::collect_ordered_block_data<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0}> Unexecuted instantiation: <exr::meta::MetaData>::collect_ordered_blocks::<<exr::meta::MetaData>::collect_ordered_block_data<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0}> <exr::meta::MetaData>::collect_ordered_blocks::<<exr::meta::MetaData>::collect_ordered_block_data<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0}> Line | Count | Source | 448 | 14 | pub fn collect_ordered_blocks<'s>(&'s self, mut get_block: impl 's + FnMut(BlockIndex) -> UncompressedBlock) | 449 | 14 | -> impl 's + Iterator<Item=(usize, UncompressedBlock)> | 450 | 14 | { | 451 | 14 | self.enumerate_ordered_header_block_indices().map(move |(index_in_header, block_index)|{ | 452 | | (index_in_header, get_block(block_index)) | 453 | 14 | }) | 454 | 14 | } |
|
455 | | |
456 | | /// Go through all the block indices in the correct order and call the specified closure for each of these blocks. |
457 | | /// That way, the blocks indices are filled with real block data and returned as an iterator. |
458 | | /// The closure returns the byte data for each block index. |
459 | 14 | pub fn collect_ordered_block_data<'s>(&'s self, mut get_block_data: impl 's + FnMut(BlockIndex) -> Vec<u8>) |
460 | 14 | -> impl 's + Iterator<Item=(usize, UncompressedBlock)> |
461 | 14 | { |
462 | 14 | self.collect_ordered_blocks(move |block_index| |
463 | 913 | UncompressedBlock { index: block_index, data: get_block_data(block_index) } Unexecuted instantiation: <exr::meta::MetaData>::collect_ordered_block_data::<_>::{closure#0} Unexecuted instantiation: <exr::meta::MetaData>::collect_ordered_block_data::<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0} Unexecuted instantiation: <exr::meta::MetaData>::collect_ordered_block_data::<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0} Unexecuted instantiation: <exr::meta::MetaData>::collect_ordered_block_data::<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0} <exr::meta::MetaData>::collect_ordered_block_data::<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0} Line | Count | Source | 463 | 913 | UncompressedBlock { index: block_index, data: get_block_data(block_index) } |
|
464 | 14 | ) |
465 | 14 | } Unexecuted instantiation: <exr::meta::MetaData>::collect_ordered_block_data::<_> Unexecuted instantiation: <exr::meta::MetaData>::collect_ordered_block_data::<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}> Unexecuted instantiation: <exr::meta::MetaData>::collect_ordered_block_data::<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}> Unexecuted instantiation: <exr::meta::MetaData>::collect_ordered_block_data::<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}> <exr::meta::MetaData>::collect_ordered_block_data::<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}> Line | Count | Source | 459 | 14 | pub fn collect_ordered_block_data<'s>(&'s self, mut get_block_data: impl 's + FnMut(BlockIndex) -> Vec<u8>) | 460 | 14 | -> impl 's + Iterator<Item=(usize, UncompressedBlock)> | 461 | 14 | { | 462 | 14 | self.collect_ordered_blocks(move |block_index| | 463 | 14 | UncompressedBlock { index: block_index, data: get_block_data(block_index) } | 464 | 14 | ) | 465 | 14 | } |
|
466 | | |
467 | | /// Validates this meta data. Returns the minimal possible requirements. |
468 | 1.71k | pub fn validate(headers: &[Header], pedantic: bool) -> Result<Requirements> { |
469 | 1.71k | if headers.len() == 0 { |
470 | 0 | return Err(Error::invalid("at least one layer is required")); |
471 | 1.71k | } |
472 | 1.71k | |
473 | 1.71k | let deep = false; // TODO deep data |
474 | 1.71k | let is_multilayer = headers.len() > 1; |
475 | 1.71k | let first_header_has_tiles = headers.iter().next() |
476 | 1.71k | .map_or(false, |header| header.blocks.has_tiles()); |
477 | | |
478 | 1.71k | let mut minimal_requirements = Requirements { |
479 | | // according to the spec, version 2 should only be necessary if `is_multilayer || deep`. |
480 | | // but the current open exr library does not support images with version 1, so always use version 2. |
481 | | file_format_version: 2, |
482 | | |
483 | | // start as low as possible, later increasing if required |
484 | | has_long_names: false, |
485 | | |
486 | 1.71k | is_single_layer_and_tiled: !is_multilayer && first_header_has_tiles, |
487 | 1.71k | has_multiple_layers: is_multilayer, |
488 | 1.71k | has_deep_data: deep, |
489 | | }; |
490 | | |
491 | 3.51k | for header in headers { |
492 | 1.88k | if header.deep { // TODO deep data (and then remove this check) |
493 | 1 | return Err(Error::unsupported("deep data not supported yet")); |
494 | 1.88k | } |
495 | 1.88k | |
496 | 1.88k | header.validate(is_multilayer, &mut minimal_requirements.has_long_names, pedantic)?; |
497 | | } |
498 | | |
499 | | // TODO validation fn! |
500 | | /*if let Some(max) = max_pixel_bytes { |
501 | | let byte_size: usize = headers.iter() |
502 | | .map(|header| header.total_pixel_bytes()) |
503 | | .sum(); |
504 | | |
505 | | if byte_size > max { |
506 | | return Err(Error::invalid("image larger than specified maximum")); |
507 | | } |
508 | | }*/ |
509 | | |
510 | 1.62k | if pedantic { // check for duplicate header names |
511 | 14 | let mut header_names = HashSet::with_capacity(headers.len()); |
512 | 28 | for header in headers { |
513 | 14 | if !header_names.insert(&header.own_attributes.layer_name) { |
514 | 0 | return Err(Error::invalid(format!( |
515 | 0 | "duplicate layer name: `{}`", |
516 | 0 | header.own_attributes.layer_name.as_ref().expect("header validation bug") |
517 | 0 | ))); |
518 | 14 | } |
519 | | } |
520 | 1.61k | } |
521 | | |
522 | 1.62k | if pedantic { |
523 | 14 | let must_share = headers.iter().flat_map(|header| header.own_attributes.other.iter()) |
524 | 14 | .any(|(_, value)| value.to_chromaticities().is_ok() || value.to_time_code().is_ok()); |
525 | 14 | |
526 | 14 | if must_share { |
527 | 0 | return Err(Error::invalid("chromaticities and time code attributes must must not exist in own attributes but shared instead")); |
528 | 14 | } |
529 | 1.61k | } |
530 | | |
531 | 1.62k | if pedantic && headers.len() > 1 { // check for attributes that should not differ in between headers |
532 | 0 | let first_header = headers.first().expect("header count validation bug"); |
533 | 0 | let first_header_attributes = &first_header.shared_attributes; |
534 | | |
535 | 0 | for header in &headers[1..] { |
536 | 0 | if &header.shared_attributes != first_header_attributes { |
537 | 0 | return Err(Error::invalid("display window, pixel aspect, chromaticities, and time code attributes must be equal for all headers")) |
538 | 0 | } |
539 | | } |
540 | 1.62k | } |
541 | | |
542 | 1.62k | debug_assert!(minimal_requirements.validate().is_ok(), "inferred requirements are invalid"); |
543 | 1.62k | Ok(minimal_requirements) |
544 | 1.71k | } |
545 | | } |
546 | | |
547 | | |
548 | | |
549 | | |
550 | | impl Requirements { |
551 | | |
552 | | // this is actually used for control flow, as the number of headers may be 1 in a multilayer file |
553 | | /// Is this file declared to contain multiple layers? |
554 | 8.17k | pub fn is_multilayer(&self) -> bool { |
555 | 8.17k | self.has_multiple_layers |
556 | 8.17k | } |
557 | | |
558 | | /// Read the value without validating. |
559 | 4.36k | pub fn read<R: Read>(read: &mut R) -> Result<Self> { |
560 | | use ::bit_field::BitField; |
561 | | |
562 | 4.36k | let version_and_flags = u32::read(read)?; |
563 | | |
564 | | // take the 8 least significant bits, they contain the file format version number |
565 | 4.36k | let version = (version_and_flags & 0x000F) as u8; |
566 | 4.36k | |
567 | 4.36k | // the 24 most significant bits are treated as a set of boolean flags |
568 | 4.36k | let is_single_tile = version_and_flags.get_bit(9); |
569 | 4.36k | let has_long_names = version_and_flags.get_bit(10); |
570 | 4.36k | let has_deep_data = version_and_flags.get_bit(11); |
571 | 4.36k | let has_multiple_layers = version_and_flags.get_bit(12); |
572 | 4.36k | |
573 | 4.36k | // all remaining bits except 9, 10, 11 and 12 are reserved and should be 0 |
574 | 4.36k | // if a file has any of these bits set to 1, it means this file contains |
575 | 4.36k | // a feature that we don't support |
576 | 4.36k | let unknown_flags = version_and_flags >> 13; // all flags excluding the 12 bits we already parsed |
577 | 4.36k | |
578 | 4.36k | if unknown_flags != 0 { // TODO test if this correctly detects unsupported files |
579 | 0 | return Err(Error::unsupported("too new file feature flags")); |
580 | 4.36k | } |
581 | 4.36k | |
582 | 4.36k | let version = Requirements { |
583 | 4.36k | file_format_version: version, |
584 | 4.36k | is_single_layer_and_tiled: is_single_tile, has_long_names, |
585 | 4.36k | has_deep_data, has_multiple_layers, |
586 | 4.36k | }; |
587 | 4.36k | |
588 | 4.36k | Ok(version) |
589 | 4.36k | } Unexecuted instantiation: <exr::meta::Requirements>::read::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: <exr::meta::Requirements>::read::<_> Unexecuted instantiation: <exr::meta::Requirements>::read::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: <exr::meta::Requirements>::read::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: <exr::meta::Requirements>::read::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: <exr::meta::Requirements>::read::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: <exr::meta::Requirements>::read::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: <exr::meta::Requirements>::read::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: <exr::meta::Requirements>::read::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: <exr::meta::Requirements>::read::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Unexecuted instantiation: <exr::meta::Requirements>::read::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> <exr::meta::Requirements>::read::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Line | Count | Source | 559 | 4.31k | pub fn read<R: Read>(read: &mut R) -> Result<Self> { | 560 | | use ::bit_field::BitField; | 561 | | | 562 | 4.31k | let version_and_flags = u32::read(read)?; | 563 | | | 564 | | // take the 8 least significant bits, they contain the file format version number | 565 | 4.31k | let version = (version_and_flags & 0x000F) as u8; | 566 | 4.31k | | 567 | 4.31k | // the 24 most significant bits are treated as a set of boolean flags | 568 | 4.31k | let is_single_tile = version_and_flags.get_bit(9); | 569 | 4.31k | let has_long_names = version_and_flags.get_bit(10); | 570 | 4.31k | let has_deep_data = version_and_flags.get_bit(11); | 571 | 4.31k | let has_multiple_layers = version_and_flags.get_bit(12); | 572 | 4.31k | | 573 | 4.31k | // all remaining bits except 9, 10, 11 and 12 are reserved and should be 0 | 574 | 4.31k | // if a file has any of these bits set to 1, it means this file contains | 575 | 4.31k | // a feature that we don't support | 576 | 4.31k | let unknown_flags = version_and_flags >> 13; // all flags excluding the 12 bits we already parsed | 577 | 4.31k | | 578 | 4.31k | if unknown_flags != 0 { // TODO test if this correctly detects unsupported files | 579 | 0 | return Err(Error::unsupported("too new file feature flags")); | 580 | 4.31k | } | 581 | 4.31k | | 582 | 4.31k | let version = Requirements { | 583 | 4.31k | file_format_version: version, | 584 | 4.31k | is_single_layer_and_tiled: is_single_tile, has_long_names, | 585 | 4.31k | has_deep_data, has_multiple_layers, | 586 | 4.31k | }; | 587 | 4.31k | | 588 | 4.31k | Ok(version) | 589 | 4.31k | } |
<exr::meta::Requirements>::read::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>> Line | Count | Source | 559 | 14 | pub fn read<R: Read>(read: &mut R) -> Result<Self> { | 560 | | use ::bit_field::BitField; | 561 | | | 562 | 14 | let version_and_flags = u32::read(read)?; | 563 | | | 564 | | // take the 8 least significant bits, they contain the file format version number | 565 | 14 | let version = (version_and_flags & 0x000F) as u8; | 566 | 14 | | 567 | 14 | // the 24 most significant bits are treated as a set of boolean flags | 568 | 14 | let is_single_tile = version_and_flags.get_bit(9); | 569 | 14 | let has_long_names = version_and_flags.get_bit(10); | 570 | 14 | let has_deep_data = version_and_flags.get_bit(11); | 571 | 14 | let has_multiple_layers = version_and_flags.get_bit(12); | 572 | 14 | | 573 | 14 | // all remaining bits except 9, 10, 11 and 12 are reserved and should be 0 | 574 | 14 | // if a file has any of these bits set to 1, it means this file contains | 575 | 14 | // a feature that we don't support | 576 | 14 | let unknown_flags = version_and_flags >> 13; // all flags excluding the 12 bits we already parsed | 577 | 14 | | 578 | 14 | if unknown_flags != 0 { // TODO test if this correctly detects unsupported files | 579 | 0 | return Err(Error::unsupported("too new file feature flags")); | 580 | 14 | } | 581 | 14 | | 582 | 14 | let version = Requirements { | 583 | 14 | file_format_version: version, | 584 | 14 | is_single_layer_and_tiled: is_single_tile, has_long_names, | 585 | 14 | has_deep_data, has_multiple_layers, | 586 | 14 | }; | 587 | 14 | | 588 | 14 | Ok(version) | 589 | 14 | } |
<exr::meta::Requirements>::read::<exr::io::PeekRead<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>>> Line | Count | Source | 559 | 29 | pub fn read<R: Read>(read: &mut R) -> Result<Self> { | 560 | | use ::bit_field::BitField; | 561 | | | 562 | 29 | let version_and_flags = u32::read(read)?; | 563 | | | 564 | | // take the 8 least significant bits, they contain the file format version number | 565 | 29 | let version = (version_and_flags & 0x000F) as u8; | 566 | 29 | | 567 | 29 | // the 24 most significant bits are treated as a set of boolean flags | 568 | 29 | let is_single_tile = version_and_flags.get_bit(9); | 569 | 29 | let has_long_names = version_and_flags.get_bit(10); | 570 | 29 | let has_deep_data = version_and_flags.get_bit(11); | 571 | 29 | let has_multiple_layers = version_and_flags.get_bit(12); | 572 | 29 | | 573 | 29 | // all remaining bits except 9, 10, 11 and 12 are reserved and should be 0 | 574 | 29 | // if a file has any of these bits set to 1, it means this file contains | 575 | 29 | // a feature that we don't support | 576 | 29 | let unknown_flags = version_and_flags >> 13; // all flags excluding the 12 bits we already parsed | 577 | 29 | | 578 | 29 | if unknown_flags != 0 { // TODO test if this correctly detects unsupported files | 579 | 0 | return Err(Error::unsupported("too new file feature flags")); | 580 | 29 | } | 581 | 29 | | 582 | 29 | let version = Requirements { | 583 | 29 | file_format_version: version, | 584 | 29 | is_single_layer_and_tiled: is_single_tile, has_long_names, | 585 | 29 | has_deep_data, has_multiple_layers, | 586 | 29 | }; | 587 | 29 | | 588 | 29 | Ok(version) | 589 | 29 | } |
|
590 | | |
591 | | /// Without validation, write this instance to the byte stream. |
592 | 14 | pub fn write<W: Write>(self, write: &mut W) -> UnitResult { |
593 | | use ::bit_field::BitField; |
594 | | |
595 | | // the 8 least significant bits contain the file format version number |
596 | | // and the flags are set to 0 |
597 | 14 | let mut version_and_flags = self.file_format_version as u32; |
598 | 14 | |
599 | 14 | // the 24 most significant bits are treated as a set of boolean flags |
600 | 14 | version_and_flags.set_bit(9, self.is_single_layer_and_tiled); |
601 | 14 | version_and_flags.set_bit(10, self.has_long_names); |
602 | 14 | version_and_flags.set_bit(11, self.has_deep_data); |
603 | 14 | version_and_flags.set_bit(12, self.has_multiple_layers); |
604 | 14 | // all remaining bits except 9, 10, 11 and 12 are reserved and should be 0 |
605 | 14 | |
606 | 14 | version_and_flags.write(write)?; |
607 | 14 | Ok(()) |
608 | 14 | } Unexecuted instantiation: <exr::meta::Requirements>::write::<_> Unexecuted instantiation: <exr::meta::Requirements>::write::<exr::io::Tracking<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>> <exr::meta::Requirements>::write::<exr::io::Tracking<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>> Line | Count | Source | 592 | 14 | pub fn write<W: Write>(self, write: &mut W) -> UnitResult { | 593 | | use ::bit_field::BitField; | 594 | | | 595 | | // the 8 least significant bits contain the file format version number | 596 | | // and the flags are set to 0 | 597 | 14 | let mut version_and_flags = self.file_format_version as u32; | 598 | 14 | | 599 | 14 | // the 24 most significant bits are treated as a set of boolean flags | 600 | 14 | version_and_flags.set_bit(9, self.is_single_layer_and_tiled); | 601 | 14 | version_and_flags.set_bit(10, self.has_long_names); | 602 | 14 | version_and_flags.set_bit(11, self.has_deep_data); | 603 | 14 | version_and_flags.set_bit(12, self.has_multiple_layers); | 604 | 14 | // all remaining bits except 9, 10, 11 and 12 are reserved and should be 0 | 605 | 14 | | 606 | 14 | version_and_flags.write(write)?; | 607 | 14 | Ok(()) | 608 | 14 | } |
|
609 | | |
610 | | /// Validate this instance. |
611 | 4.36k | pub fn validate(&self) -> UnitResult { |
612 | 4.36k | if self.file_format_version == 2 { |
613 | | |
614 | 4.36k | match ( |
615 | 4.36k | self.is_single_layer_and_tiled, self.has_deep_data, self.has_multiple_layers, |
616 | 4.36k | self.file_format_version |
617 | 4.36k | ) { |
618 | | // Single-part scan line. One normal scan line image. |
619 | 2.42k | (false, false, false, 1..=2) => Ok(()), |
620 | | |
621 | | // Single-part tile. One normal tiled image. |
622 | 1.88k | (true, false, false, 1..=2) => Ok(()), |
623 | | |
624 | | // Multi-part (new in 2.0). |
625 | | // Multiple normal images (scan line and/or tiled). |
626 | 30 | (false, false, true, 2) => Ok(()), |
627 | | |
628 | | // Single-part deep data (new in 2.0). |
629 | | // One deep tile or deep scan line part |
630 | 2 | (false, true, false, 2) => Ok(()), |
631 | | |
632 | | // Multi-part deep data (new in 2.0). |
633 | | // Multiple parts (any combination of: |
634 | | // tiles, scan lines, deep tiles and/or deep scan lines). |
635 | 22 | (false, true, true, 2) => Ok(()), |
636 | | |
637 | 0 | _ => Err(Error::invalid("file feature flags")) |
638 | | } |
639 | | } |
640 | | else { |
641 | 0 | Err(Error::unsupported("file versions other than 2.0 are not supported")) |
642 | | } |
643 | 4.36k | } |
644 | | } |
645 | | |
646 | | |
647 | | #[cfg(test)] |
648 | | mod test { |
649 | | use super::*; |
650 | | use crate::meta::header::{ImageAttributes, LayerAttributes}; |
651 | | |
652 | | #[test] |
653 | | fn round_trip_requirements() { |
654 | | let requirements = Requirements { |
655 | | file_format_version: 2, |
656 | | is_single_layer_and_tiled: true, |
657 | | has_long_names: false, |
658 | | has_deep_data: true, |
659 | | has_multiple_layers: false |
660 | | }; |
661 | | |
662 | | let mut data: Vec<u8> = Vec::new(); |
663 | | requirements.write(&mut data).unwrap(); |
664 | | let read = Requirements::read(&mut data.as_slice()).unwrap(); |
665 | | assert_eq!(requirements, read); |
666 | | } |
667 | | |
668 | | #[test] |
669 | | fn round_trip(){ |
670 | | let header = Header { |
671 | | channels: ChannelList::new(smallvec![ |
672 | | ChannelDescription { |
673 | | name: Text::from("main"), |
674 | | sample_type: SampleType::U32, |
675 | | quantize_linearly: false, |
676 | | sampling: Vec2(1, 1) |
677 | | } |
678 | | ], |
679 | | ), |
680 | | compression: Compression::Uncompressed, |
681 | | line_order: LineOrder::Increasing, |
682 | | deep_data_version: Some(1), |
683 | | chunk_count: compute_chunk_count(Compression::Uncompressed, Vec2(2000, 333), BlockDescription::ScanLines), |
684 | | max_samples_per_pixel: Some(4), |
685 | | shared_attributes: ImageAttributes { |
686 | | pixel_aspect: 3.0, |
687 | | .. ImageAttributes::new(IntegerBounds { |
688 | | position: Vec2(2,1), |
689 | | size: Vec2(11, 9) |
690 | | }) |
691 | | }, |
692 | | |
693 | | blocks: BlockDescription::ScanLines, |
694 | | deep: false, |
695 | | layer_size: Vec2(2000, 333), |
696 | | own_attributes: LayerAttributes { |
697 | | layer_name: Some(Text::from("test name lol")), |
698 | | layer_position: Vec2(3, -5), |
699 | | screen_window_center: Vec2(0.3, 99.0), |
700 | | screen_window_width: 0.19, |
701 | | .. Default::default() |
702 | | } |
703 | | }; |
704 | | |
705 | | let meta = MetaData { |
706 | | requirements: Requirements { |
707 | | file_format_version: 2, |
708 | | is_single_layer_and_tiled: false, |
709 | | has_long_names: false, |
710 | | has_deep_data: false, |
711 | | has_multiple_layers: false |
712 | | }, |
713 | | headers: smallvec![ header ], |
714 | | }; |
715 | | |
716 | | |
717 | | let mut data: Vec<u8> = Vec::new(); |
718 | | MetaData::write_validating_to_buffered(&mut data, meta.headers.as_slice(), true).unwrap(); |
719 | | let meta2 = MetaData::read_from_buffered(data.as_slice(), false).unwrap(); |
720 | | MetaData::validate(meta2.headers.as_slice(), true).unwrap(); |
721 | | assert_eq!(meta, meta2); |
722 | | } |
723 | | |
724 | | #[test] |
725 | | fn infer_low_requirements() { |
726 | | let header_version_1_short_names = Header { |
727 | | channels: ChannelList::new(smallvec![ |
728 | | ChannelDescription { |
729 | | name: Text::from("main"), |
730 | | sample_type: SampleType::U32, |
731 | | quantize_linearly: false, |
732 | | sampling: Vec2(1, 1) |
733 | | } |
734 | | ], |
735 | | ), |
736 | | compression: Compression::Uncompressed, |
737 | | line_order: LineOrder::Increasing, |
738 | | deep_data_version: Some(1), |
739 | | chunk_count: compute_chunk_count(Compression::Uncompressed, Vec2(2000, 333), BlockDescription::ScanLines), |
740 | | max_samples_per_pixel: Some(4), |
741 | | shared_attributes: ImageAttributes { |
742 | | pixel_aspect: 3.0, |
743 | | .. ImageAttributes::new(IntegerBounds { |
744 | | position: Vec2(2,1), |
745 | | size: Vec2(11, 9) |
746 | | }) |
747 | | }, |
748 | | blocks: BlockDescription::ScanLines, |
749 | | deep: false, |
750 | | layer_size: Vec2(2000, 333), |
751 | | own_attributes: LayerAttributes { |
752 | | other: vec![ |
753 | | (Text::try_from("x").unwrap(), AttributeValue::F32(3.0)), |
754 | | (Text::try_from("y").unwrap(), AttributeValue::F32(-1.0)), |
755 | | ].into_iter().collect(), |
756 | | .. Default::default() |
757 | | } |
758 | | }; |
759 | | |
760 | | let low_requirements = MetaData::validate( |
761 | | &[header_version_1_short_names], true |
762 | | ).unwrap(); |
763 | | |
764 | | assert_eq!(low_requirements.has_long_names, false); |
765 | | assert_eq!(low_requirements.file_format_version, 2); // always have version 2 |
766 | | assert_eq!(low_requirements.has_deep_data, false); |
767 | | assert_eq!(low_requirements.has_multiple_layers, false); |
768 | | } |
769 | | |
770 | | #[test] |
771 | | fn infer_high_requirements() { |
772 | | let header_version_2_long_names = Header { |
773 | | channels: ChannelList::new( |
774 | | smallvec![ |
775 | | ChannelDescription { |
776 | | name: Text::new_or_panic("main"), |
777 | | sample_type: SampleType::U32, |
778 | | quantize_linearly: false, |
779 | | sampling: Vec2(1, 1) |
780 | | } |
781 | | ], |
782 | | ), |
783 | | compression: Compression::Uncompressed, |
784 | | line_order: LineOrder::Increasing, |
785 | | deep_data_version: Some(1), |
786 | | chunk_count: compute_chunk_count(Compression::Uncompressed, Vec2(2000, 333), BlockDescription::ScanLines), |
787 | | max_samples_per_pixel: Some(4), |
788 | | shared_attributes: ImageAttributes { |
789 | | pixel_aspect: 3.0, |
790 | | .. ImageAttributes::new(IntegerBounds { |
791 | | position: Vec2(2,1), |
792 | | size: Vec2(11, 9) |
793 | | }) |
794 | | }, |
795 | | blocks: BlockDescription::ScanLines, |
796 | | deep: false, |
797 | | layer_size: Vec2(2000, 333), |
798 | | own_attributes: LayerAttributes { |
799 | | layer_name: Some(Text::new_or_panic("oasdasoidfj")), |
800 | | other: vec![ |
801 | | (Text::new_or_panic("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"), AttributeValue::F32(3.0)), |
802 | | (Text::new_or_panic("y"), AttributeValue::F32(-1.0)), |
803 | | ].into_iter().collect(), |
804 | | .. Default::default() |
805 | | } |
806 | | }; |
807 | | |
808 | | let mut layer_2 = header_version_2_long_names.clone(); |
809 | | layer_2.own_attributes.layer_name = Some(Text::new_or_panic("anythingelse")); |
810 | | |
811 | | let low_requirements = MetaData::validate( |
812 | | &[header_version_2_long_names, layer_2], true |
813 | | ).unwrap(); |
814 | | |
815 | | assert_eq!(low_requirements.has_long_names, true); |
816 | | assert_eq!(low_requirements.file_format_version, 2); |
817 | | assert_eq!(low_requirements.has_deep_data, false); |
818 | | assert_eq!(low_requirements.has_multiple_layers, true); |
819 | | } |
820 | | } |
821 | | |