Compare commits

..
1 Commits
Author SHA1 Message Date
Andreas Molzer 51ec671e3a Add checkpoint, storing an IFD offset of a tiff 2020-12-08 21:01:21 +01:00
11 changed files with 237 additions and 423 deletions

No files matched your search

-17
View File
@@ -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
New features:
+1 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tiff"
version = "0.6.1"
version = "0.6.0"
authors = [
"ccgn",
"bvssvni <bvssvni@gmail.com>",
@@ -15,7 +15,6 @@ 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"
+162 -236
View File
@@ -6,19 +6,18 @@ use std::io::{self, Read, Seek};
use std::mem;
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, Byte, Double, Float, List, Rational, RationalBig, SRational, SRationalBig, Short,
Signed, SignedBig, Unsigned, UnsignedBig,
Ascii, Byte, Double, Float, List, Rational, RationalBig, SRational, SRationalBig, Signed,
SignedBig, Unsigned, UnsignedBig,
};
#[allow(unused_qualifications)]
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Byte(u8),
Short(u16),
Signed(i32),
SignedBig(i64),
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> {
match self {
Short(val) => Ok(val.into()),
Unsigned(val) => Ok(val),
UnsignedBig(val) => Ok(u32::try_from(val)?),
val => Err(TiffError::FormatError(
@@ -77,7 +64,6 @@ impl Value {
pub fn into_u64(self) -> TiffResult<u64> {
match self {
Short(val) => Ok(val.into()),
Unsigned(val) => Ok(val.into()),
UnsignedBig(val) => Ok(val),
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>> {
match self {
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>> {
match self {
List(vec) => {
@@ -332,207 +293,172 @@ 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 => 4,
Type::LONG8 | Type::DOUBLE | Type::RATIONAL | Type::SRATIONAL => 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::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::BYTE
| Type::SBYTE
| Type::ASCII
| Type::UNDEFINED
| Type::SHORT
| Type::SSHORT
| Type::LONG
| Type::SLONG
| Type::FLOAT => unreachable!(),
Type::__NonExhaustive => unreachable!(),
});
let bigtiff_value = if decoder.bigtiff {
match (self.type_, self.count) {
(Type::BYTE, 5) => Some(offset_to_bytes(5, self)?),
(Type::BYTE, 6) => Some(offset_to_bytes(6, self)?),
(Type::BYTE, 7) => Some(offset_to_bytes(7, self)?),
(Type::BYTE, 8) => Some(offset_to_bytes(8, self)?),
(Type::SBYTE, 5) => Some(offset_to_sbytes(5, self)?),
(Type::SBYTE, 6) => Some(offset_to_sbytes(6, self)?),
(Type::SBYTE, 7) => Some(offset_to_sbytes(7, self)?),
(Type::SBYTE, 8) => Some(offset_to_sbytes(8, self)?),
(Type::SHORT, 3) => {
let mut r = self.r(bo);
Some(List(vec![
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
]))
}
(Type::SSHORT, 3) => {
let mut r = self.r(bo);
Some(List(vec![
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
]))
}
(Type::SHORT, 4) => {
let mut r = self.r(bo);
Some(List(vec![
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
]))
}
(Type::SSHORT, 4) => {
let mut r = self.r(bo);
Some(List(vec![
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
]))
}
(Type::LONG, 2) => {
let mut r = self.r(bo);
Some(List(vec![Unsigned(r.read_u32()?), Unsigned(r.read_u32()?)]))
}
(Type::SLONG, 2) => {
let mut r = self.r(bo);
Some(List(vec![Signed(r.read_i32()?), Signed(r.read_i32()?)]))
}
(Type::RATIONAL, 1) => {
let mut r = self.r(bo);
let numerator = r.read_u32()?;
let denominator = r.read_u32()?;
Some(Rational(numerator, denominator))
}
(Type::SRATIONAL, 1) => {
let mut r = self.r(bo);
let numerator = r.read_i32()?;
let denominator = r.read_i32()?;
Some(SRational(numerator, denominator))
}
_ => None,
}
} else {
None
};
// 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())
if let Some(v) = bigtiff_value {
Ok(v)
} else {
match (self.type_, self.count) {
// TODO check if this could give wrong results
// at a different endianess of file/computer.
(Type::BYTE, 1) => Ok(Unsigned(u32::from(self.offset[0]))),
(Type::BYTE, 2) => offset_to_bytes(2, self),
(Type::BYTE, 3) => offset_to_bytes(3, self),
(Type::BYTE, 4) => offset_to_bytes(4, self),
(Type::BYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(UnsignedBig(u64::from(decoder.read_byte()?)))
}),
(Type::SBYTE, 1) => Ok(Signed(i32::from(self.offset[0] as i8))),
(Type::SBYTE, 2) => offset_to_sbytes(2, self),
(Type::SBYTE, 3) => offset_to_sbytes(3, self),
(Type::SBYTE, 4) => offset_to_sbytes(4, self),
(Type::SBYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(SignedBig(i64::from(decoder.read_byte()? as i8)))
}),
(Type::SHORT, 1) => Ok(Unsigned(u32::from(self.r(bo).read_u16()?))),
(Type::SSHORT, 1) => Ok(Signed(i32::from(self.r(bo).read_i16()?))),
(Type::SHORT, 2) => {
let mut r = self.r(bo);
Ok(List(vec![
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
]))
}
(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 {
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 => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
UnsignedBig(decoder.read_long8()?)
}
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::__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!(),
(Type::UNDEFINED, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Byte(u8::from(decoder.read_byte()?)))
}),
_ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedDataType,
)),
}
}
// 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]
+62 -52
View File
@@ -3,12 +3,10 @@ use std::collections::HashMap;
use std::convert::TryFrom;
use std::io::{self, Read, Seek};
use crate::{ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
use self::ifd::Directory;
use crate::tags::{
CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag, Type,
};
use tags::{CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag, Type};
use self::stream::{
ByteOrder, DeflateReader, EndianReader, JpegReader, LZWReader, PackBitsReader, SmartReader,
@@ -176,24 +174,6 @@ pub struct Limits {
_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 {
fn default() -> Limits {
Limits {
@@ -218,6 +198,7 @@ where
bigtiff: bool,
limits: Limits,
next_ifd: Option<u64>,
current_ifd: Option<u64>,
ifd: Option<Directory>,
width: u32,
height: u32,
@@ -229,6 +210,11 @@ where
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 {
fn wrapping_add(&self, other: Self) -> Self;
}
@@ -333,6 +319,7 @@ impl<R: Read + Seek> Decoder<R> {
bigtiff: false,
limits: Default::default(),
next_ifd: None,
current_ifd: None,
ifd: None,
width: 0,
height: 0,
@@ -360,11 +347,6 @@ impl<R: Read + Seek> Decoder<R> {
PhotometricInterpretation::RGB => match self.bits_per_sample[..] {
[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)),
// 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(
TiffUnsupportedError::InterpretationWithBits(
self.photometric_interpretation,
@@ -495,7 +477,6 @@ impl<R: Read + Seek> Decoder<R> {
}
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()),
}
Ok(())
}
@@ -611,6 +592,44 @@ impl<R: Read + Seek> Decoder<R> {
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.
// An IFD entry has four fields:
//
@@ -646,7 +665,10 @@ impl<R: Read + Seek> Decoder<R> {
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 {
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>> {
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>> {
self.get_tag(tag)?.into_u64_vec()
}
@@ -775,11 +793,6 @@ impl<R: Read + Seek> Decoder<R> {
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.
/// Returns the number of bytes read.
fn expand_strip<'a>(
@@ -787,7 +800,7 @@ impl<R: Read + Seek> Decoder<R> {
buffer: DecodingBuffer<'a>,
offset: u64,
length: u64,
strip_sample_count: usize,
max_uncompressed_length: usize,
) -> TiffResult<usize> {
let color_type = self.colortype()?;
self.goto_offset_u64(offset)?;
@@ -803,7 +816,7 @@ impl<R: Read + Seek> Decoder<R> {
let (bytes, reader) = LZWReader::new(
&mut self.reader,
usize::try_from(length)?,
strip_sample_count * buffer.byte_len(),
max_uncompressed_length * buffer.byte_len(),
)?;
(bytes, Box::new(reader))
}
@@ -814,7 +827,8 @@ impl<R: Read + Seek> Decoder<R> {
(bytes, Box::new(reader))
}
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))
}
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
// other decoders might do.
if bytes / buffer.byte_len() > strip_sample_count {
if bytes / buffer.byte_len() > max_uncompressed_length {
return Err(TiffError::FormatError(
TiffFormatError::UnexpectedCompressedData {
actual_bytes: bytes,
required_bytes: strip_sample_count * buffer.byte_len(),
},
TiffFormatError::InconsistentSizesEncountered,
));
}
@@ -1064,10 +1073,7 @@ impl<R: Read + Seek> Decoder<R> {
if units_read < buffer_size {
return Err(TiffError::FormatError(
TiffFormatError::InconsistentStripSamples {
actual_samples: units_read,
required_samples: buffer_size,
},
TiffFormatError::InconsistentSizesEncountered,
));
}
if let Ok(predictor) = self.get_tag_unsigned(Tag::Predictor) {
@@ -1159,3 +1165,7 @@ impl<R: Read + Seek> Decoder<R> {
Ok(result)
}
}
impl Checkpoint {
const INITIAL_OFFSET: u64 = 0;
}
+2 -3
View File
@@ -267,11 +267,10 @@ impl LZWReader {
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 bytes_read = 0;
while uncompressed.len() < max_uncompressed_length {
while bytes_read < compressed_length && uncompressed.len() < max_uncompressed_length {
let bytes_written = uncompressed.len();
uncompressed.reserve(1 << 12);
let buffer_space = uncompressed.capacity().min(max_uncompressed_length);
let buffer_space = uncompressed.capacity();
uncompressed.resize(buffer_space, 0u8);
let result = decoder.decode_bytes(
+1 -1
View File
@@ -1,4 +1,4 @@
use crate::tags::{PhotometricInterpretation, SampleFormat};
use tags::{PhotometricInterpretation, SampleFormat};
/// Trait for different colortypes that can be encoded.
pub trait ColorType {
+5 -31
View File
@@ -3,9 +3,9 @@ use std::convert::TryFrom;
use std::io::{Seek, Write};
use std::{cmp, io, mem};
use crate::bytecast;
use crate::error::{TiffError, TiffFormatError, TiffResult};
use crate::tags::{self, ResolutionUnit, Tag, Type};
use bytecast;
use error::{TiffError, TiffFormatError, TiffResult};
use tags::{self, ResolutionUnit, Tag, Type};
pub mod colortype;
mod writer;
@@ -589,9 +589,6 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
/// // 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;
@@ -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_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;
@@ -759,28 +755,6 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> 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, &*self.strip_offsets)?;
+1 -1
View File
@@ -1,4 +1,4 @@
use crate::error::TiffResult;
use error::TiffResult;
use std::io::{self, Seek, SeekFrom, Write};
pub fn write_tiff_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
+3 -33
View File
@@ -3,12 +3,10 @@ use std::fmt;
use std::io;
use std::string;
use crate::decoder::ifd::Value;
use crate::tags::{
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag,
};
use crate::ColorType;
use decoder::ifd::Value;
use miniz_oxide::inflate::TINFLStatus;
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag};
use ColorType;
/// Tiff error kinds.
#[derive(Debug)]
@@ -43,14 +41,6 @@ pub enum TiffFormatError {
TiffSignatureInvalid,
ImageFileDirectoryNotFound,
InconsistentSizesEncountered,
UnexpectedCompressedData {
actual_bytes: usize,
required_bytes: usize,
},
InconsistentStripSamples {
actual_samples: usize,
required_samples: usize,
},
InvalidTag,
InvalidTagValueType(Tag),
RequiredTagNotFound(Tag),
@@ -74,26 +64,6 @@ 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)
-8
View File
@@ -120,14 +120,6 @@ pub enum Tag(u16) unknown("A private or extension tag") {
TileOffsets = 324,
TileByteCounts = 325,
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
}
}
-39
View File
@@ -360,42 +360,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),
}
}
}
}