Compare commits

..
41 Commits
Author SHA1 Message Date
HeroicKatora ff574b4ca2 Merge pull request #64 from image-rs/release-0.4
Document changes for release 0.4
2019-11-23 19:43:33 +01:00
HeroicKatora 6f87228458 Merge pull request #65 from image-rs/error-type
Improve the error types
2019-11-21 19:36:38 +01:00
Andreas Molzer 15929f8b8e Document changes for release 0.4
Fixes: #60
2019-11-21 19:23:03 +01:00
Andreas Molzer f13492e41d Fix a doc typo found in PR review 2019-11-21 18:46:55 +01:00
Andreas Molzer 1d9944c5c5 Mark both detailed error types as non-exhaustive
This makes it cheaper to introduce new features that might require
amending the list of variants by allowing us to introduce previously
uncovered causes or more specialized representations.
2019-11-20 20:58:45 +01:00
Andreas Molzer a14b23473b Export the inflate error publicly
It encapsulates the implementation detail of holding a miniz_oxide
status within. The derived traits are not too extensive for other
possible representations as well.
2019-11-20 20:02:55 +01:00
Andreas Molzer 101940335c Fix public constructor from miniz_oxide status
Avoids leaking the dependency on `miniz_oxide` into the public interface
by making a constructor taking a status argument crate internal. It
would otherwise not be possible to replace the deflate decoder without
breaking that interface.
2019-11-20 19:56:13 +01:00
HeroicKatora 8e1c67ee53 Merge pull request #63 from image-rs/tagged
Replace FromPrimitive with our own tag macro
2019-11-12 20:14:25 +01:00
Andreas Molzer 7b672998db Get rid of num-traits crate
Turns out the only use was as a private bound which was unused within
that method.
2019-11-12 20:06:02 +01:00
HeroicKatora 62fee106a7 Merge pull request #62 from onnotechno/multipage_image_patch
Multipage image patch
2019-11-12 13:13:57 +01:00
Onno Kammeraad fd789dc420 Cursor::new(Vec::new()) requires a &mut instead of just & 2019-11-12 13:09:09 +01:00
Andreas Molzer 07e8501ad6 Prefer matching over if-else-chain in tags macro
Thanks to the review comment for pointing this style issue out.
2019-11-12 10:53:38 +01:00
OnnoandHeroicKatora f70447325e Update tests/encode_images.rs
Co-Authored-By: HeroicKatora <HeroicKatora@users.noreply.github.com>
2019-11-12 10:40:19 +01:00
Onno Kammeraad b2eb480c44 add multipage testcase, to validate the multipage image handling 2019-11-12 09:44:27 +01:00
Onno Kammeraad 686e9b2648 added a missing offset increase while adding padding 2019-11-11 16:15:01 +01:00
Andreas Molzer 517638a38f Prefer explicit conversion over as
The discriminant of some enum types can be retrieved with the `as`
operator and in C style enums it is also possible to set a specific
discriminant. However, that may silently change—breaking the value
written to the file. It is better to use that technique mostly for ffi
purposes.
2019-11-10 23:20:19 +01:00
Andreas Molzer a6696ab02f fixup! Move everything to the tags macro 2019-11-10 19:24:22 +01:00
Andreas Molzer 258edf4a46 Remove the num-derive dependency 2019-11-10 18:02:43 +01:00
Andreas Molzer f88c9f00c3 Move everything to the tags macro 2019-11-10 18:02:42 +01:00
Andreas Molzer f80f4a06d5 Make the Unknown field optional
Several other uses that currently rely on num-traits for conversion have
no Unknown representation. Thus, the converter now outputs only known
variants or an error and the exposing function then can put the error
value into an Unknown variant instead.
2019-11-10 18:02:08 +01:00
Onno Kammeraad cbae42ea94 fixed support for multipage images, the IDF offset is added during the new and also at the end of the internal_finish. This should only be done once 2019-11-06 14:04:18 +01:00
Onno Kammeraad 80b8ec5cdb Somehow the current offset was invalid when using multipage images. Now using the seek function to get the current offset 2019-11-06 13:58:29 +01:00
Andreas Molzer ffdeb62b9f Rework the tag macro to make it reusable
Factor out parts that are unique to instantiations such as the name, the
documentation, the underlying type, the public conversion methods.

Also adds the ability to document individual items and remodels the
syntax to more closely resemble ordinary enum definitions.
2019-11-01 01:35:18 +01:00
HeroicKatora 6aea7b33c1 Merge pull request #49 from Masterchef365/deflate
Old-style DEFLATE compression support
2019-11-01 00:42:18 +01:00
HeroicKatora 770262cb02 Merge pull request #25 from birktj/new_tests
Test decoder output
2019-10-15 04:35:06 +02:00
HeroicKatora 1f957029af Merge pull request #55 from trolleyman/master
Make Limits struct fields publicly accessible
2019-10-14 21:07:11 +02:00
Masterchef365 f8d4b57120 Change implementation details for InflateError, add uncompressed cap 2019-10-04 18:52:39 -07:00
Masterchef365 6f9f2adfbf Improved error messages for inflate decompression 2019-10-02 21:06:11 -07:00
Masterchef365 205626bd62 Switch to miniz_oxide crate for DEFLATE decompression 2019-10-02 18:51:47 -07:00
Masterchef365 ce73d39d57 Add old-style DEFLATE support 2019-10-02 18:51:07 -07:00
Masterchef365 b79128d766 Add Deflate and OldDeflate compression type recognition 2019-10-02 18:51:07 -07:00
HeroicKatora c8f384d6a6 Merge pull request #58 from paolobarbolini/update-num-derive
update num-derive to 0.3, bump rustc to 1.34.2
2019-10-01 21:01:21 +02:00
Paolo Barbolini ce1d4cb289 update num-derive to 0.3 change minimal rustc to 1.34.2 2019-10-01 20:31:43 +02:00
HeroicKatora b95e5b8f95 Merge pull request #56 from trolleyman/invalid_test_names
Renamed test files
2019-09-22 23:43:51 +02:00
Callum Tolley bc05e0b7c6 Renamed files 2019-09-22 22:25:29 +01:00
Callum Tolley 3868f78067 Make Limits struct fields publicly accessible 2019-09-22 22:11:24 +01:00
HeroicKatora f6a91065d9 Merge pull request #53 from JohnTitor/remove-try-macros
Remove `try!` macros
2019-08-29 21:40:27 +02:00
Yuki Okushi ea19f0f686 Remove try! macros 2019-08-29 19:10:27 +09:00
HeroicKatora 3b85b68ae9 Merge pull request #51 from 51systems/tiff6-signed
Signed value support
2019-08-16 16:52:27 +02:00
Dustin Thomson 30eb8487a6 Signed value support
Add support for SBYTE, SSHORT, SLONG, SRATIONAL as per the Tiff 6.0 spec
Add support for multiple BYTE
2019-08-14 21:22:56 -06:00
Birk Tjelmeland 7fb354fe43 Test decoder output
These tests are based on the assumtion that the current decoder is
correct and are to make sure that future changes to the decoder
wont break it.
2019-02-01 11:19:21 +01:00
137 changed files with 834 additions and 180 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
language: rust
rust:
- 1.24.1
- 1.34.2
- stable
- beta
- nightly
+12
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+21
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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));
}
}
Loaded 100 of 137 files, more files were not shown because too many files have changed in this diff. Show more