Merge pull request #67 from aschampion/issues/misc-cleanup
Refactoring, cleanup, safety, docs & CMYK(16|32|64) support
This commit is contained in:
12 files changed
+500
-425
No files matched your search
@@ -4,3 +4,43 @@
|
|||||||
[](https://crates.io/crates/tiff)
|
[](https://crates.io/crates/tiff)
|
||||||
|
|
||||||
TIFF decoding and encoding library in pure Rust
|
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
-133
@@ -1,142 +1,16 @@
|
|||||||
//! Function for reading TIFF tags
|
//! Function for reading TIFF tags
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::convert::TryFrom;
|
||||||
use std::io::{self, Read, Seek};
|
use std::io::{self, Read, Seek};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use super::stream::{ByteOrder, EndianReader, SmartReader};
|
use super::stream::{ByteOrder, EndianReader, SmartReader};
|
||||||
|
use tags::{Tag, Type};
|
||||||
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
||||||
|
|
||||||
use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational};
|
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,
|
|
||||||
/// BigTIFF 64-bit unsigned integer
|
|
||||||
LONG8 = 16,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)]
|
#[allow(unused_qualifications)]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
@@ -181,7 +55,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
Unsigned(val) => Ok(vec![val]),
|
Unsigned(val) => Ok(vec![val]),
|
||||||
Rational(numerator, denominator) => Ok(vec![numerator, denominator]),
|
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(
|
val => Err(TiffError::FormatError(
|
||||||
TiffFormatError::UnsignedIntegerExpected(val),
|
TiffFormatError::UnsignedIntegerExpected(val),
|
||||||
)),
|
)),
|
||||||
@@ -320,11 +194,12 @@ impl Entry {
|
|||||||
))
|
))
|
||||||
}),
|
}),
|
||||||
(Type::ASCII, n) => {
|
(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);
|
return Err(TiffError::LimitsExceeded);
|
||||||
}
|
}
|
||||||
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
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))
|
Ok(Ascii(string))
|
||||||
}
|
}
|
||||||
_ => Err(TiffError::UnsupportedError(
|
_ => Err(TiffError::UnsupportedError(
|
||||||
@@ -339,11 +214,12 @@ impl Entry {
|
|||||||
R: Read + Seek,
|
R: Read + Seek,
|
||||||
F: Fn(&mut super::Decoder<R>) -> TiffResult<Value>,
|
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);
|
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()?)?;
|
decoder.goto_offset(self.r(bo).read_u32()?)?;
|
||||||
for _ in 0..value_count {
|
for _ in 0..value_count {
|
||||||
v.push(decode_fn(decoder)?)
|
v.push(decode_fn(decoder)?)
|
||||||
|
|||||||
+117
-206
@@ -1,3 +1,4 @@
|
|||||||
|
use std::convert::TryFrom;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::{self, Read, Seek};
|
use std::io::{self, Read, Seek};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
@@ -5,10 +6,10 @@ use std::cmp;
|
|||||||
use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
||||||
|
|
||||||
use self::ifd::Directory;
|
use self::ifd::Directory;
|
||||||
|
use tags::{CompressionMethod, PhotometricInterpretation, Predictor, Tag, Type};
|
||||||
|
|
||||||
use self::stream::{ByteOrder, EndianReader, LZWReader, DeflateReader, PackBitsReader, SmartReader};
|
use self::stream::{ByteOrder, EndianReader, LZWReader, DeflateReader, PackBitsReader, SmartReader};
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
pub mod ifd;
|
pub mod ifd;
|
||||||
mod stream;
|
mod stream;
|
||||||
|
|
||||||
@@ -112,87 +113,6 @@ impl<'a> DecodingBuffer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)]
|
#[derive(Debug)]
|
||||||
struct StripDecodeState {
|
struct StripDecodeState {
|
||||||
strip_index: usize,
|
strip_index: usize,
|
||||||
@@ -277,12 +197,12 @@ impl Wrapping for u64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rev_hpredict_nsamp<T>(image: &mut [T], size: (u32, u32), samples: usize)
|
fn rev_hpredict_nsamp<T>(image: &mut [T], size: (u32, u32), samples: usize) -> TiffResult<()>
|
||||||
where
|
where
|
||||||
T: Copy + Wrapping,
|
T: Copy + Wrapping,
|
||||||
{
|
{
|
||||||
let width = size.0 as usize;
|
let width = usize::try_from(size.0)?;
|
||||||
let height = size.1 as usize;
|
let height = usize::try_from(size.1)?;
|
||||||
for row in 0..height {
|
for row in 0..height {
|
||||||
for col in samples..width * samples {
|
for col in samples..width * samples {
|
||||||
let prev_pixel = image[(row * width * samples + col - samples)];
|
let prev_pixel = image[(row * width * samples + col - samples)];
|
||||||
@@ -290,13 +210,15 @@ where
|
|||||||
*pixel = pixel.wrapping_add(prev_pixel);
|
*pixel = pixel.wrapping_add(prev_pixel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType) -> TiffResult<()> {
|
fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType) -> TiffResult<()> {
|
||||||
let samples = match color_type {
|
let samples = match color_type {
|
||||||
ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) | ColorType::Gray(64) => 1,
|
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::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) => 4,
|
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(
|
return Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::HorizontalPredictor(color_type),
|
TiffUnsupportedError::HorizontalPredictor(color_type),
|
||||||
@@ -305,16 +227,16 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType)
|
|||||||
};
|
};
|
||||||
match image {
|
match image {
|
||||||
DecodingBuffer::U8(buf) => {
|
DecodingBuffer::U8(buf) => {
|
||||||
rev_hpredict_nsamp(buf, size, samples);
|
rev_hpredict_nsamp(buf, size, samples)?;
|
||||||
}
|
}
|
||||||
DecodingBuffer::U16(buf) => {
|
DecodingBuffer::U16(buf) => {
|
||||||
rev_hpredict_nsamp(buf, size, samples);
|
rev_hpredict_nsamp(buf, size, samples)?;
|
||||||
}
|
}
|
||||||
DecodingBuffer::U32(buf) => {
|
DecodingBuffer::U32(buf) => {
|
||||||
rev_hpredict_nsamp(buf, size, samples);
|
rev_hpredict_nsamp(buf, size, samples)?;
|
||||||
}
|
}
|
||||||
DecodingBuffer::U64(buf) => {
|
DecodingBuffer::U64(buf) => {
|
||||||
rev_hpredict_nsamp(buf, size, samples);
|
rev_hpredict_nsamp(buf, size, samples)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -363,9 +285,17 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PhotometricInterpretation::CMYK if self.bits_per_sample == [8, 8, 8, 8] => {
|
PhotometricInterpretation::CMYK => {
|
||||||
Ok(ColorType::CMYK(8))
|
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
|
PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero
|
||||||
if self.bits_per_sample.len() == 1 =>
|
if self.bits_per_sample.len() == 1 =>
|
||||||
{
|
{
|
||||||
@@ -424,50 +354,27 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
/// To determine whether there are more images call `TIFFDecoder::more_images` instead.
|
/// To determine whether there are more images call `TIFFDecoder::more_images` instead.
|
||||||
pub fn next_image(&mut self) -> TiffResult<()> {
|
pub fn next_image(&mut self) -> TiffResult<()> {
|
||||||
self.ifd = Some(self.read_ifd()?);
|
self.ifd = Some(self.read_ifd()?);
|
||||||
self.width = self.get_tag_u32(ifd::Tag::ImageWidth)?;
|
self.width = self.get_tag_u32(Tag::ImageWidth)?;
|
||||||
self.height = self.get_tag_u32(ifd::Tag::ImageLength)?;
|
self.height = self.get_tag_u32(Tag::ImageLength)?;
|
||||||
self.strip_decoder = None;
|
self.strip_decoder = None;
|
||||||
// TODO: error on non-SHORT value.
|
|
||||||
self.photometric_interpretation = match PhotometricInterpretation::from_u16(
|
self.photometric_interpretation = self
|
||||||
self.get_tag_u32(ifd::Tag::PhotometricInterpretation)? as u16
|
.find_tag_unsigned(Tag::PhotometricInterpretation)?
|
||||||
) {
|
.and_then(PhotometricInterpretation::from_u16)
|
||||||
Some(val) => val,
|
.ok_or(TiffUnsupportedError::UnknownInterpretation)?;
|
||||||
None => {
|
|
||||||
return Err(TiffError::UnsupportedError(
|
if let Some(val) = self.find_tag_unsigned(Tag::Compression)? {
|
||||||
TiffUnsupportedError::UnknownInterpretation,
|
self.compression_method = CompressionMethod::from_u16(val)
|
||||||
))
|
.ok_or(TiffUnsupportedError::UnknownCompressionMethod)?;
|
||||||
}
|
}
|
||||||
};
|
if let Some(val) = self.find_tag_unsigned(Tag::SamplesPerPixel)? {
|
||||||
// TODO: error on non-SHORT value.
|
self.samples = val;
|
||||||
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,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(val) = self.find_tag_u32(ifd::Tag::SamplesPerPixel)? {
|
|
||||||
self.samples = val as u8
|
|
||||||
}
|
}
|
||||||
match self.samples {
|
match self.samples {
|
||||||
1 => {
|
1 | 3 | 4 => if let Some(val) = self.find_tag_unsigned_vec(Tag::BitsPerSample)? {
|
||||||
if let Some(val) = self.find_tag_u32(ifd::Tag::BitsPerSample)? {
|
self.bits_per_sample = val;
|
||||||
self.bits_per_sample = vec![val as u8]
|
},
|
||||||
}
|
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()),
|
||||||
}
|
|
||||||
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),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -547,9 +454,9 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
// Type 2 bytes
|
// Type 2 bytes
|
||||||
// Count 4 bytes
|
// Count 4 bytes
|
||||||
// Value 4 bytes either a pointer the value itself
|
// Value 4 bytes either a pointer the value itself
|
||||||
fn read_entry(&mut self) -> TiffResult<Option<(ifd::Tag, ifd::Entry)>> {
|
fn read_entry(&mut self) -> TiffResult<Option<(Tag, ifd::Entry)>> {
|
||||||
let tag = ifd::Tag::from_u16(self.read_short()?);
|
let tag = Tag::from_u16_exhaustive(self.read_short()?);
|
||||||
let type_ = match ifd::Type::from_u16(self.read_short()?) {
|
let type_ = match Type::from_u16(self.read_short()?) {
|
||||||
Some(t) => t,
|
Some(t) => t,
|
||||||
None => {
|
None => {
|
||||||
// Unknown type. Skip this entry according to spec.
|
// Unknown type. Skip this entry according to spec.
|
||||||
@@ -595,7 +502,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
/// Tries to retrieve a tag.
|
/// Tries to retrieve a tag.
|
||||||
/// Return `Ok(None)` if the tag is not present.
|
/// 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) {
|
let entry = match self.ifd.as_ref().unwrap().get(&tag) {
|
||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
Some(entry) => entry.clone(),
|
Some(entry) => entry.clone(),
|
||||||
@@ -606,25 +513,36 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
Ok(Some(entry.val(&limits, self)?))
|
Ok(Some(entry.val(&limits, self)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to retrieve a tag and convert it to the desired type.
|
/// Tries to retrieve a tag and convert it to the desired unsigned type.
|
||||||
pub fn find_tag_u32(&mut self, tag: ifd::Tag) -> TiffResult<Option<u32>> {
|
pub fn find_tag_unsigned<T: TryFrom<u32>>(&mut self, tag: Tag) -> TiffResult<Option<T>> {
|
||||||
match self.find_tag(tag)? {
|
self.find_tag(tag)?
|
||||||
Some(val) => val.into_u32().map(Some),
|
.map(|v| v.into_u32()).transpose()?
|
||||||
None => Ok(None),
|
.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.
|
/// Tries to retrieve a vector of all a tag's values and convert them to
|
||||||
pub fn find_tag_u32_vec(&mut self, tag: ifd::Tag) -> TiffResult<Option<Vec<u32>>> {
|
/// the desired unsigned type.
|
||||||
match self.find_tag(tag)? {
|
pub fn find_tag_unsigned_vec<T: TryFrom<u32>>(&mut self, tag: Tag) -> TiffResult<Option<Vec<T>>> {
|
||||||
Some(val) => val.into_u32_vec().map(Some),
|
self.find_tag(tag)?
|
||||||
None => Ok(None),
|
.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.
|
/// Tries to retrieve a tag.
|
||||||
/// Returns an error if the tag is not present
|
/// 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)? {
|
match self.find_tag(tag)? {
|
||||||
Some(val) => Ok(val),
|
Some(val) => Ok(val),
|
||||||
None => Err(TiffError::FormatError(
|
None => Err(TiffError::FormatError(
|
||||||
@@ -634,12 +552,12 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to retrieve a tag and convert it to the desired type.
|
/// 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()
|
self.get_tag(tag)?.into_u32()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to retrieve a tag and convert it to the desired type.
|
/// 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()
|
self.get_tag(tag)?.into_u32_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -658,14 +576,14 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
CompressionMethod::None => {
|
CompressionMethod::None => {
|
||||||
let order = self.reader.byte_order;
|
let order = self.reader.byte_order;
|
||||||
(
|
(
|
||||||
length as usize,
|
usize::try_from(length)?,
|
||||||
Box::new(SmartReader::wrap(&mut self.reader, order)),
|
Box::new(SmartReader::wrap(&mut self.reader, order)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
CompressionMethod::LZW => {
|
CompressionMethod::LZW => {
|
||||||
let (bytes, reader) = LZWReader::new(
|
let (bytes, reader) = LZWReader::new(
|
||||||
&mut self.reader,
|
&mut self.reader,
|
||||||
length as usize,
|
usize::try_from(length)?,
|
||||||
max_uncompressed_length
|
max_uncompressed_length
|
||||||
)?;
|
)?;
|
||||||
(bytes, Box::new(reader))
|
(bytes, Box::new(reader))
|
||||||
@@ -675,7 +593,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
let (bytes, reader) = PackBitsReader::new(
|
let (bytes, reader) = PackBitsReader::new(
|
||||||
&mut self.reader,
|
&mut self.reader,
|
||||||
order,
|
order,
|
||||||
length as usize
|
usize::try_from(length)?,
|
||||||
)?;
|
)?;
|
||||||
(bytes, Box::new(reader))
|
(bytes, Box::new(reader))
|
||||||
}
|
}
|
||||||
@@ -704,17 +622,20 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
(ColorType::RGBA(16), DecodingBuffer::U16(ref mut buffer))
|
(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])?;
|
reader.read_u16_into(&mut buffer[..bytes / 2])?;
|
||||||
bytes / 2
|
bytes / 2
|
||||||
}
|
}
|
||||||
(ColorType::RGBA(32), DecodingBuffer::U32(ref mut buffer))
|
(ColorType::RGBA(32), DecodingBuffer::U32(ref mut buffer))
|
||||||
| (ColorType::RGB(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])?;
|
reader.read_u32_into(&mut buffer[..bytes / 4])?;
|
||||||
bytes / 4
|
bytes / 4
|
||||||
}
|
}
|
||||||
(ColorType::RGBA(64), DecodingBuffer::U64(ref mut buffer))
|
(ColorType::RGBA(64), DecodingBuffer::U64(ref mut buffer))
|
||||||
| (ColorType::RGB(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])?;
|
reader.read_u64_into(&mut buffer[..bytes / 8])?;
|
||||||
bytes / 8
|
bytes / 8
|
||||||
}
|
}
|
||||||
@@ -765,7 +686,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
/// Number of strips in image
|
/// Number of strips in image
|
||||||
pub fn strip_count(&mut self) -> TiffResult<u32> {
|
pub fn strip_count(&mut self) -> TiffResult<u32> {
|
||||||
let rows_per_strip = self
|
let rows_per_strip = self
|
||||||
.get_tag_u32(ifd::Tag::RowsPerStrip)
|
.get_tag_u32(Tag::RowsPerStrip)
|
||||||
.unwrap_or(self.height);
|
.unwrap_or(self.height);
|
||||||
|
|
||||||
if rows_per_strip == 0 {
|
if rows_per_strip == 0 {
|
||||||
@@ -777,8 +698,8 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
fn initialize_strip_decoder(&mut self) -> TiffResult<()> {
|
fn initialize_strip_decoder(&mut self) -> TiffResult<()> {
|
||||||
if self.strip_decoder.is_none() {
|
if self.strip_decoder.is_none() {
|
||||||
let strip_offsets = self.get_tag_u32_vec(ifd::Tag::StripOffsets)?;
|
let strip_offsets = self.get_tag_u32_vec(Tag::StripOffsets)?;
|
||||||
let strip_bytes = self.get_tag_u32_vec(ifd::Tag::StripByteCounts)?;
|
let strip_bytes = self.get_tag_u32_vec(Tag::StripByteCounts)?;
|
||||||
|
|
||||||
self.strip_decoder = Some(StripDecodeState {
|
self.strip_decoder = Some(StripDecodeState {
|
||||||
strip_index: 0,
|
strip_index: 0,
|
||||||
@@ -812,18 +733,18 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
TiffFormatError::InconsistentSizesEncountered,
|
TiffFormatError::InconsistentSizesEncountered,
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
let rows_per_strip = self
|
let rows_per_strip = usize::try_from(self
|
||||||
.get_tag_u32(ifd::Tag::RowsPerStrip)
|
.get_tag_u32(Tag::RowsPerStrip)
|
||||||
.unwrap_or(self.height) as usize;
|
.unwrap_or(self.height))?;
|
||||||
|
|
||||||
let strip_height = cmp::min(
|
let strip_height = cmp::min(
|
||||||
rows_per_strip,
|
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(
|
return Err(TiffError::FormatError(
|
||||||
TiffFormatError::InconsistentSizesEncountered,
|
TiffFormatError::InconsistentSizesEncountered,
|
||||||
));
|
));
|
||||||
@@ -833,7 +754,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
self.strip_decoder.as_mut().unwrap().strip_index += 1;
|
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;
|
self.strip_decoder = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -842,14 +763,13 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
TiffFormatError::InconsistentSizesEncountered,
|
TiffFormatError::InconsistentSizesEncountered,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let Ok(predictor) = self.get_tag_u32(ifd::Tag::Predictor) {
|
if let Ok(predictor) = self.get_tag_unsigned(Tag::Predictor) {
|
||||||
// TODO: error on non-SHORT value.
|
match Predictor::from_u16(predictor) {
|
||||||
match Predictor::from_u16(predictor as u16) {
|
|
||||||
Some(Predictor::None) => (),
|
Some(Predictor::None) => (),
|
||||||
Some(Predictor::Horizontal) => {
|
Some(Predictor::Horizontal) => {
|
||||||
rev_hpredict(
|
rev_hpredict(
|
||||||
buffer.copy(),
|
buffer.copy(),
|
||||||
(self.width, strip_height as u32),
|
(self.width, u32::try_from(strip_height)?),
|
||||||
self.colortype()?,
|
self.colortype()?,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@@ -864,33 +784,37 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
Ok(())
|
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
|
/// Read a single strip from the image and return it as a Vector
|
||||||
pub fn read_strip(&mut self) -> TiffResult<DecodingResult> {
|
pub fn read_strip(&mut self) -> TiffResult<DecodingResult> {
|
||||||
self.initialize_strip_decoder()?;
|
self.initialize_strip_decoder()?;
|
||||||
let index = self.strip_decoder.as_ref().unwrap().strip_index;
|
let index = self.strip_decoder.as_ref().unwrap().strip_index;
|
||||||
|
|
||||||
let rows_per_strip = self
|
let rows_per_strip = usize::try_from(self
|
||||||
.get_tag_u32(ifd::Tag::RowsPerStrip)
|
.get_tag_u32(Tag::RowsPerStrip)
|
||||||
.unwrap_or(self.height) as usize;
|
.unwrap_or(self.height))?;
|
||||||
|
|
||||||
let strip_height = cmp::min(
|
let strip_height = cmp::min(
|
||||||
rows_per_strip,
|
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 = self.result_buffer(strip_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 if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits)?,
|
|
||||||
n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits)?,
|
|
||||||
n => {
|
|
||||||
return Err(TiffError::UnsupportedError(
|
|
||||||
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.read_strip_to_buffer(result.as_buffer(0))?;
|
self.read_strip_to_buffer(result.as_buffer(0))?;
|
||||||
|
|
||||||
@@ -900,29 +824,16 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
/// Decodes the entire image and return it as a Vector
|
/// Decodes the entire image and return it as a Vector
|
||||||
pub fn read_image(&mut self) -> TiffResult<DecodingResult> {
|
pub fn read_image(&mut self) -> TiffResult<DecodingResult> {
|
||||||
self.initialize_strip_decoder()?;
|
self.initialize_strip_decoder()?;
|
||||||
let rows_per_strip = self
|
let rows_per_strip = usize::try_from(self
|
||||||
.get_tag_u32(ifd::Tag::RowsPerStrip)
|
.get_tag_u32(Tag::RowsPerStrip)
|
||||||
.unwrap_or(self.height) as usize;
|
.unwrap_or(self.height))?;
|
||||||
|
|
||||||
let samples_per_strip =
|
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 =
|
let mut result = self.result_buffer(usize::try_from(self.height)?)?;
|
||||||
self.width as usize * self.height as usize * self.bits_per_sample.iter().count();
|
|
||||||
|
|
||||||
let mut result = match self.bits_per_sample.iter().cloned().max().unwrap_or(8) {
|
for i in 0..usize::try_from(self.strip_count()?)? {
|
||||||
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 => {
|
|
||||||
return Err(TiffError::UnsupportedError(
|
|
||||||
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for i in 0..self.strip_count()? as usize {
|
|
||||||
self.read_strip_to_buffer(result.as_buffer(samples_per_strip * i))?;
|
self.read_strip_to_buffer(result.as_buffer(samples_per_strip * i))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use decoder::PhotometricInterpretation;
|
use tags::PhotometricInterpretation;
|
||||||
|
|
||||||
/// Trait for different colortypes that can be encoded.
|
/// Trait for different colortypes that can be encoded.
|
||||||
pub trait ColorType {
|
pub trait ColorType {
|
||||||
@@ -100,3 +100,24 @@ impl ColorType for CMYK8 {
|
|||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8];
|
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];
|
||||||
|
}
|
||||||
+55
-59
@@ -1,9 +1,10 @@
|
|||||||
use byteorder::NativeEndian;
|
use byteorder::NativeEndian;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::convert::TryFrom;
|
||||||
use std::io::{Seek, Write};
|
use std::io::{Seek, Write};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use decoder::ifd::{self, Tag};
|
use tags::{self, ResolutionUnit, Tag, Type};
|
||||||
use error::{TiffError, TiffFormatError, TiffResult};
|
use error::{TiffError, TiffFormatError, TiffResult};
|
||||||
|
|
||||||
pub mod colortype;
|
pub mod colortype;
|
||||||
@@ -25,17 +26,10 @@ pub struct SRational {
|
|||||||
pub d: i32,
|
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
|
/// Trait for types that can be encoded in a tiff file
|
||||||
pub trait TiffValue {
|
pub trait TiffValue {
|
||||||
const BYTE_LEN: u32;
|
const BYTE_LEN: u32;
|
||||||
const FIELD_TYPE: ifd::Type;
|
const FIELD_TYPE: Type;
|
||||||
fn count(&self) -> u32;
|
fn count(&self) -> u32;
|
||||||
fn bytes(&self) -> u32 {
|
fn bytes(&self) -> u32 {
|
||||||
self.count() * Self::BYTE_LEN
|
self.count() * Self::BYTE_LEN
|
||||||
@@ -45,7 +39,7 @@ pub trait TiffValue {
|
|||||||
|
|
||||||
impl TiffValue for [u8] {
|
impl TiffValue for [u8] {
|
||||||
const BYTE_LEN: u32 = 1;
|
const BYTE_LEN: u32 = 1;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::BYTE;
|
const FIELD_TYPE: Type = Type::BYTE;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
self.len() as u32
|
self.len() as u32
|
||||||
@@ -59,7 +53,7 @@ impl TiffValue for [u8] {
|
|||||||
|
|
||||||
impl TiffValue for [i8] {
|
impl TiffValue for [i8] {
|
||||||
const BYTE_LEN: u32 = 1;
|
const BYTE_LEN: u32 = 1;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::SBYTE;
|
const FIELD_TYPE: Type = Type::SBYTE;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
self.len() as u32
|
self.len() as u32
|
||||||
@@ -76,7 +70,7 @@ impl TiffValue for [i8] {
|
|||||||
|
|
||||||
impl TiffValue for [u16] {
|
impl TiffValue for [u16] {
|
||||||
const BYTE_LEN: u32 = 2;
|
const BYTE_LEN: u32 = 2;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::SHORT;
|
const FIELD_TYPE: Type = Type::SHORT;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
self.len() as u32
|
self.len() as u32
|
||||||
@@ -93,7 +87,7 @@ impl TiffValue for [u16] {
|
|||||||
|
|
||||||
impl TiffValue for [i16] {
|
impl TiffValue for [i16] {
|
||||||
const BYTE_LEN: u32 = 2;
|
const BYTE_LEN: u32 = 2;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::SSHORT;
|
const FIELD_TYPE: Type = Type::SSHORT;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
self.len() as u32
|
self.len() as u32
|
||||||
@@ -110,7 +104,7 @@ impl TiffValue for [i16] {
|
|||||||
|
|
||||||
impl TiffValue for [u32] {
|
impl TiffValue for [u32] {
|
||||||
const BYTE_LEN: u32 = 4;
|
const BYTE_LEN: u32 = 4;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::LONG;
|
const FIELD_TYPE: Type = Type::LONG;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
self.len() as u32
|
self.len() as u32
|
||||||
@@ -127,7 +121,7 @@ impl TiffValue for [u32] {
|
|||||||
|
|
||||||
impl TiffValue for [i32] {
|
impl TiffValue for [i32] {
|
||||||
const BYTE_LEN: u32 = 4;
|
const BYTE_LEN: u32 = 4;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::SLONG;
|
const FIELD_TYPE: Type = Type::SLONG;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
self.len() as u32
|
self.len() as u32
|
||||||
@@ -144,7 +138,7 @@ impl TiffValue for [i32] {
|
|||||||
|
|
||||||
impl TiffValue for [u64] {
|
impl TiffValue for [u64] {
|
||||||
const BYTE_LEN: u32 = 8;
|
const BYTE_LEN: u32 = 8;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::LONG8;
|
const FIELD_TYPE: Type = Type::LONG8;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
self.len() as u32
|
self.len() as u32
|
||||||
@@ -161,7 +155,7 @@ impl TiffValue for [u64] {
|
|||||||
|
|
||||||
impl TiffValue for [Rational] {
|
impl TiffValue for [Rational] {
|
||||||
const BYTE_LEN: u32 = 8;
|
const BYTE_LEN: u32 = 8;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL;
|
const FIELD_TYPE: Type = Type::RATIONAL;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
self.len() as u32
|
self.len() as u32
|
||||||
@@ -177,7 +171,7 @@ impl TiffValue for [Rational] {
|
|||||||
|
|
||||||
impl TiffValue for [SRational] {
|
impl TiffValue for [SRational] {
|
||||||
const BYTE_LEN: u32 = 8;
|
const BYTE_LEN: u32 = 8;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::SRATIONAL;
|
const FIELD_TYPE: Type = Type::SRATIONAL;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
self.len() as u32
|
self.len() as u32
|
||||||
@@ -193,7 +187,7 @@ impl TiffValue for [SRational] {
|
|||||||
|
|
||||||
impl TiffValue for u8 {
|
impl TiffValue for u8 {
|
||||||
const BYTE_LEN: u32 = 1;
|
const BYTE_LEN: u32 = 1;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::BYTE;
|
const FIELD_TYPE: Type = Type::BYTE;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
1
|
1
|
||||||
@@ -207,7 +201,7 @@ impl TiffValue for u8 {
|
|||||||
|
|
||||||
impl TiffValue for i8 {
|
impl TiffValue for i8 {
|
||||||
const BYTE_LEN: u32 = 1;
|
const BYTE_LEN: u32 = 1;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::SBYTE;
|
const FIELD_TYPE: Type = Type::SBYTE;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
1
|
1
|
||||||
@@ -221,7 +215,7 @@ impl TiffValue for i8 {
|
|||||||
|
|
||||||
impl TiffValue for u16 {
|
impl TiffValue for u16 {
|
||||||
const BYTE_LEN: u32 = 2;
|
const BYTE_LEN: u32 = 2;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::SHORT;
|
const FIELD_TYPE: Type = Type::SHORT;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
1
|
1
|
||||||
@@ -235,7 +229,7 @@ impl TiffValue for u16 {
|
|||||||
|
|
||||||
impl TiffValue for i16 {
|
impl TiffValue for i16 {
|
||||||
const BYTE_LEN: u32 = 2;
|
const BYTE_LEN: u32 = 2;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::SSHORT;
|
const FIELD_TYPE: Type = Type::SSHORT;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
1
|
1
|
||||||
@@ -249,7 +243,7 @@ impl TiffValue for i16 {
|
|||||||
|
|
||||||
impl TiffValue for u32 {
|
impl TiffValue for u32 {
|
||||||
const BYTE_LEN: u32 = 4;
|
const BYTE_LEN: u32 = 4;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::LONG;
|
const FIELD_TYPE: Type = Type::LONG;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
1
|
1
|
||||||
@@ -263,7 +257,7 @@ impl TiffValue for u32 {
|
|||||||
|
|
||||||
impl TiffValue for i32 {
|
impl TiffValue for i32 {
|
||||||
const BYTE_LEN: u32 = 4;
|
const BYTE_LEN: u32 = 4;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::SLONG;
|
const FIELD_TYPE: Type = Type::SLONG;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
1
|
1
|
||||||
@@ -277,7 +271,7 @@ impl TiffValue for i32 {
|
|||||||
|
|
||||||
impl TiffValue for u64 {
|
impl TiffValue for u64 {
|
||||||
const BYTE_LEN: u32 = 8;
|
const BYTE_LEN: u32 = 8;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::LONG8;
|
const FIELD_TYPE: Type = Type::LONG8;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
1
|
1
|
||||||
@@ -291,7 +285,7 @@ impl TiffValue for u64 {
|
|||||||
|
|
||||||
impl TiffValue for Rational {
|
impl TiffValue for Rational {
|
||||||
const BYTE_LEN: u32 = 8;
|
const BYTE_LEN: u32 = 8;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL;
|
const FIELD_TYPE: Type = Type::RATIONAL;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
1
|
1
|
||||||
@@ -306,7 +300,7 @@ impl TiffValue for Rational {
|
|||||||
|
|
||||||
impl TiffValue for SRational {
|
impl TiffValue for SRational {
|
||||||
const BYTE_LEN: u32 = 8;
|
const BYTE_LEN: u32 = 8;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::SRATIONAL;
|
const FIELD_TYPE: Type = Type::SRATIONAL;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
1
|
1
|
||||||
@@ -321,7 +315,7 @@ impl TiffValue for SRational {
|
|||||||
|
|
||||||
impl TiffValue for str {
|
impl TiffValue for str {
|
||||||
const BYTE_LEN: u32 = 1;
|
const BYTE_LEN: u32 = 1;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::ASCII;
|
const FIELD_TYPE: Type = Type::ASCII;
|
||||||
|
|
||||||
fn count(&self) -> u32 {
|
fn count(&self) -> u32 {
|
||||||
self.len() as u32 + 1
|
self.len() as u32 + 1
|
||||||
@@ -340,7 +334,7 @@ impl TiffValue for str {
|
|||||||
|
|
||||||
impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T {
|
impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T {
|
||||||
const BYTE_LEN: u32 = T::BYTE_LEN;
|
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 {
|
fn count(&self) -> u32 {
|
||||||
(*self).count()
|
(*self).count()
|
||||||
@@ -413,7 +407,7 @@ impl<W: Write + Seek> TiffEncoder<W> {
|
|||||||
where
|
where
|
||||||
[C::Inner]: TiffValue,
|
[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(
|
.ok_or_else(|| ::std::io::Error::new(
|
||||||
::std::io::ErrorKind::InvalidInput,
|
::std::io::ErrorKind::InvalidInput,
|
||||||
"Image width * height exceeds usize"))?;
|
"Image width * height exceeds usize"))?;
|
||||||
@@ -428,7 +422,7 @@ impl<W: Write + Seek> TiffEncoder<W> {
|
|||||||
|
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
while image.next_strip_sample_count() > 0 {
|
while image.next_strip_sample_count() > 0 {
|
||||||
let sample_count = image.next_strip_sample_count() as usize;
|
let sample_count = usize::try_from(image.next_strip_sample_count())?;
|
||||||
image.write_strip(&data[idx..idx + sample_count])?;
|
image.write_strip(&data[idx..idx + sample_count])?;
|
||||||
idx += sample_count;
|
idx += sample_count;
|
||||||
}
|
}
|
||||||
@@ -462,16 +456,18 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Write a single ifd tag.
|
/// 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 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);
|
let mut writer = TiffWriter::new(&mut bytes);
|
||||||
value.write(&mut writer).unwrap();
|
value.write(&mut writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ifd
|
self.ifd
|
||||||
.insert(tag.to_u16(), (<T>::FIELD_TYPE.to_u16(), value.count(), bytes));
|
.insert(tag.to_u16(), (<T>::FIELD_TYPE.to_u16(), value.count(), bytes));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_directory(&mut self) -> TiffResult<u64> {
|
fn write_directory(&mut self) -> TiffResult<u64> {
|
||||||
@@ -482,7 +478,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
|||||||
self.writer.write_bytes(bytes)?;
|
self.writer.write_bytes(bytes)?;
|
||||||
*bytes = vec![0, 0, 0, 0];
|
*bytes = vec![0, 0, 0, 0];
|
||||||
let mut writer = TiffWriter::new(bytes as &mut [u8]);
|
let mut writer = TiffWriter::new(bytes as &mut [u8]);
|
||||||
writer.write_u32(offset as u32)?;
|
writer.write_u32(u32::try_from(offset)?)?;
|
||||||
} else {
|
} else {
|
||||||
while bytes.len() < 4 {
|
while bytes.len() < 4 {
|
||||||
bytes.push(0);
|
bytes.push(0);
|
||||||
@@ -492,7 +488,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
|||||||
|
|
||||||
let offset = self.writer.offset();
|
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() {
|
for (tag, &(ref field_type, ref count, ref offset)) in self.ifd.iter() {
|
||||||
self.writer.write_u16(*tag)?;
|
self.writer.write_u16(*tag)?;
|
||||||
self.writer.write_u16(*field_type)?;
|
self.writer.write_u16(*field_type)?;
|
||||||
@@ -517,7 +513,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
|
|||||||
let curr_pos = self.writer.offset();
|
let curr_pos = self.writer.offset();
|
||||||
|
|
||||||
self.writer.goto_offset(self.ifd_pointer_pos)?;
|
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.goto_offset(curr_pos)?;
|
||||||
self.writer.write_u32(0)?;
|
self.writer.write_u32(0)?;
|
||||||
|
|
||||||
@@ -584,7 +580,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
|||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
) -> TiffResult<ImageEncoder<'a, W, T>> {
|
) -> 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);
|
let row_bytes = row_samples * u64::from(<T::Inner>::BYTE_LEN);
|
||||||
|
|
||||||
// As per tiff spec each strip should be about 8k long
|
// As per tiff spec each strip should be about 8k long
|
||||||
@@ -592,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;
|
let strip_count = (u64::from(height) + rows_per_strip - 1) / rows_per_strip;
|
||||||
|
|
||||||
encoder.write_tag(Tag::ImageWidth, width);
|
encoder.write_tag(Tag::ImageWidth, width)?;
|
||||||
encoder.write_tag(Tag::ImageLength, height);
|
encoder.write_tag(Tag::ImageLength, height)?;
|
||||||
encoder.write_tag(Tag::Compression, 1u16);
|
encoder.write_tag(Tag::Compression, tags::CompressionMethod::None.to_u16())?;
|
||||||
|
|
||||||
encoder.write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE);
|
encoder.write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE)?;
|
||||||
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16());
|
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::SamplesPerPixel, u16::try_from(<T>::BITS_PER_SAMPLE.len())?)?;
|
||||||
encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 });
|
encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 })?;
|
||||||
encoder.write_tag(Tag::YResolution, 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::ResolutionUnit, ResolutionUnit::None.to_u16())?;
|
||||||
|
|
||||||
Ok(ImageEncoder {
|
Ok(ImageEncoder {
|
||||||
encoder,
|
encoder,
|
||||||
@@ -642,15 +638,15 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
|||||||
{
|
{
|
||||||
// TODO: Compression
|
// TODO: Compression
|
||||||
let samples = self.next_strip_sample_count();
|
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(
|
return Err(::std::io::Error::new(
|
||||||
::std::io::ErrorKind::InvalidData,
|
::std::io::ErrorKind::InvalidData,
|
||||||
"Slice is wrong size for strip").into());
|
"Slice is wrong size for strip").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset = self.encoder.write_data(value)?;
|
let offset = self.encoder.write_data(value)?;
|
||||||
self.strip_offsets.push(offset as u32);
|
self.strip_offsets.push(u32::try_from(offset)?);
|
||||||
self.strip_byte_count.push(value.bytes() as u32);
|
self.strip_byte_count.push(value.bytes());
|
||||||
|
|
||||||
self.strip_idx += 1;
|
self.strip_idx += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -658,31 +654,31 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
|||||||
|
|
||||||
/// Set image resolution
|
/// Set image resolution
|
||||||
pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) {
|
pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) {
|
||||||
self.encoder.write_tag(Tag::ResolutionUnit, unit as u16);
|
self.encoder.write_tag(Tag::ResolutionUnit, unit.to_u16()).unwrap();
|
||||||
self.encoder.write_tag(Tag::XResolution, value.clone());
|
self.encoder.write_tag(Tag::XResolution, value.clone()).unwrap();
|
||||||
self.encoder.write_tag(Tag::YResolution, value);
|
self.encoder.write_tag(Tag::YResolution, value).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set image resolution unit
|
/// Set image resolution unit
|
||||||
pub fn resolution_unit(&mut self, unit: ResolutionUnit) {
|
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
|
/// Set image x-resolution
|
||||||
pub fn x_resolution(&mut self, value: Rational) {
|
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
|
/// Set image y-resolution
|
||||||
pub fn y_resolution(&mut self, value: Rational) {
|
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<()> {
|
fn finish_internal(&mut self) -> TiffResult<()> {
|
||||||
self.encoder
|
self.encoder
|
||||||
.write_tag(Tag::StripOffsets, &*self.strip_offsets);
|
.write_tag(Tag::StripOffsets, &*self.strip_offsets)?;
|
||||||
self.encoder
|
self.encoder
|
||||||
.write_tag(Tag::StripByteCounts, &*self.strip_byte_count);
|
.write_tag(Tag::StripByteCounts, &*self.strip_byte_count)?;
|
||||||
self.dropped = true;
|
self.dropped = true;
|
||||||
|
|
||||||
self.encoder.finish_internal()
|
self.encoder.finish_internal()
|
||||||
|
|||||||
+31
-4
@@ -3,8 +3,8 @@ use std::fmt;
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::string;
|
use std::string;
|
||||||
|
|
||||||
use decoder::ifd::{Tag, Value};
|
use decoder::ifd::{Value};
|
||||||
use decoder::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration};
|
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Tag};
|
||||||
use miniz_oxide::inflate::TINFLStatus;
|
use miniz_oxide::inflate::TINFLStatus;
|
||||||
use ColorType;
|
use ColorType;
|
||||||
|
|
||||||
@@ -22,6 +22,10 @@ pub enum TiffError {
|
|||||||
|
|
||||||
/// The Limits of the Decoder is exceeded.
|
/// The Limits of the Decoder is exceeded.
|
||||||
LimitsExceeded,
|
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.
|
/// The image is not formatted properly.
|
||||||
@@ -38,8 +42,9 @@ pub enum TiffFormatError {
|
|||||||
ImageFileDirectoryNotFound,
|
ImageFileDirectoryNotFound,
|
||||||
InconsistentSizesEncountered,
|
InconsistentSizesEncountered,
|
||||||
InvalidTag,
|
InvalidTag,
|
||||||
|
InvalidTagValueType(Tag),
|
||||||
RequiredTagNotFound(Tag),
|
RequiredTagNotFound(Tag),
|
||||||
UnknownPredictor(u32),
|
UnknownPredictor(u16),
|
||||||
UnsignedIntegerExpected(Value),
|
UnsignedIntegerExpected(Value),
|
||||||
SignedIntegerExpected(Value),
|
SignedIntegerExpected(Value),
|
||||||
InflateError(InflateError),
|
InflateError(InflateError),
|
||||||
@@ -57,6 +62,8 @@ impl fmt::Display for TiffFormatError {
|
|||||||
ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."),
|
ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."),
|
||||||
InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."),
|
InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."),
|
||||||
InvalidTag => write!(fmt, "Image contains invalid tag."),
|
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),
|
RequiredTagNotFound(ref tag) => write!(fmt, "Required tag `{:?}` not found.", tag),
|
||||||
UnknownPredictor(ref predictor) => {
|
UnknownPredictor(ref predictor) => {
|
||||||
write!(fmt, "Unknown predictor “{}” encountered", predictor)
|
write!(fmt, "Unknown predictor “{}” encountered", predictor)
|
||||||
@@ -139,7 +146,7 @@ impl fmt::Display for TiffUnsupportedError {
|
|||||||
write!(fmt, "Compression method {:?} is unsupported", method)
|
write!(fmt, "Compression method {:?} is unsupported", method)
|
||||||
}
|
}
|
||||||
UnsupportedSampleDepth(samples) => {
|
UnsupportedSampleDepth(samples) => {
|
||||||
write!(fmt, "{} samples per pixel is supported.", samples)
|
write!(fmt, "{} samples per pixel is unsupported.", samples)
|
||||||
}
|
}
|
||||||
UnsupportedColorType(color_type) => {
|
UnsupportedColorType(color_type) => {
|
||||||
write!(fmt, "Color type {:?} is unsupported", 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::IoError(ref e) => e.fmt(fmt),
|
||||||
TiffError::LimitsExceeded => write!(fmt, "The Decoder limits are exceeded"),
|
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::UnsupportedError(..) => "Unsupported error",
|
||||||
TiffError::IoError(..) => "IO error",
|
TiffError::IoError(..) => "IO error",
|
||||||
TiffError::LimitsExceeded => "Decoder limits exceeded",
|
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
|
/// Result of an image decoding/encoding process
|
||||||
pub type TiffResult<T> = Result<T, TiffError>;
|
pub type TiffResult<T> = Result<T, TiffError>;
|
||||||
@@ -13,6 +13,7 @@ extern crate miniz_oxide;
|
|||||||
pub mod decoder;
|
pub mod decoder;
|
||||||
pub mod encoder;
|
pub mod encoder;
|
||||||
mod error;
|
mod error;
|
||||||
|
pub mod tags;
|
||||||
|
|
||||||
pub use self::error::{InflateError, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
-1
@@ -47,6 +47,16 @@ fn test_image_color_type_unsupported(file: &str, expected_type: ColorType) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cmyk_u8() {
|
||||||
|
test_image_sum_u8("cmyk-3c-8b.tiff", ColorType::CMYK(8), 8522658);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cmyk_u16() {
|
||||||
|
test_image_sum_u16("cmyk-3c-16b.tiff", ColorType::CMYK(16), 2181426827);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_gray_u8() {
|
fn test_gray_u8() {
|
||||||
test_image_sum_u8("minisblack-1c-8b.tiff", ColorType::Gray(8), 2840893);
|
test_image_sum_u8("minisblack-1c-8b.tiff", ColorType::Gray(8), 2840893);
|
||||||
@@ -106,7 +116,7 @@ fn test_string_tags() {
|
|||||||
let path = PathBuf::from(TEST_IMAGE_DIR).join(filename);
|
let path = PathBuf::from(TEST_IMAGE_DIR).join(filename);
|
||||||
let img_file = File::open(path).expect("Cannot find test image!");
|
let img_file = File::open(path).expect("Cannot find test image!");
|
||||||
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
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 {
|
match software {
|
||||||
ifd::Value::Ascii(s) => assert_eq!(
|
ifd::Value::Ascii(s) => assert_eq!(
|
||||||
&s,
|
&s,
|
||||||
|
|||||||
+31
-21
@@ -1,9 +1,9 @@
|
|||||||
extern crate tiff;
|
extern crate tiff;
|
||||||
|
|
||||||
use tiff::decoder::{Decoder, DecodingResult};
|
use tiff::decoder::{Decoder, DecodingResult};
|
||||||
use tiff::decoder::ifd::Tag;
|
|
||||||
use tiff::encoder::{colortype, TiffEncoder, SRational};
|
use tiff::encoder::{colortype, TiffEncoder, SRational};
|
||||||
use tiff::ColorType;
|
use tiff::ColorType;
|
||||||
|
use tiff::tags::Tag;
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Cursor, Seek, SeekFrom};
|
use std::io::{Cursor, Seek, SeekFrom};
|
||||||
@@ -105,6 +105,11 @@ fn test_rgb_u8_roundtrip() {
|
|||||||
test_u8_roundtrip::<colortype::RGB8>("rgb-3c-8b.tiff", ColorType::RGB(8));
|
test_u8_roundtrip::<colortype::RGB8>("rgb-3c-8b.tiff", ColorType::RGB(8));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cmyk_u8_roundtrip() {
|
||||||
|
test_u8_roundtrip::<colortype::CMYK8>("cmyk-3c-8b.tiff", ColorType::CMYK(8));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_gray_u16_roundtrip() {
|
fn test_gray_u16_roundtrip() {
|
||||||
test_u16_roundtrip::<colortype::Gray16>("minisblack-1c-16b.tiff", ColorType::Gray(16));
|
test_u16_roundtrip::<colortype::Gray16>("minisblack-1c-16b.tiff", ColorType::Gray(16));
|
||||||
@@ -115,6 +120,11 @@ fn test_rgb_u16_roundtrip() {
|
|||||||
test_u16_roundtrip::<colortype::RGB16>("rgb-3c-16b.tiff", ColorType::RGB(16));
|
test_u16_roundtrip::<colortype::RGB16>("rgb-3c-16b.tiff", ColorType::RGB(16));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cmyk_u16_roundtrip() {
|
||||||
|
test_u16_roundtrip::<colortype::CMYK16>("cmyk-3c-16b.tiff", ColorType::CMYK(16));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_gray_u32_roundtrip() {
|
fn test_gray_u32_roundtrip() {
|
||||||
test_u32_roundtrip::<colortype::Gray32>("gradient-1c-32b.tiff", ColorType::Gray(32));
|
test_u32_roundtrip::<colortype::Gray32>("gradient-1c-32b.tiff", ColorType::Gray(32));
|
||||||
@@ -144,11 +154,11 @@ fn test_multiple_byte() {
|
|||||||
let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap();
|
let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap();
|
||||||
let encoder = image_encoder.encoder();
|
let encoder = image_encoder.encoder();
|
||||||
|
|
||||||
encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]);
|
encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65001), &[1_u8, 2][..]);
|
encoder.write_tag(Tag::Unknown(65001), &[1_u8, 2][..]).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65002), &[1_u8, 2, 3][..]);
|
encoder.write_tag(Tag::Unknown(65002), &[1_u8, 2, 3][..]).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65003), &[1_u8, 2, 3, 4][..]);
|
encoder.write_tag(Tag::Unknown(65003), &[1_u8, 2, 3, 4][..]).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65004), &[1_u8, 2, 3, 4, 5][..]);
|
encoder.write_tag(Tag::Unknown(65004), &[1_u8, 2, 3, 4, 5][..]).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
data.set_position(0);
|
data.set_position(0);
|
||||||
@@ -174,24 +184,24 @@ fn test_signed() {
|
|||||||
let encoder = image_encoder.encoder();
|
let encoder = image_encoder.encoder();
|
||||||
|
|
||||||
//Use the "reusable" tags section as per the TIFF6 spec
|
//Use the "reusable" tags section as per the TIFF6 spec
|
||||||
encoder.write_tag(Tag::Unknown(65000), -1_i8);
|
encoder.write_tag(Tag::Unknown(65000), -1_i8).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65001), &[-1_i8][..]);
|
encoder.write_tag(Tag::Unknown(65001), &[-1_i8][..]).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65002), &[-1_i8, 2][..]);
|
encoder.write_tag(Tag::Unknown(65002), &[-1_i8, 2][..]).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65003), &[-1_i8, 2, -3][..]);
|
encoder.write_tag(Tag::Unknown(65003), &[-1_i8, 2, -3][..]).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65004), &[-1_i8, 2, -3, 4][..]);
|
encoder.write_tag(Tag::Unknown(65004), &[-1_i8, 2, -3, 4][..]).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65005), &[-1_i8, 2, -3, 4, -5][..]);
|
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(65010), -1_i16).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65011), -1_i16);
|
encoder.write_tag(Tag::Unknown(65011), -1_i16).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65012), &[-1_i16, 2][..]);
|
encoder.write_tag(Tag::Unknown(65012), &[-1_i16, 2][..]).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65013), &[-1_i16, 2, -3][..]);
|
encoder.write_tag(Tag::Unknown(65013), &[-1_i16, 2, -3][..]).unwrap();
|
||||||
|
|
||||||
encoder.write_tag(Tag::Unknown(65020), -1_i32);
|
encoder.write_tag(Tag::Unknown(65020), -1_i32).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65021), &[-1_i32][..]);
|
encoder.write_tag(Tag::Unknown(65021), &[-1_i32][..]).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65022), &[-1_i32, 2][..]);
|
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(65030), SRational { n: -1, d: 100 }).unwrap();
|
||||||
encoder.write_tag(Tag::Unknown(65031), &[SRational { n: -1, d: 100 }, SRational { n: 2, d: 100 }][..]);
|
encoder.write_tag(Tag::Unknown(65031), &[SRational { n: -1, d: 100 }, SRational { n: 2, d: 100 }][..]).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Rewind the cursor for reading
|
//Rewind the cursor for reading
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user