Compare commits
161
Commits
v0.3.0
..
checkpoint
| 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 | ||
|
|
119dec5068 | ||
|
|
ec0477125f | ||
|
|
1c8e98bca5 | ||
|
|
e6985063ac | ||
|
|
42c94ac3d9 | ||
|
|
eadec8f0b9 | ||
|
|
462697e373 | ||
|
|
c537f5cc64 | ||
|
|
8f61196d70 | ||
|
|
298dc8841d | ||
|
|
f92be13f6c | ||
|
|
f9cace84da | ||
|
|
d510a9f4ab | ||
|
|
1fafcccb36 | ||
|
|
8b96793671 | ||
|
|
27ae65160d | ||
|
|
29bb8c2dfe | ||
|
|
c5fcba45a0 | ||
|
|
cbaa9dbc5b | ||
|
|
959254aee9 | ||
|
|
3f14098060 | ||
|
|
57783ba43f | ||
|
|
abd6aee19f | ||
|
|
c72a8d364d | ||
|
|
e5d2e7862c | ||
|
|
6e61de81b6 | ||
|
|
8c4cea5d97 | ||
|
|
0c53e1066a | ||
|
|
f4e985e0bd | ||
|
|
af010edcfd | ||
|
|
f32b3b9d10 | ||
|
|
1f92c562bc | ||
|
|
fca4e1f88a | ||
|
|
ff574b4ca2 | ||
|
|
6f87228458 | ||
|
|
15929f8b8e | ||
|
|
f13492e41d | ||
|
|
1d9944c5c5 | ||
|
|
a14b23473b | ||
|
|
101940335c | ||
|
|
8e1c67ee53 | ||
|
|
7b672998db | ||
|
|
62fee106a7 | ||
|
|
fd789dc420 | ||
|
|
07e8501ad6 | ||
|
|
f70447325e | ||
|
|
b2eb480c44 | ||
|
|
686e9b2648 | ||
|
|
517638a38f | ||
|
|
a6696ab02f | ||
|
|
258edf4a46 | ||
|
|
f88c9f00c3 | ||
|
|
f80f4a06d5 | ||
|
|
cbae42ea94 | ||
|
|
80b8ec5cdb | ||
|
|
ffdeb62b9f | ||
|
|
6aea7b33c1 | ||
|
|
770262cb02 | ||
|
|
1f957029af | ||
|
|
f8d4b57120 | ||
|
|
6f9f2adfbf | ||
|
|
205626bd62 | ||
|
|
ce73d39d57 | ||
|
|
b79128d766 | ||
|
|
c8f384d6a6 | ||
|
|
ce1d4cb289 | ||
|
|
b95e5b8f95 | ||
|
|
bc05e0b7c6 | ||
|
|
3868f78067 | ||
|
|
f6a91065d9 | ||
|
|
ea19f0f686 | ||
|
|
3b85b68ae9 | ||
|
|
30eb8487a6 | ||
|
|
1c2e57a22a | ||
|
|
2c7f329115 | ||
|
|
c9675360cf | ||
|
|
514b7cddaa | ||
|
|
7fb354fe43 |
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,5 +0,0 @@
|
|||||||
language: rust
|
|
||||||
rust:
|
|
||||||
- stable
|
|
||||||
- beta
|
|
||||||
- nightly
|
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
* Several enumerations are now non_exhaustive for future extensions.
|
||||||
|
These are `Tag`, `Type`, `Value`, `PhotometricInterpretation`,
|
||||||
|
`CompressionMethod`, `Predictor`.
|
||||||
|
* Enums gained a dedicated method to convert to their TIFF variant value with
|
||||||
|
the specified type. Performing these conversions by casting the discriminant
|
||||||
|
with `as` is not guaranteed to be stable, except where documented explicitly.
|
||||||
|
* Removed the num-derive and num dependencies.
|
||||||
|
* Added support for decoding `deflate` compressed images.
|
||||||
|
* Make the decoder `Limits` customizable by exposing members.
|
||||||
|
* Fixed multi-page TIFF encoding writing incorrect offsets.
|
||||||
+12
-6
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tiff"
|
name = "tiff"
|
||||||
version = "0.3.0"
|
version = "0.6.0"
|
||||||
authors = [
|
authors = [
|
||||||
"ccgn",
|
"ccgn",
|
||||||
"bvssvni <bvssvni@gmail.com>",
|
"bvssvni <bvssvni@gmail.com>",
|
||||||
@@ -17,10 +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 }
|
||||||
num-derive = "0.2"
|
|
||||||
num-traits = "0.2"
|
[dependencies.miniz_oxide]
|
||||||
|
version = "0.4.1"
|
||||||
|
features = ["no_extern_crate_alloc"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.0"
|
criterion = "0.3.1"
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "lzw"
|
||||||
|
harness = false
|
||||||
@@ -4,3 +4,43 @@
|
|||||||
[](https://crates.io/crates/tiff)
|
[](https://crates.io/crates/tiff)
|
||||||
|
|
||||||
TIFF decoding and encoding library in pure Rust
|
TIFF decoding and encoding library in pure Rust
|
||||||
|
|
||||||
|
## Supported
|
||||||
|
|
||||||
|
### Features
|
||||||
|
- Baseline spec (other than formats and tags listed below as not supported)
|
||||||
|
- Multipage
|
||||||
|
- BigTIFF
|
||||||
|
- Incremental decoding
|
||||||
|
|
||||||
|
### Formats
|
||||||
|
This table lists photometric interpretations and sample formats which are supported for encoding and decoding. The entries are `ColorType` variants for which sample bit depths are supported. Only samples where all bit depths are equal are currently supported. For example, `RGB(8)` means that the bit depth [8, 8, 8] is supported and will be interpreted as an 8 bit per channel RGB color type.
|
||||||
|
|
||||||
|
| `PhotometricInterpretation` | UINT Format | IEEEFP Format |
|
||||||
|
|-|-|-|
|
||||||
|
| `WhiteIsZero` | Gray(8\|16\|32\|64) | Gray(32\|64) |
|
||||||
|
| `BlackIsZero` | Gray(8\|16\|32\|64) | Gray(32\|64) |
|
||||||
|
| `RGB` | RGB(8\|16\|32\|64), RGBA(8\|16\|32\|64) | RGB(32\|64), RGBA(32\|64) |
|
||||||
|
| `RGBPalette` | | |
|
||||||
|
| `Mask` | | |
|
||||||
|
| `CMYK` | CMYK(8\|16\|32\|64) | CMYK(32\|64) |
|
||||||
|
| `YCbCr` | | |
|
||||||
|
| `CIELab` | | |
|
||||||
|
|
||||||
|
### Compressions
|
||||||
|
|
||||||
|
| | Decoding | Encoding |
|
||||||
|
|-|-|-|
|
||||||
|
| None | ✓ | ✓ |
|
||||||
|
| LZW | ✓ | |
|
||||||
|
| Deflate | ✓ | |
|
||||||
|
| PackBits | ✓ | |
|
||||||
|
|
||||||
|
|
||||||
|
## Not yet supported
|
||||||
|
|
||||||
|
Formats and interpretations not listed above or with empty entries are unsupported.
|
||||||
|
|
||||||
|
- Baseline tags
|
||||||
|
- `ExtraSamples`
|
||||||
|
- Extension tags
|
||||||
@@ -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);
|
||||||
+433
-149
@@ -1,114 +1,105 @@
|
|||||||
//! Function for reading TIFF tags
|
//! Function for reading TIFF tags
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::convert::{TryFrom, TryInto};
|
||||||
use std::io::{self, Read, Seek};
|
use std::io::{self, Read, Seek};
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
use super::stream::{ByteOrder, EndianReader, SmartReader};
|
use super::stream::{ByteOrder, EndianReader, SmartReader};
|
||||||
|
use tags::{Tag, Type};
|
||||||
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
||||||
|
|
||||||
use self::Value::{Ascii, List, Rational, Unsigned};
|
use self::Value::{
|
||||||
|
Ascii, Byte, Double, Float, List, Rational, RationalBig, SRational, SRationalBig, Signed,
|
||||||
macro_rules! tags {
|
SignedBig, Unsigned, UnsignedBig,
|
||||||
{$(
|
};
|
||||||
$tag:ident
|
|
||||||
$val:expr;
|
|
||||||
)*} => {
|
|
||||||
|
|
||||||
/// TIFF tag
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
|
||||||
pub enum Tag {
|
|
||||||
$($tag,)*
|
|
||||||
Unknown(u16)
|
|
||||||
}
|
|
||||||
impl Tag {
|
|
||||||
pub fn from_u16(n: u16) -> Tag {
|
|
||||||
$(if n == $val { Tag::$tag } else)* {
|
|
||||||
Tag::Unknown(n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn to_u16(&self) -> u16 {
|
|
||||||
match self {
|
|
||||||
$( Tag::$tag => $val, )*
|
|
||||||
Tag::Unknown(n) => *n,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: These tags appear in the order they are mentioned in the TIFF reference
|
|
||||||
tags! {
|
|
||||||
// Baseline tags:
|
|
||||||
Artist 315;
|
|
||||||
// grayscale images PhotometricInterpretation 1 or 3
|
|
||||||
BitsPerSample 258;
|
|
||||||
CellLength 265; // TODO add support
|
|
||||||
CellWidth 264; // TODO add support
|
|
||||||
// palette-color images (PhotometricInterpretation 3)
|
|
||||||
ColorMap 320; // TODO add support
|
|
||||||
Compression 259; // TODO add support for 2 and 32773
|
|
||||||
Copyright 33_432;
|
|
||||||
DateTime 306;
|
|
||||||
ExtraSamples 338; // TODO add support
|
|
||||||
FillOrder 266; // TODO add support
|
|
||||||
FreeByteCounts 289; // TODO add support
|
|
||||||
FreeOffsets 288; // TODO add support
|
|
||||||
GrayResponseCurve 291; // TODO add support
|
|
||||||
GrayResponseUnit 290; // TODO add support
|
|
||||||
HostComputer 316;
|
|
||||||
ImageDescription 270;
|
|
||||||
ImageLength 257;
|
|
||||||
ImageWidth 256;
|
|
||||||
Make 271;
|
|
||||||
MaxSampleValue 281; // TODO add support
|
|
||||||
MinSampleValue 280; // TODO add support
|
|
||||||
Model 272;
|
|
||||||
NewSubfileType 254; // TODO add support
|
|
||||||
Orientation 274; // TODO add support
|
|
||||||
PhotometricInterpretation 262;
|
|
||||||
PlanarConfiguration 284;
|
|
||||||
ResolutionUnit 296; // TODO add support
|
|
||||||
RowsPerStrip 278;
|
|
||||||
SamplesPerPixel 277;
|
|
||||||
Software 305;
|
|
||||||
StripByteCounts 279;
|
|
||||||
StripOffsets 273;
|
|
||||||
SubfileType 255; // TODO add support
|
|
||||||
Threshholding 263; // TODO add support
|
|
||||||
XResolution 282;
|
|
||||||
YResolution 283;
|
|
||||||
// Advanced tags
|
|
||||||
Predictor 317;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, FromPrimitive)]
|
|
||||||
pub enum Type {
|
|
||||||
BYTE = 1,
|
|
||||||
ASCII = 2,
|
|
||||||
SHORT = 3,
|
|
||||||
LONG = 4,
|
|
||||||
RATIONAL = 5,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused_qualifications)]
|
#[allow(unused_qualifications)]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
//Signed(i32),
|
Byte(u8),
|
||||||
|
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),
|
||||||
|
SRationalBig(i64, i64),
|
||||||
Ascii(String),
|
Ascii(String),
|
||||||
|
#[doc(hidden)] // Do not match against this.
|
||||||
|
__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),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_i32(self) -> TiffResult<i32> {
|
||||||
|
match self {
|
||||||
|
Signed(val) => Ok(val),
|
||||||
|
SignedBig(val) => Ok(i32::try_from(val)?),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::SignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_u64(self) -> TiffResult<u64> {
|
||||||
|
match self {
|
||||||
|
Unsigned(val) => Ok(val.into()),
|
||||||
|
UnsignedBig(val) => Ok(val),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::UnsignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_i64(self) -> TiffResult<i64> {
|
||||||
|
match self {
|
||||||
|
Signed(val) => Ok(val.into()),
|
||||||
|
SignedBig(val) => Ok(val),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::SignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
TiffFormatError::SignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_u32_vec(self) -> TiffResult<Vec<u32>> {
|
pub fn into_u32_vec(self) -> TiffResult<Vec<u32>> {
|
||||||
match self {
|
match self {
|
||||||
List(vec) => {
|
List(vec) => {
|
||||||
@@ -119,8 +110,144 @@ 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]),
|
||||||
Ascii(val) => Ok(val.chars().map(|x| x as u32).collect()),
|
RationalBig(numerator, denominator) => {
|
||||||
|
Ok(vec![u32::try_from(numerator)?, u32::try_from(denominator)?])
|
||||||
|
}
|
||||||
|
Ascii(val) => Ok(val.chars().map(u32::from).collect()),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::UnsignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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>> {
|
||||||
|
match self {
|
||||||
|
List(vec) => {
|
||||||
|
let mut new_vec = Vec::with_capacity(vec.len());
|
||||||
|
for v in vec {
|
||||||
|
match v {
|
||||||
|
SRational(numerator, denominator) => {
|
||||||
|
new_vec.push(numerator);
|
||||||
|
new_vec.push(denominator);
|
||||||
|
}
|
||||||
|
SRationalBig(numerator, denominator) => {
|
||||||
|
new_vec.push(i32::try_from(numerator)?);
|
||||||
|
new_vec.push(i32::try_from(denominator)?);
|
||||||
|
}
|
||||||
|
_ => new_vec.push(v.into_i32()?),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(new_vec)
|
||||||
|
}
|
||||||
|
Signed(val) => Ok(vec![val]),
|
||||||
|
SignedBig(val) => Ok(vec![i32::try_from(val)?]),
|
||||||
|
SRational(numerator, denominator) => Ok(vec![numerator, denominator]),
|
||||||
|
SRationalBig(numerator, denominator) => {
|
||||||
|
Ok(vec![i32::try_from(numerator)?, i32::try_from(denominator)?])
|
||||||
|
}
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::SignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_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(
|
||||||
|
TiffFormatError::SignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,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 {
|
||||||
@@ -143,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,
|
||||||
@@ -161,73 +294,224 @@ 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();
|
||||||
match (self.type_, self.count) {
|
let bigtiff_value = if decoder.bigtiff {
|
||||||
// TODO check if this could give wrong results
|
match (self.type_, self.count) {
|
||||||
// at a different endianess of file/computer.
|
(Type::BYTE, 5) => Some(offset_to_bytes(5, self)?),
|
||||||
(Type::BYTE, 1) => Ok(Unsigned(u32::from(self.offset[0]))),
|
(Type::BYTE, 6) => Some(offset_to_bytes(6, self)?),
|
||||||
(Type::SHORT, 1) => Ok(Unsigned(u32::from(self.r(bo).read_u16()?))),
|
(Type::BYTE, 7) => Some(offset_to_bytes(7, self)?),
|
||||||
(Type::SHORT, 2) => {
|
(Type::BYTE, 8) => Some(offset_to_bytes(8, self)?),
|
||||||
let mut r = self.r(bo);
|
(Type::SBYTE, 5) => Some(offset_to_sbytes(5, self)?),
|
||||||
Ok(List(vec![
|
(Type::SBYTE, 6) => Some(offset_to_sbytes(6, self)?),
|
||||||
Unsigned(u32::from(r.read_u16()?)),
|
(Type::SBYTE, 7) => Some(offset_to_sbytes(7, self)?),
|
||||||
Unsigned(u32::from(r.read_u16()?)),
|
(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,
|
||||||
}
|
}
|
||||||
(Type::SHORT, n) => {
|
} else {
|
||||||
if n as usize > limits.decoding_buffer_size / std::mem::size_of::<Value>() {
|
None
|
||||||
return Err(TiffError::LimitsExceeded);
|
};
|
||||||
|
|
||||||
|
if let Some(v) = bigtiff_value {
|
||||||
|
Ok(v)
|
||||||
|
} else {
|
||||||
|
match (self.type_, self.count) {
|
||||||
|
// TODO check if this could give wrong results
|
||||||
|
// at a different endianess of file/computer.
|
||||||
|
(Type::BYTE, 1) => Ok(Unsigned(u32::from(self.offset[0]))),
|
||||||
|
(Type::BYTE, 2) => offset_to_bytes(2, self),
|
||||||
|
(Type::BYTE, 3) => offset_to_bytes(3, self),
|
||||||
|
(Type::BYTE, 4) => offset_to_bytes(4, self),
|
||||||
|
(Type::BYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(UnsignedBig(u64::from(decoder.read_byte()?)))
|
||||||
|
}),
|
||||||
|
(Type::SBYTE, 1) => Ok(Signed(i32::from(self.offset[0] as i8))),
|
||||||
|
(Type::SBYTE, 2) => offset_to_sbytes(2, self),
|
||||||
|
(Type::SBYTE, 3) => offset_to_sbytes(3, self),
|
||||||
|
(Type::SBYTE, 4) => offset_to_sbytes(4, self),
|
||||||
|
(Type::SBYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(SignedBig(i64::from(decoder.read_byte()? as i8)))
|
||||||
|
}),
|
||||||
|
(Type::SHORT, 1) => Ok(Unsigned(u32::from(self.r(bo).read_u16()?))),
|
||||||
|
(Type::SSHORT, 1) => Ok(Signed(i32::from(self.r(bo).read_i16()?))),
|
||||||
|
(Type::SHORT, 2) => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
Ok(List(vec![
|
||||||
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
let mut v = Vec::with_capacity(n as usize);
|
(Type::SSHORT, 2) => {
|
||||||
try!(decoder.goto_offset(try!(self.r(bo).read_u32())));
|
let mut r = self.r(bo);
|
||||||
for _ in 0..n {
|
Ok(List(vec![
|
||||||
v.push(Unsigned(u32::from(decoder.read_short()?)))
|
Signed(i32::from(r.read_i16()?)),
|
||||||
|
Signed(i32::from(r.read_i16()?)),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
Ok(List(v))
|
(Type::SHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(UnsignedBig(u64::from(decoder.read_short()?)))
|
||||||
|
}),
|
||||||
|
(Type::SSHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(SignedBig(i64::from(decoder.read_sshort()?)))
|
||||||
|
}),
|
||||||
|
(Type::LONG, 1) => Ok(Unsigned(self.r(bo).read_u32()?)),
|
||||||
|
(Type::SLONG, 1) => Ok(Signed(self.r(bo).read_i32()?)),
|
||||||
|
(Type::LONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(Unsigned(decoder.read_long()?))
|
||||||
|
}),
|
||||||
|
(Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(Signed(decoder.read_slong()?))
|
||||||
|
}),
|
||||||
|
(Type::FLOAT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(Float(decoder.read_float()?))
|
||||||
|
}),
|
||||||
|
(Type::DOUBLE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(Double(decoder.read_double()?))
|
||||||
|
}),
|
||||||
|
(Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(Rational(decoder.read_long()?, decoder.read_long()?))
|
||||||
|
}),
|
||||||
|
(Type::SRATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(SRational(decoder.read_slong()?, decoder.read_slong()?))
|
||||||
|
}),
|
||||||
|
(Type::LONG8, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(UnsignedBig(decoder.read_long8()?))
|
||||||
|
}),
|
||||||
|
(Type::ASCII, n) => {
|
||||||
|
let n = usize::try_from(n)?;
|
||||||
|
if n > limits.decoding_buffer_size {
|
||||||
|
return Err(TiffError::LimitsExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n <= 4 && !decoder.bigtiff) || (n <= 8 && decoder.bigtiff) {
|
||||||
|
let mut buf = vec![0; n];
|
||||||
|
self.r(bo).read_exact(&mut buf)?;
|
||||||
|
let v = String::from_utf8(buf)?;
|
||||||
|
let v = v.trim_matches(char::from(0));
|
||||||
|
Ok(Ascii(v.into()))
|
||||||
|
} else {
|
||||||
|
if decoder.bigtiff {
|
||||||
|
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
|
||||||
|
} else {
|
||||||
|
decoder.goto_offset(self.r(bo).read_u32()?)?
|
||||||
|
}
|
||||||
|
let string = decoder.read_string(n)?;
|
||||||
|
Ok(Ascii(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Type::UNDEFINED, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(Byte(u8::from(decoder.read_byte()?)))
|
||||||
|
}),
|
||||||
|
_ => Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::UnsupportedDataType,
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
(Type::LONG, 1) => Ok(Unsigned(try!(self.r(bo).read_u32()))),
|
|
||||||
(Type::LONG, n) => {
|
|
||||||
if n as usize > limits.decoding_buffer_size / std::mem::size_of::<Value>() {
|
|
||||||
return Err(TiffError::LimitsExceeded);
|
|
||||||
}
|
|
||||||
let mut v = Vec::with_capacity(n as usize);
|
|
||||||
try!(decoder.goto_offset(try!(self.r(bo).read_u32())));
|
|
||||||
for _ in 0..n {
|
|
||||||
v.push(Unsigned(try!(decoder.read_long())))
|
|
||||||
}
|
|
||||||
Ok(List(v))
|
|
||||||
}
|
|
||||||
(Type::RATIONAL, 1) => {
|
|
||||||
try!(decoder.goto_offset(try!(self.r(bo).read_u32())));
|
|
||||||
let numerator = try!(decoder.read_long());
|
|
||||||
let denominator = try!(decoder.read_long());
|
|
||||||
Ok(Rational(numerator, denominator))
|
|
||||||
}
|
|
||||||
(Type::RATIONAL, n) => {
|
|
||||||
if n as usize > limits.decoding_buffer_size / std::mem::size_of::<Value>() {
|
|
||||||
return Err(TiffError::LimitsExceeded);
|
|
||||||
}
|
|
||||||
let mut v = Vec::with_capacity(n as usize);
|
|
||||||
try!(decoder.goto_offset(try!(self.r(bo).read_u32())));
|
|
||||||
for _ in 0..n {
|
|
||||||
let numerator = try!(decoder.read_long());
|
|
||||||
let denominator = try!(decoder.read_long());
|
|
||||||
v.push(Rational(numerator, denominator))
|
|
||||||
}
|
|
||||||
Ok(List(v))
|
|
||||||
}
|
|
||||||
(Type::ASCII, n) => {
|
|
||||||
if n as usize > limits.decoding_buffer_size {
|
|
||||||
return Err(TiffError::LimitsExceeded);
|
|
||||||
}
|
|
||||||
try!(decoder.goto_offset(try!(self.r(bo).read_u32())));
|
|
||||||
let string = try!(decoder.read_string(n as usize));
|
|
||||||
Ok(Ascii(string))
|
|
||||||
}
|
|
||||||
_ => Err(TiffError::UnsupportedError(
|
|
||||||
TiffUnsupportedError::UnsupportedDataType,
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn decode_offset<R, F>(
|
||||||
|
&self,
|
||||||
|
value_count: u64,
|
||||||
|
bo: ByteOrder,
|
||||||
|
limits: &super::Limits,
|
||||||
|
decoder: &mut super::Decoder<R>,
|
||||||
|
decode_fn: F,
|
||||||
|
) -> TiffResult<Value>
|
||||||
|
where
|
||||||
|
R: Read + Seek,
|
||||||
|
F: Fn(&mut super::Decoder<R>) -> TiffResult<Value>,
|
||||||
|
{
|
||||||
|
let value_count = usize::try_from(value_count)?;
|
||||||
|
if value_count > limits.decoding_buffer_size / mem::size_of::<Value>() {
|
||||||
|
return Err(TiffError::LimitsExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut v = Vec::with_capacity(value_count);
|
||||||
|
if decoder.bigtiff {
|
||||||
|
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
|
||||||
|
} else {
|
||||||
|
decoder.goto_offset(self.r(bo).read_u32()?)?
|
||||||
|
}
|
||||||
|
for _ in 0..value_count {
|
||||||
|
v.push(decode_fn(decoder)?)
|
||||||
|
}
|
||||||
|
Ok(List(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts a list of BYTE tags stored in an offset
|
||||||
|
#[inline]
|
||||||
|
fn offset_to_bytes(n: usize, entry: &Entry) -> TiffResult<Value> {
|
||||||
|
Ok(List(
|
||||||
|
entry.offset[0..n]
|
||||||
|
.iter()
|
||||||
|
.map(|&e| Unsigned(u32::from(e)))
|
||||||
|
.collect(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts a list of SBYTE tags stored in an offset
|
||||||
|
#[inline]
|
||||||
|
fn offset_to_sbytes(n: usize, entry: &Entry) -> TiffResult<Value> {
|
||||||
|
Ok(List(
|
||||||
|
entry.offset[0..n]
|
||||||
|
.iter()
|
||||||
|
.map(|&e| Signed(i32::from(e as i8)))
|
||||||
|
.collect(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type representing an Image File Directory
|
/// Type representing an Image File Directory
|
||||||
|
|||||||
+638
-237
File diff suppressed because it is too large.
Load diff
+311
-18
@@ -1,7 +1,8 @@
|
|||||||
//! All IO functionality needed for TIFF decoding
|
//! All IO functionality needed for TIFF decoding
|
||||||
|
|
||||||
use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
|
use crate::bytecast;
|
||||||
use lzw;
|
use crate::error::{TiffError, TiffResult};
|
||||||
|
use miniz_oxide::inflate;
|
||||||
use std::io::{self, Read, Seek};
|
use std::io::{self, Read, Seek};
|
||||||
|
|
||||||
/// Byte order of the TIFF file.
|
/// Byte order of the TIFF file.
|
||||||
@@ -21,34 +22,229 @@ 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
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_i16(&mut self) -> Result<i16, io::Error> {
|
||||||
|
let mut n = [0u8; 2];
|
||||||
|
self.read_exact(&mut n)?;
|
||||||
|
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> {
|
||||||
|
let mut n = [0u8; 4];
|
||||||
|
self.read_exact(&mut n)?;
|
||||||
|
Ok(match self.byte_order() {
|
||||||
|
ByteOrder::LittleEndian => u32::from_le_bytes(n),
|
||||||
|
ByteOrder::BigEndian => u32::from_be_bytes(n),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_u32_into(&mut self, buffer: &mut [u32]) -> Result<(), io::Error> {
|
||||||
|
self.read_exact(bytecast::u32_as_ne_mut_bytes(buffer))?;
|
||||||
match self.byte_order() {
|
match self.byte_order() {
|
||||||
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_u32::<LittleEndian>(self),
|
ByteOrder::LittleEndian => {
|
||||||
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_u32::<BigEndian>(self),
|
for n in buffer {
|
||||||
|
*n = u32::from_le(*n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ByteOrder::BigEndian => {
|
||||||
|
for n in buffer {
|
||||||
|
*n = u32::from_be(*n);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads an i32
|
||||||
|
#[inline(always)]
|
||||||
|
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() {
|
||||||
|
ByteOrder::LittleEndian => {
|
||||||
|
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
|
||||||
|
pub struct DeflateReader {
|
||||||
|
buffer: io::Cursor<Vec<u8>>,
|
||||||
|
byte_order: ByteOrder,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeflateReader {
|
||||||
|
pub fn new<R: Read + Seek>(
|
||||||
|
reader: &mut SmartReader<R>,
|
||||||
|
max_uncompressed_length: usize,
|
||||||
|
) -> TiffResult<(usize, Self)> {
|
||||||
|
let byte_order = reader.byte_order;
|
||||||
|
let mut compressed = Vec::new();
|
||||||
|
reader.read_to_end(&mut compressed)?;
|
||||||
|
|
||||||
|
// TODO: Implement streaming compression, and remove this (temporary) and somewhat
|
||||||
|
// misleading workaround.
|
||||||
|
if compressed.len() > max_uncompressed_length {
|
||||||
|
return Err(TiffError::LimitsExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
let uncompressed =
|
||||||
|
inflate::decompress_to_vec_zlib(&compressed).map_err(TiffError::from_inflate_status)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
uncompressed.len(),
|
||||||
|
Self {
|
||||||
|
byte_order,
|
||||||
|
buffer: io::Cursor::new(uncompressed),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for DeflateReader {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
self.buffer.read(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EndianReader for DeflateReader {
|
||||||
|
fn byte_order(&self) -> ByteOrder {
|
||||||
|
self.byte_order
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// ## LZW Reader
|
||||||
|
///
|
||||||
|
|
||||||
/// 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>>,
|
||||||
@@ -67,14 +263,34 @@ impl LZWReader {
|
|||||||
{
|
{
|
||||||
let order = reader.byte_order;
|
let order = reader.byte_order;
|
||||||
let mut compressed = vec![0; compressed_length as usize];
|
let mut compressed = vec![0; compressed_length as usize];
|
||||||
try!(reader.read_exact(&mut compressed[..]));
|
reader.read_exact(&mut compressed[..])?;
|
||||||
let mut uncompressed = Vec::with_capacity(max_uncompressed_length);
|
let mut 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) = try!(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();
|
||||||
@@ -102,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>>,
|
||||||
@@ -137,7 +426,7 @@ impl PackBitsReader {
|
|||||||
fn read_packbits_run<R: Read + Seek>(reader: &mut R, buffer: &mut Vec<u8>) -> io::Result<usize> {
|
fn read_packbits_run<R: Read + Seek>(reader: &mut R, buffer: &mut Vec<u8>) -> io::Result<usize> {
|
||||||
let mut header: [u8; 1] = [0];
|
let mut header: [u8; 1] = [0];
|
||||||
|
|
||||||
let bytes = try!(reader.read(&mut header));
|
let bytes = reader.read(&mut header)?;
|
||||||
|
|
||||||
match bytes {
|
match bytes {
|
||||||
0 => Ok(0),
|
0 => Ok(0),
|
||||||
@@ -145,7 +434,7 @@ fn read_packbits_run<R: Read + Seek>(reader: &mut R, buffer: &mut Vec<u8>) -> io
|
|||||||
-128 => Ok(1),
|
-128 => Ok(1),
|
||||||
h if h >= -127 && h <= -1 => {
|
h if h >= -127 && h <= -1 => {
|
||||||
let new_len = buffer.len() + (1 - h as isize) as usize;
|
let new_len = buffer.len() + (1 - h as isize) as usize;
|
||||||
try!(reader.read_exact(&mut header));
|
reader.read_exact(&mut header)?;
|
||||||
buffer.resize(new_len, header[0]);
|
buffer.resize(new_len, header[0]);
|
||||||
Ok(2)
|
Ok(2)
|
||||||
}
|
}
|
||||||
@@ -153,7 +442,7 @@ fn read_packbits_run<R: Read + Seek>(reader: &mut R, buffer: &mut Vec<u8>) -> io
|
|||||||
let num_vals = h as usize + 1;
|
let num_vals = h as usize + 1;
|
||||||
let start = buffer.len();
|
let start = buffer.len();
|
||||||
buffer.resize(start + num_vals, 0);
|
buffer.resize(start + num_vals, 0);
|
||||||
try!(reader.read_exact(&mut buffer[start..]));
|
reader.read_exact(&mut buffer[start..])?;
|
||||||
Ok(num_vals + 1)
|
Ok(num_vals + 1)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -174,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>
|
||||||
|
|||||||
+146
-1
@@ -1,4 +1,4 @@
|
|||||||
use crate::decoder::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,39 @@ 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;
|
||||||
|
impl ColorType for Gray32 {
|
||||||
|
type Inner = u32;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||||
|
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;
|
||||||
|
impl ColorType for Gray64 {
|
||||||
|
type Inner = u64;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||||
|
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;
|
||||||
@@ -29,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;
|
||||||
@@ -36,6 +73,39 @@ 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;
|
||||||
|
impl ColorType for RGB32 {
|
||||||
|
type Inner = u32;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
|
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;
|
||||||
|
impl ColorType for RGB64 {
|
||||||
|
type Inner = u64;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
|
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;
|
||||||
@@ -43,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;
|
||||||
@@ -50,6 +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;
|
||||||
|
impl ColorType for RGBA32 {
|
||||||
|
type Inner = u32;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
|
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;
|
||||||
|
impl ColorType for RGBA64 {
|
||||||
|
type Inner = u64;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
|
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;
|
||||||
@@ -57,4 +161,45 @@ 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;
|
||||||
|
impl ColorType for CMYK16 {
|
||||||
|
type Inner = u16;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CMYK32;
|
||||||
|
impl ColorType for CMYK32 {
|
||||||
|
type Inner = u32;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
|
||||||
|
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;
|
||||||
|
impl ColorType for CMYK64 {
|
||||||
|
type Inner = u64;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||||
|
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];
|
||||||
}
|
}
|
||||||
+343
-98
@@ -1,9 +1,11 @@
|
|||||||
use byteorder::NativeEndian;
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::convert::TryFrom;
|
||||||
use std::io::{Seek, Write};
|
use std::io::{Seek, Write};
|
||||||
|
use std::{cmp, io, mem};
|
||||||
|
|
||||||
use crate::decoder::ifd::{self, Tag};
|
use bytecast;
|
||||||
use crate::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;
|
||||||
@@ -18,17 +20,16 @@ pub struct Rational {
|
|||||||
pub d: u32,
|
pub d: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type to represent resolution units
|
/// Type to represent tiff values of type `SRATIONAL`
|
||||||
pub enum ResolutionUnit {
|
pub struct SRational {
|
||||||
None = 1,
|
pub n: i32,
|
||||||
Inch = 2,
|
pub d: i32,
|
||||||
Centimeter = 3,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for types that can be encoded in a tiff file
|
/// Trait for types that can be encoded in a tiff file
|
||||||
pub trait TiffValue {
|
pub trait TiffValue {
|
||||||
const BYTE_LEN: u32;
|
const BYTE_LEN: u32;
|
||||||
const FIELD_TYPE: ifd::Type;
|
const FIELD_TYPE: Type;
|
||||||
fn count(&self) -> u32;
|
fn count(&self) -> u32;
|
||||||
fn bytes(&self) -> u32 {
|
fn bytes(&self) -> u32 {
|
||||||
self.count() * Self::BYTE_LEN
|
self.count() * Self::BYTE_LEN
|
||||||
@@ -38,7 +39,7 @@ pub trait TiffValue {
|
|||||||
|
|
||||||
impl TiffValue for [u8] {
|
impl TiffValue for [u8] {
|
||||||
const BYTE_LEN: u32 = 1;
|
const BYTE_LEN: u32 = 1;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::BYTE;
|
const FIELD_TYPE: Type = Type::BYTE;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
self.len() as u32
|
self.len() as u32
|
||||||
@@ -50,18 +51,46 @@ impl TiffValue for [u8] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TiffValue for [u16] {
|
impl TiffValue for [i8] {
|
||||||
const BYTE_LEN: u32 = 2;
|
const BYTE_LEN: u32 = 1;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::SHORT;
|
const FIELD_TYPE: Type = Type::SBYTE;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
self.len() as 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
|
let slice = bytecast::i8_as_ne_bytes(self);
|
||||||
let slice =
|
writer.write_bytes(slice)?;
|
||||||
unsafe { std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 2) };
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [u16] {
|
||||||
|
const BYTE_LEN: u32 = 2;
|
||||||
|
const FIELD_TYPE: Type = Type::SHORT;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
self.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
let slice = bytecast::u16_as_ne_bytes(self);
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [i16] {
|
||||||
|
const BYTE_LEN: u32 = 2;
|
||||||
|
const FIELD_TYPE: Type = Type::SSHORT;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
self.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
let slice = bytecast::i16_as_ne_bytes(self);
|
||||||
writer.write_bytes(slice)?;
|
writer.write_bytes(slice)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -69,7 +98,52 @@ impl TiffValue for [u16] {
|
|||||||
|
|
||||||
impl TiffValue for [u32] {
|
impl TiffValue for [u32] {
|
||||||
const BYTE_LEN: u32 = 4;
|
const BYTE_LEN: u32 = 4;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::LONG;
|
const FIELD_TYPE: Type = Type::LONG;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
self.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
let slice = bytecast::u32_as_ne_bytes(self);
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [i32] {
|
||||||
|
const BYTE_LEN: u32 = 4;
|
||||||
|
const FIELD_TYPE: Type = Type::SLONG;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
self.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
let slice = bytecast::i32_as_ne_bytes(self);
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [u64] {
|
||||||
|
const BYTE_LEN: u32 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::LONG8;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
self.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
let slice = bytecast::u64_as_ne_bytes(self);
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [f32] {
|
||||||
|
const BYTE_LEN: u32 = 4;
|
||||||
|
const FIELD_TYPE: Type = Type::FLOAT;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
self.len() as u32
|
self.len() as u32
|
||||||
@@ -77,8 +151,23 @@ 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
|
// 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() * 4) };
|
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(())
|
||||||
}
|
}
|
||||||
@@ -86,7 +175,23 @@ impl TiffValue for [u32] {
|
|||||||
|
|
||||||
impl TiffValue for [Rational] {
|
impl TiffValue for [Rational] {
|
||||||
const BYTE_LEN: u32 = 8;
|
const BYTE_LEN: u32 = 8;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL;
|
const FIELD_TYPE: Type = Type::RATIONAL;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
self.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
for x in self {
|
||||||
|
x.write(writer)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [SRational] {
|
||||||
|
const BYTE_LEN: u32 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::SRATIONAL;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
self.len() as u32
|
self.len() as u32
|
||||||
@@ -102,7 +207,7 @@ impl TiffValue for [Rational] {
|
|||||||
|
|
||||||
impl TiffValue for u8 {
|
impl TiffValue for u8 {
|
||||||
const BYTE_LEN: u32 = 1;
|
const BYTE_LEN: u32 = 1;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::BYTE;
|
const FIELD_TYPE: Type = Type::BYTE;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
1
|
1
|
||||||
@@ -114,9 +219,23 @@ impl TiffValue for u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TiffValue for i8 {
|
||||||
|
const BYTE_LEN: u32 = 1;
|
||||||
|
const FIELD_TYPE: Type = Type::SBYTE;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_i8(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TiffValue for u16 {
|
impl TiffValue for u16 {
|
||||||
const BYTE_LEN: u32 = 2;
|
const BYTE_LEN: u32 = 2;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::SHORT;
|
const FIELD_TYPE: Type = Type::SHORT;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
1
|
1
|
||||||
@@ -128,9 +247,23 @@ impl TiffValue for u16 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TiffValue for i16 {
|
||||||
|
const BYTE_LEN: u32 = 2;
|
||||||
|
const FIELD_TYPE: Type = Type::SSHORT;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_i16(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TiffValue for u32 {
|
impl TiffValue for u32 {
|
||||||
const BYTE_LEN: u32 = 4;
|
const BYTE_LEN: u32 = 4;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::LONG;
|
const FIELD_TYPE: Type = Type::LONG;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
1
|
1
|
||||||
@@ -142,9 +275,65 @@ impl TiffValue for u32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TiffValue for i32 {
|
||||||
|
const BYTE_LEN: u32 = 4;
|
||||||
|
const FIELD_TYPE: Type = Type::SLONG;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_i32(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for u64 {
|
||||||
|
const BYTE_LEN: u32 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::LONG8;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_u64(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for f32 {
|
||||||
|
const BYTE_LEN: u32 = 4;
|
||||||
|
const FIELD_TYPE: Type = Type::FLOAT;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_f32(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for f64 {
|
||||||
|
const BYTE_LEN: u32 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::DOUBLE;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_f64(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TiffValue for Rational {
|
impl TiffValue for Rational {
|
||||||
const BYTE_LEN: u32 = 8;
|
const BYTE_LEN: u32 = 8;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL;
|
const FIELD_TYPE: Type = Type::RATIONAL;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
1
|
1
|
||||||
@@ -157,9 +346,24 @@ impl TiffValue for Rational {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TiffValue for SRational {
|
||||||
|
const BYTE_LEN: u32 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::SRATIONAL;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_i32(self.n)?;
|
||||||
|
writer.write_i32(self.d)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TiffValue for str {
|
impl TiffValue for str {
|
||||||
const BYTE_LEN: u32 = 1;
|
const BYTE_LEN: u32 = 1;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::ASCII;
|
const FIELD_TYPE: Type = Type::ASCII;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
self.len() as u32 + 1
|
self.len() as u32 + 1
|
||||||
@@ -178,7 +382,7 @@ impl TiffValue for str {
|
|||||||
|
|
||||||
impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T {
|
impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T {
|
||||||
const BYTE_LEN: u32 = T::BYTE_LEN;
|
const BYTE_LEN: u32 = T::BYTE_LEN;
|
||||||
const FIELD_TYPE: ifd::Type = T::FIELD_TYPE;
|
const FIELD_TYPE: Type = T::FIELD_TYPE;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
(*self).count()
|
(*self).count()
|
||||||
@@ -198,14 +402,16 @@ impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T {
|
|||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate tempfile;
|
/// # extern crate tiff;
|
||||||
/// # let mut file = tempfile::tempfile().unwrap();
|
/// # fn main() {
|
||||||
|
/// # 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::*;
|
||||||
///
|
///
|
||||||
/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
||||||
///
|
///
|
||||||
/// tiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap();
|
/// tiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap();
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct TiffEncoder<W> {
|
pub struct TiffEncoder<W> {
|
||||||
writer: TiffWriter<W>,
|
writer: TiffWriter<W>,
|
||||||
@@ -217,7 +423,9 @@ 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
|
||||||
|
encoder.writer.write_u32(0)?;
|
||||||
|
|
||||||
Ok(encoder)
|
Ok(encoder)
|
||||||
}
|
}
|
||||||
@@ -247,26 +455,9 @@ impl<W: Write + Seek> TiffEncoder<W> {
|
|||||||
where
|
where
|
||||||
[C::Inner]: TiffValue,
|
[C::Inner]: TiffValue,
|
||||||
{
|
{
|
||||||
let num_pix = (width as usize).checked_mul(height as usize)
|
|
||||||
.ok_or_else(|| std::io::Error::new(
|
|
||||||
std::io::ErrorKind::InvalidInput,
|
|
||||||
"Image width * height exceeds usize"))?;
|
|
||||||
if data.len() < num_pix {
|
|
||||||
return Err(std::io::Error::new(
|
|
||||||
std::io::ErrorKind::InvalidData,
|
|
||||||
"Input data slice is undersized for provided dimensions").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let encoder = DirectoryEncoder::new(&mut self.writer)?;
|
let 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 = image.next_strip_sample_count() as usize;
|
|
||||||
image.write_strip(&data[idx..idx + sample_count])?;
|
|
||||||
idx += sample_count;
|
|
||||||
}
|
|
||||||
image.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,7 +465,7 @@ impl<W: Write + Seek> TiffEncoder<W> {
|
|||||||
///
|
///
|
||||||
/// You should call `finish` on this when you are finished with it.
|
/// You should call `finish` on this when you are finished with it.
|
||||||
/// Encoding can silently fail while this is dropping.
|
/// Encoding can silently fail while this is dropping.
|
||||||
pub struct DirectoryEncoder<'a, W: Write + Seek> {
|
pub struct DirectoryEncoder<'a, W: 'a + Write + Seek> {
|
||||||
writer: &'a mut TiffWriter<W>,
|
writer: &'a mut TiffWriter<W>,
|
||||||
dropped: bool,
|
dropped: bool,
|
||||||
// We use BTreeMap to make sure tags are written in correct order
|
// We use BTreeMap to make sure tags are written in correct order
|
||||||
@@ -282,11 +473,11 @@ pub struct DirectoryEncoder<'a, W: Write + Seek> {
|
|||||||
ifd: BTreeMap<u16, (u16, u32, Vec<u8>)>,
|
ifd: BTreeMap<u16, (u16, u32, Vec<u8>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, W: Write + Seek> DirectoryEncoder<'a, W> {
|
impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
||||||
fn new(writer: &'a mut TiffWriter<W>) -> TiffResult<DirectoryEncoder<'a, W>> {
|
fn new(writer: &'a mut TiffWriter<W>) -> TiffResult<DirectoryEncoder<'a, W>> {
|
||||||
|
// the previous word is the IFD offset position
|
||||||
|
let ifd_pointer_pos = writer.offset() - mem::size_of::<u32>() as u64;
|
||||||
writer.pad_word_boundary()?;
|
writer.pad_word_boundary()?;
|
||||||
let ifd_pointer_pos = writer.offset();
|
|
||||||
writer.write_u32(0)?;
|
|
||||||
Ok(DirectoryEncoder {
|
Ok(DirectoryEncoder {
|
||||||
writer,
|
writer,
|
||||||
dropped: false,
|
dropped: false,
|
||||||
@@ -296,27 +487,31 @@ impl<'a, W: Write + Seek> DirectoryEncoder<'a, W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Write a single ifd tag.
|
/// Write a single ifd tag.
|
||||||
pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) {
|
pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) -> TiffResult<()> {
|
||||||
let len = <T>::BYTE_LEN * value.count();
|
let len = <T>::BYTE_LEN * value.count();
|
||||||
let mut bytes = Vec::with_capacity(len as usize);
|
let mut bytes = Vec::with_capacity(usize::try_from(len)?);
|
||||||
{
|
{
|
||||||
let mut writer = TiffWriter::new(&mut bytes);
|
let mut writer = TiffWriter::new(&mut bytes);
|
||||||
value.write(&mut writer).unwrap();
|
value.write(&mut writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ifd
|
self.ifd.insert(
|
||||||
.insert(tag.to_u16(), (<T>::FIELD_TYPE as u16, value.count(), bytes));
|
tag.to_u16(),
|
||||||
|
(<T>::FIELD_TYPE.to_u16(), value.count(), bytes),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_directory(&mut self) -> TiffResult<u64> {
|
fn write_directory(&mut self) -> TiffResult<u64> {
|
||||||
// Start by writing out all values
|
// Start by writing out all values
|
||||||
for (_, _, ref mut bytes) in self.ifd.values_mut() {
|
for &mut (_, _, ref mut bytes) in self.ifd.values_mut() {
|
||||||
if bytes.len() > 4 {
|
if bytes.len() > 4 {
|
||||||
let offset = self.writer.offset();
|
let offset = self.writer.offset();
|
||||||
self.writer.write_bytes(bytes)?;
|
self.writer.write_bytes(bytes)?;
|
||||||
*bytes = vec![0, 0, 0, 0];
|
*bytes = vec![0, 0, 0, 0];
|
||||||
let mut writer = TiffWriter::new(bytes as &mut [u8]);
|
let mut writer = TiffWriter::new(bytes as &mut [u8]);
|
||||||
writer.write_u32(offset as u32)?;
|
writer.write_u32(u32::try_from(offset)?)?;
|
||||||
} else {
|
} else {
|
||||||
while bytes.len() < 4 {
|
while bytes.len() < 4 {
|
||||||
bytes.push(0);
|
bytes.push(0);
|
||||||
@@ -326,8 +521,8 @@ impl<'a, W: Write + Seek> DirectoryEncoder<'a, W> {
|
|||||||
|
|
||||||
let offset = self.writer.offset();
|
let offset = self.writer.offset();
|
||||||
|
|
||||||
self.writer.write_u16(self.ifd.len() as u16)?;
|
self.writer.write_u16(u16::try_from(self.ifd.len())?)?;
|
||||||
for (tag, (field_type, count, offset)) in self.ifd.iter() {
|
for (tag, &(ref field_type, ref count, ref offset)) in self.ifd.iter() {
|
||||||
self.writer.write_u16(*tag)?;
|
self.writer.write_u16(*tag)?;
|
||||||
self.writer.write_u16(*field_type)?;
|
self.writer.write_u16(*field_type)?;
|
||||||
self.writer.write_u32(*count)?;
|
self.writer.write_u32(*count)?;
|
||||||
@@ -351,7 +546,7 @@ impl<'a, W: Write + Seek> DirectoryEncoder<'a, W> {
|
|||||||
let curr_pos = self.writer.offset();
|
let curr_pos = self.writer.offset();
|
||||||
|
|
||||||
self.writer.goto_offset(self.ifd_pointer_pos)?;
|
self.writer.goto_offset(self.ifd_pointer_pos)?;
|
||||||
self.writer.write_u32(ifd_pointer as u32)?;
|
self.writer.write_u32(u32::try_from(ifd_pointer)?)?;
|
||||||
self.writer.goto_offset(curr_pos)?;
|
self.writer.goto_offset(curr_pos)?;
|
||||||
self.writer.write_u32(0)?;
|
self.writer.write_u32(0)?;
|
||||||
|
|
||||||
@@ -381,14 +576,19 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
|
|||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate tempfile;
|
/// # extern crate tiff;
|
||||||
/// # let mut file = tempfile::tempfile().unwrap();
|
/// # fn main() {
|
||||||
|
/// # 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;
|
||||||
@@ -396,27 +596,30 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
|
|||||||
/// idx += sample_count;
|
/// idx += sample_count;
|
||||||
/// }
|
/// }
|
||||||
/// image.finish().unwrap();
|
/// image.finish().unwrap();
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct ImageEncoder<'a, W: Write + Seek, C: ColorType> {
|
/// You can also call write_data function wich will encode by strip and finish
|
||||||
|
pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType> {
|
||||||
encoder: DirectoryEncoder<'a, W>,
|
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>,
|
||||||
strip_byte_count: Vec<u32>,
|
strip_byte_count: Vec<u32>,
|
||||||
dropped: bool,
|
dropped: bool,
|
||||||
_phantom: std::marker::PhantomData<C>,
|
_phantom: ::std::marker::PhantomData<C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, W: Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
||||||
fn new(
|
fn new(
|
||||||
mut encoder: DirectoryEncoder<'a, W>,
|
mut encoder: DirectoryEncoder<'a, W>,
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
) -> TiffResult<ImageEncoder<'a, W, T>> {
|
) -> TiffResult<ImageEncoder<'a, W, T>> {
|
||||||
let row_samples = u64::from(width) * <T>::BITS_PER_SAMPLE.len() as u64;
|
let row_samples = u64::from(width) * u64::try_from(<T>::BITS_PER_SAMPLE.len())?;
|
||||||
let row_bytes = row_samples * u64::from(<T::Inner>::BYTE_LEN);
|
let row_bytes = row_samples * u64::from(<T::Inner>::BYTE_LEN);
|
||||||
|
|
||||||
// As per tiff spec each strip should be about 8k long
|
// As per tiff spec each strip should be about 8k long
|
||||||
@@ -424,19 +627,24 @@ impl<'a, W: Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
|||||||
|
|
||||||
let strip_count = (u64::from(height) + rows_per_strip - 1) / rows_per_strip;
|
let strip_count = (u64::from(height) + rows_per_strip - 1) / rows_per_strip;
|
||||||
|
|
||||||
encoder.write_tag(Tag::ImageWidth, width);
|
encoder.write_tag(Tag::ImageWidth, width)?;
|
||||||
encoder.write_tag(Tag::ImageLength, height);
|
encoder.write_tag(Tag::ImageLength, height)?;
|
||||||
encoder.write_tag(Tag::Compression, 1u16);
|
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)?;
|
||||||
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE as u16);
|
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::RowsPerStrip, rows_per_strip as u32);
|
encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?;
|
||||||
|
|
||||||
encoder.write_tag(Tag::SamplesPerPixel, <T>::BITS_PER_SAMPLE.len() as u16);
|
encoder.write_tag(
|
||||||
encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 });
|
Tag::SamplesPerPixel,
|
||||||
encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 });
|
u16::try_from(<T>::BITS_PER_SAMPLE.len())?,
|
||||||
encoder.write_tag(Tag::ResolutionUnit, 1u16);
|
)?;
|
||||||
|
encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 })?;
|
||||||
|
encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 })?;
|
||||||
|
encoder.write_tag(Tag::ResolutionUnit, ResolutionUnit::None.to_u16())?;
|
||||||
|
|
||||||
Ok(ImageEncoder {
|
Ok(ImageEncoder {
|
||||||
encoder,
|
encoder,
|
||||||
@@ -444,11 +652,12 @@ impl<'a, W: 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(),
|
||||||
dropped: false,
|
dropped: false,
|
||||||
_phantom: std::marker::PhantomData,
|
_phantom: ::std::marker::PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,11 +667,9 @@ impl<'a, W: 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
|
||||||
}
|
}
|
||||||
@@ -474,47 +681,85 @@ impl<'a, W: 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 value.len() as u64 != 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)?;
|
||||||
self.strip_offsets.push(offset as u32);
|
self.strip_offsets.push(u32::try_from(offset)?);
|
||||||
self.strip_byte_count.push(value.bytes() as u32);
|
self.strip_byte_count.push(value.bytes());
|
||||||
|
|
||||||
self.strip_idx += 1;
|
self.strip_idx += 1;
|
||||||
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 as u16);
|
self.encoder
|
||||||
self.encoder.write_tag(Tag::XResolution, value.clone());
|
.write_tag(Tag::ResolutionUnit, unit.to_u16())
|
||||||
self.encoder.write_tag(Tag::YResolution, value);
|
.unwrap();
|
||||||
|
self.encoder
|
||||||
|
.write_tag(Tag::XResolution, value.clone())
|
||||||
|
.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 as u16);
|
self.encoder
|
||||||
|
.write_tag(Tag::ResolutionUnit, unit.to_u16())
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set image x-resolution
|
/// Set image x-resolution
|
||||||
pub fn x_resolution(&mut self, value: Rational) {
|
pub fn x_resolution(&mut self, value: Rational) {
|
||||||
self.encoder.write_tag(Tag::XResolution, value);
|
self.encoder.write_tag(Tag::XResolution, value).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set image y-resolution
|
/// Set image y-resolution
|
||||||
pub fn y_resolution(&mut self, value: Rational) {
|
pub fn y_resolution(&mut self, value: Rational) {
|
||||||
self.encoder.write_tag(Tag::YResolution, value);
|
self.encoder.write_tag(Tag::YResolution, value).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_internal(&mut self) -> TiffResult<()> {
|
fn finish_internal(&mut self) -> TiffResult<()> {
|
||||||
self.encoder
|
self.encoder
|
||||||
.write_tag(Tag::StripOffsets, &*self.strip_offsets);
|
.write_tag(Tag::StripOffsets, &*self.strip_offsets)?;
|
||||||
self.encoder
|
self.encoder
|
||||||
.write_tag(Tag::StripByteCounts, &*self.strip_byte_count);
|
.write_tag(Tag::StripByteCounts, &*self.strip_byte_count)?;
|
||||||
self.dropped = true;
|
self.dropped = true;
|
||||||
|
|
||||||
self.encoder.finish_internal()
|
self.encoder.finish_internal()
|
||||||
|
|||||||
+49
-26
@@ -1,29 +1,17 @@
|
|||||||
use crate::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.offset += 4;
|
||||||
writer.writer.write_u16::<LittleEndian>(42)?;
|
|
||||||
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> {
|
||||||
@@ -47,27 +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;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_i8(&mut self, n: i8) -> Result<(), io::Error> {
|
||||||
|
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;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_i16(&mut self, n: i16) -> Result<(), io::Error> {
|
||||||
|
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;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_i32(&mut self, n: i32) -> Result<(), io::Error> {
|
||||||
|
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(())
|
||||||
@@ -78,6 +100,7 @@ impl<W: Write> TiffWriter<W> {
|
|||||||
let padding = [0, 0, 0];
|
let padding = [0, 0, 0];
|
||||||
let padd_len = 4 - (self.offset % 4);
|
let padd_len = 4 - (self.offset % 4);
|
||||||
self.writer.write_all(&padding[..padd_len as usize])?;
|
self.writer.write_all(&padding[..padd_len as usize])?;
|
||||||
|
self.offset += padd_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
+96
-10
@@ -3,36 +3,57 @@ use std::fmt;
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::string;
|
use std::string;
|
||||||
|
|
||||||
use decoder::ifd::{Tag, Value};
|
use decoder::ifd::Value;
|
||||||
use decoder::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration};
|
use miniz_oxide::inflate::TINFLStatus;
|
||||||
|
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag};
|
||||||
use ColorType;
|
use ColorType;
|
||||||
|
|
||||||
/// Tiff error kinds.
|
/// Tiff error kinds.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum TiffError {
|
pub enum TiffError {
|
||||||
/// The Image is not formatted properly
|
/// The Image is not formatted properly.
|
||||||
FormatError(TiffFormatError),
|
FormatError(TiffFormatError),
|
||||||
|
|
||||||
/// The Decoder does not support this image format
|
/// The Decoder does not support features required by the image.
|
||||||
UnsupportedError(TiffUnsupportedError),
|
UnsupportedError(TiffUnsupportedError),
|
||||||
|
|
||||||
/// An I/O Error occurred while decoding the image
|
/// An I/O Error occurred while decoding the image.
|
||||||
IoError(io::Error),
|
IoError(io::Error),
|
||||||
|
|
||||||
/// The Limits of the Decoder is exceeded,
|
/// The Limits of the Decoder is exceeded.
|
||||||
LimitsExceeded,
|
LimitsExceeded,
|
||||||
|
|
||||||
|
/// An integer conversion to or from a platform size failed, either due to
|
||||||
|
/// limits of the platform size or limits of the format.
|
||||||
|
IntSizeError,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
/// The image is not formatted properly.
|
||||||
|
///
|
||||||
|
/// This indicates that the encoder producing the image might behave incorrectly or that the input
|
||||||
|
/// file has been corrupted.
|
||||||
|
///
|
||||||
|
/// The list of variants may grow to incorporate errors of future features. Matching against this
|
||||||
|
/// exhaustively is not covered by interface stability guarantees.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum TiffFormatError {
|
pub enum TiffFormatError {
|
||||||
TiffSignatureNotFound,
|
TiffSignatureNotFound,
|
||||||
TiffSignatureInvalid,
|
TiffSignatureInvalid,
|
||||||
ImageFileDirectoryNotFound,
|
ImageFileDirectoryNotFound,
|
||||||
InconsistentSizesEncountered,
|
InconsistentSizesEncountered,
|
||||||
InvalidTag,
|
InvalidTag,
|
||||||
|
InvalidTagValueType(Tag),
|
||||||
RequiredTagNotFound(Tag),
|
RequiredTagNotFound(Tag),
|
||||||
UnknownPredictor(u32),
|
UnknownPredictor(u16),
|
||||||
|
ByteExpected(Value),
|
||||||
UnsignedIntegerExpected(Value),
|
UnsignedIntegerExpected(Value),
|
||||||
|
SignedIntegerExpected(Value),
|
||||||
|
InflateError(InflateError),
|
||||||
|
Format(String),
|
||||||
|
RequiredTagEmpty(Tag),
|
||||||
|
#[doc(hidden)]
|
||||||
|
/// Do not match against this variant. It may get removed.
|
||||||
|
__NonExhaustive,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for TiffFormatError {
|
impl fmt::Display for TiffFormatError {
|
||||||
@@ -44,17 +65,54 @@ 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) => {
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
SignedIntegerExpected(ref val) => {
|
||||||
|
write!(fmt, "Expected signed integer, {:?} found.", val)
|
||||||
|
}
|
||||||
|
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!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decompression failed due to faulty compressed data.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct InflateError {
|
||||||
|
status: TINFLStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InflateError {
|
||||||
|
pub(crate) fn new(status: TINFLStatus) -> Self {
|
||||||
|
Self { status }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffError {
|
||||||
|
pub(crate) fn from_inflate_status(status: TINFLStatus) -> Self {
|
||||||
|
TiffError::FormatError(TiffFormatError::InflateError(InflateError::new(status)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Decoder does not support features required by the image.
|
||||||
|
///
|
||||||
|
/// This only captures known failures for which the standard either does not require support or an
|
||||||
|
/// implementation has been planned but not yet completed. Some variants may become unused over
|
||||||
|
/// time and will then get deprecated before being removed.
|
||||||
|
///
|
||||||
|
/// The list of variants may grow. Matching against this exhaustively is not covered by interface
|
||||||
|
/// stability guarantees.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum TiffUnsupportedError {
|
pub enum TiffUnsupportedError {
|
||||||
HorizontalPredictor(ColorType),
|
HorizontalPredictor(ColorType),
|
||||||
@@ -63,10 +121,14 @@ 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>),
|
||||||
UnsupportedDataType,
|
UnsupportedDataType,
|
||||||
|
#[doc(hidden)]
|
||||||
|
/// Do not match against this variant. It may get removed.
|
||||||
|
__NonExhaustive,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for TiffUnsupportedError {
|
impl fmt::Display for TiffUnsupportedError {
|
||||||
@@ -92,7 +154,10 @@ impl fmt::Display for TiffUnsupportedError {
|
|||||||
write!(fmt, "Compression method {:?} is unsupported", method)
|
write!(fmt, "Compression method {:?} is unsupported", method)
|
||||||
}
|
}
|
||||||
UnsupportedSampleDepth(samples) => {
|
UnsupportedSampleDepth(samples) => {
|
||||||
write!(fmt, "{} samples per pixel is supported.", 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)
|
||||||
@@ -104,6 +169,7 @@ impl fmt::Display for TiffUnsupportedError {
|
|||||||
write!(fmt, "Unsupported planar configuration “{:?}”.", config)
|
write!(fmt, "Unsupported planar configuration “{:?}”.", config)
|
||||||
}
|
}
|
||||||
UnsupportedDataType => write!(fmt, "Unsupported data type."),
|
UnsupportedDataType => write!(fmt, "Unsupported data type."),
|
||||||
|
__NonExhaustive => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,6 +186,7 @@ impl fmt::Display for TiffError {
|
|||||||
),
|
),
|
||||||
TiffError::IoError(ref e) => e.fmt(fmt),
|
TiffError::IoError(ref e) => e.fmt(fmt),
|
||||||
TiffError::LimitsExceeded => write!(fmt, "The Decoder limits are exceeded"),
|
TiffError::LimitsExceeded => write!(fmt, "The Decoder limits are exceeded"),
|
||||||
|
TiffError::IntSizeError => write!(fmt, "Platform or format size limits exceeded"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,10 +198,11 @@ impl Error for TiffError {
|
|||||||
TiffError::UnsupportedError(..) => "Unsupported error",
|
TiffError::UnsupportedError(..) => "Unsupported error",
|
||||||
TiffError::IoError(..) => "IO error",
|
TiffError::IoError(..) => "IO error",
|
||||||
TiffError::LimitsExceeded => "Decoder limits exceeded",
|
TiffError::LimitsExceeded => "Decoder limits exceeded",
|
||||||
|
TiffError::IntSizeError => "Platform or format size limits exceeded",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cause(&self) -> Option<&Error> {
|
fn cause(&self) -> Option<&dyn Error> {
|
||||||
match *self {
|
match *self {
|
||||||
TiffError::IoError(ref e) => Some(e),
|
TiffError::IoError(ref e) => Some(e),
|
||||||
_ => None,
|
_ => None,
|
||||||
@@ -154,5 +222,23 @@ impl From<string::FromUtf8Error> for TiffError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<TiffFormatError> for TiffError {
|
||||||
|
fn from(err: TiffFormatError) -> TiffError {
|
||||||
|
TiffError::FormatError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TiffUnsupportedError> for TiffError {
|
||||||
|
fn from(err: TiffUnsupportedError) -> TiffError {
|
||||||
|
TiffError::UnsupportedError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::num::TryFromIntError> for TiffError {
|
||||||
|
fn from(_err: std::num::TryFromIntError) -> TiffError {
|
||||||
|
TiffError::IntSizeError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Result of an image decoding/encoding process
|
/// Result of an image decoding/encoding process
|
||||||
pub type TiffResult<T> = Result<T, TiffError>;
|
pub type TiffResult<T> = Result<T, TiffError>;
|
||||||
+7
-7
@@ -4,19 +4,19 @@
|
|||||||
//! 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;
|
||||||
#[macro_use]
|
extern crate weezl;
|
||||||
extern crate num_derive;
|
|
||||||
extern crate num_traits;
|
|
||||||
|
|
||||||
|
mod bytecast;
|
||||||
pub mod decoder;
|
pub mod decoder;
|
||||||
pub mod encoder;
|
pub mod encoder;
|
||||||
mod error;
|
mod error;
|
||||||
|
pub mod tags;
|
||||||
|
|
||||||
pub use self::error::{TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
pub use self::error::{InflateError, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
||||||
|
|
||||||
/// An enumeration over supported color types and their bit depths
|
/// An enumeration over supported color types and their bit depths
|
||||||
#[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)]
|
#[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)]
|
||||||
|
|||||||
+207
@@ -0,0 +1,207 @@
|
|||||||
|
macro_rules! tags {
|
||||||
|
{
|
||||||
|
// Permit arbitrary meta items, which include documentation.
|
||||||
|
$( #[$enum_attr:meta] )*
|
||||||
|
$vis:vis enum $name:ident($ty:tt) $(unknown($unknown_doc:literal))* {
|
||||||
|
// Each of the `Name = Val,` permitting documentation.
|
||||||
|
$($(#[$ident_attr:meta])* $tag:ident = $val:expr,)*
|
||||||
|
}
|
||||||
|
} => {
|
||||||
|
$( #[$enum_attr] )*
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||||
|
pub enum $name {
|
||||||
|
$($(#[$ident_attr])* $tag,)*
|
||||||
|
// FIXME: switch to non_exhaustive once stabilized and compiler requirement new enough
|
||||||
|
#[doc(hidden)]
|
||||||
|
__NonExhaustive,
|
||||||
|
$(
|
||||||
|
#[doc = $unknown_doc]
|
||||||
|
Unknown($ty),
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $name {
|
||||||
|
#[inline(always)]
|
||||||
|
fn __from_inner_type(n: $ty) -> Result<Self, $ty> {
|
||||||
|
match n {
|
||||||
|
$( $val => Ok($name::$tag), )*
|
||||||
|
n => Err(n),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn __to_inner_type(&self) -> $ty {
|
||||||
|
match *self {
|
||||||
|
$( $name::$tag => $val, )*
|
||||||
|
$( $name::Unknown(n) => { $unknown_doc; n }, )*
|
||||||
|
$name::__NonExhaustive => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tags!($name, $ty, $($unknown_doc)*);
|
||||||
|
};
|
||||||
|
// For u16 tags, provide direct inherent primitive conversion methods.
|
||||||
|
($name:tt, u16, $($unknown_doc:literal)*) => {
|
||||||
|
impl $name {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn from_u16(val: u16) -> Option<Self> {
|
||||||
|
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)]
|
||||||
|
pub fn to_u16(&self) -> u16 {
|
||||||
|
Self::__to_inner_type(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// For other tag types, do nothing for now. With concat_idents one could
|
||||||
|
// provide inherent conversion methods for all types.
|
||||||
|
($name:tt, $ty:tt, $($unknown_doc:literal)*) => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: These tags appear in the order they are mentioned in the TIFF reference
|
||||||
|
tags! {
|
||||||
|
/// TIFF tags
|
||||||
|
pub enum Tag(u16) unknown("A private or extension tag") {
|
||||||
|
// Baseline tags:
|
||||||
|
Artist = 315,
|
||||||
|
// grayscale images PhotometricInterpretation 1 or 3
|
||||||
|
BitsPerSample = 258,
|
||||||
|
CellLength = 265, // TODO add support
|
||||||
|
CellWidth = 264, // TODO add support
|
||||||
|
// palette-color images (PhotometricInterpretation 3)
|
||||||
|
ColorMap = 320, // TODO add support
|
||||||
|
Compression = 259, // TODO add support for 2 and 32773
|
||||||
|
Copyright = 33_432,
|
||||||
|
DateTime = 306,
|
||||||
|
ExtraSamples = 338, // TODO add support
|
||||||
|
FillOrder = 266, // TODO add support
|
||||||
|
FreeByteCounts = 289, // TODO add support
|
||||||
|
FreeOffsets = 288, // TODO add support
|
||||||
|
GrayResponseCurve = 291, // TODO add support
|
||||||
|
GrayResponseUnit = 290, // TODO add support
|
||||||
|
HostComputer = 316,
|
||||||
|
ImageDescription = 270,
|
||||||
|
ImageLength = 257,
|
||||||
|
ImageWidth = 256,
|
||||||
|
Make = 271,
|
||||||
|
MaxSampleValue = 281, // TODO add support
|
||||||
|
MinSampleValue = 280, // TODO add support
|
||||||
|
Model = 272,
|
||||||
|
NewSubfileType = 254, // TODO add support
|
||||||
|
Orientation = 274, // TODO add support
|
||||||
|
PhotometricInterpretation = 262,
|
||||||
|
PlanarConfiguration = 284,
|
||||||
|
ResolutionUnit = 296, // TODO add support
|
||||||
|
RowsPerStrip = 278,
|
||||||
|
SamplesPerPixel = 277,
|
||||||
|
Software = 305,
|
||||||
|
StripByteCounts = 279,
|
||||||
|
StripOffsets = 273,
|
||||||
|
SubfileType = 255, // TODO add support
|
||||||
|
Threshholding = 263, // TODO add support
|
||||||
|
XResolution = 282,
|
||||||
|
YResolution = 283,
|
||||||
|
// JPEG
|
||||||
|
JPEGTables = 347,
|
||||||
|
// Advanced tags
|
||||||
|
Predictor = 317,
|
||||||
|
TileWidth = 322,
|
||||||
|
TileLength = 323,
|
||||||
|
TileOffsets = 324,
|
||||||
|
TileByteCounts = 325,
|
||||||
|
SampleFormat = 339,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tags! {
|
||||||
|
/// The type of an IFD entry (a 2 byte field).
|
||||||
|
pub enum Type(u16) {
|
||||||
|
BYTE = 1,
|
||||||
|
ASCII = 2,
|
||||||
|
SHORT = 3,
|
||||||
|
LONG = 4,
|
||||||
|
RATIONAL = 5,
|
||||||
|
SBYTE = 6,
|
||||||
|
UNDEFINED = 7,
|
||||||
|
SSHORT = 8,
|
||||||
|
SLONG = 9,
|
||||||
|
SRATIONAL = 10,
|
||||||
|
FLOAT = 11,
|
||||||
|
DOUBLE = 12,
|
||||||
|
/// BigTIFF 64-bit unsigned integer
|
||||||
|
LONG8 = 16,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tags! {
|
||||||
|
/// See [TIFF compression tags](https://www.awaresystems.be/imaging/tiff/tifftags/compression.html)
|
||||||
|
/// for reference.
|
||||||
|
pub enum CompressionMethod(u16) {
|
||||||
|
None = 1,
|
||||||
|
Huffman = 2,
|
||||||
|
Fax3 = 3,
|
||||||
|
Fax4 = 4,
|
||||||
|
LZW = 5,
|
||||||
|
JPEG = 6,
|
||||||
|
// "Extended JPEG" or "new JPEG" style
|
||||||
|
ModernJPEG = 7,
|
||||||
|
Deflate = 8,
|
||||||
|
OldDeflate = 0x80B2,
|
||||||
|
PackBits = 0x8005,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tags! {
|
||||||
|
pub enum PhotometricInterpretation(u16) {
|
||||||
|
WhiteIsZero = 0,
|
||||||
|
BlackIsZero = 1,
|
||||||
|
RGB = 2,
|
||||||
|
RGBPalette = 3,
|
||||||
|
TransparencyMask = 4,
|
||||||
|
CMYK = 5,
|
||||||
|
YCbCr = 6,
|
||||||
|
CIELab = 8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tags! {
|
||||||
|
pub enum PlanarConfiguration(u16) {
|
||||||
|
Chunky = 1,
|
||||||
|
Planar = 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tags! {
|
||||||
|
pub enum Predictor(u16) {
|
||||||
|
None = 1,
|
||||||
|
Horizontal = 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tags! {
|
||||||
|
/// Type to represent resolution units
|
||||||
|
pub enum ResolutionUnit(u16) {
|
||||||
|
None = 1,
|
||||||
|
Inch = 2,
|
||||||
|
Centimeter = 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+130
-32
@@ -1,62 +1,154 @@
|
|||||||
extern crate tiff;
|
extern crate tiff;
|
||||||
|
|
||||||
use tiff::decoder::{ifd::Tag, ifd::Value, Decoder, DecodingResult};
|
use tiff::decoder::{ifd, Decoder, DecodingResult};
|
||||||
use tiff::ColorType;
|
use tiff::ColorType;
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[test]
|
const TEST_IMAGE_DIR: &str = "./tests/images/";
|
||||||
fn test_gray_u8() {
|
|
||||||
let img_file =
|
macro_rules! test_image_sum {
|
||||||
File::open("./tests/images/minisblack-1c-8b.tiff").expect("Cannot find test image!");
|
($name:ident, $buffer:ident, $sum_ty:ty) => {
|
||||||
|
fn $name(file: &str, expected_type: ColorType, expected_sum: $sum_ty) {
|
||||||
|
let path = PathBuf::from(TEST_IMAGE_DIR).join(file);
|
||||||
|
let img_file = File::open(path).expect("Cannot find test image!");
|
||||||
|
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||||
|
assert_eq!(decoder.colortype().unwrap(), expected_type);
|
||||||
|
let img_res = decoder.read_image().unwrap();
|
||||||
|
|
||||||
|
match img_res {
|
||||||
|
DecodingResult::$buffer(res) => {
|
||||||
|
let sum: $sum_ty = res.into_iter().map(<$sum_ty>::from).sum();
|
||||||
|
assert_eq!(sum, expected_sum);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong bit depth"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test_image_sum!(test_image_sum_u8, U8, u64);
|
||||||
|
test_image_sum!(test_image_sum_u16, U16, u64);
|
||||||
|
test_image_sum!(test_image_sum_u32, U32, u64);
|
||||||
|
test_image_sum!(test_image_sum_u64, U64, u64);
|
||||||
|
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
|
||||||
|
/// read from the IFD and is of the appropriate type, but the type is
|
||||||
|
/// unsupported.
|
||||||
|
fn test_image_color_type_unsupported(file: &str, expected_type: ColorType) {
|
||||||
|
let path = PathBuf::from(TEST_IMAGE_DIR).join(file);
|
||||||
|
let img_file = File::open(path).expect("Cannot find test image!");
|
||||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||||
assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(8));
|
assert_eq!(decoder.colortype().unwrap(), expected_type);
|
||||||
let img_res = decoder.read_image();
|
assert!(match decoder.read_image() {
|
||||||
assert!(img_res.is_ok());
|
Err(tiff::TiffError::UnsupportedError(
|
||||||
|
tiff::TiffUnsupportedError::UnsupportedColorType(_),
|
||||||
|
)) => true,
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rgb_u8() {
|
fn test_cmyk_u8() {
|
||||||
let img_file = File::open("./tests/images/rgb-3c-8b.tiff").expect("Cannot find test image!");
|
test_image_sum_u8("cmyk-3c-8b.tiff", ColorType::CMYK(8), 8522658);
|
||||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
}
|
||||||
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
|
|
||||||
let img_res = decoder.read_image();
|
#[test]
|
||||||
assert!(img_res.is_ok());
|
fn test_cmyk_u16() {
|
||||||
|
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]
|
||||||
|
fn test_gray_u8() {
|
||||||
|
test_image_sum_u8("minisblack-1c-8b.tiff", ColorType::Gray(8), 2840893);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gray_u12() {
|
||||||
|
test_image_color_type_unsupported("12bit.cropped.tiff", ColorType::Gray(12));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_gray_u16() {
|
fn test_gray_u16() {
|
||||||
let img_file =
|
test_image_sum_u16("minisblack-1c-16b.tiff", ColorType::Gray(16), 733126239);
|
||||||
File::open("./tests/images/minisblack-1c-16b.tiff").expect("Cannot find test image!");
|
}
|
||||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
|
||||||
assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(16));
|
#[test]
|
||||||
let img_res = decoder.read_image();
|
fn test_gray_u32() {
|
||||||
assert!(img_res.is_ok());
|
test_image_sum_u32("gradient-1c-32b.tiff", ColorType::Gray(32), 549892913787);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gray_u64() {
|
||||||
|
test_image_sum_u64("gradient-1c-64b.tiff", ColorType::Gray(64), 549892913787);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_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]
|
||||||
|
fn test_rgb_u8() {
|
||||||
|
test_image_sum_u8("rgb-3c-8b.tiff", ColorType::RGB(8), 7842108);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rgb_u12() {
|
||||||
|
test_image_color_type_unsupported("12bit.cropped.rgb.tiff", ColorType::RGB(12));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rgb_u16() {
|
fn test_rgb_u16() {
|
||||||
let img_file = File::open("./tests/images/rgb-3c-16b.tiff").expect("Cannot find test image!");
|
test_image_sum_u16("rgb-3c-16b.tiff", ColorType::RGB(16), 2024349944);
|
||||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
}
|
||||||
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(16));
|
|
||||||
let img_res = decoder.read_image();
|
#[test]
|
||||||
assert!(img_res.is_ok());
|
fn test_rgb_u32() {
|
||||||
|
test_image_sum_u32("gradient-3c-32b.tiff", ColorType::RGB(32), 2030834111716);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rgb_u64() {
|
||||||
|
test_image_sum_u64("gradient-3c-64b.tiff", ColorType::RGB(64), 2030834111716);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_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
|
||||||
// the null byte, so we check both to ensure that we're truncating properly
|
// the null byte, so we check both to ensure that we're truncating properly
|
||||||
let filenames = vec!["minisblack-1c-16b.tiff", "rgb-3c-16b.tiff"];
|
let filenames = ["minisblack-1c-16b.tiff", "rgb-3c-16b.tiff"];
|
||||||
for filename in filenames.iter() {
|
for filename in filenames.iter() {
|
||||||
let path = format!("./tests/images/{}", filename);
|
let path = PathBuf::from(TEST_IMAGE_DIR).join(filename);
|
||||||
let img_file = File::open(path).expect("can't open file");
|
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");
|
||||||
let software = decoder.get_tag(Tag::Software).unwrap();
|
let software = decoder.get_tag(tiff::tags::Tag::Software).unwrap();
|
||||||
match software {
|
match software {
|
||||||
Value::Ascii(s) => assert_eq!(
|
ifd::Value::Ascii(s) => assert_eq!(
|
||||||
s,
|
&s,
|
||||||
String::from("GraphicsMagick 1.2 unreleased Q16 http://www.GraphicsMagick.org/")
|
"GraphicsMagick 1.2 unreleased Q16 http://www.GraphicsMagick.org/"
|
||||||
),
|
),
|
||||||
_ => assert!(false),
|
_ => assert!(false),
|
||||||
};
|
};
|
||||||
@@ -85,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()
|
||||||
|
|||||||
+290
-105
@@ -1,12 +1,13 @@
|
|||||||
extern crate tempfile;
|
|
||||||
extern crate tiff;
|
extern crate tiff;
|
||||||
|
|
||||||
use tiff::decoder::{Decoder, DecodingResult};
|
use tiff::decoder::{ifd, Decoder, DecodingResult};
|
||||||
use tiff::encoder::{colortype, TiffEncoder};
|
use tiff::encoder::{colortype, SRational, TiffEncoder};
|
||||||
|
use tiff::tags::Tag;
|
||||||
use tiff::ColorType;
|
use tiff::ColorType;
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Cursor, Seek, SeekFrom};
|
use std::io::{Cursor, Seek, SeekFrom};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encode_decode() {
|
fn encode_decode() {
|
||||||
@@ -19,18 +20,26 @@ fn encode_decode() {
|
|||||||
image_data.push(val);
|
image_data.push(val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut file = tempfile::tempfile().unwrap();
|
let mut file = Cursor::new(Vec::new());
|
||||||
{
|
{
|
||||||
let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
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 {
|
||||||
@@ -53,125 +62,301 @@ fn test_encode_undersized_buffer() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TEST_IMAGE_DIR: &str = "./tests/images/";
|
||||||
|
|
||||||
|
macro_rules! test_roundtrip {
|
||||||
|
($name:ident, $buffer:ident, $buffer_ty:ty) => {
|
||||||
|
fn $name<C: colortype::ColorType<Inner = $buffer_ty>>(
|
||||||
|
file: &str,
|
||||||
|
expected_type: ColorType,
|
||||||
|
) {
|
||||||
|
let path = PathBuf::from(TEST_IMAGE_DIR).join(file);
|
||||||
|
let img_file = File::open(path).expect("Cannot find test image!");
|
||||||
|
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||||
|
assert_eq!(decoder.colortype().unwrap(), expected_type);
|
||||||
|
|
||||||
|
let image_data = match decoder.read_image().unwrap() {
|
||||||
|
DecodingResult::$buffer(res) => res,
|
||||||
|
_ => panic!("Wrong data type"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut file = Cursor::new(Vec::new());
|
||||||
|
{
|
||||||
|
let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
||||||
|
|
||||||
|
let (width, height) = decoder.dimensions().unwrap();
|
||||||
|
tiff.write_image::<C>(width, height, &image_data).unwrap();
|
||||||
|
}
|
||||||
|
file.seek(SeekFrom::Start(0)).unwrap();
|
||||||
|
{
|
||||||
|
let mut decoder = Decoder::new(&mut file).unwrap();
|
||||||
|
if let DecodingResult::$buffer(img_res) = decoder.read_image().unwrap() {
|
||||||
|
assert_eq!(image_data, img_res);
|
||||||
|
} else {
|
||||||
|
panic!("Wrong data type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test_roundtrip!(test_u8_roundtrip, U8, u8);
|
||||||
|
test_roundtrip!(test_u16_roundtrip, U16, u16);
|
||||||
|
test_roundtrip!(test_u32_roundtrip, U32, u32);
|
||||||
|
test_roundtrip!(test_u64_roundtrip, U64, u64);
|
||||||
|
test_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() {
|
||||||
let img_file =
|
test_u8_roundtrip::<colortype::Gray8>("minisblack-1c-8b.tiff", ColorType::Gray(8));
|
||||||
File::open("./tests/images/minisblack-1c-8b.tiff").expect("Cannot find test image!");
|
|
||||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
|
||||||
assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(8));
|
|
||||||
|
|
||||||
let image_data = match decoder.read_image().unwrap() {
|
|
||||||
DecodingResult::U8(res) => res,
|
|
||||||
_ => panic!("Wrong data type"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut file = tempfile::tempfile().unwrap();
|
|
||||||
{
|
|
||||||
let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
|
||||||
|
|
||||||
let (width, height) = decoder.dimensions().unwrap();
|
|
||||||
tiff.write_image::<colortype::Gray8>(width, height, &image_data)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
file.seek(SeekFrom::Start(0)).unwrap();
|
|
||||||
{
|
|
||||||
let mut decoder = Decoder::new(&mut file).unwrap();
|
|
||||||
if let DecodingResult::U8(img_res) = decoder.read_image().unwrap() {
|
|
||||||
assert_eq!(image_data, img_res);
|
|
||||||
} else {
|
|
||||||
panic!("Wrong data type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rgb_u8_roundtrip() {
|
fn test_rgb_u8_roundtrip() {
|
||||||
let img_file = File::open("./tests/images/rgb-3c-8b.tiff").expect("Cannot find test image!");
|
test_u8_roundtrip::<colortype::RGB8>("rgb-3c-8b.tiff", ColorType::RGB(8));
|
||||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
}
|
||||||
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
|
|
||||||
|
|
||||||
let image_data = match decoder.read_image().unwrap() {
|
#[test]
|
||||||
DecodingResult::U8(res) => res,
|
fn test_cmyk_u8_roundtrip() {
|
||||||
_ => panic!("Wrong data type"),
|
test_u8_roundtrip::<colortype::CMYK8>("cmyk-3c-8b.tiff", ColorType::CMYK(8));
|
||||||
};
|
|
||||||
|
|
||||||
let mut file = tempfile::tempfile().unwrap();
|
|
||||||
{
|
|
||||||
let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
|
||||||
|
|
||||||
let (width, height) = decoder.dimensions().unwrap();
|
|
||||||
tiff.write_image::<colortype::RGB8>(width, height, &image_data)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
file.seek(SeekFrom::Start(0)).unwrap();
|
|
||||||
{
|
|
||||||
let mut decoder = Decoder::new(&mut file).unwrap();
|
|
||||||
if let DecodingResult::U8(img_res) = decoder.read_image().unwrap() {
|
|
||||||
assert_eq!(image_data, img_res);
|
|
||||||
} else {
|
|
||||||
panic!("Wrong data type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_gray_u16_roundtrip() {
|
fn test_gray_u16_roundtrip() {
|
||||||
let img_file =
|
test_u16_roundtrip::<colortype::Gray16>("minisblack-1c-16b.tiff", ColorType::Gray(16));
|
||||||
File::open("./tests/images/minisblack-1c-16b.tiff").expect("Cannot find test image!");
|
|
||||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
|
||||||
assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(16));
|
|
||||||
|
|
||||||
let image_data = match decoder.read_image().unwrap() {
|
|
||||||
DecodingResult::U16(res) => res,
|
|
||||||
_ => panic!("Wrong data type"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut file = tempfile::tempfile().unwrap();
|
|
||||||
{
|
|
||||||
let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
|
||||||
|
|
||||||
let (width, height) = decoder.dimensions().unwrap();
|
|
||||||
tiff.write_image::<colortype::Gray16>(width, height, &image_data)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
file.seek(SeekFrom::Start(0)).unwrap();
|
|
||||||
{
|
|
||||||
let mut decoder = Decoder::new(&mut file).unwrap();
|
|
||||||
if let DecodingResult::U16(img_res) = decoder.read_image().unwrap() {
|
|
||||||
assert_eq!(image_data, img_res);
|
|
||||||
} else {
|
|
||||||
panic!("Wrong data type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rgb_u16_roundtrip() {
|
fn test_rgb_u16_roundtrip() {
|
||||||
let img_file = File::open("./tests/images/rgb-3c-16b.tiff").expect("Cannot find test image!");
|
test_u16_roundtrip::<colortype::RGB16>("rgb-3c-16b.tiff", ColorType::RGB(16));
|
||||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
}
|
||||||
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(16));
|
|
||||||
|
|
||||||
let image_data = match decoder.read_image().unwrap() {
|
#[test]
|
||||||
DecodingResult::U16(res) => res,
|
fn test_cmyk_u16_roundtrip() {
|
||||||
_ => panic!("Wrong data type"),
|
test_u16_roundtrip::<colortype::CMYK16>("cmyk-3c-16b.tiff", ColorType::CMYK(16));
|
||||||
};
|
}
|
||||||
|
|
||||||
let mut file = tempfile::tempfile().unwrap();
|
#[test]
|
||||||
{
|
fn test_gray_u32_roundtrip() {
|
||||||
let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
test_u32_roundtrip::<colortype::Gray32>("gradient-1c-32b.tiff", ColorType::Gray(32));
|
||||||
|
}
|
||||||
|
|
||||||
let (width, height) = decoder.dimensions().unwrap();
|
#[test]
|
||||||
tiff.write_image::<colortype::RGB16>(width, height, &image_data)
|
fn test_rgb_u32_roundtrip() {
|
||||||
.unwrap();
|
test_u32_roundtrip::<colortype::RGB32>("gradient-3c-32b.tiff", ColorType::RGB(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gray_u64_roundtrip() {
|
||||||
|
test_u64_roundtrip::<colortype::Gray64>("gradient-1c-64b.tiff", ColorType::Gray(64));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rgb_u64_roundtrip() {
|
||||||
|
test_u64_roundtrip::<colortype::RGB64>("gradient-3c-64b.tiff", ColorType::RGB(64));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
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()
|
||||||
}
|
}
|
||||||
file.seek(SeekFrom::Start(0)).unwrap();
|
fn assert_tag_u32_vec(&mut self, tag: u16) -> Vec<u32> {
|
||||||
{
|
self.get_tag(Tag::Unknown(tag))
|
||||||
let mut decoder = Decoder::new(&mut file).unwrap();
|
.unwrap()
|
||||||
if let DecodingResult::U16(img_res) = decoder.read_image().unwrap() {
|
.into_u32_vec()
|
||||||
assert_eq!(image_data, img_res);
|
.unwrap()
|
||||||
} else {
|
}
|
||||||
panic!("Wrong data type");
|
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]
|
||||||
|
fn test_multiple_byte() {
|
||||||
|
let mut data = Cursor::new(Vec::new());
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut tiff = TiffEncoder::new(&mut data).unwrap();
|
||||||
|
let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap();
|
||||||
|
let encoder = image_encoder.encoder();
|
||||||
|
|
||||||
|
encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]).unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65001), &[1_u8, 2][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65002), &[1_u8, 2, 3][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65003), &[1_u8, 2, 3, 4][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65004), &[1_u8, 2, 3, 4, 5][..])
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
data.set_position(0);
|
||||||
|
{
|
||||||
|
let mut decoder = Decoder::new(&mut data).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(decoder.assert_tag_u32_vec(65000), [1]);
|
||||||
|
assert_eq!(decoder.assert_tag_u32_vec(65001), [1, 2]);
|
||||||
|
assert_eq!(decoder.assert_tag_u32_vec(65002), [1, 2, 3]);
|
||||||
|
assert_eq!(decoder.assert_tag_u32_vec(65003), [1, 2, 3, 4]);
|
||||||
|
assert_eq!(decoder.assert_tag_u32_vec(65004), [1, 2, 3, 4, 5]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// Test writing signed tags from TIFF 6.0
|
||||||
|
fn test_signed() {
|
||||||
|
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 image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap();
|
||||||
|
let encoder = image_encoder.encoder();
|
||||||
|
|
||||||
|
//Use the "reusable" tags section as per the TIFF6 spec
|
||||||
|
encoder.write_tag(Tag::Unknown(65000), -1_i8).unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65001), &[-1_i8][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65002), &[-1_i8, 2][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65003), &[-1_i8, 2, -3][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65004), &[-1_i8, 2, -3, 4][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65005), &[-1_i8, 2, -3, 4, -5][..])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
encoder.write_tag(Tag::Unknown(65010), -1_i16).unwrap();
|
||||||
|
encoder.write_tag(Tag::Unknown(65011), -1_i16).unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65012), &[-1_i16, 2][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65013), &[-1_i16, 2, -3][..])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
encoder.write_tag(Tag::Unknown(65020), -1_i32).unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65021), &[-1_i32][..])
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65022), &[-1_i32, 2][..])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
encoder
|
||||||
|
.write_tag(Tag::Unknown(65030), make_srational(-1))
|
||||||
|
.unwrap();
|
||||||
|
encoder
|
||||||
|
.write_tag(
|
||||||
|
Tag::Unknown(65031),
|
||||||
|
&[make_srational(-1), make_srational(2)][..],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Rewind the cursor for reading
|
||||||
|
data.set_position(0);
|
||||||
|
{
|
||||||
|
let mut decoder = Decoder::new(&mut data).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(decoder.assert_tag_i32(65000), -1);
|
||||||
|
assert_eq!(decoder.assert_tag_i32_vec(65001), [-1]);
|
||||||
|
assert_eq!(decoder.assert_tag_i32_vec(65002), [-1, 2]);
|
||||||
|
assert_eq!(decoder.assert_tag_i32_vec(65003), [-1, 2, -3]);
|
||||||
|
assert_eq!(decoder.assert_tag_i32_vec(65004), [-1, 2, -3, 4]);
|
||||||
|
assert_eq!(decoder.assert_tag_i32_vec(65005), [-1, 2, -3, 4, -5],);
|
||||||
|
|
||||||
|
assert_eq!(decoder.assert_tag_i32(65010), -1);
|
||||||
|
assert_eq!(decoder.assert_tag_i32_vec(65011), [-1]);
|
||||||
|
assert_eq!(decoder.assert_tag_i32_vec(65012), [-1, 2]);
|
||||||
|
assert_eq!(decoder.assert_tag_i32_vec(65013), [-1, 2, -3]);
|
||||||
|
|
||||||
|
assert_eq!(decoder.assert_tag_i32(65020), -1);
|
||||||
|
assert_eq!(decoder.assert_tag_i32_vec(65021), [-1]);
|
||||||
|
assert_eq!(decoder.assert_tag_i32_vec(65022), [-1, 2]);
|
||||||
|
|
||||||
|
assert_eq!(decoder.assert_tag_i32_vec(65030), [-1, 100]);
|
||||||
|
assert_eq!(decoder.assert_tag_i32_vec(65031), [-1_i32, 100, 2, 100]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// check multipage image handling
|
||||||
|
fn test_multipage_image() {
|
||||||
|
let mut img_file = Cursor::new(Vec::new());
|
||||||
|
|
||||||
|
{
|
||||||
|
// first create a multipage image with 2 images
|
||||||
|
let mut img_encoder = TiffEncoder::new(&mut img_file).unwrap();
|
||||||
|
|
||||||
|
// write first grayscale image (2x2 16-bit)
|
||||||
|
let img1: Vec<u16> = [1, 2, 3, 4].to_vec();
|
||||||
|
img_encoder
|
||||||
|
.write_image::<colortype::Gray16>(2, 2, &img1[..])
|
||||||
|
.unwrap();
|
||||||
|
// write second grayscale image (3x3 8-bit)
|
||||||
|
let img2: Vec<u8> = [9, 8, 7, 6, 5, 4, 3, 2, 1].to_vec();
|
||||||
|
img_encoder
|
||||||
|
.write_image::<colortype::Gray8>(3, 3, &img2[..])
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// seek to the beginning of the file, so that it can be decoded
|
||||||
|
img_file.seek(SeekFrom::Start(0)).unwrap();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut img_decoder = Decoder::new(&mut img_file).unwrap();
|
||||||
|
|
||||||
|
// check the dimensions of the image in the first page
|
||||||
|
assert_eq!(img_decoder.dimensions().unwrap(), (2, 2));
|
||||||
|
img_decoder.next_image().unwrap();
|
||||||
|
// check the dimensions of the image in the second page
|
||||||
|
assert_eq!(img_decoder.dimensions().unwrap(), (3, 3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loaded 100 of 165 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user