Compare commits

...
27 Commits
Author SHA1 Message Date
Andreas Molzer 49f9b3941d Change lot and meta data for 0.6.1 2020-12-12 19:49:19 +01:00
Jonathan Behrens fc80c5e3dd Merge pull request #111 from tnl-rwa/large_sttrip_size
Large strip size
2020-12-07 20:50:17 -05:00
Robert Wassens 95f0a34bcc Update comment 2020-12-07 12:14:36 +01:00
Robert Wassens 3e042df438 Set default strip size to 1MB 2020-12-07 11:44:21 +01:00
Robert Wassens 5346a441ae Processed review comment 2020-12-07 11:43:09 +01:00
Robert Wassens 76173069a3 round trip test 2020-12-06 23:34:24 +01:00
HeroicKatora 6cdd340933 Merge pull request #112 from image-rs/unlimited
Add a constant unlimited limit
2020-12-06 21:24:54 +01:00
HeroicKatora ef004cee48 Merge pull request #108 from image-rs/short-read-in-lzw
Fix lzw decoding stopping short of full decode
2020-12-06 02:52:06 +01:00
Andreas Molzer 6d7d92c605 Adjust field names based on review 2020-12-06 02:34:59 +01:00
HeroicKatora 53437dd7b4 Merge pull request #110 from fintelia/entry-val-refactor
Refactor Entry::val function
2020-12-05 21:35:37 +01:00
Andreas Molzer 0c06d32ee4 Add a constant unlimited limit 2020-12-05 18:20:23 +01:00
Andreas Molzer 4d8c64bf1e Fix lzw decoding stopping short of full decode
When the required amount of data was just a tiny bit larger than full a
page size (one buffer increment), the decoder would stop since all
compressed data had been read. However, the lzw decoder contains a small
internal bit buffer for code decoding. That is, some remaining bytes had
been retained and were still left to be decoded.
2020-12-05 14:57:28 +01:00
Robert Wassens 7da8f01f75 make strip size configurable 2020-12-05 01:39:37 +01:00
Jonathan Behrens 67105fe4ec Refactor Entry::val function 2020-12-04 18:59:51 -05:00
Robert Wassens 1b38ff9410 strip size = image height 2020-12-04 12:45:46 +01:00
HeroicKatora c9703dcb8a Merge pull request #100 from ruffson/feature_geotiff
Feature GeoTIFF
2020-11-16 19:07:29 +01:00
ruffson e2caf86f6a Add intos for Short 2020-11-16 19:01:18 +01:00
ruffson 923b37d647 Remove unnecessary line 2020-11-16 18:57:50 +01:00
ruffson 9f4799912f Remove geokey parsing 2020-11-16 18:57:48 +01:00
ruffson 4b218be4ac Add geodir hashmap to decoder 2020-11-16 18:57:24 +01:00
ruffson 9a0041286e Remove print outs 2020-11-16 18:57:24 +01:00
ruffson 0a4b51055e Add gdal_nodata tag 2020-11-16 18:57:24 +01:00
ruffson 96d6d52830 Move geo code in its own dir 2020-11-16 18:57:24 +01:00
ruffson 62a1a500a5 Add support for GeoTiff geokey dir 2020-11-16 18:57:21 +01:00
ruffson 3953bd31e1 Read out short vecs and ascii from ifds 2020-11-16 18:52:04 +01:00
HeroicKatora 83004c8344 Merge pull request #104 from image-rs/edition2018
Convert to edition 2018
2020-10-31 20:31:03 +01:00
Andreas Molzer 8ab9d30eff Convert to edition 2018
Notably, This enables NLL even on older compilers such as 1.34.
2020-10-31 20:13:30 +01:00
11 changed files with 421 additions and 183 deletions

No files matched your search

+17
View File
@@ -1,3 +1,20 @@
# 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:
+2 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tiff"
version = "0.6.0"
version = "0.6.1"
authors = [
"ccgn",
"bvssvni <bvssvni@gmail.com>",
@@ -15,6 +15,7 @@ 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"
+235 -161
View File
@@ -6,18 +6,19 @@ use std::io::{self, Read, Seek};
use std::mem;
use super::stream::{ByteOrder, EndianReader, SmartReader};
use tags::{Tag, Type};
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
use crate::tags::{Tag, Type};
use crate::{TiffError, TiffFormatError, TiffResult};
use self::Value::{
Ascii, Byte, Double, Float, List, Rational, RationalBig, SRational, SRationalBig, Signed,
SignedBig, Unsigned, UnsignedBig,
Ascii, Byte, Double, Float, List, Rational, RationalBig, SRational, SRationalBig, Short,
Signed, SignedBig, Unsigned, UnsignedBig,
};
#[allow(unused_qualifications)]
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Byte(u8),
Short(u16),
Signed(i32),
SignedBig(i64),
Unsigned(u32),
@@ -42,8 +43,20 @@ 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(
@@ -64,6 +77,7 @@ 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(
@@ -100,6 +114,15 @@ 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) => {
@@ -139,6 +162,22 @@ 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) => {
@@ -293,172 +332,207 @@ 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 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
};
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!(),
};
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);
// 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!(),
});
}
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()))
// 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 {
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))
return Err(TiffError::FormatError(TiffFormatError::InvalidTag));
}
}
(Type::UNDEFINED, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Byte(u8::from(decoder.read_byte()?)))
}),
_ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedDataType,
)),
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!(),
}
}
// 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]
+51 -9
View File
@@ -3,10 +3,12 @@ use std::collections::HashMap;
use std::convert::TryFrom;
use std::io::{self, Read, Seek};
use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
use crate::{ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
use self::ifd::Directory;
use tags::{CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag, Type};
use crate::tags::{
CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag, Type,
};
use self::stream::{
ByteOrder, DeflateReader, EndianReader, JpegReader, LZWReader, PackBitsReader, SmartReader,
@@ -174,6 +176,24 @@ 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 {
@@ -340,6 +360,11 @@ 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,
@@ -470,6 +495,7 @@ impl<R: Read + Seek> Decoder<R> {
}
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()),
}
Ok(())
}
@@ -726,6 +752,10 @@ 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()
}
@@ -745,6 +775,11 @@ 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>(
@@ -752,7 +787,7 @@ impl<R: Read + Seek> Decoder<R> {
buffer: DecodingBuffer<'a>,
offset: u64,
length: u64,
max_uncompressed_length: usize,
strip_sample_count: usize,
) -> TiffResult<usize> {
let color_type = self.colortype()?;
self.goto_offset_u64(offset)?;
@@ -768,7 +803,7 @@ impl<R: Read + Seek> Decoder<R> {
let (bytes, reader) = LZWReader::new(
&mut self.reader,
usize::try_from(length)?,
max_uncompressed_length * buffer.byte_len(),
strip_sample_count * buffer.byte_len(),
)?;
(bytes, Box::new(reader))
}
@@ -779,8 +814,7 @@ impl<R: Read + Seek> Decoder<R> {
(bytes, Box::new(reader))
}
CompressionMethod::OldDeflate => {
let (bytes, reader) =
DeflateReader::new(&mut self.reader, max_uncompressed_length)?;
let (bytes, reader) = DeflateReader::new(&mut self.reader, strip_sample_count)?;
(bytes, Box::new(reader))
}
method => {
@@ -790,9 +824,14 @@ impl<R: Read + Seek> Decoder<R> {
}
};
if bytes / buffer.byte_len() > max_uncompressed_length {
// 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 {
return Err(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered,
TiffFormatError::UnexpectedCompressedData {
actual_bytes: bytes,
required_bytes: strip_sample_count * buffer.byte_len(),
},
));
}
@@ -1025,7 +1064,10 @@ impl<R: Read + Seek> Decoder<R> {
if units_read < buffer_size {
return Err(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered,
TiffFormatError::InconsistentStripSamples {
actual_samples: units_read,
required_samples: buffer_size,
},
));
}
if let Ok(predictor) = self.get_tag_unsigned(Tag::Predictor) {
+3 -2
View File
@@ -267,10 +267,11 @@ 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 bytes_read < compressed_length && uncompressed.len() < max_uncompressed_length {
while uncompressed.len() < max_uncompressed_length {
let bytes_written = uncompressed.len();
uncompressed.reserve(1 << 12);
let buffer_space = uncompressed.capacity();
let buffer_space = uncompressed.capacity().min(max_uncompressed_length);
uncompressed.resize(buffer_space, 0u8);
let result = decoder.decode_bytes(
+1 -1
View File
@@ -1,4 +1,4 @@
use tags::{PhotometricInterpretation, SampleFormat};
use crate::tags::{PhotometricInterpretation, SampleFormat};
/// Trait for different colortypes that can be encoded.
pub trait ColorType {
+31 -5
View File
@@ -3,9 +3,9 @@ use std::convert::TryFrom;
use std::io::{Seek, Write};
use std::{cmp, io, mem};
use bytecast;
use error::{TiffError, TiffFormatError, TiffResult};
use tags::{self, ResolutionUnit, Tag, Type};
use crate::bytecast;
use crate::error::{TiffError, TiffFormatError, TiffResult};
use crate::tags::{self, ResolutionUnit, Tag, Type};
pub mod colortype;
mod writer;
@@ -589,6 +589,9 @@ 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;
@@ -622,8 +625,9 @@ 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);
// As per tiff spec each strip should be about 8k long
let rows_per_strip = (8000 + row_bytes - 1) / row_bytes;
// 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;
let strip_count = (u64::from(height) + rows_per_strip - 1) / rows_per_strip;
@@ -755,6 +759,28 @@ 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 error::TiffResult;
use crate::error::TiffResult;
use std::io::{self, Seek, SeekFrom, Write};
pub fn write_tiff_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
+33 -3
View File
@@ -3,10 +3,12 @@ use std::fmt;
use std::io;
use std::string;
use decoder::ifd::Value;
use crate::decoder::ifd::Value;
use crate::tags::{
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag,
};
use crate::ColorType;
use miniz_oxide::inflate::TINFLStatus;
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag};
use ColorType;
/// Tiff error kinds.
#[derive(Debug)]
@@ -41,6 +43,14 @@ 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),
@@ -64,6 +74,26 @@ 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,6 +120,14 @@ 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,3 +360,42 @@ 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),
}
}
}
}