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