Merge pull request #70 from aschampion/features/32-bit-float-samples

Floating point sample support
This commit is contained in:
HeroicKatora authored and GitHub committed 2020-09-01 17:28:10 +02:00
commit 7928c219dd
17 files changed
+515 -23

No files matched your search

+11
View File
@@ -0,0 +1,11 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
[requires]
python_version = "3.7"
+4 -4
View File
@@ -17,12 +17,12 @@ This table lists photometric interpretations and sample formats which are suppor
| `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) | |
| `WhiteIsZero` | Gray(8\|16\|32\|64) | Gray(32\|64) |
| `BlackIsZero` | Gray(8\|16\|32\|64) | Gray(32\|64) |
| `RGB` | RGB(8\|16\|32\|64), RGBA(8\|16\|32\|64) | RGB(32\|64), RGBA(32\|64) |
| `RGBPalette` | | |
| `Mask` | | |
| `CMYK` | CMYK(8\|16\|32\|64) | |
| `CMYK` | CMYK(8\|16\|32\|64) | CMYK(32\|64) |
| `YCbCr` | | |
| `CIELab` | | |
+2
View File
@@ -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!(i32, i32_as_ne_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
View File
@@ -9,16 +9,18 @@ use super::stream::{ByteOrder, EndianReader, SmartReader};
use tags::{Tag, Type};
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)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Signed(i32),
SignedBig(i64),
Unsigned(u32),
UnsignedBig(u64),
Float(f32),
Double(f64),
List(Vec<Value>),
Rational(u32, u32),
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>> {
match self {
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>> {
match self {
List(vec) => {
@@ -337,6 +389,12 @@ impl Entry {
(Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
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| {
Ok(Rational(decoder.read_long()?, decoder.read_long()?))
}),
+151 -3
View File
@@ -6,7 +6,7 @@ use std::cmp;
use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
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};
@@ -24,6 +24,10 @@ pub enum DecodingResult {
U32(Vec<u32>),
/// A vector of 64 bit unsigned ints
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 {
@@ -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 {
match *self {
DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]),
DecodingResult::U16(ref mut buf) => DecodingBuffer::U16(&mut buf[start..]),
DecodingResult::U32(ref mut buf) => DecodingBuffer::U32(&mut buf[start..]),
DecodingResult::U64(ref mut buf) => DecodingBuffer::U64(&mut buf[start..]),
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]),
/// A slice of 64 bit unsigned ints
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> {
@@ -88,6 +114,8 @@ impl<'a> DecodingBuffer<'a> {
DecodingBuffer::U16(ref buf) => buf.len(),
DecodingBuffer::U32(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::U32(_) => 4,
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::U32(ref mut buf) => DecodingBuffer::U32(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,
bits_per_sample: Vec<u8>,
samples: u8,
sample_format: Vec<SampleFormat>,
photometric_interpretation: PhotometricInterpretation,
compression_method: CompressionMethod,
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<()>
where
T: Copy + Wrapping,
@@ -239,6 +284,22 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType)
DecodingBuffer::U64(buf) => {
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(())
}
@@ -257,6 +318,7 @@ impl<R: Read + Seek> Decoder<R> {
height: 0,
bits_per_sample: vec![1],
samples: 1,
sample_format: vec![SampleFormat::Uint],
photometric_interpretation: PhotometricInterpretation::BlackIsZero,
compression_method: CompressionMethod::None,
strip_decoder: None,
@@ -388,6 +450,16 @@ impl<R: Read + Seek> Decoder<R> {
if let Some(val) = self.find_tag_unsigned(Tag::SamplesPerPixel)? {
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 {
1 | 3 | 4 => if let Some(val) = self.find_tag_unsigned_vec(Tag::BitsPerSample)? {
self.bits_per_sample = val;
@@ -448,12 +520,23 @@ impl<R: Read + Seek> Decoder<R> {
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]
pub fn read_long8(&mut self) -> Result<u64, io::Error> {
self.reader.read_u64()
}
/// Reads a string
#[inline]
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()
}
/// 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.
pub fn get_tag_u32_vec(&mut self, tag: Tag) -> TiffResult<Vec<u32>> {
self.get_tag(tag)?.into_u32_vec()
@@ -628,6 +721,16 @@ impl<R: Read + Seek> Decoder<R> {
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.
/// Returns the number of bytes read.
fn expand_strip<'a>(
@@ -700,6 +803,18 @@ impl<R: Read + Seek> Decoder<R> {
reader.read_u32_into(&mut buffer[..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::RGB(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
}
(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_, _) => {
return Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedColorType(type_),
@@ -854,7 +989,9 @@ impl<R: Read + Seek> Decoder<R> {
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) {
let max_sample_bits = self.bits_per_sample.iter().cloned().max().unwrap_or(8);
match self.sample_format.first().unwrap_or(&SampleFormat::Uint) {
SampleFormat::Uint => match max_sample_bits {
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),
@@ -864,6 +1001,17 @@ impl<R: Read + Seek> Decoder<R> {
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()),
}
}
+54
View File
@@ -122,6 +122,60 @@ pub trait EndianReader: Read {
}
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
+85 -3
View File
@@ -1,4 +1,4 @@
use tags::PhotometricInterpretation;
use tags::{PhotometricInterpretation, SampleFormat};
/// Trait for different colortypes that can be encoded.
pub trait ColorType {
@@ -8,6 +8,8 @@ pub trait ColorType {
const TIFF_VALUE: PhotometricInterpretation;
/// The value of the tiff tag `BitsPerSample`
const BITS_PER_SAMPLE: &'static [u16];
/// The value of the tiff tag `SampleFormat`
const SAMPLE_FORMAT: &'static [SampleFormat];
}
pub struct Gray8;
@@ -15,6 +17,7 @@ impl ColorType for Gray8 {
type Inner = u8;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
const BITS_PER_SAMPLE: &'static [u16] = &[8];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
}
pub struct Gray16;
@@ -22,6 +25,7 @@ impl ColorType for Gray16 {
type Inner = u16;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
const BITS_PER_SAMPLE: &'static [u16] = &[16];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
}
pub struct Gray32;
@@ -29,6 +33,15 @@ impl ColorType for Gray32 {
type Inner = u32;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
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;
@@ -36,6 +49,15 @@ impl ColorType for Gray64 {
type Inner = u64;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
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;
@@ -43,6 +65,7 @@ impl ColorType for RGB8 {
type Inner = u8;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 3];
}
pub struct RGB16;
@@ -50,6 +73,7 @@ impl ColorType for RGB16 {
type Inner = u16;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 3];
}
pub struct RGB32;
@@ -57,6 +81,15 @@ impl ColorType for RGB32 {
type Inner = u32;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
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;
@@ -64,6 +97,15 @@ impl ColorType for RGB64 {
type Inner = u64;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
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;
@@ -71,6 +113,7 @@ impl ColorType for RGBA8 {
type Inner = u8;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
}
pub struct RGBA16;
@@ -78,20 +121,39 @@ impl ColorType for RGBA16 {
type Inner = u16;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
}
pub struct RGBA32;
impl ColorType for RGBA32 {
type Inner = u16;
type Inner = u32;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
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;
impl ColorType for RGBA64 {
type Inner = u16;
type Inner = u64;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
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;
@@ -99,6 +161,7 @@ impl ColorType for CMYK8 {
type Inner = u8;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8, 8];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
}
pub struct CMYK16;
@@ -106,6 +169,7 @@ impl ColorType for CMYK16 {
type Inner = u16;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 4];
}
pub struct CMYK32;
@@ -113,6 +177,15 @@ impl ColorType for CMYK32 {
type Inner = u32;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
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;
@@ -120,4 +193,13 @@ impl ColorType for CMYK64 {
type Inner = u64;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::CMYK;
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];
}
+62
View File
@@ -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] {
const BYTE_LEN: u32 = 8;
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 {
const BYTE_LEN: u32 = 8;
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::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::RowsPerStrip, u32::try_from(rows_per_strip)?)?;
+14
View File
@@ -81,6 +81,20 @@ impl<W: Write> TiffWriter<W> {
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> {
if self.offset % 4 != 0 {
let padding = [0, 0, 0];
+6 -2
View File
@@ -4,7 +4,7 @@ use std::io;
use std::string;
use decoder::ifd::{Value};
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Tag};
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag};
use miniz_oxide::inflate::TINFLStatus;
use ColorType;
@@ -35,7 +35,7 @@ pub enum TiffError {
///
/// The list of variants may grow to incorporate errors of future features. Matching against this
/// exhaustively is not covered by interface stability guarantees.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq)]
pub enum TiffFormatError {
TiffSignatureNotFound,
TiffSignatureInvalid,
@@ -114,6 +114,7 @@ pub enum TiffUnsupportedError {
UnknownCompressionMethod,
UnsupportedCompressionMethod(CompressionMethod),
UnsupportedSampleDepth(u8),
UnsupportedSampleFormat(Vec<SampleFormat>),
UnsupportedColorType(ColorType),
UnsupportedBitsPerChannel(u8),
UnsupportedPlanarConfig(Option<PlanarConfiguration>),
@@ -148,6 +149,9 @@ impl fmt::Display for TiffUnsupportedError {
UnsupportedSampleDepth(samples) => {
write!(fmt, "{} samples per pixel is unsupported.", samples)
}
UnsupportedSampleFormat(ref formats) => {
write!(fmt, "Sample format {:?} is unsupported.", formats)
}
UnsupportedColorType(color_type) => {
write!(fmt, "Color type {:?} is unsupported", color_type)
}
+22 -9
View File
@@ -39,16 +39,24 @@ macro_rules! tags {
}
}
tags!($name, $ty);
tags!($name, $ty, $($unknown_doc)*);
};
// For u16 tags, provide direct inherent primitive conversion methods.
($name:tt, u16) => {
($name:tt, u16, $($unknown_doc:literal)*) => {
impl $name {
#[inline(always)]
pub fn from_u16(val: u16) -> Option<Self> {
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)]
pub fn to_u16(&self) -> u16 {
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
// 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
@@ -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! {
/// The type of an IFD entry (a 2 byte field).
pub enum Type(u16) {
@@ -131,6 +133,8 @@ pub enum Type(u16) {
SSHORT = 8,
SLONG = 9,
SRATIONAL = 10,
FLOAT = 11,
DOUBLE = 12,
/// BigTIFF 64-bit unsigned integer
LONG8 = 16,
}
@@ -186,3 +190,12 @@ pub enum ResolutionUnit(u16) {
Centimeter = 3,
}
}
tags! {
pub enum SampleFormat(u16) unknown("An unknown extension sample format") {
Uint = 1,
Int = 2,
IEEEFP = 3,
Void = 4,
}
}
+22
View File
@@ -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_u32, U32, 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
/// 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]
fn test_cmyk_f32() {
test_image_sum_f32("cmyk-3c-32b-float.tiff", ColorType::CMYK(32), 496.0405);
}
#[test]
fn test_gray_u8() {
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]
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]
fn test_rgb_u8() {
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]
fn test_rgb_f32() {
test_image_sum_f32("gradient-3c-32b-float.tiff", ColorType::RGB(32), 472.8405);
}
#[test]
fn test_string_tags() {
// these files have null-terminated strings for their Software tag. One has extra bytes after
+22
View File
@@ -98,6 +98,8 @@ test_roundtrip!(test_u8_roundtrip, U8, u8);
test_roundtrip!(test_u16_roundtrip, U16, u16);
test_roundtrip!(test_u32_roundtrip, U32, u32);
test_roundtrip!(test_u64_roundtrip, U64, u64);
test_roundtrip!(test_f32_roundtrip, F32, f32);
test_roundtrip!(test_f64_roundtrip, F64, f64);
#[test]
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]
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]
fn test_multiple_byte() {
let mut data = Cursor::new(Vec::new());
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.