diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs index f0a197e..fbfa9ba 100644 --- a/src/encoder/mod.rs +++ b/src/encoder/mod.rs @@ -1,130 +1,110 @@ +use std::io::{self, Write, Seek}; +use std::collections::{BTreeMap, HashMap}; +use byteorder::{ByteOrder, WriteBytesExt, NativeEndian}; + +use crate::decoder::ifd; +use crate::error::TiffResult; + 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; +pub trait TiffValue { + const BYTE_LEN: u32; + const FIELD_TYPE: u16; fn count(&self) -> u32; - fn write(&self, writer: &mut TiffWriter) -> Result<(), io::Error>; + fn bytes(&self) -> u32 { + self.count() * Self::BYTE_LEN + } + fn write(&self, writer: &mut TiffWriter) -> TiffResult<()>; } -impl IfdType for [u8] { - type Inner = u8; - fn byte_len() -> u32 { +impl TiffValue for &[T] { + const BYTE_LEN: u32 = T::BYTE_LEN; + const FIELD_TYPE: u16 = T::FIELD_TYPE; + + fn count(&self) -> u32 { + self.iter().map(|x| x.count()).sum() + } + + fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { + for x in *self { + x.write(writer)?; + } + Ok(()) + } +} + +impl TiffValue for u8 { + const BYTE_LEN: u32 = 1; + const FIELD_TYPE: u16 = 1; + + fn count(&self) -> u32 { 1 } - fn field_type() -> u16 { + + fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { + writer.write_u8(*self)?; + Ok(()) + } +} + +impl TiffValue for u16 { + const BYTE_LEN: u32 = 2; + const FIELD_TYPE: u16 = 3; + + fn count(&self) -> u32 { 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)?; - } + + fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { + writer.write_u16(*self)?; 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 TiffValue for u32 { + const BYTE_LEN: u32 = 4; + const FIELD_TYPE: u16 = 4; -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)?; - } + + fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { + writer.write_u32(*self)?; Ok(()) } } -impl IfdType for str { - type Inner = u8; - fn byte_len() -> u32 { +impl TiffValue for Rational { + const BYTE_LEN: u32 = 8; + const FIELD_TYPE: u16 = 5; + + fn count(&self) -> u32 { 1 } - fn field_type() -> u16 { - 2 + + fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { + writer.write_u32(self.n)?; + writer.write_u32(self.d)?; + Ok(()) } +} + +impl TiffValue for str { + const BYTE_LEN: u32 = 1; + const FIELD_TYPE: u16 = 2; + fn count(&self) -> u32 { - self.len() as u32 + self.len() as u32 + 1 } - fn write(&self, writer: &mut TiffWriter) -> Result<(), io::Error> { + + fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { for x in self.chars() { if x.is_ascii() { writer.write_u8(x as u8)?; @@ -135,130 +115,229 @@ impl IfdType for str { } } + +pub trait ColorType { + type Inner: TiffValue; + const TIFF_VALUE: u8; + fn bits_per_sample() -> Vec; +} + +pub struct RGB8; +impl ColorType for RGB8 { + type Inner = u8; + const TIFF_VALUE: u8 = 2; + fn bits_per_sample() -> Vec { + vec![8,8,8] + } +} + +pub struct RGBA8; +impl ColorType for RGBA8 { + type Inner = u8; + const TIFF_VALUE: u8 = 2; + fn bits_per_sample() -> Vec { + vec![8,8,8,8] + } +} + + pub struct TiffEncoder { writer: TiffWriter, - // Sort tags by ascending order - ifd: BTreeMap)>, - ifd_offsets: HashMap, - strip_number: u32, + offset: usize, } 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)?, + pub fn new(writer: W) -> TiffResult> { + let mut encoder = TiffEncoder { + writer: TiffWriter::new(writer), + offset: 0, }; - self.writer.write_u16(42)?; + + NativeEndian::write_header(&mut encoder.writer)?; - Ok(()) + Ok(encoder) } - 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_directory(&mut self) -> TiffResult> { + DirectoryEncoder::new(&mut self.writer) } - pub fn new_ifd_entry(&mut self, tag: ifd::Tag, value: &T) { - let mut bytes: Vec = Vec::new(); + pub fn new_image(&mut self, width: u32, height: u32) -> TiffResult> { + let encoder = DirectoryEncoder::new(&mut self.writer)?; + ImageEncoder::new(encoder, width, height) + } +} + +pub struct DirectoryEncoder<'a, W> { + writer: &'a mut TiffWriter, + dropped: bool, + // We use BTreeMap to make sure tags are written in correct order + ifd_pointer_pos: u64, + ifd: BTreeMap)>, +} + +impl<'a, W: Write + Seek> DirectoryEncoder<'a, W> { + fn new(writer: &'a mut TiffWriter) -> TiffResult> { + writer.pad_word_boundary()?; + let ifd_pointer_pos = writer.offset(); + writer.write_u32(0)?; + Ok(DirectoryEncoder { + writer, + dropped: false, + ifd_pointer_pos, + ifd: BTreeMap::new(), + }) + } + + pub fn write_tag(&mut self, tag: ifd::Tag, value: T) { + let len = ::BYTE_LEN * value.count(); + let mut bytes = Vec::with_capacity(len as usize); { - let mut writer = TiffWriter::new(&mut bytes, self.writer.byte_order()); + let mut writer = TiffWriter::new(&mut bytes); value.write(&mut writer).unwrap(); } - self.ifd.insert(tag.to_u16(), (::field_type(), value.count(), bytes)); + + 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(); + pub fn modify_tag(&mut self, tag: ifd::Tag, offset: u64, value: T) { + // TODO: handle errors + let bytes = &mut self.ifd.get_mut(&tag.to_u16()).unwrap().2; + let mut writer = TiffWriter::new(std::io::Cursor::new(bytes)); + + writer.goto_offset(offset).unwrap(); + value.write(&mut writer).unwrap(); + } + + fn write_directory(&mut self) -> TiffResult { + // Start by writing out all values + for (_, _, ref mut bytes) in self.ifd.values_mut() { + if bytes.len() > 4 { + let offset = self.writer.offset(); + self.writer.write_bytes(bytes)?; + *bytes = vec![0, 0, 0, 0]; + let mut writer = TiffWriter::new(bytes as &mut [u8]); + writer.write_u32(offset as u32)?; + } + else { + while bytes.len() < 4 { + bytes.push(0); + } + } + } + + let offset = self.writer.offset(); + 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)?; - } + for (tag, (field_type, count, offset)) in self.ifd.iter() { + self.writer.write_u16(*tag)?; + self.writer.write_u16(*field_type)?; + self.writer.write_u32(*count)?; + self.writer.write_bytes(offset)?; } - self.ifd = BTreeMap::new(); - - Ok(()) + Ok(offset) } - 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> { + pub fn write_data(&mut self, value: T) -> TiffResult { let offset = self.writer.offset(); - self.writer.write_bytes(strip_value)?; + value.write(&mut self.writer); + Ok(offset) + } - let n = self.strip_number; + pub fn finish(mut self) -> TiffResult<()> { + let ifd_pointer = self.write_directory()?; + let curr_pos = self.writer.offset(); - self.update_ifd_value::<[u32]>(ifd::Tag::StripOffsets, n, &[offset])?; - self.update_ifd_value::<[u32]>(ifd::Tag::StripByteCounts, n, &[strip_value.len() as u32])?; + self.writer.goto_offset(self.ifd_pointer_pos)?; + self.writer.write_u32(ifd_pointer as u32)?; + self.writer.goto_offset(curr_pos)?; + self.writer.write_u32(0)?; + + self.dropped = true; Ok(()) } } -pub struct IfdEncoder<'a, W> { - tiff_encoder: &'a mut TiffEncoder, +impl<'a, W> Drop for DirectoryEncoder<'a, W> { + fn drop(&mut self) { + if !self.dropped { + panic!("Illegal to drop DirectoryEncoder: you should call `DirectoryEncoder::finish`") + } + } } -pub struct ImageEncoder<'a, W> { - tiff_encoder: &'a mut TiffEncoder, +pub struct ImageEncoder<'a, W, T> { + encoder: DirectoryEncoder<'a, W>, + strip_idx: u64, + strip_count: u64, + row_samples: u64, + height: u32, + rows_per_strip: u64, + _phantom: std::marker::PhantomData, +} + +impl<'a, W: Write + Seek, T: ColorType> ImageEncoder<'a, W, T> { + fn new(mut encoder: DirectoryEncoder<'a, W>, width: u32, height: u32) -> TiffResult> { + use self::ifd::Tag; + let row_samples = width as u64 * ::bits_per_sample().len() as u64; + let row_bytes = row_samples * ::BYTE_LEN as u64; + + // As per tiff spec each strip should be about 8k long + let rows_per_strip = (8000 + row_bytes - 1) / row_bytes; + + let strip_count = (height as u64 + rows_per_strip - 1) / rows_per_strip; + + encoder.write_tag(Tag::ImageWidth, width); + encoder.write_tag(Tag::ImageLength, height); + encoder.write_tag(Tag::Compression, 1u16); + + encoder.write_tag(Tag::BitsPerSample, &::bits_per_sample() as &[u16]); + encoder.write_tag(Tag::PhotometricInterpretation, ::TIFF_VALUE); + + encoder.write_tag(Tag::StripOffsets, &vec![0u32; strip_count as usize] as &[u32]); + encoder.write_tag(Tag::StripByteCounts, &vec![0u32; strip_count as usize] as &[u32]); + encoder.write_tag(Tag::RowsPerStrip, rows_per_strip as u32); + + encoder.write_tag(Tag::SamplesPerPixel, ::bits_per_sample().len() as u16); + encoder.write_tag(Tag::XResolution, Rational {n: 1, d: 1}); + encoder.write_tag(Tag::YResolution, Rational {n: 1, d: 1}); + encoder.write_tag(Tag::ResolutionUnit, 1u16); + + Ok(ImageEncoder { + encoder, + strip_count, + strip_idx: 0, + row_samples, + rows_per_strip, + height, + _phantom: std::marker::PhantomData, + }) + } + + pub fn next_strip_sample_count(&self) -> u64 { + if self.strip_idx >= self.strip_count { + return 0 + } + + let start_row = std::cmp::min(self.height as u64, self.strip_idx * self.rows_per_strip); + let end_row = std::cmp::min(self.height as u64, (self.strip_idx+1)*self.rows_per_strip); + + (end_row - start_row) * self.row_samples + } + + pub fn write_strip(&mut self, value: &[T::Inner]) -> TiffResult<()> { + // TODO: Compression + let offset = self.encoder.write_data(value)?; + self.encoder.modify_tag(ifd::Tag::StripOffsets, self.strip_idx as u64 * 4, offset as u32); + self.encoder.modify_tag(ifd::Tag::StripByteCounts, self.strip_idx as u64 * 4, value.bytes() as u32); + + self.strip_idx += 1; + Ok(()) + } + + pub fn finish(mut self) -> TiffResult<()> { + self.encoder.finish() + } } diff --git a/src/encoder/writer.rs b/src/encoder/writer.rs index 1e4ac10..6739e32 100644 --- a/src/encoder/writer.rs +++ b/src/encoder/writer.rs @@ -1,48 +1,51 @@ use std::io::{self, Write, Seek, SeekFrom}; -use byteorder::{WriteBytesExt, BigEndian, LittleEndian}; +use byteorder::{ByteOrder, WriteBytesExt, BigEndian, LittleEndian, NativeEndian}; +use crate::error::TiffResult; -/// Byte order of the TIFF file. -#[derive(Clone, Copy, Debug)] -pub enum ByteOrder { - /// little endian byte order - LittleEndian, - /// big endian byte order - BigEndian +pub trait TiffByteOrder: ByteOrder { + fn write_header(writer: &mut TiffWriter) -> TiffResult<()>; +} + +impl TiffByteOrder for LittleEndian { + fn write_header(writer: &mut TiffWriter) -> TiffResult<()> { + writer.writer.write_u16::(0x4949)?; + writer.writer.write_u16::(42)?; + writer.offset += 4; + + Ok(()) + } +} + +impl TiffByteOrder for BigEndian { + fn write_header(writer: &mut TiffWriter) -> TiffResult<()> { + writer.writer.write_u16::(0x4d4d)?; + writer.writer.write_u16::(42)?; + writer.offset += 4; + + Ok(()) + } } pub struct TiffWriter { writer: W, - byte_order: ByteOrder, - offset: u32, + offset: u64, } impl TiffWriter { - pub fn new(writer: W, byte_order: ByteOrder) -> Self { + pub fn new(writer: W) -> 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 { + pub fn offset(&self) -> u64 { self.offset } pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), io::Error> { self.writer.write_all(bytes)?; - self.offset += bytes.len() as u32; + self.offset += bytes.len() as u64; Ok(()) } @@ -53,27 +56,26 @@ impl TiffWriter { } 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.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.writer.write_u32::(n)?; self.offset += 4; Ok(()) } + pub fn write_u64(&mut self, n: u64) -> Result<(), io::Error> { + self.writer.write_u64::(n)?; + self.offset += 8; + + Ok(()) + } + pub fn pad_word_boundary(&mut self) -> Result<(), io::Error> { while self.offset % 4 != 0 { self.writer.write_u8(0)?; @@ -84,7 +86,7 @@ impl TiffWriter { } impl TiffWriter { - pub fn too_offset(&mut self, offset: u32) -> Result<(), io::Error> { + pub fn goto_offset(&mut self, offset: u64) -> Result<(), io::Error> { self.offset = offset; self.writer.seek(SeekFrom::Start(offset as u64))?; Ok(()) diff --git a/tests/encode_images.rs b/tests/encode_images.rs index 92383c7..2dbbf29 100644 --- a/tests/encode_images.rs +++ b/tests/encode_images.rs @@ -2,7 +2,7 @@ extern crate tiff; use tiff::ColorType; use tiff::decoder::{Decoder, DecodingResult, ifd::Tag, ifd::Value}; -use tiff::encoder::{TiffEncoder, Rational}; +use tiff::encoder::{TiffEncoder, RGB8}; use std::fs::File; @@ -20,26 +20,17 @@ fn encode_decode() { } { let file = std::fs::File::create("test.tiff").unwrap(); - let mut tiff = TiffEncoder::new_le(file); + let mut tiff = TiffEncoder::new(file).unwrap(); - tiff.write_header().unwrap(); - - tiff.new_ifd().unwrap(); - tiff.new_ifd_entry(Tag::ImageWidth, &[100u32][..]); - tiff.new_ifd_entry(Tag::ImageLength, &[100u32][..]); - tiff.new_ifd_entry(Tag::BitsPerSample, &[8u16,8,8][..]); - tiff.new_ifd_entry(Tag::Compression, &[1u16][..]); - tiff.new_ifd_entry(Tag::PhotometricInterpretation, &[2u16][..]); - tiff.new_ifd_entry(Tag::StripOffsets, &[0u32][..]); - tiff.new_ifd_entry(Tag::SamplesPerPixel, &[3u16][..]); - tiff.new_ifd_entry(Tag::RowsPerStrip, &[100u32][..]); - tiff.new_ifd_entry(Tag::StripByteCounts, &[0u32][..]); - tiff.new_ifd_entry(Tag::XResolution, &[Rational {n: 1, d: 1}][..]); - tiff.new_ifd_entry(Tag::YResolution, &[Rational {n: 1, d: 1}][..]); - tiff.new_ifd_entry(Tag::ResolutionUnit, &[1u16][..]); - tiff.finish_ifd().unwrap(); - - tiff.write_strip(&image_data).unwrap(); + let mut image = tiff.new_image::(100, 100).unwrap(); + + let mut idx = 0; + while image.next_strip_sample_count() > 0 { + let sample_count = image.next_strip_sample_count() as usize; + image.write_strip(&image_data[idx..idx+sample_count]).unwrap(); + idx += sample_count; + } + image.finish().unwrap(); } { let file = File::open("test.tiff").unwrap();