use binrw::{BinRead, BinWrite};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, BinRead, BinWrite)]
pub enum ByteOrder {
#[brw(magic = b"II")]
LittleEndian,
#[brw(magic = b"MM")]
BigEndian,
}
impl ByteOrder {
pub fn from_bytes(bytes: [u8; 2]) -> Option<Self> {
match &bytes {
b"II" => Some(ByteOrder::LittleEndian),
b"MM" => Some(ByteOrder::BigEndian),
_ => None,
}
}
pub fn to_bytes(self) -> [u8; 2] {
match self {
ByteOrder::LittleEndian => *b"II",
ByteOrder::BigEndian => *b"MM",
}
}
pub fn as_str(&self) -> &'static str {
match self {
ByteOrder::LittleEndian => "LE",
ByteOrder::BigEndian => "BE",
}
}
}
impl fmt::Display for ByteOrder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ByteOrder::LittleEndian => write!(f, "Little-Endian (II)"),
ByteOrder::BigEndian => write!(f, "Big-Endian (MM)"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, BinRead, BinWrite)]
#[brw(repr = u16)]
#[repr(u16)]
pub enum TiffType {
Byte = 1,
Ascii = 2,
Short = 3,
Long = 4,
Rational = 5,
SByte = 6,
Undefined = 7,
SShort = 8,
SLong = 9,
SRational = 10,
Float = 11,
Double = 12,
Ifd = 13,
Long8 = 16,
SLong8 = 17,
Ifd8 = 18,
}
impl TiffType {
pub fn from_u16(value: u16) -> Option<Self> {
match value {
1 => Some(TiffType::Byte),
2 => Some(TiffType::Ascii),
3 => Some(TiffType::Short),
4 => Some(TiffType::Long),
5 => Some(TiffType::Rational),
6 => Some(TiffType::SByte),
7 => Some(TiffType::Undefined),
8 => Some(TiffType::SShort),
9 => Some(TiffType::SLong),
10 => Some(TiffType::SRational),
11 => Some(TiffType::Float),
12 => Some(TiffType::Double),
13 => Some(TiffType::Ifd),
16 => Some(TiffType::Long8),
17 => Some(TiffType::SLong8),
18 => Some(TiffType::Ifd8),
_ => None,
}
}
pub fn size(&self) -> usize {
match self {
TiffType::Byte | TiffType::Ascii | TiffType::SByte | TiffType::Undefined => 1,
TiffType::Short | TiffType::SShort => 2,
TiffType::Long | TiffType::SLong | TiffType::Float | TiffType::Ifd => 4,
TiffType::Rational
| TiffType::SRational
| TiffType::Double
| TiffType::Long8
| TiffType::SLong8
| TiffType::Ifd8 => 8,
}
}
pub fn fits_inline(&self, count: u32) -> bool {
self.size() * (count as usize) <= 4
}
pub fn fits_inline_bigtiff(&self, count: u64) -> bool {
self.size() * (count as usize) <= 8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, BinRead, BinWrite)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Rational {
pub numerator: u32,
pub denominator: u32,
}
impl Rational {
pub fn new(numerator: u32, denominator: u32) -> Self {
Self {
numerator,
denominator,
}
}
pub fn to_f64(&self) -> f64 {
if self.denominator == 0 {
if self.numerator == 0 {
f64::NAN
} else {
f64::INFINITY
}
} else {
self.numerator as f64 / self.denominator as f64
}
}
}
impl From<(u32, u32)> for Rational {
fn from((numerator, denominator): (u32, u32)) -> Self {
Self {
numerator,
denominator,
}
}
}
impl fmt::Display for Rational {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}/{}", self.numerator, self.denominator)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, BinRead, BinWrite)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SRational {
pub numerator: i32,
pub denominator: i32,
}
impl SRational {
pub fn new(numerator: i32, denominator: i32) -> Self {
Self {
numerator,
denominator,
}
}
pub fn to_f64(&self) -> f64 {
if self.denominator == 0 {
if self.numerator == 0 {
f64::NAN
} else if self.numerator > 0 {
f64::INFINITY
} else {
f64::NEG_INFINITY
}
} else {
self.numerator as f64 / self.denominator as f64
}
}
}
impl From<(i32, i32)> for SRational {
fn from((numerator, denominator): (i32, i32)) -> Self {
Self {
numerator,
denominator,
}
}
}
impl fmt::Display for SRational {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}/{}", self.numerator, self.denominator)
}
}
impl From<Rational> for rawshift_core::metadata::URational {
fn from(r: Rational) -> Self {
Self::new(r.numerator, r.denominator)
}
}
impl From<SRational> for rawshift_core::metadata::SRational {
fn from(r: SRational) -> Self {
Self::new(r.numerator, r.denominator)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TiffValue {
Bytes(Vec<u8>),
Ascii(String),
Shorts(Vec<u16>),
Longs(Vec<u32>),
Rationals(Vec<Rational>),
SBytes(Vec<i8>),
Undefined(Vec<u8>),
SShorts(Vec<i16>),
SLongs(Vec<i32>),
SRationals(Vec<SRational>),
Floats(Vec<f32>),
Doubles(Vec<f64>),
Long8s(Vec<u64>),
SLong8s(Vec<i64>),
}
impl TiffValue {
pub fn as_u32(&self) -> Option<u32> {
match self {
TiffValue::Bytes(v) if v.len() == 1 => Some(v[0] as u32),
TiffValue::Shorts(v) if v.len() == 1 => Some(v[0] as u32),
TiffValue::Longs(v) if v.len() == 1 => Some(v[0]),
_ => None,
}
}
pub fn first_u32(&self) -> Option<u32> {
match self {
TiffValue::Bytes(v) if !v.is_empty() => Some(v[0] as u32),
TiffValue::Shorts(v) if !v.is_empty() => Some(v[0] as u32),
TiffValue::Longs(v) if !v.is_empty() => Some(v[0]),
_ => None,
}
}
pub fn as_u32_vec(&self) -> Option<Vec<u32>> {
match self {
TiffValue::Bytes(v) => Some(v.iter().map(|&x| x as u32).collect()),
TiffValue::Shorts(v) => Some(v.iter().map(|&x| x as u32).collect()),
TiffValue::Longs(v) => Some(v.clone()),
_ => None,
}
}
pub fn as_u64(&self) -> Option<u64> {
match self {
TiffValue::Bytes(v) if v.len() == 1 => Some(v[0] as u64),
TiffValue::Shorts(v) if v.len() == 1 => Some(v[0] as u64),
TiffValue::Longs(v) if v.len() == 1 => Some(v[0] as u64),
TiffValue::Long8s(v) if v.len() == 1 => Some(v[0]),
_ => None,
}
}
pub fn as_u64_vec(&self) -> Option<Vec<u64>> {
match self {
TiffValue::Bytes(v) => Some(v.iter().map(|&x| x as u64).collect()),
TiffValue::Shorts(v) => Some(v.iter().map(|&x| x as u64).collect()),
TiffValue::Longs(v) => Some(v.iter().map(|&x| x as u64).collect()),
TiffValue::Long8s(v) => Some(v.clone()),
_ => None,
}
}
pub fn as_f64_vec(&self) -> Option<Vec<f64>> {
match self {
TiffValue::Bytes(v) => Some(v.iter().map(|&x| x as f64).collect()),
TiffValue::Shorts(v) => Some(v.iter().map(|&x| x as f64).collect()),
TiffValue::Longs(v) => Some(v.iter().map(|&x| x as f64).collect()),
TiffValue::Rationals(v) => Some(v.iter().map(|r| r.to_f64()).collect()),
TiffValue::SRationals(v) => Some(v.iter().map(|r| r.to_f64()).collect()),
TiffValue::Floats(v) => Some(v.iter().map(|&x| x as f64).collect()),
TiffValue::Doubles(v) => Some(v.clone()),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
TiffValue::Ascii(s) => Some(s.as_str()),
_ => None,
}
}
pub fn len(&self) -> usize {
match self {
TiffValue::Bytes(v) | TiffValue::Undefined(v) => v.len(),
TiffValue::Ascii(s) => s.len() + 1, TiffValue::Shorts(v) => v.len(),
TiffValue::Longs(v) => v.len(),
TiffValue::Rationals(v) => v.len(),
TiffValue::SBytes(v) => v.len(),
TiffValue::SShorts(v) => v.len(),
TiffValue::SLongs(v) => v.len(),
TiffValue::SRationals(v) => v.len(),
TiffValue::Floats(v) => v.len(),
TiffValue::Doubles(v) => v.len(),
TiffValue::Long8s(v) => v.len(),
TiffValue::SLong8s(v) => v.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_byte_order_from_bytes() {
assert_eq!(ByteOrder::from_bytes(*b"II"), Some(ByteOrder::LittleEndian));
assert_eq!(ByteOrder::from_bytes(*b"MM"), Some(ByteOrder::BigEndian));
assert_eq!(ByteOrder::from_bytes(*b"XX"), None);
}
#[test]
fn test_byte_order_to_bytes() {
assert_eq!(ByteOrder::LittleEndian.to_bytes(), *b"II");
assert_eq!(ByteOrder::BigEndian.to_bytes(), *b"MM");
}
#[test]
fn test_byte_order_as_str() {
assert_eq!(ByteOrder::LittleEndian.as_str(), "LE");
assert_eq!(ByteOrder::BigEndian.as_str(), "BE");
}
#[test]
fn test_tiff_type_size() {
assert_eq!(TiffType::Byte.size(), 1);
assert_eq!(TiffType::Ascii.size(), 1);
assert_eq!(TiffType::Short.size(), 2);
assert_eq!(TiffType::Long.size(), 4);
assert_eq!(TiffType::Rational.size(), 8);
assert_eq!(TiffType::Double.size(), 8);
assert_eq!(TiffType::Long8.size(), 8);
}
#[test]
fn test_tiff_type_fits_inline() {
assert!(TiffType::Byte.fits_inline(4));
assert!(TiffType::Byte.fits_inline(1));
assert!(!TiffType::Byte.fits_inline(5));
assert!(TiffType::Short.fits_inline(2));
assert!(!TiffType::Short.fits_inline(3));
assert!(TiffType::Long.fits_inline(1));
assert!(!TiffType::Long.fits_inline(2));
assert!(!TiffType::Rational.fits_inline(1));
}
#[test]
fn test_rational_to_f64() {
let r = Rational::new(1, 2);
assert_eq!(r.to_f64(), 0.5);
let r = Rational::new(0, 0);
assert!(r.to_f64().is_nan());
let r = Rational::new(1, 0);
assert!(r.to_f64().is_infinite());
}
#[test]
fn test_srational_to_f64() {
let r = SRational::new(-1, 2);
assert_eq!(r.to_f64(), -0.5);
let r = SRational::new(-1, 0);
assert_eq!(r.to_f64(), f64::NEG_INFINITY);
}
#[test]
fn test_tiff_value_as_u32() {
let v = TiffValue::Shorts(vec![42]);
assert_eq!(v.as_u32(), Some(42));
let v = TiffValue::Longs(vec![1000]);
assert_eq!(v.as_u32(), Some(1000));
let v = TiffValue::Longs(vec![1, 2]);
assert_eq!(v.as_u32(), None); }
#[test]
fn test_tiff_value_as_u64() {
let v = TiffValue::Shorts(vec![255]);
assert_eq!(v.as_u64(), Some(255u64));
let v = TiffValue::Longs(vec![100_000]);
assert_eq!(v.as_u64(), Some(100_000u64));
let v = TiffValue::Long8s(vec![u64::MAX]);
assert_eq!(v.as_u64(), Some(u64::MAX));
let v = TiffValue::Shorts(vec![1, 2]);
assert_eq!(v.as_u64(), None);
let v = TiffValue::Ascii("hello".to_string());
assert_eq!(v.as_u64(), None);
}
#[test]
fn test_tiff_value_as_string() {
let v = TiffValue::Ascii("Canon".to_string());
assert_eq!(v.as_str(), Some("Canon"));
let v = TiffValue::Shorts(vec![1]);
assert_eq!(v.as_str(), None);
let v = TiffValue::Bytes(vec![65, 66]);
assert_eq!(v.as_str(), None);
}
}