/rust/registry/src/index.crates.io-1949cf8c6b5b557f/exr-1.74.0/src/meta/header.rs
Line | Count | Source |
1 | | |
2 | | //! Contains collections of common attributes. |
3 | | //! Defines some data types that list all standard attributes. |
4 | | |
5 | | use std::collections::HashMap; |
6 | | use crate::meta::attribute::*; // FIXME shouldn't this need some more imports???? |
7 | | use crate::meta::*; |
8 | | use crate::math::Vec2; |
9 | | |
10 | | // TODO rename header to LayerDescription! |
11 | | |
12 | | /// Describes a single layer in a file. |
13 | | /// A file can have any number of layers. |
14 | | /// The meta data contains one header per layer. |
15 | | #[derive(Clone, Debug, PartialEq)] |
16 | | pub struct Header { |
17 | | |
18 | | /// List of channels in this layer. |
19 | | pub channels: ChannelList, |
20 | | |
21 | | /// How the pixel data of all channels in this layer is compressed. May be `Compression::Uncompressed`. |
22 | | pub compression: Compression, |
23 | | |
24 | | /// Describes how the pixels of this layer are divided into smaller blocks. |
25 | | /// A single block can be loaded without processing all bytes of a file. |
26 | | /// |
27 | | /// Also describes whether a file contains multiple resolution levels: mip maps or rip maps. |
28 | | /// This allows loading not the full resolution, but the smallest sensible resolution. |
29 | | // |
30 | | // Required if file contains deep data or multiple layers. |
31 | | // Note: This value must agree with the version field's tile bit and deep data bit. |
32 | | // In this crate, this attribute will always have a value, for simplicity. |
33 | | pub blocks: BlockDescription, |
34 | | |
35 | | /// In what order the tiles of this header occur in the file. |
36 | | pub line_order: LineOrder, |
37 | | |
38 | | /// The resolution of this layer. Equivalent to the size of the `DataWindow`. |
39 | | pub layer_size: Vec2<usize>, |
40 | | |
41 | | /// Whether this layer contains deep data. |
42 | | pub deep: bool, |
43 | | |
44 | | /// This library supports only deep data version 1. |
45 | | pub deep_data_version: Option<i32>, |
46 | | |
47 | | /// Number of chunks, that is, scan line blocks or tiles, that this image has been divided into. |
48 | | /// This number is calculated once at the beginning |
49 | | /// of the read process or when creating a header object. |
50 | | /// |
51 | | /// This value includes all chunks of all resolution levels. |
52 | | /// |
53 | | /// |
54 | | /// __Warning__ |
55 | | /// _This value is relied upon. You should probably use `Header::with_encoding`, |
56 | | /// which automatically updates the chunk count._ |
57 | | pub chunk_count: usize, |
58 | | |
59 | | // Required for deep data (deepscanline and deeptile) layers. |
60 | | // Note: Since the value of "maxSamplesPerPixel" |
61 | | // maybe be unknown at the time of opening the |
62 | | // file, the value “ -1 ” is written to the file to |
63 | | // indicate an unknown value. When the file is |
64 | | // closed, this will be overwritten with the correct value. |
65 | | // If file writing does not complete |
66 | | // correctly due to an error, the value -1 will |
67 | | // remain. In this case, the value must be derived |
68 | | // by decoding each chunk in the layer |
69 | | /// Maximum number of samples in a single pixel in a deep image. |
70 | | pub max_samples_per_pixel: Option<usize>, |
71 | | |
72 | | /// Includes mandatory fields like pixel aspect or display window |
73 | | /// which must be the same for all layers. |
74 | | pub shared_attributes: ImageAttributes, |
75 | | |
76 | | /// Does not include the attributes required for reading the file contents. |
77 | | /// Excludes standard fields that must be the same for all headers. |
78 | | pub own_attributes: LayerAttributes, |
79 | | } |
80 | | |
81 | | /// Includes mandatory fields like pixel aspect or display window |
82 | | /// which must be the same for all layers. |
83 | | /// For more attributes, see struct `LayerAttributes`. |
84 | | #[derive(Clone, PartialEq, Debug)] |
85 | | pub struct ImageAttributes { |
86 | | |
87 | | /// The rectangle anywhere in the global infinite 2D space |
88 | | /// that clips all contents of the file. |
89 | | pub display_window: IntegerBounds, |
90 | | |
91 | | /// Aspect ratio of each pixel in this header. |
92 | | pub pixel_aspect: f32, |
93 | | |
94 | | /// The chromaticities attribute of the image. See the `Chromaticities` type. |
95 | | pub chromaticities: Option<Chromaticities>, |
96 | | |
97 | | /// The time code of the image. |
98 | | pub time_code: Option<TimeCode>, |
99 | | |
100 | | /// Contains custom attributes. |
101 | | /// Does not contain the attributes already present in the `ImageAttributes`. |
102 | | /// Contains only attributes that are standardized to be the same for all headers: chromaticities and time codes. |
103 | | pub other: HashMap<Text, AttributeValue>, |
104 | | } |
105 | | |
106 | | /// Does not include the attributes required for reading the file contents. |
107 | | /// Excludes standard fields that must be the same for all headers. |
108 | | /// For more attributes, see struct `ImageAttributes`. |
109 | | #[derive(Clone, PartialEq)] |
110 | | pub struct LayerAttributes { |
111 | | |
112 | | /// The name of this layer. |
113 | | /// Required if this file contains deep data or multiple layers. |
114 | | // As this is an attribute value, it is not restricted in length, may even be empty |
115 | | pub layer_name: Option<Text>, |
116 | | |
117 | | /// The top left corner of the rectangle that positions this layer |
118 | | /// within the global infinite 2D space of the whole file. |
119 | | /// This represents the position of the `DataWindow`. |
120 | | pub layer_position: Vec2<i32>, |
121 | | |
122 | | /// Part of the perspective projection. Default should be `(0, 0)`. |
123 | | // TODO same for all layers? |
124 | | pub screen_window_center: Vec2<f32>, |
125 | | |
126 | | // TODO same for all layers? |
127 | | /// Part of the perspective projection. Default should be `1`. |
128 | | pub screen_window_width: f32, |
129 | | |
130 | | /// The white luminance of the colors. |
131 | | /// Defines the luminance in candelas per square meter, Nits, of the rgb value `(1, 1, 1)`. |
132 | | // If the chromaticities and the whiteLuminance of an RGB image are |
133 | | // known, then it is possible to convert the image's pixels from RGB |
134 | | // to CIE XYZ tristimulus values (see function RGBtoXYZ() in header |
135 | | // file ImfChromaticities.h). |
136 | | pub white_luminance: Option<f32>, |
137 | | |
138 | | /// The adopted neutral of the colors. Specifies the CIE (x,y) frequency coordinates that should |
139 | | /// be considered neutral during color rendering. Pixels in the image |
140 | | /// whose CIE (x,y) frequency coordinates match the adopted neutral value should |
141 | | /// be mapped to neutral values on the given display. |
142 | | pub adopted_neutral: Option<Vec2<f32>>, |
143 | | |
144 | | /// Name of the color transform function that is applied for rendering the image. |
145 | | pub rendering_transform_name: Option<Text>, |
146 | | |
147 | | /// Name of the color transform function that computes the look modification of the image. |
148 | | pub look_modification_transform_name: Option<Text>, |
149 | | |
150 | | /// The horizontal density, in pixels per inch. |
151 | | /// The image's vertical output density can be computed using `horizontal_density * pixel_aspect_ratio`. |
152 | | pub horizontal_density: Option<f32>, |
153 | | |
154 | | /// Name of the owner. |
155 | | pub owner: Option<Text>, |
156 | | |
157 | | /// Additional textual information. |
158 | | pub comments: Option<Text>, |
159 | | |
160 | | /// The date of image creation, in `YYYY:MM:DD hh:mm:ss` format. |
161 | | // TODO parse! |
162 | | pub capture_date: Option<Text>, |
163 | | |
164 | | /// Time offset from UTC. |
165 | | pub utc_offset: Option<f32>, |
166 | | |
167 | | /// Geographical image location. |
168 | | pub longitude: Option<f32>, |
169 | | |
170 | | /// Geographical image location. |
171 | | pub latitude: Option<f32>, |
172 | | |
173 | | /// Geographical image location. |
174 | | pub altitude: Option<f32>, |
175 | | |
176 | | /// Camera focus in meters. |
177 | | pub focus: Option<f32>, |
178 | | |
179 | | /// Exposure time in seconds. |
180 | | pub exposure: Option<f32>, |
181 | | |
182 | | /// Camera aperture measured in f-stops. Equals the focal length |
183 | | /// of the lens divided by the diameter of the iris opening. |
184 | | pub aperture: Option<f32>, |
185 | | |
186 | | /// Iso-speed of the camera sensor. |
187 | | pub iso_speed: Option<f32>, |
188 | | |
189 | | /// If this is an environment map, specifies how to interpret it. |
190 | | pub environment_map: Option<EnvironmentMap>, |
191 | | |
192 | | /// Identifies film manufacturer, film type, film roll and frame position within the roll. |
193 | | pub film_key_code: Option<KeyCode>, |
194 | | |
195 | | /// Specifies how texture map images are extrapolated. |
196 | | /// Values can be `black`, `clamp`, `periodic`, or `mirror`. |
197 | | pub wrap_mode_name: Option<Text>, |
198 | | |
199 | | /// Frames per second if this is a frame in a sequence. |
200 | | pub frames_per_second: Option<Rational>, |
201 | | |
202 | | /// Specifies the view names for multi-view, for example stereo, image files. |
203 | | pub multi_view_names: Option<Vec<Text>>, |
204 | | |
205 | | /// The matrix that transforms 3D points from the world to the camera coordinate space. |
206 | | /// Left-handed coordinate system, y up, z forward. |
207 | | pub world_to_camera: Option<Matrix4x4>, |
208 | | |
209 | | /// The matrix that transforms 3D points from the world to the "Normalized Device Coordinate" space. |
210 | | /// Left-handed coordinate system, y up, z forward. |
211 | | pub world_to_normalized_device: Option<Matrix4x4>, |
212 | | |
213 | | /// Specifies whether the pixels in a deep image are sorted and non-overlapping. |
214 | | pub deep_image_state: Option<Rational>, |
215 | | |
216 | | /// If the image was cropped, contains the original data window. |
217 | | pub original_data_window: Option<IntegerBounds>, |
218 | | |
219 | | /// An 8-bit rgba image representing the rendered image. |
220 | | pub preview: Option<Preview>, |
221 | | |
222 | | /// Name of the view, which is typically either `"right"` or `"left"` for a stereoscopic image. |
223 | | pub view_name: Option<Text>, |
224 | | |
225 | | /// The name of the software that produced this image. |
226 | | pub software_name: Option<Text>, |
227 | | |
228 | | /// The near clip plane of the virtual camera projection. |
229 | | pub near_clip_plane: Option<f32>, |
230 | | |
231 | | /// The far clip plane of the virtual camera projection. |
232 | | pub far_clip_plane: Option<f32>, |
233 | | |
234 | | /// The field of view angle, along the horizontal axis, in degrees. |
235 | | pub horizontal_field_of_view: Option<f32>, |
236 | | |
237 | | /// The field of view angle, along the horizontal axis, in degrees. |
238 | | pub vertical_field_of_view: Option<f32>, |
239 | | |
240 | | /// Contains custom attributes. |
241 | | /// Does not contain the attributes already present in the `Header` or `LayerAttributes` struct. |
242 | | /// Does not contain attributes that are standardized to be the same for all layers: no chromaticities and no time codes. |
243 | | pub other: HashMap<Text, AttributeValue>, |
244 | | } |
245 | | |
246 | | |
247 | | impl LayerAttributes { |
248 | | |
249 | | /// Create default layer attributes with a data position of zero. |
250 | 0 | pub fn named(layer_name: impl Into<Text>) -> Self { |
251 | 0 | Self { |
252 | 0 | layer_name: Some(layer_name.into()), |
253 | 0 | .. Self::default() |
254 | 0 | } |
255 | 0 | } |
256 | | |
257 | | /// Set the data position of this layer. |
258 | 0 | pub fn with_position(self, data_position: Vec2<i32>) -> Self { |
259 | 0 | Self { layer_position: data_position, ..self } |
260 | 0 | } |
261 | | |
262 | | /// Set all common camera projection attributes at once. |
263 | 0 | pub fn with_camera_frustum( |
264 | 0 | self, |
265 | 0 | world_to_camera: Matrix4x4, |
266 | 0 | world_to_normalized_device: Matrix4x4, |
267 | 0 | field_of_view: impl Into<Vec2<f32>>, |
268 | 0 | depth_clip_range: std::ops::Range<f32>, |
269 | 0 | ) -> Self |
270 | | { |
271 | 0 | let fov = field_of_view.into(); |
272 | | |
273 | 0 | Self { |
274 | 0 | world_to_normalized_device: Some(world_to_normalized_device), |
275 | 0 | world_to_camera: Some(world_to_camera), |
276 | 0 | horizontal_field_of_view: Some(fov.x()), |
277 | 0 | vertical_field_of_view: Some(fov.y()), |
278 | 0 | near_clip_plane: Some(depth_clip_range.start), |
279 | 0 | far_clip_plane: Some(depth_clip_range.end), |
280 | 0 | ..self |
281 | 0 | } |
282 | 0 | } |
283 | | } |
284 | | |
285 | | impl ImageAttributes { |
286 | | |
287 | | /// Set the display position and size of this image. |
288 | 43.4k | pub fn new(display_window: IntegerBounds) -> Self { |
289 | 43.4k | Self { |
290 | 43.4k | pixel_aspect: 1.0, |
291 | 43.4k | chromaticities: None, |
292 | 43.4k | time_code: None, |
293 | 43.4k | other: Default::default(), |
294 | 43.4k | display_window, |
295 | 43.4k | } |
296 | 43.4k | } |
297 | | |
298 | | /// Set the display position to zero and use the specified size for this image. |
299 | 0 | pub fn with_size(size: impl Into<Vec2<usize>>) -> Self { |
300 | 0 | Self::new(IntegerBounds::from_dimensions(size)) |
301 | 0 | } |
302 | | } |
303 | | |
304 | | |
305 | | |
306 | | |
307 | | impl Header { |
308 | | |
309 | | /// Create a new Header with the specified name, display window and channels. |
310 | | /// Use `Header::with_encoding` and the similar methods to add further properties to the header. |
311 | | /// |
312 | | /// The other settings are left to their default values: |
313 | | /// - RLE compression |
314 | | /// - display window equal to data window |
315 | | /// - tiles (64 x 64 px) |
316 | | /// - unspecified line order |
317 | | /// - no custom attributes |
318 | 0 | pub fn new(name: Text, data_size: impl Into<Vec2<usize>>, channels: SmallVec<[ChannelDescription; 5]>) -> Self { |
319 | 0 | let data_size: Vec2<usize> = data_size.into(); |
320 | | |
321 | 0 | let compression = Compression::RLE; |
322 | 0 | let blocks = BlockDescription::Tiles(TileDescription { |
323 | 0 | tile_size: Vec2(64, 64), |
324 | 0 | level_mode: LevelMode::Singular, |
325 | 0 | rounding_mode: RoundingMode::Down |
326 | 0 | }); |
327 | | |
328 | 0 | Self { |
329 | 0 | layer_size: data_size, |
330 | 0 | compression, |
331 | 0 | blocks, |
332 | 0 |
|
333 | 0 | channels: ChannelList::new(channels), |
334 | 0 | line_order: LineOrder::Unspecified, |
335 | 0 |
|
336 | 0 | shared_attributes: ImageAttributes::with_size(data_size), |
337 | 0 | own_attributes: LayerAttributes::named(name), |
338 | 0 |
|
339 | 0 | chunk_count: compute_chunk_count(compression, data_size, blocks), |
340 | 0 |
|
341 | 0 | deep: false, |
342 | 0 | deep_data_version: None, |
343 | 0 | max_samples_per_pixel: None, |
344 | 0 | } |
345 | 0 | } |
346 | | |
347 | | /// Set the display window, that is, the global clipping rectangle. |
348 | | /// __Must be the same for all headers of a file.__ |
349 | 0 | pub fn with_display_window(mut self, display_window: IntegerBounds) -> Self { |
350 | 0 | self.shared_attributes.display_window = display_window; |
351 | 0 | self |
352 | 0 | } |
353 | | |
354 | | /// Set the offset of this layer. |
355 | 0 | pub fn with_position(mut self, position: Vec2<i32>) -> Self { |
356 | 0 | self.own_attributes.layer_position = position; |
357 | 0 | self |
358 | 0 | } |
359 | | |
360 | | /// Set compression, tiling, and line order. Automatically computes chunk count. |
361 | 0 | pub fn with_encoding(self, compression: Compression, blocks: BlockDescription, line_order: LineOrder) -> Self { |
362 | 0 | Self { |
363 | 0 | chunk_count: compute_chunk_count(compression, self.layer_size, blocks), |
364 | 0 | compression, blocks, line_order, |
365 | 0 | .. self |
366 | 0 | } |
367 | 0 | } |
368 | | |
369 | | /// Set **all** attributes of the header that are not shared with all other headers in the image. |
370 | 0 | pub fn with_attributes(self, own_attributes: LayerAttributes) -> Self { |
371 | 0 | Self { own_attributes, .. self } |
372 | 0 | } |
373 | | |
374 | | /// Set **all** attributes of the header that are shared with all other headers in the image. |
375 | 0 | pub fn with_shared_attributes(self, shared_attributes: ImageAttributes) -> Self { |
376 | 0 | Self { shared_attributes, .. self } |
377 | 0 | } |
378 | | |
379 | | /// Iterate over all blocks, in the order specified by the headers line order attribute. |
380 | | /// Unspecified line order is treated as increasing line order. |
381 | | /// Also enumerates the index of each block in the header, as if it were sorted in increasing line order. |
382 | 81 | pub fn enumerate_ordered_blocks(&self) -> impl Iterator<Item=(usize, TileIndices)> + Send { |
383 | 81 | let increasing_y = self.blocks_increasing_y_order().enumerate(); |
384 | | |
385 | | // TODO without box? |
386 | 81 | let ordered: Box<dyn Send + Iterator<Item=(usize, TileIndices)>> = { |
387 | 81 | if self.line_order == LineOrder::Decreasing { Box::new(increasing_y.rev()) } |
388 | 81 | else { Box::new(increasing_y) } |
389 | | }; |
390 | | |
391 | 81 | ordered |
392 | 81 | } |
393 | | |
394 | | /*/// Iterate over all blocks, in the order specified by the headers line order attribute. |
395 | | /// Also includes an index of the block if it were `LineOrder::Increasing`, starting at zero for this header. |
396 | | pub fn enumerate_ordered_blocks(&self) -> impl Iterator<Item = (usize, TileIndices)> + Send { |
397 | | let increasing_y = self.blocks_increasing_y_order().enumerate(); |
398 | | |
399 | | let ordered: Box<dyn Send + Iterator<Item = (usize, TileIndices)>> = { |
400 | | if self.line_order == LineOrder::Decreasing { |
401 | | Box::new(increasing_y.rev()) // TODO without box? |
402 | | } |
403 | | else { |
404 | | Box::new(increasing_y) |
405 | | } |
406 | | }; |
407 | | |
408 | | ordered |
409 | | }*/ |
410 | | |
411 | | /// Iterate over all tile indices in this header in `LineOrder::Increasing` order. |
412 | 14.8k | pub fn blocks_increasing_y_order(&self) -> impl Iterator<Item = TileIndices> + ExactSizeIterator + DoubleEndedIterator { |
413 | 94.8k | fn tiles_of(image_size: Vec2<usize>, tile_size: Vec2<usize>, level_index: Vec2<usize>) -> impl Iterator<Item=TileIndices> { |
414 | 1.80M | fn divide_and_rest(total_size: usize, block_size: usize) -> impl Iterator<Item=(usize, usize)> { |
415 | 1.80M | let block_count = compute_block_count(total_size, block_size); |
416 | 1.80M | (0..block_count).map(move |block_index| ( |
417 | 4.72M | block_index, calculate_block_size(total_size, block_size, block_index).expect("block size calculation bug") |
418 | | )) |
419 | 1.80M | } |
420 | | |
421 | 1.71M | divide_and_rest(image_size.height(), tile_size.height()).flat_map(move |(y_index, tile_height)|{ |
422 | 3.00M | divide_and_rest(image_size.width(), tile_size.width()).map(move |(x_index, tile_width)|{ |
423 | 3.00M | TileIndices { |
424 | 3.00M | size: Vec2(tile_width, tile_height), |
425 | 3.00M | location: TileCoordinates { tile_index: Vec2(x_index, y_index), level_index, }, |
426 | 3.00M | } |
427 | 3.00M | }) |
428 | 1.71M | }) |
429 | 94.8k | } |
430 | | |
431 | 14.8k | let vec: Vec<TileIndices> = { |
432 | 14.8k | if let BlockDescription::Tiles(tiles) = self.blocks { |
433 | 4.65k | match tiles.level_mode { |
434 | | LevelMode::Singular => { |
435 | 1.96k | tiles_of(self.layer_size, tiles.tile_size, Vec2(0, 0)).collect() |
436 | | }, |
437 | | LevelMode::MipMap => { |
438 | 1.42k | mip_map_levels(tiles.rounding_mode, self.layer_size) |
439 | 25.9k | .flat_map(move |(level_index, level_size)|{ |
440 | 25.9k | tiles_of(level_size, tiles.tile_size, Vec2(level_index, level_index)) |
441 | 25.9k | }) |
442 | 1.42k | .collect() |
443 | | }, |
444 | | LevelMode::RipMap => { |
445 | 1.26k | rip_map_levels(tiles.rounding_mode, self.layer_size) |
446 | 56.7k | .flat_map(move |(level_index, level_size)| { |
447 | 56.7k | tiles_of(level_size, tiles.tile_size, level_index) |
448 | 56.7k | }) |
449 | 1.26k | .collect() |
450 | | } |
451 | | } |
452 | | } |
453 | | else { |
454 | 10.2k | let tiles = Vec2(self.layer_size.0, self.compression.scan_lines_per_block()); |
455 | 10.2k | tiles_of(self.layer_size, tiles, Vec2(0, 0)).collect() |
456 | | } |
457 | | }; |
458 | | |
459 | 14.8k | vec.into_iter() // TODO without collect |
460 | 14.8k | } |
461 | | |
462 | | /* TODO |
463 | | /// The block indices of this header, ordered as they would appear in the file. |
464 | | pub fn ordered_block_indices<'s>(&'s self, layer_index: usize) -> impl 's + Iterator<Item=BlockIndex> { |
465 | | self.enumerate_ordered_blocks().map(|(chunk_index, tile)|{ |
466 | | let data_indices = self.get_absolute_block_pixel_coordinates(tile.location).expect("tile coordinate bug"); |
467 | | |
468 | | BlockIndex { |
469 | | layer: layer_index, |
470 | | level: tile.location.level_index, |
471 | | pixel_position: data_indices.position.to_usize("data indices start").expect("data index bug"), |
472 | | pixel_size: data_indices.size, |
473 | | } |
474 | | }) |
475 | | }*/ |
476 | | |
477 | | // TODO reuse this function everywhere |
478 | | /// The default pixel resolution of a single block (tile or scan line block). |
479 | | /// Not all blocks have this size, because they may be cutoff at the end of the image. |
480 | 1.93M | pub fn max_block_pixel_size(&self) -> Vec2<usize> { |
481 | 1.93M | match self.blocks { |
482 | 7.31k | BlockDescription::ScanLines => Vec2(self.layer_size.0, self.compression.scan_lines_per_block()), |
483 | 1.93M | BlockDescription::Tiles(tiles) => tiles.tile_size, |
484 | | } |
485 | 1.93M | } |
486 | | |
487 | | /// Calculate the position of a block in the global infinite 2D space of a file. May be negative. |
488 | 0 | pub fn get_block_data_window_pixel_coordinates(&self, tile: TileCoordinates) -> Result<IntegerBounds> { |
489 | 0 | let data = self.get_absolute_block_pixel_coordinates(tile)?; |
490 | 0 | Ok(data.with_origin(self.own_attributes.layer_position)) |
491 | 0 | } |
492 | | |
493 | | /// Calculate the pixel index rectangle inside this header. Is not negative. Starts at `0`. |
494 | 4.30M | pub fn get_absolute_block_pixel_coordinates(&self, tile: TileCoordinates) -> Result<IntegerBounds> { |
495 | 4.30M | if let BlockDescription::Tiles(tiles) = self.blocks { |
496 | 3.17M | let Vec2(data_width, data_height) = self.layer_size; |
497 | | |
498 | 3.17M | let data_width = compute_level_size(tiles.rounding_mode, data_width, tile.level_index.x()); |
499 | 3.17M | let data_height = compute_level_size(tiles.rounding_mode, data_height, tile.level_index.y()); |
500 | 3.17M | let absolute_tile_coordinates = tile.to_data_indices(tiles.tile_size, Vec2(data_width, data_height))?; |
501 | | |
502 | 3.17M | if absolute_tile_coordinates.position.x() as i64 >= data_width as i64 || absolute_tile_coordinates.position.y() as i64 >= data_height as i64 { |
503 | 0 | return Err(Error::invalid("data block tile index")) |
504 | 3.17M | } |
505 | | |
506 | 3.17M | Ok(absolute_tile_coordinates) |
507 | | } |
508 | | else { // this is a scanline image |
509 | 1.13M | debug_assert_eq!(tile.tile_index.0, 0, "block index calculation bug"); |
510 | | |
511 | 1.13M | let (y, height) = calculate_block_position_and_size( |
512 | 1.13M | self.layer_size.height(), |
513 | 1.13M | self.compression.scan_lines_per_block(), |
514 | 1.13M | tile.tile_index.y() |
515 | 1.87k | )?; |
516 | | |
517 | | Ok(IntegerBounds { |
518 | 1.13M | position: Vec2(0, usize_to_i32(y, "tile y")?), |
519 | 1.13M | size: Vec2(self.layer_size.width(), height) |
520 | | }) |
521 | | } |
522 | | |
523 | | // TODO deep data? |
524 | 4.30M | } |
525 | | |
526 | | /// Return the tile index, converting scan line block coordinates to tile indices. |
527 | | /// Starts at `0` and is not negative. |
528 | 653k | pub fn get_block_data_indices(&self, block: &CompressedBlock) -> Result<TileCoordinates> { |
529 | 653k | Ok(match block { |
530 | 643k | CompressedBlock::Tile(ref tile) => { |
531 | 643k | tile.coordinates |
532 | | }, |
533 | | |
534 | 9.35k | CompressedBlock::ScanLine(ref block) => { |
535 | 9.35k | let size = self.compression.scan_lines_per_block() as i32; |
536 | | |
537 | 9.35k | let diff = block.y_coordinate.checked_sub(self.own_attributes.layer_position.y()).ok_or(Error::invalid("invalid header"))?; |
538 | 9.33k | let y = diff.checked_div(size).ok_or(Error::invalid("invalid header"))?; |
539 | | |
540 | 9.33k | if y < 0 { |
541 | 146 | return Err(Error::invalid("scan block y coordinate")); |
542 | 9.19k | } |
543 | | |
544 | 9.19k | TileCoordinates { |
545 | 9.19k | tile_index: Vec2(0, y as usize), |
546 | 9.19k | level_index: Vec2(0, 0) |
547 | 9.19k | } |
548 | | }, |
549 | | |
550 | 0 | _ => return Err(Error::unsupported("deep data not supported yet")) |
551 | | }) |
552 | 653k | } |
553 | | |
554 | | /// Computes the absolute tile coordinate data indices, which start at `0`. |
555 | 0 | pub fn get_scan_line_block_tile_coordinates(&self, block_y_coordinate: i32) -> Result<TileCoordinates> { |
556 | 0 | let size = self.compression.scan_lines_per_block() as i32; |
557 | | |
558 | 0 | let diff = block_y_coordinate.checked_sub(self.own_attributes.layer_position.1).ok_or(Error::invalid("invalid header"))?; |
559 | 0 | let y = diff.checked_div(size).ok_or(Error::invalid("invalid header"))?; |
560 | | |
561 | 0 | if y < 0 { |
562 | 0 | return Err(Error::invalid("scan block y coordinate")); |
563 | 0 | } |
564 | | |
565 | 0 | Ok(TileCoordinates { |
566 | 0 | tile_index: Vec2(0, y as usize), |
567 | 0 | level_index: Vec2(0, 0) |
568 | 0 | }) |
569 | 0 | } |
570 | | |
571 | | /// Maximum byte length of an uncompressed or compressed block, used for validation. |
572 | 654k | pub fn max_block_byte_size(&self) -> usize { |
573 | 654k | self.channels.bytes_per_pixel * match self.blocks { |
574 | 645k | BlockDescription::Tiles(tiles) => tiles.tile_size.area(), |
575 | 9.87k | BlockDescription::ScanLines => self.compression.scan_lines_per_block() * self.layer_size.width() |
576 | | // TODO What about deep data??? |
577 | | } |
578 | 654k | } |
579 | | |
580 | | /// Returns the number of bytes that the pixels of this header will require |
581 | | /// when stored without compression. Respects multi-resolution levels and subsampling. |
582 | 0 | pub fn total_pixel_bytes(&self) -> usize { |
583 | 0 | assert!(!self.deep); |
584 | | |
585 | 0 | let pixel_count_of_levels = |size: Vec2<usize>| -> usize { |
586 | 0 | match self.blocks { |
587 | 0 | BlockDescription::ScanLines => size.area(), |
588 | 0 | BlockDescription::Tiles(tile_description) => match tile_description.level_mode { |
589 | 0 | LevelMode::Singular => size.area(), |
590 | | |
591 | 0 | LevelMode::MipMap => mip_map_levels(tile_description.rounding_mode, size) |
592 | 0 | .map(|(_, size)| size.area()).sum(), |
593 | | |
594 | 0 | LevelMode::RipMap => rip_map_levels(tile_description.rounding_mode, size) |
595 | 0 | .map(|(_, size)| size.area()).sum(), |
596 | | } |
597 | | } |
598 | 0 | }; |
599 | | |
600 | 0 | self.channels.list.iter() |
601 | 0 | .map(|channel: &ChannelDescription| |
602 | 0 | pixel_count_of_levels(channel.subsampled_resolution(self.layer_size)) * channel.sample_type.bytes_per_sample() |
603 | | ) |
604 | 0 | .sum() |
605 | | |
606 | 0 | } |
607 | | |
608 | | /// Approximates the maximum number of bytes that the pixels of this header will consume in a file. |
609 | | /// Due to compression, the actual byte size may be smaller. |
610 | 0 | pub fn max_pixel_file_bytes(&self) -> usize { |
611 | 0 | assert!(!self.deep); |
612 | | |
613 | 0 | self.chunk_count * 64 // at most 64 bytes overhead for each chunk (header index, tile description, chunk size, and more) |
614 | 0 | + self.total_pixel_bytes() |
615 | 0 | } |
616 | | |
617 | | /// Validate this instance. |
618 | 24.2k | pub fn validate(&self, is_multilayer: bool, long_names: &mut bool, strict: bool) -> UnitResult { |
619 | | |
620 | 24.2k | self.data_window().validate(None)?; |
621 | 24.2k | self.shared_attributes.display_window.validate(None)?; |
622 | | |
623 | 24.2k | if strict { |
624 | 81 | if is_multilayer { |
625 | 0 | if self.own_attributes.layer_name.is_none() { |
626 | 0 | return Err(missing_attribute("layer name for multi layer file")); |
627 | 0 | } |
628 | 81 | } |
629 | | |
630 | 81 | if self.blocks == BlockDescription::ScanLines && self.line_order == LineOrder::Unspecified { |
631 | 0 | return Err(Error::invalid("unspecified line order in scan line images")); |
632 | 81 | } |
633 | | |
634 | 81 | if self.layer_size == Vec2(0, 0) { |
635 | 0 | return Err(Error::invalid("empty data window")); |
636 | 81 | } |
637 | | |
638 | 81 | if self.shared_attributes.display_window.size == Vec2(0,0) { |
639 | 0 | return Err(Error::invalid("empty display window")); |
640 | 81 | } |
641 | | |
642 | 81 | if !self.shared_attributes.pixel_aspect.is_normal() || self.shared_attributes.pixel_aspect < 1.0e-6 || self.shared_attributes.pixel_aspect > 1.0e6 { |
643 | 0 | return Err(Error::invalid("pixel aspect ratio")); |
644 | 81 | } |
645 | | |
646 | 81 | if self.own_attributes.screen_window_width < 0.0 { |
647 | 0 | return Err(Error::invalid("screen window width")); |
648 | 81 | } |
649 | 24.1k | } |
650 | | |
651 | 24.2k | let allow_subsampling = !self.deep && self.blocks == BlockDescription::ScanLines; |
652 | 24.2k | self.channels.validate(allow_subsampling, self.data_window(), strict)?; |
653 | | |
654 | 30.6k | for (name, value) in &self.shared_attributes.other { |
655 | 6.55k | attribute::validate(name, value, long_names, allow_subsampling, self.data_window(), strict)?; |
656 | | } |
657 | | |
658 | 349k | for (name, value) in &self.own_attributes.other { |
659 | 325k | attribute::validate(name, value, long_names, allow_subsampling, self.data_window(), strict)?; |
660 | | } |
661 | | |
662 | | // this is only to check whether someone tampered with our precious values, to avoid writing an invalid file |
663 | 24.0k | if self.chunk_count != compute_chunk_count(self.compression, self.layer_size, self.blocks) { |
664 | 0 | return Err(Error::invalid("chunk count attribute")); // TODO this may be an expensive check? |
665 | 24.0k | } |
666 | | |
667 | | // check if attribute names appear twice |
668 | 24.0k | if strict { |
669 | 81 | for (name, _) in &self.shared_attributes.other { |
670 | 0 | if self.own_attributes.other.contains_key(name) { |
671 | 0 | return Err(Error::invalid(format!("duplicate attribute name: `{}`", name))); |
672 | 0 | } |
673 | | } |
674 | | |
675 | 3.96k | for &reserved in header::standard_names::ALL.iter() { |
676 | 3.96k | let name = Text::from_bytes_unchecked(SmallVec::from_slice(reserved)); |
677 | 3.96k | if self.own_attributes.other.contains_key(&name) || self.shared_attributes.other.contains_key(&name) { |
678 | 0 | return Err(Error::invalid(format!( |
679 | 0 | "attribute name `{}` is reserved and cannot be custom", |
680 | 0 | Text::from_bytes_unchecked(reserved.into()) |
681 | 0 | ))); |
682 | 3.96k | } |
683 | | } |
684 | 24.0k | } |
685 | | |
686 | 24.0k | if self.deep { |
687 | 0 | if strict { |
688 | 0 | if self.own_attributes.layer_name.is_none() { |
689 | 0 | return Err(missing_attribute("layer name for deep file")); |
690 | 0 | } |
691 | | |
692 | 0 | if self.max_samples_per_pixel.is_none() { |
693 | 0 | return Err(Error::invalid("missing max samples per pixel attribute for deepdata")); |
694 | 0 | } |
695 | 0 | } |
696 | | |
697 | 0 | match self.deep_data_version { |
698 | 0 | Some(1) => {}, |
699 | 0 | Some(_) => return Err(Error::unsupported("deep data version")), |
700 | 0 | None => return Err(missing_attribute("deep data version")), |
701 | | } |
702 | | |
703 | 0 | if !self.compression.supports_deep_data() { |
704 | 0 | return Err(Error::invalid("compression method does not support deep data")); |
705 | 0 | } |
706 | 24.0k | } |
707 | | |
708 | 24.0k | Ok(()) |
709 | 24.2k | } |
710 | | |
711 | | /// Read the headers without validating them. |
712 | 8.22k | pub fn read_all(read: &mut PeekRead<impl Read>, version: &Requirements, pedantic: bool) -> Result<Headers> { |
713 | 8.22k | if !version.is_multilayer() { |
714 | 7.05k | Ok(smallvec![ Header::read(read, version, pedantic)? ]) |
715 | | } |
716 | | else { |
717 | 1.17k | let mut headers = SmallVec::new(); |
718 | | |
719 | 36.9k | while !sequence_end::has_come(read)? { |
720 | 36.2k | headers.push(Header::read(read, version, pedantic)?); |
721 | | } |
722 | | |
723 | 677 | Ok(headers) |
724 | | } |
725 | 8.22k | } <exr::meta::header::Header>::read_all::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Line | Count | Source | 712 | 8.14k | pub fn read_all(read: &mut PeekRead<impl Read>, version: &Requirements, pedantic: bool) -> Result<Headers> { | 713 | 8.14k | if !version.is_multilayer() { | 714 | 6.97k | Ok(smallvec![ Header::read(read, version, pedantic)? ]) | 715 | | } | 716 | | else { | 717 | 1.17k | let mut headers = SmallVec::new(); | 718 | | | 719 | 36.9k | while !sequence_end::has_come(read)? { | 720 | 36.2k | headers.push(Header::read(read, version, pedantic)?); | 721 | | } | 722 | | | 723 | 677 | Ok(headers) | 724 | | } | 725 | 8.14k | } |
Unexecuted instantiation: <exr::meta::header::Header>::read_all::<_> <exr::meta::header::Header>::read_all::<exr::io::Tracking<std::io::cursor::Cursor<alloc::vec::Vec<u8>>>> Line | Count | Source | 712 | 81 | pub fn read_all(read: &mut PeekRead<impl Read>, version: &Requirements, pedantic: bool) -> Result<Headers> { | 713 | 81 | if !version.is_multilayer() { | 714 | 81 | Ok(smallvec![ Header::read(read, version, pedantic)? ]) | 715 | | } | 716 | | else { | 717 | 0 | let mut headers = SmallVec::new(); | 718 | | | 719 | 0 | while !sequence_end::has_come(read)? { | 720 | 0 | headers.push(Header::read(read, version, pedantic)?); | 721 | | } | 722 | | | 723 | 0 | Ok(headers) | 724 | | } | 725 | 81 | } |
|
726 | | |
727 | | /// Without validation, write the headers to the byte stream. |
728 | 81 | pub fn write_all(headers: &[Header], write: &mut impl Write, is_multilayer: bool) -> UnitResult { |
729 | 162 | for header in headers { |
730 | 81 | header.write(write)?; |
731 | | } |
732 | | |
733 | 81 | if is_multilayer { |
734 | 0 | sequence_end::write(write)?; |
735 | 81 | } |
736 | | |
737 | 81 | Ok(()) |
738 | 81 | } Unexecuted instantiation: <exr::meta::header::Header>::write_all::<_> Unexecuted instantiation: <exr::meta::header::Header>::write_all::<exr::io::Tracking<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>> <exr::meta::header::Header>::write_all::<exr::io::Tracking<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>> Line | Count | Source | 728 | 81 | pub fn write_all(headers: &[Header], write: &mut impl Write, is_multilayer: bool) -> UnitResult { | 729 | 162 | for header in headers { | 730 | 81 | header.write(write)?; | 731 | | } | 732 | | | 733 | 81 | if is_multilayer { | 734 | 0 | sequence_end::write(write)?; | 735 | 81 | } | 736 | | | 737 | 81 | Ok(()) | 738 | 81 | } |
|
739 | | |
740 | | /// Iterate over all `(name, attribute_value)` pairs in this header that would be written to a file. |
741 | | /// The order of attributes is arbitrary and may change in future versions. |
742 | | /// Will always contain all strictly required attributes, such as channels, compression, data window, and similar. |
743 | | /// Hint: Use `attribute.kind_name()` to obtain the standardized name of the attribute type. |
744 | | /// Does not validate the header or attributes. |
745 | | // This function is used for writing the attributes to files. |
746 | | #[inline] |
747 | 81 | pub fn all_named_attributes(&self) -> impl '_ + Iterator<Item=(&TextSlice, AttributeValue)> { |
748 | | use std::iter::{once, once_with, empty}; |
749 | | use crate::meta::header::standard_names::*; |
750 | | use AttributeValue::*; |
751 | | |
752 | 2.99k | #[inline] fn optional<'t, T: Clone>( |
753 | 2.99k | name: &'t TextSlice, |
754 | 2.99k | to_attribute: impl Fn(T) -> AttributeValue, |
755 | 2.99k | value: &'t Option<T> |
756 | 2.99k | ) |
757 | 2.99k | -> impl Iterator<Item=(&'t TextSlice, AttributeValue)> |
758 | | { |
759 | 2.99k | value.as_ref().map(move |value| (name, to_attribute(value.clone()))).into_iter() Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<_, _>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<[f32; 16], exr::meta::attribute::AttributeValue::Matrix4x4>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::math::Vec2<f32>, exr::meta::attribute::AttributeValue::FloatVec2>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<alloc::vec::Vec<exr::meta::attribute::Text>, exr::meta::attribute::AttributeValue::TextVector>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::IntegerBounds, exr::meta::attribute::AttributeValue::IntegerBounds>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::Chromaticities, exr::meta::attribute::AttributeValue::Chromaticities>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::EnvironmentMap, exr::meta::attribute::AttributeValue::EnvironmentMap>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::Text, exr::meta::attribute::AttributeValue::Text>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::KeyCode, exr::meta::attribute::AttributeValue::KeyCode>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::Preview, exr::meta::attribute::AttributeValue::Preview>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::TimeCode, exr::meta::attribute::AttributeValue::TimeCode>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<(i32, u32), exr::meta::attribute::AttributeValue::Rational>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<f32, exr::meta::attribute::AttributeValue::F32>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<usize, <exr::meta::header::Header>::all_named_attributes::usize_as_i32>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<i32, exr::meta::attribute::AttributeValue::I32>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<[f32; 16], exr::meta::attribute::AttributeValue::Matrix4x4>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::math::Vec2<f32>, exr::meta::attribute::AttributeValue::FloatVec2>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<alloc::vec::Vec<exr::meta::attribute::Text>, exr::meta::attribute::AttributeValue::TextVector>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::IntegerBounds, exr::meta::attribute::AttributeValue::IntegerBounds>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::Chromaticities, exr::meta::attribute::AttributeValue::Chromaticities>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::EnvironmentMap, exr::meta::attribute::AttributeValue::EnvironmentMap>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::Text, exr::meta::attribute::AttributeValue::Text>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::KeyCode, exr::meta::attribute::AttributeValue::KeyCode>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::Preview, exr::meta::attribute::AttributeValue::Preview>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::TimeCode, exr::meta::attribute::AttributeValue::TimeCode>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<(i32, u32), exr::meta::attribute::AttributeValue::Rational>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<f32, exr::meta::attribute::AttributeValue::F32>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<usize, <exr::meta::header::Header>::all_named_attributes::usize_as_i32>::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<i32, exr::meta::attribute::AttributeValue::I32>::{closure#0} |
760 | 2.99k | } Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<_, _> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<[f32; 16], exr::meta::attribute::AttributeValue::Matrix4x4> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::math::Vec2<f32>, exr::meta::attribute::AttributeValue::FloatVec2> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<alloc::vec::Vec<exr::meta::attribute::Text>, exr::meta::attribute::AttributeValue::TextVector> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::IntegerBounds, exr::meta::attribute::AttributeValue::IntegerBounds> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::Chromaticities, exr::meta::attribute::AttributeValue::Chromaticities> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::EnvironmentMap, exr::meta::attribute::AttributeValue::EnvironmentMap> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::Text, exr::meta::attribute::AttributeValue::Text> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::KeyCode, exr::meta::attribute::AttributeValue::KeyCode> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::Preview, exr::meta::attribute::AttributeValue::Preview> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::TimeCode, exr::meta::attribute::AttributeValue::TimeCode> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<(i32, u32), exr::meta::attribute::AttributeValue::Rational> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<f32, exr::meta::attribute::AttributeValue::F32> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<usize, <exr::meta::header::Header>::all_named_attributes::usize_as_i32> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::optional::<i32, exr::meta::attribute::AttributeValue::I32> <exr::meta::header::Header>::all_named_attributes::optional::<[f32; 16], exr::meta::attribute::AttributeValue::Matrix4x4> Line | Count | Source | 752 | 162 | #[inline] fn optional<'t, T: Clone>( | 753 | 162 | name: &'t TextSlice, | 754 | 162 | to_attribute: impl Fn(T) -> AttributeValue, | 755 | 162 | value: &'t Option<T> | 756 | 162 | ) | 757 | 162 | -> impl Iterator<Item=(&'t TextSlice, AttributeValue)> | 758 | | { | 759 | 162 | value.as_ref().map(move |value| (name, to_attribute(value.clone()))).into_iter() | 760 | 162 | } |
<exr::meta::header::Header>::all_named_attributes::optional::<exr::math::Vec2<f32>, exr::meta::attribute::AttributeValue::FloatVec2> Line | Count | Source | 752 | 81 | #[inline] fn optional<'t, T: Clone>( | 753 | 81 | name: &'t TextSlice, | 754 | 81 | to_attribute: impl Fn(T) -> AttributeValue, | 755 | 81 | value: &'t Option<T> | 756 | 81 | ) | 757 | 81 | -> impl Iterator<Item=(&'t TextSlice, AttributeValue)> | 758 | | { | 759 | 81 | value.as_ref().map(move |value| (name, to_attribute(value.clone()))).into_iter() | 760 | 81 | } |
<exr::meta::header::Header>::all_named_attributes::optional::<alloc::vec::Vec<exr::meta::attribute::Text>, exr::meta::attribute::AttributeValue::TextVector> Line | Count | Source | 752 | 81 | #[inline] fn optional<'t, T: Clone>( | 753 | 81 | name: &'t TextSlice, | 754 | 81 | to_attribute: impl Fn(T) -> AttributeValue, | 755 | 81 | value: &'t Option<T> | 756 | 81 | ) | 757 | 81 | -> impl Iterator<Item=(&'t TextSlice, AttributeValue)> | 758 | | { | 759 | 81 | value.as_ref().map(move |value| (name, to_attribute(value.clone()))).into_iter() | 760 | 81 | } |
<exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::IntegerBounds, exr::meta::attribute::AttributeValue::IntegerBounds> Line | Count | Source | 752 | 81 | #[inline] fn optional<'t, T: Clone>( | 753 | 81 | name: &'t TextSlice, | 754 | 81 | to_attribute: impl Fn(T) -> AttributeValue, | 755 | 81 | value: &'t Option<T> | 756 | 81 | ) | 757 | 81 | -> impl Iterator<Item=(&'t TextSlice, AttributeValue)> | 758 | | { | 759 | 81 | value.as_ref().map(move |value| (name, to_attribute(value.clone()))).into_iter() | 760 | 81 | } |
<exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::Chromaticities, exr::meta::attribute::AttributeValue::Chromaticities> Line | Count | Source | 752 | 81 | #[inline] fn optional<'t, T: Clone>( | 753 | 81 | name: &'t TextSlice, | 754 | 81 | to_attribute: impl Fn(T) -> AttributeValue, | 755 | 81 | value: &'t Option<T> | 756 | 81 | ) | 757 | 81 | -> impl Iterator<Item=(&'t TextSlice, AttributeValue)> | 758 | | { | 759 | 81 | value.as_ref().map(move |value| (name, to_attribute(value.clone()))).into_iter() | 760 | 81 | } |
<exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::EnvironmentMap, exr::meta::attribute::AttributeValue::EnvironmentMap> Line | Count | Source | 752 | 81 | #[inline] fn optional<'t, T: Clone>( | 753 | 81 | name: &'t TextSlice, | 754 | 81 | to_attribute: impl Fn(T) -> AttributeValue, | 755 | 81 | value: &'t Option<T> | 756 | 81 | ) | 757 | 81 | -> impl Iterator<Item=(&'t TextSlice, AttributeValue)> | 758 | | { | 759 | 81 | value.as_ref().map(move |value| (name, to_attribute(value.clone()))).into_iter() | 760 | 81 | } |
<exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::Text, exr::meta::attribute::AttributeValue::Text> Line | Count | Source | 752 | 729 | #[inline] fn optional<'t, T: Clone>( | 753 | 729 | name: &'t TextSlice, | 754 | 729 | to_attribute: impl Fn(T) -> AttributeValue, | 755 | 729 | value: &'t Option<T> | 756 | 729 | ) | 757 | 729 | -> impl Iterator<Item=(&'t TextSlice, AttributeValue)> | 758 | | { | 759 | 729 | value.as_ref().map(move |value| (name, to_attribute(value.clone()))).into_iter() | 760 | 729 | } |
<exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::KeyCode, exr::meta::attribute::AttributeValue::KeyCode> Line | Count | Source | 752 | 81 | #[inline] fn optional<'t, T: Clone>( | 753 | 81 | name: &'t TextSlice, | 754 | 81 | to_attribute: impl Fn(T) -> AttributeValue, | 755 | 81 | value: &'t Option<T> | 756 | 81 | ) | 757 | 81 | -> impl Iterator<Item=(&'t TextSlice, AttributeValue)> | 758 | | { | 759 | 81 | value.as_ref().map(move |value| (name, to_attribute(value.clone()))).into_iter() | 760 | 81 | } |
<exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::Preview, exr::meta::attribute::AttributeValue::Preview> Line | Count | Source | 752 | 81 | #[inline] fn optional<'t, T: Clone>( | 753 | 81 | name: &'t TextSlice, | 754 | 81 | to_attribute: impl Fn(T) -> AttributeValue, | 755 | 81 | value: &'t Option<T> | 756 | 81 | ) | 757 | 81 | -> impl Iterator<Item=(&'t TextSlice, AttributeValue)> | 758 | | { | 759 | 81 | value.as_ref().map(move |value| (name, to_attribute(value.clone()))).into_iter() | 760 | 81 | } |
<exr::meta::header::Header>::all_named_attributes::optional::<exr::meta::attribute::TimeCode, exr::meta::attribute::AttributeValue::TimeCode> Line | Count | Source | 752 | 81 | #[inline] fn optional<'t, T: Clone>( | 753 | 81 | name: &'t TextSlice, | 754 | 81 | to_attribute: impl Fn(T) -> AttributeValue, | 755 | 81 | value: &'t Option<T> | 756 | 81 | ) | 757 | 81 | -> impl Iterator<Item=(&'t TextSlice, AttributeValue)> | 758 | | { | 759 | 81 | value.as_ref().map(move |value| (name, to_attribute(value.clone()))).into_iter() | 760 | 81 | } |
<exr::meta::header::Header>::all_named_attributes::optional::<(i32, u32), exr::meta::attribute::AttributeValue::Rational> Line | Count | Source | 752 | 162 | #[inline] fn optional<'t, T: Clone>( | 753 | 162 | name: &'t TextSlice, | 754 | 162 | to_attribute: impl Fn(T) -> AttributeValue, | 755 | 162 | value: &'t Option<T> | 756 | 162 | ) | 757 | 162 | -> impl Iterator<Item=(&'t TextSlice, AttributeValue)> | 758 | | { | 759 | 162 | value.as_ref().map(move |value| (name, to_attribute(value.clone()))).into_iter() | 760 | 162 | } |
<exr::meta::header::Header>::all_named_attributes::optional::<f32, exr::meta::attribute::AttributeValue::F32> Line | Count | Source | 752 | 1.13k | #[inline] fn optional<'t, T: Clone>( | 753 | 1.13k | name: &'t TextSlice, | 754 | 1.13k | to_attribute: impl Fn(T) -> AttributeValue, | 755 | 1.13k | value: &'t Option<T> | 756 | 1.13k | ) | 757 | 1.13k | -> impl Iterator<Item=(&'t TextSlice, AttributeValue)> | 758 | | { | 759 | 1.13k | value.as_ref().map(move |value| (name, to_attribute(value.clone()))).into_iter() | 760 | 1.13k | } |
<exr::meta::header::Header>::all_named_attributes::optional::<usize, <exr::meta::header::Header>::all_named_attributes::usize_as_i32> Line | Count | Source | 752 | 81 | #[inline] fn optional<'t, T: Clone>( | 753 | 81 | name: &'t TextSlice, | 754 | 81 | to_attribute: impl Fn(T) -> AttributeValue, | 755 | 81 | value: &'t Option<T> | 756 | 81 | ) | 757 | 81 | -> impl Iterator<Item=(&'t TextSlice, AttributeValue)> | 758 | | { | 759 | 81 | value.as_ref().map(move |value| (name, to_attribute(value.clone()))).into_iter() | 760 | 81 | } |
<exr::meta::header::Header>::all_named_attributes::optional::<i32, exr::meta::attribute::AttributeValue::I32> Line | Count | Source | 752 | 81 | #[inline] fn optional<'t, T: Clone>( | 753 | 81 | name: &'t TextSlice, | 754 | 81 | to_attribute: impl Fn(T) -> AttributeValue, | 755 | 81 | value: &'t Option<T> | 756 | 81 | ) | 757 | 81 | -> impl Iterator<Item=(&'t TextSlice, AttributeValue)> | 758 | | { | 759 | 81 | value.as_ref().map(move |value| (name, to_attribute(value.clone()))).into_iter() | 760 | 81 | } |
|
761 | | |
762 | 648 | #[inline] fn required<'s, T: Clone>(name: &'s TextSlice, to_attribute: impl Fn(T) -> AttributeValue, value: &'s T) |
763 | 648 | -> impl Iterator<Item=(&'s TextSlice, AttributeValue)> |
764 | | { |
765 | 648 | once((name, to_attribute((*value).clone()))) |
766 | 648 | } Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::required::<_, _> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::required::<exr::math::Vec2<f32>, exr::meta::attribute::AttributeValue::FloatVec2> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::required::<exr::meta::attribute::ChannelList, exr::meta::attribute::AttributeValue::ChannelList> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::required::<exr::meta::attribute::IntegerBounds, exr::meta::attribute::AttributeValue::IntegerBounds> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::required::<exr::meta::attribute::LineOrder, exr::meta::attribute::AttributeValue::LineOrder> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::required::<exr::compression::Compression, exr::meta::attribute::AttributeValue::Compression> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::required::<f32, exr::meta::attribute::AttributeValue::F32> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::required::<usize, <exr::meta::header::Header>::all_named_attributes::usize_as_i32> <exr::meta::header::Header>::all_named_attributes::required::<exr::math::Vec2<f32>, exr::meta::attribute::AttributeValue::FloatVec2> Line | Count | Source | 762 | 81 | #[inline] fn required<'s, T: Clone>(name: &'s TextSlice, to_attribute: impl Fn(T) -> AttributeValue, value: &'s T) | 763 | 81 | -> impl Iterator<Item=(&'s TextSlice, AttributeValue)> | 764 | | { | 765 | 81 | once((name, to_attribute((*value).clone()))) | 766 | 81 | } |
<exr::meta::header::Header>::all_named_attributes::required::<exr::meta::attribute::ChannelList, exr::meta::attribute::AttributeValue::ChannelList> Line | Count | Source | 762 | 81 | #[inline] fn required<'s, T: Clone>(name: &'s TextSlice, to_attribute: impl Fn(T) -> AttributeValue, value: &'s T) | 763 | 81 | -> impl Iterator<Item=(&'s TextSlice, AttributeValue)> | 764 | | { | 765 | 81 | once((name, to_attribute((*value).clone()))) | 766 | 81 | } |
<exr::meta::header::Header>::all_named_attributes::required::<exr::meta::attribute::IntegerBounds, exr::meta::attribute::AttributeValue::IntegerBounds> Line | Count | Source | 762 | 81 | #[inline] fn required<'s, T: Clone>(name: &'s TextSlice, to_attribute: impl Fn(T) -> AttributeValue, value: &'s T) | 763 | 81 | -> impl Iterator<Item=(&'s TextSlice, AttributeValue)> | 764 | | { | 765 | 81 | once((name, to_attribute((*value).clone()))) | 766 | 81 | } |
<exr::meta::header::Header>::all_named_attributes::required::<exr::meta::attribute::LineOrder, exr::meta::attribute::AttributeValue::LineOrder> Line | Count | Source | 762 | 81 | #[inline] fn required<'s, T: Clone>(name: &'s TextSlice, to_attribute: impl Fn(T) -> AttributeValue, value: &'s T) | 763 | 81 | -> impl Iterator<Item=(&'s TextSlice, AttributeValue)> | 764 | | { | 765 | 81 | once((name, to_attribute((*value).clone()))) | 766 | 81 | } |
<exr::meta::header::Header>::all_named_attributes::required::<exr::compression::Compression, exr::meta::attribute::AttributeValue::Compression> Line | Count | Source | 762 | 81 | #[inline] fn required<'s, T: Clone>(name: &'s TextSlice, to_attribute: impl Fn(T) -> AttributeValue, value: &'s T) | 763 | 81 | -> impl Iterator<Item=(&'s TextSlice, AttributeValue)> | 764 | | { | 765 | 81 | once((name, to_attribute((*value).clone()))) | 766 | 81 | } |
<exr::meta::header::Header>::all_named_attributes::required::<f32, exr::meta::attribute::AttributeValue::F32> Line | Count | Source | 762 | 162 | #[inline] fn required<'s, T: Clone>(name: &'s TextSlice, to_attribute: impl Fn(T) -> AttributeValue, value: &'s T) | 763 | 162 | -> impl Iterator<Item=(&'s TextSlice, AttributeValue)> | 764 | | { | 765 | 162 | once((name, to_attribute((*value).clone()))) | 766 | 162 | } |
<exr::meta::header::Header>::all_named_attributes::required::<usize, <exr::meta::header::Header>::all_named_attributes::usize_as_i32> Line | Count | Source | 762 | 81 | #[inline] fn required<'s, T: Clone>(name: &'s TextSlice, to_attribute: impl Fn(T) -> AttributeValue, value: &'s T) | 763 | 81 | -> impl Iterator<Item=(&'s TextSlice, AttributeValue)> | 764 | | { | 765 | 81 | once((name, to_attribute((*value).clone()))) | 766 | 81 | } |
|
767 | | |
768 | | // used to type-check local variables. only requried because you cannot do `let i: impl Iterator<> = ...` |
769 | 486 | #[inline] fn expect_is_iter<'s, T: Iterator<Item=(&'s TextSlice, AttributeValue)>>(val: T) -> T { val }Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::expect_is_iter::<_> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::expect_is_iter::<core::iter::sources::once_with::OnceWith<<exr::meta::header::Header>::all_named_attributes::{closure#1}>>Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::expect_is_iter::<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::sources::empty::Empty<(&[u8], exr::meta::attribute::AttributeValue)>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::expect_is_iter::<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::sources::empty::Empty<(&[u8], exr::meta::attribute::AttributeValue)>, core::iter::sources::once::Once<(&[u8], exr::meta::attribute::AttributeValue)>>, core::iter::sources::once::Once<(&[u8], exr::meta::attribute::AttributeValue)>>, core::iter::sources::once::Once<(&[u8], exr::meta::attribute::AttributeValue)>>, core::iter::sources::once::Once<(&[u8], exr::meta::attribute::AttributeValue)>>, core::iter::sources::once::Once<(&[u8], exr::meta::attribute::AttributeValue)>>, core::iter::sources::once::Once<(&[u8], exr::meta::attribute::AttributeValue)>>, core::iter::sources::once::Once<(&[u8], exr::meta::attribute::AttributeValue)>>, core::iter::sources::once::Once<(&[u8], exr::meta::attribute::AttributeValue)>>> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::expect_is_iter::<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::sources::empty::Empty<(&[u8], exr::meta::attribute::AttributeValue)>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>> Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::expect_is_iter::<core::iter::adapters::flatten::Flatten<core::iter::sources::once_with::OnceWith<<exr::meta::header::Header>::all_named_attributes::{closure#0}>>>Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::expect_is_iter::<core::iter::adapters::flatten::Flatten<core::iter::sources::once_with::OnceWith<<exr::meta::header::Header>::all_named_attributes::{closure#2}>>><exr::meta::header::Header>::all_named_attributes::expect_is_iter::<core::iter::sources::once_with::OnceWith<<exr::meta::header::Header>::all_named_attributes::{closure#1}>>Line | Count | Source | 769 | 81 | #[inline] fn expect_is_iter<'s, T: Iterator<Item=(&'s TextSlice, AttributeValue)>>(val: T) -> T { val } |
<exr::meta::header::Header>::all_named_attributes::expect_is_iter::<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::sources::empty::Empty<(&[u8], exr::meta::attribute::AttributeValue)>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>> Line | Count | Source | 769 | 81 | #[inline] fn expect_is_iter<'s, T: Iterator<Item=(&'s TextSlice, AttributeValue)>>(val: T) -> T { val } |
<exr::meta::header::Header>::all_named_attributes::expect_is_iter::<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::sources::empty::Empty<(&[u8], exr::meta::attribute::AttributeValue)>, core::iter::sources::once::Once<(&[u8], exr::meta::attribute::AttributeValue)>>, core::iter::sources::once::Once<(&[u8], exr::meta::attribute::AttributeValue)>>, core::iter::sources::once::Once<(&[u8], exr::meta::attribute::AttributeValue)>>, core::iter::sources::once::Once<(&[u8], exr::meta::attribute::AttributeValue)>>, core::iter::sources::once::Once<(&[u8], exr::meta::attribute::AttributeValue)>>, core::iter::sources::once::Once<(&[u8], exr::meta::attribute::AttributeValue)>>, core::iter::sources::once::Once<(&[u8], exr::meta::attribute::AttributeValue)>>, core::iter::sources::once::Once<(&[u8], exr::meta::attribute::AttributeValue)>>> Line | Count | Source | 769 | 81 | #[inline] fn expect_is_iter<'s, T: Iterator<Item=(&'s TextSlice, AttributeValue)>>(val: T) -> T { val } |
<exr::meta::header::Header>::all_named_attributes::expect_is_iter::<core::iter::adapters::chain::Chain<core::iter::adapters::chain::Chain<core::iter::sources::empty::Empty<(&[u8], exr::meta::attribute::AttributeValue)>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>, core::option::IntoIter<(&[u8], exr::meta::attribute::AttributeValue)>>> Line | Count | Source | 769 | 81 | #[inline] fn expect_is_iter<'s, T: Iterator<Item=(&'s TextSlice, AttributeValue)>>(val: T) -> T { val } |
<exr::meta::header::Header>::all_named_attributes::expect_is_iter::<core::iter::adapters::flatten::Flatten<core::iter::sources::once_with::OnceWith<<exr::meta::header::Header>::all_named_attributes::{closure#0}>>>Line | Count | Source | 769 | 81 | #[inline] fn expect_is_iter<'s, T: Iterator<Item=(&'s TextSlice, AttributeValue)>>(val: T) -> T { val } |
<exr::meta::header::Header>::all_named_attributes::expect_is_iter::<core::iter::adapters::flatten::Flatten<core::iter::sources::once_with::OnceWith<<exr::meta::header::Header>::all_named_attributes::{closure#2}>>>Line | Count | Source | 769 | 81 | #[inline] fn expect_is_iter<'s, T: Iterator<Item=(&'s TextSlice, AttributeValue)>>(val: T) -> T { val } |
|
770 | | |
771 | | macro_rules! iter_all { |
772 | | ( $( $value:expr ),* ) => { |
773 | | empty() $( .chain( $value ) )* |
774 | | }; |
775 | | } |
776 | | |
777 | | macro_rules! required_attributes { |
778 | | ( $($name: ident : $variant: ident = $value: expr),* ) => { |
779 | | expect_is_iter(iter_all!( |
780 | | $( required($name, $variant, $value) ),* |
781 | | )) |
782 | | }; |
783 | | } |
784 | | |
785 | | macro_rules! optional_attributes { |
786 | | ( $($name: ident : $variant: ident = $value: expr),* ) => { |
787 | | expect_is_iter(iter_all!( |
788 | | $( optional($name, $variant, $value) ),* |
789 | | )) |
790 | | }; |
791 | | } |
792 | | |
793 | 81 | #[inline] fn usize_as_i32(value: usize) -> AttributeValue { |
794 | 81 | I32(i32::try_from(value).expect("usize exceeds i32 range")) |
795 | 81 | } Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::usize_as_i32 Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::usize_as_i32 <exr::meta::header::Header>::all_named_attributes::usize_as_i32 Line | Count | Source | 793 | 81 | #[inline] fn usize_as_i32(value: usize) -> AttributeValue { | 794 | 81 | I32(i32::try_from(value).expect("usize exceeds i32 range")) | 795 | 81 | } |
|
796 | | |
797 | | |
798 | 81 | let block_type_and_tiles = expect_is_iter(once_with(move ||{ |
799 | 81 | let (block_type, tiles) = match self.blocks { |
800 | 0 | BlockDescription::ScanLines => (attribute::BlockType::ScanLine, None), |
801 | 81 | BlockDescription::Tiles(tiles) => (attribute::BlockType::Tile, Some(tiles)) |
802 | | }; |
803 | | |
804 | 81 | once((BLOCK_TYPE, BlockType(block_type))) |
805 | 81 | .chain(tiles.map(|tiles| (TILES, TileDescription(tiles)))) Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#0}::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#0}::{closure#0}<exr::meta::header::Header>::all_named_attributes::{closure#0}::{closure#0}Line | Count | Source | 805 | 81 | .chain(tiles.map(|tiles| (TILES, TileDescription(tiles)))) |
|
806 | 81 | }).flatten()); Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#0}<exr::meta::header::Header>::all_named_attributes::{closure#0}Line | Count | Source | 798 | 81 | let block_type_and_tiles = expect_is_iter(once_with(move ||{ | 799 | 81 | let (block_type, tiles) = match self.blocks { | 800 | 0 | BlockDescription::ScanLines => (attribute::BlockType::ScanLine, None), | 801 | 81 | BlockDescription::Tiles(tiles) => (attribute::BlockType::Tile, Some(tiles)) | 802 | | }; | 803 | | | 804 | 81 | once((BLOCK_TYPE, BlockType(block_type))) | 805 | 81 | .chain(tiles.map(|tiles| (TILES, TileDescription(tiles)))) | 806 | 81 | }).flatten()); |
|
807 | | |
808 | 81 | let data_window = expect_is_iter(once_with(move ||{ |
809 | 81 | (DATA_WINDOW, IntegerBounds(self.data_window())) |
810 | 81 | })); Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#1}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#1}<exr::meta::header::Header>::all_named_attributes::{closure#1}Line | Count | Source | 808 | 81 | let data_window = expect_is_iter(once_with(move ||{ | 809 | 81 | (DATA_WINDOW, IntegerBounds(self.data_window())) | 810 | 81 | })); |
|
811 | | |
812 | | // dwa writes compression parameters as attribute. |
813 | 81 | let dwa_compr_level = expect_is_iter( |
814 | 81 | once_with(move ||{ |
815 | 0 | match self.compression { |
816 | 0 | attribute::Compression::DWAA(Some(level)) | |
817 | 0 | attribute::Compression::DWAB(Some(level)) => |
818 | 0 | Some((DWA_COMPRESSION_LEVEL, F32(level))), |
819 | | |
820 | 81 | _ => None |
821 | | } |
822 | 81 | }).flatten() Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#2}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#2}<exr::meta::header::Header>::all_named_attributes::{closure#2}Line | Count | Source | 814 | 81 | once_with(move ||{ | 815 | 0 | match self.compression { | 816 | 0 | attribute::Compression::DWAA(Some(level)) | | 817 | 0 | attribute::Compression::DWAB(Some(level)) => | 818 | 0 | Some((DWA_COMPRESSION_LEVEL, F32(level))), | 819 | | | 820 | 81 | _ => None | 821 | | } | 822 | 81 | }).flatten() |
|
823 | | ); |
824 | | |
825 | 81 | let opt_core_attrs = optional_attributes!( |
826 | 81 | DEEP_DATA_VERSION: I32 = &self.deep_data_version, |
827 | 81 | MAX_SAMPLES: usize_as_i32 = &self.max_samples_per_pixel |
828 | 81 | ).chain(block_type_and_tiles).chain(dwa_compr_level); |
829 | | |
830 | 81 | let req_core_attrs = required_attributes!( |
831 | | // chunks is not actually required, but always computed in this library anyways |
832 | 81 | CHUNKS: usize_as_i32 = &self.chunk_count, |
833 | | |
834 | 81 | CHANNELS: ChannelList = &self.channels, |
835 | 81 | COMPRESSION: Compression = &self.compression, |
836 | 81 | LINE_ORDER: LineOrder = &self.line_order, |
837 | | |
838 | 81 | DISPLAY_WINDOW: IntegerBounds = &self.shared_attributes.display_window, |
839 | 81 | PIXEL_ASPECT: F32 = &self.shared_attributes.pixel_aspect, |
840 | | |
841 | 81 | WINDOW_CENTER: FloatVec2 = &self.own_attributes.screen_window_center, |
842 | 81 | WINDOW_WIDTH: F32 = &self.own_attributes.screen_window_width |
843 | 81 | ).chain(data_window); |
844 | | |
845 | 81 | let opt_attr = optional_attributes!( |
846 | 81 | NAME: Text = &self.own_attributes.layer_name, |
847 | 81 | WHITE_LUMINANCE: F32 = &self.own_attributes.white_luminance, |
848 | 81 | ADOPTED_NEUTRAL: FloatVec2 = &self.own_attributes.adopted_neutral, |
849 | 81 | RENDERING_TRANSFORM: Text = &self.own_attributes.rendering_transform_name, |
850 | 81 | LOOK_MOD_TRANSFORM: Text = &self.own_attributes.look_modification_transform_name, |
851 | 81 | X_DENSITY: F32 = &self.own_attributes.horizontal_density, |
852 | 81 | OWNER: Text = &self.own_attributes.owner, |
853 | 81 | COMMENTS: Text = &self.own_attributes.comments, |
854 | 81 | CAPTURE_DATE: Text = &self.own_attributes.capture_date, |
855 | 81 | UTC_OFFSET: F32 = &self.own_attributes.utc_offset, |
856 | 81 | LONGITUDE: F32 = &self.own_attributes.longitude, |
857 | 81 | LATITUDE: F32 = &self.own_attributes.latitude, |
858 | 81 | ALTITUDE: F32 = &self.own_attributes.altitude, |
859 | 81 | FOCUS: F32 = &self.own_attributes.focus, |
860 | 81 | EXPOSURE_TIME: F32 = &self.own_attributes.exposure, |
861 | 81 | APERTURE: F32 = &self.own_attributes.aperture, |
862 | 81 | ISO_SPEED: F32 = &self.own_attributes.iso_speed, |
863 | 81 | ENVIRONMENT_MAP: EnvironmentMap = &self.own_attributes.environment_map, |
864 | 81 | KEY_CODE: KeyCode = &self.own_attributes.film_key_code, |
865 | 81 | TIME_CODE: TimeCode = &self.shared_attributes.time_code, |
866 | 81 | WRAP_MODES: Text = &self.own_attributes.wrap_mode_name, |
867 | 81 | FRAMES_PER_SECOND: Rational = &self.own_attributes.frames_per_second, |
868 | 81 | MULTI_VIEW: TextVector = &self.own_attributes.multi_view_names, |
869 | 81 | WORLD_TO_CAMERA: Matrix4x4 = &self.own_attributes.world_to_camera, |
870 | 81 | WORLD_TO_NDC: Matrix4x4 = &self.own_attributes.world_to_normalized_device, |
871 | 81 | DEEP_IMAGE_STATE: Rational = &self.own_attributes.deep_image_state, |
872 | 81 | ORIGINAL_DATA_WINDOW: IntegerBounds = &self.own_attributes.original_data_window, |
873 | 81 | CHROMATICITIES: Chromaticities = &self.shared_attributes.chromaticities, |
874 | 81 | PREVIEW: Preview = &self.own_attributes.preview, |
875 | 81 | VIEW: Text = &self.own_attributes.view_name, |
876 | 81 | NEAR: F32 = &self.own_attributes.near_clip_plane, |
877 | 81 | FAR: F32 = &self.own_attributes.far_clip_plane, |
878 | 81 | FOV_X: F32 = &self.own_attributes.horizontal_field_of_view, |
879 | 81 | FOV_Y: F32 = &self.own_attributes.vertical_field_of_view, |
880 | 81 | SOFTWARE: Text = &self.own_attributes.software_name |
881 | | ); |
882 | | |
883 | 81 | let other = self.own_attributes.other.iter() |
884 | 81 | .chain(self.shared_attributes.other.iter()) |
885 | 81 | .map(|(name, val)| (name.as_slice(), val.clone())); // TODO no clone Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#3}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#3}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#3} |
886 | | |
887 | 81 | req_core_attrs |
888 | 81 | .chain(opt_core_attrs) |
889 | 81 | .chain(opt_attr) |
890 | 81 | .chain(other) |
891 | 81 | } Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes <exr::meta::header::Header>::all_named_attributes Line | Count | Source | 747 | 81 | pub fn all_named_attributes(&self) -> impl '_ + Iterator<Item=(&TextSlice, AttributeValue)> { | 748 | | use std::iter::{once, once_with, empty}; | 749 | | use crate::meta::header::standard_names::*; | 750 | | use AttributeValue::*; | 751 | | | 752 | | #[inline] fn optional<'t, T: Clone>( | 753 | | name: &'t TextSlice, | 754 | | to_attribute: impl Fn(T) -> AttributeValue, | 755 | | value: &'t Option<T> | 756 | | ) | 757 | | -> impl Iterator<Item=(&'t TextSlice, AttributeValue)> | 758 | | { | 759 | | value.as_ref().map(move |value| (name, to_attribute(value.clone()))).into_iter() | 760 | | } | 761 | | | 762 | | #[inline] fn required<'s, T: Clone>(name: &'s TextSlice, to_attribute: impl Fn(T) -> AttributeValue, value: &'s T) | 763 | | -> impl Iterator<Item=(&'s TextSlice, AttributeValue)> | 764 | | { | 765 | | once((name, to_attribute((*value).clone()))) | 766 | | } | 767 | | | 768 | | // used to type-check local variables. only requried because you cannot do `let i: impl Iterator<> = ...` | 769 | | #[inline] fn expect_is_iter<'s, T: Iterator<Item=(&'s TextSlice, AttributeValue)>>(val: T) -> T { val } | 770 | | | 771 | | macro_rules! iter_all { | 772 | | ( $( $value:expr ),* ) => { | 773 | | empty() $( .chain( $value ) )* | 774 | | }; | 775 | | } | 776 | | | 777 | | macro_rules! required_attributes { | 778 | | ( $($name: ident : $variant: ident = $value: expr),* ) => { | 779 | | expect_is_iter(iter_all!( | 780 | | $( required($name, $variant, $value) ),* | 781 | | )) | 782 | | }; | 783 | | } | 784 | | | 785 | | macro_rules! optional_attributes { | 786 | | ( $($name: ident : $variant: ident = $value: expr),* ) => { | 787 | | expect_is_iter(iter_all!( | 788 | | $( optional($name, $variant, $value) ),* | 789 | | )) | 790 | | }; | 791 | | } | 792 | | | 793 | | #[inline] fn usize_as_i32(value: usize) -> AttributeValue { | 794 | | I32(i32::try_from(value).expect("usize exceeds i32 range")) | 795 | | } | 796 | | | 797 | | | 798 | 81 | let block_type_and_tiles = expect_is_iter(once_with(move ||{ | 799 | | let (block_type, tiles) = match self.blocks { | 800 | | BlockDescription::ScanLines => (attribute::BlockType::ScanLine, None), | 801 | | BlockDescription::Tiles(tiles) => (attribute::BlockType::Tile, Some(tiles)) | 802 | | }; | 803 | | | 804 | | once((BLOCK_TYPE, BlockType(block_type))) | 805 | | .chain(tiles.map(|tiles| (TILES, TileDescription(tiles)))) | 806 | 81 | }).flatten()); | 807 | | | 808 | 81 | let data_window = expect_is_iter(once_with(move ||{ | 809 | | (DATA_WINDOW, IntegerBounds(self.data_window())) | 810 | | })); | 811 | | | 812 | | // dwa writes compression parameters as attribute. | 813 | 81 | let dwa_compr_level = expect_is_iter( | 814 | 81 | once_with(move ||{ | 815 | | match self.compression { | 816 | | attribute::Compression::DWAA(Some(level)) | | 817 | | attribute::Compression::DWAB(Some(level)) => | 818 | | Some((DWA_COMPRESSION_LEVEL, F32(level))), | 819 | | | 820 | | _ => None | 821 | | } | 822 | 81 | }).flatten() | 823 | | ); | 824 | | | 825 | 81 | let opt_core_attrs = optional_attributes!( | 826 | 81 | DEEP_DATA_VERSION: I32 = &self.deep_data_version, | 827 | 81 | MAX_SAMPLES: usize_as_i32 = &self.max_samples_per_pixel | 828 | 81 | ).chain(block_type_and_tiles).chain(dwa_compr_level); | 829 | | | 830 | 81 | let req_core_attrs = required_attributes!( | 831 | | // chunks is not actually required, but always computed in this library anyways | 832 | 81 | CHUNKS: usize_as_i32 = &self.chunk_count, | 833 | | | 834 | 81 | CHANNELS: ChannelList = &self.channels, | 835 | 81 | COMPRESSION: Compression = &self.compression, | 836 | 81 | LINE_ORDER: LineOrder = &self.line_order, | 837 | | | 838 | 81 | DISPLAY_WINDOW: IntegerBounds = &self.shared_attributes.display_window, | 839 | 81 | PIXEL_ASPECT: F32 = &self.shared_attributes.pixel_aspect, | 840 | | | 841 | 81 | WINDOW_CENTER: FloatVec2 = &self.own_attributes.screen_window_center, | 842 | 81 | WINDOW_WIDTH: F32 = &self.own_attributes.screen_window_width | 843 | 81 | ).chain(data_window); | 844 | | | 845 | 81 | let opt_attr = optional_attributes!( | 846 | 81 | NAME: Text = &self.own_attributes.layer_name, | 847 | 81 | WHITE_LUMINANCE: F32 = &self.own_attributes.white_luminance, | 848 | 81 | ADOPTED_NEUTRAL: FloatVec2 = &self.own_attributes.adopted_neutral, | 849 | 81 | RENDERING_TRANSFORM: Text = &self.own_attributes.rendering_transform_name, | 850 | 81 | LOOK_MOD_TRANSFORM: Text = &self.own_attributes.look_modification_transform_name, | 851 | 81 | X_DENSITY: F32 = &self.own_attributes.horizontal_density, | 852 | 81 | OWNER: Text = &self.own_attributes.owner, | 853 | 81 | COMMENTS: Text = &self.own_attributes.comments, | 854 | 81 | CAPTURE_DATE: Text = &self.own_attributes.capture_date, | 855 | 81 | UTC_OFFSET: F32 = &self.own_attributes.utc_offset, | 856 | 81 | LONGITUDE: F32 = &self.own_attributes.longitude, | 857 | 81 | LATITUDE: F32 = &self.own_attributes.latitude, | 858 | 81 | ALTITUDE: F32 = &self.own_attributes.altitude, | 859 | 81 | FOCUS: F32 = &self.own_attributes.focus, | 860 | 81 | EXPOSURE_TIME: F32 = &self.own_attributes.exposure, | 861 | 81 | APERTURE: F32 = &self.own_attributes.aperture, | 862 | 81 | ISO_SPEED: F32 = &self.own_attributes.iso_speed, | 863 | 81 | ENVIRONMENT_MAP: EnvironmentMap = &self.own_attributes.environment_map, | 864 | 81 | KEY_CODE: KeyCode = &self.own_attributes.film_key_code, | 865 | 81 | TIME_CODE: TimeCode = &self.shared_attributes.time_code, | 866 | 81 | WRAP_MODES: Text = &self.own_attributes.wrap_mode_name, | 867 | 81 | FRAMES_PER_SECOND: Rational = &self.own_attributes.frames_per_second, | 868 | 81 | MULTI_VIEW: TextVector = &self.own_attributes.multi_view_names, | 869 | 81 | WORLD_TO_CAMERA: Matrix4x4 = &self.own_attributes.world_to_camera, | 870 | 81 | WORLD_TO_NDC: Matrix4x4 = &self.own_attributes.world_to_normalized_device, | 871 | 81 | DEEP_IMAGE_STATE: Rational = &self.own_attributes.deep_image_state, | 872 | 81 | ORIGINAL_DATA_WINDOW: IntegerBounds = &self.own_attributes.original_data_window, | 873 | 81 | CHROMATICITIES: Chromaticities = &self.shared_attributes.chromaticities, | 874 | 81 | PREVIEW: Preview = &self.own_attributes.preview, | 875 | 81 | VIEW: Text = &self.own_attributes.view_name, | 876 | 81 | NEAR: F32 = &self.own_attributes.near_clip_plane, | 877 | 81 | FAR: F32 = &self.own_attributes.far_clip_plane, | 878 | 81 | FOV_X: F32 = &self.own_attributes.horizontal_field_of_view, | 879 | 81 | FOV_Y: F32 = &self.own_attributes.vertical_field_of_view, | 880 | 81 | SOFTWARE: Text = &self.own_attributes.software_name | 881 | | ); | 882 | | | 883 | 81 | let other = self.own_attributes.other.iter() | 884 | 81 | .chain(self.shared_attributes.other.iter()) | 885 | 81 | .map(|(name, val)| (name.as_slice(), val.clone())); // TODO no clone | 886 | | | 887 | 81 | req_core_attrs | 888 | 81 | .chain(opt_core_attrs) | 889 | 81 | .chain(opt_attr) | 890 | 81 | .chain(other) | 891 | 81 | } |
|
892 | | |
893 | | /// Read the value without validating. |
894 | 43.3k | pub fn read(read: &mut PeekRead<impl Read>, requirements: &Requirements, pedantic: bool) -> Result<Self> { |
895 | 43.3k | let max_string_len = if requirements.has_long_names { 256 } else { 32 }; // TODO DRY this information |
896 | | |
897 | | // these required attributes will be filled when encountered while parsing |
898 | 43.3k | let mut tiles = None; |
899 | 43.3k | let mut block_type = None; |
900 | 43.3k | let mut version = None; |
901 | 43.3k | let mut chunk_count = None; |
902 | 43.3k | let mut max_samples_per_pixel = None; |
903 | 43.3k | let mut channels = None; |
904 | 43.3k | let mut compression = None; |
905 | 43.3k | let mut data_window = None; |
906 | 43.3k | let mut display_window = None; |
907 | 43.3k | let mut line_order = None; |
908 | 43.3k | let mut dwa_compression_level = None; |
909 | | |
910 | 43.3k | let mut layer_attributes = LayerAttributes::default(); |
911 | 43.3k | let mut image_attributes = ImageAttributes::new(IntegerBounds::zero()); |
912 | | |
913 | | // read each attribute in this header |
914 | 2.01M | while !sequence_end::has_come(read)? { |
915 | 1.97M | let (attribute_name, value) = attribute::read(read, max_string_len)?; |
916 | | |
917 | | // if the attribute value itself is ok, record it |
918 | 1.96M | match value { |
919 | 1.96M | Ok(value) => { |
920 | | use crate::meta::header::standard_names as name; |
921 | | use crate::meta::attribute::AttributeValue::*; |
922 | | |
923 | | // if the attribute is a required attribute, set the corresponding variable directly. |
924 | | // otherwise, add the attribute to the vector of custom attributes |
925 | | |
926 | | // the following attributes will only be set if the type matches the commonly used type for that attribute |
927 | 1.96M | match (attribute_name.as_slice(), value) { |
928 | 1.96M | (name::BLOCK_TYPE, Text(value)) => block_type = Some(attribute::BlockType::parse(value)?), |
929 | 1.88M | (name::TILES, TileDescription(value)) => tiles = Some(value), |
930 | 1.80M | (name::CHANNELS, ChannelList(value)) => channels = Some(value), |
931 | 1.62M | (name::COMPRESSION, Compression(value)) => compression = Some(value), |
932 | 1.52M | (name::DATA_WINDOW, IntegerBounds(value)) => data_window = Some(value), |
933 | 1.38M | (name::DISPLAY_WINDOW, IntegerBounds(value)) => display_window = Some(value), |
934 | 1.26M | (name::LINE_ORDER, LineOrder(value)) => line_order = Some(value), |
935 | 1.11M | (name::DEEP_DATA_VERSION, I32(value)) => version = Some(value), |
936 | | |
937 | 1.03M | (name::MAX_SAMPLES, I32(value)) => max_samples_per_pixel = Some( |
938 | 369 | i32_to_usize(value, "max sample count")? |
939 | | ), |
940 | | |
941 | 250 | (name::CHUNKS, I32(value)) => chunk_count = Some( |
942 | 250 | i32_to_usize(value, "chunk count")? |
943 | | ), |
944 | | |
945 | 11.8k | (name::NAME, Text(value)) => layer_attributes.layer_name = Some(value), |
946 | 1.24k | (name::WINDOW_CENTER, FloatVec2(value)) => layer_attributes.screen_window_center = value, |
947 | 919k | (name::WINDOW_WIDTH, F32(value)) => layer_attributes.screen_window_width = value, |
948 | | |
949 | 881k | (name::WHITE_LUMINANCE, F32(value)) => layer_attributes.white_luminance = Some(value), |
950 | 2.20k | (name::ADOPTED_NEUTRAL, FloatVec2(value)) => layer_attributes.adopted_neutral = Some(value), |
951 | 3.37k | (name::RENDERING_TRANSFORM, Text(value)) => layer_attributes.rendering_transform_name = Some(value), |
952 | 796k | (name::LOOK_MOD_TRANSFORM, Text(value)) => layer_attributes.look_modification_transform_name = Some(value), |
953 | 443 | (name::X_DENSITY, F32(value)) => layer_attributes.horizontal_density = Some(value), |
954 | | |
955 | 2.95k | (name::OWNER, Text(value)) => layer_attributes.owner = Some(value), |
956 | 886 | (name::COMMENTS, Text(value)) => layer_attributes.comments = Some(value), |
957 | 5.85k | (name::CAPTURE_DATE, Text(value)) => layer_attributes.capture_date = Some(value), |
958 | 85 | (name::UTC_OFFSET, F32(value)) => layer_attributes.utc_offset = Some(value), |
959 | 0 | (name::LONGITUDE, F32(value)) => layer_attributes.longitude = Some(value), |
960 | 5.16k | (name::LATITUDE, F32(value)) => layer_attributes.latitude = Some(value), |
961 | 132 | (name::ALTITUDE, F32(value)) => layer_attributes.altitude = Some(value), |
962 | 0 | (name::FOCUS, F32(value)) => layer_attributes.focus = Some(value), |
963 | 0 | (name::EXPOSURE_TIME, F32(value)) => layer_attributes.exposure = Some(value), |
964 | 504 | (name::APERTURE, F32(value)) => layer_attributes.aperture = Some(value), |
965 | 1.82k | (name::ISO_SPEED, F32(value)) => layer_attributes.iso_speed = Some(value), |
966 | 746k | (name::ENVIRONMENT_MAP, EnvironmentMap(value)) => layer_attributes.environment_map = Some(value), |
967 | 924 | (name::KEY_CODE, KeyCode(value)) => layer_attributes.film_key_code = Some(value), |
968 | 3.78k | (name::WRAP_MODES, Text(value)) => layer_attributes.wrap_mode_name = Some(value), |
969 | 696k | (name::FRAMES_PER_SECOND, Rational(value)) => layer_attributes.frames_per_second = Some(value), |
970 | 3.31k | (name::MULTI_VIEW, TextVector(value)) => layer_attributes.multi_view_names = Some(value), |
971 | 599 | (name::WORLD_TO_CAMERA, Matrix4x4(value)) => layer_attributes.world_to_camera = Some(value), |
972 | 467 | (name::WORLD_TO_NDC, Matrix4x4(value)) => layer_attributes.world_to_normalized_device = Some(value), |
973 | 687 | (name::DEEP_IMAGE_STATE, Rational(value)) => layer_attributes.deep_image_state = Some(value), |
974 | 1.47k | (name::ORIGINAL_DATA_WINDOW, IntegerBounds(value)) => layer_attributes.original_data_window = Some(value), |
975 | 633k | (name::DWA_COMPRESSION_LEVEL, F32(value)) => dwa_compression_level = Some(value), |
976 | 594 | (name::PREVIEW, Preview(value)) => layer_attributes.preview = Some(value), |
977 | 624 | (name::VIEW, Text(value)) => layer_attributes.view_name = Some(value), |
978 | | |
979 | 0 | (name::NEAR, F32(value)) => layer_attributes.near_clip_plane = Some(value), |
980 | 579k | (name::FAR, F32(value)) => layer_attributes.far_clip_plane = Some(value), |
981 | 497k | (name::FOV_X, F32(value)) => layer_attributes.horizontal_field_of_view = Some(value), |
982 | 754 | (name::FOV_Y, F32(value)) => layer_attributes.vertical_field_of_view = Some(value), |
983 | 1.42k | (name::SOFTWARE, Text(value)) => layer_attributes.software_name = Some(value), |
984 | | |
985 | 2.42k | (name::PIXEL_ASPECT, F32(value)) => image_attributes.pixel_aspect = value, |
986 | 0 | (name::TIME_CODE, TimeCode(value)) => image_attributes.time_code = Some(value), |
987 | 568 | (name::CHROMATICITIES, Chromaticities(value)) => image_attributes.chromaticities = Some(value), |
988 | | |
989 | | // insert unknown attributes of these types into image attributes, |
990 | | // as these must be the same for all headers |
991 | 5.67k | (_, value @ Chromaticities(_)) | |
992 | 29.4k | (_, value @ TimeCode(_)) => { |
993 | 29.4k | image_attributes.other.insert(attribute_name, value); |
994 | 29.4k | }, |
995 | | |
996 | | // insert unknown attributes into layer attributes |
997 | 1.63M | (_, value) => { |
998 | 1.63M | layer_attributes.other.insert(attribute_name, value); |
999 | 1.63M | }, |
1000 | | |
1001 | | } |
1002 | | }, |
1003 | | |
1004 | | // in case the attribute value itself is not ok, but the rest of the image is |
1005 | | // only abort reading the image if desired |
1006 | 259 | Err(error) => { |
1007 | 259 | if pedantic { return Err(error); } |
1008 | | } |
1009 | | } |
1010 | | } |
1011 | | |
1012 | | // construct compression with parameters from properties |
1013 | 39.0k | let compression = match (dwa_compression_level, compression) { |
1014 | 0 | (Some(level), Some(Compression::DWAA(_))) => Some(Compression::DWAA(Some(level))), |
1015 | 0 | (Some(level), Some(Compression::DWAB(_))) => Some(Compression::DWAB(Some(level))), |
1016 | 39.0k | (_, other) => other, |
1017 | | // FIXME dwa compression level gets lost if any other compression is used later in the process |
1018 | | }; |
1019 | | |
1020 | 39.0k | let compression = compression.ok_or(missing_attribute("compression"))?; |
1021 | 38.3k | image_attributes.display_window = display_window.ok_or(missing_attribute("display window"))?; |
1022 | | |
1023 | 38.3k | let data_window = data_window.ok_or(missing_attribute("data window"))?; |
1024 | 38.2k | data_window.validate(None)?; // validate now to avoid errors when computing the chunk_count |
1025 | 38.2k | layer_attributes.layer_position = data_window.position; |
1026 | | |
1027 | | |
1028 | | // validate now to avoid errors when computing the chunk_count |
1029 | 38.2k | if let Some(tiles) = tiles { tiles.validate()?; } |
1030 | 38.2k | let blocks = match block_type { |
1031 | 1.61k | None if requirements.is_single_layer_and_tiled => { |
1032 | 1.61k | BlockDescription::Tiles(tiles.ok_or(missing_attribute("tiles"))?) |
1033 | | }, |
1034 | | Some(BlockType::Tile) | Some(BlockType::DeepTile) => { |
1035 | 7.87k | BlockDescription::Tiles(tiles.ok_or(missing_attribute("tiles"))?) |
1036 | | }, |
1037 | | |
1038 | 28.8k | _ => BlockDescription::ScanLines, |
1039 | | }; |
1040 | | |
1041 | 38.2k | let computed_chunk_count = compute_chunk_count(compression, data_window.size, blocks); |
1042 | 38.2k | if chunk_count.is_some() && pedantic && chunk_count != Some(computed_chunk_count) { |
1043 | 3 | return Err(Error::invalid("chunk count not matching data size")); |
1044 | 38.2k | } |
1045 | | |
1046 | 38.2k | let header = Header { |
1047 | 38.2k | compression, |
1048 | | |
1049 | | // always compute ourselves, because we cannot trust anyone out there 😱 |
1050 | 38.2k | chunk_count: computed_chunk_count, |
1051 | | |
1052 | 38.2k | layer_size: data_window.size, |
1053 | | |
1054 | 38.2k | shared_attributes: image_attributes, |
1055 | 38.2k | own_attributes: layer_attributes, |
1056 | | |
1057 | 38.2k | channels: channels.ok_or(missing_attribute("channels"))?, |
1058 | 38.2k | line_order: line_order.unwrap_or(LineOrder::Unspecified), |
1059 | | |
1060 | 38.2k | blocks, |
1061 | 38.2k | max_samples_per_pixel, |
1062 | 38.2k | deep_data_version: version, |
1063 | 38.2k | deep: block_type == Some(BlockType::DeepScanLine) || block_type == Some(BlockType::DeepTile), |
1064 | | }; |
1065 | | |
1066 | 38.2k | Ok(header) |
1067 | 43.3k | } <exr::meta::header::Header>::read::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Line | Count | Source | 894 | 43.2k | pub fn read(read: &mut PeekRead<impl Read>, requirements: &Requirements, pedantic: bool) -> Result<Self> { | 895 | 43.2k | let max_string_len = if requirements.has_long_names { 256 } else { 32 }; // TODO DRY this information | 896 | | | 897 | | // these required attributes will be filled when encountered while parsing | 898 | 43.2k | let mut tiles = None; | 899 | 43.2k | let mut block_type = None; | 900 | 43.2k | let mut version = None; | 901 | 43.2k | let mut chunk_count = None; | 902 | 43.2k | let mut max_samples_per_pixel = None; | 903 | 43.2k | let mut channels = None; | 904 | 43.2k | let mut compression = None; | 905 | 43.2k | let mut data_window = None; | 906 | 43.2k | let mut display_window = None; | 907 | 43.2k | let mut line_order = None; | 908 | 43.2k | let mut dwa_compression_level = None; | 909 | | | 910 | 43.2k | let mut layer_attributes = LayerAttributes::default(); | 911 | 43.2k | let mut image_attributes = ImageAttributes::new(IntegerBounds::zero()); | 912 | | | 913 | | // read each attribute in this header | 914 | 2.01M | while !sequence_end::has_come(read)? { | 915 | 1.97M | let (attribute_name, value) = attribute::read(read, max_string_len)?; | 916 | | | 917 | | // if the attribute value itself is ok, record it | 918 | 1.96M | match value { | 919 | 1.96M | Ok(value) => { | 920 | | use crate::meta::header::standard_names as name; | 921 | | use crate::meta::attribute::AttributeValue::*; | 922 | | | 923 | | // if the attribute is a required attribute, set the corresponding variable directly. | 924 | | // otherwise, add the attribute to the vector of custom attributes | 925 | | | 926 | | // the following attributes will only be set if the type matches the commonly used type for that attribute | 927 | 1.96M | match (attribute_name.as_slice(), value) { | 928 | 1.96M | (name::BLOCK_TYPE, Text(value)) => block_type = Some(attribute::BlockType::parse(value)?), | 929 | 1.88M | (name::TILES, TileDescription(value)) => tiles = Some(value), | 930 | 1.80M | (name::CHANNELS, ChannelList(value)) => channels = Some(value), | 931 | 1.62M | (name::COMPRESSION, Compression(value)) => compression = Some(value), | 932 | 1.52M | (name::DATA_WINDOW, IntegerBounds(value)) => data_window = Some(value), | 933 | 1.38M | (name::DISPLAY_WINDOW, IntegerBounds(value)) => display_window = Some(value), | 934 | 1.26M | (name::LINE_ORDER, LineOrder(value)) => line_order = Some(value), | 935 | 1.11M | (name::DEEP_DATA_VERSION, I32(value)) => version = Some(value), | 936 | | | 937 | 1.03M | (name::MAX_SAMPLES, I32(value)) => max_samples_per_pixel = Some( | 938 | 369 | i32_to_usize(value, "max sample count")? | 939 | | ), | 940 | | | 941 | 169 | (name::CHUNKS, I32(value)) => chunk_count = Some( | 942 | 169 | i32_to_usize(value, "chunk count")? | 943 | | ), | 944 | | | 945 | 11.8k | (name::NAME, Text(value)) => layer_attributes.layer_name = Some(value), | 946 | 1.16k | (name::WINDOW_CENTER, FloatVec2(value)) => layer_attributes.screen_window_center = value, | 947 | 919k | (name::WINDOW_WIDTH, F32(value)) => layer_attributes.screen_window_width = value, | 948 | | | 949 | 880k | (name::WHITE_LUMINANCE, F32(value)) => layer_attributes.white_luminance = Some(value), | 950 | 2.20k | (name::ADOPTED_NEUTRAL, FloatVec2(value)) => layer_attributes.adopted_neutral = Some(value), | 951 | 3.37k | (name::RENDERING_TRANSFORM, Text(value)) => layer_attributes.rendering_transform_name = Some(value), | 952 | 796k | (name::LOOK_MOD_TRANSFORM, Text(value)) => layer_attributes.look_modification_transform_name = Some(value), | 953 | 443 | (name::X_DENSITY, F32(value)) => layer_attributes.horizontal_density = Some(value), | 954 | | | 955 | 2.95k | (name::OWNER, Text(value)) => layer_attributes.owner = Some(value), | 956 | 886 | (name::COMMENTS, Text(value)) => layer_attributes.comments = Some(value), | 957 | 5.85k | (name::CAPTURE_DATE, Text(value)) => layer_attributes.capture_date = Some(value), | 958 | 85 | (name::UTC_OFFSET, F32(value)) => layer_attributes.utc_offset = Some(value), | 959 | 0 | (name::LONGITUDE, F32(value)) => layer_attributes.longitude = Some(value), | 960 | 5.16k | (name::LATITUDE, F32(value)) => layer_attributes.latitude = Some(value), | 961 | 132 | (name::ALTITUDE, F32(value)) => layer_attributes.altitude = Some(value), | 962 | 0 | (name::FOCUS, F32(value)) => layer_attributes.focus = Some(value), | 963 | 0 | (name::EXPOSURE_TIME, F32(value)) => layer_attributes.exposure = Some(value), | 964 | 504 | (name::APERTURE, F32(value)) => layer_attributes.aperture = Some(value), | 965 | 1.82k | (name::ISO_SPEED, F32(value)) => layer_attributes.iso_speed = Some(value), | 966 | 746k | (name::ENVIRONMENT_MAP, EnvironmentMap(value)) => layer_attributes.environment_map = Some(value), | 967 | 924 | (name::KEY_CODE, KeyCode(value)) => layer_attributes.film_key_code = Some(value), | 968 | 3.78k | (name::WRAP_MODES, Text(value)) => layer_attributes.wrap_mode_name = Some(value), | 969 | 696k | (name::FRAMES_PER_SECOND, Rational(value)) => layer_attributes.frames_per_second = Some(value), | 970 | 3.31k | (name::MULTI_VIEW, TextVector(value)) => layer_attributes.multi_view_names = Some(value), | 971 | 599 | (name::WORLD_TO_CAMERA, Matrix4x4(value)) => layer_attributes.world_to_camera = Some(value), | 972 | 467 | (name::WORLD_TO_NDC, Matrix4x4(value)) => layer_attributes.world_to_normalized_device = Some(value), | 973 | 687 | (name::DEEP_IMAGE_STATE, Rational(value)) => layer_attributes.deep_image_state = Some(value), | 974 | 1.47k | (name::ORIGINAL_DATA_WINDOW, IntegerBounds(value)) => layer_attributes.original_data_window = Some(value), | 975 | 633k | (name::DWA_COMPRESSION_LEVEL, F32(value)) => dwa_compression_level = Some(value), | 976 | 594 | (name::PREVIEW, Preview(value)) => layer_attributes.preview = Some(value), | 977 | 624 | (name::VIEW, Text(value)) => layer_attributes.view_name = Some(value), | 978 | | | 979 | 0 | (name::NEAR, F32(value)) => layer_attributes.near_clip_plane = Some(value), | 980 | 579k | (name::FAR, F32(value)) => layer_attributes.far_clip_plane = Some(value), | 981 | 497k | (name::FOV_X, F32(value)) => layer_attributes.horizontal_field_of_view = Some(value), | 982 | 754 | (name::FOV_Y, F32(value)) => layer_attributes.vertical_field_of_view = Some(value), | 983 | 1.42k | (name::SOFTWARE, Text(value)) => layer_attributes.software_name = Some(value), | 984 | | | 985 | 2.34k | (name::PIXEL_ASPECT, F32(value)) => image_attributes.pixel_aspect = value, | 986 | 0 | (name::TIME_CODE, TimeCode(value)) => image_attributes.time_code = Some(value), | 987 | 568 | (name::CHROMATICITIES, Chromaticities(value)) => image_attributes.chromaticities = Some(value), | 988 | | | 989 | | // insert unknown attributes of these types into image attributes, | 990 | | // as these must be the same for all headers | 991 | 5.67k | (_, value @ Chromaticities(_)) | | 992 | 29.4k | (_, value @ TimeCode(_)) => { | 993 | 29.4k | image_attributes.other.insert(attribute_name, value); | 994 | 29.4k | }, | 995 | | | 996 | | // insert unknown attributes into layer attributes | 997 | 1.63M | (_, value) => { | 998 | 1.63M | layer_attributes.other.insert(attribute_name, value); | 999 | 1.63M | }, | 1000 | | | 1001 | | } | 1002 | | }, | 1003 | | | 1004 | | // in case the attribute value itself is not ok, but the rest of the image is | 1005 | | // only abort reading the image if desired | 1006 | 259 | Err(error) => { | 1007 | 259 | if pedantic { return Err(error); } | 1008 | | } | 1009 | | } | 1010 | | } | 1011 | | | 1012 | | // construct compression with parameters from properties | 1013 | 38.9k | let compression = match (dwa_compression_level, compression) { | 1014 | 0 | (Some(level), Some(Compression::DWAA(_))) => Some(Compression::DWAA(Some(level))), | 1015 | 0 | (Some(level), Some(Compression::DWAB(_))) => Some(Compression::DWAB(Some(level))), | 1016 | 38.9k | (_, other) => other, | 1017 | | // FIXME dwa compression level gets lost if any other compression is used later in the process | 1018 | | }; | 1019 | | | 1020 | 38.9k | let compression = compression.ok_or(missing_attribute("compression"))?; | 1021 | 38.2k | image_attributes.display_window = display_window.ok_or(missing_attribute("display window"))?; | 1022 | | | 1023 | 38.2k | let data_window = data_window.ok_or(missing_attribute("data window"))?; | 1024 | 38.2k | data_window.validate(None)?; // validate now to avoid errors when computing the chunk_count | 1025 | 38.2k | layer_attributes.layer_position = data_window.position; | 1026 | | | 1027 | | | 1028 | | // validate now to avoid errors when computing the chunk_count | 1029 | 38.2k | if let Some(tiles) = tiles { tiles.validate()?; } | 1030 | 38.2k | let blocks = match block_type { | 1031 | 1.61k | None if requirements.is_single_layer_and_tiled => { | 1032 | 1.61k | BlockDescription::Tiles(tiles.ok_or(missing_attribute("tiles"))?) | 1033 | | }, | 1034 | | Some(BlockType::Tile) | Some(BlockType::DeepTile) => { | 1035 | 7.79k | BlockDescription::Tiles(tiles.ok_or(missing_attribute("tiles"))?) | 1036 | | }, | 1037 | | | 1038 | 28.8k | _ => BlockDescription::ScanLines, | 1039 | | }; | 1040 | | | 1041 | 38.2k | let computed_chunk_count = compute_chunk_count(compression, data_window.size, blocks); | 1042 | 38.2k | if chunk_count.is_some() && pedantic && chunk_count != Some(computed_chunk_count) { | 1043 | 3 | return Err(Error::invalid("chunk count not matching data size")); | 1044 | 38.2k | } | 1045 | | | 1046 | 38.1k | let header = Header { | 1047 | 38.2k | compression, | 1048 | | | 1049 | | // always compute ourselves, because we cannot trust anyone out there 😱 | 1050 | 38.2k | chunk_count: computed_chunk_count, | 1051 | | | 1052 | 38.2k | layer_size: data_window.size, | 1053 | | | 1054 | 38.2k | shared_attributes: image_attributes, | 1055 | 38.2k | own_attributes: layer_attributes, | 1056 | | | 1057 | 38.2k | channels: channels.ok_or(missing_attribute("channels"))?, | 1058 | 38.1k | line_order: line_order.unwrap_or(LineOrder::Unspecified), | 1059 | | | 1060 | 38.1k | blocks, | 1061 | 38.1k | max_samples_per_pixel, | 1062 | 38.1k | deep_data_version: version, | 1063 | 38.1k | deep: block_type == Some(BlockType::DeepScanLine) || block_type == Some(BlockType::DeepTile), | 1064 | | }; | 1065 | | | 1066 | 38.1k | Ok(header) | 1067 | 43.2k | } |
Unexecuted instantiation: <exr::meta::header::Header>::read::<_> <exr::meta::header::Header>::read::<exr::io::Tracking<std::io::cursor::Cursor<alloc::vec::Vec<u8>>>> Line | Count | Source | 894 | 81 | pub fn read(read: &mut PeekRead<impl Read>, requirements: &Requirements, pedantic: bool) -> Result<Self> { | 895 | 81 | let max_string_len = if requirements.has_long_names { 256 } else { 32 }; // TODO DRY this information | 896 | | | 897 | | // these required attributes will be filled when encountered while parsing | 898 | 81 | let mut tiles = None; | 899 | 81 | let mut block_type = None; | 900 | 81 | let mut version = None; | 901 | 81 | let mut chunk_count = None; | 902 | 81 | let mut max_samples_per_pixel = None; | 903 | 81 | let mut channels = None; | 904 | 81 | let mut compression = None; | 905 | 81 | let mut data_window = None; | 906 | 81 | let mut display_window = None; | 907 | 81 | let mut line_order = None; | 908 | 81 | let mut dwa_compression_level = None; | 909 | | | 910 | 81 | let mut layer_attributes = LayerAttributes::default(); | 911 | 81 | let mut image_attributes = ImageAttributes::new(IntegerBounds::zero()); | 912 | | | 913 | | // read each attribute in this header | 914 | 972 | while !sequence_end::has_come(read)? { | 915 | 891 | let (attribute_name, value) = attribute::read(read, max_string_len)?; | 916 | | | 917 | | // if the attribute value itself is ok, record it | 918 | 891 | match value { | 919 | 891 | Ok(value) => { | 920 | | use crate::meta::header::standard_names as name; | 921 | | use crate::meta::attribute::AttributeValue::*; | 922 | | | 923 | | // if the attribute is a required attribute, set the corresponding variable directly. | 924 | | // otherwise, add the attribute to the vector of custom attributes | 925 | | | 926 | | // the following attributes will only be set if the type matches the commonly used type for that attribute | 927 | 891 | match (attribute_name.as_slice(), value) { | 928 | 891 | (name::BLOCK_TYPE, Text(value)) => block_type = Some(attribute::BlockType::parse(value)?), | 929 | 810 | (name::TILES, TileDescription(value)) => tiles = Some(value), | 930 | 729 | (name::CHANNELS, ChannelList(value)) => channels = Some(value), | 931 | 648 | (name::COMPRESSION, Compression(value)) => compression = Some(value), | 932 | 567 | (name::DATA_WINDOW, IntegerBounds(value)) => data_window = Some(value), | 933 | 405 | (name::DISPLAY_WINDOW, IntegerBounds(value)) => display_window = Some(value), | 934 | 324 | (name::LINE_ORDER, LineOrder(value)) => line_order = Some(value), | 935 | 243 | (name::DEEP_DATA_VERSION, I32(value)) => version = Some(value), | 936 | | | 937 | 243 | (name::MAX_SAMPLES, I32(value)) => max_samples_per_pixel = Some( | 938 | 0 | i32_to_usize(value, "max sample count")? | 939 | | ), | 940 | | | 941 | 81 | (name::CHUNKS, I32(value)) => chunk_count = Some( | 942 | 81 | i32_to_usize(value, "chunk count")? | 943 | | ), | 944 | | | 945 | 0 | (name::NAME, Text(value)) => layer_attributes.layer_name = Some(value), | 946 | 81 | (name::WINDOW_CENTER, FloatVec2(value)) => layer_attributes.screen_window_center = value, | 947 | 162 | (name::WINDOW_WIDTH, F32(value)) => layer_attributes.screen_window_width = value, | 948 | | | 949 | 81 | (name::WHITE_LUMINANCE, F32(value)) => layer_attributes.white_luminance = Some(value), | 950 | 0 | (name::ADOPTED_NEUTRAL, FloatVec2(value)) => layer_attributes.adopted_neutral = Some(value), | 951 | 0 | (name::RENDERING_TRANSFORM, Text(value)) => layer_attributes.rendering_transform_name = Some(value), | 952 | 81 | (name::LOOK_MOD_TRANSFORM, Text(value)) => layer_attributes.look_modification_transform_name = Some(value), | 953 | 0 | (name::X_DENSITY, F32(value)) => layer_attributes.horizontal_density = Some(value), | 954 | | | 955 | 0 | (name::OWNER, Text(value)) => layer_attributes.owner = Some(value), | 956 | 0 | (name::COMMENTS, Text(value)) => layer_attributes.comments = Some(value), | 957 | 0 | (name::CAPTURE_DATE, Text(value)) => layer_attributes.capture_date = Some(value), | 958 | 0 | (name::UTC_OFFSET, F32(value)) => layer_attributes.utc_offset = Some(value), | 959 | 0 | (name::LONGITUDE, F32(value)) => layer_attributes.longitude = Some(value), | 960 | 0 | (name::LATITUDE, F32(value)) => layer_attributes.latitude = Some(value), | 961 | 0 | (name::ALTITUDE, F32(value)) => layer_attributes.altitude = Some(value), | 962 | 0 | (name::FOCUS, F32(value)) => layer_attributes.focus = Some(value), | 963 | 0 | (name::EXPOSURE_TIME, F32(value)) => layer_attributes.exposure = Some(value), | 964 | 0 | (name::APERTURE, F32(value)) => layer_attributes.aperture = Some(value), | 965 | 0 | (name::ISO_SPEED, F32(value)) => layer_attributes.iso_speed = Some(value), | 966 | 0 | (name::ENVIRONMENT_MAP, EnvironmentMap(value)) => layer_attributes.environment_map = Some(value), | 967 | 0 | (name::KEY_CODE, KeyCode(value)) => layer_attributes.film_key_code = Some(value), | 968 | 0 | (name::WRAP_MODES, Text(value)) => layer_attributes.wrap_mode_name = Some(value), | 969 | 0 | (name::FRAMES_PER_SECOND, Rational(value)) => layer_attributes.frames_per_second = Some(value), | 970 | 0 | (name::MULTI_VIEW, TextVector(value)) => layer_attributes.multi_view_names = Some(value), | 971 | 0 | (name::WORLD_TO_CAMERA, Matrix4x4(value)) => layer_attributes.world_to_camera = Some(value), | 972 | 0 | (name::WORLD_TO_NDC, Matrix4x4(value)) => layer_attributes.world_to_normalized_device = Some(value), | 973 | 0 | (name::DEEP_IMAGE_STATE, Rational(value)) => layer_attributes.deep_image_state = Some(value), | 974 | 0 | (name::ORIGINAL_DATA_WINDOW, IntegerBounds(value)) => layer_attributes.original_data_window = Some(value), | 975 | 0 | (name::DWA_COMPRESSION_LEVEL, F32(value)) => dwa_compression_level = Some(value), | 976 | 0 | (name::PREVIEW, Preview(value)) => layer_attributes.preview = Some(value), | 977 | 0 | (name::VIEW, Text(value)) => layer_attributes.view_name = Some(value), | 978 | | | 979 | 0 | (name::NEAR, F32(value)) => layer_attributes.near_clip_plane = Some(value), | 980 | 0 | (name::FAR, F32(value)) => layer_attributes.far_clip_plane = Some(value), | 981 | 0 | (name::FOV_X, F32(value)) => layer_attributes.horizontal_field_of_view = Some(value), | 982 | 0 | (name::FOV_Y, F32(value)) => layer_attributes.vertical_field_of_view = Some(value), | 983 | 0 | (name::SOFTWARE, Text(value)) => layer_attributes.software_name = Some(value), | 984 | | | 985 | 81 | (name::PIXEL_ASPECT, F32(value)) => image_attributes.pixel_aspect = value, | 986 | 0 | (name::TIME_CODE, TimeCode(value)) => image_attributes.time_code = Some(value), | 987 | 0 | (name::CHROMATICITIES, Chromaticities(value)) => image_attributes.chromaticities = Some(value), | 988 | | | 989 | | // insert unknown attributes of these types into image attributes, | 990 | | // as these must be the same for all headers | 991 | 0 | (_, value @ Chromaticities(_)) | | 992 | 0 | (_, value @ TimeCode(_)) => { | 993 | 0 | image_attributes.other.insert(attribute_name, value); | 994 | 0 | }, | 995 | | | 996 | | // insert unknown attributes into layer attributes | 997 | 0 | (_, value) => { | 998 | 0 | layer_attributes.other.insert(attribute_name, value); | 999 | 0 | }, | 1000 | | | 1001 | | } | 1002 | | }, | 1003 | | | 1004 | | // in case the attribute value itself is not ok, but the rest of the image is | 1005 | | // only abort reading the image if desired | 1006 | 0 | Err(error) => { | 1007 | 0 | if pedantic { return Err(error); } | 1008 | | } | 1009 | | } | 1010 | | } | 1011 | | | 1012 | | // construct compression with parameters from properties | 1013 | 81 | let compression = match (dwa_compression_level, compression) { | 1014 | 0 | (Some(level), Some(Compression::DWAA(_))) => Some(Compression::DWAA(Some(level))), | 1015 | 0 | (Some(level), Some(Compression::DWAB(_))) => Some(Compression::DWAB(Some(level))), | 1016 | 81 | (_, other) => other, | 1017 | | // FIXME dwa compression level gets lost if any other compression is used later in the process | 1018 | | }; | 1019 | | | 1020 | 81 | let compression = compression.ok_or(missing_attribute("compression"))?; | 1021 | 81 | image_attributes.display_window = display_window.ok_or(missing_attribute("display window"))?; | 1022 | | | 1023 | 81 | let data_window = data_window.ok_or(missing_attribute("data window"))?; | 1024 | 81 | data_window.validate(None)?; // validate now to avoid errors when computing the chunk_count | 1025 | 81 | layer_attributes.layer_position = data_window.position; | 1026 | | | 1027 | | | 1028 | | // validate now to avoid errors when computing the chunk_count | 1029 | 81 | if let Some(tiles) = tiles { tiles.validate()?; } | 1030 | 81 | let blocks = match block_type { | 1031 | 0 | None if requirements.is_single_layer_and_tiled => { | 1032 | 0 | BlockDescription::Tiles(tiles.ok_or(missing_attribute("tiles"))?) | 1033 | | }, | 1034 | | Some(BlockType::Tile) | Some(BlockType::DeepTile) => { | 1035 | 81 | BlockDescription::Tiles(tiles.ok_or(missing_attribute("tiles"))?) | 1036 | | }, | 1037 | | | 1038 | 0 | _ => BlockDescription::ScanLines, | 1039 | | }; | 1040 | | | 1041 | 81 | let computed_chunk_count = compute_chunk_count(compression, data_window.size, blocks); | 1042 | 81 | if chunk_count.is_some() && pedantic && chunk_count != Some(computed_chunk_count) { | 1043 | 0 | return Err(Error::invalid("chunk count not matching data size")); | 1044 | 81 | } | 1045 | | | 1046 | 81 | let header = Header { | 1047 | 81 | compression, | 1048 | | | 1049 | | // always compute ourselves, because we cannot trust anyone out there 😱 | 1050 | 81 | chunk_count: computed_chunk_count, | 1051 | | | 1052 | 81 | layer_size: data_window.size, | 1053 | | | 1054 | 81 | shared_attributes: image_attributes, | 1055 | 81 | own_attributes: layer_attributes, | 1056 | | | 1057 | 81 | channels: channels.ok_or(missing_attribute("channels"))?, | 1058 | 81 | line_order: line_order.unwrap_or(LineOrder::Unspecified), | 1059 | | | 1060 | 81 | blocks, | 1061 | 81 | max_samples_per_pixel, | 1062 | 81 | deep_data_version: version, | 1063 | 81 | deep: block_type == Some(BlockType::DeepScanLine) || block_type == Some(BlockType::DeepTile), | 1064 | | }; | 1065 | | | 1066 | 81 | Ok(header) | 1067 | 81 | } |
|
1068 | | |
1069 | | /// Without validation, write this instance to the byte stream. |
1070 | 81 | pub fn write(&self, write: &mut impl Write) -> UnitResult { |
1071 | 891 | for (name, value) in self.all_named_attributes() { |
1072 | 891 | attribute::write(name, &value, write)?; |
1073 | | } |
1074 | | |
1075 | 81 | sequence_end::write(write)?; |
1076 | 81 | Ok(()) |
1077 | 81 | } Unexecuted instantiation: <exr::meta::header::Header>::write::<_> Unexecuted instantiation: <exr::meta::header::Header>::write::<exr::io::Tracking<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>> <exr::meta::header::Header>::write::<exr::io::Tracking<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>> Line | Count | Source | 1070 | 81 | pub fn write(&self, write: &mut impl Write) -> UnitResult { | 1071 | 891 | for (name, value) in self.all_named_attributes() { | 1072 | 891 | attribute::write(name, &value, write)?; | 1073 | | } | 1074 | | | 1075 | 81 | sequence_end::write(write)?; | 1076 | 81 | Ok(()) | 1077 | 81 | } |
|
1078 | | |
1079 | | /// The rectangle describing the bounding box of this layer |
1080 | | /// within the infinite global 2D space of the file. |
1081 | 380k | pub fn data_window(&self) -> IntegerBounds { |
1082 | 380k | IntegerBounds::new(self.own_attributes.layer_position, self.layer_size) |
1083 | 380k | } |
1084 | | } |
1085 | | |
1086 | | |
1087 | | |
1088 | | /// Collection of required attribute names. |
1089 | | pub mod standard_names { |
1090 | | macro_rules! define_required_attribute_names { |
1091 | | ( $($name: ident : $value: expr),* ) => { |
1092 | | |
1093 | | /// A list containing all reserved names. |
1094 | | pub const ALL: &'static [&'static [u8]] = &[ |
1095 | | $( $value ),* |
1096 | | ]; |
1097 | | |
1098 | | $( |
1099 | | /// The byte-string name of this required attribute as it appears in an exr file. |
1100 | | pub const $name: &'static [u8] = $value; |
1101 | | )* |
1102 | | }; |
1103 | | } |
1104 | | |
1105 | | define_required_attribute_names! { |
1106 | | TILES: b"tiles", |
1107 | | NAME: b"name", |
1108 | | BLOCK_TYPE: b"type", |
1109 | | DEEP_DATA_VERSION: b"version", |
1110 | | CHUNKS: b"chunkCount", |
1111 | | MAX_SAMPLES: b"maxSamplesPerPixel", |
1112 | | CHANNELS: b"channels", |
1113 | | COMPRESSION: b"compression", |
1114 | | DATA_WINDOW: b"dataWindow", |
1115 | | DISPLAY_WINDOW: b"displayWindow", |
1116 | | LINE_ORDER: b"lineOrder", |
1117 | | PIXEL_ASPECT: b"pixelAspectRatio", |
1118 | | WINDOW_CENTER: b"screenWindowCenter", |
1119 | | WINDOW_WIDTH: b"screenWindowWidth", |
1120 | | WHITE_LUMINANCE: b"whiteLuminance", |
1121 | | ADOPTED_NEUTRAL: b"adoptedNeutral", |
1122 | | RENDERING_TRANSFORM: b"renderingTransform", |
1123 | | LOOK_MOD_TRANSFORM: b"lookModTransform", |
1124 | | X_DENSITY: b"xDensity", |
1125 | | OWNER: b"owner", |
1126 | | COMMENTS: b"comments", |
1127 | | CAPTURE_DATE: b"capDate", |
1128 | | UTC_OFFSET: b"utcOffset", |
1129 | | LONGITUDE: b"longitude", |
1130 | | LATITUDE: b"latitude", |
1131 | | ALTITUDE: b"altitude", |
1132 | | FOCUS: b"focus", |
1133 | | EXPOSURE_TIME: b"expTime", |
1134 | | APERTURE: b"aperture", |
1135 | | ISO_SPEED: b"isoSpeed", |
1136 | | ENVIRONMENT_MAP: b"envmap", |
1137 | | KEY_CODE: b"keyCode", |
1138 | | TIME_CODE: b"timeCode", |
1139 | | WRAP_MODES: b"wrapmodes", |
1140 | | FRAMES_PER_SECOND: b"framesPerSecond", |
1141 | | MULTI_VIEW: b"multiView", |
1142 | | WORLD_TO_CAMERA: b"worldToCamera", |
1143 | | WORLD_TO_NDC: b"worldToNDC", |
1144 | | DEEP_IMAGE_STATE: b"deepImageState", |
1145 | | ORIGINAL_DATA_WINDOW: b"originalDataWindow", |
1146 | | DWA_COMPRESSION_LEVEL: b"dwaCompressionLevel", |
1147 | | PREVIEW: b"preview", |
1148 | | VIEW: b"view", |
1149 | | CHROMATICITIES: b"chromaticities", |
1150 | | NEAR: b"near", |
1151 | | FAR: b"far", |
1152 | | FOV_X: b"fieldOfViewHorizontal", |
1153 | | FOV_Y: b"fieldOfViewVertical", |
1154 | | SOFTWARE: b"software" |
1155 | | } |
1156 | | } |
1157 | | |
1158 | | |
1159 | | impl Default for LayerAttributes { |
1160 | 43.4k | fn default() -> Self { |
1161 | 43.4k | Self { |
1162 | 43.4k | layer_position: Vec2(0, 0), |
1163 | 43.4k | screen_window_center: Vec2(0.0, 0.0), |
1164 | 43.4k | screen_window_width: 1.0, |
1165 | 43.4k | layer_name: None, |
1166 | 43.4k | white_luminance: None, |
1167 | 43.4k | adopted_neutral: None, |
1168 | 43.4k | rendering_transform_name: None, |
1169 | 43.4k | look_modification_transform_name: None, |
1170 | 43.4k | horizontal_density: None, |
1171 | 43.4k | owner: None, |
1172 | 43.4k | comments: None, |
1173 | 43.4k | capture_date: None, |
1174 | 43.4k | utc_offset: None, |
1175 | 43.4k | longitude: None, |
1176 | 43.4k | latitude: None, |
1177 | 43.4k | altitude: None, |
1178 | 43.4k | focus: None, |
1179 | 43.4k | exposure: None, |
1180 | 43.4k | aperture: None, |
1181 | 43.4k | iso_speed: None, |
1182 | 43.4k | environment_map: None, |
1183 | 43.4k | film_key_code: None, |
1184 | 43.4k | wrap_mode_name: None, |
1185 | 43.4k | frames_per_second: None, |
1186 | 43.4k | multi_view_names: None, |
1187 | 43.4k | world_to_camera: None, |
1188 | 43.4k | world_to_normalized_device: None, |
1189 | 43.4k | deep_image_state: None, |
1190 | 43.4k | original_data_window: None, |
1191 | 43.4k | preview: None, |
1192 | 43.4k | view_name: None, |
1193 | 43.4k | software_name: None, |
1194 | 43.4k | near_clip_plane: None, |
1195 | 43.4k | far_clip_plane: None, |
1196 | 43.4k | horizontal_field_of_view: None, |
1197 | 43.4k | vertical_field_of_view: None, |
1198 | 43.4k | other: Default::default() |
1199 | 43.4k | } |
1200 | 43.4k | } |
1201 | | } |
1202 | | |
1203 | | impl std::fmt::Debug for LayerAttributes { |
1204 | 0 | fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
1205 | 0 | let default_self = Self::default(); |
1206 | | |
1207 | 0 | let mut debug = formatter.debug_struct("LayerAttributes (default values omitted)"); |
1208 | | |
1209 | | // always debug the following field |
1210 | 0 | debug.field("name", &self.layer_name); |
1211 | | |
1212 | | macro_rules! debug_non_default_fields { |
1213 | | ( $( $name: ident ),* ) => { $( |
1214 | | |
1215 | | if self.$name != default_self.$name { |
1216 | | debug.field(stringify!($name), &self.$name); |
1217 | | } |
1218 | | |
1219 | | )* }; |
1220 | | } |
1221 | | |
1222 | | // only debug these fields if they are not the default value |
1223 | 0 | debug_non_default_fields! { |
1224 | | screen_window_center, screen_window_width, |
1225 | | white_luminance, adopted_neutral, horizontal_density, |
1226 | | rendering_transform_name, look_modification_transform_name, |
1227 | | owner, comments, |
1228 | | capture_date, utc_offset, |
1229 | | longitude, latitude, altitude, |
1230 | | focus, exposure, aperture, iso_speed, |
1231 | | environment_map, film_key_code, wrap_mode_name, |
1232 | | frames_per_second, multi_view_names, |
1233 | | world_to_camera, world_to_normalized_device, |
1234 | | deep_image_state, original_data_window, |
1235 | | preview, view_name, |
1236 | | vertical_field_of_view, horizontal_field_of_view, |
1237 | | near_clip_plane, far_clip_plane, software_name |
1238 | | } |
1239 | | |
1240 | 0 | for (name, value) in &self.other { |
1241 | 0 | debug.field(&format!("\"{}\"", name), value); |
1242 | 0 | } |
1243 | | |
1244 | | // debug.finish_non_exhaustive() TODO |
1245 | 0 | debug.finish() |
1246 | 0 | } |
1247 | | } |