Compare commits

..
5 Commits
Author SHA1 Message Date
Andreas Molzer ee73341bbf Update notes and metadata for 0.5.0 2020-06-01 20:05:59 +02:00
HeroicKatora 6c7cc7ec30 Merge pull request #71 from aschampion/issues/69-u16-lzw-error
Fix #69
2020-03-31 16:55:39 +02:00
Andrew Champion 24d347ecf5 Tests: add regression test for issue 69 2020-02-09 18:37:16 +00:00
Andrew Champion a439baa948 Decoder: remove test make wrong assumption
The byte count for strips is the compressed length. The compressed
length may be larger than the uncompressed length, especially with poor
compression choices (multi-byte samples with packbits) or small images.
2020-02-09 18:37:16 +00:00
Andrew Champion 6e23274bea Decoder: max uncompressed length for LZW must respect buffer byte length
Fixes #69.
2020-02-09 18:24:22 +00:00
12 changed files with 167 additions and 369 deletions

No files matched your search

+9
View File
@@ -1,3 +1,12 @@
# Version 0.5.0
* Added support for 32-bit and 64-bit decoded values.
* Added CMYK(16|32|64) color type support.
* Check many internal integer conversions to increase stability. This should
only lead to images being reported as faulty that would previously silently
break platform limits. If there are any false positives, please report them.
* Remove an erroneous check of decoded length in lzw compressed images.
# Version 0.4.0 # Version 0.4.0
* Several enumerations are now non_exhaustive for future extensions. * Several enumerations are now non_exhaustive for future extensions.
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "tiff" name = "tiff"
version = "0.4.0" version = "0.5.0"
authors = [ authors = [
"ccgn", "ccgn",
"bvssvni <bvssvni@gmail.com>", "bvssvni <bvssvni@gmail.com>",
+17 -17
View File
@@ -9,7 +9,8 @@ use super::stream::{ByteOrder, EndianReader, SmartReader};
use tags::{Tag, Type}; use tags::{Tag, Type};
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError}; use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
use self::Value::{Ascii, List, Rational, SRational, Signed, Unsigned}; use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational};
#[allow(unused_qualifications)] #[allow(unused_qualifications)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -71,7 +72,7 @@ impl Value {
new_vec.push(numerator); new_vec.push(numerator);
new_vec.push(denominator); new_vec.push(denominator);
} }
_ => new_vec.push(v.into_i32()?), _ => new_vec.push(v.into_i32()?)
} }
} }
Ok(new_vec) Ok(new_vec)
@@ -181,10 +182,16 @@ impl Entry {
Ok(SRational(numerator, denominator)) Ok(SRational(numerator, denominator))
} }
(Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { (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| { (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) => { (Type::ASCII, n) => {
let n = usize::try_from(n)?; let n = usize::try_from(n)?;
@@ -202,17 +209,10 @@ impl Entry {
} }
#[inline] #[inline]
fn decode_offset<R, F>( fn decode_offset<R, F>(&self, value_count: u32, bo: ByteOrder, limits: &super::Limits, decoder: &mut super::Decoder<R>, decode_fn: F) -> TiffResult<Value>
&self, where
value_count: u32, R: Read + Seek,
bo: ByteOrder, F: Fn(&mut super::Decoder<R>) -> TiffResult<Value>,
limits: &super::Limits,
decoder: &mut super::Decoder<R>,
decode_fn: F,
) -> TiffResult<Value>
where
R: Read + Seek,
F: Fn(&mut super::Decoder<R>) -> TiffResult<Value>,
{ {
let value_count = usize::try_from(value_count)?; let value_count = usize::try_from(value_count)?;
if value_count > limits.decoding_buffer_size / mem::size_of::<Value>() { if value_count > limits.decoding_buffer_size / mem::size_of::<Value>() {
@@ -235,7 +235,7 @@ fn offset_to_bytes(n: usize, entry: &Entry) -> TiffResult<Value> {
entry.offset[0..n] entry.offset[0..n]
.iter() .iter()
.map(|&e| Unsigned(u32::from(e))) .map(|&e| Unsigned(u32::from(e)))
.collect(), .collect()
)) ))
} }
@@ -246,7 +246,7 @@ fn offset_to_sbytes(n: usize, entry: &Entry) -> TiffResult<Value> {
entry.offset[0..n] entry.offset[0..n]
.iter() .iter()
.map(|&e| Signed(i32::from(e as i8))) .map(|&e| Signed(i32::from(e as i8)))
.collect(), .collect()
)) ))
} }
+62 -71
View File
@@ -1,16 +1,14 @@
use std::cmp;
use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::collections::HashMap;
use std::io::{self, Read, Seek}; use std::io::{self, Read, Seek};
use std::cmp;
use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError}; use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
use self::ifd::Directory; use self::ifd::Directory;
use tags::{CompressionMethod, PhotometricInterpretation, Predictor, Tag, Type}; use tags::{CompressionMethod, PhotometricInterpretation, Predictor, Tag, Type};
use self::stream::{ use self::stream::{ByteOrder, EndianReader, LZWReader, DeflateReader, PackBitsReader, SmartReader};
ByteOrder, DeflateReader, EndianReader, LZWReader, PackBitsReader, SmartReader,
};
pub mod ifd; pub mod ifd;
mod stream; mod stream;
@@ -219,14 +217,8 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType)
let samples = match color_type { let samples = match color_type {
ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) | ColorType::Gray(64) => 1, ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) | ColorType::Gray(64) => 1,
ColorType::RGB(8) | ColorType::RGB(16) | ColorType::RGB(32) | ColorType::RGB(64) => 3, ColorType::RGB(8) | ColorType::RGB(16) | ColorType::RGB(32) | ColorType::RGB(64) => 3,
ColorType::RGBA(8) ColorType::RGBA(8) | ColorType::RGBA(16) | ColorType::RGBA(32) | ColorType::RGBA(64)
| ColorType::RGBA(16) | ColorType::CMYK(8) | ColorType::CMYK(16) | ColorType::CMYK(32) | ColorType::CMYK(64) => 4,
| ColorType::RGBA(32)
| ColorType::RGBA(64)
| ColorType::CMYK(8)
| ColorType::CMYK(16)
| ColorType::CMYK(32)
| ColorType::CMYK(64) => 4,
_ => { _ => {
return Err(TiffError::UnsupportedError( return Err(TiffError::UnsupportedError(
TiffUnsupportedError::HorizontalPredictor(color_type), TiffUnsupportedError::HorizontalPredictor(color_type),
@@ -281,24 +273,28 @@ impl<R: Read + Seek> Decoder<R> {
pub fn colortype(&mut self) -> TiffResult<ColorType> { pub fn colortype(&mut self) -> TiffResult<ColorType> {
match self.photometric_interpretation { match self.photometric_interpretation {
PhotometricInterpretation::RGB => match self.bits_per_sample[..] { PhotometricInterpretation::RGB => {
[r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)), match self.bits_per_sample[..] {
[r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)), [r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)),
_ => Err(TiffError::UnsupportedError( [r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)),
TiffUnsupportedError::InterpretationWithBits( _ => Err(TiffError::UnsupportedError(
self.photometric_interpretation, TiffUnsupportedError::InterpretationWithBits(
self.bits_per_sample.clone(), self.photometric_interpretation,
), self.bits_per_sample.clone(),
)), ),
)),
}
}, },
PhotometricInterpretation::CMYK => match self.bits_per_sample[..] { PhotometricInterpretation::CMYK => {
[c, m, y, k] if [c, c, c] == [m, y, k] => Ok(ColorType::CMYK(c)), match self.bits_per_sample[..] {
_ => Err(TiffError::UnsupportedError( [c, m, y, k] if [c, c, c] == [m, y, k] => Ok(ColorType::CMYK(c)),
TiffUnsupportedError::InterpretationWithBits( _ => Err(TiffError::UnsupportedError(
self.photometric_interpretation, TiffUnsupportedError::InterpretationWithBits(
self.bits_per_sample.clone(), self.photometric_interpretation,
), self.bits_per_sample.clone(),
)), ),
)),
}
}, },
PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero
if self.bits_per_sample.len() == 1 => if self.bits_per_sample.len() == 1 =>
@@ -375,11 +371,9 @@ impl<R: Read + Seek> Decoder<R> {
self.samples = val; self.samples = val;
} }
match self.samples { match self.samples {
1 | 3 | 4 => { 1 | 3 | 4 => if let Some(val) = self.find_tag_unsigned_vec(Tag::BitsPerSample)? {
if let Some(val) = self.find_tag_unsigned_vec(Tag::BitsPerSample)? { self.bits_per_sample = val;
self.bits_per_sample = val; },
}
}
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()), _ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()),
} }
Ok(()) Ok(())
@@ -522,30 +516,20 @@ impl<R: Read + Seek> Decoder<R> {
/// Tries to retrieve a tag and convert it to the desired unsigned type. /// Tries to retrieve a tag and convert it to the desired unsigned type.
pub fn find_tag_unsigned<T: TryFrom<u32>>(&mut self, tag: Tag) -> TiffResult<Option<T>> { pub fn find_tag_unsigned<T: TryFrom<u32>>(&mut self, tag: Tag) -> TiffResult<Option<T>> {
self.find_tag(tag)? self.find_tag(tag)?
.map(|v| v.into_u32()) .map(|v| v.into_u32()).transpose()?
.transpose()? .map(|value| T::try_from(value)
.map(|value| { .map_err(|_| TiffFormatError::InvalidTagValueType(tag).into()))
T::try_from(value).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into())
})
.transpose() .transpose()
} }
/// Tries to retrieve a vector of all a tag's values and convert them to /// Tries to retrieve a vector of all a tag's values and convert them to
/// the desired unsigned type. /// the desired unsigned type.
pub fn find_tag_unsigned_vec<T: TryFrom<u32>>( pub fn find_tag_unsigned_vec<T: TryFrom<u32>>(&mut self, tag: Tag) -> TiffResult<Option<Vec<T>>> {
&mut self,
tag: Tag,
) -> TiffResult<Option<Vec<T>>> {
self.find_tag(tag)? self.find_tag(tag)?
.map(|v| v.into_u32_vec()) .map(|v| v.into_u32_vec()).transpose()?
.transpose()? .map(|v| v.into_iter()
.map(|v| { .map(|u| T::try_from(u).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into()))
v.into_iter() .collect())
.map(|u| {
T::try_from(u).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into())
})
.collect()
})
.transpose() .transpose()
} }
@@ -600,19 +584,21 @@ impl<R: Read + Seek> Decoder<R> {
let (bytes, reader) = LZWReader::new( let (bytes, reader) = LZWReader::new(
&mut self.reader, &mut self.reader,
usize::try_from(length)?, usize::try_from(length)?,
max_uncompressed_length, max_uncompressed_length * buffer.byte_len()
)?; )?;
(bytes, Box::new(reader)) (bytes, Box::new(reader))
} }
CompressionMethod::PackBits => { CompressionMethod::PackBits => {
let order = self.reader.byte_order; let order = self.reader.byte_order;
let (bytes, reader) = let (bytes, reader) = PackBitsReader::new(
PackBitsReader::new(&mut self.reader, order, usize::try_from(length)?)?; &mut self.reader,
order,
usize::try_from(length)?,
)?;
(bytes, Box::new(reader)) (bytes, Box::new(reader))
} }
CompressionMethod::OldDeflate => { CompressionMethod::OldDeflate => {
let (bytes, reader) = let (bytes, reader) = DeflateReader::new(&mut self.reader, max_uncompressed_length)?;
DeflateReader::new(&mut self.reader, max_uncompressed_length)?;
(bytes, Box::new(reader)) (bytes, Box::new(reader))
} }
method => { method => {
@@ -699,7 +685,9 @@ impl<R: Read + Seek> Decoder<R> {
/// Number of strips in image /// Number of strips in image
pub fn strip_count(&mut self) -> TiffResult<u32> { pub fn strip_count(&mut self) -> TiffResult<u32> {
let rows_per_strip = self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height); let rows_per_strip = self
.get_tag_u32(Tag::RowsPerStrip)
.unwrap_or(self.height);
if rows_per_strip == 0 { if rows_per_strip == 0 {
return Ok(0); return Ok(0);
@@ -745,8 +733,9 @@ impl<R: Read + Seek> Decoder<R> {
TiffFormatError::InconsistentSizesEncountered, TiffFormatError::InconsistentSizesEncountered,
))?; ))?;
let rows_per_strip = let rows_per_strip = usize::try_from(self
usize::try_from(self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height))?; .get_tag_u32(Tag::RowsPerStrip)
.unwrap_or(self.height))?;
let strip_height = cmp::min( let strip_height = cmp::min(
rows_per_strip, rows_per_strip,
@@ -755,9 +744,7 @@ impl<R: Read + Seek> Decoder<R> {
let buffer_size = usize::try_from(self.width)? * strip_height * self.bits_per_sample.len(); let buffer_size = usize::try_from(self.width)? * strip_height * self.bits_per_sample.len();
if buffer.len() < buffer_size if buffer.len() < buffer_size {
|| usize::try_from(byte_count)? / buffer.byte_len() > buffer_size
{
return Err(TiffError::FormatError( return Err(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered, TiffFormatError::InconsistentSizesEncountered,
)); ));
@@ -805,9 +792,11 @@ impl<R: Read + Seek> Decoder<R> {
n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits), n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits),
n if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits), n if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits),
n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits), n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits),
n => Err(TiffError::UnsupportedError( n => {
TiffUnsupportedError::UnsupportedBitsPerChannel(n), Err(TiffError::UnsupportedError(
)), TiffUnsupportedError::UnsupportedBitsPerChannel(n),
))
}
} }
} }
@@ -816,8 +805,9 @@ impl<R: Read + Seek> Decoder<R> {
self.initialize_strip_decoder()?; self.initialize_strip_decoder()?;
let index = self.strip_decoder.as_ref().unwrap().strip_index; let index = self.strip_decoder.as_ref().unwrap().strip_index;
let rows_per_strip = let rows_per_strip = usize::try_from(self
usize::try_from(self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height))?; .get_tag_u32(Tag::RowsPerStrip)
.unwrap_or(self.height))?;
let strip_height = cmp::min( let strip_height = cmp::min(
rows_per_strip, rows_per_strip,
@@ -834,8 +824,9 @@ impl<R: Read + Seek> Decoder<R> {
/// Decodes the entire image and return it as a Vector /// Decodes the entire image and return it as a Vector
pub fn read_image(&mut self) -> TiffResult<DecodingResult> { pub fn read_image(&mut self) -> TiffResult<DecodingResult> {
self.initialize_strip_decoder()?; self.initialize_strip_decoder()?;
let rows_per_strip = let rows_per_strip = usize::try_from(self
usize::try_from(self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height))?; .get_tag_u32(Tag::RowsPerStrip)
.unwrap_or(self.height))?;
let samples_per_strip = let samples_per_strip =
usize::try_from(self.width)? * rows_per_strip * self.bits_per_sample.len(); usize::try_from(self.width)? * rows_per_strip * self.bits_per_sample.len();
+20 -47
View File
@@ -4,8 +4,8 @@ use std::convert::TryFrom;
use std::io::{Seek, Write}; use std::io::{Seek, Write};
use std::mem; use std::mem;
use error::{TiffError, TiffFormatError, TiffResult};
use tags::{self, ResolutionUnit, Tag, Type}; use tags::{self, ResolutionUnit, Tag, Type};
use error::{TiffError, TiffFormatError, TiffResult};
pub mod colortype; pub mod colortype;
mod writer; mod writer;
@@ -61,7 +61,8 @@ impl TiffValue for [i8] {
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> { fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
// We write using nativeedian so this should be safe // 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()) }; let slice =
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len()) };
writer.write_bytes(slice)?; writer.write_bytes(slice)?;
Ok(()) Ok(())
} }
@@ -94,12 +95,8 @@ impl TiffValue for [i16] {
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> { fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
// We write using nativeedian so this should be safe // We write using nativeedian so this should be safe
let slice = unsafe { let slice =
::std::slice::from_raw_parts( unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) };
self.as_ptr() as *const u8,
self.len() * Self::BYTE_LEN as usize,
)
};
writer.write_bytes(slice)?; writer.write_bytes(slice)?;
Ok(()) Ok(())
} }
@@ -132,12 +129,8 @@ impl TiffValue for [i32] {
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> { fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
// We write using nativeedian so this should be safe // We write using nativeedian so this should be safe
let slice = unsafe { let slice =
::std::slice::from_raw_parts( unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) };
self.as_ptr() as *const u8,
self.len() * Self::BYTE_LEN as usize,
)
};
writer.write_bytes(slice)?; writer.write_bytes(slice)?;
Ok(()) Ok(())
} }
@@ -414,20 +407,14 @@ impl<W: Write + Seek> TiffEncoder<W> {
where where
[C::Inner]: TiffValue, [C::Inner]: TiffValue,
{ {
let num_pix = usize::try_from(width)? let num_pix = usize::try_from(width)?.checked_mul(usize::try_from(height)?)
.checked_mul(usize::try_from(height)?) .ok_or_else(|| ::std::io::Error::new(
.ok_or_else(|| { ::std::io::ErrorKind::InvalidInput,
::std::io::Error::new( "Image width * height exceeds usize"))?;
::std::io::ErrorKind::InvalidInput,
"Image width * height exceeds usize",
)
})?;
if data.len() < num_pix { if data.len() < num_pix {
return Err(::std::io::Error::new( return Err(::std::io::Error::new(
::std::io::ErrorKind::InvalidData, ::std::io::ErrorKind::InvalidData,
"Input data slice is undersized for provided dimensions", "Input data slice is undersized for provided dimensions").into());
)
.into());
} }
let encoder = DirectoryEncoder::new(&mut self.writer)?; let encoder = DirectoryEncoder::new(&mut self.writer)?;
@@ -477,10 +464,8 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
value.write(&mut writer)?; value.write(&mut writer)?;
} }
self.ifd.insert( self.ifd
tag.to_u16(), .insert(tag.to_u16(), (<T>::FIELD_TYPE.to_u16(), value.count(), bytes));
(<T>::FIELD_TYPE.to_u16(), value.count(), bytes),
);
Ok(()) Ok(())
} }
@@ -612,10 +597,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?; encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?;
encoder.write_tag( encoder.write_tag(Tag::SamplesPerPixel, u16::try_from(<T>::BITS_PER_SAMPLE.len())?)?;
Tag::SamplesPerPixel,
u16::try_from(<T>::BITS_PER_SAMPLE.len())?,
)?;
encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 })?; encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 })?;
encoder.write_tag(Tag::YResolution, 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::ResolutionUnit, ResolutionUnit::None.to_u16())?;
@@ -640,8 +622,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
return 0; return 0;
} }
let start_row = let start_row = ::std::cmp::min(u64::from(self.height), self.strip_idx * self.rows_per_strip);
::std::cmp::min(u64::from(self.height), self.strip_idx * self.rows_per_strip);
let end_row = ::std::cmp::min( let end_row = ::std::cmp::min(
u64::from(self.height), u64::from(self.height),
(self.strip_idx + 1) * self.rows_per_strip, (self.strip_idx + 1) * self.rows_per_strip,
@@ -660,9 +641,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
if u64::try_from(value.len())? != samples { if u64::try_from(value.len())? != samples {
return Err(::std::io::Error::new( return Err(::std::io::Error::new(
::std::io::ErrorKind::InvalidData, ::std::io::ErrorKind::InvalidData,
"Slice is wrong size for strip", "Slice is wrong size for strip").into());
)
.into());
} }
let offset = self.encoder.write_data(value)?; let offset = self.encoder.write_data(value)?;
@@ -675,20 +654,14 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
/// Set image resolution /// Set image resolution
pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) { pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) {
self.encoder self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap();
.write_tag(Tag::ResolutionUnit, unit.to_u16()) self.encoder.write_tag(Tag::XResolution, value.clone()).unwrap();
.unwrap();
self.encoder
.write_tag(Tag::XResolution, value.clone())
.unwrap();
self.encoder.write_tag(Tag::YResolution, value).unwrap(); self.encoder.write_tag(Tag::YResolution, value).unwrap();
} }
/// Set image resolution unit /// Set image resolution unit
pub fn resolution_unit(&mut self, unit: ResolutionUnit) { pub fn resolution_unit(&mut self, unit: ResolutionUnit) {
self.encoder self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap();
.write_tag(Tag::ResolutionUnit, unit.to_u16())
.unwrap();
} }
/// Set image x-resolution /// Set image x-resolution
+1 -1
View File
@@ -1,5 +1,5 @@
use byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian, WriteBytesExt};
use error::TiffResult; use error::TiffResult;
use byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian, WriteBytesExt};
use std::io::{self, Seek, SeekFrom, Write}; use std::io::{self, Seek, SeekFrom, Write};
pub trait TiffByteOrder: ByteOrder { pub trait TiffByteOrder: ByteOrder {
+4 -5
View File
@@ -3,9 +3,9 @@ use std::fmt;
use std::io; use std::io;
use std::string; use std::string;
use decoder::ifd::Value; use decoder::ifd::{Value};
use miniz_oxide::inflate::TINFLStatus;
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Tag}; use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Tag};
use miniz_oxide::inflate::TINFLStatus;
use ColorType; use ColorType;
/// Tiff error kinds. /// Tiff error kinds.
@@ -62,9 +62,8 @@ impl fmt::Display for TiffFormatError {
ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."), ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."),
InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."), InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."),
InvalidTag => write!(fmt, "Image contains invalid tag."), InvalidTag => write!(fmt, "Image contains invalid tag."),
InvalidTagValueType(ref tag) => { InvalidTagValueType(ref tag) =>
write!(fmt, "Tag `{:?}` did not have the expected value type.", tag) write!(fmt, "Tag `{:?}` did not have the expected value type.", tag),
}
RequiredTagNotFound(ref tag) => write!(fmt, "Required tag `{:?}` not found.", tag), RequiredTagNotFound(ref tag) => write!(fmt, "Required tag `{:?}` not found.", tag),
UnknownPredictor(ref predictor) => { UnknownPredictor(ref predictor) => {
write!(fmt, "Unknown predictor “{}” encountered", predictor) write!(fmt, "Unknown predictor “{}” encountered", predictor)
+3 -2
View File
@@ -131,6 +131,7 @@ pub enum Type(u16) {
} }
} }
tags! { tags! {
pub enum CompressionMethod(u16) { pub enum CompressionMethod(u16) {
None = 1, None = 1,
@@ -172,11 +173,11 @@ pub enum Predictor(u16) {
} }
} }
tags! { tags!{
/// Type to represent resolution units /// Type to represent resolution units
pub enum ResolutionUnit(u16) { pub enum ResolutionUnit(u16) {
None = 1, None = 1,
Inch = 2, Inch = 2,
Centimeter = 3, Centimeter = 3,
} }
} }
+8 -4
View File
@@ -21,7 +21,7 @@ macro_rules! test_image_sum {
DecodingResult::$buffer(res) => { DecodingResult::$buffer(res) => {
let sum: $sum_ty = res.into_iter().map(<$sum_ty>::from).sum(); let sum: $sum_ty = res.into_iter().map(<$sum_ty>::from).sum();
assert_eq!(sum, expected_sum); assert_eq!(sum, expected_sum);
} },
_ => panic!("Wrong bit depth"), _ => panic!("Wrong bit depth"),
} }
} }
@@ -42,9 +42,7 @@ fn test_image_color_type_unsupported(file: &str, expected_type: ColorType) {
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
assert_eq!(decoder.colortype().unwrap(), expected_type); assert_eq!(decoder.colortype().unwrap(), expected_type);
assert!(match decoder.read_image() { assert!(match decoder.read_image() {
Err(tiff::TiffError::UnsupportedError( Err(tiff::TiffError::UnsupportedError(tiff::TiffUnsupportedError::UnsupportedColorType(_))) => true,
tiff::TiffUnsupportedError::UnsupportedColorType(_),
)) => true,
_ => false, _ => false,
}); });
} }
@@ -151,6 +149,12 @@ fn test_decode_data() {
} }
} }
#[test]
fn issue_69() {
test_image_sum_u16("issue_69_lzw.tiff", ColorType::Gray(16), 1015486);
test_image_sum_u16("issue_69_packbits.tiff", ColorType::Gray(16), 1015486);
}
// TODO: GrayA support // TODO: GrayA support
//#[test] //#[test]
//fn test_gray_alpha_u8() //fn test_gray_alpha_u8()
+42 -221
View File
@@ -1,9 +1,9 @@
extern crate tiff; extern crate tiff;
use tiff::decoder::{Decoder, DecodingResult}; use tiff::decoder::{Decoder, DecodingResult};
use tiff::encoder::{colortype, SRational, TiffEncoder}; use tiff::encoder::{colortype, TiffEncoder, SRational};
use tiff::tags::Tag;
use tiff::ColorType; use tiff::ColorType;
use tiff::tags::Tag;
use std::fs::File; use std::fs::File;
use std::io::{Cursor, Seek, SeekFrom}; use std::io::{Cursor, Seek, SeekFrom};
@@ -58,10 +58,7 @@ const TEST_IMAGE_DIR: &str = "./tests/images/";
macro_rules! test_roundtrip { macro_rules! test_roundtrip {
($name:ident, $buffer:ident, $buffer_ty:ty) => { ($name:ident, $buffer:ident, $buffer_ty:ty) => {
fn $name<C: colortype::ColorType<Inner = $buffer_ty>>( fn $name<C: colortype::ColorType<Inner=$buffer_ty>>(file: &str, expected_type: ColorType) {
file: &str,
expected_type: ColorType,
) {
let path = PathBuf::from(TEST_IMAGE_DIR).join(file); let path = PathBuf::from(TEST_IMAGE_DIR).join(file);
let img_file = File::open(path).expect("Cannot find test image!"); let img_file = File::open(path).expect("Cannot find test image!");
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder"); let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
@@ -77,7 +74,8 @@ macro_rules! test_roundtrip {
let mut tiff = TiffEncoder::new(&mut file).unwrap(); let mut tiff = TiffEncoder::new(&mut file).unwrap();
let (width, height) = decoder.dimensions().unwrap(); let (width, height) = decoder.dimensions().unwrap();
tiff.write_image::<C>(width, height, &image_data).unwrap(); tiff.write_image::<C>(width, height, &image_data)
.unwrap();
} }
file.seek(SeekFrom::Start(0)).unwrap(); file.seek(SeekFrom::Start(0)).unwrap();
{ {
@@ -157,64 +155,21 @@ fn test_multiple_byte() {
let encoder = image_encoder.encoder(); let encoder = image_encoder.encoder();
encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]).unwrap(); encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]).unwrap();
encoder encoder.write_tag(Tag::Unknown(65001), &[1_u8, 2][..]).unwrap();
.write_tag(Tag::Unknown(65001), &[1_u8, 2][..]) encoder.write_tag(Tag::Unknown(65002), &[1_u8, 2, 3][..]).unwrap();
.unwrap(); encoder.write_tag(Tag::Unknown(65003), &[1_u8, 2, 3, 4][..]).unwrap();
encoder encoder.write_tag(Tag::Unknown(65004), &[1_u8, 2, 3, 4, 5][..]).unwrap();
.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();
} }
data.set_position(0); data.set_position(0);
{ {
let mut decoder = Decoder::new(&mut data).unwrap(); let mut decoder = Decoder::new(&mut data).unwrap();
assert_eq!( assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_u32_vec().unwrap(), [1]);
decoder assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_u32_vec().unwrap(), [1, 2]);
.get_tag(Tag::Unknown(65000)) assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_u32_vec().unwrap(), [1, 2, 3]);
.unwrap() assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_u32_vec().unwrap(), [1, 2, 3, 4]);
.into_u32_vec() assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_u32_vec().unwrap(), [1, 2, 3, 4, 5]);
.unwrap(),
[1]
);
assert_eq!(
decoder
.get_tag(Tag::Unknown(65001))
.unwrap()
.into_u32_vec()
.unwrap(),
[1, 2]
);
assert_eq!(
decoder
.get_tag(Tag::Unknown(65002))
.unwrap()
.into_u32_vec()
.unwrap(),
[1, 2, 3]
);
assert_eq!(
decoder
.get_tag(Tag::Unknown(65003))
.unwrap()
.into_u32_vec()
.unwrap(),
[1, 2, 3, 4]
);
assert_eq!(
decoder
.get_tag(Tag::Unknown(65004))
.unwrap()
.into_u32_vec()
.unwrap(),
[1, 2, 3, 4, 5]
);
} }
} }
@@ -230,48 +185,23 @@ fn test_signed() {
//Use the "reusable" tags section as per the TIFF6 spec //Use the "reusable" tags section as per the TIFF6 spec
encoder.write_tag(Tag::Unknown(65000), -1_i8).unwrap(); encoder.write_tag(Tag::Unknown(65000), -1_i8).unwrap();
encoder encoder.write_tag(Tag::Unknown(65001), &[-1_i8][..]).unwrap();
.write_tag(Tag::Unknown(65001), &[-1_i8][..]) encoder.write_tag(Tag::Unknown(65002), &[-1_i8, 2][..]).unwrap();
.unwrap(); encoder.write_tag(Tag::Unknown(65003), &[-1_i8, 2, -3][..]).unwrap();
encoder encoder.write_tag(Tag::Unknown(65004), &[-1_i8, 2, -3, 4][..]).unwrap();
.write_tag(Tag::Unknown(65002), &[-1_i8, 2][..]) encoder.write_tag(Tag::Unknown(65005), &[-1_i8, 2, -3, 4, -5][..]).unwrap();
.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(65010), -1_i16).unwrap();
encoder.write_tag(Tag::Unknown(65011), -1_i16).unwrap(); encoder.write_tag(Tag::Unknown(65011), -1_i16).unwrap();
encoder encoder.write_tag(Tag::Unknown(65012), &[-1_i16, 2][..]).unwrap();
.write_tag(Tag::Unknown(65012), &[-1_i16, 2][..]) encoder.write_tag(Tag::Unknown(65013), &[-1_i16, 2, -3][..]).unwrap();
.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(65020), -1_i32).unwrap();
encoder encoder.write_tag(Tag::Unknown(65021), &[-1_i32][..]).unwrap();
.write_tag(Tag::Unknown(65021), &[-1_i32][..]) encoder.write_tag(Tag::Unknown(65022), &[-1_i32, 2][..]).unwrap();
.unwrap();
encoder
.write_tag(Tag::Unknown(65022), &[-1_i32, 2][..])
.unwrap();
encoder encoder.write_tag(Tag::Unknown(65030), SRational { n: -1, d: 100 }).unwrap();
.write_tag(Tag::Unknown(65030), SRational { n: -1, d: 100 }) encoder.write_tag(Tag::Unknown(65031), &[SRational { n: -1, d: 100 }, SRational { n: 2, d: 100 }][..]).unwrap();
.unwrap();
encoder
.write_tag(
Tag::Unknown(65031),
&[SRational { n: -1, d: 100 }, SRational { n: 2, d: 100 }][..],
)
.unwrap();
} }
//Rewind the cursor for reading //Rewind the cursor for reading
@@ -279,129 +209,24 @@ fn test_signed() {
{ {
let mut decoder = Decoder::new(&mut data).unwrap(); let mut decoder = Decoder::new(&mut data).unwrap();
assert_eq!( assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_i32().unwrap(), -1, );
decoder assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_i32_vec().unwrap(), [-1]);
.get_tag(Tag::Unknown(65000)) assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_i32_vec().unwrap(), [-1, 2]);
.unwrap() assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3]);
.into_i32() assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3, 4]);
.unwrap(), assert_eq!(decoder.get_tag(Tag::Unknown(65005)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3, 4, -5], );
-1,
);
assert_eq!(
decoder
.get_tag(Tag::Unknown(65001))
.unwrap()
.into_i32_vec()
.unwrap(),
[-1]
);
assert_eq!(
decoder
.get_tag(Tag::Unknown(65002))
.unwrap()
.into_i32_vec()
.unwrap(),
[-1, 2]
);
assert_eq!(
decoder
.get_tag(Tag::Unknown(65003))
.unwrap()
.into_i32_vec()
.unwrap(),
[-1, 2, -3]
);
assert_eq!(
decoder
.get_tag(Tag::Unknown(65004))
.unwrap()
.into_i32_vec()
.unwrap(),
[-1, 2, -3, 4]
);
assert_eq!(
decoder
.get_tag(Tag::Unknown(65005))
.unwrap()
.into_i32_vec()
.unwrap(),
[-1, 2, -3, 4, -5],
);
assert_eq!( assert_eq!(decoder.get_tag(Tag::Unknown(65010)).unwrap().into_i32().unwrap(), -1);
decoder assert_eq!(decoder.get_tag(Tag::Unknown(65011)).unwrap().into_i32_vec().unwrap(), [-1]);
.get_tag(Tag::Unknown(65010)) assert_eq!(decoder.get_tag(Tag::Unknown(65012)).unwrap().into_i32_vec().unwrap(), [-1, 2]);
.unwrap() assert_eq!(decoder.get_tag(Tag::Unknown(65013)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3]);
.into_i32()
.unwrap(),
-1
);
assert_eq!(
decoder
.get_tag(Tag::Unknown(65011))
.unwrap()
.into_i32_vec()
.unwrap(),
[-1]
);
assert_eq!(
decoder
.get_tag(Tag::Unknown(65012))
.unwrap()
.into_i32_vec()
.unwrap(),
[-1, 2]
);
assert_eq!(
decoder
.get_tag(Tag::Unknown(65013))
.unwrap()
.into_i32_vec()
.unwrap(),
[-1, 2, -3]
);
assert_eq!( assert_eq!(decoder.get_tag(Tag::Unknown(65020)).unwrap().into_i32().unwrap(), -1);
decoder assert_eq!(decoder.get_tag(Tag::Unknown(65021)).unwrap().into_i32_vec().unwrap(), [-1]);
.get_tag(Tag::Unknown(65020)) assert_eq!(decoder.get_tag(Tag::Unknown(65022)).unwrap().into_i32_vec().unwrap(), [-1, 2]);
.unwrap()
.into_i32()
.unwrap(),
-1
);
assert_eq!(
decoder
.get_tag(Tag::Unknown(65021))
.unwrap()
.into_i32_vec()
.unwrap(),
[-1]
);
assert_eq!(
decoder
.get_tag(Tag::Unknown(65022))
.unwrap()
.into_i32_vec()
.unwrap(),
[-1, 2]
);
assert_eq!( assert_eq!(decoder.get_tag(Tag::Unknown(65030)).unwrap().into_i32_vec().unwrap(), [-1, 100]);
decoder assert_eq!(decoder.get_tag(Tag::Unknown(65031)).unwrap().into_i32_vec().unwrap(), [-1_i32, 100, 2, 100]);
.get_tag(Tag::Unknown(65030))
.unwrap()
.into_i32_vec()
.unwrap(),
[-1, 100]
);
assert_eq!(
decoder
.get_tag(Tag::Unknown(65031))
.unwrap()
.into_i32_vec()
.unwrap(),
[-1_i32, 100, 2, 100]
);
} }
} }
@@ -416,14 +241,10 @@ fn test_multipage_image() {
// write first grayscale image (2x2 16-bit) // write first grayscale image (2x2 16-bit)
let img1: Vec<u16> = [1, 2, 3, 4].to_vec(); let img1: Vec<u16> = [1, 2, 3, 4].to_vec();
img_encoder img_encoder.write_image::<colortype::Gray16>(2, 2, &img1[..]).unwrap();
.write_image::<colortype::Gray16>(2, 2, &img1[..])
.unwrap();
// write second grayscale image (3x3 8-bit) // write second grayscale image (3x3 8-bit)
let img2: Vec<u8> = [9, 8, 7, 6, 5, 4, 3, 2, 1].to_vec(); let img2: Vec<u8> = [9, 8, 7, 6, 5, 4, 3, 2, 1].to_vec();
img_encoder img_encoder.write_image::<colortype::Gray8>(3, 3, &img2[..]).unwrap();
.write_image::<colortype::Gray8>(3, 3, &img2[..])
.unwrap();
} }
// seek to the beginning of the file, so that it can be decoded // seek to the beginning of the file, so that it can be decoded
Binary file not shown.
Binary file not shown.