#[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 {
matches!(self, Orientation::Normal)
}
pub fn imir_axis(self) -> Option<bool> {
match self {
Orientation::FlipH | Orientation::Transpose | Orientation::Transverse => Some(false),
Orientation::FlipV => Some(true),
_ => None,
}
}
pub 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,
}
}
}
#[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 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)]
pub struct Metadata {
pub orientation: Orientation,
pub content_light_level: Option<ContentLightLevel>,
pub exif: Option<Vec<u8>>,
}
impl Metadata {
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 orientation_decomposition() {
assert!(Orientation::Normal.is_identity());
assert_eq!(Orientation::Rotate90.irot_steps(), 3); assert_eq!(Orientation::Rotate270.irot_steps(), 1); assert_eq!(Orientation::Rotate180.irot_steps(), 2);
assert_eq!(Orientation::FlipH.imir_axis(), Some(false));
assert_eq!(Orientation::FlipV.imir_axis(), Some(true));
assert_eq!(Orientation::Rotate90.imir_axis(), None);
}
#[test]
fn from_exif_mapping() {
assert_eq!(Orientation::from_exif(1), Orientation::Normal);
assert_eq!(Orientation::from_exif(6), Orientation::Rotate90);
assert_eq!(Orientation::from_exif(8), Orientation::Rotate270);
assert_eq!(Orientation::from_exif(0), Orientation::Normal); assert_eq!(Orientation::from_exif(99), Orientation::Normal);
}
#[test]
fn clli_payload_layout() {
let cll = ContentLightLevel::new(1000, 400);
assert_eq!(cll.clli_payload(), [0x03, 0xE8, 0x01, 0x90]);
}
}