mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 13:30:48 +00:00
Raw-rs: make decoder for ARW1 and ARW2 formats (#1775)
* convert Tag into a trait * Create ARW 1 decoder * add decoder for sony tone curve table * create decoder for arw 2.1 format * add windsock.arw to the tests * create derive macro for Tag and use it in decoders * add license to tag-derive * add code to identify model * impl Display for Ifd * Code review * Fix type variable name * Fix compilation --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
c9a33e44bd
commit
fd3613018a
16 changed files with 720 additions and 160 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -618,6 +618,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitstream-io"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c12d1856e42f0d817a835fe55853957c85c8c8a470114029143d3f12671446e"
|
||||
|
||||
[[package]]
|
||||
name = "block"
|
||||
version = "0.1.6"
|
||||
|
@ -4770,9 +4776,11 @@ checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab"
|
|||
name = "raw-rs"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bitstream-io",
|
||||
"downloader",
|
||||
"libraw-rs",
|
||||
"num_enum 0.7.2",
|
||||
"tag-derive",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
|
@ -5895,6 +5903,14 @@ dependencies = [
|
|||
"version-compare 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tag-derive"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "take_mut"
|
||||
version = "0.2.2"
|
||||
|
|
|
@ -19,6 +19,7 @@ members = [
|
|||
"libraries/dyn-any",
|
||||
"libraries/bezier-rs",
|
||||
"libraries/raw-rs",
|
||||
"libraries/raw-rs/tag-derive",
|
||||
"website/other/bezier-rs-demos/wasm",
|
||||
]
|
||||
resolver = "2"
|
||||
|
@ -49,7 +50,7 @@ tempfile = "3"
|
|||
thiserror = "1.0"
|
||||
anyhow = "1.0.66"
|
||||
proc-macro2 = "1"
|
||||
syn = { version = "2.0", default-features = false, features = ["full"] }
|
||||
syn = { version = "2.0", default-features = false, features = ["full", "derive"] }
|
||||
quote = "1.0"
|
||||
axum = "0.6"
|
||||
chrono = "^0.4.23"
|
||||
|
|
|
@ -16,8 +16,10 @@ documentation = "https://docs.rs/raw-rs"
|
|||
raw-rs-tests = []
|
||||
|
||||
[dependencies]
|
||||
bitstream-io = "2.3.0"
|
||||
num_enum = "0.7.2"
|
||||
thiserror = { workspace = true }
|
||||
tag-derive = { path = "tag-derive" }
|
||||
|
||||
[dev-dependencies]
|
||||
libraw-rs = "0.0.4"
|
||||
|
|
100
libraries/raw-rs/src/decoder/arw1.rs
Normal file
100
libraries/raw-rs/src/decoder/arw1.rs
Normal file
|
@ -0,0 +1,100 @@
|
|||
use crate::tiff::file::TiffRead;
|
||||
use crate::tiff::tags::SonyDataOffset;
|
||||
use crate::tiff::Ifd;
|
||||
use crate::RawImage;
|
||||
|
||||
use bitstream_io::{BitRead, BitReader, Endianness, BE};
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
pub fn decode_a100<R: Read + Seek>(ifd: Ifd, file: &mut TiffRead<R>) -> RawImage {
|
||||
let data_offset = ifd.get_value::<SonyDataOffset, _>(file).unwrap();
|
||||
|
||||
let image_width = 3881;
|
||||
let image_height = 2608;
|
||||
|
||||
file.seek_from_start(data_offset).unwrap();
|
||||
let mut image = sony_arw_load_raw(image_width, image_height, &mut BitReader::<_, BE>::new(file)).unwrap();
|
||||
|
||||
let len = image.len();
|
||||
image[len - image_width..].fill(0);
|
||||
|
||||
RawImage {
|
||||
data: image,
|
||||
width: image_width,
|
||||
height: image_height,
|
||||
}
|
||||
}
|
||||
|
||||
fn read_and_huffman_decode_file<R: Read + Seek, E: Endianness>(huff: &[u16], file: &mut BitReader<R, E>) -> u32 {
|
||||
let number_of_bits = huff[0].into();
|
||||
let huffman_table = &huff[1..];
|
||||
|
||||
// `number_of_bits` will be no more than 32, so the result is put into a u32
|
||||
let bits: u32 = file.read(number_of_bits).unwrap();
|
||||
let bits = bits as usize;
|
||||
|
||||
let bits_to_seek_from = huffman_table[bits].to_le_bytes()[1] as i64 - number_of_bits as i64;
|
||||
file.seek_bits(std::io::SeekFrom::Current(bits_to_seek_from)).unwrap();
|
||||
|
||||
huffman_table[bits].to_le_bytes()[0].into()
|
||||
}
|
||||
|
||||
fn read_n_bits_from_file<R: Read + Seek, E: Endianness>(number_of_bits: u32, file: &mut BitReader<R, E>) -> u32 {
|
||||
// `number_of_bits` will be no more than 32, so the result is put into a u32
|
||||
file.read(number_of_bits).unwrap()
|
||||
}
|
||||
|
||||
/// ljpeg is a lossless variant of JPEG which gets used for decoding the embedded (thumbnail) preview images in raw files
|
||||
fn ljpeg_diff<R: Read + Seek, E: Endianness>(huff: &[u16], file: &mut BitReader<R, E>, dng_version: Option<u32>) -> i32 {
|
||||
let length = read_and_huffman_decode_file(huff, file);
|
||||
|
||||
if length == 16 && dng_version.map(|x| x >= 0x1010000).unwrap_or(true) {
|
||||
return -32768;
|
||||
}
|
||||
|
||||
let diff = read_n_bits_from_file(length, file) as i32;
|
||||
|
||||
if length == 0 || (diff & (1 << (length - 1))) == 0 {
|
||||
diff - (1 << length) - 1
|
||||
} else {
|
||||
diff
|
||||
}
|
||||
}
|
||||
|
||||
fn sony_arw_load_raw<R: Read + Seek>(width: usize, height: usize, file: &mut BitReader<R, BE>) -> Option<Vec<u16>> {
|
||||
const TABLE: [u16; 18] = [
|
||||
0x0f11, 0x0f10, 0x0e0f, 0x0d0e, 0x0c0d, 0x0b0c, 0x0a0b, 0x090a, 0x0809, 0x0708, 0x0607, 0x0506, 0x0405, 0x0304, 0x0303, 0x0300, 0x0202, 0x0201,
|
||||
];
|
||||
|
||||
let mut huffman_table = [0_u16; 32770];
|
||||
// The first element is the number of bits to read
|
||||
huffman_table[0] = 15;
|
||||
|
||||
let mut n = 0;
|
||||
for x in TABLE {
|
||||
let first_byte = x >> 8;
|
||||
let repeats = 0x8000 >> first_byte;
|
||||
for _ in 0_u16..repeats {
|
||||
n += 1;
|
||||
huffman_table[n] = x;
|
||||
}
|
||||
}
|
||||
|
||||
let mut sum = 0;
|
||||
let mut image = vec![0_u16; width * height];
|
||||
for column in (0..width).rev() {
|
||||
for row in (0..height).step_by(2).chain((1..height).step_by(2)) {
|
||||
sum += ljpeg_diff(&huffman_table, file, None);
|
||||
|
||||
if (sum >> 12) != 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
if row < height {
|
||||
image[row * width + column] = sum as u16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(image)
|
||||
}
|
116
libraries/raw-rs/src/decoder/arw2.rs
Normal file
116
libraries/raw-rs/src/decoder/arw2.rs
Normal file
|
@ -0,0 +1,116 @@
|
|||
use crate::tiff::file::{Endian, TiffRead};
|
||||
use crate::tiff::tags::{BitsPerSample, CfaPattern, CfaPatternDim, Compression, ImageLength, ImageWidth, SonyToneCurve, StripByteCounts, StripOffsets, Tag};
|
||||
use crate::tiff::values::CurveLookupTable;
|
||||
use crate::tiff::{Ifd, TiffError};
|
||||
use crate::RawImage;
|
||||
|
||||
use std::io::{Read, Seek};
|
||||
use tag_derive::Tag;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Tag)]
|
||||
struct Arw2Ifd {
|
||||
image_width: ImageWidth,
|
||||
image_height: ImageLength,
|
||||
bits_per_sample: BitsPerSample,
|
||||
compression: Compression,
|
||||
cfa_pattern: CfaPattern,
|
||||
cfa_pattern_dim: CfaPatternDim,
|
||||
strip_offsets: StripOffsets,
|
||||
strip_byte_counts: StripByteCounts,
|
||||
sony_tone_curve: SonyToneCurve,
|
||||
}
|
||||
|
||||
pub fn decode<R: Read + Seek>(ifd: Ifd, file: &mut TiffRead<R>) -> RawImage {
|
||||
let ifd = ifd.get_value::<Arw2Ifd, _>(file).unwrap();
|
||||
|
||||
assert!(ifd.strip_offsets.len() == ifd.strip_byte_counts.len());
|
||||
assert!(ifd.strip_offsets.len() == 1);
|
||||
assert!(ifd.compression == 32767);
|
||||
|
||||
let image_width: usize = ifd.image_width.try_into().unwrap();
|
||||
let image_height: usize = ifd.image_height.try_into().unwrap();
|
||||
let bits_per_sample: usize = ifd.bits_per_sample.into();
|
||||
let [cfa_pattern_width, cfa_pattern_height] = ifd.cfa_pattern_dim;
|
||||
assert!(cfa_pattern_width == 2 && cfa_pattern_height == 2);
|
||||
|
||||
file.seek_from_start(ifd.strip_offsets[0]).unwrap();
|
||||
let mut image = sony_arw2_load_raw(image_width, image_height, ifd.sony_tone_curve, file).unwrap();
|
||||
|
||||
// Converting the bps from 12 to 14 so that ARW 2.3.1 and 2.3.5 have the same 14 bps.
|
||||
image.iter_mut().for_each(|x| *x <<= 2);
|
||||
|
||||
RawImage {
|
||||
data: image,
|
||||
width: image_width,
|
||||
height: image_height,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_u32(buffer: &[u8], endian: Endian) -> Option<u32> {
|
||||
Some(match endian {
|
||||
Endian::Little => u32::from_le_bytes(buffer.try_into().ok()?),
|
||||
Endian::Big => u32::from_be_bytes(buffer.try_into().ok()?),
|
||||
})
|
||||
}
|
||||
|
||||
fn as_u16(buffer: &[u8], endian: Endian) -> Option<u16> {
|
||||
Some(match endian {
|
||||
Endian::Little => u16::from_le_bytes(buffer.try_into().ok()?),
|
||||
Endian::Big => u16::from_be_bytes(buffer.try_into().ok()?),
|
||||
})
|
||||
}
|
||||
|
||||
fn sony_arw2_load_raw<R: Read + Seek>(width: usize, height: usize, curve: CurveLookupTable, file: &mut TiffRead<R>) -> Option<Vec<u16>> {
|
||||
let mut image = vec![0_u16; height * width];
|
||||
let mut data = vec![0_u8; width + 1];
|
||||
|
||||
for row in 0..height {
|
||||
file.read_exact(&mut data[0..width]).unwrap();
|
||||
|
||||
let mut column = 0;
|
||||
let mut data_index = 0;
|
||||
|
||||
while column < width - 30 {
|
||||
let data_value = as_u32(&data[data_index..][..4], file.endian()).unwrap();
|
||||
let max = (0x7ff & data_value) as u16;
|
||||
let min = (0x7ff & data_value >> 11) as u16;
|
||||
let index_to_set_max = 0x0f & data_value >> 22;
|
||||
let index_to_set_min = 0x0f & data_value >> 26;
|
||||
|
||||
let max_minus_min = max as i32 - min as i32;
|
||||
let shift_by_bits = (0..4).find(|&shift| (0x80 << shift) > max_minus_min).unwrap_or(4);
|
||||
|
||||
let mut pixel = [0_u16; 16];
|
||||
let mut bit = 30;
|
||||
for i in 0..16 {
|
||||
pixel[i] = match () {
|
||||
_ if i as u32 == index_to_set_max => max,
|
||||
_ if i as u32 == index_to_set_min => min,
|
||||
_ => {
|
||||
let result = as_u16(&data[(data_index + (bit >> 3))..][..2], file.endian()).unwrap();
|
||||
let result = ((result >> (bit & 7)) & 0x07f) << shift_by_bits;
|
||||
|
||||
bit += 7;
|
||||
|
||||
(result + min).min(0x7ff)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for value in pixel {
|
||||
image[row * width + column] = curve.get((value << 1).into()) >> 2;
|
||||
|
||||
// Skip between interlaced columns
|
||||
column += 2;
|
||||
}
|
||||
|
||||
// Switch to the opposite interlaced columns
|
||||
column -= if column & 1 == 0 { 31 } else { 1 };
|
||||
|
||||
data_index += 16;
|
||||
}
|
||||
}
|
||||
|
||||
Some(image)
|
||||
}
|
|
@ -1 +1,3 @@
|
|||
pub mod arw1;
|
||||
pub mod arw2;
|
||||
pub mod uncompressed;
|
||||
|
|
|
@ -1,38 +1,47 @@
|
|||
use crate::tiff::file::TiffRead;
|
||||
use crate::tiff::tags::{BITS_PER_SAMPLE, CFA_PATTERN, CFA_PATTERN_DIM, COMPRESSION, IMAGE_LENGTH, IMAGE_WIDTH, ROWS_PER_STRIP, SAMPLES_PER_PIXEL, STRIP_BYTE_COUNTS, STRIP_OFFSETS};
|
||||
use crate::tiff::Ifd;
|
||||
use crate::tiff::tags::{BitsPerSample, CfaPattern, CfaPatternDim, Compression, ImageLength, ImageWidth, RowsPerStrip, StripByteCounts, StripOffsets, Tag};
|
||||
use crate::tiff::{Ifd, TiffError};
|
||||
use crate::RawImage;
|
||||
use std::io::{Read, Seek};
|
||||
use tag_derive::Tag;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Tag)]
|
||||
struct ArwUncompressedIfd {
|
||||
image_width: ImageWidth,
|
||||
image_height: ImageLength,
|
||||
rows_per_strip: RowsPerStrip,
|
||||
bits_per_sample: BitsPerSample,
|
||||
compression: Compression,
|
||||
cfa_pattern: CfaPattern,
|
||||
cfa_pattern_dim: CfaPatternDim,
|
||||
strip_offsets: StripOffsets,
|
||||
strip_byte_counts: StripByteCounts,
|
||||
}
|
||||
|
||||
pub fn decode<R: Read + Seek>(ifd: Ifd, file: &mut TiffRead<R>) -> RawImage {
|
||||
let strip_offsets = ifd.get(STRIP_OFFSETS, file).unwrap();
|
||||
let strip_byte_counts = ifd.get(STRIP_BYTE_COUNTS, file).unwrap();
|
||||
assert!(strip_offsets.len() == strip_byte_counts.len());
|
||||
let ifd = ifd.get_value::<ArwUncompressedIfd, _>(file).unwrap();
|
||||
|
||||
let image_width: usize = ifd.get(IMAGE_WIDTH, file).unwrap().try_into().unwrap();
|
||||
let image_height: usize = ifd.get(IMAGE_LENGTH, file).unwrap().try_into().unwrap();
|
||||
let rows_per_strip: usize = ifd.get(ROWS_PER_STRIP, file).unwrap().try_into().unwrap();
|
||||
let bits_per_sample: usize = ifd.get(BITS_PER_SAMPLE, file).unwrap().into();
|
||||
let bytes_per_sample: usize = bits_per_sample.div_ceil(8);
|
||||
let samples_per_pixel: usize = ifd.get(SAMPLES_PER_PIXEL, file).unwrap().into();
|
||||
let compression = ifd.get(COMPRESSION, file).unwrap();
|
||||
assert!(compression == 1); // 1 is the value for uncompressed format
|
||||
// let photometric_interpretation = ifd.get(PHOTOMETRIC_INTERPRETATION, file).unwrap();
|
||||
assert!(ifd.strip_offsets.len() == ifd.strip_byte_counts.len());
|
||||
assert!(ifd.strip_offsets.len() == 1);
|
||||
assert!(ifd.compression == 1); // 1 is the value for uncompressed format
|
||||
|
||||
let [cfa_pattern_width, cfa_pattern_height] = ifd.get(CFA_PATTERN_DIM, file).unwrap();
|
||||
let image_width: usize = ifd.image_width.try_into().unwrap();
|
||||
let image_height: usize = ifd.image_height.try_into().unwrap();
|
||||
let rows_per_strip: usize = ifd.rows_per_strip.try_into().unwrap();
|
||||
let bits_per_sample: usize = ifd.bits_per_sample.into();
|
||||
let [cfa_pattern_width, cfa_pattern_height] = ifd.cfa_pattern_dim;
|
||||
assert!(cfa_pattern_width == 2 && cfa_pattern_height == 2);
|
||||
|
||||
let cfa_pattern = ifd.get(CFA_PATTERN, file).unwrap();
|
||||
|
||||
let rows_per_strip_last = image_height % rows_per_strip;
|
||||
let bytes_per_row = bytes_per_sample * samples_per_pixel * image_width;
|
||||
|
||||
let mut image: Vec<u16> = Vec::with_capacity(image_height * image_width);
|
||||
|
||||
for i in 0..strip_offsets.len() {
|
||||
file.seek_from_start(strip_offsets[i]).unwrap();
|
||||
let row_count = if i == strip_offsets.len() { rows_per_strip_last } else { rows_per_strip };
|
||||
for _ in 0..row_count {
|
||||
for i in 0..ifd.strip_offsets.len() {
|
||||
file.seek_from_start(ifd.strip_offsets[i]).unwrap();
|
||||
|
||||
let last = i == ifd.strip_offsets.len();
|
||||
let rows = if last { image_height % rows_per_strip } else { rows_per_strip };
|
||||
|
||||
for _ in 0..rows {
|
||||
for _ in 0..image_width {
|
||||
image.push(file.read_u16().unwrap());
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
pub mod decoder;
|
||||
pub mod tiff;
|
||||
|
||||
use tag_derive::Tag;
|
||||
use tiff::file::TiffRead;
|
||||
use tiff::tags::{Compression, ImageLength, ImageWidth, Model, StripByteCounts, SubIfd, Tag};
|
||||
use tiff::{Ifd, TiffError};
|
||||
|
||||
use std::io::{Read, Seek};
|
||||
use thiserror::Error;
|
||||
use tiff::file::TiffRead;
|
||||
use tiff::tags::{COMPRESSION, SUBIFD};
|
||||
use tiff::{Ifd, TiffError};
|
||||
|
||||
pub struct RawImage {
|
||||
pub data: Vec<u16>,
|
||||
|
@ -20,21 +22,44 @@ pub struct Image<T> {
|
|||
pub channels: u8,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Tag)]
|
||||
struct ArwIfd {
|
||||
image_width: ImageWidth,
|
||||
image_height: ImageLength,
|
||||
compression: Compression,
|
||||
strip_byte_counts: StripByteCounts,
|
||||
}
|
||||
|
||||
pub fn decode<R: Read + Seek>(reader: &mut R) -> Result<RawImage, DecoderError> {
|
||||
let mut file = TiffRead::new(reader)?;
|
||||
let ifd = Ifd::new_first_ifd(&mut file)?;
|
||||
|
||||
// TODO: This is only for the tests to pass for now. Replace this with the correct implementation when the decoder is complete.
|
||||
let subifd = ifd.get(SUBIFD, &mut file)?;
|
||||
let model = ifd.get_value::<Model, _>(&mut file)?;
|
||||
|
||||
Ok(decoder::uncompressed::decode(subifd, &mut file))
|
||||
if model == "DSLR-A100" {
|
||||
Ok(decoder::arw1::decode_a100(ifd, &mut file))
|
||||
} else {
|
||||
let sub_ifd = ifd.get_value::<SubIfd, _>(&mut file)?;
|
||||
let arw_ifd = sub_ifd.get_value::<ArwIfd, _>(&mut file)?;
|
||||
|
||||
if arw_ifd.compression == 1 {
|
||||
Ok(decoder::uncompressed::decode(sub_ifd, &mut file))
|
||||
} else if arw_ifd.strip_byte_counts[0] == arw_ifd.image_width * arw_ifd.image_height {
|
||||
Ok(decoder::arw2::decode(sub_ifd, &mut file))
|
||||
} else {
|
||||
// TODO: implement for arw 1.
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_8bit(image: RawImage) -> Image<u8> {
|
||||
pub fn process_8bit(_image: RawImage) -> Image<u8> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn process_16bit(image: RawImage) -> Image<u16> {
|
||||
pub fn process_16bit(_image: RawImage) -> Image<u16> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::io::{Error, ErrorKind, Read, Result, Seek, SeekFrom};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
enum Endian {
|
||||
pub enum Endian {
|
||||
Little,
|
||||
Big,
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ impl<R: Read + Seek> TiffRead<R> {
|
|||
pub fn new(mut reader: R) -> Result<Self> {
|
||||
let error = Error::new(ErrorKind::InvalidData, "Invalid Tiff format");
|
||||
|
||||
let mut data = [0u8; 2];
|
||||
let mut data = [0_u8; 2];
|
||||
reader.read_exact(&mut data)?;
|
||||
let endian = if data[0] == 0x49 && data[1] == 0x49 {
|
||||
Endian::Little
|
||||
|
@ -36,6 +36,10 @@ impl<R: Read + Seek> TiffRead<R> {
|
|||
|
||||
Ok(Self { reader, endian })
|
||||
}
|
||||
|
||||
pub fn endian(&self) -> Endian {
|
||||
self.endian
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> Read for TiffRead<R> {
|
||||
|
@ -61,7 +65,7 @@ impl<R: Read + Seek> TiffRead<R> {
|
|||
}
|
||||
|
||||
pub fn read_n<const N: usize>(&mut self) -> Result<[u8; N]> {
|
||||
let mut data = [0u8; N];
|
||||
let mut data = [0_u8; N];
|
||||
self.read_exact(&mut data)?;
|
||||
Ok(data)
|
||||
}
|
||||
|
|
|
@ -4,13 +4,13 @@ mod types;
|
|||
pub mod values;
|
||||
|
||||
use file::TiffRead;
|
||||
use num_enum::{FromPrimitive, IntoPrimitive, TryFromPrimitive};
|
||||
use tags::Tag;
|
||||
|
||||
use num_enum::{FromPrimitive, IntoPrimitive};
|
||||
use std::fmt::Display;
|
||||
use std::io::{Read, Seek};
|
||||
use thiserror::Error;
|
||||
|
||||
use tags::Tag;
|
||||
use types::TagType;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, IntoPrimitive)]
|
||||
#[repr(u16)]
|
||||
pub enum TagId {
|
||||
|
@ -19,6 +19,8 @@ pub enum TagId {
|
|||
BitsPerSample = 0x102,
|
||||
Compression = 0x103,
|
||||
PhotometricInterpretation = 0x104,
|
||||
Make = 0x10f,
|
||||
Model = 0x110,
|
||||
StripOffsets = 0x111,
|
||||
SamplesPerPixel = 0x115,
|
||||
RowsPerStrip = 0x116,
|
||||
|
@ -26,6 +28,7 @@ pub enum TagId {
|
|||
SubIfd = 0x14a,
|
||||
JpegOffset = 0x201,
|
||||
JpegLength = 0x202,
|
||||
SonyToneCurve = 0x7010,
|
||||
CfaPatternDim = 0x828d,
|
||||
CfaPattern = 0x828e,
|
||||
|
||||
|
@ -34,26 +37,29 @@ pub enum TagId {
|
|||
}
|
||||
|
||||
#[repr(u16)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, IntoPrimitive)]
|
||||
pub enum IfdTagType {
|
||||
Ascii = 2,
|
||||
Byte = 1,
|
||||
Ascii = 2,
|
||||
Short = 3,
|
||||
Long = 4,
|
||||
Rational = 5,
|
||||
SByte = 6,
|
||||
Undefined = 7,
|
||||
SShort = 8,
|
||||
SLong = 9,
|
||||
SRational = 10,
|
||||
Float = 11,
|
||||
Double = 12,
|
||||
Undefined = 7,
|
||||
|
||||
#[num_enum(catch_all)]
|
||||
Unknown(u16),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct IfdEntry {
|
||||
tag: TagId,
|
||||
type_: u16,
|
||||
the_type: IfdTagType,
|
||||
count: u32,
|
||||
value: u32,
|
||||
}
|
||||
|
@ -66,15 +72,15 @@ pub struct Ifd {
|
|||
}
|
||||
|
||||
impl Ifd {
|
||||
pub fn new_first_ifd<R: Read + Seek>(file: &mut TiffRead<R>) -> std::io::Result<Self> {
|
||||
pub fn new_first_ifd<R: Read + Seek>(file: &mut TiffRead<R>) -> Result<Self, TiffError> {
|
||||
file.seek_from_start(4)?;
|
||||
let current_ifd_offset = file.read_u32()?;
|
||||
Ifd::new_from_offset(file, current_ifd_offset)
|
||||
}
|
||||
|
||||
pub fn new_from_offset<R: Read + Seek>(file: &mut TiffRead<R>, offset: u32) -> std::io::Result<Self> {
|
||||
pub fn new_from_offset<R: Read + Seek>(file: &mut TiffRead<R>, offset: u32) -> Result<Self, TiffError> {
|
||||
if offset == 0 {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Ifd at offset zero does not exist"));
|
||||
return Err(TiffError::InvalidOffset);
|
||||
}
|
||||
|
||||
file.seek_from_start(offset)?;
|
||||
|
@ -83,15 +89,15 @@ impl Ifd {
|
|||
let mut ifd_entries = Vec::with_capacity(num.into());
|
||||
for _ in 0..num {
|
||||
let tag = file.read_u16()?.into();
|
||||
let type_ = file.read_u16()?;
|
||||
let the_type = file.read_u16()?.into();
|
||||
let count = file.read_u32()?;
|
||||
let value = file.read_u32()?;
|
||||
|
||||
ifd_entries.push(IfdEntry { tag, type_, count, value });
|
||||
ifd_entries.push(IfdEntry { tag, the_type, count, value });
|
||||
}
|
||||
|
||||
let next_ifd_offset = file.read_u32()?;
|
||||
let next_ifd_offset = if next_ifd_offset == 0 { Some(next_ifd_offset) } else { None };
|
||||
let next_ifd_offset = if next_ifd_offset == 0 { None } else { Some(next_ifd_offset) };
|
||||
|
||||
Ok(Ifd {
|
||||
current_ifd_offset: offset,
|
||||
|
@ -100,7 +106,7 @@ impl Ifd {
|
|||
})
|
||||
}
|
||||
|
||||
fn next_ifd<R: Read + Seek>(&self, file: &mut TiffRead<R>) -> std::io::Result<Self> {
|
||||
fn next_ifd<R: Read + Seek>(&self, file: &mut TiffRead<R>) -> Result<Self, TiffError> {
|
||||
Ifd::new_from_offset(file, self.next_ifd_offset.unwrap_or(0))
|
||||
}
|
||||
|
||||
|
@ -112,12 +118,33 @@ impl Ifd {
|
|||
self.ifd_entries.iter()
|
||||
}
|
||||
|
||||
pub fn get<T: TagType, R: Read + Seek>(&self, tag: Tag<T>, file: &mut TiffRead<R>) -> Result<T::Output, TiffError> {
|
||||
let tag_id = tag.id();
|
||||
let index: u32 = self.iter().position(|x| x.tag == tag_id).ok_or(TiffError::InvalidTag)?.try_into()?;
|
||||
pub fn get_value<T: Tag, R: Read + Seek>(&self, file: &mut TiffRead<R>) -> Result<T::Output, TiffError> {
|
||||
T::get(self, file)
|
||||
}
|
||||
}
|
||||
|
||||
file.seek_from_start(self.current_ifd_offset + 2 + 12 * index + 2)?;
|
||||
T::read(file)
|
||||
impl Display for Ifd {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("IFD offset: ")?;
|
||||
self.current_ifd_offset.fmt(f)?;
|
||||
f.write_str("\n")?;
|
||||
|
||||
for ifd_entry in self.ifd_entries() {
|
||||
f.write_fmt(format_args!(
|
||||
"|- Tag: {:x?}, Type: {:?}, Count: {}, Value: {:x}\n",
|
||||
ifd_entry.tag, ifd_entry.the_type, ifd_entry.count, ifd_entry.value
|
||||
))?;
|
||||
}
|
||||
|
||||
f.write_str("Next IFD offset: ")?;
|
||||
if let Some(offset) = self.next_ifd_offset {
|
||||
offset.fmt(f)?;
|
||||
} else {
|
||||
f.write_str("None")?;
|
||||
}
|
||||
f.write_str("\n")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,8 +156,10 @@ pub enum TiffError {
|
|||
InvalidType,
|
||||
#[error("The count was invalid")]
|
||||
InvalidCount,
|
||||
#[error("The tag was invalid")]
|
||||
InvalidTag,
|
||||
#[error("The tag was missing")]
|
||||
MissingTag,
|
||||
#[error("The offset was invalid or zero")]
|
||||
InvalidOffset,
|
||||
#[error("An error occurred when converting integer from one type to another")]
|
||||
ConversionError(#[from] std::num::TryFromIntError),
|
||||
#[error("An IO Error ocurred")]
|
||||
|
|
|
@ -1,41 +1,188 @@
|
|||
use super::types::{Array, ConstArray, TagType, TypeByte, TypeIfd, TypeLong, TypeNumber, TypeShort};
|
||||
use super::TagId;
|
||||
use super::types::{Array, ConstArray, TagType, TypeByte, TypeIfd, TypeLong, TypeNumber, TypeShort, TypeSonyToneCurve, TypeString};
|
||||
use super::{Ifd, TagId, TiffError, TiffRead};
|
||||
|
||||
pub struct Tag<T: TagType> {
|
||||
tag_id: TagId,
|
||||
name: &'static str,
|
||||
tag_type: std::marker::PhantomData<T>,
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
pub trait SimpleTag {
|
||||
type Type: TagType;
|
||||
|
||||
const ID: TagId;
|
||||
const NAME: &'static str;
|
||||
}
|
||||
|
||||
impl<T: TagType> Tag<T> {
|
||||
const fn new(tag_id: TagId, name: &'static str) -> Self {
|
||||
Tag {
|
||||
tag_id,
|
||||
name,
|
||||
tag_type: std::marker::PhantomData,
|
||||
pub struct ImageWidth;
|
||||
pub struct ImageLength;
|
||||
pub struct BitsPerSample;
|
||||
pub struct Compression;
|
||||
pub struct PhotometricInterpretation;
|
||||
pub struct Make;
|
||||
pub struct Model;
|
||||
pub struct StripOffsets;
|
||||
pub struct SamplesPerPixel;
|
||||
pub struct RowsPerStrip;
|
||||
pub struct StripByteCounts;
|
||||
pub struct SubIfd;
|
||||
pub struct JpegOffset;
|
||||
pub struct JpegLength;
|
||||
pub struct CfaPatternDim;
|
||||
pub struct CfaPattern;
|
||||
pub struct SonyDataOffset;
|
||||
pub struct SonyToneCurve;
|
||||
|
||||
impl SimpleTag for ImageWidth {
|
||||
type Type = TypeNumber;
|
||||
|
||||
const ID: TagId = TagId::ImageWidth;
|
||||
const NAME: &'static str = "Image Width";
|
||||
}
|
||||
|
||||
impl SimpleTag for ImageLength {
|
||||
type Type = TypeNumber;
|
||||
|
||||
const ID: TagId = TagId::ImageLength;
|
||||
const NAME: &'static str = "Image Length";
|
||||
}
|
||||
|
||||
impl SimpleTag for BitsPerSample {
|
||||
type Type = TypeShort;
|
||||
|
||||
const ID: TagId = TagId::BitsPerSample;
|
||||
const NAME: &'static str = "Bits per Sample";
|
||||
}
|
||||
|
||||
impl SimpleTag for Compression {
|
||||
type Type = TypeShort;
|
||||
|
||||
const ID: TagId = TagId::Compression;
|
||||
const NAME: &'static str = "Compression";
|
||||
}
|
||||
|
||||
impl SimpleTag for PhotometricInterpretation {
|
||||
type Type = TypeShort;
|
||||
|
||||
const ID: TagId = TagId::PhotometricInterpretation;
|
||||
const NAME: &'static str = "Photometric Interpretation";
|
||||
}
|
||||
|
||||
impl SimpleTag for Make {
|
||||
type Type = TypeString;
|
||||
|
||||
const ID: TagId = TagId::Make;
|
||||
const NAME: &'static str = "Make";
|
||||
}
|
||||
|
||||
impl SimpleTag for Model {
|
||||
type Type = TypeString;
|
||||
|
||||
const ID: TagId = TagId::Model;
|
||||
const NAME: &'static str = "Model";
|
||||
}
|
||||
|
||||
impl SimpleTag for StripOffsets {
|
||||
type Type = Array<TypeNumber>;
|
||||
|
||||
const ID: TagId = TagId::StripOffsets;
|
||||
const NAME: &'static str = "Strip Offsets";
|
||||
}
|
||||
|
||||
impl SimpleTag for SamplesPerPixel {
|
||||
type Type = TypeShort;
|
||||
|
||||
const ID: TagId = TagId::SamplesPerPixel;
|
||||
const NAME: &'static str = "Samples per Pixel";
|
||||
}
|
||||
|
||||
impl SimpleTag for RowsPerStrip {
|
||||
type Type = TypeNumber;
|
||||
|
||||
const ID: TagId = TagId::RowsPerStrip;
|
||||
const NAME: &'static str = "Rows per Strip";
|
||||
}
|
||||
|
||||
impl SimpleTag for StripByteCounts {
|
||||
type Type = Array<TypeNumber>;
|
||||
|
||||
const ID: TagId = TagId::StripByteCounts;
|
||||
const NAME: &'static str = "Strip Byte Counts";
|
||||
}
|
||||
|
||||
impl SimpleTag for SubIfd {
|
||||
type Type = TypeIfd;
|
||||
|
||||
const ID: TagId = TagId::SubIfd;
|
||||
const NAME: &'static str = "SubIFD";
|
||||
}
|
||||
|
||||
impl SimpleTag for JpegOffset {
|
||||
type Type = TypeLong;
|
||||
|
||||
const ID: TagId = TagId::JpegOffset;
|
||||
const NAME: &'static str = "Jpeg Offset";
|
||||
}
|
||||
|
||||
impl SimpleTag for JpegLength {
|
||||
type Type = TypeLong;
|
||||
|
||||
const ID: TagId = TagId::JpegLength;
|
||||
const NAME: &'static str = "Jpeg Length";
|
||||
}
|
||||
|
||||
impl SimpleTag for CfaPatternDim {
|
||||
type Type = ConstArray<TypeShort, 2>;
|
||||
|
||||
const ID: TagId = TagId::CfaPatternDim;
|
||||
const NAME: &'static str = "CFA Pattern Dimension";
|
||||
}
|
||||
|
||||
impl SimpleTag for CfaPattern {
|
||||
type Type = Array<TypeByte>;
|
||||
|
||||
const ID: TagId = TagId::CfaPattern;
|
||||
const NAME: &'static str = "CFA Pattern";
|
||||
}
|
||||
|
||||
impl SimpleTag for SonyDataOffset {
|
||||
type Type = TypeLong;
|
||||
|
||||
const ID: TagId = TagId::SubIfd;
|
||||
const NAME: &'static str = "Sony Data Offset";
|
||||
}
|
||||
|
||||
impl SimpleTag for SonyToneCurve {
|
||||
type Type = TypeSonyToneCurve;
|
||||
|
||||
const ID: TagId = TagId::SonyToneCurve;
|
||||
const NAME: &'static str = "Sony Tone Curve";
|
||||
}
|
||||
|
||||
pub trait Tag {
|
||||
type Output;
|
||||
|
||||
fn get<R: Read + Seek>(ifd: &Ifd, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError>;
|
||||
}
|
||||
|
||||
impl<T: SimpleTag> Tag for T {
|
||||
type Output = <T::Type as TagType>::Output;
|
||||
|
||||
fn get<R: Read + Seek>(ifd: &Ifd, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
let tag_id = T::ID;
|
||||
let index: u32 = ifd.iter().position(|x| x.tag == tag_id).ok_or(TiffError::MissingTag)?.try_into()?;
|
||||
|
||||
file.seek_from_start(ifd.current_ifd_offset + 2 + 12 * index + 2)?;
|
||||
T::Type::read(file)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Tag> Tag for Option<T> {
|
||||
type Output = Option<T::Output>;
|
||||
|
||||
fn get<R: Read + Seek>(ifd: &Ifd, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
let result = T::get(ifd, file);
|
||||
|
||||
match result {
|
||||
Err(TiffError::MissingTag) => Ok(None),
|
||||
Ok(x) => Ok(Some(x)),
|
||||
Err(x) => Err(x),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> TagId {
|
||||
self.tag_id
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &'static str {
|
||||
self.name
|
||||
}
|
||||
}
|
||||
|
||||
pub const IMAGE_WIDTH: Tag<TypeNumber> = Tag::new(TagId::ImageWidth, "Image Width");
|
||||
pub const IMAGE_LENGTH: Tag<TypeNumber> = Tag::new(TagId::ImageLength, "Image Length");
|
||||
pub const BITS_PER_SAMPLE: Tag<TypeShort> = Tag::new(TagId::BitsPerSample, "Bits per Sample");
|
||||
pub const COMPRESSION: Tag<TypeShort> = Tag::new(TagId::Compression, "Compression");
|
||||
pub const PHOTOMETRIC_INTERPRETATION: Tag<TypeShort> = Tag::new(TagId::PhotometricInterpretation, "Photometric Interpretation");
|
||||
pub const STRIP_OFFSETS: Tag<Array<TypeNumber>> = Tag::new(TagId::StripOffsets, "Strip Offsets");
|
||||
pub const SAMPLES_PER_PIXEL: Tag<TypeShort> = Tag::new(TagId::SamplesPerPixel, "Samples per Pixel");
|
||||
pub const ROWS_PER_STRIP: Tag<TypeNumber> = Tag::new(TagId::RowsPerStrip, "Rows per Strip");
|
||||
pub const STRIP_BYTE_COUNTS: Tag<Array<TypeNumber>> = Tag::new(TagId::StripByteCounts, "Strip Byte Counts");
|
||||
pub const SUBIFD: Tag<TypeIfd> = Tag::new(TagId::SubIfd, "SubIFD");
|
||||
pub const JPEG_OFFSET: Tag<TypeLong> = Tag::new(TagId::JpegOffset, "Jpeg Offset");
|
||||
pub const JPEG_LENGTH: Tag<TypeLong> = Tag::new(TagId::JpegLength, "Jpeg Length");
|
||||
pub const CFA_PATTERN_DIM: Tag<ConstArray<TypeShort, 2>> = Tag::new(TagId::CfaPatternDim, "CFA Pattern Dimension");
|
||||
pub const CFA_PATTERN: Tag<Array<TypeByte>> = Tag::new(TagId::CfaPattern, "CFA Pattern");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::io::{Read, Seek};
|
||||
|
||||
use super::file::TiffRead;
|
||||
use super::values::Rational;
|
||||
use super::values::{CurveLookupTable, Rational};
|
||||
use super::{Ifd, IfdTagType, TiffError};
|
||||
|
||||
pub struct TypeAscii;
|
||||
|
@ -24,16 +24,16 @@ pub struct TypeIfd;
|
|||
pub trait PrimitiveType {
|
||||
type Output;
|
||||
|
||||
fn get_size(type_: IfdTagType) -> Option<u32>;
|
||||
fn get_size(the_type: IfdTagType) -> Option<u32>;
|
||||
|
||||
fn read_primitive<R: Read + Seek>(type_: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError>;
|
||||
fn read_primitive<R: Read + Seek>(the_type: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError>;
|
||||
}
|
||||
|
||||
impl PrimitiveType for TypeAscii {
|
||||
type Output = char;
|
||||
|
||||
fn get_size(type_: IfdTagType) -> Option<u32> {
|
||||
match type_ {
|
||||
fn get_size(the_type: IfdTagType) -> Option<u32> {
|
||||
match the_type {
|
||||
IfdTagType::Ascii => Some(1),
|
||||
_ => None,
|
||||
}
|
||||
|
@ -52,8 +52,8 @@ impl PrimitiveType for TypeAscii {
|
|||
impl PrimitiveType for TypeByte {
|
||||
type Output = u8;
|
||||
|
||||
fn get_size(type_: IfdTagType) -> Option<u32> {
|
||||
match type_ {
|
||||
fn get_size(the_type: IfdTagType) -> Option<u32> {
|
||||
match the_type {
|
||||
IfdTagType::Byte => Some(1),
|
||||
_ => None,
|
||||
}
|
||||
|
@ -67,8 +67,8 @@ impl PrimitiveType for TypeByte {
|
|||
impl PrimitiveType for TypeShort {
|
||||
type Output = u16;
|
||||
|
||||
fn get_size(type_: IfdTagType) -> Option<u32> {
|
||||
match type_ {
|
||||
fn get_size(the_type: IfdTagType) -> Option<u32> {
|
||||
match the_type {
|
||||
IfdTagType::Short => Some(2),
|
||||
_ => None,
|
||||
}
|
||||
|
@ -82,8 +82,8 @@ impl PrimitiveType for TypeShort {
|
|||
impl PrimitiveType for TypeLong {
|
||||
type Output = u32;
|
||||
|
||||
fn get_size(type_: IfdTagType) -> Option<u32> {
|
||||
match type_ {
|
||||
fn get_size(the_type: IfdTagType) -> Option<u32> {
|
||||
match the_type {
|
||||
IfdTagType::Long => Some(4),
|
||||
_ => None,
|
||||
}
|
||||
|
@ -97,16 +97,16 @@ impl PrimitiveType for TypeLong {
|
|||
impl PrimitiveType for TypeRational {
|
||||
type Output = Rational<u32>;
|
||||
|
||||
fn get_size(type_: IfdTagType) -> Option<u32> {
|
||||
match type_ {
|
||||
fn get_size(the_type: IfdTagType) -> Option<u32> {
|
||||
match the_type {
|
||||
IfdTagType::Rational => Some(8),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn read_primitive<R: Read + Seek>(type_: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
let numerator = TypeLong::read_primitive(type_, file)?;
|
||||
let denominator = TypeLong::read_primitive(type_, file)?;
|
||||
fn read_primitive<R: Read + Seek>(the_type: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
let numerator = TypeLong::read_primitive(the_type, file)?;
|
||||
let denominator = TypeLong::read_primitive(the_type, file)?;
|
||||
|
||||
Ok(Rational { numerator, denominator })
|
||||
}
|
||||
|
@ -115,8 +115,8 @@ impl PrimitiveType for TypeRational {
|
|||
impl PrimitiveType for TypeSByte {
|
||||
type Output = i8;
|
||||
|
||||
fn get_size(type_: IfdTagType) -> Option<u32> {
|
||||
match type_ {
|
||||
fn get_size(the_type: IfdTagType) -> Option<u32> {
|
||||
match the_type {
|
||||
IfdTagType::SByte => Some(1),
|
||||
_ => None,
|
||||
}
|
||||
|
@ -130,8 +130,8 @@ impl PrimitiveType for TypeSByte {
|
|||
impl PrimitiveType for TypeSShort {
|
||||
type Output = i16;
|
||||
|
||||
fn get_size(type_: IfdTagType) -> Option<u32> {
|
||||
match type_ {
|
||||
fn get_size(the_type: IfdTagType) -> Option<u32> {
|
||||
match the_type {
|
||||
IfdTagType::SShort => Some(2),
|
||||
_ => None,
|
||||
}
|
||||
|
@ -145,8 +145,8 @@ impl PrimitiveType for TypeSShort {
|
|||
impl PrimitiveType for TypeSLong {
|
||||
type Output = i32;
|
||||
|
||||
fn get_size(type_: IfdTagType) -> Option<u32> {
|
||||
match type_ {
|
||||
fn get_size(the_type: IfdTagType) -> Option<u32> {
|
||||
match the_type {
|
||||
IfdTagType::SLong => Some(4),
|
||||
_ => None,
|
||||
}
|
||||
|
@ -160,16 +160,16 @@ impl PrimitiveType for TypeSLong {
|
|||
impl PrimitiveType for TypeSRational {
|
||||
type Output = Rational<i32>;
|
||||
|
||||
fn get_size(type_: IfdTagType) -> Option<u32> {
|
||||
match type_ {
|
||||
fn get_size(the_type: IfdTagType) -> Option<u32> {
|
||||
match the_type {
|
||||
IfdTagType::SRational => Some(8),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn read_primitive<R: Read + Seek>(type_: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
let numerator = TypeSLong::read_primitive(type_, file)?;
|
||||
let denominator = TypeSLong::read_primitive(type_, file)?;
|
||||
fn read_primitive<R: Read + Seek>(the_type: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
let numerator = TypeSLong::read_primitive(the_type, file)?;
|
||||
let denominator = TypeSLong::read_primitive(the_type, file)?;
|
||||
|
||||
Ok(Rational { numerator, denominator })
|
||||
}
|
||||
|
@ -178,8 +178,8 @@ impl PrimitiveType for TypeSRational {
|
|||
impl PrimitiveType for TypeFloat {
|
||||
type Output = f32;
|
||||
|
||||
fn get_size(type_: IfdTagType) -> Option<u32> {
|
||||
match type_ {
|
||||
fn get_size(the_type: IfdTagType) -> Option<u32> {
|
||||
match the_type {
|
||||
IfdTagType::Float => Some(4),
|
||||
_ => None,
|
||||
}
|
||||
|
@ -193,8 +193,8 @@ impl PrimitiveType for TypeFloat {
|
|||
impl PrimitiveType for TypeDouble {
|
||||
type Output = f64;
|
||||
|
||||
fn get_size(type_: IfdTagType) -> Option<u32> {
|
||||
match type_ {
|
||||
fn get_size(the_type: IfdTagType) -> Option<u32> {
|
||||
match the_type {
|
||||
IfdTagType::Double => Some(8),
|
||||
_ => None,
|
||||
}
|
||||
|
@ -220,20 +220,20 @@ impl PrimitiveType for TypeUndefined {
|
|||
impl PrimitiveType for TypeNumber {
|
||||
type Output = u32;
|
||||
|
||||
fn get_size(type_: IfdTagType) -> Option<u32> {
|
||||
match type_ {
|
||||
IfdTagType::Byte => TypeByte::get_size(type_),
|
||||
IfdTagType::Short => TypeShort::get_size(type_),
|
||||
IfdTagType::Long => TypeLong::get_size(type_),
|
||||
fn get_size(the_type: IfdTagType) -> Option<u32> {
|
||||
match the_type {
|
||||
IfdTagType::Byte => TypeByte::get_size(the_type),
|
||||
IfdTagType::Short => TypeShort::get_size(the_type),
|
||||
IfdTagType::Long => TypeLong::get_size(the_type),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn read_primitive<R: Read + Seek>(type_: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
Ok(match type_ {
|
||||
IfdTagType::Byte => TypeByte::read_primitive(type_, file)?.into(),
|
||||
IfdTagType::Short => TypeShort::read_primitive(type_, file)?.into(),
|
||||
IfdTagType::Long => TypeLong::read_primitive(type_, file)?,
|
||||
fn read_primitive<R: Read + Seek>(the_type: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
Ok(match the_type {
|
||||
IfdTagType::Byte => TypeByte::read_primitive(the_type, file)?.into(),
|
||||
IfdTagType::Short => TypeShort::read_primitive(the_type, file)?.into(),
|
||||
IfdTagType::Long => TypeLong::read_primitive(the_type, file)?,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
@ -242,20 +242,20 @@ impl PrimitiveType for TypeNumber {
|
|||
impl PrimitiveType for TypeSNumber {
|
||||
type Output = i32;
|
||||
|
||||
fn get_size(type_: IfdTagType) -> Option<u32> {
|
||||
match type_ {
|
||||
IfdTagType::SByte => TypeSByte::get_size(type_),
|
||||
IfdTagType::SShort => TypeSShort::get_size(type_),
|
||||
IfdTagType::SLong => TypeSLong::get_size(type_),
|
||||
fn get_size(the_type: IfdTagType) -> Option<u32> {
|
||||
match the_type {
|
||||
IfdTagType::SByte => TypeSByte::get_size(the_type),
|
||||
IfdTagType::SShort => TypeSShort::get_size(the_type),
|
||||
IfdTagType::SLong => TypeSLong::get_size(the_type),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn read_primitive<R: Read + Seek>(type_: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
Ok(match type_ {
|
||||
IfdTagType::SByte => TypeSByte::read_primitive(type_, file)?.into(),
|
||||
IfdTagType::SShort => TypeSShort::read_primitive(type_, file)?.into(),
|
||||
IfdTagType::SLong => TypeSLong::read_primitive(type_, file)?,
|
||||
fn read_primitive<R: Read + Seek>(the_type: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
Ok(match the_type {
|
||||
IfdTagType::SByte => TypeSByte::read_primitive(the_type, file)?.into(),
|
||||
IfdTagType::SShort => TypeSShort::read_primitive(the_type, file)?.into(),
|
||||
IfdTagType::SLong => TypeSLong::read_primitive(the_type, file)?,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
@ -264,15 +264,12 @@ impl PrimitiveType for TypeSNumber {
|
|||
impl PrimitiveType for TypeIfd {
|
||||
type Output = Ifd;
|
||||
|
||||
fn get_size(type_: IfdTagType) -> Option<u32> {
|
||||
match type_ {
|
||||
IfdTagType::Long => Some(4),
|
||||
_ => None,
|
||||
}
|
||||
fn get_size(the_type: IfdTagType) -> Option<u32> {
|
||||
TypeLong::get_size(the_type)
|
||||
}
|
||||
|
||||
fn read_primitive<R: Read + Seek>(type_: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
let offset = TypeLong::read_primitive(type_, file)?;
|
||||
fn read_primitive<R: Read + Seek>(the_type: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
let offset = TypeLong::read_primitive(the_type, file)?;
|
||||
Ok(Ifd::new_from_offset(file, offset)?)
|
||||
}
|
||||
}
|
||||
|
@ -287,20 +284,20 @@ impl<T: PrimitiveType> TagType for T {
|
|||
type Output = T::Output;
|
||||
|
||||
fn read<R: Read + Seek>(file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
let type_ = IfdTagType::try_from(file.read_u16()?).map_err(|_| TiffError::InvalidType)?;
|
||||
let the_type = IfdTagType::try_from(file.read_u16()?).map_err(|_| TiffError::InvalidType)?;
|
||||
let count = file.read_u32()?;
|
||||
|
||||
if count != 1 {
|
||||
return Err(TiffError::InvalidCount);
|
||||
}
|
||||
|
||||
let size = T::get_size(type_).ok_or(TiffError::InvalidType)?;
|
||||
let size = T::get_size(the_type).ok_or(TiffError::InvalidType)?;
|
||||
if count * size > 4 {
|
||||
let offset = file.read_u32()?;
|
||||
file.seek_from_start(offset)?;
|
||||
}
|
||||
|
||||
T::read_primitive(type_, file)
|
||||
T::read_primitive(the_type, file)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -316,10 +313,10 @@ impl<T: PrimitiveType> TagType for Array<T> {
|
|||
type Output = Vec<T::Output>;
|
||||
|
||||
fn read<R: Read + Seek>(file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
let type_ = IfdTagType::try_from(file.read_u16()?).map_err(|_| TiffError::InvalidType)?;
|
||||
let the_type = IfdTagType::try_from(file.read_u16()?).map_err(|_| TiffError::InvalidType)?;
|
||||
let count = file.read_u32()?;
|
||||
|
||||
let size = T::get_size(type_).ok_or(TiffError::InvalidType)?;
|
||||
let size = T::get_size(the_type).ok_or(TiffError::InvalidType)?;
|
||||
if count * size > 4 {
|
||||
let offset = file.read_u32()?;
|
||||
file.seek_from_start(offset)?;
|
||||
|
@ -327,7 +324,7 @@ impl<T: PrimitiveType> TagType for Array<T> {
|
|||
|
||||
let mut ans = Vec::with_capacity(count.try_into()?);
|
||||
for _ in 0..count {
|
||||
ans.push(T::read_primitive(type_, file)?);
|
||||
ans.push(T::read_primitive(the_type, file)?);
|
||||
}
|
||||
Ok(ans)
|
||||
}
|
||||
|
@ -337,14 +334,14 @@ impl<T: PrimitiveType, const N: usize> TagType for ConstArray<T, N> {
|
|||
type Output = [T::Output; N];
|
||||
|
||||
fn read<R: Read + Seek>(file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
let type_ = IfdTagType::try_from(file.read_u16()?).map_err(|_| TiffError::InvalidType)?;
|
||||
let the_type = IfdTagType::try_from(file.read_u16()?).map_err(|_| TiffError::InvalidType)?;
|
||||
let count = file.read_u32()?;
|
||||
|
||||
if count != N.try_into()? {
|
||||
return Err(TiffError::InvalidCount);
|
||||
}
|
||||
|
||||
let size = T::get_size(type_).ok_or(TiffError::InvalidType)?;
|
||||
let size = T::get_size(the_type).ok_or(TiffError::InvalidType)?;
|
||||
if count * size > 4 {
|
||||
let offset = file.read_u32()?;
|
||||
file.seek_from_start(offset)?;
|
||||
|
@ -352,8 +349,32 @@ impl<T: PrimitiveType, const N: usize> TagType for ConstArray<T, N> {
|
|||
|
||||
let mut ans = Vec::with_capacity(count.try_into()?);
|
||||
for _ in 0..count {
|
||||
ans.push(T::read_primitive(type_, file)?);
|
||||
ans.push(T::read_primitive(the_type, file)?);
|
||||
}
|
||||
ans.try_into().map_err(|_| TiffError::InvalidCount)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TypeString;
|
||||
pub struct TypeSonyToneCurve;
|
||||
|
||||
impl TagType for TypeString {
|
||||
type Output = String;
|
||||
|
||||
fn read<R: Read + Seek>(file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
let string = Array::<TypeAscii>::read(file)?;
|
||||
|
||||
// Skip the NUL character at the end
|
||||
let len = string.len();
|
||||
Ok(string.into_iter().take(len - 1).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl TagType for TypeSonyToneCurve {
|
||||
type Output = CurveLookupTable;
|
||||
|
||||
fn read<R: Read + Seek>(file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
let values = ConstArray::<TypeShort, 4>::read(file)?;
|
||||
Ok(CurveLookupTable::from_sony_tone_table(values))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,3 +2,29 @@ pub struct Rational<T> {
|
|||
pub numerator: T,
|
||||
pub denominator: T,
|
||||
}
|
||||
|
||||
pub struct CurveLookupTable {
|
||||
table: Vec<u16>,
|
||||
}
|
||||
|
||||
impl CurveLookupTable {
|
||||
pub fn from_sony_tone_table(values: [u16; 4]) -> CurveLookupTable {
|
||||
let mut sony_curve = [0, 0, 0, 0, 0, 4095];
|
||||
for i in 0..4 {
|
||||
sony_curve[i + 1] = values[i] >> 2 & 0xfff;
|
||||
}
|
||||
|
||||
let mut table = vec![0_u16; (sony_curve[5] + 1).into()];
|
||||
for i in 0..5 {
|
||||
for j in (sony_curve[i] + 1)..=sony_curve[i + 1] {
|
||||
table[j as usize] = table[(j - 1) as usize] + (1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
CurveLookupTable { table }
|
||||
}
|
||||
|
||||
pub fn get(&self, x: usize) -> u16 {
|
||||
self.table[x]
|
||||
}
|
||||
}
|
||||
|
|
16
libraries/raw-rs/tag-derive/Cargo.toml
Normal file
16
libraries/raw-rs/tag-derive/Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "tag-derive"
|
||||
version = "0.0.1"
|
||||
publish = false
|
||||
edition = "2021"
|
||||
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||
description = "Derive macro for the Tag trait in raw-rs"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/GraphiteEditor/Graphite/tree/master/libraries/raw-rs/tag-derive"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
quote.workspace = true
|
||||
syn.workspace = true
|
46
libraries/raw-rs/tag-derive/src/lib.rs
Normal file
46
libraries/raw-rs/tag-derive/src/lib.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{Data, DeriveInput, Fields};
|
||||
|
||||
#[proc_macro_derive(Tag)]
|
||||
pub fn tag_derive(input: TokenStream) -> TokenStream {
|
||||
let ast: DeriveInput = syn::parse(input).unwrap();
|
||||
|
||||
let name = &ast.ident;
|
||||
|
||||
let data_struct = if let Data::Struct(data_struct) = ast.data {
|
||||
data_struct
|
||||
} else {
|
||||
panic!("Tag trait can only be derived for structs")
|
||||
};
|
||||
|
||||
let named_fields = if let Fields::Named(named_fields) = data_struct.fields {
|
||||
named_fields
|
||||
} else {
|
||||
panic!("Tag trait can only be derived for structs with named_fields")
|
||||
};
|
||||
|
||||
let struct_idents: Vec<_> = named_fields.named.iter().map(|field| field.ident.clone().unwrap()).collect();
|
||||
let struct_types: Vec<_> = named_fields.named.iter().map(|field| field.ty.clone()).collect();
|
||||
|
||||
let new_name = format_ident!("_{}", name);
|
||||
|
||||
let gen = quote! {
|
||||
struct #new_name {
|
||||
#( #struct_idents: <#struct_types as Tag>::Output ),*
|
||||
}
|
||||
|
||||
impl Tag for #name {
|
||||
type Output = #new_name;
|
||||
|
||||
fn get<R: Read + Seek>(ifd: &Ifd, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
#( let #struct_idents = <#struct_types as Tag>::get(ifd, file)?; )*
|
||||
Ok(#new_name { #( #struct_idents ),* })
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
gen.into()
|
||||
}
|
|
@ -9,7 +9,7 @@ use raw_rs::RawImage;
|
|||
use downloader::{Download, Downloader};
|
||||
use libraw::Processor;
|
||||
|
||||
const TEST_FILES: [&str; 1] = ["ILCE-7M3-ARW2.3.5-blossoms.arw"];
|
||||
const TEST_FILES: [&str; 3] = ["ILCE-7M3-ARW2.3.5-blossoms.arw", "ILCE-7RM4-ARW2.3.5-kestrel.arw", "ILCE-6000-ARW2.3.1-windsock.arw"];
|
||||
const BASE_URL: &str = "https://static.graphite.rs/test-data/libraries/raw-rs/";
|
||||
const BASE_PATH: &str = "./tests/images";
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue