use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{Arc, OnceLock};
use super::super::error::{IfdId, TiffParseError};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Endian {
Little,
Big,
}
pub(crate) mod tags {
pub const IMAGE_WIDTH: u16 = 256;
pub const IMAGE_LENGTH: u16 = 257;
pub const BITS_PER_SAMPLE: u16 = 258;
pub const COMPRESSION: u16 = 259;
pub const PHOTOMETRIC: u16 = 262;
pub const IMAGE_DESCRIPTION: u16 = 270;
pub const STRIP_OFFSETS: u16 = 273;
pub const SAMPLES_PER_PIXEL: u16 = 277;
pub const ROWS_PER_STRIP: u16 = 278;
pub const STRIP_BYTE_COUNTS: u16 = 279;
#[cfg(test)]
pub const SUB_IFDS: u16 = 330;
pub const TILE_WIDTH: u16 = 322;
pub const TILE_LENGTH: u16 = 323;
pub const TILE_OFFSETS: u16 = 324;
pub const TILE_BYTE_COUNTS: u16 = 325;
pub const X_RESOLUTION: u16 = 282;
pub const Y_RESOLUTION: u16 = 283;
pub const RESOLUTION_UNIT: u16 = 296;
pub const PREDICTOR: u16 = 317;
pub const JPEG_TABLES: u16 = 347;
pub const XMP: u16 = 700;
pub const ICC_PROFILE: u16 = 34675;
pub const NDPI_MARKER: u16 = 65420;
}
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub(crate) enum TiffType {
Byte, Ascii, Short, Long, Rational, SByte, Undefined, SShort, SLong, SRational, Float, Double, Ifd, Long8, SLong8, Ifd8, }
impl TiffType {
pub fn from_u16(id: u16) -> Option<TiffType> {
match id {
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 byte_size(&self) -> u64 {
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,
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct InlineValue {
len: u8,
bytes: [u8; 12],
}
impl InlineValue {
pub fn new(data: &[u8]) -> Self {
assert!(data.len() <= 12, "InlineValue data exceeds 12 bytes");
let mut bytes = [0u8; 12];
bytes[..data.len()].copy_from_slice(data);
InlineValue {
len: data.len() as u8,
bytes,
}
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..self.len as usize]
}
}
pub(crate) enum TagValue {
Inline(InlineValue),
Lazy {
offset: u64,
byte_len: u64,
resolved: OnceLock<Result<Vec<u8>, TiffParseError>>,
},
}
impl std::fmt::Debug for TagValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TagValue::Inline(v) => f.debug_tuple("Inline").field(v).finish(),
TagValue::Lazy {
offset, byte_len, ..
} => f
.debug_struct("Lazy")
.field("offset", offset)
.field("byte_len", byte_len)
.finish(),
}
}
}
pub(crate) struct TagEntry {
pub tiff_type: TiffType,
pub count: u64,
pub value: TagValue,
pub(super) decoded_u64: OnceLock<Vec<u64>>,
}
impl std::fmt::Debug for TagEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TagEntry")
.field("tiff_type", &self.tiff_type)
.field("count", &self.count)
.field("value", &self.value)
.finish()
}
}
impl TagEntry {
pub fn new_inline(tiff_type: TiffType, count: u64, data: &[u8]) -> Self {
TagEntry {
tiff_type,
count,
value: TagValue::Inline(InlineValue::new(data)),
decoded_u64: OnceLock::new(),
}
}
pub fn new_lazy(tiff_type: TiffType, count: u64, offset: u64, byte_len: u64) -> Self {
TagEntry {
tiff_type,
count,
value: TagValue::Lazy {
offset,
byte_len,
resolved: OnceLock::new(),
},
decoded_u64: OnceLock::new(),
}
}
}
pub(crate) struct Ifd {
pub id: IfdId,
pub offset: u64,
pub tags: HashMap<u16, TagEntry>,
pub sub_ifds: Vec<IfdId>,
}
impl std::fmt::Debug for Ifd {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Ifd")
.field("id", &self.id)
.field("offset", &self.offset)
.field("tag_count", &self.tags.len())
.field("sub_ifds", &self.sub_ifds)
.finish()
}
}
pub(crate) struct TiffContainer {
pub(super) path: Arc<PathBuf>,
pub(super) file_len: u64,
pub(super) file: std::fs::File,
#[cfg(windows)]
pub(super) pread_lock: std::sync::Mutex<()>,
pub(super) endian: Endian,
pub(super) bigtiff: bool,
pub(super) ndpi: bool,
pub(super) top_ifds: Vec<IfdId>,
pub(super) ifds: HashMap<IfdId, Ifd>,
}
impl std::fmt::Debug for TiffContainer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TiffContainer")
.field("path", &self.path)
.field("endian", &self.endian)
.field("bigtiff", &self.bigtiff)
.field("ndpi", &self.ndpi)
.field("ifd_count", &self.ifds.len())
.finish()
}
}