Implemented basic tiff encoding
This commit is contained in:
1 parent
80be60dfd4
commit
fb30648c12
4 files changed
+363
No files matched your search
@@ -26,6 +26,12 @@ macro_rules! tags {
|
|||||||
Tag::Unknown(n)
|
Tag::Unknown(n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn to_u16(&self) -> u16 {
|
||||||
|
match self {
|
||||||
|
$( Tag::$tag => $val, )*
|
||||||
|
Tag::Unknown(n) => *n,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<W: Write>(&self, writer: &mut TiffWriter<W>) -> 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<W: Write>(&self, writer: &mut TiffWriter<W>) -> 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<W: Write>(&self, writer: &mut TiffWriter<W>) -> 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<W: Write>(&self, writer: &mut TiffWriter<W>) -> 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<W: Write>(&self, writer: &mut TiffWriter<W>) -> 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<W: Write>(&self, writer: &mut TiffWriter<W>) -> 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<W: Write>(&self, writer: &mut TiffWriter<W>) -> 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<W> {
|
||||||
|
writer: TiffWriter<W>,
|
||||||
|
// Sort tags by ascending order
|
||||||
|
ifd: BTreeMap<u16, (u16, u32, Vec<u8>)>,
|
||||||
|
ifd_offsets: HashMap<u16, u32>,
|
||||||
|
strip_number: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write + Seek> TiffEncoder<W> {
|
||||||
|
pub fn new_le(writer: W) -> TiffEncoder<W> {
|
||||||
|
TiffEncoder {
|
||||||
|
writer: TiffWriter::new_le(writer),
|
||||||
|
ifd: BTreeMap::new(),
|
||||||
|
ifd_offsets: HashMap::new(),
|
||||||
|
strip_number: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_be(writer: W) -> TiffEncoder<W> {
|
||||||
|
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<T: IfdType + ?Sized>(&mut self, tag: ifd::Tag, value: &T) {
|
||||||
|
let mut bytes: Vec<u8> = Vec::new();
|
||||||
|
{
|
||||||
|
let mut writer = TiffWriter::new(&mut bytes, self.writer.byte_order());
|
||||||
|
value.write(&mut writer).unwrap();
|
||||||
|
}
|
||||||
|
self.ifd.insert(tag.to_u16(), (<T>::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<T: IfdType + ?Sized>(&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 * <T>::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<W>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ImageEncoder<'a, W> {
|
||||||
|
tiff_encoder: &'a mut TiffEncoder<W>,
|
||||||
|
}
|
||||||
@@ -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<W> {
|
||||||
|
writer: W,
|
||||||
|
byte_order: ByteOrder,
|
||||||
|
offset: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> TiffWriter<W> {
|
||||||
|
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::<LittleEndian>(n),
|
||||||
|
ByteOrder::BigEndian => self.writer.write_u16::<BigEndian>(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::<LittleEndian>(n),
|
||||||
|
ByteOrder::BigEndian => self.writer.write_u32::<BigEndian>(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<W: Seek> TiffWriter<W> {
|
||||||
|
pub fn too_offset(&mut self, offset: u32) -> Result<(), io::Error> {
|
||||||
|
self.offset = offset;
|
||||||
|
self.writer.seek(SeekFrom::Start(offset as u64))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ extern crate lzw;
|
|||||||
extern crate num_traits;
|
extern crate num_traits;
|
||||||
|
|
||||||
pub mod decoder;
|
pub mod decoder;
|
||||||
|
pub mod encoder;
|
||||||
mod error;
|
mod error;
|
||||||
|
|
||||||
pub use self::error::{TiffError, TiffFormatError, TiffUnsupportedError, TiffResult};
|
pub use self::error::{TiffError, TiffFormatError, TiffUnsupportedError, TiffResult};
|
||||||
|
|||||||
Reference in new issue
Block a user