Compare commits

..
Author SHA1 Message Date
Andreas Molzer 2635bdd7d5 Update release notes for alpha release for BigTIFF 2020-08-13 18:02:26 +02:00
Farkal ff874612ba Allow to decode LONG8 (u64)
TileOffsets and TileByteCounts have the LONG8 type on bigtiff.
2020-08-10 15:32:47 +02:00
HeroicKatora ea217dc238 Merge pull request #83 from Farkal/big-tiff
Big tiff decoding support
2020-08-08 12:33:54 +02:00
Etienne Prothon b2da2f2333 Merge branch 'master' of github.com:Farkal/image-tiff into big-tiff 2020-07-27 12:01:55 +02:00
Farkal ba1daa18b2 Allow custom encoding process (#84)
Simplify encoding of tags
2020-07-27 11:29:09 +02:00
Etienne Prothon ba3f6e604a feat: Add BigTiff enum values 2020-07-27 10:52:43 +02:00
HeroicKatora a471591f06 Merge pull request #77 from Farkal/read_ascii_in_offset
Read ascii encoded in offset
2020-07-24 02:25:17 +02:00
Etienne Prothon dacc2864e8 feat: better compatibility with old api 2020-07-24 01:22:33 +02:00
Etienne Prothon 797e3dca07 fix: Big tiff decoding and add tests 2020-07-24 00:47:14 +02:00
Etienne Prothon 23751bd8c5 feat: Support decoding big tiff 2020-07-23 01:33:41 +02:00
HeroicKatora 7fb68dbbe7 Merge pull request #78 from Farkal/support_tiled_data_tags
Support tile and sample format tags
2020-07-22 18:52:57 +02:00
HeroicKatora 4afd7ed462 Merge pull request #81 from spenczar/patch-1
Update link to TIFF specification in docs
2020-07-21 18:30:27 +02:00
Spencer Nelson aa49e7d6f0 Update link to TIFF specification in docs
Adobe refactored their site and the specification has moved.
2020-07-21 09:23:12 -07:00
Etienne Prothon cbb41fd479 feat: Support tile and sample format tags 2020-07-15 16:55:34 +02:00
Etienne Prothon 9edc0e656e fix: remove unwrap 2020-07-15 16:39:00 +02:00
Etienne Prothon 9f4873bae8 fix: Read ascii encoded in offset 2020-07-15 16:21:09 +02:00
HeroicKatora 472cf6d07d Merge pull request #75 from image-rs/release-0.5
Update notes and metadata for 0.5.0
2020-06-01 20:13:42 +02:00
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
17 changed files with 607 additions and 493 deletions

No files matched your search

+18
View File
@@ -1,3 +1,21 @@
# Version 0.6.0-alpha
* Support for decoding BigTIFF with 64-bit offsets
* Better support for adding auxiliary tags before encoding image data
* Fixed decoding of inline ASCII in tags
* Fixed handling after null terminator in ASCII data
* Recognize tile and sample format tags
* Note: these notes will be merged into 0.6.0 when it is released
# 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.6.0-alpha"
authors = [ authors = [
"ccgn", "ccgn",
"bvssvni <bvssvni@gmail.com>", "bvssvni <bvssvni@gmail.com>",
+265 -94
View File
@@ -1,7 +1,7 @@
//! Function for reading TIFF tags //! Function for reading TIFF tags
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::{TryInto, TryFrom};
use std::io::{self, Read, Seek}; use std::io::{self, Read, Seek};
use std::mem; use std::mem;
@@ -9,16 +9,21 @@ 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, RationalBig, UnsignedBig, SignedBig, SRationalBig};
#[allow(unused_qualifications)] #[allow(unused_qualifications)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Value { pub enum Value {
Signed(i32), Signed(i32),
SignedBig(i64),
Unsigned(u32), Unsigned(u32),
UnsignedBig(u64),
List(Vec<Value>), List(Vec<Value>),
Rational(u32, u32), Rational(u32, u32),
RationalBig(u64, u64),
SRational(i32, i32), SRational(i32, i32),
SRationalBig(i64, i64),
Ascii(String), Ascii(String),
#[doc(hidden)] // Do not match against this. #[doc(hidden)] // Do not match against this.
__NonExhaustive, __NonExhaustive,
@@ -28,6 +33,7 @@ impl Value {
pub fn into_u32(self) -> TiffResult<u32> { pub fn into_u32(self) -> TiffResult<u32> {
match self { match self {
Unsigned(val) => Ok(val), Unsigned(val) => Ok(val),
UnsignedBig(val) => Ok(u32::try_from(val)?),
val => Err(TiffError::FormatError( val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val), TiffFormatError::UnsignedIntegerExpected(val),
)), )),
@@ -37,6 +43,27 @@ impl Value {
pub fn into_i32(self) -> TiffResult<i32> { pub fn into_i32(self) -> TiffResult<i32> {
match self { match self {
Signed(val) => Ok(val), Signed(val) => Ok(val),
SignedBig(val) => Ok(i32::try_from(val)?),
val => Err(TiffError::FormatError(
TiffFormatError::SignedIntegerExpected(val),
)),
}
}
pub fn into_u64(self) -> TiffResult<u64> {
match self {
Unsigned(val) => Ok(val.into()),
UnsignedBig(val) => Ok(val),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
}
}
pub fn into_i64(self) -> TiffResult<i64> {
match self {
Signed(val) => Ok(val.into()),
SignedBig(val) => Ok(val),
val => Err(TiffError::FormatError( val => Err(TiffError::FormatError(
TiffFormatError::SignedIntegerExpected(val), TiffFormatError::SignedIntegerExpected(val),
)), )),
@@ -53,7 +80,9 @@ impl Value {
Ok(new_vec) Ok(new_vec)
} }
Unsigned(val) => Ok(vec![val]), Unsigned(val) => Ok(vec![val]),
UnsignedBig(val) => Ok(vec![u32::try_from(val)?]),
Rational(numerator, denominator) => Ok(vec![numerator, denominator]), Rational(numerator, denominator) => Ok(vec![numerator, denominator]),
RationalBig(numerator, denominator) => Ok(vec![u32::try_from(numerator)?, u32::try_from(denominator)?]),
Ascii(val) => Ok(val.chars().map(u32::from).collect()), Ascii(val) => Ok(val.chars().map(u32::from).collect()),
val => Err(TiffError::FormatError( val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val), TiffFormatError::UnsignedIntegerExpected(val),
@@ -71,25 +100,80 @@ impl Value {
new_vec.push(numerator); new_vec.push(numerator);
new_vec.push(denominator); new_vec.push(denominator);
} }
SRationalBig(numerator, denominator) => {
new_vec.push(i32::try_from(numerator)?);
new_vec.push(i32::try_from(denominator)?);
}
_ => new_vec.push(v.into_i32()?), _ => new_vec.push(v.into_i32()?),
} }
} }
Ok(new_vec) Ok(new_vec)
} }
Signed(val) => Ok(vec![val]), Signed(val) => Ok(vec![val]),
SignedBig(val) => Ok(vec![i32::try_from(val)?]),
SRational(numerator, denominator) => Ok(vec![numerator, denominator]), SRational(numerator, denominator) => Ok(vec![numerator, denominator]),
SRationalBig(numerator, denominator) => Ok(vec![i32::try_from(numerator)?, i32::try_from(denominator)?]),
val => Err(TiffError::FormatError(
TiffFormatError::SignedIntegerExpected(val),
)),
}
}
pub fn into_u64_vec(self) -> TiffResult<Vec<u64>> {
match self {
List(vec) => {
let mut new_vec = Vec::with_capacity(vec.len());
for v in vec {
new_vec.push(v.into_u64()?)
}
Ok(new_vec)
}
Unsigned(val) => Ok(vec![val.into()]),
UnsignedBig(val) => Ok(vec![val]),
Rational(numerator, denominator) => Ok(vec![numerator.into(), denominator.into()]),
RationalBig(numerator, denominator) => Ok(vec![numerator, denominator]),
Ascii(val) => Ok(val.chars().map(u32::from).map(u64::from).collect()),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
}
}
pub fn into_i64_vec(self) -> TiffResult<Vec<i64>> {
match self {
List(vec) => {
let mut new_vec = Vec::with_capacity(vec.len());
for v in vec {
match v {
SRational(numerator, denominator) => {
new_vec.push(numerator.into());
new_vec.push(denominator.into());
}
SRationalBig(numerator, denominator) => {
new_vec.push(numerator);
new_vec.push(denominator);
}
_ => new_vec.push(v.into_i64()?),
}
}
Ok(new_vec)
}
Signed(val) => Ok(vec![val.into()]),
SignedBig(val) => Ok(vec![val]),
SRational(numerator, denominator) => Ok(vec![numerator.into(), denominator.into()]),
SRationalBig(numerator, denominator) => Ok(vec![numerator, denominator]),
val => Err(TiffError::FormatError( val => Err(TiffError::FormatError(
TiffFormatError::SignedIntegerExpected(val), TiffFormatError::SignedIntegerExpected(val),
)), )),
} }
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Entry { pub struct Entry {
type_: Type, type_: Type,
count: u32, count: u64,
offset: [u8; 4], offset: [u8; 8],
} }
impl ::std::fmt::Debug for Entry { impl ::std::fmt::Debug for Entry {
@@ -103,6 +187,12 @@ impl ::std::fmt::Debug for Entry {
impl Entry { impl Entry {
pub fn new(type_: Type, count: u32, offset: [u8; 4]) -> Entry { pub fn new(type_: Type, count: u32, offset: [u8; 4]) -> Entry {
let mut offset = offset.to_vec();
offset.append(&mut vec![0; 4]);
Entry::new_u64(type_, count.into(), offset[..].try_into().unwrap())
}
pub fn new_u64(type_: Type, count: u64, offset: [u8; 8]) -> Entry {
Entry { Entry {
type_, type_,
count, count,
@@ -121,98 +211,175 @@ impl Entry {
decoder: &mut super::Decoder<R>, decoder: &mut super::Decoder<R>,
) -> TiffResult<Value> { ) -> TiffResult<Value> {
let bo = decoder.byte_order(); let bo = decoder.byte_order();
match (self.type_, self.count) { let bigtiff_value = if decoder.bigtiff {
// TODO check if this could give wrong results match (self.type_, self.count) {
// at a different endianess of file/computer. (Type::BYTE, 5) => Some(offset_to_bytes(5, self)?),
(Type::BYTE, 1) => Ok(Unsigned(u32::from(self.offset[0]))), (Type::BYTE, 6) => Some(offset_to_bytes(6, self)?),
(Type::BYTE, 2) => offset_to_bytes(2, self), (Type::BYTE, 7) => Some(offset_to_bytes(7, self)?),
(Type::BYTE, 3) => offset_to_bytes(3, self), (Type::BYTE, 8) => Some(offset_to_bytes(8, self)?),
(Type::BYTE, 4) => offset_to_bytes(4, self), (Type::SBYTE, 5) => Some(offset_to_sbytes(5, self)?),
(Type::BYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { (Type::SBYTE, 6) => Some(offset_to_sbytes(6, self)?),
Ok(Unsigned(u32::from(decoder.read_byte()?))) (Type::SBYTE, 7) => Some(offset_to_sbytes(7, self)?),
}), (Type::SBYTE, 8) => Some(offset_to_sbytes(8, self)?),
(Type::SBYTE, 1) => Ok(Signed(i32::from(self.offset[0] as i8))), (Type::SHORT, 3) => {
(Type::SBYTE, 2) => offset_to_sbytes(2, self), let mut r = self.r(bo);
(Type::SBYTE, 3) => offset_to_sbytes(3, self), Some(List(vec![
(Type::SBYTE, 4) => offset_to_sbytes(4, self), Unsigned(u32::from(r.read_u16()?)),
(Type::SBYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| { Unsigned(u32::from(r.read_u16()?)),
Ok(Signed(i32::from(decoder.read_byte()? as i8))) Unsigned(u32::from(r.read_u16()?)),
}), ]))
(Type::SHORT, 1) => Ok(Unsigned(u32::from(self.r(bo).read_u16()?))),
(Type::SSHORT, 1) => Ok(Signed(i32::from(self.r(bo).read_i16()?))),
(Type::SHORT, 2) => {
let mut r = self.r(bo);
Ok(List(vec![
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
]))
}
(Type::SSHORT, 2) => {
let mut r = self.r(bo);
Ok(List(vec![
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
]))
}
(Type::SHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Unsigned(u32::from(decoder.read_short()?)))
}),
(Type::SSHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Signed(i32::from(decoder.read_sshort()?)))
}),
(Type::LONG, 1) => Ok(Unsigned(self.r(bo).read_u32()?)),
(Type::SLONG, 1) => Ok(Signed(self.r(bo).read_i32()?)),
(Type::LONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Unsigned(decoder.read_long()?))
}),
(Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Signed(decoder.read_slong()?))
}),
(Type::RATIONAL, 1) => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
let numerator = decoder.read_long()?;
let denominator = decoder.read_long()?;
Ok(Rational(numerator, denominator))
}
(Type::SRATIONAL, 1) => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
let numerator = decoder.read_slong()?;
let denominator = decoder.read_slong()?;
Ok(SRational(numerator, denominator))
}
(Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
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()?))
}),
(Type::ASCII, n) => {
let n = usize::try_from(n)?;
if n > limits.decoding_buffer_size {
return Err(TiffError::LimitsExceeded);
} }
decoder.goto_offset(self.r(bo).read_u32()?)?; (Type::SSHORT, 3) => {
let string = decoder.read_string(n)?; let mut r = self.r(bo);
Ok(Ascii(string)) Some(List(vec![
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
]))
}
(Type::SHORT, 4) => {
let mut r = self.r(bo);
Some(List(vec![
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
]))
}
(Type::SSHORT, 4) => {
let mut r = self.r(bo);
Some(List(vec![
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
]))
}
(Type::LONG, 2) => {
let mut r = self.r(bo);
Some(List(vec![
Unsigned(r.read_u32()?),
Unsigned(r.read_u32()?),
]))
}
(Type::SLONG, 2) => {
let mut r = self.r(bo);
Some(List(vec![
Signed(r.read_i32()?),
Signed(r.read_i32()?),
]))
}
(Type::RATIONAL, 1) => {
let mut r = self.r(bo);
let numerator = r.read_u32()?;
let denominator = r.read_u32()?;
Some(Rational(numerator, denominator))
}
(Type::SRATIONAL, 1) => {
let mut r = self.r(bo);
let numerator = r.read_i32()?;
let denominator = r.read_i32()?;
Some(SRational(numerator, denominator))
}
_ => None
}
} else {
None
};
if let Some(v) = bigtiff_value {
Ok(v)
} else {
match (self.type_, self.count) {
// TODO check if this could give wrong results
// at a different endianess of file/computer.
(Type::BYTE, 1) => Ok(Unsigned(u32::from(self.offset[0]))),
(Type::BYTE, 2) => offset_to_bytes(2, self),
(Type::BYTE, 3) => offset_to_bytes(3, self),
(Type::BYTE, 4) => offset_to_bytes(4, self),
(Type::BYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(UnsignedBig(u64::from(decoder.read_byte()?)))
}),
(Type::SBYTE, 1) => Ok(Signed(i32::from(self.offset[0] as i8))),
(Type::SBYTE, 2) => offset_to_sbytes(2, self),
(Type::SBYTE, 3) => offset_to_sbytes(3, self),
(Type::SBYTE, 4) => offset_to_sbytes(4, self),
(Type::SBYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(SignedBig(i64::from(decoder.read_byte()? as i8)))
}),
(Type::SHORT, 1) => Ok(Unsigned(u32::from(self.r(bo).read_u16()?))),
(Type::SSHORT, 1) => Ok(Signed(i32::from(self.r(bo).read_i16()?))),
(Type::SHORT, 2) => {
let mut r = self.r(bo);
Ok(List(vec![
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
]))
}
(Type::SSHORT, 2) => {
let mut r = self.r(bo);
Ok(List(vec![
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
]))
}
(Type::SHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(UnsignedBig(u64::from(decoder.read_short()?)))
}),
(Type::SSHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(SignedBig(i64::from(decoder.read_sshort()?)))
}),
(Type::LONG, 1) => Ok(Unsigned(self.r(bo).read_u32()?)),
(Type::SLONG, 1) => Ok(Signed(self.r(bo).read_i32()?)),
(Type::LONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Unsigned(decoder.read_long()?))
}),
(Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Signed(decoder.read_slong()?))
}),
(Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
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()?))
}),
(Type::LONG8, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(UnsignedBig(decoder.read_long8()?))
}),
(Type::ASCII, n) => {
let n = usize::try_from(n)?;
if n > limits.decoding_buffer_size {
return Err(TiffError::LimitsExceeded);
}
if (n <= 4 && !decoder.bigtiff) || (n <= 8 && decoder.bigtiff) {
let mut buf = vec![0; n];
self.r(bo).read_exact(&mut buf )?;
let v = String::from_utf8(buf)?;
let v = v.trim_matches(char::from(0));
Ok(Ascii(v.into()))
} else {
if decoder.bigtiff {
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
} else {
decoder.goto_offset(self.r(bo).read_u32()?)?
}
let string = decoder.read_string(n)?;
Ok(Ascii(string))
}
}
_ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedDataType,
)),
} }
_ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedDataType,
)),
} }
} }
#[inline] #[inline]
fn decode_offset<R, F>( fn decode_offset<R, F>(&self, value_count: u64, 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>() {
@@ -220,7 +387,11 @@ impl Entry {
} }
let mut v = Vec::with_capacity(value_count); let mut v = Vec::with_capacity(value_count);
decoder.goto_offset(self.r(bo).read_u32()?)?; if decoder.bigtiff {
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
} else {
decoder.goto_offset(self.r(bo).read_u32()?)?
}
for _ in 0..value_count { for _ in 0..value_count {
v.push(decode_fn(decoder)?) v.push(decode_fn(decoder)?)
} }
@@ -235,7 +406,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 +417,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()
)) ))
} }
+158 -100
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;
@@ -118,8 +116,8 @@ impl<'a> DecodingBuffer<'a> {
#[derive(Debug)] #[derive(Debug)]
struct StripDecodeState { struct StripDecodeState {
strip_index: usize, strip_index: usize,
strip_offsets: Vec<u32>, strip_offsets: Vec<u64>,
strip_bytes: Vec<u32>, strip_bytes: Vec<u64>,
} }
/// Decoding limits /// Decoding limits
@@ -159,8 +157,9 @@ where
{ {
reader: SmartReader<R>, reader: SmartReader<R>,
byte_order: ByteOrder, byte_order: ByteOrder,
bigtiff: bool,
limits: Limits, limits: Limits,
next_ifd: Option<u32>, next_ifd: Option<u64>,
ifd: Option<Directory>, ifd: Option<Directory>,
width: u32, width: u32,
height: u32, height: u32,
@@ -219,14 +218,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),
@@ -256,6 +249,7 @@ impl<R: Read + Seek> Decoder<R> {
Decoder { Decoder {
reader: SmartReader::wrap(r, ByteOrder::LittleEndian), reader: SmartReader::wrap(r, ByteOrder::LittleEndian),
byte_order: ByteOrder::LittleEndian, byte_order: ByteOrder::LittleEndian,
bigtiff: false,
limits: Default::default(), limits: Default::default(),
next_ifd: None, next_ifd: None,
ifd: None, ifd: None,
@@ -281,24 +275,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 =>
@@ -334,12 +332,28 @@ impl<R: Read + Seek> Decoder<R> {
)) ))
} }
} }
if self.read_short()? != 42 { match self.read_short()? {
return Err(TiffError::FormatError( 42 => self.bigtiff = false,
43 => {
self.bigtiff = true;
// Read bytesize of offsets (in bigtiff it's alway 8 but provide a way to move to 16 some day)
if self.read_short()? != 8 {
return Err(TiffError::FormatError(
TiffFormatError::TiffSignatureNotFound,
))
}
// This constant should always be 0
if self.read_short()? != 0 {
return Err(TiffError::FormatError(
TiffFormatError::TiffSignatureNotFound,
))
}
},
_ => return Err(TiffError::FormatError(
TiffFormatError::TiffSignatureInvalid, TiffFormatError::TiffSignatureInvalid,
)); ))
} }
self.next_ifd = match self.read_long()? { self.next_ifd = match self.read_ifd_offset()? {
0 => None, 0 => None,
n => Some(n), n => Some(n),
}; };
@@ -375,11 +389,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(())
@@ -395,6 +407,15 @@ impl<R: Read + Seek> Decoder<R> {
self.byte_order self.byte_order
} }
#[inline]
pub fn read_ifd_offset(&mut self) -> Result<u64, io::Error> {
if self.bigtiff {
self.read_long8()
} else {
self.read_long().map(u64::from)
}
}
/// Reads a TIFF byte value /// Reads a TIFF byte value
#[inline] #[inline]
pub fn read_byte(&mut self) -> Result<u8, io::Error> { pub fn read_byte(&mut self) -> Result<u8, io::Error> {
@@ -427,29 +448,53 @@ impl<R: Read + Seek> Decoder<R> {
self.reader.read_i32() self.reader.read_i32()
} }
#[inline]
pub fn read_long8(&mut self) -> Result<u64, io::Error> {
self.reader.read_u64()
}
/// Reads a string /// Reads a string
#[inline] #[inline]
pub fn read_string(&mut self, length: usize) -> TiffResult<String> { pub fn read_string(&mut self, length: usize) -> TiffResult<String> {
let mut out = String::with_capacity(length); let mut out = vec![0; length];
self.reader.read_to_string(&mut out)?; self.reader.read_exact(&mut out)?;
// Strings may be null-terminated, so we trim anything downstream of the null byte // Strings may be null-terminated, so we trim anything downstream of the null byte
let trimmed = out.bytes().take_while(|&n| n != 0).collect::<Vec<u8>>(); if let Some(first) = out.iter().position(|&b| b == 0) {
Ok(String::from_utf8(trimmed)?) out.truncate(first);
}
Ok(String::from_utf8(out)?)
} }
/// Reads a TIFF IFA offset/value field /// Reads a TIFF IFA offset/value field
#[inline] #[inline]
pub fn read_offset(&mut self) -> Result<[u8; 4], io::Error> { pub fn read_offset(&mut self) -> TiffResult<[u8; 4]> {
if self.bigtiff {
return Err(TiffError::FormatError(TiffFormatError::InconsistentSizesEncountered))
}
let mut val = [0; 4]; let mut val = [0; 4];
self.reader.read_exact(&mut val)?; self.reader.read_exact(&mut val)?;
Ok(val) Ok(val)
} }
/// Reads a TIFF IFA offset/value field
#[inline]
pub fn read_offset_u64(&mut self) -> Result<[u8; 8], io::Error> {
let mut val = [0; 8];
self.reader.read_exact(&mut val)?;
Ok(val)
}
/// Moves the cursor to the specified offset /// Moves the cursor to the specified offset
#[inline] #[inline]
pub fn goto_offset(&mut self, offset: u32) -> io::Result<()> { pub fn goto_offset(&mut self, offset: u32) -> io::Result<()> {
self.goto_offset_u64(offset.into())
}
#[inline]
pub fn goto_offset_u64(&mut self, offset: u64) -> io::Result<()> {
self.reader self.reader
.seek(io::SeekFrom::Start(u64::from(offset))) .seek(io::SeekFrom::Start(offset))
.map(|_| ()) .map(|_| ())
} }
@@ -471,13 +516,22 @@ impl<R: Read + Seek> Decoder<R> {
return Ok(None); return Ok(None);
} }
}; };
Ok(Some(( let entry = if self.bigtiff {
tag, ifd::Entry::new_u64(
type_,
self.read_long8()?,
self.read_offset_u64()?
)
} else {
ifd::Entry::new( ifd::Entry::new(
type_, type_,
self.read_long()?, // count self.read_long()?,
self.read_offset()?, // offset self.read_offset()?
), )
};
Ok(Some((
tag,
entry,
))) )))
} }
@@ -490,16 +544,17 @@ impl<R: Read + Seek> Decoder<R> {
TiffFormatError::ImageFileDirectoryNotFound, TiffFormatError::ImageFileDirectoryNotFound,
)) ))
} }
Some(offset) => self.goto_offset(offset)?, Some(offset) => self.goto_offset_u64(offset)?,
} }
for _ in 0..self.read_short()? { let num_tags = if self.bigtiff { self.read_long8()? } else { self.read_short()?.into() };
for _ in 0..num_tags {
let (tag, entry) = match self.read_entry()? { let (tag, entry) = match self.read_entry()? {
Some(val) => val, Some(val) => val,
None => continue, // Unknown data type in tag, skip None => continue, // Unknown data type in tag, skip
}; };
dir.insert(tag, entry); dir.insert(tag, entry);
} }
self.next_ifd = match self.read_long()? { self.next_ifd = match self.read_ifd_offset()? {
0 => None, 0 => None,
n => Some(n), n => Some(n),
}; };
@@ -520,38 +575,28 @@ 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<u64>>(&mut self, tag: Tag) -> TiffResult<Option<T>> {
self.find_tag(tag)? self.find_tag(tag)?
.map(|v| v.into_u32()) .map(|v| v.into_u64()).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<u64>>(&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_u64_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()
} }
/// 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.
/// Returns an error if the tag is not present. /// Returns an error if the tag is not present.
pub fn get_tag_unsigned<T: TryFrom<u32>>(&mut self, tag: Tag) -> TiffResult<T> { pub fn get_tag_unsigned<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<T> {
self.find_tag_unsigned(tag)? self.find_tag_unsigned(tag)?
.ok_or_else(|| TiffFormatError::RequiredTagNotFound(tag).into()) .ok_or_else(|| TiffFormatError::RequiredTagNotFound(tag).into())
} }
@@ -571,24 +616,30 @@ impl<R: Read + Seek> Decoder<R> {
pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult<u32> { pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult<u32> {
self.get_tag(tag)?.into_u32() self.get_tag(tag)?.into_u32()
} }
pub fn get_tag_u64(&mut self, tag: Tag) -> TiffResult<u64> {
self.get_tag(tag)?.into_u64()
}
/// Tries to retrieve a tag and convert it to the desired type. /// Tries to retrieve a tag and convert it to the desired type.
pub fn get_tag_u32_vec(&mut self, tag: Tag) -> TiffResult<Vec<u32>> { pub fn get_tag_u32_vec(&mut self, tag: Tag) -> TiffResult<Vec<u32>> {
self.get_tag(tag)?.into_u32_vec() self.get_tag(tag)?.into_u32_vec()
} }
pub fn get_tag_u64_vec(&mut self, tag: Tag) -> TiffResult<Vec<u64>> {
self.get_tag(tag)?.into_u64_vec()
}
/// Decompresses the strip into the supplied buffer. /// Decompresses the strip into the supplied buffer.
/// Returns the number of bytes read. /// Returns the number of bytes read.
fn expand_strip<'a>( fn expand_strip<'a>(
&mut self, &mut self,
buffer: DecodingBuffer<'a>, buffer: DecodingBuffer<'a>,
offset: u32, offset: u64,
length: u32, length: u64,
max_uncompressed_length: usize, max_uncompressed_length: usize,
) -> TiffResult<usize> { ) -> TiffResult<usize> {
let color_type = self.colortype()?; let color_type = self.colortype()?;
self.goto_offset(offset)?; self.goto_offset_u64(offset)?;
let (bytes, mut reader): (usize, Box<EndianReader>) = match self.compression_method { let (bytes, mut reader): (usize, Box<dyn EndianReader>) = match self.compression_method {
CompressionMethod::None => { CompressionMethod::None => {
let order = self.reader.byte_order; let order = self.reader.byte_order;
( (
@@ -600,19 +651,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 +752,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);
@@ -710,8 +765,8 @@ impl<R: Read + Seek> Decoder<R> {
fn initialize_strip_decoder(&mut self) -> TiffResult<()> { fn initialize_strip_decoder(&mut self) -> TiffResult<()> {
if self.strip_decoder.is_none() { if self.strip_decoder.is_none() {
let strip_offsets = self.get_tag_u32_vec(Tag::StripOffsets)?; let strip_offsets = self.get_tag_u64_vec(Tag::StripOffsets)?;
let strip_bytes = self.get_tag_u32_vec(Tag::StripByteCounts)?; let strip_bytes = self.get_tag_u64_vec(Tag::StripByteCounts)?;
self.strip_decoder = Some(StripDecodeState { self.strip_decoder = Some(StripDecodeState {
strip_index: 0, strip_index: 0,
@@ -745,8 +800,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 +811,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 +859,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 +872,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 +891,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();
+48 -61
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,32 +407,9 @@ impl<W: Write + Seek> TiffEncoder<W> {
where where
[C::Inner]: TiffValue, [C::Inner]: 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 {
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 encoder = DirectoryEncoder::new(&mut self.writer)?;
let mut image: ImageEncoder<W, C> = ImageEncoder::new(encoder, width, height)?; let image: ImageEncoder<W, C> = ImageEncoder::new(encoder, width, height)?;
image.write_data(data)
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])?;
idx += sample_count;
}
image.finish()
} }
} }
@@ -477,10 +447,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(())
} }
@@ -563,10 +531,14 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
/// # let mut file = std::io::Cursor::new(Vec::new()); /// # let mut file = std::io::Cursor::new(Vec::new());
/// # let image_data = vec![0; 100*100*3]; /// # let image_data = vec![0; 100*100*3];
/// use tiff::encoder::*; /// use tiff::encoder::*;
/// use tiff::tags::Tag;
/// ///
/// let mut tiff = TiffEncoder::new(&mut file).unwrap(); /// let mut tiff = TiffEncoder::new(&mut file).unwrap();
/// let mut image = tiff.new_image::<colortype::RGB8>(100, 100).unwrap(); /// let mut image = tiff.new_image::<colortype::RGB8>(100, 100).unwrap();
/// ///
/// // You can encode tags here
/// image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap();
///
/// let mut idx = 0; /// let mut idx = 0;
/// while image.next_strip_sample_count() > 0 { /// while image.next_strip_sample_count() > 0 {
/// let sample_count = image.next_strip_sample_count() as usize; /// let sample_count = image.next_strip_sample_count() as usize;
@@ -576,11 +548,13 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
/// image.finish().unwrap(); /// image.finish().unwrap();
/// # } /// # }
/// ``` /// ```
/// You can also call write_data function wich will encode by strip and finish
pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType> { pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType> {
encoder: DirectoryEncoder<'a, W>, encoder: DirectoryEncoder<'a, W>,
strip_idx: u64, strip_idx: u64,
strip_count: u64, strip_count: u64,
row_samples: u64, row_samples: u64,
width: u32,
height: u32, height: u32,
rows_per_strip: u64, rows_per_strip: u64,
strip_offsets: Vec<u32>, strip_offsets: Vec<u32>,
@@ -612,10 +586,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())?;
@@ -626,6 +597,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
strip_idx: 0, strip_idx: 0,
row_samples, row_samples,
rows_per_strip, rows_per_strip,
width,
height, height,
strip_offsets: Vec::new(), strip_offsets: Vec::new(),
strip_byte_count: Vec::new(), strip_byte_count: Vec::new(),
@@ -640,8 +612,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 +631,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)?;
@@ -673,22 +642,40 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
Ok(()) Ok(())
} }
/// Write strips from data
pub fn write_data(mut self, data: &[T::Inner]) -> TiffResult<()>
where
[T::Inner]: TiffValue,
{
let num_pix = usize::try_from(self.width)?.checked_mul(usize::try_from(self.height)?)
.ok_or_else(|| ::std::io::Error::new(
::std::io::ErrorKind::InvalidInput,
"Image width * height exceeds usize"))?;
if data.len() < num_pix {
return Err(::std::io::Error::new(
::std::io::ErrorKind::InvalidData,
"Input data slice is undersized for provided dimensions").into());
}
let mut idx = 0;
while self.next_strip_sample_count() > 0 {
let sample_count = usize::try_from(self.next_strip_sample_count())?;
self.write_strip(&data[idx..idx + sample_count])?;
idx += sample_count;
}
self.finish()?;
Ok(())
}
/// 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)
+1 -1
View File
@@ -4,7 +4,7 @@
//! lossless and lossy compression. //! lossless and lossy compression.
//! //!
//! # Related Links //! # Related Links
//! * <http://partners.adobe.com/public/developer/tiff/index.html> - The TIFF specification //! * <https://www.adobe.io/open/standards/TIFF.html> - The TIFF specification
extern crate byteorder; extern crate byteorder;
extern crate lzw; extern crate lzw;
+8 -2
View File
@@ -105,6 +105,11 @@ pub enum Tag(u16) unknown("A private or extension tag") {
YResolution = 283, YResolution = 283,
// Advanced tags // Advanced tags
Predictor = 317, Predictor = 317,
TileWidth = 322,
TileLength = 323,
TileOffsets = 324,
TileByteCounts = 325,
SampleFormat = 339,
} }
} }
@@ -131,6 +136,7 @@ pub enum Type(u16) {
} }
} }
tags! { tags! {
pub enum CompressionMethod(u16) { pub enum CompressionMethod(u16) {
None = 1, None = 1,
@@ -172,11 +178,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,
} }
} }
+46
View File
@@ -0,0 +1,46 @@
extern crate tiff;
use tiff::decoder::Decoder;
use tiff::tags::Tag;
use tiff::ColorType;
use std::fs::File;
use std::path::PathBuf;
const TEST_IMAGE_DIR: &str = "./tests/images/bigtiff";
#[test]
fn test_big_tiff() {
let filenames = ["BigTIFF.tif", "BigTIFFMotorola.tif", "BigTIFFLong.tif"];
for filename in filenames.iter() {
let path = PathBuf::from(TEST_IMAGE_DIR).join(filename);
let img_file = File::open(path).expect("Cannot find test image!");
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
assert_eq!(
decoder.dimensions().expect("Cannot get dimensions"),
(64, 64)
);
assert_eq!(
decoder.colortype().expect("Cannot get colortype"),
ColorType::RGB(8)
);
assert_eq!(
decoder
.get_tag_u64(Tag::StripOffsets)
.expect("Cannot get StripOffsets"),
16
);
assert_eq!(
decoder
.get_tag_u64(Tag::RowsPerStrip)
.expect("Cannot get RowsPerStrip"),
64
);
assert_eq!(
decoder
.get_tag_u64(Tag::StripByteCounts)
.expect("Cannot get StripByteCounts"),
12288
)
}
}
+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()
+49 -224
View File
@@ -1,9 +1,9 @@
extern crate tiff; extern crate tiff;
use tiff::decoder::{Decoder, DecodingResult}; use tiff::decoder::{Decoder, DecodingResult, ifd};
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};
@@ -24,14 +24,18 @@ fn encode_decode() {
{ {
let mut tiff = TiffEncoder::new(&mut file).unwrap(); let mut tiff = TiffEncoder::new(&mut file).unwrap();
tiff.write_image::<colortype::RGB8>(100, 100, &image_data) let mut image = tiff.new_image::<colortype::RGB8>(100, 100)
.unwrap(); .unwrap();
image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap();
image.write_data(&image_data).unwrap();
} }
{ {
file.seek(SeekFrom::Start(0)).unwrap(); file.seek(SeekFrom::Start(0)).unwrap();
let mut decoder = Decoder::new(&mut file).unwrap(); let mut decoder = Decoder::new(&mut file).unwrap();
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8)); assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
assert_eq!(decoder.dimensions().unwrap(), (100, 100)); 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() { if let DecodingResult::U8(img_res) = decoder.read_image().unwrap() {
assert_eq!(image_data, img_res); assert_eq!(image_data, img_res);
} else { } else {
@@ -58,10 +62,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 +78,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 +159,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 +189,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 +213,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 +245,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
@@ -438,4 +263,4 @@ fn test_multipage_image() {
// check the dimensions of the image in the second page // check the dimensions of the image in the second page
assert_eq!(img_decoder.dimensions().unwrap(), (3, 3)); assert_eq!(img_decoder.dimensions().unwrap(), (3, 3));
} }
} }
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.