Compare commits
83
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51ec671e3a | ||
|
|
89aaab12dc | ||
|
|
481ef3781a | ||
|
|
369c602952 | ||
|
|
ed22865f04 | ||
|
|
459b4f3908 | ||
|
|
e72401d4ac | ||
|
|
6281360f10 | ||
|
|
d00ab0cc61 | ||
|
|
6353f71e28 | ||
|
|
7c4ee96aee | ||
|
|
cb5319c426 | ||
|
|
53aefea6a4 | ||
|
|
cb900819d9 | ||
|
|
35ea719d9e | ||
|
|
41050c14ed | ||
|
|
5fd0534ea2 | ||
|
|
72474b569e | ||
|
|
63f09b267a | ||
|
|
b455bd9f2b | ||
|
|
46918b395c | ||
|
|
a510b03e1c | ||
|
|
2cb57a4d04 | ||
|
|
da9bdac8a2 | ||
|
|
647de77b70 | ||
|
|
727d78a57c | ||
|
|
49da46bbd0 | ||
|
|
7b41c823e9 | ||
|
|
bf24b3f935 | ||
|
|
2b12c1b162 | ||
|
|
82c00a84d8 | ||
|
|
ec5b4b9a9e | ||
|
|
f4f11e6399 | ||
|
|
49b1f78ce5 | ||
|
|
e48691ca32 | ||
|
|
7ad6b0e70d | ||
|
|
7cc5b98d9c | ||
|
|
7928c219dd | ||
|
|
44c75a66ab | ||
|
|
d8bfa84d06 | ||
|
|
1492087426 | ||
|
|
23090a3513 | ||
|
|
200355d3bc | ||
|
|
469883aa79 | ||
|
|
99c038c929 | ||
|
|
f3eebee1e9 | ||
|
|
b853d841f3 | ||
|
|
418979f14b | ||
|
|
2635bdd7d5 | ||
|
|
ff874612ba | ||
|
|
ea217dc238 | ||
|
|
768e0d4af7 | ||
|
|
3b0da4d1ae | ||
|
|
54c3ebf358 | ||
|
|
5f48444fa0 | ||
|
|
b2da2f2333 | ||
|
|
ba1daa18b2 | ||
|
|
ba3f6e604a | ||
|
|
a471591f06 | ||
|
|
dacc2864e8 | ||
|
|
797e3dca07 | ||
|
|
23751bd8c5 | ||
|
|
7fb68dbbe7 | ||
|
|
4afd7ed462 | ||
|
|
aa49e7d6f0 | ||
|
|
cbb41fd479 | ||
|
|
9edc0e656e | ||
|
|
9f4873bae8 | ||
|
|
472cf6d07d | ||
|
|
ee73341bbf | ||
|
|
6c7cc7ec30 | ||
|
|
71f3aa8b15 | ||
|
|
0ec8e14910 | ||
|
|
d79d8c3353 | ||
|
|
ae4ae9c36e | ||
|
|
8d4bdce7aa | ||
|
|
a41e33e42a | ||
|
|
c2084835fa | ||
|
|
97c888f166 | ||
|
|
24d347ecf5 | ||
|
|
a439baa948 | ||
|
|
6e23274bea | ||
|
|
bbc49291e4 |
No files matched your search
@@ -0,0 +1,40 @@
|
|||||||
|
name: Rust CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
rust: ["1.34.2", stable, beta, nightly]
|
||||||
|
command: [build, test]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- run: rustup default ${{ matrix.rust }}
|
||||||
|
- name: build
|
||||||
|
run: >
|
||||||
|
cargo build --verbose --no-default-features --features "$FEATURES"
|
||||||
|
- name: test
|
||||||
|
run: >
|
||||||
|
cargo test --tests --benches --no-default-features --features "$FEATURES"
|
||||||
|
if: ${{ matrix.rust != '1.34.2' }}
|
||||||
|
env:
|
||||||
|
FEATURES: ${{ matrix.features }}
|
||||||
|
rustfmt:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
components: rustfmt
|
||||||
|
- name: Run rustfmt check
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: -- --check
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
language: rust
|
|
||||||
rust:
|
|
||||||
- 1.34.2
|
|
||||||
- stable
|
|
||||||
- beta
|
|
||||||
- nightly
|
|
||||||
+26
@@ -1,3 +1,29 @@
|
|||||||
|
# Version 0.6.0
|
||||||
|
|
||||||
|
New features:
|
||||||
|
* Support for decoding BigTIFF with 64-bit offsets
|
||||||
|
* The value types byte, `f32`, `f64` are now recognized
|
||||||
|
* Support for Modern JPEG encoded images
|
||||||
|
|
||||||
|
Improvements:
|
||||||
|
* Better support for adding auxiliary tags before encoding image data
|
||||||
|
* Switched to lzw decoder library `weezl` for performance
|
||||||
|
* The `ColorType` trait now supports `SAMPLE_ENCODING` hints
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
* Fixed decoding of inline ASCII in tags
|
||||||
|
* Fixed handling after null terminator in ASCII data
|
||||||
|
* Recognize tile and sample format tags
|
||||||
|
|
||||||
|
# Version 0.5.0
|
||||||
|
|
||||||
|
* Added support for 32-bit and 64-bit decoded values.
|
||||||
|
* Added CMYK(16|32|64) color type support.
|
||||||
|
* Check many internal integer conversions to increase stability. This should
|
||||||
|
only lead to images being reported as faulty that would previously silently
|
||||||
|
break platform limits. If there are any false positives, please report them.
|
||||||
|
* Remove an erroneous check of decoded length in lzw compressed images.
|
||||||
|
|
||||||
# Version 0.4.0
|
# Version 0.4.0
|
||||||
|
|
||||||
* Several enumerations are now non_exhaustive for future extensions.
|
* Several enumerations are now non_exhaustive for future extensions.
|
||||||
|
|||||||
+14
-4
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tiff"
|
name = "tiff"
|
||||||
version = "0.4.0"
|
version = "0.6.0"
|
||||||
authors = [
|
authors = [
|
||||||
"ccgn",
|
"ccgn",
|
||||||
"bvssvni <bvssvni@gmail.com>",
|
"bvssvni <bvssvni@gmail.com>",
|
||||||
@@ -17,6 +17,16 @@ repository = "https://github.com/image-rs/image-tiff"
|
|||||||
exclude = ["tests/images/*", "tests/fuzz_images/*"]
|
exclude = ["tests/images/*", "tests/fuzz_images/*"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.2"
|
weezl = "0.1.0"
|
||||||
lzw = "0.10"
|
jpeg = { package = "jpeg-decoder", version = "0.1.17", default-features = false }
|
||||||
miniz_oxide = "0.3"
|
|
||||||
|
[dependencies.miniz_oxide]
|
||||||
|
version = "0.4.1"
|
||||||
|
features = ["no_extern_crate_alloc"]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
criterion = "0.3.1"
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "lzw"
|
||||||
|
harness = false
|
||||||
@@ -10,6 +10,7 @@ TIFF decoding and encoding library in pure Rust
|
|||||||
### Features
|
### Features
|
||||||
- Baseline spec (other than formats and tags listed below as not supported)
|
- Baseline spec (other than formats and tags listed below as not supported)
|
||||||
- Multipage
|
- Multipage
|
||||||
|
- BigTIFF
|
||||||
- Incremental decoding
|
- Incremental decoding
|
||||||
|
|
||||||
### Formats
|
### Formats
|
||||||
@@ -17,12 +18,12 @@ This table lists photometric interpretations and sample formats which are suppor
|
|||||||
|
|
||||||
| `PhotometricInterpretation` | UINT Format | IEEEFP Format |
|
| `PhotometricInterpretation` | UINT Format | IEEEFP Format |
|
||||||
|-|-|-|
|
|-|-|-|
|
||||||
| `WhiteIsZero` | Gray(8\|16\|32\|64) | |
|
| `WhiteIsZero` | Gray(8\|16\|32\|64) | Gray(32\|64) |
|
||||||
| `BlackIsZero` | Gray(8\|16\|32\|64) | |
|
| `BlackIsZero` | Gray(8\|16\|32\|64) | Gray(32\|64) |
|
||||||
| `RGB` | RGB(8\|16\|32\|64), RGBA(8\|16\|32\|64) | |
|
| `RGB` | RGB(8\|16\|32\|64), RGBA(8\|16\|32\|64) | RGB(32\|64), RGBA(32\|64) |
|
||||||
| `RGBPalette` | | |
|
| `RGBPalette` | | |
|
||||||
| `Mask` | | |
|
| `Mask` | | |
|
||||||
| `CMYK` | CMYK(8\|16\|32\|64) | |
|
| `CMYK` | CMYK(8\|16\|32\|64) | CMYK(32\|64) |
|
||||||
| `YCbCr` | | |
|
| `YCbCr` | | |
|
||||||
| `CIELab` | | |
|
| `CIELab` | | |
|
||||||
|
|
||||||
@@ -43,4 +44,3 @@ Formats and interpretations not listed above or with empty entries are unsupport
|
|||||||
- Baseline tags
|
- Baseline tags
|
||||||
- `ExtraSamples`
|
- `ExtraSamples`
|
||||||
- Extension tags
|
- Extension tags
|
||||||
- BigTIFF
|
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
extern crate criterion;
|
||||||
|
extern crate tiff;
|
||||||
|
|
||||||
|
use criterion::{
|
||||||
|
black_box, measurement::Measurement, BenchmarkGroup, BenchmarkId, Criterion, Throughput,
|
||||||
|
};
|
||||||
|
use tiff::decoder::Decoder;
|
||||||
|
|
||||||
|
fn read_image(image: &[u8]) {
|
||||||
|
let image = std::io::Cursor::new(image);
|
||||||
|
let decoder = Decoder::new(black_box(image));
|
||||||
|
let mut reader = decoder.unwrap();
|
||||||
|
|
||||||
|
while {
|
||||||
|
reader.read_image().unwrap();
|
||||||
|
reader.more_images()
|
||||||
|
} {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
struct BenchDef {
|
||||||
|
data: &'static [u8],
|
||||||
|
id: &'static str,
|
||||||
|
sample_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_bench_def<M: Measurement>(group: &mut BenchmarkGroup<M>, def: BenchDef) {
|
||||||
|
group
|
||||||
|
.sample_size(def.sample_size)
|
||||||
|
.throughput(Throughput::Bytes(def.data.len() as u64))
|
||||||
|
.bench_with_input(
|
||||||
|
BenchmarkId::new(def.id, def.data.len()),
|
||||||
|
def.data,
|
||||||
|
|b, input| b.iter(|| read_image(input)),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut c = Criterion::default().configure_from_args();
|
||||||
|
let mut group = c.benchmark_group("tiff-lzw");
|
||||||
|
|
||||||
|
macro_rules! data_file {
|
||||||
|
($name:literal) => {
|
||||||
|
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), $name))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
run_bench_def(
|
||||||
|
&mut group,
|
||||||
|
BenchDef {
|
||||||
|
data: data_file!("/tests/images/issue_69_lzw.tiff"),
|
||||||
|
id: "issue-69-lzw.tif",
|
||||||
|
sample_size: 500,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
run_bench_def(
|
||||||
|
&mut group,
|
||||||
|
BenchDef {
|
||||||
|
data: data_file!("/tests/benches/kodim02-lzw.tif"),
|
||||||
|
id: "kodim02-lzw.tif",
|
||||||
|
sample_size: 20,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
run_bench_def(
|
||||||
|
&mut group,
|
||||||
|
BenchDef {
|
||||||
|
data: data_file!("/tests/benches/kodim07-lzw.tif"),
|
||||||
|
id: "kodim07-lzw.tif",
|
||||||
|
sample_size: 20,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
run_bench_def(
|
||||||
|
&mut group,
|
||||||
|
BenchDef {
|
||||||
|
data: data_file!("/tests/benches/Transparency-lzw.tif"),
|
||||||
|
id: "Transparency-lzw.tif",
|
||||||
|
sample_size: 20,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
//! Trivial, internal byte transmutation.
|
||||||
|
//!
|
||||||
|
//! A dependency like bytemuck would give us extra assurance of the safety but overall would not
|
||||||
|
//! reduce the amount of total unsafety. We don't use it in the interface where the traits would
|
||||||
|
//! really become useful.
|
||||||
|
//!
|
||||||
|
//! SAFETY: These are benign casts as we apply them to fixed size integer types only. All of them
|
||||||
|
//! are naturally aligned, valid for all bit patterns and their alignment is surely at most their
|
||||||
|
//! size (we assert the latter fact since it is 'implementation defined' if following the letter of
|
||||||
|
//! the unsafe code guidelines).
|
||||||
|
//!
|
||||||
|
//! TODO: Would like to use std-lib here.
|
||||||
|
use std::{mem, slice};
|
||||||
|
|
||||||
|
macro_rules! integral_slice_as_bytes{($int:ty, $const:ident $(,$mut:ident)*) => {
|
||||||
|
pub(crate) fn $const(slice: &[$int]) -> &[u8] {
|
||||||
|
assert!(mem::align_of::<$int>() <= mem::size_of::<$int>());
|
||||||
|
unsafe { slice::from_raw_parts(slice.as_ptr() as *const u8, mem::size_of_val(slice)) }
|
||||||
|
}
|
||||||
|
$(pub(crate) fn $mut(slice: &mut [$int]) -> &mut [u8] {
|
||||||
|
assert!(mem::align_of::<$int>() <= mem::size_of::<$int>());
|
||||||
|
unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut u8, mem::size_of_val(slice)) }
|
||||||
|
})*
|
||||||
|
}}
|
||||||
|
|
||||||
|
integral_slice_as_bytes!(i8, i8_as_ne_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!(u32, u32_as_ne_bytes, u32_as_ne_mut_bytes);
|
||||||
|
integral_slice_as_bytes!(i32, i32_as_ne_bytes);
|
||||||
|
integral_slice_as_bytes!(u64, u64_as_ne_bytes, u64_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);
|
||||||
+300
-36
@@ -1,7 +1,7 @@
|
|||||||
//! Function for reading TIFF tags
|
//! Function for reading TIFF tags
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::{TryFrom, TryInto};
|
||||||
use std::io::{self, Read, Seek};
|
use std::io::{self, Read, Seek};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
@@ -9,26 +9,43 @@ use super::stream::{ByteOrder, EndianReader, SmartReader};
|
|||||||
use tags::{Tag, Type};
|
use tags::{Tag, Type};
|
||||||
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
||||||
|
|
||||||
use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational};
|
use self::Value::{
|
||||||
|
Ascii, Byte, Double, Float, List, Rational, RationalBig, SRational, SRationalBig, Signed,
|
||||||
|
SignedBig, Unsigned, UnsignedBig,
|
||||||
|
};
|
||||||
|
|
||||||
#[allow(unused_qualifications)]
|
#[allow(unused_qualifications)]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
|
Byte(u8),
|
||||||
Signed(i32),
|
Signed(i32),
|
||||||
|
SignedBig(i64),
|
||||||
Unsigned(u32),
|
Unsigned(u32),
|
||||||
|
UnsignedBig(u64),
|
||||||
|
Float(f32),
|
||||||
|
Double(f64),
|
||||||
List(Vec<Value>),
|
List(Vec<Value>),
|
||||||
Rational(u32, u32),
|
Rational(u32, u32),
|
||||||
|
RationalBig(u64, u64),
|
||||||
SRational(i32, i32),
|
SRational(i32, i32),
|
||||||
|
SRationalBig(i64, i64),
|
||||||
Ascii(String),
|
Ascii(String),
|
||||||
#[doc(hidden)] // Do not match against this.
|
#[doc(hidden)] // Do not match against this.
|
||||||
__NonExhaustive,
|
__NonExhaustive,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
|
pub fn into_u8(self) -> TiffResult<u8> {
|
||||||
|
match self {
|
||||||
|
Byte(val) => Ok(val),
|
||||||
|
val => Err(TiffError::FormatError(TiffFormatError::ByteExpected(val))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_u32(self) -> TiffResult<u32> {
|
pub fn into_u32(self) -> TiffResult<u32> {
|
||||||
match self {
|
match self {
|
||||||
Unsigned(val) => Ok(val),
|
Unsigned(val) => Ok(val),
|
||||||
|
UnsignedBig(val) => Ok(u32::try_from(val)?),
|
||||||
val => Err(TiffError::FormatError(
|
val => Err(TiffError::FormatError(
|
||||||
TiffFormatError::UnsignedIntegerExpected(val),
|
TiffFormatError::UnsignedIntegerExpected(val),
|
||||||
)),
|
)),
|
||||||
@@ -38,6 +55,45 @@ impl Value {
|
|||||||
pub fn into_i32(self) -> TiffResult<i32> {
|
pub fn into_i32(self) -> TiffResult<i32> {
|
||||||
match self {
|
match self {
|
||||||
Signed(val) => Ok(val),
|
Signed(val) => Ok(val),
|
||||||
|
SignedBig(val) => Ok(i32::try_from(val)?),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::SignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_u64(self) -> TiffResult<u64> {
|
||||||
|
match self {
|
||||||
|
Unsigned(val) => Ok(val.into()),
|
||||||
|
UnsignedBig(val) => Ok(val),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::UnsignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_i64(self) -> TiffResult<i64> {
|
||||||
|
match self {
|
||||||
|
Signed(val) => Ok(val.into()),
|
||||||
|
SignedBig(val) => Ok(val),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::SignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_f32(self) -> TiffResult<f32> {
|
||||||
|
match self {
|
||||||
|
Float(val) => Ok(val),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::SignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_f64(self) -> TiffResult<f64> {
|
||||||
|
match self {
|
||||||
|
Double(val) => Ok(val),
|
||||||
val => Err(TiffError::FormatError(
|
val => Err(TiffError::FormatError(
|
||||||
TiffFormatError::SignedIntegerExpected(val),
|
TiffFormatError::SignedIntegerExpected(val),
|
||||||
)),
|
)),
|
||||||
@@ -54,7 +110,11 @@ impl Value {
|
|||||||
Ok(new_vec)
|
Ok(new_vec)
|
||||||
}
|
}
|
||||||
Unsigned(val) => Ok(vec![val]),
|
Unsigned(val) => Ok(vec![val]),
|
||||||
|
UnsignedBig(val) => Ok(vec![u32::try_from(val)?]),
|
||||||
Rational(numerator, denominator) => Ok(vec![numerator, denominator]),
|
Rational(numerator, denominator) => Ok(vec![numerator, denominator]),
|
||||||
|
RationalBig(numerator, denominator) => {
|
||||||
|
Ok(vec![u32::try_from(numerator)?, u32::try_from(denominator)?])
|
||||||
|
}
|
||||||
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),
|
||||||
@@ -62,6 +122,23 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_u8_vec(self) -> TiffResult<Vec<u8>> {
|
||||||
|
match self {
|
||||||
|
List(vec) => {
|
||||||
|
let mut new_vec = Vec::with_capacity(vec.len());
|
||||||
|
for v in vec {
|
||||||
|
new_vec.push(v.into_u8()?)
|
||||||
|
}
|
||||||
|
Ok(new_vec)
|
||||||
|
}
|
||||||
|
Byte(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) => {
|
||||||
@@ -72,13 +149,102 @@ impl Value {
|
|||||||
new_vec.push(numerator);
|
new_vec.push(numerator);
|
||||||
new_vec.push(denominator);
|
new_vec.push(denominator);
|
||||||
}
|
}
|
||||||
_ => new_vec.push(v.into_i32()?)
|
SRationalBig(numerator, denominator) => {
|
||||||
|
new_vec.push(i32::try_from(numerator)?);
|
||||||
|
new_vec.push(i32::try_from(denominator)?);
|
||||||
|
}
|
||||||
|
_ => new_vec.push(v.into_i32()?),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(new_vec)
|
Ok(new_vec)
|
||||||
}
|
}
|
||||||
Signed(val) => Ok(vec![val]),
|
Signed(val) => Ok(vec![val]),
|
||||||
|
SignedBig(val) => Ok(vec![i32::try_from(val)?]),
|
||||||
SRational(numerator, denominator) => Ok(vec![numerator, denominator]),
|
SRational(numerator, denominator) => Ok(vec![numerator, denominator]),
|
||||||
|
SRationalBig(numerator, denominator) => {
|
||||||
|
Ok(vec![i32::try_from(numerator)?, i32::try_from(denominator)?])
|
||||||
|
}
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::SignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_f32_vec(self) -> TiffResult<Vec<f32>> {
|
||||||
|
match self {
|
||||||
|
List(vec) => {
|
||||||
|
let mut new_vec = Vec::with_capacity(vec.len());
|
||||||
|
for v in vec {
|
||||||
|
new_vec.push(v.into_f32()?)
|
||||||
|
}
|
||||||
|
Ok(new_vec)
|
||||||
|
}
|
||||||
|
Float(val) => Ok(vec![val]),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::UnsignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_f64_vec(self) -> TiffResult<Vec<f64>> {
|
||||||
|
match self {
|
||||||
|
List(vec) => {
|
||||||
|
let mut new_vec = Vec::with_capacity(vec.len());
|
||||||
|
for v in vec {
|
||||||
|
new_vec.push(v.into_f64()?)
|
||||||
|
}
|
||||||
|
Ok(new_vec)
|
||||||
|
}
|
||||||
|
Double(val) => Ok(vec![val]),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::UnsignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_u64_vec(self) -> TiffResult<Vec<u64>> {
|
||||||
|
match self {
|
||||||
|
List(vec) => {
|
||||||
|
let mut new_vec = Vec::with_capacity(vec.len());
|
||||||
|
for v in vec {
|
||||||
|
new_vec.push(v.into_u64()?)
|
||||||
|
}
|
||||||
|
Ok(new_vec)
|
||||||
|
}
|
||||||
|
Unsigned(val) => Ok(vec![val.into()]),
|
||||||
|
UnsignedBig(val) => Ok(vec![val]),
|
||||||
|
Rational(numerator, denominator) => Ok(vec![numerator.into(), denominator.into()]),
|
||||||
|
RationalBig(numerator, denominator) => Ok(vec![numerator, denominator]),
|
||||||
|
Ascii(val) => Ok(val.chars().map(u32::from).map(u64::from).collect()),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::UnsignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_i64_vec(self) -> TiffResult<Vec<i64>> {
|
||||||
|
match self {
|
||||||
|
List(vec) => {
|
||||||
|
let mut new_vec = Vec::with_capacity(vec.len());
|
||||||
|
for v in vec {
|
||||||
|
match v {
|
||||||
|
SRational(numerator, denominator) => {
|
||||||
|
new_vec.push(numerator.into());
|
||||||
|
new_vec.push(denominator.into());
|
||||||
|
}
|
||||||
|
SRationalBig(numerator, denominator) => {
|
||||||
|
new_vec.push(numerator);
|
||||||
|
new_vec.push(denominator);
|
||||||
|
}
|
||||||
|
_ => new_vec.push(v.into_i64()?),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(new_vec)
|
||||||
|
}
|
||||||
|
Signed(val) => Ok(vec![val.into()]),
|
||||||
|
SignedBig(val) => Ok(vec![val]),
|
||||||
|
SRational(numerator, denominator) => Ok(vec![numerator.into(), denominator.into()]),
|
||||||
|
SRationalBig(numerator, denominator) => Ok(vec![numerator, denominator]),
|
||||||
val => Err(TiffError::FormatError(
|
val => Err(TiffError::FormatError(
|
||||||
TiffFormatError::SignedIntegerExpected(val),
|
TiffFormatError::SignedIntegerExpected(val),
|
||||||
)),
|
)),
|
||||||
@@ -89,8 +255,8 @@ impl Value {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Entry {
|
pub struct Entry {
|
||||||
type_: Type,
|
type_: Type,
|
||||||
count: u32,
|
count: u64,
|
||||||
offset: [u8; 4],
|
offset: [u8; 8],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::fmt::Debug for Entry {
|
impl ::std::fmt::Debug for Entry {
|
||||||
@@ -104,6 +270,12 @@ impl ::std::fmt::Debug for Entry {
|
|||||||
|
|
||||||
impl Entry {
|
impl Entry {
|
||||||
pub fn new(type_: Type, count: u32, offset: [u8; 4]) -> Entry {
|
pub fn new(type_: Type, count: u32, offset: [u8; 4]) -> Entry {
|
||||||
|
let mut offset = offset.to_vec();
|
||||||
|
offset.append(&mut vec![0; 4]);
|
||||||
|
Entry::new_u64(type_, count.into(), offset[..].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_u64(type_: Type, count: u64, offset: [u8; 8]) -> Entry {
|
||||||
Entry {
|
Entry {
|
||||||
type_,
|
type_,
|
||||||
count,
|
count,
|
||||||
@@ -122,6 +294,79 @@ impl Entry {
|
|||||||
decoder: &mut super::Decoder<R>,
|
decoder: &mut super::Decoder<R>,
|
||||||
) -> TiffResult<Value> {
|
) -> TiffResult<Value> {
|
||||||
let bo = decoder.byte_order();
|
let bo = decoder.byte_order();
|
||||||
|
let bigtiff_value = if decoder.bigtiff {
|
||||||
|
match (self.type_, self.count) {
|
||||||
|
(Type::BYTE, 5) => Some(offset_to_bytes(5, self)?),
|
||||||
|
(Type::BYTE, 6) => Some(offset_to_bytes(6, self)?),
|
||||||
|
(Type::BYTE, 7) => Some(offset_to_bytes(7, self)?),
|
||||||
|
(Type::BYTE, 8) => Some(offset_to_bytes(8, self)?),
|
||||||
|
(Type::SBYTE, 5) => Some(offset_to_sbytes(5, self)?),
|
||||||
|
(Type::SBYTE, 6) => Some(offset_to_sbytes(6, self)?),
|
||||||
|
(Type::SBYTE, 7) => Some(offset_to_sbytes(7, self)?),
|
||||||
|
(Type::SBYTE, 8) => Some(offset_to_sbytes(8, self)?),
|
||||||
|
(Type::SHORT, 3) => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
Some(List(vec![
|
||||||
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
(Type::SSHORT, 3) => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
Some(List(vec![
|
||||||
|
Signed(i32::from(r.read_i16()?)),
|
||||||
|
Signed(i32::from(r.read_i16()?)),
|
||||||
|
Signed(i32::from(r.read_i16()?)),
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
(Type::SHORT, 4) => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
Some(List(vec![
|
||||||
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
(Type::SSHORT, 4) => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
Some(List(vec![
|
||||||
|
Signed(i32::from(r.read_i16()?)),
|
||||||
|
Signed(i32::from(r.read_i16()?)),
|
||||||
|
Signed(i32::from(r.read_i16()?)),
|
||||||
|
Signed(i32::from(r.read_i16()?)),
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
(Type::LONG, 2) => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
Some(List(vec![Unsigned(r.read_u32()?), Unsigned(r.read_u32()?)]))
|
||||||
|
}
|
||||||
|
(Type::SLONG, 2) => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
Some(List(vec![Signed(r.read_i32()?), Signed(r.read_i32()?)]))
|
||||||
|
}
|
||||||
|
(Type::RATIONAL, 1) => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
let numerator = r.read_u32()?;
|
||||||
|
let denominator = r.read_u32()?;
|
||||||
|
Some(Rational(numerator, denominator))
|
||||||
|
}
|
||||||
|
(Type::SRATIONAL, 1) => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
let numerator = r.read_i32()?;
|
||||||
|
let denominator = r.read_i32()?;
|
||||||
|
Some(SRational(numerator, denominator))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(v) = bigtiff_value {
|
||||||
|
Ok(v)
|
||||||
|
} else {
|
||||||
match (self.type_, self.count) {
|
match (self.type_, self.count) {
|
||||||
// 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.
|
||||||
@@ -130,14 +375,14 @@ impl Entry {
|
|||||||
(Type::BYTE, 3) => offset_to_bytes(3, self),
|
(Type::BYTE, 3) => offset_to_bytes(3, self),
|
||||||
(Type::BYTE, 4) => offset_to_bytes(4, self),
|
(Type::BYTE, 4) => offset_to_bytes(4, self),
|
||||||
(Type::BYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
(Type::BYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
Ok(Unsigned(u32::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, 1) => Ok(Signed(i32::from(self.offset[0] as i8))),
|
||||||
(Type::SBYTE, 2) => offset_to_sbytes(2, self),
|
(Type::SBYTE, 2) => offset_to_sbytes(2, self),
|
||||||
(Type::SBYTE, 3) => offset_to_sbytes(3, self),
|
(Type::SBYTE, 3) => offset_to_sbytes(3, self),
|
||||||
(Type::SBYTE, 4) => offset_to_sbytes(4, self),
|
(Type::SBYTE, 4) => offset_to_sbytes(4, self),
|
||||||
(Type::SBYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
(Type::SBYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
Ok(Signed(i32::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, 1) => Ok(Unsigned(u32::from(self.r(bo).read_u16()?))),
|
||||||
(Type::SSHORT, 1) => Ok(Signed(i32::from(self.r(bo).read_i16()?))),
|
(Type::SSHORT, 1) => Ok(Signed(i32::from(self.r(bo).read_i16()?))),
|
||||||
@@ -156,10 +401,10 @@ impl Entry {
|
|||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
(Type::SHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
(Type::SHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
Ok(Unsigned(u32::from(decoder.read_short()?)))
|
Ok(UnsignedBig(u64::from(decoder.read_short()?)))
|
||||||
}),
|
}),
|
||||||
(Type::SSHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
(Type::SSHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
Ok(Signed(i32::from(decoder.read_sshort()?)))
|
Ok(SignedBig(i64::from(decoder.read_sshort()?)))
|
||||||
}),
|
}),
|
||||||
(Type::LONG, 1) => Ok(Unsigned(self.r(bo).read_u32()?)),
|
(Type::LONG, 1) => Ok(Unsigned(self.r(bo).read_u32()?)),
|
||||||
(Type::SLONG, 1) => Ok(Signed(self.r(bo).read_i32()?)),
|
(Type::SLONG, 1) => Ok(Signed(self.r(bo).read_i32()?)),
|
||||||
@@ -169,47 +414,62 @@ impl Entry {
|
|||||||
(Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
(Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
Ok(Signed(decoder.read_slong()?))
|
Ok(Signed(decoder.read_slong()?))
|
||||||
}),
|
}),
|
||||||
(Type::RATIONAL, 1) => {
|
(Type::FLOAT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
Ok(Float(decoder.read_float()?))
|
||||||
let numerator = decoder.read_long()?;
|
}),
|
||||||
let denominator = decoder.read_long()?;
|
(Type::DOUBLE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
Ok(Rational(numerator, denominator))
|
Ok(Double(decoder.read_double()?))
|
||||||
}
|
}),
|
||||||
(Type::SRATIONAL, 1) => {
|
|
||||||
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
|
||||||
let numerator = decoder.read_slong()?;
|
|
||||||
let denominator = decoder.read_slong()?;
|
|
||||||
Ok(SRational(numerator, denominator))
|
|
||||||
}
|
|
||||||
(Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
(Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
Ok(Rational(
|
Ok(Rational(decoder.read_long()?, decoder.read_long()?))
|
||||||
decoder.read_long()?,
|
|
||||||
decoder.read_long()?,
|
|
||||||
))
|
|
||||||
}),
|
}),
|
||||||
(Type::SRATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
(Type::SRATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
Ok(SRational(
|
Ok(SRational(decoder.read_slong()?, decoder.read_slong()?))
|
||||||
decoder.read_slong()?,
|
}),
|
||||||
decoder.read_slong()?,
|
(Type::LONG8, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
))
|
Ok(UnsignedBig(decoder.read_long8()?))
|
||||||
}),
|
}),
|
||||||
(Type::ASCII, n) => {
|
(Type::ASCII, n) => {
|
||||||
let n = usize::try_from(n)?;
|
let n = usize::try_from(n)?;
|
||||||
if n > limits.decoding_buffer_size {
|
if n > limits.decoding_buffer_size {
|
||||||
return Err(TiffError::LimitsExceeded);
|
return Err(TiffError::LimitsExceeded);
|
||||||
}
|
}
|
||||||
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
|
||||||
|
if (n <= 4 && !decoder.bigtiff) || (n <= 8 && decoder.bigtiff) {
|
||||||
|
let mut buf = vec![0; n];
|
||||||
|
self.r(bo).read_exact(&mut buf)?;
|
||||||
|
let v = String::from_utf8(buf)?;
|
||||||
|
let v = v.trim_matches(char::from(0));
|
||||||
|
Ok(Ascii(v.into()))
|
||||||
|
} else {
|
||||||
|
if decoder.bigtiff {
|
||||||
|
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
|
||||||
|
} else {
|
||||||
|
decoder.goto_offset(self.r(bo).read_u32()?)?
|
||||||
|
}
|
||||||
let string = decoder.read_string(n)?;
|
let string = decoder.read_string(n)?;
|
||||||
Ok(Ascii(string))
|
Ok(Ascii(string))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
(Type::UNDEFINED, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(Byte(u8::from(decoder.read_byte()?)))
|
||||||
|
}),
|
||||||
_ => Err(TiffError::UnsupportedError(
|
_ => Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::UnsupportedDataType,
|
TiffUnsupportedError::UnsupportedDataType,
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn decode_offset<R, F>(&self, value_count: u32, bo: ByteOrder, limits: &super::Limits, decoder: &mut super::Decoder<R>, decode_fn: F) -> TiffResult<Value>
|
fn decode_offset<R, F>(
|
||||||
|
&self,
|
||||||
|
value_count: u64,
|
||||||
|
bo: ByteOrder,
|
||||||
|
limits: &super::Limits,
|
||||||
|
decoder: &mut super::Decoder<R>,
|
||||||
|
decode_fn: F,
|
||||||
|
) -> TiffResult<Value>
|
||||||
where
|
where
|
||||||
R: Read + Seek,
|
R: Read + Seek,
|
||||||
F: Fn(&mut super::Decoder<R>) -> TiffResult<Value>,
|
F: Fn(&mut super::Decoder<R>) -> TiffResult<Value>,
|
||||||
@@ -220,7 +480,11 @@ impl Entry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut v = Vec::with_capacity(value_count);
|
let mut v = Vec::with_capacity(value_count);
|
||||||
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
if decoder.bigtiff {
|
||||||
|
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
|
||||||
|
} else {
|
||||||
|
decoder.goto_offset(self.r(bo).read_u32()?)?
|
||||||
|
}
|
||||||
for _ in 0..value_count {
|
for _ in 0..value_count {
|
||||||
v.push(decode_fn(decoder)?)
|
v.push(decode_fn(decoder)?)
|
||||||
}
|
}
|
||||||
@@ -235,7 +499,7 @@ fn offset_to_bytes(n: usize, entry: &Entry) -> TiffResult<Value> {
|
|||||||
entry.offset[0..n]
|
entry.offset[0..n]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&e| Unsigned(u32::from(e)))
|
.map(|&e| Unsigned(u32::from(e)))
|
||||||
.collect()
|
.collect(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +510,7 @@ fn offset_to_sbytes(n: usize, entry: &Entry) -> TiffResult<Value> {
|
|||||||
entry.offset[0..n]
|
entry.offset[0..n]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&e| Signed(i32::from(e as i8)))
|
.map(|&e| Signed(i32::from(e as i8)))
|
||||||
.collect()
|
.collect(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+416
-87
@@ -1,14 +1,16 @@
|
|||||||
use std::convert::TryFrom;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::io::{self, Read, Seek};
|
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::io::{self, Read, Seek};
|
||||||
|
|
||||||
use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
||||||
|
|
||||||
use self::ifd::Directory;
|
use self::ifd::Directory;
|
||||||
use tags::{CompressionMethod, PhotometricInterpretation, Predictor, Tag, Type};
|
use tags::{CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag, Type};
|
||||||
|
|
||||||
use self::stream::{ByteOrder, EndianReader, LZWReader, DeflateReader, PackBitsReader, SmartReader};
|
use self::stream::{
|
||||||
|
ByteOrder, DeflateReader, EndianReader, JpegReader, LZWReader, PackBitsReader, SmartReader,
|
||||||
|
};
|
||||||
|
|
||||||
pub mod ifd;
|
pub mod ifd;
|
||||||
mod stream;
|
mod stream;
|
||||||
@@ -24,6 +26,10 @@ pub enum DecodingResult {
|
|||||||
U32(Vec<u32>),
|
U32(Vec<u32>),
|
||||||
/// A vector of 64 bit unsigned ints
|
/// A vector of 64 bit unsigned ints
|
||||||
U64(Vec<u64>),
|
U64(Vec<u64>),
|
||||||
|
/// A vector of 32 bit IEEE floats
|
||||||
|
F32(Vec<f32>),
|
||||||
|
/// A vector of 64 bit IEEE floats
|
||||||
|
F64(Vec<f64>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DecodingResult {
|
impl DecodingResult {
|
||||||
@@ -59,12 +65,30 @@ impl DecodingResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_f32(size: usize, limits: &Limits) -> TiffResult<DecodingResult> {
|
||||||
|
if size > limits.decoding_buffer_size / std::mem::size_of::<f32>() {
|
||||||
|
Err(TiffError::LimitsExceeded)
|
||||||
|
} else {
|
||||||
|
Ok(DecodingResult::F32(vec![0.0; size]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_f64(size: usize, limits: &Limits) -> TiffResult<DecodingResult> {
|
||||||
|
if size > limits.decoding_buffer_size / std::mem::size_of::<f64>() {
|
||||||
|
Err(TiffError::LimitsExceeded)
|
||||||
|
} else {
|
||||||
|
Ok(DecodingResult::F64(vec![0.0; size]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_buffer(&mut self, start: usize) -> DecodingBuffer {
|
pub fn as_buffer(&mut self, start: usize) -> DecodingBuffer {
|
||||||
match *self {
|
match *self {
|
||||||
DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]),
|
DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]),
|
||||||
DecodingResult::U16(ref mut buf) => DecodingBuffer::U16(&mut buf[start..]),
|
DecodingResult::U16(ref mut buf) => DecodingBuffer::U16(&mut buf[start..]),
|
||||||
DecodingResult::U32(ref mut buf) => DecodingBuffer::U32(&mut buf[start..]),
|
DecodingResult::U32(ref mut buf) => DecodingBuffer::U32(&mut buf[start..]),
|
||||||
DecodingResult::U64(ref mut buf) => DecodingBuffer::U64(&mut buf[start..]),
|
DecodingResult::U64(ref mut buf) => DecodingBuffer::U64(&mut buf[start..]),
|
||||||
|
DecodingResult::F32(ref mut buf) => DecodingBuffer::F32(&mut buf[start..]),
|
||||||
|
DecodingResult::F64(ref mut buf) => DecodingBuffer::F64(&mut buf[start..]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,6 +103,10 @@ pub enum DecodingBuffer<'a> {
|
|||||||
U32(&'a mut [u32]),
|
U32(&'a mut [u32]),
|
||||||
/// A slice of 64 bit unsigned ints
|
/// A slice of 64 bit unsigned ints
|
||||||
U64(&'a mut [u64]),
|
U64(&'a mut [u64]),
|
||||||
|
/// A slice of 32 bit IEEE floats
|
||||||
|
F32(&'a mut [f32]),
|
||||||
|
/// A slice of 64 bit IEEE floats
|
||||||
|
F64(&'a mut [f64]),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DecodingBuffer<'a> {
|
impl<'a> DecodingBuffer<'a> {
|
||||||
@@ -88,6 +116,8 @@ impl<'a> DecodingBuffer<'a> {
|
|||||||
DecodingBuffer::U16(ref buf) => buf.len(),
|
DecodingBuffer::U16(ref buf) => buf.len(),
|
||||||
DecodingBuffer::U32(ref buf) => buf.len(),
|
DecodingBuffer::U32(ref buf) => buf.len(),
|
||||||
DecodingBuffer::U64(ref buf) => buf.len(),
|
DecodingBuffer::U64(ref buf) => buf.len(),
|
||||||
|
DecodingBuffer::F32(ref buf) => buf.len(),
|
||||||
|
DecodingBuffer::F64(ref buf) => buf.len(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +127,8 @@ impl<'a> DecodingBuffer<'a> {
|
|||||||
DecodingBuffer::U16(_) => 2,
|
DecodingBuffer::U16(_) => 2,
|
||||||
DecodingBuffer::U32(_) => 4,
|
DecodingBuffer::U32(_) => 4,
|
||||||
DecodingBuffer::U64(_) => 8,
|
DecodingBuffer::U64(_) => 8,
|
||||||
|
DecodingBuffer::F32(_) => 4,
|
||||||
|
DecodingBuffer::F64(_) => 8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +141,8 @@ impl<'a> DecodingBuffer<'a> {
|
|||||||
DecodingBuffer::U16(ref mut buf) => DecodingBuffer::U16(buf),
|
DecodingBuffer::U16(ref mut buf) => DecodingBuffer::U16(buf),
|
||||||
DecodingBuffer::U32(ref mut buf) => DecodingBuffer::U32(buf),
|
DecodingBuffer::U32(ref mut buf) => DecodingBuffer::U32(buf),
|
||||||
DecodingBuffer::U64(ref mut buf) => DecodingBuffer::U64(buf),
|
DecodingBuffer::U64(ref mut buf) => DecodingBuffer::U64(buf),
|
||||||
|
DecodingBuffer::F32(ref mut buf) => DecodingBuffer::F32(buf),
|
||||||
|
DecodingBuffer::F64(ref mut buf) => DecodingBuffer::F64(buf),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,8 +150,8 @@ impl<'a> DecodingBuffer<'a> {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct StripDecodeState {
|
struct StripDecodeState {
|
||||||
strip_index: usize,
|
strip_index: usize,
|
||||||
strip_offsets: Vec<u32>,
|
strip_offsets: Vec<u64>,
|
||||||
strip_bytes: Vec<u32>,
|
strip_bytes: Vec<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decoding limits
|
/// Decoding limits
|
||||||
@@ -131,6 +165,9 @@ pub struct Limits {
|
|||||||
/// The maximum size of any ifd value in bytes, the default is
|
/// The maximum size of any ifd value in bytes, the default is
|
||||||
/// 1MiB.
|
/// 1MiB.
|
||||||
pub ifd_value_size: usize,
|
pub ifd_value_size: usize,
|
||||||
|
/// Maximum size for intermediate buffer which may be used to limit the amount of data read per
|
||||||
|
/// segment even if the entire image is decoded at once.
|
||||||
|
pub intermediate_buffer_size: usize,
|
||||||
/// The purpose of this is to prevent all the fields of the struct from
|
/// The purpose of this is to prevent all the fields of the struct from
|
||||||
/// being public, as this would make adding new fields a major version
|
/// being public, as this would make adding new fields a major version
|
||||||
/// bump.
|
/// bump.
|
||||||
@@ -141,6 +178,7 @@ impl Default for Limits {
|
|||||||
fn default() -> Limits {
|
fn default() -> Limits {
|
||||||
Limits {
|
Limits {
|
||||||
decoding_buffer_size: 256 * 1024 * 1024,
|
decoding_buffer_size: 256 * 1024 * 1024,
|
||||||
|
intermediate_buffer_size: 128 * 1024 * 1024,
|
||||||
ifd_value_size: 1024 * 1024,
|
ifd_value_size: 1024 * 1024,
|
||||||
_non_exhaustive: (),
|
_non_exhaustive: (),
|
||||||
}
|
}
|
||||||
@@ -157,18 +195,26 @@ where
|
|||||||
{
|
{
|
||||||
reader: SmartReader<R>,
|
reader: SmartReader<R>,
|
||||||
byte_order: ByteOrder,
|
byte_order: ByteOrder,
|
||||||
|
bigtiff: bool,
|
||||||
limits: Limits,
|
limits: Limits,
|
||||||
next_ifd: Option<u32>,
|
next_ifd: Option<u64>,
|
||||||
|
current_ifd: Option<u64>,
|
||||||
ifd: Option<Directory>,
|
ifd: Option<Directory>,
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
bits_per_sample: Vec<u8>,
|
bits_per_sample: Vec<u8>,
|
||||||
samples: u8,
|
samples: u8,
|
||||||
|
sample_format: Vec<SampleFormat>,
|
||||||
photometric_interpretation: PhotometricInterpretation,
|
photometric_interpretation: PhotometricInterpretation,
|
||||||
compression_method: CompressionMethod,
|
compression_method: CompressionMethod,
|
||||||
strip_decoder: Option<StripDecodeState>,
|
strip_decoder: Option<StripDecodeState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stores state necessary to seek back (or forward) to particular part of the tiff.
|
||||||
|
pub struct Checkpoint {
|
||||||
|
offset: u64,
|
||||||
|
}
|
||||||
|
|
||||||
trait Wrapping {
|
trait Wrapping {
|
||||||
fn wrapping_add(&self, other: Self) -> Self;
|
fn wrapping_add(&self, other: Self) -> Self;
|
||||||
}
|
}
|
||||||
@@ -217,8 +263,14 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType)
|
|||||||
let samples = match color_type {
|
let samples = match color_type {
|
||||||
ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) | ColorType::Gray(64) => 1,
|
ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) | ColorType::Gray(64) => 1,
|
||||||
ColorType::RGB(8) | ColorType::RGB(16) | ColorType::RGB(32) | ColorType::RGB(64) => 3,
|
ColorType::RGB(8) | ColorType::RGB(16) | ColorType::RGB(32) | ColorType::RGB(64) => 3,
|
||||||
ColorType::RGBA(8) | ColorType::RGBA(16) | ColorType::RGBA(32) | ColorType::RGBA(64)
|
ColorType::RGBA(8)
|
||||||
| ColorType::CMYK(8) | ColorType::CMYK(16) | ColorType::CMYK(32) | ColorType::CMYK(64) => 4,
|
| ColorType::RGBA(16)
|
||||||
|
| ColorType::RGBA(32)
|
||||||
|
| ColorType::RGBA(64)
|
||||||
|
| ColorType::CMYK(8)
|
||||||
|
| ColorType::CMYK(16)
|
||||||
|
| ColorType::CMYK(32)
|
||||||
|
| ColorType::CMYK(64) => 4,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(TiffError::UnsupportedError(
|
return Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::HorizontalPredictor(color_type),
|
TiffUnsupportedError::HorizontalPredictor(color_type),
|
||||||
@@ -238,6 +290,22 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType)
|
|||||||
DecodingBuffer::U64(buf) => {
|
DecodingBuffer::U64(buf) => {
|
||||||
rev_hpredict_nsamp(buf, size, samples)?;
|
rev_hpredict_nsamp(buf, size, samples)?;
|
||||||
}
|
}
|
||||||
|
DecodingBuffer::F32(_buf) => {
|
||||||
|
// FIXME: check how this is defined.
|
||||||
|
// See issue #89.
|
||||||
|
// rev_hpredict_nsamp(buf, size, samples)?;
|
||||||
|
return Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::HorizontalPredictor(color_type),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
DecodingBuffer::F64(_buf) => {
|
||||||
|
//FIXME: check how this is defined.
|
||||||
|
// See issue #89.
|
||||||
|
// rev_hpredict_nsamp(buf, size, samples)?;
|
||||||
|
return Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::HorizontalPredictor(color_type),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -248,13 +316,16 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
Decoder {
|
Decoder {
|
||||||
reader: SmartReader::wrap(r, ByteOrder::LittleEndian),
|
reader: SmartReader::wrap(r, ByteOrder::LittleEndian),
|
||||||
byte_order: ByteOrder::LittleEndian,
|
byte_order: ByteOrder::LittleEndian,
|
||||||
|
bigtiff: false,
|
||||||
limits: Default::default(),
|
limits: Default::default(),
|
||||||
next_ifd: None,
|
next_ifd: None,
|
||||||
|
current_ifd: None,
|
||||||
ifd: None,
|
ifd: None,
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
bits_per_sample: vec![1],
|
bits_per_sample: vec![1],
|
||||||
samples: 1,
|
samples: 1,
|
||||||
|
sample_format: vec![SampleFormat::Uint],
|
||||||
photometric_interpretation: PhotometricInterpretation::BlackIsZero,
|
photometric_interpretation: PhotometricInterpretation::BlackIsZero,
|
||||||
compression_method: CompressionMethod::None,
|
compression_method: CompressionMethod::None,
|
||||||
strip_decoder: None,
|
strip_decoder: None,
|
||||||
@@ -273,8 +344,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
pub fn colortype(&mut self) -> TiffResult<ColorType> {
|
pub fn colortype(&mut self) -> TiffResult<ColorType> {
|
||||||
match self.photometric_interpretation {
|
match self.photometric_interpretation {
|
||||||
PhotometricInterpretation::RGB => {
|
PhotometricInterpretation::RGB => match self.bits_per_sample[..] {
|
||||||
match self.bits_per_sample[..] {
|
|
||||||
[r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)),
|
[r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)),
|
||||||
[r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)),
|
[r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)),
|
||||||
_ => Err(TiffError::UnsupportedError(
|
_ => Err(TiffError::UnsupportedError(
|
||||||
@@ -283,10 +353,8 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
self.bits_per_sample.clone(),
|
self.bits_per_sample.clone(),
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
}
|
|
||||||
},
|
},
|
||||||
PhotometricInterpretation::CMYK => {
|
PhotometricInterpretation::CMYK => match self.bits_per_sample[..] {
|
||||||
match self.bits_per_sample[..] {
|
|
||||||
[c, m, y, k] if [c, c, c] == [m, y, k] => Ok(ColorType::CMYK(c)),
|
[c, m, y, k] if [c, c, c] == [m, y, k] => Ok(ColorType::CMYK(c)),
|
||||||
_ => Err(TiffError::UnsupportedError(
|
_ => Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::InterpretationWithBits(
|
TiffUnsupportedError::InterpretationWithBits(
|
||||||
@@ -294,7 +362,6 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
self.bits_per_sample.clone(),
|
self.bits_per_sample.clone(),
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
}
|
|
||||||
},
|
},
|
||||||
PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero
|
PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero
|
||||||
if self.bits_per_sample.len() == 1 =>
|
if self.bits_per_sample.len() == 1 =>
|
||||||
@@ -330,12 +397,30 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.read_short()? != 42 {
|
match self.read_short()? {
|
||||||
|
42 => self.bigtiff = false,
|
||||||
|
43 => {
|
||||||
|
self.bigtiff = true;
|
||||||
|
// Read bytesize of offsets (in bigtiff it's alway 8 but provide a way to move to 16 some day)
|
||||||
|
if self.read_short()? != 8 {
|
||||||
return Err(TiffError::FormatError(
|
return Err(TiffError::FormatError(
|
||||||
TiffFormatError::TiffSignatureInvalid,
|
TiffFormatError::TiffSignatureNotFound,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
self.next_ifd = match self.read_long()? {
|
// This constant should always be 0
|
||||||
|
if self.read_short()? != 0 {
|
||||||
|
return Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::TiffSignatureNotFound,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::TiffSignatureInvalid,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.next_ifd = match self.read_ifd_offset()? {
|
||||||
0 => None,
|
0 => None,
|
||||||
n => Some(n),
|
n => Some(n),
|
||||||
};
|
};
|
||||||
@@ -370,10 +455,26 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
if let Some(val) = self.find_tag_unsigned(Tag::SamplesPerPixel)? {
|
if let Some(val) = self.find_tag_unsigned(Tag::SamplesPerPixel)? {
|
||||||
self.samples = val;
|
self.samples = val;
|
||||||
}
|
}
|
||||||
|
if let Some(vals) = self.find_tag_unsigned_vec(Tag::SampleFormat)? {
|
||||||
|
self.sample_format = vals
|
||||||
|
.into_iter()
|
||||||
|
.map(SampleFormat::from_u16_exhaustive)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// TODO: for now, only homogenous formats across samples are supported.
|
||||||
|
if !self.sample_format.windows(2).all(|s| s[0] == s[1]) {
|
||||||
|
return Err(TiffUnsupportedError::UnsupportedSampleFormat(
|
||||||
|
self.sample_format.clone(),
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
match self.samples {
|
match self.samples {
|
||||||
1 | 3 | 4 => if let Some(val) = self.find_tag_unsigned_vec(Tag::BitsPerSample)? {
|
1 | 3 | 4 => {
|
||||||
|
if let Some(val) = self.find_tag_unsigned_vec(Tag::BitsPerSample)? {
|
||||||
self.bits_per_sample = val;
|
self.bits_per_sample = val;
|
||||||
},
|
}
|
||||||
|
}
|
||||||
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()),
|
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -389,6 +490,15 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
self.byte_order
|
self.byte_order
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn read_ifd_offset(&mut self) -> Result<u64, io::Error> {
|
||||||
|
if self.bigtiff {
|
||||||
|
self.read_long8()
|
||||||
|
} else {
|
||||||
|
self.read_long().map(u64::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Reads a TIFF byte value
|
/// Reads a TIFF byte value
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_byte(&mut self) -> Result<u8, io::Error> {
|
pub fn read_byte(&mut self) -> Result<u8, io::Error> {
|
||||||
@@ -421,30 +531,103 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
self.reader.read_i32()
|
self.reader.read_i32()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads a TIFF float value
|
||||||
|
#[inline]
|
||||||
|
pub fn read_float(&mut self) -> Result<f32, io::Error> {
|
||||||
|
self.reader.read_f32()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads a TIFF double value
|
||||||
|
#[inline]
|
||||||
|
pub fn read_double(&mut self) -> Result<f64, io::Error> {
|
||||||
|
self.reader.read_f64()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn read_long8(&mut self) -> Result<u64, io::Error> {
|
||||||
|
self.reader.read_u64()
|
||||||
|
}
|
||||||
|
|
||||||
/// Reads a string
|
/// Reads a string
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_string(&mut self, length: usize) -> TiffResult<String> {
|
pub fn read_string(&mut self, length: usize) -> TiffResult<String> {
|
||||||
let mut out = String::with_capacity(length);
|
let mut out = vec![0; length];
|
||||||
self.reader.read_to_string(&mut out)?;
|
self.reader.read_exact(&mut out)?;
|
||||||
// Strings may be null-terminated, so we trim anything downstream of the null byte
|
// Strings may be null-terminated, so we trim anything downstream of the null byte
|
||||||
let trimmed = out.bytes().take_while(|&n| n != 0).collect::<Vec<u8>>();
|
if let Some(first) = out.iter().position(|&b| b == 0) {
|
||||||
Ok(String::from_utf8(trimmed)?)
|
out.truncate(first);
|
||||||
|
}
|
||||||
|
Ok(String::from_utf8(out)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads a TIFF IFA offset/value field
|
/// Reads a TIFF IFA offset/value field
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_offset(&mut self) -> Result<[u8; 4], io::Error> {
|
pub fn read_offset(&mut self) -> TiffResult<[u8; 4]> {
|
||||||
|
if self.bigtiff {
|
||||||
|
return Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::InconsistentSizesEncountered,
|
||||||
|
));
|
||||||
|
}
|
||||||
let mut val = [0; 4];
|
let mut val = [0; 4];
|
||||||
self.reader.read_exact(&mut val)?;
|
self.reader.read_exact(&mut val)?;
|
||||||
Ok(val)
|
Ok(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads a TIFF IFA offset/value field
|
||||||
|
#[inline]
|
||||||
|
pub fn read_offset_u64(&mut self) -> Result<[u8; 8], io::Error> {
|
||||||
|
let mut val = [0; 8];
|
||||||
|
self.reader.read_exact(&mut val)?;
|
||||||
|
Ok(val)
|
||||||
|
}
|
||||||
|
|
||||||
/// Moves the cursor to the specified offset
|
/// Moves the cursor to the specified offset
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn goto_offset(&mut self, offset: u32) -> io::Result<()> {
|
pub fn goto_offset(&mut self, offset: u32) -> io::Result<()> {
|
||||||
self.reader
|
self.goto_offset_u64(offset.into())
|
||||||
.seek(io::SeekFrom::Start(u64::from(offset)))
|
}
|
||||||
.map(|_| ())
|
|
||||||
|
#[inline]
|
||||||
|
pub fn goto_offset_u64(&mut self, offset: u64) -> io::Result<()> {
|
||||||
|
self.reader.seek(io::SeekFrom::Start(offset)).map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve a checkpoint that allows seeking before the TIFF header.
|
||||||
|
///
|
||||||
|
/// This is a special checkpoint where the caller should call `init` after restoring the
|
||||||
|
/// checkpoint and no ifd is being read.
|
||||||
|
pub fn checkpoint_init(&self) -> Checkpoint {
|
||||||
|
Checkpoint { offset: Checkpoint::INITIAL_OFFSET }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve a `Checkpoint` that can be used to seek to the current image directory.
|
||||||
|
pub fn checkpoint(&self) -> Checkpoint {
|
||||||
|
match self.current_ifd {
|
||||||
|
None => self.checkpoint_init(),
|
||||||
|
Some(offset) => Checkpoint { offset },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the next image directory as a checkpoint.
|
||||||
|
///
|
||||||
|
/// This allows jumping forward to the next image directory later, without needing to pay the
|
||||||
|
/// additional cost of parsing it right away.
|
||||||
|
pub fn checkpoint_next(&self) -> Option<Checkpoint> {
|
||||||
|
match self.next_ifd {
|
||||||
|
None => None,
|
||||||
|
Some(offset) => Some(Checkpoint { offset })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Restore the state of a previous checkpoint.
|
||||||
|
pub fn restore(&mut self, checkpoint: &Checkpoint) -> TiffResult<()> {
|
||||||
|
if checkpoint.offset == Checkpoint::INITIAL_OFFSET {
|
||||||
|
self.goto_offset_u64(0)?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
self.next_ifd = Some(checkpoint.offset);
|
||||||
|
self.next_image()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads a IFD entry.
|
/// Reads a IFD entry.
|
||||||
@@ -465,14 +648,12 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(Some((
|
let entry = if self.bigtiff {
|
||||||
tag,
|
ifd::Entry::new_u64(type_, self.read_long8()?, self.read_offset_u64()?)
|
||||||
ifd::Entry::new(
|
} else {
|
||||||
type_,
|
ifd::Entry::new(type_, self.read_long()?, self.read_offset()?)
|
||||||
self.read_long()?, // count
|
};
|
||||||
self.read_offset()?, // offset
|
Ok(Some((tag, entry)))
|
||||||
),
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the next IFD
|
/// Reads the next IFD
|
||||||
@@ -484,16 +665,26 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
TiffFormatError::ImageFileDirectoryNotFound,
|
TiffFormatError::ImageFileDirectoryNotFound,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
Some(offset) => self.goto_offset(offset)?,
|
Some(offset) => {
|
||||||
|
self.goto_offset_u64(offset)?;
|
||||||
|
self.current_ifd = Some(offset);
|
||||||
}
|
}
|
||||||
for _ in 0..self.read_short()? {
|
}
|
||||||
|
let num_tags = if self.bigtiff {
|
||||||
|
self.read_long8()?
|
||||||
|
} else {
|
||||||
|
self.read_short()?.into()
|
||||||
|
};
|
||||||
|
for _ in 0..num_tags {
|
||||||
let (tag, entry) = match self.read_entry()? {
|
let (tag, entry) = match self.read_entry()? {
|
||||||
Some(val) => val,
|
Some(val) => val,
|
||||||
None => continue, // Unknown data type in tag, skip
|
None => {
|
||||||
|
continue;
|
||||||
|
} // Unknown data type in tag, skip
|
||||||
};
|
};
|
||||||
dir.insert(tag, entry);
|
dir.insert(tag, entry);
|
||||||
}
|
}
|
||||||
self.next_ifd = match self.read_long()? {
|
self.next_ifd = match self.read_ifd_offset()? {
|
||||||
0 => None,
|
0 => None,
|
||||||
n => Some(n),
|
n => Some(n),
|
||||||
};
|
};
|
||||||
@@ -514,28 +705,38 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to retrieve a tag and convert it to the desired unsigned type.
|
/// Tries to retrieve a tag and convert it to the desired unsigned type.
|
||||||
pub fn find_tag_unsigned<T: TryFrom<u32>>(&mut self, tag: Tag) -> TiffResult<Option<T>> {
|
pub fn find_tag_unsigned<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<Option<T>> {
|
||||||
self.find_tag(tag)?
|
self.find_tag(tag)?
|
||||||
.map(|v| v.into_u32()).transpose()?
|
.map(|v| v.into_u64())
|
||||||
.map(|value| T::try_from(value)
|
.transpose()?
|
||||||
.map_err(|_| TiffFormatError::InvalidTagValueType(tag).into()))
|
.map(|value| {
|
||||||
|
T::try_from(value).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into())
|
||||||
|
})
|
||||||
.transpose()
|
.transpose()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to retrieve a vector of all a tag's values and convert them to
|
/// Tries to retrieve a vector of all a tag's values and convert them to
|
||||||
/// the desired unsigned type.
|
/// the desired unsigned type.
|
||||||
pub fn find_tag_unsigned_vec<T: TryFrom<u32>>(&mut self, tag: Tag) -> TiffResult<Option<Vec<T>>> {
|
pub fn find_tag_unsigned_vec<T: TryFrom<u64>>(
|
||||||
|
&mut self,
|
||||||
|
tag: Tag,
|
||||||
|
) -> TiffResult<Option<Vec<T>>> {
|
||||||
self.find_tag(tag)?
|
self.find_tag(tag)?
|
||||||
.map(|v| v.into_u32_vec()).transpose()?
|
.map(|v| v.into_u64_vec())
|
||||||
.map(|v| v.into_iter()
|
.transpose()?
|
||||||
.map(|u| T::try_from(u).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into()))
|
.map(|v| {
|
||||||
.collect())
|
v.into_iter()
|
||||||
|
.map(|u| {
|
||||||
|
T::try_from(u).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into())
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
.transpose()
|
.transpose()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to retrieve a tag and convert it to the desired unsigned type.
|
/// Tries to retrieve a tag and convert it to the desired unsigned type.
|
||||||
/// Returns an error if the tag is not present.
|
/// Returns an error if the tag is not present.
|
||||||
pub fn get_tag_unsigned<T: TryFrom<u32>>(&mut self, tag: Tag) -> TiffResult<T> {
|
pub fn get_tag_unsigned<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<T> {
|
||||||
self.find_tag_unsigned(tag)?
|
self.find_tag_unsigned(tag)?
|
||||||
.ok_or_else(|| TiffFormatError::RequiredTagNotFound(tag).into())
|
.ok_or_else(|| TiffFormatError::RequiredTagNotFound(tag).into())
|
||||||
}
|
}
|
||||||
@@ -555,24 +756,55 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult<u32> {
|
pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult<u32> {
|
||||||
self.get_tag(tag)?.into_u32()
|
self.get_tag(tag)?.into_u32()
|
||||||
}
|
}
|
||||||
|
pub fn get_tag_u64(&mut self, tag: Tag) -> TiffResult<u64> {
|
||||||
|
self.get_tag(tag)?.into_u64()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to retrieve a tag and convert it to the desired type.
|
||||||
|
pub fn get_tag_f32(&mut self, tag: Tag) -> TiffResult<f32> {
|
||||||
|
self.get_tag(tag)?.into_f32()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to retrieve a tag and convert it to the desired type.
|
||||||
|
pub fn get_tag_f64(&mut self, tag: Tag) -> TiffResult<f64> {
|
||||||
|
self.get_tag(tag)?.into_f64()
|
||||||
|
}
|
||||||
|
|
||||||
/// Tries to retrieve a tag and convert it to the desired type.
|
/// Tries to retrieve a tag and convert it to the desired type.
|
||||||
pub fn get_tag_u32_vec(&mut self, tag: Tag) -> TiffResult<Vec<u32>> {
|
pub fn get_tag_u32_vec(&mut self, tag: Tag) -> TiffResult<Vec<u32>> {
|
||||||
self.get_tag(tag)?.into_u32_vec()
|
self.get_tag(tag)?.into_u32_vec()
|
||||||
}
|
}
|
||||||
|
pub fn get_tag_u64_vec(&mut self, tag: Tag) -> TiffResult<Vec<u64>> {
|
||||||
|
self.get_tag(tag)?.into_u64_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to retrieve a tag and convert it to the desired type.
|
||||||
|
pub fn get_tag_f32_vec(&mut self, tag: Tag) -> TiffResult<Vec<f32>> {
|
||||||
|
self.get_tag(tag)?.into_f32_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to retrieve a tag and convert it to the desired type.
|
||||||
|
pub fn get_tag_f64_vec(&mut self, tag: Tag) -> TiffResult<Vec<f64>> {
|
||||||
|
self.get_tag(tag)?.into_f64_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to retrieve a tag and convert it to a 8bit vector.
|
||||||
|
pub fn get_tag_u8_vec(&mut self, tag: Tag) -> TiffResult<Vec<u8>> {
|
||||||
|
self.get_tag(tag)?.into_u8_vec()
|
||||||
|
}
|
||||||
|
|
||||||
/// Decompresses the strip into the supplied buffer.
|
/// Decompresses the strip into the supplied buffer.
|
||||||
/// Returns the number of bytes read.
|
/// Returns the number of bytes read.
|
||||||
fn expand_strip<'a>(
|
fn expand_strip<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: DecodingBuffer<'a>,
|
buffer: DecodingBuffer<'a>,
|
||||||
offset: u32,
|
offset: u64,
|
||||||
length: u32,
|
length: u64,
|
||||||
max_uncompressed_length: usize,
|
max_uncompressed_length: usize,
|
||||||
) -> TiffResult<usize> {
|
) -> TiffResult<usize> {
|
||||||
let color_type = self.colortype()?;
|
let color_type = self.colortype()?;
|
||||||
self.goto_offset(offset)?;
|
self.goto_offset_u64(offset)?;
|
||||||
let (bytes, mut reader): (usize, Box<EndianReader>) = match self.compression_method {
|
let (bytes, mut reader): (usize, Box<dyn EndianReader>) = match self.compression_method {
|
||||||
CompressionMethod::None => {
|
CompressionMethod::None => {
|
||||||
let order = self.reader.byte_order;
|
let order = self.reader.byte_order;
|
||||||
(
|
(
|
||||||
@@ -584,21 +816,19 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
let (bytes, reader) = LZWReader::new(
|
let (bytes, reader) = LZWReader::new(
|
||||||
&mut self.reader,
|
&mut self.reader,
|
||||||
usize::try_from(length)?,
|
usize::try_from(length)?,
|
||||||
max_uncompressed_length
|
max_uncompressed_length * buffer.byte_len(),
|
||||||
)?;
|
)?;
|
||||||
(bytes, Box::new(reader))
|
(bytes, Box::new(reader))
|
||||||
}
|
}
|
||||||
CompressionMethod::PackBits => {
|
CompressionMethod::PackBits => {
|
||||||
let order = self.reader.byte_order;
|
let order = self.reader.byte_order;
|
||||||
let (bytes, reader) = PackBitsReader::new(
|
let (bytes, reader) =
|
||||||
&mut self.reader,
|
PackBitsReader::new(&mut self.reader, order, usize::try_from(length)?)?;
|
||||||
order,
|
|
||||||
usize::try_from(length)?,
|
|
||||||
)?;
|
|
||||||
(bytes, Box::new(reader))
|
(bytes, Box::new(reader))
|
||||||
}
|
}
|
||||||
CompressionMethod::OldDeflate => {
|
CompressionMethod::OldDeflate => {
|
||||||
let (bytes, reader) = DeflateReader::new(&mut self.reader, max_uncompressed_length)?;
|
let (bytes, reader) =
|
||||||
|
DeflateReader::new(&mut self.reader, max_uncompressed_length)?;
|
||||||
(bytes, Box::new(reader))
|
(bytes, Box::new(reader))
|
||||||
}
|
}
|
||||||
method => {
|
method => {
|
||||||
@@ -633,6 +863,18 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
reader.read_u32_into(&mut buffer[..bytes / 4])?;
|
reader.read_u32_into(&mut buffer[..bytes / 4])?;
|
||||||
bytes / 4
|
bytes / 4
|
||||||
}
|
}
|
||||||
|
(ColorType::RGBA(32), DecodingBuffer::F32(ref mut buffer))
|
||||||
|
| (ColorType::RGB(32), DecodingBuffer::F32(ref mut buffer))
|
||||||
|
| (ColorType::CMYK(32), DecodingBuffer::F32(ref mut buffer)) => {
|
||||||
|
reader.read_f32_into(&mut buffer[..bytes / 4])?;
|
||||||
|
bytes / 4
|
||||||
|
}
|
||||||
|
(ColorType::RGBA(64), DecodingBuffer::F64(ref mut buffer))
|
||||||
|
| (ColorType::RGB(64), DecodingBuffer::F64(ref mut buffer))
|
||||||
|
| (ColorType::CMYK(64), DecodingBuffer::F64(ref mut buffer)) => {
|
||||||
|
reader.read_f64_into(&mut buffer[..bytes / 8])?;
|
||||||
|
bytes / 8
|
||||||
|
}
|
||||||
(ColorType::RGBA(64), DecodingBuffer::U64(ref mut buffer))
|
(ColorType::RGBA(64), DecodingBuffer::U64(ref mut buffer))
|
||||||
| (ColorType::RGB(64), DecodingBuffer::U64(ref mut buffer))
|
| (ColorType::RGB(64), DecodingBuffer::U64(ref mut buffer))
|
||||||
| (ColorType::CMYK(64), DecodingBuffer::U64(ref mut buffer)) => {
|
| (ColorType::CMYK(64), DecodingBuffer::U64(ref mut buffer)) => {
|
||||||
@@ -675,6 +917,26 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
}
|
}
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
|
(ColorType::Gray(32), DecodingBuffer::F32(ref mut buffer)) => {
|
||||||
|
reader.read_f32_into(&mut buffer[..bytes / 4])?;
|
||||||
|
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
||||||
|
for datum in buffer[..bytes / 4].iter_mut() {
|
||||||
|
// FIXME: assumes [0, 1) range for floats
|
||||||
|
*datum = 1.0 - *datum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bytes / 4
|
||||||
|
}
|
||||||
|
(ColorType::Gray(64), DecodingBuffer::F64(ref mut buffer)) => {
|
||||||
|
reader.read_f64_into(&mut buffer[..bytes / 8])?;
|
||||||
|
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
||||||
|
for datum in buffer[..bytes / 8].iter_mut() {
|
||||||
|
// FIXME: assumes [0, 1) range for floats
|
||||||
|
*datum = 1.0 - *datum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bytes / 8
|
||||||
|
}
|
||||||
(type_, _) => {
|
(type_, _) => {
|
||||||
return Err(TiffError::UnsupportedError(
|
return Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::UnsupportedColorType(type_),
|
TiffUnsupportedError::UnsupportedColorType(type_),
|
||||||
@@ -685,9 +947,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
/// Number of strips in image
|
/// Number of strips in image
|
||||||
pub fn strip_count(&mut self) -> TiffResult<u32> {
|
pub fn strip_count(&mut self) -> TiffResult<u32> {
|
||||||
let rows_per_strip = self
|
let rows_per_strip = self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height);
|
||||||
.get_tag_u32(Tag::RowsPerStrip)
|
|
||||||
.unwrap_or(self.height);
|
|
||||||
|
|
||||||
if rows_per_strip == 0 {
|
if rows_per_strip == 0 {
|
||||||
return Ok(0);
|
return Ok(0);
|
||||||
@@ -698,8 +958,8 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
fn initialize_strip_decoder(&mut self) -> TiffResult<()> {
|
fn initialize_strip_decoder(&mut self) -> TiffResult<()> {
|
||||||
if self.strip_decoder.is_none() {
|
if self.strip_decoder.is_none() {
|
||||||
let strip_offsets = self.get_tag_u32_vec(Tag::StripOffsets)?;
|
let strip_offsets = self.get_tag_u64_vec(Tag::StripOffsets)?;
|
||||||
let strip_bytes = self.get_tag_u32_vec(Tag::StripByteCounts)?;
|
let strip_bytes = self.get_tag_u64_vec(Tag::StripByteCounts)?;
|
||||||
|
|
||||||
self.strip_decoder = Some(StripDecodeState {
|
self.strip_decoder = Some(StripDecodeState {
|
||||||
strip_index: 0,
|
strip_index: 0,
|
||||||
@@ -710,6 +970,60 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_jpeg(&mut self) -> TiffResult<DecodingResult> {
|
||||||
|
let offsets = self.get_tag_u32_vec(Tag::StripOffsets)?;
|
||||||
|
let bytes = self.get_tag_u32_vec(Tag::StripByteCounts)?;
|
||||||
|
|
||||||
|
let jpeg_tables: Option<Vec<u8>> = match self.find_tag(Tag::JPEGTables) {
|
||||||
|
Ok(None) => None,
|
||||||
|
Ok(_) => Some(self.get_tag_u8_vec(Tag::JPEGTables)?),
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
if offsets.len() == 0 {
|
||||||
|
return Err(TiffError::FormatError(TiffFormatError::RequiredTagEmpty(
|
||||||
|
Tag::StripOffsets,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if offsets.len() != bytes.len() {
|
||||||
|
return Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::InconsistentSizesEncountered,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut res_img = Vec::with_capacity(offsets[0] as usize);
|
||||||
|
|
||||||
|
for (idx, offset) in offsets.iter().enumerate() {
|
||||||
|
if bytes[idx] as usize > self.limits.intermediate_buffer_size {
|
||||||
|
return Err(TiffError::LimitsExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.goto_offset(*offset)?;
|
||||||
|
let jpeg_reader = JpegReader::new(&mut self.reader, bytes[idx], &jpeg_tables)?;
|
||||||
|
let mut decoder = jpeg::Decoder::new(jpeg_reader);
|
||||||
|
|
||||||
|
match decoder.decode() {
|
||||||
|
Ok(mut val) => res_img.append(&mut val),
|
||||||
|
Err(e) => {
|
||||||
|
return match e {
|
||||||
|
jpeg::Error::Io(io_err) => Err(TiffError::IoError(io_err)),
|
||||||
|
jpeg::Error::Format(fmt_err) => {
|
||||||
|
Err(TiffError::FormatError(TiffFormatError::Format(fmt_err)))
|
||||||
|
}
|
||||||
|
jpeg::Error::Unsupported(_) => Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::UnknownInterpretation,
|
||||||
|
)),
|
||||||
|
jpeg::Error::Internal(_) => Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::UnknownInterpretation,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(DecodingResult::U8(res_img))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_strip_to_buffer(&mut self, mut buffer: DecodingBuffer) -> TiffResult<()> {
|
pub fn read_strip_to_buffer(&mut self, mut buffer: DecodingBuffer) -> TiffResult<()> {
|
||||||
self.initialize_strip_decoder()?;
|
self.initialize_strip_decoder()?;
|
||||||
|
|
||||||
@@ -733,18 +1047,17 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
TiffFormatError::InconsistentSizesEncountered,
|
TiffFormatError::InconsistentSizesEncountered,
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
let rows_per_strip = usize::try_from(self
|
let tag_rows = self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height);
|
||||||
.get_tag_u32(Tag::RowsPerStrip)
|
let rows_per_strip = usize::try_from(tag_rows)?;
|
||||||
.unwrap_or(self.height))?;
|
|
||||||
|
|
||||||
let strip_height = cmp::min(
|
let sized_width = usize::try_from(self.width)?;
|
||||||
rows_per_strip,
|
let sized_height = usize::try_from(self.height)?;
|
||||||
usize::try_from(self.height)? - index * rows_per_strip,
|
|
||||||
);
|
|
||||||
|
|
||||||
let buffer_size = usize::try_from(self.width)? * strip_height * self.bits_per_sample.len();
|
let strip_height = cmp::min(rows_per_strip, sized_height - index * rows_per_strip);
|
||||||
|
|
||||||
if buffer.len() < buffer_size || usize::try_from(byte_count)? / buffer.byte_len() > buffer_size {
|
let buffer_size = sized_width * strip_height * self.bits_per_sample.len();
|
||||||
|
|
||||||
|
if buffer.len() < buffer_size {
|
||||||
return Err(TiffError::FormatError(
|
return Err(TiffError::FormatError(
|
||||||
TiffFormatError::InconsistentSizesEncountered,
|
TiffFormatError::InconsistentSizesEncountered,
|
||||||
));
|
));
|
||||||
@@ -787,15 +1100,26 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
fn result_buffer(&self, height: usize) -> TiffResult<DecodingResult> {
|
fn result_buffer(&self, height: usize) -> TiffResult<DecodingResult> {
|
||||||
let buffer_size = usize::try_from(self.width)? * height * self.bits_per_sample.len();
|
let buffer_size = usize::try_from(self.width)? * height * self.bits_per_sample.len();
|
||||||
|
|
||||||
match self.bits_per_sample.iter().cloned().max().unwrap_or(8) {
|
let max_sample_bits = self.bits_per_sample.iter().cloned().max().unwrap_or(8);
|
||||||
|
match self.sample_format.first().unwrap_or(&SampleFormat::Uint) {
|
||||||
|
SampleFormat::Uint => match max_sample_bits {
|
||||||
n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits),
|
n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits),
|
||||||
n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits),
|
n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits),
|
||||||
n if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits),
|
n if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits),
|
||||||
n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits),
|
n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits),
|
||||||
n => {
|
n => Err(TiffError::UnsupportedError(
|
||||||
Err(TiffError::UnsupportedError(
|
|
||||||
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
||||||
))
|
)),
|
||||||
|
},
|
||||||
|
SampleFormat::IEEEFP => match max_sample_bits {
|
||||||
|
32 => DecodingResult::new_f32(buffer_size, &self.limits),
|
||||||
|
64 => DecodingResult::new_f64(buffer_size, &self.limits),
|
||||||
|
n => Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
format => {
|
||||||
|
Err(TiffUnsupportedError::UnsupportedSampleFormat(vec![format.clone()]).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -805,9 +1129,8 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
self.initialize_strip_decoder()?;
|
self.initialize_strip_decoder()?;
|
||||||
let index = self.strip_decoder.as_ref().unwrap().strip_index;
|
let index = self.strip_decoder.as_ref().unwrap().strip_index;
|
||||||
|
|
||||||
let rows_per_strip = usize::try_from(self
|
let rows_per_strip =
|
||||||
.get_tag_u32(Tag::RowsPerStrip)
|
usize::try_from(self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height))?;
|
||||||
.unwrap_or(self.height))?;
|
|
||||||
|
|
||||||
let strip_height = cmp::min(
|
let strip_height = cmp::min(
|
||||||
rows_per_strip,
|
rows_per_strip,
|
||||||
@@ -823,10 +1146,13 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
/// Decodes the entire image and return it as a Vector
|
/// Decodes the entire image and return it as a Vector
|
||||||
pub fn read_image(&mut self) -> TiffResult<DecodingResult> {
|
pub fn read_image(&mut self) -> TiffResult<DecodingResult> {
|
||||||
|
if self.compression_method == CompressionMethod::ModernJPEG {
|
||||||
|
return self.read_jpeg();
|
||||||
|
}
|
||||||
|
|
||||||
self.initialize_strip_decoder()?;
|
self.initialize_strip_decoder()?;
|
||||||
let rows_per_strip = usize::try_from(self
|
let rows_per_strip =
|
||||||
.get_tag_u32(Tag::RowsPerStrip)
|
usize::try_from(self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height))?;
|
||||||
.unwrap_or(self.height))?;
|
|
||||||
|
|
||||||
let samples_per_strip =
|
let samples_per_strip =
|
||||||
usize::try_from(self.width)? * rows_per_strip * self.bits_per_sample.len();
|
usize::try_from(self.width)? * rows_per_strip * self.bits_per_sample.len();
|
||||||
@@ -836,7 +1162,10 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
for i in 0..usize::try_from(self.strip_count()?)? {
|
for i in 0..usize::try_from(self.strip_count()?)? {
|
||||||
self.read_strip_to_buffer(result.as_buffer(samples_per_strip * i))?;
|
self.read_strip_to_buffer(result.as_buffer(samples_per_strip * i))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Checkpoint {
|
||||||
|
const INITIAL_OFFSET: u64 = 0;
|
||||||
|
}
|
||||||
+238
-44
@@ -1,8 +1,7 @@
|
|||||||
//! All IO functionality needed for TIFF decoding
|
//! All IO functionality needed for TIFF decoding
|
||||||
|
|
||||||
|
use crate::bytecast;
|
||||||
use crate::error::{TiffError, TiffResult};
|
use crate::error::{TiffError, TiffResult};
|
||||||
use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
|
|
||||||
use lzw;
|
|
||||||
use miniz_oxide::inflate;
|
use miniz_oxide::inflate;
|
||||||
use std::io::{self, Read, Seek};
|
use std::io::{self, Read, Seek};
|
||||||
|
|
||||||
@@ -23,84 +22,178 @@ pub trait EndianReader: Read {
|
|||||||
/// Reads an u16
|
/// Reads an u16
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn read_u16(&mut self) -> Result<u16, io::Error> {
|
fn read_u16(&mut self) -> Result<u16, io::Error> {
|
||||||
match self.byte_order() {
|
let mut n = [0u8; 2];
|
||||||
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_u16::<LittleEndian>(self),
|
self.read_exact(&mut n)?;
|
||||||
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_u16::<BigEndian>(self),
|
Ok(match self.byte_order() {
|
||||||
}
|
ByteOrder::LittleEndian => u16::from_le_bytes(n),
|
||||||
|
ByteOrder::BigEndian => u16::from_be_bytes(n),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn read_u16_into(&mut self, buffer: &mut [u16]) -> Result<(), io::Error> {
|
fn read_u16_into(&mut self, buffer: &mut [u16]) -> Result<(), io::Error> {
|
||||||
|
self.read_exact(bytecast::u16_as_ne_mut_bytes(buffer))?;
|
||||||
match self.byte_order() {
|
match self.byte_order() {
|
||||||
ByteOrder::LittleEndian => {
|
ByteOrder::LittleEndian => {
|
||||||
<Self as ReadBytesExt>::read_u16_into::<LittleEndian>(self, buffer)
|
for n in buffer {
|
||||||
|
*n = u16::from_le(*n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ByteOrder::BigEndian => {
|
ByteOrder::BigEndian => {
|
||||||
<Self as ReadBytesExt>::read_u16_into::<BigEndian>(self, buffer)
|
for n in buffer {
|
||||||
|
*n = u16::from_be(*n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Reads an i16
|
/// Reads an i16
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn read_i16(&mut self) -> Result<i16, io::Error> {
|
fn read_i16(&mut self) -> Result<i16, io::Error> {
|
||||||
match self.byte_order() {
|
let mut n = [0u8; 2];
|
||||||
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_i16::<LittleEndian>(self),
|
self.read_exact(&mut n)?;
|
||||||
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_i16::<BigEndian>(self),
|
Ok(match self.byte_order() {
|
||||||
}
|
ByteOrder::LittleEndian => i16::from_le_bytes(n),
|
||||||
|
ByteOrder::BigEndian => i16::from_be_bytes(n),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads an u32
|
/// Reads an u32
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn read_u32(&mut self) -> Result<u32, io::Error> {
|
fn read_u32(&mut self) -> Result<u32, io::Error> {
|
||||||
match self.byte_order() {
|
let mut n = [0u8; 4];
|
||||||
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_u32::<LittleEndian>(self),
|
self.read_exact(&mut n)?;
|
||||||
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_u32::<BigEndian>(self),
|
Ok(match self.byte_order() {
|
||||||
}
|
ByteOrder::LittleEndian => u32::from_le_bytes(n),
|
||||||
|
ByteOrder::BigEndian => u32::from_be_bytes(n),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn read_u32_into(&mut self, buffer: &mut [u32]) -> Result<(), io::Error> {
|
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() {
|
match self.byte_order() {
|
||||||
ByteOrder::LittleEndian => {
|
ByteOrder::LittleEndian => {
|
||||||
<Self as ReadBytesExt>::read_u32_into::<LittleEndian>(self, buffer)
|
for n in buffer {
|
||||||
|
*n = u32::from_le(*n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ByteOrder::BigEndian => {
|
ByteOrder::BigEndian => {
|
||||||
<Self as ReadBytesExt>::read_u32_into::<BigEndian>(self, buffer)
|
for n in buffer {
|
||||||
|
*n = u32::from_be(*n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
/// Reads an u64
|
|
||||||
#[inline(always)]
|
|
||||||
fn read_u64(&mut self) -> Result<u64, io::Error> {
|
|
||||||
match self.byte_order() {
|
|
||||||
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_u64::<LittleEndian>(self),
|
|
||||||
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_u64::<BigEndian>(self),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn read_u64_into(&mut self, buffer: &mut [u64]) -> Result<(), io::Error> {
|
|
||||||
match self.byte_order() {
|
|
||||||
ByteOrder::LittleEndian => {
|
|
||||||
<Self as ReadBytesExt>::read_u64_into::<LittleEndian>(self, buffer)
|
|
||||||
}
|
|
||||||
ByteOrder::BigEndian => {
|
|
||||||
<Self as ReadBytesExt>::read_u64_into::<BigEndian>(self, buffer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads an i32
|
/// 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> {
|
||||||
|
let mut n = [0u8; 4];
|
||||||
|
self.read_exact(&mut n)?;
|
||||||
|
Ok(match self.byte_order() {
|
||||||
|
ByteOrder::LittleEndian => i32::from_le_bytes(n),
|
||||||
|
ByteOrder::BigEndian => i32::from_be_bytes(n),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads an u64
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_u64(&mut self) -> Result<u64, io::Error> {
|
||||||
|
let mut n = [0u8; 8];
|
||||||
|
self.read_exact(&mut n)?;
|
||||||
|
Ok(match self.byte_order() {
|
||||||
|
ByteOrder::LittleEndian => u64::from_le_bytes(n),
|
||||||
|
ByteOrder::BigEndian => u64::from_be_bytes(n),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_u64_into(&mut self, buffer: &mut [u64]) -> Result<(), io::Error> {
|
||||||
|
self.read_exact(bytecast::u64_as_ne_mut_bytes(buffer))?;
|
||||||
match self.byte_order() {
|
match self.byte_order() {
|
||||||
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_i32::<LittleEndian>(self),
|
ByteOrder::LittleEndian => {
|
||||||
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_i32::<BigEndian>(self),
|
for n in buffer {
|
||||||
|
*n = u64::from_le(*n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ByteOrder::BigEndian => {
|
||||||
|
for n in buffer {
|
||||||
|
*n = u64::from_be(*n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads an f32
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_f32(&mut self) -> Result<f32, io::Error> {
|
||||||
|
let mut n = [0u8; 4];
|
||||||
|
self.read_exact(&mut n)?;
|
||||||
|
Ok(f32::from_bits(match self.byte_order() {
|
||||||
|
ByteOrder::LittleEndian => u32::from_le_bytes(n),
|
||||||
|
ByteOrder::BigEndian => u32::from_be_bytes(n),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_f64(&mut self) -> Result<f64, io::Error> {
|
||||||
|
let mut n = [0u8; 8];
|
||||||
|
self.read_exact(&mut n)?;
|
||||||
|
Ok(f64::from_bits(match self.byte_order() {
|
||||||
|
ByteOrder::LittleEndian => u64::from_le_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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// # READERS
|
||||||
|
///
|
||||||
|
|
||||||
|
///
|
||||||
|
/// ## Deflate Reader
|
||||||
|
///
|
||||||
|
|
||||||
/// Reader that decompresses DEFLATE streams
|
/// Reader that decompresses DEFLATE streams
|
||||||
pub struct DeflateReader {
|
pub struct DeflateReader {
|
||||||
@@ -148,6 +241,10 @@ impl EndianReader for DeflateReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// ## LZW Reader
|
||||||
|
///
|
||||||
|
|
||||||
/// 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>>,
|
||||||
@@ -168,12 +265,32 @@ impl LZWReader {
|
|||||||
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::with_capacity(max_uncompressed_length);
|
||||||
let mut decoder = lzw::DecoderEarlyChange::new(lzw::MsbReader::new(), 8);
|
let mut decoder = weezl::decode::Decoder::with_tiff_size_switch(weezl::BitOrder::Msb, 8);
|
||||||
let mut bytes_read = 0;
|
let mut bytes_read = 0;
|
||||||
while bytes_read < compressed_length && uncompressed.len() < max_uncompressed_length {
|
while bytes_read < compressed_length && uncompressed.len() < max_uncompressed_length {
|
||||||
let (len, bytes) = decoder.decode_bytes(&compressed[bytes_read..])?;
|
let bytes_written = uncompressed.len();
|
||||||
bytes_read += len;
|
uncompressed.reserve(1 << 12);
|
||||||
uncompressed.extend_from_slice(bytes);
|
let buffer_space = uncompressed.capacity();
|
||||||
|
uncompressed.resize(buffer_space, 0u8);
|
||||||
|
|
||||||
|
let result = decoder.decode_bytes(
|
||||||
|
&compressed[bytes_read..],
|
||||||
|
&mut uncompressed[bytes_written..],
|
||||||
|
);
|
||||||
|
bytes_read += result.consumed_in;
|
||||||
|
uncompressed.truncate(bytes_written + result.consumed_out);
|
||||||
|
|
||||||
|
match result.status {
|
||||||
|
Ok(weezl::LzwStatus::Ok) => {}
|
||||||
|
Ok(weezl::LzwStatus::Done) => break,
|
||||||
|
Ok(weezl::LzwStatus::NoProgress) => {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::UnexpectedEof,
|
||||||
|
"no lzw end code found",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Err(err) => return Err(io::Error::new(io::ErrorKind::InvalidData, err)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let bytes = uncompressed.len();
|
let bytes = uncompressed.len();
|
||||||
@@ -201,6 +318,79 @@ impl EndianReader for LZWReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// ## JPEG Reader (for "new-style" JPEG format (TIFF compression tag 7))
|
||||||
|
///
|
||||||
|
|
||||||
|
pub(crate) struct JpegReader {
|
||||||
|
buffer: io::Cursor<Vec<u8>>,
|
||||||
|
byte_order: ByteOrder,
|
||||||
|
}
|
||||||
|
impl JpegReader {
|
||||||
|
/// Constructs new JpegReader wrapping a SmartReader.
|
||||||
|
/// Because JPEG compression in TIFF allows to save quantization and/or huffman tables in one
|
||||||
|
/// central location, the constructor accepts this data as `jpeg_tables` here containing either
|
||||||
|
/// or both.
|
||||||
|
/// These `jpeg_tables` are simply prepended to the remaining jpeg image data.
|
||||||
|
/// Because these `jpeg_tables` start with a `SOI` (HEX: `0xFFD8`) or __start of image__ marker
|
||||||
|
/// which is also at the beginning of the remaining JPEG image data and would
|
||||||
|
/// confuse the JPEG renderer, one of these has to be taken off. In this case the first two
|
||||||
|
/// bytes of the remaining JPEG data is removed because it follows `jpeg_tables`.
|
||||||
|
/// Similary, `jpeg_tables` ends with a `EOI` (HEX: `0xFFD9`) or __end of image__ marker,
|
||||||
|
/// this has to be removed as well (last two bytes of `jpeg_tables`).
|
||||||
|
|
||||||
|
pub fn new<R>(
|
||||||
|
reader: &mut SmartReader<R>,
|
||||||
|
length: u32,
|
||||||
|
jpeg_tables: &Option<Vec<u8>>,
|
||||||
|
) -> io::Result<JpegReader>
|
||||||
|
where
|
||||||
|
R: Read + Seek,
|
||||||
|
{
|
||||||
|
let order = reader.byte_order;
|
||||||
|
|
||||||
|
// Read jpeg image data
|
||||||
|
let mut segment = vec![0; length as usize];
|
||||||
|
reader.read_exact(&mut segment[..])?;
|
||||||
|
|
||||||
|
match jpeg_tables {
|
||||||
|
Some(tables) => {
|
||||||
|
let mut jpeg_data = tables.clone();
|
||||||
|
let truncated_length = jpeg_data.len() - 2;
|
||||||
|
jpeg_data.truncate(truncated_length);
|
||||||
|
jpeg_data.extend_from_slice(&mut segment[2..]);
|
||||||
|
|
||||||
|
Ok(JpegReader {
|
||||||
|
buffer: io::Cursor::new(jpeg_data),
|
||||||
|
byte_order: order,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
None => Ok(JpegReader {
|
||||||
|
buffer: io::Cursor::new(segment),
|
||||||
|
byte_order: order,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for JpegReader {
|
||||||
|
// #[inline]
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
self.buffer.read(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EndianReader for JpegReader {
|
||||||
|
#[inline(always)]
|
||||||
|
fn byte_order(&self) -> ByteOrder {
|
||||||
|
self.byte_order
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// ## PackBits Reader
|
||||||
|
///
|
||||||
|
|
||||||
/// 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>>,
|
||||||
@@ -273,6 +463,10 @@ impl EndianReader for PackBitsReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// ## SmartReader Reader
|
||||||
|
///
|
||||||
|
|
||||||
/// Reader that is aware of the byte order.
|
/// Reader that is aware of the byte order.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SmartReader<R>
|
pub struct SmartReader<R>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use tags::PhotometricInterpretation;
|
use 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 {
|
||||||
@@ -8,6 +8,8 @@ pub trait ColorType {
|
|||||||
const TIFF_VALUE: PhotometricInterpretation;
|
const TIFF_VALUE: PhotometricInterpretation;
|
||||||
/// The value of the tiff tag `BitsPerSample`
|
/// The value of the tiff tag `BitsPerSample`
|
||||||
const BITS_PER_SAMPLE: &'static [u16];
|
const BITS_PER_SAMPLE: &'static [u16];
|
||||||
|
/// The value of the tiff tag `SampleFormat`
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Gray8;
|
pub struct Gray8;
|
||||||
@@ -15,6 +17,7 @@ impl ColorType for Gray8 {
|
|||||||
type Inner = u8;
|
type Inner = u8;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[8];
|
const BITS_PER_SAMPLE: &'static [u16] = &[8];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Gray16;
|
pub struct Gray16;
|
||||||
@@ -22,6 +25,7 @@ impl ColorType for Gray16 {
|
|||||||
type Inner = u16;
|
type Inner = u16;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[16];
|
const BITS_PER_SAMPLE: &'static [u16] = &[16];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Gray32;
|
pub struct Gray32;
|
||||||
@@ -29,6 +33,15 @@ impl ColorType for Gray32 {
|
|||||||
type Inner = u32;
|
type Inner = u32;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[32];
|
const BITS_PER_SAMPLE: &'static [u16] = &[32];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Gray32Float;
|
||||||
|
impl ColorType for Gray32Float {
|
||||||
|
type Inner = f32;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[32];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Gray64;
|
pub struct Gray64;
|
||||||
@@ -36,6 +49,15 @@ impl ColorType for Gray64 {
|
|||||||
type Inner = u64;
|
type Inner = u64;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[64];
|
const BITS_PER_SAMPLE: &'static [u16] = &[64];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Gray64Float;
|
||||||
|
impl ColorType for Gray64Float {
|
||||||
|
type Inner = f64;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[64];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RGB8;
|
pub struct RGB8;
|
||||||
@@ -43,6 +65,7 @@ impl ColorType for RGB8 {
|
|||||||
type Inner = u8;
|
type Inner = u8;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8];
|
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 3];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RGB16;
|
pub struct RGB16;
|
||||||
@@ -50,6 +73,7 @@ impl ColorType for RGB16 {
|
|||||||
type Inner = u16;
|
type Inner = u16;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16];
|
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 3];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RGB32;
|
pub struct RGB32;
|
||||||
@@ -57,6 +81,15 @@ impl ColorType for RGB32 {
|
|||||||
type Inner = u32;
|
type Inner = u32;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32];
|
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RGB32Float;
|
||||||
|
impl ColorType for RGB32Float {
|
||||||
|
type Inner = f32;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 3];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RGB64;
|
pub struct RGB64;
|
||||||
@@ -64,6 +97,15 @@ impl ColorType for RGB64 {
|
|||||||
type Inner = u64;
|
type Inner = u64;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64];
|
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RGB64Float;
|
||||||
|
impl ColorType for RGB64Float {
|
||||||
|
type Inner = f64;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 3];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RGBA8;
|
pub struct RGBA8;
|
||||||
@@ -71,6 +113,7 @@ impl ColorType for RGBA8 {
|
|||||||
type Inner = u8;
|
type Inner = u8;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8];
|
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RGBA16;
|
pub struct RGBA16;
|
||||||
@@ -78,20 +121,39 @@ impl ColorType for RGBA16 {
|
|||||||
type Inner = u16;
|
type Inner = u16;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16];
|
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RGBA32;
|
pub struct RGBA32;
|
||||||
impl ColorType for RGBA32 {
|
impl ColorType for RGBA32 {
|
||||||
type Inner = u16;
|
type Inner = u32;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
|
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RGBA32Float;
|
||||||
|
impl ColorType for RGBA32Float {
|
||||||
|
type Inner = f32;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RGBA64;
|
pub struct RGBA64;
|
||||||
impl ColorType for RGBA64 {
|
impl ColorType for RGBA64 {
|
||||||
type Inner = u16;
|
type Inner = u64;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
|
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RGBA64Float;
|
||||||
|
impl ColorType for RGBA64Float {
|
||||||
|
type Inner = f64;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CMYK8;
|
pub struct CMYK8;
|
||||||
@@ -99,6 +161,7 @@ impl ColorType for CMYK8 {
|
|||||||
type Inner = u8;
|
type Inner = u8;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8];
|
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CMYK16;
|
pub struct CMYK16;
|
||||||
@@ -106,6 +169,7 @@ impl ColorType for CMYK16 {
|
|||||||
type Inner = u16;
|
type Inner = u16;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16];
|
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CMYK32;
|
pub struct CMYK32;
|
||||||
@@ -113,6 +177,15 @@ impl ColorType for CMYK32 {
|
|||||||
type Inner = u32;
|
type Inner = u32;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
|
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CMYK32Float;
|
||||||
|
impl ColorType for CMYK32Float {
|
||||||
|
type Inner = f32;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CMYK64;
|
pub struct CMYK64;
|
||||||
@@ -120,4 +193,13 @@ impl ColorType for CMYK64 {
|
|||||||
type Inner = u64;
|
type Inner = u64;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
|
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CMYK64Float;
|
||||||
|
impl ColorType for CMYK64Float {
|
||||||
|
type Inner = f64;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 4];
|
||||||
}
|
}
|
||||||
+135
-54
@@ -1,11 +1,11 @@
|
|||||||
use byteorder::NativeEndian;
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::io::{Seek, Write};
|
use std::io::{Seek, Write};
|
||||||
use std::mem;
|
use std::{cmp, io, mem};
|
||||||
|
|
||||||
use tags::{self, ResolutionUnit, Tag, Type};
|
use bytecast;
|
||||||
use error::{TiffError, TiffFormatError, TiffResult};
|
use error::{TiffError, TiffFormatError, TiffResult};
|
||||||
|
use tags::{self, ResolutionUnit, Tag, Type};
|
||||||
|
|
||||||
pub mod colortype;
|
pub mod colortype;
|
||||||
mod writer;
|
mod writer;
|
||||||
@@ -60,9 +60,7 @@ impl TiffValue for [i8] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
// We write using nativeedian so this should be safe
|
let slice = bytecast::i8_as_ne_bytes(self);
|
||||||
let slice =
|
|
||||||
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len()) };
|
|
||||||
writer.write_bytes(slice)?;
|
writer.write_bytes(slice)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -77,9 +75,7 @@ impl TiffValue for [u16] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
// We write using nativeedian so this sould be safe
|
let slice = bytecast::u16_as_ne_bytes(self);
|
||||||
let slice =
|
|
||||||
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 2) };
|
|
||||||
writer.write_bytes(slice)?;
|
writer.write_bytes(slice)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -94,9 +90,7 @@ impl TiffValue for [i16] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
// We write using nativeedian so this should be safe
|
let slice = bytecast::i16_as_ne_bytes(self);
|
||||||
let slice =
|
|
||||||
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) };
|
|
||||||
writer.write_bytes(slice)?;
|
writer.write_bytes(slice)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -111,9 +105,7 @@ impl TiffValue for [u32] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
// We write using nativeedian so this sould be safe
|
let slice = bytecast::u32_as_ne_bytes(self);
|
||||||
let slice =
|
|
||||||
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 4) };
|
|
||||||
writer.write_bytes(slice)?;
|
writer.write_bytes(slice)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -128,9 +120,7 @@ impl TiffValue for [i32] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
// We write using nativeedian so this should be safe
|
let slice = bytecast::i32_as_ne_bytes(self);
|
||||||
let slice =
|
|
||||||
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) };
|
|
||||||
writer.write_bytes(slice)?;
|
writer.write_bytes(slice)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -144,10 +134,40 @@ impl TiffValue for [u64] {
|
|||||||
self.len() as 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<()> {
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
// We write using nativeedian so this sould be safe
|
// We write using nativeedian so this sould be safe
|
||||||
let slice =
|
let slice = bytecast::f32_as_ne_bytes(self);
|
||||||
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 8) };
|
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)?;
|
writer.write_bytes(slice)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -283,6 +303,34 @@ impl TiffValue for u64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
impl TiffValue for Rational {
|
||||||
const BYTE_LEN: u32 = 8;
|
const BYTE_LEN: u32 = 8;
|
||||||
const FIELD_TYPE: Type = Type::RATIONAL;
|
const FIELD_TYPE: Type = Type::RATIONAL;
|
||||||
@@ -375,7 +423,7 @@ impl<W: Write + Seek> TiffEncoder<W> {
|
|||||||
writer: TiffWriter::new(writer),
|
writer: TiffWriter::new(writer),
|
||||||
};
|
};
|
||||||
|
|
||||||
NativeEndian::write_header(&mut encoder.writer)?;
|
write_tiff_header(&mut encoder.writer)?;
|
||||||
// blank the IFD offset location
|
// blank the IFD offset location
|
||||||
encoder.writer.write_u32(0)?;
|
encoder.writer.write_u32(0)?;
|
||||||
|
|
||||||
@@ -407,26 +455,9 @@ impl<W: Write + Seek> TiffEncoder<W> {
|
|||||||
where
|
where
|
||||||
[C::Inner]: TiffValue,
|
[C::Inner]: TiffValue,
|
||||||
{
|
{
|
||||||
let num_pix = usize::try_from(width)?.checked_mul(usize::try_from(height)?)
|
|
||||||
.ok_or_else(|| ::std::io::Error::new(
|
|
||||||
::std::io::ErrorKind::InvalidInput,
|
|
||||||
"Image width * height exceeds usize"))?;
|
|
||||||
if data.len() < num_pix {
|
|
||||||
return Err(::std::io::Error::new(
|
|
||||||
::std::io::ErrorKind::InvalidData,
|
|
||||||
"Input data slice is undersized for provided dimensions").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let encoder = DirectoryEncoder::new(&mut self.writer)?;
|
let encoder = DirectoryEncoder::new(&mut self.writer)?;
|
||||||
let mut image: ImageEncoder<W, C> = ImageEncoder::new(encoder, width, height)?;
|
let image: ImageEncoder<W, C> = ImageEncoder::new(encoder, width, height)?;
|
||||||
|
image.write_data(data)
|
||||||
let mut idx = 0;
|
|
||||||
while image.next_strip_sample_count() > 0 {
|
|
||||||
let sample_count = usize::try_from(image.next_strip_sample_count())?;
|
|
||||||
image.write_strip(&data[idx..idx + sample_count])?;
|
|
||||||
idx += sample_count;
|
|
||||||
}
|
|
||||||
image.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,8 +495,10 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
|||||||
value.write(&mut writer)?;
|
value.write(&mut writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ifd
|
self.ifd.insert(
|
||||||
.insert(tag.to_u16(), (<T>::FIELD_TYPE.to_u16(), value.count(), bytes));
|
tag.to_u16(),
|
||||||
|
(<T>::FIELD_TYPE.to_u16(), value.count(), bytes),
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -548,10 +581,14 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
|
|||||||
/// # let mut file = std::io::Cursor::new(Vec::new());
|
/// # let mut file = std::io::Cursor::new(Vec::new());
|
||||||
/// # let image_data = vec![0; 100*100*3];
|
/// # let image_data = vec![0; 100*100*3];
|
||||||
/// use tiff::encoder::*;
|
/// use tiff::encoder::*;
|
||||||
|
/// use tiff::tags::Tag;
|
||||||
///
|
///
|
||||||
/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
||||||
/// let mut image = tiff.new_image::<colortype::RGB8>(100, 100).unwrap();
|
/// let mut image = tiff.new_image::<colortype::RGB8>(100, 100).unwrap();
|
||||||
///
|
///
|
||||||
|
/// // You can encode tags here
|
||||||
|
/// image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap();
|
||||||
|
///
|
||||||
/// let mut idx = 0;
|
/// 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;
|
||||||
@@ -561,11 +598,13 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
|
|||||||
/// image.finish().unwrap();
|
/// image.finish().unwrap();
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
/// You can also call write_data function wich will encode by strip and finish
|
||||||
pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType> {
|
pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType> {
|
||||||
encoder: DirectoryEncoder<'a, W>,
|
encoder: DirectoryEncoder<'a, W>,
|
||||||
strip_idx: u64,
|
strip_idx: u64,
|
||||||
strip_count: u64,
|
strip_count: u64,
|
||||||
row_samples: u64,
|
row_samples: u64,
|
||||||
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
rows_per_strip: u64,
|
rows_per_strip: u64,
|
||||||
strip_offsets: Vec<u32>,
|
strip_offsets: Vec<u32>,
|
||||||
@@ -593,11 +632,16 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
|||||||
encoder.write_tag(Tag::Compression, tags::CompressionMethod::None.to_u16())?;
|
encoder.write_tag(Tag::Compression, tags::CompressionMethod::None.to_u16())?;
|
||||||
|
|
||||||
encoder.write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE)?;
|
encoder.write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE)?;
|
||||||
|
let sample_format: Vec<_> = <T>::SAMPLE_FORMAT.iter().map(|s| s.to_u16()).collect();
|
||||||
|
encoder.write_tag(Tag::SampleFormat, &sample_format[..])?;
|
||||||
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16())?;
|
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16())?;
|
||||||
|
|
||||||
encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?;
|
encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?;
|
||||||
|
|
||||||
encoder.write_tag(Tag::SamplesPerPixel, u16::try_from(<T>::BITS_PER_SAMPLE.len())?)?;
|
encoder.write_tag(
|
||||||
|
Tag::SamplesPerPixel,
|
||||||
|
u16::try_from(<T>::BITS_PER_SAMPLE.len())?,
|
||||||
|
)?;
|
||||||
encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 })?;
|
encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 })?;
|
||||||
encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 })?;
|
encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 })?;
|
||||||
encoder.write_tag(Tag::ResolutionUnit, ResolutionUnit::None.to_u16())?;
|
encoder.write_tag(Tag::ResolutionUnit, ResolutionUnit::None.to_u16())?;
|
||||||
@@ -608,6 +652,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
|||||||
strip_idx: 0,
|
strip_idx: 0,
|
||||||
row_samples,
|
row_samples,
|
||||||
rows_per_strip,
|
rows_per_strip,
|
||||||
|
width,
|
||||||
height,
|
height,
|
||||||
strip_offsets: Vec::new(),
|
strip_offsets: Vec::new(),
|
||||||
strip_byte_count: Vec::new(),
|
strip_byte_count: Vec::new(),
|
||||||
@@ -622,11 +667,9 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let start_row = ::std::cmp::min(u64::from(self.height), self.strip_idx * self.rows_per_strip);
|
let raw_start_row = self.strip_idx * self.rows_per_strip;
|
||||||
let end_row = ::std::cmp::min(
|
let start_row = cmp::min(u64::from(self.height), raw_start_row);
|
||||||
u64::from(self.height),
|
let end_row = cmp::min(u64::from(self.height), raw_start_row + self.rows_per_strip);
|
||||||
(self.strip_idx + 1) * self.rows_per_strip,
|
|
||||||
);
|
|
||||||
|
|
||||||
(end_row - start_row) * self.row_samples
|
(end_row - start_row) * self.row_samples
|
||||||
}
|
}
|
||||||
@@ -639,9 +682,11 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
|||||||
// TODO: Compression
|
// TODO: Compression
|
||||||
let samples = self.next_strip_sample_count();
|
let samples = self.next_strip_sample_count();
|
||||||
if u64::try_from(value.len())? != samples {
|
if u64::try_from(value.len())? != samples {
|
||||||
return Err(::std::io::Error::new(
|
return Err(io::Error::new(
|
||||||
::std::io::ErrorKind::InvalidData,
|
io::ErrorKind::InvalidData,
|
||||||
"Slice is wrong size for strip").into());
|
"Slice is wrong size for strip",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset = self.encoder.write_data(value)?;
|
let offset = self.encoder.write_data(value)?;
|
||||||
@@ -652,16 +697,52 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write strips from data
|
||||||
|
pub fn write_data(mut self, data: &[T::Inner]) -> TiffResult<()>
|
||||||
|
where
|
||||||
|
[T::Inner]: TiffValue,
|
||||||
|
{
|
||||||
|
let num_pix = usize::try_from(self.width)?
|
||||||
|
.checked_mul(usize::try_from(self.height)?)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"Image width * height exceeds usize",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
if data.len() < num_pix {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"Input data slice is undersized for provided dimensions",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
let mut idx = 0;
|
||||||
|
while self.next_strip_sample_count() > 0 {
|
||||||
|
let sample_count = usize::try_from(self.next_strip_sample_count())?;
|
||||||
|
self.write_strip(&data[idx..idx + sample_count])?;
|
||||||
|
idx += sample_count;
|
||||||
|
}
|
||||||
|
self.finish()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Set image resolution
|
/// Set image resolution
|
||||||
pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) {
|
pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) {
|
||||||
self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap();
|
self.encoder
|
||||||
self.encoder.write_tag(Tag::XResolution, value.clone()).unwrap();
|
.write_tag(Tag::ResolutionUnit, unit.to_u16())
|
||||||
|
.unwrap();
|
||||||
|
self.encoder
|
||||||
|
.write_tag(Tag::XResolution, value.clone())
|
||||||
|
.unwrap();
|
||||||
self.encoder.write_tag(Tag::YResolution, value).unwrap();
|
self.encoder.write_tag(Tag::YResolution, value).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set image resolution unit
|
/// Set image resolution unit
|
||||||
pub fn resolution_unit(&mut self, unit: ResolutionUnit) {
|
pub fn resolution_unit(&mut self, unit: ResolutionUnit) {
|
||||||
self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap();
|
self.encoder
|
||||||
|
.write_tag(Tag::ResolutionUnit, unit.to_u16())
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set image x-resolution
|
/// Set image x-resolution
|
||||||
|
|||||||
+28
-26
@@ -1,30 +1,18 @@
|
|||||||
use error::TiffResult;
|
use error::TiffResult;
|
||||||
use byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian, WriteBytesExt};
|
|
||||||
use std::io::{self, Seek, SeekFrom, Write};
|
use std::io::{self, Seek, SeekFrom, Write};
|
||||||
|
|
||||||
pub trait TiffByteOrder: ByteOrder {
|
pub fn write_tiff_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
fn write_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;
|
||||||
|
|
||||||
impl TiffByteOrder for LittleEndian {
|
writer.writer.write_all(&[boi, boi])?;
|
||||||
fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
writer.writer.write_all(&42u16.to_ne_bytes())?;
|
||||||
writer.writer.write_u16::<LittleEndian>(0x4949)?;
|
|
||||||
writer.writer.write_u16::<LittleEndian>(42)?;
|
|
||||||
writer.offset += 4;
|
writer.offset += 4;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffByteOrder for BigEndian {
|
|
||||||
fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
|
||||||
writer.writer.write_u16::<BigEndian>(0x4d4d)?;
|
|
||||||
writer.writer.write_u16::<BigEndian>(42)?;
|
|
||||||
writer.offset += 4;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TiffWriter<W> {
|
pub struct TiffWriter<W> {
|
||||||
writer: W,
|
writer: W,
|
||||||
@@ -47,47 +35,61 @@ impl<W: Write> TiffWriter<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_u8(&mut self, n: u8) -> Result<(), io::Error> {
|
pub fn write_u8(&mut self, n: u8) -> Result<(), io::Error> {
|
||||||
self.writer.write_u8(n)?;
|
self.writer.write_all(&n.to_ne_bytes())?;
|
||||||
self.offset += 1;
|
self.offset += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_i8(&mut self, n: i8) -> Result<(), io::Error> {
|
pub fn write_i8(&mut self, n: i8) -> Result<(), io::Error> {
|
||||||
self.writer.write_i8(n)?;
|
self.writer.write_all(&n.to_ne_bytes())?;
|
||||||
self.offset += 1;
|
self.offset += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_u16(&mut self, n: u16) -> Result<(), io::Error> {
|
pub fn write_u16(&mut self, n: u16) -> Result<(), io::Error> {
|
||||||
self.writer.write_u16::<NativeEndian>(n)?;
|
self.writer.write_all(&n.to_ne_bytes())?;
|
||||||
self.offset += 2;
|
self.offset += 2;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_i16(&mut self, n: i16) -> Result<(), io::Error> {
|
pub fn write_i16(&mut self, n: i16) -> Result<(), io::Error> {
|
||||||
self.writer.write_i16::<NativeEndian>(n)?;
|
self.writer.write_all(&n.to_ne_bytes())?;
|
||||||
self.offset += 2;
|
self.offset += 2;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_u32(&mut self, n: u32) -> Result<(), io::Error> {
|
pub fn write_u32(&mut self, n: u32) -> Result<(), io::Error> {
|
||||||
self.writer.write_u32::<NativeEndian>(n)?;
|
self.writer.write_all(&n.to_ne_bytes())?;
|
||||||
self.offset += 4;
|
self.offset += 4;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_i32(&mut self, n: i32) -> Result<(), io::Error> {
|
pub fn write_i32(&mut self, n: i32) -> Result<(), io::Error> {
|
||||||
self.writer.write_i32::<NativeEndian>(n)?;
|
self.writer.write_all(&n.to_ne_bytes())?;
|
||||||
self.offset += 4;
|
self.offset += 4;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_u64(&mut self, n: u64) -> Result<(), io::Error> {
|
pub fn write_u64(&mut self, n: u64) -> Result<(), io::Error> {
|
||||||
self.writer.write_u64::<NativeEndian>(n)?;
|
self.writer.write_all(&n.to_ne_bytes())?;
|
||||||
|
self.offset += 8;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_f32(&mut self, n: f32) -> Result<(), io::Error> {
|
||||||
|
self.writer.write_all(&u32::to_ne_bytes(n.to_bits()))?;
|
||||||
|
self.offset += 4;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_f64(&mut self, n: f64) -> Result<(), io::Error> {
|
||||||
|
self.writer.write_all(&u64::to_ne_bytes(n.to_bits()))?;
|
||||||
self.offset += 8;
|
self.offset += 8;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
+16
-5
@@ -3,9 +3,9 @@ use std::fmt;
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::string;
|
use std::string;
|
||||||
|
|
||||||
use decoder::ifd::{Value};
|
use decoder::ifd::Value;
|
||||||
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Tag};
|
|
||||||
use miniz_oxide::inflate::TINFLStatus;
|
use miniz_oxide::inflate::TINFLStatus;
|
||||||
|
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag};
|
||||||
use ColorType;
|
use ColorType;
|
||||||
|
|
||||||
/// Tiff error kinds.
|
/// Tiff error kinds.
|
||||||
@@ -35,7 +35,7 @@ pub enum TiffError {
|
|||||||
///
|
///
|
||||||
/// The list of variants may grow to incorporate errors of future features. Matching against this
|
/// The list of variants may grow to incorporate errors of future features. Matching against this
|
||||||
/// exhaustively is not covered by interface stability guarantees.
|
/// exhaustively is not covered by interface stability guarantees.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum TiffFormatError {
|
pub enum TiffFormatError {
|
||||||
TiffSignatureNotFound,
|
TiffSignatureNotFound,
|
||||||
TiffSignatureInvalid,
|
TiffSignatureInvalid,
|
||||||
@@ -45,9 +45,12 @@ pub enum TiffFormatError {
|
|||||||
InvalidTagValueType(Tag),
|
InvalidTagValueType(Tag),
|
||||||
RequiredTagNotFound(Tag),
|
RequiredTagNotFound(Tag),
|
||||||
UnknownPredictor(u16),
|
UnknownPredictor(u16),
|
||||||
|
ByteExpected(Value),
|
||||||
UnsignedIntegerExpected(Value),
|
UnsignedIntegerExpected(Value),
|
||||||
SignedIntegerExpected(Value),
|
SignedIntegerExpected(Value),
|
||||||
InflateError(InflateError),
|
InflateError(InflateError),
|
||||||
|
Format(String),
|
||||||
|
RequiredTagEmpty(Tag),
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
/// Do not match against this variant. It may get removed.
|
/// Do not match against this variant. It may get removed.
|
||||||
__NonExhaustive,
|
__NonExhaustive,
|
||||||
@@ -62,12 +65,14 @@ impl fmt::Display for TiffFormatError {
|
|||||||
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."),
|
||||||
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)
|
||||||
|
}
|
||||||
RequiredTagNotFound(ref tag) => write!(fmt, "Required tag `{:?}` not found.", tag),
|
RequiredTagNotFound(ref tag) => write!(fmt, "Required tag `{:?}` not found.", tag),
|
||||||
UnknownPredictor(ref predictor) => {
|
UnknownPredictor(ref predictor) => {
|
||||||
write!(fmt, "Unknown predictor “{}” encountered", predictor)
|
write!(fmt, "Unknown predictor “{}” encountered", predictor)
|
||||||
}
|
}
|
||||||
|
ByteExpected(ref val) => write!(fmt, "Expected byte, {:?} found.", val),
|
||||||
UnsignedIntegerExpected(ref val) => {
|
UnsignedIntegerExpected(ref val) => {
|
||||||
write!(fmt, "Expected unsigned integer, {:?} found.", val)
|
write!(fmt, "Expected unsigned integer, {:?} found.", val)
|
||||||
}
|
}
|
||||||
@@ -75,6 +80,8 @@ impl fmt::Display for TiffFormatError {
|
|||||||
write!(fmt, "Expected signed integer, {:?} found.", val)
|
write!(fmt, "Expected signed integer, {:?} found.", val)
|
||||||
}
|
}
|
||||||
InflateError(_) => write!(fmt, "Failed to decode inflate data."),
|
InflateError(_) => write!(fmt, "Failed to decode inflate data."),
|
||||||
|
Format(ref val) => write!(fmt, "Invalid format: {:?}.", val),
|
||||||
|
RequiredTagEmpty(ref val) => write!(fmt, "Required tag {:?} was empty.", val),
|
||||||
__NonExhaustive => unreachable!(),
|
__NonExhaustive => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,6 +121,7 @@ pub enum TiffUnsupportedError {
|
|||||||
UnknownCompressionMethod,
|
UnknownCompressionMethod,
|
||||||
UnsupportedCompressionMethod(CompressionMethod),
|
UnsupportedCompressionMethod(CompressionMethod),
|
||||||
UnsupportedSampleDepth(u8),
|
UnsupportedSampleDepth(u8),
|
||||||
|
UnsupportedSampleFormat(Vec<SampleFormat>),
|
||||||
UnsupportedColorType(ColorType),
|
UnsupportedColorType(ColorType),
|
||||||
UnsupportedBitsPerChannel(u8),
|
UnsupportedBitsPerChannel(u8),
|
||||||
UnsupportedPlanarConfig(Option<PlanarConfiguration>),
|
UnsupportedPlanarConfig(Option<PlanarConfiguration>),
|
||||||
@@ -148,6 +156,9 @@ impl fmt::Display for TiffUnsupportedError {
|
|||||||
UnsupportedSampleDepth(samples) => {
|
UnsupportedSampleDepth(samples) => {
|
||||||
write!(fmt, "{} samples per pixel is unsupported.", samples)
|
write!(fmt, "{} samples per pixel is unsupported.", samples)
|
||||||
}
|
}
|
||||||
|
UnsupportedSampleFormat(ref formats) => {
|
||||||
|
write!(fmt, "Sample format {:?} is unsupported.", formats)
|
||||||
|
}
|
||||||
UnsupportedColorType(color_type) => {
|
UnsupportedColorType(color_type) => {
|
||||||
write!(fmt, "Color type {:?} is unsupported", color_type)
|
write!(fmt, "Color type {:?} is unsupported", color_type)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -4,12 +4,13 @@
|
|||||||
//! lossless and lossy compression.
|
//! lossless and lossy compression.
|
||||||
//!
|
//!
|
||||||
//! # Related Links
|
//! # Related Links
|
||||||
//! * <http://partners.adobe.com/public/developer/tiff/index.html> - The TIFF specification
|
//! * <https://www.adobe.io/open/standards/TIFF.html> - The TIFF specification
|
||||||
|
|
||||||
extern crate byteorder;
|
extern crate jpeg;
|
||||||
extern crate lzw;
|
|
||||||
extern crate miniz_oxide;
|
extern crate miniz_oxide;
|
||||||
|
extern crate weezl;
|
||||||
|
|
||||||
|
mod bytecast;
|
||||||
pub mod decoder;
|
pub mod decoder;
|
||||||
pub mod encoder;
|
pub mod encoder;
|
||||||
mod error;
|
mod error;
|
||||||
|
|||||||
+34
-10
@@ -39,16 +39,24 @@ macro_rules! tags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tags!($name, $ty);
|
tags!($name, $ty, $($unknown_doc)*);
|
||||||
};
|
};
|
||||||
// For u16 tags, provide direct inherent primitive conversion methods.
|
// For u16 tags, provide direct inherent primitive conversion methods.
|
||||||
($name:tt, u16) => {
|
($name:tt, u16, $($unknown_doc:literal)*) => {
|
||||||
impl $name {
|
impl $name {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_u16(val: u16) -> Option<Self> {
|
pub fn from_u16(val: u16) -> Option<Self> {
|
||||||
Self::__from_inner_type(val).ok()
|
Self::__from_inner_type(val).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn from_u16_exhaustive(val: u16) -> Self {
|
||||||
|
$unknown_doc;
|
||||||
|
Self::__from_inner_type(val).unwrap_or_else(|_| $name::Unknown(val))
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn to_u16(&self) -> u16 {
|
pub fn to_u16(&self) -> u16 {
|
||||||
Self::__to_inner_type(self)
|
Self::__to_inner_type(self)
|
||||||
@@ -57,7 +65,7 @@ macro_rules! tags {
|
|||||||
};
|
};
|
||||||
// For other tag types, do nothing for now. With concat_idents one could
|
// For other tag types, do nothing for now. With concat_idents one could
|
||||||
// provide inherent conversion methods for all types.
|
// provide inherent conversion methods for all types.
|
||||||
($name:tt, $ty:tt) => {};
|
($name:tt, $ty:tt, $($unknown_doc:literal)*) => {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: These tags appear in the order they are mentioned in the TIFF reference
|
// Note: These tags appear in the order they are mentioned in the TIFF reference
|
||||||
@@ -103,14 +111,15 @@ 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,
|
||||||
}
|
TileLength = 323,
|
||||||
|
TileOffsets = 324,
|
||||||
impl Tag {
|
TileByteCounts = 325,
|
||||||
pub fn from_u16_exhaustive(val: u16) -> Self {
|
SampleFormat = 339,
|
||||||
Self::from_u16(val).unwrap_or_else(|| Tag::Unknown(val))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,16 +132,20 @@ pub enum Type(u16) {
|
|||||||
LONG = 4,
|
LONG = 4,
|
||||||
RATIONAL = 5,
|
RATIONAL = 5,
|
||||||
SBYTE = 6,
|
SBYTE = 6,
|
||||||
|
UNDEFINED = 7,
|
||||||
SSHORT = 8,
|
SSHORT = 8,
|
||||||
SLONG = 9,
|
SLONG = 9,
|
||||||
SRATIONAL = 10,
|
SRATIONAL = 10,
|
||||||
|
FLOAT = 11,
|
||||||
|
DOUBLE = 12,
|
||||||
/// BigTIFF 64-bit unsigned integer
|
/// BigTIFF 64-bit unsigned integer
|
||||||
LONG8 = 16,
|
LONG8 = 16,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
tags! {
|
tags! {
|
||||||
|
/// See [TIFF compression tags](https://www.awaresystems.be/imaging/tiff/tifftags/compression.html)
|
||||||
|
/// for reference.
|
||||||
pub enum CompressionMethod(u16) {
|
pub enum CompressionMethod(u16) {
|
||||||
None = 1,
|
None = 1,
|
||||||
Huffman = 2,
|
Huffman = 2,
|
||||||
@@ -140,6 +153,8 @@ pub enum CompressionMethod(u16) {
|
|||||||
Fax4 = 4,
|
Fax4 = 4,
|
||||||
LZW = 5,
|
LZW = 5,
|
||||||
JPEG = 6,
|
JPEG = 6,
|
||||||
|
// "Extended JPEG" or "new JPEG" style
|
||||||
|
ModernJPEG = 7,
|
||||||
Deflate = 8,
|
Deflate = 8,
|
||||||
OldDeflate = 0x80B2,
|
OldDeflate = 0x80B2,
|
||||||
PackBits = 0x8005,
|
PackBits = 0x8005,
|
||||||
@@ -181,3 +196,12 @@ pub enum ResolutionUnit(u16) {
|
|||||||
Centimeter = 3,
|
Centimeter = 3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tags! {
|
||||||
|
pub enum SampleFormat(u16) unknown("An unknown extension sample format") {
|
||||||
|
Uint = 1,
|
||||||
|
Int = 2,
|
||||||
|
IEEEFP = 3,
|
||||||
|
Void = 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
Copyrights:
|
||||||
|
|
||||||
|
kodim*.png: Eastman Kodak Company, released for unrestricted use
|
||||||
|
Transparency.png: Public Domain, according to Wikimedia
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,46 @@
|
|||||||
|
extern crate tiff;
|
||||||
|
|
||||||
|
use tiff::decoder::Decoder;
|
||||||
|
use tiff::tags::Tag;
|
||||||
|
use tiff::ColorType;
|
||||||
|
|
||||||
|
use std::fs::File;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
const TEST_IMAGE_DIR: &str = "./tests/images/bigtiff";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_big_tiff() {
|
||||||
|
let filenames = ["BigTIFF.tif", "BigTIFFMotorola.tif", "BigTIFFLong.tif"];
|
||||||
|
for filename in filenames.iter() {
|
||||||
|
let path = PathBuf::from(TEST_IMAGE_DIR).join(filename);
|
||||||
|
let img_file = File::open(path).expect("Cannot find test image!");
|
||||||
|
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||||
|
assert_eq!(
|
||||||
|
decoder.dimensions().expect("Cannot get dimensions"),
|
||||||
|
(64, 64)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
decoder.colortype().expect("Cannot get colortype"),
|
||||||
|
ColorType::RGB(8)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
decoder
|
||||||
|
.get_tag_u64(Tag::StripOffsets)
|
||||||
|
.expect("Cannot get StripOffsets"),
|
||||||
|
16
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
decoder
|
||||||
|
.get_tag_u64(Tag::RowsPerStrip)
|
||||||
|
.expect("Cannot get RowsPerStrip"),
|
||||||
|
64
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
decoder
|
||||||
|
.get_tag_u64(Tag::StripByteCounts)
|
||||||
|
.expect("Cannot get StripByteCounts"),
|
||||||
|
12288
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+36
-2
@@ -21,7 +21,7 @@ macro_rules! test_image_sum {
|
|||||||
DecodingResult::$buffer(res) => {
|
DecodingResult::$buffer(res) => {
|
||||||
let sum: $sum_ty = res.into_iter().map(<$sum_ty>::from).sum();
|
let sum: $sum_ty = res.into_iter().map(<$sum_ty>::from).sum();
|
||||||
assert_eq!(sum, expected_sum);
|
assert_eq!(sum, expected_sum);
|
||||||
},
|
}
|
||||||
_ => panic!("Wrong bit depth"),
|
_ => panic!("Wrong bit depth"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,6 +32,8 @@ test_image_sum!(test_image_sum_u8, U8, u64);
|
|||||||
test_image_sum!(test_image_sum_u16, U16, u64);
|
test_image_sum!(test_image_sum_u16, U16, u64);
|
||||||
test_image_sum!(test_image_sum_u32, U32, u64);
|
test_image_sum!(test_image_sum_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_f64, F64, f64);
|
||||||
|
|
||||||
/// Tests that a decoder can be constructed for an image and the color type
|
/// Tests that a decoder can be constructed for an image and the color type
|
||||||
/// read from the IFD and is of the appropriate type, but the type is
|
/// read from the IFD and is of the appropriate type, but the type is
|
||||||
@@ -42,7 +44,9 @@ fn test_image_color_type_unsupported(file: &str, expected_type: ColorType) {
|
|||||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||||
assert_eq!(decoder.colortype().unwrap(), expected_type);
|
assert_eq!(decoder.colortype().unwrap(), expected_type);
|
||||||
assert!(match decoder.read_image() {
|
assert!(match decoder.read_image() {
|
||||||
Err(tiff::TiffError::UnsupportedError(tiff::TiffUnsupportedError::UnsupportedColorType(_))) => true,
|
Err(tiff::TiffError::UnsupportedError(
|
||||||
|
tiff::TiffUnsupportedError::UnsupportedColorType(_),
|
||||||
|
)) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -57,6 +61,11 @@ fn test_cmyk_u16() {
|
|||||||
test_image_sum_u16("cmyk-3c-16b.tiff", ColorType::CMYK(16), 2181426827);
|
test_image_sum_u16("cmyk-3c-16b.tiff", ColorType::CMYK(16), 2181426827);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cmyk_f32() {
|
||||||
|
test_image_sum_f32("cmyk-3c-32b-float.tiff", ColorType::CMYK(32), 496.0405);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_gray_u8() {
|
fn test_gray_u8() {
|
||||||
test_image_sum_u8("minisblack-1c-8b.tiff", ColorType::Gray(8), 2840893);
|
test_image_sum_u8("minisblack-1c-8b.tiff", ColorType::Gray(8), 2840893);
|
||||||
@@ -82,6 +91,20 @@ fn test_gray_u64() {
|
|||||||
test_image_sum_u64("gradient-1c-64b.tiff", ColorType::Gray(64), 549892913787);
|
test_image_sum_u64("gradient-1c-64b.tiff", ColorType::Gray(64), 549892913787);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gray_f32() {
|
||||||
|
test_image_sum_f32("gradient-1c-32b-float.tiff", ColorType::Gray(32), 128.03194);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gray_f64() {
|
||||||
|
test_image_sum_f64(
|
||||||
|
"gradient-1c-64b-float.tiff",
|
||||||
|
ColorType::Gray(64),
|
||||||
|
128.0319210877642,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rgb_u8() {
|
fn test_rgb_u8() {
|
||||||
test_image_sum_u8("rgb-3c-8b.tiff", ColorType::RGB(8), 7842108);
|
test_image_sum_u8("rgb-3c-8b.tiff", ColorType::RGB(8), 7842108);
|
||||||
@@ -107,6 +130,11 @@ fn test_rgb_u64() {
|
|||||||
test_image_sum_u64("gradient-3c-64b.tiff", ColorType::RGB(64), 2030834111716);
|
test_image_sum_u64("gradient-3c-64b.tiff", ColorType::RGB(64), 2030834111716);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rgb_f32() {
|
||||||
|
test_image_sum_f32("gradient-3c-32b-float.tiff", ColorType::RGB(32), 472.8405);
|
||||||
|
}
|
||||||
|
|
||||||
#[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
|
||||||
@@ -149,6 +177,12 @@ fn test_decode_data() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn issue_69() {
|
||||||
|
test_image_sum_u16("issue_69_lzw.tiff", ColorType::Gray(16), 1015486);
|
||||||
|
test_image_sum_u16("issue_69_packbits.tiff", ColorType::Gray(16), 1015486);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: GrayA support
|
// TODO: GrayA support
|
||||||
//#[test]
|
//#[test]
|
||||||
//fn test_gray_alpha_u8()
|
//fn test_gray_alpha_u8()
|
||||||
|
|||||||
+144
-44
@@ -1,9 +1,9 @@
|
|||||||
extern crate tiff;
|
extern crate tiff;
|
||||||
|
|
||||||
use tiff::decoder::{Decoder, DecodingResult};
|
use tiff::decoder::{ifd, Decoder, DecodingResult};
|
||||||
use tiff::encoder::{colortype, TiffEncoder, SRational};
|
use tiff::encoder::{colortype, SRational, TiffEncoder};
|
||||||
use tiff::ColorType;
|
|
||||||
use tiff::tags::Tag;
|
use tiff::tags::Tag;
|
||||||
|
use tiff::ColorType;
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Cursor, Seek, SeekFrom};
|
use std::io::{Cursor, Seek, SeekFrom};
|
||||||
@@ -24,14 +24,22 @@ fn encode_decode() {
|
|||||||
{
|
{
|
||||||
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)
|
let mut image = tiff.new_image::<colortype::RGB8>(100, 100).unwrap();
|
||||||
|
image
|
||||||
|
.encoder()
|
||||||
|
.write_tag(Tag::Artist, "Image-tiff")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
image.write_data(&image_data).unwrap();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
file.seek(SeekFrom::Start(0)).unwrap();
|
file.seek(SeekFrom::Start(0)).unwrap();
|
||||||
let mut decoder = Decoder::new(&mut file).unwrap();
|
let mut decoder = Decoder::new(&mut file).unwrap();
|
||||||
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
|
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
|
||||||
assert_eq!(decoder.dimensions().unwrap(), (100, 100));
|
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() {
|
if let DecodingResult::U8(img_res) = decoder.read_image().unwrap() {
|
||||||
assert_eq!(image_data, img_res);
|
assert_eq!(image_data, img_res);
|
||||||
} else {
|
} else {
|
||||||
@@ -58,7 +66,10 @@ const TEST_IMAGE_DIR: &str = "./tests/images/";
|
|||||||
|
|
||||||
macro_rules! test_roundtrip {
|
macro_rules! test_roundtrip {
|
||||||
($name:ident, $buffer:ident, $buffer_ty:ty) => {
|
($name:ident, $buffer:ident, $buffer_ty:ty) => {
|
||||||
fn $name<C: colortype::ColorType<Inner=$buffer_ty>>(file: &str, expected_type: ColorType) {
|
fn $name<C: colortype::ColorType<Inner = $buffer_ty>>(
|
||||||
|
file: &str,
|
||||||
|
expected_type: ColorType,
|
||||||
|
) {
|
||||||
let path = PathBuf::from(TEST_IMAGE_DIR).join(file);
|
let path = PathBuf::from(TEST_IMAGE_DIR).join(file);
|
||||||
let img_file = File::open(path).expect("Cannot find test image!");
|
let img_file = File::open(path).expect("Cannot find test image!");
|
||||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||||
@@ -74,8 +85,7 @@ macro_rules! test_roundtrip {
|
|||||||
let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
||||||
|
|
||||||
let (width, height) = decoder.dimensions().unwrap();
|
let (width, height) = decoder.dimensions().unwrap();
|
||||||
tiff.write_image::<C>(width, height, &image_data)
|
tiff.write_image::<C>(width, height, &image_data).unwrap();
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
file.seek(SeekFrom::Start(0)).unwrap();
|
file.seek(SeekFrom::Start(0)).unwrap();
|
||||||
{
|
{
|
||||||
@@ -94,6 +104,8 @@ test_roundtrip!(test_u8_roundtrip, U8, u8);
|
|||||||
test_roundtrip!(test_u16_roundtrip, U16, u16);
|
test_roundtrip!(test_u16_roundtrip, U16, u16);
|
||||||
test_roundtrip!(test_u32_roundtrip, U32, u32);
|
test_roundtrip!(test_u32_roundtrip, U32, u32);
|
||||||
test_roundtrip!(test_u64_roundtrip, U64, u64);
|
test_roundtrip!(test_u64_roundtrip, U64, u64);
|
||||||
|
test_roundtrip!(test_f32_roundtrip, F32, f32);
|
||||||
|
test_roundtrip!(test_f64_roundtrip, F64, f64);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_gray_u8_roundtrip() {
|
fn test_gray_u8_roundtrip() {
|
||||||
@@ -145,6 +157,54 @@ fn test_rgb_u64_roundtrip() {
|
|||||||
test_u64_roundtrip::<colortype::RGB64>("gradient-3c-64b.tiff", ColorType::RGB(64));
|
test_u64_roundtrip::<colortype::RGB64>("gradient-3c-64b.tiff", ColorType::RGB(64));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gray_f32_roundtrip() {
|
||||||
|
test_f32_roundtrip::<colortype::Gray32Float>("gradient-1c-32b-float.tiff", ColorType::Gray(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rgb_f32_roundtrip() {
|
||||||
|
test_f32_roundtrip::<colortype::RGB32Float>("gradient-3c-32b-float.tiff", ColorType::RGB(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cmyk_f32_roundtrip() {
|
||||||
|
test_f32_roundtrip::<colortype::CMYK32Float>("cmyk-3c-32b-float.tiff", ColorType::CMYK(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gray_f64_roundtrip() {
|
||||||
|
test_f64_roundtrip::<colortype::Gray64Float>("gradient-1c-64b-float.tiff", ColorType::Gray(64));
|
||||||
|
}
|
||||||
|
|
||||||
|
trait AssertDecode {
|
||||||
|
fn assert_tag_u32(&mut self, tag: u16) -> 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_vec(&mut self, tag: u16) -> Vec<i32>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: std::io::Read + std::io::Seek> AssertDecode for Decoder<R> {
|
||||||
|
fn assert_tag_u32(&mut self, tag: u16) -> u32 {
|
||||||
|
self.get_tag(Tag::Unknown(tag)).unwrap().into_u32().unwrap()
|
||||||
|
}
|
||||||
|
fn assert_tag_u32_vec(&mut self, tag: u16) -> Vec<u32> {
|
||||||
|
self.get_tag(Tag::Unknown(tag))
|
||||||
|
.unwrap()
|
||||||
|
.into_u32_vec()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
fn assert_tag_i32(&mut self, tag: u16) -> i32 {
|
||||||
|
self.get_tag(Tag::Unknown(tag)).unwrap().into_i32().unwrap()
|
||||||
|
}
|
||||||
|
fn assert_tag_i32_vec(&mut self, tag: u16) -> Vec<i32> {
|
||||||
|
self.get_tag(Tag::Unknown(tag))
|
||||||
|
.unwrap()
|
||||||
|
.into_i32_vec()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_multiple_byte() {
|
fn test_multiple_byte() {
|
||||||
let mut data = Cursor::new(Vec::new());
|
let mut data = Cursor::new(Vec::new());
|
||||||
@@ -155,21 +215,29 @@ fn test_multiple_byte() {
|
|||||||
let encoder = image_encoder.encoder();
|
let encoder = image_encoder.encoder();
|
||||||
|
|
||||||
encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]).unwrap();
|
encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65001), &[1_u8, 2][..]).unwrap();
|
encoder
|
||||||
encoder.write_tag(Tag::Unknown(65002), &[1_u8, 2, 3][..]).unwrap();
|
.write_tag(Tag::Unknown(65001), &[1_u8, 2][..])
|
||||||
encoder.write_tag(Tag::Unknown(65003), &[1_u8, 2, 3, 4][..]).unwrap();
|
.unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65004), &[1_u8, 2, 3, 4, 5][..]).unwrap();
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65002), &[1_u8, 2, 3][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65003), &[1_u8, 2, 3, 4][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65004), &[1_u8, 2, 3, 4, 5][..])
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
data.set_position(0);
|
data.set_position(0);
|
||||||
{
|
{
|
||||||
let mut decoder = Decoder::new(&mut data).unwrap();
|
let mut decoder = Decoder::new(&mut data).unwrap();
|
||||||
|
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_u32_vec().unwrap(), [1]);
|
assert_eq!(decoder.assert_tag_u32_vec(65000), [1]);
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_u32_vec().unwrap(), [1, 2]);
|
assert_eq!(decoder.assert_tag_u32_vec(65001), [1, 2]);
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_u32_vec().unwrap(), [1, 2, 3]);
|
assert_eq!(decoder.assert_tag_u32_vec(65002), [1, 2, 3]);
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_u32_vec().unwrap(), [1, 2, 3, 4]);
|
assert_eq!(decoder.assert_tag_u32_vec(65003), [1, 2, 3, 4]);
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_u32_vec().unwrap(), [1, 2, 3, 4, 5]);
|
assert_eq!(decoder.assert_tag_u32_vec(65004), [1, 2, 3, 4, 5]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,6 +245,9 @@ fn test_multiple_byte() {
|
|||||||
/// Test writing signed tags from TIFF 6.0
|
/// Test writing signed tags from TIFF 6.0
|
||||||
fn test_signed() {
|
fn test_signed() {
|
||||||
let mut data = Cursor::new(Vec::new());
|
let mut data = Cursor::new(Vec::new());
|
||||||
|
fn make_srational(i: i32) -> SRational {
|
||||||
|
SRational { n: i, d: 100 }
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut tiff = TiffEncoder::new(&mut data).unwrap();
|
let mut tiff = TiffEncoder::new(&mut data).unwrap();
|
||||||
@@ -185,23 +256,48 @@ fn test_signed() {
|
|||||||
|
|
||||||
//Use the "reusable" tags section as per the TIFF6 spec
|
//Use the "reusable" tags section as per the TIFF6 spec
|
||||||
encoder.write_tag(Tag::Unknown(65000), -1_i8).unwrap();
|
encoder.write_tag(Tag::Unknown(65000), -1_i8).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65001), &[-1_i8][..]).unwrap();
|
encoder
|
||||||
encoder.write_tag(Tag::Unknown(65002), &[-1_i8, 2][..]).unwrap();
|
.write_tag(Tag::Unknown(65001), &[-1_i8][..])
|
||||||
encoder.write_tag(Tag::Unknown(65003), &[-1_i8, 2, -3][..]).unwrap();
|
.unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65004), &[-1_i8, 2, -3, 4][..]).unwrap();
|
encoder
|
||||||
encoder.write_tag(Tag::Unknown(65005), &[-1_i8, 2, -3, 4, -5][..]).unwrap();
|
.write_tag(Tag::Unknown(65002), &[-1_i8, 2][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65003), &[-1_i8, 2, -3][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65004), &[-1_i8, 2, -3, 4][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65005), &[-1_i8, 2, -3, 4, -5][..])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
encoder.write_tag(Tag::Unknown(65010), -1_i16).unwrap();
|
encoder.write_tag(Tag::Unknown(65010), -1_i16).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65011), -1_i16).unwrap();
|
encoder.write_tag(Tag::Unknown(65011), -1_i16).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65012), &[-1_i16, 2][..]).unwrap();
|
encoder
|
||||||
encoder.write_tag(Tag::Unknown(65013), &[-1_i16, 2, -3][..]).unwrap();
|
.write_tag(Tag::Unknown(65012), &[-1_i16, 2][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65013), &[-1_i16, 2, -3][..])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
encoder.write_tag(Tag::Unknown(65020), -1_i32).unwrap();
|
encoder.write_tag(Tag::Unknown(65020), -1_i32).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65021), &[-1_i32][..]).unwrap();
|
encoder
|
||||||
encoder.write_tag(Tag::Unknown(65022), &[-1_i32, 2][..]).unwrap();
|
.write_tag(Tag::Unknown(65021), &[-1_i32][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65022), &[-1_i32, 2][..])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
encoder.write_tag(Tag::Unknown(65030), SRational { n: -1, d: 100 }).unwrap();
|
encoder
|
||||||
encoder.write_tag(Tag::Unknown(65031), &[SRational { n: -1, d: 100 }, SRational { n: 2, d: 100 }][..]).unwrap();
|
.write_tag(Tag::Unknown(65030), make_srational(-1))
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(
|
||||||
|
Tag::Unknown(65031),
|
||||||
|
&[make_srational(-1), make_srational(2)][..],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Rewind the cursor for reading
|
//Rewind the cursor for reading
|
||||||
@@ -209,24 +305,24 @@ fn test_signed() {
|
|||||||
{
|
{
|
||||||
let mut decoder = Decoder::new(&mut data).unwrap();
|
let mut decoder = Decoder::new(&mut data).unwrap();
|
||||||
|
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_i32().unwrap(), -1, );
|
assert_eq!(decoder.assert_tag_i32(65000), -1);
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_i32_vec().unwrap(), [-1]);
|
assert_eq!(decoder.assert_tag_i32_vec(65001), [-1]);
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_i32_vec().unwrap(), [-1, 2]);
|
assert_eq!(decoder.assert_tag_i32_vec(65002), [-1, 2]);
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3]);
|
assert_eq!(decoder.assert_tag_i32_vec(65003), [-1, 2, -3]);
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3, 4]);
|
assert_eq!(decoder.assert_tag_i32_vec(65004), [-1, 2, -3, 4]);
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65005)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3, 4, -5], );
|
assert_eq!(decoder.assert_tag_i32_vec(65005), [-1, 2, -3, 4, -5],);
|
||||||
|
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65010)).unwrap().into_i32().unwrap(), -1);
|
assert_eq!(decoder.assert_tag_i32(65010), -1);
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65011)).unwrap().into_i32_vec().unwrap(), [-1]);
|
assert_eq!(decoder.assert_tag_i32_vec(65011), [-1]);
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65012)).unwrap().into_i32_vec().unwrap(), [-1, 2]);
|
assert_eq!(decoder.assert_tag_i32_vec(65012), [-1, 2]);
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65013)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3]);
|
assert_eq!(decoder.assert_tag_i32_vec(65013), [-1, 2, -3]);
|
||||||
|
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65020)).unwrap().into_i32().unwrap(), -1);
|
assert_eq!(decoder.assert_tag_i32(65020), -1);
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65021)).unwrap().into_i32_vec().unwrap(), [-1]);
|
assert_eq!(decoder.assert_tag_i32_vec(65021), [-1]);
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65022)).unwrap().into_i32_vec().unwrap(), [-1, 2]);
|
assert_eq!(decoder.assert_tag_i32_vec(65022), [-1, 2]);
|
||||||
|
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65030)).unwrap().into_i32_vec().unwrap(), [-1, 100]);
|
assert_eq!(decoder.assert_tag_i32_vec(65030), [-1, 100]);
|
||||||
assert_eq!(decoder.get_tag(Tag::Unknown(65031)).unwrap().into_i32_vec().unwrap(), [-1_i32, 100, 2, 100]);
|
assert_eq!(decoder.assert_tag_i32_vec(65031), [-1_i32, 100, 2, 100]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,10 +337,14 @@ fn test_multipage_image() {
|
|||||||
|
|
||||||
// write first grayscale image (2x2 16-bit)
|
// write first grayscale image (2x2 16-bit)
|
||||||
let img1: Vec<u16> = [1, 2, 3, 4].to_vec();
|
let img1: Vec<u16> = [1, 2, 3, 4].to_vec();
|
||||||
img_encoder.write_image::<colortype::Gray16>(2, 2, &img1[..]).unwrap();
|
img_encoder
|
||||||
|
.write_image::<colortype::Gray16>(2, 2, &img1[..])
|
||||||
|
.unwrap();
|
||||||
// write second grayscale image (3x3 8-bit)
|
// write second grayscale image (3x3 8-bit)
|
||||||
let img2: Vec<u8> = [9, 8, 7, 6, 5, 4, 3, 2, 1].to_vec();
|
let img2: Vec<u8> = [9, 8, 7, 6, 5, 4, 3, 2, 1].to_vec();
|
||||||
img_encoder.write_image::<colortype::Gray8>(3, 3, &img2[..]).unwrap();
|
img_encoder
|
||||||
|
.write_image::<colortype::Gray8>(3, 3, &img2[..])
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// seek to the beginning of the file, so that it can be decoded
|
// seek to the beginning of the file, so that it can be decoded
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user