Compare commits

..
55 Commits
Author SHA1 Message Date
Andreas Molzer 2635bdd7d5 Update release notes for alpha release for BigTIFF 2020-08-13 18:02:26 +02:00
Farkal ff874612ba Allow to decode LONG8 (u64)
TileOffsets and TileByteCounts have the LONG8 type on bigtiff.
2020-08-10 15:32:47 +02:00
HeroicKatora ea217dc238 Merge pull request #83 from Farkal/big-tiff
Big tiff decoding support
2020-08-08 12:33:54 +02:00
Etienne Prothon b2da2f2333 Merge branch 'master' of github.com:Farkal/image-tiff into big-tiff 2020-07-27 12:01:55 +02:00
Farkal ba1daa18b2 Allow custom encoding process (#84)
Simplify encoding of tags
2020-07-27 11:29:09 +02:00
Etienne Prothon ba3f6e604a feat: Add BigTiff enum values 2020-07-27 10:52:43 +02:00
HeroicKatora a471591f06 Merge pull request #77 from Farkal/read_ascii_in_offset
Read ascii encoded in offset
2020-07-24 02:25:17 +02:00
Etienne Prothon dacc2864e8 feat: better compatibility with old api 2020-07-24 01:22:33 +02:00
Etienne Prothon 797e3dca07 fix: Big tiff decoding and add tests 2020-07-24 00:47:14 +02:00
Etienne Prothon 23751bd8c5 feat: Support decoding big tiff 2020-07-23 01:33:41 +02:00
HeroicKatora 7fb68dbbe7 Merge pull request #78 from Farkal/support_tiled_data_tags
Support tile and sample format tags
2020-07-22 18:52:57 +02:00
HeroicKatora 4afd7ed462 Merge pull request #81 from spenczar/patch-1
Update link to TIFF specification in docs
2020-07-21 18:30:27 +02:00
Spencer Nelson aa49e7d6f0 Update link to TIFF specification in docs
Adobe refactored their site and the specification has moved.
2020-07-21 09:23:12 -07:00
Etienne Prothon cbb41fd479 feat: Support tile and sample format tags 2020-07-15 16:55:34 +02:00
Etienne Prothon 9edc0e656e fix: remove unwrap 2020-07-15 16:39:00 +02:00
Etienne Prothon 9f4873bae8 fix: Read ascii encoded in offset 2020-07-15 16:21:09 +02:00
HeroicKatora 472cf6d07d Merge pull request #75 from image-rs/release-0.5
Update notes and metadata for 0.5.0
2020-06-01 20:13:42 +02:00
Andreas Molzer ee73341bbf Update notes and metadata for 0.5.0 2020-06-01 20:05:59 +02:00
HeroicKatora 6c7cc7ec30 Merge pull request #71 from aschampion/issues/69-u16-lzw-error
Fix #69
2020-03-31 16:55:39 +02:00
Andrew Champion 24d347ecf5 Tests: add regression test for issue 69 2020-02-09 18:37:16 +00:00
Andrew Champion a439baa948 Decoder: remove test make wrong assumption
The byte count for strips is the compressed length. The compressed
length may be larger than the uncompressed length, especially with poor
compression choices (multi-byte samples with packbits) or small images.
2020-02-09 18:37:16 +00:00
Andrew Champion 6e23274bea Decoder: max uncompressed length for LZW must respect buffer byte length
Fixes #69.
2020-02-09 18:24:22 +00:00
HeroicKatora 119dec5068 Merge pull request #67 from aschampion/issues/misc-cleanup
Refactoring, cleanup, safety, docs & CMYK(16|32|64) support
2020-02-06 17:55:26 +01:00
Andrew Champion ec0477125f Tags: restore primitive conversions as inherent methods 2020-02-01 18:25:05 +00:00
Andrew Champion 1c8e98bca5 Squash to cast checking 2020-01-02 12:47:39 +00:00
Andrew Champion e6985063ac Fix formatting in README table 2019-12-21 15:57:24 +00:00
Andrew Champion 42c94ac3d9 Add support for CMYK(16|32|64) encoding 2019-12-21 15:57:24 +00:00
Andrew Champion eadec8f0b9 Remove most unchecked size/index casts
Convert most unchecked size/index `as` casts to checked casts with
`TryFrom`, included another top-level error variant.

This has several motivations:
- Making the library as robust as possible, including to platform
differences.
- Unchecked casts are susceptible to breakage during refactorings.
- Preparing for BigTIFF support, which will require the introduction of
generics and associated types for some parts of the de/encoders.
Unchecked primitive casts cannot be bound, while TryFrom can, and
overflowing conversions become more likely.
- Remaining u16 casts may be prone in failure in existing practice and
not just in theory during future changes.
2019-12-21 15:57:24 +00:00
Andrew Champion 462697e373 Add support for CMYK(16|32|64) decoding 2019-12-21 15:57:24 +00:00
Andrew Champion c537f5cc64 Add decode test for CMYK(8) 2019-12-21 15:57:24 +00:00
Andrew Champion 8f61196d70 Note 32 and 64 bit support in README 2019-12-21 15:57:24 +00:00
Andrew Champion 298dc8841d Remove unchecked casts and magic values for some tags 2019-12-21 15:57:24 +00:00
Andrew Champion f92be13f6c Factor tags into own module
Tag representations are not decoder- or encoder-specific, so should live
in their own module accessible to both for reuse. The decoder::ifd
module remains since this module is mainly dedicated to decoding the
IFD.
2019-12-21 15:57:24 +00:00
Andrew Champion f9cace84da Deduplicate primitive conversions for tag enum 2019-12-21 15:57:24 +00:00
Andrew Champion d510a9f4ab Strictly handle expected tag value types
Resolve several TODOs where tag values were cast from u32 without checks
by providing generic methods that error when the value fails casting to
the desired type. In addition to being more correct, this simplifies and
shortens decoding logic.

Remove `find_tag_u32` and `find_tag_u32_vec` as they are now redundant
and a GitHub code search found no usages outside copies of
`image-tiff`.
2019-12-21 15:57:24 +00:00
Andrew Champion 1fafcccb36 Remove needless cast (minor) 2019-12-21 15:57:24 +00:00
Andrew Champion 8b96793671 Simplify expression (minor) 2019-12-21 15:57:24 +00:00
Andrew Champion 27ae65160d Deduplicate decoding buffer construction 2019-12-21 15:57:24 +00:00
Andrew Champion 29bb8c2dfe Summarize supported features in README 2019-12-21 15:57:24 +00:00
Andrew Champion c5fcba45a0 Fix typo in error display message 2019-12-21 15:57:24 +00:00
HeroicKatora cbaa9dbc5b Merge pull request #68 from aschampion/refactors/test-dedup
Deduplicate tests and remove tempfile dev-dependency
2019-12-21 16:47:42 +01:00
Andrew Champion 959254aee9 Deduplicate encoding tests
Also removes dev dependency on tempfile.
2019-12-21 15:12:25 +00:00
Andrew Champion 3f14098060 Deduplicate decoding tests 2019-12-21 15:11:47 +00:00
Andrew Champion 57783ba43f Merge pull request #66 from aschampion/features/32-bit
Support 32- and 64-bit integer sample grayscale and RGB(A) images
2019-12-19 13:19:35 +00:00
Andrew Champion abd6aee19f Remove outdate and ambiguous TODO (minor) 2019-12-16 22:10:57 +00:00
Andrew Champion c72a8d364d Add tests for exotic 12-bit sample images
These images should correctly report their color depths, but fail to
decode as they are unsupported.
2019-12-16 18:53:44 +00:00
Andrew Champion e5d2e7862c Parse all homogeneous bit depth RGB(A) color types
Make the behavior of RGB and RGBA like Gray in returning ColorTypes for
all homogeneous bit depths, even if those depths are not 8, 16, 32 or
64. Note that this does not change which color types can actually be
decoded.
2019-12-11 15:03:05 +00:00
Andrew Champion 6e61de81b6 Simplify color type matching 2019-12-11 12:56:11 +00:00
Andrew Champion 8c4cea5d97 Support 64-bit encoding
Note that this adds the LONG8 data type from BigTIFF because of bounds
requiring TiffValue. However, this datatype should not actually be
written to tags.
2019-12-10 20:18:53 +00:00
Andrew Champion 0c53e1066a Support 64-bit rgb and rgba decoding 2019-12-10 20:18:53 +00:00
Andrew Champion f4e985e0bd Support 64-bit grayscale decoding 2019-12-10 20:18:53 +00:00
Andrew Champion af010edcfd Support 32-bit encoding 2019-12-10 20:18:53 +00:00
Andrew Champion f32b3b9d10 Fix warnings in encoding test 2019-12-10 20:18:53 +00:00
Andrew Champion 1f92c562bc Support 32-bit rgb and rgba decoding 2019-12-10 20:18:51 +00:00
Andrew Champion fca4e1f88a Support 32-bit grayscale decoding 2019-12-10 20:18:23 +00:00
27 changed files with 1303 additions and 741 deletions

No files matched your search

+18
View File
@@ -1,3 +1,21 @@
# Version 0.6.0-alpha
* Support for decoding BigTIFF with 64-bit offsets
* Better support for adding auxiliary tags before encoding image data
* Fixed decoding of inline ASCII in tags
* Fixed handling after null terminator in ASCII data
* Recognize tile and sample format tags
* Note: these notes will be merged into 0.6.0 when it is released
# Version 0.5.0
* Added support for 32-bit and 64-bit decoded values.
* Added CMYK(16|32|64) color type support.
* Check many internal integer conversions to increase stability. This should
only lead to images being reported as faulty that would previously silently
break platform limits. If there are any false positives, please report them.
* Remove an erroneous check of decoded length in lzw compressed images.
# Version 0.4.0
* Several enumerations are now non_exhaustive for future extensions.
+1 -4
View File
@@ -1,6 +1,6 @@
[package]
name = "tiff"
version = "0.4.0"
version = "0.6.0-alpha"
authors = [
"ccgn",
"bvssvni <bvssvni@gmail.com>",
@@ -20,6 +20,3 @@ exclude = ["tests/images/*", "tests/fuzz_images/*"]
byteorder = "1.2"
lzw = "0.10"
miniz_oxide = "0.3"
[dev-dependencies]
tempfile = "3.0"
+40
View File
@@ -4,3 +4,43 @@
[![Further crate info](https://img.shields.io/crates/v/tiff.svg)](https://crates.io/crates/tiff)
TIFF decoding and encoding library in pure Rust
## Supported
### Features
- Baseline spec (other than formats and tags listed below as not supported)
- Multipage
- Incremental decoding
### Formats
This table lists photometric interpretations and sample formats which are supported for encoding and decoding. The entries are `ColorType` variants for which sample bit depths are supported. Only samples where all bit depths are equal are currently supported. For example, `RGB(8)` means that the bit depth [8, 8, 8] is supported and will be interpreted as an 8 bit per channel RGB color type.
| `PhotometricInterpretation` | UINT Format | IEEEFP Format |
|-|-|-|
| `WhiteIsZero` | Gray(8\|16\|32\|64) | |
| `BlackIsZero` | Gray(8\|16\|32\|64) | |
| `RGB` | RGB(8\|16\|32\|64), RGBA(8\|16\|32\|64) | |
| `RGBPalette` | | |
| `Mask` | | |
| `CMYK` | CMYK(8\|16\|32\|64) | |
| `YCbCr` | | |
| `CIELab` | | |
### Compressions
| | Decoding | Encoding |
|-|-|-|
| None | ✓ | ✓ |
| LZW | ✓ | |
| Deflate | ✓ | |
| PackBits | ✓ | |
## Not yet supported
Formats and interpretations not listed above or with empty entries are unsupported.
- Baseline tags
- `ExtraSamples`
- Extension tags
- BigTIFF
+265 -216
View File
@@ -1,149 +1,29 @@
//! Function for reading TIFF tags
use std::collections::HashMap;
use std::convert::{TryInto, TryFrom};
use std::io::{self, Read, Seek};
use std::mem;
use super::stream::{ByteOrder, EndianReader, SmartReader};
use tags::{Tag, Type};
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational};
macro_rules! tags {
{
// 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,)*
}
} => {
$( #[$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),
}
}
#[inline(always)]
fn __to_inner_type(&self) -> $ty {
match *self {
$( $name::$tag => $val, )*
$( $name::Unknown(n) => { $unknown_doc; n }, )*
$name::__NonExhaustive => unreachable!(),
}
}
}
}
}
// 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,
// grayscale images PhotometricInterpretation 1 or 3
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,
// Advanced tags
Predictor = 317,
}
}
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)
}
}
use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational, RationalBig, UnsignedBig, SignedBig, SRationalBig};
#[allow(unused_qualifications)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Value {
Signed(i32),
SignedBig(i64),
Unsigned(u32),
UnsignedBig(u64),
List(Vec<Value>),
Rational(u32, u32),
RationalBig(u64, u64),
SRational(i32, i32),
SRationalBig(i64, i64),
Ascii(String),
#[doc(hidden)] // Do not match against this.
__NonExhaustive,
@@ -153,6 +33,7 @@ impl Value {
pub fn into_u32(self) -> TiffResult<u32> {
match self {
Unsigned(val) => Ok(val),
UnsignedBig(val) => Ok(u32::try_from(val)?),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
@@ -162,6 +43,27 @@ impl Value {
pub fn into_i32(self) -> TiffResult<i32> {
match self {
Signed(val) => Ok(val),
SignedBig(val) => Ok(i32::try_from(val)?),
val => Err(TiffError::FormatError(
TiffFormatError::SignedIntegerExpected(val),
)),
}
}
pub fn into_u64(self) -> TiffResult<u64> {
match self {
Unsigned(val) => Ok(val.into()),
UnsignedBig(val) => Ok(val),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
}
}
pub fn into_i64(self) -> TiffResult<i64> {
match self {
Signed(val) => Ok(val.into()),
SignedBig(val) => Ok(val),
val => Err(TiffError::FormatError(
TiffFormatError::SignedIntegerExpected(val),
)),
@@ -178,8 +80,10 @@ impl Value {
Ok(new_vec)
}
Unsigned(val) => Ok(vec![val]),
UnsignedBig(val) => Ok(vec![u32::try_from(val)?]),
Rational(numerator, denominator) => Ok(vec![numerator, denominator]),
Ascii(val) => Ok(val.chars().map(|x| x as u32).collect()),
RationalBig(numerator, denominator) => Ok(vec![u32::try_from(numerator)?, u32::try_from(denominator)?]),
Ascii(val) => Ok(val.chars().map(u32::from).collect()),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
@@ -196,25 +100,80 @@ impl Value {
new_vec.push(numerator);
new_vec.push(denominator);
}
_ => new_vec.push(v.into_i32()?)
SRationalBig(numerator, denominator) => {
new_vec.push(i32::try_from(numerator)?);
new_vec.push(i32::try_from(denominator)?);
}
_ => new_vec.push(v.into_i32()?),
}
}
Ok(new_vec)
}
Signed(val) => Ok(vec![val]),
SignedBig(val) => Ok(vec![i32::try_from(val)?]),
SRational(numerator, denominator) => Ok(vec![numerator, denominator]),
SRationalBig(numerator, denominator) => Ok(vec![i32::try_from(numerator)?, i32::try_from(denominator)?]),
val => Err(TiffError::FormatError(
TiffFormatError::SignedIntegerExpected(val),
)),
}
}
pub fn into_u64_vec(self) -> TiffResult<Vec<u64>> {
match self {
List(vec) => {
let mut new_vec = Vec::with_capacity(vec.len());
for v in vec {
new_vec.push(v.into_u64()?)
}
Ok(new_vec)
}
Unsigned(val) => Ok(vec![val.into()]),
UnsignedBig(val) => Ok(vec![val]),
Rational(numerator, denominator) => Ok(vec![numerator.into(), denominator.into()]),
RationalBig(numerator, denominator) => Ok(vec![numerator, denominator]),
Ascii(val) => Ok(val.chars().map(u32::from).map(u64::from).collect()),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
}
}
pub fn into_i64_vec(self) -> TiffResult<Vec<i64>> {
match self {
List(vec) => {
let mut new_vec = Vec::with_capacity(vec.len());
for v in vec {
match v {
SRational(numerator, denominator) => {
new_vec.push(numerator.into());
new_vec.push(denominator.into());
}
SRationalBig(numerator, denominator) => {
new_vec.push(numerator);
new_vec.push(denominator);
}
_ => new_vec.push(v.into_i64()?),
}
}
Ok(new_vec)
}
Signed(val) => Ok(vec![val.into()]),
SignedBig(val) => Ok(vec![val]),
SRational(numerator, denominator) => Ok(vec![numerator.into(), denominator.into()]),
SRationalBig(numerator, denominator) => Ok(vec![numerator, denominator]),
val => Err(TiffError::FormatError(
TiffFormatError::SignedIntegerExpected(val),
)),
}
}
}
#[derive(Clone)]
pub struct Entry {
type_: Type,
count: u32,
offset: [u8; 4],
count: u64,
offset: [u8; 8],
}
impl ::std::fmt::Debug for Entry {
@@ -228,6 +187,12 @@ impl ::std::fmt::Debug for Entry {
impl Entry {
pub fn new(type_: Type, count: u32, offset: [u8; 4]) -> Entry {
let mut offset = offset.to_vec();
offset.append(&mut vec![0; 4]);
Entry::new_u64(type_, count.into(), offset[..].try_into().unwrap())
}
pub fn new_u64(type_: Type, count: u64, offset: [u8; 8]) -> Entry {
Entry {
type_,
count,
@@ -246,103 +211,187 @@ impl Entry {
decoder: &mut super::Decoder<R>,
) -> TiffResult<Value> {
let bo = decoder.byte_order();
match (self.type_, self.count) {
// TODO check if this could give wrong results
// at a different endianess of file/computer.
(Type::BYTE, 1) => Ok(Unsigned(u32::from(self.offset[0]))),
(Type::BYTE, 2) => offset_to_bytes(2, self),
(Type::BYTE, 3) => offset_to_bytes(3, self),
(Type::BYTE, 4) => offset_to_bytes(4, self),
(Type::BYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(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![
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
]))
}
(Type::SSHORT, 2) => {
let mut r = self.r(bo);
Ok(List(vec![
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
]))
}
(Type::SHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Unsigned(u32::from(decoder.read_short()?)))
}),
(Type::SSHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Signed(i32::from(decoder.read_sshort()?)))
}),
(Type::LONG, 1) => Ok(Unsigned(self.r(bo).read_u32()?)),
(Type::SLONG, 1) => Ok(Signed(self.r(bo).read_i32()?)),
(Type::LONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Unsigned(decoder.read_long()?))
}),
(Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Signed(decoder.read_slong()?))
}),
(Type::RATIONAL, 1) => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
let numerator = decoder.read_long()?;
let denominator = decoder.read_long()?;
Ok(Rational(numerator, denominator))
}
(Type::SRATIONAL, 1) => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
let numerator = decoder.read_slong()?;
let denominator = decoder.read_slong()?;
Ok(SRational(numerator, denominator))
}
(Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Rational(
decoder.read_long()?,
decoder.read_long()?,
))
}),
(Type::SRATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(SRational(
decoder.read_slong()?,
decoder.read_slong()?,
))
}),
(Type::ASCII, n) => {
if n as usize > limits.decoding_buffer_size {
return Err(TiffError::LimitsExceeded);
let bigtiff_value = if decoder.bigtiff {
match (self.type_, self.count) {
(Type::BYTE, 5) => Some(offset_to_bytes(5, self)?),
(Type::BYTE, 6) => Some(offset_to_bytes(6, self)?),
(Type::BYTE, 7) => Some(offset_to_bytes(7, self)?),
(Type::BYTE, 8) => Some(offset_to_bytes(8, self)?),
(Type::SBYTE, 5) => Some(offset_to_sbytes(5, self)?),
(Type::SBYTE, 6) => Some(offset_to_sbytes(6, self)?),
(Type::SBYTE, 7) => Some(offset_to_sbytes(7, self)?),
(Type::SBYTE, 8) => Some(offset_to_sbytes(8, self)?),
(Type::SHORT, 3) => {
let mut r = self.r(bo);
Some(List(vec![
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
]))
}
decoder.goto_offset(self.r(bo).read_u32()?)?;
let string = decoder.read_string(n as usize)?;
Ok(Ascii(string))
(Type::SSHORT, 3) => {
let mut r = self.r(bo);
Some(List(vec![
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
]))
}
(Type::SHORT, 4) => {
let mut r = self.r(bo);
Some(List(vec![
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
]))
}
(Type::SSHORT, 4) => {
let mut r = self.r(bo);
Some(List(vec![
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
]))
}
(Type::LONG, 2) => {
let mut r = self.r(bo);
Some(List(vec![
Unsigned(r.read_u32()?),
Unsigned(r.read_u32()?),
]))
}
(Type::SLONG, 2) => {
let mut r = self.r(bo);
Some(List(vec![
Signed(r.read_i32()?),
Signed(r.read_i32()?),
]))
}
(Type::RATIONAL, 1) => {
let mut r = self.r(bo);
let numerator = r.read_u32()?;
let denominator = r.read_u32()?;
Some(Rational(numerator, denominator))
}
(Type::SRATIONAL, 1) => {
let mut r = self.r(bo);
let numerator = r.read_i32()?;
let denominator = r.read_i32()?;
Some(SRational(numerator, denominator))
}
_ => None
}
} else {
None
};
if let Some(v) = bigtiff_value {
Ok(v)
} else {
match (self.type_, self.count) {
// TODO check if this could give wrong results
// at a different endianess of file/computer.
(Type::BYTE, 1) => Ok(Unsigned(u32::from(self.offset[0]))),
(Type::BYTE, 2) => offset_to_bytes(2, self),
(Type::BYTE, 3) => offset_to_bytes(3, self),
(Type::BYTE, 4) => offset_to_bytes(4, self),
(Type::BYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(UnsignedBig(u64::from(decoder.read_byte()?)))
}),
(Type::SBYTE, 1) => Ok(Signed(i32::from(self.offset[0] as i8))),
(Type::SBYTE, 2) => offset_to_sbytes(2, self),
(Type::SBYTE, 3) => offset_to_sbytes(3, self),
(Type::SBYTE, 4) => offset_to_sbytes(4, self),
(Type::SBYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(SignedBig(i64::from(decoder.read_byte()? as i8)))
}),
(Type::SHORT, 1) => Ok(Unsigned(u32::from(self.r(bo).read_u16()?))),
(Type::SSHORT, 1) => Ok(Signed(i32::from(self.r(bo).read_i16()?))),
(Type::SHORT, 2) => {
let mut r = self.r(bo);
Ok(List(vec![
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
]))
}
(Type::SSHORT, 2) => {
let mut r = self.r(bo);
Ok(List(vec![
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
]))
}
(Type::SHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(UnsignedBig(u64::from(decoder.read_short()?)))
}),
(Type::SSHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(SignedBig(i64::from(decoder.read_sshort()?)))
}),
(Type::LONG, 1) => Ok(Unsigned(self.r(bo).read_u32()?)),
(Type::SLONG, 1) => Ok(Signed(self.r(bo).read_i32()?)),
(Type::LONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Unsigned(decoder.read_long()?))
}),
(Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Signed(decoder.read_slong()?))
}),
(Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Rational(decoder.read_long()?, decoder.read_long()?))
}),
(Type::SRATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(SRational(decoder.read_slong()?, decoder.read_slong()?))
}),
(Type::LONG8, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(UnsignedBig(decoder.read_long8()?))
}),
(Type::ASCII, n) => {
let n = usize::try_from(n)?;
if n > limits.decoding_buffer_size {
return Err(TiffError::LimitsExceeded);
}
if (n <= 4 && !decoder.bigtiff) || (n <= 8 && decoder.bigtiff) {
let mut buf = vec![0; n];
self.r(bo).read_exact(&mut buf )?;
let v = String::from_utf8(buf)?;
let v = v.trim_matches(char::from(0));
Ok(Ascii(v.into()))
} else {
if decoder.bigtiff {
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
} else {
decoder.goto_offset(self.r(bo).read_u32()?)?
}
let string = decoder.read_string(n)?;
Ok(Ascii(string))
}
}
_ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedDataType,
)),
}
_ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedDataType,
)),
}
}
#[inline]
fn decode_offset<R, F>(&self, value_count: u32, bo: ByteOrder, limits: &super::Limits, decoder: &mut super::Decoder<R>, decode_fn: F) -> TiffResult<Value>
fn decode_offset<R, F>(&self, value_count: u64, 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>() {
let value_count = usize::try_from(value_count)?;
if value_count > 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()?)?;
let mut v = Vec::with_capacity(value_count);
if decoder.bigtiff {
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
} else {
decoder.goto_offset(self.r(bo).read_u32()?)?
}
for _ in 0..value_count {
v.push(decode_fn(decoder)?)
}
+300 -241
View File
@@ -1,3 +1,4 @@
use std::convert::TryFrom;
use std::collections::HashMap;
use std::io::{self, Read, Seek};
use std::cmp;
@@ -5,10 +6,10 @@ use std::cmp;
use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
use self::ifd::Directory;
use tags::{CompressionMethod, PhotometricInterpretation, Predictor, Tag, Type};
use self::stream::{ByteOrder, EndianReader, LZWReader, DeflateReader, PackBitsReader, SmartReader};
#[macro_use]
pub mod ifd;
mod stream;
@@ -19,6 +20,10 @@ pub enum DecodingResult {
U8(Vec<u8>),
/// A vector of unsigned words
U16(Vec<u16>),
/// A vector of 32 bit unsigned ints
U32(Vec<u32>),
/// A vector of 64 bit unsigned ints
U64(Vec<u64>),
}
impl DecodingResult {
@@ -38,10 +43,28 @@ impl DecodingResult {
}
}
fn new_u32(size: usize, limits: &Limits) -> TiffResult<DecodingResult> {
if size > limits.decoding_buffer_size / 4 {
Err(TiffError::LimitsExceeded)
} else {
Ok(DecodingResult::U32(vec![0; size]))
}
}
fn new_u64(size: usize, limits: &Limits) -> TiffResult<DecodingResult> {
if size > limits.decoding_buffer_size / 8 {
Err(TiffError::LimitsExceeded)
} else {
Ok(DecodingResult::U64(vec![0; size]))
}
}
pub fn as_buffer(&mut self, start: usize) -> DecodingBuffer {
match *self {
DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]),
DecodingResult::U16(ref mut buf) => DecodingBuffer::U16(&mut buf[start..]),
DecodingResult::U32(ref mut buf) => DecodingBuffer::U32(&mut buf[start..]),
DecodingResult::U64(ref mut buf) => DecodingBuffer::U64(&mut buf[start..]),
}
}
}
@@ -52,6 +75,10 @@ pub enum DecodingBuffer<'a> {
U8(&'a mut [u8]),
/// A slice of unsigned words
U16(&'a mut [u16]),
/// A slice of 32 bit unsigned ints
U32(&'a mut [u32]),
/// A slice of 64 bit unsigned ints
U64(&'a mut [u64]),
}
impl<'a> DecodingBuffer<'a> {
@@ -59,6 +86,8 @@ impl<'a> DecodingBuffer<'a> {
match *self {
DecodingBuffer::U8(ref buf) => buf.len(),
DecodingBuffer::U16(ref buf) => buf.len(),
DecodingBuffer::U32(ref buf) => buf.len(),
DecodingBuffer::U64(ref buf) => buf.len(),
}
}
@@ -66,6 +95,8 @@ impl<'a> DecodingBuffer<'a> {
match *self {
DecodingBuffer::U8(_) => 1,
DecodingBuffer::U16(_) => 2,
DecodingBuffer::U32(_) => 4,
DecodingBuffer::U64(_) => 8,
}
}
@@ -76,96 +107,17 @@ impl<'a> DecodingBuffer<'a> {
match *self {
DecodingBuffer::U8(ref mut buf) => DecodingBuffer::U8(buf),
DecodingBuffer::U16(ref mut buf) => DecodingBuffer::U16(buf),
DecodingBuffer::U32(ref mut buf) => DecodingBuffer::U32(buf),
DecodingBuffer::U64(ref mut buf) => DecodingBuffer::U64(buf),
}
}
}
tags! {
pub enum PhotometricInterpretation(u16) {
WhiteIsZero = 0,
BlackIsZero = 1,
RGB = 2,
RGBPalette = 3,
TransparencyMask = 4,
CMYK = 5,
YCbCr = 6,
CIELab = 8,
}
}
tags! {
pub enum CompressionMethod(u16) {
None = 1,
Huffman = 2,
Fax3 = 3,
Fax4 = 4,
LZW = 5,
JPEG = 6,
Deflate = 8,
OldDeflate = 0x80B2,
PackBits = 0x8005,
}
}
tags! {
pub enum PlanarConfiguration(u16) {
Chunky = 1,
Planar = 2,
}
}
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 {
strip_index: usize,
strip_offsets: Vec<u32>,
strip_bytes: Vec<u32>,
strip_offsets: Vec<u64>,
strip_bytes: Vec<u64>,
}
/// Decoding limits
@@ -205,8 +157,9 @@ where
{
reader: SmartReader<R>,
byte_order: ByteOrder,
bigtiff: bool,
limits: Limits,
next_ifd: Option<u32>,
next_ifd: Option<u64>,
ifd: Option<Directory>,
width: u32,
height: u32,
@@ -233,12 +186,24 @@ impl Wrapping for u16 {
}
}
fn rev_hpredict_nsamp<T>(image: &mut [T], size: (u32, u32), samples: usize)
impl Wrapping for u32 {
fn wrapping_add(&self, other: Self) -> Self {
u32::wrapping_add(*self, other)
}
}
impl Wrapping for u64 {
fn wrapping_add(&self, other: Self) -> Self {
u64::wrapping_add(*self, other)
}
}
fn rev_hpredict_nsamp<T>(image: &mut [T], size: (u32, u32), samples: usize) -> TiffResult<()>
where
T: Copy + Wrapping,
{
let width = size.0 as usize;
let height = size.1 as usize;
let width = usize::try_from(size.0)?;
let height = usize::try_from(size.1)?;
for row in 0..height {
for col in samples..width * samples {
let prev_pixel = image[(row * width * samples + col - samples)];
@@ -246,13 +211,15 @@ where
*pixel = pixel.wrapping_add(prev_pixel);
}
}
Ok(())
}
fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType) -> TiffResult<()> {
let samples = match color_type {
ColorType::Gray(8) | ColorType::Gray(16) => 1,
ColorType::RGB(8) | ColorType::RGB(16) => 3,
ColorType::RGBA(8) | ColorType::RGBA(16) | ColorType::CMYK(8) => 4,
ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) | ColorType::Gray(64) => 1,
ColorType::RGB(8) | ColorType::RGB(16) | ColorType::RGB(32) | ColorType::RGB(64) => 3,
ColorType::RGBA(8) | ColorType::RGBA(16) | ColorType::RGBA(32) | ColorType::RGBA(64)
| ColorType::CMYK(8) | ColorType::CMYK(16) | ColorType::CMYK(32) | ColorType::CMYK(64) => 4,
_ => {
return Err(TiffError::UnsupportedError(
TiffUnsupportedError::HorizontalPredictor(color_type),
@@ -261,10 +228,16 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType)
};
match image {
DecodingBuffer::U8(buf) => {
rev_hpredict_nsamp(buf, size, samples);
rev_hpredict_nsamp(buf, size, samples)?;
}
DecodingBuffer::U16(buf) => {
rev_hpredict_nsamp(buf, size, samples);
rev_hpredict_nsamp(buf, size, samples)?;
}
DecodingBuffer::U32(buf) => {
rev_hpredict_nsamp(buf, size, samples)?;
}
DecodingBuffer::U64(buf) => {
rev_hpredict_nsamp(buf, size, samples)?;
}
}
Ok(())
@@ -276,6 +249,7 @@ impl<R: Read + Seek> Decoder<R> {
Decoder {
reader: SmartReader::wrap(r, ByteOrder::LittleEndian),
byte_order: ByteOrder::LittleEndian,
bigtiff: false,
limits: Default::default(),
next_ifd: None,
ifd: None,
@@ -301,22 +275,29 @@ impl<R: Read + Seek> Decoder<R> {
pub fn colortype(&mut self) -> TiffResult<ColorType> {
match self.photometric_interpretation {
// TODO: catch also [ 8, 8, 8, _] this does not work due to a bug in rust atm
PhotometricInterpretation::RGB if self.bits_per_sample == [8, 8, 8, 8] => {
Ok(ColorType::RGBA(8))
}
PhotometricInterpretation::RGB if self.bits_per_sample == [8, 8, 8] => {
Ok(ColorType::RGB(8))
}
PhotometricInterpretation::RGB if self.bits_per_sample == [16, 16, 16, 16] => {
Ok(ColorType::RGBA(16))
}
PhotometricInterpretation::RGB if self.bits_per_sample == [16, 16, 16] => {
Ok(ColorType::RGB(16))
}
PhotometricInterpretation::CMYK if self.bits_per_sample == [8, 8, 8, 8] => {
Ok(ColorType::CMYK(8))
}
PhotometricInterpretation::RGB => {
match self.bits_per_sample[..] {
[r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)),
[r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)),
_ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::InterpretationWithBits(
self.photometric_interpretation,
self.bits_per_sample.clone(),
),
)),
}
},
PhotometricInterpretation::CMYK => {
match self.bits_per_sample[..] {
[c, m, y, k] if [c, c, c] == [m, y, k] => Ok(ColorType::CMYK(c)),
_ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::InterpretationWithBits(
self.photometric_interpretation,
self.bits_per_sample.clone(),
),
)),
}
},
PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero
if self.bits_per_sample.len() == 1 =>
{
@@ -351,12 +332,28 @@ impl<R: Read + Seek> Decoder<R> {
))
}
}
if self.read_short()? != 42 {
return Err(TiffError::FormatError(
match self.read_short()? {
42 => self.bigtiff = false,
43 => {
self.bigtiff = true;
// Read bytesize of offsets (in bigtiff it's alway 8 but provide a way to move to 16 some day)
if self.read_short()? != 8 {
return Err(TiffError::FormatError(
TiffFormatError::TiffSignatureNotFound,
))
}
// This constant should always be 0
if self.read_short()? != 0 {
return Err(TiffError::FormatError(
TiffFormatError::TiffSignatureNotFound,
))
}
},
_ => return Err(TiffError::FormatError(
TiffFormatError::TiffSignatureInvalid,
));
))
}
self.next_ifd = match self.read_long()? {
self.next_ifd = match self.read_ifd_offset()? {
0 => None,
n => Some(n),
};
@@ -375,50 +372,27 @@ impl<R: Read + Seek> Decoder<R> {
/// To determine whether there are more images call `TIFFDecoder::more_images` instead.
pub fn next_image(&mut self) -> TiffResult<()> {
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.width = self.get_tag_u32(Tag::ImageWidth)?;
self.height = self.get_tag_u32(Tag::ImageLength)?;
self.strip_decoder = None;
// 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(
TiffUnsupportedError::UnknownInterpretation,
))
}
};
// 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(
TiffUnsupportedError::UnknownCompressionMethod,
))
}
}
self.photometric_interpretation = self
.find_tag_unsigned(Tag::PhotometricInterpretation)?
.and_then(PhotometricInterpretation::from_u16)
.ok_or(TiffUnsupportedError::UnknownInterpretation)?;
if let Some(val) = self.find_tag_unsigned(Tag::Compression)? {
self.compression_method = CompressionMethod::from_u16(val)
.ok_or(TiffUnsupportedError::UnknownCompressionMethod)?;
}
if let Some(val) = self.find_tag_u32(ifd::Tag::SamplesPerPixel)? {
self.samples = val as u8
if let Some(val) = self.find_tag_unsigned(Tag::SamplesPerPixel)? {
self.samples = val;
}
match self.samples {
1 => {
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) = self.find_tag_u32_vec(ifd::Tag::BitsPerSample)? {
self.bits_per_sample = val.iter().map(|&v| v as u8).collect()
}
}
_ => {
return Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedSampleDepth(self.samples),
))
}
1 | 3 | 4 => if let Some(val) = self.find_tag_unsigned_vec(Tag::BitsPerSample)? {
self.bits_per_sample = val;
},
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()),
}
Ok(())
}
@@ -433,6 +407,15 @@ impl<R: Read + Seek> Decoder<R> {
self.byte_order
}
#[inline]
pub fn read_ifd_offset(&mut self) -> Result<u64, io::Error> {
if self.bigtiff {
self.read_long8()
} else {
self.read_long().map(u64::from)
}
}
/// Reads a TIFF byte value
#[inline]
pub fn read_byte(&mut self) -> Result<u8, io::Error> {
@@ -465,29 +448,53 @@ impl<R: Read + Seek> Decoder<R> {
self.reader.read_i32()
}
#[inline]
pub fn read_long8(&mut self) -> Result<u64, io::Error> {
self.reader.read_u64()
}
/// Reads a string
#[inline]
pub fn read_string(&mut self, length: usize) -> TiffResult<String> {
let mut out = String::with_capacity(length);
self.reader.read_to_string(&mut out)?;
let mut out = vec![0; length];
self.reader.read_exact(&mut out)?;
// Strings may be null-terminated, so we trim anything downstream of the null byte
let trimmed = out.bytes().take_while(|&n| n != 0).collect::<Vec<u8>>();
Ok(String::from_utf8(trimmed)?)
if let Some(first) = out.iter().position(|&b| b == 0) {
out.truncate(first);
}
Ok(String::from_utf8(out)?)
}
/// Reads a TIFF IFA offset/value field
#[inline]
pub fn read_offset(&mut self) -> Result<[u8; 4], io::Error> {
pub fn read_offset(&mut self) -> TiffResult<[u8; 4]> {
if self.bigtiff {
return Err(TiffError::FormatError(TiffFormatError::InconsistentSizesEncountered))
}
let mut val = [0; 4];
self.reader.read_exact(&mut val)?;
Ok(val)
}
/// Reads a TIFF IFA offset/value field
#[inline]
pub fn read_offset_u64(&mut self) -> Result<[u8; 8], io::Error> {
let mut val = [0; 8];
self.reader.read_exact(&mut val)?;
Ok(val)
}
/// Moves the cursor to the specified offset
#[inline]
pub fn goto_offset(&mut self, offset: u32) -> io::Result<()> {
self.goto_offset_u64(offset.into())
}
#[inline]
pub fn goto_offset_u64(&mut self, offset: u64) -> io::Result<()> {
self.reader
.seek(io::SeekFrom::Start(u64::from(offset)))
.seek(io::SeekFrom::Start(offset))
.map(|_| ())
}
@@ -498,9 +505,9 @@ impl<R: Read + Seek> Decoder<R> {
// Type 2 bytes
// 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(self.read_short()?);
let type_ = match ifd::Type::from_u16(self.read_short()?) {
fn read_entry(&mut self) -> TiffResult<Option<(Tag, ifd::Entry)>> {
let tag = Tag::from_u16_exhaustive(self.read_short()?);
let type_ = match Type::from_u16(self.read_short()?) {
Some(t) => t,
None => {
// Unknown type. Skip this entry according to spec.
@@ -509,13 +516,22 @@ impl<R: Read + Seek> Decoder<R> {
return Ok(None);
}
};
Ok(Some((
tag,
let entry = if self.bigtiff {
ifd::Entry::new_u64(
type_,
self.read_long8()?,
self.read_offset_u64()?
)
} else {
ifd::Entry::new(
type_,
self.read_long()?, // count
self.read_offset()?, // offset
),
self.read_long()?,
self.read_offset()?
)
};
Ok(Some((
tag,
entry,
)))
}
@@ -528,16 +544,17 @@ impl<R: Read + Seek> Decoder<R> {
TiffFormatError::ImageFileDirectoryNotFound,
))
}
Some(offset) => self.goto_offset(offset)?,
Some(offset) => self.goto_offset_u64(offset)?,
}
for _ in 0..self.read_short()? {
let num_tags = if self.bigtiff { self.read_long8()? } else { self.read_short()?.into() };
for _ in 0..num_tags {
let (tag, entry) = match self.read_entry()? {
Some(val) => val,
None => continue, // Unknown data type in tag, skip
};
dir.insert(tag, entry);
}
self.next_ifd = match self.read_long()? {
self.next_ifd = match self.read_ifd_offset()? {
0 => None,
n => Some(n),
};
@@ -546,7 +563,7 @@ impl<R: Read + Seek> Decoder<R> {
/// Tries to retrieve a tag.
/// Return `Ok(None)` if the tag is not present.
pub fn find_tag(&mut self, tag: ifd::Tag) -> TiffResult<Option<ifd::Value>> {
pub fn find_tag(&mut self, tag: Tag) -> TiffResult<Option<ifd::Value>> {
let entry = match self.ifd.as_ref().unwrap().get(&tag) {
None => return Ok(None),
Some(entry) => entry.clone(),
@@ -557,25 +574,36 @@ impl<R: Read + Seek> Decoder<R> {
Ok(Some(entry.val(&limits, self)?))
}
/// Tries to retrieve a tag and convert it to the desired type.
pub fn find_tag_u32(&mut self, tag: ifd::Tag) -> TiffResult<Option<u32>> {
match self.find_tag(tag)? {
Some(val) => val.into_u32().map(Some),
None => Ok(None),
}
/// Tries to retrieve a tag and convert it to the desired unsigned type.
pub fn find_tag_unsigned<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<Option<T>> {
self.find_tag(tag)?
.map(|v| v.into_u64()).transpose()?
.map(|value| T::try_from(value)
.map_err(|_| TiffFormatError::InvalidTagValueType(tag).into()))
.transpose()
}
/// Tries to retrieve a tag and convert it to the desired type.
pub fn find_tag_u32_vec(&mut self, tag: ifd::Tag) -> TiffResult<Option<Vec<u32>>> {
match self.find_tag(tag)? {
Some(val) => val.into_u32_vec().map(Some),
None => Ok(None),
}
/// Tries to retrieve a vector of all a tag's values and convert them to
/// the desired unsigned type.
pub fn find_tag_unsigned_vec<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<Option<Vec<T>>> {
self.find_tag(tag)?
.map(|v| v.into_u64_vec()).transpose()?
.map(|v| v.into_iter()
.map(|u| T::try_from(u).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into()))
.collect())
.transpose()
}
/// Tries to retrieve a tag and convert it to the desired unsigned type.
/// Returns an error if the tag is not present.
pub fn get_tag_unsigned<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<T> {
self.find_tag_unsigned(tag)?
.ok_or_else(|| TiffFormatError::RequiredTagNotFound(tag).into())
}
/// 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> {
pub fn get_tag(&mut self, tag: Tag) -> TiffResult<ifd::Value> {
match self.find_tag(tag)? {
Some(val) => Ok(val),
None => Err(TiffError::FormatError(
@@ -585,39 +613,45 @@ impl<R: Read + Seek> Decoder<R> {
}
/// Tries to retrieve a tag and convert it to the desired type.
pub fn get_tag_u32(&mut self, tag: ifd::Tag) -> TiffResult<u32> {
pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult<u32> {
self.get_tag(tag)?.into_u32()
}
pub fn get_tag_u64(&mut self, tag: Tag) -> TiffResult<u64> {
self.get_tag(tag)?.into_u64()
}
/// Tries to retrieve a tag and convert it to the desired type.
pub fn get_tag_u32_vec(&mut self, tag: ifd::Tag) -> TiffResult<Vec<u32>> {
pub fn get_tag_u32_vec(&mut self, tag: Tag) -> TiffResult<Vec<u32>> {
self.get_tag(tag)?.into_u32_vec()
}
pub fn get_tag_u64_vec(&mut self, tag: Tag) -> TiffResult<Vec<u64>> {
self.get_tag(tag)?.into_u64_vec()
}
/// Decompresses the strip into the supplied buffer.
/// Returns the number of bytes read.
fn expand_strip<'a>(
&mut self,
buffer: DecodingBuffer<'a>,
offset: u32,
length: u32,
offset: u64,
length: u64,
max_uncompressed_length: usize,
) -> TiffResult<usize> {
let color_type = self.colortype()?;
self.goto_offset(offset)?;
let (bytes, mut reader): (usize, Box<EndianReader>) = match self.compression_method {
self.goto_offset_u64(offset)?;
let (bytes, mut reader): (usize, Box<dyn EndianReader>) = match self.compression_method {
CompressionMethod::None => {
let order = self.reader.byte_order;
(
length as usize,
usize::try_from(length)?,
Box::new(SmartReader::wrap(&mut self.reader, order)),
)
}
CompressionMethod::LZW => {
let (bytes, reader) = LZWReader::new(
&mut self.reader,
length as usize,
max_uncompressed_length
usize::try_from(length)?,
max_uncompressed_length * buffer.byte_len()
)?;
(bytes, Box::new(reader))
}
@@ -626,7 +660,7 @@ impl<R: Read + Seek> Decoder<R> {
let (bytes, reader) = PackBitsReader::new(
&mut self.reader,
order,
length as usize
usize::try_from(length)?,
)?;
(bytes, Box::new(reader))
}
@@ -655,10 +689,41 @@ impl<R: Read + Seek> Decoder<R> {
bytes
}
(ColorType::RGBA(16), DecodingBuffer::U16(ref mut buffer))
| (ColorType::RGB(16), DecodingBuffer::U16(ref mut buffer)) => {
| (ColorType::RGB(16), DecodingBuffer::U16(ref mut buffer))
| (ColorType::CMYK(16), DecodingBuffer::U16(ref mut buffer)) => {
reader.read_u16_into(&mut buffer[..bytes / 2])?;
bytes / 2
}
(ColorType::RGBA(32), DecodingBuffer::U32(ref mut buffer))
| (ColorType::RGB(32), DecodingBuffer::U32(ref mut buffer))
| (ColorType::CMYK(32), DecodingBuffer::U32(ref mut buffer)) => {
reader.read_u32_into(&mut buffer[..bytes / 4])?;
bytes / 4
}
(ColorType::RGBA(64), DecodingBuffer::U64(ref mut buffer))
| (ColorType::RGB(64), DecodingBuffer::U64(ref mut buffer))
| (ColorType::CMYK(64), DecodingBuffer::U64(ref mut buffer)) => {
reader.read_u64_into(&mut buffer[..bytes / 8])?;
bytes / 8
}
(ColorType::Gray(64), DecodingBuffer::U64(ref mut buffer)) => {
reader.read_u64_into(&mut buffer[..bytes / 8])?;
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
for datum in buffer[..bytes / 8].iter_mut() {
*datum = 0xffff_ffff_ffff_ffff - *datum
}
}
bytes / 8
}
(ColorType::Gray(32), DecodingBuffer::U32(ref mut buffer)) => {
reader.read_u32_into(&mut buffer[..bytes / 4])?;
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
for datum in buffer[..bytes / 4].iter_mut() {
*datum = 0xffff_ffff - *datum
}
}
bytes / 4
}
(ColorType::Gray(16), DecodingBuffer::U16(ref mut buffer)) => {
reader.read_u16_into(&mut buffer[..bytes / 2])?;
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
@@ -688,7 +753,7 @@ impl<R: Read + Seek> Decoder<R> {
/// Number of strips in image
pub fn strip_count(&mut self) -> TiffResult<u32> {
let rows_per_strip = self
.get_tag_u32(ifd::Tag::RowsPerStrip)
.get_tag_u32(Tag::RowsPerStrip)
.unwrap_or(self.height);
if rows_per_strip == 0 {
@@ -700,8 +765,8 @@ impl<R: Read + Seek> Decoder<R> {
fn initialize_strip_decoder(&mut self) -> TiffResult<()> {
if self.strip_decoder.is_none() {
let strip_offsets = self.get_tag_u32_vec(ifd::Tag::StripOffsets)?;
let strip_bytes = self.get_tag_u32_vec(ifd::Tag::StripByteCounts)?;
let strip_offsets = self.get_tag_u64_vec(Tag::StripOffsets)?;
let strip_bytes = self.get_tag_u64_vec(Tag::StripByteCounts)?;
self.strip_decoder = Some(StripDecodeState {
strip_index: 0,
@@ -735,18 +800,18 @@ impl<R: Read + Seek> Decoder<R> {
TiffFormatError::InconsistentSizesEncountered,
))?;
let rows_per_strip = self
.get_tag_u32(ifd::Tag::RowsPerStrip)
.unwrap_or(self.height) as usize;
let rows_per_strip = usize::try_from(self
.get_tag_u32(Tag::RowsPerStrip)
.unwrap_or(self.height))?;
let strip_height = cmp::min(
rows_per_strip,
self.height as usize - index * rows_per_strip,
usize::try_from(self.height)? - index * rows_per_strip,
);
let buffer_size = self.width as usize * strip_height * self.bits_per_sample.len();
let buffer_size = usize::try_from(self.width)? * strip_height * self.bits_per_sample.len();
if buffer.len() < buffer_size || byte_count as usize / buffer.byte_len() > buffer_size {
if buffer.len() < buffer_size {
return Err(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered,
));
@@ -756,7 +821,7 @@ impl<R: Read + Seek> Decoder<R> {
self.strip_decoder.as_mut().unwrap().strip_index += 1;
if index as u32 == self.strip_count()? {
if u32::try_from(index)? == self.strip_count()? {
self.strip_decoder = None;
}
@@ -765,14 +830,13 @@ impl<R: Read + Seek> Decoder<R> {
TiffFormatError::InconsistentSizesEncountered,
));
}
if let Ok(predictor) = self.get_tag_u32(ifd::Tag::Predictor) {
// TODO: error on non-SHORT value.
match Predictor::from_u16(predictor as u16) {
if let Ok(predictor) = self.get_tag_unsigned(Tag::Predictor) {
match Predictor::from_u16(predictor) {
Some(Predictor::None) => (),
Some(Predictor::Horizontal) => {
rev_hpredict(
buffer.copy(),
(self.width, strip_height as u32),
(self.width, u32::try_from(strip_height)?),
self.colortype()?,
)?;
}
@@ -787,31 +851,37 @@ impl<R: Read + Seek> Decoder<R> {
Ok(())
}
fn result_buffer(&self, height: usize) -> TiffResult<DecodingResult> {
let buffer_size = usize::try_from(self.width)? * height * self.bits_per_sample.len();
match self.bits_per_sample.iter().cloned().max().unwrap_or(8) {
n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits),
n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits),
n if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits),
n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits),
n => {
Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
))
}
}
}
/// Read a single strip from the image and return it as a Vector
pub fn read_strip(&mut self) -> TiffResult<DecodingResult> {
self.initialize_strip_decoder()?;
let index = self.strip_decoder.as_ref().unwrap().strip_index;
let rows_per_strip = self
.get_tag_u32(ifd::Tag::RowsPerStrip)
.unwrap_or(self.height) as usize;
let rows_per_strip = usize::try_from(self
.get_tag_u32(Tag::RowsPerStrip)
.unwrap_or(self.height))?;
let strip_height = cmp::min(
rows_per_strip,
self.height as usize - index * rows_per_strip,
usize::try_from(self.height)? - index * rows_per_strip,
);
let buffer_size = self.width as usize * strip_height * self.bits_per_sample.iter().count();
let mut result = match self.bits_per_sample.iter().cloned().max().unwrap_or(8) {
n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits)?,
n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits)?,
n => {
return Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
))
}
};
let mut result = self.result_buffer(strip_height)?;
self.read_strip_to_buffer(result.as_buffer(0))?;
@@ -821,27 +891,16 @@ impl<R: Read + Seek> Decoder<R> {
/// Decodes the entire image and return it as a Vector
pub fn read_image(&mut self) -> TiffResult<DecodingResult> {
self.initialize_strip_decoder()?;
let rows_per_strip = self
.get_tag_u32(ifd::Tag::RowsPerStrip)
.unwrap_or(self.height) as usize;
let rows_per_strip = usize::try_from(self
.get_tag_u32(Tag::RowsPerStrip)
.unwrap_or(self.height))?;
let samples_per_strip =
self.width as usize * rows_per_strip * self.bits_per_sample.iter().count();
usize::try_from(self.width)? * rows_per_strip * self.bits_per_sample.len();
let buffer_size =
self.width as usize * self.height as usize * self.bits_per_sample.iter().count();
let mut result = self.result_buffer(usize::try_from(self.height)?)?;
let mut result = match self.bits_per_sample.iter().cloned().max().unwrap_or(8) {
n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits)?,
n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits)?,
n => {
return Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
))
}
};
for i in 0..self.strip_count()? as usize {
for i in 0..usize::try_from(self.strip_count()?)? {
self.read_strip_to_buffer(result.as_buffer(samples_per_strip * i))?;
}
+33
View File
@@ -59,6 +59,39 @@ pub trait EndianReader: Read {
}
}
#[inline(always)]
fn read_u32_into(&mut self, buffer: &mut [u32]) -> Result<(), io::Error> {
match self.byte_order() {
ByteOrder::LittleEndian => {
<Self as ReadBytesExt>::read_u32_into::<LittleEndian>(self, buffer)
}
ByteOrder::BigEndian => {
<Self as ReadBytesExt>::read_u32_into::<BigEndian>(self, buffer)
}
}
}
/// Reads an u64
#[inline(always)]
fn read_u64(&mut self) -> Result<u64, io::Error> {
match self.byte_order() {
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_u64::<LittleEndian>(self),
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_u64::<BigEndian>(self),
}
}
#[inline(always)]
fn read_u64_into(&mut self, buffer: &mut [u64]) -> Result<(), io::Error> {
match self.byte_order() {
ByteOrder::LittleEndian => {
<Self as ReadBytesExt>::read_u64_into::<LittleEndian>(self, buffer)
}
ByteOrder::BigEndian => {
<Self as ReadBytesExt>::read_u64_into::<BigEndian>(self, buffer)
}
}
}
/// Reads an i32
#[inline(always)]
fn read_i32(&mut self) -> Result<i32, io::Error> {
+64 -1
View File
@@ -1,4 +1,4 @@
use decoder::PhotometricInterpretation;
use tags::PhotometricInterpretation;
/// Trait for different colortypes that can be encoded.
pub trait ColorType {
@@ -24,6 +24,20 @@ impl ColorType for Gray16 {
const BITS_PER_SAMPLE: &'static [u16] = &[16];
}
pub struct Gray32;
impl ColorType for Gray32 {
type Inner = u32;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
const BITS_PER_SAMPLE: &'static [u16] = &[32];
}
pub struct Gray64;
impl ColorType for Gray64 {
type Inner = u64;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
const BITS_PER_SAMPLE: &'static [u16] = &[64];
}
pub struct RGB8;
impl ColorType for RGB8 {
type Inner = u8;
@@ -38,6 +52,20 @@ impl ColorType for RGB16 {
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16];
}
pub struct RGB32;
impl ColorType for RGB32 {
type Inner = u32;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32];
}
pub struct RGB64;
impl ColorType for RGB64 {
type Inner = u64;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64];
}
pub struct RGBA8;
impl ColorType for RGBA8 {
type Inner = u8;
@@ -52,9 +80,44 @@ impl ColorType for RGBA16 {
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16];
}
pub struct RGBA32;
impl ColorType for RGBA32 {
type Inner = u16;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
}
pub struct RGBA64;
impl ColorType for RGBA64 {
type Inner = u16;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
}
pub struct CMYK8;
impl ColorType for CMYK8 {
type Inner = u8;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8];
}
pub struct CMYK16;
impl ColorType for CMYK16 {
type Inner = u16;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16];
}
pub struct CMYK32;
impl ColorType for CMYK32 {
type Inner = u32;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
}
pub struct CMYK64;
impl ColorType for CMYK64 {
type Inner = u64;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
}
+117 -78
View File
@@ -1,9 +1,10 @@
use byteorder::NativeEndian;
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::io::{Seek, Write};
use std::mem;
use decoder::ifd::{self, Tag};
use tags::{self, ResolutionUnit, Tag, Type};
use error::{TiffError, TiffFormatError, TiffResult};
pub mod colortype;
@@ -25,17 +26,10 @@ pub struct SRational {
pub d: i32,
}
/// Type to represent resolution units
pub enum ResolutionUnit {
None = 1,
Inch = 2,
Centimeter = 3,
}
/// Trait for types that can be encoded in a tiff file
pub trait TiffValue {
const BYTE_LEN: u32;
const FIELD_TYPE: ifd::Type;
const FIELD_TYPE: Type;
fn count(&self) -> u32;
fn bytes(&self) -> u32 {
self.count() * Self::BYTE_LEN
@@ -45,7 +39,7 @@ pub trait TiffValue {
impl TiffValue for [u8] {
const BYTE_LEN: u32 = 1;
const FIELD_TYPE: ifd::Type = ifd::Type::BYTE;
const FIELD_TYPE: Type = Type::BYTE;
fn count(&self) -> u32 {
self.len() as u32
@@ -59,7 +53,7 @@ impl TiffValue for [u8] {
impl TiffValue for [i8] {
const BYTE_LEN: u32 = 1;
const FIELD_TYPE: ifd::Type = ifd::Type::SBYTE;
const FIELD_TYPE: Type = Type::SBYTE;
fn count(&self) -> u32 {
self.len() as u32
@@ -76,7 +70,7 @@ impl TiffValue for [i8] {
impl TiffValue for [u16] {
const BYTE_LEN: u32 = 2;
const FIELD_TYPE: ifd::Type = ifd::Type::SHORT;
const FIELD_TYPE: Type = Type::SHORT;
fn count(&self) -> u32 {
self.len() as u32
@@ -93,7 +87,7 @@ impl TiffValue for [u16] {
impl TiffValue for [i16] {
const BYTE_LEN: u32 = 2;
const FIELD_TYPE: ifd::Type = ifd::Type::SSHORT;
const FIELD_TYPE: Type = Type::SSHORT;
fn count(&self) -> u32 {
self.len() as u32
@@ -110,7 +104,7 @@ impl TiffValue for [i16] {
impl TiffValue for [u32] {
const BYTE_LEN: u32 = 4;
const FIELD_TYPE: ifd::Type = ifd::Type::LONG;
const FIELD_TYPE: Type = Type::LONG;
fn count(&self) -> u32 {
self.len() as u32
@@ -127,7 +121,7 @@ impl TiffValue for [u32] {
impl TiffValue for [i32] {
const BYTE_LEN: u32 = 4;
const FIELD_TYPE: ifd::Type = ifd::Type::SLONG;
const FIELD_TYPE: Type = Type::SLONG;
fn count(&self) -> u32 {
self.len() as u32
@@ -142,9 +136,26 @@ impl TiffValue for [i32] {
}
}
impl TiffValue for [u64] {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: Type = Type::LONG8;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
// We write using nativeedian so this sould be safe
let slice =
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 8) };
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [Rational] {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL;
const FIELD_TYPE: Type = Type::RATIONAL;
fn count(&self) -> u32 {
self.len() as u32
@@ -160,7 +171,7 @@ impl TiffValue for [Rational] {
impl TiffValue for [SRational] {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: ifd::Type = ifd::Type::SRATIONAL;
const FIELD_TYPE: Type = Type::SRATIONAL;
fn count(&self) -> u32 {
self.len() as u32
@@ -176,7 +187,7 @@ impl TiffValue for [SRational] {
impl TiffValue for u8 {
const BYTE_LEN: u32 = 1;
const FIELD_TYPE: ifd::Type = ifd::Type::BYTE;
const FIELD_TYPE: Type = Type::BYTE;
fn count(&self) -> u32 {
1
@@ -190,7 +201,7 @@ impl TiffValue for u8 {
impl TiffValue for i8 {
const BYTE_LEN: u32 = 1;
const FIELD_TYPE: ifd::Type = ifd::Type::SBYTE;
const FIELD_TYPE: Type = Type::SBYTE;
fn count(&self) -> u32 {
1
@@ -204,7 +215,7 @@ impl TiffValue for i8 {
impl TiffValue for u16 {
const BYTE_LEN: u32 = 2;
const FIELD_TYPE: ifd::Type = ifd::Type::SHORT;
const FIELD_TYPE: Type = Type::SHORT;
fn count(&self) -> u32 {
1
@@ -218,7 +229,7 @@ impl TiffValue for u16 {
impl TiffValue for i16 {
const BYTE_LEN: u32 = 2;
const FIELD_TYPE: ifd::Type = ifd::Type::SSHORT;
const FIELD_TYPE: Type = Type::SSHORT;
fn count(&self) -> u32 {
1
@@ -232,7 +243,7 @@ impl TiffValue for i16 {
impl TiffValue for u32 {
const BYTE_LEN: u32 = 4;
const FIELD_TYPE: ifd::Type = ifd::Type::LONG;
const FIELD_TYPE: Type = Type::LONG;
fn count(&self) -> u32 {
1
@@ -246,7 +257,7 @@ impl TiffValue for u32 {
impl TiffValue for i32 {
const BYTE_LEN: u32 = 4;
const FIELD_TYPE: ifd::Type = ifd::Type::SLONG;
const FIELD_TYPE: Type = Type::SLONG;
fn count(&self) -> u32 {
1
@@ -258,9 +269,23 @@ impl TiffValue for i32 {
}
}
impl TiffValue for u64 {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: Type = Type::LONG8;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u64(*self)?;
Ok(())
}
}
impl TiffValue for Rational {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL;
const FIELD_TYPE: Type = Type::RATIONAL;
fn count(&self) -> u32 {
1
@@ -275,7 +300,7 @@ impl TiffValue for Rational {
impl TiffValue for SRational {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: ifd::Type = ifd::Type::SRATIONAL;
const FIELD_TYPE: Type = Type::SRATIONAL;
fn count(&self) -> u32 {
1
@@ -290,7 +315,7 @@ impl TiffValue for SRational {
impl TiffValue for str {
const BYTE_LEN: u32 = 1;
const FIELD_TYPE: ifd::Type = ifd::Type::ASCII;
const FIELD_TYPE: Type = Type::ASCII;
fn count(&self) -> u32 {
self.len() as u32 + 1
@@ -309,7 +334,7 @@ impl TiffValue for str {
impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T {
const BYTE_LEN: u32 = T::BYTE_LEN;
const FIELD_TYPE: ifd::Type = T::FIELD_TYPE;
const FIELD_TYPE: Type = T::FIELD_TYPE;
fn count(&self) -> u32 {
(*self).count()
@@ -329,10 +354,9 @@ impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T {
///
/// # Examples
/// ```
/// # extern crate tempfile;
/// # extern crate tiff;
/// # fn main() {
/// # let mut file = tempfile::tempfile().unwrap();
/// # let mut file = std::io::Cursor::new(Vec::new());
/// # let image_data = vec![0; 100*100*3];
/// use tiff::encoder::*;
///
@@ -383,26 +407,9 @@ impl<W: Write + Seek> TiffEncoder<W> {
where
[C::Inner]: TiffValue,
{
let num_pix = (width as usize).checked_mul(height as usize)
.ok_or_else(|| ::std::io::Error::new(
::std::io::ErrorKind::InvalidInput,
"Image width * height exceeds usize"))?;
if data.len() < num_pix {
return Err(::std::io::Error::new(
::std::io::ErrorKind::InvalidData,
"Input data slice is undersized for provided dimensions").into());
}
let encoder = DirectoryEncoder::new(&mut self.writer)?;
let mut image: ImageEncoder<W, C> = ImageEncoder::new(encoder, width, height)?;
let mut idx = 0;
while image.next_strip_sample_count() > 0 {
let sample_count = image.next_strip_sample_count() as usize;
image.write_strip(&data[idx..idx + sample_count])?;
idx += sample_count;
}
image.finish()
let image: ImageEncoder<W, C> = ImageEncoder::new(encoder, width, height)?;
image.write_data(data)
}
}
@@ -432,16 +439,18 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
}
/// Write a single ifd tag.
pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) {
pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) -> TiffResult<()> {
let len = <T>::BYTE_LEN * value.count();
let mut bytes = Vec::with_capacity(len as usize);
let mut bytes = Vec::with_capacity(usize::try_from(len)?);
{
let mut writer = TiffWriter::new(&mut bytes);
value.write(&mut writer).unwrap();
value.write(&mut writer)?;
}
self.ifd
.insert(tag.to_u16(), (<T>::FIELD_TYPE.to_u16(), value.count(), bytes));
Ok(())
}
fn write_directory(&mut self) -> TiffResult<u64> {
@@ -452,7 +461,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
self.writer.write_bytes(bytes)?;
*bytes = vec![0, 0, 0, 0];
let mut writer = TiffWriter::new(bytes as &mut [u8]);
writer.write_u32(offset as u32)?;
writer.write_u32(u32::try_from(offset)?)?;
} else {
while bytes.len() < 4 {
bytes.push(0);
@@ -462,7 +471,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
let offset = self.writer.offset();
self.writer.write_u16(self.ifd.len() as u16)?;
self.writer.write_u16(u16::try_from(self.ifd.len())?)?;
for (tag, &(ref field_type, ref count, ref offset)) in self.ifd.iter() {
self.writer.write_u16(*tag)?;
self.writer.write_u16(*field_type)?;
@@ -487,7 +496,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
let curr_pos = self.writer.offset();
self.writer.goto_offset(self.ifd_pointer_pos)?;
self.writer.write_u32(ifd_pointer as u32)?;
self.writer.write_u32(u32::try_from(ifd_pointer)?)?;
self.writer.goto_offset(curr_pos)?;
self.writer.write_u32(0)?;
@@ -517,16 +526,19 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
///
/// # Examples
/// ```
/// # extern crate tempfile;
/// # extern crate tiff;
/// # fn main() {
/// # let mut file = tempfile::tempfile().unwrap();
/// # let mut file = std::io::Cursor::new(Vec::new());
/// # let image_data = vec![0; 100*100*3];
/// use tiff::encoder::*;
/// use tiff::tags::Tag;
///
/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
/// let mut image = tiff.new_image::<colortype::RGB8>(100, 100).unwrap();
///
/// // You can encode tags here
/// image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap();
///
/// let mut idx = 0;
/// while image.next_strip_sample_count() > 0 {
/// let sample_count = image.next_strip_sample_count() as usize;
@@ -536,11 +548,13 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
/// image.finish().unwrap();
/// # }
/// ```
/// You can also call write_data function wich will encode by strip and finish
pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType> {
encoder: DirectoryEncoder<'a, W>,
strip_idx: u64,
strip_count: u64,
row_samples: u64,
width: u32,
height: u32,
rows_per_strip: u64,
strip_offsets: Vec<u32>,
@@ -555,7 +569,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
width: u32,
height: u32,
) -> TiffResult<ImageEncoder<'a, W, T>> {
let row_samples = u64::from(width) * <T>::BITS_PER_SAMPLE.len() as u64;
let row_samples = u64::from(width) * u64::try_from(<T>::BITS_PER_SAMPLE.len())?;
let row_bytes = row_samples * u64::from(<T::Inner>::BYTE_LEN);
// As per tiff spec each strip should be about 8k long
@@ -563,19 +577,19 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
let strip_count = (u64::from(height) + rows_per_strip - 1) / rows_per_strip;
encoder.write_tag(Tag::ImageWidth, width);
encoder.write_tag(Tag::ImageLength, height);
encoder.write_tag(Tag::Compression, 1u16);
encoder.write_tag(Tag::ImageWidth, width)?;
encoder.write_tag(Tag::ImageLength, height)?;
encoder.write_tag(Tag::Compression, tags::CompressionMethod::None.to_u16())?;
encoder.write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE);
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16());
encoder.write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE)?;
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16())?;
encoder.write_tag(Tag::RowsPerStrip, rows_per_strip as u32);
encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?;
encoder.write_tag(Tag::SamplesPerPixel, <T>::BITS_PER_SAMPLE.len() as u16);
encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 });
encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 });
encoder.write_tag(Tag::ResolutionUnit, 1u16);
encoder.write_tag(Tag::SamplesPerPixel, u16::try_from(<T>::BITS_PER_SAMPLE.len())?)?;
encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 })?;
encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 })?;
encoder.write_tag(Tag::ResolutionUnit, ResolutionUnit::None.to_u16())?;
Ok(ImageEncoder {
encoder,
@@ -583,6 +597,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
strip_idx: 0,
row_samples,
rows_per_strip,
width,
height,
strip_offsets: Vec::new(),
strip_byte_count: Vec::new(),
@@ -613,47 +628,71 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
{
// TODO: Compression
let samples = self.next_strip_sample_count();
if value.len() as u64 != samples {
if u64::try_from(value.len())? != samples {
return Err(::std::io::Error::new(
::std::io::ErrorKind::InvalidData,
"Slice is wrong size for strip").into());
}
let offset = self.encoder.write_data(value)?;
self.strip_offsets.push(offset as u32);
self.strip_byte_count.push(value.bytes() as u32);
self.strip_offsets.push(u32::try_from(offset)?);
self.strip_byte_count.push(value.bytes());
self.strip_idx += 1;
Ok(())
}
/// Write strips from data
pub fn write_data(mut self, data: &[T::Inner]) -> TiffResult<()>
where
[T::Inner]: TiffValue,
{
let num_pix = usize::try_from(self.width)?.checked_mul(usize::try_from(self.height)?)
.ok_or_else(|| ::std::io::Error::new(
::std::io::ErrorKind::InvalidInput,
"Image width * height exceeds usize"))?;
if data.len() < num_pix {
return Err(::std::io::Error::new(
::std::io::ErrorKind::InvalidData,
"Input data slice is undersized for provided dimensions").into());
}
let mut idx = 0;
while self.next_strip_sample_count() > 0 {
let sample_count = usize::try_from(self.next_strip_sample_count())?;
self.write_strip(&data[idx..idx + sample_count])?;
idx += sample_count;
}
self.finish()?;
Ok(())
}
/// Set image resolution
pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) {
self.encoder.write_tag(Tag::ResolutionUnit, unit as u16);
self.encoder.write_tag(Tag::XResolution, value.clone());
self.encoder.write_tag(Tag::YResolution, value);
self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap();
self.encoder.write_tag(Tag::XResolution, value.clone()).unwrap();
self.encoder.write_tag(Tag::YResolution, value).unwrap();
}
/// Set image resolution unit
pub fn resolution_unit(&mut self, unit: ResolutionUnit) {
self.encoder.write_tag(Tag::ResolutionUnit, unit as u16);
self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap();
}
/// Set image x-resolution
pub fn x_resolution(&mut self, value: Rational) {
self.encoder.write_tag(Tag::XResolution, value);
self.encoder.write_tag(Tag::XResolution, value).unwrap();
}
/// Set image y-resolution
pub fn y_resolution(&mut self, value: Rational) {
self.encoder.write_tag(Tag::YResolution, value);
self.encoder.write_tag(Tag::YResolution, value).unwrap();
}
fn finish_internal(&mut self) -> TiffResult<()> {
self.encoder
.write_tag(Tag::StripOffsets, &*self.strip_offsets);
.write_tag(Tag::StripOffsets, &*self.strip_offsets)?;
self.encoder
.write_tag(Tag::StripByteCounts, &*self.strip_byte_count);
.write_tag(Tag::StripByteCounts, &*self.strip_byte_count)?;
self.dropped = true;
self.encoder.finish_internal()
+31 -4
View File
@@ -3,8 +3,8 @@ use std::fmt;
use std::io;
use std::string;
use decoder::ifd::{Tag, Value};
use decoder::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration};
use decoder::ifd::{Value};
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Tag};
use miniz_oxide::inflate::TINFLStatus;
use ColorType;
@@ -22,6 +22,10 @@ pub enum TiffError {
/// The Limits of the Decoder is exceeded.
LimitsExceeded,
/// An integer conversion to or from a platform size failed, either due to
/// limits of the platform size or limits of the format.
IntSizeError,
}
/// The image is not formatted properly.
@@ -38,8 +42,9 @@ pub enum TiffFormatError {
ImageFileDirectoryNotFound,
InconsistentSizesEncountered,
InvalidTag,
InvalidTagValueType(Tag),
RequiredTagNotFound(Tag),
UnknownPredictor(u32),
UnknownPredictor(u16),
UnsignedIntegerExpected(Value),
SignedIntegerExpected(Value),
InflateError(InflateError),
@@ -57,6 +62,8 @@ impl fmt::Display for TiffFormatError {
ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."),
InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."),
InvalidTag => write!(fmt, "Image contains invalid tag."),
InvalidTagValueType(ref tag) =>
write!(fmt, "Tag `{:?}` did not have the expected value type.", tag),
RequiredTagNotFound(ref tag) => write!(fmt, "Required tag `{:?}` not found.", tag),
UnknownPredictor(ref predictor) => {
write!(fmt, "Unknown predictor “{}” encountered", predictor)
@@ -139,7 +146,7 @@ impl fmt::Display for TiffUnsupportedError {
write!(fmt, "Compression method {:?} is unsupported", method)
}
UnsupportedSampleDepth(samples) => {
write!(fmt, "{} samples per pixel is supported.", samples)
write!(fmt, "{} samples per pixel is unsupported.", samples)
}
UnsupportedColorType(color_type) => {
write!(fmt, "Color type {:?} is unsupported", color_type)
@@ -168,6 +175,7 @@ impl fmt::Display for TiffError {
),
TiffError::IoError(ref e) => e.fmt(fmt),
TiffError::LimitsExceeded => write!(fmt, "The Decoder limits are exceeded"),
TiffError::IntSizeError => write!(fmt, "Platform or format size limits exceeded"),
}
}
}
@@ -179,6 +187,7 @@ impl Error for TiffError {
TiffError::UnsupportedError(..) => "Unsupported error",
TiffError::IoError(..) => "IO error",
TiffError::LimitsExceeded => "Decoder limits exceeded",
TiffError::IntSizeError => "Platform or format size limits exceeded",
}
}
@@ -202,5 +211,23 @@ impl From<string::FromUtf8Error> for TiffError {
}
}
impl From<TiffFormatError> for TiffError {
fn from(err: TiffFormatError) -> TiffError {
TiffError::FormatError(err)
}
}
impl From<TiffUnsupportedError> for TiffError {
fn from(err: TiffUnsupportedError) -> TiffError {
TiffError::UnsupportedError(err)
}
}
impl From<std::num::TryFromIntError> for TiffError {
fn from(_err: std::num::TryFromIntError) -> TiffError {
TiffError::IntSizeError
}
}
/// Result of an image decoding/encoding process
pub type TiffResult<T> = Result<T, TiffError>;
+2 -1
View File
@@ -4,7 +4,7 @@
//! lossless and lossy compression.
//!
//! # Related Links
//! * <http://partners.adobe.com/public/developer/tiff/index.html> - The TIFF specification
//! * <https://www.adobe.io/open/standards/TIFF.html> - The TIFF specification
extern crate byteorder;
extern crate lzw;
@@ -13,6 +13,7 @@ extern crate miniz_oxide;
pub mod decoder;
pub mod encoder;
mod error;
pub mod tags;
pub use self::error::{InflateError, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
+188
View File
@@ -0,0 +1,188 @@
macro_rules! tags {
{
// Permit arbitrary meta items, which include documentation.
$( #[$enum_attr:meta] )*
$vis:vis enum $name:ident($ty:tt) $(unknown($unknown_doc:literal))* {
// Each of the `Name = Val,` permitting documentation.
$($(#[$ident_attr:meta])* $tag:ident = $val:expr,)*
}
} => {
$( #[$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),
}
}
#[inline(always)]
fn __to_inner_type(&self) -> $ty {
match *self {
$( $name::$tag => $val, )*
$( $name::Unknown(n) => { $unknown_doc; n }, )*
$name::__NonExhaustive => unreachable!(),
}
}
}
tags!($name, $ty);
};
// For u16 tags, provide direct inherent primitive conversion methods.
($name:tt, u16) => {
impl $name {
#[inline(always)]
pub fn from_u16(val: u16) -> Option<Self> {
Self::__from_inner_type(val).ok()
}
#[inline(always)]
pub fn to_u16(&self) -> u16 {
Self::__to_inner_type(self)
}
}
};
// For other tag types, do nothing for now. With concat_idents one could
// provide inherent conversion methods for all types.
($name:tt, $ty:tt) => {};
}
// 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,
// grayscale images PhotometricInterpretation 1 or 3
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,
// Advanced tags
Predictor = 317,
TileWidth = 322,
TileLength = 323,
TileOffsets = 324,
TileByteCounts = 325,
SampleFormat = 339,
}
}
impl Tag {
pub fn from_u16_exhaustive(val: u16) -> Self {
Self::from_u16(val).unwrap_or_else(|| Tag::Unknown(val))
}
}
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,
/// BigTIFF 64-bit unsigned integer
LONG8 = 16,
}
}
tags! {
pub enum CompressionMethod(u16) {
None = 1,
Huffman = 2,
Fax3 = 3,
Fax4 = 4,
LZW = 5,
JPEG = 6,
Deflate = 8,
OldDeflate = 0x80B2,
PackBits = 0x8005,
}
}
tags! {
pub enum PhotometricInterpretation(u16) {
WhiteIsZero = 0,
BlackIsZero = 1,
RGB = 2,
RGBPalette = 3,
TransparencyMask = 4,
CMYK = 5,
YCbCr = 6,
CIELab = 8,
}
}
tags! {
pub enum PlanarConfiguration(u16) {
Chunky = 1,
Planar = 2,
}
}
tags! {
pub enum Predictor(u16) {
None = 1,
Horizontal = 2,
}
}
tags!{
/// Type to represent resolution units
pub enum ResolutionUnit(u16) {
None = 1,
Inch = 2,
Centimeter = 3,
}
}
+46
View File
@@ -0,0 +1,46 @@
extern crate tiff;
use tiff::decoder::Decoder;
use tiff::tags::Tag;
use tiff::ColorType;
use std::fs::File;
use std::path::PathBuf;
const TEST_IMAGE_DIR: &str = "./tests/images/bigtiff";
#[test]
fn test_big_tiff() {
let filenames = ["BigTIFF.tif", "BigTIFFMotorola.tif", "BigTIFFLong.tif"];
for filename in filenames.iter() {
let path = PathBuf::from(TEST_IMAGE_DIR).join(filename);
let img_file = File::open(path).expect("Cannot find test image!");
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
assert_eq!(
decoder.dimensions().expect("Cannot get dimensions"),
(64, 64)
);
assert_eq!(
decoder.colortype().expect("Cannot get colortype"),
ColorType::RGB(8)
);
assert_eq!(
decoder
.get_tag_u64(Tag::StripOffsets)
.expect("Cannot get StripOffsets"),
16
);
assert_eq!(
decoder
.get_tag_u64(Tag::RowsPerStrip)
.expect("Cannot get RowsPerStrip"),
64
);
assert_eq!(
decoder
.get_tag_u64(Tag::StripByteCounts)
.expect("Cannot get StripByteCounts"),
12288
)
}
}
+96 -66
View File
@@ -4,99 +4,123 @@ use tiff::decoder::{ifd, Decoder, DecodingResult};
use tiff::ColorType;
use std::fs::File;
use std::path::PathBuf;
#[test]
fn test_gray_u8() {
let img_file =
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().unwrap();
if let DecodingResult::U8(res) = img_res {
let mut res_sum: u64 = 0;
for x in res {
res_sum += x as u64;
const TEST_IMAGE_DIR: &str = "./tests/images/";
macro_rules! test_image_sum {
($name:ident, $buffer:ident, $sum_ty:ty) => {
fn $name(file: &str, expected_type: ColorType, expected_sum: $sum_ty) {
let path = PathBuf::from(TEST_IMAGE_DIR).join(file);
let img_file = File::open(path).expect("Cannot find test image!");
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
assert_eq!(decoder.colortype().unwrap(), expected_type);
let img_res = decoder.read_image().unwrap();
match img_res {
DecodingResult::$buffer(res) => {
let sum: $sum_ty = res.into_iter().map(<$sum_ty>::from).sum();
assert_eq!(sum, expected_sum);
},
_ => panic!("Wrong bit depth"),
}
}
};
}
assert_eq!(res_sum, 2840893);
}
else {
panic!("Wrong bit depth")
}
test_image_sum!(test_image_sum_u8, U8, u64);
test_image_sum!(test_image_sum_u16, U16, u64);
test_image_sum!(test_image_sum_u32, U32, u64);
test_image_sum!(test_image_sum_u64, U64, u64);
/// Tests that a decoder can be constructed for an image and the color type
/// read from the IFD and is of the appropriate type, but the type is
/// unsupported.
fn test_image_color_type_unsupported(file: &str, expected_type: ColorType) {
let path = PathBuf::from(TEST_IMAGE_DIR).join(file);
let img_file = File::open(path).expect("Cannot find test image!");
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
assert_eq!(decoder.colortype().unwrap(), expected_type);
assert!(match decoder.read_image() {
Err(tiff::TiffError::UnsupportedError(tiff::TiffUnsupportedError::UnsupportedColorType(_))) => true,
_ => false,
});
}
#[test]
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().unwrap();
if let DecodingResult::U8(res) = img_res {
let mut res_sum: u64 = 0;
for x in res {
res_sum += x as u64;
}
fn test_cmyk_u8() {
test_image_sum_u8("cmyk-3c-8b.tiff", ColorType::CMYK(8), 8522658);
}
assert_eq!(res_sum, 7842108)
}
else {
panic!("Wrong bit depth")
}
#[test]
fn test_cmyk_u16() {
test_image_sum_u16("cmyk-3c-16b.tiff", ColorType::CMYK(16), 2181426827);
}
#[test]
fn test_gray_u8() {
test_image_sum_u8("minisblack-1c-8b.tiff", ColorType::Gray(8), 2840893);
}
#[test]
fn test_gray_u12() {
test_image_color_type_unsupported("12bit.cropped.tiff", ColorType::Gray(12));
}
#[test]
fn test_gray_u16() {
let img_file =
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().unwrap();
if let DecodingResult::U16(res) = img_res {
let mut res_sum: u64 = 0;
for x in res {
res_sum += x as u64;
}
test_image_sum_u16("minisblack-1c-16b.tiff", ColorType::Gray(16), 733126239);
}
assert_eq!(res_sum, 733126239);
}
else {
panic!("Wrong bit depth")
}
#[test]
fn test_gray_u32() {
test_image_sum_u32("gradient-1c-32b.tiff", ColorType::Gray(32), 549892913787);
}
#[test]
fn test_gray_u64() {
test_image_sum_u64("gradient-1c-64b.tiff", ColorType::Gray(64), 549892913787);
}
#[test]
fn test_rgb_u8() {
test_image_sum_u8("rgb-3c-8b.tiff", ColorType::RGB(8), 7842108);
}
#[test]
fn test_rgb_u12() {
test_image_color_type_unsupported("12bit.cropped.rgb.tiff", ColorType::RGB(12));
}
#[test]
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().unwrap();
if let DecodingResult::U16(res) = img_res {
let mut res_sum: u64 = 0;
for x in res {
res_sum += x as u64;
}
test_image_sum_u16("rgb-3c-16b.tiff", ColorType::RGB(16), 2024349944);
}
assert_eq!(res_sum, 2024349944);
}
else {
panic!("Wrong bit depth")
}
#[test]
fn test_rgb_u32() {
test_image_sum_u32("gradient-3c-32b.tiff", ColorType::RGB(32), 2030834111716);
}
#[test]
fn test_rgb_u64() {
test_image_sum_u64("gradient-3c-64b.tiff", ColorType::RGB(64), 2030834111716);
}
#[test]
fn test_string_tags() {
// these files have null-terminated strings for their Software tag. One has extra bytes after
// the null byte, so we check both to ensure that we're truncating properly
let filenames = vec!["minisblack-1c-16b.tiff", "rgb-3c-16b.tiff"];
let filenames = ["minisblack-1c-16b.tiff", "rgb-3c-16b.tiff"];
for filename in filenames.iter() {
let path = format!("./tests/images/{}", filename);
let img_file = File::open(path).expect("can't open file");
let path = PathBuf::from(TEST_IMAGE_DIR).join(filename);
let img_file = File::open(path).expect("Cannot find test image!");
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
let software = decoder.get_tag(ifd::Tag::Software).unwrap();
let software = decoder.get_tag(tiff::tags::Tag::Software).unwrap();
match software {
ifd::Value::Ascii(s) => assert_eq!(
s,
String::from("GraphicsMagick 1.2 unreleased Q16 http://www.GraphicsMagick.org/")
&s,
"GraphicsMagick 1.2 unreleased Q16 http://www.GraphicsMagick.org/"
),
_ => assert!(false),
};
@@ -125,6 +149,12 @@ fn test_decode_data() {
}
}
#[test]
fn issue_69() {
test_image_sum_u16("issue_69_lzw.tiff", ColorType::Gray(16), 1015486);
test_image_sum_u16("issue_69_packbits.tiff", ColorType::Gray(16), 1015486);
}
// TODO: GrayA support
//#[test]
//fn test_gray_alpha_u8()
+102 -130
View File
@@ -1,13 +1,13 @@
extern crate tempfile;
extern crate tiff;
use tiff::decoder::{Decoder, DecodingResult};
use tiff::decoder::ifd::Tag;
use tiff::decoder::{Decoder, DecodingResult, ifd};
use tiff::encoder::{colortype, TiffEncoder, SRational};
use tiff::ColorType;
use tiff::tags::Tag;
use std::fs::File;
use std::io::{Cursor, Seek, SeekFrom};
use std::path::PathBuf;
#[test]
fn encode_decode() {
@@ -20,18 +20,22 @@ fn encode_decode() {
image_data.push(val);
}
}
let mut file = tempfile::tempfile().unwrap();
let mut file = Cursor::new(Vec::new());
{
let mut tiff = TiffEncoder::new(&mut file).unwrap();
tiff.write_image::<colortype::RGB8>(100, 100, &image_data)
let mut image = tiff.new_image::<colortype::RGB8>(100, 100)
.unwrap();
image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap();
image.write_data(&image_data).unwrap();
}
{
file.seek(SeekFrom::Start(0)).unwrap();
let mut decoder = Decoder::new(&mut file).unwrap();
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
assert_eq!(decoder.dimensions().unwrap(), (100, 100));
assert_eq!(decoder.get_tag(Tag::Artist).unwrap(), ifd::Value::Ascii("Image-tiff".into()));
if let DecodingResult::U8(img_res) = decoder.read_image().unwrap() {
assert_eq!(image_data, img_res);
} else {
@@ -54,127 +58,95 @@ fn test_encode_undersized_buffer() {
}
}
const TEST_IMAGE_DIR: &str = "./tests/images/";
macro_rules! test_roundtrip {
($name:ident, $buffer:ident, $buffer_ty:ty) => {
fn $name<C: colortype::ColorType<Inner=$buffer_ty>>(file: &str, expected_type: ColorType) {
let path = PathBuf::from(TEST_IMAGE_DIR).join(file);
let img_file = File::open(path).expect("Cannot find test image!");
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
assert_eq!(decoder.colortype().unwrap(), expected_type);
let image_data = match decoder.read_image().unwrap() {
DecodingResult::$buffer(res) => res,
_ => panic!("Wrong data type"),
};
let mut file = Cursor::new(Vec::new());
{
let mut tiff = TiffEncoder::new(&mut file).unwrap();
let (width, height) = decoder.dimensions().unwrap();
tiff.write_image::<C>(width, height, &image_data)
.unwrap();
}
file.seek(SeekFrom::Start(0)).unwrap();
{
let mut decoder = Decoder::new(&mut file).unwrap();
if let DecodingResult::$buffer(img_res) = decoder.read_image().unwrap() {
assert_eq!(image_data, img_res);
} else {
panic!("Wrong data type");
}
}
}
};
}
test_roundtrip!(test_u8_roundtrip, U8, u8);
test_roundtrip!(test_u16_roundtrip, U16, u16);
test_roundtrip!(test_u32_roundtrip, U32, u32);
test_roundtrip!(test_u64_roundtrip, U64, u64);
#[test]
fn test_gray_u8_roundtrip() {
let img_file =
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 image_data = match decoder.read_image().unwrap() {
DecodingResult::U8(res) => res,
_ => panic!("Wrong data type"),
};
let mut file = tempfile::tempfile().unwrap();
{
let mut tiff = TiffEncoder::new(&mut file).unwrap();
let (width, height) = decoder.dimensions().unwrap();
tiff.write_image::<colortype::Gray8>(width, height, &image_data)
.unwrap();
}
file.seek(SeekFrom::Start(0)).unwrap();
{
let mut decoder = Decoder::new(&mut file).unwrap();
if let DecodingResult::U8(img_res) = decoder.read_image().unwrap() {
assert_eq!(image_data, img_res);
} else {
panic!("Wrong data type");
}
}
test_u8_roundtrip::<colortype::Gray8>("minisblack-1c-8b.tiff", ColorType::Gray(8));
}
#[test]
fn test_rgb_u8_roundtrip() {
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));
test_u8_roundtrip::<colortype::RGB8>("rgb-3c-8b.tiff", ColorType::RGB(8));
}
let image_data = match decoder.read_image().unwrap() {
DecodingResult::U8(res) => res,
_ => panic!("Wrong data type"),
};
let mut file = tempfile::tempfile().unwrap();
{
let mut tiff = TiffEncoder::new(&mut file).unwrap();
let (width, height) = decoder.dimensions().unwrap();
tiff.write_image::<colortype::RGB8>(width, height, &image_data)
.unwrap();
}
file.seek(SeekFrom::Start(0)).unwrap();
{
let mut decoder = Decoder::new(&mut file).unwrap();
if let DecodingResult::U8(img_res) = decoder.read_image().unwrap() {
assert_eq!(image_data, img_res);
} else {
panic!("Wrong data type");
}
}
#[test]
fn test_cmyk_u8_roundtrip() {
test_u8_roundtrip::<colortype::CMYK8>("cmyk-3c-8b.tiff", ColorType::CMYK(8));
}
#[test]
fn test_gray_u16_roundtrip() {
let img_file =
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 image_data = match decoder.read_image().unwrap() {
DecodingResult::U16(res) => res,
_ => panic!("Wrong data type"),
};
let mut file = tempfile::tempfile().unwrap();
{
let mut tiff = TiffEncoder::new(&mut file).unwrap();
let (width, height) = decoder.dimensions().unwrap();
tiff.write_image::<colortype::Gray16>(width, height, &image_data)
.unwrap();
}
file.seek(SeekFrom::Start(0)).unwrap();
{
let mut decoder = Decoder::new(&mut file).unwrap();
if let DecodingResult::U16(img_res) = decoder.read_image().unwrap() {
assert_eq!(image_data, img_res);
} else {
panic!("Wrong data type");
}
}
test_u16_roundtrip::<colortype::Gray16>("minisblack-1c-16b.tiff", ColorType::Gray(16));
}
#[test]
fn test_rgb_u16_roundtrip() {
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));
test_u16_roundtrip::<colortype::RGB16>("rgb-3c-16b.tiff", ColorType::RGB(16));
}
let image_data = match decoder.read_image().unwrap() {
DecodingResult::U16(res) => res,
_ => panic!("Wrong data type"),
};
#[test]
fn test_cmyk_u16_roundtrip() {
test_u16_roundtrip::<colortype::CMYK16>("cmyk-3c-16b.tiff", ColorType::CMYK(16));
}
let mut file = tempfile::tempfile().unwrap();
{
let mut tiff = TiffEncoder::new(&mut file).unwrap();
#[test]
fn test_gray_u32_roundtrip() {
test_u32_roundtrip::<colortype::Gray32>("gradient-1c-32b.tiff", ColorType::Gray(32));
}
let (width, height) = decoder.dimensions().unwrap();
tiff.write_image::<colortype::RGB16>(width, height, &image_data)
.unwrap();
}
file.seek(SeekFrom::Start(0)).unwrap();
{
let mut decoder = Decoder::new(&mut file).unwrap();
if let DecodingResult::U16(img_res) = decoder.read_image().unwrap() {
assert_eq!(image_data, img_res);
} else {
panic!("Wrong data type");
}
}
#[test]
fn test_rgb_u32_roundtrip() {
test_u32_roundtrip::<colortype::RGB32>("gradient-3c-32b.tiff", ColorType::RGB(32));
}
#[test]
fn test_gray_u64_roundtrip() {
test_u64_roundtrip::<colortype::Gray64>("gradient-1c-64b.tiff", ColorType::Gray(64));
}
#[test]
fn test_rgb_u64_roundtrip() {
test_u64_roundtrip::<colortype::RGB64>("gradient-3c-64b.tiff", ColorType::RGB(64));
}
#[test]
@@ -186,11 +158,11 @@ fn test_multiple_byte() {
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][..]);
encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]).unwrap();
encoder.write_tag(Tag::Unknown(65001), &[1_u8, 2][..]).unwrap();
encoder.write_tag(Tag::Unknown(65002), &[1_u8, 2, 3][..]).unwrap();
encoder.write_tag(Tag::Unknown(65003), &[1_u8, 2, 3, 4][..]).unwrap();
encoder.write_tag(Tag::Unknown(65004), &[1_u8, 2, 3, 4, 5][..]).unwrap();
}
data.set_position(0);
@@ -216,24 +188,24 @@ fn test_signed() {
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(65000), -1_i8).unwrap();
encoder.write_tag(Tag::Unknown(65001), &[-1_i8][..]).unwrap();
encoder.write_tag(Tag::Unknown(65002), &[-1_i8, 2][..]).unwrap();
encoder.write_tag(Tag::Unknown(65003), &[-1_i8, 2, -3][..]).unwrap();
encoder.write_tag(Tag::Unknown(65004), &[-1_i8, 2, -3, 4][..]).unwrap();
encoder.write_tag(Tag::Unknown(65005), &[-1_i8, 2, -3, 4, -5][..]).unwrap();
encoder.write_tag(Tag::Unknown(65010), -1_i16);
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(65010), -1_i16).unwrap();
encoder.write_tag(Tag::Unknown(65011), -1_i16).unwrap();
encoder.write_tag(Tag::Unknown(65012), &[-1_i16, 2][..]).unwrap();
encoder.write_tag(Tag::Unknown(65013), &[-1_i16, 2, -3][..]).unwrap();
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(65020), -1_i32).unwrap();
encoder.write_tag(Tag::Unknown(65021), &[-1_i32][..]).unwrap();
encoder.write_tag(Tag::Unknown(65022), &[-1_i32, 2][..]).unwrap();
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 }][..]);
encoder.write_tag(Tag::Unknown(65030), SRational { n: -1, d: 100 }).unwrap();
encoder.write_tag(Tag::Unknown(65031), &[SRational { n: -1, d: 100 }, SRational { n: 2, d: 100 }][..]).unwrap();
}
//Rewind the cursor for reading
@@ -273,10 +245,10 @@ fn test_multipage_image() {
// 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[..]);
img_encoder.write_image::<colortype::Gray16>(2, 2, &img1[..]).unwrap();
// 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[..]);
img_encoder.write_image::<colortype::Gray8>(3, 3, &img2[..]).unwrap();
}
// seek to the beginning of the file, so that it can be decoded
@@ -291,4 +263,4 @@ fn test_multipage_image() {
// check the dimensions of the image in the second page
assert_eq!(img_decoder.dimensions().unwrap(), (3, 3));
}
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.