Compare commits

..
Author SHA1 Message Date
Atul Bhosale 0ed3d90f19 Add cargo fmt to travis build config 2020-03-18 23:29:36 +05:30
42 changed files with 1025 additions and 3541 deletions

No files matched your search

-40
View File
@@ -1,40 +0,0 @@
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
+11
View File
@@ -0,0 +1,11 @@
language: rust
rust:
- 1.34.2
- stable
- beta
- nightly
script:
- if [[ "$TRAVIS_RUST_VERSION" == *stable* ]]; then rustup component add rustfmt; cargo fmt --version; cargo fmt -- --check || (echo "Please reformat your code with 'cargo fmt' (version $(cargo fmt --version))"; false); fi
- cargo build -v
- cargo test -v
-74
View File
@@ -1,77 +1,3 @@
# Version 0.7.0
New features:
* Support for encoding BigTiff ([#122](https://github.com/image-rs/image-tiff/pull/122))
* _Breaking:_ Encoder types now have a generic parameter to differentiate BigTiff and standard Tiff encoding. Defaults to standard Tiff.
* Basic tile decoding ([#125](https://github.com/image-rs/image-tiff/pull/125))
* _Breaking:_ There is a new `TiffError::UsageError` variant.
* Support for datatypes `Int8` and `Int16` ([#114](https://github.com/image-rs/image-tiff/pull/114))
* _Breaking:_ `DecodingResult` and `DecodingBuffer` have the two new variants `I8` and `I16`.
* Support for `i32` arrays ([#118](https://github.com/image-rs/image-tiff/pull/118/files))
* _Breaking:_ `DecodingResult` and `DecodingBuffer` have a new `I32` variant.
* Support for `Ifd` and `IfdBig` tag types and `I64` data type ([#119](https://github.com/image-rs/image-tiff/pull/119))
* _Breaking:_ `DecodingResult` and `DecodingBuffer` have a new `I64` variant.
* Add `SMinSampleValue` and `SMaxSampleValue` ([#123](https://github.com/image-rs/image-tiff/pull/123))
Changes:
* Improve deflate support ([#132](https://github.com/image-rs/image-tiff/pull/132))
* Switch to streaming decompression via `flate2`. Aside from performance improvements and lower RAM consumption, this fixes a bug where `max_uncompressed_length` was precalculated for a single tile but then used as a hard limit on the whole data, failing to decompress any tiled images.
* Add support for new `Deflate` tag in addition to `OldDeflate`.
* _Breaking:_ Remove `InflateError`, which is no longer needed with `flate2` ([#134](https://github.com/image-rs/image-tiff/pull/134))
* _Breaking:_ Support for `MinIsWhite` is restricted to unsigned and floating
point values. This is expected to be be re-added once some contradictory
interpretation regarding semantics for signed values is resolved.
Fixes:
* Validate that ASCII tags are valid ASCII and end with a null byte ([#121](https://github.com/image-rs/image-tiff/pull/121))
Internal:
* Simplify decompression logic ([#126](https://github.com/image-rs/image-tiff/pull/126))
* Simplify `expand_strip` ([#128](https://github.com/image-rs/image-tiff/pull/128))
# Version 0.6.1
New features:
* Support for reading `u16` and ascii string tags.
* Added `Limits::unlimited` for disabling all limits.
* Added `ImageEncoder::rows_per_strip` to overwrite the default.
Changes:
* The default strip size for chunked encoding is now 1MB, up from 8KB. This
should lead to more efficient decoding and compression.
Fixes:
* Fixed a bug where LZW compressed strips could not be decoded, instead
returning an error `Inconsistent sizes encountered`.
* Reading a tag with a complex type and a single value returns the proper Value
variant, instead of a vector with one entry.
# Version 0.6.0
New features:
* 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.
+4 -12
View File
@@ -1,6 +1,6 @@
[package]
name = "tiff"
version = "0.7.0"
version = "0.4.0"
authors = [
"ccgn",
"bvssvni <bvssvni@gmail.com>",
@@ -15,16 +15,8 @@ license = "MIT"
description = "TIFF decoding and encoding library in pure Rust"
repository = "https://github.com/image-rs/image-tiff"
exclude = ["tests/images/*", "tests/fuzz_images/*"]
edition = "2018"
[dependencies]
weezl = "0.1.0"
jpeg = { package = "jpeg-decoder", version = "0.1.17", default-features = false }
flate2 = "1.0.20"
[dev-dependencies]
criterion = "0.3.1"
[[bench]]
name = "lzw"
harness = false
byteorder = "1.2"
lzw = "0.10"
miniz_oxide = "0.3"
+6 -6
View File
@@ -1,5 +1,5 @@
# image-tiff
[![Build Status](https://github.com/image-rs/image-tiff/workflows/Rust%20CI/badge.svg)](https://github.com/image-rs/image-tiff/actions)
[![Build Status](https://travis-ci.org/image-rs/image-tiff.svg?branch=master)](https://travis-ci.org/image-rs/image-tiff)
[![Documentation](https://docs.rs/tiff/badge.svg)](https://docs.rs/tiff)
[![Further crate info](https://img.shields.io/crates/v/tiff.svg)](https://crates.io/crates/tiff)
@@ -10,7 +10,6 @@ TIFF decoding and encoding library in pure Rust
### Features
- Baseline spec (other than formats and tags listed below as not supported)
- Multipage
- BigTIFF
- Incremental decoding
### Formats
@@ -18,12 +17,12 @@ This table lists photometric interpretations and sample formats which are suppor
| `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) |
| `WhiteIsZero` | Gray(8\|16\|32\|64) | |
| `BlackIsZero` | Gray(8\|16\|32\|64) | |
| `RGB` | RGB(8\|16\|32\|64), RGBA(8\|16\|32\|64) | |
| `RGBPalette` | | |
| `Mask` | | |
| `CMYK` | CMYK(8\|16\|32\|64) | CMYK(32\|64) |
| `CMYK` | CMYK(8\|16\|32\|64) | |
| `YCbCr` | | |
| `CIELab` | | |
@@ -44,3 +43,4 @@ Formats and interpretations not listed above or with empty entries are unsupport
- Baseline tags
- `ExtraSamples`
- Extension tags
- BigTIFF
-82
View File
@@ -1,82 +0,0 @@
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,
},
);
}
-34
View File
@@ -1,34 +0,0 @@
//! 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, i8_as_ne_mut_bytes);
integral_slice_as_bytes!(u16, u16_as_ne_bytes, u16_as_ne_mut_bytes);
integral_slice_as_bytes!(i16, i16_as_ne_bytes, i16_as_ne_mut_bytes);
integral_slice_as_bytes!(u32, u32_as_ne_bytes, u32_as_ne_mut_bytes);
integral_slice_as_bytes!(i32, i32_as_ne_bytes, i32_as_ne_mut_bytes);
integral_slice_as_bytes!(u64, u64_as_ne_bytes, u64_as_ne_mut_bytes);
integral_slice_as_bytes!(i64, i64_as_ne_bytes, i64_as_ne_mut_bytes);
integral_slice_as_bytes!(f32, f32_as_ne_bytes, f32_as_ne_mut_bytes);
integral_slice_as_bytes!(f64, f64_as_ne_bytes, f64_as_ne_mut_bytes);
+81 -475
View File
@@ -1,69 +1,34 @@
//! Function for reading TIFF tags
use std::collections::HashMap;
use std::convert::{TryFrom, TryInto};
use std::convert::TryFrom;
use std::io::{self, Read, Seek};
use std::mem;
use std::str;
use super::stream::{ByteOrder, EndianReader, SmartReader};
use crate::tags::{Tag, Type};
use crate::{TiffError, TiffFormatError, TiffResult};
use tags::{Tag, Type};
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational};
use self::Value::{
Ascii, Byte, Double, Float, Ifd, IfdBig, List, Rational, RationalBig, SRational, SRationalBig,
Short, Signed, SignedBig, Unsigned, UnsignedBig,
};
#[allow(unused_qualifications)]
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Value {
Byte(u8),
Short(u16),
Signed(i32),
SignedBig(i64),
Unsigned(u32),
UnsignedBig(u64),
Float(f32),
Double(f64),
List(Vec<Value>),
Rational(u32, u32),
RationalBig(u64, u64),
SRational(i32, i32),
SRationalBig(i64, i64),
Ascii(String),
Ifd(u32),
IfdBig(u64),
#[doc(hidden)] // Do not match against this.
__NonExhaustive,
}
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_u16(self) -> TiffResult<u16> {
match self {
Short(val) => Ok(val),
Unsigned(val) => Ok(u16::try_from(val)?),
UnsignedBig(val) => Ok(u16::try_from(val)?),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
}
}
pub fn into_u32(self) -> TiffResult<u32> {
match self {
Short(val) => Ok(val.into()),
Unsigned(val) => Ok(val),
UnsignedBig(val) => Ok(u32::try_from(val)?),
Ifd(val) => Ok(val),
IfdBig(val) => Ok(u32::try_from(val)?),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
@@ -73,57 +38,6 @@ impl Value {
pub fn into_i32(self) -> TiffResult<i32> {
match self {
Signed(val) => Ok(val),
SignedBig(val) => Ok(i32::try_from(val)?),
val => Err(TiffError::FormatError(
TiffFormatError::SignedIntegerExpected(val),
)),
}
}
pub fn into_u64(self) -> TiffResult<u64> {
match self {
Short(val) => Ok(val.into()),
Unsigned(val) => Ok(val.into()),
UnsignedBig(val) => Ok(val),
Ifd(val) => Ok(val.into()),
IfdBig(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_string(self) -> TiffResult<String> {
match self {
Ascii(val) => Ok(val),
val => Err(TiffError::FormatError(
TiffFormatError::SignedIntegerExpected(val),
)),
@@ -140,13 +54,7 @@ impl Value {
Ok(new_vec)
}
Unsigned(val) => Ok(vec![val]),
UnsignedBig(val) => Ok(vec![u32::try_from(val)?]),
Rational(numerator, denominator) => Ok(vec![numerator, denominator]),
RationalBig(numerator, denominator) => {
Ok(vec![u32::try_from(numerator)?, u32::try_from(denominator)?])
}
Ifd(val) => Ok(vec![val]),
IfdBig(val) => Ok(vec![u32::try_from(val)?]),
Ascii(val) => Ok(val.chars().map(u32::from).collect()),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
@@ -154,39 +62,6 @@ impl Value {
}
}
pub fn into_u8_vec(self) -> TiffResult<Vec<u8>> {
match self {
List(vec) => {
let mut new_vec = Vec::with_capacity(vec.len());
for v in vec {
new_vec.push(v.into_u8()?)
}
Ok(new_vec)
}
Byte(val) => Ok(vec![val]),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
}
}
pub fn into_u16_vec(self) -> TiffResult<Vec<u16>> {
match self {
List(vec) => {
let mut new_vec = Vec::with_capacity(vec.len());
for v in vec {
new_vec.push(v.into_u16()?)
}
Ok(new_vec)
}
Short(val) => Ok(vec![val]),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
}
}
pub fn into_i32_vec(self) -> TiffResult<Vec<i32>> {
match self {
List(vec) => {
@@ -197,104 +72,13 @@ impl Value {
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()?),
_ => 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]),
Ifd(val) => Ok(vec![val.into()]),
IfdBig(val) => Ok(vec![val]),
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),
)),
@@ -305,8 +89,8 @@ impl Value {
#[derive(Clone)]
pub struct Entry {
type_: Type,
count: u64,
offset: [u8; 8],
count: u32,
offset: [u8; 4],
}
impl ::std::fmt::Debug for Entry {
@@ -320,12 +104,6 @@ impl ::std::fmt::Debug for Entry {
impl Entry {
pub fn new(type_: Type, count: u32, offset: [u8; 4]) -> Entry {
let mut offset = offset.to_vec();
offset.append(&mut vec![0; 4]);
Entry::new_u64(type_, count.into(), offset[..].try_into().unwrap())
}
pub fn new_u64(type_: Type, count: u64, offset: [u8; 8]) -> Entry {
Entry {
type_,
count,
@@ -343,266 +121,98 @@ impl Entry {
limits: &super::Limits,
decoder: &mut super::Decoder<R>,
) -> TiffResult<Value> {
// Case 1: there are no values so we can return immediately.
if self.count == 0 {
return Ok(List(Vec::new()));
}
let bo = decoder.byte_order();
let value_bytes = self.count
* match self.type_ {
Type::BYTE | Type::SBYTE | Type::ASCII | Type::UNDEFINED => 1,
Type::SHORT | Type::SSHORT => 2,
Type::LONG | Type::SLONG | Type::FLOAT | Type::IFD => 4,
Type::LONG8
| Type::SLONG8
| Type::DOUBLE
| Type::RATIONAL
| Type::SRATIONAL
| Type::IFD8 => 8,
Type::__NonExhaustive => unreachable!(),
};
// Case 2: there is one value.
if self.count == 1 {
// 2a: the value is 5-8 bytes and we're in BigTiff mode.
if decoder.bigtiff && value_bytes > 4 && value_bytes <= 8 {
return Ok(match self.type_ {
Type::LONG8 => UnsignedBig(self.r(bo).read_u64()?),
Type::SLONG8 => SignedBig(self.r(bo).read_i64()?),
Type::DOUBLE => Double(self.r(bo).read_f64()?),
Type::RATIONAL => {
let mut r = self.r(bo);
Rational(r.read_u32()?, r.read_u32()?)
}
Type::SRATIONAL => {
let mut r = self.r(bo);
SRational(r.read_i32()?, r.read_i32()?)
}
Type::IFD8 => IfdBig(self.r(bo).read_u64()?),
Type::BYTE
| Type::SBYTE
| Type::ASCII
| Type::UNDEFINED
| Type::SHORT
| Type::SSHORT
| Type::LONG
| Type::SLONG
| Type::FLOAT
| Type::IFD => unreachable!(),
Type::__NonExhaustive => unreachable!(),
});
}
// 2b: the value is at most 4 bytes or doesn't fit in the offset field.
return Ok(match self.type_ {
Type::BYTE => Unsigned(u32::from(self.offset[0])),
Type::SBYTE => Signed(i32::from(self.offset[0] as i8)),
Type::UNDEFINED => Byte(self.offset[0]),
Type::SHORT => Unsigned(u32::from(self.r(bo).read_u16()?)),
Type::SSHORT => Signed(i32::from(self.r(bo).read_i16()?)),
Type::LONG => Unsigned(self.r(bo).read_u32()?),
Type::SLONG => Signed(self.r(bo).read_i32()?),
Type::FLOAT => Float(self.r(bo).read_f32()?),
Type::ASCII => {
if self.offset[0] == 0 {
Ascii("".to_string())
} else {
return Err(TiffError::FormatError(TiffFormatError::InvalidTag));
}
}
Type::LONG8 => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
UnsignedBig(decoder.read_long8()?)
}
Type::SLONG8 => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
SignedBig(decoder.read_slong8()?)
}
Type::DOUBLE => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
Double(decoder.read_double()?)
}
Type::RATIONAL => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
Rational(decoder.read_long()?, decoder.read_long()?)
}
Type::SRATIONAL => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
SRational(decoder.read_slong()?, decoder.read_slong()?)
}
Type::IFD => Ifd(self.r(bo).read_u32()?),
Type::IFD8 => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
IfdBig(decoder.read_long8()?)
}
Type::__NonExhaustive => unreachable!(),
});
}
// Case 3: There is more than one value, but it fits in the offset field.
if value_bytes <= 4 || decoder.bigtiff && value_bytes <= 8 {
match self.type_ {
Type::BYTE => return Ok(offset_to_bytes(self.count as usize, self)?),
Type::SBYTE => return Ok(offset_to_sbytes(self.count as usize, self)?),
Type::ASCII => {
let mut buf = vec![0; self.count as usize];
self.r(bo).read_exact(&mut buf)?;
if buf.is_ascii() && buf.ends_with(&[0]) {
let v = str::from_utf8(&buf)?;
let v = v.trim_matches(char::from(0));
return Ok(Ascii(v.into()));
} else {
return Err(TiffError::FormatError(TiffFormatError::InvalidTag));
}
}
Type::UNDEFINED => {
return Ok(List(
self.offset[0..self.count as usize]
.iter()
.map(|&b| Byte(b))
.collect(),
));
}
Type::SHORT => {
let mut r = self.r(bo);
let mut v = Vec::new();
for _ in 0..self.count {
v.push(Short(r.read_u16()?));
}
return Ok(List(v));
}
Type::SSHORT => {
let mut r = self.r(bo);
let mut v = Vec::new();
for _ in 0..self.count {
v.push(Signed(i32::from(r.read_i16()?)));
}
return Ok(List(v));
}
Type::LONG => {
let mut r = self.r(bo);
let mut v = Vec::new();
for _ in 0..self.count {
v.push(Unsigned(r.read_u32()?));
}
return Ok(List(v));
}
Type::SLONG => {
let mut r = self.r(bo);
let mut v = Vec::new();
for _ in 0..self.count {
v.push(Signed(r.read_i32()?));
}
return Ok(List(v));
}
Type::FLOAT => {
let mut r = self.r(bo);
let mut v = Vec::new();
for _ in 0..self.count {
v.push(Float(r.read_f32()?));
}
return Ok(List(v));
}
Type::IFD => {
let mut r = self.r(bo);
let mut v = Vec::new();
for _ in 0..self.count {
v.push(Ifd(r.read_u32()?));
}
return Ok(List(v));
}
Type::LONG8
| Type::SLONG8
| Type::RATIONAL
| Type::SRATIONAL
| Type::DOUBLE
| Type::IFD8 => {
unreachable!()
}
Type::__NonExhaustive => unreachable!(),
}
}
// Case 4: there is more than one value, and it doesn't fit in the offset field.
match self.type_ {
match (self.type_, self.count) {
// TODO check if this could give wrong results
// at a different endianess of file/computer.
Type::BYTE => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(UnsignedBig(u64::from(decoder.read_byte()?)))
(Type::BYTE, 1) => Ok(Unsigned(u32::from(self.offset[0]))),
(Type::BYTE, 2) => offset_to_bytes(2, self),
(Type::BYTE, 3) => offset_to_bytes(3, self),
(Type::BYTE, 4) => offset_to_bytes(4, self),
(Type::BYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Unsigned(u32::from(decoder.read_byte()?)))
}),
Type::SBYTE => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(SignedBig(i64::from(decoder.read_byte()? as i8)))
(Type::SBYTE, 1) => Ok(Signed(i32::from(self.offset[0] as i8))),
(Type::SBYTE, 2) => offset_to_sbytes(2, self),
(Type::SBYTE, 3) => offset_to_sbytes(3, self),
(Type::SBYTE, 4) => offset_to_sbytes(4, self),
(Type::SBYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Signed(i32::from(decoder.read_byte()? as i8)))
}),
Type::SHORT => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(UnsignedBig(u64::from(decoder.read_short()?)))
(Type::SHORT, 1) => Ok(Unsigned(u32::from(self.r(bo).read_u16()?))),
(Type::SSHORT, 1) => Ok(Signed(i32::from(self.r(bo).read_i16()?))),
(Type::SHORT, 2) => {
let mut r = self.r(bo);
Ok(List(vec![
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
]))
}
(Type::SSHORT, 2) => {
let mut r = self.r(bo);
Ok(List(vec![
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
]))
}
(Type::SHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Unsigned(u32::from(decoder.read_short()?)))
}),
Type::SSHORT => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(SignedBig(i64::from(decoder.read_sshort()?)))
(Type::SSHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Signed(i32::from(decoder.read_sshort()?)))
}),
Type::LONG => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
(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 => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
(Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Signed(decoder.read_slong()?))
}),
Type::FLOAT => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(Float(decoder.read_float()?))
(Type::RATIONAL, 1) => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
let numerator = decoder.read_long()?;
let denominator = decoder.read_long()?;
Ok(Rational(numerator, denominator))
}
(Type::SRATIONAL, 1) => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
let numerator = decoder.read_slong()?;
let denominator = decoder.read_slong()?;
Ok(SRational(numerator, denominator))
}
(Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Rational(
decoder.read_long()?,
decoder.read_long()?,
))
}),
Type::DOUBLE => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(Double(decoder.read_double()?))
(Type::SRATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(SRational(
decoder.read_slong()?,
decoder.read_slong()?,
))
}),
Type::RATIONAL => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(Rational(decoder.read_long()?, decoder.read_long()?))
}),
Type::SRATIONAL => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(SRational(decoder.read_slong()?, decoder.read_slong()?))
}),
Type::LONG8 => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(UnsignedBig(decoder.read_long8()?))
}),
Type::SLONG8 => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(SignedBig(decoder.read_slong8()?))
}),
Type::IFD => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(Ifd(decoder.read_long()?))
}),
Type::IFD8 => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(IfdBig(decoder.read_long8()?))
}),
Type::UNDEFINED => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(Byte(u8::from(decoder.read_byte()?)))
}),
Type::ASCII => {
let n = usize::try_from(self.count)?;
(Type::ASCII, n) => {
let n = usize::try_from(n)?;
if n > limits.decoding_buffer_size {
return Err(TiffError::LimitsExceeded);
}
if decoder.bigtiff {
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
} else {
decoder.goto_offset(self.r(bo).read_u32()?)?
}
decoder.goto_offset(self.r(bo).read_u32()?)?;
let string = decoder.read_string(n)?;
Ok(Ascii(string))
}
Type::__NonExhaustive => unreachable!(),
_ => 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>,
fn decode_offset<R, F>(&self, value_count: u32, bo: ByteOrder, limits: &super::Limits, decoder: &mut super::Decoder<R>, decode_fn: F) -> TiffResult<Value>
where
R: Read + Seek,
F: Fn(&mut super::Decoder<R>) -> TiffResult<Value>,
{
let value_count = usize::try_from(value_count)?;
if value_count > limits.decoding_buffer_size / mem::size_of::<Value>() {
@@ -610,11 +220,7 @@ impl Entry {
}
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()?)?
}
decoder.goto_offset(self.r(bo).read_u32()?)?;
for _ in 0..value_count {
v.push(decode_fn(decoder)?)
}
@@ -629,7 +235,7 @@ fn offset_to_bytes(n: usize, entry: &Entry) -> TiffResult<Value> {
entry.offset[0..n]
.iter()
.map(|&e| Unsigned(u32::from(e)))
.collect(),
.collect()
))
}
@@ -640,7 +246,7 @@ fn offset_to_sbytes(n: usize, entry: &Entry) -> TiffResult<Value> {
entry.offset[0..n]
.iter()
.map(|&e| Signed(i32::from(e as i8)))
.collect(),
.collect()
))
}
+225 -1047
View File
File diff suppressed because it is too large. Load diff
+157 -192
View File
@@ -1,5 +1,9 @@
//! All IO functionality needed for TIFF decoding
use crate::error::{TiffError, TiffResult};
use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
use lzw;
use miniz_oxide::inflate;
use std::io::{self, Read, Seek};
/// Byte order of the TIFF file.
@@ -19,120 +23,135 @@ pub trait EndianReader: Read {
/// Reads an u16
#[inline(always)]
fn read_u16(&mut self) -> Result<u16, io::Error> {
let mut n = [0u8; 2];
self.read_exact(&mut n)?;
Ok(match self.byte_order() {
ByteOrder::LittleEndian => u16::from_le_bytes(n),
ByteOrder::BigEndian => u16::from_be_bytes(n),
})
match self.byte_order() {
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_u16::<LittleEndian>(self),
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_u16::<BigEndian>(self),
}
}
/// Reads an i8
#[inline(always)]
fn read_i8(&mut self) -> Result<i8, io::Error> {
let mut n = [0u8; 1];
self.read_exact(&mut n)?;
Ok(match self.byte_order() {
ByteOrder::LittleEndian => i8::from_le_bytes(n),
ByteOrder::BigEndian => i8::from_be_bytes(n),
})
fn read_u16_into(&mut self, buffer: &mut [u16]) -> Result<(), io::Error> {
match self.byte_order() {
ByteOrder::LittleEndian => {
<Self as ReadBytesExt>::read_u16_into::<LittleEndian>(self, buffer)
}
ByteOrder::BigEndian => {
<Self as ReadBytesExt>::read_u16_into::<BigEndian>(self, buffer)
}
}
}
/// 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),
})
match self.byte_order() {
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_i16::<LittleEndian>(self),
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_i16::<BigEndian>(self),
}
}
/// Reads an u32
#[inline(always)]
fn read_u32(&mut self) -> Result<u32, io::Error> {
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),
})
match self.byte_order() {
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_u32::<LittleEndian>(self),
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_u32::<BigEndian>(self),
}
}
/// 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),
})
fn read_u32_into(&mut self, buffer: &mut [u32]) -> Result<(), io::Error> {
match self.byte_order() {
ByteOrder::LittleEndian => {
<Self as ReadBytesExt>::read_u32_into::<LittleEndian>(self, buffer)
}
ByteOrder::BigEndian => {
<Self as ReadBytesExt>::read_u32_into::<BigEndian>(self, buffer)
}
}
}
/// Reads an u64
#[inline(always)]
fn read_u64(&mut self) -> Result<u64, io::Error> {
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),
})
match self.byte_order() {
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_u64::<LittleEndian>(self),
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_u64::<BigEndian>(self),
}
}
/// Reads an i64
#[inline(always)]
fn read_i64(&mut self) -> Result<i64, io::Error> {
let mut n = [0u8; 8];
self.read_exact(&mut n)?;
Ok(match self.byte_order() {
ByteOrder::LittleEndian => i64::from_le_bytes(n),
ByteOrder::BigEndian => i64::from_be_bytes(n),
})
fn read_u64_into(&mut self, buffer: &mut [u64]) -> Result<(), io::Error> {
match self.byte_order() {
ByteOrder::LittleEndian => {
<Self as ReadBytesExt>::read_u64_into::<LittleEndian>(self, buffer)
}
ByteOrder::BigEndian => {
<Self as ReadBytesExt>::read_u64_into::<BigEndian>(self, buffer)
}
}
}
/// Reads an f32
/// Reads an i32
#[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),
}))
}
/// 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),
}))
fn read_i32(&mut self) -> Result<i32, io::Error> {
match self.byte_order() {
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_i32::<LittleEndian>(self),
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_i32::<BigEndian>(self),
}
}
}
///
/// # READERS
///
/// Reader that decompresses DEFLATE streams
pub struct DeflateReader {
buffer: io::Cursor<Vec<u8>>,
byte_order: ByteOrder,
}
///
/// ## Deflate Reader
///
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)?;
pub type DeflateReader<'r, R> = flate2::read::ZlibDecoder<&'r mut SmartReader<R>>;
// TODO: Implement streaming compression, and remove this (temporary) and somewhat
// misleading workaround.
if compressed.len() > max_uncompressed_length {
return Err(TiffError::LimitsExceeded);
}
///
/// ## LZW Reader
///
let uncompressed =
inflate::decompress_to_vec_zlib(&compressed).map_err(TiffError::from_inflate_status)?;
Ok((
uncompressed.len(),
Self {
byte_order,
buffer: io::Cursor::new(uncompressed),
},
))
}
}
impl Read for DeflateReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.buffer.read(buf)
}
}
impl EndianReader for DeflateReader {
fn byte_order(&self) -> ByteOrder {
self.byte_order
}
}
/// Reader that decompresses LZW streams
pub struct LZWReader {
buffer: io::Cursor<Vec<u8>>,
byte_order: ByteOrder,
}
impl LZWReader {
@@ -145,40 +164,24 @@ impl LZWReader {
where
R: Read + Seek,
{
let order = reader.byte_order;
let mut compressed = vec![0; compressed_length as usize];
reader.read_exact(&mut compressed[..])?;
let mut uncompressed = vec![0; max_uncompressed_length];
let mut decoder = weezl::decode::Decoder::with_tiff_size_switch(weezl::BitOrder::Msb, 8);
let mut uncompressed = Vec::with_capacity(max_uncompressed_length);
let mut decoder = lzw::DecoderEarlyChange::new(lzw::MsbReader::new(), 8);
let mut bytes_read = 0;
let mut bytes_written = 0;
while bytes_written < max_uncompressed_length {
let result = decoder.decode_bytes(
&compressed[bytes_read..],
&mut uncompressed[bytes_written..],
);
bytes_read += result.consumed_in;
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)),
}
while bytes_read < compressed_length && uncompressed.len() < max_uncompressed_length {
let (len, bytes) = decoder.decode_bytes(&compressed[bytes_read..])?;
bytes_read += len;
uncompressed.extend_from_slice(bytes);
}
uncompressed.resize(bytes_written, 0);
let bytes = uncompressed.len();
Ok((
uncompressed.len(),
bytes,
LZWReader {
buffer: io::Cursor::new(uncompressed),
byte_order: order,
},
))
}
@@ -191,113 +194,71 @@ impl Read for LZWReader {
}
}
///
/// ## JPEG Reader (for "new-style" JPEG format (TIFF compression tag 7))
///
pub(crate) struct JpegReader {
buffer: io::Cursor<Vec<u8>>,
}
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,
{
// 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(&segment[2..]);
Ok(JpegReader {
buffer: io::Cursor::new(jpeg_data),
})
}
None => Ok(JpegReader {
buffer: io::Cursor::new(segment),
}),
}
impl EndianReader for LZWReader {
#[inline(always)]
fn byte_order(&self) -> ByteOrder {
self.byte_order
}
}
impl Read for JpegReader {
// #[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.buffer.read(buf)
}
}
///
/// ## PackBits Reader
///
/// Reader that unpacks Apple's `PackBits` format
pub struct PackBitsReader {
buffer: io::Cursor<Vec<u8>>,
byte_order: ByteOrder,
}
impl PackBitsReader {
/// Wraps a reader
pub fn new<R: Read + Seek>(
mut reader: R,
byte_order: ByteOrder,
length: usize,
) -> io::Result<(usize, PackBitsReader)> {
let mut buffer = Vec::new();
let mut header: [u8; 1] = [0];
let mut data: [u8; 1] = [0];
let mut bytes_read = 0;
while bytes_read < length {
reader.read_exact(&mut header)?;
bytes_read += 1;
let h = header[0] as i8;
if h >= -127 && h <= -1 {
let new_len = buffer.len() + (1 - h as isize) as usize;
reader.read_exact(&mut data)?;
buffer.resize(new_len, data[0]);
bytes_read += 1;
} else if h >= 0 {
let num_vals = h as usize + 1;
let start = buffer.len();
buffer.resize(start + num_vals, 0);
reader.read_exact(&mut buffer[start..])?;
bytes_read += num_vals
} else {
// h = -128 is a no-op.
let mut read: usize = 0;
while read < length {
let lread = read_packbits_run(&mut reader, &mut buffer)?;
if lread == 0 {
return Err(io::ErrorKind::UnexpectedEof.into());
}
read += lread;
}
Ok((
buffer.len(),
PackBitsReader {
buffer: io::Cursor::new(buffer),
byte_order,
},
))
}
}
fn read_packbits_run<R: Read + Seek>(reader: &mut R, buffer: &mut Vec<u8>) -> io::Result<usize> {
let mut header: [u8; 1] = [0];
let bytes = reader.read(&mut header)?;
match bytes {
0 => Ok(0),
_ => match header[0] as i8 {
-128 => Ok(1),
h if h >= -127 && h <= -1 => {
let new_len = buffer.len() + (1 - h as isize) as usize;
reader.read_exact(&mut header)?;
buffer.resize(new_len, header[0]);
Ok(2)
}
h => {
let num_vals = h as usize + 1;
let start = buffer.len();
buffer.resize(start + num_vals, 0);
reader.read_exact(&mut buffer[start..])?;
Ok(num_vals + 1)
}
},
}
}
impl Read for PackBitsReader {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
@@ -305,15 +266,18 @@ impl Read for PackBitsReader {
}
}
///
/// ## SmartReader Reader
///
impl EndianReader for PackBitsReader {
#[inline(always)]
fn byte_order(&self) -> ByteOrder {
self.byte_order
}
}
/// Reader that is aware of the byte order.
#[derive(Debug)]
pub struct SmartReader<R>
where
R: Read,
R: Read + Seek,
{
reader: R,
pub byte_order: ByteOrder,
@@ -321,7 +285,7 @@ where
impl<R> SmartReader<R>
where
R: Read,
R: Read + Seek,
{
/// Wraps a reader
pub fn wrap(reader: R, byte_order: ByteOrder) -> SmartReader<R> {
@@ -331,7 +295,7 @@ where
impl<R> EndianReader for SmartReader<R>
where
R: Read,
R: Read + Seek,
{
#[inline(always)]
fn byte_order(&self) -> ByteOrder {
@@ -339,7 +303,7 @@ where
}
}
impl<R: Read> Read for SmartReader<R> {
impl<R: Read + Seek> Read for SmartReader<R> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.reader.read(buf)
@@ -366,7 +330,8 @@ mod test {
let encoded_len = encoded.len();
let buff = io::Cursor::new(encoded);
let (_, mut decoder) = PackBitsReader::new(buff, encoded_len).unwrap();
let (_, mut decoder) =
PackBitsReader::new(buff, ByteOrder::LittleEndian, encoded_len).unwrap();
let mut decoded = Vec::new();
decoder.read_to_end(&mut decoded).unwrap();
+3 -85
View File
@@ -1,4 +1,4 @@
use crate::tags::{PhotometricInterpretation, SampleFormat};
use tags::PhotometricInterpretation;
/// Trait for different colortypes that can be encoded.
pub trait ColorType {
@@ -8,8 +8,6 @@ pub trait ColorType {
const TIFF_VALUE: PhotometricInterpretation;
/// The value of the tiff tag `BitsPerSample`
const BITS_PER_SAMPLE: &'static [u16];
/// The value of the tiff tag `SampleFormat`
const SAMPLE_FORMAT: &'static [SampleFormat];
}
pub struct Gray8;
@@ -17,7 +15,6 @@ impl ColorType for Gray8 {
type Inner = u8;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
const BITS_PER_SAMPLE: &'static [u16] = &[8];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
}
pub struct Gray16;
@@ -25,7 +22,6 @@ impl ColorType for Gray16 {
type Inner = u16;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
const BITS_PER_SAMPLE: &'static [u16] = &[16];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
}
pub struct Gray32;
@@ -33,15 +29,6 @@ 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;
@@ -49,15 +36,6 @@ 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;
@@ -65,7 +43,6 @@ impl ColorType for RGB8 {
type Inner = u8;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 3];
}
pub struct RGB16;
@@ -73,7 +50,6 @@ impl ColorType for RGB16 {
type Inner = u16;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 3];
}
pub struct RGB32;
@@ -81,15 +57,6 @@ 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;
@@ -97,15 +64,6 @@ 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;
@@ -113,7 +71,6 @@ impl ColorType for RGBA8 {
type Inner = u8;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
}
pub struct RGBA16;
@@ -121,39 +78,20 @@ impl ColorType for RGBA16 {
type Inner = u16;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
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;
type Inner = u16;
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;
type Inner = u16;
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;
@@ -161,7 +99,6 @@ impl ColorType for CMYK8 {
type Inner = u8;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
}
pub struct CMYK16;
@@ -169,7 +106,6 @@ 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;
@@ -177,15 +113,6 @@ 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;
@@ -193,13 +120,4 @@ 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];
}
+422 -319
View File
@@ -1,31 +1,354 @@
pub use tiff_value::*;
use byteorder::NativeEndian;
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::io::{Seek, Write};
use std::mem;
use std::{
cmp,
collections::BTreeMap,
convert::{TryFrom, TryInto},
io::{self, Seek, Write},
marker::PhantomData,
mem,
num::TryFromIntError,
};
use crate::{
error::TiffResult,
tags::{self, ResolutionUnit, Tag},
};
use tags::{self, ResolutionUnit, Tag, Type};
use error::{TiffError, TiffFormatError, TiffResult};
pub mod colortype;
mod tiff_value;
mod writer;
use self::colortype::*;
use self::writer::*;
/// Encoder for Tiff and BigTiff files.
/// Type to represent tiff values of type `RATIONAL`
#[derive(Clone)]
pub struct Rational {
pub n: u32,
pub d: u32,
}
/// Type to represent tiff values of type `SRATIONAL`
pub struct SRational {
pub n: i32,
pub d: i32,
}
/// Trait for types that can be encoded in a tiff file
pub trait TiffValue {
const BYTE_LEN: u32;
const FIELD_TYPE: Type;
fn count(&self) -> u32;
fn bytes(&self) -> u32 {
self.count() * Self::BYTE_LEN
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()>;
}
impl TiffValue for [u8] {
const BYTE_LEN: u32 = 1;
const FIELD_TYPE: Type = Type::BYTE;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_bytes(self)?;
Ok(())
}
}
impl TiffValue for [i8] {
const BYTE_LEN: u32 = 1;
const FIELD_TYPE: Type = Type::SBYTE;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
// We write using nativeedian so this should be safe
let slice =
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len()) };
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [u16] {
const BYTE_LEN: u32 = 2;
const FIELD_TYPE: Type = Type::SHORT;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
// We write using nativeedian so this sould be safe
let slice =
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 2) };
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<()> {
// We write using nativeedian so this should be safe
let slice =
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) };
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [u32] {
const BYTE_LEN: u32 = 4;
const FIELD_TYPE: Type = Type::LONG;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
// We write using nativeedian so this sould be safe
let slice =
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 4) };
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<()> {
// We write using nativeedian so this should be safe
let slice =
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) };
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [u64] {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: Type = Type::LONG8;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
// We write using nativeedian so this sould be safe
let slice =
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 8) };
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [Rational] {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: Type = Type::RATIONAL;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
for x in self {
x.write(writer)?;
}
Ok(())
}
}
impl TiffValue for [SRational] {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: Type = Type::SRATIONAL;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
for x in self {
x.write(writer)?;
}
Ok(())
}
}
impl TiffValue for u8 {
const BYTE_LEN: u32 = 1;
const FIELD_TYPE: Type = Type::BYTE;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u8(*self)?;
Ok(())
}
}
impl TiffValue for i8 {
const BYTE_LEN: u32 = 1;
const FIELD_TYPE: Type = Type::SBYTE;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i8(*self)?;
Ok(())
}
}
impl TiffValue for u16 {
const BYTE_LEN: u32 = 2;
const FIELD_TYPE: Type = Type::SHORT;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u16(*self)?;
Ok(())
}
}
impl TiffValue for i16 {
const BYTE_LEN: u32 = 2;
const FIELD_TYPE: Type = Type::SSHORT;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i16(*self)?;
Ok(())
}
}
impl TiffValue for u32 {
const BYTE_LEN: u32 = 4;
const FIELD_TYPE: Type = Type::LONG;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u32(*self)?;
Ok(())
}
}
impl TiffValue for i32 {
const BYTE_LEN: u32 = 4;
const FIELD_TYPE: Type = Type::SLONG;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i32(*self)?;
Ok(())
}
}
impl TiffValue for u64 {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: Type = Type::LONG8;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u64(*self)?;
Ok(())
}
}
impl TiffValue for Rational {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: Type = Type::RATIONAL;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u32(self.n)?;
writer.write_u32(self.d)?;
Ok(())
}
}
impl TiffValue for SRational {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: Type = Type::SRATIONAL;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i32(self.n)?;
writer.write_i32(self.d)?;
Ok(())
}
}
impl TiffValue for str {
const BYTE_LEN: u32 = 1;
const FIELD_TYPE: Type = Type::ASCII;
fn count(&self) -> u32 {
self.len() as u32 + 1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
if self.is_ascii() && !self.bytes().any(|b| b == 0) {
writer.write_bytes(self.as_bytes())?;
writer.write_u8(0)?;
Ok(())
} else {
Err(TiffError::FormatError(TiffFormatError::InvalidTag))
}
}
}
impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T {
const BYTE_LEN: u32 = T::BYTE_LEN;
const FIELD_TYPE: Type = T::FIELD_TYPE;
fn count(&self) -> u32 {
(*self).count()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
(*self).write(writer)
}
}
/// Tiff encoder.
///
/// With this type you can get a `DirectoryEncoder` or a `ImageEncoder`
/// to encode Tiff/BigTiff ifd directories with images.
/// to encode tiff ifd directories with images.
///
/// See `DirectoryEncoder` and `ImageEncoder`.
///
@@ -37,68 +360,39 @@ use self::writer::*;
/// # let image_data = vec![0; 100*100*3];
/// use tiff::encoder::*;
///
/// // create a standard Tiff file
/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
///
/// tiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap();
///
/// // create a BigTiff file
/// let mut bigtiff = TiffEncoder::new_big(&mut file).unwrap();
/// bigtiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap();
///
/// # }
/// ```
pub struct TiffEncoder<W, K: TiffKind = TiffKindStandard> {
pub struct TiffEncoder<W> {
writer: TiffWriter<W>,
kind: PhantomData<K>,
}
/// Constructor functions to create standard Tiff files.
impl<W: Write + Seek> TiffEncoder<W> {
/// Creates a new encoder for standard Tiff files.
///
/// To create BigTiff files, use [`new_big`][TiffEncoder::new_big] or
/// [`new_generic`][TiffEncoder::new_generic].
pub fn new(writer: W) -> TiffResult<TiffEncoder<W, TiffKindStandard>> {
TiffEncoder::new_generic(writer)
}
}
/// Constructor functions to create BigTiff files.
impl<W: Write + Seek> TiffEncoder<W, TiffKindBig> {
/// Creates a new encoder for BigTiff files.
///
/// To create standard Tiff files, use [`new`][TiffEncoder::new] or
/// [`new_generic`][TiffEncoder::new_generic].
pub fn new_big(writer: W) -> TiffResult<Self> {
TiffEncoder::new_generic(writer)
}
}
/// Generic functions that are available for both Tiff and BigTiff encoders.
impl<W: Write + Seek, K: TiffKind> TiffEncoder<W, K> {
/// Creates a new Tiff or BigTiff encoder, inferred from the return type.
pub fn new_generic(writer: W) -> TiffResult<Self> {
pub fn new(writer: W) -> TiffResult<TiffEncoder<W>> {
let mut encoder = TiffEncoder {
writer: TiffWriter::new(writer),
kind: PhantomData,
};
K::write_header(&mut encoder.writer)?;
NativeEndian::write_header(&mut encoder.writer)?;
// blank the IFD offset location
encoder.writer.write_u32(0)?;
Ok(encoder)
}
/// Create a [`DirectoryEncoder`] to encode an ifd directory.
pub fn new_directory(&mut self) -> TiffResult<DirectoryEncoder<W, K>> {
/// Create a `DirectoryEncoder` to encode an ifd directory.
pub fn new_directory(&mut self) -> TiffResult<DirectoryEncoder<W>> {
DirectoryEncoder::new(&mut self.writer)
}
/// Create an [`ImageEncoder`] to encode an image one slice at a time.
/// Create an 'ImageEncoder' to encode an image one slice at a time.
pub fn new_image<C: ColorType>(
&mut self,
width: u32,
height: u32,
) -> TiffResult<ImageEncoder<W, C, K>> {
) -> TiffResult<ImageEncoder<W, C>> {
let encoder = DirectoryEncoder::new(&mut self.writer)?;
ImageEncoder::new(encoder, width, height)
}
@@ -113,9 +407,26 @@ impl<W: Write + Seek, K: TiffKind> TiffEncoder<W, K> {
where
[C::Inner]: TiffValue,
{
let num_pix = usize::try_from(width)?.checked_mul(usize::try_from(height)?)
.ok_or_else(|| ::std::io::Error::new(
::std::io::ErrorKind::InvalidInput,
"Image width * height exceeds usize"))?;
if data.len() < num_pix {
return Err(::std::io::Error::new(
::std::io::ErrorKind::InvalidData,
"Input data slice is undersized for provided dimensions").into());
}
let encoder = DirectoryEncoder::new(&mut self.writer)?;
let image: ImageEncoder<W, C, K> = ImageEncoder::new(encoder, width, height)?;
image.write_data(data)
let mut image: ImageEncoder<W, C> = ImageEncoder::new(encoder, width, height)?;
let mut idx = 0;
while image.next_strip_sample_count() > 0 {
let sample_count = usize::try_from(image.next_strip_sample_count())?;
image.write_strip(&data[idx..idx + sample_count])?;
idx += sample_count;
}
image.finish()
}
}
@@ -123,19 +434,19 @@ impl<W: Write + Seek, K: TiffKind> TiffEncoder<W, K> {
///
/// You should call `finish` on this when you are finished with it.
/// Encoding can silently fail while this is dropping.
pub struct DirectoryEncoder<'a, W: 'a + Write + Seek, K: TiffKind> {
pub struct DirectoryEncoder<'a, W: 'a + Write + Seek> {
writer: &'a mut TiffWriter<W>,
dropped: bool,
// We use BTreeMap to make sure tags are written in correct order
ifd_pointer_pos: u64,
ifd: BTreeMap<u16, DirectoryEntry<K::OffsetType>>,
ifd: BTreeMap<u16, (u16, u32, Vec<u8>)>,
}
impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
fn new(writer: &'a mut TiffWriter<W>) -> TiffResult<Self> {
impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
fn new(writer: &'a mut TiffWriter<W>) -> TiffResult<DirectoryEncoder<'a, W>> {
// the previous word is the IFD offset position
let ifd_pointer_pos = writer.offset() - mem::size_of::<K::OffsetType>() as u64;
writer.pad_word_boundary()?; // TODO: Do we need to adjust this for BigTiff?
let ifd_pointer_pos = writer.offset() - mem::size_of::<u32>() as u64;
writer.pad_word_boundary()?;
Ok(DirectoryEncoder {
writer,
dropped: false,
@@ -146,41 +457,30 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
/// Write a single ifd tag.
pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) -> TiffResult<()> {
let mut bytes = Vec::with_capacity(value.bytes());
let len = <T>::BYTE_LEN * value.count();
let mut bytes = Vec::with_capacity(usize::try_from(len)?);
{
let mut writer = TiffWriter::new(&mut bytes);
value.write(&mut writer)?;
}
self.ifd.insert(
tag.to_u16(),
DirectoryEntry {
data_type: <T>::FIELD_TYPE.to_u16(),
count: value.count().try_into()?,
data: bytes,
},
);
self.ifd
.insert(tag.to_u16(), (<T>::FIELD_TYPE.to_u16(), value.count(), bytes));
Ok(())
}
fn write_directory(&mut self) -> TiffResult<u64> {
// Start by writing out all values
for &mut DirectoryEntry {
data: ref mut bytes,
..
} in self.ifd.values_mut()
{
let data_bytes = mem::size_of::<K::OffsetType>();
if bytes.len() > data_bytes {
for &mut (_, _, ref mut bytes) in self.ifd.values_mut() {
if bytes.len() > 4 {
let offset = self.writer.offset();
self.writer.write_bytes(bytes)?;
*bytes = vec![0; data_bytes];
*bytes = vec![0, 0, 0, 0];
let mut writer = TiffWriter::new(bytes as &mut [u8]);
K::write_offset(&mut writer, offset)?;
writer.write_u32(u32::try_from(offset)?)?;
} else {
while bytes.len() < data_bytes {
while bytes.len() < 4 {
bytes.push(0);
}
}
@@ -188,19 +488,11 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
let offset = self.writer.offset();
K::write_entry_count(&mut self.writer, self.ifd.len())?;
for (
tag,
&DirectoryEntry {
data_type: ref field_type,
ref count,
data: ref offset,
},
) in self.ifd.iter()
{
self.writer.write_u16(u16::try_from(self.ifd.len())?)?;
for (tag, &(ref field_type, ref count, ref offset)) in self.ifd.iter() {
self.writer.write_u16(*tag)?;
self.writer.write_u16(*field_type)?;
(*count).write(&mut self.writer)?;
self.writer.write_u32(*count)?;
self.writer.write_bytes(offset)?;
}
@@ -221,9 +513,9 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
let curr_pos = self.writer.offset();
self.writer.goto_offset(self.ifd_pointer_pos)?;
K::write_offset(&mut self.writer, ifd_pointer)?;
self.writer.write_u32(u32::try_from(ifd_pointer)?)?;
self.writer.goto_offset(curr_pos)?;
K::write_offset(&mut self.writer, 0)?;
self.writer.write_u32(0)?;
self.dropped = true;
@@ -236,7 +528,7 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
}
}
impl<'a, W: Write + Seek, K: TiffKind> Drop for DirectoryEncoder<'a, W, K> {
impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
fn drop(&mut self) {
if !self.dropped {
let _ = self.finish_internal();
@@ -256,17 +548,10 @@ impl<'a, W: Write + Seek, K: TiffKind> Drop for DirectoryEncoder<'a, W, K> {
/// # let mut file = std::io::Cursor::new(Vec::new());
/// # let image_data = vec![0; 100*100*3];
/// use tiff::encoder::*;
/// use tiff::tags::Tag;
///
/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
/// let mut image = tiff.new_image::<colortype::RGB8>(100, 100).unwrap();
///
/// // You can encode tags here
/// image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap();
///
/// // Strip size can be configured before writing data
/// image.rows_per_strip(2).unwrap();
///
/// let mut idx = 0;
/// while image.next_strip_sample_count() > 0 {
/// let sample_count = image.next_strip_sample_count() as usize;
@@ -276,29 +561,30 @@ impl<'a, W: Write + Seek, K: TiffKind> Drop for DirectoryEncoder<'a, W, K> {
/// image.finish().unwrap();
/// # }
/// ```
/// You can also call write_data function wich will encode by strip and finish
pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType, K: TiffKind> {
encoder: DirectoryEncoder<'a, W, K>,
pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType> {
encoder: DirectoryEncoder<'a, W>,
strip_idx: u64,
strip_count: u64,
row_samples: u64,
width: u32,
height: u32,
rows_per_strip: u64,
strip_offsets: Vec<K::OffsetType>,
strip_byte_count: Vec<K::OffsetType>,
strip_offsets: Vec<u32>,
strip_byte_count: Vec<u32>,
dropped: bool,
_phantom: ::std::marker::PhantomData<C>,
}
impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind> ImageEncoder<'a, W, T, K> {
fn new(mut encoder: DirectoryEncoder<'a, W, K>, width: u32, height: u32) -> TiffResult<Self> {
impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
fn new(
mut encoder: DirectoryEncoder<'a, W>,
width: u32,
height: u32,
) -> TiffResult<ImageEncoder<'a, W, T>> {
let row_samples = u64::from(width) * u64::try_from(<T>::BITS_PER_SAMPLE.len())?;
let row_bytes = row_samples * u64::from(<T::Inner>::BYTE_LEN);
// Limit the strip size to prevent potential memory and security issues.
// Also keep the multiple strip handling 'oiled'
let rows_per_strip = (1_000_000 + row_bytes - 1) / row_bytes;
// As per tiff spec each strip should be about 8k long
let rows_per_strip = (8000 + row_bytes - 1) / row_bytes;
let strip_count = (u64::from(height) + rows_per_strip - 1) / rows_per_strip;
@@ -307,16 +593,11 @@ impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind> ImageEncoder<'a, W, T,
encoder.write_tag(Tag::Compression, tags::CompressionMethod::None.to_u16())?;
encoder.write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE)?;
let sample_format: Vec<_> = <T>::SAMPLE_FORMAT.iter().map(|s| s.to_u16()).collect();
encoder.write_tag(Tag::SampleFormat, &sample_format[..])?;
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16())?;
encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?;
encoder.write_tag(
Tag::SamplesPerPixel,
u16::try_from(<T>::BITS_PER_SAMPLE.len())?,
)?;
encoder.write_tag(Tag::SamplesPerPixel, u16::try_from(<T>::BITS_PER_SAMPLE.len())?)?;
encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 })?;
encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 })?;
encoder.write_tag(Tag::ResolutionUnit, ResolutionUnit::None.to_u16())?;
@@ -327,7 +608,6 @@ impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind> ImageEncoder<'a, W, T,
strip_idx: 0,
row_samples,
rows_per_strip,
width,
height,
strip_offsets: Vec::new(),
strip_byte_count: Vec::new(),
@@ -342,9 +622,11 @@ impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind> ImageEncoder<'a, W, T,
return 0;
}
let raw_start_row = self.strip_idx * self.rows_per_strip;
let start_row = cmp::min(u64::from(self.height), raw_start_row);
let end_row = cmp::min(u64::from(self.height), raw_start_row + self.rows_per_strip);
let start_row = ::std::cmp::min(u64::from(self.height), self.strip_idx * self.rows_per_strip);
let end_row = ::std::cmp::min(
u64::from(self.height),
(self.strip_idx + 1) * self.rows_per_strip,
);
(end_row - start_row) * self.row_samples
}
@@ -357,67 +639,29 @@ impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind> ImageEncoder<'a, W, T,
// TODO: Compression
let samples = self.next_strip_sample_count();
if u64::try_from(value.len())? != samples {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Slice is wrong size for strip",
)
.into());
return Err(::std::io::Error::new(
::std::io::ErrorKind::InvalidData,
"Slice is wrong size for strip").into());
}
let offset = self.encoder.write_data(value)?;
self.strip_offsets.push(K::convert_offset(offset)?);
self.strip_byte_count.push(value.bytes().try_into()?);
self.strip_offsets.push(u32::try_from(offset)?);
self.strip_byte_count.push(value.bytes());
self.strip_idx += 1;
Ok(())
}
/// Write strips from data
pub fn write_data(mut self, data: &[T::Inner]) -> TiffResult<()>
where
[T::Inner]: TiffValue,
{
let num_pix = usize::try_from(self.width)?
.checked_mul(usize::try_from(self.height)?)
.ok_or_else(|| {
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
pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) {
self.encoder
.write_tag(Tag::ResolutionUnit, unit.to_u16())
.unwrap();
self.encoder
.write_tag(Tag::XResolution, value.clone())
.unwrap();
self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap();
self.encoder.write_tag(Tag::XResolution, value.clone()).unwrap();
self.encoder.write_tag(Tag::YResolution, value).unwrap();
}
/// Set image resolution unit
pub fn resolution_unit(&mut self, unit: ResolutionUnit) {
self.encoder
.write_tag(Tag::ResolutionUnit, unit.to_u16())
.unwrap();
self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap();
}
/// Set image x-resolution
@@ -430,42 +674,18 @@ impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind> ImageEncoder<'a, W, T,
self.encoder.write_tag(Tag::YResolution, value).unwrap();
}
/// Set image number of lines per strip
///
/// This function needs to be called before any calls to `write_data` or
/// `write_strip` and will return an error otherwise.
pub fn rows_per_strip(&mut self, value: u32) -> TiffResult<()> {
if self.strip_idx != 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Cannot change strip size after data was written",
)
.into());
}
// Write tag as 32 bits
self.encoder.write_tag(Tag::RowsPerStrip, value)?;
let value: u64 = value as u64;
self.strip_count = (self.height as u64 + value - 1) / value;
self.rows_per_strip = value;
Ok(())
}
fn finish_internal(&mut self) -> TiffResult<()> {
self.encoder
.write_tag(Tag::StripOffsets, K::convert_slice(&self.strip_offsets))?;
self.encoder.write_tag(
Tag::StripByteCounts,
K::convert_slice(&self.strip_byte_count),
)?;
.write_tag(Tag::StripOffsets, &*self.strip_offsets)?;
self.encoder
.write_tag(Tag::StripByteCounts, &*self.strip_byte_count)?;
self.dropped = true;
self.encoder.finish_internal()
}
/// Get a reference of the underlying `DirectoryEncoder`
pub fn encoder(&mut self) -> &mut DirectoryEncoder<'a, W, K> {
pub fn encoder(&mut self) -> &mut DirectoryEncoder<'a, W> {
&mut self.encoder
}
@@ -475,127 +695,10 @@ impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind> ImageEncoder<'a, W, T,
}
}
impl<'a, W: Write + Seek, C: ColorType, K: TiffKind> Drop for ImageEncoder<'a, W, C, K> {
impl<'a, W: Write + Seek, C: ColorType> Drop for ImageEncoder<'a, W, C> {
fn drop(&mut self) {
if !self.dropped {
let _ = self.finish_internal();
}
}
}
struct DirectoryEntry<S> {
data_type: u16,
count: S,
data: Vec<u8>,
}
/// Trait to abstract over Tiff/BigTiff differences.
///
/// Implemented for [`TiffKindStandard`] and [`TiffKindBig`].
pub trait TiffKind {
/// The type of offset fields, `u32` for normal Tiff, `u64` for BigTiff.
type OffsetType: TryFrom<usize, Error = TryFromIntError> + Into<u64> + TiffValue;
/// Needed for the `convert_slice` method.
type OffsetArrayType: ?Sized + TiffValue;
/// Write the (Big)Tiff header.
fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()>;
/// Convert a file offset to `Self::OffsetType`.
///
/// This returns an error for normal Tiff if the offset is larger than `u32::MAX`.
fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType>;
/// Write an offset value to the given writer.
///
/// Like `convert_offset`, this errors if `offset > u32::MAX` for normal Tiff.
fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()>;
/// Write the IFD entry count field with the given `count` value.
///
/// The entry count field is an `u16` for normal Tiff and `u64` for BigTiff. Errors
/// if the given `usize` is larger than the representable values.
fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()>;
/// Internal helper method for satisfying Rust's type checker.
///
/// The `TiffValue` trait is implemented for both primitive values (e.g. `u8`, `u32`) and
/// slices of primitive values (e.g. `[u8]`, `[u32]`). However, this is not represented in
/// the type system, so there is no guarantee that that for all `T: TiffValue` there is also
/// an implementation of `TiffValue` for `[T]`. This method works around that problem by
/// providing a conversion from `[T]` to some value that implements `TiffValue`, thereby
/// making all slices of `OffsetType` usable with `write_tag` and similar methods.
///
/// Implementations of this trait should always set `OffsetArrayType` to `[OffsetType]`.
fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType;
}
/// Create a standard Tiff file.
pub struct TiffKindStandard;
impl TiffKind for TiffKindStandard {
type OffsetType = u32;
type OffsetArrayType = [u32];
fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
write_tiff_header(writer)?;
// blank the IFD offset location
writer.write_u32(0)?;
Ok(())
}
fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType> {
Ok(Self::OffsetType::try_from(offset)?)
}
fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()> {
writer.write_u32(u32::try_from(offset)?)?;
Ok(())
}
fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()> {
writer.write_u16(u16::try_from(count)?)?;
Ok(())
}
fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType {
slice
}
}
/// Create a BigTiff file.
pub struct TiffKindBig;
impl TiffKind for TiffKindBig {
type OffsetType = u64;
type OffsetArrayType = [u64];
fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
write_bigtiff_header(writer)?;
// blank the IFD offset location
writer.write_u64(0)?;
Ok(())
}
fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType> {
Ok(offset)
}
fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()> {
writer.write_u64(offset)?;
Ok(())
}
fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()> {
writer.write_u64(u64::try_from(count)?)?;
Ok(())
}
fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType {
slice
}
}
-483
View File
@@ -1,483 +0,0 @@
use std::io::Write;
use crate::{bytecast, tags::Type, TiffError, TiffFormatError, TiffResult};
use super::writer::TiffWriter;
/// Trait for types that can be encoded in a tiff file
pub trait TiffValue {
const BYTE_LEN: u8;
const FIELD_TYPE: Type;
fn count(&self) -> usize;
fn bytes(&self) -> usize {
self.count() * usize::from(Self::BYTE_LEN)
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()>;
}
impl TiffValue for [u8] {
const BYTE_LEN: u8 = 1;
const FIELD_TYPE: Type = Type::BYTE;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_bytes(self)?;
Ok(())
}
}
impl TiffValue for [i8] {
const BYTE_LEN: u8 = 1;
const FIELD_TYPE: Type = Type::SBYTE;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::i8_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [u16] {
const BYTE_LEN: u8 = 2;
const FIELD_TYPE: Type = Type::SHORT;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::u16_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [i16] {
const BYTE_LEN: u8 = 2;
const FIELD_TYPE: Type = Type::SSHORT;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::i16_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [u32] {
const BYTE_LEN: u8 = 4;
const FIELD_TYPE: Type = Type::LONG;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::u32_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [i32] {
const BYTE_LEN: u8 = 4;
const FIELD_TYPE: Type = Type::SLONG;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::i32_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [u64] {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::LONG8;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::u64_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [i64] {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::SLONG8;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::i64_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [f32] {
const BYTE_LEN: u8 = 4;
const FIELD_TYPE: Type = Type::FLOAT;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
// We write using nativeedian so this sould be safe
let slice = bytecast::f32_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [f64] {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::DOUBLE;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
// We write using nativeedian so this sould be safe
let slice = bytecast::f64_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [Ifd] {
const BYTE_LEN: u8 = 4;
const FIELD_TYPE: Type = Type::IFD;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
for x in self {
x.write(writer)?;
}
Ok(())
}
}
impl TiffValue for [Ifd8] {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::IFD8;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
for x in self {
x.write(writer)?;
}
Ok(())
}
}
impl TiffValue for [Rational] {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::RATIONAL;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
for x in self {
x.write(writer)?;
}
Ok(())
}
}
impl TiffValue for [SRational] {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::SRATIONAL;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
for x in self {
x.write(writer)?;
}
Ok(())
}
}
impl TiffValue for u8 {
const BYTE_LEN: u8 = 1;
const FIELD_TYPE: Type = Type::BYTE;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u8(*self)?;
Ok(())
}
}
impl TiffValue for i8 {
const BYTE_LEN: u8 = 1;
const FIELD_TYPE: Type = Type::SBYTE;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i8(*self)?;
Ok(())
}
}
impl TiffValue for u16 {
const BYTE_LEN: u8 = 2;
const FIELD_TYPE: Type = Type::SHORT;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u16(*self)?;
Ok(())
}
}
impl TiffValue for i16 {
const BYTE_LEN: u8 = 2;
const FIELD_TYPE: Type = Type::SSHORT;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i16(*self)?;
Ok(())
}
}
impl TiffValue for u32 {
const BYTE_LEN: u8 = 4;
const FIELD_TYPE: Type = Type::LONG;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u32(*self)?;
Ok(())
}
}
impl TiffValue for i32 {
const BYTE_LEN: u8 = 4;
const FIELD_TYPE: Type = Type::SLONG;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i32(*self)?;
Ok(())
}
}
impl TiffValue for u64 {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::LONG8;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u64(*self)?;
Ok(())
}
}
impl TiffValue for i64 {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::SLONG8;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i64(*self)?;
Ok(())
}
}
impl TiffValue for f32 {
const BYTE_LEN: u8 = 4;
const FIELD_TYPE: Type = Type::FLOAT;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_f32(*self)?;
Ok(())
}
}
impl TiffValue for f64 {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::DOUBLE;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_f64(*self)?;
Ok(())
}
}
impl TiffValue for Ifd {
const BYTE_LEN: u8 = 4;
const FIELD_TYPE: Type = Type::IFD;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u32(self.0)?;
Ok(())
}
}
impl TiffValue for Ifd8 {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::IFD8;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u64(self.0)?;
Ok(())
}
}
impl TiffValue for Rational {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::RATIONAL;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u32(self.n)?;
writer.write_u32(self.d)?;
Ok(())
}
}
impl TiffValue for SRational {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::SRATIONAL;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i32(self.n)?;
writer.write_i32(self.d)?;
Ok(())
}
}
impl TiffValue for str {
const BYTE_LEN: u8 = 1;
const FIELD_TYPE: Type = Type::ASCII;
fn count(&self) -> usize {
self.len() + 1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
if self.is_ascii() && !self.bytes().any(|b| b == 0) {
writer.write_bytes(self.as_bytes())?;
writer.write_u8(0)?;
Ok(())
} else {
Err(TiffError::FormatError(TiffFormatError::InvalidTag))
}
}
}
impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T {
const BYTE_LEN: u8 = T::BYTE_LEN;
const FIELD_TYPE: Type = T::FIELD_TYPE;
fn count(&self) -> usize {
(*self).count()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
(*self).write(writer)
}
}
/// Type to represent tiff values of type `IFD`
#[derive(Clone)]
pub struct Ifd(pub u32);
/// Type to represent tiff values of type `IFD8`
#[derive(Clone)]
pub struct Ifd8(pub u64);
/// Type to represent tiff values of type `RATIONAL`
#[derive(Clone)]
pub struct Rational {
pub n: u32,
pub d: u32,
}
/// Type to represent tiff values of type `SRATIONAL`
#[derive(Clone)]
pub struct SRational {
pub n: i32,
pub d: i32,
}
+26 -60
View File
@@ -1,42 +1,29 @@
use crate::error::TiffResult;
use error::TiffResult;
use byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian, WriteBytesExt};
use std::io::{self, Seek, SeekFrom, Write};
pub fn write_tiff_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;
writer.writer.write_all(&[boi, boi])?;
writer.writer.write_all(&42u16.to_ne_bytes())?;
writer.offset += 4;
Ok(())
pub trait TiffByteOrder: ByteOrder {
fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()>;
}
/// Writes a BigTiff header, excluding the IFD offset field.
///
/// Writes the byte order, version number, offset byte size, and zero constant fields. Does
// _not_ write the offset to the first IFD, this should be done by the caller.
pub fn write_bigtiff_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
#[cfg(target_endian = "little")]
let boi: u8 = 0x49;
#[cfg(not(target_endian = "little"))]
let boi: u8 = 0x4d;
impl TiffByteOrder for LittleEndian {
fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.writer.write_u16::<LittleEndian>(0x4949)?;
writer.writer.write_u16::<LittleEndian>(42)?;
writer.offset += 4;
// byte order indication
writer.writer.write_all(&[boi, boi])?;
// version number
writer.writer.write_all(&43u16.to_ne_bytes())?;
// bytesize of offsets (pointer size)
writer.writer.write_all(&8u16.to_ne_bytes())?;
// always 0
writer.writer.write_all(&0u16.to_ne_bytes())?;
Ok(())
}
}
// we wrote 8 bytes, so set the internal offset accordingly
writer.offset += 8;
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(())
Ok(())
}
}
pub struct TiffWriter<W> {
@@ -60,68 +47,47 @@ impl<W: Write> TiffWriter<W> {
}
pub fn write_u8(&mut self, n: u8) -> Result<(), io::Error> {
self.writer.write_all(&n.to_ne_bytes())?;
self.writer.write_u8(n)?;
self.offset += 1;
Ok(())
}
pub fn write_i8(&mut self, n: i8) -> Result<(), io::Error> {
self.writer.write_all(&n.to_ne_bytes())?;
self.writer.write_i8(n)?;
self.offset += 1;
Ok(())
}
pub fn write_u16(&mut self, n: u16) -> Result<(), io::Error> {
self.writer.write_all(&n.to_ne_bytes())?;
self.writer.write_u16::<NativeEndian>(n)?;
self.offset += 2;
Ok(())
}
pub fn write_i16(&mut self, n: i16) -> Result<(), io::Error> {
self.writer.write_all(&n.to_ne_bytes())?;
self.writer.write_i16::<NativeEndian>(n)?;
self.offset += 2;
Ok(())
}
pub fn write_u32(&mut self, n: u32) -> Result<(), io::Error> {
self.writer.write_all(&n.to_ne_bytes())?;
self.writer.write_u32::<NativeEndian>(n)?;
self.offset += 4;
Ok(())
}
pub fn write_i32(&mut self, n: i32) -> Result<(), io::Error> {
self.writer.write_all(&n.to_ne_bytes())?;
self.writer.write_i32::<NativeEndian>(n)?;
self.offset += 4;
Ok(())
}
pub fn write_u64(&mut self, n: u64) -> Result<(), io::Error> {
self.writer.write_all(&n.to_ne_bytes())?;
self.offset += 8;
Ok(())
}
pub fn write_i64(&mut self, n: i64) -> Result<(), io::Error> {
self.writer.write_all(&n.to_ne_bytes())?;
self.offset += 8;
Ok(())
}
pub fn write_f32(&mut self, n: f32) -> Result<(), io::Error> {
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.writer.write_u64::<NativeEndian>(n)?;
self.offset += 8;
Ok(())
+27 -82
View File
@@ -1,14 +1,12 @@
use std::error::Error;
use std::fmt;
use std::io;
use std::str;
use std::string;
use crate::decoder::{ifd::Value, ChunkType};
use crate::tags::{
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag,
};
use crate::ColorType;
use decoder::ifd::{Value};
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Tag};
use miniz_oxide::inflate::TINFLStatus;
use ColorType;
/// Tiff error kinds.
#[derive(Debug)]
@@ -28,9 +26,6 @@ pub enum TiffError {
/// An integer conversion to or from a platform size failed, either due to
/// limits of the platform size or limits of the format.
IntSizeError,
/// The image does not support the requested operation
UsageError(UsageError),
}
/// The image is not formatted properly.
@@ -40,29 +35,19 @@ pub enum TiffError {
///
/// 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)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TiffFormatError {
TiffSignatureNotFound,
TiffSignatureInvalid,
ImageFileDirectoryNotFound,
InconsistentSizesEncountered,
UnexpectedCompressedData {
actual_bytes: usize,
required_bytes: usize,
},
InconsistentStripSamples {
actual_samples: usize,
required_samples: usize,
},
InvalidTag,
InvalidTagValueType(Tag),
RequiredTagNotFound(Tag),
UnknownPredictor(u16),
ByteExpected(Value),
UnsignedIntegerExpected(Value),
SignedIntegerExpected(Value),
Format(String),
RequiredTagEmpty(Tag),
InflateError(InflateError),
#[doc(hidden)]
/// Do not match against this variant. It may get removed.
__NonExhaustive,
@@ -76,48 +61,43 @@ impl fmt::Display for TiffFormatError {
TiffSignatureInvalid => write!(fmt, "TIFF signature invalid."),
ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."),
InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."),
UnexpectedCompressedData {
actual_bytes,
required_bytes,
} => {
write!(
fmt,
"Decompression returned different amount of bytes than expected: got {}, expected {}.",
actual_bytes, required_bytes
)
}
InconsistentStripSamples {
actual_samples,
required_samples,
} => {
write!(
fmt,
"Inconsistent elements in strip: got {}, expected {}.",
actual_samples, required_samples
)
}
InvalidTag => write!(fmt, "Image contains invalid tag."),
InvalidTagValueType(ref tag) => {
write!(fmt, "Tag `{:?}` did not have the expected value type.", tag)
}
InvalidTagValueType(ref tag) =>
write!(fmt, "Tag `{:?}` did not have the expected value type.", tag),
RequiredTagNotFound(ref tag) => write!(fmt, "Required tag `{:?}` not found.", tag),
UnknownPredictor(ref predictor) => {
write!(fmt, "Unknown predictor “{}” encountered", predictor)
}
ByteExpected(ref val) => write!(fmt, "Expected byte, {:?} found.", val),
UnsignedIntegerExpected(ref val) => {
write!(fmt, "Expected unsigned integer, {:?} found.", val)
}
SignedIntegerExpected(ref val) => {
write!(fmt, "Expected signed integer, {:?} found.", val)
}
Format(ref val) => write!(fmt, "Invalid format: {:?}.", val),
RequiredTagEmpty(ref val) => write!(fmt, "Required tag {:?} was empty.", val),
InflateError(_) => write!(fmt, "Failed to decode inflate data."),
__NonExhaustive => unreachable!(),
}
}
}
/// Decompression failed due to faulty compressed data.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct InflateError {
status: TINFLStatus,
}
impl InflateError {
pub(crate) fn new(status: TINFLStatus) -> Self {
Self { status }
}
}
impl TiffError {
pub(crate) fn from_inflate_status(status: TINFLStatus) -> Self {
TiffError::FormatError(TiffFormatError::InflateError(InflateError::new(status)))
}
}
/// The Decoder does not support features required by the image.
///
/// This only captures known failures for which the standard either does not require support or an
@@ -134,7 +114,6 @@ pub enum TiffUnsupportedError {
UnknownCompressionMethod,
UnsupportedCompressionMethod(CompressionMethod),
UnsupportedSampleDepth(u8),
UnsupportedSampleFormat(Vec<SampleFormat>),
UnsupportedColorType(ColorType),
UnsupportedBitsPerChannel(u8),
UnsupportedPlanarConfig(Option<PlanarConfiguration>),
@@ -169,9 +148,6 @@ impl fmt::Display for TiffUnsupportedError {
UnsupportedSampleDepth(samples) => {
write!(fmt, "{} samples per pixel is unsupported.", samples)
}
UnsupportedSampleFormat(ref formats) => {
write!(fmt, "Sample format {:?} is unsupported.", formats)
}
UnsupportedColorType(color_type) => {
write!(fmt, "Color type {:?} is unsupported", color_type)
}
@@ -187,29 +163,6 @@ impl fmt::Display for TiffUnsupportedError {
}
}
/// User attempted to use the Decoder in a way that is incompatible with a specific image.
///
/// For example: attempting to read a tile from a stripped image.
#[derive(Debug)]
pub enum UsageError {
InvalidChunkType(ChunkType, ChunkType),
}
impl fmt::Display for UsageError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::UsageError::*;
match *self {
InvalidChunkType(expected, actual) => {
write!(
fmt,
"Requested operation is only valid for images with chunk encoding of type: {:?}, got {:?}.",
expected, actual
)
}
}
}
}
impl fmt::Display for TiffError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
@@ -223,7 +176,6 @@ impl fmt::Display for TiffError {
TiffError::IoError(ref e) => e.fmt(fmt),
TiffError::LimitsExceeded => write!(fmt, "The Decoder limits are exceeded"),
TiffError::IntSizeError => write!(fmt, "Platform or format size limits exceeded"),
TiffError::UsageError(ref e) => write!(fmt, "Usage error: {}", e),
}
}
}
@@ -236,7 +188,6 @@ impl Error for TiffError {
TiffError::IoError(..) => "IO error",
TiffError::LimitsExceeded => "Decoder limits exceeded",
TiffError::IntSizeError => "Platform or format size limits exceeded",
TiffError::UsageError(..) => "Invalid usage",
}
}
@@ -254,12 +205,6 @@ impl From<io::Error> for TiffError {
}
}
impl From<str::Utf8Error> for TiffError {
fn from(_err: str::Utf8Error) -> TiffError {
TiffError::FormatError(TiffFormatError::InvalidTag)
}
}
impl From<string::FromUtf8Error> for TiffError {
fn from(_err: string::FromUtf8Error) -> TiffError {
TiffError::FormatError(TiffFormatError::InvalidTag)
+5 -5
View File
@@ -4,18 +4,18 @@
//! lossless and lossy compression.
//!
//! # Related Links
//! * <https://www.adobe.io/open/standards/TIFF.html> - The TIFF specification
//! * <http://partners.adobe.com/public/developer/tiff/index.html> - The TIFF specification
extern crate jpeg;
extern crate weezl;
extern crate byteorder;
extern crate lzw;
extern crate miniz_oxide;
mod bytecast;
pub mod decoder;
pub mod encoder;
mod error;
pub mod tags;
pub use self::error::{TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError};
pub use self::error::{InflateError, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
/// An enumeration over supported color types and their bit depths
#[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)]
+12 -65
View File
@@ -39,24 +39,16 @@ macro_rules! tags {
}
}
tags!($name, $ty, $($unknown_doc)*);
tags!($name, $ty);
};
// For u16 tags, provide direct inherent primitive conversion methods.
($name:tt, u16, $($unknown_doc:literal)*) => {
($name:tt, u16) => {
impl $name {
#[inline(always)]
pub fn from_u16(val: u16) -> Option<Self> {
Self::__from_inner_type(val).ok()
}
$(
#[inline(always)]
pub fn 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)
@@ -65,7 +57,7 @@ macro_rules! tags {
};
// 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)*) => {};
($name:tt, $ty:tt) => {};
}
// Note: These tags appear in the order they are mentioned in the TIFF reference
@@ -113,68 +105,34 @@ pub enum Tag(u16) unknown("A private or extension tag") {
YResolution = 283,
// Advanced tags
Predictor = 317,
TileWidth = 322,
TileLength = 323,
TileOffsets = 324,
TileByteCounts = 325,
// Data Sample Format
SampleFormat = 339,
SMinSampleValue = 340, // TODO add support
SMaxSampleValue = 341, // TODO add support
// JPEG
JPEGTables = 347,
// GeoTIFF
ModelPixelScaleTag = 33550, // (SoftDesk)
ModelTransformationTag = 34264, // (JPL Carto Group)
ModelTiepointTag = 33922, // (Intergraph)
GeoKeyDirectoryTag = 34735, // (SPOT)
GeoDoubleParamsTag = 34736, // (SPOT)
GeoAsciiParamsTag = 34737, // (SPOT)
GdalNodata = 42113, // Contains areas with missing data
}
}
impl Tag {
pub fn from_u16_exhaustive(val: u16) -> Self {
Self::from_u16(val).unwrap_or_else(|| Tag::Unknown(val))
}
}
tags! {
/// The type of an IFD entry (a 2 byte field).
pub enum Type(u16) {
/// 8-bit unsigned integer
BYTE = 1,
/// 8-bit byte that contains a 7-bit ASCII code; the last byte must be zero
ASCII = 2,
/// 16-bit unsigned integer
SHORT = 3,
/// 32-bit unsigned integer
LONG = 4,
/// Fraction stored as two 32-bit unsigned integers
RATIONAL = 5,
/// 8-bit signed integer
SBYTE = 6,
/// 8-bit byte that may contain anything, depending on the field
UNDEFINED = 7,
/// 16-bit signed integer
SSHORT = 8,
/// 32-bit signed integer
SLONG = 9,
/// Fraction stored as two 32-bit signed integers
SRATIONAL = 10,
/// 32-bit IEEE floating point
FLOAT = 11,
/// 64-bit IEEE floating point
DOUBLE = 12,
/// 32-bit unsigned integer (offset)
IFD = 13,
/// BigTIFF 64-bit unsigned integer
LONG8 = 16,
/// BigTIFF 64-bit signed integer
SLONG8 = 17,
/// BigTIFF 64-bit unsigned integer (offset)
IFD8 = 18,
}
}
tags! {
/// See [TIFF compression tags](https://www.awaresystems.be/imaging/tiff/tifftags/compression.html)
/// for reference.
pub enum CompressionMethod(u16) {
None = 1,
Huffman = 2,
@@ -182,8 +140,6 @@ pub enum CompressionMethod(u16) {
Fax4 = 4,
LZW = 5,
JPEG = 6,
// "Extended JPEG" or "new JPEG" style
ModernJPEG = 7,
Deflate = 8,
OldDeflate = 0x80B2,
PackBits = 0x8005,
@@ -217,20 +173,11 @@ pub enum Predictor(u16) {
}
}
tags! {
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,
}
}
}
-4
View File
@@ -1,4 +0,0 @@
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.
-46
View File
@@ -1,46 +0,0 @@
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
)
}
}
+2 -127
View File
@@ -21,7 +21,7 @@ macro_rules! test_image_sum {
DecodingResult::$buffer(res) => {
let sum: $sum_ty = res.into_iter().map(<$sum_ty>::from).sum();
assert_eq!(sum, expected_sum);
}
},
_ => panic!("Wrong bit depth"),
}
}
@@ -29,13 +29,9 @@ macro_rules! test_image_sum {
}
test_image_sum!(test_image_sum_u8, U8, u64);
test_image_sum!(test_image_sum_i8, I8, i64);
test_image_sum!(test_image_sum_u16, U16, u64);
test_image_sum!(test_image_sum_i16, I16, i64);
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
@@ -46,9 +42,7 @@ fn test_image_color_type_unsupported(file: &str, expected_type: ColorType) {
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
assert_eq!(decoder.colortype().unwrap(), expected_type);
assert!(match decoder.read_image() {
Err(tiff::TiffError::UnsupportedError(
tiff::TiffUnsupportedError::UnsupportedColorType(_),
)) => true,
Err(tiff::TiffError::UnsupportedError(tiff::TiffUnsupportedError::UnsupportedColorType(_))) => true,
_ => false,
});
}
@@ -63,11 +57,6 @@ 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);
@@ -93,20 +82,6 @@ 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);
@@ -132,31 +107,6 @@ 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]
fn test_int8() {
test_image_sum_i8("int8.tif", ColorType::Gray(8), 3111)
}
#[test]
fn test_int8_rgb() {
test_image_sum_i8("int8_rgb.tif", ColorType::RGB(8), -10344)
}
#[test]
fn test_int16() {
test_image_sum_i16("int16.tif", ColorType::Gray(16), 354396);
}
#[test]
fn test_int16_rgb() {
test_image_sum_i16("int16_rgb.tif", ColorType::RGB(16), 1063188);
}
#[test]
fn test_string_tags() {
// these files have null-terminated strings for their Software tag. One has extra bytes after
@@ -199,12 +149,6 @@ fn test_decode_data() {
}
}
#[test]
fn issue_69() {
test_image_sum_u16("issue_69_lzw.tiff", ColorType::Gray(16), 1015486);
test_image_sum_u16("issue_69_packbits.tiff", ColorType::Gray(16), 1015486);
}
// TODO: GrayA support
//#[test]
//fn test_gray_alpha_u8()
@@ -215,72 +159,3 @@ fn issue_69() {
//let img_res = decoder.read_image();
//assert!(img_res.is_ok());
//}
#[test]
fn test_tiled_rgb_u8() {
test_image_sum_u8("tiled-rgb-u8.tif", ColorType::RGB(8), 39528948);
}
#[test]
fn test_tiled_rect_rgb_u8() {
test_image_sum_u8("tiled-rect-rgb-u8.tif", ColorType::RGB(8), 62081032);
}
/* #[test]
fn test_tiled_jpeg_rgb_u8() {
test_image_sum_u8("tiled-jpeg-rgb-u8.tif", ColorType::RGB(8), 93031606);
} */
#[test]
fn test_tiled_oversize_gray_i8() {
test_image_sum_i8("tiled-oversize-gray-i8.tif", ColorType::Gray(8), 1214996);
}
#[test]
fn test_tiled_cmyk_i8() {
test_image_sum_i8("tiled-cmyk-i8.tif", ColorType::CMYK(8), 1759101);
}
#[test]
fn test_tiled_incremental() {
let file = "tiled-rgb-u8.tif";
let expected_type = ColorType::RGB(8);
let sums = [
188760, 195639, 108148, 81986, 665088, 366140, 705317, 423366, 172033, 324455, 244102,
81853, 181258, 247971, 129486, 55600, 565625, 422102, 730888, 379271, 232142, 292549,
244045, 86866, 188141, 115036, 150785, 84389, 353170, 459325, 719619, 329594, 278663,
220474, 243048, 113563, 189152, 109684, 179391, 122188, 279651, 622093, 724682, 302459,
268428, 204499, 224255, 124674, 170668, 121868, 192768, 183367, 378029, 585651, 657712,
296790, 241444, 197083, 198429, 134869, 182318, 86034, 203655, 182338, 297255, 601284,
633813, 242531, 228578, 206441, 193552, 125412, 181527, 165439, 202531, 159538, 268388,
565790, 611382, 272967, 236497, 215154, 158881, 90806, 106114, 182342, 191824, 186138,
215174, 393193, 701228, 198866, 227944, 193830, 166330, 49008, 55719, 122820, 197316,
161969, 203152, 170986, 624427, 188605, 186187, 111064, 115192, 39538, 48626, 163929,
144682, 135796, 194141, 154198, 584125, 180255, 153524, 121433, 132641, 35743, 47798,
152343, 162874, 167664, 160175, 133038, 659882, 138339, 166470, 124173, 118929, 51317,
45267, 155776, 161331, 161006, 130052, 137618, 337291, 106481, 161999, 127343, 87724,
59540, 63907, 155677, 140668, 141523, 108061, 168657, 186482, 98599, 147614, 139963, 90444,
56602, 92547, 125644, 134212, 126569, 144153, 179800, 174516, 133969, 129399, 117681,
83305, 55075, 110737, 115108, 128572, 128911, 130922, 179986, 143288, 145884, 155856,
96683, 94057, 56238, 79649, 71651, 70182, 75010, 77009, 98855, 78979, 74341, 83482, 53403,
59842, 30305,
];
let path = PathBuf::from(TEST_IMAGE_DIR).join(file);
let img_file = File::open(path).expect("Cannot find test image!");
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
assert_eq!(decoder.colortype().unwrap(), expected_type);
let tiles = decoder.tile_count().unwrap();
assert_eq!(tiles as usize, sums.len());
for tile in 0..tiles {
match decoder.read_tile().unwrap() {
DecodingResult::U8(res) => {
let sum: u64 = res.into_iter().map(<u64>::from).sum();
assert_eq!(sum, sums[tile as usize]);
}
_ => panic!("Wrong bit depth"),
}
}
}
+44 -303
View File
@@ -1,9 +1,9 @@
extern crate tiff;
use tiff::decoder::{ifd, Decoder, DecodingResult};
use tiff::encoder::{colortype, Ifd, Ifd8, SRational, TiffEncoder};
use tiff::tags::Tag;
use tiff::decoder::{Decoder, DecodingResult};
use tiff::encoder::{colortype, TiffEncoder, SRational};
use tiff::ColorType;
use tiff::tags::Tag;
use std::fs::File;
use std::io::{Cursor, Seek, SeekFrom};
@@ -24,22 +24,14 @@ fn encode_decode() {
{
let mut tiff = TiffEncoder::new(&mut file).unwrap();
let mut image = tiff.new_image::<colortype::RGB8>(100, 100).unwrap();
image
.encoder()
.write_tag(Tag::Artist, "Image-tiff")
tiff.write_image::<colortype::RGB8>(100, 100, &image_data)
.unwrap();
image.write_data(&image_data).unwrap();
}
{
file.seek(SeekFrom::Start(0)).unwrap();
let mut decoder = Decoder::new(&mut file).unwrap();
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
assert_eq!(decoder.dimensions().unwrap(), (100, 100));
assert_eq!(
decoder.get_tag(Tag::Artist).unwrap(),
ifd::Value::Ascii("Image-tiff".into())
);
if let DecodingResult::U8(img_res) = decoder.read_image().unwrap() {
assert_eq!(image_data, img_res);
} else {
@@ -48,92 +40,6 @@ fn encode_decode() {
}
}
#[test]
fn encode_decode_big() {
let mut image_data = Vec::new();
for x in 0..100 {
for y in 0..100u8 {
let val = x + y;
image_data.push(val);
image_data.push(val);
image_data.push(val);
}
}
let mut file = Cursor::new(Vec::new());
{
let mut tiff = TiffEncoder::new_big(&mut file).unwrap();
let mut image = tiff.new_image::<colortype::RGB8>(100, 100).unwrap();
image
.encoder()
.write_tag(Tag::Artist, "Image-tiff")
.unwrap();
image.write_data(&image_data).unwrap();
}
{
file.seek(SeekFrom::Start(0)).unwrap();
let mut decoder = Decoder::new(&mut file).unwrap();
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
assert_eq!(decoder.dimensions().unwrap(), (100, 100));
assert_eq!(
decoder.get_tag(Tag::Artist).unwrap(),
ifd::Value::Ascii("Image-tiff".into())
);
if let DecodingResult::U8(img_res) = decoder.read_image().unwrap() {
assert_eq!(image_data, img_res);
} else {
panic!("Wrong data type");
}
}
}
#[test]
fn test_encode_ifd() {
let mut data = Cursor::new(Vec::new());
{
let mut tiff = TiffEncoder::new(&mut data).unwrap();
let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap();
let encoder = image_encoder.encoder();
// Use the "reusable" tags section as per the TIFF6 spec
encoder.write_tag(Tag::Unknown(65000), Ifd(42u32)).unwrap();
encoder
.write_tag(Tag::Unknown(65001), &[Ifd(100u32)][..])
.unwrap();
encoder
.write_tag(Tag::Unknown(65002), &[Ifd(1u32), Ifd(2u32), Ifd(3u32)][..])
.unwrap();
encoder.write_tag(Tag::Unknown(65010), Ifd8(43u64)).unwrap();
encoder
.write_tag(Tag::Unknown(65011), &[Ifd8(100u64)][..])
.unwrap();
encoder
.write_tag(
Tag::Unknown(65012),
&[Ifd8(1u64), Ifd8(2u64), Ifd8(3u64)][..],
)
.unwrap();
}
// Rewind the cursor for reading
data.set_position(0);
{
let mut decoder = Decoder::new(&mut data).unwrap();
assert_eq!(decoder.assert_tag_u32(65000), 42);
assert_eq!(decoder.assert_tag_u32_vec(65000), [42]);
assert_eq!(decoder.assert_tag_u32_vec(65001), [100]);
assert_eq!(decoder.assert_tag_u32_vec(65002), [1, 2, 3]);
assert_eq!(decoder.assert_tag_u64(65010), 43);
assert_eq!(decoder.assert_tag_u64_vec(65010), [43]);
assert_eq!(decoder.assert_tag_u64_vec(65011), [100]);
assert_eq!(decoder.assert_tag_u64_vec(65012), [1, 2, 3]);
}
}
#[test]
/// Test that attempting to encode when the input buffer is undersized returns
/// an error rather than panicking.
@@ -152,10 +58,7 @@ 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,
) {
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");
@@ -171,7 +74,8 @@ macro_rules! test_roundtrip {
let mut tiff = TiffEncoder::new(&mut file).unwrap();
let (width, height) = decoder.dimensions().unwrap();
tiff.write_image::<C>(width, height, &image_data).unwrap();
tiff.write_image::<C>(width, height, &image_data)
.unwrap();
}
file.seek(SeekFrom::Start(0)).unwrap();
{
@@ -190,8 +94,6 @@ 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]
fn test_gray_u8_roundtrip() {
@@ -243,76 +145,6 @@ 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>;
fn assert_tag_u64(&mut self, tag: u16) -> u64;
fn assert_tag_u64_vec(&mut self, tag: u16) -> Vec<u64>;
fn assert_tag_i64(&mut self, tag: u16) -> i64;
fn assert_tag_i64_vec(&mut self, tag: u16) -> Vec<i64>;
}
impl<R: std::io::Read + std::io::Seek> AssertDecode for Decoder<R> {
fn assert_tag_u32(&mut self, tag: u16) -> u32 {
self.get_tag(Tag::Unknown(tag)).unwrap().into_u32().unwrap()
}
fn assert_tag_u32_vec(&mut self, tag: u16) -> Vec<u32> {
self.get_tag(Tag::Unknown(tag))
.unwrap()
.into_u32_vec()
.unwrap()
}
fn assert_tag_i32(&mut self, tag: u16) -> i32 {
self.get_tag(Tag::Unknown(tag)).unwrap().into_i32().unwrap()
}
fn assert_tag_i32_vec(&mut self, tag: u16) -> Vec<i32> {
self.get_tag(Tag::Unknown(tag))
.unwrap()
.into_i32_vec()
.unwrap()
}
fn assert_tag_u64(&mut self, tag: u16) -> u64 {
self.get_tag(Tag::Unknown(tag)).unwrap().into_u64().unwrap()
}
fn assert_tag_u64_vec(&mut self, tag: u16) -> Vec<u64> {
self.get_tag(Tag::Unknown(tag))
.unwrap()
.into_u64_vec()
.unwrap()
}
fn assert_tag_i64(&mut self, tag: u16) -> i64 {
self.get_tag(Tag::Unknown(tag)).unwrap().into_i64().unwrap()
}
fn assert_tag_i64_vec(&mut self, tag: u16) -> Vec<i64> {
self.get_tag(Tag::Unknown(tag))
.unwrap()
.into_i64_vec()
.unwrap()
}
}
#[test]
fn test_multiple_byte() {
let mut data = Cursor::new(Vec::new());
@@ -323,29 +155,21 @@ fn test_multiple_byte() {
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();
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]);
assert_eq!(decoder.get_tag(Tag::Unknown(65000)).unwrap().into_u32_vec().unwrap(), [1]);
assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_u32_vec().unwrap(), [1, 2]);
assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_u32_vec().unwrap(), [1, 2, 3]);
assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_u32_vec().unwrap(), [1, 2, 3, 4]);
assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_u32_vec().unwrap(), [1, 2, 3, 4, 5]);
}
}
@@ -353,9 +177,6 @@ fn test_multiple_byte() {
/// 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();
@@ -364,56 +185,23 @@ fn test_signed() {
//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(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(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(65021), &[-1_i32][..]).unwrap();
encoder.write_tag(Tag::Unknown(65022), &[-1_i32, 2][..]).unwrap();
encoder.write_tag(Tag::Unknown(65030), -1_i64).unwrap();
encoder
.write_tag(Tag::Unknown(65031), &[-1_i64][..])
.unwrap();
encoder
.write_tag(Tag::Unknown(65032), &[-1_i64, 2][..])
.unwrap();
encoder
.write_tag(Tag::Unknown(65040), make_srational(-1))
.unwrap();
encoder
.write_tag(
Tag::Unknown(65041),
&[make_srational(-1), make_srational(2)][..],
)
.unwrap();
encoder.write_tag(Tag::Unknown(65030), SRational { n: -1, d: 100 }).unwrap();
encoder.write_tag(Tag::Unknown(65031), &[SRational { n: -1, d: 100 }, SRational { n: 2, d: 100 }][..]).unwrap();
}
//Rewind the cursor for reading
@@ -421,28 +209,24 @@ fn test_signed() {
{
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.get_tag(Tag::Unknown(65000)).unwrap().into_i32().unwrap(), -1, );
assert_eq!(decoder.get_tag(Tag::Unknown(65001)).unwrap().into_i32_vec().unwrap(), [-1]);
assert_eq!(decoder.get_tag(Tag::Unknown(65002)).unwrap().into_i32_vec().unwrap(), [-1, 2]);
assert_eq!(decoder.get_tag(Tag::Unknown(65003)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3]);
assert_eq!(decoder.get_tag(Tag::Unknown(65004)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3, 4]);
assert_eq!(decoder.get_tag(Tag::Unknown(65005)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3, 4, -5], );
assert_eq!(decoder.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.get_tag(Tag::Unknown(65010)).unwrap().into_i32().unwrap(), -1);
assert_eq!(decoder.get_tag(Tag::Unknown(65011)).unwrap().into_i32_vec().unwrap(), [-1]);
assert_eq!(decoder.get_tag(Tag::Unknown(65012)).unwrap().into_i32_vec().unwrap(), [-1, 2]);
assert_eq!(decoder.get_tag(Tag::Unknown(65013)).unwrap().into_i32_vec().unwrap(), [-1, 2, -3]);
assert_eq!(decoder.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.get_tag(Tag::Unknown(65020)).unwrap().into_i32().unwrap(), -1);
assert_eq!(decoder.get_tag(Tag::Unknown(65021)).unwrap().into_i32_vec().unwrap(), [-1]);
assert_eq!(decoder.get_tag(Tag::Unknown(65022)).unwrap().into_i32_vec().unwrap(), [-1, 2]);
assert_eq!(decoder.assert_tag_i64(65030), -1);
assert_eq!(decoder.assert_tag_i64_vec(65031), [-1]);
assert_eq!(decoder.assert_tag_i64_vec(65032), [-1, 2]);
assert_eq!(decoder.assert_tag_i32_vec(65040), [-1, 100]);
assert_eq!(decoder.assert_tag_i32_vec(65041), [-1_i32, 100, 2, 100]);
assert_eq!(decoder.get_tag(Tag::Unknown(65030)).unwrap().into_i32_vec().unwrap(), [-1, 100]);
assert_eq!(decoder.get_tag(Tag::Unknown(65031)).unwrap().into_i32_vec().unwrap(), [-1_i32, 100, 2, 100]);
}
}
@@ -457,14 +241,10 @@ fn test_multipage_image() {
// write first grayscale image (2x2 16-bit)
let img1: Vec<u16> = [1, 2, 3, 4].to_vec();
img_encoder
.write_image::<colortype::Gray16>(2, 2, &img1[..])
.unwrap();
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();
img_encoder.write_image::<colortype::Gray8>(3, 3, &img2[..]).unwrap();
}
// seek to the beginning of the file, so that it can be decoded
@@ -480,42 +260,3 @@ fn test_multipage_image() {
assert_eq!(img_decoder.dimensions().unwrap(), (3, 3));
}
}
#[test]
/// verify rows per strip setting
fn test_rows_per_strip() {
let mut file = Cursor::new(Vec::new());
{
let mut img_encoder = TiffEncoder::new(&mut file).unwrap();
let mut image = img_encoder.new_image::<colortype::Gray8>(100, 100).unwrap();
assert_eq!(image.next_strip_sample_count(), 100 * 100);
image.rows_per_strip(2).unwrap();
assert_eq!(image.next_strip_sample_count(), 2 * 100);
let img2: Vec<u8> = vec![0; 2 * 100];
image.write_strip(&img2[..]).unwrap();
assert!(image.rows_per_strip(5).is_err());
for i in 1..50 {
let img2: Vec<u8> = vec![i; 2 * 100];
image.write_strip(&img2[..]).unwrap();
}
assert!(image.write_strip(&img2[..]).is_err());
image.finish().unwrap();
}
file.seek(SeekFrom::Start(0)).unwrap();
{
let mut decoder = Decoder::new(&mut file).unwrap();
assert_eq!(decoder.get_tag_u64(Tag::RowsPerStrip).unwrap(), 2);
assert_eq!(decoder.strip_count().unwrap(), 50);
for i in 0..50 {
let img2 = [i; 2 * 100];
match decoder.read_strip().unwrap() {
DecodingResult::U8(data) => assert_eq!(&img2[..], &data[..]),
other => panic!("Incorrect strip type {:?}", other),
}
}
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.