use std::collections::HashMap;
use std::str;
use crate::decoders::basics::*;
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum Tag {
PanaWidth = 0x0002,
PanaLength = 0x0003,
NefWB0 = 0x000C,
PanaWBsR = 0x0011,
PanaWBsB = 0x0012,
NrwWB = 0x0014,
NefSerial = 0x001d,
PanaWBs2R = 0x0024,
PanaWBs2G = 0x0025,
PanaWBs2B = 0x0026,
Cr2PowerShotWB = 0x0029,
NewSubFileType = 0x00FE,
Cr2OldOffset = 0x0081,
NefMeta1 = 0x008c,
NefMeta2 = 0x0096,
NefWB1 = 0x0097,
Cr2OldWB = 0x00A4,
NefKey = 0x00a7,
ImageWidth = 0x0100,
ImageLength = 0x0101,
BitsPerSample = 0x0102,
Compression = 0x0103,
PhotometricInt = 0x0106,
Make = 0x010F,
Model = 0x0110,
StripOffsets = 0x0111,
Orientation = 0x0112,
SamplesPerPixel = 0x0115,
StripByteCounts = 0x0117,
PanaOffsets = 0x0118,
GrayResponse = 0x0123,
Software = 0x0131,
TileWidth = 0x0142,
TileLength = 0x0143,
TileOffsets = 0x0144,
SubIFDs = 0x014A,
PefBlackLevels = 0x0200,
PefWB = 0x0201,
PefHuffman = 0x0220,
Xmp = 0x02BC,
DcrWB = 0x03FD,
OrfBlackLevels = 0x0600,
DcrLinearization = 0x090D,
EpsonWB = 0x0E80,
KodakWB = 0x0F00,
OlympusRedMul = 0x1017,
OlympusBlueMul = 0x1018,
OlympusImgProc = 0x2040,
RafOldWB = 0x2ff0,
Cr2ColorData = 0x4001,
SonyCurve = 0x7010,
SonyOffset = 0x7200,
SonyLength = 0x7201,
SonyKey = 0x7221,
SonyGRBG = 0x7303,
SonyRGGB = 0x7313,
CFAPattern = 0x828E,
KodakIFD = 0x8290,
LeafMetadata = 0x8606,
ExifIFDPointer = 0x8769,
Makernote = 0x927C,
SrwSensorAreas = 0xA010,
SrwRGGBLevels = 0xA021,
SrwRGGBBlacks = 0xA028,
Cr2Id = 0xc5d8,
DNGVersion = 0xC612,
Linearization = 0xC618,
BlackLevels = 0xC61A,
WhiteLevel = 0xC61D,
ColorMatrix1 = 0xC621,
ColorMatrix2 = 0xC622,
AsShotNeutral = 0xC628,
DNGPrivateArea = 0xC634,
Cr2StripeWidths = 0xC640,
ActiveArea = 0xC68D,
MaskedAreas = 0xC68E,
RafRawSubIFD = 0xF000,
RafImageWidth = 0xF001,
RafImageLength = 0xF002,
RafBitsPerSample = 0xF003,
RafOffsets = 0xF007,
RafWBGRB = 0xF00E,
KdcWB = 0xFA2A,
KdcWidth = 0xFD00,
KdcLength = 0xFD01,
KdcOffset = 0xFD04,
KdcIFD = 0xFE00,
}
const DATASHIFTS: [u8;14] = [0,0,0,1,2,3,0,0,1,2, 3, 2, 3, 2];
fn t (tag: Tag) -> u16 {
tag as u16
}
#[derive(Debug, Copy, Clone)]
pub struct TiffEntry<'a> {
tag: u16,
typ: u16,
count: usize,
parent_offset: usize,
doffset: usize,
data: &'a [u8],
endian: Endian,
}
#[derive(Debug, Clone)]
pub struct TiffIFD<'a> {
entries: HashMap<u16,TiffEntry<'a>>,
subifds: Vec<TiffIFD<'a>>,
nextifd: usize,
start_offset: usize,
endian: Endian,
}
impl<'a> TiffIFD<'a> {
pub fn new_file(buf: &'a[u8]) -> Result<TiffIFD<'a>, String> {
if buf[0..8] == b"FUJIFILM"[..] {
let ifd1 = TiffIFD::new_root(buf, (BEu32(buf, 84)+12) as usize)?;
let endian = ifd1.get_endian();
let mut subifds = vec![ifd1];
let mut entries = HashMap::new();
let ioffset = BEu32(buf, 100) as usize;
match TiffIFD::new_root(buf, ioffset) {
Ok(val) => {subifds.push(val);}
Err(_) => {
entries.insert(Tag::RafOffsets as u16, TiffEntry{
tag: t(Tag::RafOffsets),
typ: 4, count: 1,
parent_offset: 0,
doffset: 100,
data: &buf[100..104],
endian: BIG_ENDIAN,
});
},
}
match TiffIFD::new_fuji(buf, BEu32(buf, 92) as usize) {
Ok(val) => subifds.push(val),
Err(_) => {}
}
Ok(TiffIFD {
entries: entries,
subifds: subifds,
nextifd: 0,
start_offset: 0,
endian: endian,
})
} else {
TiffIFD::new_root(buf, 0)
}
}
pub fn new_root(buf: &'a[u8], offset: usize) -> Result<TiffIFD<'a>, String> {
let mut subifds = Vec::new();
let endian = match LEu16(buf, offset) {
0x4949 => LITTLE_ENDIAN,
0x4d4d => BIG_ENDIAN,
x => {return Err(format!("TIFF: don't know marker 0x{:x}", x).to_string())},
};
let mut nextifd = endian.ru32(buf, offset+4) as usize;
for _ in 0..100 { let ifd = TiffIFD::new(&buf[offset..], nextifd, 0, offset, 0, endian)?;
nextifd = ifd.nextifd;
subifds.push(ifd);
if nextifd == 0 {
break
}
}
Ok(TiffIFD {
entries: HashMap::new(),
subifds: subifds,
nextifd: 0,
start_offset: offset,
endian: endian,
})
}
pub fn new(buf: &'a[u8], offset: usize, base_offset: usize, start_offset: usize, depth: u32, e: Endian) -> Result<TiffIFD<'a>, String> {
let mut entries = HashMap::new();
let mut subifds = Vec::new();
let num = e.ru16(buf, offset); if num > 4000 {
return Err(format!("too many entries in IFD ({})", num).to_string())
}
for i in 0..num {
let entry_offset: usize = offset + 2 + (i as usize)*12;
if Tag::n(e.ru16(buf, entry_offset)).is_none() {
continue;
}
let entry = TiffEntry::new(buf, entry_offset, base_offset, offset, e);
if entry.tag == t(Tag::SubIFDs)
|| entry.tag == t(Tag::ExifIFDPointer)
|| entry.tag == t(Tag::RafRawSubIFD)
|| entry.tag == t(Tag::KodakIFD)
|| entry.tag == t(Tag::KdcIFD) {
if depth < 10 { for i in 0..entry.count {
let ifd = TiffIFD::new(buf, entry.get_u32(i as usize) as usize, base_offset, start_offset, depth+1, e);
match ifd {
Ok(val) => {subifds.push(val);},
Err(_) => {entries.insert(entry.tag, entry);}, }
}
}
} else if entry.tag == t(Tag::Makernote) {
if depth < 10 { let ifd = TiffIFD::new_makernote(buf, entry.doffset(), base_offset, depth+1, e);
match ifd {
Ok(val) => {subifds.push(val);},
Err(_) => {entries.insert(entry.tag, entry);}, }
}
} else {
entries.insert(entry.tag, entry);
}
}
Ok(TiffIFD {
entries: entries,
subifds: subifds,
nextifd: e.ru32(buf, offset + (2+num*12) as usize) as usize,
start_offset: start_offset,
endian: e,
})
}
pub fn new_makernote(buf: &'a[u8], offset: usize, base_offset: usize, depth: u32, e: Endian) -> Result<TiffIFD<'a>, String> {
let mut off = 0;
let data = &buf[offset..];
let mut endian = e;
if data[0..5] == b"OLYMP"[..] {
off += 8;
if data[0..7] == b"OLYMPUS"[..] {
off += 4;
}
let mut mainifd = TiffIFD::new(buf, offset+off, base_offset, 0, depth, endian)?;
if off == 12 {
let ioff = if let Some(entry) = mainifd.find_entry(Tag::OlympusImgProc) {
entry.get_usize(0)
} else { 0 };
if ioff != 0 {
let iprocifd = TiffIFD::new(&buf[offset+ioff..], 0, ioff, 0, depth, endian)?;
mainifd.subifds.push(iprocifd);
}
}
return Ok(mainifd)
}
if data[0..5] == b"EPSON"[..] {
off += 8;
}
if data[0..4] == b"AOC\0"[..] {
off +=4;
}
if data[0..6] == b"PENTAX"[..] {
off += 8;
let endian = if data[off..off+2] == b"II"[..] {LITTLE_ENDIAN} else {BIG_ENDIAN};
return TiffIFD::new(&buf[offset..], 10, base_offset, 0, depth, endian)
}
if data[0..7] == b"Nikon\0\x02"[..] {
off += 10;
let endian = if data[off..off+2] == b"II"[..] {LITTLE_ENDIAN} else {BIG_ENDIAN};
return TiffIFD::new(&buf[off+offset..], 8, base_offset, 0, depth, endian)
}
if data[off..off+2] == b"II"[..] {
off +=2;
endian = LITTLE_ENDIAN;
} if data[off..off+2] == b"MM"[..] {
off +=2;
endian = BIG_ENDIAN;
}
TiffIFD::new(buf, offset+off, base_offset, 0, depth, endian)
}
pub fn new_fuji(buf: &'a[u8], offset: usize) -> Result<TiffIFD<'a>, String> {
let mut entries = HashMap::new();
let num = BEu32(buf, offset); if num > 4000 {
return Err(format!("too many entries in IFD ({})", num).to_string())
}
let mut off = offset+4;
for _ in 0..num {
let tag = BEu16(buf, off);
let len = BEu16(buf, off+2);
if tag == t(Tag::ImageWidth) {
entries.insert(t(Tag::ImageWidth), TiffEntry {
tag: t(Tag::ImageWidth),
typ: 3, count: 2,
parent_offset: 0,
doffset: off+4,
data: &buf[off+4..off+8],
endian: BIG_ENDIAN,
});
} else if tag == t(Tag::RafOldWB) {
entries.insert(t(Tag::RafOldWB), TiffEntry {
tag: t(Tag::RafOldWB),
typ: 3, count: 4,
parent_offset: 0,
doffset: off+4,
data: &buf[off+4..off+12],
endian: BIG_ENDIAN,
});
}
off += (len+4) as usize;
}
Ok(TiffIFD {
entries: entries,
subifds: Vec::new(),
nextifd: 0,
start_offset: 0,
endian: BIG_ENDIAN,
})
}
pub fn find_entry(&self, tag: Tag) -> Option<&TiffEntry> {
if self.entries.contains_key(&t(tag)) {
self.entries.get(&t(tag))
} else {
for ifd in &self.subifds {
match ifd.find_entry(tag) {
Some(x) => return Some(x),
None => {},
}
}
None
}
}
pub fn has_entry(&self, tag: Tag) -> bool {
self.find_entry(tag).is_some()
}
pub fn find_ifds_with_tag(&self, tag: Tag) -> Vec<&TiffIFD> {
let mut ifds = Vec::new();
if self.entries.contains_key(&t(tag)) {
ifds.push(self);
}
for ifd in &self.subifds {
if ifd.entries.contains_key(&t(tag)) {
ifds.push(ifd);
}
ifds.extend(ifd.find_ifds_with_tag(tag));
}
ifds
}
pub fn find_first_ifd(&self, tag: Tag) -> Option<&TiffIFD> {
let ifds = self.find_ifds_with_tag(tag);
if ifds.len() == 0 {
None
} else {
Some(ifds[0])
}
}
pub fn get_endian(&self) -> Endian { self.endian }
pub fn little_endian(&self) -> bool { self.endian.little() }
pub fn start_offset(&self) -> usize { self.start_offset }
}
impl<'a> TiffEntry<'a> {
pub fn new(buf: &'a[u8], offset: usize, base_offset: usize, parent_offset: usize, e: Endian) -> TiffEntry<'a> {
let tag = e.ru16(buf, offset);
let mut typ = e.ru16(buf, offset+2);
let count = e.ru32(buf, offset+4) as usize;
if typ == 0 || typ > 13 {
typ = 1;
}
let bytesize: usize = count << DATASHIFTS[typ as usize];
let doffset: usize = if bytesize <= 4 {
offset + 8
} else {
(e.ru32(buf, offset+8) as usize) - base_offset
};
TiffEntry {
tag: tag,
typ: typ,
count: count,
parent_offset: parent_offset,
doffset: doffset,
data: &buf[doffset .. doffset+bytesize],
endian: e,
}
}
pub fn copy_with_new_data(&self, data: &'a[u8]) -> TiffEntry<'a> {
let mut copy = self.clone();
copy.data = data;
copy
}
pub fn copy_offset_from_parent(&self, buffer: &'a[u8]) -> TiffEntry<'a> {
self.copy_with_new_data(&buffer[self.parent_offset+self.doffset..])
}
pub fn doffset(&self) -> usize { self.doffset }
pub fn parent_offset(&self) -> usize { self.parent_offset }
pub fn count(&self) -> usize { self.count }
pub fn get_u16(&self, idx: usize) -> u16 {
match self.typ {
1 => self.data[idx] as u16,
3 | 8 => self.get_force_u16(idx),
_ => panic!("Trying to read typ {} for a u32", self.typ),
}
}
pub fn get_u32(&self, idx: usize) -> u32 {
match self.typ {
1 | 3 | 8 => self.get_u16(idx) as u32,
4 | 7 | 9 | 13 => self.get_force_u32(idx),
_ => panic!("Trying to read typ {} for a u32", self.typ),
}
}
pub fn get_usize(&self, idx: usize) -> usize { self.get_u32(idx) as usize }
pub fn get_force_u32(&self, idx: usize) -> u32 {
self.endian.ru32(self.data, idx*4)
}
pub fn get_force_u16(&self, idx: usize) -> u16 {
self.endian.ru16(self.data, idx*2)
}
pub fn get_f32(&self, idx: usize) -> f32 {
if self.typ == 5 { let a = self.endian.ru32(self.data, idx*8) as f32;
let b = self.endian.ru32(self.data, idx*8+4) as f32;
a / b
} else if self.typ == 10 { let a = self.endian.ri32(self.data, idx*8) as f32;
let b = self.endian.ri32(self.data, idx*8+4) as f32;
a / b
} else {
self.get_u32(idx) as f32
}
}
pub fn get_str(&self) -> &str {
let len = match self.data.iter().position(|&x| x == 0) {
Some(p) => p,
None => self.data.len(),
};
match str::from_utf8(&self.data[0..len]) {
Ok(val) => val.trim(),
Err(err) => std::panic::panic_any(err),
}
}
pub fn get_data(&self) -> &[u8] {
self.data
}
}