use zenpixels::Orientation;
pub fn parse_exif_orientation(data: &[u8]) -> Option<Orientation> {
crate::exif::Exif::parse(data)?.orientation()
}
pub fn set_exif_orientation(data: &[u8], value: Orientation) -> Option<alloc::vec::Vec<u8>> {
crate::exif::set_orientation(data, value)
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec::Vec;
fn tiff(order_be: bool, value: u32, type_id: u16) -> Vec<u8> {
let mut v = Vec::new();
let w16 = |v: &mut Vec<u8>, x: u16| {
v.extend_from_slice(&if order_be {
x.to_be_bytes()
} else {
x.to_le_bytes()
})
};
let w32 = |v: &mut Vec<u8>, x: u32| {
v.extend_from_slice(&if order_be {
x.to_be_bytes()
} else {
x.to_le_bytes()
})
};
v.extend_from_slice(if order_be { b"MM" } else { b"II" });
w16(&mut v, 42);
w32(&mut v, 8);
w16(&mut v, 1); w16(&mut v, 0x0112);
w16(&mut v, type_id);
w32(&mut v, 1);
if type_id == 3 {
w16(&mut v, value as u16);
w16(&mut v, 0);
} else {
w32(&mut v, value);
}
w32(&mut v, 0); v
}
#[test]
fn short_little_endian() {
assert_eq!(
parse_exif_orientation(&tiff(false, 6, 3)),
Some(Orientation::Rotate90)
);
}
#[test]
fn short_big_endian() {
assert_eq!(
parse_exif_orientation(&tiff(true, 6, 3)),
Some(Orientation::Rotate90)
);
}
#[test]
fn long_type_both_orders() {
assert_eq!(
parse_exif_orientation(&tiff(false, 8, 4)),
Some(Orientation::Rotate270)
);
assert_eq!(
parse_exif_orientation(&tiff(true, 8, 4)),
Some(Orientation::Rotate270)
);
}
#[test]
fn with_exif_prefix() {
let mut blob = b"Exif\0\0".to_vec();
blob.extend_from_slice(&tiff(false, 6, 3));
assert_eq!(parse_exif_orientation(&blob), Some(Orientation::Rotate90));
}
#[test]
fn invalid_inputs_return_none() {
assert_eq!(parse_exif_orientation(b"garbage"), None);
assert_eq!(parse_exif_orientation(&[]), None);
assert_eq!(parse_exif_orientation(&[0u8; 7]), None);
assert_eq!(parse_exif_orientation(&tiff(false, 9, 3)), None);
assert_eq!(parse_exif_orientation(&tiff(false, 0, 3)), None);
}
#[test]
fn set_orientation_roundtrips_all_orders_and_types() {
for be in [false, true] {
for &type_id in &[3u16, 4] {
let blob = tiff(be, 6, type_id);
assert_eq!(parse_exif_orientation(&blob), Some(Orientation::Rotate90));
let rewritten =
set_exif_orientation(&blob, Orientation::Identity).expect("tag present");
assert_eq!(rewritten.len(), blob.len()); assert_eq!(
parse_exif_orientation(&rewritten),
Some(Orientation::Identity)
);
}
}
}
#[test]
fn set_orientation_with_exif_prefix() {
let mut blob = b"Exif\0\0".to_vec();
blob.extend_from_slice(&tiff(false, 6, 3));
let out = set_exif_orientation(&blob, Orientation::Rotate180).expect("tag present");
assert_eq!(parse_exif_orientation(&out), Some(Orientation::Rotate180));
}
#[test]
fn set_orientation_absent_tag_or_garbage_is_none() {
let mut v = b"II".to_vec();
v.extend_from_slice(&42u16.to_le_bytes());
v.extend_from_slice(&8u32.to_le_bytes());
v.extend_from_slice(&1u16.to_le_bytes()); v.extend_from_slice(&0x010Fu16.to_le_bytes()); v.extend_from_slice(&3u16.to_le_bytes());
v.extend_from_slice(&1u32.to_le_bytes());
v.extend_from_slice(&[0, 0, 0, 0]);
v.extend_from_slice(&0u32.to_le_bytes());
assert_eq!(set_exif_orientation(&v, Orientation::Identity), None);
assert_eq!(
set_exif_orientation(b"garbage", Orientation::Identity),
None
);
assert_eq!(set_exif_orientation(&[], Orientation::Identity), None);
}
}