Support floating point predictor (predictor 3)

Based on work by @TomasKralCZ in #135
This commit is contained in:
Jonathan Behrens committed 2022-06-22 20:14:25 -07:00
1 parent 8a6e93f66e
commit 8e5c5b44c5
7 files changed
+86 -1

No files matched your search

+31 -1
View File
@@ -4,8 +4,8 @@ use super::stream::{
PackBitsReader,
};
use super::tag_reader::TagReader;
use super::{fp_predict_f32, fp_predict_f64, DecodingBuffer, Limits};
use super::{stream::SmartReader, ChunkType};
use super::{DecodingBuffer, Limits};
use crate::tags::{CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag};
use crate::{ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
use std::convert::{TryFrom, TryInto};
@@ -406,6 +406,11 @@ impl Image {
TiffUnsupportedError::HorizontalPredictor(color_type),
))
}
Predictor::FloatingPoint => {
return Err(TiffError::UnsupportedError(
TiffUnsupportedError::FloatingPointPredictor(color_type),
));
}
Predictor::__NonExhaustive => unreachable!(),
},
(type_, _) => {
@@ -423,6 +428,13 @@ impl Image {
TiffUnsupportedError::HorizontalPredictor(color_type),
));
}
(Predictor::FloatingPoint, DecodingBuffer::F32(_))
| (Predictor::FloatingPoint, DecodingBuffer::F64(_)) => {}
(Predictor::FloatingPoint, _) => {
return Err(TiffError::UnsupportedError(
TiffUnsupportedError::FloatingPointPredictor(color_type),
));
}
_ => {}
}
@@ -471,6 +483,24 @@ impl Image {
if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
super::invert_colors(&mut buffer.subrange(0..total_samples), color_type);
}
} else if padding.0 > 0 && self.predictor == Predictor::FloatingPoint {
// The floating point predictor shuffles the padding bytes into the encoded output, so
// this case is handled specially when needed.
for row in 0..(chunk_dimensions.1 - padding.1) {
let row_start = row * output_width * samples;
let row_end = row_start + (chunk_dimensions.0 - padding.0) * samples;
let mut encoded = vec![0u8; chunk_dimensions.0 * samples * byte_len];
reader.read_exact(&mut encoded)?;
match buffer.subrange(row_start..row_end) {
DecodingBuffer::F32(buf) => fp_predict_f32(&mut encoded, buf, samples),
DecodingBuffer::F64(buf) => fp_predict_f64(&mut encoded, buf, samples),
_ => unreachable!(),
}
if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
super::invert_colors(&mut buffer.subrange(row_start..row_end), color_type);
}
}
} else {
for row in 0..(chunk_dimensions.1 - padding.1) {
let row_start = row * output_width * samples;
+38
View File
@@ -365,6 +365,36 @@ fn rev_hpredict_nsamp<T: Copy + Wrapping>(image: &mut [T], samples: usize) {
}
}
pub fn fp_predict_f32(input: &mut [u8], output: &mut [f32], samples: usize) {
rev_hpredict_nsamp(input, samples);
for i in 0..output.len() {
// TODO: use f32::from_be_bytes() when we can (version 1.40)
output[i] = f32::from_bits(u32::from_be_bytes([
input[input.len() / 4 * 0 + i],
input[input.len() / 4 * 1 + i],
input[input.len() / 4 * 2 + i],
input[input.len() / 4 * 3 + i],
]));
}
}
pub fn fp_predict_f64(input: &mut [u8], output: &mut [f64], samples: usize) {
rev_hpredict_nsamp(input, samples);
for i in 0..output.len() {
// TODO: use f64::from_be_bytes() when we can (version 1.40)
output[i] = f64::from_bits(u64::from_be_bytes([
input[input.len() / 8 * 0 + i],
input[input.len() / 8 * 1 + i],
input[input.len() / 8 * 2 + i],
input[input.len() / 8 * 3 + i],
input[input.len() / 8 * 4 + i],
input[input.len() / 8 * 5 + i],
input[input.len() / 8 * 6 + i],
input[input.len() / 8 * 7 + i],
]));
}
}
fn fix_endianness_and_predict(
mut image: DecodingBuffer,
samples: usize,
@@ -389,6 +419,14 @@ fn fix_endianness_and_predict(
DecodingBuffer::F32(_) | DecodingBuffer::F64(_) => unreachable!(),
}
}
Predictor::FloatingPoint => {
let mut buffer_copy = image.as_bytes_mut().to_vec();
match image {
DecodingBuffer::F32(buf) => fp_predict_f32(&mut buffer_copy, buf, samples),
DecodingBuffer::F64(buf) => fp_predict_f64(&mut buffer_copy, buf, samples),
_ => unreachable!(),
}
}
Predictor::__NonExhaustive => unreachable!(),
}
}
+6
View File
@@ -134,6 +134,7 @@ impl fmt::Display for TiffFormatError {
/// stability guarantees.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TiffUnsupportedError {
FloatingPointPredictor(ColorType),
HorizontalPredictor(ColorType),
InterpretationWithBits(PhotometricInterpretation, Vec<u8>),
UnknownInterpretation,
@@ -154,6 +155,11 @@ impl fmt::Display for TiffUnsupportedError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
use self::TiffUnsupportedError::*;
match *self {
FloatingPointPredictor(color_type) => write!(
fmt,
"Floating point predictor for {:?} is unsupported.",
color_type
),
HorizontalPredictor(color_type) => write!(
fmt,
"Horizontal predictor for {:?} is unsupported.",
+1
View File
@@ -214,6 +214,7 @@ tags! {
pub enum Predictor(u16) {
None = 1,
Horizontal = 2,
FloatingPoint = 3,
}
}
+10
View File
@@ -494,3 +494,13 @@ fn timeout() {
fn test_no_rows_per_strip() {
test_image_sum_u8("no_rows_per_strip.tiff", ColorType::RGB(8), 99448840);
}
#[test]
fn test_predictor_3_rgb_f32() {
test_image_sum_f32("predictor-3-rgb-f32.tif", ColorType::RGB(32), 54004.33);
}
#[test]
fn test_predictor_3_gray_f32() {
test_image_sum_f32("predictor-3-gray-f32.tif", ColorType::Gray(32), 20008.275);
}
Binary file not shown.
Binary file not shown.