1 |
|
2 | //! Contains all meta data attributes.
|
3 | //! Each layer can have any number of [`Attribute`]s, including custom attributes.
|
4 |
|
5 | use smallvec::SmallVec;
|
6 |
|
7 |
|
8 | /// Contains one of all possible attributes.
|
9 | /// Includes a variant for custom attributes.
|
10 | #[derive (Debug, Clone, PartialEq)]
|
11 | pub enum AttributeValue {
|
12 |
|
13 | /// Channel meta data.
|
14 | ChannelList(ChannelList),
|
15 |
|
16 | /// Color space definition.
|
17 | Chromaticities(Chromaticities),
|
18 |
|
19 | /// Compression method of this layer.
|
20 | Compression(Compression),
|
21 |
|
22 | /// This image is an environment map.
|
23 | EnvironmentMap(EnvironmentMap),
|
24 |
|
25 | /// Film roll information.
|
26 | KeyCode(KeyCode),
|
27 |
|
28 | /// Order of the bocks in the file.
|
29 | LineOrder(LineOrder),
|
30 |
|
31 | /// A 3x3 matrix of floats.
|
32 | Matrix3x3(Matrix3x3),
|
33 |
|
34 | /// A 4x4 matrix of floats.
|
35 | Matrix4x4(Matrix4x4),
|
36 |
|
37 | /// 8-bit rgba Preview of the image.
|
38 | Preview(Preview),
|
39 |
|
40 | /// An integer dividend and divisor.
|
41 | Rational(Rational),
|
42 |
|
43 | /// Deep or flat and tiled or scan line.
|
44 | BlockType(BlockType),
|
45 |
|
46 | /// List of texts.
|
47 | TextVector(Vec<Text>),
|
48 |
|
49 | /// How to tile up the image.
|
50 | TileDescription(TileDescription),
|
51 |
|
52 | /// Timepoint and more.
|
53 | TimeCode(TimeCode),
|
54 |
|
55 | /// A string of byte-chars.
|
56 | Text(Text),
|
57 |
|
58 | /// 64-bit float
|
59 | F64(f64),
|
60 |
|
61 | /// 32-bit float
|
62 | F32(f32),
|
63 |
|
64 | /// 32-bit signed integer
|
65 | I32(i32),
|
66 |
|
67 | /// 2D integer rectangle.
|
68 | IntegerBounds(IntegerBounds),
|
69 |
|
70 | /// 2D float rectangle.
|
71 | FloatRect(FloatRect),
|
72 |
|
73 | /// 2D integer vector.
|
74 | IntVec2(Vec2<i32>),
|
75 |
|
76 | /// 2D float vector.
|
77 | FloatVec2(Vec2<f32>),
|
78 |
|
79 | /// 3D integer vector.
|
80 | IntVec3((i32, i32, i32)),
|
81 |
|
82 | /// 3D float vector.
|
83 | FloatVec3((f32, f32, f32)),
|
84 |
|
85 | /// A custom attribute.
|
86 | /// Contains the type name of this value.
|
87 | Custom {
|
88 |
|
89 | /// The name of the type this attribute is an instance of.
|
90 | kind: Text,
|
91 |
|
92 | /// The value, stored in little-endian byte order, of the value.
|
93 | /// Use the `exr::io::Data` trait to extract binary values from this vector.
|
94 | bytes: Vec<u8>
|
95 | },
|
96 | }
|
97 |
|
98 | /// A byte array with each byte being a char.
|
99 | /// This is not UTF an must be constructed from a standard string.
|
100 | // TODO is this ascii? use a rust ascii crate?
|
101 | #[derive (Clone, PartialEq, Ord, PartialOrd, Default)] // hash implemented manually
|
102 | pub struct Text {
|
103 | bytes: TextBytes,
|
104 | }
|
105 |
|
106 | /// Contains time information for this frame within a sequence.
|
107 | /// Also defined methods to compile this information into a
|
108 | /// `TV60`, `TV50` or `Film24` bit sequence, packed into `u32`.
|
109 | ///
|
110 | /// Satisfies the [SMPTE standard 12M-1999](https://en.wikipedia.org/wiki/SMPTE_timecode).
|
111 | /// For more in-depth information, see [philrees.co.uk/timecode](http://www.philrees.co.uk/articles/timecode.htm).
|
112 | #[derive (Copy, Debug, Clone, Eq, PartialEq, Hash, Default)]
|
113 | pub struct TimeCode {
|
114 |
|
115 | /// Hours 0 - 23 are valid.
|
116 | pub hours: u8,
|
117 |
|
118 | /// Minutes 0 - 59 are valid.
|
119 | pub minutes: u8,
|
120 |
|
121 | /// Seconds 0 - 59 are valid.
|
122 | pub seconds: u8,
|
123 |
|
124 | /// Frame Indices 0 - 29 are valid.
|
125 | pub frame: u8,
|
126 |
|
127 | /// Whether this is a drop frame.
|
128 | pub drop_frame: bool,
|
129 |
|
130 | /// Whether this is a color frame.
|
131 | pub color_frame: bool,
|
132 |
|
133 | /// Field Phase.
|
134 | pub field_phase: bool,
|
135 |
|
136 | /// Flags for `TimeCode.binary_groups`.
|
137 | pub binary_group_flags: [bool; 3],
|
138 |
|
139 | /// The user-defined control codes.
|
140 | /// Every entry in this array can use at most 3 bits.
|
141 | /// This results in a maximum value of 15, including 0, for each `u8`.
|
142 | pub binary_groups: [u8; 8]
|
143 | }
|
144 |
|
145 | /// layer type, specifies block type and deepness.
|
146 | #[derive (Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
147 | pub enum BlockType {
|
148 |
|
149 | /// Corresponds to the string value `scanlineimage`.
|
150 | ScanLine,
|
151 |
|
152 | /// Corresponds to the string value `tiledimage`.
|
153 | Tile,
|
154 |
|
155 | /// Corresponds to the string value `deepscanline`.
|
156 | DeepScanLine,
|
157 |
|
158 | /// Corresponds to the string value `deeptile`.
|
159 | DeepTile,
|
160 | }
|
161 |
|
162 | /// The string literals used to represent a `BlockType` in a file.
|
163 | pub mod block_type_strings {
|
164 |
|
165 | /// Type attribute text value of flat scan lines
|
166 | pub const SCAN_LINE: &'static [u8] = b"scanlineimage" ;
|
167 |
|
168 | /// Type attribute text value of flat tiles
|
169 | pub const TILE: &'static [u8] = b"tiledimage" ;
|
170 |
|
171 | /// Type attribute text value of deep scan lines
|
172 | pub const DEEP_SCAN_LINE: &'static [u8] = b"deepscanline" ;
|
173 |
|
174 | /// Type attribute text value of deep tiles
|
175 | pub const DEEP_TILE: &'static [u8] = b"deeptile" ;
|
176 | }
|
177 |
|
178 |
|
179 | pub use crate::compression::Compression;
|
180 |
|
181 | /// The integer rectangle describing where an layer is placed on the infinite 2D global space.
|
182 | pub type DataWindow = IntegerBounds;
|
183 |
|
184 | /// The integer rectangle limiting which part of the infinite 2D global space should be displayed.
|
185 | pub type DisplayWindow = IntegerBounds;
|
186 |
|
187 | /// An integer dividend and divisor, together forming a ratio.
|
188 | pub type Rational = (i32, u32);
|
189 |
|
190 | /// A float matrix with four rows and four columns.
|
191 | pub type Matrix4x4 = [f32; 4*4];
|
192 |
|
193 | /// A float matrix with three rows and three columns.
|
194 | pub type Matrix3x3 = [f32; 3*3];
|
195 |
|
196 | /// A rectangular section anywhere in 2D integer space.
|
197 | /// Valid from minimum coordinate (including) `-1,073,741,822`
|
198 | /// to maximum coordinate (including) `1,073,741,822`, the value of (`i32::MAX/2 -1`).
|
199 | #[derive (Clone, Copy, Debug, Eq, PartialEq, Default, Hash)]
|
200 | pub struct IntegerBounds {
|
201 |
|
202 | /// The top left corner of this rectangle.
|
203 | /// The `Box2I32` includes this pixel if the size is not zero.
|
204 | pub position: Vec2<i32>,
|
205 |
|
206 | /// How many pixels to include in this `Box2I32`.
|
207 | /// Extends to the right and downwards.
|
208 | /// Does not include the actual boundary, just like `Vec::len()`.
|
209 | pub size: Vec2<usize>,
|
210 | }
|
211 |
|
212 | /// A rectangular section anywhere in 2D float space.
|
213 | #[derive (Clone, Copy, Debug, PartialEq)]
|
214 | pub struct FloatRect {
|
215 |
|
216 | /// The top left corner location of the rectangle (inclusive)
|
217 | pub min: Vec2<f32>,
|
218 |
|
219 | /// The bottom right corner location of the rectangle (inclusive)
|
220 | pub max: Vec2<f32>
|
221 | }
|
222 |
|
223 | /// A List of channels. Channels must be sorted alphabetically.
|
224 | #[derive (Clone, Debug, Eq, PartialEq, Hash)]
|
225 | pub struct ChannelList {
|
226 |
|
227 | /// The channels in this list.
|
228 | pub list: SmallVec<[ChannelDescription; 5]>,
|
229 |
|
230 | /// The number of bytes that one pixel in this image needs.
|
231 | // FIXME this needs to account for subsampling anywhere?
|
232 | pub bytes_per_pixel: usize, // FIXME only makes sense for flat images!
|
233 |
|
234 | /// The sample type of all channels, if all channels have the same type.
|
235 | pub uniform_sample_type: Option<SampleType>,
|
236 | }
|
237 |
|
238 | /// A single channel in an layer.
|
239 | /// Does not contain the actual pixel data,
|
240 | /// but instead merely describes it.
|
241 | #[derive (Clone, Debug, Eq, PartialEq, Hash)]
|
242 | pub struct ChannelDescription {
|
243 |
|
244 | /// One of "R", "G", or "B" most of the time.
|
245 | pub name: Text,
|
246 |
|
247 | /// U32, F16 or F32.
|
248 | pub sample_type: SampleType,
|
249 |
|
250 | /// This attribute only tells lossy compression methods
|
251 | /// whether this value should be quantized exponentially or linearly.
|
252 | ///
|
253 | /// Should be `false` for red, green, or blue channels.
|
254 | /// Should be `true` for hue, chroma, saturation, or alpha channels.
|
255 | pub quantize_linearly: bool,
|
256 |
|
257 | /// How many of the samples are skipped compared to the other channels in this layer.
|
258 | ///
|
259 | /// Can be used for chroma subsampling for manual lossy data compression.
|
260 | /// Values other than 1 are allowed only in flat, scan-line based images.
|
261 | /// If an image is deep or tiled, x and y sampling rates for all of its channels must be 1.
|
262 | pub sampling: Vec2<usize>,
|
263 | }
|
264 |
|
265 | /// The type of samples in this channel.
|
266 | #[derive (Clone, Debug, Eq, PartialEq, Copy, Hash)]
|
267 | pub enum SampleType {
|
268 |
|
269 | /// This channel contains 32-bit unsigned int values.
|
270 | U32,
|
271 |
|
272 | /// This channel contains 16-bit float values.
|
273 | F16,
|
274 |
|
275 | /// This channel contains 32-bit float values.
|
276 | F32,
|
277 | }
|
278 |
|
279 | /// The color space of the pixels.
|
280 | ///
|
281 | /// If a file doesn't have a chromaticities attribute, display software
|
282 | /// should assume that the file's primaries and the white point match `Rec. ITU-R BT.709-3`.
|
283 | #[derive (Debug, Clone, Copy, PartialEq)]
|
284 | pub struct Chromaticities {
|
285 |
|
286 | /// "Red" location on the CIE XY chromaticity diagram.
|
287 | pub red: Vec2<f32>,
|
288 |
|
289 | /// "Green" location on the CIE XY chromaticity diagram.
|
290 | pub green: Vec2<f32>,
|
291 |
|
292 | /// "Blue" location on the CIE XY chromaticity diagram.
|
293 | pub blue: Vec2<f32>,
|
294 |
|
295 | /// "White" location on the CIE XY chromaticity diagram.
|
296 | pub white: Vec2<f32>
|
297 | }
|
298 |
|
299 | /// If this attribute is present, it describes
|
300 | /// how this texture should be projected onto an environment.
|
301 | #[derive (Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
302 | pub enum EnvironmentMap {
|
303 |
|
304 | /// This image is an environment map projected like a world map.
|
305 | LatitudeLongitude,
|
306 |
|
307 | /// This image contains the six sides of a cube.
|
308 | Cube,
|
309 | }
|
310 |
|
311 | /// Uniquely identifies a motion picture film frame.
|
312 | #[derive (Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
313 | pub struct KeyCode {
|
314 |
|
315 | /// Identifies a film manufacturer.
|
316 | pub film_manufacturer_code: i32,
|
317 |
|
318 | /// Identifies a film type.
|
319 | pub film_type: i32,
|
320 |
|
321 | /// Specifies the film roll prefix.
|
322 | pub film_roll_prefix: i32,
|
323 |
|
324 | /// Specifies the film count.
|
325 | pub count: i32,
|
326 |
|
327 | /// Specifies the perforation offset.
|
328 | pub perforation_offset: i32,
|
329 |
|
330 | /// Specifies the perforation count of each single frame.
|
331 | pub perforations_per_frame: i32,
|
332 |
|
333 | /// Specifies the perforation count of each single film.
|
334 | pub perforations_per_count: i32,
|
335 | }
|
336 |
|
337 | /// In what order the `Block`s of pixel data appear in a file.
|
338 | #[derive (Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
339 | pub enum LineOrder {
|
340 |
|
341 | /// The blocks in the file are ordered in descending rows from left to right.
|
342 | /// When compressing in parallel, this option requires potentially large amounts of memory.
|
343 | /// In that case, use `LineOrder::Unspecified` for best performance.
|
344 | Increasing,
|
345 |
|
346 | /// The blocks in the file are ordered in ascending rows from right to left.
|
347 | /// When compressing in parallel, this option requires potentially large amounts of memory.
|
348 | /// In that case, use `LineOrder::Unspecified` for best performance.
|
349 | Decreasing,
|
350 |
|
351 | /// The blocks are not ordered in a specific way inside the file.
|
352 | /// In multi-core file writing, this option offers the best performance.
|
353 | Unspecified,
|
354 | }
|
355 |
|
356 | /// A small `rgba` image of `i8` values that approximates the real exr image.
|
357 | // TODO is this linear?
|
358 | #[derive (Clone, Eq, PartialEq)]
|
359 | pub struct Preview {
|
360 |
|
361 | /// The dimensions of the preview image.
|
362 | pub size: Vec2<usize>,
|
363 |
|
364 | /// An array with a length of 4 × width × height.
|
365 | /// The pixels are stored in `LineOrder::Increasing`.
|
366 | /// Each pixel consists of the four `u8` values red, green, blue, alpha.
|
367 | pub pixel_data: Vec<i8>,
|
368 | }
|
369 |
|
370 | /// Describes how the layer is divided into tiles.
|
371 | /// Specifies the size of each tile in the image
|
372 | /// and whether this image contains multiple resolution levels.
|
373 | #[derive (Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
374 | pub struct TileDescription {
|
375 |
|
376 | /// The size of each tile.
|
377 | /// Stays the same number of pixels across all levels.
|
378 | pub tile_size: Vec2<usize>,
|
379 |
|
380 | /// Whether to also store smaller versions of the image.
|
381 | pub level_mode: LevelMode,
|
382 |
|
383 | /// Whether to round up or down when calculating Mip/Rip levels.
|
384 | pub rounding_mode: RoundingMode,
|
385 | }
|
386 |
|
387 | /// Whether to also store increasingly smaller versions of the original image.
|
388 | #[derive (Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
389 | pub enum LevelMode {
|
390 |
|
391 | /// Only a single level.
|
392 | Singular,
|
393 |
|
394 | /// Levels with a similar aspect ratio.
|
395 | MipMap,
|
396 |
|
397 | /// Levels with all possible aspect ratios.
|
398 | RipMap,
|
399 | }
|
400 |
|
401 |
|
402 | /// The raw bytes that make up a string in an exr file.
|
403 | /// Each `u8` is a single char.
|
404 | // will mostly be "R", "G", "B" or "deepscanlineimage"
|
405 | pub type TextBytes = SmallVec<[u8; 24]>;
|
406 |
|
407 | /// A byte slice, interpreted as text
|
408 | pub type TextSlice = [u8];
|
409 |
|
410 |
|
411 | use crate::io::*;
|
412 | use crate::meta::{sequence_end};
|
413 | use crate::error::*;
|
414 | use crate::math::{RoundingMode, Vec2};
|
415 | use half::f16;
|
416 | use std::convert::{TryFrom};
|
417 | use std::borrow::Borrow;
|
418 | use std::hash::{Hash, Hasher};
|
419 | use bit_field::BitField;
|
420 |
|
421 |
|
422 | fn invalid_type() -> Error {
|
423 | Error::invalid(message:"attribute type mismatch" )
|
424 | }
|
425 |
|
426 |
|
427 | impl Text {
|
428 |
|
429 | /// Create a `Text` from an `str` reference.
|
430 | /// Returns `None` if this string contains unsupported chars.
|
431 | pub fn new_or_none(string: impl AsRef<str>) -> Option<Self> {
|
432 | let vec : Option<TextBytes> = string.as_ref().chars()
|
433 | .map(|character| u8::try_from(character as u64).ok())
|
434 | .collect();
|
435 |
|
436 | vec.map(Self::from_bytes_unchecked)
|
437 | }
|
438 |
|
439 | /// Create a `Text` from an `str` reference.
|
440 | /// Panics if this string contains unsupported chars.
|
441 | pub fn new_or_panic(string: impl AsRef<str>) -> Self {
|
442 | Self::new_or_none(string).expect("exr::Text contains unsupported characters" )
|
443 | }
|
444 |
|
445 | /// Create a `Text` from a slice of bytes,
|
446 | /// without checking any of the bytes.
|
447 | pub fn from_slice_unchecked(text: &TextSlice) -> Self {
|
448 | Self::from_bytes_unchecked(SmallVec::from_slice(text))
|
449 | }
|
450 |
|
451 | /// Create a `Text` from the specified bytes object,
|
452 | /// without checking any of the bytes.
|
453 | pub fn from_bytes_unchecked(bytes: TextBytes) -> Self {
|
454 | Text { bytes }
|
455 | }
|
456 |
|
457 | /// The internal ASCII bytes this text is made of.
|
458 | pub fn as_slice(&self) -> &TextSlice {
|
459 | self.bytes.as_slice()
|
460 | }
|
461 |
|
462 | /// Check whether this string is valid, adjusting `long_names` if required.
|
463 | /// If `long_names` is not provided, text length will be entirely unchecked.
|
464 | pub fn validate(&self, null_terminated: bool, long_names: Option<&mut bool>) -> UnitResult {
|
465 | Self::validate_bytes(self.as_slice(), null_terminated, long_names)
|
466 | }
|
467 |
|
468 | /// Check whether some bytes are valid, adjusting `long_names` if required.
|
469 | /// If `long_names` is not provided, text length will be entirely unchecked.
|
470 | pub fn validate_bytes(text: &TextSlice, null_terminated: bool, long_names: Option<&mut bool>) -> UnitResult {
|
471 | if null_terminated && text.is_empty() {
|
472 | return Err(Error::invalid("text must not be empty" ));
|
473 | }
|
474 |
|
475 | if let Some(long) = long_names {
|
476 | if text.len() >= 256 { return Err(Error::invalid("text must not be longer than 255" )); }
|
477 | if text.len() >= 32 { *long = true; }
|
478 | }
|
479 |
|
480 | Ok(())
|
481 | }
|
482 |
|
483 | /// The byte count this string would occupy if it were encoded as a null-terminated string.
|
484 | pub fn null_terminated_byte_size(&self) -> usize {
|
485 | self.bytes.len() + sequence_end::byte_size()
|
486 | }
|
487 |
|
488 | /// The byte count this string would occupy if it were encoded as a size-prefixed string.
|
489 | pub fn i32_sized_byte_size(&self) -> usize {
|
490 | self.bytes.len() + i32::BYTE_SIZE
|
491 | }
|
492 |
|
493 | /// Write the length of a string and then the contents with that length.
|
494 | pub fn write_i32_sized<W: Write>(&self, write: &mut W) -> UnitResult {
|
495 | debug_assert!(self.validate( false, None).is_ok(), "text size bug" );
|
496 | i32::write(usize_to_i32(self.bytes.len()), write)?;
|
497 | Self::write_unsized_bytes(self.bytes.as_slice(), write)
|
498 | }
|
499 |
|
500 | /// Without validation, write this instance to the byte stream.
|
501 | fn write_unsized_bytes<W: Write>(bytes: &[u8], write: &mut W) -> UnitResult {
|
502 | u8::write_slice(write, bytes)?;
|
503 | Ok(())
|
504 | }
|
505 |
|
506 | /// Read the length of a string and then the contents with that length.
|
507 | pub fn read_i32_sized<R: Read>(read: &mut R, max_size: usize) -> Result<Self> {
|
508 | let size = i32_to_usize(i32::read(read)?, "vector size" )?;
|
509 | Ok(Text::from_bytes_unchecked(SmallVec::from_vec(u8::read_vec(read, size, 1024, Some(max_size), "text attribute length" )?)))
|
510 | }
|
511 |
|
512 | /// Read the contents with that length.
|
513 | pub fn read_sized<R: Read>(read: &mut R, size: usize) -> Result<Self> {
|
514 | const SMALL_SIZE: usize = 24;
|
515 |
|
516 | // for small strings, read into small vec without heap allocation
|
517 | if size <= SMALL_SIZE {
|
518 | let mut buffer = [0_u8; SMALL_SIZE];
|
519 | let data = &mut buffer[..size];
|
520 |
|
521 | read.read_exact(data)?;
|
522 | Ok(Text::from_bytes_unchecked(SmallVec::from_slice(data)))
|
523 | }
|
524 |
|
525 | // for large strings, read a dynamic vec of arbitrary size
|
526 | else {
|
527 | Ok(Text::from_bytes_unchecked(SmallVec::from_vec(u8::read_vec(read, size, 1024, None, "text attribute length" )?)))
|
528 | }
|
529 | }
|
530 |
|
531 | /// Write the string contents and a null-terminator.
|
532 | pub fn write_null_terminated<W: Write>(&self, write: &mut W) -> UnitResult {
|
533 | Self::write_null_terminated_bytes(self.as_slice(), write)
|
534 | }
|
535 |
|
536 | /// Write the string contents and a null-terminator.
|
537 | fn write_null_terminated_bytes<W: Write>(bytes: &[u8], write: &mut W) -> UnitResult {
|
538 | debug_assert!(!bytes.is_empty(), "text is empty bug" ); // required to avoid mixup with "sequece_end"
|
539 |
|
540 | Text::write_unsized_bytes(bytes, write)?;
|
541 | sequence_end::write(write)?;
|
542 | Ok(())
|
543 | }
|
544 |
|
545 | /// Read a string until the null-terminator is found. Then skips the null-terminator.
|
546 | pub fn read_null_terminated<R: Read>(read: &mut R, max_len: usize) -> Result<Self> {
|
547 | let mut bytes = smallvec![ u8::read(read)? ]; // null-terminated strings are always at least 1 byte
|
548 |
|
549 | loop {
|
550 | match u8::read(read)? {
|
551 | 0 => break,
|
552 | non_terminator => bytes.push(non_terminator),
|
553 | }
|
554 |
|
555 | if bytes.len() > max_len {
|
556 | return Err(Error::invalid("text too long" ))
|
557 | }
|
558 | }
|
559 |
|
560 | Ok(Text { bytes })
|
561 | }
|
562 |
|
563 | /// Allows any text length since it is only used for attribute values,
|
564 | /// but not attribute names, attribute type names, or channel names.
|
565 | fn read_vec_of_i32_sized(
|
566 | read: &mut PeekRead<impl Read>,
|
567 | total_byte_size: usize
|
568 | ) -> Result<Vec<Text>>
|
569 | {
|
570 | let mut result = Vec::with_capacity(2);
|
571 |
|
572 | // length of the text-vector can be inferred from attribute size
|
573 | let mut processed_bytes = 0;
|
574 |
|
575 | while processed_bytes < total_byte_size {
|
576 | let text = Text::read_i32_sized(read, total_byte_size)?;
|
577 | processed_bytes += ::std::mem::size_of::<i32>(); // size i32 of the text
|
578 | processed_bytes += text.bytes.len();
|
579 | result.push(text);
|
580 | }
|
581 |
|
582 | // the expected byte size did not match the actual text byte size
|
583 | if processed_bytes != total_byte_size {
|
584 | return Err(Error::invalid("text array byte size" ))
|
585 | }
|
586 |
|
587 | Ok(result)
|
588 | }
|
589 |
|
590 | /// Allows any text length since it is only used for attribute values,
|
591 | /// but not attribute names, attribute type names, or channel names.
|
592 | fn write_vec_of_i32_sized_texts<W: Write>(write: &mut W, texts: &[Text]) -> UnitResult {
|
593 | // length of the text-vector can be inferred from attribute size
|
594 | for text in texts {
|
595 | text.write_i32_sized(write)?;
|
596 | }
|
597 |
|
598 | Ok(())
|
599 | }
|
600 |
|
601 | /// The underlying bytes that represent this text.
|
602 | pub fn bytes(&self) -> &[u8] {
|
603 | self.bytes.as_slice()
|
604 | }
|
605 |
|
606 | /// Iterate over the individual chars in this text, similar to `String::chars()`.
|
607 | /// Does not do any heap-allocation but borrows from this instance instead.
|
608 | pub fn chars(&self) -> impl '_ + Iterator<Item = char> {
|
609 | self.bytes.iter().map(|&byte| byte as char)
|
610 | }
|
611 |
|
612 | /// Compare this `exr::Text` with a plain `&str`.
|
613 | pub fn eq(&self, string: &str) -> bool {
|
614 | string.chars().eq(self.chars())
|
615 | }
|
616 |
|
617 | /// Compare this `exr::Text` with a plain `&str` ignoring capitalization.
|
618 | pub fn eq_case_insensitive(&self, string: &str) -> bool {
|
619 | // this is technically not working for a "turkish i", but those cannot be encoded in exr files anyways
|
620 | let self_chars = self.chars().map(|char| char.to_ascii_lowercase());
|
621 | let string_chars = string.chars().flat_map(|ch| ch.to_lowercase());
|
622 |
|
623 | string_chars.eq(self_chars)
|
624 | }
|
625 | }
|
626 |
|
627 | impl PartialEq<str> for Text {
|
628 | fn eq(&self, other: &str) -> bool {
|
629 | self.eq(string:other)
|
630 | }
|
631 | }
|
632 |
|
633 | impl PartialEq<Text> for str {
|
634 | fn eq(&self, other: &Text) -> bool {
|
635 | other.eq(self)
|
636 | }
|
637 | }
|
638 |
|
639 | impl Eq for Text {}
|
640 |
|
641 | impl Borrow<TextSlice> for Text {
|
642 | fn borrow(&self) -> &TextSlice {
|
643 | self.as_slice()
|
644 | }
|
645 | }
|
646 |
|
647 | // forwarding implementation. guarantees `text.borrow().hash() == text.hash()` (required for Borrow)
|
648 | impl Hash for Text {
|
649 | fn hash<H: Hasher>(&self, state: &mut H) {
|
650 | self.bytes.hash(state)
|
651 | }
|
652 | }
|
653 |
|
654 | impl Into<String> for Text {
|
655 | fn into(self) -> String {
|
656 | self.to_string()
|
657 | }
|
658 | }
|
659 |
|
660 | impl<'s> From<&'s str> for Text {
|
661 |
|
662 | /// Panics if the string contains an unsupported character
|
663 | fn from(str: &'s str) -> Self {
|
664 | Self::new_or_panic(string:str)
|
665 | }
|
666 | }
|
667 |
|
668 |
|
669 | /* TODO (currently conflicts with From<&str>)
|
670 | impl<'s> TryFrom<&'s str> for Text {
|
671 | type Error = String;
|
672 |
|
673 | fn try_from(value: &'s str) -> std::result::Result<Self, Self::Error> {
|
674 | Text::new_or_none(value)
|
675 | .ok_or_else(|| format!(
|
676 | "exr::Text does not support all characters in the string `{}`",
|
677 | value
|
678 | ))
|
679 | }
|
680 | }*/
|
681 |
|
682 |
|
683 | impl ::std::fmt::Debug for Text {
|
684 | fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
685 | write!(f, "exr::Text( \"{}\")" , self)
|
686 | }
|
687 | }
|
688 |
|
689 | // automatically implements to_string for us
|
690 | impl ::std::fmt::Display for Text {
|
691 | fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
692 | use std::fmt::Write;
|
693 |
|
694 | for &byte: u8 in self.bytes.iter() {
|
695 | f.write_char(byte as char)?;
|
696 | }
|
697 |
|
698 | Ok(())
|
699 | }
|
700 | }
|
701 |
|
702 |
|
703 | impl ChannelList {
|
704 |
|
705 | /// Does not validate channel order.
|
706 | pub fn new(channels: SmallVec<[ChannelDescription; 5]>) -> Self {
|
707 | let uniform_sample_type = {
|
708 | if let Some(first) = channels.first() {
|
709 | let has_uniform_types = channels.iter().skip(1)
|
710 | .all(|chan| chan.sample_type == first.sample_type);
|
711 |
|
712 | if has_uniform_types { Some(first.sample_type) } else { None }
|
713 | }
|
714 | else { None }
|
715 | };
|
716 |
|
717 | ChannelList {
|
718 | bytes_per_pixel: channels.iter().map(|channel| channel.sample_type.bytes_per_sample()).sum(),
|
719 | list: channels, uniform_sample_type,
|
720 | }
|
721 | }
|
722 |
|
723 | /// Iterate over the channels, and adds to each channel the byte offset of the channels sample type.
|
724 | /// Assumes the internal channel list is properly sorted.
|
725 | pub fn channels_with_byte_offset(&self) -> impl Iterator<Item=(usize, &ChannelDescription)> {
|
726 | self.list.iter().scan(0, |byte_position, channel|{
|
727 | let previous_position = *byte_position;
|
728 | *byte_position += channel.sample_type.bytes_per_sample();
|
729 | Some((previous_position, channel))
|
730 | })
|
731 | }
|
732 |
|
733 | /// Return the index of the channel with the exact name, case sensitive, or none.
|
734 | /// Potentially uses less than linear time.
|
735 | pub fn find_index_of_channel(&self, exact_name: &Text) -> Option<usize> {
|
736 | self.list.binary_search_by_key(&exact_name.bytes(), |chan| chan.name.bytes()).ok()
|
737 | }
|
738 |
|
739 | // TODO use this in compression methods
|
740 | /*pub fn pixel_section_indices(&self, bounds: IntegerBounds) -> impl '_ + Iterator<Item=(&Channel, usize, usize)> {
|
741 | (bounds.position.y() .. bounds.end().y()).flat_map(|y| {
|
742 | self.list
|
743 | .filter(|channel| mod_p(y, usize_to_i32(channel.sampling.1)) == 0)
|
744 | .flat_map(|channel|{
|
745 | (bounds.position.x() .. bounds.end().x())
|
746 | .filter(|x| mod_p(*x, usize_to_i32(channel.sampling.0)) == 0)
|
747 | .map(|x| (channel, x, y))
|
748 | })
|
749 | })
|
750 | }*/
|
751 | }
|
752 |
|
753 | impl BlockType {
|
754 |
|
755 | /// The corresponding attribute type name literal
|
756 | const TYPE_NAME: &'static [u8] = type_names::TEXT;
|
757 |
|
758 | /// Return a `BlockType` object from the specified attribute text value.
|
759 | pub fn parse(text: Text) -> Result<Self> {
|
760 | match text.as_slice() {
|
761 | block_type_strings::SCAN_LINE => Ok(BlockType::ScanLine),
|
762 | block_type_strings::TILE => Ok(BlockType::Tile),
|
763 |
|
764 | block_type_strings::DEEP_SCAN_LINE => Ok(BlockType::DeepScanLine),
|
765 | block_type_strings::DEEP_TILE => Ok(BlockType::DeepTile),
|
766 |
|
767 | _ => Err(Error::invalid("block type attribute value" )),
|
768 | }
|
769 | }
|
770 |
|
771 | /// Without validation, write this instance to the byte stream.
|
772 | pub fn write(&self, write: &mut impl Write) -> UnitResult {
|
773 | u8::write_slice(write, self.to_text_bytes())?;
|
774 | Ok(())
|
775 | }
|
776 |
|
777 | /// Returns the raw attribute text value this type is represented by in a file.
|
778 | pub fn to_text_bytes(&self) -> &[u8] {
|
779 | match self {
|
780 | BlockType::ScanLine => block_type_strings::SCAN_LINE,
|
781 | BlockType::Tile => block_type_strings::TILE,
|
782 | BlockType::DeepScanLine => block_type_strings::DEEP_SCAN_LINE,
|
783 | BlockType::DeepTile => block_type_strings::DEEP_TILE,
|
784 | }
|
785 | }
|
786 |
|
787 | /// Number of bytes this would consume in an exr file.
|
788 | pub fn byte_size(&self) -> usize {
|
789 | self.to_text_bytes().len()
|
790 | }
|
791 | }
|
792 |
|
793 |
|
794 | impl IntegerBounds {
|
795 |
|
796 | /// Create a box with no size located at (0,0).
|
797 | pub fn zero() -> Self {
|
798 | Self::from_dimensions(Vec2(0, 0))
|
799 | }
|
800 |
|
801 | /// Create a box with a size starting at zero.
|
802 | pub fn from_dimensions(size: impl Into<Vec2<usize>>) -> Self {
|
803 | Self::new(Vec2(0,0), size)
|
804 | }
|
805 |
|
806 | /// Create a box with a size and an origin point.
|
807 | pub fn new(start: impl Into<Vec2<i32>>, size: impl Into<Vec2<usize>>) -> Self {
|
808 | Self { position: start.into(), size: size.into() }
|
809 | }
|
810 |
|
811 | /// Returns the top-right coordinate of the rectangle.
|
812 | /// The row and column described by this vector are not included in the rectangle,
|
813 | /// just like `Vec::len()`.
|
814 | pub fn end(self) -> Vec2<i32> {
|
815 | self.position + self.size.to_i32() // larger than max int32 is panic
|
816 | }
|
817 |
|
818 | /// Returns the maximum coordinate that a value in this rectangle may have.
|
819 | pub fn max(self) -> Vec2<i32> {
|
820 | self.end() - Vec2(1,1)
|
821 | }
|
822 |
|
823 | /// Validate this instance.
|
824 | pub fn validate(&self, max_size: Option<Vec2<usize>>) -> UnitResult {
|
825 | if let Some(max_size) = max_size {
|
826 | if self.size.width() > max_size.width() || self.size.height() > max_size.height() {
|
827 | return Err(Error::invalid("window attribute dimension value" ));
|
828 | }
|
829 | }
|
830 |
|
831 | let min_i64 = Vec2(self.position.x() as i64, self.position.y() as i64);
|
832 |
|
833 | let max_i64 = Vec2(
|
834 | self.position.x() as i64 + self.size.width() as i64,
|
835 | self.position.y() as i64 + self.size.height() as i64,
|
836 | );
|
837 |
|
838 | Self::validate_min_max_u64(min_i64, max_i64)
|
839 | }
|
840 |
|
841 | fn validate_min_max_u64(min: Vec2<i64>, max: Vec2<i64>) -> UnitResult {
|
842 | let max_box_size_as_i64 = (i32::MAX / 2) as i64; // as defined in the original c++ library
|
843 |
|
844 | if max.x() >= max_box_size_as_i64
|
845 | || max.y() >= max_box_size_as_i64
|
846 | || min.x() <= -max_box_size_as_i64
|
847 | || min.y() <= -max_box_size_as_i64
|
848 | {
|
849 | return Err(Error::invalid("window size exceeding integer maximum" ));
|
850 | }
|
851 |
|
852 | Ok(())
|
853 | }
|
854 |
|
855 | /// Number of bytes this would consume in an exr file.
|
856 | pub fn byte_size() -> usize {
|
857 | 4 * i32::BYTE_SIZE
|
858 | }
|
859 |
|
860 | /// Without validation, write this instance to the byte stream.
|
861 | pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
|
862 | let Vec2(x_min, y_min) = self.position;
|
863 | let Vec2(x_max, y_max) = self.max();
|
864 |
|
865 | x_min.write(write)?;
|
866 | y_min.write(write)?;
|
867 | x_max.write(write)?;
|
868 | y_max.write(write)?;
|
869 | Ok(())
|
870 | }
|
871 |
|
872 | /// Read the value without validating.
|
873 | pub fn read<R: Read>(read: &mut R) -> Result<Self> {
|
874 | let x_min = i32::read(read)?;
|
875 | let y_min = i32::read(read)?;
|
876 | let x_max = i32::read(read)?;
|
877 | let y_max = i32::read(read)?;
|
878 |
|
879 | let min = Vec2(x_min.min(x_max), y_min.min(y_max));
|
880 | let max = Vec2(x_min.max(x_max), y_min.max(y_max));
|
881 |
|
882 | // prevent addition overflow
|
883 | Self::validate_min_max_u64(
|
884 | Vec2(min.x() as i64, min.y() as i64),
|
885 | Vec2(max.x() as i64, max.y() as i64),
|
886 | )?;
|
887 |
|
888 | // add one to max because the max inclusive, but the size is not
|
889 | let size = Vec2(max.x() + 1 - min.x(), max.y() + 1 - min.y());
|
890 | let size = size.to_usize("box coordinates" )?;
|
891 |
|
892 | Ok(IntegerBounds { position: min, size })
|
893 | }
|
894 |
|
895 | /// Create a new rectangle which is offset by the specified origin.
|
896 | pub fn with_origin(self, origin: Vec2<i32>) -> Self { // TODO rename to "move" or "translate"?
|
897 | IntegerBounds { position: self.position + origin, .. self }
|
898 | }
|
899 |
|
900 | /// Returns whether the specified rectangle is equal to or inside this rectangle.
|
901 | pub fn contains(self, subset: Self) -> bool {
|
902 | subset.position.x() >= self.position.x()
|
903 | && subset.position.y() >= self.position.y()
|
904 | && subset.end().x() <= self.end().x()
|
905 | && subset.end().y() <= self.end().y()
|
906 | }
|
907 | }
|
908 |
|
909 |
|
910 | impl FloatRect {
|
911 |
|
912 | /// Number of bytes this would consume in an exr file.
|
913 | pub fn byte_size() -> usize {
|
914 | 4 * f32::BYTE_SIZE
|
915 | }
|
916 |
|
917 | /// Without validation, write this instance to the byte stream.
|
918 | pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
|
919 | self.min.x().write(write)?;
|
920 | self.min.y().write(write)?;
|
921 | self.max.x().write(write)?;
|
922 | self.max.y().write(write)?;
|
923 | Ok(())
|
924 | }
|
925 |
|
926 | /// Read the value without validating.
|
927 | pub fn read<R: Read>(read: &mut R) -> Result<Self> {
|
928 | let x_min = f32::read(read)?;
|
929 | let y_min = f32::read(read)?;
|
930 | let x_max = f32::read(read)?;
|
931 | let y_max = f32::read(read)?;
|
932 |
|
933 | Ok(FloatRect {
|
934 | min: Vec2(x_min, y_min),
|
935 | max: Vec2(x_max, y_max)
|
936 | })
|
937 | }
|
938 | }
|
939 |
|
940 | impl SampleType {
|
941 |
|
942 | /// How many bytes a single sample takes up.
|
943 | pub fn bytes_per_sample(&self) -> usize {
|
944 | match self {
|
945 | SampleType::F16 => f16::BYTE_SIZE,
|
946 | SampleType::F32 => f32::BYTE_SIZE,
|
947 | SampleType::U32 => u32::BYTE_SIZE,
|
948 | }
|
949 | }
|
950 |
|
951 | /// Number of bytes this would consume in an exr file.
|
952 | pub fn byte_size() -> usize {
|
953 | i32::BYTE_SIZE
|
954 | }
|
955 |
|
956 | /// Without validation, write this instance to the byte stream.
|
957 | pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
|
958 | match *self {
|
959 | SampleType::U32 => 0_i32,
|
960 | SampleType::F16 => 1_i32,
|
961 | SampleType::F32 => 2_i32,
|
962 | }.write(write)?;
|
963 |
|
964 | Ok(())
|
965 | }
|
966 |
|
967 | /// Read the value without validating.
|
968 | pub fn read<R: Read>(read: &mut R) -> Result<Self> {
|
969 | // there's definitely going to be more than 255 different pixel types in the future
|
970 | Ok(match i32::read(read)? {
|
971 | 0 => SampleType::U32,
|
972 | 1 => SampleType::F16,
|
973 | 2 => SampleType::F32,
|
974 | _ => return Err(Error::invalid("pixel type attribute value" )),
|
975 | })
|
976 | }
|
977 | }
|
978 |
|
979 | impl ChannelDescription {
|
980 | /// Choose whether to compress samples linearly or not, based on the channel name.
|
981 | /// Luminance-based channels will be compressed differently than linear data such as alpha.
|
982 | pub fn guess_quantization_linearity(name: &Text) -> bool {
|
983 | !(
|
984 | name.eq_case_insensitive("R" ) || name.eq_case_insensitive("G" ) ||
|
985 | name.eq_case_insensitive("B" ) || name.eq_case_insensitive("L" ) ||
|
986 | name.eq_case_insensitive("Y" ) || name.eq_case_insensitive("X" ) ||
|
987 | name.eq_case_insensitive("Z" )
|
988 | )
|
989 | }
|
990 |
|
991 | /// Create a new channel with the specified properties and a sampling rate of (1,1).
|
992 | /// Automatically chooses the linearity for compression based on the channel name.
|
993 | pub fn named(name: impl Into<Text>, sample_type: SampleType) -> Self {
|
994 | let name = name.into();
|
995 | let linearity = Self::guess_quantization_linearity(&name);
|
996 | Self::new(name, sample_type, linearity)
|
997 | }
|
998 |
|
999 | /*pub fn from_name<T: Into<Sample> + Default>(name: impl Into<Text>) -> Self {
|
1000 | Self::named(name, T::default().into().sample_type())
|
1001 | }*/
|
1002 |
|
1003 | /// Create a new channel with the specified properties and a sampling rate of (1,1).
|
1004 | pub fn new(name: impl Into<Text>, sample_type: SampleType, quantize_linearly: bool) -> Self {
|
1005 | Self { name: name.into(), sample_type, quantize_linearly, sampling: Vec2(1, 1) }
|
1006 | }
|
1007 |
|
1008 | /// The count of pixels this channel contains, respecting subsampling.
|
1009 | // FIXME this must be used everywhere
|
1010 | pub fn subsampled_pixels(&self, dimensions: Vec2<usize>) -> usize {
|
1011 | self.subsampled_resolution(dimensions).area()
|
1012 | }
|
1013 |
|
1014 | /// The resolution pf this channel, respecting subsampling.
|
1015 | pub fn subsampled_resolution(&self, dimensions: Vec2<usize>) -> Vec2<usize> {
|
1016 | dimensions / self.sampling
|
1017 | }
|
1018 |
|
1019 | /// Number of bytes this would consume in an exr file.
|
1020 | pub fn byte_size(&self) -> usize {
|
1021 | self.name.null_terminated_byte_size()
|
1022 | + SampleType::byte_size()
|
1023 | + 1 // is_linear
|
1024 | + 3 // reserved bytes
|
1025 | + 2 * u32::BYTE_SIZE // sampling x, y
|
1026 | }
|
1027 |
|
1028 | /// Without validation, write this instance to the byte stream.
|
1029 | pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
|
1030 | Text::write_null_terminated(&self.name, write)?;
|
1031 | self.sample_type.write(write)?;
|
1032 |
|
1033 | match self.quantize_linearly {
|
1034 | false => 0_u8,
|
1035 | true => 1_u8,
|
1036 | }.write(write)?;
|
1037 |
|
1038 | i8::write_slice(write, &[0_i8, 0_i8, 0_i8])?;
|
1039 | i32::write(usize_to_i32(self.sampling.x()), write)?;
|
1040 | i32::write(usize_to_i32(self.sampling.y()), write)?;
|
1041 | Ok(())
|
1042 | }
|
1043 |
|
1044 | /// Read the value without validating.
|
1045 | pub fn read<R: Read>(read: &mut R) -> Result<Self> {
|
1046 | let name = Text::read_null_terminated(read, 256)?;
|
1047 | let sample_type = SampleType::read(read)?;
|
1048 |
|
1049 | let is_linear = match u8::read(read)? {
|
1050 | 1 => true,
|
1051 | 0 => false,
|
1052 | _ => return Err(Error::invalid("channel linearity attribute value" )),
|
1053 | };
|
1054 |
|
1055 | let mut reserved = [0_i8; 3];
|
1056 | i8::read_slice(read, &mut reserved)?;
|
1057 |
|
1058 | let x_sampling = i32_to_usize(i32::read(read)?, "x channel sampling" )?;
|
1059 | let y_sampling = i32_to_usize(i32::read(read)?, "y channel sampling" )?;
|
1060 |
|
1061 | Ok(ChannelDescription {
|
1062 | name, sample_type,
|
1063 | quantize_linearly: is_linear,
|
1064 | sampling: Vec2(x_sampling, y_sampling),
|
1065 | })
|
1066 | }
|
1067 |
|
1068 | /// Validate this instance.
|
1069 | pub fn validate(&self, allow_sampling: bool, data_window: IntegerBounds, strict: bool) -> UnitResult {
|
1070 | self.name.validate(true, None)?; // TODO spec says this does not affect `requirements.long_names` but is that true?
|
1071 |
|
1072 | if self.sampling.x() == 0 || self.sampling.y() == 0 {
|
1073 | return Err(Error::invalid("zero sampling factor" ));
|
1074 | }
|
1075 |
|
1076 | if strict && !allow_sampling && self.sampling != Vec2(1,1) {
|
1077 | return Err(Error::invalid("subsampling is only allowed in flat scan line images" ));
|
1078 | }
|
1079 |
|
1080 | if data_window.position.x() % self.sampling.x() as i32 != 0 || data_window.position.y() % self.sampling.y() as i32 != 0 {
|
1081 | return Err(Error::invalid("channel sampling factor not dividing data window position" ));
|
1082 | }
|
1083 |
|
1084 | if data_window.size.x() % self.sampling.x() != 0 || data_window.size.y() % self.sampling.y() != 0 {
|
1085 | return Err(Error::invalid("channel sampling factor not dividing data window size" ));
|
1086 | }
|
1087 |
|
1088 | if self.sampling != Vec2(1,1) {
|
1089 | // TODO this must only be implemented in the crate::image module and child modules,
|
1090 | // should not be too difficult
|
1091 |
|
1092 | return Err(Error::unsupported("channel subsampling not supported yet" ));
|
1093 | }
|
1094 |
|
1095 | Ok(())
|
1096 | }
|
1097 | }
|
1098 |
|
1099 | impl ChannelList {
|
1100 |
|
1101 | /// Number of bytes this would consume in an exr file.
|
1102 | pub fn byte_size(&self) -> usize {
|
1103 | self.list.iter().map(ChannelDescription::byte_size).sum::<usize>() + sequence_end::byte_size()
|
1104 | }
|
1105 |
|
1106 | /// Without validation, write this instance to the byte stream.
|
1107 | /// Assumes channels are sorted alphabetically and all values are validated.
|
1108 | pub fn write(&self, write: &mut impl Write) -> UnitResult {
|
1109 | for channel in &self.list {
|
1110 | channel.write(write)?;
|
1111 | }
|
1112 |
|
1113 | sequence_end::write(write)?;
|
1114 | Ok(())
|
1115 | }
|
1116 |
|
1117 | /// Read the value without validating.
|
1118 | pub fn read(read: &mut PeekRead<impl Read>) -> Result<Self> {
|
1119 | let mut channels = SmallVec::new();
|
1120 | while !sequence_end::has_come(read)? {
|
1121 | channels.push(ChannelDescription::read(read)?);
|
1122 | }
|
1123 |
|
1124 | Ok(ChannelList::new(channels))
|
1125 | }
|
1126 |
|
1127 | /// Check if channels are valid and sorted.
|
1128 | pub fn validate(&self, allow_sampling: bool, data_window: IntegerBounds, strict: bool) -> UnitResult {
|
1129 | let mut iter = self.list.iter().map(|chan| chan.validate(allow_sampling, data_window, strict).map(|_| &chan.name));
|
1130 | let mut previous = iter.next().ok_or(Error::invalid("at least one channel is required" ))??;
|
1131 |
|
1132 | for result in iter {
|
1133 | let value = result?;
|
1134 | if strict && previous == value { return Err(Error::invalid("channel names are not unique" )); }
|
1135 | else if previous > value { return Err(Error::invalid("channel names are not sorted alphabetically" )); }
|
1136 | else { previous = value; }
|
1137 | }
|
1138 |
|
1139 | Ok(())
|
1140 | }
|
1141 | }
|
1142 |
|
1143 | fn u8_to_decimal32(binary: u8) -> u32 {
|
1144 | let units: u32 = binary as u32 % 10;
|
1145 | let tens: u32 = (binary as u32 / 10) % 10;
|
1146 | units | (tens << 4)
|
1147 | }
|
1148 |
|
1149 | // assumes value fits into u8
|
1150 | fn u8_from_decimal32(coded: u32) -> u8 {
|
1151 | ((coded & 0x0f) + 10 * ((coded >> 4) & 0x0f)) as u8
|
1152 | }
|
1153 |
|
1154 | // https://github.com/AcademySoftwareFoundation/openexr/blob/master/src/lib/OpenEXR/ImfTimeCode.cpp
|
1155 | impl TimeCode {
|
1156 |
|
1157 | /// Number of bytes this would consume in an exr file.
|
1158 | pub const BYTE_SIZE: usize = 2 * u32::BYTE_SIZE;
|
1159 |
|
1160 | /// Returns an error if this time code is considered invalid.
|
1161 | pub fn validate(&self, strict: bool) -> UnitResult {
|
1162 | if strict {
|
1163 | if self.frame > 29 { Err(Error::invalid("time code frame larger than 29" )) }
|
1164 | else if self.seconds > 59 { Err(Error::invalid("time code seconds larger than 59" )) }
|
1165 | else if self.minutes > 59 { Err(Error::invalid("time code minutes larger than 59" )) }
|
1166 | else if self.hours > 23 { Err(Error::invalid("time code hours larger than 23" )) }
|
1167 | else if self.binary_groups.iter().any(|&group| group > 15) {
|
1168 | Err(Error::invalid("time code binary group value too large for 3 bits" ))
|
1169 | }
|
1170 | else { Ok(()) }
|
1171 | }
|
1172 | else { Ok(()) }
|
1173 | }
|
1174 |
|
1175 |
|
1176 | /// Pack the SMPTE time code into a u32 value, according to TV60 packing.
|
1177 | /// This is the encoding which is used within a binary exr file.
|
1178 | pub fn pack_time_as_tv60_u32(&self) -> Result<u32> {
|
1179 | // validate strictly to prevent set_bit panic! below
|
1180 | self.validate(true)?;
|
1181 |
|
1182 | Ok(*0_u32
|
1183 | .set_bits(0..6, u8_to_decimal32(self.frame))
|
1184 | .set_bit(6, self.drop_frame)
|
1185 | .set_bit(7, self.color_frame)
|
1186 | .set_bits(8..15, u8_to_decimal32(self.seconds))
|
1187 | .set_bit(15, self.field_phase)
|
1188 | .set_bits(16..23, u8_to_decimal32(self.minutes))
|
1189 | .set_bit(23, self.binary_group_flags[0])
|
1190 | .set_bits(24..30, u8_to_decimal32(self.hours))
|
1191 | .set_bit(30, self.binary_group_flags[1])
|
1192 | .set_bit(31, self.binary_group_flags[2])
|
1193 | )
|
1194 | }
|
1195 |
|
1196 | /// Unpack a time code from one TV60 encoded u32 value and the encoded user data.
|
1197 | /// This is the encoding which is used within a binary exr file.
|
1198 | pub fn from_tv60_time(tv60_time: u32, user_data: u32) -> Self {
|
1199 | Self {
|
1200 | frame: u8_from_decimal32(tv60_time.get_bits(0..6)), // cast cannot fail, as these are less than 8 bits
|
1201 | drop_frame: tv60_time.get_bit(6),
|
1202 | color_frame: tv60_time.get_bit(7),
|
1203 | seconds: u8_from_decimal32(tv60_time.get_bits(8..15)), // cast cannot fail, as these are less than 8 bits
|
1204 | field_phase: tv60_time.get_bit(15),
|
1205 | minutes: u8_from_decimal32(tv60_time.get_bits(16..23)), // cast cannot fail, as these are less than 8 bits
|
1206 | hours: u8_from_decimal32(tv60_time.get_bits(24..30)), // cast cannot fail, as these are less than 8 bits
|
1207 | binary_group_flags: [
|
1208 | tv60_time.get_bit(23),
|
1209 | tv60_time.get_bit(30),
|
1210 | tv60_time.get_bit(31),
|
1211 | ],
|
1212 |
|
1213 | binary_groups: Self::unpack_user_data_from_u32(user_data)
|
1214 | }
|
1215 | }
|
1216 |
|
1217 | /// Pack the SMPTE time code into a u32 value, according to TV50 packing.
|
1218 | /// This encoding does not support the `drop_frame` flag, it will be lost.
|
1219 | pub fn pack_time_as_tv50_u32(&self) -> Result<u32> {
|
1220 | Ok(*self.pack_time_as_tv60_u32()?
|
1221 |
|
1222 | // swap some fields by replacing some bits in the packed u32
|
1223 | .set_bit(6, false)
|
1224 | .set_bit(15, self.binary_group_flags[0])
|
1225 | .set_bit(30, self.binary_group_flags[1])
|
1226 | .set_bit(23, self.binary_group_flags[2])
|
1227 | .set_bit(31, self.field_phase)
|
1228 | )
|
1229 | }
|
1230 |
|
1231 | /// Unpack a time code from one TV50 encoded u32 value and the encoded user data.
|
1232 | /// This encoding does not support the `drop_frame` flag, it will always be false.
|
1233 | pub fn from_tv50_time(tv50_time: u32, user_data: u32) -> Self {
|
1234 | Self {
|
1235 | drop_frame: false, // do not use bit [6]
|
1236 |
|
1237 | // swap some fields:
|
1238 | field_phase: tv50_time.get_bit(31),
|
1239 | binary_group_flags: [
|
1240 | tv50_time.get_bit(15),
|
1241 | tv50_time.get_bit(30),
|
1242 | tv50_time.get_bit(23),
|
1243 | ],
|
1244 |
|
1245 | .. Self::from_tv60_time(tv50_time, user_data)
|
1246 | }
|
1247 | }
|
1248 |
|
1249 |
|
1250 | /// Pack the SMPTE time code into a u32 value, according to FILM24 packing.
|
1251 | /// This encoding does not support the `drop_frame` and `color_frame` flags, they will be lost.
|
1252 | pub fn pack_time_as_film24_u32(&self) -> Result<u32> {
|
1253 | Ok(*self.pack_time_as_tv60_u32()?
|
1254 | .set_bit(6, false)
|
1255 | .set_bit(7, false)
|
1256 | )
|
1257 | }
|
1258 |
|
1259 | /// Unpack a time code from one TV60 encoded u32 value and the encoded user data.
|
1260 | /// This encoding does not support the `drop_frame` and `color_frame` flags, they will always be `false`.
|
1261 | pub fn from_film24_time(film24_time: u32, user_data: u32) -> Self {
|
1262 | Self {
|
1263 | drop_frame: false, // bit [6]
|
1264 | color_frame: false, // bit [7]
|
1265 | .. Self::from_tv60_time(film24_time, user_data)
|
1266 | }
|
1267 | }
|
1268 |
|
1269 |
|
1270 | // in rust, group index starts at zero, not at one.
|
1271 | fn user_data_bit_indices(group_index: usize) -> std::ops::Range<usize> {
|
1272 | let min_bit = 4 * group_index;
|
1273 | min_bit .. min_bit + 4 // +4, not +3, as `Range` is exclusive
|
1274 | }
|
1275 |
|
1276 | /// Pack the user data `u8` array into one u32.
|
1277 | /// User data values are clamped to the valid range (maximum value is 4).
|
1278 | pub fn pack_user_data_as_u32(&self) -> u32 {
|
1279 | let packed = self.binary_groups.iter().enumerate().fold(0_u32, |mut packed, (group_index, group_value)|
|
1280 | *packed.set_bits(Self::user_data_bit_indices(group_index), *group_value.min(&15) as u32)
|
1281 | );
|
1282 |
|
1283 | debug_assert_eq!(Self::unpack_user_data_from_u32(packed), self.binary_groups, "round trip user data encoding" );
|
1284 | packed
|
1285 | }
|
1286 |
|
1287 | // Unpack the encoded u32 user data to an array of bytes, each byte having a value from 0 to 4.
|
1288 | fn unpack_user_data_from_u32(user_data: u32) -> [u8; 8] {
|
1289 | (0..8).map(|group_index| user_data.get_bits(Self::user_data_bit_indices(group_index)) as u8)
|
1290 | .collect::<SmallVec<[u8;8]>>().into_inner().expect("array index bug" )
|
1291 | }
|
1292 |
|
1293 |
|
1294 | /// Write this time code to the byte stream, encoded as TV60 integers.
|
1295 | /// Returns an `Error::Invalid` if the fields are out of the allowed range.
|
1296 | pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
|
1297 | self.pack_time_as_tv60_u32()?.write(write)?; // will validate
|
1298 | self.pack_user_data_as_u32().write(write)?;
|
1299 | Ok(())
|
1300 | }
|
1301 |
|
1302 | /// Read the time code, without validating, extracting from TV60 integers.
|
1303 | pub fn read<R: Read>(read: &mut R) -> Result<Self> {
|
1304 | let time_and_flags = u32::read(read)?;
|
1305 | let user_data = u32::read(read)?;
|
1306 | Ok(Self::from_tv60_time(time_and_flags, user_data))
|
1307 | }
|
1308 | }
|
1309 |
|
1310 | impl Chromaticities {
|
1311 |
|
1312 | /// Number of bytes this would consume in an exr file.
|
1313 | pub fn byte_size() -> usize {
|
1314 | 8 * f32::BYTE_SIZE
|
1315 | }
|
1316 |
|
1317 | /// Without validation, write this instance to the byte stream.
|
1318 | pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
|
1319 | self.red.x().write(write)?;
|
1320 | self.red.y().write(write)?;
|
1321 |
|
1322 | self.green.x().write(write)?;
|
1323 | self.green.y().write(write)?;
|
1324 |
|
1325 | self.blue.x().write(write)?;
|
1326 | self.blue.y().write(write)?;
|
1327 |
|
1328 | self.white.x().write(write)?;
|
1329 | self.white.y().write(write)?;
|
1330 | Ok(())
|
1331 | }
|
1332 |
|
1333 | /// Read the value without validating.
|
1334 | pub fn read<R: Read>(read: &mut R) -> Result<Self> {
|
1335 | Ok(Chromaticities {
|
1336 | red: Vec2(f32::read(read)?, f32::read(read)?),
|
1337 | green: Vec2(f32::read(read)?, f32::read(read)?),
|
1338 | blue: Vec2(f32::read(read)?, f32::read(read)?),
|
1339 | white: Vec2(f32::read(read)?, f32::read(read)?),
|
1340 | })
|
1341 | }
|
1342 | }
|
1343 |
|
1344 | impl Compression {
|
1345 |
|
1346 | /// Number of bytes this would consume in an exr file.
|
1347 | pub fn byte_size() -> usize { u8::BYTE_SIZE }
|
1348 |
|
1349 | /// Without validation, write this instance to the byte stream.
|
1350 | pub fn write<W: Write>(self, write: &mut W) -> UnitResult {
|
1351 | use self::Compression::*;
|
1352 | match self {
|
1353 | Uncompressed => 0_u8,
|
1354 | RLE => 1_u8,
|
1355 | ZIP1 => 2_u8,
|
1356 | ZIP16 => 3_u8,
|
1357 | PIZ => 4_u8,
|
1358 | PXR24 => 5_u8,
|
1359 | B44 => 6_u8,
|
1360 | B44A => 7_u8,
|
1361 | DWAA(_) => 8_u8,
|
1362 | DWAB(_) => 9_u8,
|
1363 | }.write(write)?;
|
1364 | Ok(())
|
1365 | }
|
1366 |
|
1367 | /// Read the value without validating.
|
1368 | pub fn read<R: Read>(read: &mut R) -> Result<Self> {
|
1369 | use self::Compression::*;
|
1370 | Ok(match u8::read(read)? {
|
1371 | 0 => Uncompressed,
|
1372 | 1 => RLE,
|
1373 | 2 => ZIP1,
|
1374 | 3 => ZIP16,
|
1375 | 4 => PIZ,
|
1376 | 5 => PXR24,
|
1377 | 6 => B44,
|
1378 | 7 => B44A,
|
1379 | 8 => DWAA(None),
|
1380 | 9 => DWAB(None),
|
1381 | _ => return Err(Error::unsupported("unknown compression method" )),
|
1382 | })
|
1383 | }
|
1384 | }
|
1385 |
|
1386 | impl EnvironmentMap {
|
1387 |
|
1388 | /// Number of bytes this would consume in an exr file.
|
1389 | pub fn byte_size() -> usize {
|
1390 | u8::BYTE_SIZE
|
1391 | }
|
1392 |
|
1393 | /// Without validation, write this instance to the byte stream.
|
1394 | pub fn write<W: Write>(self, write: &mut W) -> UnitResult {
|
1395 | use self::EnvironmentMap::*;
|
1396 | match self {
|
1397 | LatitudeLongitude => 0_u8,
|
1398 | Cube => 1_u8
|
1399 | }.write(write)?;
|
1400 |
|
1401 | Ok(())
|
1402 | }
|
1403 |
|
1404 | /// Read the value without validating.
|
1405 | pub fn read<R: Read>(read: &mut R) -> Result<Self> {
|
1406 | use self::EnvironmentMap::*;
|
1407 | Ok(match u8::read(read)? {
|
1408 | 0 => LatitudeLongitude,
|
1409 | 1 => Cube,
|
1410 | _ => return Err(Error::invalid("environment map attribute value" )),
|
1411 | })
|
1412 | }
|
1413 | }
|
1414 |
|
1415 | impl KeyCode {
|
1416 |
|
1417 | /// Number of bytes this would consume in an exr file.
|
1418 | pub fn byte_size() -> usize {
|
1419 | 6 * i32::BYTE_SIZE
|
1420 | }
|
1421 |
|
1422 | /// Without validation, write this instance to the byte stream.
|
1423 | pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
|
1424 | self.film_manufacturer_code.write(write)?;
|
1425 | self.film_type.write(write)?;
|
1426 | self.film_roll_prefix.write(write)?;
|
1427 | self.count.write(write)?;
|
1428 | self.perforation_offset.write(write)?;
|
1429 | self.perforations_per_count.write(write)?;
|
1430 | Ok(())
|
1431 | }
|
1432 |
|
1433 | /// Read the value without validating.
|
1434 | pub fn read<R: Read>(read: &mut R) -> Result<Self> {
|
1435 | Ok(KeyCode {
|
1436 | film_manufacturer_code: i32::read(read)?,
|
1437 | film_type: i32::read(read)?,
|
1438 | film_roll_prefix: i32::read(read)?,
|
1439 | count: i32::read(read)?,
|
1440 | perforation_offset: i32::read(read)?,
|
1441 | perforations_per_frame: i32::read(read)?,
|
1442 | perforations_per_count: i32::read(read)?,
|
1443 | })
|
1444 | }
|
1445 | }
|
1446 |
|
1447 | impl LineOrder {
|
1448 |
|
1449 | /// Number of bytes this would consume in an exr file.
|
1450 | pub fn byte_size() -> usize {
|
1451 | u8::BYTE_SIZE
|
1452 | }
|
1453 |
|
1454 | /// Without validation, write this instance to the byte stream.
|
1455 | pub fn write<W: Write>(self, write: &mut W) -> UnitResult {
|
1456 | use self::LineOrder::*;
|
1457 | match self {
|
1458 | Increasing => 0_u8,
|
1459 | Decreasing => 1_u8,
|
1460 | Unspecified => 2_u8,
|
1461 | }.write(write)?;
|
1462 |
|
1463 | Ok(())
|
1464 | }
|
1465 |
|
1466 | /// Read the value without validating.
|
1467 | pub fn read<R: Read>(read: &mut R) -> Result<Self> {
|
1468 | use self::LineOrder::*;
|
1469 | Ok(match u8::read(read)? {
|
1470 | 0 => Increasing,
|
1471 | 1 => Decreasing,
|
1472 | 2 => Unspecified,
|
1473 | _ => return Err(Error::invalid("line order attribute value" )),
|
1474 | })
|
1475 | }
|
1476 | }
|
1477 |
|
1478 |
|
1479 |
|
1480 |
|
1481 | impl Preview {
|
1482 |
|
1483 | /// Number of bytes this would consume in an exr file.
|
1484 | pub fn byte_size(&self) -> usize {
|
1485 | 2 * u32::BYTE_SIZE + self.pixel_data.len()
|
1486 | }
|
1487 |
|
1488 | /// Without validation, write this instance to the byte stream.
|
1489 | pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
|
1490 | u32::write(self.size.width() as u32, write)?;
|
1491 | u32::write(self.size.height() as u32, write)?;
|
1492 |
|
1493 | i8::write_slice(write, &self.pixel_data)?;
|
1494 | Ok(())
|
1495 | }
|
1496 |
|
1497 | /// Read the value without validating.
|
1498 | pub fn read<R: Read>(read: &mut R) -> Result<Self> {
|
1499 | let width = u32::read(read)? as usize;
|
1500 | let height = u32::read(read)? as usize;
|
1501 |
|
1502 | if let Some(pixel_count) = width.checked_mul(height) {
|
1503 | // Multiply by the number of bytes per pixel.
|
1504 | if let Some(byte_count) = pixel_count.checked_mul(4) {
|
1505 | let pixel_data = i8::read_vec(
|
1506 | read,
|
1507 | byte_count,
|
1508 | 1024 * 1024 * 4,
|
1509 | None,
|
1510 | "preview attribute pixel count" ,
|
1511 | )?;
|
1512 |
|
1513 | let preview = Preview {
|
1514 | size: Vec2(width, height),
|
1515 | pixel_data,
|
1516 | };
|
1517 |
|
1518 | return Ok(preview);
|
1519 | }
|
1520 | }
|
1521 |
|
1522 | return Err(Error::invalid(
|
1523 | format!("Overflow while calculating preview image Attribute size \
|
1524 | (width: {}, height: {})." ,
|
1525 | width,
|
1526 | height)));
|
1527 | }
|
1528 |
|
1529 | /// Validate this instance.
|
1530 | pub fn validate(&self, strict: bool) -> UnitResult {
|
1531 | if strict && (self.size.area() * 4 != self.pixel_data.len()) {
|
1532 | return Err(Error::invalid("preview dimensions do not match content length" ))
|
1533 | }
|
1534 |
|
1535 | Ok(())
|
1536 | }
|
1537 | }
|
1538 |
|
1539 | impl ::std::fmt::Debug for Preview {
|
1540 | fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
1541 | write!(f, "Preview ( {}x {} px)" , self.size.width(), self.size.height())
|
1542 | }
|
1543 | }
|
1544 |
|
1545 | impl TileDescription {
|
1546 |
|
1547 | /// Number of bytes this would consume in an exr file.
|
1548 | pub fn byte_size() -> usize {
|
1549 | 2 * u32::BYTE_SIZE + 1 // size x,y + (level mode + rounding mode)
|
1550 | }
|
1551 |
|
1552 | /// Without validation, write this instance to the byte stream.
|
1553 | pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
|
1554 | u32::write(self.tile_size.width() as u32, write)?;
|
1555 | u32::write(self.tile_size.height() as u32, write)?;
|
1556 |
|
1557 | let level_mode = match self.level_mode {
|
1558 | LevelMode::Singular => 0_u8,
|
1559 | LevelMode::MipMap => 1_u8,
|
1560 | LevelMode::RipMap => 2_u8,
|
1561 | };
|
1562 |
|
1563 | let rounding_mode = match self.rounding_mode {
|
1564 | RoundingMode::Down => 0_u8,
|
1565 | RoundingMode::Up => 1_u8,
|
1566 | };
|
1567 |
|
1568 | let mode: u8 = level_mode + (rounding_mode * 16);
|
1569 | mode.write(write)?;
|
1570 | Ok(())
|
1571 | }
|
1572 |
|
1573 | /// Read the value without validating.
|
1574 | pub fn read<R: Read>(read: &mut R) -> Result<Self> {
|
1575 | let x_size = u32::read(read)? as usize;
|
1576 | let y_size = u32::read(read)? as usize;
|
1577 |
|
1578 | let mode = u8::read(read)?;
|
1579 |
|
1580 | // wow you really saved that one byte here
|
1581 | // mode = level_mode + (rounding_mode * 16)
|
1582 | let level_mode = mode & 0b00001111; // wow that works
|
1583 | let rounding_mode = mode >> 4; // wow that works
|
1584 |
|
1585 | let level_mode = match level_mode {
|
1586 | 0 => LevelMode::Singular,
|
1587 | 1 => LevelMode::MipMap,
|
1588 | 2 => LevelMode::RipMap,
|
1589 | _ => return Err(Error::invalid("tile description level mode" )),
|
1590 | };
|
1591 |
|
1592 | let rounding_mode = match rounding_mode {
|
1593 | 0 => RoundingMode::Down,
|
1594 | 1 => RoundingMode::Up,
|
1595 | _ => return Err(Error::invalid("tile description rounding mode" )),
|
1596 | };
|
1597 |
|
1598 | Ok(TileDescription { tile_size: Vec2(x_size, y_size), level_mode, rounding_mode, })
|
1599 | }
|
1600 |
|
1601 | /// Validate this instance.
|
1602 | pub fn validate(&self) -> UnitResult {
|
1603 | let max = i32::MAX as i64 / 2;
|
1604 |
|
1605 | if self.tile_size.width() == 0 || self.tile_size.height() == 0
|
1606 | || self.tile_size.width() as i64 >= max || self.tile_size.height() as i64 >= max
|
1607 | {
|
1608 | return Err(Error::invalid("tile size" ))
|
1609 | }
|
1610 |
|
1611 | Ok(())
|
1612 | }
|
1613 | }
|
1614 |
|
1615 |
|
1616 | /// Number of bytes this attribute would consume in an exr file.
|
1617 | // TODO instead of pre calculating byte size, write to a tmp buffer whose length is inspected before actually writing?
|
1618 | pub fn byte_size(name: &Text, value: &AttributeValue) -> usize {
|
1619 | name.null_terminated_byte_size()
|
1620 | + value.kind_name().len() + sequence_end::byte_size()
|
1621 | + i32::BYTE_SIZE // serialized byte size
|
1622 | + value.byte_size()
|
1623 | }
|
1624 |
|
1625 | /// Without validation, write this attribute to the byte stream.
|
1626 | pub fn write<W: Write>(name: &[u8], value: &AttributeValue, write: &mut W) -> UnitResult {
|
1627 | Text::write_null_terminated_bytes(bytes:name, write)?;
|
1628 | Text::write_null_terminated_bytes(bytes:value.kind_name(), write)?;
|
1629 | i32::write(self:value.byte_size() as i32, write)?;
|
1630 | value.write(write)
|
1631 | }
|
1632 |
|
1633 | /// Read the attribute without validating. The result may be `Ok` even if this single attribute is invalid.
|
1634 | pub fn read(read: &mut PeekRead<impl Read>, max_size: usize) -> Result<(Text, Result<AttributeValue>)> {
|
1635 | let name: Text = Text::read_null_terminated(read, max_len:max_size)?;
|
1636 | let kind: Text = Text::read_null_terminated(read, max_len:max_size)?;
|
1637 | let size: usize = i32_to_usize(value:i32::read(read)?, error_message:"attribute size" )?;
|
1638 | let value: Result = AttributeValue::read(read, kind, byte_size:size)?;
|
1639 | Ok((name, value))
|
1640 | }
|
1641 |
|
1642 | /// Validate this attribute.
|
1643 | pub fn validate(name: &Text, value: &AttributeValue, long_names: &mut bool, allow_sampling: bool, data_window: IntegerBounds, strict: bool) -> UnitResult {
|
1644 | name.validate(null_terminated:true, long_names:Some(long_names))?; // only name text has length restriction
|
1645 | value.validate(allow_sampling, data_window, strict) // attribute value text length is never restricted
|
1646 | }
|
1647 |
|
1648 |
|
1649 | impl AttributeValue {
|
1650 |
|
1651 | /// Number of bytes this would consume in an exr file.
|
1652 | pub fn byte_size(&self) -> usize {
|
1653 | use self::AttributeValue::*;
|
1654 |
|
1655 | match *self {
|
1656 | IntegerBounds(_) => self::IntegerBounds::byte_size(),
|
1657 | FloatRect(_) => self::FloatRect::byte_size(),
|
1658 |
|
1659 | I32(_) => i32::BYTE_SIZE,
|
1660 | F32(_) => f32::BYTE_SIZE,
|
1661 | F64(_) => f64::BYTE_SIZE,
|
1662 |
|
1663 | Rational(_) => { i32::BYTE_SIZE + u32::BYTE_SIZE },
|
1664 | TimeCode(_) => self::TimeCode::BYTE_SIZE,
|
1665 |
|
1666 | IntVec2(_) => { 2 * i32::BYTE_SIZE },
|
1667 | FloatVec2(_) => { 2 * f32::BYTE_SIZE },
|
1668 | IntVec3(_) => { 3 * i32::BYTE_SIZE },
|
1669 | FloatVec3(_) => { 3 * f32::BYTE_SIZE },
|
1670 |
|
1671 | ChannelList(ref channels) => channels.byte_size(),
|
1672 | Chromaticities(_) => self::Chromaticities::byte_size(),
|
1673 | Compression(_) => self::Compression::byte_size(),
|
1674 | EnvironmentMap(_) => self::EnvironmentMap::byte_size(),
|
1675 |
|
1676 | KeyCode(_) => self::KeyCode::byte_size(),
|
1677 | LineOrder(_) => self::LineOrder::byte_size(),
|
1678 |
|
1679 | Matrix3x3(ref value) => value.len() * f32::BYTE_SIZE,
|
1680 | Matrix4x4(ref value) => value.len() * f32::BYTE_SIZE,
|
1681 |
|
1682 | Preview(ref value) => value.byte_size(),
|
1683 |
|
1684 | // attribute value texts never have limited size.
|
1685 | // also, don't serialize size, as it can be inferred from attribute size
|
1686 | Text(ref value) => value.bytes.len(),
|
1687 |
|
1688 | TextVector(ref value) => value.iter().map(self::Text::i32_sized_byte_size).sum(),
|
1689 | TileDescription(_) => self::TileDescription::byte_size(),
|
1690 | Custom { ref bytes, .. } => bytes.len(),
|
1691 | BlockType(ref kind) => kind.byte_size()
|
1692 | }
|
1693 | }
|
1694 |
|
1695 | /// The exr name string of the type that an attribute can have.
|
1696 | pub fn kind_name(&self) -> &[u8] {
|
1697 | use self::AttributeValue::*;
|
1698 | use self::type_names as ty;
|
1699 |
|
1700 | match *self {
|
1701 | IntegerBounds(_) => ty::I32BOX2,
|
1702 | FloatRect(_) => ty::F32BOX2,
|
1703 | I32(_) => ty::I32,
|
1704 | F32(_) => ty::F32,
|
1705 | F64(_) => ty::F64,
|
1706 | Rational(_) => ty::RATIONAL,
|
1707 | TimeCode(_) => ty::TIME_CODE,
|
1708 | IntVec2(_) => ty::I32VEC2,
|
1709 | FloatVec2(_) => ty::F32VEC2,
|
1710 | IntVec3(_) => ty::I32VEC3,
|
1711 | FloatVec3(_) => ty::F32VEC3,
|
1712 | ChannelList(_) => ty::CHANNEL_LIST,
|
1713 | Chromaticities(_) => ty::CHROMATICITIES,
|
1714 | Compression(_) => ty::COMPRESSION,
|
1715 | EnvironmentMap(_) => ty::ENVIRONMENT_MAP,
|
1716 | KeyCode(_) => ty::KEY_CODE,
|
1717 | LineOrder(_) => ty::LINE_ORDER,
|
1718 | Matrix3x3(_) => ty::F32MATRIX3X3,
|
1719 | Matrix4x4(_) => ty::F32MATRIX4X4,
|
1720 | Preview(_) => ty::PREVIEW,
|
1721 | Text(_) => ty::TEXT,
|
1722 | TextVector(_) => ty::TEXT_VECTOR,
|
1723 | TileDescription(_) => ty::TILES,
|
1724 | Custom { ref kind, .. } => &kind.bytes,
|
1725 | BlockType(_) => super::BlockType::TYPE_NAME,
|
1726 | }
|
1727 | }
|
1728 |
|
1729 | /// Without validation, write this instance to the byte stream.
|
1730 | pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
|
1731 | use self::AttributeValue::*;
|
1732 | match *self {
|
1733 | IntegerBounds(value) => value.write(write)?,
|
1734 | FloatRect(value) => value.write(write)?,
|
1735 |
|
1736 | I32(value) => value.write(write)?,
|
1737 | F32(value) => value.write(write)?,
|
1738 | F64(value) => value.write(write)?,
|
1739 |
|
1740 | Rational((a, b)) => { a.write(write)?; b.write(write)?; },
|
1741 | TimeCode(codes) => { codes.write(write)?; },
|
1742 |
|
1743 | IntVec2(Vec2(x, y)) => { x.write(write)?; y.write(write)?; },
|
1744 | FloatVec2(Vec2(x, y)) => { x.write(write)?; y.write(write)?; },
|
1745 | IntVec3((x, y, z)) => { x.write(write)?; y.write(write)?; z.write(write)?; },
|
1746 | FloatVec3((x, y, z)) => { x.write(write)?; y.write(write)?; z.write(write)?; },
|
1747 |
|
1748 | ChannelList(ref channels) => channels.write(write)?,
|
1749 | Chromaticities(ref value) => value.write(write)?,
|
1750 | Compression(value) => value.write(write)?,
|
1751 | EnvironmentMap(value) => value.write(write)?,
|
1752 |
|
1753 | KeyCode(value) => value.write(write)?,
|
1754 | LineOrder(value) => value.write(write)?,
|
1755 |
|
1756 | Matrix3x3(mut value) => f32::write_slice(write, &mut value)?,
|
1757 | Matrix4x4(mut value) => f32::write_slice(write, &mut value)?,
|
1758 |
|
1759 | Preview(ref value) => { value.write(write)?; },
|
1760 |
|
1761 | // attribute value texts never have limited size.
|
1762 | // also, don't serialize size, as it can be inferred from attribute size
|
1763 | Text(ref value) => u8::write_slice(write, value.bytes.as_slice())?,
|
1764 |
|
1765 | TextVector(ref value) => self::Text::write_vec_of_i32_sized_texts(write, value)?,
|
1766 | TileDescription(ref value) => value.write(write)?,
|
1767 | Custom { ref bytes, .. } => u8::write_slice(write, &bytes)?, // write.write(&bytes).map(|_| ()),
|
1768 | BlockType(kind) => kind.write(write)?
|
1769 | };
|
1770 |
|
1771 | Ok(())
|
1772 | }
|
1773 |
|
1774 | /// Read the value without validating.
|
1775 | /// Returns `Ok(Ok(attribute))` for valid attributes.
|
1776 | /// Returns `Ok(Err(Error))` for invalid attributes from a valid byte source.
|
1777 | /// Returns `Err(Error)` for invalid byte sources, for example for invalid files.
|
1778 | pub fn read(read: &mut PeekRead<impl Read>, kind: Text, byte_size: usize) -> Result<Result<Self>> {
|
1779 | use self::AttributeValue::*;
|
1780 | use self::type_names as ty;
|
1781 |
|
1782 | // always read bytes
|
1783 | let attribute_bytes = u8::read_vec(read, byte_size, 128, None, "attribute value size" )?;
|
1784 | // TODO no allocation for small attributes // : SmallVec<[u8; 64]> = smallvec![0; byte_size];
|
1785 |
|
1786 | let parse_attribute = move || {
|
1787 | let reader = &mut attribute_bytes.as_slice();
|
1788 |
|
1789 | Ok(match kind.bytes.as_slice() {
|
1790 | ty::I32BOX2 => IntegerBounds(self::IntegerBounds::read(reader)?),
|
1791 | ty::F32BOX2 => FloatRect(self::FloatRect::read(reader)?),
|
1792 |
|
1793 | ty::I32 => I32(i32::read(reader)?),
|
1794 | ty::F32 => F32(f32::read(reader)?),
|
1795 | ty::F64 => F64(f64::read(reader)?),
|
1796 |
|
1797 | ty::RATIONAL => Rational({
|
1798 | let a = i32::read(reader)?;
|
1799 | let b = u32::read(reader)?;
|
1800 | (a, b)
|
1801 | }),
|
1802 |
|
1803 | ty::TIME_CODE => TimeCode(self::TimeCode::read(reader)?),
|
1804 |
|
1805 | ty::I32VEC2 => IntVec2({
|
1806 | let a = i32::read(reader)?;
|
1807 | let b = i32::read(reader)?;
|
1808 | Vec2(a, b)
|
1809 | }),
|
1810 |
|
1811 | ty::F32VEC2 => FloatVec2({
|
1812 | let a = f32::read(reader)?;
|
1813 | let b = f32::read(reader)?;
|
1814 | Vec2(a, b)
|
1815 | }),
|
1816 |
|
1817 | ty::I32VEC3 => IntVec3({
|
1818 | let a = i32::read(reader)?;
|
1819 | let b = i32::read(reader)?;
|
1820 | let c = i32::read(reader)?;
|
1821 | (a, b, c)
|
1822 | }),
|
1823 |
|
1824 | ty::F32VEC3 => FloatVec3({
|
1825 | let a = f32::read(reader)?;
|
1826 | let b = f32::read(reader)?;
|
1827 | let c = f32::read(reader)?;
|
1828 | (a, b, c)
|
1829 | }),
|
1830 |
|
1831 | ty::CHANNEL_LIST => ChannelList(self::ChannelList::read(&mut PeekRead::new(attribute_bytes.as_slice()))?),
|
1832 | ty::CHROMATICITIES => Chromaticities(self::Chromaticities::read(reader)?),
|
1833 | ty::COMPRESSION => Compression(self::Compression::read(reader)?),
|
1834 | ty::ENVIRONMENT_MAP => EnvironmentMap(self::EnvironmentMap::read(reader)?),
|
1835 |
|
1836 | ty::KEY_CODE => KeyCode(self::KeyCode::read(reader)?),
|
1837 | ty::LINE_ORDER => LineOrder(self::LineOrder::read(reader)?),
|
1838 |
|
1839 | ty::F32MATRIX3X3 => Matrix3x3({
|
1840 | let mut result = [0.0_f32; 9];
|
1841 | f32::read_slice(reader, &mut result)?;
|
1842 | result
|
1843 | }),
|
1844 |
|
1845 | ty::F32MATRIX4X4 => Matrix4x4({
|
1846 | let mut result = [0.0_f32; 16];
|
1847 | f32::read_slice(reader, &mut result)?;
|
1848 | result
|
1849 | }),
|
1850 |
|
1851 | ty::PREVIEW => Preview(self::Preview::read(reader)?),
|
1852 | ty::TEXT => Text(self::Text::read_sized(reader, byte_size)?),
|
1853 |
|
1854 | // the number of strings can be inferred from the total attribute size
|
1855 | ty::TEXT_VECTOR => TextVector(self::Text::read_vec_of_i32_sized(
|
1856 | &mut PeekRead::new(attribute_bytes.as_slice()),
|
1857 | byte_size
|
1858 | )?),
|
1859 |
|
1860 | ty::TILES => TileDescription(self::TileDescription::read(reader)?),
|
1861 |
|
1862 | _ => Custom { kind: kind.clone(), bytes: attribute_bytes.clone() } // TODO no clone
|
1863 | })
|
1864 | };
|
1865 |
|
1866 | Ok(parse_attribute())
|
1867 | }
|
1868 |
|
1869 | /// Validate this instance.
|
1870 | pub fn validate(&self, allow_sampling: bool, data_window: IntegerBounds, strict: bool) -> UnitResult {
|
1871 | use self::AttributeValue::*;
|
1872 |
|
1873 | match *self {
|
1874 | ChannelList(ref channels) => channels.validate(allow_sampling, data_window, strict)?,
|
1875 | TileDescription(ref value) => value.validate()?,
|
1876 | Preview(ref value) => value.validate(strict)?,
|
1877 | TimeCode(ref time_code) => time_code.validate(strict)?,
|
1878 |
|
1879 | TextVector(ref vec) => if strict && vec.is_empty() {
|
1880 | return Err(Error::invalid("text vector may not be empty" ))
|
1881 | },
|
1882 |
|
1883 | _ => {}
|
1884 | };
|
1885 |
|
1886 | Ok(())
|
1887 | }
|
1888 |
|
1889 |
|
1890 | /// Return `Ok(i32)` if this attribute is an i32.
|
1891 | pub fn to_i32(&self) -> Result<i32> {
|
1892 | match *self {
|
1893 | AttributeValue::I32(value) => Ok(value),
|
1894 | _ => Err(invalid_type())
|
1895 | }
|
1896 | }
|
1897 |
|
1898 | /// Return `Ok(f32)` if this attribute is an f32.
|
1899 | pub fn to_f32(&self) -> Result<f32> {
|
1900 | match *self {
|
1901 | AttributeValue::F32(value) => Ok(value),
|
1902 | _ => Err(invalid_type())
|
1903 | }
|
1904 | }
|
1905 |
|
1906 | /// Return `Ok(Text)` if this attribute is a text.
|
1907 | pub fn into_text(self) -> Result<Text> {
|
1908 | match self {
|
1909 | AttributeValue::Text(value) => Ok(value),
|
1910 | _ => Err(invalid_type())
|
1911 | }
|
1912 | }
|
1913 |
|
1914 | /// Return `Ok(Text)` if this attribute is a text.
|
1915 | pub fn to_text(&self) -> Result<&Text> {
|
1916 | match self {
|
1917 | AttributeValue::Text(value) => Ok(value),
|
1918 | _ => Err(invalid_type())
|
1919 | }
|
1920 | }
|
1921 |
|
1922 | /// Return `Ok(Chromaticities)` if this attribute is a chromaticities attribute.
|
1923 | pub fn to_chromaticities(&self) -> Result<Chromaticities> {
|
1924 | match *self {
|
1925 | AttributeValue::Chromaticities(value) => Ok(value),
|
1926 | _ => Err(invalid_type())
|
1927 | }
|
1928 | }
|
1929 |
|
1930 | /// Return `Ok(TimeCode)` if this attribute is a time code.
|
1931 | pub fn to_time_code(&self) -> Result<TimeCode> {
|
1932 | match *self {
|
1933 | AttributeValue::TimeCode(value) => Ok(value),
|
1934 | _ => Err(invalid_type())
|
1935 | }
|
1936 | }
|
1937 | }
|
1938 |
|
1939 |
|
1940 |
|
1941 | /// Contains string literals identifying the type of an attribute.
|
1942 | pub mod type_names {
|
1943 | macro_rules! define_attribute_type_names {
|
1944 | ( $($name: ident : $value: expr),* ) => {
|
1945 | $(
|
1946 | /// The byte-string name of this attribute type as it appears in an exr file.
|
1947 | pub const $name: &'static [u8] = $value;
|
1948 | )*
|
1949 | };
|
1950 | }
|
1951 |
|
1952 | define_attribute_type_names! {
|
1953 | I32BOX2: b"box2i" ,
|
1954 | F32BOX2: b"box2f" ,
|
1955 | I32: b"int" ,
|
1956 | F32: b"float" ,
|
1957 | F64: b"double" ,
|
1958 | RATIONAL: b"rational" ,
|
1959 | TIME_CODE: b"timecode" ,
|
1960 | I32VEC2: b"v2i" ,
|
1961 | F32VEC2: b"v2f" ,
|
1962 | I32VEC3: b"v3i" ,
|
1963 | F32VEC3: b"v3f" ,
|
1964 | CHANNEL_LIST: b"chlist" ,
|
1965 | CHROMATICITIES: b"chromaticities" ,
|
1966 | COMPRESSION: b"compression" ,
|
1967 | ENVIRONMENT_MAP:b"envmap" ,
|
1968 | KEY_CODE: b"keycode" ,
|
1969 | LINE_ORDER: b"lineOrder" ,
|
1970 | F32MATRIX3X3: b"m33f" ,
|
1971 | F32MATRIX4X4: b"m44f" ,
|
1972 | PREVIEW: b"preview" ,
|
1973 | TEXT: b"string" ,
|
1974 | TEXT_VECTOR: b"stringvector" ,
|
1975 | TILES: b"tiledesc"
|
1976 | }
|
1977 | }
|
1978 |
|
1979 |
|
1980 | #[cfg (test)]
|
1981 | mod test {
|
1982 | use super::*;
|
1983 | use ::std::io::Cursor;
|
1984 | use rand::{random, thread_rng, Rng};
|
1985 |
|
1986 | #[test ]
|
1987 | fn text_ord() {
|
1988 | for _ in 0..1024 {
|
1989 | let text1 = Text::from_bytes_unchecked((0..4).map(|_| rand::random::<u8>()).collect());
|
1990 | let text2 = Text::from_bytes_unchecked((0..4).map(|_| rand::random::<u8>()).collect());
|
1991 |
|
1992 | assert_eq!(text1.to_string().cmp(&text2.to_string()), text1.cmp(&text2), "in text {:?} vs {:?}" , text1, text2);
|
1993 | }
|
1994 | }
|
1995 |
|
1996 | #[test ]
|
1997 | fn rounding_up(){
|
1998 | let round_up = RoundingMode::Up;
|
1999 | assert_eq!(round_up.divide(10, 10), 1, "divide equal" );
|
2000 | assert_eq!(round_up.divide(10, 2), 5, "divide even" );
|
2001 | assert_eq!(round_up.divide(10, 5), 2, "divide even" );
|
2002 |
|
2003 | assert_eq!(round_up.divide(8, 5), 2, "round up" );
|
2004 | assert_eq!(round_up.divide(10, 3), 4, "round up" );
|
2005 | assert_eq!(round_up.divide(100, 50), 2, "divide even" );
|
2006 | assert_eq!(round_up.divide(100, 49), 3, "round up" );
|
2007 | }
|
2008 |
|
2009 | #[test ]
|
2010 | fn rounding_down(){
|
2011 | let round_down = RoundingMode::Down;
|
2012 | assert_eq!(round_down.divide(8, 5), 1, "round down" );
|
2013 | assert_eq!(round_down.divide(10, 3), 3, "round down" );
|
2014 | assert_eq!(round_down.divide(100, 50), 2, "divide even" );
|
2015 | assert_eq!(round_down.divide(100, 49), 2, "round down" );
|
2016 | assert_eq!(round_down.divide(100, 51), 1, "round down" );
|
2017 | }
|
2018 |
|
2019 | #[test ]
|
2020 | fn tile_description_write_read_roundtrip(){
|
2021 | let tiles = [
|
2022 | TileDescription {
|
2023 | tile_size: Vec2(31, 7),
|
2024 | level_mode: LevelMode::MipMap,
|
2025 | rounding_mode: RoundingMode::Down,
|
2026 | },
|
2027 |
|
2028 | TileDescription {
|
2029 | tile_size: Vec2(0, 0),
|
2030 | level_mode: LevelMode::Singular,
|
2031 | rounding_mode: RoundingMode::Up,
|
2032 | },
|
2033 |
|
2034 | TileDescription {
|
2035 | tile_size: Vec2(4294967294, 4294967295),
|
2036 | level_mode: LevelMode::RipMap,
|
2037 | rounding_mode: RoundingMode::Down,
|
2038 | },
|
2039 | ];
|
2040 |
|
2041 | for tile in &tiles {
|
2042 | let mut bytes = Vec::new();
|
2043 | tile.write(&mut bytes).unwrap();
|
2044 |
|
2045 | let new_tile = TileDescription::read(&mut Cursor::new(bytes)).unwrap();
|
2046 | assert_eq!(*tile, new_tile, "tile round trip" );
|
2047 | }
|
2048 | }
|
2049 |
|
2050 | #[test ]
|
2051 | fn attribute_write_read_roundtrip_and_byte_size(){
|
2052 | let attributes = [
|
2053 | (
|
2054 | Text::from("greeting" ),
|
2055 | AttributeValue::Text(Text::from("hello" )),
|
2056 | ),
|
2057 | (
|
2058 | Text::from("age" ),
|
2059 | AttributeValue::I32(923),
|
2060 | ),
|
2061 | (
|
2062 | Text::from("leg count" ),
|
2063 | AttributeValue::F64(9.114939599234),
|
2064 | ),
|
2065 | (
|
2066 | Text::from("rabbit area" ),
|
2067 | AttributeValue::FloatRect(FloatRect {
|
2068 | min: Vec2(23.4234, 345.23),
|
2069 | max: Vec2(68623.0, 3.12425926538),
|
2070 | }),
|
2071 | ),
|
2072 | (
|
2073 | Text::from("rabbit area int" ),
|
2074 | AttributeValue::IntegerBounds(IntegerBounds {
|
2075 | position: Vec2(23, 345),
|
2076 | size: Vec2(68623, 3),
|
2077 | }),
|
2078 | ),
|
2079 | (
|
2080 | Text::from("rabbit area int" ),
|
2081 | AttributeValue::IntegerBounds(IntegerBounds {
|
2082 | position: Vec2(-(i32::MAX / 2 - 1), -(i32::MAX / 2 - 1)),
|
2083 | size: Vec2(i32::MAX as usize - 2, i32::MAX as usize - 2),
|
2084 | }),
|
2085 | ),
|
2086 | (
|
2087 | Text::from("rabbit area int 2" ),
|
2088 | AttributeValue::IntegerBounds(IntegerBounds {
|
2089 | position: Vec2(0, 0),
|
2090 | size: Vec2(i32::MAX as usize / 2 - 1, i32::MAX as usize / 2 - 1),
|
2091 | }),
|
2092 | ),
|
2093 | (
|
2094 | Text::from("tests are difficult" ),
|
2095 | AttributeValue::TextVector(vec![
|
2096 | Text::from("sdoifjpsdv" ),
|
2097 | Text::from("sdoifjpsdvxxxx" ),
|
2098 | Text::from("sdoifjasd" ),
|
2099 | Text::from("sdoifj" ),
|
2100 | Text::from("sdoifjddddddddasdasd" ),
|
2101 | ]),
|
2102 | ),
|
2103 | (
|
2104 | Text::from("what should we eat tonight" ),
|
2105 | AttributeValue::Preview(Preview {
|
2106 | size: Vec2(10, 30),
|
2107 | pixel_data: vec![31; 10 * 30 * 4],
|
2108 | }),
|
2109 | ),
|
2110 | (
|
2111 | Text::from("leg count, again" ),
|
2112 | AttributeValue::ChannelList(ChannelList::new(smallvec![
|
2113 | ChannelDescription {
|
2114 | name: Text::from("Green" ),
|
2115 | sample_type: SampleType::F16,
|
2116 | quantize_linearly: false,
|
2117 | sampling: Vec2(1,2)
|
2118 | },
|
2119 | ChannelDescription {
|
2120 | name: Text::from("Red" ),
|
2121 | sample_type: SampleType::F32,
|
2122 | quantize_linearly: true,
|
2123 | sampling: Vec2(1,2)
|
2124 | },
|
2125 | ChannelDescription {
|
2126 | name: Text::from("Purple" ),
|
2127 | sample_type: SampleType::U32,
|
2128 | quantize_linearly: false,
|
2129 | sampling: Vec2(0,0)
|
2130 | }
|
2131 | ],
|
2132 | )),
|
2133 | ),
|
2134 | ];
|
2135 |
|
2136 | for (name, value) in &attributes {
|
2137 | let mut bytes = Vec::new();
|
2138 | super::write(name.as_slice(), value, &mut bytes).unwrap();
|
2139 | assert_eq!(super::byte_size(name, value), bytes.len(), "attribute.byte_size() for {:?}" , (name, value));
|
2140 |
|
2141 | let new_attribute = super::read(&mut PeekRead::new(Cursor::new(bytes)), 300).unwrap();
|
2142 | assert_eq!((name.clone(), value.clone()), (new_attribute.0, new_attribute.1.unwrap()), "attribute round trip" );
|
2143 | }
|
2144 |
|
2145 |
|
2146 | {
|
2147 | let (name, value) = (
|
2148 | Text::from("asdkaspfokpaosdkfpaokswdpoakpsfokaposdkf" ),
|
2149 | AttributeValue::I32(0),
|
2150 | );
|
2151 |
|
2152 | let mut long_names = false;
|
2153 | super::validate(&name, &value, &mut long_names, false, IntegerBounds::zero(), false).unwrap();
|
2154 | assert!(long_names);
|
2155 | }
|
2156 |
|
2157 | {
|
2158 | let (name, value) = (
|
2159 | Text::from("sdöksadöofkaspdolkpöasolfkcöalsod,kfcöaslodkcpöasolkfposdöksadöofkaspdolkpöasolfkcöalsod,kfcöaslodkcpöasolkfposdöksadöofkaspdolkpöasolfkcöalsod,kfcöaslodkcpöasolkfposdöksadöofkaspdolkpöasolfkcöalsod,kfcöaslodkcpöasolkfposdöksadöofkaspdolkpöasolfkcöalsod,kfcöaslodkcpöasolkfposdöksadöofkaspdolkpöasolfkcöalsod,kfcöaslodkcpöasolkfpo" ),
|
2160 | AttributeValue::I32(0),
|
2161 | );
|
2162 |
|
2163 | super::validate(&name, &value, &mut false, false, IntegerBounds::zero(), false).expect_err("name length check failed" );
|
2164 | }
|
2165 | }
|
2166 |
|
2167 | #[test ]
|
2168 | fn time_code_pack(){
|
2169 | let mut rng = thread_rng();
|
2170 |
|
2171 | let codes = std::iter::repeat_with(|| TimeCode {
|
2172 | hours: rng.gen_range(0 .. 24),
|
2173 | minutes: rng.gen_range(0 .. 60),
|
2174 | seconds: rng.gen_range(0 .. 60),
|
2175 | frame: rng.gen_range(0 .. 29),
|
2176 | drop_frame: random(),
|
2177 | color_frame: random(),
|
2178 | field_phase: random(),
|
2179 | binary_group_flags: [random(),random(),random()],
|
2180 | binary_groups: std::iter::repeat_with(|| rng.gen_range(0 .. 16)).take(8)
|
2181 | .collect::<SmallVec<[u8;8]>>().into_inner().unwrap()
|
2182 | });
|
2183 |
|
2184 | for code in codes.take(500) {
|
2185 | code.validate(true).expect("invalid timecode test input" );
|
2186 |
|
2187 | { // through tv60 packing, roundtrip
|
2188 | let packed_tv60 = code.pack_time_as_tv60_u32().expect("invalid timecode test input" );
|
2189 | let packed_user = code.pack_user_data_as_u32();
|
2190 | assert_eq!(TimeCode::from_tv60_time(packed_tv60, packed_user), code);
|
2191 | }
|
2192 |
|
2193 | { // through bytes, roundtrip
|
2194 | let mut bytes = Vec::<u8>::new();
|
2195 | code.write(&mut bytes).unwrap();
|
2196 | let decoded = TimeCode::read(&mut bytes.as_slice()).unwrap();
|
2197 | assert_eq!(code, decoded);
|
2198 | }
|
2199 |
|
2200 | {
|
2201 | let tv50_code = TimeCode {
|
2202 | drop_frame: false, // apparently, tv50 does not support drop frame, so do not use this value
|
2203 | .. code
|
2204 | };
|
2205 |
|
2206 | let packed_tv50 = code.pack_time_as_tv50_u32().expect("invalid timecode test input" );
|
2207 | let packed_user = code.pack_user_data_as_u32();
|
2208 | assert_eq!(TimeCode::from_tv50_time(packed_tv50, packed_user), tv50_code);
|
2209 | }
|
2210 |
|
2211 | {
|
2212 | let film24_code = TimeCode {
|
2213 | // apparently, film24 does not support some flags, so do not use those values
|
2214 | color_frame: false,
|
2215 | drop_frame: false,
|
2216 | .. code
|
2217 | };
|
2218 |
|
2219 | let packed_film24 = code.pack_time_as_film24_u32().expect("invalid timecode test input" );
|
2220 | let packed_user = code.pack_user_data_as_u32();
|
2221 | assert_eq!(TimeCode::from_film24_time(packed_film24, packed_user), film24_code);
|
2222 | }
|
2223 | }
|
2224 | }
|
2225 |
|
2226 | }
|
2227 | |