Compare commits
34
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ed3d90f19 | ||
|
|
119dec5068 | ||
|
|
ec0477125f | ||
|
|
1c8e98bca5 | ||
|
|
e6985063ac | ||
|
|
42c94ac3d9 | ||
|
|
eadec8f0b9 | ||
|
|
462697e373 | ||
|
|
c537f5cc64 | ||
|
|
8f61196d70 | ||
|
|
298dc8841d | ||
|
|
f92be13f6c | ||
|
|
f9cace84da | ||
|
|
d510a9f4ab | ||
|
|
1fafcccb36 | ||
|
|
8b96793671 | ||
|
|
27ae65160d | ||
|
|
29bb8c2dfe | ||
|
|
c5fcba45a0 | ||
|
|
cbaa9dbc5b | ||
|
|
959254aee9 | ||
|
|
3f14098060 | ||
|
|
57783ba43f | ||
|
|
abd6aee19f | ||
|
|
c72a8d364d | ||
|
|
e5d2e7862c | ||
|
|
6e61de81b6 | ||
|
|
8c4cea5d97 | ||
|
|
0c53e1066a | ||
|
|
f4e985e0bd | ||
|
|
af010edcfd | ||
|
|
f32b3b9d10 | ||
|
|
1f92c562bc | ||
|
|
fca4e1f88a |
No files matched your search
@@ -4,3 +4,8 @@ rust:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
|
||||
script:
|
||||
- if [[ "$TRAVIS_RUST_VERSION" == *stable* ]]; then rustup component add rustfmt; cargo fmt --version; cargo fmt -- --check || (echo "Please reformat your code with 'cargo fmt' (version $(cargo fmt --version))"; false); fi
|
||||
- cargo build -v
|
||||
- cargo test -v
|
||||
@@ -20,6 +20,3 @@ exclude = ["tests/images/*", "tests/fuzz_images/*"]
|
||||
byteorder = "1.2"
|
||||
lzw = "0.10"
|
||||
miniz_oxide = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.0"
|
||||
@@ -4,3 +4,43 @@
|
||||
[](https://crates.io/crates/tiff)
|
||||
|
||||
TIFF decoding and encoding library in pure Rust
|
||||
|
||||
## Supported
|
||||
|
||||
### Features
|
||||
- Baseline spec (other than formats and tags listed below as not supported)
|
||||
- Multipage
|
||||
- Incremental decoding
|
||||
|
||||
### Formats
|
||||
This table lists photometric interpretations and sample formats which are supported for encoding and decoding. The entries are `ColorType` variants for which sample bit depths are supported. Only samples where all bit depths are equal are currently supported. For example, `RGB(8)` means that the bit depth [8, 8, 8] is supported and will be interpreted as an 8 bit per channel RGB color type.
|
||||
|
||||
| `PhotometricInterpretation` | UINT Format | IEEEFP Format |
|
||||
|-|-|-|
|
||||
| `WhiteIsZero` | Gray(8\|16\|32\|64) | |
|
||||
| `BlackIsZero` | Gray(8\|16\|32\|64) | |
|
||||
| `RGB` | RGB(8\|16\|32\|64), RGBA(8\|16\|32\|64) | |
|
||||
| `RGBPalette` | | |
|
||||
| `Mask` | | |
|
||||
| `CMYK` | CMYK(8\|16\|32\|64) | |
|
||||
| `YCbCr` | | |
|
||||
| `CIELab` | | |
|
||||
|
||||
### Compressions
|
||||
|
||||
| | Decoding | Encoding |
|
||||
|-|-|-|
|
||||
| None | ✓ | ✓ |
|
||||
| LZW | ✓ | |
|
||||
| Deflate | ✓ | |
|
||||
| PackBits | ✓ | |
|
||||
|
||||
|
||||
## Not yet supported
|
||||
|
||||
Formats and interpretations not listed above or with empty entries are unsupported.
|
||||
|
||||
- Baseline tags
|
||||
- `ExtraSamples`
|
||||
- Extension tags
|
||||
- BigTIFF
|
||||
+9
-131
@@ -1,140 +1,16 @@
|
||||
//! Function for reading TIFF tags
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
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 self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational};
|
||||
|
||||
macro_rules! tags {
|
||||
{
|
||||
// Permit arbitrary meta items, which include documentation.
|
||||
$( #[$enum_attr:meta] )*
|
||||
$vis:vis enum $name:ident($ty:ty) $(unknown($unknown_doc:literal))* {
|
||||
// Each of the `Name = Val,` permitting documentation.
|
||||
$($(#[$ident_attr:meta])* $tag:ident = $val:expr,)*
|
||||
}
|
||||
} => {
|
||||
$( #[$enum_attr] )*
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum $name {
|
||||
$($(#[$ident_attr])* $tag,)*
|
||||
// FIXME: switch to non_exhaustive once stabilized and compiler requirement new enough
|
||||
#[doc(hidden)]
|
||||
__NonExhaustive,
|
||||
$(
|
||||
#[doc = $unknown_doc]
|
||||
Unknown($ty),
|
||||
)*
|
||||
}
|
||||
|
||||
impl $name {
|
||||
#[inline(always)]
|
||||
fn __from_inner_type(n: $ty) -> Result<Self, $ty> {
|
||||
match n {
|
||||
$( $val => Ok($name::$tag), )*
|
||||
n => Err(n),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn __to_inner_type(&self) -> $ty {
|
||||
match *self {
|
||||
$( $name::$tag => $val, )*
|
||||
$( $name::Unknown(n) => { $unknown_doc; n }, )*
|
||||
$name::__NonExhaustive => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: These tags appear in the order they are mentioned in the TIFF reference
|
||||
tags! {
|
||||
/// TIFF tags
|
||||
pub enum Tag(u16) unknown("A private or extension tag") {
|
||||
// Baseline tags:
|
||||
Artist = 315,
|
||||
// grayscale images PhotometricInterpretation 1 or 3
|
||||
BitsPerSample = 258,
|
||||
CellLength = 265, // TODO add support
|
||||
CellWidth = 264, // TODO add support
|
||||
// palette-color images (PhotometricInterpretation 3)
|
||||
ColorMap = 320, // TODO add support
|
||||
Compression = 259, // TODO add support for 2 and 32773
|
||||
Copyright = 33_432,
|
||||
DateTime = 306,
|
||||
ExtraSamples = 338, // TODO add support
|
||||
FillOrder = 266, // TODO add support
|
||||
FreeByteCounts = 289, // TODO add support
|
||||
FreeOffsets = 288, // TODO add support
|
||||
GrayResponseCurve = 291, // TODO add support
|
||||
GrayResponseUnit = 290, // TODO add support
|
||||
HostComputer = 316,
|
||||
ImageDescription = 270,
|
||||
ImageLength = 257,
|
||||
ImageWidth = 256,
|
||||
Make = 271,
|
||||
MaxSampleValue = 281, // TODO add support
|
||||
MinSampleValue = 280, // TODO add support
|
||||
Model = 272,
|
||||
NewSubfileType = 254, // TODO add support
|
||||
Orientation = 274, // TODO add support
|
||||
PhotometricInterpretation = 262,
|
||||
PlanarConfiguration = 284,
|
||||
ResolutionUnit = 296, // TODO add support
|
||||
RowsPerStrip = 278,
|
||||
SamplesPerPixel = 277,
|
||||
Software = 305,
|
||||
StripByteCounts = 279,
|
||||
StripOffsets = 273,
|
||||
SubfileType = 255, // TODO add support
|
||||
Threshholding = 263, // TODO add support
|
||||
XResolution = 282,
|
||||
YResolution = 283,
|
||||
// Advanced tags
|
||||
Predictor = 317,
|
||||
}
|
||||
}
|
||||
|
||||
impl Tag {
|
||||
pub fn from_u16(val: u16) -> Self {
|
||||
Self::__from_inner_type(val).unwrap_or_else(Tag::Unknown)
|
||||
}
|
||||
|
||||
pub fn to_u16(&self) -> u16 {
|
||||
Self::__to_inner_type(self)
|
||||
}
|
||||
}
|
||||
|
||||
tags! {
|
||||
/// The type of an IFD entry (a 2 byte field).
|
||||
pub enum Type(u16) {
|
||||
BYTE = 1,
|
||||
ASCII = 2,
|
||||
SHORT = 3,
|
||||
LONG = 4,
|
||||
RATIONAL = 5,
|
||||
SBYTE = 6,
|
||||
SSHORT = 8,
|
||||
SLONG = 9,
|
||||
SRATIONAL = 10,
|
||||
}
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn from_u16(val: u16) -> Option<Self> {
|
||||
Self::__from_inner_type(val).ok()
|
||||
}
|
||||
|
||||
pub fn to_u16(&self) -> u16 {
|
||||
Self::__to_inner_type(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[allow(unused_qualifications)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
@@ -179,7 +55,7 @@ impl Value {
|
||||
}
|
||||
Unsigned(val) => Ok(vec![val]),
|
||||
Rational(numerator, denominator) => Ok(vec![numerator, denominator]),
|
||||
Ascii(val) => Ok(val.chars().map(|x| x as u32).collect()),
|
||||
Ascii(val) => Ok(val.chars().map(u32::from).collect()),
|
||||
val => Err(TiffError::FormatError(
|
||||
TiffFormatError::UnsignedIntegerExpected(val),
|
||||
)),
|
||||
@@ -318,11 +194,12 @@ impl Entry {
|
||||
))
|
||||
}),
|
||||
(Type::ASCII, n) => {
|
||||
if n as usize > limits.decoding_buffer_size {
|
||||
let n = usize::try_from(n)?;
|
||||
if n > limits.decoding_buffer_size {
|
||||
return Err(TiffError::LimitsExceeded);
|
||||
}
|
||||
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
||||
let string = decoder.read_string(n as usize)?;
|
||||
let string = decoder.read_string(n)?;
|
||||
Ok(Ascii(string))
|
||||
}
|
||||
_ => Err(TiffError::UnsupportedError(
|
||||
@@ -337,11 +214,12 @@ impl Entry {
|
||||
R: Read + Seek,
|
||||
F: Fn(&mut super::Decoder<R>) -> TiffResult<Value>,
|
||||
{
|
||||
if value_count as usize > limits.decoding_buffer_size / mem::size_of::<Value>() {
|
||||
let value_count = usize::try_from(value_count)?;
|
||||
if value_count > limits.decoding_buffer_size / mem::size_of::<Value>() {
|
||||
return Err(TiffError::LimitsExceeded);
|
||||
}
|
||||
|
||||
let mut v = Vec::with_capacity(value_count as usize);
|
||||
let mut v = Vec::with_capacity(value_count);
|
||||
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
||||
for _ in 0..value_count {
|
||||
v.push(decode_fn(decoder)?)
|
||||
|
||||
+207
-215
@@ -1,3 +1,4 @@
|
||||
use std::convert::TryFrom;
|
||||
use std::collections::HashMap;
|
||||
use std::io::{self, Read, Seek};
|
||||
use std::cmp;
|
||||
@@ -5,10 +6,10 @@ use std::cmp;
|
||||
use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
||||
|
||||
use self::ifd::Directory;
|
||||
use tags::{CompressionMethod, PhotometricInterpretation, Predictor, Tag, Type};
|
||||
|
||||
use self::stream::{ByteOrder, EndianReader, LZWReader, DeflateReader, PackBitsReader, SmartReader};
|
||||
|
||||
#[macro_use]
|
||||
pub mod ifd;
|
||||
mod stream;
|
||||
|
||||
@@ -19,6 +20,10 @@ pub enum DecodingResult {
|
||||
U8(Vec<u8>),
|
||||
/// A vector of unsigned words
|
||||
U16(Vec<u16>),
|
||||
/// A vector of 32 bit unsigned ints
|
||||
U32(Vec<u32>),
|
||||
/// A vector of 64 bit unsigned ints
|
||||
U64(Vec<u64>),
|
||||
}
|
||||
|
||||
impl DecodingResult {
|
||||
@@ -38,10 +43,28 @@ impl DecodingResult {
|
||||
}
|
||||
}
|
||||
|
||||
fn new_u32(size: usize, limits: &Limits) -> TiffResult<DecodingResult> {
|
||||
if size > limits.decoding_buffer_size / 4 {
|
||||
Err(TiffError::LimitsExceeded)
|
||||
} else {
|
||||
Ok(DecodingResult::U32(vec![0; size]))
|
||||
}
|
||||
}
|
||||
|
||||
fn new_u64(size: usize, limits: &Limits) -> TiffResult<DecodingResult> {
|
||||
if size > limits.decoding_buffer_size / 8 {
|
||||
Err(TiffError::LimitsExceeded)
|
||||
} else {
|
||||
Ok(DecodingResult::U64(vec![0; size]))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_buffer(&mut self, start: usize) -> DecodingBuffer {
|
||||
match *self {
|
||||
DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]),
|
||||
DecodingResult::U16(ref mut buf) => DecodingBuffer::U16(&mut buf[start..]),
|
||||
DecodingResult::U32(ref mut buf) => DecodingBuffer::U32(&mut buf[start..]),
|
||||
DecodingResult::U64(ref mut buf) => DecodingBuffer::U64(&mut buf[start..]),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,6 +75,10 @@ pub enum DecodingBuffer<'a> {
|
||||
U8(&'a mut [u8]),
|
||||
/// A slice of unsigned words
|
||||
U16(&'a mut [u16]),
|
||||
/// A slice of 32 bit unsigned ints
|
||||
U32(&'a mut [u32]),
|
||||
/// A slice of 64 bit unsigned ints
|
||||
U64(&'a mut [u64]),
|
||||
}
|
||||
|
||||
impl<'a> DecodingBuffer<'a> {
|
||||
@@ -59,6 +86,8 @@ impl<'a> DecodingBuffer<'a> {
|
||||
match *self {
|
||||
DecodingBuffer::U8(ref buf) => buf.len(),
|
||||
DecodingBuffer::U16(ref buf) => buf.len(),
|
||||
DecodingBuffer::U32(ref buf) => buf.len(),
|
||||
DecodingBuffer::U64(ref buf) => buf.len(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +95,8 @@ impl<'a> DecodingBuffer<'a> {
|
||||
match *self {
|
||||
DecodingBuffer::U8(_) => 1,
|
||||
DecodingBuffer::U16(_) => 2,
|
||||
DecodingBuffer::U32(_) => 4,
|
||||
DecodingBuffer::U64(_) => 8,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,91 +107,12 @@ impl<'a> DecodingBuffer<'a> {
|
||||
match *self {
|
||||
DecodingBuffer::U8(ref mut buf) => DecodingBuffer::U8(buf),
|
||||
DecodingBuffer::U16(ref mut buf) => DecodingBuffer::U16(buf),
|
||||
DecodingBuffer::U32(ref mut buf) => DecodingBuffer::U32(buf),
|
||||
DecodingBuffer::U64(ref mut buf) => DecodingBuffer::U64(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tags! {
|
||||
pub enum PhotometricInterpretation(u16) {
|
||||
WhiteIsZero = 0,
|
||||
BlackIsZero = 1,
|
||||
RGB = 2,
|
||||
RGBPalette = 3,
|
||||
TransparencyMask = 4,
|
||||
CMYK = 5,
|
||||
YCbCr = 6,
|
||||
CIELab = 8,
|
||||
}
|
||||
}
|
||||
|
||||
tags! {
|
||||
pub enum CompressionMethod(u16) {
|
||||
None = 1,
|
||||
Huffman = 2,
|
||||
Fax3 = 3,
|
||||
Fax4 = 4,
|
||||
LZW = 5,
|
||||
JPEG = 6,
|
||||
Deflate = 8,
|
||||
OldDeflate = 0x80B2,
|
||||
PackBits = 0x8005,
|
||||
}
|
||||
}
|
||||
|
||||
tags! {
|
||||
pub enum PlanarConfiguration(u16) {
|
||||
Chunky = 1,
|
||||
Planar = 2,
|
||||
}
|
||||
}
|
||||
|
||||
tags! {
|
||||
enum Predictor(u16) {
|
||||
None = 1,
|
||||
Horizontal = 2,
|
||||
}
|
||||
}
|
||||
|
||||
impl PhotometricInterpretation {
|
||||
pub fn from_u16(val: u16) -> Option<Self> {
|
||||
Self::__from_inner_type(val).ok()
|
||||
}
|
||||
|
||||
pub fn to_u16(&self) -> u16 {
|
||||
Self::__to_inner_type(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl CompressionMethod {
|
||||
pub fn from_u16(val: u16) -> Option<Self> {
|
||||
Self::__from_inner_type(val).ok()
|
||||
}
|
||||
|
||||
pub fn to_u16(&self) -> u16 {
|
||||
Self::__to_inner_type(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl PlanarConfiguration {
|
||||
pub fn from_u16(val: u16) -> Option<Self> {
|
||||
Self::__from_inner_type(val).ok()
|
||||
}
|
||||
|
||||
pub fn to_u16(&self) -> u16 {
|
||||
Self::__to_inner_type(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Predictor {
|
||||
pub fn from_u16(val: u16) -> Option<Self> {
|
||||
Self::__from_inner_type(val).ok()
|
||||
}
|
||||
|
||||
pub fn to_u16(&self) -> u16 {
|
||||
Self::__to_inner_type(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct StripDecodeState {
|
||||
strip_index: usize,
|
||||
@@ -233,12 +185,24 @@ impl Wrapping for u16 {
|
||||
}
|
||||
}
|
||||
|
||||
fn rev_hpredict_nsamp<T>(image: &mut [T], size: (u32, u32), samples: usize)
|
||||
impl Wrapping for u32 {
|
||||
fn wrapping_add(&self, other: Self) -> Self {
|
||||
u32::wrapping_add(*self, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Wrapping for u64 {
|
||||
fn wrapping_add(&self, other: Self) -> Self {
|
||||
u64::wrapping_add(*self, other)
|
||||
}
|
||||
}
|
||||
|
||||
fn rev_hpredict_nsamp<T>(image: &mut [T], size: (u32, u32), samples: usize) -> TiffResult<()>
|
||||
where
|
||||
T: Copy + Wrapping,
|
||||
{
|
||||
let width = size.0 as usize;
|
||||
let height = size.1 as usize;
|
||||
let width = usize::try_from(size.0)?;
|
||||
let height = usize::try_from(size.1)?;
|
||||
for row in 0..height {
|
||||
for col in samples..width * samples {
|
||||
let prev_pixel = image[(row * width * samples + col - samples)];
|
||||
@@ -246,13 +210,15 @@ where
|
||||
*pixel = pixel.wrapping_add(prev_pixel);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType) -> TiffResult<()> {
|
||||
let samples = match color_type {
|
||||
ColorType::Gray(8) | ColorType::Gray(16) => 1,
|
||||
ColorType::RGB(8) | ColorType::RGB(16) => 3,
|
||||
ColorType::RGBA(8) | ColorType::RGBA(16) | ColorType::CMYK(8) => 4,
|
||||
ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) | ColorType::Gray(64) => 1,
|
||||
ColorType::RGB(8) | ColorType::RGB(16) | ColorType::RGB(32) | ColorType::RGB(64) => 3,
|
||||
ColorType::RGBA(8) | ColorType::RGBA(16) | ColorType::RGBA(32) | ColorType::RGBA(64)
|
||||
| ColorType::CMYK(8) | ColorType::CMYK(16) | ColorType::CMYK(32) | ColorType::CMYK(64) => 4,
|
||||
_ => {
|
||||
return Err(TiffError::UnsupportedError(
|
||||
TiffUnsupportedError::HorizontalPredictor(color_type),
|
||||
@@ -261,10 +227,16 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType)
|
||||
};
|
||||
match image {
|
||||
DecodingBuffer::U8(buf) => {
|
||||
rev_hpredict_nsamp(buf, size, samples);
|
||||
rev_hpredict_nsamp(buf, size, samples)?;
|
||||
}
|
||||
DecodingBuffer::U16(buf) => {
|
||||
rev_hpredict_nsamp(buf, size, samples);
|
||||
rev_hpredict_nsamp(buf, size, samples)?;
|
||||
}
|
||||
DecodingBuffer::U32(buf) => {
|
||||
rev_hpredict_nsamp(buf, size, samples)?;
|
||||
}
|
||||
DecodingBuffer::U64(buf) => {
|
||||
rev_hpredict_nsamp(buf, size, samples)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -301,22 +273,29 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
|
||||
pub fn colortype(&mut self) -> TiffResult<ColorType> {
|
||||
match self.photometric_interpretation {
|
||||
// TODO: catch also [ 8, 8, 8, _] this does not work due to a bug in rust atm
|
||||
PhotometricInterpretation::RGB if self.bits_per_sample == [8, 8, 8, 8] => {
|
||||
Ok(ColorType::RGBA(8))
|
||||
}
|
||||
PhotometricInterpretation::RGB if self.bits_per_sample == [8, 8, 8] => {
|
||||
Ok(ColorType::RGB(8))
|
||||
}
|
||||
PhotometricInterpretation::RGB if self.bits_per_sample == [16, 16, 16, 16] => {
|
||||
Ok(ColorType::RGBA(16))
|
||||
}
|
||||
PhotometricInterpretation::RGB if self.bits_per_sample == [16, 16, 16] => {
|
||||
Ok(ColorType::RGB(16))
|
||||
}
|
||||
PhotometricInterpretation::CMYK if self.bits_per_sample == [8, 8, 8, 8] => {
|
||||
Ok(ColorType::CMYK(8))
|
||||
}
|
||||
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)),
|
||||
_ => Err(TiffError::UnsupportedError(
|
||||
TiffUnsupportedError::InterpretationWithBits(
|
||||
self.photometric_interpretation,
|
||||
self.bits_per_sample.clone(),
|
||||
),
|
||||
)),
|
||||
}
|
||||
},
|
||||
PhotometricInterpretation::CMYK => {
|
||||
match self.bits_per_sample[..] {
|
||||
[c, m, y, k] if [c, c, c] == [m, y, k] => Ok(ColorType::CMYK(c)),
|
||||
_ => Err(TiffError::UnsupportedError(
|
||||
TiffUnsupportedError::InterpretationWithBits(
|
||||
self.photometric_interpretation,
|
||||
self.bits_per_sample.clone(),
|
||||
),
|
||||
)),
|
||||
}
|
||||
},
|
||||
PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero
|
||||
if self.bits_per_sample.len() == 1 =>
|
||||
{
|
||||
@@ -375,50 +354,27 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
/// To determine whether there are more images call `TIFFDecoder::more_images` instead.
|
||||
pub fn next_image(&mut self) -> TiffResult<()> {
|
||||
self.ifd = Some(self.read_ifd()?);
|
||||
self.width = self.get_tag_u32(ifd::Tag::ImageWidth)?;
|
||||
self.height = self.get_tag_u32(ifd::Tag::ImageLength)?;
|
||||
self.width = self.get_tag_u32(Tag::ImageWidth)?;
|
||||
self.height = self.get_tag_u32(Tag::ImageLength)?;
|
||||
self.strip_decoder = None;
|
||||
// TODO: error on non-SHORT value.
|
||||
self.photometric_interpretation = match PhotometricInterpretation::from_u16(
|
||||
self.get_tag_u32(ifd::Tag::PhotometricInterpretation)? as u16
|
||||
) {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
return Err(TiffError::UnsupportedError(
|
||||
TiffUnsupportedError::UnknownInterpretation,
|
||||
))
|
||||
}
|
||||
};
|
||||
// TODO: error on non-SHORT value.
|
||||
if let Some(val) = self.find_tag_u32(ifd::Tag::Compression)? {
|
||||
match CompressionMethod::from_u16(val as u16) {
|
||||
Some(method) => self.compression_method = method,
|
||||
None => {
|
||||
return Err(TiffError::UnsupportedError(
|
||||
TiffUnsupportedError::UnknownCompressionMethod,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
self.photometric_interpretation = self
|
||||
.find_tag_unsigned(Tag::PhotometricInterpretation)?
|
||||
.and_then(PhotometricInterpretation::from_u16)
|
||||
.ok_or(TiffUnsupportedError::UnknownInterpretation)?;
|
||||
|
||||
if let Some(val) = self.find_tag_unsigned(Tag::Compression)? {
|
||||
self.compression_method = CompressionMethod::from_u16(val)
|
||||
.ok_or(TiffUnsupportedError::UnknownCompressionMethod)?;
|
||||
}
|
||||
if let Some(val) = self.find_tag_u32(ifd::Tag::SamplesPerPixel)? {
|
||||
self.samples = val as u8
|
||||
if let Some(val) = self.find_tag_unsigned(Tag::SamplesPerPixel)? {
|
||||
self.samples = val;
|
||||
}
|
||||
match self.samples {
|
||||
1 => {
|
||||
if let Some(val) = self.find_tag_u32(ifd::Tag::BitsPerSample)? {
|
||||
self.bits_per_sample = vec![val as u8]
|
||||
}
|
||||
}
|
||||
3 | 4 => {
|
||||
if let Some(val) = self.find_tag_u32_vec(ifd::Tag::BitsPerSample)? {
|
||||
self.bits_per_sample = val.iter().map(|&v| v as u8).collect()
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(TiffError::UnsupportedError(
|
||||
TiffUnsupportedError::UnsupportedSampleDepth(self.samples),
|
||||
))
|
||||
}
|
||||
1 | 3 | 4 => if let Some(val) = self.find_tag_unsigned_vec(Tag::BitsPerSample)? {
|
||||
self.bits_per_sample = val;
|
||||
},
|
||||
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -498,9 +454,9 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
// Type 2 bytes
|
||||
// Count 4 bytes
|
||||
// Value 4 bytes either a pointer the value itself
|
||||
fn read_entry(&mut self) -> TiffResult<Option<(ifd::Tag, ifd::Entry)>> {
|
||||
let tag = ifd::Tag::from_u16(self.read_short()?);
|
||||
let type_ = match ifd::Type::from_u16(self.read_short()?) {
|
||||
fn read_entry(&mut self) -> TiffResult<Option<(Tag, ifd::Entry)>> {
|
||||
let tag = Tag::from_u16_exhaustive(self.read_short()?);
|
||||
let type_ = match Type::from_u16(self.read_short()?) {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
// Unknown type. Skip this entry according to spec.
|
||||
@@ -546,7 +502,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
|
||||
/// Tries to retrieve a tag.
|
||||
/// Return `Ok(None)` if the tag is not present.
|
||||
pub fn find_tag(&mut self, tag: ifd::Tag) -> TiffResult<Option<ifd::Value>> {
|
||||
pub fn find_tag(&mut self, tag: Tag) -> TiffResult<Option<ifd::Value>> {
|
||||
let entry = match self.ifd.as_ref().unwrap().get(&tag) {
|
||||
None => return Ok(None),
|
||||
Some(entry) => entry.clone(),
|
||||
@@ -557,25 +513,36 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
Ok(Some(entry.val(&limits, self)?))
|
||||
}
|
||||
|
||||
/// Tries to retrieve a tag and convert it to the desired type.
|
||||
pub fn find_tag_u32(&mut self, tag: ifd::Tag) -> TiffResult<Option<u32>> {
|
||||
match self.find_tag(tag)? {
|
||||
Some(val) => val.into_u32().map(Some),
|
||||
None => Ok(None),
|
||||
}
|
||||
/// Tries to retrieve a tag and convert it to the desired unsigned type.
|
||||
pub fn find_tag_unsigned<T: TryFrom<u32>>(&mut self, tag: Tag) -> TiffResult<Option<T>> {
|
||||
self.find_tag(tag)?
|
||||
.map(|v| v.into_u32()).transpose()?
|
||||
.map(|value| T::try_from(value)
|
||||
.map_err(|_| TiffFormatError::InvalidTagValueType(tag).into()))
|
||||
.transpose()
|
||||
}
|
||||
|
||||
/// Tries to retrieve a tag and convert it to the desired type.
|
||||
pub fn find_tag_u32_vec(&mut self, tag: ifd::Tag) -> TiffResult<Option<Vec<u32>>> {
|
||||
match self.find_tag(tag)? {
|
||||
Some(val) => val.into_u32_vec().map(Some),
|
||||
None => Ok(None),
|
||||
}
|
||||
/// Tries to retrieve a vector of all a tag's values and convert them to
|
||||
/// the desired unsigned type.
|
||||
pub fn find_tag_unsigned_vec<T: TryFrom<u32>>(&mut self, tag: Tag) -> TiffResult<Option<Vec<T>>> {
|
||||
self.find_tag(tag)?
|
||||
.map(|v| v.into_u32_vec()).transpose()?
|
||||
.map(|v| v.into_iter()
|
||||
.map(|u| T::try_from(u).map_err(|_| TiffFormatError::InvalidTagValueType(tag).into()))
|
||||
.collect())
|
||||
.transpose()
|
||||
}
|
||||
|
||||
/// Tries to retrieve a tag and convert it to the desired unsigned type.
|
||||
/// Returns an error if the tag is not present.
|
||||
pub fn get_tag_unsigned<T: TryFrom<u32>>(&mut self, tag: Tag) -> TiffResult<T> {
|
||||
self.find_tag_unsigned(tag)?
|
||||
.ok_or_else(|| TiffFormatError::RequiredTagNotFound(tag).into())
|
||||
}
|
||||
|
||||
/// Tries to retrieve a tag.
|
||||
/// Returns an error if the tag is not present
|
||||
pub fn get_tag(&mut self, tag: ifd::Tag) -> TiffResult<ifd::Value> {
|
||||
pub fn get_tag(&mut self, tag: Tag) -> TiffResult<ifd::Value> {
|
||||
match self.find_tag(tag)? {
|
||||
Some(val) => Ok(val),
|
||||
None => Err(TiffError::FormatError(
|
||||
@@ -585,12 +552,12 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
}
|
||||
|
||||
/// Tries to retrieve a tag and convert it to the desired type.
|
||||
pub fn get_tag_u32(&mut self, tag: ifd::Tag) -> TiffResult<u32> {
|
||||
pub fn get_tag_u32(&mut self, tag: Tag) -> TiffResult<u32> {
|
||||
self.get_tag(tag)?.into_u32()
|
||||
}
|
||||
|
||||
/// Tries to retrieve a tag and convert it to the desired type.
|
||||
pub fn get_tag_u32_vec(&mut self, tag: ifd::Tag) -> TiffResult<Vec<u32>> {
|
||||
pub fn get_tag_u32_vec(&mut self, tag: Tag) -> TiffResult<Vec<u32>> {
|
||||
self.get_tag(tag)?.into_u32_vec()
|
||||
}
|
||||
|
||||
@@ -609,14 +576,14 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
CompressionMethod::None => {
|
||||
let order = self.reader.byte_order;
|
||||
(
|
||||
length as usize,
|
||||
usize::try_from(length)?,
|
||||
Box::new(SmartReader::wrap(&mut self.reader, order)),
|
||||
)
|
||||
}
|
||||
CompressionMethod::LZW => {
|
||||
let (bytes, reader) = LZWReader::new(
|
||||
&mut self.reader,
|
||||
length as usize,
|
||||
usize::try_from(length)?,
|
||||
max_uncompressed_length
|
||||
)?;
|
||||
(bytes, Box::new(reader))
|
||||
@@ -626,7 +593,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
let (bytes, reader) = PackBitsReader::new(
|
||||
&mut self.reader,
|
||||
order,
|
||||
length as usize
|
||||
usize::try_from(length)?,
|
||||
)?;
|
||||
(bytes, Box::new(reader))
|
||||
}
|
||||
@@ -655,10 +622,41 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
bytes
|
||||
}
|
||||
(ColorType::RGBA(16), DecodingBuffer::U16(ref mut buffer))
|
||||
| (ColorType::RGB(16), DecodingBuffer::U16(ref mut buffer)) => {
|
||||
| (ColorType::RGB(16), DecodingBuffer::U16(ref mut buffer))
|
||||
| (ColorType::CMYK(16), DecodingBuffer::U16(ref mut buffer)) => {
|
||||
reader.read_u16_into(&mut buffer[..bytes / 2])?;
|
||||
bytes / 2
|
||||
}
|
||||
(ColorType::RGBA(32), DecodingBuffer::U32(ref mut buffer))
|
||||
| (ColorType::RGB(32), DecodingBuffer::U32(ref mut buffer))
|
||||
| (ColorType::CMYK(32), DecodingBuffer::U32(ref mut buffer)) => {
|
||||
reader.read_u32_into(&mut buffer[..bytes / 4])?;
|
||||
bytes / 4
|
||||
}
|
||||
(ColorType::RGBA(64), DecodingBuffer::U64(ref mut buffer))
|
||||
| (ColorType::RGB(64), DecodingBuffer::U64(ref mut buffer))
|
||||
| (ColorType::CMYK(64), DecodingBuffer::U64(ref mut buffer)) => {
|
||||
reader.read_u64_into(&mut buffer[..bytes / 8])?;
|
||||
bytes / 8
|
||||
}
|
||||
(ColorType::Gray(64), DecodingBuffer::U64(ref mut buffer)) => {
|
||||
reader.read_u64_into(&mut buffer[..bytes / 8])?;
|
||||
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
||||
for datum in buffer[..bytes / 8].iter_mut() {
|
||||
*datum = 0xffff_ffff_ffff_ffff - *datum
|
||||
}
|
||||
}
|
||||
bytes / 8
|
||||
}
|
||||
(ColorType::Gray(32), DecodingBuffer::U32(ref mut buffer)) => {
|
||||
reader.read_u32_into(&mut buffer[..bytes / 4])?;
|
||||
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
||||
for datum in buffer[..bytes / 4].iter_mut() {
|
||||
*datum = 0xffff_ffff - *datum
|
||||
}
|
||||
}
|
||||
bytes / 4
|
||||
}
|
||||
(ColorType::Gray(16), DecodingBuffer::U16(ref mut buffer)) => {
|
||||
reader.read_u16_into(&mut buffer[..bytes / 2])?;
|
||||
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
||||
@@ -688,7 +686,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
/// Number of strips in image
|
||||
pub fn strip_count(&mut self) -> TiffResult<u32> {
|
||||
let rows_per_strip = self
|
||||
.get_tag_u32(ifd::Tag::RowsPerStrip)
|
||||
.get_tag_u32(Tag::RowsPerStrip)
|
||||
.unwrap_or(self.height);
|
||||
|
||||
if rows_per_strip == 0 {
|
||||
@@ -700,8 +698,8 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
|
||||
fn initialize_strip_decoder(&mut self) -> TiffResult<()> {
|
||||
if self.strip_decoder.is_none() {
|
||||
let strip_offsets = self.get_tag_u32_vec(ifd::Tag::StripOffsets)?;
|
||||
let strip_bytes = self.get_tag_u32_vec(ifd::Tag::StripByteCounts)?;
|
||||
let strip_offsets = self.get_tag_u32_vec(Tag::StripOffsets)?;
|
||||
let strip_bytes = self.get_tag_u32_vec(Tag::StripByteCounts)?;
|
||||
|
||||
self.strip_decoder = Some(StripDecodeState {
|
||||
strip_index: 0,
|
||||
@@ -735,18 +733,18 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
TiffFormatError::InconsistentSizesEncountered,
|
||||
))?;
|
||||
|
||||
let rows_per_strip = self
|
||||
.get_tag_u32(ifd::Tag::RowsPerStrip)
|
||||
.unwrap_or(self.height) as usize;
|
||||
let rows_per_strip = usize::try_from(self
|
||||
.get_tag_u32(Tag::RowsPerStrip)
|
||||
.unwrap_or(self.height))?;
|
||||
|
||||
let strip_height = cmp::min(
|
||||
rows_per_strip,
|
||||
self.height as usize - index * rows_per_strip,
|
||||
usize::try_from(self.height)? - index * rows_per_strip,
|
||||
);
|
||||
|
||||
let buffer_size = self.width as usize * strip_height * self.bits_per_sample.len();
|
||||
let buffer_size = usize::try_from(self.width)? * strip_height * self.bits_per_sample.len();
|
||||
|
||||
if buffer.len() < buffer_size || byte_count as usize / buffer.byte_len() > buffer_size {
|
||||
if buffer.len() < buffer_size || usize::try_from(byte_count)? / buffer.byte_len() > buffer_size {
|
||||
return Err(TiffError::FormatError(
|
||||
TiffFormatError::InconsistentSizesEncountered,
|
||||
));
|
||||
@@ -756,7 +754,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
|
||||
self.strip_decoder.as_mut().unwrap().strip_index += 1;
|
||||
|
||||
if index as u32 == self.strip_count()? {
|
||||
if u32::try_from(index)? == self.strip_count()? {
|
||||
self.strip_decoder = None;
|
||||
}
|
||||
|
||||
@@ -765,14 +763,13 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
TiffFormatError::InconsistentSizesEncountered,
|
||||
));
|
||||
}
|
||||
if let Ok(predictor) = self.get_tag_u32(ifd::Tag::Predictor) {
|
||||
// TODO: error on non-SHORT value.
|
||||
match Predictor::from_u16(predictor as u16) {
|
||||
if let Ok(predictor) = self.get_tag_unsigned(Tag::Predictor) {
|
||||
match Predictor::from_u16(predictor) {
|
||||
Some(Predictor::None) => (),
|
||||
Some(Predictor::Horizontal) => {
|
||||
rev_hpredict(
|
||||
buffer.copy(),
|
||||
(self.width, strip_height as u32),
|
||||
(self.width, u32::try_from(strip_height)?),
|
||||
self.colortype()?,
|
||||
)?;
|
||||
}
|
||||
@@ -787,31 +784,37 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn result_buffer(&self, height: usize) -> TiffResult<DecodingResult> {
|
||||
let buffer_size = usize::try_from(self.width)? * height * self.bits_per_sample.len();
|
||||
|
||||
match self.bits_per_sample.iter().cloned().max().unwrap_or(8) {
|
||||
n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits),
|
||||
n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits),
|
||||
n if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits),
|
||||
n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits),
|
||||
n => {
|
||||
Err(TiffError::UnsupportedError(
|
||||
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a single strip from the image and return it as a Vector
|
||||
pub fn read_strip(&mut self) -> TiffResult<DecodingResult> {
|
||||
self.initialize_strip_decoder()?;
|
||||
let index = self.strip_decoder.as_ref().unwrap().strip_index;
|
||||
|
||||
let rows_per_strip = self
|
||||
.get_tag_u32(ifd::Tag::RowsPerStrip)
|
||||
.unwrap_or(self.height) as usize;
|
||||
let rows_per_strip = usize::try_from(self
|
||||
.get_tag_u32(Tag::RowsPerStrip)
|
||||
.unwrap_or(self.height))?;
|
||||
|
||||
let strip_height = cmp::min(
|
||||
rows_per_strip,
|
||||
self.height as usize - index * rows_per_strip,
|
||||
usize::try_from(self.height)? - index * rows_per_strip,
|
||||
);
|
||||
|
||||
let buffer_size = self.width as usize * strip_height * self.bits_per_sample.iter().count();
|
||||
|
||||
let mut result = match self.bits_per_sample.iter().cloned().max().unwrap_or(8) {
|
||||
n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits)?,
|
||||
n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits)?,
|
||||
n => {
|
||||
return Err(TiffError::UnsupportedError(
|
||||
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
||||
))
|
||||
}
|
||||
};
|
||||
let mut result = self.result_buffer(strip_height)?;
|
||||
|
||||
self.read_strip_to_buffer(result.as_buffer(0))?;
|
||||
|
||||
@@ -821,27 +824,16 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
/// Decodes the entire image and return it as a Vector
|
||||
pub fn read_image(&mut self) -> TiffResult<DecodingResult> {
|
||||
self.initialize_strip_decoder()?;
|
||||
let rows_per_strip = self
|
||||
.get_tag_u32(ifd::Tag::RowsPerStrip)
|
||||
.unwrap_or(self.height) as usize;
|
||||
let rows_per_strip = usize::try_from(self
|
||||
.get_tag_u32(Tag::RowsPerStrip)
|
||||
.unwrap_or(self.height))?;
|
||||
|
||||
let samples_per_strip =
|
||||
self.width as usize * rows_per_strip * self.bits_per_sample.iter().count();
|
||||
usize::try_from(self.width)? * rows_per_strip * self.bits_per_sample.len();
|
||||
|
||||
let buffer_size =
|
||||
self.width as usize * self.height as usize * self.bits_per_sample.iter().count();
|
||||
let mut result = self.result_buffer(usize::try_from(self.height)?)?;
|
||||
|
||||
let mut result = match self.bits_per_sample.iter().cloned().max().unwrap_or(8) {
|
||||
n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits)?,
|
||||
n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits)?,
|
||||
n => {
|
||||
return Err(TiffError::UnsupportedError(
|
||||
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
for i in 0..self.strip_count()? as usize {
|
||||
for i in 0..usize::try_from(self.strip_count()?)? {
|
||||
self.read_strip_to_buffer(result.as_buffer(samples_per_strip * i))?;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,39 @@ pub trait EndianReader: Read {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_u32_into(&mut self, buffer: &mut [u32]) -> Result<(), io::Error> {
|
||||
match self.byte_order() {
|
||||
ByteOrder::LittleEndian => {
|
||||
<Self as ReadBytesExt>::read_u32_into::<LittleEndian>(self, buffer)
|
||||
}
|
||||
ByteOrder::BigEndian => {
|
||||
<Self as ReadBytesExt>::read_u32_into::<BigEndian>(self, buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads an u64
|
||||
#[inline(always)]
|
||||
fn read_u64(&mut self) -> Result<u64, io::Error> {
|
||||
match self.byte_order() {
|
||||
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_u64::<LittleEndian>(self),
|
||||
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_u64::<BigEndian>(self),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_u64_into(&mut self, buffer: &mut [u64]) -> Result<(), io::Error> {
|
||||
match self.byte_order() {
|
||||
ByteOrder::LittleEndian => {
|
||||
<Self as ReadBytesExt>::read_u64_into::<LittleEndian>(self, buffer)
|
||||
}
|
||||
ByteOrder::BigEndian => {
|
||||
<Self as ReadBytesExt>::read_u64_into::<BigEndian>(self, buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads an i32
|
||||
#[inline(always)]
|
||||
fn read_i32(&mut self) -> Result<i32, io::Error> {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use decoder::PhotometricInterpretation;
|
||||
use tags::PhotometricInterpretation;
|
||||
|
||||
/// Trait for different colortypes that can be encoded.
|
||||
pub trait ColorType {
|
||||
@@ -24,6 +24,20 @@ impl ColorType for Gray16 {
|
||||
const BITS_PER_SAMPLE: &'static [u16] = &[16];
|
||||
}
|
||||
|
||||
pub struct Gray32;
|
||||
impl ColorType for Gray32 {
|
||||
type Inner = u32;
|
||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||
const BITS_PER_SAMPLE: &'static [u16] = &[32];
|
||||
}
|
||||
|
||||
pub struct Gray64;
|
||||
impl ColorType for Gray64 {
|
||||
type Inner = u64;
|
||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||
const BITS_PER_SAMPLE: &'static [u16] = &[64];
|
||||
}
|
||||
|
||||
pub struct RGB8;
|
||||
impl ColorType for RGB8 {
|
||||
type Inner = u8;
|
||||
@@ -38,6 +52,20 @@ impl ColorType for RGB16 {
|
||||
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16];
|
||||
}
|
||||
|
||||
pub struct RGB32;
|
||||
impl ColorType for RGB32 {
|
||||
type Inner = u32;
|
||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32];
|
||||
}
|
||||
|
||||
pub struct RGB64;
|
||||
impl ColorType for RGB64 {
|
||||
type Inner = u64;
|
||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64];
|
||||
}
|
||||
|
||||
pub struct RGBA8;
|
||||
impl ColorType for RGBA8 {
|
||||
type Inner = u8;
|
||||
@@ -52,9 +80,44 @@ impl ColorType for RGBA16 {
|
||||
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16];
|
||||
}
|
||||
|
||||
pub struct RGBA32;
|
||||
impl ColorType for RGBA32 {
|
||||
type Inner = u16;
|
||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
|
||||
}
|
||||
|
||||
pub struct RGBA64;
|
||||
impl ColorType for RGBA64 {
|
||||
type Inner = u16;
|
||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
|
||||
}
|
||||
|
||||
pub struct CMYK8;
|
||||
impl ColorType for CMYK8 {
|
||||
type Inner = u8;
|
||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8];
|
||||
}
|
||||
|
||||
pub struct CMYK16;
|
||||
impl ColorType for CMYK16 {
|
||||
type Inner = u16;
|
||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16];
|
||||
}
|
||||
|
||||
pub struct CMYK32;
|
||||
impl ColorType for CMYK32 {
|
||||
type Inner = u32;
|
||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
|
||||
}
|
||||
|
||||
pub struct CMYK64;
|
||||
impl ColorType for CMYK64 {
|
||||
type Inner = u64;
|
||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
|
||||
}
|
||||
+86
-61
@@ -1,9 +1,10 @@
|
||||
use byteorder::NativeEndian;
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::io::{Seek, Write};
|
||||
use std::mem;
|
||||
|
||||
use decoder::ifd::{self, Tag};
|
||||
use tags::{self, ResolutionUnit, Tag, Type};
|
||||
use error::{TiffError, TiffFormatError, TiffResult};
|
||||
|
||||
pub mod colortype;
|
||||
@@ -25,17 +26,10 @@ pub struct SRational {
|
||||
pub d: i32,
|
||||
}
|
||||
|
||||
/// Type to represent resolution units
|
||||
pub enum ResolutionUnit {
|
||||
None = 1,
|
||||
Inch = 2,
|
||||
Centimeter = 3,
|
||||
}
|
||||
|
||||
/// Trait for types that can be encoded in a tiff file
|
||||
pub trait TiffValue {
|
||||
const BYTE_LEN: u32;
|
||||
const FIELD_TYPE: ifd::Type;
|
||||
const FIELD_TYPE: Type;
|
||||
fn count(&self) -> u32;
|
||||
fn bytes(&self) -> u32 {
|
||||
self.count() * Self::BYTE_LEN
|
||||
@@ -45,7 +39,7 @@ pub trait TiffValue {
|
||||
|
||||
impl TiffValue for [u8] {
|
||||
const BYTE_LEN: u32 = 1;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::BYTE;
|
||||
const FIELD_TYPE: Type = Type::BYTE;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
self.len() as u32
|
||||
@@ -59,7 +53,7 @@ impl TiffValue for [u8] {
|
||||
|
||||
impl TiffValue for [i8] {
|
||||
const BYTE_LEN: u32 = 1;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::SBYTE;
|
||||
const FIELD_TYPE: Type = Type::SBYTE;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
self.len() as u32
|
||||
@@ -76,7 +70,7 @@ impl TiffValue for [i8] {
|
||||
|
||||
impl TiffValue for [u16] {
|
||||
const BYTE_LEN: u32 = 2;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::SHORT;
|
||||
const FIELD_TYPE: Type = Type::SHORT;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
self.len() as u32
|
||||
@@ -93,7 +87,7 @@ impl TiffValue for [u16] {
|
||||
|
||||
impl TiffValue for [i16] {
|
||||
const BYTE_LEN: u32 = 2;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::SSHORT;
|
||||
const FIELD_TYPE: Type = Type::SSHORT;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
self.len() as u32
|
||||
@@ -110,7 +104,7 @@ impl TiffValue for [i16] {
|
||||
|
||||
impl TiffValue for [u32] {
|
||||
const BYTE_LEN: u32 = 4;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::LONG;
|
||||
const FIELD_TYPE: Type = Type::LONG;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
self.len() as u32
|
||||
@@ -127,7 +121,7 @@ impl TiffValue for [u32] {
|
||||
|
||||
impl TiffValue for [i32] {
|
||||
const BYTE_LEN: u32 = 4;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::SLONG;
|
||||
const FIELD_TYPE: Type = Type::SLONG;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
self.len() as u32
|
||||
@@ -142,9 +136,26 @@ impl TiffValue for [i32] {
|
||||
}
|
||||
}
|
||||
|
||||
impl TiffValue for [u64] {
|
||||
const BYTE_LEN: u32 = 8;
|
||||
const FIELD_TYPE: Type = Type::LONG8;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
self.len() as u32
|
||||
}
|
||||
|
||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||
// We write using nativeedian so this sould be safe
|
||||
let slice =
|
||||
unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 8) };
|
||||
writer.write_bytes(slice)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TiffValue for [Rational] {
|
||||
const BYTE_LEN: u32 = 8;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL;
|
||||
const FIELD_TYPE: Type = Type::RATIONAL;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
self.len() as u32
|
||||
@@ -160,7 +171,7 @@ impl TiffValue for [Rational] {
|
||||
|
||||
impl TiffValue for [SRational] {
|
||||
const BYTE_LEN: u32 = 8;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::SRATIONAL;
|
||||
const FIELD_TYPE: Type = Type::SRATIONAL;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
self.len() as u32
|
||||
@@ -176,7 +187,7 @@ impl TiffValue for [SRational] {
|
||||
|
||||
impl TiffValue for u8 {
|
||||
const BYTE_LEN: u32 = 1;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::BYTE;
|
||||
const FIELD_TYPE: Type = Type::BYTE;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
1
|
||||
@@ -190,7 +201,7 @@ impl TiffValue for u8 {
|
||||
|
||||
impl TiffValue for i8 {
|
||||
const BYTE_LEN: u32 = 1;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::SBYTE;
|
||||
const FIELD_TYPE: Type = Type::SBYTE;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
1
|
||||
@@ -204,7 +215,7 @@ impl TiffValue for i8 {
|
||||
|
||||
impl TiffValue for u16 {
|
||||
const BYTE_LEN: u32 = 2;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::SHORT;
|
||||
const FIELD_TYPE: Type = Type::SHORT;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
1
|
||||
@@ -218,7 +229,7 @@ impl TiffValue for u16 {
|
||||
|
||||
impl TiffValue for i16 {
|
||||
const BYTE_LEN: u32 = 2;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::SSHORT;
|
||||
const FIELD_TYPE: Type = Type::SSHORT;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
1
|
||||
@@ -232,7 +243,7 @@ impl TiffValue for i16 {
|
||||
|
||||
impl TiffValue for u32 {
|
||||
const BYTE_LEN: u32 = 4;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::LONG;
|
||||
const FIELD_TYPE: Type = Type::LONG;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
1
|
||||
@@ -246,7 +257,7 @@ impl TiffValue for u32 {
|
||||
|
||||
impl TiffValue for i32 {
|
||||
const BYTE_LEN: u32 = 4;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::SLONG;
|
||||
const FIELD_TYPE: Type = Type::SLONG;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
1
|
||||
@@ -258,9 +269,23 @@ impl TiffValue for i32 {
|
||||
}
|
||||
}
|
||||
|
||||
impl TiffValue for u64 {
|
||||
const BYTE_LEN: u32 = 8;
|
||||
const FIELD_TYPE: Type = Type::LONG8;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||
writer.write_u64(*self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TiffValue for Rational {
|
||||
const BYTE_LEN: u32 = 8;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL;
|
||||
const FIELD_TYPE: Type = Type::RATIONAL;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
1
|
||||
@@ -275,7 +300,7 @@ impl TiffValue for Rational {
|
||||
|
||||
impl TiffValue for SRational {
|
||||
const BYTE_LEN: u32 = 8;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::SRATIONAL;
|
||||
const FIELD_TYPE: Type = Type::SRATIONAL;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
1
|
||||
@@ -290,7 +315,7 @@ impl TiffValue for SRational {
|
||||
|
||||
impl TiffValue for str {
|
||||
const BYTE_LEN: u32 = 1;
|
||||
const FIELD_TYPE: ifd::Type = ifd::Type::ASCII;
|
||||
const FIELD_TYPE: Type = Type::ASCII;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
self.len() as u32 + 1
|
||||
@@ -309,7 +334,7 @@ impl TiffValue for str {
|
||||
|
||||
impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T {
|
||||
const BYTE_LEN: u32 = T::BYTE_LEN;
|
||||
const FIELD_TYPE: ifd::Type = T::FIELD_TYPE;
|
||||
const FIELD_TYPE: Type = T::FIELD_TYPE;
|
||||
|
||||
fn count(&self) -> u32 {
|
||||
(*self).count()
|
||||
@@ -329,10 +354,9 @@ impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T {
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # extern crate tempfile;
|
||||
/// # extern crate tiff;
|
||||
/// # fn main() {
|
||||
/// # let mut file = tempfile::tempfile().unwrap();
|
||||
/// # let mut file = std::io::Cursor::new(Vec::new());
|
||||
/// # let image_data = vec![0; 100*100*3];
|
||||
/// use tiff::encoder::*;
|
||||
///
|
||||
@@ -383,7 +407,7 @@ impl<W: Write + Seek> TiffEncoder<W> {
|
||||
where
|
||||
[C::Inner]: TiffValue,
|
||||
{
|
||||
let num_pix = (width as usize).checked_mul(height as usize)
|
||||
let num_pix = usize::try_from(width)?.checked_mul(usize::try_from(height)?)
|
||||
.ok_or_else(|| ::std::io::Error::new(
|
||||
::std::io::ErrorKind::InvalidInput,
|
||||
"Image width * height exceeds usize"))?;
|
||||
@@ -398,7 +422,7 @@ impl<W: Write + Seek> TiffEncoder<W> {
|
||||
|
||||
let mut idx = 0;
|
||||
while image.next_strip_sample_count() > 0 {
|
||||
let sample_count = image.next_strip_sample_count() as usize;
|
||||
let sample_count = usize::try_from(image.next_strip_sample_count())?;
|
||||
image.write_strip(&data[idx..idx + sample_count])?;
|
||||
idx += sample_count;
|
||||
}
|
||||
@@ -432,16 +456,18 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
||||
}
|
||||
|
||||
/// Write a single ifd tag.
|
||||
pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) {
|
||||
pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) -> TiffResult<()> {
|
||||
let len = <T>::BYTE_LEN * value.count();
|
||||
let mut bytes = Vec::with_capacity(len as usize);
|
||||
let mut bytes = Vec::with_capacity(usize::try_from(len)?);
|
||||
{
|
||||
let mut writer = TiffWriter::new(&mut bytes);
|
||||
value.write(&mut writer).unwrap();
|
||||
value.write(&mut writer)?;
|
||||
}
|
||||
|
||||
self.ifd
|
||||
.insert(tag.to_u16(), (<T>::FIELD_TYPE.to_u16(), value.count(), bytes));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_directory(&mut self) -> TiffResult<u64> {
|
||||
@@ -452,7 +478,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
||||
self.writer.write_bytes(bytes)?;
|
||||
*bytes = vec![0, 0, 0, 0];
|
||||
let mut writer = TiffWriter::new(bytes as &mut [u8]);
|
||||
writer.write_u32(offset as u32)?;
|
||||
writer.write_u32(u32::try_from(offset)?)?;
|
||||
} else {
|
||||
while bytes.len() < 4 {
|
||||
bytes.push(0);
|
||||
@@ -462,7 +488,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
||||
|
||||
let offset = self.writer.offset();
|
||||
|
||||
self.writer.write_u16(self.ifd.len() as u16)?;
|
||||
self.writer.write_u16(u16::try_from(self.ifd.len())?)?;
|
||||
for (tag, &(ref field_type, ref count, ref offset)) in self.ifd.iter() {
|
||||
self.writer.write_u16(*tag)?;
|
||||
self.writer.write_u16(*field_type)?;
|
||||
@@ -487,7 +513,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
||||
let curr_pos = self.writer.offset();
|
||||
|
||||
self.writer.goto_offset(self.ifd_pointer_pos)?;
|
||||
self.writer.write_u32(ifd_pointer as u32)?;
|
||||
self.writer.write_u32(u32::try_from(ifd_pointer)?)?;
|
||||
self.writer.goto_offset(curr_pos)?;
|
||||
self.writer.write_u32(0)?;
|
||||
|
||||
@@ -517,10 +543,9 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # extern crate tempfile;
|
||||
/// # extern crate tiff;
|
||||
/// # fn main() {
|
||||
/// # let mut file = tempfile::tempfile().unwrap();
|
||||
/// # let mut file = std::io::Cursor::new(Vec::new());
|
||||
/// # let image_data = vec![0; 100*100*3];
|
||||
/// use tiff::encoder::*;
|
||||
///
|
||||
@@ -555,7 +580,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> TiffResult<ImageEncoder<'a, W, T>> {
|
||||
let row_samples = u64::from(width) * <T>::BITS_PER_SAMPLE.len() as u64;
|
||||
let row_samples = u64::from(width) * u64::try_from(<T>::BITS_PER_SAMPLE.len())?;
|
||||
let row_bytes = row_samples * u64::from(<T::Inner>::BYTE_LEN);
|
||||
|
||||
// As per tiff spec each strip should be about 8k long
|
||||
@@ -563,19 +588,19 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
||||
|
||||
let strip_count = (u64::from(height) + rows_per_strip - 1) / rows_per_strip;
|
||||
|
||||
encoder.write_tag(Tag::ImageWidth, width);
|
||||
encoder.write_tag(Tag::ImageLength, height);
|
||||
encoder.write_tag(Tag::Compression, 1u16);
|
||||
encoder.write_tag(Tag::ImageWidth, width)?;
|
||||
encoder.write_tag(Tag::ImageLength, height)?;
|
||||
encoder.write_tag(Tag::Compression, tags::CompressionMethod::None.to_u16())?;
|
||||
|
||||
encoder.write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE);
|
||||
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16());
|
||||
encoder.write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE)?;
|
||||
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16())?;
|
||||
|
||||
encoder.write_tag(Tag::RowsPerStrip, rows_per_strip as u32);
|
||||
encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?;
|
||||
|
||||
encoder.write_tag(Tag::SamplesPerPixel, <T>::BITS_PER_SAMPLE.len() as u16);
|
||||
encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 });
|
||||
encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 });
|
||||
encoder.write_tag(Tag::ResolutionUnit, 1u16);
|
||||
encoder.write_tag(Tag::SamplesPerPixel, u16::try_from(<T>::BITS_PER_SAMPLE.len())?)?;
|
||||
encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 })?;
|
||||
encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 })?;
|
||||
encoder.write_tag(Tag::ResolutionUnit, ResolutionUnit::None.to_u16())?;
|
||||
|
||||
Ok(ImageEncoder {
|
||||
encoder,
|
||||
@@ -613,15 +638,15 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
||||
{
|
||||
// TODO: Compression
|
||||
let samples = self.next_strip_sample_count();
|
||||
if value.len() as u64 != samples {
|
||||
if u64::try_from(value.len())? != samples {
|
||||
return Err(::std::io::Error::new(
|
||||
::std::io::ErrorKind::InvalidData,
|
||||
"Slice is wrong size for strip").into());
|
||||
}
|
||||
|
||||
let offset = self.encoder.write_data(value)?;
|
||||
self.strip_offsets.push(offset as u32);
|
||||
self.strip_byte_count.push(value.bytes() as u32);
|
||||
self.strip_offsets.push(u32::try_from(offset)?);
|
||||
self.strip_byte_count.push(value.bytes());
|
||||
|
||||
self.strip_idx += 1;
|
||||
Ok(())
|
||||
@@ -629,31 +654,31 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
||||
|
||||
/// Set image resolution
|
||||
pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) {
|
||||
self.encoder.write_tag(Tag::ResolutionUnit, unit as u16);
|
||||
self.encoder.write_tag(Tag::XResolution, value.clone());
|
||||
self.encoder.write_tag(Tag::YResolution, value);
|
||||
self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap();
|
||||
self.encoder.write_tag(Tag::XResolution, value.clone()).unwrap();
|
||||
self.encoder.write_tag(Tag::YResolution, value).unwrap();
|
||||
}
|
||||
|
||||
/// Set image resolution unit
|
||||
pub fn resolution_unit(&mut self, unit: ResolutionUnit) {
|
||||
self.encoder.write_tag(Tag::ResolutionUnit, unit as u16);
|
||||
self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap();
|
||||
}
|
||||
|
||||
/// Set image x-resolution
|
||||
pub fn x_resolution(&mut self, value: Rational) {
|
||||
self.encoder.write_tag(Tag::XResolution, value);
|
||||
self.encoder.write_tag(Tag::XResolution, value).unwrap();
|
||||
}
|
||||
|
||||
/// Set image y-resolution
|
||||
pub fn y_resolution(&mut self, value: Rational) {
|
||||
self.encoder.write_tag(Tag::YResolution, value);
|
||||
self.encoder.write_tag(Tag::YResolution, value).unwrap();
|
||||
}
|
||||
|
||||
fn finish_internal(&mut self) -> TiffResult<()> {
|
||||
self.encoder
|
||||
.write_tag(Tag::StripOffsets, &*self.strip_offsets);
|
||||
.write_tag(Tag::StripOffsets, &*self.strip_offsets)?;
|
||||
self.encoder
|
||||
.write_tag(Tag::StripByteCounts, &*self.strip_byte_count);
|
||||
.write_tag(Tag::StripByteCounts, &*self.strip_byte_count)?;
|
||||
self.dropped = true;
|
||||
|
||||
self.encoder.finish_internal()
|
||||
|
||||
+31
-4
@@ -3,8 +3,8 @@ use std::fmt;
|
||||
use std::io;
|
||||
use std::string;
|
||||
|
||||
use decoder::ifd::{Tag, Value};
|
||||
use decoder::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration};
|
||||
use decoder::ifd::{Value};
|
||||
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Tag};
|
||||
use miniz_oxide::inflate::TINFLStatus;
|
||||
use ColorType;
|
||||
|
||||
@@ -22,6 +22,10 @@ pub enum TiffError {
|
||||
|
||||
/// The Limits of the Decoder is exceeded.
|
||||
LimitsExceeded,
|
||||
|
||||
/// An integer conversion to or from a platform size failed, either due to
|
||||
/// limits of the platform size or limits of the format.
|
||||
IntSizeError,
|
||||
}
|
||||
|
||||
/// The image is not formatted properly.
|
||||
@@ -38,8 +42,9 @@ pub enum TiffFormatError {
|
||||
ImageFileDirectoryNotFound,
|
||||
InconsistentSizesEncountered,
|
||||
InvalidTag,
|
||||
InvalidTagValueType(Tag),
|
||||
RequiredTagNotFound(Tag),
|
||||
UnknownPredictor(u32),
|
||||
UnknownPredictor(u16),
|
||||
UnsignedIntegerExpected(Value),
|
||||
SignedIntegerExpected(Value),
|
||||
InflateError(InflateError),
|
||||
@@ -57,6 +62,8 @@ impl fmt::Display for TiffFormatError {
|
||||
ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."),
|
||||
InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."),
|
||||
InvalidTag => write!(fmt, "Image contains invalid tag."),
|
||||
InvalidTagValueType(ref tag) =>
|
||||
write!(fmt, "Tag `{:?}` did not have the expected value type.", tag),
|
||||
RequiredTagNotFound(ref tag) => write!(fmt, "Required tag `{:?}` not found.", tag),
|
||||
UnknownPredictor(ref predictor) => {
|
||||
write!(fmt, "Unknown predictor “{}” encountered", predictor)
|
||||
@@ -139,7 +146,7 @@ impl fmt::Display for TiffUnsupportedError {
|
||||
write!(fmt, "Compression method {:?} is unsupported", method)
|
||||
}
|
||||
UnsupportedSampleDepth(samples) => {
|
||||
write!(fmt, "{} samples per pixel is supported.", samples)
|
||||
write!(fmt, "{} samples per pixel is unsupported.", samples)
|
||||
}
|
||||
UnsupportedColorType(color_type) => {
|
||||
write!(fmt, "Color type {:?} is unsupported", color_type)
|
||||
@@ -168,6 +175,7 @@ impl fmt::Display for TiffError {
|
||||
),
|
||||
TiffError::IoError(ref e) => e.fmt(fmt),
|
||||
TiffError::LimitsExceeded => write!(fmt, "The Decoder limits are exceeded"),
|
||||
TiffError::IntSizeError => write!(fmt, "Platform or format size limits exceeded"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,6 +187,7 @@ impl Error for TiffError {
|
||||
TiffError::UnsupportedError(..) => "Unsupported error",
|
||||
TiffError::IoError(..) => "IO error",
|
||||
TiffError::LimitsExceeded => "Decoder limits exceeded",
|
||||
TiffError::IntSizeError => "Platform or format size limits exceeded",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,5 +211,23 @@ impl From<string::FromUtf8Error> for TiffError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TiffFormatError> for TiffError {
|
||||
fn from(err: TiffFormatError) -> TiffError {
|
||||
TiffError::FormatError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TiffUnsupportedError> for TiffError {
|
||||
fn from(err: TiffUnsupportedError) -> TiffError {
|
||||
TiffError::UnsupportedError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::num::TryFromIntError> for TiffError {
|
||||
fn from(_err: std::num::TryFromIntError) -> TiffError {
|
||||
TiffError::IntSizeError
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of an image decoding/encoding process
|
||||
pub type TiffResult<T> = Result<T, TiffError>;
|
||||
@@ -13,6 +13,7 @@ extern crate miniz_oxide;
|
||||
pub mod decoder;
|
||||
pub mod encoder;
|
||||
mod error;
|
||||
pub mod tags;
|
||||
|
||||
pub use self::error::{InflateError, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
||||
|
||||
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
macro_rules! tags {
|
||||
{
|
||||
// Permit arbitrary meta items, which include documentation.
|
||||
$( #[$enum_attr:meta] )*
|
||||
$vis:vis enum $name:ident($ty:tt) $(unknown($unknown_doc:literal))* {
|
||||
// Each of the `Name = Val,` permitting documentation.
|
||||
$($(#[$ident_attr:meta])* $tag:ident = $val:expr,)*
|
||||
}
|
||||
} => {
|
||||
$( #[$enum_attr] )*
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum $name {
|
||||
$($(#[$ident_attr])* $tag,)*
|
||||
// FIXME: switch to non_exhaustive once stabilized and compiler requirement new enough
|
||||
#[doc(hidden)]
|
||||
__NonExhaustive,
|
||||
$(
|
||||
#[doc = $unknown_doc]
|
||||
Unknown($ty),
|
||||
)*
|
||||
}
|
||||
|
||||
impl $name {
|
||||
#[inline(always)]
|
||||
fn __from_inner_type(n: $ty) -> Result<Self, $ty> {
|
||||
match n {
|
||||
$( $val => Ok($name::$tag), )*
|
||||
n => Err(n),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn __to_inner_type(&self) -> $ty {
|
||||
match *self {
|
||||
$( $name::$tag => $val, )*
|
||||
$( $name::Unknown(n) => { $unknown_doc; n }, )*
|
||||
$name::__NonExhaustive => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tags!($name, $ty);
|
||||
};
|
||||
// For u16 tags, provide direct inherent primitive conversion methods.
|
||||
($name:tt, u16) => {
|
||||
impl $name {
|
||||
#[inline(always)]
|
||||
pub fn from_u16(val: u16) -> Option<Self> {
|
||||
Self::__from_inner_type(val).ok()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn to_u16(&self) -> u16 {
|
||||
Self::__to_inner_type(self)
|
||||
}
|
||||
}
|
||||
};
|
||||
// For other tag types, do nothing for now. With concat_idents one could
|
||||
// provide inherent conversion methods for all types.
|
||||
($name:tt, $ty:tt) => {};
|
||||
}
|
||||
|
||||
// Note: These tags appear in the order they are mentioned in the TIFF reference
|
||||
tags! {
|
||||
/// TIFF tags
|
||||
pub enum Tag(u16) unknown("A private or extension tag") {
|
||||
// Baseline tags:
|
||||
Artist = 315,
|
||||
// grayscale images PhotometricInterpretation 1 or 3
|
||||
BitsPerSample = 258,
|
||||
CellLength = 265, // TODO add support
|
||||
CellWidth = 264, // TODO add support
|
||||
// palette-color images (PhotometricInterpretation 3)
|
||||
ColorMap = 320, // TODO add support
|
||||
Compression = 259, // TODO add support for 2 and 32773
|
||||
Copyright = 33_432,
|
||||
DateTime = 306,
|
||||
ExtraSamples = 338, // TODO add support
|
||||
FillOrder = 266, // TODO add support
|
||||
FreeByteCounts = 289, // TODO add support
|
||||
FreeOffsets = 288, // TODO add support
|
||||
GrayResponseCurve = 291, // TODO add support
|
||||
GrayResponseUnit = 290, // TODO add support
|
||||
HostComputer = 316,
|
||||
ImageDescription = 270,
|
||||
ImageLength = 257,
|
||||
ImageWidth = 256,
|
||||
Make = 271,
|
||||
MaxSampleValue = 281, // TODO add support
|
||||
MinSampleValue = 280, // TODO add support
|
||||
Model = 272,
|
||||
NewSubfileType = 254, // TODO add support
|
||||
Orientation = 274, // TODO add support
|
||||
PhotometricInterpretation = 262,
|
||||
PlanarConfiguration = 284,
|
||||
ResolutionUnit = 296, // TODO add support
|
||||
RowsPerStrip = 278,
|
||||
SamplesPerPixel = 277,
|
||||
Software = 305,
|
||||
StripByteCounts = 279,
|
||||
StripOffsets = 273,
|
||||
SubfileType = 255, // TODO add support
|
||||
Threshholding = 263, // TODO add support
|
||||
XResolution = 282,
|
||||
YResolution = 283,
|
||||
// Advanced tags
|
||||
Predictor = 317,
|
||||
}
|
||||
}
|
||||
|
||||
impl Tag {
|
||||
pub fn from_u16_exhaustive(val: u16) -> Self {
|
||||
Self::from_u16(val).unwrap_or_else(|| Tag::Unknown(val))
|
||||
}
|
||||
}
|
||||
|
||||
tags! {
|
||||
/// The type of an IFD entry (a 2 byte field).
|
||||
pub enum Type(u16) {
|
||||
BYTE = 1,
|
||||
ASCII = 2,
|
||||
SHORT = 3,
|
||||
LONG = 4,
|
||||
RATIONAL = 5,
|
||||
SBYTE = 6,
|
||||
SSHORT = 8,
|
||||
SLONG = 9,
|
||||
SRATIONAL = 10,
|
||||
/// BigTIFF 64-bit unsigned integer
|
||||
LONG8 = 16,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tags! {
|
||||
pub enum CompressionMethod(u16) {
|
||||
None = 1,
|
||||
Huffman = 2,
|
||||
Fax3 = 3,
|
||||
Fax4 = 4,
|
||||
LZW = 5,
|
||||
JPEG = 6,
|
||||
Deflate = 8,
|
||||
OldDeflate = 0x80B2,
|
||||
PackBits = 0x8005,
|
||||
}
|
||||
}
|
||||
|
||||
tags! {
|
||||
pub enum PhotometricInterpretation(u16) {
|
||||
WhiteIsZero = 0,
|
||||
BlackIsZero = 1,
|
||||
RGB = 2,
|
||||
RGBPalette = 3,
|
||||
TransparencyMask = 4,
|
||||
CMYK = 5,
|
||||
YCbCr = 6,
|
||||
CIELab = 8,
|
||||
}
|
||||
}
|
||||
|
||||
tags! {
|
||||
pub enum PlanarConfiguration(u16) {
|
||||
Chunky = 1,
|
||||
Planar = 2,
|
||||
}
|
||||
}
|
||||
|
||||
tags! {
|
||||
pub enum Predictor(u16) {
|
||||
None = 1,
|
||||
Horizontal = 2,
|
||||
}
|
||||
}
|
||||
|
||||
tags!{
|
||||
/// Type to represent resolution units
|
||||
pub enum ResolutionUnit(u16) {
|
||||
None = 1,
|
||||
Inch = 2,
|
||||
Centimeter = 3,
|
||||
}
|
||||
}
|
||||
+90
-66
@@ -4,99 +4,123 @@ use tiff::decoder::{ifd, Decoder, DecodingResult};
|
||||
use tiff::ColorType;
|
||||
|
||||
use std::fs::File;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
fn test_gray_u8() {
|
||||
let img_file =
|
||||
File::open("./tests/images/minisblack-1c-8b.tiff").expect("Cannot find test image!");
|
||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||
assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(8));
|
||||
let img_res = decoder.read_image().unwrap();
|
||||
if let DecodingResult::U8(res) = img_res {
|
||||
let mut res_sum: u64 = 0;
|
||||
for x in res {
|
||||
res_sum += x as u64;
|
||||
const TEST_IMAGE_DIR: &str = "./tests/images/";
|
||||
|
||||
macro_rules! test_image_sum {
|
||||
($name:ident, $buffer:ident, $sum_ty:ty) => {
|
||||
fn $name(file: &str, expected_type: ColorType, expected_sum: $sum_ty) {
|
||||
let path = PathBuf::from(TEST_IMAGE_DIR).join(file);
|
||||
let img_file = File::open(path).expect("Cannot find test image!");
|
||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||
assert_eq!(decoder.colortype().unwrap(), expected_type);
|
||||
let img_res = decoder.read_image().unwrap();
|
||||
|
||||
match img_res {
|
||||
DecodingResult::$buffer(res) => {
|
||||
let sum: $sum_ty = res.into_iter().map(<$sum_ty>::from).sum();
|
||||
assert_eq!(sum, expected_sum);
|
||||
},
|
||||
_ => panic!("Wrong bit depth"),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
assert_eq!(res_sum, 2840893);
|
||||
}
|
||||
else {
|
||||
panic!("Wrong bit depth")
|
||||
}
|
||||
test_image_sum!(test_image_sum_u8, U8, u64);
|
||||
test_image_sum!(test_image_sum_u16, U16, u64);
|
||||
test_image_sum!(test_image_sum_u32, U32, u64);
|
||||
test_image_sum!(test_image_sum_u64, U64, u64);
|
||||
|
||||
/// Tests that a decoder can be constructed for an image and the color type
|
||||
/// read from the IFD and is of the appropriate type, but the type is
|
||||
/// unsupported.
|
||||
fn test_image_color_type_unsupported(file: &str, expected_type: ColorType) {
|
||||
let path = PathBuf::from(TEST_IMAGE_DIR).join(file);
|
||||
let img_file = File::open(path).expect("Cannot find test image!");
|
||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||
assert_eq!(decoder.colortype().unwrap(), expected_type);
|
||||
assert!(match decoder.read_image() {
|
||||
Err(tiff::TiffError::UnsupportedError(tiff::TiffUnsupportedError::UnsupportedColorType(_))) => true,
|
||||
_ => false,
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rgb_u8() {
|
||||
let img_file = File::open("./tests/images/rgb-3c-8b.tiff").expect("Cannot find test image!");
|
||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
|
||||
let img_res = decoder.read_image().unwrap();
|
||||
if let DecodingResult::U8(res) = img_res {
|
||||
let mut res_sum: u64 = 0;
|
||||
for x in res {
|
||||
res_sum += x as u64;
|
||||
}
|
||||
fn test_cmyk_u8() {
|
||||
test_image_sum_u8("cmyk-3c-8b.tiff", ColorType::CMYK(8), 8522658);
|
||||
}
|
||||
|
||||
assert_eq!(res_sum, 7842108)
|
||||
}
|
||||
else {
|
||||
panic!("Wrong bit depth")
|
||||
}
|
||||
#[test]
|
||||
fn test_cmyk_u16() {
|
||||
test_image_sum_u16("cmyk-3c-16b.tiff", ColorType::CMYK(16), 2181426827);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gray_u8() {
|
||||
test_image_sum_u8("minisblack-1c-8b.tiff", ColorType::Gray(8), 2840893);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gray_u12() {
|
||||
test_image_color_type_unsupported("12bit.cropped.tiff", ColorType::Gray(12));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gray_u16() {
|
||||
let img_file =
|
||||
File::open("./tests/images/minisblack-1c-16b.tiff").expect("Cannot find test image!");
|
||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||
assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(16));
|
||||
let img_res = decoder.read_image().unwrap();
|
||||
if let DecodingResult::U16(res) = img_res {
|
||||
let mut res_sum: u64 = 0;
|
||||
for x in res {
|
||||
res_sum += x as u64;
|
||||
}
|
||||
test_image_sum_u16("minisblack-1c-16b.tiff", ColorType::Gray(16), 733126239);
|
||||
}
|
||||
|
||||
assert_eq!(res_sum, 733126239);
|
||||
}
|
||||
else {
|
||||
panic!("Wrong bit depth")
|
||||
}
|
||||
#[test]
|
||||
fn test_gray_u32() {
|
||||
test_image_sum_u32("gradient-1c-32b.tiff", ColorType::Gray(32), 549892913787);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gray_u64() {
|
||||
test_image_sum_u64("gradient-1c-64b.tiff", ColorType::Gray(64), 549892913787);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rgb_u8() {
|
||||
test_image_sum_u8("rgb-3c-8b.tiff", ColorType::RGB(8), 7842108);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rgb_u12() {
|
||||
test_image_color_type_unsupported("12bit.cropped.rgb.tiff", ColorType::RGB(12));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rgb_u16() {
|
||||
let img_file = File::open("./tests/images/rgb-3c-16b.tiff").expect("Cannot find test image!");
|
||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(16));
|
||||
let img_res = decoder.read_image().unwrap();
|
||||
if let DecodingResult::U16(res) = img_res {
|
||||
let mut res_sum: u64 = 0;
|
||||
for x in res {
|
||||
res_sum += x as u64;
|
||||
}
|
||||
test_image_sum_u16("rgb-3c-16b.tiff", ColorType::RGB(16), 2024349944);
|
||||
}
|
||||
|
||||
assert_eq!(res_sum, 2024349944);
|
||||
}
|
||||
else {
|
||||
panic!("Wrong bit depth")
|
||||
}
|
||||
#[test]
|
||||
fn test_rgb_u32() {
|
||||
test_image_sum_u32("gradient-3c-32b.tiff", ColorType::RGB(32), 2030834111716);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rgb_u64() {
|
||||
test_image_sum_u64("gradient-3c-64b.tiff", ColorType::RGB(64), 2030834111716);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_tags() {
|
||||
// these files have null-terminated strings for their Software tag. One has extra bytes after
|
||||
// the null byte, so we check both to ensure that we're truncating properly
|
||||
let filenames = vec!["minisblack-1c-16b.tiff", "rgb-3c-16b.tiff"];
|
||||
let filenames = ["minisblack-1c-16b.tiff", "rgb-3c-16b.tiff"];
|
||||
for filename in filenames.iter() {
|
||||
let path = format!("./tests/images/{}", filename);
|
||||
let img_file = File::open(path).expect("can't open file");
|
||||
let path = PathBuf::from(TEST_IMAGE_DIR).join(filename);
|
||||
let img_file = File::open(path).expect("Cannot find test image!");
|
||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||
let software = decoder.get_tag(ifd::Tag::Software).unwrap();
|
||||
let software = decoder.get_tag(tiff::tags::Tag::Software).unwrap();
|
||||
match software {
|
||||
ifd::Value::Ascii(s) => assert_eq!(
|
||||
s,
|
||||
String::from("GraphicsMagick 1.2 unreleased Q16 http://www.GraphicsMagick.org/")
|
||||
&s,
|
||||
"GraphicsMagick 1.2 unreleased Q16 http://www.GraphicsMagick.org/"
|
||||
),
|
||||
_ => assert!(false),
|
||||
};
|
||||
|
||||
+95
-127
@@ -1,13 +1,13 @@
|
||||
extern crate tempfile;
|
||||
extern crate tiff;
|
||||
|
||||
use tiff::decoder::{Decoder, DecodingResult};
|
||||
use tiff::decoder::ifd::Tag;
|
||||
use tiff::encoder::{colortype, TiffEncoder, SRational};
|
||||
use tiff::ColorType;
|
||||
use tiff::tags::Tag;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{Cursor, Seek, SeekFrom};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
fn encode_decode() {
|
||||
@@ -20,7 +20,7 @@ fn encode_decode() {
|
||||
image_data.push(val);
|
||||
}
|
||||
}
|
||||
let mut file = tempfile::tempfile().unwrap();
|
||||
let mut file = Cursor::new(Vec::new());
|
||||
{
|
||||
let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
||||
|
||||
@@ -54,127 +54,95 @@ fn test_encode_undersized_buffer() {
|
||||
}
|
||||
}
|
||||
|
||||
const TEST_IMAGE_DIR: &str = "./tests/images/";
|
||||
|
||||
macro_rules! test_roundtrip {
|
||||
($name:ident, $buffer:ident, $buffer_ty:ty) => {
|
||||
fn $name<C: colortype::ColorType<Inner=$buffer_ty>>(file: &str, expected_type: ColorType) {
|
||||
let path = PathBuf::from(TEST_IMAGE_DIR).join(file);
|
||||
let img_file = File::open(path).expect("Cannot find test image!");
|
||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||
assert_eq!(decoder.colortype().unwrap(), expected_type);
|
||||
|
||||
let image_data = match decoder.read_image().unwrap() {
|
||||
DecodingResult::$buffer(res) => res,
|
||||
_ => panic!("Wrong data type"),
|
||||
};
|
||||
|
||||
let mut file = Cursor::new(Vec::new());
|
||||
{
|
||||
let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
||||
|
||||
let (width, height) = decoder.dimensions().unwrap();
|
||||
tiff.write_image::<C>(width, height, &image_data)
|
||||
.unwrap();
|
||||
}
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
{
|
||||
let mut decoder = Decoder::new(&mut file).unwrap();
|
||||
if let DecodingResult::$buffer(img_res) = decoder.read_image().unwrap() {
|
||||
assert_eq!(image_data, img_res);
|
||||
} else {
|
||||
panic!("Wrong data type");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_roundtrip!(test_u8_roundtrip, U8, u8);
|
||||
test_roundtrip!(test_u16_roundtrip, U16, u16);
|
||||
test_roundtrip!(test_u32_roundtrip, U32, u32);
|
||||
test_roundtrip!(test_u64_roundtrip, U64, u64);
|
||||
|
||||
#[test]
|
||||
fn test_gray_u8_roundtrip() {
|
||||
let img_file =
|
||||
File::open("./tests/images/minisblack-1c-8b.tiff").expect("Cannot find test image!");
|
||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||
assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(8));
|
||||
|
||||
let image_data = match decoder.read_image().unwrap() {
|
||||
DecodingResult::U8(res) => res,
|
||||
_ => panic!("Wrong data type"),
|
||||
};
|
||||
|
||||
let mut file = tempfile::tempfile().unwrap();
|
||||
{
|
||||
let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
||||
|
||||
let (width, height) = decoder.dimensions().unwrap();
|
||||
tiff.write_image::<colortype::Gray8>(width, height, &image_data)
|
||||
.unwrap();
|
||||
}
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
{
|
||||
let mut decoder = Decoder::new(&mut file).unwrap();
|
||||
if let DecodingResult::U8(img_res) = decoder.read_image().unwrap() {
|
||||
assert_eq!(image_data, img_res);
|
||||
} else {
|
||||
panic!("Wrong data type");
|
||||
}
|
||||
}
|
||||
test_u8_roundtrip::<colortype::Gray8>("minisblack-1c-8b.tiff", ColorType::Gray(8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rgb_u8_roundtrip() {
|
||||
let img_file = File::open("./tests/images/rgb-3c-8b.tiff").expect("Cannot find test image!");
|
||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
|
||||
test_u8_roundtrip::<colortype::RGB8>("rgb-3c-8b.tiff", ColorType::RGB(8));
|
||||
}
|
||||
|
||||
let image_data = match decoder.read_image().unwrap() {
|
||||
DecodingResult::U8(res) => res,
|
||||
_ => panic!("Wrong data type"),
|
||||
};
|
||||
|
||||
let mut file = tempfile::tempfile().unwrap();
|
||||
{
|
||||
let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
||||
|
||||
let (width, height) = decoder.dimensions().unwrap();
|
||||
tiff.write_image::<colortype::RGB8>(width, height, &image_data)
|
||||
.unwrap();
|
||||
}
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
{
|
||||
let mut decoder = Decoder::new(&mut file).unwrap();
|
||||
if let DecodingResult::U8(img_res) = decoder.read_image().unwrap() {
|
||||
assert_eq!(image_data, img_res);
|
||||
} else {
|
||||
panic!("Wrong data type");
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_cmyk_u8_roundtrip() {
|
||||
test_u8_roundtrip::<colortype::CMYK8>("cmyk-3c-8b.tiff", ColorType::CMYK(8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gray_u16_roundtrip() {
|
||||
let img_file =
|
||||
File::open("./tests/images/minisblack-1c-16b.tiff").expect("Cannot find test image!");
|
||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||
assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(16));
|
||||
|
||||
let image_data = match decoder.read_image().unwrap() {
|
||||
DecodingResult::U16(res) => res,
|
||||
_ => panic!("Wrong data type"),
|
||||
};
|
||||
|
||||
let mut file = tempfile::tempfile().unwrap();
|
||||
{
|
||||
let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
||||
|
||||
let (width, height) = decoder.dimensions().unwrap();
|
||||
tiff.write_image::<colortype::Gray16>(width, height, &image_data)
|
||||
.unwrap();
|
||||
}
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
{
|
||||
let mut decoder = Decoder::new(&mut file).unwrap();
|
||||
if let DecodingResult::U16(img_res) = decoder.read_image().unwrap() {
|
||||
assert_eq!(image_data, img_res);
|
||||
} else {
|
||||
panic!("Wrong data type");
|
||||
}
|
||||
}
|
||||
test_u16_roundtrip::<colortype::Gray16>("minisblack-1c-16b.tiff", ColorType::Gray(16));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rgb_u16_roundtrip() {
|
||||
let img_file = File::open("./tests/images/rgb-3c-16b.tiff").expect("Cannot find test image!");
|
||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(16));
|
||||
test_u16_roundtrip::<colortype::RGB16>("rgb-3c-16b.tiff", ColorType::RGB(16));
|
||||
}
|
||||
|
||||
let image_data = match decoder.read_image().unwrap() {
|
||||
DecodingResult::U16(res) => res,
|
||||
_ => panic!("Wrong data type"),
|
||||
};
|
||||
#[test]
|
||||
fn test_cmyk_u16_roundtrip() {
|
||||
test_u16_roundtrip::<colortype::CMYK16>("cmyk-3c-16b.tiff", ColorType::CMYK(16));
|
||||
}
|
||||
|
||||
let mut file = tempfile::tempfile().unwrap();
|
||||
{
|
||||
let mut tiff = TiffEncoder::new(&mut file).unwrap();
|
||||
#[test]
|
||||
fn test_gray_u32_roundtrip() {
|
||||
test_u32_roundtrip::<colortype::Gray32>("gradient-1c-32b.tiff", ColorType::Gray(32));
|
||||
}
|
||||
|
||||
let (width, height) = decoder.dimensions().unwrap();
|
||||
tiff.write_image::<colortype::RGB16>(width, height, &image_data)
|
||||
.unwrap();
|
||||
}
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
{
|
||||
let mut decoder = Decoder::new(&mut file).unwrap();
|
||||
if let DecodingResult::U16(img_res) = decoder.read_image().unwrap() {
|
||||
assert_eq!(image_data, img_res);
|
||||
} else {
|
||||
panic!("Wrong data type");
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_rgb_u32_roundtrip() {
|
||||
test_u32_roundtrip::<colortype::RGB32>("gradient-3c-32b.tiff", ColorType::RGB(32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gray_u64_roundtrip() {
|
||||
test_u64_roundtrip::<colortype::Gray64>("gradient-1c-64b.tiff", ColorType::Gray(64));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rgb_u64_roundtrip() {
|
||||
test_u64_roundtrip::<colortype::RGB64>("gradient-3c-64b.tiff", ColorType::RGB(64));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -186,11 +154,11 @@ fn test_multiple_byte() {
|
||||
let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap();
|
||||
let encoder = image_encoder.encoder();
|
||||
|
||||
encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]);
|
||||
encoder.write_tag(Tag::Unknown(65001), &[1_u8, 2][..]);
|
||||
encoder.write_tag(Tag::Unknown(65002), &[1_u8, 2, 3][..]);
|
||||
encoder.write_tag(Tag::Unknown(65003), &[1_u8, 2, 3, 4][..]);
|
||||
encoder.write_tag(Tag::Unknown(65004), &[1_u8, 2, 3, 4, 5][..]);
|
||||
encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]).unwrap();
|
||||
encoder.write_tag(Tag::Unknown(65001), &[1_u8, 2][..]).unwrap();
|
||||
encoder.write_tag(Tag::Unknown(65002), &[1_u8, 2, 3][..]).unwrap();
|
||||
encoder.write_tag(Tag::Unknown(65003), &[1_u8, 2, 3, 4][..]).unwrap();
|
||||
encoder.write_tag(Tag::Unknown(65004), &[1_u8, 2, 3, 4, 5][..]).unwrap();
|
||||
}
|
||||
|
||||
data.set_position(0);
|
||||
@@ -216,24 +184,24 @@ fn test_signed() {
|
||||
let encoder = image_encoder.encoder();
|
||||
|
||||
//Use the "reusable" tags section as per the TIFF6 spec
|
||||
encoder.write_tag(Tag::Unknown(65000), -1_i8);
|
||||
encoder.write_tag(Tag::Unknown(65001), &[-1_i8][..]);
|
||||
encoder.write_tag(Tag::Unknown(65002), &[-1_i8, 2][..]);
|
||||
encoder.write_tag(Tag::Unknown(65003), &[-1_i8, 2, -3][..]);
|
||||
encoder.write_tag(Tag::Unknown(65004), &[-1_i8, 2, -3, 4][..]);
|
||||
encoder.write_tag(Tag::Unknown(65005), &[-1_i8, 2, -3, 4, -5][..]);
|
||||
encoder.write_tag(Tag::Unknown(65000), -1_i8).unwrap();
|
||||
encoder.write_tag(Tag::Unknown(65001), &[-1_i8][..]).unwrap();
|
||||
encoder.write_tag(Tag::Unknown(65002), &[-1_i8, 2][..]).unwrap();
|
||||
encoder.write_tag(Tag::Unknown(65003), &[-1_i8, 2, -3][..]).unwrap();
|
||||
encoder.write_tag(Tag::Unknown(65004), &[-1_i8, 2, -3, 4][..]).unwrap();
|
||||
encoder.write_tag(Tag::Unknown(65005), &[-1_i8, 2, -3, 4, -5][..]).unwrap();
|
||||
|
||||
encoder.write_tag(Tag::Unknown(65010), -1_i16);
|
||||
encoder.write_tag(Tag::Unknown(65011), -1_i16);
|
||||
encoder.write_tag(Tag::Unknown(65012), &[-1_i16, 2][..]);
|
||||
encoder.write_tag(Tag::Unknown(65013), &[-1_i16, 2, -3][..]);
|
||||
encoder.write_tag(Tag::Unknown(65010), -1_i16).unwrap();
|
||||
encoder.write_tag(Tag::Unknown(65011), -1_i16).unwrap();
|
||||
encoder.write_tag(Tag::Unknown(65012), &[-1_i16, 2][..]).unwrap();
|
||||
encoder.write_tag(Tag::Unknown(65013), &[-1_i16, 2, -3][..]).unwrap();
|
||||
|
||||
encoder.write_tag(Tag::Unknown(65020), -1_i32);
|
||||
encoder.write_tag(Tag::Unknown(65021), &[-1_i32][..]);
|
||||
encoder.write_tag(Tag::Unknown(65022), &[-1_i32, 2][..]);
|
||||
encoder.write_tag(Tag::Unknown(65020), -1_i32).unwrap();
|
||||
encoder.write_tag(Tag::Unknown(65021), &[-1_i32][..]).unwrap();
|
||||
encoder.write_tag(Tag::Unknown(65022), &[-1_i32, 2][..]).unwrap();
|
||||
|
||||
encoder.write_tag(Tag::Unknown(65030), SRational { n: -1, d: 100 });
|
||||
encoder.write_tag(Tag::Unknown(65031), &[SRational { n: -1, d: 100 }, SRational { n: 2, d: 100 }][..]);
|
||||
encoder.write_tag(Tag::Unknown(65030), SRational { n: -1, d: 100 }).unwrap();
|
||||
encoder.write_tag(Tag::Unknown(65031), &[SRational { n: -1, d: 100 }, SRational { n: 2, d: 100 }][..]).unwrap();
|
||||
}
|
||||
|
||||
//Rewind the cursor for reading
|
||||
@@ -273,10 +241,10 @@ fn test_multipage_image() {
|
||||
|
||||
// write first grayscale image (2x2 16-bit)
|
||||
let img1: Vec<u16> = [1, 2, 3, 4].to_vec();
|
||||
img_encoder.write_image::<colortype::Gray16>(2, 2, &img1[..]);
|
||||
img_encoder.write_image::<colortype::Gray16>(2, 2, &img1[..]).unwrap();
|
||||
// write second grayscale image (3x3 8-bit)
|
||||
let img2: Vec<u8> = [9, 8, 7, 6, 5, 4, 3, 2, 1].to_vec();
|
||||
img_encoder.write_image::<colortype::Gray8>(3, 3, &img2[..]);
|
||||
img_encoder.write_image::<colortype::Gray8>(3, 3, &img2[..]).unwrap();
|
||||
}
|
||||
|
||||
// seek to the beginning of the file, so that it can be decoded
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user