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 pub fn from_exif(exif_orientation: u8) -> Option<Self> {
36 match exif_orientation {
37 1 => Some(Self::NoTransforms),
38 2 => Some(Self::FlipHorizontal),
39 3 => Some(Self::Rotate180),
40 4 => Some(Self::FlipVertical),
41 5 => Some(Self::Rotate90FlipH),
42 6 => Some(Self::Rotate90),
43 7 => Some(Self::Rotate270FlipH),
44 8 => Some(Self::Rotate270),
45 0 | 9.. => None,
46 }
47 }
48
49 pub fn to_exif(self) -> u8 {
51 match self {
52 Self::NoTransforms => 1,
53 Self::FlipHorizontal => 2,
54 Self::Rotate180 => 3,
55 Self::FlipVertical => 4,
56 Self::Rotate90FlipH => 5,
57 Self::Rotate90 => 6,
58 Self::Rotate270FlipH => 7,
59 Self::Rotate270 => 8,
60 }
61 }
62
63 pub(crate) fn from_exif_chunk(chunk: &[u8]) -> Option<Self> {
64 let mut reader = Cursor::new(chunk);
65
66 let mut magic = [0; 4];
67 reader.read_exact(&mut magic).ok()?;
68
69 match magic {
70 [0x49, 0x49, 42, 0] => {
71 let ifd_offset = reader.read_u32::<LittleEndian>().ok()?;
72 reader.set_position(ifd_offset as u64);
73 let entries = reader.read_u16::<LittleEndian>().ok()?;
74 for _ in 0..entries {
75 let tag = reader.read_u16::<LittleEndian>().ok()?;
76 let format = reader.read_u16::<LittleEndian>().ok()?;
77 let count = reader.read_u32::<LittleEndian>().ok()?;
78 let value = reader.read_u16::<LittleEndian>().ok()?;
79 let _padding = reader.read_u16::<LittleEndian>().ok()?;
80 if tag == 0x112 && format == 3 && count == 1 {
81 return Self::from_exif(value.min(255) as u8);
82 }
83 }
84 }
85 [0x4d, 0x4d, 0, 42] => {
86 let ifd_offset = reader.read_u32::<BigEndian>().ok()?;
87 reader.set_position(ifd_offset as u64);
88 let entries = reader.read_u16::<BigEndian>().ok()?;
89 for _ in 0..entries {
90 let tag = reader.read_u16::<BigEndian>().ok()?;
91 let format = reader.read_u16::<BigEndian>().ok()?;
92 let count = reader.read_u32::<BigEndian>().ok()?;
93 let value = reader.read_u16::<BigEndian>().ok()?;
94 let _padding = reader.read_u16::<BigEndian>().ok()?;
95 if tag == 0x112 && format == 3 && count == 1 {
96 return Self::from_exif(value.min(255) as u8);
97 }
98 }
99 }
100 _ => {}
101 }
102 None
103 }
104}