image/
metadata.rs
1use std::io::{Cursor, Read};
4
5use byteorder_lite::{BigEndian, LittleEndian, ReadBytesExt};
6
7#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
14pub enum Orientation {
15 NoTransforms,
17 Rotate90,
19 Rotate180,
21 Rotate270,
23 FlipHorizontal,
25 FlipVertical,
27 Rotate90FlipH,
29 Rotate270FlipH,
31}
32
33impl Orientation {
34 #[must_use]
36 pub fn from_exif(exif_orientation: u8) -> Option<Self> {
37 match exif_orientation {
38 1 => Some(Self::NoTransforms),
39 2 => Some(Self::FlipHorizontal),
40 3 => Some(Self::Rotate180),
41 4 => Some(Self::FlipVertical),
42 5 => Some(Self::Rotate90FlipH),
43 6 => Some(Self::Rotate90),
44 7 => Some(Self::Rotate270FlipH),
45 8 => Some(Self::Rotate270),
46 0 | 9.. => None,
47 }
48 }
49
50 #[must_use]
52 pub fn to_exif(self) -> u8 {
53 match self {
54 Self::NoTransforms => 1,
55 Self::FlipHorizontal => 2,
56 Self::Rotate180 => 3,
57 Self::FlipVertical => 4,
58 Self::Rotate90FlipH => 5,
59 Self::Rotate90 => 6,
60 Self::Rotate270FlipH => 7,
61 Self::Rotate270 => 8,
62 }
63 }
64
65 pub(crate) fn from_exif_chunk(chunk: &[u8]) -> Option<Self> {
66 let mut reader = Cursor::new(chunk);
67
68 let mut magic = [0; 4];
69 reader.read_exact(&mut magic).ok()?;
70
71 match magic {
72 [0x49, 0x49, 42, 0] => {
73 let ifd_offset = reader.read_u32::<LittleEndian>().ok()?;
74 reader.set_position(u64::from(ifd_offset));
75 let entries = reader.read_u16::<LittleEndian>().ok()?;
76 for _ in 0..entries {
77 let tag = reader.read_u16::<LittleEndian>().ok()?;
78 let format = reader.read_u16::<LittleEndian>().ok()?;
79 let count = reader.read_u32::<LittleEndian>().ok()?;
80 let value = reader.read_u16::<LittleEndian>().ok()?;
81 let _padding = reader.read_u16::<LittleEndian>().ok()?;
82 if tag == 0x112 && format == 3 && count == 1 {
83 return Self::from_exif(value.min(255) as u8);
84 }
85 }
86 }
87 [0x4d, 0x4d, 0, 42] => {
88 let ifd_offset = reader.read_u32::<BigEndian>().ok()?;
89 reader.set_position(u64::from(ifd_offset));
90 let entries = reader.read_u16::<BigEndian>().ok()?;
91 for _ in 0..entries {
92 let tag = reader.read_u16::<BigEndian>().ok()?;
93 let format = reader.read_u16::<BigEndian>().ok()?;
94 let count = reader.read_u32::<BigEndian>().ok()?;
95 let value = reader.read_u16::<BigEndian>().ok()?;
96 let _padding = reader.read_u16::<BigEndian>().ok()?;
97 if tag == 0x112 && format == 3 && count == 1 {
98 return Self::from_exif(value.min(255) as u8);
99 }
100 }
101 }
102 _ => {}
103 }
104 None
105 }
106}