Merge pull request #67 from aschampion/issues/misc-cleanup

Refactoring, cleanup, safety, docs & CMYK(16|32|64) support
This commit is contained in:
HeroicKatora authored and GitHub committed 2020-02-06 17:55:26 +01:00
commit 119dec5068
12 files changed
+500 -425

No files matched your search

+40
View File
@@ -4,3 +4,43 @@
[![Further crate info](https://img.shields.io/crates/v/tiff.svg)](https://crates.io/crates/tiff) [![Further crate info](https://img.shields.io/crates/v/tiff.svg)](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
View File
@@ -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
View File
@@ -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))?;
} }
+22 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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>;
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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.