#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum Orientation {
#[default]
Normal,
FlipH,
Rotate180,
FlipV,
Transpose,
Rotate90,
Transverse,
Rotate270,
}
impl Orientation {
pub fn from_exif(v: u16) -> Self {
match v {
2 => Orientation::FlipH,
3 => Orientation::Rotate180,
4 => Orientation::FlipV,
5 => Orientation::Transpose,
6 => Orientation::Rotate90,
7 => Orientation::Transverse,
8 => Orientation::Rotate270,
_ => Orientation::Normal,
}
}
pub fn is_identity(self) -> bool {
self.irot_steps() == 0 && self.imir_axis().is_none()
}
pub(crate) fn imir_axis(self) -> Option<bool> {
match self {
Orientation::FlipH | Orientation::Transpose | Orientation::Transverse => Some(false),
Orientation::FlipV => Some(true),
_ => None,
}
}
pub(crate) fn irot_steps(self) -> u8 {
match self {
Orientation::Normal | Orientation::FlipH | Orientation::FlipV => 0,
Orientation::Rotate180 => 2,
Orientation::Rotate90 => 3,
Orientation::Rotate270 => 1,
Orientation::Transpose => 3,
Orientation::Transverse => 1,
}
}
pub(crate) fn from_irot_imir(steps: u8, axis: Option<bool>) -> Self {
match (steps & 3, axis) {
(0, None) => Orientation::Normal,
(2, None) => Orientation::Rotate180,
(3, None) => Orientation::Rotate90,
(1, None) => Orientation::Rotate270,
(0, Some(false)) => Orientation::FlipH,
(0, Some(true)) => Orientation::FlipV,
(3, Some(false)) => Orientation::Transpose,
(1, Some(false)) => Orientation::Transverse,
_ => Orientation::Normal,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ContentLightLevel {
pub max_content_light_level: u16,
pub max_pic_average_light_level: u16,
}
impl ContentLightLevel {
pub fn new(max_cll: u16, max_fall: u16) -> Self {
ContentLightLevel {
max_content_light_level: max_cll,
max_pic_average_light_level: max_fall,
}
}
pub(crate) fn clli_payload(&self) -> [u8; 4] {
let cll = self.max_content_light_level.to_be_bytes();
let fall = self.max_pic_average_light_level.to_be_bytes();
[cll[0], cll[1], fall[0], fall[1]]
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ImageMetadata {
pub orientation: Orientation,
pub content_light_level: Option<ContentLightLevel>,
pub exif: Option<Vec<u8>>,
}
impl ImageMetadata {
pub fn new() -> Self {
Self::default()
}
pub fn with_orientation(mut self, o: Orientation) -> Self {
self.orientation = o;
self
}
pub fn with_content_light_level(mut self, cll: ContentLightLevel) -> Self {
self.content_light_level = Some(cll);
self
}
pub fn with_exif(mut self, exif: Vec<u8>) -> Self {
self.exif = Some(exif);
self
}
pub fn is_empty(&self) -> bool {
self.orientation.is_identity() && self.content_light_level.is_none() && self.exif.is_none()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn exif_mapping() {
assert_eq!(Orientation::from_exif(1), Orientation::Normal);
assert_eq!(Orientation::from_exif(6), Orientation::Rotate90);
assert_eq!(Orientation::from_exif(99), Orientation::Normal);
}
#[test]
fn rotate90_has_irot_no_imir() {
assert_eq!(Orientation::Rotate90.irot_steps(), 3);
assert_eq!(Orientation::Rotate90.imir_axis(), None);
assert!(!Orientation::Rotate90.is_identity());
assert!(Orientation::Normal.is_identity());
}
#[test]
fn clli_payload_layout() {
let c = ContentLightLevel::new(1000, 400);
assert_eq!(c.clli_payload(), [0x03, 0xE8, 0x01, 0x90]);
}
}