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
|
||||
rust:
|
||||
- 1.24.1
|
||||
- 1.34.2
|
||||
- stable
|
||||
- beta
|
||||
- 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]
|
||||
name = "tiff"
|
||||
version = "0.3.1"
|
||||
version = "0.4.0"
|
||||
authors = [
|
||||
"ccgn",
|
||||
"bvssvni <bvssvni@gmail.com>",
|
||||
@@ -19,8 +19,7 @@ exclude = ["tests/images/*", "tests/fuzz_images/*"]
|
||||
[dependencies]
|
||||
byteorder = "1.2"
|
||||
lzw = "0.10"
|
||||
num-derive = "0.2"
|
||||
num-traits = "0.2"
|
||||
miniz_oxide = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.0"
|
||||
+238
-97
@@ -7,30 +7,45 @@ use std::mem;
|
||||
use super::stream::{ByteOrder, EndianReader, SmartReader};
|
||||
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
||||
|
||||
use self::Value::{Ascii, List, Rational, Unsigned};
|
||||
use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational};
|
||||
|
||||
macro_rules! tags {
|
||||
{$(
|
||||
$tag:ident
|
||||
$val:expr;
|
||||
)*} => {
|
||||
|
||||
/// TIFF tag
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum Tag {
|
||||
$($tag,)*
|
||||
Unknown(u16)
|
||||
{
|
||||
// Permit arbitrary meta items, which include documentation.
|
||||
$( #[$enum_attr:meta] )*
|
||||
$vis:vis enum $name:ident($ty:ty) $(unknown($unknown_doc:literal))* {
|
||||
// Each of the `Name = Val,` permitting documentation.
|
||||
$($(#[$ident_attr:meta])* $tag:ident = $val:expr,)*
|
||||
}
|
||||
impl Tag {
|
||||
pub fn from_u16(n: u16) -> Tag {
|
||||
$(if n == $val { Tag::$tag } else)* {
|
||||
Tag::Unknown(n)
|
||||
} => {
|
||||
$( #[$enum_attr] )*
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
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 {
|
||||
$( Tag::$tag => $val, )*
|
||||
Tag::Unknown(n) => n,
|
||||
$( $name::$tag => $val, )*
|
||||
$( $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
|
||||
tags! {
|
||||
/// TIFF tags
|
||||
pub enum Tag(u16) unknown("A private or extension tag") {
|
||||
// Baseline tags:
|
||||
Artist 315;
|
||||
Artist = 315,
|
||||
// grayscale images PhotometricInterpretation 1 or 3
|
||||
BitsPerSample 258;
|
||||
CellLength 265; // TODO add support
|
||||
CellWidth 264; // TODO add support
|
||||
BitsPerSample = 258,
|
||||
CellLength = 265, // TODO add support
|
||||
CellWidth = 264, // TODO add support
|
||||
// palette-color images (PhotometricInterpretation 3)
|
||||
ColorMap 320; // TODO add support
|
||||
Compression 259; // TODO add support for 2 and 32773
|
||||
Copyright 33_432;
|
||||
DateTime 306;
|
||||
ExtraSamples 338; // TODO add support
|
||||
FillOrder 266; // TODO add support
|
||||
FreeByteCounts 289; // TODO add support
|
||||
FreeOffsets 288; // TODO add support
|
||||
GrayResponseCurve 291; // TODO add support
|
||||
GrayResponseUnit 290; // TODO add support
|
||||
HostComputer 316;
|
||||
ImageDescription 270;
|
||||
ImageLength 257;
|
||||
ImageWidth 256;
|
||||
Make 271;
|
||||
MaxSampleValue 281; // TODO add support
|
||||
MinSampleValue 280; // TODO add support
|
||||
Model 272;
|
||||
NewSubfileType 254; // TODO add support
|
||||
Orientation 274; // TODO add support
|
||||
PhotometricInterpretation 262;
|
||||
PlanarConfiguration 284;
|
||||
ResolutionUnit 296; // TODO add support
|
||||
RowsPerStrip 278;
|
||||
SamplesPerPixel 277;
|
||||
Software 305;
|
||||
StripByteCounts 279;
|
||||
StripOffsets 273;
|
||||
SubfileType 255; // TODO add support
|
||||
Threshholding 263; // TODO add support
|
||||
XResolution 282;
|
||||
YResolution 283;
|
||||
ColorMap = 320, // TODO add support
|
||||
Compression = 259, // TODO add support for 2 and 32773
|
||||
Copyright = 33_432,
|
||||
DateTime = 306,
|
||||
ExtraSamples = 338, // TODO add support
|
||||
FillOrder = 266, // TODO add support
|
||||
FreeByteCounts = 289, // TODO add support
|
||||
FreeOffsets = 288, // TODO add support
|
||||
GrayResponseCurve = 291, // TODO add support
|
||||
GrayResponseUnit = 290, // TODO add support
|
||||
HostComputer = 316,
|
||||
ImageDescription = 270,
|
||||
ImageLength = 257,
|
||||
ImageWidth = 256,
|
||||
Make = 271,
|
||||
MaxSampleValue = 281, // TODO add support
|
||||
MinSampleValue = 280, // TODO add support
|
||||
Model = 272,
|
||||
NewSubfileType = 254, // TODO add support
|
||||
Orientation = 274, // TODO add support
|
||||
PhotometricInterpretation = 262,
|
||||
PlanarConfiguration = 284,
|
||||
ResolutionUnit = 296, // TODO add support
|
||||
RowsPerStrip = 278,
|
||||
SamplesPerPixel = 277,
|
||||
Software = 305,
|
||||
StripByteCounts = 279,
|
||||
StripOffsets = 273,
|
||||
SubfileType = 255, // TODO add support
|
||||
Threshholding = 263, // TODO add support
|
||||
XResolution = 282,
|
||||
YResolution = 283,
|
||||
// Advanced tags
|
||||
Predictor 317;
|
||||
Predictor = 317,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, FromPrimitive)]
|
||||
pub enum Type {
|
||||
impl Tag {
|
||||
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,
|
||||
ASCII = 2,
|
||||
SHORT = 3,
|
||||
LONG = 4,
|
||||
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)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Value {
|
||||
//Signed(i32),
|
||||
Signed(i32),
|
||||
Unsigned(u32),
|
||||
List(Vec<Value>),
|
||||
Rational(u32, u32),
|
||||
SRational(i32, i32),
|
||||
Ascii(String),
|
||||
#[doc(hidden)] // Do not match against this.
|
||||
__NonExhaustive,
|
||||
}
|
||||
|
||||
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>> {
|
||||
match self {
|
||||
List(vec) => {
|
||||
@@ -122,6 +180,32 @@ impl Value {
|
||||
Unsigned(val) => Ok(vec![val]),
|
||||
Rational(numerator, denominator) => Ok(vec![numerator, denominator]),
|
||||
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
|
||||
// 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(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::SSHORT, 1) => Ok(Signed(i32::from(self.r(bo).read_i16()?))),
|
||||
(Type::SHORT, 2) => {
|
||||
let mut r = self.r(bo);
|
||||
Ok(List(vec![
|
||||
@@ -174,54 +272,57 @@ impl Entry {
|
||||
Unsigned(u32::from(r.read_u16()?)),
|
||||
]))
|
||||
}
|
||||
(Type::SHORT, 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(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::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) => {
|
||||
try!(decoder.goto_offset(try!(self.r(bo).read_u32())));
|
||||
let numerator = try!(decoder.read_long());
|
||||
let denominator = try!(decoder.read_long());
|
||||
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
||||
let numerator = decoder.read_long()?;
|
||||
let denominator = decoder.read_long()?;
|
||||
Ok(Rational(numerator, denominator))
|
||||
}
|
||||
(Type::RATIONAL, 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 {
|
||||
let numerator = try!(decoder.read_long());
|
||||
let denominator = try!(decoder.read_long());
|
||||
v.push(Rational(numerator, denominator))
|
||||
}
|
||||
Ok(List(v))
|
||||
(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) => {
|
||||
if n as usize > limits.decoding_buffer_size {
|
||||
return Err(TiffError::LimitsExceeded);
|
||||
}
|
||||
try!(decoder.goto_offset(try!(self.r(bo).read_u32())));
|
||||
let string = try!(decoder.read_string(n as usize));
|
||||
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
||||
let string = decoder.read_string(n as usize)?;
|
||||
Ok(Ascii(string))
|
||||
}
|
||||
_ => 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
|
||||
|
||||
+131
-52
@@ -1,4 +1,3 @@
|
||||
use num_traits::{FromPrimitive, Num};
|
||||
use std::collections::HashMap;
|
||||
use std::io::{self, Read, Seek};
|
||||
use std::cmp;
|
||||
@@ -7,8 +6,9 @@ use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
||||
|
||||
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;
|
||||
mod stream;
|
||||
|
||||
@@ -80,8 +80,8 @@ impl<'a> DecodingBuffer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, FromPrimitive)]
|
||||
pub enum PhotometricInterpretation {
|
||||
tags! {
|
||||
pub enum PhotometricInterpretation(u16) {
|
||||
WhiteIsZero = 0,
|
||||
BlackIsZero = 1,
|
||||
RGB = 2,
|
||||
@@ -91,29 +91,75 @@ pub enum PhotometricInterpretation {
|
||||
YCbCr = 6,
|
||||
CIELab = 8,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, FromPrimitive)]
|
||||
pub enum CompressionMethod {
|
||||
tags! {
|
||||
pub enum CompressionMethod(u16) {
|
||||
None = 1,
|
||||
Huffman = 2,
|
||||
Fax3 = 3,
|
||||
Fax4 = 4,
|
||||
LZW = 5,
|
||||
JPEG = 6,
|
||||
Deflate = 8,
|
||||
OldDeflate = 0x80B2,
|
||||
PackBits = 0x8005,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, FromPrimitive)]
|
||||
pub enum PlanarConfiguration {
|
||||
tags! {
|
||||
pub enum PlanarConfiguration(u16) {
|
||||
Chunky = 1,
|
||||
Planar = 2,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, FromPrimitive)]
|
||||
enum Predictor {
|
||||
tags! {
|
||||
enum Predictor(u16) {
|
||||
None = 1,
|
||||
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)]
|
||||
struct StripDecodeState {
|
||||
@@ -129,10 +175,14 @@ pub struct Limits {
|
||||
/// 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
|
||||
/// 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
|
||||
/// 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 {
|
||||
@@ -140,6 +190,7 @@ impl Default for Limits {
|
||||
Limits {
|
||||
decoding_buffer_size: 256 * 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)
|
||||
where
|
||||
T: Num + Copy + Wrapping,
|
||||
T: Copy + Wrapping,
|
||||
{
|
||||
let width = size.0 as usize;
|
||||
let height = size.1 as usize;
|
||||
@@ -284,7 +335,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
|
||||
fn read_header(&mut self) -> TiffResult<()> {
|
||||
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 {
|
||||
b"II" => {
|
||||
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(
|
||||
TiffFormatError::TiffSignatureInvalid,
|
||||
));
|
||||
}
|
||||
self.next_ifd = match try!(self.read_long()) {
|
||||
self.next_ifd = match self.read_long()? {
|
||||
0 => None,
|
||||
n => Some(n),
|
||||
};
|
||||
@@ -314,7 +365,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
|
||||
/// Initializes the decoder.
|
||||
pub fn init(mut self) -> TiffResult<Decoder<R>> {
|
||||
try!(self.read_header());
|
||||
self.read_header()?;
|
||||
self.next_image()?;
|
||||
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.
|
||||
/// To determine whether there are more images call `TIFFDecoder::more_images` instead.
|
||||
pub fn next_image(&mut self) -> TiffResult<()> {
|
||||
self.ifd = Some(try!(self.read_ifd()));
|
||||
self.width = try!(self.get_tag_u32(ifd::Tag::ImageWidth));
|
||||
self.height = try!(self.get_tag_u32(ifd::Tag::ImageLength));
|
||||
self.ifd = Some(self.read_ifd()?);
|
||||
self.width = self.get_tag_u32(ifd::Tag::ImageWidth)?;
|
||||
self.height = self.get_tag_u32(ifd::Tag::ImageLength)?;
|
||||
self.strip_decoder = None;
|
||||
self.photometric_interpretation = match FromPrimitive::from_u32(try!(
|
||||
self.get_tag_u32(ifd::Tag::PhotometricInterpretation)
|
||||
)) {
|
||||
// TODO: error on non-SHORT value.
|
||||
self.photometric_interpretation = match PhotometricInterpretation::from_u16(
|
||||
self.get_tag_u32(ifd::Tag::PhotometricInterpretation)? as u16
|
||||
) {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
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)) {
|
||||
match FromPrimitive::from_u32(val) {
|
||||
// TODO: error on non-SHORT value.
|
||||
if let Some(val) = self.find_tag_u32(ifd::Tag::Compression)? {
|
||||
match CompressionMethod::from_u16(val as u16) {
|
||||
Some(method) => self.compression_method = method,
|
||||
None => {
|
||||
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
|
||||
}
|
||||
match self.samples {
|
||||
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]
|
||||
}
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -380,18 +433,38 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
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
|
||||
#[inline]
|
||||
pub fn read_short(&mut self) -> Result<u16, io::Error> {
|
||||
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
|
||||
#[inline]
|
||||
pub fn read_long(&mut self) -> Result<u32, io::Error> {
|
||||
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
|
||||
#[inline]
|
||||
pub fn read_string(&mut self, length: usize) -> TiffResult<String> {
|
||||
@@ -406,7 +479,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
#[inline]
|
||||
pub fn read_offset(&mut self) -> Result<[u8; 4], io::Error> {
|
||||
let mut val = [0; 4];
|
||||
try!(self.reader.read_exact(&mut val));
|
||||
self.reader.read_exact(&mut val)?;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
@@ -426,13 +499,13 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
// Count 4 bytes
|
||||
// Value 4 bytes either a pointer the value itself
|
||||
fn read_entry(&mut self) -> TiffResult<Option<(ifd::Tag, ifd::Entry)>> {
|
||||
let tag = ifd::Tag::from_u16(try!(self.read_short()));
|
||||
let type_: ifd::Type = match FromPrimitive::from_u16(try!(self.read_short())) {
|
||||
let tag = ifd::Tag::from_u16(self.read_short()?);
|
||||
let type_ = match ifd::Type::from_u16(self.read_short()?) {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
// Unknown type. Skip this entry according to spec.
|
||||
try!(self.read_long());
|
||||
try!(self.read_long());
|
||||
self.read_long()?;
|
||||
self.read_long()?;
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
@@ -440,8 +513,8 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
tag,
|
||||
ifd::Entry::new(
|
||||
type_,
|
||||
try!(self.read_long()), // count
|
||||
try!(self.read_offset()), // offset
|
||||
self.read_long()?, // count
|
||||
self.read_offset()?, // offset
|
||||
),
|
||||
)))
|
||||
}
|
||||
@@ -455,16 +528,16 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
TiffFormatError::ImageFileDirectoryNotFound,
|
||||
))
|
||||
}
|
||||
Some(offset) => try!(self.goto_offset(offset)),
|
||||
Some(offset) => self.goto_offset(offset)?,
|
||||
}
|
||||
for _ in 0..try!(self.read_short()) {
|
||||
let (tag, entry) = match try!(self.read_entry()) {
|
||||
for _ in 0..self.read_short()? {
|
||||
let (tag, entry) = match self.read_entry()? {
|
||||
Some(val) => val,
|
||||
None => continue, // Unknown data type in tag, skip
|
||||
};
|
||||
dir.insert(tag, entry);
|
||||
}
|
||||
self.next_ifd = match try!(self.read_long()) {
|
||||
self.next_ifd = match self.read_long()? {
|
||||
0 => None,
|
||||
n => Some(n),
|
||||
};
|
||||
@@ -481,7 +554,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
|
||||
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.
|
||||
@@ -503,7 +576,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
/// Tries to retrieve a tag.
|
||||
/// Returns an error if the tag is not present
|
||||
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),
|
||||
None => Err(TiffError::FormatError(
|
||||
TiffFormatError::RequiredTagNotFound(tag),
|
||||
@@ -530,8 +603,8 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
length: u32,
|
||||
max_uncompressed_length: usize,
|
||||
) -> TiffResult<usize> {
|
||||
let color_type = try!(self.colortype());
|
||||
try!(self.goto_offset(offset));
|
||||
let color_type = self.colortype()?;
|
||||
self.goto_offset(offset)?;
|
||||
let (bytes, mut reader): (usize, Box<EndianReader>) = match self.compression_method {
|
||||
CompressionMethod::None => {
|
||||
let order = self.reader.byte_order;
|
||||
@@ -541,20 +614,24 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
)
|
||||
}
|
||||
CompressionMethod::LZW => {
|
||||
let (bytes, reader) = try!(LZWReader::new(
|
||||
let (bytes, reader) = LZWReader::new(
|
||||
&mut self.reader,
|
||||
length as usize,
|
||||
max_uncompressed_length
|
||||
));
|
||||
)?;
|
||||
(bytes, Box::new(reader))
|
||||
}
|
||||
CompressionMethod::PackBits => {
|
||||
let order = self.reader.byte_order;
|
||||
let (bytes, reader) = try!(PackBitsReader::new(
|
||||
let (bytes, reader) = PackBitsReader::new(
|
||||
&mut self.reader,
|
||||
order,
|
||||
length as usize
|
||||
));
|
||||
)?;
|
||||
(bytes, Box::new(reader))
|
||||
}
|
||||
CompressionMethod::OldDeflate => {
|
||||
let (bytes, reader) = DeflateReader::new(&mut self.reader, max_uncompressed_length)?;
|
||||
(bytes, Box::new(reader))
|
||||
}
|
||||
method => {
|
||||
@@ -574,16 +651,16 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
(ColorType::RGB(8), DecodingBuffer::U8(ref mut buffer))
|
||||
| (ColorType::RGBA(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
|
||||
}
|
||||
(ColorType::RGBA(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
|
||||
}
|
||||
(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 {
|
||||
for datum in buffer[..bytes / 2].iter_mut() {
|
||||
*datum = 0xffff - *datum
|
||||
@@ -592,7 +669,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
bytes / 2
|
||||
}
|
||||
(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 {
|
||||
for byte in buffer[..bytes].iter_mut() {
|
||||
*byte = 0xff - *byte
|
||||
@@ -689,7 +766,8 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
));
|
||||
}
|
||||
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::Horizontal) => {
|
||||
rev_hpredict(
|
||||
@@ -703,6 +781,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
predictor,
|
||||
)))
|
||||
}
|
||||
Some(Predictor::__NonExhaustive) => unreachable!(),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
+71
-5
@@ -1,7 +1,9 @@
|
||||
//! All IO functionality needed for TIFF decoding
|
||||
|
||||
use crate::error::{TiffError, TiffResult};
|
||||
use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
|
||||
use lzw;
|
||||
use miniz_oxide::inflate;
|
||||
use std::io::{self, Read, Seek};
|
||||
|
||||
/// 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
|
||||
#[inline(always)]
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
@@ -67,12 +133,12 @@ impl LZWReader {
|
||||
{
|
||||
let order = reader.byte_order;
|
||||
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 decoder = lzw::DecoderEarlyChange::new(lzw::MsbReader::new(), 8);
|
||||
let mut bytes_read = 0;
|
||||
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;
|
||||
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> {
|
||||
let mut header: [u8; 1] = [0];
|
||||
|
||||
let bytes = try!(reader.read(&mut header));
|
||||
let bytes = reader.read(&mut header)?;
|
||||
|
||||
match bytes {
|
||||
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),
|
||||
h if h >= -127 && h <= -1 => {
|
||||
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]);
|
||||
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 start = buffer.len();
|
||||
buffer.resize(start + num_vals, 0);
|
||||
try!(reader.read_exact(&mut buffer[start..]));
|
||||
reader.read_exact(&mut buffer[start..])?;
|
||||
Ok(num_vals + 1)
|
||||
}
|
||||
},
|
||||
|
||||
+137
-4
@@ -1,6 +1,7 @@
|
||||
use byteorder::NativeEndian;
|
||||
use std::collections::BTreeMap;
|
||||
use std::io::{Seek, Write};
|
||||
use std::mem;
|
||||
|
||||
use decoder::ifd::{self, Tag};
|
||||
use error::{TiffError, TiffFormatError, TiffResult};
|
||||
@@ -18,6 +19,12 @@ pub struct Rational {
|
||||
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
|
||||
pub enum ResolutionUnit {
|
||||
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] {
|
||||
const BYTE_LEN: u32 = 2;
|
||||
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] {
|
||||
const BYTE_LEN: u32 = 4;
|
||||
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] {
|
||||
const BYTE_LEN: u32 = 8;
|
||||
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 {
|
||||
const BYTE_LEN: u32 = 1;
|
||||
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 {
|
||||
const BYTE_LEN: u32 = 2;
|
||||
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 {
|
||||
const BYTE_LEN: u32 = 4;
|
||||
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 {
|
||||
const BYTE_LEN: u32 = 8;
|
||||
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 {
|
||||
const BYTE_LEN: u32 = 1;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::ASCII;
|
||||
@@ -221,6 +352,8 @@ impl<W: Write + Seek> TiffEncoder<W> {
|
||||
};
|
||||
|
||||
NativeEndian::write_header(&mut encoder.writer)?;
|
||||
// blank the IFD offset location
|
||||
encoder.writer.write_u32(0)?;
|
||||
|
||||
Ok(encoder)
|
||||
}
|
||||
@@ -287,9 +420,9 @@ pub struct DirectoryEncoder<'a, W: 'a + Write + Seek> {
|
||||
|
||||
impl<'a, W: 'a + Write + Seek> 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()?;
|
||||
let ifd_pointer_pos = writer.offset();
|
||||
writer.write_u32(0)?;
|
||||
Ok(DirectoryEncoder {
|
||||
writer,
|
||||
dropped: false,
|
||||
@@ -308,7 +441,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
||||
}
|
||||
|
||||
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> {
|
||||
@@ -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::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);
|
||||
|
||||
|
||||
@@ -52,6 +52,12 @@ impl<W: Write> TiffWriter<W> {
|
||||
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> {
|
||||
self.writer.write_u16::<NativeEndian>(n)?;
|
||||
self.offset += 2;
|
||||
@@ -59,6 +65,13 @@ impl<W: Write> TiffWriter<W> {
|
||||
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> {
|
||||
self.writer.write_u32::<NativeEndian>(n)?;
|
||||
self.offset += 4;
|
||||
@@ -66,6 +79,13 @@ impl<W: Write> TiffWriter<W> {
|
||||
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> {
|
||||
self.writer.write_u64::<NativeEndian>(n)?;
|
||||
self.offset += 8;
|
||||
@@ -78,6 +98,7 @@ impl<W: Write> TiffWriter<W> {
|
||||
let padding = [0, 0, 0];
|
||||
let padd_len = 4 - (self.offset % 4);
|
||||
self.writer.write_all(&padding[..padd_len as usize])?;
|
||||
self.offset += padd_len;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
+53
-5
@@ -5,24 +5,32 @@ use std::string;
|
||||
|
||||
use decoder::ifd::{Tag, Value};
|
||||
use decoder::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration};
|
||||
use miniz_oxide::inflate::TINFLStatus;
|
||||
use ColorType;
|
||||
|
||||
/// Tiff error kinds.
|
||||
#[derive(Debug)]
|
||||
pub enum TiffError {
|
||||
/// The Image is not formatted properly
|
||||
/// The Image is not formatted properly.
|
||||
FormatError(TiffFormatError),
|
||||
|
||||
/// The Decoder does not support this image format
|
||||
/// The Decoder does not support features required by the image.
|
||||
UnsupportedError(TiffUnsupportedError),
|
||||
|
||||
/// An I/O Error occurred while decoding the image
|
||||
/// An I/O Error occurred while decoding the image.
|
||||
IoError(io::Error),
|
||||
|
||||
/// The Limits of the Decoder is exceeded,
|
||||
/// The Limits of the Decoder is exceeded.
|
||||
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)]
|
||||
pub enum TiffFormatError {
|
||||
TiffSignatureNotFound,
|
||||
@@ -33,6 +41,11 @@ pub enum TiffFormatError {
|
||||
RequiredTagNotFound(Tag),
|
||||
UnknownPredictor(u32),
|
||||
UnsignedIntegerExpected(Value),
|
||||
SignedIntegerExpected(Value),
|
||||
InflateError(InflateError),
|
||||
#[doc(hidden)]
|
||||
/// Do not match against this variant. It may get removed.
|
||||
__NonExhaustive,
|
||||
}
|
||||
|
||||
impl fmt::Display for TiffFormatError {
|
||||
@@ -51,10 +64,41 @@ impl fmt::Display for TiffFormatError {
|
||||
UnsignedIntegerExpected(ref 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)]
|
||||
pub enum TiffUnsupportedError {
|
||||
HorizontalPredictor(ColorType),
|
||||
@@ -67,6 +111,9 @@ pub enum TiffUnsupportedError {
|
||||
UnsupportedBitsPerChannel(u8),
|
||||
UnsupportedPlanarConfig(Option<PlanarConfiguration>),
|
||||
UnsupportedDataType,
|
||||
#[doc(hidden)]
|
||||
/// Do not match against this variant. It may get removed.
|
||||
__NonExhaustive,
|
||||
}
|
||||
|
||||
impl fmt::Display for TiffUnsupportedError {
|
||||
@@ -104,6 +151,7 @@ impl fmt::Display for TiffUnsupportedError {
|
||||
write!(fmt, "Unsupported planar configuration “{:?}”.", config)
|
||||
}
|
||||
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 {
|
||||
TiffError::IoError(ref e) => Some(e),
|
||||
_ => None,
|
||||
|
||||
+2
-4
@@ -8,15 +8,13 @@
|
||||
|
||||
extern crate byteorder;
|
||||
extern crate lzw;
|
||||
#[macro_use]
|
||||
extern crate num_derive;
|
||||
extern crate num_traits;
|
||||
extern crate miniz_oxide;
|
||||
|
||||
pub mod decoder;
|
||||
pub mod encoder;
|
||||
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
|
||||
#[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!");
|
||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||
assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(8));
|
||||
let img_res = decoder.read_image();
|
||||
assert!(img_res.is_ok());
|
||||
let img_res = decoder.read_image().unwrap();
|
||||
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]
|
||||
@@ -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 mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
|
||||
let img_res = decoder.read_image();
|
||||
assert!(img_res.is_ok());
|
||||
let img_res = decoder.read_image().unwrap();
|
||||
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]
|
||||
@@ -30,8 +50,18 @@ fn test_gray_u16() {
|
||||
File::open("./tests/images/minisblack-1c-16b.tiff").expect("Cannot find test image!");
|
||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||
assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(16));
|
||||
let img_res = decoder.read_image();
|
||||
assert!(img_res.is_ok());
|
||||
let img_res = decoder.read_image().unwrap();
|
||||
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]
|
||||
@@ -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 mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(16));
|
||||
let img_res = decoder.read_image();
|
||||
assert!(img_res.is_ok());
|
||||
let img_res = decoder.read_image().unwrap();
|
||||
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]
|
||||
|
||||
+118
-1
@@ -2,7 +2,8 @@ extern crate tempfile;
|
||||
extern crate tiff;
|
||||
|
||||
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 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