Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff574b4ca2 | ||
|
|
6f87228458 | ||
|
|
15929f8b8e | ||
|
|
f13492e41d | ||
|
|
1d9944c5c5 | ||
|
|
a14b23473b | ||
|
|
101940335c | ||
|
|
8e1c67ee53 | ||
|
|
7b672998db | ||
|
|
62fee106a7 | ||
|
|
fd789dc420 | ||
|
|
07e8501ad6 | ||
|
|
f70447325e | ||
|
|
b2eb480c44 | ||
|
|
686e9b2648 | ||
|
|
517638a38f | ||
|
|
a6696ab02f | ||
|
|
258edf4a46 | ||
|
|
f88c9f00c3 | ||
|
|
f80f4a06d5 | ||
|
|
cbae42ea94 | ||
|
|
80b8ec5cdb | ||
|
|
ffdeb62b9f | ||
|
|
6aea7b33c1 | ||
|
|
770262cb02 | ||
|
|
1f957029af | ||
|
|
f8d4b57120 | ||
|
|
6f9f2adfbf | ||
|
|
205626bd62 | ||
|
|
ce73d39d57 | ||
|
|
b79128d766 | ||
|
|
c8f384d6a6 | ||
|
|
ce1d4cb289 | ||
|
|
b95e5b8f95 | ||
|
|
bc05e0b7c6 | ||
|
|
3868f78067 | ||
|
|
f6a91065d9 | ||
|
|
ea19f0f686 | ||
|
|
3b85b68ae9 | ||
|
|
30eb8487a6 | ||
|
|
7fb354fe43 |
No files matched your search
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
language: rust
|
language: rust
|
||||||
rust:
|
rust:
|
||||||
- 1.24.1
|
- 1.34.2
|
||||||
- stable
|
- stable
|
||||||
- beta
|
- beta
|
||||||
- nightly
|
- nightly
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
# Version 0.4.0
|
||||||
|
|
||||||
|
* Several enumerations are now non_exhaustive for future extensions.
|
||||||
|
These are `Tag`, `Type`, `Value`, `PhotometricInterpretation`,
|
||||||
|
`CompressionMethod`, `Predictor`.
|
||||||
|
* Enums gained a dedicated method to convert to their TIFF variant value with
|
||||||
|
the specified type. Performing these conversions by casting the discriminant
|
||||||
|
with `as` is not guaranteed to be stable, except where documented explicitly.
|
||||||
|
* Removed the num-derive and num dependencies.
|
||||||
|
* Added support for decoding `deflate` compressed images.
|
||||||
|
* Make the decoder `Limits` customizable by exposing members.
|
||||||
|
* Fixed multi-page TIFF encoding writing incorrect offsets.
|
||||||
+2
-3
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tiff"
|
name = "tiff"
|
||||||
version = "0.3.1"
|
version = "0.4.0"
|
||||||
authors = [
|
authors = [
|
||||||
"ccgn",
|
"ccgn",
|
||||||
"bvssvni <bvssvni@gmail.com>",
|
"bvssvni <bvssvni@gmail.com>",
|
||||||
@@ -19,8 +19,7 @@ exclude = ["tests/images/*", "tests/fuzz_images/*"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.2"
|
byteorder = "1.2"
|
||||||
lzw = "0.10"
|
lzw = "0.10"
|
||||||
num-derive = "0.2"
|
miniz_oxide = "0.3"
|
||||||
num-traits = "0.2"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.0"
|
tempfile = "3.0"
|
||||||
+238
-97
@@ -7,30 +7,45 @@ use std::mem;
|
|||||||
use super::stream::{ByteOrder, EndianReader, SmartReader};
|
use super::stream::{ByteOrder, EndianReader, SmartReader};
|
||||||
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
||||||
|
|
||||||
use self::Value::{Ascii, List, Rational, Unsigned};
|
use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational};
|
||||||
|
|
||||||
macro_rules! tags {
|
macro_rules! tags {
|
||||||
{$(
|
{
|
||||||
$tag:ident
|
// Permit arbitrary meta items, which include documentation.
|
||||||
$val:expr;
|
$( #[$enum_attr:meta] )*
|
||||||
)*} => {
|
$vis:vis enum $name:ident($ty:ty) $(unknown($unknown_doc:literal))* {
|
||||||
|
// Each of the `Name = Val,` permitting documentation.
|
||||||
/// TIFF tag
|
$($(#[$ident_attr:meta])* $tag:ident = $val:expr,)*
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
|
||||||
pub enum Tag {
|
|
||||||
$($tag,)*
|
|
||||||
Unknown(u16)
|
|
||||||
}
|
}
|
||||||
impl Tag {
|
} => {
|
||||||
pub fn from_u16(n: u16) -> Tag {
|
$( #[$enum_attr] )*
|
||||||
$(if n == $val { Tag::$tag } else)* {
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||||
Tag::Unknown(n)
|
pub enum $name {
|
||||||
|
$($(#[$ident_attr])* $tag,)*
|
||||||
|
// FIXME: switch to non_exhaustive once stabilized and compiler requirement new enough
|
||||||
|
#[doc(hidden)]
|
||||||
|
__NonExhaustive,
|
||||||
|
$(
|
||||||
|
#[doc = $unknown_doc]
|
||||||
|
Unknown($ty),
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $name {
|
||||||
|
#[inline(always)]
|
||||||
|
fn __from_inner_type(n: $ty) -> Result<Self, $ty> {
|
||||||
|
match n {
|
||||||
|
$( $val => Ok($name::$tag), )*
|
||||||
|
n => Err(n),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn to_u16(&self) -> u16 {
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn __to_inner_type(&self) -> $ty {
|
||||||
match *self {
|
match *self {
|
||||||
$( Tag::$tag => $val, )*
|
$( $name::$tag => $val, )*
|
||||||
Tag::Unknown(n) => n,
|
$( $name::Unknown(n) => { $unknown_doc; n }, )*
|
||||||
|
$name::__NonExhaustive => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,66 +54,99 @@ macro_rules! tags {
|
|||||||
|
|
||||||
// Note: These tags appear in the order they are mentioned in the TIFF reference
|
// Note: These tags appear in the order they are mentioned in the TIFF reference
|
||||||
tags! {
|
tags! {
|
||||||
|
/// TIFF tags
|
||||||
|
pub enum Tag(u16) unknown("A private or extension tag") {
|
||||||
// Baseline tags:
|
// Baseline tags:
|
||||||
Artist 315;
|
Artist = 315,
|
||||||
// grayscale images PhotometricInterpretation 1 or 3
|
// grayscale images PhotometricInterpretation 1 or 3
|
||||||
BitsPerSample 258;
|
BitsPerSample = 258,
|
||||||
CellLength 265; // TODO add support
|
CellLength = 265, // TODO add support
|
||||||
CellWidth 264; // TODO add support
|
CellWidth = 264, // TODO add support
|
||||||
// palette-color images (PhotometricInterpretation 3)
|
// palette-color images (PhotometricInterpretation 3)
|
||||||
ColorMap 320; // TODO add support
|
ColorMap = 320, // TODO add support
|
||||||
Compression 259; // TODO add support for 2 and 32773
|
Compression = 259, // TODO add support for 2 and 32773
|
||||||
Copyright 33_432;
|
Copyright = 33_432,
|
||||||
DateTime 306;
|
DateTime = 306,
|
||||||
ExtraSamples 338; // TODO add support
|
ExtraSamples = 338, // TODO add support
|
||||||
FillOrder 266; // TODO add support
|
FillOrder = 266, // TODO add support
|
||||||
FreeByteCounts 289; // TODO add support
|
FreeByteCounts = 289, // TODO add support
|
||||||
FreeOffsets 288; // TODO add support
|
FreeOffsets = 288, // TODO add support
|
||||||
GrayResponseCurve 291; // TODO add support
|
GrayResponseCurve = 291, // TODO add support
|
||||||
GrayResponseUnit 290; // TODO add support
|
GrayResponseUnit = 290, // TODO add support
|
||||||
HostComputer 316;
|
HostComputer = 316,
|
||||||
ImageDescription 270;
|
ImageDescription = 270,
|
||||||
ImageLength 257;
|
ImageLength = 257,
|
||||||
ImageWidth 256;
|
ImageWidth = 256,
|
||||||
Make 271;
|
Make = 271,
|
||||||
MaxSampleValue 281; // TODO add support
|
MaxSampleValue = 281, // TODO add support
|
||||||
MinSampleValue 280; // TODO add support
|
MinSampleValue = 280, // TODO add support
|
||||||
Model 272;
|
Model = 272,
|
||||||
NewSubfileType 254; // TODO add support
|
NewSubfileType = 254, // TODO add support
|
||||||
Orientation 274; // TODO add support
|
Orientation = 274, // TODO add support
|
||||||
PhotometricInterpretation 262;
|
PhotometricInterpretation = 262,
|
||||||
PlanarConfiguration 284;
|
PlanarConfiguration = 284,
|
||||||
ResolutionUnit 296; // TODO add support
|
ResolutionUnit = 296, // TODO add support
|
||||||
RowsPerStrip 278;
|
RowsPerStrip = 278,
|
||||||
SamplesPerPixel 277;
|
SamplesPerPixel = 277,
|
||||||
Software 305;
|
Software = 305,
|
||||||
StripByteCounts 279;
|
StripByteCounts = 279,
|
||||||
StripOffsets 273;
|
StripOffsets = 273,
|
||||||
SubfileType 255; // TODO add support
|
SubfileType = 255, // TODO add support
|
||||||
Threshholding 263; // TODO add support
|
Threshholding = 263, // TODO add support
|
||||||
XResolution 282;
|
XResolution = 282,
|
||||||
YResolution 283;
|
YResolution = 283,
|
||||||
// Advanced tags
|
// Advanced tags
|
||||||
Predictor 317;
|
Predictor = 317,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, FromPrimitive)]
|
impl Tag {
|
||||||
pub enum Type {
|
pub fn from_u16(val: u16) -> Self {
|
||||||
|
Self::__from_inner_type(val).unwrap_or_else(Tag::Unknown)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_u16(&self) -> u16 {
|
||||||
|
Self::__to_inner_type(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tags! {
|
||||||
|
/// The type of an IFD entry (a 2 byte field).
|
||||||
|
pub enum Type(u16) {
|
||||||
BYTE = 1,
|
BYTE = 1,
|
||||||
ASCII = 2,
|
ASCII = 2,
|
||||||
SHORT = 3,
|
SHORT = 3,
|
||||||
LONG = 4,
|
LONG = 4,
|
||||||
RATIONAL = 5,
|
RATIONAL = 5,
|
||||||
|
SBYTE = 6,
|
||||||
|
SSHORT = 8,
|
||||||
|
SLONG = 9,
|
||||||
|
SRATIONAL = 10,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Type {
|
||||||
|
pub fn from_u16(val: u16) -> Option<Self> {
|
||||||
|
Self::__from_inner_type(val).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_u16(&self) -> u16 {
|
||||||
|
Self::__to_inner_type(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[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),
|
||||||
Unsigned(u32),
|
Unsigned(u32),
|
||||||
List(Vec<Value>),
|
List(Vec<Value>),
|
||||||
Rational(u32, u32),
|
Rational(u32, u32),
|
||||||
|
SRational(i32, i32),
|
||||||
Ascii(String),
|
Ascii(String),
|
||||||
|
#[doc(hidden)] // Do not match against this.
|
||||||
|
__NonExhaustive,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
@@ -110,6 +158,16 @@ impl Value {
|
|||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_i32(self) -> TiffResult<i32> {
|
||||||
|
match self {
|
||||||
|
Signed(val) => Ok(val),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::SignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_u32_vec(self) -> TiffResult<Vec<u32>> {
|
pub fn into_u32_vec(self) -> TiffResult<Vec<u32>> {
|
||||||
match self {
|
match self {
|
||||||
List(vec) => {
|
List(vec) => {
|
||||||
@@ -122,6 +180,32 @@ impl Value {
|
|||||||
Unsigned(val) => Ok(vec![val]),
|
Unsigned(val) => Ok(vec![val]),
|
||||||
Rational(numerator, denominator) => Ok(vec![numerator, denominator]),
|
Rational(numerator, denominator) => Ok(vec![numerator, denominator]),
|
||||||
Ascii(val) => Ok(val.chars().map(|x| x as u32).collect()),
|
Ascii(val) => Ok(val.chars().map(|x| x as u32).collect()),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::UnsignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_i32_vec(self) -> TiffResult<Vec<i32>> {
|
||||||
|
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);
|
||||||
|
new_vec.push(denominator);
|
||||||
|
}
|
||||||
|
_ => new_vec.push(v.into_i32()?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(new_vec)
|
||||||
|
}
|
||||||
|
Signed(val) => Ok(vec![val]),
|
||||||
|
SRational(numerator, denominator) => Ok(vec![numerator, denominator]),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::SignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,7 +250,21 @@ impl Entry {
|
|||||||
// TODO check if this could give wrong results
|
// TODO check if this could give wrong results
|
||||||
// at a different endianess of file/computer.
|
// at a different endianess of file/computer.
|
||||||
(Type::BYTE, 1) => Ok(Unsigned(u32::from(self.offset[0]))),
|
(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(Unsigned(u32::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(Signed(i32::from(decoder.read_byte()? as i8)))
|
||||||
|
}),
|
||||||
(Type::SHORT, 1) => Ok(Unsigned(u32::from(self.r(bo).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) => {
|
(Type::SHORT, 2) => {
|
||||||
let mut r = self.r(bo);
|
let mut r = self.r(bo);
|
||||||
Ok(List(vec![
|
Ok(List(vec![
|
||||||
@@ -174,54 +272,57 @@ impl Entry {
|
|||||||
Unsigned(u32::from(r.read_u16()?)),
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
(Type::SHORT, n) => {
|
(Type::SSHORT, 2) => {
|
||||||
if n as usize > limits.decoding_buffer_size / mem::size_of::<Value>() {
|
let mut r = self.r(bo);
|
||||||
return Err(TiffError::LimitsExceeded);
|
Ok(List(vec![
|
||||||
}
|
Signed(i32::from(r.read_i16()?)),
|
||||||
let mut v = Vec::with_capacity(n as usize);
|
Signed(i32::from(r.read_i16()?)),
|
||||||
try!(decoder.goto_offset(try!(self.r(bo).read_u32())));
|
]))
|
||||||
for _ in 0..n {
|
|
||||||
v.push(Unsigned(u32::from(decoder.read_short()?)))
|
|
||||||
}
|
|
||||||
Ok(List(v))
|
|
||||||
}
|
|
||||||
(Type::LONG, 1) => Ok(Unsigned(try!(self.r(bo).read_u32()))),
|
|
||||||
(Type::LONG, n) => {
|
|
||||||
if n as usize > limits.decoding_buffer_size / mem::size_of::<Value>() {
|
|
||||||
return Err(TiffError::LimitsExceeded);
|
|
||||||
}
|
|
||||||
let mut v = Vec::with_capacity(n as usize);
|
|
||||||
try!(decoder.goto_offset(try!(self.r(bo).read_u32())));
|
|
||||||
for _ in 0..n {
|
|
||||||
v.push(Unsigned(try!(decoder.read_long())))
|
|
||||||
}
|
|
||||||
Ok(List(v))
|
|
||||||
}
|
}
|
||||||
|
(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) => {
|
(Type::RATIONAL, 1) => {
|
||||||
try!(decoder.goto_offset(try!(self.r(bo).read_u32())));
|
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
||||||
let numerator = try!(decoder.read_long());
|
let numerator = decoder.read_long()?;
|
||||||
let denominator = try!(decoder.read_long());
|
let denominator = decoder.read_long()?;
|
||||||
Ok(Rational(numerator, denominator))
|
Ok(Rational(numerator, denominator))
|
||||||
}
|
}
|
||||||
(Type::RATIONAL, n) => {
|
(Type::SRATIONAL, 1) => {
|
||||||
if n as usize > limits.decoding_buffer_size / mem::size_of::<Value>() {
|
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
||||||
return Err(TiffError::LimitsExceeded);
|
let numerator = decoder.read_slong()?;
|
||||||
}
|
let denominator = decoder.read_slong()?;
|
||||||
let mut v = Vec::with_capacity(n as usize);
|
Ok(SRational(numerator, denominator))
|
||||||
try!(decoder.goto_offset(try!(self.r(bo).read_u32())));
|
|
||||||
for _ in 0..n {
|
|
||||||
let numerator = try!(decoder.read_long());
|
|
||||||
let denominator = try!(decoder.read_long());
|
|
||||||
v.push(Rational(numerator, denominator))
|
|
||||||
}
|
|
||||||
Ok(List(v))
|
|
||||||
}
|
}
|
||||||
|
(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) => {
|
(Type::ASCII, n) => {
|
||||||
if n as usize > limits.decoding_buffer_size {
|
if n as usize > limits.decoding_buffer_size {
|
||||||
return Err(TiffError::LimitsExceeded);
|
return Err(TiffError::LimitsExceeded);
|
||||||
}
|
}
|
||||||
try!(decoder.goto_offset(try!(self.r(bo).read_u32())));
|
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
||||||
let string = try!(decoder.read_string(n as usize));
|
let string = decoder.read_string(n as usize)?;
|
||||||
Ok(Ascii(string))
|
Ok(Ascii(string))
|
||||||
}
|
}
|
||||||
_ => Err(TiffError::UnsupportedError(
|
_ => Err(TiffError::UnsupportedError(
|
||||||
@@ -229,6 +330,46 @@ impl Entry {
|
|||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn decode_offset<R, F>(&self, value_count: u32, bo: ByteOrder, 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>,
|
||||||
|
{
|
||||||
|
if value_count as usize > limits.decoding_buffer_size / mem::size_of::<Value>() {
|
||||||
|
return Err(TiffError::LimitsExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut v = Vec::with_capacity(value_count as usize);
|
||||||
|
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
||||||
|
for _ in 0..value_count {
|
||||||
|
v.push(decode_fn(decoder)?)
|
||||||
|
}
|
||||||
|
Ok(List(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts a list of BYTE tags stored in an offset
|
||||||
|
#[inline]
|
||||||
|
fn offset_to_bytes(n: usize, entry: &Entry) -> TiffResult<Value> {
|
||||||
|
Ok(List(
|
||||||
|
entry.offset[0..n]
|
||||||
|
.iter()
|
||||||
|
.map(|&e| Unsigned(u32::from(e)))
|
||||||
|
.collect()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts a list of SBYTE tags stored in an offset
|
||||||
|
#[inline]
|
||||||
|
fn offset_to_sbytes(n: usize, entry: &Entry) -> TiffResult<Value> {
|
||||||
|
Ok(List(
|
||||||
|
entry.offset[0..n]
|
||||||
|
.iter()
|
||||||
|
.map(|&e| Signed(i32::from(e as i8)))
|
||||||
|
.collect()
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type representing an Image File Directory
|
/// Type representing an Image File Directory
|
||||||
|
|||||||
+131
-52
@@ -1,4 +1,3 @@
|
|||||||
use num_traits::{FromPrimitive, Num};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::{self, Read, Seek};
|
use std::io::{self, Read, Seek};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
@@ -7,8 +6,9 @@ use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
|||||||
|
|
||||||
use self::ifd::Directory;
|
use self::ifd::Directory;
|
||||||
|
|
||||||
use self::stream::{ByteOrder, EndianReader, LZWReader, PackBitsReader, SmartReader};
|
use self::stream::{ByteOrder, EndianReader, LZWReader, DeflateReader, PackBitsReader, SmartReader};
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
pub mod ifd;
|
pub mod ifd;
|
||||||
mod stream;
|
mod stream;
|
||||||
|
|
||||||
@@ -80,8 +80,8 @@ impl<'a> DecodingBuffer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, FromPrimitive)]
|
tags! {
|
||||||
pub enum PhotometricInterpretation {
|
pub enum PhotometricInterpretation(u16) {
|
||||||
WhiteIsZero = 0,
|
WhiteIsZero = 0,
|
||||||
BlackIsZero = 1,
|
BlackIsZero = 1,
|
||||||
RGB = 2,
|
RGB = 2,
|
||||||
@@ -91,29 +91,75 @@ pub enum PhotometricInterpretation {
|
|||||||
YCbCr = 6,
|
YCbCr = 6,
|
||||||
CIELab = 8,
|
CIELab = 8,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, FromPrimitive)]
|
tags! {
|
||||||
pub enum CompressionMethod {
|
pub enum CompressionMethod(u16) {
|
||||||
None = 1,
|
None = 1,
|
||||||
Huffman = 2,
|
Huffman = 2,
|
||||||
Fax3 = 3,
|
Fax3 = 3,
|
||||||
Fax4 = 4,
|
Fax4 = 4,
|
||||||
LZW = 5,
|
LZW = 5,
|
||||||
JPEG = 6,
|
JPEG = 6,
|
||||||
|
Deflate = 8,
|
||||||
|
OldDeflate = 0x80B2,
|
||||||
PackBits = 0x8005,
|
PackBits = 0x8005,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, FromPrimitive)]
|
tags! {
|
||||||
pub enum PlanarConfiguration {
|
pub enum PlanarConfiguration(u16) {
|
||||||
Chunky = 1,
|
Chunky = 1,
|
||||||
Planar = 2,
|
Planar = 2,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, FromPrimitive)]
|
tags! {
|
||||||
enum Predictor {
|
enum Predictor(u16) {
|
||||||
None = 1,
|
None = 1,
|
||||||
Horizontal = 2,
|
Horizontal = 2,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PhotometricInterpretation {
|
||||||
|
pub fn from_u16(val: u16) -> Option<Self> {
|
||||||
|
Self::__from_inner_type(val).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_u16(&self) -> u16 {
|
||||||
|
Self::__to_inner_type(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompressionMethod {
|
||||||
|
pub fn from_u16(val: u16) -> Option<Self> {
|
||||||
|
Self::__from_inner_type(val).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_u16(&self) -> u16 {
|
||||||
|
Self::__to_inner_type(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlanarConfiguration {
|
||||||
|
pub fn from_u16(val: u16) -> Option<Self> {
|
||||||
|
Self::__from_inner_type(val).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_u16(&self) -> u16 {
|
||||||
|
Self::__to_inner_type(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Predictor {
|
||||||
|
pub fn from_u16(val: u16) -> Option<Self> {
|
||||||
|
Self::__from_inner_type(val).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_u16(&self) -> u16 {
|
||||||
|
Self::__to_inner_type(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct StripDecodeState {
|
struct StripDecodeState {
|
||||||
@@ -129,10 +175,14 @@ pub struct Limits {
|
|||||||
/// 256MiB. If the entire image is decoded at once, then this will
|
/// 256MiB. If the entire image is decoded at once, then this will
|
||||||
/// be the maximum size of the image. If it is decoded one strip at a
|
/// be the maximum size of the image. If it is decoded one strip at a
|
||||||
/// time, this will be the maximum size of a strip.
|
/// time, this will be the maximum size of a strip.
|
||||||
decoding_buffer_size: usize,
|
pub decoding_buffer_size: usize,
|
||||||
/// The maximum size of any ifd value in bytes, the default is
|
/// The maximum size of any ifd value in bytes, the default is
|
||||||
/// 1MiB.
|
/// 1MiB.
|
||||||
ifd_value_size: usize,
|
pub ifd_value_size: usize,
|
||||||
|
/// The purpose of this is to prevent all the fields of the struct from
|
||||||
|
/// being public, as this would make adding new fields a major version
|
||||||
|
/// bump.
|
||||||
|
_non_exhaustive: (),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Limits {
|
impl Default for Limits {
|
||||||
@@ -140,6 +190,7 @@ impl Default for Limits {
|
|||||||
Limits {
|
Limits {
|
||||||
decoding_buffer_size: 256 * 1024 * 1024,
|
decoding_buffer_size: 256 * 1024 * 1024,
|
||||||
ifd_value_size: 1024 * 1024,
|
ifd_value_size: 1024 * 1024,
|
||||||
|
_non_exhaustive: (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,7 +235,7 @@ impl Wrapping for u16 {
|
|||||||
|
|
||||||
fn rev_hpredict_nsamp<T>(image: &mut [T], size: (u32, u32), samples: usize)
|
fn rev_hpredict_nsamp<T>(image: &mut [T], size: (u32, u32), samples: usize)
|
||||||
where
|
where
|
||||||
T: Num + Copy + Wrapping,
|
T: Copy + Wrapping,
|
||||||
{
|
{
|
||||||
let width = size.0 as usize;
|
let width = size.0 as usize;
|
||||||
let height = size.1 as usize;
|
let height = size.1 as usize;
|
||||||
@@ -284,7 +335,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
fn read_header(&mut self) -> TiffResult<()> {
|
fn read_header(&mut self) -> TiffResult<()> {
|
||||||
let mut endianess = Vec::with_capacity(2);
|
let mut endianess = Vec::with_capacity(2);
|
||||||
try!(self.reader.by_ref().take(2).read_to_end(&mut endianess));
|
self.reader.by_ref().take(2).read_to_end(&mut endianess)?;
|
||||||
match &*endianess {
|
match &*endianess {
|
||||||
b"II" => {
|
b"II" => {
|
||||||
self.byte_order = ByteOrder::LittleEndian;
|
self.byte_order = ByteOrder::LittleEndian;
|
||||||
@@ -300,12 +351,12 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if try!(self.read_short()) != 42 {
|
if self.read_short()? != 42 {
|
||||||
return Err(TiffError::FormatError(
|
return Err(TiffError::FormatError(
|
||||||
TiffFormatError::TiffSignatureInvalid,
|
TiffFormatError::TiffSignatureInvalid,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
self.next_ifd = match try!(self.read_long()) {
|
self.next_ifd = match self.read_long()? {
|
||||||
0 => None,
|
0 => None,
|
||||||
n => Some(n),
|
n => Some(n),
|
||||||
};
|
};
|
||||||
@@ -314,7 +365,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
/// Initializes the decoder.
|
/// Initializes the decoder.
|
||||||
pub fn init(mut self) -> TiffResult<Decoder<R>> {
|
pub fn init(mut self) -> TiffResult<Decoder<R>> {
|
||||||
try!(self.read_header());
|
self.read_header()?;
|
||||||
self.next_image()?;
|
self.next_image()?;
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
@@ -323,13 +374,14 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
/// If there is no further image in the TIFF file a format error is returned.
|
/// If there is no further image in the TIFF file a format error is returned.
|
||||||
/// To determine whether there are more images call `TIFFDecoder::more_images` instead.
|
/// To determine whether there are more images call `TIFFDecoder::more_images` instead.
|
||||||
pub fn next_image(&mut self) -> TiffResult<()> {
|
pub fn next_image(&mut self) -> TiffResult<()> {
|
||||||
self.ifd = Some(try!(self.read_ifd()));
|
self.ifd = Some(self.read_ifd()?);
|
||||||
self.width = try!(self.get_tag_u32(ifd::Tag::ImageWidth));
|
self.width = self.get_tag_u32(ifd::Tag::ImageWidth)?;
|
||||||
self.height = try!(self.get_tag_u32(ifd::Tag::ImageLength));
|
self.height = self.get_tag_u32(ifd::Tag::ImageLength)?;
|
||||||
self.strip_decoder = None;
|
self.strip_decoder = None;
|
||||||
self.photometric_interpretation = match FromPrimitive::from_u32(try!(
|
// TODO: error on non-SHORT value.
|
||||||
self.get_tag_u32(ifd::Tag::PhotometricInterpretation)
|
self.photometric_interpretation = match PhotometricInterpretation::from_u16(
|
||||||
)) {
|
self.get_tag_u32(ifd::Tag::PhotometricInterpretation)? as u16
|
||||||
|
) {
|
||||||
Some(val) => val,
|
Some(val) => val,
|
||||||
None => {
|
None => {
|
||||||
return Err(TiffError::UnsupportedError(
|
return Err(TiffError::UnsupportedError(
|
||||||
@@ -337,8 +389,9 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Some(val) = try!(self.find_tag_u32(ifd::Tag::Compression)) {
|
// TODO: error on non-SHORT value.
|
||||||
match FromPrimitive::from_u32(val) {
|
if let Some(val) = self.find_tag_u32(ifd::Tag::Compression)? {
|
||||||
|
match CompressionMethod::from_u16(val as u16) {
|
||||||
Some(method) => self.compression_method = method,
|
Some(method) => self.compression_method = method,
|
||||||
None => {
|
None => {
|
||||||
return Err(TiffError::UnsupportedError(
|
return Err(TiffError::UnsupportedError(
|
||||||
@@ -347,17 +400,17 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(val) = try!(self.find_tag_u32(ifd::Tag::SamplesPerPixel)) {
|
if let Some(val) = self.find_tag_u32(ifd::Tag::SamplesPerPixel)? {
|
||||||
self.samples = val as u8
|
self.samples = val as u8
|
||||||
}
|
}
|
||||||
match self.samples {
|
match self.samples {
|
||||||
1 => {
|
1 => {
|
||||||
if let Some(val) = try!(self.find_tag_u32(ifd::Tag::BitsPerSample)) {
|
if let Some(val) = self.find_tag_u32(ifd::Tag::BitsPerSample)? {
|
||||||
self.bits_per_sample = vec![val as u8]
|
self.bits_per_sample = vec![val as u8]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
3 | 4 => {
|
3 | 4 => {
|
||||||
if let Some(val) = try!(self.find_tag_u32_vec(ifd::Tag::BitsPerSample)) {
|
if let Some(val) = self.find_tag_u32_vec(ifd::Tag::BitsPerSample)? {
|
||||||
self.bits_per_sample = val.iter().map(|&v| v as u8).collect()
|
self.bits_per_sample = val.iter().map(|&v| v as u8).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -380,18 +433,38 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
self.byte_order
|
self.byte_order
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads a TIFF byte value
|
||||||
|
#[inline]
|
||||||
|
pub fn read_byte(&mut self) -> Result<u8, io::Error> {
|
||||||
|
let mut buf = [0; 1];
|
||||||
|
self.reader.read_exact(&mut buf)?;
|
||||||
|
Ok(buf[0])
|
||||||
|
}
|
||||||
|
|
||||||
/// Reads a TIFF short value
|
/// Reads a TIFF short value
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_short(&mut self) -> Result<u16, io::Error> {
|
pub fn read_short(&mut self) -> Result<u16, io::Error> {
|
||||||
self.reader.read_u16()
|
self.reader.read_u16()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads a TIFF sshort value
|
||||||
|
#[inline]
|
||||||
|
pub fn read_sshort(&mut self) -> Result<i16, io::Error> {
|
||||||
|
self.reader.read_i16()
|
||||||
|
}
|
||||||
|
|
||||||
/// Reads a TIFF long value
|
/// Reads a TIFF long value
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_long(&mut self) -> Result<u32, io::Error> {
|
pub fn read_long(&mut self) -> Result<u32, io::Error> {
|
||||||
self.reader.read_u32()
|
self.reader.read_u32()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads a TIFF slong value
|
||||||
|
#[inline]
|
||||||
|
pub fn read_slong(&mut self) -> Result<i32, io::Error> {
|
||||||
|
self.reader.read_i32()
|
||||||
|
}
|
||||||
|
|
||||||
/// 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> {
|
||||||
@@ -406,7 +479,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_offset(&mut self) -> Result<[u8; 4], io::Error> {
|
pub fn read_offset(&mut self) -> Result<[u8; 4], io::Error> {
|
||||||
let mut val = [0; 4];
|
let mut val = [0; 4];
|
||||||
try!(self.reader.read_exact(&mut val));
|
self.reader.read_exact(&mut val)?;
|
||||||
Ok(val)
|
Ok(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,13 +499,13 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
// Count 4 bytes
|
// Count 4 bytes
|
||||||
// Value 4 bytes either a pointer the value itself
|
// Value 4 bytes either a pointer the value itself
|
||||||
fn read_entry(&mut self) -> TiffResult<Option<(ifd::Tag, ifd::Entry)>> {
|
fn read_entry(&mut self) -> TiffResult<Option<(ifd::Tag, ifd::Entry)>> {
|
||||||
let tag = ifd::Tag::from_u16(try!(self.read_short()));
|
let tag = ifd::Tag::from_u16(self.read_short()?);
|
||||||
let type_: ifd::Type = match FromPrimitive::from_u16(try!(self.read_short())) {
|
let type_ = match ifd::Type::from_u16(self.read_short()?) {
|
||||||
Some(t) => t,
|
Some(t) => t,
|
||||||
None => {
|
None => {
|
||||||
// Unknown type. Skip this entry according to spec.
|
// Unknown type. Skip this entry according to spec.
|
||||||
try!(self.read_long());
|
self.read_long()?;
|
||||||
try!(self.read_long());
|
self.read_long()?;
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -440,8 +513,8 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
tag,
|
tag,
|
||||||
ifd::Entry::new(
|
ifd::Entry::new(
|
||||||
type_,
|
type_,
|
||||||
try!(self.read_long()), // count
|
self.read_long()?, // count
|
||||||
try!(self.read_offset()), // offset
|
self.read_offset()?, // offset
|
||||||
),
|
),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
@@ -455,16 +528,16 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
TiffFormatError::ImageFileDirectoryNotFound,
|
TiffFormatError::ImageFileDirectoryNotFound,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
Some(offset) => try!(self.goto_offset(offset)),
|
Some(offset) => self.goto_offset(offset)?,
|
||||||
}
|
}
|
||||||
for _ in 0..try!(self.read_short()) {
|
for _ in 0..self.read_short()? {
|
||||||
let (tag, entry) = match try!(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 try!(self.read_long()) {
|
self.next_ifd = match self.read_long()? {
|
||||||
0 => None,
|
0 => None,
|
||||||
n => Some(n),
|
n => Some(n),
|
||||||
};
|
};
|
||||||
@@ -481,7 +554,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
let limits = self.limits.clone();
|
let limits = self.limits.clone();
|
||||||
|
|
||||||
Ok(Some(try!(entry.val(&limits, self))))
|
Ok(Some(entry.val(&limits, self)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to retrieve a tag and convert it to the desired type.
|
/// Tries to retrieve a tag and convert it to the desired type.
|
||||||
@@ -503,7 +576,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
/// Tries to retrieve a tag.
|
/// Tries to retrieve a tag.
|
||||||
/// Returns an error if the tag is not present
|
/// Returns an error if the tag is not present
|
||||||
pub fn get_tag(&mut self, tag: ifd::Tag) -> TiffResult<ifd::Value> {
|
pub fn get_tag(&mut self, tag: ifd::Tag) -> TiffResult<ifd::Value> {
|
||||||
match try!(self.find_tag(tag)) {
|
match self.find_tag(tag)? {
|
||||||
Some(val) => Ok(val),
|
Some(val) => Ok(val),
|
||||||
None => Err(TiffError::FormatError(
|
None => Err(TiffError::FormatError(
|
||||||
TiffFormatError::RequiredTagNotFound(tag),
|
TiffFormatError::RequiredTagNotFound(tag),
|
||||||
@@ -530,8 +603,8 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
length: u32,
|
length: u32,
|
||||||
max_uncompressed_length: usize,
|
max_uncompressed_length: usize,
|
||||||
) -> TiffResult<usize> {
|
) -> TiffResult<usize> {
|
||||||
let color_type = try!(self.colortype());
|
let color_type = self.colortype()?;
|
||||||
try!(self.goto_offset(offset));
|
self.goto_offset(offset)?;
|
||||||
let (bytes, mut reader): (usize, Box<EndianReader>) = match self.compression_method {
|
let (bytes, mut reader): (usize, Box<EndianReader>) = match self.compression_method {
|
||||||
CompressionMethod::None => {
|
CompressionMethod::None => {
|
||||||
let order = self.reader.byte_order;
|
let order = self.reader.byte_order;
|
||||||
@@ -541,20 +614,24 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
CompressionMethod::LZW => {
|
CompressionMethod::LZW => {
|
||||||
let (bytes, reader) = try!(LZWReader::new(
|
let (bytes, reader) = LZWReader::new(
|
||||||
&mut self.reader,
|
&mut self.reader,
|
||||||
length as usize,
|
length as usize,
|
||||||
max_uncompressed_length
|
max_uncompressed_length
|
||||||
));
|
)?;
|
||||||
(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) = try!(PackBitsReader::new(
|
let (bytes, reader) = PackBitsReader::new(
|
||||||
&mut self.reader,
|
&mut self.reader,
|
||||||
order,
|
order,
|
||||||
length as usize
|
length as usize
|
||||||
));
|
)?;
|
||||||
|
(bytes, Box::new(reader))
|
||||||
|
}
|
||||||
|
CompressionMethod::OldDeflate => {
|
||||||
|
let (bytes, reader) = DeflateReader::new(&mut self.reader, max_uncompressed_length)?;
|
||||||
(bytes, Box::new(reader))
|
(bytes, Box::new(reader))
|
||||||
}
|
}
|
||||||
method => {
|
method => {
|
||||||
@@ -574,16 +651,16 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
(ColorType::RGB(8), DecodingBuffer::U8(ref mut buffer))
|
(ColorType::RGB(8), DecodingBuffer::U8(ref mut buffer))
|
||||||
| (ColorType::RGBA(8), DecodingBuffer::U8(ref mut buffer))
|
| (ColorType::RGBA(8), DecodingBuffer::U8(ref mut buffer))
|
||||||
| (ColorType::CMYK(8), DecodingBuffer::U8(ref mut buffer)) => {
|
| (ColorType::CMYK(8), DecodingBuffer::U8(ref mut buffer)) => {
|
||||||
try!(reader.read_exact(&mut buffer[..bytes]));
|
reader.read_exact(&mut buffer[..bytes])?;
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
(ColorType::RGBA(16), DecodingBuffer::U16(ref mut buffer))
|
(ColorType::RGBA(16), DecodingBuffer::U16(ref mut buffer))
|
||||||
| (ColorType::RGB(16), DecodingBuffer::U16(ref mut buffer)) => {
|
| (ColorType::RGB(16), DecodingBuffer::U16(ref mut buffer)) => {
|
||||||
try!(reader.read_u16_into(&mut buffer[..bytes / 2]));
|
reader.read_u16_into(&mut buffer[..bytes / 2])?;
|
||||||
bytes / 2
|
bytes / 2
|
||||||
}
|
}
|
||||||
(ColorType::Gray(16), DecodingBuffer::U16(ref mut buffer)) => {
|
(ColorType::Gray(16), DecodingBuffer::U16(ref mut buffer)) => {
|
||||||
try!(reader.read_u16_into(&mut buffer[..bytes / 2]));
|
reader.read_u16_into(&mut buffer[..bytes / 2])?;
|
||||||
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
||||||
for datum in buffer[..bytes / 2].iter_mut() {
|
for datum in buffer[..bytes / 2].iter_mut() {
|
||||||
*datum = 0xffff - *datum
|
*datum = 0xffff - *datum
|
||||||
@@ -592,7 +669,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
bytes / 2
|
bytes / 2
|
||||||
}
|
}
|
||||||
(ColorType::Gray(n), DecodingBuffer::U8(ref mut buffer)) if n <= 8 => {
|
(ColorType::Gray(n), DecodingBuffer::U8(ref mut buffer)) if n <= 8 => {
|
||||||
try!(reader.read_exact(&mut buffer[..bytes]));
|
reader.read_exact(&mut buffer[..bytes])?;
|
||||||
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
||||||
for byte in buffer[..bytes].iter_mut() {
|
for byte in buffer[..bytes].iter_mut() {
|
||||||
*byte = 0xff - *byte
|
*byte = 0xff - *byte
|
||||||
@@ -689,7 +766,8 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let Ok(predictor) = self.get_tag_u32(ifd::Tag::Predictor) {
|
if let Ok(predictor) = self.get_tag_u32(ifd::Tag::Predictor) {
|
||||||
match FromPrimitive::from_u32(predictor) {
|
// TODO: error on non-SHORT value.
|
||||||
|
match Predictor::from_u16(predictor as u16) {
|
||||||
Some(Predictor::None) => (),
|
Some(Predictor::None) => (),
|
||||||
Some(Predictor::Horizontal) => {
|
Some(Predictor::Horizontal) => {
|
||||||
rev_hpredict(
|
rev_hpredict(
|
||||||
@@ -703,6 +781,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
predictor,
|
predictor,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
Some(Predictor::__NonExhaustive) => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
+71
-5
@@ -1,7 +1,9 @@
|
|||||||
//! All IO functionality needed for TIFF decoding
|
//! All IO functionality needed for TIFF decoding
|
||||||
|
|
||||||
|
use crate::error::{TiffError, TiffResult};
|
||||||
use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
|
use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
|
||||||
use lzw;
|
use lzw;
|
||||||
|
use miniz_oxide::inflate;
|
||||||
use std::io::{self, Read, Seek};
|
use std::io::{self, Read, Seek};
|
||||||
|
|
||||||
/// Byte order of the TIFF file.
|
/// Byte order of the TIFF file.
|
||||||
@@ -39,6 +41,15 @@ pub trait EndianReader: Read {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads an i16
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_i16(&mut self) -> Result<i16, io::Error> {
|
||||||
|
match self.byte_order() {
|
||||||
|
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_i16::<LittleEndian>(self),
|
||||||
|
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_i16::<BigEndian>(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Reads an u32
|
/// Reads an u32
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn read_u32(&mut self) -> Result<u32, io::Error> {
|
fn read_u32(&mut self) -> Result<u32, io::Error> {
|
||||||
@@ -47,6 +58,61 @@ pub trait EndianReader: Read {
|
|||||||
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_u32::<BigEndian>(self),
|
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_u32::<BigEndian>(self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads an i32
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_i32(&mut self) -> Result<i32, io::Error> {
|
||||||
|
match self.byte_order() {
|
||||||
|
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_i32::<LittleEndian>(self),
|
||||||
|
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_i32::<BigEndian>(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reader that decompresses DEFLATE streams
|
||||||
|
pub struct DeflateReader {
|
||||||
|
buffer: io::Cursor<Vec<u8>>,
|
||||||
|
byte_order: ByteOrder,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeflateReader {
|
||||||
|
pub fn new<R: Read + Seek>(
|
||||||
|
reader: &mut SmartReader<R>,
|
||||||
|
max_uncompressed_length: usize,
|
||||||
|
) -> TiffResult<(usize, Self)> {
|
||||||
|
let byte_order = reader.byte_order;
|
||||||
|
let mut compressed = Vec::new();
|
||||||
|
reader.read_to_end(&mut compressed)?;
|
||||||
|
|
||||||
|
// TODO: Implement streaming compression, and remove this (temporary) and somewhat
|
||||||
|
// misleading workaround.
|
||||||
|
if compressed.len() > max_uncompressed_length {
|
||||||
|
return Err(TiffError::LimitsExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
let uncompressed =
|
||||||
|
inflate::decompress_to_vec_zlib(&compressed).map_err(TiffError::from_inflate_status)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
uncompressed.len(),
|
||||||
|
Self {
|
||||||
|
byte_order,
|
||||||
|
buffer: io::Cursor::new(uncompressed),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for DeflateReader {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
self.buffer.read(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EndianReader for DeflateReader {
|
||||||
|
fn byte_order(&self) -> ByteOrder {
|
||||||
|
self.byte_order
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reader that decompresses LZW streams
|
/// Reader that decompresses LZW streams
|
||||||
@@ -67,12 +133,12 @@ impl LZWReader {
|
|||||||
{
|
{
|
||||||
let order = reader.byte_order;
|
let order = reader.byte_order;
|
||||||
let mut compressed = vec![0; compressed_length as usize];
|
let mut compressed = vec![0; compressed_length as usize];
|
||||||
try!(reader.read_exact(&mut compressed[..]));
|
reader.read_exact(&mut compressed[..])?;
|
||||||
let mut uncompressed = Vec::with_capacity(max_uncompressed_length);
|
let mut uncompressed = Vec::with_capacity(max_uncompressed_length);
|
||||||
let mut decoder = lzw::DecoderEarlyChange::new(lzw::MsbReader::new(), 8);
|
let mut decoder = lzw::DecoderEarlyChange::new(lzw::MsbReader::new(), 8);
|
||||||
let mut bytes_read = 0;
|
let mut bytes_read = 0;
|
||||||
while bytes_read < compressed_length && uncompressed.len() < max_uncompressed_length {
|
while bytes_read < compressed_length && uncompressed.len() < max_uncompressed_length {
|
||||||
let (len, bytes) = try!(decoder.decode_bytes(&compressed[bytes_read..]));
|
let (len, bytes) = decoder.decode_bytes(&compressed[bytes_read..])?;
|
||||||
bytes_read += len;
|
bytes_read += len;
|
||||||
uncompressed.extend_from_slice(bytes);
|
uncompressed.extend_from_slice(bytes);
|
||||||
}
|
}
|
||||||
@@ -137,7 +203,7 @@ impl PackBitsReader {
|
|||||||
fn read_packbits_run<R: Read + Seek>(reader: &mut R, buffer: &mut Vec<u8>) -> io::Result<usize> {
|
fn read_packbits_run<R: Read + Seek>(reader: &mut R, buffer: &mut Vec<u8>) -> io::Result<usize> {
|
||||||
let mut header: [u8; 1] = [0];
|
let mut header: [u8; 1] = [0];
|
||||||
|
|
||||||
let bytes = try!(reader.read(&mut header));
|
let bytes = reader.read(&mut header)?;
|
||||||
|
|
||||||
match bytes {
|
match bytes {
|
||||||
0 => Ok(0),
|
0 => Ok(0),
|
||||||
@@ -145,7 +211,7 @@ fn read_packbits_run<R: Read + Seek>(reader: &mut R, buffer: &mut Vec<u8>) -> io
|
|||||||
-128 => Ok(1),
|
-128 => Ok(1),
|
||||||
h if h >= -127 && h <= -1 => {
|
h if h >= -127 && h <= -1 => {
|
||||||
let new_len = buffer.len() + (1 - h as isize) as usize;
|
let new_len = buffer.len() + (1 - h as isize) as usize;
|
||||||
try!(reader.read_exact(&mut header));
|
reader.read_exact(&mut header)?;
|
||||||
buffer.resize(new_len, header[0]);
|
buffer.resize(new_len, header[0]);
|
||||||
Ok(2)
|
Ok(2)
|
||||||
}
|
}
|
||||||
@@ -153,7 +219,7 @@ fn read_packbits_run<R: Read + Seek>(reader: &mut R, buffer: &mut Vec<u8>) -> io
|
|||||||
let num_vals = h as usize + 1;
|
let num_vals = h as usize + 1;
|
||||||
let start = buffer.len();
|
let start = buffer.len();
|
||||||
buffer.resize(start + num_vals, 0);
|
buffer.resize(start + num_vals, 0);
|
||||||
try!(reader.read_exact(&mut buffer[start..]));
|
reader.read_exact(&mut buffer[start..])?;
|
||||||
Ok(num_vals + 1)
|
Ok(num_vals + 1)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+137
-4
@@ -1,6 +1,7 @@
|
|||||||
use byteorder::NativeEndian;
|
use byteorder::NativeEndian;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::io::{Seek, Write};
|
use std::io::{Seek, Write};
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
use decoder::ifd::{self, Tag};
|
use decoder::ifd::{self, Tag};
|
||||||
use error::{TiffError, TiffFormatError, TiffResult};
|
use error::{TiffError, TiffFormatError, TiffResult};
|
||||||
@@ -18,6 +19,12 @@ pub struct Rational {
|
|||||||
pub d: u32,
|
pub d: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Type to represent tiff values of type `SRATIONAL`
|
||||||
|
pub struct SRational {
|
||||||
|
pub n: i32,
|
||||||
|
pub d: i32,
|
||||||
|
}
|
||||||
|
|
||||||
/// Type to represent resolution units
|
/// Type to represent resolution units
|
||||||
pub enum ResolutionUnit {
|
pub enum ResolutionUnit {
|
||||||
None = 1,
|
None = 1,
|
||||||
@@ -50,6 +57,23 @@ impl TiffValue for [u8] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [i8] {
|
||||||
|
const BYTE_LEN: u32 = 1;
|
||||||
|
const FIELD_TYPE: ifd::Type = ifd::Type::SBYTE;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
self.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
// We write using nativeedian so this should be safe
|
||||||
|
let slice =
|
||||||
|
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len()) };
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TiffValue for [u16] {
|
impl TiffValue for [u16] {
|
||||||
const BYTE_LEN: u32 = 2;
|
const BYTE_LEN: u32 = 2;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::SHORT;
|
const FIELD_TYPE: ifd::Type = ifd::Type::SHORT;
|
||||||
@@ -67,6 +91,23 @@ impl TiffValue for [u16] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [i16] {
|
||||||
|
const BYTE_LEN: u32 = 2;
|
||||||
|
const FIELD_TYPE: ifd::Type = ifd::Type::SSHORT;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
self.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
// We write using nativeedian so this should be safe
|
||||||
|
let slice =
|
||||||
|
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) };
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TiffValue for [u32] {
|
impl TiffValue for [u32] {
|
||||||
const BYTE_LEN: u32 = 4;
|
const BYTE_LEN: u32 = 4;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::LONG;
|
const FIELD_TYPE: ifd::Type = ifd::Type::LONG;
|
||||||
@@ -84,6 +125,23 @@ impl TiffValue for [u32] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [i32] {
|
||||||
|
const BYTE_LEN: u32 = 4;
|
||||||
|
const FIELD_TYPE: ifd::Type = ifd::Type::SLONG;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
self.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
// We write using nativeedian so this should be safe
|
||||||
|
let slice =
|
||||||
|
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) };
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TiffValue for [Rational] {
|
impl TiffValue for [Rational] {
|
||||||
const BYTE_LEN: u32 = 8;
|
const BYTE_LEN: u32 = 8;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL;
|
const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL;
|
||||||
@@ -100,6 +158,22 @@ impl TiffValue for [Rational] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [SRational] {
|
||||||
|
const BYTE_LEN: u32 = 8;
|
||||||
|
const FIELD_TYPE: ifd::Type = ifd::Type::SRATIONAL;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
self.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
for x in self {
|
||||||
|
x.write(writer)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TiffValue for u8 {
|
impl TiffValue for u8 {
|
||||||
const BYTE_LEN: u32 = 1;
|
const BYTE_LEN: u32 = 1;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::BYTE;
|
const FIELD_TYPE: ifd::Type = ifd::Type::BYTE;
|
||||||
@@ -114,6 +188,20 @@ impl TiffValue for u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TiffValue for i8 {
|
||||||
|
const BYTE_LEN: u32 = 1;
|
||||||
|
const FIELD_TYPE: ifd::Type = ifd::Type::SBYTE;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_i8(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TiffValue for u16 {
|
impl TiffValue for u16 {
|
||||||
const BYTE_LEN: u32 = 2;
|
const BYTE_LEN: u32 = 2;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::SHORT;
|
const FIELD_TYPE: ifd::Type = ifd::Type::SHORT;
|
||||||
@@ -128,6 +216,20 @@ impl TiffValue for u16 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TiffValue for i16 {
|
||||||
|
const BYTE_LEN: u32 = 2;
|
||||||
|
const FIELD_TYPE: ifd::Type = ifd::Type::SSHORT;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_i16(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TiffValue for u32 {
|
impl TiffValue for u32 {
|
||||||
const BYTE_LEN: u32 = 4;
|
const BYTE_LEN: u32 = 4;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::LONG;
|
const FIELD_TYPE: ifd::Type = ifd::Type::LONG;
|
||||||
@@ -142,6 +244,20 @@ impl TiffValue for u32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TiffValue for i32 {
|
||||||
|
const BYTE_LEN: u32 = 4;
|
||||||
|
const FIELD_TYPE: ifd::Type = ifd::Type::SLONG;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_i32(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TiffValue for Rational {
|
impl TiffValue for Rational {
|
||||||
const BYTE_LEN: u32 = 8;
|
const BYTE_LEN: u32 = 8;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL;
|
const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL;
|
||||||
@@ -157,6 +273,21 @@ impl TiffValue for Rational {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TiffValue for SRational {
|
||||||
|
const BYTE_LEN: u32 = 8;
|
||||||
|
const FIELD_TYPE: ifd::Type = ifd::Type::SRATIONAL;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_i32(self.n)?;
|
||||||
|
writer.write_i32(self.d)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TiffValue for str {
|
impl TiffValue for str {
|
||||||
const BYTE_LEN: u32 = 1;
|
const BYTE_LEN: u32 = 1;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::ASCII;
|
const FIELD_TYPE: ifd::Type = ifd::Type::ASCII;
|
||||||
@@ -221,6 +352,8 @@ impl<W: Write + Seek> TiffEncoder<W> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
NativeEndian::write_header(&mut encoder.writer)?;
|
NativeEndian::write_header(&mut encoder.writer)?;
|
||||||
|
// blank the IFD offset location
|
||||||
|
encoder.writer.write_u32(0)?;
|
||||||
|
|
||||||
Ok(encoder)
|
Ok(encoder)
|
||||||
}
|
}
|
||||||
@@ -287,9 +420,9 @@ pub struct DirectoryEncoder<'a, W: 'a + Write + Seek> {
|
|||||||
|
|
||||||
impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
||||||
fn new(writer: &'a mut TiffWriter<W>) -> TiffResult<DirectoryEncoder<'a, W>> {
|
fn new(writer: &'a mut TiffWriter<W>) -> TiffResult<DirectoryEncoder<'a, W>> {
|
||||||
|
// the previous word is the IFD offset position
|
||||||
|
let ifd_pointer_pos = writer.offset() - mem::size_of::<u32>() as u64;
|
||||||
writer.pad_word_boundary()?;
|
writer.pad_word_boundary()?;
|
||||||
let ifd_pointer_pos = writer.offset();
|
|
||||||
writer.write_u32(0)?;
|
|
||||||
Ok(DirectoryEncoder {
|
Ok(DirectoryEncoder {
|
||||||
writer,
|
writer,
|
||||||
dropped: false,
|
dropped: false,
|
||||||
@@ -308,7 +441,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.ifd
|
self.ifd
|
||||||
.insert(tag.to_u16(), (<T>::FIELD_TYPE as u16, value.count(), bytes));
|
.insert(tag.to_u16(), (<T>::FIELD_TYPE.to_u16(), value.count(), bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_directory(&mut self) -> TiffResult<u64> {
|
fn write_directory(&mut self) -> TiffResult<u64> {
|
||||||
@@ -435,7 +568,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
|||||||
encoder.write_tag(Tag::Compression, 1u16);
|
encoder.write_tag(Tag::Compression, 1u16);
|
||||||
|
|
||||||
encoder.write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE);
|
encoder.write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE);
|
||||||
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE as u16);
|
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16());
|
||||||
|
|
||||||
encoder.write_tag(Tag::RowsPerStrip, rows_per_strip as u32);
|
encoder.write_tag(Tag::RowsPerStrip, rows_per_strip as u32);
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,12 @@ impl<W: Write> TiffWriter<W> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_i8(&mut self, n: i8) -> Result<(), io::Error> {
|
||||||
|
self.writer.write_i8(n)?;
|
||||||
|
self.offset += 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn write_u16(&mut self, n: u16) -> Result<(), io::Error> {
|
pub fn write_u16(&mut self, n: u16) -> Result<(), io::Error> {
|
||||||
self.writer.write_u16::<NativeEndian>(n)?;
|
self.writer.write_u16::<NativeEndian>(n)?;
|
||||||
self.offset += 2;
|
self.offset += 2;
|
||||||
@@ -59,6 +65,13 @@ impl<W: Write> TiffWriter<W> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_i16(&mut self, n: i16) -> Result<(), io::Error> {
|
||||||
|
self.writer.write_i16::<NativeEndian>(n)?;
|
||||||
|
self.offset += 2;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn write_u32(&mut self, n: u32) -> Result<(), io::Error> {
|
pub fn write_u32(&mut self, n: u32) -> Result<(), io::Error> {
|
||||||
self.writer.write_u32::<NativeEndian>(n)?;
|
self.writer.write_u32::<NativeEndian>(n)?;
|
||||||
self.offset += 4;
|
self.offset += 4;
|
||||||
@@ -66,6 +79,13 @@ impl<W: Write> TiffWriter<W> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_i32(&mut self, n: i32) -> Result<(), io::Error> {
|
||||||
|
self.writer.write_i32::<NativeEndian>(n)?;
|
||||||
|
self.offset += 4;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn write_u64(&mut self, n: u64) -> Result<(), io::Error> {
|
pub fn write_u64(&mut self, n: u64) -> Result<(), io::Error> {
|
||||||
self.writer.write_u64::<NativeEndian>(n)?;
|
self.writer.write_u64::<NativeEndian>(n)?;
|
||||||
self.offset += 8;
|
self.offset += 8;
|
||||||
@@ -78,6 +98,7 @@ impl<W: Write> TiffWriter<W> {
|
|||||||
let padding = [0, 0, 0];
|
let padding = [0, 0, 0];
|
||||||
let padd_len = 4 - (self.offset % 4);
|
let padd_len = 4 - (self.offset % 4);
|
||||||
self.writer.write_all(&padding[..padd_len as usize])?;
|
self.writer.write_all(&padding[..padd_len as usize])?;
|
||||||
|
self.offset += padd_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
+53
-5
@@ -5,24 +5,32 @@ use std::string;
|
|||||||
|
|
||||||
use decoder::ifd::{Tag, Value};
|
use decoder::ifd::{Tag, Value};
|
||||||
use decoder::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration};
|
use decoder::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration};
|
||||||
|
use miniz_oxide::inflate::TINFLStatus;
|
||||||
use ColorType;
|
use ColorType;
|
||||||
|
|
||||||
/// Tiff error kinds.
|
/// Tiff error kinds.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum TiffError {
|
pub enum TiffError {
|
||||||
/// The Image is not formatted properly
|
/// The Image is not formatted properly.
|
||||||
FormatError(TiffFormatError),
|
FormatError(TiffFormatError),
|
||||||
|
|
||||||
/// The Decoder does not support this image format
|
/// The Decoder does not support features required by the image.
|
||||||
UnsupportedError(TiffUnsupportedError),
|
UnsupportedError(TiffUnsupportedError),
|
||||||
|
|
||||||
/// An I/O Error occurred while decoding the image
|
/// An I/O Error occurred while decoding the image.
|
||||||
IoError(io::Error),
|
IoError(io::Error),
|
||||||
|
|
||||||
/// The Limits of the Decoder is exceeded,
|
/// The Limits of the Decoder is exceeded.
|
||||||
LimitsExceeded,
|
LimitsExceeded,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The image is not formatted properly.
|
||||||
|
///
|
||||||
|
/// This indicates that the encoder producing the image might behave incorrectly or that the input
|
||||||
|
/// file has been corrupted.
|
||||||
|
///
|
||||||
|
/// The list of variants may grow to incorporate errors of future features. Matching against this
|
||||||
|
/// exhaustively is not covered by interface stability guarantees.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum TiffFormatError {
|
pub enum TiffFormatError {
|
||||||
TiffSignatureNotFound,
|
TiffSignatureNotFound,
|
||||||
@@ -33,6 +41,11 @@ pub enum TiffFormatError {
|
|||||||
RequiredTagNotFound(Tag),
|
RequiredTagNotFound(Tag),
|
||||||
UnknownPredictor(u32),
|
UnknownPredictor(u32),
|
||||||
UnsignedIntegerExpected(Value),
|
UnsignedIntegerExpected(Value),
|
||||||
|
SignedIntegerExpected(Value),
|
||||||
|
InflateError(InflateError),
|
||||||
|
#[doc(hidden)]
|
||||||
|
/// Do not match against this variant. It may get removed.
|
||||||
|
__NonExhaustive,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for TiffFormatError {
|
impl fmt::Display for TiffFormatError {
|
||||||
@@ -51,10 +64,41 @@ impl fmt::Display for TiffFormatError {
|
|||||||
UnsignedIntegerExpected(ref val) => {
|
UnsignedIntegerExpected(ref val) => {
|
||||||
write!(fmt, "Expected unsigned integer, {:?} found.", val)
|
write!(fmt, "Expected unsigned integer, {:?} found.", val)
|
||||||
}
|
}
|
||||||
|
SignedIntegerExpected(ref val) => {
|
||||||
|
write!(fmt, "Expected signed integer, {:?} found.", val)
|
||||||
|
}
|
||||||
|
InflateError(_) => write!(fmt, "Failed to decode inflate data."),
|
||||||
|
__NonExhaustive => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decompression failed due to faulty compressed data.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct InflateError {
|
||||||
|
status: TINFLStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InflateError {
|
||||||
|
pub(crate) fn new(status: TINFLStatus) -> Self {
|
||||||
|
Self { status }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffError {
|
||||||
|
pub(crate) fn from_inflate_status(status: TINFLStatus) -> Self {
|
||||||
|
TiffError::FormatError(TiffFormatError::InflateError(InflateError::new(status)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Decoder does not support features required by the image.
|
||||||
|
///
|
||||||
|
/// This only captures known failures for which the standard either does not require support or an
|
||||||
|
/// implementation has been planned but not yet completed. Some variants may become unused over
|
||||||
|
/// time and will then get deprecated before being removed.
|
||||||
|
///
|
||||||
|
/// The list of variants may grow. Matching against this exhaustively is not covered by interface
|
||||||
|
/// stability guarantees.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum TiffUnsupportedError {
|
pub enum TiffUnsupportedError {
|
||||||
HorizontalPredictor(ColorType),
|
HorizontalPredictor(ColorType),
|
||||||
@@ -67,6 +111,9 @@ pub enum TiffUnsupportedError {
|
|||||||
UnsupportedBitsPerChannel(u8),
|
UnsupportedBitsPerChannel(u8),
|
||||||
UnsupportedPlanarConfig(Option<PlanarConfiguration>),
|
UnsupportedPlanarConfig(Option<PlanarConfiguration>),
|
||||||
UnsupportedDataType,
|
UnsupportedDataType,
|
||||||
|
#[doc(hidden)]
|
||||||
|
/// Do not match against this variant. It may get removed.
|
||||||
|
__NonExhaustive,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for TiffUnsupportedError {
|
impl fmt::Display for TiffUnsupportedError {
|
||||||
@@ -104,6 +151,7 @@ impl fmt::Display for TiffUnsupportedError {
|
|||||||
write!(fmt, "Unsupported planar configuration “{:?}”.", config)
|
write!(fmt, "Unsupported planar configuration “{:?}”.", config)
|
||||||
}
|
}
|
||||||
UnsupportedDataType => write!(fmt, "Unsupported data type."),
|
UnsupportedDataType => write!(fmt, "Unsupported data type."),
|
||||||
|
__NonExhaustive => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +182,7 @@ impl Error for TiffError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cause(&self) -> Option<&Error> {
|
fn cause(&self) -> Option<&dyn Error> {
|
||||||
match *self {
|
match *self {
|
||||||
TiffError::IoError(ref e) => Some(e),
|
TiffError::IoError(ref e) => Some(e),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|||||||
+2
-4
@@ -8,15 +8,13 @@
|
|||||||
|
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
extern crate lzw;
|
extern crate lzw;
|
||||||
#[macro_use]
|
extern crate miniz_oxide;
|
||||||
extern crate num_derive;
|
|
||||||
extern crate num_traits;
|
|
||||||
|
|
||||||
pub mod decoder;
|
pub mod decoder;
|
||||||
pub mod encoder;
|
pub mod encoder;
|
||||||
mod error;
|
mod error;
|
||||||
|
|
||||||
pub use self::error::{TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
pub use self::error::{InflateError, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
||||||
|
|
||||||
/// An enumeration over supported color types and their bit depths
|
/// An enumeration over supported color types and their bit depths
|
||||||
#[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)]
|
#[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)]
|
||||||
|
|||||||
+48
-8
@@ -11,8 +11,18 @@ fn test_gray_u8() {
|
|||||||
File::open("./tests/images/minisblack-1c-8b.tiff").expect("Cannot find test image!");
|
File::open("./tests/images/minisblack-1c-8b.tiff").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");
|
||||||
assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(8));
|
assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(8));
|
||||||
let img_res = decoder.read_image();
|
let img_res = decoder.read_image().unwrap();
|
||||||
assert!(img_res.is_ok());
|
if let DecodingResult::U8(res) = img_res {
|
||||||
|
let mut res_sum: u64 = 0;
|
||||||
|
for x in res {
|
||||||
|
res_sum += x as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(res_sum, 2840893);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic!("Wrong bit depth")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -20,8 +30,18 @@ fn test_rgb_u8() {
|
|||||||
let img_file = File::open("./tests/images/rgb-3c-8b.tiff").expect("Cannot find test image!");
|
let img_file = File::open("./tests/images/rgb-3c-8b.tiff").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");
|
||||||
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
|
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
|
||||||
let img_res = decoder.read_image();
|
let img_res = decoder.read_image().unwrap();
|
||||||
assert!(img_res.is_ok());
|
if let DecodingResult::U8(res) = img_res {
|
||||||
|
let mut res_sum: u64 = 0;
|
||||||
|
for x in res {
|
||||||
|
res_sum += x as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(res_sum, 7842108)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic!("Wrong bit depth")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -30,8 +50,18 @@ fn test_gray_u16() {
|
|||||||
File::open("./tests/images/minisblack-1c-16b.tiff").expect("Cannot find test image!");
|
File::open("./tests/images/minisblack-1c-16b.tiff").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");
|
||||||
assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(16));
|
assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(16));
|
||||||
let img_res = decoder.read_image();
|
let img_res = decoder.read_image().unwrap();
|
||||||
assert!(img_res.is_ok());
|
if let DecodingResult::U16(res) = img_res {
|
||||||
|
let mut res_sum: u64 = 0;
|
||||||
|
for x in res {
|
||||||
|
res_sum += x as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(res_sum, 733126239);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic!("Wrong bit depth")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -39,8 +69,18 @@ fn test_rgb_u16() {
|
|||||||
let img_file = File::open("./tests/images/rgb-3c-16b.tiff").expect("Cannot find test image!");
|
let img_file = File::open("./tests/images/rgb-3c-16b.tiff").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");
|
||||||
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(16));
|
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(16));
|
||||||
let img_res = decoder.read_image();
|
let img_res = decoder.read_image().unwrap();
|
||||||
assert!(img_res.is_ok());
|
if let DecodingResult::U16(res) = img_res {
|
||||||
|
let mut res_sum: u64 = 0;
|
||||||
|
for x in res {
|
||||||
|
res_sum += x as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(res_sum, 2024349944);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic!("Wrong bit depth")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+118
-1
@@ -2,7 +2,8 @@ extern crate tempfile;
|
|||||||
extern crate tiff;
|
extern crate tiff;
|
||||||
|
|
||||||
use tiff::decoder::{Decoder, DecodingResult};
|
use tiff::decoder::{Decoder, DecodingResult};
|
||||||
use tiff::encoder::{colortype, TiffEncoder};
|
use tiff::decoder::ifd::Tag;
|
||||||
|
use tiff::encoder::{colortype, TiffEncoder, SRational};
|
||||||
use tiff::ColorType;
|
use tiff::ColorType;
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@@ -175,3 +176,119 @@ fn test_rgb_u16_roundtrip() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_multiple_byte() {
|
||||||
|
let mut data = Cursor::new(Vec::new());
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut tiff = TiffEncoder::new(&mut data).unwrap();
|
||||||
|
let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap();
|
||||||
|
let encoder = image_encoder.encoder();
|
||||||
|
|
||||||
|
encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]);
|
||||||
|
encoder.write_tag(Tag::Unknown(65001), &[1_u8, 2][..]);
|
||||||
|
encoder.write_tag(Tag::Unknown(65002), &[1_u8, 2, 3][..]);
|
||||||
|
encoder.write_tag(Tag::Unknown(65003), &[1_u8, 2, 3, 4][..]);
|
||||||
|
encoder.write_tag(Tag::Unknown(65004), &[1_u8, 2, 3, 4, 5][..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.set_position(0);
|
||||||
|
{
|
||||||
|
let mut decoder = Decoder::new(&mut data).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_u32_vec().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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// Test writing signed tags from TIFF 6.0
|
||||||
|
fn test_signed() {
|
||||||
|
let mut data = Cursor::new(Vec::new());
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut tiff = TiffEncoder::new(&mut data).unwrap();
|
||||||
|
let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap();
|
||||||
|
let encoder = image_encoder.encoder();
|
||||||
|
|
||||||
|
//Use the "reusable" tags section as per the TIFF6 spec
|
||||||
|
encoder.write_tag(Tag::Unknown(65000), -1_i8);
|
||||||
|
encoder.write_tag(Tag::Unknown(65001), &[-1_i8][..]);
|
||||||
|
encoder.write_tag(Tag::Unknown(65002), &[-1_i8, 2][..]);
|
||||||
|
encoder.write_tag(Tag::Unknown(65003), &[-1_i8, 2, -3][..]);
|
||||||
|
encoder.write_tag(Tag::Unknown(65004), &[-1_i8, 2, -3, 4][..]);
|
||||||
|
encoder.write_tag(Tag::Unknown(65005), &[-1_i8, 2, -3, 4, -5][..]);
|
||||||
|
|
||||||
|
encoder.write_tag(Tag::Unknown(65010), -1_i16);
|
||||||
|
encoder.write_tag(Tag::Unknown(65011), -1_i16);
|
||||||
|
encoder.write_tag(Tag::Unknown(65012), &[-1_i16, 2][..]);
|
||||||
|
encoder.write_tag(Tag::Unknown(65013), &[-1_i16, 2, -3][..]);
|
||||||
|
|
||||||
|
encoder.write_tag(Tag::Unknown(65020), -1_i32);
|
||||||
|
encoder.write_tag(Tag::Unknown(65021), &[-1_i32][..]);
|
||||||
|
encoder.write_tag(Tag::Unknown(65022), &[-1_i32, 2][..]);
|
||||||
|
|
||||||
|
encoder.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 }][..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Rewind the cursor for reading
|
||||||
|
data.set_position(0);
|
||||||
|
{
|
||||||
|
let mut decoder = Decoder::new(&mut data).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_i32().unwrap(), -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!(decoder.get_tag(Tag::Unknown(65010)).unwrap().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!(decoder.get_tag(Tag::Unknown(65020)).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!(decoder.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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// check multipage image handling
|
||||||
|
fn test_multipage_image() {
|
||||||
|
let mut img_file = Cursor::new(Vec::new());
|
||||||
|
|
||||||
|
{
|
||||||
|
// first create a multipage image with 2 images
|
||||||
|
let mut img_encoder = TiffEncoder::new(&mut img_file).unwrap();
|
||||||
|
|
||||||
|
// write first grayscale image (2x2 16-bit)
|
||||||
|
let img1: Vec<u16> = [1, 2, 3, 4].to_vec();
|
||||||
|
img_encoder.write_image::<colortype::Gray16>(2, 2, &img1[..]);
|
||||||
|
// write second grayscale image (3x3 8-bit)
|
||||||
|
let img2: Vec<u8> = [9, 8, 7, 6, 5, 4, 3, 2, 1].to_vec();
|
||||||
|
img_encoder.write_image::<colortype::Gray8>(3, 3, &img2[..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// seek to the beginning of the file, so that it can be decoded
|
||||||
|
img_file.seek(SeekFrom::Start(0)).unwrap();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut img_decoder = Decoder::new(&mut img_file).unwrap();
|
||||||
|
|
||||||
|
// check the dimensions of the image in the first page
|
||||||
|
assert_eq!(img_decoder.dimensions().unwrap(), (2, 2));
|
||||||
|
img_decoder.next_image().unwrap();
|
||||||
|
// check the dimensions of the image in the second page
|
||||||
|
assert_eq!(img_decoder.dimensions().unwrap(), (3, 3));
|
||||||
|
}
|
||||||
|
}
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loaded 100 of 137 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user