Add support for IEEEFP Gray(32) decoding
This commit is contained in:
1 parent
119dec5068
commit
bbc49291e4
7 files changed
+115
-13
No files matched your search
@@ -17,8 +17,8 @@ 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) |
|
||||||
| `BlackIsZero` | Gray(8\|16\|32\|64) | |
|
| `BlackIsZero` | Gray(8\|16\|32\|64) | Gray(32) |
|
||||||
| `RGB` | RGB(8\|16\|32\|64), RGBA(8\|16\|32\|64) | |
|
| `RGB` | RGB(8\|16\|32\|64), RGBA(8\|16\|32\|64) | |
|
||||||
| `RGBPalette` | | |
|
| `RGBPalette` | | |
|
||||||
| `Mask` | | |
|
| `Mask` | | |
|
||||||
|
|||||||
+72
-11
@@ -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,8 @@ 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>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DecodingResult {
|
impl DecodingResult {
|
||||||
@@ -59,12 +61,21 @@ 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]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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..]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,6 +90,8 @@ 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]),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DecodingBuffer<'a> {
|
impl<'a> DecodingBuffer<'a> {
|
||||||
@@ -88,6 +101,7 @@ 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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +111,7 @@ 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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +124,7 @@ 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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,6 +180,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>,
|
||||||
@@ -197,6 +214,12 @@ impl Wrapping for u64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Wrapping for f32 {
|
||||||
|
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,
|
||||||
@@ -238,6 +261,9 @@ 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) => {
|
||||||
|
rev_hpredict_nsamp(buf, size, samples)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -255,6 +281,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,
|
||||||
@@ -370,6 +397,18 @@ 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.iter()
|
||||||
|
.map(|&v| SampleFormat::from_u16(v)
|
||||||
|
.ok_or_else(|| TiffUnsupportedError::UnsupportedSampleFormat(vals.clone()))
|
||||||
|
)
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
|
// FIXME: 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(vals).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;
|
||||||
@@ -675,6 +714,16 @@ 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
|
||||||
|
}
|
||||||
(type_, _) => {
|
(type_, _) => {
|
||||||
return Err(TiffError::UnsupportedError(
|
return Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::UnsupportedColorType(type_),
|
TiffUnsupportedError::UnsupportedColorType(type_),
|
||||||
@@ -787,16 +836,28 @@ 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),
|
||||||
|
n => {
|
||||||
|
Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
format => Err(TiffUnsupportedError::UnsupportedSampleFormat(vec![format.to_u16()]).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,27 @@ pub trait EndianReader: Read {
|
|||||||
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_i32::<BigEndian>(self),
|
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_i32::<BigEndian>(self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads an f32
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_f32(&mut self) -> Result<f32, io::Error> {
|
||||||
|
match self.byte_order() {
|
||||||
|
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_f32::<LittleEndian>(self),
|
||||||
|
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_f32::<BigEndian>(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_f32_into(&mut self, buffer: &mut [f32]) -> Result<(), io::Error> {
|
||||||
|
match self.byte_order() {
|
||||||
|
ByteOrder::LittleEndian => {
|
||||||
|
<Self as ReadBytesExt>::read_f32_into::<LittleEndian>(self, buffer)
|
||||||
|
}
|
||||||
|
ByteOrder::BigEndian => {
|
||||||
|
<Self as ReadBytesExt>::read_f32_into::<BigEndian>(self, buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reader that decompresses DEFLATE streams
|
/// Reader that decompresses DEFLATE streams
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ pub enum TiffUnsupportedError {
|
|||||||
UnknownCompressionMethod,
|
UnknownCompressionMethod,
|
||||||
UnsupportedCompressionMethod(CompressionMethod),
|
UnsupportedCompressionMethod(CompressionMethod),
|
||||||
UnsupportedSampleDepth(u8),
|
UnsupportedSampleDepth(u8),
|
||||||
|
UnsupportedSampleFormat(Vec<u16>),
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|||||||
+10
@@ -95,6 +95,7 @@ pub enum Tag(u16) unknown("A private or extension tag") {
|
|||||||
PlanarConfiguration = 284,
|
PlanarConfiguration = 284,
|
||||||
ResolutionUnit = 296, // TODO add support
|
ResolutionUnit = 296, // TODO add support
|
||||||
RowsPerStrip = 278,
|
RowsPerStrip = 278,
|
||||||
|
SampleFormat = 339,
|
||||||
SamplesPerPixel = 277,
|
SamplesPerPixel = 277,
|
||||||
Software = 305,
|
Software = 305,
|
||||||
StripByteCounts = 279,
|
StripByteCounts = 279,
|
||||||
@@ -181,3 +182,12 @@ pub enum ResolutionUnit(u16) {
|
|||||||
Centimeter = 3,
|
Centimeter = 3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tags! {
|
||||||
|
pub enum SampleFormat(u16) {
|
||||||
|
UINT = 1,
|
||||||
|
INT = 2,
|
||||||
|
IEEEFP = 3,
|
||||||
|
VOID = 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ 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);
|
||||||
|
|
||||||
/// 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
|
||||||
@@ -82,6 +83,11 @@ 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]
|
#[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);
|
||||||
|
|||||||
Binary file not shown.
Reference in new issue
Block a user