1 | use std::io::{self, BufRead, Seek, SeekFrom}; |
2 | |
3 | use crate::{ |
4 | util::{read_i32, read_null_terminated_string, read_u32, Endian}, |
5 | ImageResult, ImageSize, |
6 | }; |
7 | |
8 | pub fn size<R: BufRead + Seek>(reader: &mut R) -> ImageResult<ImageSize> { |
9 | reader.seek(SeekFrom::Start(4))?; |
10 | |
11 | // If long names flag is set then max attribute name and type name is 255, otherwise it's only 31 |
12 | let flags = read_u32(reader, &Endian::Little)?; |
13 | let long_names = flags & 0x400 != 0; |
14 | let max_name_size = if long_names { 255 } else { 31 }; |
15 | |
16 | // Read header attributes until we find the dataWindow attribute |
17 | loop { |
18 | let attr_name = read_null_terminated_string(reader, max_name_size)?; |
19 | if attr_name.is_empty() { |
20 | break; // End of the header |
21 | } |
22 | |
23 | let attr_type = read_null_terminated_string(reader, max_name_size)?; |
24 | |
25 | // Skip attr_size |
26 | let attr_size = read_u32(reader, &Endian::Little)?; |
27 | |
28 | if attr_name == "dataWindow" && attr_type == "box2i" { |
29 | // Read the data window values |
30 | let x_min = read_i32(reader, &Endian::Little)? as i64; |
31 | let y_min = read_i32(reader, &Endian::Little)? as i64; |
32 | let x_max = read_i32(reader, &Endian::Little)? as i64; |
33 | let y_max = read_i32(reader, &Endian::Little)? as i64; |
34 | |
35 | if x_min > x_max || y_min > y_max { |
36 | continue; |
37 | } |
38 | |
39 | let width = (x_max - x_min + 1) as usize; |
40 | let height = (y_max - y_min + 1) as usize; |
41 | |
42 | return Ok(ImageSize { width, height }); |
43 | } else { |
44 | // Skip the attribute value |
45 | reader.seek(SeekFrom::Current(attr_size as i64))?; |
46 | } |
47 | } |
48 | |
49 | Err(io::Error::new(io::ErrorKind::InvalidData, "Data window not found" ).into()) |
50 | } |
51 | |
52 | pub fn matches(header: &[u8]) -> bool { |
53 | let exr_magic_number: [u8; 4] = [0x76, 0x2f, 0x31, 0x01]; |
54 | header.starts_with(&exr_magic_number) |
55 | } |
56 | |