| 1 | use crate::util::*; |
| 2 | use crate::{ImageResult, ImageSize}; |
| 3 | |
| 4 | use std::io::{BufRead, Cursor, Seek, SeekFrom}; |
| 5 | |
| 6 | pub fn size<R: BufRead + Seek>(reader: &mut R) -> ImageResult<ImageSize> { |
| 7 | reader.seek(SeekFrom::Start(0))?; |
| 8 | |
| 9 | let mut endian_marker = [0; 2]; |
| 10 | reader.read_exact(&mut endian_marker)?; |
| 11 | |
| 12 | // Get the endianness which determines how we read the input |
| 13 | let endianness = if &endian_marker[0..2] == b"II" { |
| 14 | Endian::Little |
| 15 | } else if &endian_marker[0..2] == b"MM" { |
| 16 | Endian::Big |
| 17 | } else { |
| 18 | // Shouldn't get here by normal means, but handle invalid header anyway |
| 19 | return Err( |
| 20 | std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid TIFF header" ).into(), |
| 21 | ); |
| 22 | }; |
| 23 | |
| 24 | // Read the IFD offset from the header |
| 25 | reader.seek(SeekFrom::Start(4))?; |
| 26 | let ifd_offset = read_u32(reader, &endianness)?; |
| 27 | |
| 28 | // IFD offset cannot be 0 |
| 29 | if ifd_offset == 0 { |
| 30 | return Err( |
| 31 | std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid IFD offset" ).into(), |
| 32 | ); |
| 33 | } |
| 34 | |
| 35 | // Jump to the IFD offset |
| 36 | reader.seek(SeekFrom::Start(ifd_offset.into()))?; |
| 37 | |
| 38 | // Read how many IFD records there are |
| 39 | let ifd_count = read_u16(reader, &endianness)?; |
| 40 | let mut width = None; |
| 41 | let mut height = None; |
| 42 | |
| 43 | for _ifd in 0..ifd_count { |
| 44 | let tag = read_u16(reader, &endianness)?; |
| 45 | let kind = read_u16(reader, &endianness)?; |
| 46 | let _count = read_u32(reader, &endianness)?; |
| 47 | |
| 48 | let value_bytes = match kind { |
| 49 | // BYTE | ASCII | SBYTE | UNDEFINED |
| 50 | 1 | 2 | 6 | 7 => 1, |
| 51 | // SHORT | SSHORT |
| 52 | 3 | 8 => 2, |
| 53 | // LONG | SLONG | FLOAT | IFD |
| 54 | 4 | 9 | 11 | 13 => 4, |
| 55 | // RATIONAL | SRATIONAL |
| 56 | 5 | 10 => 4 * 2, |
| 57 | // DOUBLE | LONG8 | SLONG8 | IFD8 |
| 58 | 12 | 16 | 17 | 18 => 8, |
| 59 | // Anything else is invalid |
| 60 | _ => { |
| 61 | return Err(std::io::Error::new( |
| 62 | std::io::ErrorKind::InvalidData, |
| 63 | "Invalid IFD type" , |
| 64 | ) |
| 65 | .into()) |
| 66 | } |
| 67 | }; |
| 68 | |
| 69 | let mut value_buffer = [0; 4]; |
| 70 | reader.read_exact(&mut value_buffer)?; |
| 71 | |
| 72 | let mut r = Cursor::new(&value_buffer[..]); |
| 73 | let value = match value_bytes { |
| 74 | 2 => Some(read_u16(&mut r, &endianness)? as u32), |
| 75 | 4 => Some(read_u32(&mut r, &endianness)?), |
| 76 | _ => None, |
| 77 | }; |
| 78 | |
| 79 | // Tag 0x100 is the image width, 0x101 is image height |
| 80 | if tag == 0x100 { |
| 81 | width = value; |
| 82 | } else if tag == 0x101 { |
| 83 | height = value; |
| 84 | } |
| 85 | |
| 86 | // If we've read both values we need, return the data |
| 87 | if let (Some(width), Some(height)) = (width, height) { |
| 88 | return Ok(ImageSize { |
| 89 | width: width as usize, |
| 90 | height: height as usize, |
| 91 | }); |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | // If no width/height pair was found return invalid data |
| 96 | Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "No dimensions in IFD tags" ).into()) |
| 97 | } |
| 98 | |
| 99 | pub fn matches(header: &[u8]) -> bool { |
| 100 | header.starts_with(needle:b"II \x2A\x00" ) || header.starts_with(needle:b"MM \x00\x2A" ) |
| 101 | } |
| 102 | |