1use crate::car::CSIItem;
8use crate::model::rendition::{self, RenditionType};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum ExifOrientation {
12 None,
13 Normal,
14 Mirrored,
15 Rotated180,
16 Rotated180Mirrored,
17 Rotated90,
18 Rotated90Mirrored,
19 Rotated270,
20 Rotated270Mirrored,
21 Unknown(u32),
22}
23
24impl ExifOrientation {
25 pub fn from_raw(value: &rendition::EXIFOrientationValue) -> Self {
26 match value {
30 rendition::EXIFOrientationValue::None => Self::None,
31 rendition::EXIFOrientationValue::Normal => Self::Normal,
32 rendition::EXIFOrientationValue::Mirrored => Self::Mirrored,
33 rendition::EXIFOrientationValue::Rotated180 => Self::Rotated180,
34 rendition::EXIFOrientationValue::Rotated180Mirrored => Self::Rotated180Mirrored,
35 rendition::EXIFOrientationValue::Rotated90 => Self::Rotated90Mirrored,
36 rendition::EXIFOrientationValue::Rotated90Mirrored => Self::Rotated90,
37 rendition::EXIFOrientationValue::Rotated270 => Self::Rotated270Mirrored,
38 rendition::EXIFOrientationValue::Rotated270Mirrored => Self::Rotated270,
39 rendition::EXIFOrientationValue::Unknown { tag } => Self::Unknown(*tag),
40 }
41 }
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub struct TlvRect {
46 pub x: u32,
47 pub y: u32,
48 pub width: u32,
49 pub height: u32,
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub struct TlvMetric {
54 pub width: u32,
55 pub height: u32,
56}
57
58#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct TlvMetrics {
60 pub top_right_inset: TlvMetric,
61 pub bottom_left_inset: TlvMetric,
62 pub image_size: TlvMetric,
63}
64
65#[derive(Debug, Clone, PartialEq, Eq)]
66pub struct TlvReference {
67 pub x: u32,
68 pub y: u32,
69 pub width: u32,
70 pub height: u32,
71 pub layout: u16,
72 pub key_byte_len: usize,
73}
74
75#[derive(Debug, Clone, PartialEq, Eq)]
76pub struct UnknownTlv {
77 pub tag: u32,
78 pub byte_len: usize,
79}
80
81#[derive(Debug, Clone, Default, PartialEq, Eq)]
82pub struct TlvMetadata {
83 pub slices: Vec<TlvRect>,
84 pub metrics: Option<TlvMetrics>,
85 pub uti: Option<String>,
86 pub exif_orientation: Option<ExifOrientation>,
87 pub bytes_per_row: Option<u32>,
88 pub reference: Option<TlvReference>,
89 pub unknown: Vec<UnknownTlv>,
90}
91
92impl CSIItem {
93 pub fn tlv_metadata(&self) -> TlvMetadata {
94 let mut metadata = TlvMetadata::default();
95
96 for tlv in &self.header().tlv_data {
97 match tlv {
98 RenditionType::Slices(slice) => {
99 metadata
100 .slices
101 .extend(slice.data.iter().map(|slice| TlvRect {
102 x: slice.x,
103 y: slice.y,
104 width: slice.width,
105 height: slice.height,
106 }));
107 }
108 RenditionType::Metrics(metrics) => {
109 metadata.metrics = Some(TlvMetrics {
110 top_right_inset: TlvMetric {
111 width: metrics.top_right_inset.width,
112 height: metrics.top_right_inset.height,
113 },
114 bottom_left_inset: TlvMetric {
115 width: metrics.bottom_left_inset.width,
116 height: metrics.bottom_left_inset.height,
117 },
118 image_size: TlvMetric {
119 width: metrics.image_size.width,
120 height: metrics.image_size.height,
121 },
122 });
123 }
124 RenditionType::UTI(uti) => {
125 metadata.uti = Some(
126 String::from_utf8_lossy(&uti.string)
127 .trim_end_matches('\0')
128 .to_string(),
129 );
130 }
131 RenditionType::EXIFOrientation(orientation) => {
132 metadata.exif_orientation =
133 Some(ExifOrientation::from_raw(&orientation.orientation));
134 }
135 RenditionType::BytesPerRow { bytes_per_row, .. } => {
136 metadata.bytes_per_row = Some(*bytes_per_row);
137 }
138 RenditionType::Reference(reference) => {
139 metadata.reference = Some(TlvReference {
140 x: reference.x,
141 y: reference.y,
142 width: reference.width,
143 height: reference.height,
144 layout: reference.layout,
145 key_byte_len: reference.keys.len(),
146 });
147 }
148 RenditionType::Unknown { tag, data } => {
149 metadata.unknown.push(UnknownTlv {
150 tag: *tag,
151 byte_len: data.data.len(),
152 });
153 }
154 RenditionType::BlendModeAndOpacity(_) => {}
155 }
156 }
157
158 metadata
159 }
160
161 pub fn exif_orientation(&self) -> Option<ExifOrientation> {
162 self.header().tlv_data.iter().find_map(|tlv| match tlv {
163 RenditionType::EXIFOrientation(orientation) => {
164 Some(ExifOrientation::from_raw(&orientation.orientation))
165 }
166 _ => None,
167 })
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::ExifOrientation;
174 use crate::model::rendition::EXIFOrientationValue;
175
176 #[test]
177 fn exif_orientation_from_raw_maps_standard_exif_5_to_8() {
178 assert_eq!(
179 ExifOrientation::from_raw(&EXIFOrientationValue::Rotated90),
180 ExifOrientation::Rotated90Mirrored
181 );
182 assert_eq!(
183 ExifOrientation::from_raw(&EXIFOrientationValue::Rotated90Mirrored),
184 ExifOrientation::Rotated90
185 );
186 assert_eq!(
187 ExifOrientation::from_raw(&EXIFOrientationValue::Rotated270),
188 ExifOrientation::Rotated270Mirrored
189 );
190 assert_eq!(
191 ExifOrientation::from_raw(&EXIFOrientationValue::Rotated270Mirrored),
192 ExifOrientation::Rotated270
193 );
194 }
195}