Compare commits
1
Commits
v0.6.1
..
checkpoint
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51ec671e3a |
No files matched your search
-17
@@ -1,20 +1,3 @@
|
|||||||
# 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
|
# Version 0.6.0
|
||||||
|
|
||||||
New features:
|
New features:
|
||||||
|
|||||||
+1
-2
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tiff"
|
name = "tiff"
|
||||||
version = "0.6.1"
|
version = "0.6.0"
|
||||||
authors = [
|
authors = [
|
||||||
"ccgn",
|
"ccgn",
|
||||||
"bvssvni <bvssvni@gmail.com>",
|
"bvssvni <bvssvni@gmail.com>",
|
||||||
@@ -15,7 +15,6 @@ license = "MIT"
|
|||||||
description = "TIFF decoding and encoding library in pure Rust"
|
description = "TIFF decoding and encoding library in pure Rust"
|
||||||
repository = "https://github.com/image-rs/image-tiff"
|
repository = "https://github.com/image-rs/image-tiff"
|
||||||
exclude = ["tests/images/*", "tests/fuzz_images/*"]
|
exclude = ["tests/images/*", "tests/fuzz_images/*"]
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
weezl = "0.1.0"
|
weezl = "0.1.0"
|
||||||
|
|||||||
+162
-236
@@ -6,19 +6,18 @@ use std::io::{self, Read, Seek};
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use super::stream::{ByteOrder, EndianReader, SmartReader};
|
use super::stream::{ByteOrder, EndianReader, SmartReader};
|
||||||
use crate::tags::{Tag, Type};
|
use tags::{Tag, Type};
|
||||||
use crate::{TiffError, TiffFormatError, TiffResult};
|
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
||||||
|
|
||||||
use self::Value::{
|
use self::Value::{
|
||||||
Ascii, Byte, Double, Float, List, Rational, RationalBig, SRational, SRationalBig, Short,
|
Ascii, Byte, Double, Float, List, Rational, RationalBig, SRational, SRationalBig, Signed,
|
||||||
Signed, SignedBig, Unsigned, UnsignedBig,
|
SignedBig, Unsigned, UnsignedBig,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(unused_qualifications)]
|
#[allow(unused_qualifications)]
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Byte(u8),
|
Byte(u8),
|
||||||
Short(u16),
|
|
||||||
Signed(i32),
|
Signed(i32),
|
||||||
SignedBig(i64),
|
SignedBig(i64),
|
||||||
Unsigned(u32),
|
Unsigned(u32),
|
||||||
@@ -43,20 +42,8 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_u16(self) -> TiffResult<u16> {
|
|
||||||
match self {
|
|
||||||
Short(val) => Ok(val),
|
|
||||||
Unsigned(val) => Ok(u16::try_from(val)?),
|
|
||||||
UnsignedBig(val) => Ok(u16::try_from(val)?),
|
|
||||||
val => Err(TiffError::FormatError(
|
|
||||||
TiffFormatError::UnsignedIntegerExpected(val),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_u32(self) -> TiffResult<u32> {
|
pub fn into_u32(self) -> TiffResult<u32> {
|
||||||
match self {
|
match self {
|
||||||
Short(val) => Ok(val.into()),
|
|
||||||
Unsigned(val) => Ok(val),
|
Unsigned(val) => Ok(val),
|
||||||
UnsignedBig(val) => Ok(u32::try_from(val)?),
|
UnsignedBig(val) => Ok(u32::try_from(val)?),
|
||||||
val => Err(TiffError::FormatError(
|
val => Err(TiffError::FormatError(
|
||||||
@@ -77,7 +64,6 @@ impl Value {
|
|||||||
|
|
||||||
pub fn into_u64(self) -> TiffResult<u64> {
|
pub fn into_u64(self) -> TiffResult<u64> {
|
||||||
match self {
|
match self {
|
||||||
Short(val) => Ok(val.into()),
|
|
||||||
Unsigned(val) => Ok(val.into()),
|
Unsigned(val) => Ok(val.into()),
|
||||||
UnsignedBig(val) => Ok(val),
|
UnsignedBig(val) => Ok(val),
|
||||||
val => Err(TiffError::FormatError(
|
val => Err(TiffError::FormatError(
|
||||||
@@ -114,15 +100,6 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_string(self) -> TiffResult<String> {
|
|
||||||
match self {
|
|
||||||
Ascii(val) => Ok(val),
|
|
||||||
val => Err(TiffError::FormatError(
|
|
||||||
TiffFormatError::SignedIntegerExpected(val),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_u32_vec(self) -> TiffResult<Vec<u32>> {
|
pub fn into_u32_vec(self) -> TiffResult<Vec<u32>> {
|
||||||
match self {
|
match self {
|
||||||
List(vec) => {
|
List(vec) => {
|
||||||
@@ -162,22 +139,6 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_u16_vec(self) -> TiffResult<Vec<u16>> {
|
|
||||||
match self {
|
|
||||||
List(vec) => {
|
|
||||||
let mut new_vec = Vec::with_capacity(vec.len());
|
|
||||||
for v in vec {
|
|
||||||
new_vec.push(v.into_u16()?)
|
|
||||||
}
|
|
||||||
Ok(new_vec)
|
|
||||||
}
|
|
||||||
Short(val) => Ok(vec![val]),
|
|
||||||
val => Err(TiffError::FormatError(
|
|
||||||
TiffFormatError::UnsignedIntegerExpected(val),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_i32_vec(self) -> TiffResult<Vec<i32>> {
|
pub fn into_i32_vec(self) -> TiffResult<Vec<i32>> {
|
||||||
match self {
|
match self {
|
||||||
List(vec) => {
|
List(vec) => {
|
||||||
@@ -332,207 +293,172 @@ impl Entry {
|
|||||||
limits: &super::Limits,
|
limits: &super::Limits,
|
||||||
decoder: &mut super::Decoder<R>,
|
decoder: &mut super::Decoder<R>,
|
||||||
) -> TiffResult<Value> {
|
) -> TiffResult<Value> {
|
||||||
// Case 1: there are no values so we can return immediately.
|
|
||||||
if self.count == 0 {
|
|
||||||
return Ok(List(Vec::new()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let bo = decoder.byte_order();
|
let bo = decoder.byte_order();
|
||||||
let value_bytes = self.count
|
let bigtiff_value = if decoder.bigtiff {
|
||||||
* match self.type_ {
|
match (self.type_, self.count) {
|
||||||
Type::BYTE | Type::SBYTE | Type::ASCII | Type::UNDEFINED => 1,
|
(Type::BYTE, 5) => Some(offset_to_bytes(5, self)?),
|
||||||
Type::SHORT | Type::SSHORT => 2,
|
(Type::BYTE, 6) => Some(offset_to_bytes(6, self)?),
|
||||||
Type::LONG | Type::SLONG | Type::FLOAT => 4,
|
(Type::BYTE, 7) => Some(offset_to_bytes(7, self)?),
|
||||||
Type::LONG8 | Type::DOUBLE | Type::RATIONAL | Type::SRATIONAL => 8,
|
(Type::BYTE, 8) => Some(offset_to_bytes(8, self)?),
|
||||||
Type::__NonExhaustive => unreachable!(),
|
(Type::SBYTE, 5) => Some(offset_to_sbytes(5, self)?),
|
||||||
};
|
(Type::SBYTE, 6) => Some(offset_to_sbytes(6, self)?),
|
||||||
|
(Type::SBYTE, 7) => Some(offset_to_sbytes(7, self)?),
|
||||||
// Case 2: there is one value.
|
(Type::SBYTE, 8) => Some(offset_to_sbytes(8, self)?),
|
||||||
if self.count == 1 {
|
(Type::SHORT, 3) => {
|
||||||
// 2a: the value is 5-8 bytes and we're in BigTiff mode.
|
let mut r = self.r(bo);
|
||||||
if decoder.bigtiff && value_bytes > 4 && value_bytes <= 8 {
|
Some(List(vec![
|
||||||
return Ok(match self.type_ {
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
Type::LONG8 => UnsignedBig(self.r(bo).read_u64()?),
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
Type::DOUBLE => Double(self.r(bo).read_f64()?),
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
Type::RATIONAL => {
|
]))
|
||||||
let mut r = self.r(bo);
|
}
|
||||||
Rational(r.read_u32()?, r.read_u32()?)
|
(Type::SSHORT, 3) => {
|
||||||
}
|
let mut r = self.r(bo);
|
||||||
Type::SRATIONAL => {
|
Some(List(vec![
|
||||||
let mut r = self.r(bo);
|
Signed(i32::from(r.read_i16()?)),
|
||||||
SRational(r.read_i32()?, r.read_i32()?)
|
Signed(i32::from(r.read_i16()?)),
|
||||||
}
|
Signed(i32::from(r.read_i16()?)),
|
||||||
Type::BYTE
|
]))
|
||||||
| Type::SBYTE
|
}
|
||||||
| Type::ASCII
|
(Type::SHORT, 4) => {
|
||||||
| Type::UNDEFINED
|
let mut r = self.r(bo);
|
||||||
| Type::SHORT
|
Some(List(vec![
|
||||||
| Type::SSHORT
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
| Type::LONG
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
| Type::SLONG
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
| Type::FLOAT => unreachable!(),
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
Type::__NonExhaustive => unreachable!(),
|
]))
|
||||||
});
|
}
|
||||||
|
(Type::SSHORT, 4) => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
Some(List(vec![
|
||||||
|
Signed(i32::from(r.read_i16()?)),
|
||||||
|
Signed(i32::from(r.read_i16()?)),
|
||||||
|
Signed(i32::from(r.read_i16()?)),
|
||||||
|
Signed(i32::from(r.read_i16()?)),
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
(Type::LONG, 2) => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
Some(List(vec![Unsigned(r.read_u32()?), Unsigned(r.read_u32()?)]))
|
||||||
|
}
|
||||||
|
(Type::SLONG, 2) => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
Some(List(vec![Signed(r.read_i32()?), Signed(r.read_i32()?)]))
|
||||||
|
}
|
||||||
|
(Type::RATIONAL, 1) => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
let numerator = r.read_u32()?;
|
||||||
|
let denominator = r.read_u32()?;
|
||||||
|
Some(Rational(numerator, denominator))
|
||||||
|
}
|
||||||
|
(Type::SRATIONAL, 1) => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
let numerator = r.read_i32()?;
|
||||||
|
let denominator = r.read_i32()?;
|
||||||
|
Some(SRational(numerator, denominator))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
// 2b: the value is at most 4 bytes or doesn't fit in the offset field.
|
if let Some(v) = bigtiff_value {
|
||||||
return Ok(match self.type_ {
|
Ok(v)
|
||||||
Type::BYTE => Unsigned(u32::from(self.offset[0])),
|
} else {
|
||||||
Type::SBYTE => Signed(i32::from(self.offset[0] as i8)),
|
match (self.type_, self.count) {
|
||||||
Type::UNDEFINED => Byte(self.offset[0]),
|
// TODO check if this could give wrong results
|
||||||
Type::SHORT => Unsigned(u32::from(self.r(bo).read_u16()?)),
|
// at a different endianess of file/computer.
|
||||||
Type::SSHORT => Signed(i32::from(self.r(bo).read_i16()?)),
|
(Type::BYTE, 1) => Ok(Unsigned(u32::from(self.offset[0]))),
|
||||||
Type::LONG => Unsigned(self.r(bo).read_u32()?),
|
(Type::BYTE, 2) => offset_to_bytes(2, self),
|
||||||
Type::SLONG => Signed(self.r(bo).read_i32()?),
|
(Type::BYTE, 3) => offset_to_bytes(3, self),
|
||||||
Type::FLOAT => Float(self.r(bo).read_f32()?),
|
(Type::BYTE, 4) => offset_to_bytes(4, self),
|
||||||
Type::ASCII => {
|
(Type::BYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
if self.offset[0] == 0 {
|
Ok(UnsignedBig(u64::from(decoder.read_byte()?)))
|
||||||
Ascii("".to_string())
|
}),
|
||||||
|
(Type::SBYTE, 1) => Ok(Signed(i32::from(self.offset[0] as i8))),
|
||||||
|
(Type::SBYTE, 2) => offset_to_sbytes(2, self),
|
||||||
|
(Type::SBYTE, 3) => offset_to_sbytes(3, self),
|
||||||
|
(Type::SBYTE, 4) => offset_to_sbytes(4, self),
|
||||||
|
(Type::SBYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(SignedBig(i64::from(decoder.read_byte()? as i8)))
|
||||||
|
}),
|
||||||
|
(Type::SHORT, 1) => Ok(Unsigned(u32::from(self.r(bo).read_u16()?))),
|
||||||
|
(Type::SSHORT, 1) => Ok(Signed(i32::from(self.r(bo).read_i16()?))),
|
||||||
|
(Type::SHORT, 2) => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
Ok(List(vec![
|
||||||
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
|
Unsigned(u32::from(r.read_u16()?)),
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
(Type::SSHORT, 2) => {
|
||||||
|
let mut r = self.r(bo);
|
||||||
|
Ok(List(vec![
|
||||||
|
Signed(i32::from(r.read_i16()?)),
|
||||||
|
Signed(i32::from(r.read_i16()?)),
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
(Type::SHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(UnsignedBig(u64::from(decoder.read_short()?)))
|
||||||
|
}),
|
||||||
|
(Type::SSHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(SignedBig(i64::from(decoder.read_sshort()?)))
|
||||||
|
}),
|
||||||
|
(Type::LONG, 1) => Ok(Unsigned(self.r(bo).read_u32()?)),
|
||||||
|
(Type::SLONG, 1) => Ok(Signed(self.r(bo).read_i32()?)),
|
||||||
|
(Type::LONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(Unsigned(decoder.read_long()?))
|
||||||
|
}),
|
||||||
|
(Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(Signed(decoder.read_slong()?))
|
||||||
|
}),
|
||||||
|
(Type::FLOAT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(Float(decoder.read_float()?))
|
||||||
|
}),
|
||||||
|
(Type::DOUBLE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(Double(decoder.read_double()?))
|
||||||
|
}),
|
||||||
|
(Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(Rational(decoder.read_long()?, decoder.read_long()?))
|
||||||
|
}),
|
||||||
|
(Type::SRATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(SRational(decoder.read_slong()?, decoder.read_slong()?))
|
||||||
|
}),
|
||||||
|
(Type::LONG8, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(UnsignedBig(decoder.read_long8()?))
|
||||||
|
}),
|
||||||
|
(Type::ASCII, n) => {
|
||||||
|
let n = usize::try_from(n)?;
|
||||||
|
if n > limits.decoding_buffer_size {
|
||||||
|
return Err(TiffError::LimitsExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n <= 4 && !decoder.bigtiff) || (n <= 8 && decoder.bigtiff) {
|
||||||
|
let mut buf = vec![0; n];
|
||||||
|
self.r(bo).read_exact(&mut buf)?;
|
||||||
|
let v = String::from_utf8(buf)?;
|
||||||
|
let v = v.trim_matches(char::from(0));
|
||||||
|
Ok(Ascii(v.into()))
|
||||||
} else {
|
} else {
|
||||||
return Err(TiffError::FormatError(TiffFormatError::InvalidTag));
|
if decoder.bigtiff {
|
||||||
|
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
|
||||||
|
} else {
|
||||||
|
decoder.goto_offset(self.r(bo).read_u32()?)?
|
||||||
|
}
|
||||||
|
let string = decoder.read_string(n)?;
|
||||||
|
Ok(Ascii(string))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Type::LONG8 => {
|
(Type::UNDEFINED, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
Ok(Byte(u8::from(decoder.read_byte()?)))
|
||||||
UnsignedBig(decoder.read_long8()?)
|
}),
|
||||||
}
|
_ => Err(TiffError::UnsupportedError(
|
||||||
Type::DOUBLE => {
|
TiffUnsupportedError::UnsupportedDataType,
|
||||||
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::__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)?;
|
|
||||||
let v = String::from_utf8(buf)?;
|
|
||||||
let v = v.trim_matches(char::from(0));
|
|
||||||
return Ok(Ascii(v.into()));
|
|
||||||
}
|
|
||||||
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::LONG8 | Type::RATIONAL | Type::SRATIONAL | Type::DOUBLE => unreachable!(),
|
|
||||||
Type::__NonExhaustive => unreachable!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 4: there is more than one value, and it doesn't fit in the offset field.
|
|
||||||
match self.type_ {
|
|
||||||
// TODO check if this could give wrong results
|
|
||||||
// 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::SBYTE => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
|
||||||
Ok(SignedBig(i64::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::SSHORT => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
|
||||||
Ok(SignedBig(i64::from(decoder.read_sshort()?)))
|
|
||||||
}),
|
|
||||||
Type::LONG => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
|
||||||
Ok(Unsigned(decoder.read_long()?))
|
|
||||||
}),
|
|
||||||
Type::SLONG => self.decode_offset(self.count, 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::DOUBLE => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
|
||||||
Ok(Double(decoder.read_double()?))
|
|
||||||
}),
|
|
||||||
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::UNDEFINED => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
|
|
||||||
Ok(Byte(u8::from(decoder.read_byte()?)))
|
|
||||||
}),
|
|
||||||
Type::ASCII => {
|
|
||||||
let n = usize::try_from(self.count)?;
|
|
||||||
if n > limits.decoding_buffer_size {
|
|
||||||
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()?)?
|
|
||||||
}
|
|
||||||
let string = decoder.read_string(n)?;
|
|
||||||
Ok(Ascii(string))
|
|
||||||
}
|
|
||||||
Type::__NonExhaustive => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
+62
-52
@@ -3,12 +3,10 @@ use std::collections::HashMap;
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::io::{self, Read, Seek};
|
use std::io::{self, Read, Seek};
|
||||||
|
|
||||||
use crate::{ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
||||||
|
|
||||||
use self::ifd::Directory;
|
use self::ifd::Directory;
|
||||||
use crate::tags::{
|
use tags::{CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag, Type};
|
||||||
CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag, Type,
|
|
||||||
};
|
|
||||||
|
|
||||||
use self::stream::{
|
use self::stream::{
|
||||||
ByteOrder, DeflateReader, EndianReader, JpegReader, LZWReader, PackBitsReader, SmartReader,
|
ByteOrder, DeflateReader, EndianReader, JpegReader, LZWReader, PackBitsReader, SmartReader,
|
||||||
@@ -176,24 +174,6 @@ pub struct Limits {
|
|||||||
_non_exhaustive: (),
|
_non_exhaustive: (),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Limits {
|
|
||||||
/// A configuration that does not impose any limits.
|
|
||||||
///
|
|
||||||
/// This is a good start if the caller only wants to impose selective limits, contrary to the
|
|
||||||
/// default limits which allows selectively disabling limits.
|
|
||||||
///
|
|
||||||
/// Note that this configuration is likely to crash on excessively large images since,
|
|
||||||
/// naturally, the machine running the program does not have infinite memory.
|
|
||||||
pub fn unlimited() -> Limits {
|
|
||||||
Limits {
|
|
||||||
decoding_buffer_size: usize::max_value(),
|
|
||||||
ifd_value_size: usize::max_value(),
|
|
||||||
intermediate_buffer_size: usize::max_value(),
|
|
||||||
_non_exhaustive: (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Limits {
|
impl Default for Limits {
|
||||||
fn default() -> Limits {
|
fn default() -> Limits {
|
||||||
Limits {
|
Limits {
|
||||||
@@ -218,6 +198,7 @@ where
|
|||||||
bigtiff: bool,
|
bigtiff: bool,
|
||||||
limits: Limits,
|
limits: Limits,
|
||||||
next_ifd: Option<u64>,
|
next_ifd: Option<u64>,
|
||||||
|
current_ifd: Option<u64>,
|
||||||
ifd: Option<Directory>,
|
ifd: Option<Directory>,
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
@@ -229,6 +210,11 @@ where
|
|||||||
strip_decoder: Option<StripDecodeState>,
|
strip_decoder: Option<StripDecodeState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stores state necessary to seek back (or forward) to particular part of the tiff.
|
||||||
|
pub struct Checkpoint {
|
||||||
|
offset: u64,
|
||||||
|
}
|
||||||
|
|
||||||
trait Wrapping {
|
trait Wrapping {
|
||||||
fn wrapping_add(&self, other: Self) -> Self;
|
fn wrapping_add(&self, other: Self) -> Self;
|
||||||
}
|
}
|
||||||
@@ -333,6 +319,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
bigtiff: false,
|
bigtiff: false,
|
||||||
limits: Default::default(),
|
limits: Default::default(),
|
||||||
next_ifd: None,
|
next_ifd: None,
|
||||||
|
current_ifd: None,
|
||||||
ifd: None,
|
ifd: None,
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
@@ -360,11 +347,6 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
PhotometricInterpretation::RGB => match self.bits_per_sample[..] {
|
PhotometricInterpretation::RGB => match self.bits_per_sample[..] {
|
||||||
[r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)),
|
[r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)),
|
||||||
[r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)),
|
[r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)),
|
||||||
// FIXME: We should _ignore_ other components. In particular:
|
|
||||||
// > Beware of extra components. Some TIFF files may have more components per pixel
|
|
||||||
// than you think. A Baseline TIFF reader must skip over them gracefully,using the
|
|
||||||
// values of the SamplesPerPixel and BitsPerSample fields.
|
|
||||||
// > -- TIFF 6.0 Specification, Section 7, Additional Baseline requirements.
|
|
||||||
_ => Err(TiffError::UnsupportedError(
|
_ => Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::InterpretationWithBits(
|
TiffUnsupportedError::InterpretationWithBits(
|
||||||
self.photometric_interpretation,
|
self.photometric_interpretation,
|
||||||
@@ -495,7 +477,6 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
}
|
}
|
||||||
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()),
|
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,6 +592,44 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
self.reader.seek(io::SeekFrom::Start(offset)).map(|_| ())
|
self.reader.seek(io::SeekFrom::Start(offset)).map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieve a checkpoint that allows seeking before the TIFF header.
|
||||||
|
///
|
||||||
|
/// This is a special checkpoint where the caller should call `init` after restoring the
|
||||||
|
/// checkpoint and no ifd is being read.
|
||||||
|
pub fn checkpoint_init(&self) -> Checkpoint {
|
||||||
|
Checkpoint { offset: Checkpoint::INITIAL_OFFSET }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve a `Checkpoint` that can be used to seek to the current image directory.
|
||||||
|
pub fn checkpoint(&self) -> Checkpoint {
|
||||||
|
match self.current_ifd {
|
||||||
|
None => self.checkpoint_init(),
|
||||||
|
Some(offset) => Checkpoint { offset },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the next image directory as a checkpoint.
|
||||||
|
///
|
||||||
|
/// This allows jumping forward to the next image directory later, without needing to pay the
|
||||||
|
/// additional cost of parsing it right away.
|
||||||
|
pub fn checkpoint_next(&self) -> Option<Checkpoint> {
|
||||||
|
match self.next_ifd {
|
||||||
|
None => None,
|
||||||
|
Some(offset) => Some(Checkpoint { offset })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Restore the state of a previous checkpoint.
|
||||||
|
pub fn restore(&mut self, checkpoint: &Checkpoint) -> TiffResult<()> {
|
||||||
|
if checkpoint.offset == Checkpoint::INITIAL_OFFSET {
|
||||||
|
self.goto_offset_u64(0)?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
self.next_ifd = Some(checkpoint.offset);
|
||||||
|
self.next_image()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Reads a IFD entry.
|
/// Reads a IFD entry.
|
||||||
// An IFD entry has four fields:
|
// An IFD entry has four fields:
|
||||||
//
|
//
|
||||||
@@ -646,7 +665,10 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
TiffFormatError::ImageFileDirectoryNotFound,
|
TiffFormatError::ImageFileDirectoryNotFound,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
Some(offset) => self.goto_offset_u64(offset)?,
|
Some(offset) => {
|
||||||
|
self.goto_offset_u64(offset)?;
|
||||||
|
self.current_ifd = Some(offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let num_tags = if self.bigtiff {
|
let num_tags = if self.bigtiff {
|
||||||
self.read_long8()?
|
self.read_long8()?
|
||||||
@@ -752,10 +774,6 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
pub fn get_tag_u32_vec(&mut self, tag: Tag) -> TiffResult<Vec<u32>> {
|
pub fn get_tag_u32_vec(&mut self, tag: Tag) -> TiffResult<Vec<u32>> {
|
||||||
self.get_tag(tag)?.into_u32_vec()
|
self.get_tag(tag)?.into_u32_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tag_u16_vec(&mut self, tag: Tag) -> TiffResult<Vec<u16>> {
|
|
||||||
self.get_tag(tag)?.into_u16_vec()
|
|
||||||
}
|
|
||||||
pub fn get_tag_u64_vec(&mut self, tag: Tag) -> TiffResult<Vec<u64>> {
|
pub fn get_tag_u64_vec(&mut self, tag: Tag) -> TiffResult<Vec<u64>> {
|
||||||
self.get_tag(tag)?.into_u64_vec()
|
self.get_tag(tag)?.into_u64_vec()
|
||||||
}
|
}
|
||||||
@@ -775,11 +793,6 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
self.get_tag(tag)?.into_u8_vec()
|
self.get_tag(tag)?.into_u8_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to retrieve a tag and convert it to a ascii vector.
|
|
||||||
pub fn get_tag_ascii_string(&mut self, tag: Tag) -> TiffResult<String> {
|
|
||||||
self.get_tag(tag)?.into_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decompresses the strip into the supplied buffer.
|
/// Decompresses the strip into the supplied buffer.
|
||||||
/// Returns the number of bytes read.
|
/// Returns the number of bytes read.
|
||||||
fn expand_strip<'a>(
|
fn expand_strip<'a>(
|
||||||
@@ -787,7 +800,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
buffer: DecodingBuffer<'a>,
|
buffer: DecodingBuffer<'a>,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
length: u64,
|
length: u64,
|
||||||
strip_sample_count: usize,
|
max_uncompressed_length: usize,
|
||||||
) -> TiffResult<usize> {
|
) -> TiffResult<usize> {
|
||||||
let color_type = self.colortype()?;
|
let color_type = self.colortype()?;
|
||||||
self.goto_offset_u64(offset)?;
|
self.goto_offset_u64(offset)?;
|
||||||
@@ -803,7 +816,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
let (bytes, reader) = LZWReader::new(
|
let (bytes, reader) = LZWReader::new(
|
||||||
&mut self.reader,
|
&mut self.reader,
|
||||||
usize::try_from(length)?,
|
usize::try_from(length)?,
|
||||||
strip_sample_count * buffer.byte_len(),
|
max_uncompressed_length * buffer.byte_len(),
|
||||||
)?;
|
)?;
|
||||||
(bytes, Box::new(reader))
|
(bytes, Box::new(reader))
|
||||||
}
|
}
|
||||||
@@ -814,7 +827,8 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
(bytes, Box::new(reader))
|
(bytes, Box::new(reader))
|
||||||
}
|
}
|
||||||
CompressionMethod::OldDeflate => {
|
CompressionMethod::OldDeflate => {
|
||||||
let (bytes, reader) = DeflateReader::new(&mut self.reader, strip_sample_count)?;
|
let (bytes, reader) =
|
||||||
|
DeflateReader::new(&mut self.reader, max_uncompressed_length)?;
|
||||||
(bytes, Box::new(reader))
|
(bytes, Box::new(reader))
|
||||||
}
|
}
|
||||||
method => {
|
method => {
|
||||||
@@ -824,14 +838,9 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: this might be suboptimal. We might default remaining bits to ´0`, which some
|
if bytes / buffer.byte_len() > max_uncompressed_length {
|
||||||
// other decoders might do.
|
|
||||||
if bytes / buffer.byte_len() > strip_sample_count {
|
|
||||||
return Err(TiffError::FormatError(
|
return Err(TiffError::FormatError(
|
||||||
TiffFormatError::UnexpectedCompressedData {
|
TiffFormatError::InconsistentSizesEncountered,
|
||||||
actual_bytes: bytes,
|
|
||||||
required_bytes: strip_sample_count * buffer.byte_len(),
|
|
||||||
},
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1064,10 +1073,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
if units_read < buffer_size {
|
if units_read < buffer_size {
|
||||||
return Err(TiffError::FormatError(
|
return Err(TiffError::FormatError(
|
||||||
TiffFormatError::InconsistentStripSamples {
|
TiffFormatError::InconsistentSizesEncountered,
|
||||||
actual_samples: units_read,
|
|
||||||
required_samples: buffer_size,
|
|
||||||
},
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let Ok(predictor) = self.get_tag_unsigned(Tag::Predictor) {
|
if let Ok(predictor) = self.get_tag_unsigned(Tag::Predictor) {
|
||||||
@@ -1159,3 +1165,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Checkpoint {
|
||||||
|
const INITIAL_OFFSET: u64 = 0;
|
||||||
|
}
|
||||||
@@ -267,11 +267,10 @@ impl LZWReader {
|
|||||||
let mut uncompressed = Vec::with_capacity(max_uncompressed_length);
|
let mut uncompressed = Vec::with_capacity(max_uncompressed_length);
|
||||||
let mut decoder = weezl::decode::Decoder::with_tiff_size_switch(weezl::BitOrder::Msb, 8);
|
let mut decoder = weezl::decode::Decoder::with_tiff_size_switch(weezl::BitOrder::Msb, 8);
|
||||||
let mut bytes_read = 0;
|
let mut bytes_read = 0;
|
||||||
|
while bytes_read < compressed_length && uncompressed.len() < max_uncompressed_length {
|
||||||
while uncompressed.len() < max_uncompressed_length {
|
|
||||||
let bytes_written = uncompressed.len();
|
let bytes_written = uncompressed.len();
|
||||||
uncompressed.reserve(1 << 12);
|
uncompressed.reserve(1 << 12);
|
||||||
let buffer_space = uncompressed.capacity().min(max_uncompressed_length);
|
let buffer_space = uncompressed.capacity();
|
||||||
uncompressed.resize(buffer_space, 0u8);
|
uncompressed.resize(buffer_space, 0u8);
|
||||||
|
|
||||||
let result = decoder.decode_bytes(
|
let result = decoder.decode_bytes(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::tags::{PhotometricInterpretation, SampleFormat};
|
use tags::{PhotometricInterpretation, SampleFormat};
|
||||||
|
|
||||||
/// Trait for different colortypes that can be encoded.
|
/// Trait for different colortypes that can be encoded.
|
||||||
pub trait ColorType {
|
pub trait ColorType {
|
||||||
|
|||||||
+5
-31
@@ -3,9 +3,9 @@ use std::convert::TryFrom;
|
|||||||
use std::io::{Seek, Write};
|
use std::io::{Seek, Write};
|
||||||
use std::{cmp, io, mem};
|
use std::{cmp, io, mem};
|
||||||
|
|
||||||
use crate::bytecast;
|
use bytecast;
|
||||||
use crate::error::{TiffError, TiffFormatError, TiffResult};
|
use error::{TiffError, TiffFormatError, TiffResult};
|
||||||
use crate::tags::{self, ResolutionUnit, Tag, Type};
|
use tags::{self, ResolutionUnit, Tag, Type};
|
||||||
|
|
||||||
pub mod colortype;
|
pub mod colortype;
|
||||||
mod writer;
|
mod writer;
|
||||||
@@ -589,9 +589,6 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
|
|||||||
/// // You can encode tags here
|
/// // You can encode tags here
|
||||||
/// image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap();
|
/// image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap();
|
||||||
///
|
///
|
||||||
/// // Strip size can be configured before writing data
|
|
||||||
/// image.rows_per_strip(2).unwrap();
|
|
||||||
///
|
|
||||||
/// let mut idx = 0;
|
/// let mut idx = 0;
|
||||||
/// while image.next_strip_sample_count() > 0 {
|
/// while image.next_strip_sample_count() > 0 {
|
||||||
/// let sample_count = image.next_strip_sample_count() as usize;
|
/// let sample_count = image.next_strip_sample_count() as usize;
|
||||||
@@ -625,9 +622,8 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
|||||||
let row_samples = u64::from(width) * u64::try_from(<T>::BITS_PER_SAMPLE.len())?;
|
let row_samples = u64::from(width) * u64::try_from(<T>::BITS_PER_SAMPLE.len())?;
|
||||||
let row_bytes = row_samples * u64::from(<T::Inner>::BYTE_LEN);
|
let row_bytes = row_samples * u64::from(<T::Inner>::BYTE_LEN);
|
||||||
|
|
||||||
// Limit the strip size to prevent potential memory and security issues.
|
// As per tiff spec each strip should be about 8k long
|
||||||
// Also keep the multiple strip handling 'oiled'
|
let rows_per_strip = (8000 + row_bytes - 1) / row_bytes;
|
||||||
let rows_per_strip = (1_000_000 + row_bytes - 1) / row_bytes;
|
|
||||||
|
|
||||||
let strip_count = (u64::from(height) + rows_per_strip - 1) / rows_per_strip;
|
let strip_count = (u64::from(height) + rows_per_strip - 1) / rows_per_strip;
|
||||||
|
|
||||||
@@ -759,28 +755,6 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
|||||||
self.encoder.write_tag(Tag::YResolution, value).unwrap();
|
self.encoder.write_tag(Tag::YResolution, value).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set image number of lines per strip
|
|
||||||
///
|
|
||||||
/// This function needs to be called before any calls to `write_data` or
|
|
||||||
/// `write_strip` and will return an error otherwise.
|
|
||||||
pub fn rows_per_strip(&mut self, value: u32) -> TiffResult<()> {
|
|
||||||
if self.strip_idx != 0 {
|
|
||||||
return Err(io::Error::new(
|
|
||||||
io::ErrorKind::InvalidInput,
|
|
||||||
"Cannot change strip size after data was written",
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
// Write tag as 32 bits
|
|
||||||
self.encoder.write_tag(Tag::RowsPerStrip, value)?;
|
|
||||||
|
|
||||||
let value: u64 = value as u64;
|
|
||||||
self.strip_count = (self.height as u64 + value - 1) / value;
|
|
||||||
self.rows_per_strip = value;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish_internal(&mut self) -> TiffResult<()> {
|
fn finish_internal(&mut self) -> TiffResult<()> {
|
||||||
self.encoder
|
self.encoder
|
||||||
.write_tag(Tag::StripOffsets, &*self.strip_offsets)?;
|
.write_tag(Tag::StripOffsets, &*self.strip_offsets)?;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::error::TiffResult;
|
use error::TiffResult;
|
||||||
use std::io::{self, Seek, SeekFrom, Write};
|
use std::io::{self, Seek, SeekFrom, Write};
|
||||||
|
|
||||||
pub fn write_tiff_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
pub fn write_tiff_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
|||||||
+3
-33
@@ -3,12 +3,10 @@ use std::fmt;
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::string;
|
use std::string;
|
||||||
|
|
||||||
use crate::decoder::ifd::Value;
|
use decoder::ifd::Value;
|
||||||
use crate::tags::{
|
|
||||||
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag,
|
|
||||||
};
|
|
||||||
use crate::ColorType;
|
|
||||||
use miniz_oxide::inflate::TINFLStatus;
|
use miniz_oxide::inflate::TINFLStatus;
|
||||||
|
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag};
|
||||||
|
use ColorType;
|
||||||
|
|
||||||
/// Tiff error kinds.
|
/// Tiff error kinds.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -43,14 +41,6 @@ pub enum TiffFormatError {
|
|||||||
TiffSignatureInvalid,
|
TiffSignatureInvalid,
|
||||||
ImageFileDirectoryNotFound,
|
ImageFileDirectoryNotFound,
|
||||||
InconsistentSizesEncountered,
|
InconsistentSizesEncountered,
|
||||||
UnexpectedCompressedData {
|
|
||||||
actual_bytes: usize,
|
|
||||||
required_bytes: usize,
|
|
||||||
},
|
|
||||||
InconsistentStripSamples {
|
|
||||||
actual_samples: usize,
|
|
||||||
required_samples: usize,
|
|
||||||
},
|
|
||||||
InvalidTag,
|
InvalidTag,
|
||||||
InvalidTagValueType(Tag),
|
InvalidTagValueType(Tag),
|
||||||
RequiredTagNotFound(Tag),
|
RequiredTagNotFound(Tag),
|
||||||
@@ -74,26 +64,6 @@ impl fmt::Display for TiffFormatError {
|
|||||||
TiffSignatureInvalid => write!(fmt, "TIFF signature invalid."),
|
TiffSignatureInvalid => write!(fmt, "TIFF signature invalid."),
|
||||||
ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."),
|
ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."),
|
||||||
InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."),
|
InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."),
|
||||||
UnexpectedCompressedData {
|
|
||||||
actual_bytes,
|
|
||||||
required_bytes,
|
|
||||||
} => {
|
|
||||||
write!(
|
|
||||||
fmt,
|
|
||||||
"Decompression returned different amount of bytes than expected: got {}, expected {}.",
|
|
||||||
actual_bytes, required_bytes
|
|
||||||
)
|
|
||||||
}
|
|
||||||
InconsistentStripSamples {
|
|
||||||
actual_samples,
|
|
||||||
required_samples,
|
|
||||||
} => {
|
|
||||||
write!(
|
|
||||||
fmt,
|
|
||||||
"Inconsistent elements in strip: got {}, expected {}.",
|
|
||||||
actual_samples, required_samples
|
|
||||||
)
|
|
||||||
}
|
|
||||||
InvalidTag => write!(fmt, "Image contains invalid tag."),
|
InvalidTag => write!(fmt, "Image contains invalid tag."),
|
||||||
InvalidTagValueType(ref tag) => {
|
InvalidTagValueType(ref tag) => {
|
||||||
write!(fmt, "Tag `{:?}` did not have the expected value type.", tag)
|
write!(fmt, "Tag `{:?}` did not have the expected value type.", tag)
|
||||||
|
|||||||
@@ -120,14 +120,6 @@ pub enum Tag(u16) unknown("A private or extension tag") {
|
|||||||
TileOffsets = 324,
|
TileOffsets = 324,
|
||||||
TileByteCounts = 325,
|
TileByteCounts = 325,
|
||||||
SampleFormat = 339,
|
SampleFormat = 339,
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -360,42 +360,3 @@ fn test_multipage_image() {
|
|||||||
assert_eq!(img_decoder.dimensions().unwrap(), (3, 3));
|
assert_eq!(img_decoder.dimensions().unwrap(), (3, 3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
/// verify rows per strip setting
|
|
||||||
fn test_rows_per_strip() {
|
|
||||||
let mut file = Cursor::new(Vec::new());
|
|
||||||
{
|
|
||||||
let mut img_encoder = TiffEncoder::new(&mut file).unwrap();
|
|
||||||
|
|
||||||
let mut image = img_encoder.new_image::<colortype::Gray8>(100, 100).unwrap();
|
|
||||||
assert_eq!(image.next_strip_sample_count(), 100 * 100);
|
|
||||||
image.rows_per_strip(2).unwrap();
|
|
||||||
assert_eq!(image.next_strip_sample_count(), 2 * 100);
|
|
||||||
|
|
||||||
let img2: Vec<u8> = vec![0; 2 * 100];
|
|
||||||
image.write_strip(&img2[..]).unwrap();
|
|
||||||
assert!(image.rows_per_strip(5).is_err());
|
|
||||||
for i in 1..50 {
|
|
||||||
let img2: Vec<u8> = vec![i; 2 * 100];
|
|
||||||
image.write_strip(&img2[..]).unwrap();
|
|
||||||
}
|
|
||||||
assert!(image.write_strip(&img2[..]).is_err());
|
|
||||||
image.finish().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
file.seek(SeekFrom::Start(0)).unwrap();
|
|
||||||
{
|
|
||||||
let mut decoder = Decoder::new(&mut file).unwrap();
|
|
||||||
assert_eq!(decoder.get_tag_u64(Tag::RowsPerStrip).unwrap(), 2);
|
|
||||||
assert_eq!(decoder.strip_count().unwrap(), 50);
|
|
||||||
|
|
||||||
for i in 0..50 {
|
|
||||||
let img2 = [i; 2 * 100];
|
|
||||||
match decoder.read_strip().unwrap() {
|
|
||||||
DecodingResult::U8(data) => assert_eq!(&img2[..], &data[..]),
|
|
||||||
other => panic!("Incorrect strip type {:?}", other),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user