Merge pull request #70 from aschampion/features/32-bit-float-samples
Floating point sample support
This commit is contained in:
17 files changed
+524
-32
No files matched your search
@@ -0,0 +1,11 @@
|
|||||||
|
[[source]]
|
||||||
|
name = "pypi"
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3.7"
|
||||||
@@ -17,12 +17,12 @@ This table lists photometric interpretations and sample formats which are suppor
|
|||||||
|
|
||||||
| `PhotometricInterpretation` | UINT Format | IEEEFP Format |
|
| `PhotometricInterpretation` | UINT Format | IEEEFP Format |
|
||||||
|-|-|-|
|
|-|-|-|
|
||||||
| `WhiteIsZero` | Gray(8\|16\|32\|64) | |
|
| `WhiteIsZero` | Gray(8\|16\|32\|64) | Gray(32\|64) |
|
||||||
| `BlackIsZero` | Gray(8\|16\|32\|64) | |
|
| `BlackIsZero` | Gray(8\|16\|32\|64) | Gray(32\|64) |
|
||||||
| `RGB` | RGB(8\|16\|32\|64), RGBA(8\|16\|32\|64) | |
|
| `RGB` | RGB(8\|16\|32\|64), RGBA(8\|16\|32\|64) | RGB(32\|64), RGBA(32\|64) |
|
||||||
| `RGBPalette` | | |
|
| `RGBPalette` | | |
|
||||||
| `Mask` | | |
|
| `Mask` | | |
|
||||||
| `CMYK` | CMYK(8\|16\|32\|64) | |
|
| `CMYK` | CMYK(8\|16\|32\|64) | CMYK(32\|64) |
|
||||||
| `YCbCr` | | |
|
| `YCbCr` | | |
|
||||||
| `CIELab` | | |
|
| `CIELab` | | |
|
||||||
|
|
||||||
|
|||||||
@@ -29,3 +29,5 @@ integral_slice_as_bytes!(i16, i16_as_ne_bytes);
|
|||||||
integral_slice_as_bytes!(u32, u32_as_ne_bytes, u32_as_ne_mut_bytes);
|
integral_slice_as_bytes!(u32, u32_as_ne_bytes, u32_as_ne_mut_bytes);
|
||||||
integral_slice_as_bytes!(i32, i32_as_ne_bytes);
|
integral_slice_as_bytes!(i32, i32_as_ne_bytes);
|
||||||
integral_slice_as_bytes!(u64, u64_as_ne_bytes, u64_as_ne_mut_bytes);
|
integral_slice_as_bytes!(u64, u64_as_ne_bytes, u64_as_ne_mut_bytes);
|
||||||
|
integral_slice_as_bytes!(f32, f32_as_ne_bytes, f32_as_ne_mut_bytes);
|
||||||
|
integral_slice_as_bytes!(f64, f64_as_ne_bytes, f64_as_ne_mut_bytes);
|
||||||
+60
-2
@@ -9,16 +9,18 @@ use super::stream::{ByteOrder, EndianReader, SmartReader};
|
|||||||
use tags::{Tag, Type};
|
use tags::{Tag, Type};
|
||||||
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
||||||
|
|
||||||
use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational, RationalBig, UnsignedBig, SignedBig, SRationalBig};
|
use self::Value::{Ascii, List, Float, Double, Rational, Unsigned, Signed, SRational, RationalBig, UnsignedBig, SignedBig, SRationalBig};
|
||||||
|
|
||||||
|
|
||||||
#[allow(unused_qualifications)]
|
#[allow(unused_qualifications)]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Signed(i32),
|
Signed(i32),
|
||||||
SignedBig(i64),
|
SignedBig(i64),
|
||||||
Unsigned(u32),
|
Unsigned(u32),
|
||||||
UnsignedBig(u64),
|
UnsignedBig(u64),
|
||||||
|
Float(f32),
|
||||||
|
Double(f64),
|
||||||
List(Vec<Value>),
|
List(Vec<Value>),
|
||||||
Rational(u32, u32),
|
Rational(u32, u32),
|
||||||
RationalBig(u64, u64),
|
RationalBig(u64, u64),
|
||||||
@@ -70,6 +72,24 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_f32(self) -> TiffResult<f32> {
|
||||||
|
match self {
|
||||||
|
Float(val) => Ok(val),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::SignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_f64(self) -> TiffResult<f64> {
|
||||||
|
match self {
|
||||||
|
Double(val) => Ok(val),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::SignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_u32_vec(self) -> TiffResult<Vec<u32>> {
|
pub fn into_u32_vec(self) -> TiffResult<Vec<u32>> {
|
||||||
match self {
|
match self {
|
||||||
List(vec) => {
|
List(vec) => {
|
||||||
@@ -119,6 +139,38 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_f32_vec(self) -> TiffResult<Vec<f32>> {
|
||||||
|
match self {
|
||||||
|
List(vec) => {
|
||||||
|
let mut new_vec = Vec::with_capacity(vec.len());
|
||||||
|
for v in vec {
|
||||||
|
new_vec.push(v.into_f32()?)
|
||||||
|
}
|
||||||
|
Ok(new_vec)
|
||||||
|
}
|
||||||
|
Float(val) => Ok(vec![val]),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::UnsignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_f64_vec(self) -> TiffResult<Vec<f64>> {
|
||||||
|
match self {
|
||||||
|
List(vec) => {
|
||||||
|
let mut new_vec = Vec::with_capacity(vec.len());
|
||||||
|
for v in vec {
|
||||||
|
new_vec.push(v.into_f64()?)
|
||||||
|
}
|
||||||
|
Ok(new_vec)
|
||||||
|
}
|
||||||
|
Double(val) => Ok(vec![val]),
|
||||||
|
val => Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::UnsignedIntegerExpected(val),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_u64_vec(self) -> TiffResult<Vec<u64>> {
|
pub fn into_u64_vec(self) -> TiffResult<Vec<u64>> {
|
||||||
match self {
|
match self {
|
||||||
List(vec) => {
|
List(vec) => {
|
||||||
@@ -337,6 +389,12 @@ impl Entry {
|
|||||||
(Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
(Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
Ok(Signed(decoder.read_slong()?))
|
Ok(Signed(decoder.read_slong()?))
|
||||||
}),
|
}),
|
||||||
|
(Type::FLOAT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(Float(decoder.read_float()?))
|
||||||
|
}),
|
||||||
|
(Type::DOUBLE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
|
Ok(Double(decoder.read_double()?))
|
||||||
|
}),
|
||||||
(Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
(Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
|
||||||
Ok(Rational(decoder.read_long()?, decoder.read_long()?))
|
Ok(Rational(decoder.read_long()?, decoder.read_long()?))
|
||||||
}),
|
}),
|
||||||
|
|||||||
+160
-12
@@ -6,7 +6,7 @@ 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 tags::{CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag, Type};
|
||||||
|
|
||||||
use self::stream::{ByteOrder, EndianReader, LZWReader, DeflateReader, PackBitsReader, SmartReader};
|
use self::stream::{ByteOrder, EndianReader, LZWReader, DeflateReader, PackBitsReader, SmartReader};
|
||||||
|
|
||||||
@@ -24,6 +24,10 @@ pub enum DecodingResult {
|
|||||||
U32(Vec<u32>),
|
U32(Vec<u32>),
|
||||||
/// A vector of 64 bit unsigned ints
|
/// A vector of 64 bit unsigned ints
|
||||||
U64(Vec<u64>),
|
U64(Vec<u64>),
|
||||||
|
/// A vector of 32 bit IEEE floats
|
||||||
|
F32(Vec<f32>),
|
||||||
|
/// A vector of 64 bit IEEE floats
|
||||||
|
F64(Vec<f64>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DecodingResult {
|
impl DecodingResult {
|
||||||
@@ -59,12 +63,30 @@ impl DecodingResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_f32(size: usize, limits: &Limits) -> TiffResult<DecodingResult> {
|
||||||
|
if size > limits.decoding_buffer_size / std::mem::size_of::<f32>() {
|
||||||
|
Err(TiffError::LimitsExceeded)
|
||||||
|
} else {
|
||||||
|
Ok(DecodingResult::F32(vec![0.0; size]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_f64(size: usize, limits: &Limits) -> TiffResult<DecodingResult> {
|
||||||
|
if size > limits.decoding_buffer_size / std::mem::size_of::<f64>() {
|
||||||
|
Err(TiffError::LimitsExceeded)
|
||||||
|
} else {
|
||||||
|
Ok(DecodingResult::F64(vec![0.0; size]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_buffer(&mut self, start: usize) -> DecodingBuffer {
|
pub fn as_buffer(&mut self, start: usize) -> DecodingBuffer {
|
||||||
match *self {
|
match *self {
|
||||||
DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]),
|
DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]),
|
||||||
DecodingResult::U16(ref mut buf) => DecodingBuffer::U16(&mut buf[start..]),
|
DecodingResult::U16(ref mut buf) => DecodingBuffer::U16(&mut buf[start..]),
|
||||||
DecodingResult::U32(ref mut buf) => DecodingBuffer::U32(&mut buf[start..]),
|
DecodingResult::U32(ref mut buf) => DecodingBuffer::U32(&mut buf[start..]),
|
||||||
DecodingResult::U64(ref mut buf) => DecodingBuffer::U64(&mut buf[start..]),
|
DecodingResult::U64(ref mut buf) => DecodingBuffer::U64(&mut buf[start..]),
|
||||||
|
DecodingResult::F32(ref mut buf) => DecodingBuffer::F32(&mut buf[start..]),
|
||||||
|
DecodingResult::F64(ref mut buf) => DecodingBuffer::F64(&mut buf[start..]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,6 +101,10 @@ pub enum DecodingBuffer<'a> {
|
|||||||
U32(&'a mut [u32]),
|
U32(&'a mut [u32]),
|
||||||
/// A slice of 64 bit unsigned ints
|
/// A slice of 64 bit unsigned ints
|
||||||
U64(&'a mut [u64]),
|
U64(&'a mut [u64]),
|
||||||
|
/// A slice of 32 bit IEEE floats
|
||||||
|
F32(&'a mut [f32]),
|
||||||
|
/// A slice of 64 bit IEEE floats
|
||||||
|
F64(&'a mut [f64]),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DecodingBuffer<'a> {
|
impl<'a> DecodingBuffer<'a> {
|
||||||
@@ -88,6 +114,8 @@ impl<'a> DecodingBuffer<'a> {
|
|||||||
DecodingBuffer::U16(ref buf) => buf.len(),
|
DecodingBuffer::U16(ref buf) => buf.len(),
|
||||||
DecodingBuffer::U32(ref buf) => buf.len(),
|
DecodingBuffer::U32(ref buf) => buf.len(),
|
||||||
DecodingBuffer::U64(ref buf) => buf.len(),
|
DecodingBuffer::U64(ref buf) => buf.len(),
|
||||||
|
DecodingBuffer::F32(ref buf) => buf.len(),
|
||||||
|
DecodingBuffer::F64(ref buf) => buf.len(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +125,8 @@ impl<'a> DecodingBuffer<'a> {
|
|||||||
DecodingBuffer::U16(_) => 2,
|
DecodingBuffer::U16(_) => 2,
|
||||||
DecodingBuffer::U32(_) => 4,
|
DecodingBuffer::U32(_) => 4,
|
||||||
DecodingBuffer::U64(_) => 8,
|
DecodingBuffer::U64(_) => 8,
|
||||||
|
DecodingBuffer::F32(_) => 4,
|
||||||
|
DecodingBuffer::F64(_) => 8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +139,8 @@ impl<'a> DecodingBuffer<'a> {
|
|||||||
DecodingBuffer::U16(ref mut buf) => DecodingBuffer::U16(buf),
|
DecodingBuffer::U16(ref mut buf) => DecodingBuffer::U16(buf),
|
||||||
DecodingBuffer::U32(ref mut buf) => DecodingBuffer::U32(buf),
|
DecodingBuffer::U32(ref mut buf) => DecodingBuffer::U32(buf),
|
||||||
DecodingBuffer::U64(ref mut buf) => DecodingBuffer::U64(buf),
|
DecodingBuffer::U64(ref mut buf) => DecodingBuffer::U64(buf),
|
||||||
|
DecodingBuffer::F32(ref mut buf) => DecodingBuffer::F32(buf),
|
||||||
|
DecodingBuffer::F64(ref mut buf) => DecodingBuffer::F64(buf),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,6 +197,7 @@ where
|
|||||||
height: u32,
|
height: u32,
|
||||||
bits_per_sample: Vec<u8>,
|
bits_per_sample: Vec<u8>,
|
||||||
samples: u8,
|
samples: u8,
|
||||||
|
sample_format: Vec<SampleFormat>,
|
||||||
photometric_interpretation: PhotometricInterpretation,
|
photometric_interpretation: PhotometricInterpretation,
|
||||||
compression_method: CompressionMethod,
|
compression_method: CompressionMethod,
|
||||||
strip_decoder: Option<StripDecodeState>,
|
strip_decoder: Option<StripDecodeState>,
|
||||||
@@ -198,6 +231,18 @@ impl Wrapping for u64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Wrapping for f32 {
|
||||||
|
fn wrapping_add(&self, other: Self) -> Self {
|
||||||
|
self + other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Wrapping for f64 {
|
||||||
|
fn wrapping_add(&self, other: Self) -> Self {
|
||||||
|
self + other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn rev_hpredict_nsamp<T>(image: &mut [T], size: (u32, u32), samples: usize) -> TiffResult<()>
|
fn rev_hpredict_nsamp<T>(image: &mut [T], size: (u32, u32), samples: usize) -> TiffResult<()>
|
||||||
where
|
where
|
||||||
T: Copy + Wrapping,
|
T: Copy + Wrapping,
|
||||||
@@ -239,6 +284,22 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType)
|
|||||||
DecodingBuffer::U64(buf) => {
|
DecodingBuffer::U64(buf) => {
|
||||||
rev_hpredict_nsamp(buf, size, samples)?;
|
rev_hpredict_nsamp(buf, size, samples)?;
|
||||||
}
|
}
|
||||||
|
DecodingBuffer::F32(_buf) => {
|
||||||
|
// FIXME: check how this is defined.
|
||||||
|
// See issue #89.
|
||||||
|
// rev_hpredict_nsamp(buf, size, samples)?;
|
||||||
|
return Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::HorizontalPredictor(color_type)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
DecodingBuffer::F64(_buf) => {
|
||||||
|
//FIXME: check how this is defined.
|
||||||
|
// See issue #89.
|
||||||
|
// rev_hpredict_nsamp(buf, size, samples)?;
|
||||||
|
return Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::HorizontalPredictor(color_type)
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -257,6 +318,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
height: 0,
|
height: 0,
|
||||||
bits_per_sample: vec![1],
|
bits_per_sample: vec![1],
|
||||||
samples: 1,
|
samples: 1,
|
||||||
|
sample_format: vec![SampleFormat::Uint],
|
||||||
photometric_interpretation: PhotometricInterpretation::BlackIsZero,
|
photometric_interpretation: PhotometricInterpretation::BlackIsZero,
|
||||||
compression_method: CompressionMethod::None,
|
compression_method: CompressionMethod::None,
|
||||||
strip_decoder: None,
|
strip_decoder: None,
|
||||||
@@ -388,6 +450,16 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
if let Some(val) = self.find_tag_unsigned(Tag::SamplesPerPixel)? {
|
if let Some(val) = self.find_tag_unsigned(Tag::SamplesPerPixel)? {
|
||||||
self.samples = val;
|
self.samples = val;
|
||||||
}
|
}
|
||||||
|
if let Some(vals) = self.find_tag_unsigned_vec(Tag::SampleFormat)? {
|
||||||
|
self.sample_format = vals.into_iter()
|
||||||
|
.map(SampleFormat::from_u16_exhaustive)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// TODO: for now, only homogenous formats across samples are supported.
|
||||||
|
if !self.sample_format.windows(2).all(|s| s[0] == s[1]) {
|
||||||
|
return Err(TiffUnsupportedError::UnsupportedSampleFormat(self.sample_format.clone()).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
match self.samples {
|
match self.samples {
|
||||||
1 | 3 | 4 => if let Some(val) = self.find_tag_unsigned_vec(Tag::BitsPerSample)? {
|
1 | 3 | 4 => if let Some(val) = self.find_tag_unsigned_vec(Tag::BitsPerSample)? {
|
||||||
self.bits_per_sample = val;
|
self.bits_per_sample = val;
|
||||||
@@ -448,12 +520,23 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
self.reader.read_i32()
|
self.reader.read_i32()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads a TIFF float value
|
||||||
|
#[inline]
|
||||||
|
pub fn read_float(&mut self) -> Result<f32, io::Error> {
|
||||||
|
self.reader.read_f32()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads a TIFF double value
|
||||||
|
#[inline]
|
||||||
|
pub fn read_double(&mut self) -> Result<f64, io::Error> {
|
||||||
|
self.reader.read_f64()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_long8(&mut self) -> Result<u64, io::Error> {
|
pub fn read_long8(&mut self) -> Result<u64, io::Error> {
|
||||||
self.reader.read_u64()
|
self.reader.read_u64()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Reads a string
|
/// Reads a string
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_string(&mut self, length: usize) -> TiffResult<String> {
|
pub fn read_string(&mut self, length: usize) -> TiffResult<String> {
|
||||||
@@ -620,6 +703,16 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
self.get_tag(tag)?.into_u64()
|
self.get_tag(tag)?.into_u64()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tries to retrieve a tag and convert it to the desired type.
|
||||||
|
pub fn get_tag_f32(&mut self, tag: Tag) -> TiffResult<f32> {
|
||||||
|
self.get_tag(tag)?.into_f32()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to retrieve a tag and convert it to the desired type.
|
||||||
|
pub fn get_tag_f64(&mut self, tag: Tag) -> TiffResult<f64> {
|
||||||
|
self.get_tag(tag)?.into_f64()
|
||||||
|
}
|
||||||
|
|
||||||
/// 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: 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()
|
||||||
@@ -628,6 +721,16 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
self.get_tag(tag)?.into_u64_vec()
|
self.get_tag(tag)?.into_u64_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tries to retrieve a tag and convert it to the desired type.
|
||||||
|
pub fn get_tag_f32_vec(&mut self, tag: Tag) -> TiffResult<Vec<f32>> {
|
||||||
|
self.get_tag(tag)?.into_f32_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to retrieve a tag and convert it to the desired type.
|
||||||
|
pub fn get_tag_f64_vec(&mut self, tag: Tag) -> TiffResult<Vec<f64>> {
|
||||||
|
self.get_tag(tag)?.into_f64_vec()
|
||||||
|
}
|
||||||
|
|
||||||
/// Decompresses the strip into the supplied buffer.
|
/// Decompresses the strip into the supplied buffer.
|
||||||
/// Returns the number of bytes read.
|
/// Returns the number of bytes read.
|
||||||
fn expand_strip<'a>(
|
fn expand_strip<'a>(
|
||||||
@@ -700,6 +803,18 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
reader.read_u32_into(&mut buffer[..bytes / 4])?;
|
reader.read_u32_into(&mut buffer[..bytes / 4])?;
|
||||||
bytes / 4
|
bytes / 4
|
||||||
}
|
}
|
||||||
|
(ColorType::RGBA(32), DecodingBuffer::F32(ref mut buffer))
|
||||||
|
| (ColorType::RGB(32), DecodingBuffer::F32(ref mut buffer))
|
||||||
|
| (ColorType::CMYK(32), DecodingBuffer::F32(ref mut buffer)) => {
|
||||||
|
reader.read_f32_into(&mut buffer[..bytes / 4])?;
|
||||||
|
bytes / 4
|
||||||
|
}
|
||||||
|
(ColorType::RGBA(64), DecodingBuffer::F64(ref mut buffer))
|
||||||
|
| (ColorType::RGB(64), DecodingBuffer::F64(ref mut buffer))
|
||||||
|
| (ColorType::CMYK(64), DecodingBuffer::F64(ref mut buffer)) => {
|
||||||
|
reader.read_f64_into(&mut buffer[..bytes / 8])?;
|
||||||
|
bytes / 8
|
||||||
|
}
|
||||||
(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)) => {
|
| (ColorType::CMYK(64), DecodingBuffer::U64(ref mut buffer)) => {
|
||||||
@@ -742,6 +857,26 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
}
|
}
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
|
(ColorType::Gray(32), DecodingBuffer::F32(ref mut buffer)) => {
|
||||||
|
reader.read_f32_into(&mut buffer[..bytes / 4])?;
|
||||||
|
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
||||||
|
for datum in buffer[..bytes / 4].iter_mut() {
|
||||||
|
// FIXME: assumes [0, 1) range for floats
|
||||||
|
*datum = 1.0 - *datum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bytes / 4
|
||||||
|
}
|
||||||
|
(ColorType::Gray(64), DecodingBuffer::F64(ref mut buffer)) => {
|
||||||
|
reader.read_f64_into(&mut buffer[..bytes / 8])?;
|
||||||
|
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
||||||
|
for datum in buffer[..bytes / 8].iter_mut() {
|
||||||
|
// FIXME: assumes [0, 1) range for floats
|
||||||
|
*datum = 1.0 - *datum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bytes / 8
|
||||||
|
}
|
||||||
(type_, _) => {
|
(type_, _) => {
|
||||||
return Err(TiffError::UnsupportedError(
|
return Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::UnsupportedColorType(type_),
|
TiffUnsupportedError::UnsupportedColorType(type_),
|
||||||
@@ -854,16 +989,29 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
fn result_buffer(&self, height: usize) -> TiffResult<DecodingResult> {
|
fn result_buffer(&self, height: usize) -> TiffResult<DecodingResult> {
|
||||||
let buffer_size = usize::try_from(self.width)? * height * self.bits_per_sample.len();
|
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) {
|
let max_sample_bits = self.bits_per_sample.iter().cloned().max().unwrap_or(8);
|
||||||
n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits),
|
match self.sample_format.first().unwrap_or(&SampleFormat::Uint) {
|
||||||
n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits),
|
SampleFormat::Uint => match max_sample_bits {
|
||||||
n if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits),
|
n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits),
|
||||||
n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits),
|
n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits),
|
||||||
n => {
|
n if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits),
|
||||||
Err(TiffError::UnsupportedError(
|
n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits),
|
||||||
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
n => {
|
||||||
))
|
Err(TiffError::UnsupportedError(
|
||||||
}
|
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SampleFormat::IEEEFP => match max_sample_bits {
|
||||||
|
32 => DecodingResult::new_f32(buffer_size, &self.limits),
|
||||||
|
64 => DecodingResult::new_f64(buffer_size, &self.limits),
|
||||||
|
n => {
|
||||||
|
Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
format => Err(TiffUnsupportedError::UnsupportedSampleFormat(vec![format.clone()]).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -122,6 +122,60 @@ pub trait EndianReader: Read {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads an f32
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_f32(&mut self) -> Result<f32, io::Error> {
|
||||||
|
let mut n = [0u8; 4];
|
||||||
|
self.read_exact(&mut n)?;
|
||||||
|
Ok(f32::from_bits(match self.byte_order() {
|
||||||
|
ByteOrder::LittleEndian => u32::from_le_bytes(n),
|
||||||
|
ByteOrder::BigEndian => u32::from_be_bytes(n),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_f32_into(&mut self, buffer: &mut [f32]) -> Result<(), io::Error> {
|
||||||
|
self.read_exact(bytecast::f32_as_ne_mut_bytes(buffer))?;
|
||||||
|
match self.byte_order() {
|
||||||
|
ByteOrder::LittleEndian =>
|
||||||
|
for n in buffer {
|
||||||
|
*n = f32::from_bits(u32::from_le(n.to_bits()));
|
||||||
|
},
|
||||||
|
ByteOrder::BigEndian =>
|
||||||
|
for n in buffer {
|
||||||
|
*n = f32::from_bits(u32::from_be(n.to_bits()));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads an f64
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_f64(&mut self) -> Result<f64, io::Error> {
|
||||||
|
let mut n = [0u8; 8];
|
||||||
|
self.read_exact(&mut n)?;
|
||||||
|
Ok(f64::from_bits(match self.byte_order() {
|
||||||
|
ByteOrder::LittleEndian => u64::from_le_bytes(n),
|
||||||
|
ByteOrder::BigEndian => u64::from_be_bytes(n),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_f64_into(&mut self, buffer: &mut [f64]) -> Result<(), io::Error> {
|
||||||
|
self.read_exact(bytecast::f64_as_ne_mut_bytes(buffer))?;
|
||||||
|
match self.byte_order() {
|
||||||
|
ByteOrder::LittleEndian =>
|
||||||
|
for n in buffer {
|
||||||
|
*n = f64::from_bits(u64::from_le(n.to_bits()));
|
||||||
|
},
|
||||||
|
ByteOrder::BigEndian =>
|
||||||
|
for n in buffer {
|
||||||
|
*n = f64::from_bits(u64::from_be(n.to_bits()));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reader that decompresses DEFLATE streams
|
/// Reader that decompresses DEFLATE streams
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use tags::PhotometricInterpretation;
|
use tags::{PhotometricInterpretation, SampleFormat};
|
||||||
|
|
||||||
/// Trait for different colortypes that can be encoded.
|
/// Trait for different colortypes that can be encoded.
|
||||||
pub trait ColorType {
|
pub trait ColorType {
|
||||||
@@ -8,6 +8,8 @@ pub trait ColorType {
|
|||||||
const TIFF_VALUE: PhotometricInterpretation;
|
const TIFF_VALUE: PhotometricInterpretation;
|
||||||
/// The value of the tiff tag `BitsPerSample`
|
/// The value of the tiff tag `BitsPerSample`
|
||||||
const BITS_PER_SAMPLE: &'static [u16];
|
const BITS_PER_SAMPLE: &'static [u16];
|
||||||
|
/// The value of the tiff tag `SampleFormat`
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Gray8;
|
pub struct Gray8;
|
||||||
@@ -15,6 +17,7 @@ impl ColorType for Gray8 {
|
|||||||
type Inner = u8;
|
type Inner = u8;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[8];
|
const BITS_PER_SAMPLE: &'static [u16] = &[8];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Gray16;
|
pub struct Gray16;
|
||||||
@@ -22,6 +25,7 @@ impl ColorType for Gray16 {
|
|||||||
type Inner = u16;
|
type Inner = u16;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[16];
|
const BITS_PER_SAMPLE: &'static [u16] = &[16];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Gray32;
|
pub struct Gray32;
|
||||||
@@ -29,6 +33,15 @@ impl ColorType for Gray32 {
|
|||||||
type Inner = u32;
|
type Inner = u32;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[32];
|
const BITS_PER_SAMPLE: &'static [u16] = &[32];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Gray32Float;
|
||||||
|
impl ColorType for Gray32Float {
|
||||||
|
type Inner = f32;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[32];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Gray64;
|
pub struct Gray64;
|
||||||
@@ -36,6 +49,15 @@ impl ColorType for Gray64 {
|
|||||||
type Inner = u64;
|
type Inner = u64;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[64];
|
const BITS_PER_SAMPLE: &'static [u16] = &[64];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Gray64Float;
|
||||||
|
impl ColorType for Gray64Float {
|
||||||
|
type Inner = f64;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[64];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RGB8;
|
pub struct RGB8;
|
||||||
@@ -43,6 +65,7 @@ impl ColorType for RGB8 {
|
|||||||
type Inner = u8;
|
type Inner = u8;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8];
|
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 3];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RGB16;
|
pub struct RGB16;
|
||||||
@@ -50,6 +73,7 @@ impl ColorType for RGB16 {
|
|||||||
type Inner = u16;
|
type Inner = u16;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16];
|
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 3];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RGB32;
|
pub struct RGB32;
|
||||||
@@ -57,6 +81,15 @@ impl ColorType for RGB32 {
|
|||||||
type Inner = u32;
|
type Inner = u32;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32];
|
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RGB32Float;
|
||||||
|
impl ColorType for RGB32Float {
|
||||||
|
type Inner = f32;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 3];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RGB64;
|
pub struct RGB64;
|
||||||
@@ -64,6 +97,15 @@ impl ColorType for RGB64 {
|
|||||||
type Inner = u64;
|
type Inner = u64;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64];
|
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RGB64Float;
|
||||||
|
impl ColorType for RGB64Float {
|
||||||
|
type Inner = f64;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 3];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RGBA8;
|
pub struct RGBA8;
|
||||||
@@ -71,6 +113,7 @@ impl ColorType for RGBA8 {
|
|||||||
type Inner = u8;
|
type Inner = u8;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8];
|
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RGBA16;
|
pub struct RGBA16;
|
||||||
@@ -78,20 +121,39 @@ impl ColorType for RGBA16 {
|
|||||||
type Inner = u16;
|
type Inner = u16;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16];
|
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RGBA32;
|
pub struct RGBA32;
|
||||||
impl ColorType for RGBA32 {
|
impl ColorType for RGBA32 {
|
||||||
type Inner = u16;
|
type Inner = u32;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
|
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RGBA32Float;
|
||||||
|
impl ColorType for RGBA32Float {
|
||||||
|
type Inner = f32;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RGBA64;
|
pub struct RGBA64;
|
||||||
impl ColorType for RGBA64 {
|
impl ColorType for RGBA64 {
|
||||||
type Inner = u16;
|
type Inner = u64;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
|
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RGBA64Float;
|
||||||
|
impl ColorType for RGBA64Float {
|
||||||
|
type Inner = f64;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CMYK8;
|
pub struct CMYK8;
|
||||||
@@ -99,6 +161,7 @@ impl ColorType for CMYK8 {
|
|||||||
type Inner = u8;
|
type Inner = u8;
|
||||||
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];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CMYK16;
|
pub struct CMYK16;
|
||||||
@@ -106,6 +169,7 @@ impl ColorType for CMYK16 {
|
|||||||
type Inner = u16;
|
type Inner = u16;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16];
|
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CMYK32;
|
pub struct CMYK32;
|
||||||
@@ -113,6 +177,15 @@ impl ColorType for CMYK32 {
|
|||||||
type Inner = u32;
|
type Inner = u32;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
|
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CMYK32Float;
|
||||||
|
impl ColorType for CMYK32Float {
|
||||||
|
type Inner = f32;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CMYK64;
|
pub struct CMYK64;
|
||||||
@@ -120,4 +193,13 @@ impl ColorType for CMYK64 {
|
|||||||
type Inner = u64;
|
type Inner = u64;
|
||||||
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||||
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
|
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CMYK64Float;
|
||||||
|
impl ColorType for CMYK64Float {
|
||||||
|
type Inner = f64;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 4];
|
||||||
}
|
}
|
||||||
@@ -141,6 +141,38 @@ impl TiffValue for [u64] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [f32] {
|
||||||
|
const BYTE_LEN: u32 = 4;
|
||||||
|
const FIELD_TYPE: Type = Type::FLOAT;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
self.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
// We write using nativeedian so this sould be safe
|
||||||
|
let slice = bytecast::f32_as_ne_bytes(self);
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for [f64] {
|
||||||
|
const BYTE_LEN: u32 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::DOUBLE;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
self.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
// We write using nativeedian so this sould be safe
|
||||||
|
let slice = bytecast::f64_as_ne_bytes(self);
|
||||||
|
writer.write_bytes(slice)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TiffValue for [Rational] {
|
impl TiffValue for [Rational] {
|
||||||
const BYTE_LEN: u32 = 8;
|
const BYTE_LEN: u32 = 8;
|
||||||
const FIELD_TYPE: Type = Type::RATIONAL;
|
const FIELD_TYPE: Type = Type::RATIONAL;
|
||||||
@@ -271,6 +303,34 @@ impl TiffValue for u64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TiffValue for f32 {
|
||||||
|
const BYTE_LEN: u32 = 4;
|
||||||
|
const FIELD_TYPE: Type = Type::FLOAT;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_f32(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiffValue for f64 {
|
||||||
|
const BYTE_LEN: u32 = 8;
|
||||||
|
const FIELD_TYPE: Type = Type::DOUBLE;
|
||||||
|
|
||||||
|
fn count(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
|
||||||
|
writer.write_f64(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TiffValue for Rational {
|
impl TiffValue for Rational {
|
||||||
const BYTE_LEN: u32 = 8;
|
const BYTE_LEN: u32 = 8;
|
||||||
const FIELD_TYPE: Type = Type::RATIONAL;
|
const FIELD_TYPE: Type = Type::RATIONAL;
|
||||||
@@ -570,6 +630,8 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
|
|||||||
encoder.write_tag(Tag::Compression, tags::CompressionMethod::None.to_u16())?;
|
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)?;
|
||||||
|
let sample_format: Vec<_> = <T>::SAMPLE_FORMAT.iter().map(|s| s.to_u16()).collect();
|
||||||
|
encoder.write_tag(Tag::SampleFormat, &sample_format[..])?;
|
||||||
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, u32::try_from(rows_per_strip)?)?;
|
encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?;
|
||||||
|
|||||||
@@ -81,6 +81,20 @@ impl<W: Write> TiffWriter<W> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_f32(&mut self, n: f32) -> Result<(), io::Error> {
|
||||||
|
self.writer.write_all(&u32::to_ne_bytes(n.to_bits()))?;
|
||||||
|
self.offset += 4;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_f64(&mut self, n: f64) -> Result<(), io::Error> {
|
||||||
|
self.writer.write_all(&u64::to_ne_bytes(n.to_bits()))?;
|
||||||
|
self.offset += 8;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn pad_word_boundary(&mut self) -> Result<(), io::Error> {
|
pub fn pad_word_boundary(&mut self) -> Result<(), io::Error> {
|
||||||
if self.offset % 4 != 0 {
|
if self.offset % 4 != 0 {
|
||||||
let padding = [0, 0, 0];
|
let padding = [0, 0, 0];
|
||||||
|
|||||||
+6
-2
@@ -4,7 +4,7 @@ use std::io;
|
|||||||
use std::string;
|
use std::string;
|
||||||
|
|
||||||
use decoder::ifd::{Value};
|
use decoder::ifd::{Value};
|
||||||
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Tag};
|
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag};
|
||||||
use miniz_oxide::inflate::TINFLStatus;
|
use miniz_oxide::inflate::TINFLStatus;
|
||||||
use ColorType;
|
use ColorType;
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ pub enum TiffError {
|
|||||||
///
|
///
|
||||||
/// The list of variants may grow to incorporate errors of future features. Matching against this
|
/// The list of variants may grow to incorporate errors of future features. Matching against this
|
||||||
/// exhaustively is not covered by interface stability guarantees.
|
/// exhaustively is not covered by interface stability guarantees.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum TiffFormatError {
|
pub enum TiffFormatError {
|
||||||
TiffSignatureNotFound,
|
TiffSignatureNotFound,
|
||||||
TiffSignatureInvalid,
|
TiffSignatureInvalid,
|
||||||
@@ -114,6 +114,7 @@ pub enum TiffUnsupportedError {
|
|||||||
UnknownCompressionMethod,
|
UnknownCompressionMethod,
|
||||||
UnsupportedCompressionMethod(CompressionMethod),
|
UnsupportedCompressionMethod(CompressionMethod),
|
||||||
UnsupportedSampleDepth(u8),
|
UnsupportedSampleDepth(u8),
|
||||||
|
UnsupportedSampleFormat(Vec<SampleFormat>),
|
||||||
UnsupportedColorType(ColorType),
|
UnsupportedColorType(ColorType),
|
||||||
UnsupportedBitsPerChannel(u8),
|
UnsupportedBitsPerChannel(u8),
|
||||||
UnsupportedPlanarConfig(Option<PlanarConfiguration>),
|
UnsupportedPlanarConfig(Option<PlanarConfiguration>),
|
||||||
@@ -148,6 +149,9 @@ impl fmt::Display for TiffUnsupportedError {
|
|||||||
UnsupportedSampleDepth(samples) => {
|
UnsupportedSampleDepth(samples) => {
|
||||||
write!(fmt, "{} samples per pixel is unsupported.", samples)
|
write!(fmt, "{} samples per pixel is unsupported.", samples)
|
||||||
}
|
}
|
||||||
|
UnsupportedSampleFormat(ref formats) => {
|
||||||
|
write!(fmt, "Sample format {:?} is unsupported.", formats)
|
||||||
|
}
|
||||||
UnsupportedColorType(color_type) => {
|
UnsupportedColorType(color_type) => {
|
||||||
write!(fmt, "Color type {:?} is unsupported", color_type)
|
write!(fmt, "Color type {:?} is unsupported", color_type)
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-9
@@ -39,16 +39,24 @@ macro_rules! tags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tags!($name, $ty);
|
tags!($name, $ty, $($unknown_doc)*);
|
||||||
};
|
};
|
||||||
// For u16 tags, provide direct inherent primitive conversion methods.
|
// For u16 tags, provide direct inherent primitive conversion methods.
|
||||||
($name:tt, u16) => {
|
($name:tt, u16, $($unknown_doc:literal)*) => {
|
||||||
impl $name {
|
impl $name {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_u16(val: u16) -> Option<Self> {
|
pub fn from_u16(val: u16) -> Option<Self> {
|
||||||
Self::__from_inner_type(val).ok()
|
Self::__from_inner_type(val).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn from_u16_exhaustive(val: u16) -> Self {
|
||||||
|
$unknown_doc;
|
||||||
|
Self::__from_inner_type(val).unwrap_or_else(|_| $name::Unknown(val))
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn to_u16(&self) -> u16 {
|
pub fn to_u16(&self) -> u16 {
|
||||||
Self::__to_inner_type(self)
|
Self::__to_inner_type(self)
|
||||||
@@ -57,7 +65,7 @@ macro_rules! tags {
|
|||||||
};
|
};
|
||||||
// For other tag types, do nothing for now. With concat_idents one could
|
// For other tag types, do nothing for now. With concat_idents one could
|
||||||
// provide inherent conversion methods for all types.
|
// provide inherent conversion methods for all types.
|
||||||
($name:tt, $ty:tt) => {};
|
($name:tt, $ty:tt, $($unknown_doc:literal)*) => {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: These tags appear in the order they are mentioned in the TIFF reference
|
// Note: These tags appear in the order they are mentioned in the TIFF reference
|
||||||
@@ -113,12 +121,6 @@ pub enum Tag(u16) unknown("A private or extension tag") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tag {
|
|
||||||
pub fn from_u16_exhaustive(val: u16) -> Self {
|
|
||||||
Self::from_u16(val).unwrap_or_else(|| Tag::Unknown(val))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tags! {
|
tags! {
|
||||||
/// The type of an IFD entry (a 2 byte field).
|
/// The type of an IFD entry (a 2 byte field).
|
||||||
pub enum Type(u16) {
|
pub enum Type(u16) {
|
||||||
@@ -131,6 +133,8 @@ pub enum Type(u16) {
|
|||||||
SSHORT = 8,
|
SSHORT = 8,
|
||||||
SLONG = 9,
|
SLONG = 9,
|
||||||
SRATIONAL = 10,
|
SRATIONAL = 10,
|
||||||
|
FLOAT = 11,
|
||||||
|
DOUBLE = 12,
|
||||||
/// BigTIFF 64-bit unsigned integer
|
/// BigTIFF 64-bit unsigned integer
|
||||||
LONG8 = 16,
|
LONG8 = 16,
|
||||||
}
|
}
|
||||||
@@ -186,3 +190,12 @@ pub enum ResolutionUnit(u16) {
|
|||||||
Centimeter = 3,
|
Centimeter = 3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tags! {
|
||||||
|
pub enum SampleFormat(u16) unknown("An unknown extension sample format") {
|
||||||
|
Uint = 1,
|
||||||
|
Int = 2,
|
||||||
|
IEEEFP = 3,
|
||||||
|
Void = 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,8 @@ test_image_sum!(test_image_sum_u8, U8, u64);
|
|||||||
test_image_sum!(test_image_sum_u16, U16, u64);
|
test_image_sum!(test_image_sum_u16, U16, u64);
|
||||||
test_image_sum!(test_image_sum_u32, U32, u64);
|
test_image_sum!(test_image_sum_u32, U32, u64);
|
||||||
test_image_sum!(test_image_sum_u64, U64, u64);
|
test_image_sum!(test_image_sum_u64, U64, u64);
|
||||||
|
test_image_sum!(test_image_sum_f32, F32, f32);
|
||||||
|
test_image_sum!(test_image_sum_f64, F64, f64);
|
||||||
|
|
||||||
/// Tests that a decoder can be constructed for an image and the color type
|
/// Tests that a decoder can be constructed for an image and the color type
|
||||||
/// read from the IFD and is of the appropriate type, but the type is
|
/// read from the IFD and is of the appropriate type, but the type is
|
||||||
@@ -57,6 +59,11 @@ fn test_cmyk_u16() {
|
|||||||
test_image_sum_u16("cmyk-3c-16b.tiff", ColorType::CMYK(16), 2181426827);
|
test_image_sum_u16("cmyk-3c-16b.tiff", ColorType::CMYK(16), 2181426827);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cmyk_f32() {
|
||||||
|
test_image_sum_f32("cmyk-3c-32b-float.tiff", ColorType::CMYK(32), 496.0405);
|
||||||
|
}
|
||||||
|
|
||||||
#[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);
|
||||||
@@ -82,6 +89,16 @@ fn test_gray_u64() {
|
|||||||
test_image_sum_u64("gradient-1c-64b.tiff", ColorType::Gray(64), 549892913787);
|
test_image_sum_u64("gradient-1c-64b.tiff", ColorType::Gray(64), 549892913787);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gray_f32() {
|
||||||
|
test_image_sum_f32("gradient-1c-32b-float.tiff", ColorType::Gray(32), 128.03194);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gray_f64() {
|
||||||
|
test_image_sum_f64("gradient-1c-64b-float.tiff", ColorType::Gray(64), 128.0319210877642);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rgb_u8() {
|
fn test_rgb_u8() {
|
||||||
test_image_sum_u8("rgb-3c-8b.tiff", ColorType::RGB(8), 7842108);
|
test_image_sum_u8("rgb-3c-8b.tiff", ColorType::RGB(8), 7842108);
|
||||||
@@ -107,6 +124,11 @@ fn test_rgb_u64() {
|
|||||||
test_image_sum_u64("gradient-3c-64b.tiff", ColorType::RGB(64), 2030834111716);
|
test_image_sum_u64("gradient-3c-64b.tiff", ColorType::RGB(64), 2030834111716);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rgb_f32() {
|
||||||
|
test_image_sum_f32("gradient-3c-32b-float.tiff", ColorType::RGB(32), 472.8405);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_string_tags() {
|
fn test_string_tags() {
|
||||||
// these files have null-terminated strings for their Software tag. One has extra bytes after
|
// these files have null-terminated strings for their Software tag. One has extra bytes after
|
||||||
|
|||||||
@@ -98,6 +98,8 @@ test_roundtrip!(test_u8_roundtrip, U8, u8);
|
|||||||
test_roundtrip!(test_u16_roundtrip, U16, u16);
|
test_roundtrip!(test_u16_roundtrip, U16, u16);
|
||||||
test_roundtrip!(test_u32_roundtrip, U32, u32);
|
test_roundtrip!(test_u32_roundtrip, U32, u32);
|
||||||
test_roundtrip!(test_u64_roundtrip, U64, u64);
|
test_roundtrip!(test_u64_roundtrip, U64, u64);
|
||||||
|
test_roundtrip!(test_f32_roundtrip, F32, f32);
|
||||||
|
test_roundtrip!(test_f64_roundtrip, F64, f64);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_gray_u8_roundtrip() {
|
fn test_gray_u8_roundtrip() {
|
||||||
@@ -149,6 +151,26 @@ fn test_rgb_u64_roundtrip() {
|
|||||||
test_u64_roundtrip::<colortype::RGB64>("gradient-3c-64b.tiff", ColorType::RGB(64));
|
test_u64_roundtrip::<colortype::RGB64>("gradient-3c-64b.tiff", ColorType::RGB(64));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gray_f32_roundtrip() {
|
||||||
|
test_f32_roundtrip::<colortype::Gray32Float>("gradient-1c-32b-float.tiff", ColorType::Gray(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rgb_f32_roundtrip() {
|
||||||
|
test_f32_roundtrip::<colortype::RGB32Float>("gradient-3c-32b-float.tiff", ColorType::RGB(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cmyk_f32_roundtrip() {
|
||||||
|
test_f32_roundtrip::<colortype::CMYK32Float>("cmyk-3c-32b-float.tiff", ColorType::CMYK(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gray_f64_roundtrip() {
|
||||||
|
test_f64_roundtrip::<colortype::Gray64Float>("gradient-1c-64b-float.tiff", ColorType::Gray(64));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_multiple_byte() {
|
fn test_multiple_byte() {
|
||||||
let mut data = Cursor::new(Vec::new());
|
let mut data = Cursor::new(Vec::new());
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user