Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8bc935618 | ||
|
|
150ff8303b | ||
|
|
e44b98ddf9 | ||
|
|
4df26011d9 | ||
|
|
4dc7c2a986 | ||
|
|
b7c4f668ae | ||
|
|
9fe9b2e0a0 | ||
|
|
b7e8f210d1 | ||
|
|
371a9b53c3 | ||
|
|
d0e4f37a28 | ||
|
|
46c7efd836 | ||
|
|
65832e01bd | ||
|
|
b9590e0842 | ||
|
|
521b58dc07 | ||
|
|
e70121efbb | ||
|
|
a6fb855121 | ||
|
|
a874b9c3a5 | ||
|
|
d4ce174236 | ||
|
|
a43aa1fbc7 | ||
|
|
69c34ac33c | ||
|
|
00e0eb9006 | ||
|
|
ebd0e266f7 | ||
|
|
9b94e9de88 | ||
|
|
47dfa0aa3e | ||
|
|
1eb9c4fab7 | ||
|
|
201b95f749 | ||
|
|
7641dcbd3f | ||
|
|
60c6e0cd90 | ||
|
|
65a1c553af | ||
|
|
fddc048849 | ||
|
|
68f2182918 | ||
|
|
b2a7e35c85 | ||
|
|
91fe529859 | ||
|
|
5ca2730416 | ||
|
|
8fcacc829f | ||
|
|
59d0b7389d | ||
|
|
0ac444b63f | ||
|
|
ce1ee6776f | ||
|
|
29a020e5ec | ||
|
|
9021985477 | ||
|
|
d4c958f915 | ||
|
|
dc04282290 | ||
|
|
4cfed1c7b1 | ||
|
|
745ed62808 | ||
|
|
6fe9b78509 | ||
|
|
f5754159cb | ||
|
|
53d0256585 | ||
|
|
09aa28d699 | ||
|
|
d086e289dd | ||
|
|
2cba04dfe7 | ||
|
|
3de87a7001 | ||
|
|
4900c82871 | ||
|
|
8a8e3ed753 | ||
|
|
cd2f4f6462 | ||
|
|
aa13e31894 | ||
|
|
6c211b9179 | ||
|
|
49f9b3941d | ||
|
|
332ab1dc4d | ||
|
|
ec98e32a3b | ||
|
|
7ec2b75c70 | ||
|
|
9788085786 | ||
|
|
fc80c5e3dd | ||
|
|
95f0a34bcc | ||
|
|
3e042df438 | ||
|
|
5346a441ae | ||
|
|
76173069a3 | ||
|
|
6cdd340933 | ||
|
|
ef004cee48 | ||
|
|
6d7d92c605 | ||
|
|
53437dd7b4 | ||
|
|
0c06d32ee4 | ||
|
|
4d8c64bf1e | ||
|
|
7da8f01f75 | ||
|
|
67105fe4ec | ||
|
|
1b38ff9410 | ||
|
|
c9703dcb8a | ||
|
|
e2caf86f6a | ||
|
|
923b37d647 | ||
|
|
9f4799912f | ||
|
|
4b218be4ac | ||
|
|
9a0041286e | ||
|
|
0a4b51055e | ||
|
|
96d6d52830 | ||
|
|
62a1a500a5 | ||
|
|
3953bd31e1 | ||
|
|
83004c8344 | ||
|
|
8ab9d30eff | ||
|
|
89aaab12dc | ||
|
|
481ef3781a |
No files matched your search
+49
-1
@@ -1,4 +1,52 @@
|
|||||||
# Version 0.6.0-alpha
|
# Version 0.7.0
|
||||||
|
|
||||||
|
New features:
|
||||||
|
* Support for encoding BigTiff ([#122](https://github.com/image-rs/image-tiff/pull/122))
|
||||||
|
* _Breaking:_ Encoder types now have a generic parameter to differentiate BigTiff and standard Tiff encoding. Defaults to standard Tiff.
|
||||||
|
* Basic tile decoding ([#125](https://github.com/image-rs/image-tiff/pull/125))
|
||||||
|
* _Breaking:_ There is a new `TiffError::UsageError` variant.
|
||||||
|
* Support for datatypes `Int8` and `Int16` ([#114](https://github.com/image-rs/image-tiff/pull/114))
|
||||||
|
* _Breaking:_ `DecodingResult` and `DecodingBuffer` have the two new variants `I8` and `I16`.
|
||||||
|
* Support for `i32` arrays ([#118](https://github.com/image-rs/image-tiff/pull/118/files))
|
||||||
|
* _Breaking:_ `DecodingResult` and `DecodingBuffer` have a new `I32` variant.
|
||||||
|
* Support for `Ifd` and `IfdBig` tag types and `I64` data type ([#119](https://github.com/image-rs/image-tiff/pull/119))
|
||||||
|
* _Breaking:_ `DecodingResult` and `DecodingBuffer` have a new `I64` variant.
|
||||||
|
* Add `SMinSampleValue` and `SMaxSampleValue` ([#123](https://github.com/image-rs/image-tiff/pull/123))
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
* Improve deflate support ([#132](https://github.com/image-rs/image-tiff/pull/132))
|
||||||
|
* Switch to streaming decompression via `flate2`. Aside from performance improvements and lower RAM consumption, this fixes a bug where `max_uncompressed_length` was precalculated for a single tile but then used as a hard limit on the whole data, failing to decompress any tiled images.
|
||||||
|
* Add support for new `Deflate` tag in addition to `OldDeflate`.
|
||||||
|
* _Breaking:_ Remove `InflateError`, which is no longer needed with `flate2` ([#134](https://github.com/image-rs/image-tiff/pull/134))
|
||||||
|
* _Breaking:_ Support for `MinIsWhite` is restricted to unsigned and floating
|
||||||
|
point values. This is expected to be be re-added once some contradictory
|
||||||
|
interpretation regarding semantics for signed values is resolved.
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
* Validate that ASCII tags are valid ASCII and end with a null byte ([#121](https://github.com/image-rs/image-tiff/pull/121))
|
||||||
|
|
||||||
|
Internal:
|
||||||
|
* Simplify decompression logic ([#126](https://github.com/image-rs/image-tiff/pull/126))
|
||||||
|
* Simplify `expand_strip` ([#128](https://github.com/image-rs/image-tiff/pull/128))
|
||||||
|
|
||||||
|
# Version 0.6.1
|
||||||
|
|
||||||
|
New features:
|
||||||
|
* Support for reading `u16` and ascii string tags.
|
||||||
|
* Added `Limits::unlimited` for disabling all limits.
|
||||||
|
* Added `ImageEncoder::rows_per_strip` to overwrite the default.
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
* The default strip size for chunked encoding is now 1MB, up from 8KB. This
|
||||||
|
should lead to more efficient decoding and compression.
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
* Fixed a bug where LZW compressed strips could not be decoded, instead
|
||||||
|
returning an error `Inconsistent sizes encountered`.
|
||||||
|
* Reading a tag with a complex type and a single value returns the proper Value
|
||||||
|
variant, instead of a vector with one entry.
|
||||||
|
|
||||||
|
# Version 0.6.0
|
||||||
|
|
||||||
New features:
|
New features:
|
||||||
* Support for decoding BigTIFF with 64-bit offsets
|
* Support for decoding BigTIFF with 64-bit offsets
|
||||||
|
|||||||
+3
-5
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tiff"
|
name = "tiff"
|
||||||
version = "0.6.0"
|
version = "0.7.0"
|
||||||
authors = [
|
authors = [
|
||||||
"ccgn",
|
"ccgn",
|
||||||
"bvssvni <bvssvni@gmail.com>",
|
"bvssvni <bvssvni@gmail.com>",
|
||||||
@@ -15,14 +15,12 @@ license = "MIT"
|
|||||||
description = "TIFF decoding and encoding library in pure Rust"
|
description = "TIFF decoding and encoding library in pure Rust"
|
||||||
repository = "https://github.com/image-rs/image-tiff"
|
repository = "https://github.com/image-rs/image-tiff"
|
||||||
exclude = ["tests/images/*", "tests/fuzz_images/*"]
|
exclude = ["tests/images/*", "tests/fuzz_images/*"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
weezl = "0.1.0"
|
weezl = "0.1.0"
|
||||||
jpeg = { package = "jpeg-decoder", version = "0.1.17", default-features = false }
|
jpeg = { package = "jpeg-decoder", version = "0.1.17", default-features = false }
|
||||||
|
flate2 = "1.0.20"
|
||||||
[dependencies.miniz_oxide]
|
|
||||||
version = "0.4.1"
|
|
||||||
features = ["no_extern_crate_alloc"]
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3.1"
|
criterion = "0.3.1"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# image-tiff
|
# image-tiff
|
||||||
[](https://travis-ci.org/image-rs/image-tiff)
|
[](https://github.com/image-rs/image-tiff/actions)
|
||||||
[](https://docs.rs/tiff)
|
[](https://docs.rs/tiff)
|
||||||
[](https://crates.io/crates/tiff)
|
[](https://crates.io/crates/tiff)
|
||||||
|
|
||||||
|
|||||||
+4
-3
@@ -23,11 +23,12 @@ macro_rules! integral_slice_as_bytes{($int:ty, $const:ident $(,$mut:ident)*) =>
|
|||||||
})*
|
})*
|
||||||
}}
|
}}
|
||||||
|
|
||||||
integral_slice_as_bytes!(i8, i8_as_ne_bytes);
|
integral_slice_as_bytes!(i8, i8_as_ne_bytes, i8_as_ne_mut_bytes);
|
||||||
integral_slice_as_bytes!(u16, u16_as_ne_bytes, u16_as_ne_mut_bytes);
|
integral_slice_as_bytes!(u16, u16_as_ne_bytes, u16_as_ne_mut_bytes);
|
||||||
integral_slice_as_bytes!(i16, i16_as_ne_bytes);
|
integral_slice_as_bytes!(i16, i16_as_ne_bytes, i16_as_ne_mut_bytes);
|
||||||
integral_slice_as_bytes!(u32, u32_as_ne_bytes, u32_as_ne_mut_bytes);
|
integral_slice_as_bytes!(u32, u32_as_ne_bytes, u32_as_ne_mut_bytes);
|
||||||
integral_slice_as_bytes!(i32, i32_as_ne_bytes);
|
integral_slice_as_bytes!(i32, i32_as_ne_bytes, i32_as_ne_mut_bytes);
|
||||||
integral_slice_as_bytes!(u64, u64_as_ne_bytes, u64_as_ne_mut_bytes);
|
integral_slice_as_bytes!(u64, u64_as_ne_bytes, u64_as_ne_mut_bytes);
|
||||||
|
integral_slice_as_bytes!(i64, i64_as_ne_bytes, i64_as_ne_mut_bytes);
|
||||||
integral_slice_as_bytes!(f32, f32_as_ne_bytes, f32_as_ne_mut_bytes);
|
integral_slice_as_bytes!(f32, f32_as_ne_bytes, f32_as_ne_mut_bytes);
|
||||||
integral_slice_as_bytes!(f64, f64_as_ne_bytes, f64_as_ne_mut_bytes);
|
integral_slice_as_bytes!(f64, f64_as_ne_bytes, f64_as_ne_mut_bytes);
|
||||||
+259
-129
@@ -4,20 +4,22 @@ use std::collections::HashMap;
|
|||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
use std::io::{self, Read, Seek};
|
use std::io::{self, Read, Seek};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
use super::stream::{ByteOrder, EndianReader, SmartReader};
|
use super::stream::{ByteOrder, EndianReader, SmartReader};
|
||||||
use tags::{Tag, Type};
|
use crate::tags::{Tag, Type};
|
||||||
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
use crate::{TiffError, TiffFormatError, TiffResult};
|
||||||
|
|
||||||
use self::Value::{
|
use self::Value::{
|
||||||
Ascii, Byte, Double, Float, List, Rational, RationalBig, SRational, SRationalBig, Signed,
|
Ascii, Byte, Double, Float, Ifd, IfdBig, List, Rational, RationalBig, SRational, SRationalBig,
|
||||||
SignedBig, Unsigned, UnsignedBig,
|
Short, Signed, SignedBig, Unsigned, UnsignedBig,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(unused_qualifications)]
|
#[allow(unused_qualifications)]
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Byte(u8),
|
Byte(u8),
|
||||||
|
Short(u16),
|
||||||
Signed(i32),
|
Signed(i32),
|
||||||
SignedBig(i64),
|
SignedBig(i64),
|
||||||
Unsigned(u32),
|
Unsigned(u32),
|
||||||
@@ -30,6 +32,8 @@ pub enum Value {
|
|||||||
SRational(i32, i32),
|
SRational(i32, i32),
|
||||||
SRationalBig(i64, i64),
|
SRationalBig(i64, i64),
|
||||||
Ascii(String),
|
Ascii(String),
|
||||||
|
Ifd(u32),
|
||||||
|
IfdBig(u64),
|
||||||
#[doc(hidden)] // Do not match against this.
|
#[doc(hidden)] // Do not match against this.
|
||||||
__NonExhaustive,
|
__NonExhaustive,
|
||||||
}
|
}
|
||||||
@@ -42,10 +46,24 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_u16(self) -> TiffResult<u16> {
|
||||||
|
match self {
|
||||||
|
Short(val) => Ok(val),
|
||||||
|
Unsigned(val) => Ok(u16::try_from(val)?),
|
||||||
|
UnsignedBig(val) => Ok(u16::try_from(val)?),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::UnsignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_u32(self) -> TiffResult<u32> {
|
pub fn into_u32(self) -> TiffResult<u32> {
|
||||||
match self {
|
match self {
|
||||||
|
Short(val) => Ok(val.into()),
|
||||||
Unsigned(val) => Ok(val),
|
Unsigned(val) => Ok(val),
|
||||||
UnsignedBig(val) => Ok(u32::try_from(val)?),
|
UnsignedBig(val) => Ok(u32::try_from(val)?),
|
||||||
|
Ifd(val) => Ok(val),
|
||||||
|
IfdBig(val) => Ok(u32::try_from(val)?),
|
||||||
val => Err(TiffError::FormatError(
|
val => Err(TiffError::FormatError(
|
||||||
TiffFormatError::UnsignedIntegerExpected(val),
|
TiffFormatError::UnsignedIntegerExpected(val),
|
||||||
)),
|
)),
|
||||||
@@ -64,8 +82,11 @@ impl Value {
|
|||||||
|
|
||||||
pub fn into_u64(self) -> TiffResult<u64> {
|
pub fn into_u64(self) -> TiffResult<u64> {
|
||||||
match self {
|
match self {
|
||||||
|
Short(val) => Ok(val.into()),
|
||||||
Unsigned(val) => Ok(val.into()),
|
Unsigned(val) => Ok(val.into()),
|
||||||
UnsignedBig(val) => Ok(val),
|
UnsignedBig(val) => Ok(val),
|
||||||
|
Ifd(val) => Ok(val.into()),
|
||||||
|
IfdBig(val) => Ok(val),
|
||||||
val => Err(TiffError::FormatError(
|
val => Err(TiffError::FormatError(
|
||||||
TiffFormatError::UnsignedIntegerExpected(val),
|
TiffFormatError::UnsignedIntegerExpected(val),
|
||||||
)),
|
)),
|
||||||
@@ -100,6 +121,15 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_string(self) -> TiffResult<String> {
|
||||||
|
match self {
|
||||||
|
Ascii(val) => Ok(val),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::SignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_u32_vec(self) -> TiffResult<Vec<u32>> {
|
pub fn into_u32_vec(self) -> TiffResult<Vec<u32>> {
|
||||||
match self {
|
match self {
|
||||||
List(vec) => {
|
List(vec) => {
|
||||||
@@ -115,6 +145,8 @@ impl Value {
|
|||||||
RationalBig(numerator, denominator) => {
|
RationalBig(numerator, denominator) => {
|
||||||
Ok(vec![u32::try_from(numerator)?, u32::try_from(denominator)?])
|
Ok(vec![u32::try_from(numerator)?, u32::try_from(denominator)?])
|
||||||
}
|
}
|
||||||
|
Ifd(val) => Ok(vec![val]),
|
||||||
|
IfdBig(val) => Ok(vec![u32::try_from(val)?]),
|
||||||
Ascii(val) => Ok(val.chars().map(u32::from).collect()),
|
Ascii(val) => Ok(val.chars().map(u32::from).collect()),
|
||||||
val => Err(TiffError::FormatError(
|
val => Err(TiffError::FormatError(
|
||||||
TiffFormatError::UnsignedIntegerExpected(val),
|
TiffFormatError::UnsignedIntegerExpected(val),
|
||||||
@@ -139,6 +171,22 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_u16_vec(self) -> TiffResult<Vec<u16>> {
|
||||||
|
match self {
|
||||||
|
List(vec) => {
|
||||||
|
let mut new_vec = Vec::with_capacity(vec.len());
|
||||||
|
for v in vec {
|
||||||
|
new_vec.push(v.into_u16()?)
|
||||||
|
}
|
||||||
|
Ok(new_vec)
|
||||||
|
}
|
||||||
|
Short(val) => Ok(vec![val]),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::UnsignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_i32_vec(self) -> TiffResult<Vec<i32>> {
|
pub fn into_i32_vec(self) -> TiffResult<Vec<i32>> {
|
||||||
match self {
|
match self {
|
||||||
List(vec) => {
|
List(vec) => {
|
||||||
@@ -215,6 +263,8 @@ impl Value {
|
|||||||
UnsignedBig(val) => Ok(vec![val]),
|
UnsignedBig(val) => Ok(vec![val]),
|
||||||
Rational(numerator, denominator) => Ok(vec![numerator.into(), denominator.into()]),
|
Rational(numerator, denominator) => Ok(vec![numerator.into(), denominator.into()]),
|
||||||
RationalBig(numerator, denominator) => Ok(vec![numerator, denominator]),
|
RationalBig(numerator, denominator) => Ok(vec![numerator, denominator]),
|
||||||
|
Ifd(val) => Ok(vec![val.into()]),
|
||||||
|
IfdBig(val) => Ok(vec![val]),
|
||||||
Ascii(val) => Ok(val.chars().map(u32::from).map(u64::from).collect()),
|
Ascii(val) => Ok(val.chars().map(u32::from).map(u64::from).collect()),
|
||||||
val => Err(TiffError::FormatError(
|
val => Err(TiffError::FormatError(
|
||||||
TiffFormatError::UnsignedIntegerExpected(val),
|
TiffFormatError::UnsignedIntegerExpected(val),
|
||||||
@@ -293,155 +343,242 @@ impl Entry {
|
|||||||
limits: &super::Limits,
|
limits: &super::Limits,
|
||||||
decoder: &mut super::Decoder<R>,
|
decoder: &mut super::Decoder<R>,
|
||||||
) -> TiffResult<Value> {
|
) -> TiffResult<Value> {
|
||||||
|
// Case 1: there are no values so we can return immediately.
|
||||||
|
if self.count == 0 {
|
||||||
|
return Ok(List(Vec::new()));
|
||||||
|
}
|
||||||
|
|
||||||
let bo = decoder.byte_order();
|
let bo = decoder.byte_order();
|
||||||
let bigtiff_value = if decoder.bigtiff {
|
let value_bytes = self.count
|
||||||
match (self.type_, self.count) {
|
* match self.type_ {
|
||||||
(Type::BYTE, 5) => Some(offset_to_bytes(5, self)?),
|
Type::BYTE | Type::SBYTE | Type::ASCII | Type::UNDEFINED => 1,
|
||||||
(Type::BYTE, 6) => Some(offset_to_bytes(6, self)?),
|
Type::SHORT | Type::SSHORT => 2,
|
||||||
(Type::BYTE, 7) => Some(offset_to_bytes(7, self)?),
|
Type::LONG | Type::SLONG | Type::FLOAT | Type::IFD => 4,
|
||||||
(Type::BYTE, 8) => Some(offset_to_bytes(8, self)?),
|
Type::LONG8
|
||||||
(Type::SBYTE, 5) => Some(offset_to_sbytes(5, self)?),
|
| Type::SLONG8
|
||||||
(Type::SBYTE, 6) => Some(offset_to_sbytes(6, self)?),
|
| Type::DOUBLE
|
||||||
(Type::SBYTE, 7) => Some(offset_to_sbytes(7, self)?),
|
| Type::RATIONAL
|
||||||
(Type::SBYTE, 8) => Some(offset_to_sbytes(8, self)?),
|
| Type::SRATIONAL
|
||||||
(Type::SHORT, 3) => {
|
| Type::IFD8 => 8,
|
||||||
let mut r = self.r(bo);
|
Type::__NonExhaustive => unreachable!(),
|
||||||
Some(List(vec![
|
|
||||||
Unsigned(u32::from(r.read_u16()?)),
|
|
||||||
Unsigned(u32::from(r.read_u16()?)),
|
|
||||||
Unsigned(u32::from(r.read_u16()?)),
|
|
||||||
]))
|
|
||||||
}
|
|
||||||
(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 {
|
// Case 2: there is one value.
|
||||||
Ok(v)
|
if self.count == 1 {
|
||||||
|
// 2a: the value is 5-8 bytes and we're in BigTiff mode.
|
||||||
|
if decoder.bigtiff && value_bytes > 4 && value_bytes <= 8 {
|
||||||
|
return Ok(match self.type_ {
|
||||||
|
Type::LONG8 => UnsignedBig(self.r(bo).read_u64()?),
|
||||||
|
Type::SLONG8 => SignedBig(self.r(bo).read_i64()?),
|
||||||
|
Type::DOUBLE => Double(self.r(bo).read_f64()?),
|
||||||
|
Type::RATIONAL => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
Rational(r.read_u32()?, r.read_u32()?)
|
||||||
|
}
|
||||||
|
Type::SRATIONAL => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
SRational(r.read_i32()?, r.read_i32()?)
|
||||||
|
}
|
||||||
|
Type::IFD8 => IfdBig(self.r(bo).read_u64()?),
|
||||||
|
Type::BYTE
|
||||||
|
| Type::SBYTE
|
||||||
|
| Type::ASCII
|
||||||
|
| Type::UNDEFINED
|
||||||
|
| Type::SHORT
|
||||||
|
| Type::SSHORT
|
||||||
|
| Type::LONG
|
||||||
|
| Type::SLONG
|
||||||
|
| Type::FLOAT
|
||||||
|
| Type::IFD => unreachable!(),
|
||||||
|
Type::__NonExhaustive => unreachable!(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2b: the value is at most 4 bytes or doesn't fit in the offset field.
|
||||||
|
return Ok(match self.type_ {
|
||||||
|
Type::BYTE => Unsigned(u32::from(self.offset[0])),
|
||||||
|
Type::SBYTE => Signed(i32::from(self.offset[0] as i8)),
|
||||||
|
Type::UNDEFINED => Byte(self.offset[0]),
|
||||||
|
Type::SHORT => Unsigned(u32::from(self.r(bo).read_u16()?)),
|
||||||
|
Type::SSHORT => Signed(i32::from(self.r(bo).read_i16()?)),
|
||||||
|
Type::LONG => Unsigned(self.r(bo).read_u32()?),
|
||||||
|
Type::SLONG => Signed(self.r(bo).read_i32()?),
|
||||||
|
Type::FLOAT => Float(self.r(bo).read_f32()?),
|
||||||
|
Type::ASCII => {
|
||||||
|
if self.offset[0] == 0 {
|
||||||
|
Ascii("".to_string())
|
||||||
} else {
|
} else {
|
||||||
match (self.type_, self.count) {
|
return Err(TiffError::FormatError(TiffFormatError::InvalidTag));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::LONG8 => {
|
||||||
|
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
||||||
|
UnsignedBig(decoder.read_long8()?)
|
||||||
|
}
|
||||||
|
Type::SLONG8 => {
|
||||||
|
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
||||||
|
SignedBig(decoder.read_slong8()?)
|
||||||
|
}
|
||||||
|
Type::DOUBLE => {
|
||||||
|
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
||||||
|
Double(decoder.read_double()?)
|
||||||
|
}
|
||||||
|
Type::RATIONAL => {
|
||||||
|
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
||||||
|
Rational(decoder.read_long()?, decoder.read_long()?)
|
||||||
|
}
|
||||||
|
Type::SRATIONAL => {
|
||||||
|
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
||||||
|
SRational(decoder.read_slong()?, decoder.read_slong()?)
|
||||||
|
}
|
||||||
|
Type::IFD => Ifd(self.r(bo).read_u32()?),
|
||||||
|
Type::IFD8 => {
|
||||||
|
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
||||||
|
IfdBig(decoder.read_long8()?)
|
||||||
|
}
|
||||||
|
Type::__NonExhaustive => unreachable!(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 3: There is more than one value, but it fits in the offset field.
|
||||||
|
if value_bytes <= 4 || decoder.bigtiff && value_bytes <= 8 {
|
||||||
|
match self.type_ {
|
||||||
|
Type::BYTE => return Ok(offset_to_bytes(self.count as usize, self)?),
|
||||||
|
Type::SBYTE => return Ok(offset_to_sbytes(self.count as usize, self)?),
|
||||||
|
Type::ASCII => {
|
||||||
|
let mut buf = vec![0; self.count as usize];
|
||||||
|
self.r(bo).read_exact(&mut buf)?;
|
||||||
|
if buf.is_ascii() && buf.ends_with(&[0]) {
|
||||||
|
let v = str::from_utf8(&buf)?;
|
||||||
|
let v = v.trim_matches(char::from(0));
|
||||||
|
return Ok(Ascii(v.into()));
|
||||||
|
} else {
|
||||||
|
return Err(TiffError::FormatError(TiffFormatError::InvalidTag));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::UNDEFINED => {
|
||||||
|
return Ok(List(
|
||||||
|
self.offset[0..self.count as usize]
|
||||||
|
.iter()
|
||||||
|
.map(|&b| Byte(b))
|
||||||
|
.collect(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Type::SHORT => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
let mut v = Vec::new();
|
||||||
|
for _ in 0..self.count {
|
||||||
|
v.push(Short(r.read_u16()?));
|
||||||
|
}
|
||||||
|
return Ok(List(v));
|
||||||
|
}
|
||||||
|
Type::SSHORT => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
let mut v = Vec::new();
|
||||||
|
for _ in 0..self.count {
|
||||||
|
v.push(Signed(i32::from(r.read_i16()?)));
|
||||||
|
}
|
||||||
|
return Ok(List(v));
|
||||||
|
}
|
||||||
|
Type::LONG => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
let mut v = Vec::new();
|
||||||
|
for _ in 0..self.count {
|
||||||
|
v.push(Unsigned(r.read_u32()?));
|
||||||
|
}
|
||||||
|
return Ok(List(v));
|
||||||
|
}
|
||||||
|
Type::SLONG => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
let mut v = Vec::new();
|
||||||
|
for _ in 0..self.count {
|
||||||
|
v.push(Signed(r.read_i32()?));
|
||||||
|
}
|
||||||
|
return Ok(List(v));
|
||||||
|
}
|
||||||
|
Type::FLOAT => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
let mut v = Vec::new();
|
||||||
|
for _ in 0..self.count {
|
||||||
|
v.push(Float(r.read_f32()?));
|
||||||
|
}
|
||||||
|
return Ok(List(v));
|
||||||
|
}
|
||||||
|
Type::IFD => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
let mut v = Vec::new();
|
||||||
|
for _ in 0..self.count {
|
||||||
|
v.push(Ifd(r.read_u32()?));
|
||||||
|
}
|
||||||
|
return Ok(List(v));
|
||||||
|
}
|
||||||
|
Type::LONG8
|
||||||
|
| Type::SLONG8
|
||||||
|
| Type::RATIONAL
|
||||||
|
| Type::SRATIONAL
|
||||||
|
| Type::DOUBLE
|
||||||
|
| Type::IFD8 => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
Type::__NonExhaustive => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 4: there is more than one value, and it doesn't fit in the offset field.
|
||||||
|
match self.type_ {
|
||||||
// TODO check if this could give wrong results
|
// TODO check if this could give wrong results
|
||||||
// at a different endianess of file/computer.
|
// at a different endianess of file/computer.
|
||||||
(Type::BYTE, 1) => Ok(Unsigned(u32::from(self.offset[0]))),
|
Type::BYTE => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
||||||
(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()?)))
|
Ok(UnsignedBig(u64::from(decoder.read_byte()?)))
|
||||||
}),
|
}),
|
||||||
(Type::SBYTE, 1) => Ok(Signed(i32::from(self.offset[0] as i8))),
|
Type::SBYTE => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
||||||
(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)))
|
Ok(SignedBig(i64::from(decoder.read_byte()? as i8)))
|
||||||
}),
|
}),
|
||||||
(Type::SHORT, 1) => Ok(Unsigned(u32::from(self.r(bo).read_u16()?))),
|
Type::SHORT => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
||||||
(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()?)))
|
Ok(UnsignedBig(u64::from(decoder.read_short()?)))
|
||||||
}),
|
}),
|
||||||
(Type::SSHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
Type::SSHORT => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
||||||
Ok(SignedBig(i64::from(decoder.read_sshort()?)))
|
Ok(SignedBig(i64::from(decoder.read_sshort()?)))
|
||||||
}),
|
}),
|
||||||
(Type::LONG, 1) => Ok(Unsigned(self.r(bo).read_u32()?)),
|
Type::LONG => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
||||||
(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()?))
|
Ok(Unsigned(decoder.read_long()?))
|
||||||
}),
|
}),
|
||||||
(Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
Type::SLONG => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
||||||
Ok(Signed(decoder.read_slong()?))
|
Ok(Signed(decoder.read_slong()?))
|
||||||
}),
|
}),
|
||||||
(Type::FLOAT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
Type::FLOAT => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
||||||
Ok(Float(decoder.read_float()?))
|
Ok(Float(decoder.read_float()?))
|
||||||
}),
|
}),
|
||||||
(Type::DOUBLE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
Type::DOUBLE => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
||||||
Ok(Double(decoder.read_double()?))
|
Ok(Double(decoder.read_double()?))
|
||||||
}),
|
}),
|
||||||
(Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
Type::RATIONAL => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
||||||
Ok(Rational(decoder.read_long()?, decoder.read_long()?))
|
Ok(Rational(decoder.read_long()?, decoder.read_long()?))
|
||||||
}),
|
}),
|
||||||
(Type::SRATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
Type::SRATIONAL => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
||||||
Ok(SRational(decoder.read_slong()?, decoder.read_slong()?))
|
Ok(SRational(decoder.read_slong()?, decoder.read_slong()?))
|
||||||
}),
|
}),
|
||||||
(Type::LONG8, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
Type::LONG8 => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
||||||
Ok(UnsignedBig(decoder.read_long8()?))
|
Ok(UnsignedBig(decoder.read_long8()?))
|
||||||
}),
|
}),
|
||||||
(Type::ASCII, n) => {
|
Type::SLONG8 => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
||||||
let n = usize::try_from(n)?;
|
Ok(SignedBig(decoder.read_slong8()?))
|
||||||
|
}),
|
||||||
|
Type::IFD => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(Ifd(decoder.read_long()?))
|
||||||
|
}),
|
||||||
|
Type::IFD8 => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(IfdBig(decoder.read_long8()?))
|
||||||
|
}),
|
||||||
|
Type::UNDEFINED => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(Byte(u8::from(decoder.read_byte()?)))
|
||||||
|
}),
|
||||||
|
Type::ASCII => {
|
||||||
|
let n = usize::try_from(self.count)?;
|
||||||
if n > limits.decoding_buffer_size {
|
if n > limits.decoding_buffer_size {
|
||||||
return Err(TiffError::LimitsExceeded);
|
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 {
|
if decoder.bigtiff {
|
||||||
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
|
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
|
||||||
} else {
|
} else {
|
||||||
@@ -450,14 +587,7 @@ impl Entry {
|
|||||||
let string = decoder.read_string(n)?;
|
let string = decoder.read_string(n)?;
|
||||||
Ok(Ascii(string))
|
Ok(Ascii(string))
|
||||||
}
|
}
|
||||||
}
|
Type::__NonExhaustive => unreachable!(),
|
||||||
(Type::UNDEFINED, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
|
||||||
Ok(Byte(u8::from(decoder.read_byte()?)))
|
|
||||||
}),
|
|
||||||
_ => Err(TiffError::UnsupportedError(
|
|
||||||
TiffUnsupportedError::UnsupportedDataType,
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+713
-168
File diff suppressed because it is too large.
Load diff
+53
-212
@@ -1,8 +1,5 @@
|
|||||||
//! All IO functionality needed for TIFF decoding
|
//! All IO functionality needed for TIFF decoding
|
||||||
|
|
||||||
use crate::bytecast;
|
|
||||||
use crate::error::{TiffError, TiffResult};
|
|
||||||
use miniz_oxide::inflate;
|
|
||||||
use std::io::{self, Read, Seek};
|
use std::io::{self, Read, Seek};
|
||||||
|
|
||||||
/// Byte order of the TIFF file.
|
/// Byte order of the TIFF file.
|
||||||
@@ -30,22 +27,15 @@ pub trait EndianReader: Read {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads an i8
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn read_u16_into(&mut self, buffer: &mut [u16]) -> Result<(), io::Error> {
|
fn read_i8(&mut self) -> Result<i8, io::Error> {
|
||||||
self.read_exact(bytecast::u16_as_ne_mut_bytes(buffer))?;
|
let mut n = [0u8; 1];
|
||||||
match self.byte_order() {
|
self.read_exact(&mut n)?;
|
||||||
ByteOrder::LittleEndian => {
|
Ok(match self.byte_order() {
|
||||||
for n in buffer {
|
ByteOrder::LittleEndian => i8::from_le_bytes(n),
|
||||||
*n = u16::from_le(*n);
|
ByteOrder::BigEndian => i8::from_be_bytes(n),
|
||||||
}
|
})
|
||||||
}
|
|
||||||
ByteOrder::BigEndian => {
|
|
||||||
for n in buffer {
|
|
||||||
*n = u16::from_be(*n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads an i16
|
/// Reads an i16
|
||||||
@@ -70,24 +60,6 @@ pub trait EndianReader: Read {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn read_u32_into(&mut self, buffer: &mut [u32]) -> Result<(), io::Error> {
|
|
||||||
self.read_exact(bytecast::u32_as_ne_mut_bytes(buffer))?;
|
|
||||||
match self.byte_order() {
|
|
||||||
ByteOrder::LittleEndian => {
|
|
||||||
for n in buffer {
|
|
||||||
*n = u32::from_le(*n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ByteOrder::BigEndian => {
|
|
||||||
for n in buffer {
|
|
||||||
*n = u32::from_be(*n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads an i32
|
/// Reads an i32
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn read_i32(&mut self) -> Result<i32, io::Error> {
|
fn read_i32(&mut self) -> Result<i32, io::Error> {
|
||||||
@@ -110,22 +82,15 @@ pub trait EndianReader: Read {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads an i64
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn read_u64_into(&mut self, buffer: &mut [u64]) -> Result<(), io::Error> {
|
fn read_i64(&mut self) -> Result<i64, io::Error> {
|
||||||
self.read_exact(bytecast::u64_as_ne_mut_bytes(buffer))?;
|
let mut n = [0u8; 8];
|
||||||
match self.byte_order() {
|
self.read_exact(&mut n)?;
|
||||||
ByteOrder::LittleEndian => {
|
Ok(match self.byte_order() {
|
||||||
for n in buffer {
|
ByteOrder::LittleEndian => i64::from_le_bytes(n),
|
||||||
*n = u64::from_le(*n);
|
ByteOrder::BigEndian => i64::from_be_bytes(n),
|
||||||
}
|
})
|
||||||
}
|
|
||||||
ByteOrder::BigEndian => {
|
|
||||||
for n in buffer {
|
|
||||||
*n = u64::from_be(*n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads an f32
|
/// Reads an f32
|
||||||
@@ -139,24 +104,6 @@ pub trait EndianReader: Read {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn read_f32_into(&mut self, buffer: &mut [f32]) -> Result<(), io::Error> {
|
|
||||||
self.read_exact(bytecast::f32_as_ne_mut_bytes(buffer))?;
|
|
||||||
match self.byte_order() {
|
|
||||||
ByteOrder::LittleEndian => {
|
|
||||||
for n in buffer {
|
|
||||||
*n = f32::from_bits(u32::from_le(n.to_bits()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ByteOrder::BigEndian => {
|
|
||||||
for n in buffer {
|
|
||||||
*n = f32::from_bits(u32::from_be(n.to_bits()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads an f64
|
/// Reads an f64
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn read_f64(&mut self) -> Result<f64, io::Error> {
|
fn read_f64(&mut self) -> Result<f64, io::Error> {
|
||||||
@@ -167,24 +114,6 @@ pub trait EndianReader: Read {
|
|||||||
ByteOrder::BigEndian => u64::from_be_bytes(n),
|
ByteOrder::BigEndian => u64::from_be_bytes(n),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn read_f64_into(&mut self, buffer: &mut [f64]) -> Result<(), io::Error> {
|
|
||||||
self.read_exact(bytecast::f64_as_ne_mut_bytes(buffer))?;
|
|
||||||
match self.byte_order() {
|
|
||||||
ByteOrder::LittleEndian => {
|
|
||||||
for n in buffer {
|
|
||||||
*n = f64::from_bits(u64::from_le(n.to_bits()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ByteOrder::BigEndian => {
|
|
||||||
for n in buffer {
|
|
||||||
*n = f64::from_bits(u64::from_be(n.to_bits()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -195,51 +124,7 @@ pub trait EndianReader: Read {
|
|||||||
/// ## Deflate Reader
|
/// ## Deflate Reader
|
||||||
///
|
///
|
||||||
|
|
||||||
/// Reader that decompresses DEFLATE streams
|
pub type DeflateReader<'r, R> = flate2::read::ZlibDecoder<&'r mut SmartReader<R>>;
|
||||||
pub struct DeflateReader {
|
|
||||||
buffer: io::Cursor<Vec<u8>>,
|
|
||||||
byte_order: ByteOrder,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DeflateReader {
|
|
||||||
pub fn new<R: Read + Seek>(
|
|
||||||
reader: &mut SmartReader<R>,
|
|
||||||
max_uncompressed_length: usize,
|
|
||||||
) -> TiffResult<(usize, Self)> {
|
|
||||||
let byte_order = reader.byte_order;
|
|
||||||
let mut compressed = Vec::new();
|
|
||||||
reader.read_to_end(&mut compressed)?;
|
|
||||||
|
|
||||||
// TODO: Implement streaming compression, and remove this (temporary) and somewhat
|
|
||||||
// misleading workaround.
|
|
||||||
if compressed.len() > max_uncompressed_length {
|
|
||||||
return Err(TiffError::LimitsExceeded);
|
|
||||||
}
|
|
||||||
|
|
||||||
let uncompressed =
|
|
||||||
inflate::decompress_to_vec_zlib(&compressed).map_err(TiffError::from_inflate_status)?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
uncompressed.len(),
|
|
||||||
Self {
|
|
||||||
byte_order,
|
|
||||||
buffer: io::Cursor::new(uncompressed),
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Read for DeflateReader {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
self.buffer.read(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EndianReader for DeflateReader {
|
|
||||||
fn byte_order(&self) -> ByteOrder {
|
|
||||||
self.byte_order
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// ## LZW Reader
|
/// ## LZW Reader
|
||||||
@@ -248,7 +133,6 @@ impl EndianReader for DeflateReader {
|
|||||||
/// Reader that decompresses LZW streams
|
/// Reader that decompresses LZW streams
|
||||||
pub struct LZWReader {
|
pub struct LZWReader {
|
||||||
buffer: io::Cursor<Vec<u8>>,
|
buffer: io::Cursor<Vec<u8>>,
|
||||||
byte_order: ByteOrder,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LZWReader {
|
impl LZWReader {
|
||||||
@@ -261,24 +145,21 @@ impl LZWReader {
|
|||||||
where
|
where
|
||||||
R: Read + Seek,
|
R: Read + Seek,
|
||||||
{
|
{
|
||||||
let order = reader.byte_order;
|
|
||||||
let mut compressed = vec![0; compressed_length as usize];
|
let mut compressed = vec![0; compressed_length as usize];
|
||||||
reader.read_exact(&mut compressed[..])?;
|
reader.read_exact(&mut compressed[..])?;
|
||||||
let mut uncompressed = Vec::with_capacity(max_uncompressed_length);
|
let mut uncompressed = vec![0; max_uncompressed_length];
|
||||||
let mut decoder = weezl::decode::Decoder::with_tiff_size_switch(weezl::BitOrder::Msb, 8);
|
let mut decoder = weezl::decode::Decoder::with_tiff_size_switch(weezl::BitOrder::Msb, 8);
|
||||||
let mut bytes_read = 0;
|
|
||||||
while bytes_read < compressed_length && uncompressed.len() < max_uncompressed_length {
|
|
||||||
let bytes_written = uncompressed.len();
|
|
||||||
uncompressed.reserve(1 << 12);
|
|
||||||
let buffer_space = uncompressed.capacity();
|
|
||||||
uncompressed.resize(buffer_space, 0u8);
|
|
||||||
|
|
||||||
|
let mut bytes_read = 0;
|
||||||
|
let mut bytes_written = 0;
|
||||||
|
|
||||||
|
while bytes_written < max_uncompressed_length {
|
||||||
let result = decoder.decode_bytes(
|
let result = decoder.decode_bytes(
|
||||||
&compressed[bytes_read..],
|
&compressed[bytes_read..],
|
||||||
&mut uncompressed[bytes_written..],
|
&mut uncompressed[bytes_written..],
|
||||||
);
|
);
|
||||||
bytes_read += result.consumed_in;
|
bytes_read += result.consumed_in;
|
||||||
uncompressed.truncate(bytes_written + result.consumed_out);
|
bytes_written += result.consumed_out;
|
||||||
|
|
||||||
match result.status {
|
match result.status {
|
||||||
Ok(weezl::LzwStatus::Ok) => {}
|
Ok(weezl::LzwStatus::Ok) => {}
|
||||||
@@ -293,12 +174,11 @@ impl LZWReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let bytes = uncompressed.len();
|
uncompressed.resize(bytes_written, 0);
|
||||||
Ok((
|
Ok((
|
||||||
bytes,
|
uncompressed.len(),
|
||||||
LZWReader {
|
LZWReader {
|
||||||
buffer: io::Cursor::new(uncompressed),
|
buffer: io::Cursor::new(uncompressed),
|
||||||
byte_order: order,
|
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -311,20 +191,12 @@ impl Read for LZWReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EndianReader for LZWReader {
|
|
||||||
#[inline(always)]
|
|
||||||
fn byte_order(&self) -> ByteOrder {
|
|
||||||
self.byte_order
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// ## JPEG Reader (for "new-style" JPEG format (TIFF compression tag 7))
|
/// ## JPEG Reader (for "new-style" JPEG format (TIFF compression tag 7))
|
||||||
///
|
///
|
||||||
|
|
||||||
pub(crate) struct JpegReader {
|
pub(crate) struct JpegReader {
|
||||||
buffer: io::Cursor<Vec<u8>>,
|
buffer: io::Cursor<Vec<u8>>,
|
||||||
byte_order: ByteOrder,
|
|
||||||
}
|
}
|
||||||
impl JpegReader {
|
impl JpegReader {
|
||||||
/// Constructs new JpegReader wrapping a SmartReader.
|
/// Constructs new JpegReader wrapping a SmartReader.
|
||||||
@@ -347,8 +219,6 @@ impl JpegReader {
|
|||||||
where
|
where
|
||||||
R: Read + Seek,
|
R: Read + Seek,
|
||||||
{
|
{
|
||||||
let order = reader.byte_order;
|
|
||||||
|
|
||||||
// Read jpeg image data
|
// Read jpeg image data
|
||||||
let mut segment = vec![0; length as usize];
|
let mut segment = vec![0; length as usize];
|
||||||
reader.read_exact(&mut segment[..])?;
|
reader.read_exact(&mut segment[..])?;
|
||||||
@@ -358,16 +228,14 @@ impl JpegReader {
|
|||||||
let mut jpeg_data = tables.clone();
|
let mut jpeg_data = tables.clone();
|
||||||
let truncated_length = jpeg_data.len() - 2;
|
let truncated_length = jpeg_data.len() - 2;
|
||||||
jpeg_data.truncate(truncated_length);
|
jpeg_data.truncate(truncated_length);
|
||||||
jpeg_data.extend_from_slice(&mut segment[2..]);
|
jpeg_data.extend_from_slice(&segment[2..]);
|
||||||
|
|
||||||
Ok(JpegReader {
|
Ok(JpegReader {
|
||||||
buffer: io::Cursor::new(jpeg_data),
|
buffer: io::Cursor::new(jpeg_data),
|
||||||
byte_order: order,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
None => Ok(JpegReader {
|
None => Ok(JpegReader {
|
||||||
buffer: io::Cursor::new(segment),
|
buffer: io::Cursor::new(segment),
|
||||||
byte_order: order,
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -380,13 +248,6 @@ impl Read for JpegReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EndianReader for JpegReader {
|
|
||||||
#[inline(always)]
|
|
||||||
fn byte_order(&self) -> ByteOrder {
|
|
||||||
self.byte_order
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// ## PackBits Reader
|
/// ## PackBits Reader
|
||||||
///
|
///
|
||||||
@@ -394,58 +255,46 @@ impl EndianReader for JpegReader {
|
|||||||
/// Reader that unpacks Apple's `PackBits` format
|
/// Reader that unpacks Apple's `PackBits` format
|
||||||
pub struct PackBitsReader {
|
pub struct PackBitsReader {
|
||||||
buffer: io::Cursor<Vec<u8>>,
|
buffer: io::Cursor<Vec<u8>>,
|
||||||
byte_order: ByteOrder,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackBitsReader {
|
impl PackBitsReader {
|
||||||
/// Wraps a reader
|
/// Wraps a reader
|
||||||
pub fn new<R: Read + Seek>(
|
pub fn new<R: Read + Seek>(
|
||||||
mut reader: R,
|
mut reader: R,
|
||||||
byte_order: ByteOrder,
|
|
||||||
length: usize,
|
length: usize,
|
||||||
) -> io::Result<(usize, PackBitsReader)> {
|
) -> io::Result<(usize, PackBitsReader)> {
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
let mut read: usize = 0;
|
|
||||||
while read < length {
|
|
||||||
let lread = read_packbits_run(&mut reader, &mut buffer)?;
|
|
||||||
if lread == 0 {
|
|
||||||
return Err(io::ErrorKind::UnexpectedEof.into());
|
|
||||||
}
|
|
||||||
read += lread;
|
|
||||||
}
|
|
||||||
Ok((
|
|
||||||
buffer.len(),
|
|
||||||
PackBitsReader {
|
|
||||||
buffer: io::Cursor::new(buffer),
|
|
||||||
byte_order,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_packbits_run<R: Read + Seek>(reader: &mut R, buffer: &mut Vec<u8>) -> io::Result<usize> {
|
|
||||||
let mut header: [u8; 1] = [0];
|
let mut header: [u8; 1] = [0];
|
||||||
|
let mut data: [u8; 1] = [0];
|
||||||
|
|
||||||
let bytes = reader.read(&mut header)?;
|
let mut bytes_read = 0;
|
||||||
|
while bytes_read < length {
|
||||||
match bytes {
|
|
||||||
0 => Ok(0),
|
|
||||||
_ => match header[0] as i8 {
|
|
||||||
-128 => Ok(1),
|
|
||||||
h if h >= -127 && h <= -1 => {
|
|
||||||
let new_len = buffer.len() + (1 - h as isize) as usize;
|
|
||||||
reader.read_exact(&mut header)?;
|
reader.read_exact(&mut header)?;
|
||||||
buffer.resize(new_len, header[0]);
|
bytes_read += 1;
|
||||||
Ok(2)
|
|
||||||
}
|
let h = header[0] as i8;
|
||||||
h => {
|
if h >= -127 && h <= -1 {
|
||||||
|
let new_len = buffer.len() + (1 - h as isize) as usize;
|
||||||
|
reader.read_exact(&mut data)?;
|
||||||
|
buffer.resize(new_len, data[0]);
|
||||||
|
bytes_read += 1;
|
||||||
|
} else if h >= 0 {
|
||||||
let num_vals = h as usize + 1;
|
let num_vals = h as usize + 1;
|
||||||
let start = buffer.len();
|
let start = buffer.len();
|
||||||
buffer.resize(start + num_vals, 0);
|
buffer.resize(start + num_vals, 0);
|
||||||
reader.read_exact(&mut buffer[start..])?;
|
reader.read_exact(&mut buffer[start..])?;
|
||||||
Ok(num_vals + 1)
|
bytes_read += num_vals
|
||||||
|
} else {
|
||||||
|
// h = -128 is a no-op.
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
buffer.len(),
|
||||||
|
PackBitsReader {
|
||||||
|
buffer: io::Cursor::new(buffer),
|
||||||
},
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,13 +305,6 @@ impl Read for PackBitsReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EndianReader for PackBitsReader {
|
|
||||||
#[inline(always)]
|
|
||||||
fn byte_order(&self) -> ByteOrder {
|
|
||||||
self.byte_order
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// ## SmartReader Reader
|
/// ## SmartReader Reader
|
||||||
///
|
///
|
||||||
@@ -471,7 +313,7 @@ impl EndianReader for PackBitsReader {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SmartReader<R>
|
pub struct SmartReader<R>
|
||||||
where
|
where
|
||||||
R: Read + Seek,
|
R: Read,
|
||||||
{
|
{
|
||||||
reader: R,
|
reader: R,
|
||||||
pub byte_order: ByteOrder,
|
pub byte_order: ByteOrder,
|
||||||
@@ -479,7 +321,7 @@ where
|
|||||||
|
|
||||||
impl<R> SmartReader<R>
|
impl<R> SmartReader<R>
|
||||||
where
|
where
|
||||||
R: Read + Seek,
|
R: Read,
|
||||||
{
|
{
|
||||||
/// Wraps a reader
|
/// Wraps a reader
|
||||||
pub fn wrap(reader: R, byte_order: ByteOrder) -> SmartReader<R> {
|
pub fn wrap(reader: R, byte_order: ByteOrder) -> SmartReader<R> {
|
||||||
@@ -489,7 +331,7 @@ where
|
|||||||
|
|
||||||
impl<R> EndianReader for SmartReader<R>
|
impl<R> EndianReader for SmartReader<R>
|
||||||
where
|
where
|
||||||
R: Read + Seek,
|
R: Read,
|
||||||
{
|
{
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn byte_order(&self) -> ByteOrder {
|
fn byte_order(&self) -> ByteOrder {
|
||||||
@@ -497,7 +339,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> Read for SmartReader<R> {
|
impl<R: Read> Read for SmartReader<R> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
self.reader.read(buf)
|
self.reader.read(buf)
|
||||||
@@ -524,8 +366,7 @@ mod test {
|
|||||||
let encoded_len = encoded.len();
|
let encoded_len = encoded.len();
|
||||||
|
|
||||||
let buff = io::Cursor::new(encoded);
|
let buff = io::Cursor::new(encoded);
|
||||||
let (_, mut decoder) =
|
let (_, mut decoder) = PackBitsReader::new(buff, encoded_len).unwrap();
|
||||||
PackBitsReader::new(buff, ByteOrder::LittleEndian, encoded_len).unwrap();
|
|
||||||
|
|
||||||
let mut decoded = Vec::new();
|
let mut decoded = Vec::new();
|
||||||
decoder.read_to_end(&mut decoded).unwrap();
|
decoder.read_to_end(&mut decoded).unwrap();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use tags::{PhotometricInterpretation, SampleFormat};
|
use crate::tags::{PhotometricInterpretation, SampleFormat};
|
||||||
|
|
||||||
/// Trait for different colortypes that can be encoded.
|
/// Trait for different colortypes that can be encoded.
|
||||||
pub trait ColorType {
|
pub trait ColorType {
|
||||||
|
|||||||
+255
-439
@@ -1,402 +1,31 @@
|
|||||||
use std::collections::BTreeMap;
|
pub use tiff_value::*;
|
||||||
use std::convert::TryFrom;
|
|
||||||
use std::io::{Seek, Write};
|
|
||||||
use std::{cmp, io, mem};
|
|
||||||
|
|
||||||
use bytecast;
|
use std::{
|
||||||
use error::{TiffError, TiffFormatError, TiffResult};
|
cmp,
|
||||||
use tags::{self, ResolutionUnit, Tag, Type};
|
collections::BTreeMap,
|
||||||
|
convert::{TryFrom, TryInto},
|
||||||
|
io::{self, Seek, Write},
|
||||||
|
marker::PhantomData,
|
||||||
|
mem,
|
||||||
|
num::TryFromIntError,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::TiffResult,
|
||||||
|
tags::{self, ResolutionUnit, Tag},
|
||||||
|
};
|
||||||
|
|
||||||
pub mod colortype;
|
pub mod colortype;
|
||||||
|
mod tiff_value;
|
||||||
mod writer;
|
mod writer;
|
||||||
|
|
||||||
use self::colortype::*;
|
use self::colortype::*;
|
||||||
use self::writer::*;
|
use self::writer::*;
|
||||||
|
|
||||||
/// Type to represent tiff values of type `RATIONAL`
|
/// Encoder for Tiff and BigTiff files.
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Rational {
|
|
||||||
pub n: u32,
|
|
||||||
pub d: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type to represent tiff values of type `SRATIONAL`
|
|
||||||
pub struct SRational {
|
|
||||||
pub n: i32,
|
|
||||||
pub d: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait for types that can be encoded in a tiff file
|
|
||||||
pub trait TiffValue {
|
|
||||||
const BYTE_LEN: u32;
|
|
||||||
const FIELD_TYPE: Type;
|
|
||||||
fn count(&self) -> u32;
|
|
||||||
fn bytes(&self) -> u32 {
|
|
||||||
self.count() * Self::BYTE_LEN
|
|
||||||
}
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for [u8] {
|
|
||||||
const BYTE_LEN: u32 = 1;
|
|
||||||
const FIELD_TYPE: Type = Type::BYTE;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
self.len() as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
writer.write_bytes(self)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for [i8] {
|
|
||||||
const BYTE_LEN: u32 = 1;
|
|
||||||
const FIELD_TYPE: Type = Type::SBYTE;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
self.len() as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
let slice = bytecast::i8_as_ne_bytes(self);
|
|
||||||
writer.write_bytes(slice)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for [u16] {
|
|
||||||
const BYTE_LEN: u32 = 2;
|
|
||||||
const FIELD_TYPE: Type = Type::SHORT;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
self.len() as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
let slice = bytecast::u16_as_ne_bytes(self);
|
|
||||||
writer.write_bytes(slice)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for [i16] {
|
|
||||||
const BYTE_LEN: u32 = 2;
|
|
||||||
const FIELD_TYPE: Type = Type::SSHORT;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
self.len() as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
let slice = bytecast::i16_as_ne_bytes(self);
|
|
||||||
writer.write_bytes(slice)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for [u32] {
|
|
||||||
const BYTE_LEN: u32 = 4;
|
|
||||||
const FIELD_TYPE: Type = Type::LONG;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
self.len() as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
let slice = bytecast::u32_as_ne_bytes(self);
|
|
||||||
writer.write_bytes(slice)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for [i32] {
|
|
||||||
const BYTE_LEN: u32 = 4;
|
|
||||||
const FIELD_TYPE: Type = Type::SLONG;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
self.len() as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
let slice = bytecast::i32_as_ne_bytes(self);
|
|
||||||
writer.write_bytes(slice)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<()> {
|
|
||||||
let slice = bytecast::u64_as_ne_bytes(self);
|
|
||||||
writer.write_bytes(slice)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for [f32] {
|
|
||||||
const BYTE_LEN: u32 = 4;
|
|
||||||
const FIELD_TYPE: Type = Type::FLOAT;
|
|
||||||
|
|
||||||
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 = bytecast::f32_as_ne_bytes(self);
|
|
||||||
writer.write_bytes(slice)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for [f64] {
|
|
||||||
const BYTE_LEN: u32 = 8;
|
|
||||||
const FIELD_TYPE: Type = Type::DOUBLE;
|
|
||||||
|
|
||||||
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 = bytecast::f64_as_ne_bytes(self);
|
|
||||||
writer.write_bytes(slice)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for [Rational] {
|
|
||||||
const BYTE_LEN: u32 = 8;
|
|
||||||
const FIELD_TYPE: Type = Type::RATIONAL;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
self.len() as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
for x in self {
|
|
||||||
x.write(writer)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for [SRational] {
|
|
||||||
const BYTE_LEN: u32 = 8;
|
|
||||||
const FIELD_TYPE: Type = Type::SRATIONAL;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
self.len() as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
for x in self {
|
|
||||||
x.write(writer)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for u8 {
|
|
||||||
const BYTE_LEN: u32 = 1;
|
|
||||||
const FIELD_TYPE: Type = Type::BYTE;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
writer.write_u8(*self)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for i8 {
|
|
||||||
const BYTE_LEN: u32 = 1;
|
|
||||||
const FIELD_TYPE: Type = Type::SBYTE;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
writer.write_i8(*self)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for u16 {
|
|
||||||
const BYTE_LEN: u32 = 2;
|
|
||||||
const FIELD_TYPE: Type = Type::SHORT;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
writer.write_u16(*self)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for i16 {
|
|
||||||
const BYTE_LEN: u32 = 2;
|
|
||||||
const FIELD_TYPE: Type = Type::SSHORT;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
writer.write_i16(*self)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for u32 {
|
|
||||||
const BYTE_LEN: u32 = 4;
|
|
||||||
const FIELD_TYPE: Type = Type::LONG;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
writer.write_u32(*self)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for i32 {
|
|
||||||
const BYTE_LEN: u32 = 4;
|
|
||||||
const FIELD_TYPE: Type = Type::SLONG;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
writer.write_i32(*self)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for 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 f32 {
|
|
||||||
const BYTE_LEN: u32 = 4;
|
|
||||||
const FIELD_TYPE: Type = Type::FLOAT;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
writer.write_f32(*self)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for f64 {
|
|
||||||
const BYTE_LEN: u32 = 8;
|
|
||||||
const FIELD_TYPE: Type = Type::DOUBLE;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
writer.write_f64(*self)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for Rational {
|
|
||||||
const BYTE_LEN: u32 = 8;
|
|
||||||
const FIELD_TYPE: Type = Type::RATIONAL;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
writer.write_u32(self.n)?;
|
|
||||||
writer.write_u32(self.d)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for SRational {
|
|
||||||
const BYTE_LEN: u32 = 8;
|
|
||||||
const FIELD_TYPE: Type = Type::SRATIONAL;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
writer.write_i32(self.n)?;
|
|
||||||
writer.write_i32(self.d)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffValue for str {
|
|
||||||
const BYTE_LEN: u32 = 1;
|
|
||||||
const FIELD_TYPE: Type = Type::ASCII;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
self.len() as u32 + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
if self.is_ascii() && !self.bytes().any(|b| b == 0) {
|
|
||||||
writer.write_bytes(self.as_bytes())?;
|
|
||||||
writer.write_u8(0)?;
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(TiffError::FormatError(TiffFormatError::InvalidTag))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T {
|
|
||||||
const BYTE_LEN: u32 = T::BYTE_LEN;
|
|
||||||
const FIELD_TYPE: Type = T::FIELD_TYPE;
|
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
|
||||||
(*self).count()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
(*self).write(writer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tiff encoder.
|
|
||||||
///
|
///
|
||||||
/// With this type you can get a `DirectoryEncoder` or a `ImageEncoder`
|
/// With this type you can get a `DirectoryEncoder` or a `ImageEncoder`
|
||||||
/// to encode tiff ifd directories with images.
|
/// to encode Tiff/BigTiff ifd directories with images.
|
||||||
///
|
///
|
||||||
/// See `DirectoryEncoder` and `ImageEncoder`.
|
/// See `DirectoryEncoder` and `ImageEncoder`.
|
||||||
///
|
///
|
||||||
@@ -408,39 +37,68 @@ impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T {
|
|||||||
/// # let image_data = vec![0; 100*100*3];
|
/// # let image_data = vec![0; 100*100*3];
|
||||||
/// use tiff::encoder::*;
|
/// use tiff::encoder::*;
|
||||||
///
|
///
|
||||||
|
/// // create a standard Tiff file
|
||||||
/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
||||||
///
|
|
||||||
/// tiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap();
|
/// tiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap();
|
||||||
|
///
|
||||||
|
/// // create a BigTiff file
|
||||||
|
/// let mut bigtiff = TiffEncoder::new_big(&mut file).unwrap();
|
||||||
|
/// bigtiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap();
|
||||||
|
///
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct TiffEncoder<W> {
|
pub struct TiffEncoder<W, K: TiffKind = TiffKindStandard> {
|
||||||
writer: TiffWriter<W>,
|
writer: TiffWriter<W>,
|
||||||
|
kind: PhantomData<K>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructor functions to create standard Tiff files.
|
||||||
impl<W: Write + Seek> TiffEncoder<W> {
|
impl<W: Write + Seek> TiffEncoder<W> {
|
||||||
pub fn new(writer: W) -> TiffResult<TiffEncoder<W>> {
|
/// Creates a new encoder for standard Tiff files.
|
||||||
|
///
|
||||||
|
/// To create BigTiff files, use [`new_big`][TiffEncoder::new_big] or
|
||||||
|
/// [`new_generic`][TiffEncoder::new_generic].
|
||||||
|
pub fn new(writer: W) -> TiffResult<TiffEncoder<W, TiffKindStandard>> {
|
||||||
|
TiffEncoder::new_generic(writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor functions to create BigTiff files.
|
||||||
|
impl<W: Write + Seek> TiffEncoder<W, TiffKindBig> {
|
||||||
|
/// Creates a new encoder for BigTiff files.
|
||||||
|
///
|
||||||
|
/// To create standard Tiff files, use [`new`][TiffEncoder::new] or
|
||||||
|
/// [`new_generic`][TiffEncoder::new_generic].
|
||||||
|
pub fn new_big(writer: W) -> TiffResult<Self> {
|
||||||
|
TiffEncoder::new_generic(writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic functions that are available for both Tiff and BigTiff encoders.
|
||||||
|
impl<W: Write + Seek, K: TiffKind> TiffEncoder<W, K> {
|
||||||
|
/// Creates a new Tiff or BigTiff encoder, inferred from the return type.
|
||||||
|
pub fn new_generic(writer: W) -> TiffResult<Self> {
|
||||||
let mut encoder = TiffEncoder {
|
let mut encoder = TiffEncoder {
|
||||||
writer: TiffWriter::new(writer),
|
writer: TiffWriter::new(writer),
|
||||||
|
kind: PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
write_tiff_header(&mut encoder.writer)?;
|
K::write_header(&mut encoder.writer)?;
|
||||||
// blank the IFD offset location
|
|
||||||
encoder.writer.write_u32(0)?;
|
|
||||||
|
|
||||||
Ok(encoder)
|
Ok(encoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a `DirectoryEncoder` to encode an ifd directory.
|
/// Create a [`DirectoryEncoder`] to encode an ifd directory.
|
||||||
pub fn new_directory(&mut self) -> TiffResult<DirectoryEncoder<W>> {
|
pub fn new_directory(&mut self) -> TiffResult<DirectoryEncoder<W, K>> {
|
||||||
DirectoryEncoder::new(&mut self.writer)
|
DirectoryEncoder::new(&mut self.writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an 'ImageEncoder' to encode an image one slice at a time.
|
/// Create an [`ImageEncoder`] to encode an image one slice at a time.
|
||||||
pub fn new_image<C: ColorType>(
|
pub fn new_image<C: ColorType>(
|
||||||
&mut self,
|
&mut self,
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
) -> TiffResult<ImageEncoder<W, C>> {
|
) -> TiffResult<ImageEncoder<W, C, K>> {
|
||||||
let encoder = DirectoryEncoder::new(&mut self.writer)?;
|
let encoder = DirectoryEncoder::new(&mut self.writer)?;
|
||||||
ImageEncoder::new(encoder, width, height)
|
ImageEncoder::new(encoder, width, height)
|
||||||
}
|
}
|
||||||
@@ -456,7 +114,7 @@ impl<W: Write + Seek> TiffEncoder<W> {
|
|||||||
[C::Inner]: TiffValue,
|
[C::Inner]: TiffValue,
|
||||||
{
|
{
|
||||||
let encoder = DirectoryEncoder::new(&mut self.writer)?;
|
let encoder = DirectoryEncoder::new(&mut self.writer)?;
|
||||||
let image: ImageEncoder<W, C> = ImageEncoder::new(encoder, width, height)?;
|
let image: ImageEncoder<W, C, K> = ImageEncoder::new(encoder, width, height)?;
|
||||||
image.write_data(data)
|
image.write_data(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -465,19 +123,19 @@ impl<W: Write + Seek> TiffEncoder<W> {
|
|||||||
///
|
///
|
||||||
/// You should call `finish` on this when you are finished with it.
|
/// You should call `finish` on this when you are finished with it.
|
||||||
/// Encoding can silently fail while this is dropping.
|
/// Encoding can silently fail while this is dropping.
|
||||||
pub struct DirectoryEncoder<'a, W: 'a + Write + Seek> {
|
pub struct DirectoryEncoder<'a, W: 'a + Write + Seek, K: TiffKind> {
|
||||||
writer: &'a mut TiffWriter<W>,
|
writer: &'a mut TiffWriter<W>,
|
||||||
dropped: bool,
|
dropped: bool,
|
||||||
// We use BTreeMap to make sure tags are written in correct order
|
// We use BTreeMap to make sure tags are written in correct order
|
||||||
ifd_pointer_pos: u64,
|
ifd_pointer_pos: u64,
|
||||||
ifd: BTreeMap<u16, (u16, u32, Vec<u8>)>,
|
ifd: BTreeMap<u16, DirectoryEntry<K::OffsetType>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
|
||||||
fn new(writer: &'a mut TiffWriter<W>) -> TiffResult<DirectoryEncoder<'a, W>> {
|
fn new(writer: &'a mut TiffWriter<W>) -> TiffResult<Self> {
|
||||||
// the previous word is the IFD offset position
|
// the previous word is the IFD offset position
|
||||||
let ifd_pointer_pos = writer.offset() - mem::size_of::<u32>() as u64;
|
let ifd_pointer_pos = writer.offset() - mem::size_of::<K::OffsetType>() as u64;
|
||||||
writer.pad_word_boundary()?;
|
writer.pad_word_boundary()?; // TODO: Do we need to adjust this for BigTiff?
|
||||||
Ok(DirectoryEncoder {
|
Ok(DirectoryEncoder {
|
||||||
writer,
|
writer,
|
||||||
dropped: false,
|
dropped: false,
|
||||||
@@ -488,8 +146,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
|||||||
|
|
||||||
/// Write a single ifd tag.
|
/// Write a single ifd tag.
|
||||||
pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) -> TiffResult<()> {
|
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(value.bytes());
|
||||||
let mut bytes = Vec::with_capacity(usize::try_from(len)?);
|
|
||||||
{
|
{
|
||||||
let mut writer = TiffWriter::new(&mut bytes);
|
let mut writer = TiffWriter::new(&mut bytes);
|
||||||
value.write(&mut writer)?;
|
value.write(&mut writer)?;
|
||||||
@@ -497,7 +154,11 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
|||||||
|
|
||||||
self.ifd.insert(
|
self.ifd.insert(
|
||||||
tag.to_u16(),
|
tag.to_u16(),
|
||||||
(<T>::FIELD_TYPE.to_u16(), value.count(), bytes),
|
DirectoryEntry {
|
||||||
|
data_type: <T>::FIELD_TYPE.to_u16(),
|
||||||
|
count: value.count().try_into()?,
|
||||||
|
data: bytes,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -505,15 +166,21 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
|||||||
|
|
||||||
fn write_directory(&mut self) -> TiffResult<u64> {
|
fn write_directory(&mut self) -> TiffResult<u64> {
|
||||||
// Start by writing out all values
|
// Start by writing out all values
|
||||||
for &mut (_, _, ref mut bytes) in self.ifd.values_mut() {
|
for &mut DirectoryEntry {
|
||||||
if bytes.len() > 4 {
|
data: ref mut bytes,
|
||||||
|
..
|
||||||
|
} in self.ifd.values_mut()
|
||||||
|
{
|
||||||
|
let data_bytes = mem::size_of::<K::OffsetType>();
|
||||||
|
|
||||||
|
if bytes.len() > data_bytes {
|
||||||
let offset = self.writer.offset();
|
let offset = self.writer.offset();
|
||||||
self.writer.write_bytes(bytes)?;
|
self.writer.write_bytes(bytes)?;
|
||||||
*bytes = vec![0, 0, 0, 0];
|
*bytes = vec![0; data_bytes];
|
||||||
let mut writer = TiffWriter::new(bytes as &mut [u8]);
|
let mut writer = TiffWriter::new(bytes as &mut [u8]);
|
||||||
writer.write_u32(u32::try_from(offset)?)?;
|
K::write_offset(&mut writer, offset)?;
|
||||||
} else {
|
} else {
|
||||||
while bytes.len() < 4 {
|
while bytes.len() < data_bytes {
|
||||||
bytes.push(0);
|
bytes.push(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -521,11 +188,19 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
|||||||
|
|
||||||
let offset = self.writer.offset();
|
let offset = self.writer.offset();
|
||||||
|
|
||||||
self.writer.write_u16(u16::try_from(self.ifd.len())?)?;
|
K::write_entry_count(&mut self.writer, self.ifd.len())?;
|
||||||
for (tag, &(ref field_type, ref count, ref offset)) in self.ifd.iter() {
|
for (
|
||||||
|
tag,
|
||||||
|
&DirectoryEntry {
|
||||||
|
data_type: ref field_type,
|
||||||
|
ref count,
|
||||||
|
data: ref offset,
|
||||||
|
},
|
||||||
|
) in self.ifd.iter()
|
||||||
|
{
|
||||||
self.writer.write_u16(*tag)?;
|
self.writer.write_u16(*tag)?;
|
||||||
self.writer.write_u16(*field_type)?;
|
self.writer.write_u16(*field_type)?;
|
||||||
self.writer.write_u32(*count)?;
|
(*count).write(&mut self.writer)?;
|
||||||
self.writer.write_bytes(offset)?;
|
self.writer.write_bytes(offset)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,9 +221,9 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
|||||||
let curr_pos = self.writer.offset();
|
let curr_pos = self.writer.offset();
|
||||||
|
|
||||||
self.writer.goto_offset(self.ifd_pointer_pos)?;
|
self.writer.goto_offset(self.ifd_pointer_pos)?;
|
||||||
self.writer.write_u32(u32::try_from(ifd_pointer)?)?;
|
K::write_offset(&mut self.writer, ifd_pointer)?;
|
||||||
self.writer.goto_offset(curr_pos)?;
|
self.writer.goto_offset(curr_pos)?;
|
||||||
self.writer.write_u32(0)?;
|
K::write_offset(&mut self.writer, 0)?;
|
||||||
|
|
||||||
self.dropped = true;
|
self.dropped = true;
|
||||||
|
|
||||||
@@ -561,7 +236,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
|
impl<'a, W: Write + Seek, K: TiffKind> Drop for DirectoryEncoder<'a, W, K> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if !self.dropped {
|
if !self.dropped {
|
||||||
let _ = self.finish_internal();
|
let _ = self.finish_internal();
|
||||||
@@ -589,6 +264,9 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
|
|||||||
/// // You can encode tags here
|
/// // You can encode tags here
|
||||||
/// image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap();
|
/// image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap();
|
||||||
///
|
///
|
||||||
|
/// // Strip size can be configured before writing data
|
||||||
|
/// image.rows_per_strip(2).unwrap();
|
||||||
|
///
|
||||||
/// let mut idx = 0;
|
/// let mut idx = 0;
|
||||||
/// while image.next_strip_sample_count() > 0 {
|
/// while image.next_strip_sample_count() > 0 {
|
||||||
/// let sample_count = image.next_strip_sample_count() as usize;
|
/// let sample_count = image.next_strip_sample_count() as usize;
|
||||||
@@ -599,31 +277,28 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
/// You can also call write_data function wich will encode by strip and finish
|
/// You can also call write_data function wich will encode by strip and finish
|
||||||
pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType> {
|
pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType, K: TiffKind> {
|
||||||
encoder: DirectoryEncoder<'a, W>,
|
encoder: DirectoryEncoder<'a, W, K>,
|
||||||
strip_idx: u64,
|
strip_idx: u64,
|
||||||
strip_count: u64,
|
strip_count: u64,
|
||||||
row_samples: u64,
|
row_samples: u64,
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
rows_per_strip: u64,
|
rows_per_strip: u64,
|
||||||
strip_offsets: Vec<u32>,
|
strip_offsets: Vec<K::OffsetType>,
|
||||||
strip_byte_count: Vec<u32>,
|
strip_byte_count: Vec<K::OffsetType>,
|
||||||
dropped: bool,
|
dropped: bool,
|
||||||
_phantom: ::std::marker::PhantomData<C>,
|
_phantom: ::std::marker::PhantomData<C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind> ImageEncoder<'a, W, T, K> {
|
||||||
fn new(
|
fn new(mut encoder: DirectoryEncoder<'a, W, K>, width: u32, height: u32) -> TiffResult<Self> {
|
||||||
mut encoder: DirectoryEncoder<'a, W>,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
) -> TiffResult<ImageEncoder<'a, W, T>> {
|
|
||||||
let row_samples = u64::from(width) * u64::try_from(<T>::BITS_PER_SAMPLE.len())?;
|
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);
|
let row_bytes = row_samples * u64::from(<T::Inner>::BYTE_LEN);
|
||||||
|
|
||||||
// As per tiff spec each strip should be about 8k long
|
// Limit the strip size to prevent potential memory and security issues.
|
||||||
let rows_per_strip = (8000 + row_bytes - 1) / row_bytes;
|
// Also keep the multiple strip handling 'oiled'
|
||||||
|
let rows_per_strip = (1_000_000 + row_bytes - 1) / row_bytes;
|
||||||
|
|
||||||
let strip_count = (u64::from(height) + rows_per_strip - 1) / rows_per_strip;
|
let strip_count = (u64::from(height) + rows_per_strip - 1) / rows_per_strip;
|
||||||
|
|
||||||
@@ -690,8 +365,8 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let offset = self.encoder.write_data(value)?;
|
let offset = self.encoder.write_data(value)?;
|
||||||
self.strip_offsets.push(u32::try_from(offset)?);
|
self.strip_offsets.push(K::convert_offset(offset)?);
|
||||||
self.strip_byte_count.push(value.bytes());
|
self.strip_byte_count.push(value.bytes().try_into()?);
|
||||||
|
|
||||||
self.strip_idx += 1;
|
self.strip_idx += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -755,18 +430,42 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
|||||||
self.encoder.write_tag(Tag::YResolution, value).unwrap();
|
self.encoder.write_tag(Tag::YResolution, value).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set image number of lines per strip
|
||||||
|
///
|
||||||
|
/// This function needs to be called before any calls to `write_data` or
|
||||||
|
/// `write_strip` and will return an error otherwise.
|
||||||
|
pub fn rows_per_strip(&mut self, value: u32) -> TiffResult<()> {
|
||||||
|
if self.strip_idx != 0 {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"Cannot change strip size after data was written",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
// Write tag as 32 bits
|
||||||
|
self.encoder.write_tag(Tag::RowsPerStrip, value)?;
|
||||||
|
|
||||||
|
let value: u64 = value as u64;
|
||||||
|
self.strip_count = (self.height as u64 + value - 1) / value;
|
||||||
|
self.rows_per_strip = value;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn finish_internal(&mut self) -> TiffResult<()> {
|
fn finish_internal(&mut self) -> TiffResult<()> {
|
||||||
self.encoder
|
self.encoder
|
||||||
.write_tag(Tag::StripOffsets, &*self.strip_offsets)?;
|
.write_tag(Tag::StripOffsets, K::convert_slice(&self.strip_offsets))?;
|
||||||
self.encoder
|
self.encoder.write_tag(
|
||||||
.write_tag(Tag::StripByteCounts, &*self.strip_byte_count)?;
|
Tag::StripByteCounts,
|
||||||
|
K::convert_slice(&self.strip_byte_count),
|
||||||
|
)?;
|
||||||
self.dropped = true;
|
self.dropped = true;
|
||||||
|
|
||||||
self.encoder.finish_internal()
|
self.encoder.finish_internal()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference of the underlying `DirectoryEncoder`
|
/// Get a reference of the underlying `DirectoryEncoder`
|
||||||
pub fn encoder(&mut self) -> &mut DirectoryEncoder<'a, W> {
|
pub fn encoder(&mut self) -> &mut DirectoryEncoder<'a, W, K> {
|
||||||
&mut self.encoder
|
&mut self.encoder
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -776,10 +475,127 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, W: Write + Seek, C: ColorType> Drop for ImageEncoder<'a, W, C> {
|
impl<'a, W: Write + Seek, C: ColorType, K: TiffKind> Drop for ImageEncoder<'a, W, C, K> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if !self.dropped {
|
if !self.dropped {
|
||||||
let _ = self.finish_internal();
|
let _ = self.finish_internal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DirectoryEntry<S> {
|
||||||
|
data_type: u16,
|
||||||
|
count: S,
|
||||||
|
data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait to abstract over Tiff/BigTiff differences.
|
||||||
|
///
|
||||||
|
/// Implemented for [`TiffKindStandard`] and [`TiffKindBig`].
|
||||||
|
pub trait TiffKind {
|
||||||
|
/// The type of offset fields, `u32` for normal Tiff, `u64` for BigTiff.
|
||||||
|
type OffsetType: TryFrom<usize, Error = TryFromIntError> + Into<u64> + TiffValue;
|
||||||
|
|
||||||
|
/// Needed for the `convert_slice` method.
|
||||||
|
type OffsetArrayType: ?Sized + TiffValue;
|
||||||
|
|
||||||
|
/// Write the (Big)Tiff header.
|
||||||
|
fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()>;
|
||||||
|
|
||||||
|
/// Convert a file offset to `Self::OffsetType`.
|
||||||
|
///
|
||||||
|
/// This returns an error for normal Tiff if the offset is larger than `u32::MAX`.
|
||||||
|
fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType>;
|
||||||
|
|
||||||
|
/// Write an offset value to the given writer.
|
||||||
|
///
|
||||||
|
/// Like `convert_offset`, this errors if `offset > u32::MAX` for normal Tiff.
|
||||||
|
fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()>;
|
||||||
|
|
||||||
|
/// Write the IFD entry count field with the given `count` value.
|
||||||
|
///
|
||||||
|
/// The entry count field is an `u16` for normal Tiff and `u64` for BigTiff. Errors
|
||||||
|
/// if the given `usize` is larger than the representable values.
|
||||||
|
fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()>;
|
||||||
|
|
||||||
|
/// Internal helper method for satisfying Rust's type checker.
|
||||||
|
///
|
||||||
|
/// The `TiffValue` trait is implemented for both primitive values (e.g. `u8`, `u32`) and
|
||||||
|
/// slices of primitive values (e.g. `[u8]`, `[u32]`). However, this is not represented in
|
||||||
|
/// the type system, so there is no guarantee that that for all `T: TiffValue` there is also
|
||||||
|
/// an implementation of `TiffValue` for `[T]`. This method works around that problem by
|
||||||
|
/// providing a conversion from `[T]` to some value that implements `TiffValue`, thereby
|
||||||
|
/// making all slices of `OffsetType` usable with `write_tag` and similar methods.
|
||||||
|
///
|
||||||
|
/// Implementations of this trait should always set `OffsetArrayType` to `[OffsetType]`.
|
||||||
|
fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a standard Tiff file.
|
||||||
|
pub struct TiffKindStandard;
|
||||||
|
|
||||||
|
impl TiffKind for TiffKindStandard {
|
||||||
|
type OffsetType = u32;
|
||||||
|
type OffsetArrayType = [u32];
|
||||||
|
|
||||||
|
fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
write_tiff_header(writer)?;
|
||||||
|
// blank the IFD offset location
|
||||||
|
writer.write_u32(0)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType> {
|
||||||
|
Ok(Self::OffsetType::try_from(offset)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()> {
|
||||||
|
writer.write_u32(u32::try_from(offset)?)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()> {
|
||||||
|
writer.write_u16(u16::try_from(count)?)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType {
|
||||||
|
slice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a BigTiff file.
|
||||||
|
pub struct TiffKindBig;
|
||||||
|
|
||||||
|
impl TiffKind for TiffKindBig {
|
||||||
|
type OffsetType = u64;
|
||||||
|
type OffsetArrayType = [u64];
|
||||||
|
|
||||||
|
fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
write_bigtiff_header(writer)?;
|
||||||
|
// blank the IFD offset location
|
||||||
|
writer.write_u64(0)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType> {
|
||||||
|
Ok(offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()> {
|
||||||
|
writer.write_u64(offset)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()> {
|
||||||
|
writer.write_u64(u64::try_from(count)?)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType {
|
||||||
|
slice
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,483 @@
|
|||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use crate::{bytecast, tags::Type, TiffError, TiffFormatError, TiffResult};
|
||||||
|
|
||||||
|
use super::writer::TiffWriter;
|
||||||
|
|
||||||
|
/// Trait for types that can be encoded in a tiff file
|
||||||
|
pub trait TiffValue {
|
||||||
|
const BYTE_LEN: u8;
|
||||||
|
const FIELD_TYPE: Type;
|
||||||
|
fn count(&self) -> usize;
|
||||||
|
fn bytes(&self) -> usize {
|
||||||
|
self.count() * usize::from(Self::BYTE_LEN)
|
||||||
|
}
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [u8] {
|
||||||
|
const BYTE_LEN: u8 = 1;
|
||||||
|
const FIELD_TYPE: Type = Type::BYTE;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_bytes(self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [i8] {
|
||||||
|
const BYTE_LEN: u8 = 1;
|
||||||
|
const FIELD_TYPE: Type = Type::SBYTE;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
let slice = bytecast::i8_as_ne_bytes(self);
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [u16] {
|
||||||
|
const BYTE_LEN: u8 = 2;
|
||||||
|
const FIELD_TYPE: Type = Type::SHORT;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
let slice = bytecast::u16_as_ne_bytes(self);
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [i16] {
|
||||||
|
const BYTE_LEN: u8 = 2;
|
||||||
|
const FIELD_TYPE: Type = Type::SSHORT;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
let slice = bytecast::i16_as_ne_bytes(self);
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [u32] {
|
||||||
|
const BYTE_LEN: u8 = 4;
|
||||||
|
const FIELD_TYPE: Type = Type::LONG;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
let slice = bytecast::u32_as_ne_bytes(self);
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [i32] {
|
||||||
|
const BYTE_LEN: u8 = 4;
|
||||||
|
const FIELD_TYPE: Type = Type::SLONG;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
let slice = bytecast::i32_as_ne_bytes(self);
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [u64] {
|
||||||
|
const BYTE_LEN: u8 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::LONG8;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
let slice = bytecast::u64_as_ne_bytes(self);
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [i64] {
|
||||||
|
const BYTE_LEN: u8 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::SLONG8;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
let slice = bytecast::i64_as_ne_bytes(self);
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [f32] {
|
||||||
|
const BYTE_LEN: u8 = 4;
|
||||||
|
const FIELD_TYPE: Type = Type::FLOAT;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
// We write using nativeedian so this sould be safe
|
||||||
|
let slice = bytecast::f32_as_ne_bytes(self);
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [f64] {
|
||||||
|
const BYTE_LEN: u8 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::DOUBLE;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
// We write using nativeedian so this sould be safe
|
||||||
|
let slice = bytecast::f64_as_ne_bytes(self);
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [Ifd] {
|
||||||
|
const BYTE_LEN: u8 = 4;
|
||||||
|
const FIELD_TYPE: Type = Type::IFD;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
for x in self {
|
||||||
|
x.write(writer)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [Ifd8] {
|
||||||
|
const BYTE_LEN: u8 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::IFD8;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
for x in self {
|
||||||
|
x.write(writer)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [Rational] {
|
||||||
|
const BYTE_LEN: u8 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::RATIONAL;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
for x in self {
|
||||||
|
x.write(writer)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [SRational] {
|
||||||
|
const BYTE_LEN: u8 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::SRATIONAL;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
for x in self {
|
||||||
|
x.write(writer)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for u8 {
|
||||||
|
const BYTE_LEN: u8 = 1;
|
||||||
|
const FIELD_TYPE: Type = Type::BYTE;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_u8(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for i8 {
|
||||||
|
const BYTE_LEN: u8 = 1;
|
||||||
|
const FIELD_TYPE: Type = Type::SBYTE;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_i8(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for u16 {
|
||||||
|
const BYTE_LEN: u8 = 2;
|
||||||
|
const FIELD_TYPE: Type = Type::SHORT;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_u16(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for i16 {
|
||||||
|
const BYTE_LEN: u8 = 2;
|
||||||
|
const FIELD_TYPE: Type = Type::SSHORT;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_i16(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for u32 {
|
||||||
|
const BYTE_LEN: u8 = 4;
|
||||||
|
const FIELD_TYPE: Type = Type::LONG;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_u32(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for i32 {
|
||||||
|
const BYTE_LEN: u8 = 4;
|
||||||
|
const FIELD_TYPE: Type = Type::SLONG;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_i32(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for u64 {
|
||||||
|
const BYTE_LEN: u8 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::LONG8;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_u64(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for i64 {
|
||||||
|
const BYTE_LEN: u8 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::SLONG8;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_i64(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for f32 {
|
||||||
|
const BYTE_LEN: u8 = 4;
|
||||||
|
const FIELD_TYPE: Type = Type::FLOAT;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_f32(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for f64 {
|
||||||
|
const BYTE_LEN: u8 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::DOUBLE;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_f64(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for Ifd {
|
||||||
|
const BYTE_LEN: u8 = 4;
|
||||||
|
const FIELD_TYPE: Type = Type::IFD;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_u32(self.0)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for Ifd8 {
|
||||||
|
const BYTE_LEN: u8 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::IFD8;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_u64(self.0)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for Rational {
|
||||||
|
const BYTE_LEN: u8 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::RATIONAL;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_u32(self.n)?;
|
||||||
|
writer.write_u32(self.d)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for SRational {
|
||||||
|
const BYTE_LEN: u8 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::SRATIONAL;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_i32(self.n)?;
|
||||||
|
writer.write_i32(self.d)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for str {
|
||||||
|
const BYTE_LEN: u8 = 1;
|
||||||
|
const FIELD_TYPE: Type = Type::ASCII;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
self.len() + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
if self.is_ascii() && !self.bytes().any(|b| b == 0) {
|
||||||
|
writer.write_bytes(self.as_bytes())?;
|
||||||
|
writer.write_u8(0)?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(TiffError::FormatError(TiffFormatError::InvalidTag))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T {
|
||||||
|
const BYTE_LEN: u8 = T::BYTE_LEN;
|
||||||
|
const FIELD_TYPE: Type = T::FIELD_TYPE;
|
||||||
|
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
(*self).count()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
(*self).write(writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type to represent tiff values of type `IFD`
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Ifd(pub u32);
|
||||||
|
|
||||||
|
/// Type to represent tiff values of type `IFD8`
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Ifd8(pub u64);
|
||||||
|
|
||||||
|
/// Type to represent tiff values of type `RATIONAL`
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Rational {
|
||||||
|
pub n: u32,
|
||||||
|
pub d: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type to represent tiff values of type `SRATIONAL`
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SRational {
|
||||||
|
pub n: i32,
|
||||||
|
pub d: i32,
|
||||||
|
}
|
||||||
+33
-1
@@ -1,4 +1,4 @@
|
|||||||
use error::TiffResult;
|
use crate::error::TiffResult;
|
||||||
use std::io::{self, Seek, SeekFrom, Write};
|
use std::io::{self, Seek, SeekFrom, Write};
|
||||||
|
|
||||||
pub fn write_tiff_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
pub fn write_tiff_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
@@ -14,6 +14,31 @@ pub fn write_tiff_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()>
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes a BigTiff header, excluding the IFD offset field.
|
||||||
|
///
|
||||||
|
/// Writes the byte order, version number, offset byte size, and zero constant fields. Does
|
||||||
|
// _not_ write the offset to the first IFD, this should be done by the caller.
|
||||||
|
pub fn write_bigtiff_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
let boi: u8 = 0x49;
|
||||||
|
#[cfg(not(target_endian = "little"))]
|
||||||
|
let boi: u8 = 0x4d;
|
||||||
|
|
||||||
|
// byte order indication
|
||||||
|
writer.writer.write_all(&[boi, boi])?;
|
||||||
|
// version number
|
||||||
|
writer.writer.write_all(&43u16.to_ne_bytes())?;
|
||||||
|
// bytesize of offsets (pointer size)
|
||||||
|
writer.writer.write_all(&8u16.to_ne_bytes())?;
|
||||||
|
// always 0
|
||||||
|
writer.writer.write_all(&0u16.to_ne_bytes())?;
|
||||||
|
|
||||||
|
// we wrote 8 bytes, so set the internal offset accordingly
|
||||||
|
writer.offset += 8;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TiffWriter<W> {
|
pub struct TiffWriter<W> {
|
||||||
writer: W,
|
writer: W,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
@@ -81,6 +106,13 @@ impl<W: Write> TiffWriter<W> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_i64(&mut self, n: i64) -> Result<(), io::Error> {
|
||||||
|
self.writer.write_all(&n.to_ne_bytes())?;
|
||||||
|
self.offset += 8;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn write_f32(&mut self, n: f32) -> Result<(), io::Error> {
|
pub fn write_f32(&mut self, n: f32) -> Result<(), io::Error> {
|
||||||
self.writer.write_all(&u32::to_ne_bytes(n.to_bits()))?;
|
self.writer.write_all(&u32::to_ne_bytes(n.to_bits()))?;
|
||||||
self.offset += 4;
|
self.offset += 4;
|
||||||
|
|||||||
+68
-24
@@ -1,12 +1,14 @@
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::str;
|
||||||
use std::string;
|
use std::string;
|
||||||
|
|
||||||
use decoder::ifd::Value;
|
use crate::decoder::{ifd::Value, ChunkType};
|
||||||
use miniz_oxide::inflate::TINFLStatus;
|
use crate::tags::{
|
||||||
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag};
|
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag,
|
||||||
use ColorType;
|
};
|
||||||
|
use crate::ColorType;
|
||||||
|
|
||||||
/// Tiff error kinds.
|
/// Tiff error kinds.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -26,6 +28,9 @@ pub enum TiffError {
|
|||||||
/// An integer conversion to or from a platform size failed, either due to
|
/// An integer conversion to or from a platform size failed, either due to
|
||||||
/// limits of the platform size or limits of the format.
|
/// limits of the platform size or limits of the format.
|
||||||
IntSizeError,
|
IntSizeError,
|
||||||
|
|
||||||
|
/// The image does not support the requested operation
|
||||||
|
UsageError(UsageError),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The image is not formatted properly.
|
/// The image is not formatted properly.
|
||||||
@@ -41,6 +46,14 @@ pub enum TiffFormatError {
|
|||||||
TiffSignatureInvalid,
|
TiffSignatureInvalid,
|
||||||
ImageFileDirectoryNotFound,
|
ImageFileDirectoryNotFound,
|
||||||
InconsistentSizesEncountered,
|
InconsistentSizesEncountered,
|
||||||
|
UnexpectedCompressedData {
|
||||||
|
actual_bytes: usize,
|
||||||
|
required_bytes: usize,
|
||||||
|
},
|
||||||
|
InconsistentStripSamples {
|
||||||
|
actual_samples: usize,
|
||||||
|
required_samples: usize,
|
||||||
|
},
|
||||||
InvalidTag,
|
InvalidTag,
|
||||||
InvalidTagValueType(Tag),
|
InvalidTagValueType(Tag),
|
||||||
RequiredTagNotFound(Tag),
|
RequiredTagNotFound(Tag),
|
||||||
@@ -48,7 +61,6 @@ pub enum TiffFormatError {
|
|||||||
ByteExpected(Value),
|
ByteExpected(Value),
|
||||||
UnsignedIntegerExpected(Value),
|
UnsignedIntegerExpected(Value),
|
||||||
SignedIntegerExpected(Value),
|
SignedIntegerExpected(Value),
|
||||||
InflateError(InflateError),
|
|
||||||
Format(String),
|
Format(String),
|
||||||
RequiredTagEmpty(Tag),
|
RequiredTagEmpty(Tag),
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@@ -64,6 +76,26 @@ impl fmt::Display for TiffFormatError {
|
|||||||
TiffSignatureInvalid => write!(fmt, "TIFF signature invalid."),
|
TiffSignatureInvalid => write!(fmt, "TIFF signature invalid."),
|
||||||
ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."),
|
ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."),
|
||||||
InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."),
|
InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."),
|
||||||
|
UnexpectedCompressedData {
|
||||||
|
actual_bytes,
|
||||||
|
required_bytes,
|
||||||
|
} => {
|
||||||
|
write!(
|
||||||
|
fmt,
|
||||||
|
"Decompression returned different amount of bytes than expected: got {}, expected {}.",
|
||||||
|
actual_bytes, required_bytes
|
||||||
|
)
|
||||||
|
}
|
||||||
|
InconsistentStripSamples {
|
||||||
|
actual_samples,
|
||||||
|
required_samples,
|
||||||
|
} => {
|
||||||
|
write!(
|
||||||
|
fmt,
|
||||||
|
"Inconsistent elements in strip: got {}, expected {}.",
|
||||||
|
actual_samples, required_samples
|
||||||
|
)
|
||||||
|
}
|
||||||
InvalidTag => write!(fmt, "Image contains invalid tag."),
|
InvalidTag => write!(fmt, "Image contains invalid tag."),
|
||||||
InvalidTagValueType(ref tag) => {
|
InvalidTagValueType(ref tag) => {
|
||||||
write!(fmt, "Tag `{:?}` did not have the expected value type.", tag)
|
write!(fmt, "Tag `{:?}` did not have the expected value type.", tag)
|
||||||
@@ -79,7 +111,6 @@ impl fmt::Display for TiffFormatError {
|
|||||||
SignedIntegerExpected(ref val) => {
|
SignedIntegerExpected(ref val) => {
|
||||||
write!(fmt, "Expected signed integer, {:?} found.", val)
|
write!(fmt, "Expected signed integer, {:?} found.", val)
|
||||||
}
|
}
|
||||||
InflateError(_) => write!(fmt, "Failed to decode inflate data."),
|
|
||||||
Format(ref val) => write!(fmt, "Invalid format: {:?}.", val),
|
Format(ref val) => write!(fmt, "Invalid format: {:?}.", val),
|
||||||
RequiredTagEmpty(ref val) => write!(fmt, "Required tag {:?} was empty.", val),
|
RequiredTagEmpty(ref val) => write!(fmt, "Required tag {:?} was empty.", val),
|
||||||
__NonExhaustive => unreachable!(),
|
__NonExhaustive => unreachable!(),
|
||||||
@@ -87,24 +118,6 @@ impl fmt::Display for TiffFormatError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decompression failed due to faulty compressed data.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct InflateError {
|
|
||||||
status: TINFLStatus,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InflateError {
|
|
||||||
pub(crate) fn new(status: TINFLStatus) -> Self {
|
|
||||||
Self { status }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffError {
|
|
||||||
pub(crate) fn from_inflate_status(status: TINFLStatus) -> Self {
|
|
||||||
TiffError::FormatError(TiffFormatError::InflateError(InflateError::new(status)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The Decoder does not support features required by the image.
|
/// The Decoder does not support features required by the image.
|
||||||
///
|
///
|
||||||
/// This only captures known failures for which the standard either does not require support or an
|
/// This only captures known failures for which the standard either does not require support or an
|
||||||
@@ -174,6 +187,29 @@ impl fmt::Display for TiffUnsupportedError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// User attempted to use the Decoder in a way that is incompatible with a specific image.
|
||||||
|
///
|
||||||
|
/// For example: attempting to read a tile from a stripped image.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum UsageError {
|
||||||
|
InvalidChunkType(ChunkType, ChunkType),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for UsageError {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
use self::UsageError::*;
|
||||||
|
match *self {
|
||||||
|
InvalidChunkType(expected, actual) => {
|
||||||
|
write!(
|
||||||
|
fmt,
|
||||||
|
"Requested operation is only valid for images with chunk encoding of type: {:?}, got {:?}.",
|
||||||
|
expected, actual
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for TiffError {
|
impl fmt::Display for TiffError {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
@@ -187,6 +223,7 @@ impl fmt::Display for TiffError {
|
|||||||
TiffError::IoError(ref e) => e.fmt(fmt),
|
TiffError::IoError(ref e) => e.fmt(fmt),
|
||||||
TiffError::LimitsExceeded => write!(fmt, "The Decoder limits are exceeded"),
|
TiffError::LimitsExceeded => write!(fmt, "The Decoder limits are exceeded"),
|
||||||
TiffError::IntSizeError => write!(fmt, "Platform or format size limits exceeded"),
|
TiffError::IntSizeError => write!(fmt, "Platform or format size limits exceeded"),
|
||||||
|
TiffError::UsageError(ref e) => write!(fmt, "Usage error: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,6 +236,7 @@ impl Error for TiffError {
|
|||||||
TiffError::IoError(..) => "IO error",
|
TiffError::IoError(..) => "IO error",
|
||||||
TiffError::LimitsExceeded => "Decoder limits exceeded",
|
TiffError::LimitsExceeded => "Decoder limits exceeded",
|
||||||
TiffError::IntSizeError => "Platform or format size limits exceeded",
|
TiffError::IntSizeError => "Platform or format size limits exceeded",
|
||||||
|
TiffError::UsageError(..) => "Invalid usage",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,6 +254,12 @@ impl From<io::Error> for TiffError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<str::Utf8Error> for TiffError {
|
||||||
|
fn from(_err: str::Utf8Error) -> TiffError {
|
||||||
|
TiffError::FormatError(TiffFormatError::InvalidTag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<string::FromUtf8Error> for TiffError {
|
impl From<string::FromUtf8Error> for TiffError {
|
||||||
fn from(_err: string::FromUtf8Error) -> TiffError {
|
fn from(_err: string::FromUtf8Error) -> TiffError {
|
||||||
TiffError::FormatError(TiffFormatError::InvalidTag)
|
TiffError::FormatError(TiffFormatError::InvalidTag)
|
||||||
|
|||||||
+1
-2
@@ -7,7 +7,6 @@
|
|||||||
//! * <https://www.adobe.io/open/standards/TIFF.html> - The TIFF specification
|
//! * <https://www.adobe.io/open/standards/TIFF.html> - The TIFF specification
|
||||||
|
|
||||||
extern crate jpeg;
|
extern crate jpeg;
|
||||||
extern crate miniz_oxide;
|
|
||||||
extern crate weezl;
|
extern crate weezl;
|
||||||
|
|
||||||
mod bytecast;
|
mod bytecast;
|
||||||
@@ -16,7 +15,7 @@ pub mod encoder;
|
|||||||
mod error;
|
mod error;
|
||||||
pub mod tags;
|
pub mod tags;
|
||||||
|
|
||||||
pub use self::error::{InflateError, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
pub use self::error::{TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError};
|
||||||
|
|
||||||
/// An enumeration over supported color types and their bit depths
|
/// An enumeration over supported color types and their bit depths
|
||||||
#[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)]
|
#[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)]
|
||||||
|
|||||||
+31
-2
@@ -111,35 +111,64 @@ pub enum Tag(u16) unknown("A private or extension tag") {
|
|||||||
Threshholding = 263, // TODO add support
|
Threshholding = 263, // TODO add support
|
||||||
XResolution = 282,
|
XResolution = 282,
|
||||||
YResolution = 283,
|
YResolution = 283,
|
||||||
// JPEG
|
|
||||||
JPEGTables = 347,
|
|
||||||
// Advanced tags
|
// Advanced tags
|
||||||
Predictor = 317,
|
Predictor = 317,
|
||||||
TileWidth = 322,
|
TileWidth = 322,
|
||||||
TileLength = 323,
|
TileLength = 323,
|
||||||
TileOffsets = 324,
|
TileOffsets = 324,
|
||||||
TileByteCounts = 325,
|
TileByteCounts = 325,
|
||||||
|
// Data Sample Format
|
||||||
SampleFormat = 339,
|
SampleFormat = 339,
|
||||||
|
SMinSampleValue = 340, // TODO add support
|
||||||
|
SMaxSampleValue = 341, // TODO add support
|
||||||
|
// JPEG
|
||||||
|
JPEGTables = 347,
|
||||||
|
// GeoTIFF
|
||||||
|
ModelPixelScaleTag = 33550, // (SoftDesk)
|
||||||
|
ModelTransformationTag = 34264, // (JPL Carto Group)
|
||||||
|
ModelTiepointTag = 33922, // (Intergraph)
|
||||||
|
GeoKeyDirectoryTag = 34735, // (SPOT)
|
||||||
|
GeoDoubleParamsTag = 34736, // (SPOT)
|
||||||
|
GeoAsciiParamsTag = 34737, // (SPOT)
|
||||||
|
GdalNodata = 42113, // Contains areas with missing data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tags! {
|
tags! {
|
||||||
/// The type of an IFD entry (a 2 byte field).
|
/// The type of an IFD entry (a 2 byte field).
|
||||||
pub enum Type(u16) {
|
pub enum Type(u16) {
|
||||||
|
/// 8-bit unsigned integer
|
||||||
BYTE = 1,
|
BYTE = 1,
|
||||||
|
/// 8-bit byte that contains a 7-bit ASCII code; the last byte must be zero
|
||||||
ASCII = 2,
|
ASCII = 2,
|
||||||
|
/// 16-bit unsigned integer
|
||||||
SHORT = 3,
|
SHORT = 3,
|
||||||
|
/// 32-bit unsigned integer
|
||||||
LONG = 4,
|
LONG = 4,
|
||||||
|
/// Fraction stored as two 32-bit unsigned integers
|
||||||
RATIONAL = 5,
|
RATIONAL = 5,
|
||||||
|
/// 8-bit signed integer
|
||||||
SBYTE = 6,
|
SBYTE = 6,
|
||||||
|
/// 8-bit byte that may contain anything, depending on the field
|
||||||
UNDEFINED = 7,
|
UNDEFINED = 7,
|
||||||
|
/// 16-bit signed integer
|
||||||
SSHORT = 8,
|
SSHORT = 8,
|
||||||
|
/// 32-bit signed integer
|
||||||
SLONG = 9,
|
SLONG = 9,
|
||||||
|
/// Fraction stored as two 32-bit signed integers
|
||||||
SRATIONAL = 10,
|
SRATIONAL = 10,
|
||||||
|
/// 32-bit IEEE floating point
|
||||||
FLOAT = 11,
|
FLOAT = 11,
|
||||||
|
/// 64-bit IEEE floating point
|
||||||
DOUBLE = 12,
|
DOUBLE = 12,
|
||||||
|
/// 32-bit unsigned integer (offset)
|
||||||
|
IFD = 13,
|
||||||
/// BigTIFF 64-bit unsigned integer
|
/// BigTIFF 64-bit unsigned integer
|
||||||
LONG8 = 16,
|
LONG8 = 16,
|
||||||
|
/// BigTIFF 64-bit signed integer
|
||||||
|
SLONG8 = 17,
|
||||||
|
/// BigTIFF 64-bit unsigned integer (offset)
|
||||||
|
IFD8 = 18,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ macro_rules! test_image_sum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test_image_sum!(test_image_sum_u8, U8, u64);
|
test_image_sum!(test_image_sum_u8, U8, u64);
|
||||||
|
test_image_sum!(test_image_sum_i8, I8, i64);
|
||||||
test_image_sum!(test_image_sum_u16, U16, u64);
|
test_image_sum!(test_image_sum_u16, U16, u64);
|
||||||
|
test_image_sum!(test_image_sum_i16, I16, i64);
|
||||||
test_image_sum!(test_image_sum_u32, U32, u64);
|
test_image_sum!(test_image_sum_u32, U32, u64);
|
||||||
test_image_sum!(test_image_sum_u64, U64, u64);
|
test_image_sum!(test_image_sum_u64, U64, u64);
|
||||||
test_image_sum!(test_image_sum_f32, F32, f32);
|
test_image_sum!(test_image_sum_f32, F32, f32);
|
||||||
@@ -135,6 +137,26 @@ fn test_rgb_f32() {
|
|||||||
test_image_sum_f32("gradient-3c-32b-float.tiff", ColorType::RGB(32), 472.8405);
|
test_image_sum_f32("gradient-3c-32b-float.tiff", ColorType::RGB(32), 472.8405);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_int8() {
|
||||||
|
test_image_sum_i8("int8.tif", ColorType::Gray(8), 3111)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_int8_rgb() {
|
||||||
|
test_image_sum_i8("int8_rgb.tif", ColorType::RGB(8), -10344)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_int16() {
|
||||||
|
test_image_sum_i16("int16.tif", ColorType::Gray(16), 354396);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_int16_rgb() {
|
||||||
|
test_image_sum_i16("int16_rgb.tif", ColorType::RGB(16), 1063188);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_string_tags() {
|
fn test_string_tags() {
|
||||||
// these files have null-terminated strings for their Software tag. One has extra bytes after
|
// these files have null-terminated strings for their Software tag. One has extra bytes after
|
||||||
@@ -193,3 +215,72 @@ fn issue_69() {
|
|||||||
//let img_res = decoder.read_image();
|
//let img_res = decoder.read_image();
|
||||||
//assert!(img_res.is_ok());
|
//assert!(img_res.is_ok());
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tiled_rgb_u8() {
|
||||||
|
test_image_sum_u8("tiled-rgb-u8.tif", ColorType::RGB(8), 39528948);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tiled_rect_rgb_u8() {
|
||||||
|
test_image_sum_u8("tiled-rect-rgb-u8.tif", ColorType::RGB(8), 62081032);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #[test]
|
||||||
|
fn test_tiled_jpeg_rgb_u8() {
|
||||||
|
test_image_sum_u8("tiled-jpeg-rgb-u8.tif", ColorType::RGB(8), 93031606);
|
||||||
|
} */
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tiled_oversize_gray_i8() {
|
||||||
|
test_image_sum_i8("tiled-oversize-gray-i8.tif", ColorType::Gray(8), 1214996);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tiled_cmyk_i8() {
|
||||||
|
test_image_sum_i8("tiled-cmyk-i8.tif", ColorType::CMYK(8), 1759101);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tiled_incremental() {
|
||||||
|
let file = "tiled-rgb-u8.tif";
|
||||||
|
let expected_type = ColorType::RGB(8);
|
||||||
|
let sums = [
|
||||||
|
188760, 195639, 108148, 81986, 665088, 366140, 705317, 423366, 172033, 324455, 244102,
|
||||||
|
81853, 181258, 247971, 129486, 55600, 565625, 422102, 730888, 379271, 232142, 292549,
|
||||||
|
244045, 86866, 188141, 115036, 150785, 84389, 353170, 459325, 719619, 329594, 278663,
|
||||||
|
220474, 243048, 113563, 189152, 109684, 179391, 122188, 279651, 622093, 724682, 302459,
|
||||||
|
268428, 204499, 224255, 124674, 170668, 121868, 192768, 183367, 378029, 585651, 657712,
|
||||||
|
296790, 241444, 197083, 198429, 134869, 182318, 86034, 203655, 182338, 297255, 601284,
|
||||||
|
633813, 242531, 228578, 206441, 193552, 125412, 181527, 165439, 202531, 159538, 268388,
|
||||||
|
565790, 611382, 272967, 236497, 215154, 158881, 90806, 106114, 182342, 191824, 186138,
|
||||||
|
215174, 393193, 701228, 198866, 227944, 193830, 166330, 49008, 55719, 122820, 197316,
|
||||||
|
161969, 203152, 170986, 624427, 188605, 186187, 111064, 115192, 39538, 48626, 163929,
|
||||||
|
144682, 135796, 194141, 154198, 584125, 180255, 153524, 121433, 132641, 35743, 47798,
|
||||||
|
152343, 162874, 167664, 160175, 133038, 659882, 138339, 166470, 124173, 118929, 51317,
|
||||||
|
45267, 155776, 161331, 161006, 130052, 137618, 337291, 106481, 161999, 127343, 87724,
|
||||||
|
59540, 63907, 155677, 140668, 141523, 108061, 168657, 186482, 98599, 147614, 139963, 90444,
|
||||||
|
56602, 92547, 125644, 134212, 126569, 144153, 179800, 174516, 133969, 129399, 117681,
|
||||||
|
83305, 55075, 110737, 115108, 128572, 128911, 130922, 179986, 143288, 145884, 155856,
|
||||||
|
96683, 94057, 56238, 79649, 71651, 70182, 75010, 77009, 98855, 78979, 74341, 83482, 53403,
|
||||||
|
59842, 30305,
|
||||||
|
];
|
||||||
|
|
||||||
|
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 tiles = decoder.tile_count().unwrap();
|
||||||
|
assert_eq!(tiles as usize, sums.len());
|
||||||
|
|
||||||
|
for tile in 0..tiles {
|
||||||
|
match decoder.read_tile().unwrap() {
|
||||||
|
DecodingResult::U8(res) => {
|
||||||
|
let sum: u64 = res.into_iter().map(<u64>::from).sum();
|
||||||
|
assert_eq!(sum, sums[tile as usize]);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong bit depth"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+164
-5
@@ -1,7 +1,7 @@
|
|||||||
extern crate tiff;
|
extern crate tiff;
|
||||||
|
|
||||||
use tiff::decoder::{ifd, Decoder, DecodingResult};
|
use tiff::decoder::{ifd, Decoder, DecodingResult};
|
||||||
use tiff::encoder::{colortype, SRational, TiffEncoder};
|
use tiff::encoder::{colortype, Ifd, Ifd8, SRational, TiffEncoder};
|
||||||
use tiff::tags::Tag;
|
use tiff::tags::Tag;
|
||||||
use tiff::ColorType;
|
use tiff::ColorType;
|
||||||
|
|
||||||
@@ -48,6 +48,92 @@ fn encode_decode() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn encode_decode_big() {
|
||||||
|
let mut image_data = Vec::new();
|
||||||
|
for x in 0..100 {
|
||||||
|
for y in 0..100u8 {
|
||||||
|
let val = x + y;
|
||||||
|
image_data.push(val);
|
||||||
|
image_data.push(val);
|
||||||
|
image_data.push(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut file = Cursor::new(Vec::new());
|
||||||
|
{
|
||||||
|
let mut tiff = TiffEncoder::new_big(&mut file).unwrap();
|
||||||
|
|
||||||
|
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 {
|
||||||
|
panic!("Wrong data type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode_ifd() {
|
||||||
|
let mut data = Cursor::new(Vec::new());
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut tiff = TiffEncoder::new(&mut data).unwrap();
|
||||||
|
let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap();
|
||||||
|
let encoder = image_encoder.encoder();
|
||||||
|
|
||||||
|
// Use the "reusable" tags section as per the TIFF6 spec
|
||||||
|
encoder.write_tag(Tag::Unknown(65000), Ifd(42u32)).unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65001), &[Ifd(100u32)][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65002), &[Ifd(1u32), Ifd(2u32), Ifd(3u32)][..])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
encoder.write_tag(Tag::Unknown(65010), Ifd8(43u64)).unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65011), &[Ifd8(100u64)][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(
|
||||||
|
Tag::Unknown(65012),
|
||||||
|
&[Ifd8(1u64), Ifd8(2u64), Ifd8(3u64)][..],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rewind the cursor for reading
|
||||||
|
data.set_position(0);
|
||||||
|
{
|
||||||
|
let mut decoder = Decoder::new(&mut data).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(decoder.assert_tag_u32(65000), 42);
|
||||||
|
assert_eq!(decoder.assert_tag_u32_vec(65000), [42]);
|
||||||
|
assert_eq!(decoder.assert_tag_u32_vec(65001), [100]);
|
||||||
|
assert_eq!(decoder.assert_tag_u32_vec(65002), [1, 2, 3]);
|
||||||
|
|
||||||
|
assert_eq!(decoder.assert_tag_u64(65010), 43);
|
||||||
|
assert_eq!(decoder.assert_tag_u64_vec(65010), [43]);
|
||||||
|
assert_eq!(decoder.assert_tag_u64_vec(65011), [100]);
|
||||||
|
assert_eq!(decoder.assert_tag_u64_vec(65012), [1, 2, 3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
/// Test that attempting to encode when the input buffer is undersized returns
|
/// Test that attempting to encode when the input buffer is undersized returns
|
||||||
/// an error rather than panicking.
|
/// an error rather than panicking.
|
||||||
@@ -182,6 +268,10 @@ trait AssertDecode {
|
|||||||
fn assert_tag_u32_vec(&mut self, tag: u16) -> Vec<u32>;
|
fn assert_tag_u32_vec(&mut self, tag: u16) -> Vec<u32>;
|
||||||
fn assert_tag_i32(&mut self, tag: u16) -> i32;
|
fn assert_tag_i32(&mut self, tag: u16) -> i32;
|
||||||
fn assert_tag_i32_vec(&mut self, tag: u16) -> Vec<i32>;
|
fn assert_tag_i32_vec(&mut self, tag: u16) -> Vec<i32>;
|
||||||
|
fn assert_tag_u64(&mut self, tag: u16) -> u64;
|
||||||
|
fn assert_tag_u64_vec(&mut self, tag: u16) -> Vec<u64>;
|
||||||
|
fn assert_tag_i64(&mut self, tag: u16) -> i64;
|
||||||
|
fn assert_tag_i64_vec(&mut self, tag: u16) -> Vec<i64>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: std::io::Read + std::io::Seek> AssertDecode for Decoder<R> {
|
impl<R: std::io::Read + std::io::Seek> AssertDecode for Decoder<R> {
|
||||||
@@ -203,6 +293,24 @@ impl<R: std::io::Read + std::io::Seek> AssertDecode for Decoder<R> {
|
|||||||
.into_i32_vec()
|
.into_i32_vec()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
fn assert_tag_u64(&mut self, tag: u16) -> u64 {
|
||||||
|
self.get_tag(Tag::Unknown(tag)).unwrap().into_u64().unwrap()
|
||||||
|
}
|
||||||
|
fn assert_tag_u64_vec(&mut self, tag: u16) -> Vec<u64> {
|
||||||
|
self.get_tag(Tag::Unknown(tag))
|
||||||
|
.unwrap()
|
||||||
|
.into_u64_vec()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
fn assert_tag_i64(&mut self, tag: u16) -> i64 {
|
||||||
|
self.get_tag(Tag::Unknown(tag)).unwrap().into_i64().unwrap()
|
||||||
|
}
|
||||||
|
fn assert_tag_i64_vec(&mut self, tag: u16) -> Vec<i64> {
|
||||||
|
self.get_tag(Tag::Unknown(tag))
|
||||||
|
.unwrap()
|
||||||
|
.into_i64_vec()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -289,12 +397,20 @@ fn test_signed() {
|
|||||||
.write_tag(Tag::Unknown(65022), &[-1_i32, 2][..])
|
.write_tag(Tag::Unknown(65022), &[-1_i32, 2][..])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
encoder.write_tag(Tag::Unknown(65030), -1_i64).unwrap();
|
||||||
encoder
|
encoder
|
||||||
.write_tag(Tag::Unknown(65030), make_srational(-1))
|
.write_tag(Tag::Unknown(65031), &[-1_i64][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65032), &[-1_i64, 2][..])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65040), make_srational(-1))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
encoder
|
encoder
|
||||||
.write_tag(
|
.write_tag(
|
||||||
Tag::Unknown(65031),
|
Tag::Unknown(65041),
|
||||||
&[make_srational(-1), make_srational(2)][..],
|
&[make_srational(-1), make_srational(2)][..],
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -321,8 +437,12 @@ fn test_signed() {
|
|||||||
assert_eq!(decoder.assert_tag_i32_vec(65021), [-1]);
|
assert_eq!(decoder.assert_tag_i32_vec(65021), [-1]);
|
||||||
assert_eq!(decoder.assert_tag_i32_vec(65022), [-1, 2]);
|
assert_eq!(decoder.assert_tag_i32_vec(65022), [-1, 2]);
|
||||||
|
|
||||||
assert_eq!(decoder.assert_tag_i32_vec(65030), [-1, 100]);
|
assert_eq!(decoder.assert_tag_i64(65030), -1);
|
||||||
assert_eq!(decoder.assert_tag_i32_vec(65031), [-1_i32, 100, 2, 100]);
|
assert_eq!(decoder.assert_tag_i64_vec(65031), [-1]);
|
||||||
|
assert_eq!(decoder.assert_tag_i64_vec(65032), [-1, 2]);
|
||||||
|
|
||||||
|
assert_eq!(decoder.assert_tag_i32_vec(65040), [-1, 100]);
|
||||||
|
assert_eq!(decoder.assert_tag_i32_vec(65041), [-1_i32, 100, 2, 100]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,3 +480,42 @@ fn test_multipage_image() {
|
|||||||
assert_eq!(img_decoder.dimensions().unwrap(), (3, 3));
|
assert_eq!(img_decoder.dimensions().unwrap(), (3, 3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// verify rows per strip setting
|
||||||
|
fn test_rows_per_strip() {
|
||||||
|
let mut file = Cursor::new(Vec::new());
|
||||||
|
{
|
||||||
|
let mut img_encoder = TiffEncoder::new(&mut file).unwrap();
|
||||||
|
|
||||||
|
let mut image = img_encoder.new_image::<colortype::Gray8>(100, 100).unwrap();
|
||||||
|
assert_eq!(image.next_strip_sample_count(), 100 * 100);
|
||||||
|
image.rows_per_strip(2).unwrap();
|
||||||
|
assert_eq!(image.next_strip_sample_count(), 2 * 100);
|
||||||
|
|
||||||
|
let img2: Vec<u8> = vec![0; 2 * 100];
|
||||||
|
image.write_strip(&img2[..]).unwrap();
|
||||||
|
assert!(image.rows_per_strip(5).is_err());
|
||||||
|
for i in 1..50 {
|
||||||
|
let img2: Vec<u8> = vec![i; 2 * 100];
|
||||||
|
image.write_strip(&img2[..]).unwrap();
|
||||||
|
}
|
||||||
|
assert!(image.write_strip(&img2[..]).is_err());
|
||||||
|
image.finish().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
file.seek(SeekFrom::Start(0)).unwrap();
|
||||||
|
{
|
||||||
|
let mut decoder = Decoder::new(&mut file).unwrap();
|
||||||
|
assert_eq!(decoder.get_tag_u64(Tag::RowsPerStrip).unwrap(), 2);
|
||||||
|
assert_eq!(decoder.strip_count().unwrap(), 50);
|
||||||
|
|
||||||
|
for i in 0..50 {
|
||||||
|
let img2 = [i; 2 * 100];
|
||||||
|
match decoder.read_strip().unwrap() {
|
||||||
|
DecodingResult::U8(data) => assert_eq!(&img2[..], &data[..]),
|
||||||
|
other => panic!("Incorrect strip type {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
BIN
Binary file not shown.
Executable
BIN
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.
Reference in new issue
Block a user