From fb30648c12a774e9a4603a77ab83b38c4a5f0878 Mon Sep 17 00:00:00 2001 From: Birk Tjelmeland Date: Thu, 31 Jan 2019 21:20:49 +0100 Subject: [PATCH] Implemented basic tiff encoding --- src/decoder/ifd.rs | 6 + src/encoder/mod.rs | 264 ++++++++++++++++++++++++++++++++++++++++++ src/encoder/writer.rs | 92 +++++++++++++++ src/lib.rs | 1 + 4 files changed, 363 insertions(+) create mode 100644 src/encoder/mod.rs create mode 100644 src/encoder/writer.rs diff --git a/src/decoder/ifd.rs b/src/decoder/ifd.rs index 3679e51..515006e 100644 --- a/src/decoder/ifd.rs +++ b/src/decoder/ifd.rs @@ -26,6 +26,12 @@ macro_rules! tags { Tag::Unknown(n) } } + pub fn to_u16(&self) -> u16 { + match self { + $( Tag::$tag => $val, )* + Tag::Unknown(n) => *n, + } + } } } } diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs new file mode 100644 index 0000000..f0a197e --- /dev/null +++ b/src/encoder/mod.rs @@ -0,0 +1,264 @@ +mod writer; +use self::writer::*; +use std::io::{self, Write, Seek}; +use crate::decoder::ifd; +use std::collections::{BTreeMap, HashMap}; + +pub struct Rational { + pub n: u32, + pub d: u32, +} + +pub trait IfdType { + type Inner; + fn byte_len() -> u32; + fn field_type() -> u16; + fn count(&self) -> u32; + fn write(&self, writer: &mut TiffWriter) -> Result<(), io::Error>; +} + +impl IfdType for [u8] { + type Inner = u8; + fn byte_len() -> u32 { + 1 + } + fn field_type() -> u16 { + 1 + } + fn count(&self) -> u32 { + self.len() as u32 + } + fn write(&self, writer: &mut TiffWriter) -> Result<(), io::Error> { + for x in self { + writer.write_u8(*x)?; + } + Ok(()) + } +} + +impl IfdType for [u16] { + type Inner = u16; + fn byte_len() -> u32 { + 2 + } + fn field_type() -> u16 { + 3 + } + fn count(&self) -> u32 { + self.len() as u32 + } + fn write(&self, writer: &mut TiffWriter) -> Result<(), io::Error> { + for x in self { + writer.write_u16(*x)?; + } + Ok(()) + } +} + +impl IfdType for [u32] { + type Inner = u32; + fn byte_len() -> u32 { + 4 + } + fn field_type() -> u16 { + 4 + } + fn count(&self) -> u32 { + self.len() as u32 + } + fn write(&self, writer: &mut TiffWriter) -> Result<(), io::Error> { + for x in self { + writer.write_u32(*x)?; + } + Ok(()) + } +} + +impl IfdType for [Rational] { + type Inner = Rational; + fn byte_len() -> u32 { + 8 + } + fn field_type() -> u16 { + 5 + } + fn count(&self) -> u32 { + self.len() as u32 + } + fn write(&self, writer: &mut TiffWriter) -> Result<(), io::Error> { + for x in self { + writer.write_u32(x.n)?; + writer.write_u32(x.d)?; + } + Ok(()) + } +} + +impl IfdType for [i8] { + type Inner = i8; + fn byte_len() -> u32 { + 1 + } + fn field_type() -> u16 { + 6 + } + fn count(&self) -> u32 { + self.len() as u32 + } + fn write(&self, writer: &mut TiffWriter) -> Result<(), io::Error> { + for x in self { + writer.write_u8(*x as u8)?; + } + Ok(()) + } +} + +impl IfdType for str { + type Inner = u8; + fn byte_len() -> u32 { + 1 + } + fn field_type() -> u16 { + 2 + } + fn count(&self) -> u32 { + self.len() as u32 + } + fn write(&self, writer: &mut TiffWriter) -> Result<(), io::Error> { + for x in self.chars() { + if x.is_ascii() { + writer.write_u8(x as u8)?; + } + } + writer.write_u8(0)?; + Ok(()) + } +} + +pub struct TiffEncoder { + writer: TiffWriter, + // Sort tags by ascending order + ifd: BTreeMap)>, + ifd_offsets: HashMap, + strip_number: u32, +} + +impl TiffEncoder { + pub fn new_le(writer: W) -> TiffEncoder { + TiffEncoder { + writer: TiffWriter::new_le(writer), + ifd: BTreeMap::new(), + ifd_offsets: HashMap::new(), + strip_number: 0, + } + } + + pub fn new_be(writer: W) -> TiffEncoder { + TiffEncoder { + writer: TiffWriter::new_be(writer), + ifd: BTreeMap::new(), + ifd_offsets: HashMap::new(), + strip_number: 0, + } + } + + pub fn write_header(&mut self) -> Result<(), io::Error> { + match self.writer.byte_order() { + ByteOrder::LittleEndian => self.writer.write_u16(0x4949)?, + ByteOrder::BigEndian => self.writer.write_u16(0x4d4d)?, + }; + self.writer.write_u16(42)?; + + Ok(()) + } + + pub fn new_ifd(&mut self) -> Result<(), io::Error> { + self.writer.pad_word_boundary()?; + let offset = self.writer.offset(); + self.writer.write_u32(offset + 4)?; + + self.ifd = BTreeMap::new(); + + Ok(()) + } + + pub fn new_ifd_entry(&mut self, tag: ifd::Tag, value: &T) { + let mut bytes: Vec = Vec::new(); + { + let mut writer = TiffWriter::new(&mut bytes, self.writer.byte_order()); + value.write(&mut writer).unwrap(); + } + self.ifd.insert(tag.to_u16(), (::field_type(), value.count(), bytes)); + } + + pub fn finish_ifd(&mut self) -> Result<(), io::Error> { + self.ifd_offsets = HashMap::new(); + self.writer.write_u16(self.ifd.len() as u16)?; + + let mut value_offset = self.writer.offset() + self.ifd.len() as u32 * 12 + 4; + + { + let mut ifd_values = Vec::new(); + + for (tag, (type_, count, bytes)) in &self.ifd { + self.writer.write_u16(*tag)?; + self.writer.write_u16(*type_)?; + self.writer.write_u32(*count)?; + if bytes.len() <= 4 { + self.ifd_offsets.insert(*tag, self.writer.offset()); + self.writer.write_bytes(bytes)?; + for _ in 0..4 - bytes.len() { + self.writer.write_u8(0)?; + } + } + else { + self.ifd_offsets.insert(*tag, value_offset); + self.writer.write_u32(value_offset)?; + ifd_values.push(bytes); + value_offset += bytes.len() as u32; + } + } + + self.writer.write_u32(0)?; + + for value in ifd_values { + self.writer.write_bytes(value)?; + } + } + + self.ifd = BTreeMap::new(); + + Ok(()) + } + + pub fn update_ifd_value(&mut self, tag: ifd::Tag, idx: u32, value: &T) -> Result<(), io::Error> { + if let Some(off) = self.ifd_offsets.get(&tag.to_u16()) { + let curr_offset = self.writer.offset(); + self.writer.too_offset(off + idx * ::byte_len())?; + value.write(&mut self.writer)?; + self.writer.too_offset(curr_offset)?; + } + + Ok(()) + } + + pub fn write_strip(&mut self, strip_value: &[u8]) -> Result<(), io::Error> { + let offset = self.writer.offset(); + self.writer.write_bytes(strip_value)?; + + let n = self.strip_number; + + self.update_ifd_value::<[u32]>(ifd::Tag::StripOffsets, n, &[offset])?; + self.update_ifd_value::<[u32]>(ifd::Tag::StripByteCounts, n, &[strip_value.len() as u32])?; + + Ok(()) + } +} + +pub struct IfdEncoder<'a, W> { + tiff_encoder: &'a mut TiffEncoder, +} + +pub struct ImageEncoder<'a, W> { + tiff_encoder: &'a mut TiffEncoder, +} diff --git a/src/encoder/writer.rs b/src/encoder/writer.rs new file mode 100644 index 0000000..1e4ac10 --- /dev/null +++ b/src/encoder/writer.rs @@ -0,0 +1,92 @@ +use std::io::{self, Write, Seek, SeekFrom}; +use byteorder::{WriteBytesExt, BigEndian, LittleEndian}; + +/// Byte order of the TIFF file. +#[derive(Clone, Copy, Debug)] +pub enum ByteOrder { + /// little endian byte order + LittleEndian, + /// big endian byte order + BigEndian +} + +pub struct TiffWriter { + writer: W, + byte_order: ByteOrder, + offset: u32, +} + +impl TiffWriter { + pub fn new(writer: W, byte_order: ByteOrder) -> Self { + Self { + writer, + byte_order, + offset: 0, + } + } + pub fn new_le(writer: W) -> Self { + Self::new(writer, ByteOrder::LittleEndian) + } + + pub fn new_be(writer: W) -> Self { + Self::new(writer, ByteOrder::BigEndian) + } + + pub fn byte_order(&self) -> ByteOrder { + self.byte_order + } + + pub fn offset(&self) -> u32 { + self.offset + } + + pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), io::Error> { + self.writer.write_all(bytes)?; + self.offset += bytes.len() as u32; + Ok(()) + } + + pub fn write_u8(&mut self, n: u8) -> Result<(), io::Error> { + self.writer.write_u8(n)?; + self.offset += 1; + Ok(()) + } + + pub fn write_u16(&mut self, n: u16) -> Result<(), io::Error> { + match self.byte_order { + ByteOrder::LittleEndian => self.writer.write_u16::(n), + ByteOrder::BigEndian => self.writer.write_u16::(n), + }?; + + self.offset += 2; + + Ok(()) + } + + pub fn write_u32(&mut self, n: u32) -> Result<(), io::Error> { + match self.byte_order { + ByteOrder::LittleEndian => self.writer.write_u32::(n), + ByteOrder::BigEndian => self.writer.write_u32::(n), + }?; + + self.offset += 4; + + Ok(()) + } + + pub fn pad_word_boundary(&mut self) -> Result<(), io::Error> { + while self.offset % 4 != 0 { + self.writer.write_u8(0)?; + } + + Ok(()) + } +} + +impl TiffWriter { + pub fn too_offset(&mut self, offset: u32) -> Result<(), io::Error> { + self.offset = offset; + self.writer.seek(SeekFrom::Start(offset as u64))?; + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 2722a90..bf4a1bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ extern crate lzw; extern crate num_traits; pub mod decoder; +pub mod encoder; mod error; pub use self::error::{TiffError, TiffFormatError, TiffUnsupportedError, TiffResult};