Create new library Raw-rs including a basic TIFF decoder (#1757)

* add a basic tiff decoder in raw-rs

* cargo fmt

* add readme and license files

* add warning about being in-progress

* add testing framework for raw-rs

* add new type IFD and rename tag

* remove test_each and merge into single test

* cargo fmt

* make sure images folder stays in git

* rename image_length with image_height

* change name of test file

* Readme changes

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Elbert Ronnie
2024-06-02 04:58:06 -07:00
committed by GitHub
co-authored by Keavon Chambers
parent 6d74abb4de
commit 72ccba09af
17 changed files with 1354 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
pub mod uncompressed;
@@ -0,0 +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::RawImage;
use std::io::{Read, Seek};
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 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();
let [cfa_pattern_width, cfa_pattern_height] = ifd.get(CFA_PATTERN_DIM, file).unwrap();
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 _ in 0..image_width {
image.push(file.read_u16().unwrap());
}
}
}
RawImage {
data: image,
width: image_width,
height: image_height,
}
}
+49
View File
@@ -0,0 +1,49 @@
pub mod decoder;
pub mod tiff;
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>,
pub width: usize,
pub height: usize,
}
pub struct Image<T> {
pub data: Vec<T>,
pub width: usize,
pub height: usize,
pub channels: u8,
}
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)?;
Ok(decoder::uncompressed::decode(subifd, &mut file))
}
pub fn process_8bit(image: RawImage) -> Image<u8> {
todo!()
}
pub fn process_16bit(image: RawImage) -> Image<u16> {
todo!()
}
#[derive(Error, Debug)]
pub enum DecoderError {
#[error("An error occurred when trying to parse the TIFF format")]
TiffError(#[from] TiffError),
#[error("An error occurred when converting integer from one type to another")]
ConversionError(#[from] std::num::TryFromIntError),
#[error("An IO Error ocurred")]
IoError(#[from] std::io::Error),
}
+148
View File
@@ -0,0 +1,148 @@
use std::io::{Error, ErrorKind, Read, Result, Seek, SeekFrom};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Endian {
Little,
Big,
}
pub struct TiffRead<R: Read + Seek> {
reader: R,
endian: Endian,
}
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];
reader.read_exact(&mut data)?;
let endian = if data[0] == 0x49 && data[1] == 0x49 {
Endian::Little
} else if data[0] == 0x4d && data[1] == 0x4d {
Endian::Big
} else {
return Err(error);
};
reader.read_exact(&mut data)?;
let magic_number = match endian {
Endian::Little => u16::from_le_bytes(data),
Endian::Big => u16::from_be_bytes(data),
};
if magic_number != 42 {
return Err(error);
}
Ok(Self { reader, endian })
}
}
impl<R: Read + Seek> Read for TiffRead<R> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
self.reader.read(buf)
}
}
impl<R: Read + Seek> Seek for TiffRead<R> {
fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
self.reader.seek(pos)
}
}
impl<R: Read + Seek> TiffRead<R> {
pub fn seek_from_start(&mut self, offset: u32) -> Result<u64> {
self.reader.seek(SeekFrom::Start(offset.into()))
}
pub fn read_ascii(&mut self) -> Result<char> {
let data = self.read_n::<1>()?;
Ok(data[0] as char)
}
pub fn read_n<const N: usize>(&mut self) -> Result<[u8; N]> {
let mut data = [0u8; N];
self.read_exact(&mut data)?;
Ok(data)
}
pub fn read_u8(&mut self) -> Result<u8> {
let data = self.read_n()?;
match self.endian {
Endian::Little => Ok(u8::from_le_bytes(data)),
Endian::Big => Ok(u8::from_be_bytes(data)),
}
}
pub fn read_u16(&mut self) -> Result<u16> {
let data = self.read_n()?;
match self.endian {
Endian::Little => Ok(u16::from_le_bytes(data)),
Endian::Big => Ok(u16::from_be_bytes(data)),
}
}
pub fn read_u32(&mut self) -> Result<u32> {
let data = self.read_n()?;
match self.endian {
Endian::Little => Ok(u32::from_le_bytes(data)),
Endian::Big => Ok(u32::from_be_bytes(data)),
}
}
pub fn read_u64(&mut self) -> Result<u64> {
let data = self.read_n()?;
match self.endian {
Endian::Little => Ok(u64::from_le_bytes(data)),
Endian::Big => Ok(u64::from_be_bytes(data)),
}
}
pub fn read_i8(&mut self) -> Result<i8> {
let data = self.read_n()?;
match self.endian {
Endian::Little => Ok(i8::from_le_bytes(data)),
Endian::Big => Ok(i8::from_be_bytes(data)),
}
}
pub fn read_i16(&mut self) -> Result<i16> {
let data = self.read_n()?;
match self.endian {
Endian::Little => Ok(i16::from_le_bytes(data)),
Endian::Big => Ok(i16::from_be_bytes(data)),
}
}
pub fn read_i32(&mut self) -> Result<i32> {
let data = self.read_n()?;
match self.endian {
Endian::Little => Ok(i32::from_le_bytes(data)),
Endian::Big => Ok(i32::from_be_bytes(data)),
}
}
pub fn read_i64(&mut self) -> Result<i64> {
let data = self.read_n()?;
match self.endian {
Endian::Little => Ok(i64::from_le_bytes(data)),
Endian::Big => Ok(i64::from_be_bytes(data)),
}
}
pub fn read_f32(&mut self) -> Result<f32> {
let data = self.read_n()?;
match self.endian {
Endian::Little => Ok(f32::from_le_bytes(data)),
Endian::Big => Ok(f32::from_be_bytes(data)),
}
}
pub fn read_f64(&mut self) -> Result<f64> {
let data = self.read_n()?;
match self.endian {
Endian::Little => Ok(f64::from_le_bytes(data)),
Endian::Big => Ok(f64::from_be_bytes(data)),
}
}
}
+138
View File
@@ -0,0 +1,138 @@
pub mod file;
pub mod tags;
mod types;
pub mod values;
use file::TiffRead;
use num_enum::{FromPrimitive, IntoPrimitive, TryFromPrimitive};
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 {
ImageWidth = 0x100,
ImageLength = 0x101,
BitsPerSample = 0x102,
Compression = 0x103,
PhotometricInterpretation = 0x104,
StripOffsets = 0x111,
SamplesPerPixel = 0x115,
RowsPerStrip = 0x116,
StripByteCounts = 0x117,
SubIfd = 0x14a,
JpegOffset = 0x201,
JpegLength = 0x202,
CfaPatternDim = 0x828d,
CfaPattern = 0x828e,
#[num_enum(catch_all)]
Unknown(u16),
}
#[repr(u16)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
pub enum IfdTagType {
Ascii = 2,
Byte = 1,
Short = 3,
Long = 4,
Rational = 5,
SByte = 6,
SShort = 8,
SLong = 9,
SRational = 10,
Float = 11,
Double = 12,
Undefined = 7,
}
#[derive(Copy, Clone, Debug)]
pub struct IfdEntry {
tag: TagId,
type_: u16,
count: u32,
value: u32,
}
#[derive(Clone, Debug)]
pub struct Ifd {
current_ifd_offset: u32,
ifd_entries: Vec<IfdEntry>,
next_ifd_offset: Option<u32>,
}
impl Ifd {
pub fn new_first_ifd<R: Read + Seek>(file: &mut TiffRead<R>) -> std::io::Result<Self> {
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> {
if offset == 0 {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Ifd at offset zero does not exist"));
}
file.seek_from_start(offset)?;
let num = file.read_u16()?;
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 count = file.read_u32()?;
let value = file.read_u32()?;
ifd_entries.push(IfdEntry { tag, 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 };
Ok(Ifd {
current_ifd_offset: offset,
ifd_entries,
next_ifd_offset,
})
}
fn next_ifd<R: Read + Seek>(&self, file: &mut TiffRead<R>) -> std::io::Result<Self> {
Ifd::new_from_offset(file, self.next_ifd_offset.unwrap_or(0))
}
pub fn ifd_entries(&self) -> &[IfdEntry] {
&self.ifd_entries
}
pub fn iter(&self) -> impl Iterator<Item = &IfdEntry> {
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()?;
file.seek_from_start(self.current_ifd_offset + 2 + 12 * index + 2)?;
T::read(file)
}
}
#[derive(Error, Debug)]
pub enum TiffError {
#[error("The value was invalid")]
InvalidValue,
#[error("The type was invalid")]
InvalidType,
#[error("The count was invalid")]
InvalidCount,
#[error("The tag was invalid")]
InvalidTag,
#[error("An error occurred when converting integer from one type to another")]
ConversionError(#[from] std::num::TryFromIntError),
#[error("An IO Error ocurred")]
IoError(#[from] std::io::Error),
}
+41
View File
@@ -0,0 +1,41 @@
use super::types::{Array, ConstArray, TagType, TypeByte, TypeIfd, TypeLong, TypeNumber, TypeShort};
use super::TagId;
pub struct Tag<T: TagType> {
tag_id: TagId,
name: &'static str,
tag_type: std::marker::PhantomData<T>,
}
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 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");
+359
View File
@@ -0,0 +1,359 @@
use std::io::{Read, Seek};
use super::file::TiffRead;
use super::values::Rational;
use super::{Ifd, IfdTagType, TiffError};
pub struct TypeAscii;
pub struct TypeByte;
pub struct TypeShort;
pub struct TypeLong;
pub struct TypeRational;
pub struct TypeSByte;
pub struct TypeSShort;
pub struct TypeSLong;
pub struct TypeSRational;
pub struct TypeFloat;
pub struct TypeDouble;
pub struct TypeUndefined;
pub struct TypeNumber;
pub struct TypeSNumber;
pub struct TypeIfd;
pub trait PrimitiveType {
type Output;
fn get_size(type_: IfdTagType) -> Option<u32>;
fn read_primitive<R: Read + Seek>(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_ {
IfdTagType::Ascii => Some(1),
_ => None,
}
}
fn read_primitive<R: Read + Seek>(_: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
let value = file.read_ascii()?;
if value.is_ascii() {
Ok(value)
} else {
Err(TiffError::InvalidValue)
}
}
}
impl PrimitiveType for TypeByte {
type Output = u8;
fn get_size(type_: IfdTagType) -> Option<u32> {
match type_ {
IfdTagType::Byte => Some(1),
_ => None,
}
}
fn read_primitive<R: Read + Seek>(_: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
Ok(file.read_u8()?)
}
}
impl PrimitiveType for TypeShort {
type Output = u16;
fn get_size(type_: IfdTagType) -> Option<u32> {
match type_ {
IfdTagType::Short => Some(2),
_ => None,
}
}
fn read_primitive<R: Read + Seek>(_: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
Ok(file.read_u16()?)
}
}
impl PrimitiveType for TypeLong {
type Output = u32;
fn get_size(type_: IfdTagType) -> Option<u32> {
match type_ {
IfdTagType::Long => Some(4),
_ => None,
}
}
fn read_primitive<R: Read + Seek>(_: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
Ok(file.read_u32()?)
}
}
impl PrimitiveType for TypeRational {
type Output = Rational<u32>;
fn get_size(type_: IfdTagType) -> Option<u32> {
match 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)?;
Ok(Rational { numerator, denominator })
}
}
impl PrimitiveType for TypeSByte {
type Output = i8;
fn get_size(type_: IfdTagType) -> Option<u32> {
match type_ {
IfdTagType::SByte => Some(1),
_ => None,
}
}
fn read_primitive<R: Read + Seek>(_: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
Ok(file.read_i8()?)
}
}
impl PrimitiveType for TypeSShort {
type Output = i16;
fn get_size(type_: IfdTagType) -> Option<u32> {
match type_ {
IfdTagType::SShort => Some(2),
_ => None,
}
}
fn read_primitive<R: Read + Seek>(_: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
Ok(file.read_i16()?)
}
}
impl PrimitiveType for TypeSLong {
type Output = i32;
fn get_size(type_: IfdTagType) -> Option<u32> {
match type_ {
IfdTagType::SLong => Some(4),
_ => None,
}
}
fn read_primitive<R: Read + Seek>(_: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
Ok(file.read_i32()?)
}
}
impl PrimitiveType for TypeSRational {
type Output = Rational<i32>;
fn get_size(type_: IfdTagType) -> Option<u32> {
match 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)?;
Ok(Rational { numerator, denominator })
}
}
impl PrimitiveType for TypeFloat {
type Output = f32;
fn get_size(type_: IfdTagType) -> Option<u32> {
match type_ {
IfdTagType::Float => Some(4),
_ => None,
}
}
fn read_primitive<R: Read + Seek>(_: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
Ok(file.read_f32()?)
}
}
impl PrimitiveType for TypeDouble {
type Output = f64;
fn get_size(type_: IfdTagType) -> Option<u32> {
match type_ {
IfdTagType::Double => Some(8),
_ => None,
}
}
fn read_primitive<R: Read + Seek>(_: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
Ok(file.read_f64()?)
}
}
impl PrimitiveType for TypeUndefined {
type Output = ();
fn get_size(_: IfdTagType) -> Option<u32> {
todo!()
}
fn read_primitive<R: Read + Seek>(_: IfdTagType, _: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
todo!()
}
}
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_),
_ => 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)?,
_ => unreachable!(),
})
}
}
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_),
_ => 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)?,
_ => unreachable!(),
})
}
}
impl PrimitiveType for TypeIfd {
type Output = Ifd;
fn get_size(type_: IfdTagType) -> Option<u32> {
match type_ {
IfdTagType::Long => Some(4),
_ => None,
}
}
fn read_primitive<R: Read + Seek>(type_: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
let offset = TypeLong::read_primitive(type_, file)?;
Ok(Ifd::new_from_offset(file, offset)?)
}
}
pub trait TagType {
type Output;
fn read<R: Read + Seek>(file: &mut TiffRead<R>) -> Result<Self::Output, TiffError>;
}
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 count = file.read_u32()?;
if count != 1 {
return Err(TiffError::InvalidCount);
}
let size = T::get_size(type_).ok_or(TiffError::InvalidType)?;
if count * size > 4 {
let offset = file.read_u32()?;
file.seek_from_start(offset)?;
}
T::read_primitive(type_, file)
}
}
pub struct Array<T: PrimitiveType> {
primitive_type: std::marker::PhantomData<T>,
}
pub struct ConstArray<T: PrimitiveType, const N: usize> {
primitive_type: std::marker::PhantomData<T>,
}
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 count = file.read_u32()?;
let size = T::get_size(type_).ok_or(TiffError::InvalidType)?;
if count * size > 4 {
let offset = file.read_u32()?;
file.seek_from_start(offset)?;
}
let mut ans = Vec::with_capacity(count.try_into()?);
for _ in 0..count {
ans.push(T::read_primitive(type_, file)?);
}
Ok(ans)
}
}
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 count = file.read_u32()?;
if count != N.try_into()? {
return Err(TiffError::InvalidCount);
}
let size = T::get_size(type_).ok_or(TiffError::InvalidType)?;
if count * size > 4 {
let offset = file.read_u32()?;
file.seek_from_start(offset)?;
}
let mut ans = Vec::with_capacity(count.try_into()?);
for _ in 0..count {
ans.push(T::read_primitive(type_, file)?);
}
ans.try_into().map_err(|_| TiffError::InvalidCount)
}
}
+4
View File
@@ -0,0 +1,4 @@
pub struct Rational<T> {
pub numerator: T,
pub denominator: T,
}