1use std::collections::HashMap;
5
6use crate::meta::attribute::*; use crate::{math::Vec2, meta::*};
8
9#[derive(Clone, Debug, PartialEq)]
15pub struct Header {
16 pub channels: ChannelList,
18
19 pub compression: Compression,
22
23 pub blocks: BlockDescription,
33
34 pub line_order: LineOrder,
36
37 pub layer_size: Vec2<usize>,
40
41 pub deep: bool,
43
44 pub deep_data_version: Option<i32>,
46
47 pub chunk_count: usize,
59
60 pub max_samples_per_pixel: Option<usize>,
72
73 pub shared_attributes: ImageAttributes,
76
77 pub own_attributes: LayerAttributes,
80}
81
82#[derive(Clone, PartialEq, Debug)]
86pub struct ImageAttributes {
87 pub display_window: IntegerBounds,
90
91 pub pixel_aspect: f32,
93
94 pub chromaticities: Option<Chromaticities>,
97
98 pub time_code: Option<TimeCode>,
100
101 pub other: HashMap<Text, AttributeValue>,
106}
107
108#[derive(Clone, PartialEq)]
112pub struct LayerAttributes {
113 pub layer_name: Option<Text>,
117
118 pub layer_position: Vec2<i32>,
122
123 pub screen_window_center: Vec2<f32>,
126
127 pub screen_window_width: f32,
130
131 pub white_luminance: Option<f32>,
139
140 pub adopted_neutral: Option<Vec2<f32>>,
146
147 pub rendering_transform_name: Option<Text>,
150
151 pub look_modification_transform_name: Option<Text>,
154
155 pub horizontal_density: Option<f32>,
159
160 pub owner: Option<Text>,
162
163 pub comments: Option<Text>,
165
166 pub capture_date: Option<Text>,
169
170 pub utc_offset: Option<f32>,
172
173 pub longitude: Option<f32>,
175
176 pub latitude: Option<f32>,
178
179 pub altitude: Option<f32>,
181
182 pub focus: Option<f32>,
184
185 pub exposure: Option<f32>,
187
188 pub aperture: Option<f32>,
191
192 pub iso_speed: Option<f32>,
194
195 pub environment_map: Option<EnvironmentMap>,
197
198 pub film_key_code: Option<KeyCode>,
201
202 pub wrap_mode_name: Option<Text>,
205
206 pub frames_per_second: Option<Rational>,
208
209 pub multi_view_names: Option<Vec<Text>>,
212
213 pub world_to_camera: Option<Matrix4x4>,
216
217 pub world_to_normalized_device: Option<Matrix4x4>,
221
222 pub deep_image_state: Option<Rational>,
225
226 pub original_data_window: Option<IntegerBounds>,
228
229 pub preview: Option<Preview>,
231
232 pub view_name: Option<Text>,
235
236 pub software_name: Option<Text>,
238
239 pub near_clip_plane: Option<f32>,
241
242 pub far_clip_plane: Option<f32>,
244
245 pub horizontal_field_of_view: Option<f32>,
247
248 pub vertical_field_of_view: Option<f32>,
250
251 pub other: HashMap<Text, AttributeValue>,
257}
258
259impl LayerAttributes {
260 pub fn named(layer_name: impl Into<Text>) -> Self {
262 Self {
263 layer_name: Some(layer_name.into()),
264 ..Self::default()
265 }
266 }
267
268 #[must_use]
270 pub fn with_position(self, data_position: Vec2<i32>) -> Self {
271 Self {
272 layer_position: data_position,
273 ..self
274 }
275 }
276
277 pub fn with_camera_frustum(
279 self,
280 world_to_camera: Matrix4x4,
281 world_to_normalized_device: Matrix4x4,
282 field_of_view: impl Into<Vec2<f32>>,
283 depth_clip_range: std::ops::Range<f32>,
284 ) -> Self {
285 let fov = field_of_view.into();
286
287 Self {
288 world_to_normalized_device: Some(world_to_normalized_device),
289 world_to_camera: Some(world_to_camera),
290 horizontal_field_of_view: Some(fov.x()),
291 vertical_field_of_view: Some(fov.y()),
292 near_clip_plane: Some(depth_clip_range.start),
293 far_clip_plane: Some(depth_clip_range.end),
294 ..self
295 }
296 }
297}
298
299impl ImageAttributes {
300 #[must_use]
302 pub fn new(display_window: IntegerBounds) -> Self {
303 Self {
304 pixel_aspect: 1.0,
305 chromaticities: None,
306 time_code: None,
307 other: Default::default(),
308 display_window,
309 }
310 }
311
312 pub fn with_size(size: impl Into<Vec2<usize>>) -> Self {
315 Self::new(IntegerBounds::from_dimensions(size))
316 }
317}
318
319impl Header {
320 pub fn new(
331 name: Text,
332 data_size: impl Into<Vec2<usize>>,
333 channels: SmallVec<[ChannelDescription; 5]>,
334 ) -> Self {
335 let data_size: Vec2<usize> = data_size.into();
336
337 let compression = Compression::RLE;
338 let blocks = BlockDescription::Tiles(TileDescription {
339 tile_size: Vec2(64, 64),
340 level_mode: LevelMode::Singular,
341 rounding_mode: RoundingMode::Down,
342 });
343
344 Self {
345 layer_size: data_size,
346 compression,
347 blocks,
348
349 channels: ChannelList::new(channels),
350 line_order: LineOrder::Unspecified,
351
352 shared_attributes: ImageAttributes::with_size(data_size),
353 own_attributes: LayerAttributes::named(name),
354
355 chunk_count: compute_chunk_count(compression, data_size, blocks),
356
357 deep: false,
358 deep_data_version: None,
359 max_samples_per_pixel: None,
360 }
361 }
362
363 #[must_use]
366 pub const fn with_display_window(mut self, display_window: IntegerBounds) -> Self {
367 self.shared_attributes.display_window = display_window;
368 self
369 }
370
371 #[must_use]
373 pub const fn with_position(mut self, position: Vec2<i32>) -> Self {
374 self.own_attributes.layer_position = position;
375 self
376 }
377
378 #[must_use]
381 pub fn with_encoding(
382 self,
383 compression: Compression,
384 blocks: BlockDescription,
385 line_order: LineOrder,
386 ) -> Self {
387 Self {
388 chunk_count: compute_chunk_count(compression, self.layer_size, blocks),
389 compression,
390 blocks,
391 line_order,
392 ..self
393 }
394 }
395
396 #[must_use]
399 pub fn with_attributes(self, own_attributes: LayerAttributes) -> Self {
400 Self {
401 own_attributes,
402 ..self
403 }
404 }
405
406 #[must_use]
409 pub fn with_shared_attributes(self, shared_attributes: ImageAttributes) -> Self {
410 Self {
411 shared_attributes,
412 ..self
413 }
414 }
415
416 pub fn enumerate_ordered_blocks(&self) -> impl Iterator<Item = (usize, TileIndices)> + Send {
421 let increasing_y = self.blocks_increasing_y_order().enumerate();
422
423 let ordered: Box<dyn Send + Iterator<Item = (usize, TileIndices)>> = {
425 if self.line_order == LineOrder::Decreasing {
426 Box::new(increasing_y.rev())
427 } else {
428 Box::new(increasing_y)
429 }
430 };
431
432 ordered
433 }
434
435 pub fn blocks_increasing_y_order(
457 &self,
458 ) -> impl ExactSizeIterator<Item = TileIndices> + DoubleEndedIterator {
459 fn tiles_of(
460 image_size: Vec2<usize>,
461 tile_size: Vec2<usize>,
462 level_index: Vec2<usize>,
463 ) -> impl Iterator<Item = TileIndices> {
464 fn divide_and_rest(
465 total_size: usize,
466 block_size: usize,
467 ) -> impl Iterator<Item = (usize, usize)> {
468 let block_count = compute_block_count(total_size, block_size);
469 (0..block_count).map(move |block_index| {
470 (
471 block_index,
472 calculate_block_size(total_size, block_size, block_index)
473 .expect("block size calculation bug"),
474 )
475 })
476 }
477
478 divide_and_rest(image_size.height(), tile_size.height()).flat_map(
479 move |(y_index, tile_height)| {
480 divide_and_rest(image_size.width(), tile_size.width()).map(
481 move |(x_index, tile_width)| TileIndices {
482 size: Vec2(tile_width, tile_height),
483 location: TileCoordinates {
484 tile_index: Vec2(x_index, y_index),
485 level_index,
486 },
487 },
488 )
489 },
490 )
491 }
492
493 let vec: Vec<TileIndices> = {
494 if let BlockDescription::Tiles(tiles) = self.blocks {
495 match tiles.level_mode {
496 LevelMode::Singular => {
497 tiles_of(self.layer_size, tiles.tile_size, Vec2(0, 0)).collect()
498 }
499 LevelMode::MipMap => mip_map_levels(tiles.rounding_mode, self.layer_size)
500 .flat_map(move |(level_index, level_size)| {
501 tiles_of(level_size, tiles.tile_size, Vec2(level_index, level_index))
502 })
503 .collect(),
504 LevelMode::RipMap => rip_map_levels(tiles.rounding_mode, self.layer_size)
505 .flat_map(move |(level_index, level_size)| {
506 tiles_of(level_size, tiles.tile_size, level_index)
507 })
508 .collect(),
509 }
510 } else {
511 let tiles = Vec2(self.layer_size.0, self.compression.scan_lines_per_block());
512 tiles_of(self.layer_size, tiles, Vec2(0, 0)).collect()
513 }
514 };
515
516 vec.into_iter() }
518
519 pub const fn max_block_pixel_size(&self) -> Vec2<usize> {
541 match self.blocks {
542 BlockDescription::ScanLines => {
543 Vec2(self.layer_size.0, self.compression.scan_lines_per_block())
544 }
545 BlockDescription::Tiles(tiles) => tiles.tile_size,
546 }
547 }
548
549 pub fn get_block_data_window_pixel_coordinates(
552 &self,
553 tile: TileCoordinates,
554 ) -> Result<IntegerBounds> {
555 let data = self.get_absolute_block_pixel_coordinates(tile)?;
556 Ok(data.with_origin(self.own_attributes.layer_position))
557 }
558
559 pub fn get_absolute_block_pixel_coordinates(
562 &self,
563 tile: TileCoordinates,
564 ) -> Result<IntegerBounds> {
565 if let BlockDescription::Tiles(tiles) = self.blocks {
566 let Vec2(data_width, data_height) = self.layer_size;
567
568 let data_width =
569 compute_level_size(tiles.rounding_mode, data_width, tile.level_index.x());
570 let data_height =
571 compute_level_size(tiles.rounding_mode, data_height, tile.level_index.y());
572 let absolute_tile_coordinates =
573 tile.to_data_indices(tiles.tile_size, Vec2(data_width, data_height))?;
574
575 if i64::from(absolute_tile_coordinates.position.x()) >= data_width as i64
576 || i64::from(absolute_tile_coordinates.position.y()) >= data_height as i64
577 {
578 return Err(Error::invalid("data block tile index"));
579 }
580
581 Ok(absolute_tile_coordinates)
582 } else {
583 debug_assert_eq!(tile.tile_index.0, 0, "block index calculation bug");
585
586 let (y, height) = calculate_block_position_and_size(
587 self.layer_size.height(),
588 self.compression.scan_lines_per_block(),
589 tile.tile_index.y(),
590 )?;
591
592 Ok(IntegerBounds {
593 position: Vec2(0, usize_to_i32(y, "tile y")?),
594 size: Vec2(self.layer_size.width(), height),
595 })
596 }
597
598 }
600
601 pub fn get_block_data_indices(&self, block: &CompressedBlock) -> Result<TileCoordinates> {
604 Ok(match block {
605 CompressedBlock::Tile(ref tile) => tile.coordinates,
606
607 CompressedBlock::ScanLine(ref block) => {
608 let size = self.compression.scan_lines_per_block() as i32;
609
610 let diff = block
611 .y_coordinate
612 .checked_sub(self.own_attributes.layer_position.y())
613 .ok_or_else(|| Error::invalid("invalid header"))?;
614 let y = diff.checked_div(size).ok_or_else(|| Error::invalid("invalid header"))?;
615
616 if y < 0 {
617 return Err(Error::invalid("scan block y coordinate"));
618 }
619
620 TileCoordinates {
621 tile_index: Vec2(0, y as usize),
622 level_index: Vec2(0, 0),
623 }
624 }
625
626 _ => return Err(Error::unsupported("deep data not supported yet")),
627 })
628 }
629
630 pub fn get_scan_line_block_tile_coordinates(
632 &self,
633 block_y_coordinate: i32,
634 ) -> Result<TileCoordinates> {
635 let size = self.compression.scan_lines_per_block() as i32;
636
637 let diff = block_y_coordinate
638 .checked_sub(self.own_attributes.layer_position.1)
639 .ok_or_else(|| Error::invalid("invalid header"))?;
640 let y = diff.checked_div(size).ok_or_else(|| Error::invalid("invalid header"))?;
641
642 if y < 0 {
643 return Err(Error::invalid("scan block y coordinate"));
644 }
645
646 Ok(TileCoordinates {
647 tile_index: Vec2(0, y as usize),
648 level_index: Vec2(0, 0),
649 })
650 }
651
652 pub fn max_block_byte_size(&self) -> usize {
655 self.channels.bytes_per_pixel
656 * match self.blocks {
657 BlockDescription::Tiles(tiles) => tiles.tile_size.area(),
658 BlockDescription::ScanLines => {
659 self.compression.scan_lines_per_block() * self.layer_size.width()
660 } }
662 }
663
664 pub fn total_pixel_bytes(&self) -> usize {
668 assert!(!self.deep);
669
670 let pixel_count_of_levels = |size: Vec2<usize>| -> usize {
671 match self.blocks {
672 BlockDescription::ScanLines => size.area(),
673 BlockDescription::Tiles(tile_description) => match tile_description.level_mode {
674 LevelMode::Singular => size.area(),
675
676 LevelMode::MipMap => mip_map_levels(tile_description.rounding_mode, size)
677 .map(|(_, size)| size.area())
678 .sum(),
679
680 LevelMode::RipMap => rip_map_levels(tile_description.rounding_mode, size)
681 .map(|(_, size)| size.area())
682 .sum(),
683 },
684 }
685 };
686
687 self.channels
688 .list
689 .iter()
690 .map(|channel: &ChannelDescription| {
691 pixel_count_of_levels(channel.subsampled_resolution(self.layer_size))
692 * channel.sample_type.bytes_per_sample()
693 })
694 .sum()
695 }
696
697 pub fn max_pixel_file_bytes(&self) -> usize {
701 assert!(!self.deep);
702
703 self.chunk_count * 64 + self.total_pixel_bytes()
705 }
706
707 pub fn validate(&self, is_multilayer: bool, long_names: &mut bool, strict: bool) -> UnitResult {
709 self.data_window().validate(None)?;
710 self.shared_attributes.display_window.validate(None)?;
711
712 if strict {
713 if is_multilayer && self.own_attributes.layer_name.is_none() {
714 return Err(missing_attribute("layer name for multi layer file"));
715 }
716
717 if self.blocks == BlockDescription::ScanLines
718 && self.line_order == LineOrder::Unspecified
719 {
720 return Err(Error::invalid("unspecified line order in scan line images"));
721 }
722
723 if self.layer_size == Vec2(0, 0) {
724 return Err(Error::invalid("empty data window"));
725 }
726
727 if self.shared_attributes.display_window.size == Vec2(0, 0) {
728 return Err(Error::invalid("empty display window"));
729 }
730
731 if !self.shared_attributes.pixel_aspect.is_normal()
732 || self.shared_attributes.pixel_aspect < 1.0e-6
733 || self.shared_attributes.pixel_aspect > 1.0e6
734 {
735 return Err(Error::invalid("pixel aspect ratio"));
736 }
737
738 if self.own_attributes.screen_window_width < 0.0 {
739 return Err(Error::invalid("screen window width"));
740 }
741 }
742
743 let allow_subsampling = !self.deep && self.blocks == BlockDescription::ScanLines;
744 self.channels.validate(allow_subsampling, self.data_window(), strict)?;
745
746 for (name, value) in &self.shared_attributes.other {
747 attribute::validate(
748 name,
749 value,
750 long_names,
751 allow_subsampling,
752 self.data_window(),
753 strict,
754 )?;
755 }
756
757 for (name, value) in &self.own_attributes.other {
758 attribute::validate(
759 name,
760 value,
761 long_names,
762 allow_subsampling,
763 self.data_window(),
764 strict,
765 )?;
766 }
767
768 if self.chunk_count != compute_chunk_count(self.compression, self.layer_size, self.blocks) {
771 return Err(Error::invalid("chunk count attribute")); }
773
774 if strict {
776 for name in self.shared_attributes.other.keys() {
777 if self.own_attributes.other.contains_key(name) {
778 return Err(Error::invalid(format!("duplicate attribute name: `{name}`")));
779 }
780 }
781
782 for &reserved in header::standard_names::ALL {
783 let name = Text::from_bytes_unchecked(SmallVec::from_slice(reserved));
784 if self.own_attributes.other.contains_key(&name)
785 || self.shared_attributes.other.contains_key(&name)
786 {
787 return Err(Error::invalid(format!(
788 "attribute name `{}` is reserved and cannot be custom",
789 Text::from_bytes_unchecked(reserved.into())
790 )));
791 }
792 }
793 }
794
795 if self.deep {
796 if strict {
797 if self.own_attributes.layer_name.is_none() {
798 return Err(missing_attribute("layer name for deep file"));
799 }
800
801 if self.max_samples_per_pixel.is_none() {
802 return Err(Error::invalid(
803 "missing max samples per pixel attribute for deepdata",
804 ));
805 }
806 }
807
808 match self.deep_data_version {
809 Some(1) => {}
810 Some(_) => return Err(Error::unsupported("deep data version")),
811 None => return Err(missing_attribute("deep data version")),
812 }
813
814 if !self.compression.supports_deep_data() {
815 return Err(Error::invalid("compression method does not support deep data"));
816 }
817 }
818
819 Ok(())
820 }
821
822 pub fn read_all(
824 read: &mut PeekRead<impl Read>,
825 version: &Requirements,
826 pedantic: bool,
827 ) -> Result<Headers> {
828 if version.is_multilayer() {
829 let mut headers = SmallVec::new();
830
831 while !sequence_end::has_come(read)? {
832 headers.push(Self::read(read, version, pedantic)?);
833 }
834
835 Ok(headers)
836 } else {
837 Ok(smallvec![Self::read(read, version, pedantic)?])
838 }
839 }
840
841 pub fn write_all(headers: &[Self], write: &mut impl Write, is_multilayer: bool) -> UnitResult {
843 for header in headers {
844 header.write(write)?;
845 }
846
847 if is_multilayer {
848 sequence_end::write(write)?;
849 }
850
851 Ok(())
852 }
853
854 #[inline]
863 pub fn all_named_attributes(&self) -> impl '_ + Iterator<Item = (&TextSlice, AttributeValue)> {
864 use std::iter::{empty, once, once_with};
865
866 use AttributeValue::*;
867
868 use crate::meta::header::standard_names::*;
869
870 #[inline]
871 fn optional<'t, T: Clone>(
872 name: &'t TextSlice,
873 to_attribute: impl Fn(T) -> AttributeValue,
874 value: &'t Option<T>,
875 ) -> impl Iterator<Item = (&'t TextSlice, AttributeValue)> {
876 value.as_ref().map(move |value| (name, to_attribute(value.clone()))).into_iter()
877 }
878
879 #[inline]
880 fn required<'s, T: Clone>(
881 name: &'s TextSlice,
882 to_attribute: impl Fn(T) -> AttributeValue,
883 value: &'s T,
884 ) -> impl Iterator<Item = (&'s TextSlice, AttributeValue)> {
885 once((name, to_attribute((*value).clone())))
886 }
887
888 #[inline]
891 const fn expect_is_iter<'s, T: Iterator<Item = (&'s TextSlice, AttributeValue)>>(
892 val: T,
893 ) -> T {
894 val
895 }
896
897 macro_rules! iter_all {
898 ( $( $value:expr ),* ) => {
899 empty() $( .chain( $value ) )*
900 };
901 }
902
903 macro_rules! required_attributes {
904 ( $($name: ident : $variant: ident = $value: expr),* ) => {
905 expect_is_iter(iter_all!(
906 $( required($name, $variant, $value) ),*
907 ))
908 };
909 }
910
911 macro_rules! optional_attributes {
912 ( $($name: ident : $variant: ident = $value: expr),* ) => {
913 expect_is_iter(iter_all!(
914 $( optional($name, $variant, $value) ),*
915 ))
916 };
917 }
918
919 #[inline]
920 fn usize_as_i32(value: usize) -> AttributeValue {
921 I32(i32::try_from(value).expect("usize exceeds i32 range"))
922 }
923
924 let block_type_and_tiles = expect_is_iter(
925 once_with(move || {
926 let (block_type, tiles) = match self.blocks {
927 BlockDescription::ScanLines => (attribute::BlockType::ScanLine, None),
928 BlockDescription::Tiles(tiles) => (attribute::BlockType::Tile, Some(tiles)),
929 };
930
931 once((BLOCK_TYPE, BlockType(block_type)))
932 .chain(tiles.map(|tiles| (TILES, TileDescription(tiles))))
933 })
934 .flatten(),
935 );
936
937 let data_window =
938 expect_is_iter(once_with(move || (DATA_WINDOW, IntegerBounds(self.data_window()))));
939
940 let dwa_compr_level = expect_is_iter(
942 once_with(move || match self.compression {
943 attribute::Compression::DWAA(Some(level))
944 | attribute::Compression::DWAB(Some(level)) => {
945 Some((DWA_COMPRESSION_LEVEL, F32(level)))
946 }
947
948 _ => None,
949 })
950 .flatten(),
951 );
952
953 let opt_core_attrs = optional_attributes!(
954 DEEP_DATA_VERSION: I32 = &self.deep_data_version,
955 MAX_SAMPLES: usize_as_i32 = &self.max_samples_per_pixel
956 )
957 .chain(block_type_and_tiles)
958 .chain(dwa_compr_level);
959
960 let req_core_attrs = required_attributes!(
961 CHUNKS: usize_as_i32 = &self.chunk_count,
963
964 CHANNELS: ChannelList = &self.channels,
965 COMPRESSION: Compression = &self.compression,
966 LINE_ORDER: LineOrder = &self.line_order,
967
968 DISPLAY_WINDOW: IntegerBounds = &self.shared_attributes.display_window,
969 PIXEL_ASPECT: F32 = &self.shared_attributes.pixel_aspect,
970
971 WINDOW_CENTER: FloatVec2 = &self.own_attributes.screen_window_center,
972 WINDOW_WIDTH: F32 = &self.own_attributes.screen_window_width
973 )
974 .chain(data_window);
975
976 let opt_attr = optional_attributes!(
977 NAME: Text = &self.own_attributes.layer_name,
978 WHITE_LUMINANCE: F32 = &self.own_attributes.white_luminance,
979 ADOPTED_NEUTRAL: FloatVec2 = &self.own_attributes.adopted_neutral,
980 RENDERING_TRANSFORM: Text = &self.own_attributes.rendering_transform_name,
981 LOOK_MOD_TRANSFORM: Text = &self.own_attributes.look_modification_transform_name,
982 X_DENSITY: F32 = &self.own_attributes.horizontal_density,
983 OWNER: Text = &self.own_attributes.owner,
984 COMMENTS: Text = &self.own_attributes.comments,
985 CAPTURE_DATE: Text = &self.own_attributes.capture_date,
986 UTC_OFFSET: F32 = &self.own_attributes.utc_offset,
987 LONGITUDE: F32 = &self.own_attributes.longitude,
988 LATITUDE: F32 = &self.own_attributes.latitude,
989 ALTITUDE: F32 = &self.own_attributes.altitude,
990 FOCUS: F32 = &self.own_attributes.focus,
991 EXPOSURE_TIME: F32 = &self.own_attributes.exposure,
992 APERTURE: F32 = &self.own_attributes.aperture,
993 ISO_SPEED: F32 = &self.own_attributes.iso_speed,
994 ENVIRONMENT_MAP: EnvironmentMap = &self.own_attributes.environment_map,
995 KEY_CODE: KeyCode = &self.own_attributes.film_key_code,
996 TIME_CODE: TimeCode = &self.shared_attributes.time_code,
997 WRAP_MODES: Text = &self.own_attributes.wrap_mode_name,
998 FRAMES_PER_SECOND: Rational = &self.own_attributes.frames_per_second,
999 MULTI_VIEW: TextVector = &self.own_attributes.multi_view_names,
1000 WORLD_TO_CAMERA: Matrix4x4 = &self.own_attributes.world_to_camera,
1001 WORLD_TO_NDC: Matrix4x4 = &self.own_attributes.world_to_normalized_device,
1002 DEEP_IMAGE_STATE: Rational = &self.own_attributes.deep_image_state,
1003 ORIGINAL_DATA_WINDOW: IntegerBounds = &self.own_attributes.original_data_window,
1004 CHROMATICITIES: Chromaticities = &self.shared_attributes.chromaticities,
1005 PREVIEW: Preview = &self.own_attributes.preview,
1006 VIEW: Text = &self.own_attributes.view_name,
1007 NEAR: F32 = &self.own_attributes.near_clip_plane,
1008 FAR: F32 = &self.own_attributes.far_clip_plane,
1009 FOV_X: F32 = &self.own_attributes.horizontal_field_of_view,
1010 FOV_Y: F32 = &self.own_attributes.vertical_field_of_view,
1011 SOFTWARE: Text = &self.own_attributes.software_name
1012 );
1013
1014 let other = self
1015 .own_attributes
1016 .other
1017 .iter()
1018 .chain(self.shared_attributes.other.iter())
1019 .map(|(name, val)| (name.as_slice(), val.clone())); req_core_attrs.chain(opt_core_attrs).chain(opt_attr).chain(other)
1022 }
1023
1024 pub fn read(
1026 read: &mut PeekRead<impl Read>,
1027 requirements: &Requirements,
1028 pedantic: bool,
1029 ) -> Result<Self> {
1030 let max_string_len = if requirements.has_long_names {
1031 256
1032 } else {
1033 32
1034 }; let mut tiles = None;
1038 let mut block_type = None;
1039 let mut version = None;
1040 let mut chunk_count = None;
1041 let mut max_samples_per_pixel = None;
1042 let mut channels = None;
1043 let mut compression = None;
1044 let mut data_window = None;
1045 let mut display_window = None;
1046 let mut line_order = None;
1047 let mut dwa_compression_level = None;
1048
1049 let mut layer_attributes = LayerAttributes::default();
1050 let mut image_attributes = ImageAttributes::new(IntegerBounds::zero());
1051
1052 while !sequence_end::has_come(read)? {
1054 let (attribute_name, value) = attribute::read(read, max_string_len)?;
1055
1056 match value {
1058 Ok(value) => {
1059 use crate::meta::{
1060 attribute::AttributeValue::*, header::standard_names as name,
1061 };
1062
1063 match (attribute_name.as_slice(), value) {
1070 (name::BLOCK_TYPE, Text(value)) => {
1071 block_type = Some(attribute::BlockType::parse(value)?);
1072 }
1073 (name::TILES, TileDescription(value)) => tiles = Some(value),
1074 (name::CHANNELS, ChannelList(value)) => channels = Some(value),
1075 (name::COMPRESSION, Compression(value)) => compression = Some(value),
1076 (name::DATA_WINDOW, IntegerBounds(value)) => data_window = Some(value),
1077 (name::DISPLAY_WINDOW, IntegerBounds(value)) => {
1078 display_window = Some(value);
1079 }
1080 (name::LINE_ORDER, LineOrder(value)) => line_order = Some(value),
1081 (name::DEEP_DATA_VERSION, I32(value)) => version = Some(value),
1082
1083 (name::MAX_SAMPLES, I32(value)) => {
1084 max_samples_per_pixel = Some(i32_to_usize(value, "max sample count")?);
1085 }
1086
1087 (name::CHUNKS, I32(value)) => {
1088 chunk_count = Some(i32_to_usize(value, "chunk count")?);
1089 }
1090
1091 (name::NAME, Text(value)) => layer_attributes.layer_name = Some(value),
1092 (name::WINDOW_CENTER, FloatVec2(value)) => {
1093 layer_attributes.screen_window_center = value;
1094 }
1095 (name::WINDOW_WIDTH, F32(value)) => {
1096 layer_attributes.screen_window_width = value;
1097 }
1098
1099 (name::WHITE_LUMINANCE, F32(value)) => {
1100 layer_attributes.white_luminance = Some(value);
1101 }
1102 (name::ADOPTED_NEUTRAL, FloatVec2(value)) => {
1103 layer_attributes.adopted_neutral = Some(value);
1104 }
1105 (name::RENDERING_TRANSFORM, Text(value)) => {
1106 layer_attributes.rendering_transform_name = Some(value);
1107 }
1108 (name::LOOK_MOD_TRANSFORM, Text(value)) => {
1109 layer_attributes.look_modification_transform_name = Some(value);
1110 }
1111 (name::X_DENSITY, F32(value)) => {
1112 layer_attributes.horizontal_density = Some(value);
1113 }
1114
1115 (name::OWNER, Text(value)) => layer_attributes.owner = Some(value),
1116 (name::COMMENTS, Text(value)) => layer_attributes.comments = Some(value),
1117 (name::CAPTURE_DATE, Text(value)) => {
1118 layer_attributes.capture_date = Some(value);
1119 }
1120 (name::UTC_OFFSET, F32(value)) => layer_attributes.utc_offset = Some(value),
1121 (name::LONGITUDE, F32(value)) => layer_attributes.longitude = Some(value),
1122 (name::LATITUDE, F32(value)) => layer_attributes.latitude = Some(value),
1123 (name::ALTITUDE, F32(value)) => layer_attributes.altitude = Some(value),
1124 (name::FOCUS, F32(value)) => layer_attributes.focus = Some(value),
1125 (name::EXPOSURE_TIME, F32(value)) => {
1126 layer_attributes.exposure = Some(value);
1127 }
1128 (name::APERTURE, F32(value)) => layer_attributes.aperture = Some(value),
1129 (name::ISO_SPEED, F32(value)) => layer_attributes.iso_speed = Some(value),
1130 (name::ENVIRONMENT_MAP, EnvironmentMap(value)) => {
1131 layer_attributes.environment_map = Some(value);
1132 }
1133 (name::KEY_CODE, KeyCode(value)) => {
1134 layer_attributes.film_key_code = Some(value);
1135 }
1136 (name::WRAP_MODES, Text(value)) => {
1137 layer_attributes.wrap_mode_name = Some(value);
1138 }
1139 (name::FRAMES_PER_SECOND, Rational(value)) => {
1140 layer_attributes.frames_per_second = Some(value);
1141 }
1142 (name::MULTI_VIEW, TextVector(value)) => {
1143 layer_attributes.multi_view_names = Some(value);
1144 }
1145 (name::WORLD_TO_CAMERA, Matrix4x4(value)) => {
1146 layer_attributes.world_to_camera = Some(value);
1147 }
1148 (name::WORLD_TO_NDC, Matrix4x4(value)) => {
1149 layer_attributes.world_to_normalized_device = Some(value);
1150 }
1151 (name::DEEP_IMAGE_STATE, Rational(value)) => {
1152 layer_attributes.deep_image_state = Some(value);
1153 }
1154 (name::ORIGINAL_DATA_WINDOW, IntegerBounds(value)) => {
1155 layer_attributes.original_data_window = Some(value);
1156 }
1157 (name::DWA_COMPRESSION_LEVEL, F32(value)) => {
1158 dwa_compression_level = Some(value);
1159 }
1160 (name::PREVIEW, Preview(value)) => layer_attributes.preview = Some(value),
1161 (name::VIEW, Text(value)) => layer_attributes.view_name = Some(value),
1162
1163 (name::NEAR, F32(value)) => layer_attributes.near_clip_plane = Some(value),
1164 (name::FAR, F32(value)) => layer_attributes.far_clip_plane = Some(value),
1165 (name::FOV_X, F32(value)) => {
1166 layer_attributes.horizontal_field_of_view = Some(value);
1167 }
1168 (name::FOV_Y, F32(value)) => {
1169 layer_attributes.vertical_field_of_view = Some(value);
1170 }
1171 (name::SOFTWARE, Text(value)) => {
1172 layer_attributes.software_name = Some(value);
1173 }
1174
1175 (name::PIXEL_ASPECT, F32(value)) => image_attributes.pixel_aspect = value,
1176 (name::TIME_CODE, TimeCode(value)) => {
1177 image_attributes.time_code = Some(value);
1178 }
1179 (name::CHROMATICITIES, Chromaticities(value)) => {
1180 image_attributes.chromaticities = Some(value);
1181 }
1182
1183 (_, value @ (Chromaticities(_) | TimeCode(_))) => {
1186 image_attributes.other.insert(attribute_name, value);
1187 }
1188
1189 (_, value) => {
1191 layer_attributes.other.insert(attribute_name, value);
1192 }
1193 }
1194 }
1195
1196 Err(error) => {
1199 if pedantic {
1200 return Err(error);
1201 }
1202 }
1203 }
1204 }
1205
1206 let compression = match (dwa_compression_level, compression) {
1208 (Some(level), Some(Compression::DWAA(_))) => Some(Compression::DWAA(Some(level))),
1209 (Some(level), Some(Compression::DWAB(_))) => Some(Compression::DWAB(Some(level))),
1210 (_, other) => other,
1211 };
1214
1215 let compression = compression.ok_or_else(|| missing_attribute("compression"))?;
1216 image_attributes.display_window =
1217 display_window.ok_or_else(|| missing_attribute("display window"))?;
1218
1219 let data_window = data_window.ok_or_else(|| missing_attribute("data window"))?;
1220 data_window.validate(None)?; layer_attributes.layer_position = data_window.position;
1222
1223 if let Some(tiles) = tiles {
1225 tiles.validate()?;
1226 }
1227 let blocks = match block_type {
1228 None if requirements.is_single_layer_and_tiled => {
1229 BlockDescription::Tiles(tiles.ok_or_else(|| missing_attribute("tiles"))?)
1230 }
1231 Some(BlockType::Tile | BlockType::DeepTile) => {
1232 BlockDescription::Tiles(tiles.ok_or_else(|| missing_attribute("tiles"))?)
1233 }
1234
1235 _ => BlockDescription::ScanLines,
1236 };
1237
1238 let computed_chunk_count = compute_chunk_count(compression, data_window.size, blocks);
1239 if chunk_count.is_some() && pedantic && chunk_count != Some(computed_chunk_count) {
1240 return Err(Error::invalid("chunk count not matching data size"));
1241 }
1242
1243 let header = Self {
1244 compression,
1245
1246 chunk_count: computed_chunk_count,
1248
1249 layer_size: data_window.size,
1250
1251 shared_attributes: image_attributes,
1252 own_attributes: layer_attributes,
1253
1254 channels: channels.ok_or_else(|| missing_attribute("channels"))?,
1255 line_order: line_order.unwrap_or(LineOrder::Unspecified),
1256
1257 blocks,
1258 max_samples_per_pixel,
1259 deep_data_version: version,
1260 deep: block_type == Some(BlockType::DeepScanLine)
1261 || block_type == Some(BlockType::DeepTile),
1262 };
1263
1264 Ok(header)
1265 }
1266
1267 pub fn write(&self, write: &mut impl Write) -> UnitResult {
1269 for (name, value) in self.all_named_attributes() {
1270 attribute::write(name, &value, write)?;
1271 }
1272
1273 sequence_end::write(write)?;
1274 Ok(())
1275 }
1276
1277 pub fn data_window(&self) -> IntegerBounds {
1280 IntegerBounds::new(self.own_attributes.layer_position, self.layer_size)
1281 }
1282}
1283
1284pub mod standard_names {
1286 macro_rules! define_required_attribute_names {
1287 ( $($name: ident : $value: expr),* ) => {
1288
1289 pub const ALL: &'static [&'static [u8]] = &[
1291 $( $value ),*
1292 ];
1293
1294 $(
1295 pub const $name: &'static [u8] = $value;
1297 )*
1298 };
1299 }
1300
1301 define_required_attribute_names! {
1302 TILES: b"tiles",
1303 NAME: b"name",
1304 BLOCK_TYPE: b"type",
1305 DEEP_DATA_VERSION: b"version",
1306 CHUNKS: b"chunkCount",
1307 MAX_SAMPLES: b"maxSamplesPerPixel",
1308 CHANNELS: b"channels",
1309 COMPRESSION: b"compression",
1310 DATA_WINDOW: b"dataWindow",
1311 DISPLAY_WINDOW: b"displayWindow",
1312 LINE_ORDER: b"lineOrder",
1313 PIXEL_ASPECT: b"pixelAspectRatio",
1314 WINDOW_CENTER: b"screenWindowCenter",
1315 WINDOW_WIDTH: b"screenWindowWidth",
1316 WHITE_LUMINANCE: b"whiteLuminance",
1317 ADOPTED_NEUTRAL: b"adoptedNeutral",
1318 RENDERING_TRANSFORM: b"renderingTransform",
1319 LOOK_MOD_TRANSFORM: b"lookModTransform",
1320 X_DENSITY: b"xDensity",
1321 OWNER: b"owner",
1322 COMMENTS: b"comments",
1323 CAPTURE_DATE: b"capDate",
1324 UTC_OFFSET: b"utcOffset",
1325 LONGITUDE: b"longitude",
1326 LATITUDE: b"latitude",
1327 ALTITUDE: b"altitude",
1328 FOCUS: b"focus",
1329 EXPOSURE_TIME: b"expTime",
1330 APERTURE: b"aperture",
1331 ISO_SPEED: b"isoSpeed",
1332 ENVIRONMENT_MAP: b"envmap",
1333 KEY_CODE: b"keyCode",
1334 TIME_CODE: b"timeCode",
1335 WRAP_MODES: b"wrapmodes",
1336 FRAMES_PER_SECOND: b"framesPerSecond",
1337 MULTI_VIEW: b"multiView",
1338 WORLD_TO_CAMERA: b"worldToCamera",
1339 WORLD_TO_NDC: b"worldToNDC",
1340 DEEP_IMAGE_STATE: b"deepImageState",
1341 ORIGINAL_DATA_WINDOW: b"originalDataWindow",
1342 DWA_COMPRESSION_LEVEL: b"dwaCompressionLevel",
1343 PREVIEW: b"preview",
1344 VIEW: b"view",
1345 CHROMATICITIES: b"chromaticities",
1346 NEAR: b"near",
1347 FAR: b"far",
1348 FOV_X: b"fieldOfViewHorizontal",
1349 FOV_Y: b"fieldOfViewVertical",
1350 SOFTWARE: b"software"
1351 }
1352}
1353
1354impl Default for LayerAttributes {
1355 fn default() -> Self {
1356 Self {
1357 layer_position: Vec2(0, 0),
1358 screen_window_center: Vec2(0.0, 0.0),
1359 screen_window_width: 1.0,
1360 layer_name: None,
1361 white_luminance: None,
1362 adopted_neutral: None,
1363 rendering_transform_name: None,
1364 look_modification_transform_name: None,
1365 horizontal_density: None,
1366 owner: None,
1367 comments: None,
1368 capture_date: None,
1369 utc_offset: None,
1370 longitude: None,
1371 latitude: None,
1372 altitude: None,
1373 focus: None,
1374 exposure: None,
1375 aperture: None,
1376 iso_speed: None,
1377 environment_map: None,
1378 film_key_code: None,
1379 wrap_mode_name: None,
1380 frames_per_second: None,
1381 multi_view_names: None,
1382 world_to_camera: None,
1383 world_to_normalized_device: None,
1384 deep_image_state: None,
1385 original_data_window: None,
1386 preview: None,
1387 view_name: None,
1388 software_name: None,
1389 near_clip_plane: None,
1390 far_clip_plane: None,
1391 horizontal_field_of_view: None,
1392 vertical_field_of_view: None,
1393 other: Default::default(),
1394 }
1395 }
1396}
1397
1398impl std::fmt::Debug for LayerAttributes {
1399 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1400 let default_self = Self::default();
1401
1402 let mut debug = formatter.debug_struct("LayerAttributes (default values omitted)");
1403
1404 debug.field("name", &self.layer_name);
1406
1407 macro_rules! debug_non_default_fields {
1408 ( $( $name: ident ),* ) => { $(
1409
1410 if self.$name != default_self.$name {
1411 debug.field(stringify!($name), &self.$name);
1412 }
1413
1414 )* };
1415 }
1416
1417 debug_non_default_fields! {
1419 screen_window_center, screen_window_width,
1420 white_luminance, adopted_neutral, horizontal_density,
1421 rendering_transform_name, look_modification_transform_name,
1422 owner, comments,
1423 capture_date, utc_offset,
1424 longitude, latitude, altitude,
1425 focus, exposure, aperture, iso_speed,
1426 environment_map, film_key_code, wrap_mode_name,
1427 frames_per_second, multi_view_names,
1428 world_to_camera, world_to_normalized_device,
1429 deep_image_state, original_data_window,
1430 preview, view_name,
1431 vertical_field_of_view, horizontal_field_of_view,
1432 near_clip_plane, far_clip_plane, software_name
1433 }
1434
1435 for (name, value) in &self.other {
1436 debug.field(&format!("\"{name}\""), value);
1437 }
1438
1439 debug.finish()
1441 }
1442}