1
2pub mod attribute;
7pub mod header;
8
9
10use crate::io::*;
11use ::smallvec::SmallVec;
12use self::attribute::*;
13use crate::block::chunk::{TileCoordinates, CompressedBlock};
14use crate::error::*;
15use std::fs::File;
16use std::io::{BufReader};
17use crate::math::*;
18use std::collections::{HashSet};
19use std::convert::TryFrom;
20use crate::meta::header::{Header};
21use crate::block::{BlockIndex, UncompressedBlock};
22
23
24#[derive(Debug, Clone, PartialEq)]
32pub struct MetaData {
33
34 pub requirements: Requirements,
36
37 pub headers: Headers,
40}
41
42
43pub type Headers = SmallVec<[Header; 3]>;
45
46pub type OffsetTables = SmallVec<[OffsetTable; 3]>;
48
49
50pub type OffsetTable = Vec<u64>;
62
63
64#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
68pub struct Requirements {
69
70 pub file_format_version: u8,
73
74 pub is_single_layer_and_tiled: bool,
77
78 pub has_long_names: bool,
82
83 pub has_deep_data: bool,
85
86 pub has_multiple_layers: bool,
88}
89
90
91#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
93pub struct TileIndices {
94
95 pub location: TileCoordinates,
97
98 pub size: Vec2<usize>,
100}
101
102#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
104pub enum BlockDescription {
105
106 ScanLines,
109
110 Tiles(TileDescription)
114}
115
116
117impl BlockDescription {
142
143 pub fn has_tiles(&self) -> bool {
145 match self {
146 BlockDescription::Tiles { .. } => true,
147 _ => false
148 }
149 }
150}
151
152
153
154
155
156pub mod magic_number {
159 use super::*;
160
161 pub const BYTES: [u8; 4] = [0x76, 0x2f, 0x31, 0x01];
163
164 pub fn write(write: &mut impl Write) -> Result<()> {
166 u8::write_slice(write, &self::BYTES)
167 }
168
169 pub fn is_exr(read: &mut impl Read) -> Result<bool> {
172 let mut magic_num = [0; 4];
173 u8::read_slice(read, &mut magic_num)?;
174 Ok(magic_num == self::BYTES)
175 }
176
177 pub fn validate_exr(read: &mut impl Read) -> UnitResult {
179 if self::is_exr(read)? {
180 Ok(())
181
182 } else {
183 Err(Error::invalid("file identifier missing"))
184 }
185 }
186}
187
188pub mod sequence_end {
190 use super::*;
191
192 pub fn byte_size() -> usize {
194 1
195 }
196
197 pub fn write<W: Write>(write: &mut W) -> UnitResult {
199 0_u8.write(write)
200 }
201
202 pub fn has_come(read: &mut PeekRead<impl Read>) -> Result<bool> {
204 Ok(read.skip_if_eq(0)?)
205 }
206}
207
208fn missing_attribute(name: &str) -> Error {
209 Error::invalid(format!("missing or invalid {} attribute", name))
210}
211
212
213pub fn compute_block_count(full_res: usize, tile_size: usize) -> usize {
215 RoundingMode::Up.divide(full_res, tile_size)
218}
219
220#[inline]
222pub fn calculate_block_position_and_size(total_size: usize, block_size: usize, block_index: usize) -> Result<(usize, usize)> {
223 let block_position = block_size * block_index;
224
225 Ok((
226 block_position,
227 calculate_block_size(total_size, block_size, block_position)?
228 ))
229}
230
231#[inline]
235pub fn calculate_block_size(total_size: usize, block_size: usize, block_position: usize) -> Result<usize> {
236 if block_position >= total_size {
237 return Err(Error::invalid("block index"))
238 }
239
240 if block_position + block_size <= total_size {
241 Ok(block_size)
242 }
243 else {
244 Ok(total_size - block_position)
245 }
246}
247
248
249pub fn compute_level_count(round: RoundingMode, full_res: usize) -> usize {
252 usize::try_from(round.log2(u32::try_from(full_res).unwrap())).unwrap() + 1
253}
254
255pub fn compute_level_size(round: RoundingMode, full_res: usize, level_index: usize) -> usize {
258 assert!(level_index < std::mem::size_of::<usize>() * 8, "largest level size exceeds maximum integer value");
259 round.divide(full_res, 1 << level_index).max(1)
260}
261
262pub fn rip_map_levels(round: RoundingMode, max_resolution: Vec2<usize>) -> impl Iterator<Item=(Vec2<usize>, Vec2<usize>)> {
267 rip_map_indices(round, max_resolution).map(move |level_indices|{
268 let width = compute_level_size(round, max_resolution.width(), level_indices.x());
270 let height = compute_level_size(round, max_resolution.height(), level_indices.y());
271 (level_indices, Vec2(width, height))
272 })
273}
274
275pub fn mip_map_levels(round: RoundingMode, max_resolution: Vec2<usize>) -> impl Iterator<Item=(usize, Vec2<usize>)> {
280 mip_map_indices(round, max_resolution)
281 .map(move |level_index|{
282 let width = compute_level_size(round, max_resolution.width(), level_index);
284 let height = compute_level_size(round, max_resolution.height(), level_index);
285 (level_index, Vec2(width, height))
286 })
287}
288
289pub fn rip_map_indices(round: RoundingMode, max_resolution: Vec2<usize>) -> impl Iterator<Item=Vec2<usize>> {
292 let (width, height) = (
293 compute_level_count(round, max_resolution.width()),
294 compute_level_count(round, max_resolution.height())
295 );
296
297 (0..height).flat_map(move |y_level|{
298 (0..width).map(move |x_level|{
299 Vec2(x_level, y_level)
300 })
301 })
302}
303
304pub fn mip_map_indices(round: RoundingMode, max_resolution: Vec2<usize>) -> impl Iterator<Item=usize> {
307 0..compute_level_count(round, max_resolution.width().max(max_resolution.height()))
308}
309
310pub fn compute_chunk_count(compression: Compression, data_size: Vec2<usize>, blocks: BlockDescription) -> usize {
315
316 if let BlockDescription::Tiles(tiles) = blocks {
317 let round = tiles.rounding_mode;
318 let Vec2(tile_width, tile_height) = tiles.tile_size;
319
320 use crate::meta::attribute::LevelMode::*;
322 match tiles.level_mode {
323 Singular => {
324 let tiles_x = compute_block_count(data_size.width(), tile_width);
325 let tiles_y = compute_block_count(data_size.height(), tile_height);
326 tiles_x * tiles_y
327 }
328
329 MipMap => {
330 mip_map_levels(round, data_size).map(|(_, Vec2(level_width, level_height))| {
331 compute_block_count(level_width, tile_width) * compute_block_count(level_height, tile_height)
332 }).sum()
333 },
334
335 RipMap => {
336 rip_map_levels(round, data_size).map(|(_, Vec2(level_width, level_height))| {
337 compute_block_count(level_width, tile_width) * compute_block_count(level_height, tile_height)
338 }).sum()
339 }
340 }
341 }
342
343 else {
345 compute_block_count(data_size.height(), compression.scan_lines_per_block())
346 }
347}
348
349
350
351impl MetaData {
352
353 #[must_use]
357 pub fn read_from_file(path: impl AsRef<::std::path::Path>, pedantic: bool) -> Result<Self> {
358 Self::read_from_unbuffered(File::open(path)?, pedantic)
359 }
360
361 #[must_use]
366 pub fn read_from_unbuffered(unbuffered: impl Read, pedantic: bool) -> Result<Self> {
367 Self::read_from_buffered(BufReader::new(unbuffered), pedantic)
368 }
369
370 #[must_use]
375 pub fn read_from_buffered(buffered: impl Read, pedantic: bool) -> Result<Self> {
376 let mut read = PeekRead::new(buffered);
377 MetaData::read_unvalidated_from_buffered_peekable(&mut read, pedantic)
378 }
379
380 #[must_use]
382 pub(crate) fn read_unvalidated_from_buffered_peekable(read: &mut PeekRead<impl Read>, pedantic: bool) -> Result<Self> {
383 magic_number::validate_exr(read)?;
384
385 let requirements = Requirements::read(read)?;
386
387 requirements.validate()?;
389
390 let headers = Header::read_all(read, &requirements, pedantic)?;
391
392 Ok(MetaData { requirements, headers })
394 }
395
396 #[must_use]
398 pub(crate) fn read_validated_from_buffered_peekable(
399 read: &mut PeekRead<impl Read>, pedantic: bool
400 ) -> Result<Self> {
401 let meta_data = Self::read_unvalidated_from_buffered_peekable(read, !pedantic)?;
402 MetaData::validate(meta_data.headers.as_slice(), pedantic)?;
403 Ok(meta_data)
404 }
405
406 pub(crate) fn write_validating_to_buffered(write: &mut impl Write, headers: &[Header], pedantic: bool) -> Result<Requirements> {
410 let minimal_requirements = Self::validate(headers, pedantic)?;
413
414 magic_number::write(write)?;
415 minimal_requirements.write(write)?;
416 Header::write_all(headers, write, minimal_requirements.has_multiple_layers)?;
417 Ok(minimal_requirements)
418 }
419
420 pub fn read_offset_tables(read: &mut PeekRead<impl Read>, headers: &Headers) -> Result<OffsetTables> {
422 headers.iter()
423 .map(|header| u64::read_vec(read, header.chunk_count, u16::MAX as usize, None, "offset table size"))
424 .collect()
425 }
426
427 pub fn skip_offset_tables(read: &mut PeekRead<impl Read>, headers: &Headers) -> Result<usize> {
430 let chunk_count: usize = headers.iter().map(|header| header.chunk_count).sum();
431 crate::io::skip_bytes(read, chunk_count * u64::BYTE_SIZE)?; Ok(chunk_count)
433 }
434
435 pub fn enumerate_ordered_header_block_indices(&self) -> impl '_ + Iterator<Item=(usize, BlockIndex)> {
442 crate::block::enumerate_ordered_header_block_indices(&self.headers)
443 }
444
445 pub fn collect_ordered_blocks<'s>(&'s self, mut get_block: impl 's + FnMut(BlockIndex) -> UncompressedBlock)
449 -> impl 's + Iterator<Item=(usize, UncompressedBlock)>
450 {
451 self.enumerate_ordered_header_block_indices().map(move |(index_in_header, block_index)|{
452 (index_in_header, get_block(block_index))
453 })
454 }
455
456 pub fn collect_ordered_block_data<'s>(&'s self, mut get_block_data: impl 's + FnMut(BlockIndex) -> Vec<u8>)
460 -> impl 's + Iterator<Item=(usize, UncompressedBlock)>
461 {
462 self.collect_ordered_blocks(move |block_index|
463 UncompressedBlock { index: block_index, data: get_block_data(block_index) }
464 )
465 }
466
467 pub fn validate(headers: &[Header], pedantic: bool) -> Result<Requirements> {
469 if headers.len() == 0 {
470 return Err(Error::invalid("at least one layer is required"));
471 }
472
473 let deep = false; let is_multilayer = headers.len() > 1;
475 let first_header_has_tiles = headers.iter().next()
476 .map_or(false, |header| header.blocks.has_tiles());
477
478 let mut minimal_requirements = Requirements {
479 file_format_version: 2,
482
483 has_long_names: false,
485
486 is_single_layer_and_tiled: !is_multilayer && first_header_has_tiles,
487 has_multiple_layers: is_multilayer,
488 has_deep_data: deep,
489 };
490
491 for header in headers {
492 if header.deep { return Err(Error::unsupported("deep data not supported yet"));
494 }
495
496 header.validate(is_multilayer, &mut minimal_requirements.has_long_names, pedantic)?;
497 }
498
499 if pedantic { let mut header_names = HashSet::with_capacity(headers.len());
512 for header in headers {
513 if !header_names.insert(&header.own_attributes.layer_name) {
514 return Err(Error::invalid(format!(
515 "duplicate layer name: `{}`",
516 header.own_attributes.layer_name.as_ref().expect("header validation bug")
517 )));
518 }
519 }
520 }
521
522 if pedantic {
523 let must_share = headers.iter().flat_map(|header| header.own_attributes.other.iter())
524 .any(|(_, value)| value.to_chromaticities().is_ok() || value.to_time_code().is_ok());
525
526 if must_share {
527 return Err(Error::invalid("chromaticities and time code attributes must must not exist in own attributes but shared instead"));
528 }
529 }
530
531 if pedantic && headers.len() > 1 { let first_header = headers.first().expect("header count validation bug");
533 let first_header_attributes = &first_header.shared_attributes;
534
535 for header in &headers[1..] {
536 if &header.shared_attributes != first_header_attributes {
537 return Err(Error::invalid("display window, pixel aspect, chromaticities, and time code attributes must be equal for all headers"))
538 }
539 }
540 }
541
542 debug_assert!(minimal_requirements.validate().is_ok(), "inferred requirements are invalid");
543 Ok(minimal_requirements)
544 }
545}
546
547
548
549
550impl Requirements {
551
552 pub fn is_multilayer(&self) -> bool {
555 self.has_multiple_layers
556 }
557
558 pub fn read<R: Read>(read: &mut R) -> Result<Self> {
560 use ::bit_field::BitField;
561
562 let version_and_flags = u32::read(read)?;
563
564 let version = (version_and_flags & 0x000F) as u8;
566
567 let is_single_tile = version_and_flags.get_bit(9);
569 let has_long_names = version_and_flags.get_bit(10);
570 let has_deep_data = version_and_flags.get_bit(11);
571 let has_multiple_layers = version_and_flags.get_bit(12);
572
573 let unknown_flags = version_and_flags >> 13; if unknown_flags != 0 { return Err(Error::unsupported("too new file feature flags"));
580 }
581
582 let version = Requirements {
583 file_format_version: version,
584 is_single_layer_and_tiled: is_single_tile, has_long_names,
585 has_deep_data, has_multiple_layers,
586 };
587
588 Ok(version)
589 }
590
591 pub fn write<W: Write>(self, write: &mut W) -> UnitResult {
593 use ::bit_field::BitField;
594
595 let mut version_and_flags = self.file_format_version as u32;
598
599 version_and_flags.set_bit(9, self.is_single_layer_and_tiled);
601 version_and_flags.set_bit(10, self.has_long_names);
602 version_and_flags.set_bit(11, self.has_deep_data);
603 version_and_flags.set_bit(12, self.has_multiple_layers);
604 version_and_flags.write(write)?;
607 Ok(())
608 }
609
610 pub fn validate(&self) -> UnitResult {
612 if self.file_format_version == 2 {
613
614 match (
615 self.is_single_layer_and_tiled, self.has_deep_data, self.has_multiple_layers,
616 self.file_format_version
617 ) {
618 (false, false, false, 1..=2) => Ok(()),
620
621 (true, false, false, 1..=2) => Ok(()),
623
624 (false, false, true, 2) => Ok(()),
627
628 (false, true, false, 2) => Ok(()),
631
632 (false, true, true, 2) => Ok(()),
636
637 _ => Err(Error::invalid("file feature flags"))
638 }
639 }
640 else {
641 Err(Error::unsupported("file versions other than 2.0 are not supported"))
642 }
643 }
644}
645
646
647#[cfg(test)]
648mod test {
649 use super::*;
650 use crate::meta::header::{ImageAttributes, LayerAttributes};
651
652 #[test]
653 fn round_trip_requirements() {
654 let requirements = Requirements {
655 file_format_version: 2,
656 is_single_layer_and_tiled: true,
657 has_long_names: false,
658 has_deep_data: true,
659 has_multiple_layers: false
660 };
661
662 let mut data: Vec<u8> = Vec::new();
663 requirements.write(&mut data).unwrap();
664 let read = Requirements::read(&mut data.as_slice()).unwrap();
665 assert_eq!(requirements, read);
666 }
667
668 #[test]
669 fn round_trip(){
670 let header = Header {
671 channels: ChannelList::new(smallvec![
672 ChannelDescription {
673 name: Text::from("main"),
674 sample_type: SampleType::U32,
675 quantize_linearly: false,
676 sampling: Vec2(1, 1)
677 }
678 ],
679 ),
680 compression: Compression::Uncompressed,
681 line_order: LineOrder::Increasing,
682 deep_data_version: Some(1),
683 chunk_count: compute_chunk_count(Compression::Uncompressed, Vec2(2000, 333), BlockDescription::ScanLines),
684 max_samples_per_pixel: Some(4),
685 shared_attributes: ImageAttributes {
686 pixel_aspect: 3.0,
687 .. ImageAttributes::new(IntegerBounds {
688 position: Vec2(2,1),
689 size: Vec2(11, 9)
690 })
691 },
692
693 blocks: BlockDescription::ScanLines,
694 deep: false,
695 layer_size: Vec2(2000, 333),
696 own_attributes: LayerAttributes {
697 layer_name: Some(Text::from("test name lol")),
698 layer_position: Vec2(3, -5),
699 screen_window_center: Vec2(0.3, 99.0),
700 screen_window_width: 0.19,
701 .. Default::default()
702 }
703 };
704
705 let meta = MetaData {
706 requirements: Requirements {
707 file_format_version: 2,
708 is_single_layer_and_tiled: false,
709 has_long_names: false,
710 has_deep_data: false,
711 has_multiple_layers: false
712 },
713 headers: smallvec![ header ],
714 };
715
716
717 let mut data: Vec<u8> = Vec::new();
718 MetaData::write_validating_to_buffered(&mut data, meta.headers.as_slice(), true).unwrap();
719 let meta2 = MetaData::read_from_buffered(data.as_slice(), false).unwrap();
720 MetaData::validate(meta2.headers.as_slice(), true).unwrap();
721 assert_eq!(meta, meta2);
722 }
723
724 #[test]
725 fn infer_low_requirements() {
726 let header_version_1_short_names = Header {
727 channels: ChannelList::new(smallvec![
728 ChannelDescription {
729 name: Text::from("main"),
730 sample_type: SampleType::U32,
731 quantize_linearly: false,
732 sampling: Vec2(1, 1)
733 }
734 ],
735 ),
736 compression: Compression::Uncompressed,
737 line_order: LineOrder::Increasing,
738 deep_data_version: Some(1),
739 chunk_count: compute_chunk_count(Compression::Uncompressed, Vec2(2000, 333), BlockDescription::ScanLines),
740 max_samples_per_pixel: Some(4),
741 shared_attributes: ImageAttributes {
742 pixel_aspect: 3.0,
743 .. ImageAttributes::new(IntegerBounds {
744 position: Vec2(2,1),
745 size: Vec2(11, 9)
746 })
747 },
748 blocks: BlockDescription::ScanLines,
749 deep: false,
750 layer_size: Vec2(2000, 333),
751 own_attributes: LayerAttributes {
752 other: vec![
753 (Text::try_from("x").unwrap(), AttributeValue::F32(3.0)),
754 (Text::try_from("y").unwrap(), AttributeValue::F32(-1.0)),
755 ].into_iter().collect(),
756 .. Default::default()
757 }
758 };
759
760 let low_requirements = MetaData::validate(
761 &[header_version_1_short_names], true
762 ).unwrap();
763
764 assert_eq!(low_requirements.has_long_names, false);
765 assert_eq!(low_requirements.file_format_version, 2); assert_eq!(low_requirements.has_deep_data, false);
767 assert_eq!(low_requirements.has_multiple_layers, false);
768 }
769
770 #[test]
771 fn infer_high_requirements() {
772 let header_version_2_long_names = Header {
773 channels: ChannelList::new(
774 smallvec![
775 ChannelDescription {
776 name: Text::new_or_panic("main"),
777 sample_type: SampleType::U32,
778 quantize_linearly: false,
779 sampling: Vec2(1, 1)
780 }
781 ],
782 ),
783 compression: Compression::Uncompressed,
784 line_order: LineOrder::Increasing,
785 deep_data_version: Some(1),
786 chunk_count: compute_chunk_count(Compression::Uncompressed, Vec2(2000, 333), BlockDescription::ScanLines),
787 max_samples_per_pixel: Some(4),
788 shared_attributes: ImageAttributes {
789 pixel_aspect: 3.0,
790 .. ImageAttributes::new(IntegerBounds {
791 position: Vec2(2,1),
792 size: Vec2(11, 9)
793 })
794 },
795 blocks: BlockDescription::ScanLines,
796 deep: false,
797 layer_size: Vec2(2000, 333),
798 own_attributes: LayerAttributes {
799 layer_name: Some(Text::new_or_panic("oasdasoidfj")),
800 other: vec![
801 (Text::new_or_panic("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"), AttributeValue::F32(3.0)),
802 (Text::new_or_panic("y"), AttributeValue::F32(-1.0)),
803 ].into_iter().collect(),
804 .. Default::default()
805 }
806 };
807
808 let mut layer_2 = header_version_2_long_names.clone();
809 layer_2.own_attributes.layer_name = Some(Text::new_or_panic("anythingelse"));
810
811 let low_requirements = MetaData::validate(
812 &[header_version_2_long_names, layer_2], true
813 ).unwrap();
814
815 assert_eq!(low_requirements.has_long_names, true);
816 assert_eq!(low_requirements.file_format_version, 2);
817 assert_eq!(low_requirements.has_deep_data, false);
818 assert_eq!(low_requirements.has_multiple_layers, true);
819 }
820}
821