From 828d278da28ed91bed5acc35f4072fe38eca74c9 Mon Sep 17 00:00:00 2001 From: Etienne Prothon Date: Thu, 16 Jul 2020 11:50:48 +0200 Subject: [PATCH 1/2] WIP: add encoding with compression and additional tags --- Cargo.toml | 2 +- src/encoder/mod.rs | 47 ++++++++++++++++++++++++++++++++++-------- src/encoder/writer.rs | 2 +- tests/encode_images.rs | 16 +++++++------- 4 files changed, 48 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ee785344..c77dd766 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,5 +18,5 @@ exclude = ["tests/images/*", "tests/fuzz_images/*"] [dependencies] byteorder = "1.2" -lzw = "0.10" +lzw = { git = "https://github.com/Rogach/lzw" } miniz_oxide = "0.3" diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs index 22e39d33..17c77a0e 100644 --- a/src/encoder/mod.rs +++ b/src/encoder/mod.rs @@ -1,10 +1,11 @@ use byteorder::NativeEndian; use std::collections::BTreeMap; use std::convert::TryFrom; -use std::io::{Seek, Write}; +use std::io::{Cursor, Seek, Write}; use std::mem; +use lzw::{EncoderTIFF, MsbWriter}; -use tags::{self, ResolutionUnit, Tag, Type}; +use tags::{self, ResolutionUnit, Tag, Type, CompressionMethod}; use error::{TiffError, TiffFormatError, TiffResult}; pub mod colortype; @@ -388,21 +389,25 @@ impl TiffEncoder { } /// Create an 'ImageEncoder' to encode an image one slice at a time. - pub fn new_image( + pub fn new_image( &mut self, width: u32, height: u32, + compression_method: CompressionMethod, + additional_tags: Vec<(Tag, Box)> ) -> TiffResult> { let encoder = DirectoryEncoder::new(&mut self.writer)?; - ImageEncoder::new(encoder, width, height) + ImageEncoder::new(encoder, width, height, compression_method, additional_tags) } /// Convenience function to write an entire image from memory. - pub fn write_image( + pub fn write_image( &mut self, width: u32, height: u32, data: &[C::Inner], + compression: CompressionMethod, + additional_tags: Vec<(Tag, Box)> ) -> TiffResult<()> where [C::Inner]: TiffValue, @@ -418,7 +423,7 @@ impl TiffEncoder { } let encoder = DirectoryEncoder::new(&mut self.writer)?; - let mut image: ImageEncoder = ImageEncoder::new(encoder, width, height)?; + let mut image: ImageEncoder = ImageEncoder::new(encoder, width, height, compression, additional_tags)?; let mut idx = 0; while image.next_strip_sample_count() > 0 { @@ -571,14 +576,17 @@ pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType> { strip_offsets: Vec, strip_byte_count: Vec, dropped: bool, + compression_method: CompressionMethod, _phantom: ::std::marker::PhantomData, } impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> { - fn new( + fn new( mut encoder: DirectoryEncoder<'a, W>, width: u32, height: u32, + compression_method: CompressionMethod, + additional_tags: Vec<(Tag, Box)> ) -> TiffResult> { let row_samples = u64::from(width) * u64::try_from(::BITS_PER_SAMPLE.len())?; let row_bytes = row_samples * u64::from(::BYTE_LEN); @@ -590,7 +598,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> { encoder.write_tag(Tag::ImageWidth, width)?; encoder.write_tag(Tag::ImageLength, height)?; - encoder.write_tag(Tag::Compression, tags::CompressionMethod::None.to_u16())?; + encoder.write_tag(Tag::Compression, compression_method.to_u16())?; encoder.write_tag(Tag::BitsPerSample, ::BITS_PER_SAMPLE)?; encoder.write_tag(Tag::PhotometricInterpretation, ::TIFF_VALUE.to_u16())?; @@ -602,6 +610,10 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> { encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 })?; encoder.write_tag(Tag::ResolutionUnit, ResolutionUnit::None.to_u16())?; + for (t, v) in additional_tags { + encoder.write_tag(t, v.as_ref())?; + } + Ok(ImageEncoder { encoder, strip_count, @@ -612,6 +624,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> { strip_offsets: Vec::new(), strip_byte_count: Vec::new(), dropped: false, + compression_method, _phantom: ::std::marker::PhantomData, }) } @@ -644,7 +657,23 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> { "Slice is wrong size for strip").into()); } - let offset = self.encoder.write_data(value)?; + let offset = match self.compression_method { + CompressionMethod::LZW => { + let mut buf = TiffWriter::new(Cursor::new(Vec::new())); + value.write(&mut buf)?; + let mut compressed = vec![]; + { + let mut lzw_encoder = EncoderTIFF::new(MsbWriter::new(&mut compressed), 8)?; + lzw_encoder.encode_bytes(&buf.writer.into_inner()[..])?; + } + self.encoder.write_data(&compressed[..])? + }, + _ => { + self.encoder.write_data(value)? + } + + }; + self.strip_offsets.push(u32::try_from(offset)?); self.strip_byte_count.push(value.bytes()); diff --git a/src/encoder/writer.rs b/src/encoder/writer.rs index ba4c4c59..590882f6 100644 --- a/src/encoder/writer.rs +++ b/src/encoder/writer.rs @@ -27,7 +27,7 @@ impl TiffByteOrder for BigEndian { } pub struct TiffWriter { - writer: W, + pub writer: W, offset: u64, } diff --git a/tests/encode_images.rs b/tests/encode_images.rs index 9d1ecfdb..c359b949 100644 --- a/tests/encode_images.rs +++ b/tests/encode_images.rs @@ -3,7 +3,7 @@ extern crate tiff; use tiff::decoder::{Decoder, DecodingResult}; use tiff::encoder::{colortype, TiffEncoder, SRational}; use tiff::ColorType; -use tiff::tags::Tag; +use tiff::tags::{Tag, CompressionMethod}; use std::fs::File; use std::io::{Cursor, Seek, SeekFrom}; @@ -24,7 +24,7 @@ fn encode_decode() { { let mut tiff = TiffEncoder::new(&mut file).unwrap(); - tiff.write_image::(100, 100, &image_data) + tiff.write_image::(100, 100, &image_data, CompressionMethod::None, vec![]) .unwrap(); } { @@ -49,7 +49,7 @@ fn test_encode_undersized_buffer() { let output = Vec::new(); let mut output_stream = Cursor::new(output); if let Ok(mut tiff) = TiffEncoder::new(&mut output_stream) { - let res = tiff.write_image::(50, 50, &input_data); + let res = tiff.write_image::(50, 50, &input_data, CompressionMethod::None, vec![]); assert!(res.is_err()); } } @@ -74,7 +74,7 @@ macro_rules! test_roundtrip { let mut tiff = TiffEncoder::new(&mut file).unwrap(); let (width, height) = decoder.dimensions().unwrap(); - tiff.write_image::(width, height, &image_data) + tiff.write_image::(width, height, &image_data, CompressionMethod::None, vec![]) .unwrap(); } file.seek(SeekFrom::Start(0)).unwrap(); @@ -151,7 +151,7 @@ fn test_multiple_byte() { { let mut tiff = TiffEncoder::new(&mut data).unwrap(); - let mut image_encoder = tiff.new_image::(1, 1).unwrap(); + let mut image_encoder = tiff.new_image::(1, 1, CompressionMethod::None, vec![]).unwrap(); let encoder = image_encoder.encoder(); encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]).unwrap(); @@ -180,7 +180,7 @@ fn test_signed() { { let mut tiff = TiffEncoder::new(&mut data).unwrap(); - let mut image_encoder = tiff.new_image::(1, 1).unwrap(); + let mut image_encoder = tiff.new_image::(1, 1, CompressionMethod::None, vec![]).unwrap(); let encoder = image_encoder.encoder(); //Use the "reusable" tags section as per the TIFF6 spec @@ -241,10 +241,10 @@ fn test_multipage_image() { // write first grayscale image (2x2 16-bit) let img1: Vec = [1, 2, 3, 4].to_vec(); - img_encoder.write_image::(2, 2, &img1[..]).unwrap(); + img_encoder.write_image::(2, 2, &img1[..], CompressionMethod::None, vec![]).unwrap(); // write second grayscale image (3x3 8-bit) let img2: Vec = [9, 8, 7, 6, 5, 4, 3, 2, 1].to_vec(); - img_encoder.write_image::(3, 3, &img2[..]).unwrap(); + img_encoder.write_image::(3, 3, &img2[..], CompressionMethod::None, vec![]).unwrap(); } // seek to the beginning of the file, so that it can be decoded From 094422f70d77b6cabe736b5636efb57327e97713 Mon Sep 17 00:00:00 2001 From: Etienne Prothon Date: Wed, 22 Jul 2020 16:06:00 +0200 Subject: [PATCH 2/2] refactor: Use enum to manage TiffValues Use struct for Colortype Add support for f32 and f64 Allow to write multiples tags --- src/decoder/ifd.rs | 94 ++++-- src/decoder/mod.rs | 14 +- src/decoder/stream.rs | 18 ++ src/encoder/colortype.rs | 236 +++++++-------- src/encoder/mod.rs | 628 +++++++++++++++++++-------------------- src/encoder/writer.rs | 15 + src/error.rs | 10 +- src/tags.rs | 2 + tests/encode_images.rs | 97 +++--- 9 files changed, 597 insertions(+), 517 deletions(-) diff --git a/src/decoder/ifd.rs b/src/decoder/ifd.rs index 58965d1e..5bfb0583 100644 --- a/src/decoder/ifd.rs +++ b/src/decoder/ifd.rs @@ -9,14 +9,15 @@ use super::stream::{ByteOrder, EndianReader, SmartReader}; use tags::{Tag, Type}; use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError}; -use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational}; - +use self::Value::{Ascii, List, Rational, SRational, Signed, Unsigned, Float, Double}; #[allow(unused_qualifications)] -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq)] pub enum Value { Signed(i32), Unsigned(u32), + Float(f32), + Double(f64), List(Vec), Rational(u32, u32), SRational(i32, i32), @@ -44,6 +45,24 @@ impl Value { } } + pub fn into_f32(self) -> TiffResult { + match self { + Float(val) => Ok(val), + val => Err(TiffError::FormatError( + TiffFormatError::FloatExpected(val), + )), + } + } + + pub fn into_f64(self) -> TiffResult { + match self { + Double(val) => Ok(val), + val => Err(TiffError::FormatError( + TiffFormatError::DoubleExpected(val), + )), + } + } + pub fn into_u32_vec(self) -> TiffResult> { match self { List(vec) => { @@ -72,7 +91,7 @@ impl Value { new_vec.push(numerator); new_vec.push(denominator); } - _ => new_vec.push(v.into_i32()?) + _ => new_vec.push(v.into_i32()?), } } Ok(new_vec) @@ -84,6 +103,38 @@ impl Value { )), } } + + pub fn into_f32_vec(self) -> TiffResult> { + match self { + List(vec) => { + let mut new_vec = Vec::with_capacity(vec.len()); + for v in vec { + new_vec.push(v.into_f32()?) + } + Ok(new_vec) + }, + Float(val) => Ok(vec![val]), + val => Err(TiffError::FormatError( + TiffFormatError::UnsignedIntegerExpected(val), + )), + } + } + + pub fn into_f64_vec(self) -> TiffResult> { + match self { + List(vec) => { + let mut new_vec = Vec::with_capacity(vec.len()); + for v in vec { + new_vec.push(v.into_f64()?) + } + Ok(new_vec) + } + Double(val) => Ok(vec![val]), + val => Err(TiffError::FormatError( + TiffFormatError::UnsignedIntegerExpected(val), + )), + } + } } #[derive(Clone)] @@ -169,6 +220,12 @@ impl Entry { (Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { Ok(Signed(decoder.read_slong()?)) }), + (Type::FLOAT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { + Ok(Float(decoder.read_float()?)) + }), + (Type::DOUBLE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { + Ok(Double(decoder.read_double()?)) + }), (Type::RATIONAL, 1) => { decoder.goto_offset(self.r(bo).read_u32()?)?; let numerator = decoder.read_long()?; @@ -182,16 +239,10 @@ impl Entry { Ok(SRational(numerator, denominator)) } (Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(Rational( - decoder.read_long()?, - decoder.read_long()?, - )) + Ok(Rational(decoder.read_long()?, decoder.read_long()?)) }), (Type::SRATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { - Ok(SRational( - decoder.read_slong()?, - decoder.read_slong()?, - )) + Ok(SRational(decoder.read_slong()?, decoder.read_slong()?)) }), (Type::ASCII, n) => { let n = usize::try_from(n)?; @@ -209,10 +260,17 @@ impl Entry { } #[inline] - fn decode_offset(&self, value_count: u32, bo: ByteOrder, limits: &super::Limits, decoder: &mut super::Decoder, decode_fn: F) -> TiffResult - where - R: Read + Seek, - F: Fn(&mut super::Decoder) -> TiffResult, + fn decode_offset( + &self, + value_count: u32, + bo: ByteOrder, + limits: &super::Limits, + decoder: &mut super::Decoder, + decode_fn: F, + ) -> TiffResult + where + R: Read + Seek, + F: Fn(&mut super::Decoder) -> TiffResult, { let value_count = usize::try_from(value_count)?; if value_count > limits.decoding_buffer_size / mem::size_of::() { @@ -235,7 +293,7 @@ fn offset_to_bytes(n: usize, entry: &Entry) -> TiffResult { entry.offset[0..n] .iter() .map(|&e| Unsigned(u32::from(e))) - .collect() + .collect(), )) } @@ -246,7 +304,7 @@ fn offset_to_sbytes(n: usize, entry: &Entry) -> TiffResult { entry.offset[0..n] .iter() .map(|&e| Signed(i32::from(e as i8))) - .collect() + .collect(), )) } diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index a2cd6a09..94314f20 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -421,6 +421,18 @@ impl Decoder { self.reader.read_i32() } + /// Reads a TIFF float valu + #[inline] + pub fn read_float(&mut self) -> Result { + self.reader.read_f32() + } + + /// Reads a TIFF double value + #[inline] + pub fn read_double(&mut self) -> Result { + self.reader.read_f64() + } + /// Reads a string #[inline] pub fn read_string(&mut self, length: usize) -> TiffResult { @@ -572,7 +584,7 @@ impl Decoder { ) -> TiffResult { let color_type = self.colortype()?; self.goto_offset(offset)?; - let (bytes, mut reader): (usize, Box) = match self.compression_method { + let (bytes, mut reader): (usize, Box) = match self.compression_method { CompressionMethod::None => { let order = self.reader.byte_order; ( diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index caf4e3ce..4d92ae3e 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -100,6 +100,24 @@ pub trait EndianReader: Read { ByteOrder::BigEndian => ::read_i32::(self), } } + + /// Reads an f32 + #[inline(always)] + fn read_f32(&mut self) -> Result { + match self.byte_order() { + ByteOrder::LittleEndian => ::read_f32::(self), + ByteOrder::BigEndian => ::read_f32::(self), + } + } + + /// Reads an f64 + #[inline(always)] + fn read_f64(&mut self) -> Result { + match self.byte_order() { + ByteOrder::LittleEndian => ::read_f64::(self), + ByteOrder::BigEndian => ::read_f64::(self), + } + } } /// Reader that decompresses DEFLATE streams diff --git a/src/encoder/colortype.rs b/src/encoder/colortype.rs index 2f970668..b262e8a6 100644 --- a/src/encoder/colortype.rs +++ b/src/encoder/colortype.rs @@ -1,123 +1,117 @@ +use super::Value; use tags::PhotometricInterpretation; -/// Trait for different colortypes that can be encoded. -pub trait ColorType { - /// The type of each sample of this colortype - type Inner: super::TiffValue; - /// The value of the tiff tag `PhotometricInterpretation` - const TIFF_VALUE: PhotometricInterpretation; - /// The value of the tiff tag `BitsPerSample` - const BITS_PER_SAMPLE: &'static [u16]; -} - -pub struct Gray8; -impl ColorType for Gray8 { - type Inner = u8; - const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero; - const BITS_PER_SAMPLE: &'static [u16] = &[8]; -} - -pub struct Gray16; -impl ColorType for Gray16 { - type Inner = u16; - const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero; - const BITS_PER_SAMPLE: &'static [u16] = &[16]; -} - -pub struct Gray32; -impl ColorType for Gray32 { - type Inner = u32; - const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero; - const BITS_PER_SAMPLE: &'static [u16] = &[32]; -} - -pub struct Gray64; -impl ColorType for Gray64 { - type Inner = u64; - const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero; - const BITS_PER_SAMPLE: &'static [u16] = &[64]; -} - -pub struct RGB8; -impl ColorType for RGB8 { - type Inner = u8; - const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; - const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8]; -} - -pub struct RGB16; -impl ColorType for RGB16 { - type Inner = u16; - const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; - const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16]; -} - -pub struct RGB32; -impl ColorType for RGB32 { - type Inner = u32; - const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; - const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32]; -} - -pub struct RGB64; -impl ColorType for RGB64 { - type Inner = u64; - const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; - const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64]; -} - -pub struct RGBA8; -impl ColorType for RGBA8 { - type Inner = u8; - const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; - const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8]; -} - -pub struct RGBA16; -impl ColorType for RGBA16 { - type Inner = u16; - const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; - const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16]; -} - -pub struct RGBA32; -impl ColorType for RGBA32 { - type Inner = u16; - const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; - const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32]; -} - -pub struct RGBA64; -impl ColorType for RGBA64 { - type Inner = u16; - const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB; - const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64]; -} - -pub struct CMYK8; -impl ColorType for CMYK8 { - type Inner = u8; - const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK; - const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8]; -} - -pub struct CMYK16; -impl ColorType for CMYK16 { - type Inner = u16; - const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK; - const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16]; -} - -pub struct CMYK32; -impl ColorType for CMYK32 { - type Inner = u32; - const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK; - const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32]; -} - -pub struct CMYK64; -impl ColorType for CMYK64 { - type Inner = u64; - const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK; - const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64]; -} +pub struct ColorType { + pub value: Value, + pub tiff_value: PhotometricInterpretation, + pub bit_per_sample: &'static [u16], +} + +pub const GRAY8: ColorType = ColorType { + value: Value::U8(0), + tiff_value: PhotometricInterpretation::BlackIsZero, + bit_per_sample: &[8], +}; +pub const GRAYI8: ColorType = ColorType { + value: Value::I8(0), + tiff_value: PhotometricInterpretation::BlackIsZero, + bit_per_sample: &[8], +}; +pub const GRAY16: ColorType = ColorType { + value: Value::U16(0), + tiff_value: PhotometricInterpretation::BlackIsZero, + bit_per_sample: &[16], +}; +pub const GRAYI16: ColorType = ColorType { + value: Value::I16(0), + tiff_value: PhotometricInterpretation::BlackIsZero, + bit_per_sample: &[16], +}; +pub const GRAY32: ColorType = ColorType { + value: Value::U32(0), + tiff_value: PhotometricInterpretation::BlackIsZero, + bit_per_sample: &[32], +}; +pub const GRAYI32: ColorType = ColorType { + value: Value::I32(0), + tiff_value: PhotometricInterpretation::BlackIsZero, + bit_per_sample: &[32], +}; +pub const GRAYF32: ColorType = ColorType { + value: Value::F32(0_f32), + tiff_value: PhotometricInterpretation::BlackIsZero, + bit_per_sample: &[32], +}; +pub const GRAY64: ColorType = ColorType { + value: Value::U64(0), + tiff_value: PhotometricInterpretation::BlackIsZero, + bit_per_sample: &[64], +}; +pub const GRAYF64: ColorType = ColorType { + value: Value::F64(0_f64), + tiff_value: PhotometricInterpretation::BlackIsZero, + bit_per_sample: &[64], +}; + +pub const RGB8: ColorType = ColorType { + value: Value::U8(0), + tiff_value: PhotometricInterpretation::RGB, + bit_per_sample: &[8, 8, 8], +}; +pub const RGB16: ColorType = ColorType { + value: Value::U16(0), + tiff_value: PhotometricInterpretation::RGB, + bit_per_sample: &[16, 16, 16], +}; +pub const RGB32: ColorType = ColorType { + value: Value::U32(0), + tiff_value: PhotometricInterpretation::RGB, + bit_per_sample: &[32, 32, 32], +}; +pub const RGB64: ColorType = ColorType { + value: Value::U64(0), + tiff_value: PhotometricInterpretation::RGB, + bit_per_sample: &[64, 64, 64], +}; + +pub const RGBA8: ColorType = ColorType { + value: Value::U8(0), + tiff_value: PhotometricInterpretation::RGB, + bit_per_sample: &[8, 8, 8, 8], +}; +pub const RGBA16: ColorType = ColorType { + value: Value::U16(0), + tiff_value: PhotometricInterpretation::RGB, + bit_per_sample: &[16, 16, 16, 16], +}; +pub const RGBA32: ColorType = ColorType { + value: Value::U32(0), + tiff_value: PhotometricInterpretation::RGB, + bit_per_sample: &[32, 32, 32, 32], +}; +pub const RGBA64: ColorType = ColorType { + value: Value::U64(0), + tiff_value: PhotometricInterpretation::RGB, + bit_per_sample: &[64, 64, 64, 64], +}; + +pub const CMYK8: ColorType = ColorType { + value: Value::U8(0), + tiff_value: PhotometricInterpretation::CMYK, + bit_per_sample: &[8, 8, 8, 8], +}; +pub const CMYK16: ColorType = ColorType { + value: Value::U16(0), + tiff_value: PhotometricInterpretation::CMYK, + bit_per_sample: &[16, 16, 16, 16], +}; +pub const CMYK32: ColorType = ColorType { + value: Value::U32(0), + tiff_value: PhotometricInterpretation::CMYK, + bit_per_sample: &[32, 32, 32, 32], +}; +pub const CMYK64: ColorType = ColorType { + value: Value::U64(0), + tiff_value: PhotometricInterpretation::CMYK, + bit_per_sample: &[64, 64, 64, 64], +}; diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs index 17c77a0e..0b5a1e1f 100644 --- a/src/encoder/mod.rs +++ b/src/encoder/mod.rs @@ -5,7 +5,7 @@ use std::io::{Cursor, Seek, Write}; use std::mem; use lzw::{EncoderTIFF, MsbWriter}; -use tags::{self, ResolutionUnit, Tag, Type, CompressionMethod}; +use tags::{ResolutionUnit, Tag, Type, CompressionMethod}; use error::{TiffError, TiffFormatError, TiffResult}; pub mod colortype; @@ -15,357 +15,325 @@ use self::colortype::*; use self::writer::*; /// Type to represent tiff values of type `RATIONAL` -#[derive(Clone)] +#[derive(Clone, Copy)] pub struct Rational { pub n: u32, pub d: u32, } /// Type to represent tiff values of type `SRATIONAL` +#[derive(Clone, Copy)] pub struct SRational { pub n: i32, pub d: i32, } +pub enum Value { + U8(u8), + I8(i8), + U16(u16), + I16(i16), + U32(u32), + I32(i32), + F32(f32), + U64(u64), + F64(f64), + Rational(Rational), + SRational(SRational), + AU8(Vec), + AI8(Vec), + AU16(Vec), + AI16(Vec), + AU32(Vec), + AI32(Vec), + AF32(Vec), + AU64(Vec), + AF64(Vec), + ARational(Vec), + ASRational(Vec), + Str(String), +} + /// Trait for types that can be encoded in a tiff file pub trait TiffValue { - const BYTE_LEN: u32; - const FIELD_TYPE: Type; + fn byte_len(&self) -> u32; + fn field_type(&self) -> Type; fn count(&self) -> u32; fn bytes(&self) -> u32 { - self.count() * Self::BYTE_LEN + self.count() * self.byte_len() } + fn get_slice(&self, start: usize, end: usize) -> Self; fn write(&self, writer: &mut TiffWriter) -> TiffResult<()>; } -impl TiffValue for [u8] { - const BYTE_LEN: u32 = 1; - const FIELD_TYPE: Type = Type::BYTE; - - fn count(&self) -> u32 { - self.len() as u32 +impl TiffValue for Value { + fn byte_len(&self) -> u32 { + match &self { + Self::AU8(_) | Self::AI8(_) | Self::U8(_) | Self::I8(_) | Self::Str(_) => 1, + Self::AU16(_) | Self::AI16(_) | Self::U16(_) | Self::I16(_) => 2, + Self::AU32(_) | Self::AI32(_) | Self::AF32(_) | Self::U32(_) | Self::I32(_) | Self::F32(_) => 4, + Self::AU64(_) | Self::ARational(_) | Self::ASRational(_) | Self::AF64(_) | Self::U64(_) | Self::Rational(_) | Self::SRational(_) | Self::F64(_) => 8, + } } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - writer.write_bytes(self)?; - Ok(()) + fn field_type(&self) -> Type { + match &self { + Self::AU8(_) | Self::U8(_) => Type::BYTE, + Self::AI8(_) | Self::I8(_) => Type::SBYTE, + Self::AU16(_) | Self::U16(_) => Type::SHORT, + Self::AI16(_) | Self::I16(_) => Type::SSHORT, + Self::AU32(_) | Self::U32(_) => Type::LONG, + Self::AI32(_) | Self::I32(_) => Type::SLONG, + Self::AF32(_) | Self::F32(_) => Type::FLOAT, + Self::AU64(_) | Self::U64(_) => Type::LONG8, + Self::AF64(_) | Self::F64(_) => Type::DOUBLE, + Self::ARational(_) | Self::Rational(_) => Type::RATIONAL, + Self::ASRational(_) | Self::SRational(_) => Type::SRATIONAL, + Self::Str(_) => Type::ASCII, + } } -} - -impl TiffValue for [i8] { - const BYTE_LEN: u32 = 1; - const FIELD_TYPE: Type = Type::SBYTE; - fn count(&self) -> u32 { - self.len() as u32 - } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - // We write using nativeedian so this should be safe - let slice = - unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len()) }; - writer.write_bytes(slice)?; - Ok(()) + match &self { + Self::AU8(v) => v.len() as u32, + Self::AI8(v) => v.len() as u32, + Self::AU16(v) => v.len() as u32, + Self::AI16(v) => v.len() as u32, + Self::AU32(v) => v.len() as u32, + Self::AI32(v) => v.len() as u32, + Self::AF32(v) => v.len() as u32, + Self::AU64(v) => v.len() as u32, + Self::AF64(v) => v.len() as u32, + Self::ARational(v) => v.len() as u32, + Self::ASRational(v) => v.len() as u32, + Self::U8(_) | Self::I8(_) | Self::U16(_) | Self::I16(_) | + Self::U32(_) | Self::I32(_) | Self::F32(_) | Self::U64(_) | + Self::Rational(_) | Self::SRational(_) | Self::F64(_) => 1, + Self::Str(v) => v.len() as u32 + 1, + } } -} - -impl TiffValue for [u16] { - const BYTE_LEN: u32 = 2; - const FIELD_TYPE: Type = Type::SHORT; - - fn count(&self) -> u32 { - self.len() as u32 + fn get_slice(&self, start: usize, end: usize) -> Self { + match &self { + Self::AU8(v) => Self::AU8(v[start..end].into()), + Self::AI8(v) => Self::AI8(v[start..end].into()), + Self::AU16(v) => Self::AU16(v[start..end].into()), + Self::AI16(v) => Self::AI16(v[start..end].into()), + Self::AU32(v) => Self::AU32(v[start..end].into()), + Self::AI32(v) => Self::AI32(v[start..end].into()), + Self::AF32(v) => Self::AF32(v[start..end].into()), + Self::AU64(v) => Self::AU64(v[start..end].into()), + Self::AF64(v) => Self::AF64(v[start..end].into()), + Self::ARational(v) => Self::ARational(v[start..end].into()), + Self::ASRational(v) => Self::ASRational(v[start..end].into()), + Self::Str(v) => Self::Str(v[start..end].into()), + Self::U8(_) | Self::I8(_) | Self::U16(_) | Self::I16(_) | + Self::U32(_) | Self::I32(_) | Self::F32(_) | Self::U64(_) | + Self::Rational(_) | Self::SRational(_) | Self::F64(_) => unimplemented!() + } } - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - // We write using nativeedian so this sould be safe - let slice = - unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 2) }; - writer.write_bytes(slice)?; + match &self { + Self::AU8(v) => writer.write_bytes(v)?, + Self::AI8(v) => { + let slice = unsafe { ::std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len()) }; + writer.write_bytes(slice)?; + }, + Self::AU16(v) => { + let slice = + unsafe { ::std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * self.byte_len() as usize) }; + writer.write_bytes(slice)?; + } + Self::AI16(v) => { + let slice = + unsafe { ::std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * self.byte_len() as usize) }; + writer.write_bytes(slice)?; + }, + Self::AU32(v) => { + let slice = + unsafe { ::std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * self.byte_len() as usize) }; + writer.write_bytes(slice)?; + }, + Self::AI32(v) => { + let slice = + unsafe { ::std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * self.byte_len() as usize) }; + writer.write_bytes(slice)?; + }, + Self::AF32(v) => { + let slice = unsafe { + ::std::slice::from_raw_parts( + v.as_ptr() as *const u8, + v.len() * self.byte_len() as usize, + ) + }; + writer.write_bytes(slice)?; + }, + Self::AU64(v) => { + let slice = + unsafe { ::std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * self.byte_len() as usize) }; + writer.write_bytes(slice)?; + }, + Self::AF64(v) => { + let slice = unsafe { + ::std::slice::from_raw_parts( + v.as_ptr() as *const u8, + v.len() * self.byte_len() as usize, + ) + }; + writer.write_bytes(slice)?; + }, + Self::ARational(v) => { + for x in v { + Value::Rational(*x).write(writer)?; + } + }, + Self::ASRational(v) => { + for x in v { + Value::SRational(*x).write(writer)?; + } + }, + Self::U8(v) => writer.write_u8(*v)?, + Self::I8(v) => writer.write_i8(*v)?, + Self::U16(v) => writer.write_u16(*v)?, + Self::I16(v) => writer.write_i16(*v)?, + Self::U32(v) => writer.write_u32(*v)?, + Self::I32(v) => writer.write_i32(*v)?, + Self::F32(v) => writer.write_f32(*v)?, + Self::U64(v) => writer.write_u64(*v)?, + Self::Rational(v) => { + writer.write_u32(v.n)?; + writer.write_u32(v.d)?; + }, + Self::SRational(v) => { + writer.write_i32(v.n)?; + writer.write_i32(v.d)?; + }, + Self::F64(v) => writer.write_f64(*v)?, + Self::Str(v) => { + if v.is_ascii() && !v.bytes().any(|b| b == 0) { + writer.write_bytes(v.as_bytes())?; + writer.write_u8(0)?; + } else { + return Err(TiffError::FormatError(TiffFormatError::InvalidTag)) + } + } , + } Ok(()) } } -impl TiffValue for [i16] { - const BYTE_LEN: u32 = 2; - const FIELD_TYPE: Type = Type::SSHORT; - - fn count(&self) -> u32 { - self.len() as u32 - } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - // We write using nativeedian so this should be safe - let slice = - unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) }; - writer.write_bytes(slice)?; - Ok(()) +impl From for Value { + fn from(v: u8) -> Self { + Value::U8(v) } } -impl TiffValue for [u32] { - const BYTE_LEN: u32 = 4; - const FIELD_TYPE: Type = Type::LONG; - - fn count(&self) -> u32 { - self.len() as u32 - } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - // We write using nativeedian so this sould be safe - let slice = - unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 4) }; - writer.write_bytes(slice)?; - Ok(()) +impl From for Value { + fn from(v: i8) -> Self { + Value::I8(v) } } -impl TiffValue for [i32] { - const BYTE_LEN: u32 = 4; - const FIELD_TYPE: Type = Type::SLONG; - - fn count(&self) -> u32 { - self.len() as u32 - } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - // We write using nativeedian so this should be safe - let slice = - unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) }; - writer.write_bytes(slice)?; - Ok(()) +impl From for Value { + fn from(v: u16) -> Self { + Value::U16(v) } } - -impl TiffValue for [u64] { - const BYTE_LEN: u32 = 8; - const FIELD_TYPE: Type = Type::LONG8; - - fn count(&self) -> u32 { - self.len() as u32 - } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - // We write using nativeedian so this sould be safe - let slice = - unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 8) }; - writer.write_bytes(slice)?; - Ok(()) +impl From for Value { + fn from(v: i16) -> Self { + Value::I16(v) } } - -impl TiffValue for [Rational] { - const BYTE_LEN: u32 = 8; - const FIELD_TYPE: Type = Type::RATIONAL; - - fn count(&self) -> u32 { - self.len() as u32 - } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - for x in self { - x.write(writer)?; - } - Ok(()) +impl From for Value { + fn from(v: u32) -> Self { + Value::U32(v) } } - -impl TiffValue for [SRational] { - const BYTE_LEN: u32 = 8; - const FIELD_TYPE: Type = Type::SRATIONAL; - - fn count(&self) -> u32 { - self.len() as u32 - } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - for x in self { - x.write(writer)?; - } - Ok(()) +impl From for Value { + fn from(v: i32) -> Self { + Value::I32(v) } } - -impl TiffValue for u8 { - const BYTE_LEN: u32 = 1; - const FIELD_TYPE: Type = Type::BYTE; - - fn count(&self) -> u32 { - 1 - } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - writer.write_u8(*self)?; - Ok(()) +impl From for Value { + fn from(v: f32) -> Self { + Value::F32(v) } } - -impl TiffValue for i8 { - const BYTE_LEN: u32 = 1; - const FIELD_TYPE: Type = Type::SBYTE; - - fn count(&self) -> u32 { - 1 - } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - writer.write_i8(*self)?; - Ok(()) +impl From for Value { + fn from(v: u64) -> Self { + Value::U64(v) } } - -impl TiffValue for u16 { - const BYTE_LEN: u32 = 2; - const FIELD_TYPE: Type = Type::SHORT; - - fn count(&self) -> u32 { - 1 - } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - writer.write_u16(*self)?; - Ok(()) +impl From for Value { + fn from(v: f64) -> Self { + Value::F64(v) } } - -impl TiffValue for i16 { - const BYTE_LEN: u32 = 2; - const FIELD_TYPE: Type = Type::SSHORT; - - fn count(&self) -> u32 { - 1 - } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - writer.write_i16(*self)?; - Ok(()) +impl From for Value { + fn from(v: Rational) -> Self { + Value::Rational(v) } } - -impl TiffValue for u32 { - const BYTE_LEN: u32 = 4; - const FIELD_TYPE: Type = Type::LONG; - - fn count(&self) -> u32 { - 1 - } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - writer.write_u32(*self)?; - Ok(()) +impl From for Value { + fn from(v: SRational) -> Self { + Value::SRational(v) } } -impl TiffValue for i32 { - const BYTE_LEN: u32 = 4; - const FIELD_TYPE: Type = Type::SLONG; - fn count(&self) -> u32 { - 1 - } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - writer.write_i32(*self)?; - Ok(()) +impl From<&[u8]> for Value { + fn from(v: &[u8]) -> Self { + Value::AU8(v.into()) } } - -impl TiffValue for u64 { - const BYTE_LEN: u32 = 8; - const FIELD_TYPE: Type = Type::LONG8; - - fn count(&self) -> u32 { - 1 +impl From<&[i8]> for Value { + fn from(v: &[i8]) -> Self { + Value::AI8(v.into()) } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - writer.write_u64(*self)?; - Ok(()) +} +impl From<&[u16]> for Value { + fn from(v: &[u16]) -> Self { + Value::AU16(v.into()) } } - -impl TiffValue for Rational { - const BYTE_LEN: u32 = 8; - const FIELD_TYPE: Type = Type::RATIONAL; - - fn count(&self) -> u32 { - 1 +impl From<&[i16]> for Value { + fn from(v: &[i16]) -> Self { + Value::AI16(v.into()) } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - writer.write_u32(self.n)?; - writer.write_u32(self.d)?; - Ok(()) +} +impl From<&[u32]> for Value { + fn from(v: &[u32]) -> Self { + Value::AU32(v.into()) } } - -impl TiffValue for SRational { - const BYTE_LEN: u32 = 8; - const FIELD_TYPE: Type = Type::SRATIONAL; - - fn count(&self) -> u32 { - 1 +impl From<&[i32]> for Value { + fn from(v: &[i32]) -> Self { + Value::AI32(v.into()) } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - writer.write_i32(self.n)?; - writer.write_i32(self.d)?; - Ok(()) +} +impl From<&[f32]> for Value { + fn from(v: &[f32]) -> Self { + Value::AF32(v.into()) } } - -impl TiffValue for str { - const BYTE_LEN: u32 = 1; - const FIELD_TYPE: Type = Type::ASCII; - - fn count(&self) -> u32 { - self.len() as u32 + 1 +impl From<&[u64]> for Value { + fn from(v: &[u64]) -> Self { + Value::AU64(v.into()) } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - if self.is_ascii() && !self.bytes().any(|b| b == 0) { - writer.write_bytes(self.as_bytes())?; - writer.write_u8(0)?; - Ok(()) - } else { - Err(TiffError::FormatError(TiffFormatError::InvalidTag)) - } +} +impl From<&[f64]> for Value { + fn from(v: &[f64]) -> Self { + Value::AF64(v.into()) } } - -impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T { - const BYTE_LEN: u32 = T::BYTE_LEN; - const FIELD_TYPE: Type = T::FIELD_TYPE; - - fn count(&self) -> u32 { - (*self).count() +impl From<&[Rational]> for Value { + fn from(v: &[Rational]) -> Self { + Value::ARational(v.into()) } - - fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - (*self).write(writer) +} +impl From<&[SRational]> for Value { + fn from(v: &[SRational]) -> Self { + Value::ASRational(v.into()) } } -/// Tiff encoder. -/// -/// With this type you can get a `DirectoryEncoder` or a `ImageEncoder` -/// to encode tiff ifd directories with images. -/// -/// See `DirectoryEncoder` and `ImageEncoder`. -/// -/// # Examples -/// ``` -/// # extern crate tiff; -/// # fn main() { -/// # let mut file = std::io::Cursor::new(Vec::new()); -/// # let image_data = vec![0; 100*100*3]; -/// use tiff::encoder::*; -/// -/// let mut tiff = TiffEncoder::new(&mut file).unwrap(); -/// -/// tiff.write_image::(100, 100, &image_data).unwrap(); -/// # } -/// ``` pub struct TiffEncoder { writer: TiffWriter, } @@ -389,46 +357,49 @@ impl TiffEncoder { } /// Create an 'ImageEncoder' to encode an image one slice at a time. - pub fn new_image( + pub fn new_image( &mut self, width: u32, height: u32, + colortype: ColorType, compression_method: CompressionMethod, - additional_tags: Vec<(Tag, Box)> - ) -> TiffResult> { + additional_tags: Vec<(Tag, Value)> + ) -> TiffResult> { let encoder = DirectoryEncoder::new(&mut self.writer)?; - ImageEncoder::new(encoder, width, height, compression_method, additional_tags) + ImageEncoder::new(encoder, width, height, colortype, compression_method, additional_tags) } /// Convenience function to write an entire image from memory. - pub fn write_image( + pub fn write_image( &mut self, width: u32, height: u32, - data: &[C::Inner], + data: &V, + colortype: ColorType, compression: CompressionMethod, - additional_tags: Vec<(Tag, Box)> + additional_tags: Vec<(Tag, Value)> ) -> TiffResult<()> where - [C::Inner]: TiffValue, + V: TiffValue, { let num_pix = usize::try_from(width)?.checked_mul(usize::try_from(height)?) .ok_or_else(|| ::std::io::Error::new( ::std::io::ErrorKind::InvalidInput, "Image width * height exceeds usize"))?; - if data.len() < num_pix { + if usize::try_from(data.count())? < num_pix { return Err(::std::io::Error::new( ::std::io::ErrorKind::InvalidData, "Input data slice is undersized for provided dimensions").into()); } let encoder = DirectoryEncoder::new(&mut self.writer)?; - let mut image: ImageEncoder = ImageEncoder::new(encoder, width, height, compression, additional_tags)?; + let mut image: ImageEncoder = ImageEncoder::new(encoder, width, height, colortype, compression, additional_tags)?; let mut idx = 0; while image.next_strip_sample_count() > 0 { let sample_count = usize::try_from(image.next_strip_sample_count())?; - image.write_strip(&data[idx..idx + sample_count])?; + image.write_strip(&data.get_slice(idx, idx + sample_count))?; + // image.write_strip(&data[idx..idx + sample_count])?; idx += sample_count; } image.finish() @@ -462,7 +433,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> { /// Write a single ifd tag. pub fn write_tag(&mut self, tag: Tag, value: T) -> TiffResult<()> { - let len = ::BYTE_LEN * value.count(); + let len = value.byte_len() * value.count(); let mut bytes = Vec::with_capacity(usize::try_from(len)?); { let mut writer = TiffWriter::new(&mut bytes); @@ -470,7 +441,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> { } self.ifd - .insert(tag.to_u16(), (::FIELD_TYPE.to_u16(), value.count(), bytes)); + .insert(tag.to_u16(), (value.field_type().to_u16(), value.count(), bytes)); Ok(()) } @@ -507,7 +478,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> { /// Write some data to the tiff file, the offset of the data is returned. /// /// This could be used to write tiff strips. - pub fn write_data(&mut self, value: T) -> TiffResult { + pub fn write_data(&mut self, value: &T) -> TiffResult { let offset = self.writer.offset(); value.write(&mut self.writer)?; Ok(offset) @@ -552,21 +523,23 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> { /// # fn main() { /// # let mut file = std::io::Cursor::new(Vec::new()); /// # let image_data = vec![0; 100*100*3]; +/// # let image_data = Value::from(&image_data[..]); /// use tiff::encoder::*; +/// use tiff::tags::CompressionMethod; /// /// let mut tiff = TiffEncoder::new(&mut file).unwrap(); -/// let mut image = tiff.new_image::(100, 100).unwrap(); +/// let mut image = tiff.new_image(100, 100, colortype::RGB8, CompressionMethod::None, vec![]).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(); +/// image.write_strip(&image_data.get_slice(idx, idx+sample_count)).unwrap(); /// idx += sample_count; /// } /// image.finish().unwrap(); /// # } /// ``` -pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType> { +pub struct ImageEncoder<'a, W: 'a + Write + Seek> { encoder: DirectoryEncoder<'a, W>, strip_idx: u64, strip_count: u64, @@ -577,41 +550,41 @@ pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType> { strip_byte_count: Vec, dropped: bool, compression_method: CompressionMethod, - _phantom: ::std::marker::PhantomData, } -impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> { - fn new( +impl<'a, W: 'a + Write + Seek> ImageEncoder<'a, W> { + fn new( mut encoder: DirectoryEncoder<'a, W>, width: u32, height: u32, + colortype: ColorType, compression_method: CompressionMethod, - additional_tags: Vec<(Tag, Box)> - ) -> TiffResult> { - let row_samples = u64::from(width) * u64::try_from(::BITS_PER_SAMPLE.len())?; - let row_bytes = row_samples * u64::from(::BYTE_LEN); + additional_tags: Vec<(Tag, Value)> + ) -> TiffResult> { + let row_samples = u64::from(width) * u64::try_from(colortype.bit_per_sample.len())?; + let row_bytes = row_samples * u64::from(colortype.value.byte_len()); // As per tiff spec each strip should be about 8k long let rows_per_strip = (8000 + row_bytes - 1) / row_bytes; let strip_count = (u64::from(height) + rows_per_strip - 1) / rows_per_strip; - encoder.write_tag(Tag::ImageWidth, width)?; - encoder.write_tag(Tag::ImageLength, height)?; - encoder.write_tag(Tag::Compression, compression_method.to_u16())?; + encoder.write_tag(Tag::ImageWidth, Value::from(width))?; + encoder.write_tag(Tag::ImageLength, Value::from(height))?; + encoder.write_tag(Tag::Compression, Value::from(compression_method.to_u16()))?; - encoder.write_tag(Tag::BitsPerSample, ::BITS_PER_SAMPLE)?; - encoder.write_tag(Tag::PhotometricInterpretation, ::TIFF_VALUE.to_u16())?; + encoder.write_tag(Tag::BitsPerSample, Value::from(colortype.bit_per_sample))?; + encoder.write_tag(Tag::PhotometricInterpretation, Value::from(colortype.tiff_value.to_u16()))?; - encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?; + encoder.write_tag(Tag::RowsPerStrip, Value::from(u32::try_from(rows_per_strip)?))?; - encoder.write_tag(Tag::SamplesPerPixel, u16::try_from(::BITS_PER_SAMPLE.len())?)?; - 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, ResolutionUnit::None.to_u16())?; + encoder.write_tag(Tag::SamplesPerPixel, Value::from(u16::try_from(colortype.bit_per_sample.len())?))?; + encoder.write_tag(Tag::XResolution, Value::from(Rational { n: 1, d: 1 }))?; + encoder.write_tag(Tag::YResolution, Value::from(Rational { n: 1, d: 1 }))?; + encoder.write_tag(Tag::ResolutionUnit, Value::from(ResolutionUnit::None.to_u16()))?; for (t, v) in additional_tags { - encoder.write_tag(t, v.as_ref())?; + encoder.write_tag(t, v)?; } Ok(ImageEncoder { @@ -625,7 +598,6 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> { strip_byte_count: Vec::new(), dropped: false, compression_method, - _phantom: ::std::marker::PhantomData, }) } @@ -645,13 +617,13 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> { } /// Write a single strip. - pub fn write_strip(&mut self, value: &[T::Inner]) -> TiffResult<()> + pub fn write_strip(&mut self, value: &T) -> TiffResult<()> where - [T::Inner]: TiffValue, + T: TiffValue, { // TODO: Compression let samples = self.next_strip_sample_count(); - if u64::try_from(value.len())? != samples { + if u64::from(value.count()) != samples { return Err(::std::io::Error::new( ::std::io::ErrorKind::InvalidData, "Slice is wrong size for strip").into()); @@ -666,7 +638,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> { let mut lzw_encoder = EncoderTIFF::new(MsbWriter::new(&mut compressed), 8)?; lzw_encoder.encode_bytes(&buf.writer.into_inner()[..])?; } - self.encoder.write_data(&compressed[..])? + self.encoder.write_data(&Value::from(&compressed[..]))? }, _ => { self.encoder.write_data(value)? @@ -683,31 +655,31 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> { /// Set image resolution pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) { - self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap(); - self.encoder.write_tag(Tag::XResolution, value.clone()).unwrap(); - self.encoder.write_tag(Tag::YResolution, value).unwrap(); + self.encoder.write_tag(Tag::ResolutionUnit, Value::from(unit.to_u16())).unwrap(); + self.encoder.write_tag(Tag::XResolution, Value::from(value)).unwrap(); + self.encoder.write_tag(Tag::YResolution, Value::from(value)).unwrap(); } /// Set image resolution unit pub fn resolution_unit(&mut self, unit: ResolutionUnit) { - self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap(); + self.encoder.write_tag(Tag::ResolutionUnit, Value::from(unit.to_u16())).unwrap(); } /// Set image x-resolution pub fn x_resolution(&mut self, value: Rational) { - self.encoder.write_tag(Tag::XResolution, value).unwrap(); + self.encoder.write_tag(Tag::XResolution, Value::from(value)).unwrap(); } /// Set image y-resolution pub fn y_resolution(&mut self, value: Rational) { - self.encoder.write_tag(Tag::YResolution, value).unwrap(); + self.encoder.write_tag(Tag::YResolution, Value::from(value)).unwrap(); } fn finish_internal(&mut self) -> TiffResult<()> { self.encoder - .write_tag(Tag::StripOffsets, &*self.strip_offsets)?; + .write_tag(Tag::StripOffsets, Value::from(&self.strip_offsets[..]))?; self.encoder - .write_tag(Tag::StripByteCounts, &*self.strip_byte_count)?; + .write_tag(Tag::StripByteCounts, Value::from(&self.strip_byte_count[..]))?; self.dropped = true; self.encoder.finish_internal() @@ -724,7 +696,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> { } } -impl<'a, W: Write + Seek, C: ColorType> Drop for ImageEncoder<'a, W, C> { +impl<'a, W: Write + Seek> Drop for ImageEncoder<'a, W> { fn drop(&mut self) { if !self.dropped { let _ = self.finish_internal(); diff --git a/src/encoder/writer.rs b/src/encoder/writer.rs index 590882f6..3cc33855 100644 --- a/src/encoder/writer.rs +++ b/src/encoder/writer.rs @@ -86,12 +86,27 @@ impl TiffWriter { Ok(()) } + pub fn write_f32(&mut self, n: f32) -> Result<(), io::Error> { + self.writer.write_f32::(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 write_f64(&mut self, n: f64) -> Result<(), io::Error> { + self.writer.write_f64::(n)?; + self.offset += 8; + + Ok(()) + } + pub fn pad_word_boundary(&mut self) -> Result<(), io::Error> { if self.offset % 4 != 0 { diff --git a/src/error.rs b/src/error.rs index 02acbe06..9db8f2ef 100644 --- a/src/error.rs +++ b/src/error.rs @@ -35,7 +35,7 @@ pub enum TiffError { /// /// The list of variants may grow to incorporate errors of future features. Matching against this /// exhaustively is not covered by interface stability guarantees. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq)] pub enum TiffFormatError { TiffSignatureNotFound, TiffSignatureInvalid, @@ -47,6 +47,8 @@ pub enum TiffFormatError { UnknownPredictor(u16), UnsignedIntegerExpected(Value), SignedIntegerExpected(Value), + FloatExpected(Value), + DoubleExpected(Value), InflateError(InflateError), #[doc(hidden)] /// Do not match against this variant. It may get removed. @@ -74,6 +76,12 @@ impl fmt::Display for TiffFormatError { SignedIntegerExpected(ref val) => { write!(fmt, "Expected signed integer, {:?} found.", val) } + FloatExpected(ref val) => { + write!(fmt, "Expected float, {:?} found.", val) + } + DoubleExpected(ref val) => { + write!(fmt, "Expected double, {:?} found.", val) + } InflateError(_) => write!(fmt, "Failed to decode inflate data."), __NonExhaustive => unreachable!(), } diff --git a/src/tags.rs b/src/tags.rs index fabb74d5..80f4a6d1 100644 --- a/src/tags.rs +++ b/src/tags.rs @@ -126,6 +126,8 @@ pub enum Type(u16) { SSHORT = 8, SLONG = 9, SRATIONAL = 10, + FLOAT = 11, + DOUBLE = 12, /// BigTIFF 64-bit unsigned integer LONG8 = 16, } diff --git a/tests/encode_images.rs b/tests/encode_images.rs index c359b949..42e8a3e6 100644 --- a/tests/encode_images.rs +++ b/tests/encode_images.rs @@ -1,7 +1,7 @@ extern crate tiff; -use tiff::decoder::{Decoder, DecodingResult}; -use tiff::encoder::{colortype, TiffEncoder, SRational}; +use tiff::decoder::{ifd, Decoder, DecodingResult}; +use tiff::encoder::{colortype, TiffEncoder, SRational, Value}; use tiff::ColorType; use tiff::tags::{Tag, CompressionMethod}; @@ -24,7 +24,7 @@ fn encode_decode() { { let mut tiff = TiffEncoder::new(&mut file).unwrap(); - tiff.write_image::(100, 100, &image_data, CompressionMethod::None, vec![]) + tiff.write_image(100, 100, &Value::from(&image_data[..]), colortype::RGB8, CompressionMethod::None, vec![(Tag::Artist, Value::Str("Image-tiff".into()))]) .unwrap(); } { @@ -32,6 +32,7 @@ fn encode_decode() { let mut decoder = Decoder::new(&mut file).unwrap(); assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8)); assert_eq!(decoder.dimensions().unwrap(), (100, 100)); + assert_eq!(decoder.get_tag(Tag::Artist).unwrap(), ifd::Value::Ascii("Image-tiff".into())); if let DecodingResult::U8(img_res) = decoder.read_image().unwrap() { assert_eq!(image_data, img_res); } else { @@ -49,7 +50,7 @@ fn test_encode_undersized_buffer() { let output = Vec::new(); let mut output_stream = Cursor::new(output); if let Ok(mut tiff) = TiffEncoder::new(&mut output_stream) { - let res = tiff.write_image::(50, 50, &input_data, CompressionMethod::None, vec![]); + let res = tiff.write_image(50, 50, &Value::AI32(input_data), colortype::RGB8, CompressionMethod::None, vec![]); assert!(res.is_err()); } } @@ -57,8 +58,8 @@ fn test_encode_undersized_buffer() { const TEST_IMAGE_DIR: &str = "./tests/images/"; macro_rules! test_roundtrip { - ($name:ident, $buffer:ident, $buffer_ty:ty) => { - fn $name>(file: &str, expected_type: ColorType) { + ($name:ident, $buffer:ident) => { + fn $name(colortype: colortype::ColorType, file: &str, expected_type: ColorType) { let path = PathBuf::from(TEST_IMAGE_DIR).join(file); let img_file = File::open(path).expect("Cannot find test image!"); let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); @@ -74,7 +75,7 @@ macro_rules! test_roundtrip { let mut tiff = TiffEncoder::new(&mut file).unwrap(); let (width, height) = decoder.dimensions().unwrap(); - tiff.write_image::(width, height, &image_data, CompressionMethod::None, vec![]) + tiff.write_image(width, height, &Value::from(&image_data[..]), colortype, CompressionMethod::None, vec![]) .unwrap(); } file.seek(SeekFrom::Start(0)).unwrap(); @@ -90,59 +91,59 @@ macro_rules! test_roundtrip { }; } -test_roundtrip!(test_u8_roundtrip, U8, u8); -test_roundtrip!(test_u16_roundtrip, U16, u16); -test_roundtrip!(test_u32_roundtrip, U32, u32); -test_roundtrip!(test_u64_roundtrip, U64, u64); +test_roundtrip!(test_u8_roundtrip, U8); +test_roundtrip!(test_u16_roundtrip, U16); +test_roundtrip!(test_u32_roundtrip, U32); +test_roundtrip!(test_u64_roundtrip, U64); #[test] fn test_gray_u8_roundtrip() { - test_u8_roundtrip::("minisblack-1c-8b.tiff", ColorType::Gray(8)); + test_u8_roundtrip(colortype::GRAY8, "minisblack-1c-8b.tiff", ColorType::Gray(8)); } #[test] fn test_rgb_u8_roundtrip() { - test_u8_roundtrip::("rgb-3c-8b.tiff", ColorType::RGB(8)); + test_u8_roundtrip(colortype::RGB8, "rgb-3c-8b.tiff", ColorType::RGB(8)); } #[test] fn test_cmyk_u8_roundtrip() { - test_u8_roundtrip::("cmyk-3c-8b.tiff", ColorType::CMYK(8)); + test_u8_roundtrip(colortype::CMYK8, "cmyk-3c-8b.tiff", ColorType::CMYK(8)); } #[test] fn test_gray_u16_roundtrip() { - test_u16_roundtrip::("minisblack-1c-16b.tiff", ColorType::Gray(16)); + test_u16_roundtrip(colortype::GRAY16, "minisblack-1c-16b.tiff", ColorType::Gray(16)); } #[test] fn test_rgb_u16_roundtrip() { - test_u16_roundtrip::("rgb-3c-16b.tiff", ColorType::RGB(16)); + test_u16_roundtrip(colortype::RGB16, "rgb-3c-16b.tiff", ColorType::RGB(16)); } #[test] fn test_cmyk_u16_roundtrip() { - test_u16_roundtrip::("cmyk-3c-16b.tiff", ColorType::CMYK(16)); + test_u16_roundtrip(colortype::CMYK16, "cmyk-3c-16b.tiff", ColorType::CMYK(16)); } #[test] fn test_gray_u32_roundtrip() { - test_u32_roundtrip::("gradient-1c-32b.tiff", ColorType::Gray(32)); + test_u32_roundtrip(colortype::GRAY32, "gradient-1c-32b.tiff", ColorType::Gray(32)); } #[test] fn test_rgb_u32_roundtrip() { - test_u32_roundtrip::("gradient-3c-32b.tiff", ColorType::RGB(32)); + test_u32_roundtrip(colortype::RGB32, "gradient-3c-32b.tiff", ColorType::RGB(32)); } #[test] fn test_gray_u64_roundtrip() { - test_u64_roundtrip::("gradient-1c-64b.tiff", ColorType::Gray(64)); + test_u64_roundtrip(colortype::GRAY64, "gradient-1c-64b.tiff", ColorType::Gray(64)); } #[test] fn test_rgb_u64_roundtrip() { - test_u64_roundtrip::("gradient-3c-64b.tiff", ColorType::RGB(64)); + test_u64_roundtrip(colortype::RGB64, "gradient-3c-64b.tiff", ColorType::RGB(64)); } #[test] @@ -151,14 +152,14 @@ fn test_multiple_byte() { { let mut tiff = TiffEncoder::new(&mut data).unwrap(); - let mut image_encoder = tiff.new_image::(1, 1, CompressionMethod::None, vec![]).unwrap(); + let mut image_encoder = tiff.new_image(1, 1, colortype::GRAY8, CompressionMethod::None, vec![]).unwrap(); let encoder = image_encoder.encoder(); - encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]).unwrap(); - encoder.write_tag(Tag::Unknown(65001), &[1_u8, 2][..]).unwrap(); - encoder.write_tag(Tag::Unknown(65002), &[1_u8, 2, 3][..]).unwrap(); - encoder.write_tag(Tag::Unknown(65003), &[1_u8, 2, 3, 4][..]).unwrap(); - encoder.write_tag(Tag::Unknown(65004), &[1_u8, 2, 3, 4, 5][..]).unwrap(); + encoder.write_tag(Tag::Unknown(65000), Value::from(&[1_u8][..])).unwrap(); + encoder.write_tag(Tag::Unknown(65001), Value::from(&[1_u8, 2][..])).unwrap(); + encoder.write_tag(Tag::Unknown(65002), Value::from(&[1_u8, 2, 3][..])).unwrap(); + encoder.write_tag(Tag::Unknown(65003), Value::from(&[1_u8, 2, 3, 4][..])).unwrap(); + encoder.write_tag(Tag::Unknown(65004), Value::from(&[1_u8, 2, 3, 4, 5][..])).unwrap(); } data.set_position(0); @@ -180,28 +181,28 @@ fn test_signed() { { let mut tiff = TiffEncoder::new(&mut data).unwrap(); - let mut image_encoder = tiff.new_image::(1, 1, CompressionMethod::None, vec![]).unwrap(); + let mut image_encoder = tiff.new_image(1, 1, colortype::GRAY8, CompressionMethod::None, vec![]).unwrap(); let encoder = image_encoder.encoder(); //Use the "reusable" tags section as per the TIFF6 spec - encoder.write_tag(Tag::Unknown(65000), -1_i8).unwrap(); - encoder.write_tag(Tag::Unknown(65001), &[-1_i8][..]).unwrap(); - encoder.write_tag(Tag::Unknown(65002), &[-1_i8, 2][..]).unwrap(); - encoder.write_tag(Tag::Unknown(65003), &[-1_i8, 2, -3][..]).unwrap(); - encoder.write_tag(Tag::Unknown(65004), &[-1_i8, 2, -3, 4][..]).unwrap(); - encoder.write_tag(Tag::Unknown(65005), &[-1_i8, 2, -3, 4, -5][..]).unwrap(); - - encoder.write_tag(Tag::Unknown(65010), -1_i16).unwrap(); - encoder.write_tag(Tag::Unknown(65011), -1_i16).unwrap(); - encoder.write_tag(Tag::Unknown(65012), &[-1_i16, 2][..]).unwrap(); - encoder.write_tag(Tag::Unknown(65013), &[-1_i16, 2, -3][..]).unwrap(); - - encoder.write_tag(Tag::Unknown(65020), -1_i32).unwrap(); - encoder.write_tag(Tag::Unknown(65021), &[-1_i32][..]).unwrap(); - encoder.write_tag(Tag::Unknown(65022), &[-1_i32, 2][..]).unwrap(); - - encoder.write_tag(Tag::Unknown(65030), SRational { n: -1, d: 100 }).unwrap(); - encoder.write_tag(Tag::Unknown(65031), &[SRational { n: -1, d: 100 }, SRational { n: 2, d: 100 }][..]).unwrap(); + encoder.write_tag(Tag::Unknown(65000), Value::from(-1_i8)).unwrap(); + encoder.write_tag(Tag::Unknown(65001), Value::from(&[-1_i8][..])).unwrap(); + encoder.write_tag(Tag::Unknown(65002), Value::from(&[-1_i8, 2][..])).unwrap(); + encoder.write_tag(Tag::Unknown(65003), Value::from(&[-1_i8, 2, -3][..])).unwrap(); + encoder.write_tag(Tag::Unknown(65004), Value::from(&[-1_i8, 2, -3, 4][..])).unwrap(); + encoder.write_tag(Tag::Unknown(65005), Value::from(&[-1_i8, 2, -3, 4, -5][..])).unwrap(); + + encoder.write_tag(Tag::Unknown(65010), Value::from(-1_i16)).unwrap(); + encoder.write_tag(Tag::Unknown(65011), Value::from(-1_i16)).unwrap(); + encoder.write_tag(Tag::Unknown(65012), Value::from(&[-1_i16, 2][..])).unwrap(); + encoder.write_tag(Tag::Unknown(65013), Value::from(&[-1_i16, 2, -3][..])).unwrap(); + + encoder.write_tag(Tag::Unknown(65020), Value::from(-1_i32)).unwrap(); + encoder.write_tag(Tag::Unknown(65021), Value::from(&[-1_i32][..])).unwrap(); + encoder.write_tag(Tag::Unknown(65022), Value::from(&[-1_i32, 2][..])).unwrap(); + + encoder.write_tag(Tag::Unknown(65030), Value::from(SRational { n: -1, d: 100 })).unwrap(); + encoder.write_tag(Tag::Unknown(65031), Value::from(&[SRational { n: -1, d: 100 }, SRational { n: 2, d: 100 }][..])).unwrap(); } //Rewind the cursor for reading @@ -241,10 +242,10 @@ fn test_multipage_image() { // write first grayscale image (2x2 16-bit) let img1: Vec = [1, 2, 3, 4].to_vec(); - img_encoder.write_image::(2, 2, &img1[..], CompressionMethod::None, vec![]).unwrap(); + img_encoder.write_image(2, 2, &Value::AU16(img1), colortype::GRAY16, CompressionMethod::None, vec![]).unwrap(); // write second grayscale image (3x3 8-bit) let img2: Vec = [9, 8, 7, 6, 5, 4, 3, 2, 1].to_vec(); - img_encoder.write_image::(3, 3, &img2[..], CompressionMethod::None, vec![]).unwrap(); + img_encoder.write_image(3, 3, &Value::AU8(img2), colortype::GRAY8, CompressionMethod::None, vec![]).unwrap(); } // seek to the beginning of the file, so that it can be decoded