/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 | 0 | pub fn new(display_window: IntegerBounds) -> Self { |
289 | 0 | Self { |
290 | 0 | pixel_aspect: 1.0, |
291 | 0 | chromaticities: None, |
292 | 0 | time_code: None, |
293 | 0 | other: Default::default(), |
294 | 0 | display_window, |
295 | 0 | } |
296 | 0 | } |
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 | 0 | pub fn enumerate_ordered_blocks(&self) -> impl Iterator<Item=(usize, TileIndices)> + Send { |
383 | 0 | let increasing_y = self.blocks_increasing_y_order().enumerate(); |
384 | | |
385 | | // TODO without box? |
386 | 0 | let ordered: Box<dyn Send + Iterator<Item=(usize, TileIndices)>> = { |
387 | 0 | if self.line_order == LineOrder::Decreasing { Box::new(increasing_y.rev()) } |
388 | 0 | else { Box::new(increasing_y) } |
389 | | }; |
390 | | |
391 | 0 | ordered |
392 | 0 | } |
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 | 0 | pub fn blocks_increasing_y_order(&self) -> impl Iterator<Item = TileIndices> + ExactSizeIterator + DoubleEndedIterator { |
413 | 0 | fn tiles_of(image_size: Vec2<usize>, tile_size: Vec2<usize>, level_index: Vec2<usize>) -> impl Iterator<Item=TileIndices> { |
414 | 0 | fn divide_and_rest(total_size: usize, block_size: usize) -> impl Iterator<Item=(usize, usize)> { |
415 | 0 | let block_count = compute_block_count(total_size, block_size); |
416 | 0 | (0..block_count).map(move |block_index| ( |
417 | 0 | block_index, calculate_block_size(total_size, block_size, block_index).expect("block size calculation bug") |
418 | | )) |
419 | 0 | } |
420 | | |
421 | 0 | divide_and_rest(image_size.height(), tile_size.height()).flat_map(move |(y_index, tile_height)|{ |
422 | 0 | divide_and_rest(image_size.width(), tile_size.width()).map(move |(x_index, tile_width)|{ |
423 | 0 | TileIndices { |
424 | 0 | size: Vec2(tile_width, tile_height), |
425 | 0 | location: TileCoordinates { tile_index: Vec2(x_index, y_index), level_index, }, |
426 | 0 | } |
427 | 0 | }) |
428 | 0 | }) |
429 | 0 | } |
430 | | |
431 | 0 | let vec: Vec<TileIndices> = { |
432 | 0 | if let BlockDescription::Tiles(tiles) = self.blocks { |
433 | 0 | match tiles.level_mode { |
434 | | LevelMode::Singular => { |
435 | 0 | tiles_of(self.layer_size, tiles.tile_size, Vec2(0, 0)).collect() |
436 | | }, |
437 | | LevelMode::MipMap => { |
438 | 0 | mip_map_levels(tiles.rounding_mode, self.layer_size) |
439 | 0 | .flat_map(move |(level_index, level_size)|{ |
440 | 0 | tiles_of(level_size, tiles.tile_size, Vec2(level_index, level_index)) |
441 | 0 | }) |
442 | 0 | .collect() |
443 | | }, |
444 | | LevelMode::RipMap => { |
445 | 0 | rip_map_levels(tiles.rounding_mode, self.layer_size) |
446 | 0 | .flat_map(move |(level_index, level_size)| { |
447 | 0 | tiles_of(level_size, tiles.tile_size, level_index) |
448 | 0 | }) |
449 | 0 | .collect() |
450 | | } |
451 | | } |
452 | | } |
453 | | else { |
454 | 0 | let tiles = Vec2(self.layer_size.0, self.compression.scan_lines_per_block()); |
455 | 0 | tiles_of(self.layer_size, tiles, Vec2(0, 0)).collect() |
456 | | } |
457 | | }; |
458 | | |
459 | 0 | vec.into_iter() // TODO without collect |
460 | 0 | } |
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 | 0 | pub fn max_block_pixel_size(&self) -> Vec2<usize> { |
481 | 0 | match self.blocks { |
482 | 0 | BlockDescription::ScanLines => Vec2(self.layer_size.0, self.compression.scan_lines_per_block()), |
483 | 0 | BlockDescription::Tiles(tiles) => tiles.tile_size, |
484 | | } |
485 | 0 | } |
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 | 0 | pub fn get_absolute_block_pixel_coordinates(&self, tile: TileCoordinates) -> Result<IntegerBounds> { |
495 | 0 | if let BlockDescription::Tiles(tiles) = self.blocks { |
496 | 0 | let Vec2(data_width, data_height) = self.layer_size; |
497 | | |
498 | 0 | let data_width = compute_level_size(tiles.rounding_mode, data_width, tile.level_index.x()); |
499 | 0 | let data_height = compute_level_size(tiles.rounding_mode, data_height, tile.level_index.y()); |
500 | 0 | let absolute_tile_coordinates = tile.to_data_indices(tiles.tile_size, Vec2(data_width, data_height))?; |
501 | | |
502 | 0 | 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 | 0 | } |
505 | | |
506 | 0 | Ok(absolute_tile_coordinates) |
507 | | } |
508 | | else { // this is a scanline image |
509 | 0 | debug_assert_eq!(tile.tile_index.0, 0, "block index calculation bug"); |
510 | | |
511 | 0 | let (y, height) = calculate_block_position_and_size( |
512 | 0 | self.layer_size.height(), |
513 | 0 | self.compression.scan_lines_per_block(), |
514 | 0 | tile.tile_index.y() |
515 | 0 | )?; |
516 | | |
517 | | Ok(IntegerBounds { |
518 | 0 | position: Vec2(0, usize_to_i32(y, "tile y")?), |
519 | 0 | size: Vec2(self.layer_size.width(), height) |
520 | | }) |
521 | | } |
522 | | |
523 | | // TODO deep data? |
524 | 0 | } |
525 | | |
526 | | /// Return the tile index, converting scan line block coordinates to tile indices. |
527 | | /// Starts at `0` and is not negative. |
528 | 0 | pub fn get_block_data_indices(&self, block: &CompressedBlock) -> Result<TileCoordinates> { |
529 | 0 | Ok(match block { |
530 | 0 | CompressedBlock::Tile(ref tile) => { |
531 | 0 | tile.coordinates |
532 | | }, |
533 | | |
534 | 0 | CompressedBlock::ScanLine(ref block) => { |
535 | 0 | let size = self.compression.scan_lines_per_block() as i32; |
536 | | |
537 | 0 | let diff = block.y_coordinate.checked_sub(self.own_attributes.layer_position.y()).ok_or(Error::invalid("invalid header"))?; |
538 | 0 | let y = diff.checked_div(size).ok_or(Error::invalid("invalid header"))?; |
539 | | |
540 | 0 | if y < 0 { |
541 | 0 | return Err(Error::invalid("scan block y coordinate")); |
542 | 0 | } |
543 | | |
544 | 0 | TileCoordinates { |
545 | 0 | tile_index: Vec2(0, y as usize), |
546 | 0 | level_index: Vec2(0, 0) |
547 | 0 | } |
548 | | }, |
549 | | |
550 | 0 | _ => return Err(Error::unsupported("deep data not supported yet")) |
551 | | }) |
552 | 0 | } |
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 | 0 | pub fn max_block_byte_size(&self) -> usize { |
573 | 0 | self.channels.bytes_per_pixel * match self.blocks { |
574 | 0 | BlockDescription::Tiles(tiles) => tiles.tile_size.area(), |
575 | 0 | BlockDescription::ScanLines => self.compression.scan_lines_per_block() * self.layer_size.width() |
576 | | // TODO What about deep data??? |
577 | | } |
578 | 0 | } |
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 | 0 | pub fn validate(&self, is_multilayer: bool, long_names: &mut bool, strict: bool) -> UnitResult { |
619 | | |
620 | 0 | self.data_window().validate(None)?; |
621 | 0 | self.shared_attributes.display_window.validate(None)?; |
622 | | |
623 | 0 | if strict { |
624 | 0 | 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 | 0 | } |
629 | | |
630 | 0 | if self.blocks == BlockDescription::ScanLines && self.line_order == LineOrder::Unspecified { |
631 | 0 | return Err(Error::invalid("unspecified line order in scan line images")); |
632 | 0 | } |
633 | | |
634 | 0 | if self.layer_size == Vec2(0, 0) { |
635 | 0 | return Err(Error::invalid("empty data window")); |
636 | 0 | } |
637 | | |
638 | 0 | if self.shared_attributes.display_window.size == Vec2(0,0) { |
639 | 0 | return Err(Error::invalid("empty display window")); |
640 | 0 | } |
641 | | |
642 | 0 | 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 | 0 | } |
645 | | |
646 | 0 | if self.own_attributes.screen_window_width < 0.0 { |
647 | 0 | return Err(Error::invalid("screen window width")); |
648 | 0 | } |
649 | 0 | } |
650 | | |
651 | 0 | let allow_subsampling = !self.deep && self.blocks == BlockDescription::ScanLines; |
652 | 0 | self.channels.validate(allow_subsampling, self.data_window(), strict)?; |
653 | | |
654 | 0 | for (name, value) in &self.shared_attributes.other { |
655 | 0 | attribute::validate(name, value, long_names, allow_subsampling, self.data_window(), strict)?; |
656 | | } |
657 | | |
658 | 0 | for (name, value) in &self.own_attributes.other { |
659 | 0 | 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 | 0 | 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 | 0 | } |
666 | | |
667 | | // check if attribute names appear twice |
668 | 0 | if strict { |
669 | 0 | 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 | 0 | for &reserved in header::standard_names::ALL.iter() { |
676 | 0 | let name = Text::from_bytes_unchecked(SmallVec::from_slice(reserved)); |
677 | 0 | 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 | 0 | } |
683 | | } |
684 | 0 | } |
685 | | |
686 | 0 | 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 | 0 | } |
707 | | |
708 | 0 | Ok(()) |
709 | 0 | } |
710 | | |
711 | | /// Read the headers without validating them. |
712 | 0 | pub fn read_all(read: &mut PeekRead<impl Read>, version: &Requirements, pedantic: bool) -> Result<Headers> { |
713 | 0 | if !version.is_multilayer() { |
714 | 0 | 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 | 0 | } Unexecuted instantiation: <exr::meta::header::Header>::read_all::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::header::Header>::read_all::<_> Unexecuted instantiation: <exr::meta::header::Header>::read_all::<exr::io::Tracking<std::io::cursor::Cursor<alloc::vec::Vec<u8>>>> |
726 | | |
727 | | /// Without validation, write the headers to the byte stream. |
728 | 0 | pub fn write_all(headers: &[Header], write: &mut impl Write, is_multilayer: bool) -> UnitResult { |
729 | 0 | for header in headers { |
730 | 0 | header.write(write)?; |
731 | | } |
732 | | |
733 | 0 | if is_multilayer { |
734 | 0 | sequence_end::write(write)?; |
735 | 0 | } |
736 | | |
737 | 0 | Ok(()) |
738 | 0 | } 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>>>> Unexecuted instantiation: <exr::meta::header::Header>::write_all::<exr::io::Tracking<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>> |
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 | 0 | 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 | 0 | #[inline] fn optional<'t, T: Clone>( |
753 | 0 | name: &'t TextSlice, |
754 | 0 | to_attribute: impl Fn(T) -> AttributeValue, |
755 | 0 | value: &'t Option<T> |
756 | 0 | ) |
757 | 0 | -> impl Iterator<Item=(&'t TextSlice, AttributeValue)> |
758 | | { |
759 | 0 | 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 | 0 | } 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> 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> |
761 | | |
762 | 0 | #[inline] fn required<'s, T: Clone>(name: &'s TextSlice, to_attribute: impl Fn(T) -> AttributeValue, value: &'s T) |
763 | 0 | -> impl Iterator<Item=(&'s TextSlice, AttributeValue)> |
764 | | { |
765 | 0 | once((name, to_attribute((*value).clone()))) |
766 | 0 | } 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> 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> |
767 | | |
768 | | // used to type-check local variables. only requried because you cannot do `let i: impl Iterator<> = ...` |
769 | 0 | #[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}>>>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}>>> |
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 | 0 | #[inline] fn usize_as_i32(value: usize) -> AttributeValue { |
794 | 0 | I32(i32::try_from(value).expect("usize exceeds i32 range")) |
795 | 0 | } Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::usize_as_i32 Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::usize_as_i32 Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::usize_as_i32 |
796 | | |
797 | | |
798 | 0 | let block_type_and_tiles = expect_is_iter(once_with(move ||{ |
799 | 0 | let (block_type, tiles) = match self.blocks { |
800 | 0 | BlockDescription::ScanLines => (attribute::BlockType::ScanLine, None), |
801 | 0 | BlockDescription::Tiles(tiles) => (attribute::BlockType::Tile, Some(tiles)) |
802 | | }; |
803 | | |
804 | 0 | once((BLOCK_TYPE, BlockType(block_type))) |
805 | 0 | .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}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#0}::{closure#0} |
806 | 0 | }).flatten()); Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#0}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#0} |
807 | | |
808 | 0 | let data_window = expect_is_iter(once_with(move ||{ |
809 | 0 | (DATA_WINDOW, IntegerBounds(self.data_window())) |
810 | 0 | })); Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#1}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#1}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#1} |
811 | | |
812 | | // dwa writes compression parameters as attribute. |
813 | 0 | let dwa_compr_level = expect_is_iter( |
814 | 0 | 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 | 0 | _ => None |
821 | | } |
822 | 0 | }).flatten() Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#2}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#2}Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes::{closure#2} |
823 | | ); |
824 | | |
825 | 0 | let opt_core_attrs = optional_attributes!( |
826 | 0 | DEEP_DATA_VERSION: I32 = &self.deep_data_version, |
827 | 0 | MAX_SAMPLES: usize_as_i32 = &self.max_samples_per_pixel |
828 | 0 | ).chain(block_type_and_tiles).chain(dwa_compr_level); |
829 | | |
830 | 0 | let req_core_attrs = required_attributes!( |
831 | | // chunks is not actually required, but always computed in this library anyways |
832 | 0 | CHUNKS: usize_as_i32 = &self.chunk_count, |
833 | | |
834 | 0 | CHANNELS: ChannelList = &self.channels, |
835 | 0 | COMPRESSION: Compression = &self.compression, |
836 | 0 | LINE_ORDER: LineOrder = &self.line_order, |
837 | | |
838 | 0 | DISPLAY_WINDOW: IntegerBounds = &self.shared_attributes.display_window, |
839 | 0 | PIXEL_ASPECT: F32 = &self.shared_attributes.pixel_aspect, |
840 | | |
841 | 0 | WINDOW_CENTER: FloatVec2 = &self.own_attributes.screen_window_center, |
842 | 0 | WINDOW_WIDTH: F32 = &self.own_attributes.screen_window_width |
843 | 0 | ).chain(data_window); |
844 | | |
845 | 0 | let opt_attr = optional_attributes!( |
846 | 0 | NAME: Text = &self.own_attributes.layer_name, |
847 | 0 | WHITE_LUMINANCE: F32 = &self.own_attributes.white_luminance, |
848 | 0 | ADOPTED_NEUTRAL: FloatVec2 = &self.own_attributes.adopted_neutral, |
849 | 0 | RENDERING_TRANSFORM: Text = &self.own_attributes.rendering_transform_name, |
850 | 0 | LOOK_MOD_TRANSFORM: Text = &self.own_attributes.look_modification_transform_name, |
851 | 0 | X_DENSITY: F32 = &self.own_attributes.horizontal_density, |
852 | 0 | OWNER: Text = &self.own_attributes.owner, |
853 | 0 | COMMENTS: Text = &self.own_attributes.comments, |
854 | 0 | CAPTURE_DATE: Text = &self.own_attributes.capture_date, |
855 | 0 | UTC_OFFSET: F32 = &self.own_attributes.utc_offset, |
856 | 0 | LONGITUDE: F32 = &self.own_attributes.longitude, |
857 | 0 | LATITUDE: F32 = &self.own_attributes.latitude, |
858 | 0 | ALTITUDE: F32 = &self.own_attributes.altitude, |
859 | 0 | FOCUS: F32 = &self.own_attributes.focus, |
860 | 0 | EXPOSURE_TIME: F32 = &self.own_attributes.exposure, |
861 | 0 | APERTURE: F32 = &self.own_attributes.aperture, |
862 | 0 | ISO_SPEED: F32 = &self.own_attributes.iso_speed, |
863 | 0 | ENVIRONMENT_MAP: EnvironmentMap = &self.own_attributes.environment_map, |
864 | 0 | KEY_CODE: KeyCode = &self.own_attributes.film_key_code, |
865 | 0 | TIME_CODE: TimeCode = &self.shared_attributes.time_code, |
866 | 0 | WRAP_MODES: Text = &self.own_attributes.wrap_mode_name, |
867 | 0 | FRAMES_PER_SECOND: Rational = &self.own_attributes.frames_per_second, |
868 | 0 | MULTI_VIEW: TextVector = &self.own_attributes.multi_view_names, |
869 | 0 | WORLD_TO_CAMERA: Matrix4x4 = &self.own_attributes.world_to_camera, |
870 | 0 | WORLD_TO_NDC: Matrix4x4 = &self.own_attributes.world_to_normalized_device, |
871 | 0 | DEEP_IMAGE_STATE: Rational = &self.own_attributes.deep_image_state, |
872 | 0 | ORIGINAL_DATA_WINDOW: IntegerBounds = &self.own_attributes.original_data_window, |
873 | 0 | CHROMATICITIES: Chromaticities = &self.shared_attributes.chromaticities, |
874 | 0 | PREVIEW: Preview = &self.own_attributes.preview, |
875 | 0 | VIEW: Text = &self.own_attributes.view_name, |
876 | 0 | NEAR: F32 = &self.own_attributes.near_clip_plane, |
877 | 0 | FAR: F32 = &self.own_attributes.far_clip_plane, |
878 | 0 | FOV_X: F32 = &self.own_attributes.horizontal_field_of_view, |
879 | 0 | FOV_Y: F32 = &self.own_attributes.vertical_field_of_view, |
880 | 0 | SOFTWARE: Text = &self.own_attributes.software_name |
881 | | ); |
882 | | |
883 | 0 | let other = self.own_attributes.other.iter() |
884 | 0 | .chain(self.shared_attributes.other.iter()) |
885 | 0 | .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 | 0 | req_core_attrs |
888 | 0 | .chain(opt_core_attrs) |
889 | 0 | .chain(opt_attr) |
890 | 0 | .chain(other) |
891 | 0 | } Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes Unexecuted instantiation: <exr::meta::header::Header>::all_named_attributes |
892 | | |
893 | | /// Read the value without validating. |
894 | 0 | pub fn read(read: &mut PeekRead<impl Read>, requirements: &Requirements, pedantic: bool) -> Result<Self> { |
895 | 0 | 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 | 0 | let mut tiles = None; |
899 | 0 | let mut block_type = None; |
900 | 0 | let mut version = None; |
901 | 0 | let mut chunk_count = None; |
902 | 0 | let mut max_samples_per_pixel = None; |
903 | 0 | let mut channels = None; |
904 | 0 | let mut compression = None; |
905 | 0 | let mut data_window = None; |
906 | 0 | let mut display_window = None; |
907 | 0 | let mut line_order = None; |
908 | 0 | let mut dwa_compression_level = None; |
909 | | |
910 | 0 | let mut layer_attributes = LayerAttributes::default(); |
911 | 0 | let mut image_attributes = ImageAttributes::new(IntegerBounds::zero()); |
912 | | |
913 | | // read each attribute in this header |
914 | 0 | while !sequence_end::has_come(read)? { |
915 | 0 | let (attribute_name, value) = attribute::read(read, max_string_len)?; |
916 | | |
917 | | // if the attribute value itself is ok, record it |
918 | 0 | match value { |
919 | 0 | 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 | 0 | match (attribute_name.as_slice(), value) { |
928 | 0 | (name::BLOCK_TYPE, Text(value)) => block_type = Some(attribute::BlockType::parse(value)?), |
929 | 0 | (name::TILES, TileDescription(value)) => tiles = Some(value), |
930 | 0 | (name::CHANNELS, ChannelList(value)) => channels = Some(value), |
931 | 0 | (name::COMPRESSION, Compression(value)) => compression = Some(value), |
932 | 0 | (name::DATA_WINDOW, IntegerBounds(value)) => data_window = Some(value), |
933 | 0 | (name::DISPLAY_WINDOW, IntegerBounds(value)) => display_window = Some(value), |
934 | 0 | (name::LINE_ORDER, LineOrder(value)) => line_order = Some(value), |
935 | 0 | (name::DEEP_DATA_VERSION, I32(value)) => version = Some(value), |
936 | | |
937 | 0 | (name::MAX_SAMPLES, I32(value)) => max_samples_per_pixel = Some( |
938 | 0 | i32_to_usize(value, "max sample count")? |
939 | | ), |
940 | | |
941 | 0 | (name::CHUNKS, I32(value)) => chunk_count = Some( |
942 | 0 | i32_to_usize(value, "chunk count")? |
943 | | ), |
944 | | |
945 | 0 | (name::NAME, Text(value)) => layer_attributes.layer_name = Some(value), |
946 | 0 | (name::WINDOW_CENTER, FloatVec2(value)) => layer_attributes.screen_window_center = value, |
947 | 0 | (name::WINDOW_WIDTH, F32(value)) => layer_attributes.screen_window_width = value, |
948 | | |
949 | 0 | (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 | 0 | (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 | 0 | (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 | 0 | 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 | 0 | (_, other) => other, |
1017 | | // FIXME dwa compression level gets lost if any other compression is used later in the process |
1018 | | }; |
1019 | | |
1020 | 0 | let compression = compression.ok_or(missing_attribute("compression"))?; |
1021 | 0 | image_attributes.display_window = display_window.ok_or(missing_attribute("display window"))?; |
1022 | | |
1023 | 0 | let data_window = data_window.ok_or(missing_attribute("data window"))?; |
1024 | 0 | data_window.validate(None)?; // validate now to avoid errors when computing the chunk_count |
1025 | 0 | layer_attributes.layer_position = data_window.position; |
1026 | | |
1027 | | |
1028 | | // validate now to avoid errors when computing the chunk_count |
1029 | 0 | if let Some(tiles) = tiles { tiles.validate()?; } |
1030 | 0 | 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 | 0 | BlockDescription::Tiles(tiles.ok_or(missing_attribute("tiles"))?) |
1036 | | }, |
1037 | | |
1038 | 0 | _ => BlockDescription::ScanLines, |
1039 | | }; |
1040 | | |
1041 | 0 | let computed_chunk_count = compute_chunk_count(compression, data_window.size, blocks); |
1042 | 0 | 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 | 0 | } |
1045 | | |
1046 | 0 | let header = Header { |
1047 | 0 | compression, |
1048 | | |
1049 | | // always compute ourselves, because we cannot trust anyone out there 😱 |
1050 | 0 | chunk_count: computed_chunk_count, |
1051 | | |
1052 | 0 | layer_size: data_window.size, |
1053 | | |
1054 | 0 | shared_attributes: image_attributes, |
1055 | 0 | own_attributes: layer_attributes, |
1056 | | |
1057 | 0 | channels: channels.ok_or(missing_attribute("channels"))?, |
1058 | 0 | line_order: line_order.unwrap_or(LineOrder::Unspecified), |
1059 | | |
1060 | 0 | blocks, |
1061 | 0 | max_samples_per_pixel, |
1062 | 0 | deep_data_version: version, |
1063 | 0 | deep: block_type == Some(BlockType::DeepScanLine) || block_type == Some(BlockType::DeepTile), |
1064 | | }; |
1065 | | |
1066 | 0 | Ok(header) |
1067 | 0 | } Unexecuted instantiation: <exr::meta::header::Header>::read::<exr::io::Tracking<std::io::cursor::Cursor<&[u8]>>> Unexecuted instantiation: <exr::meta::header::Header>::read::<_> Unexecuted instantiation: <exr::meta::header::Header>::read::<exr::io::Tracking<std::io::cursor::Cursor<alloc::vec::Vec<u8>>>> |
1068 | | |
1069 | | /// Without validation, write this instance to the byte stream. |
1070 | 0 | pub fn write(&self, write: &mut impl Write) -> UnitResult { |
1071 | 0 | for (name, value) in self.all_named_attributes() { |
1072 | 0 | attribute::write(name, &value, write)?; |
1073 | | } |
1074 | | |
1075 | 0 | sequence_end::write(write)?; |
1076 | 0 | Ok(()) |
1077 | 0 | } 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>>>> Unexecuted instantiation: <exr::meta::header::Header>::write::<exr::io::Tracking<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>> |
1078 | | |
1079 | | /// The rectangle describing the bounding box of this layer |
1080 | | /// within the infinite global 2D space of the file. |
1081 | 0 | pub fn data_window(&self) -> IntegerBounds { |
1082 | 0 | IntegerBounds::new(self.own_attributes.layer_position, self.layer_size) |
1083 | 0 | } |
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 | 0 | fn default() -> Self { |
1161 | 0 | Self { |
1162 | 0 | layer_position: Vec2(0, 0), |
1163 | 0 | screen_window_center: Vec2(0.0, 0.0), |
1164 | 0 | screen_window_width: 1.0, |
1165 | 0 | layer_name: None, |
1166 | 0 | white_luminance: None, |
1167 | 0 | adopted_neutral: None, |
1168 | 0 | rendering_transform_name: None, |
1169 | 0 | look_modification_transform_name: None, |
1170 | 0 | horizontal_density: None, |
1171 | 0 | owner: None, |
1172 | 0 | comments: None, |
1173 | 0 | capture_date: None, |
1174 | 0 | utc_offset: None, |
1175 | 0 | longitude: None, |
1176 | 0 | latitude: None, |
1177 | 0 | altitude: None, |
1178 | 0 | focus: None, |
1179 | 0 | exposure: None, |
1180 | 0 | aperture: None, |
1181 | 0 | iso_speed: None, |
1182 | 0 | environment_map: None, |
1183 | 0 | film_key_code: None, |
1184 | 0 | wrap_mode_name: None, |
1185 | 0 | frames_per_second: None, |
1186 | 0 | multi_view_names: None, |
1187 | 0 | world_to_camera: None, |
1188 | 0 | world_to_normalized_device: None, |
1189 | 0 | deep_image_state: None, |
1190 | 0 | original_data_window: None, |
1191 | 0 | preview: None, |
1192 | 0 | view_name: None, |
1193 | 0 | software_name: None, |
1194 | 0 | near_clip_plane: None, |
1195 | 0 | far_clip_plane: None, |
1196 | 0 | horizontal_field_of_view: None, |
1197 | 0 | vertical_field_of_view: None, |
1198 | 0 | other: Default::default() |
1199 | 0 | } |
1200 | 0 | } |
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 | | } |