Coverage Report

Created: 2026-01-09 07:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}