Merge remote-tracking branch 'image-tiff/master' into access-tile
This commit is contained in:
commit
d5e3f4aa8a
7 files changed
+142
-106
No files matched your search
+61
-25
@@ -4,8 +4,8 @@ use super::stream::{
|
|||||||
PackBitsReader,
|
PackBitsReader,
|
||||||
};
|
};
|
||||||
use super::tag_reader::TagReader;
|
use super::tag_reader::TagReader;
|
||||||
|
use super::{fp_predict_f32, fp_predict_f64, DecodingBuffer, Limits};
|
||||||
use super::{stream::SmartReader, ChunkType};
|
use super::{stream::SmartReader, ChunkType};
|
||||||
use super::{DecodingBuffer, Limits};
|
|
||||||
use crate::tags::{CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag};
|
use crate::tags::{CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag};
|
||||||
use crate::{ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
use crate::{ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
@@ -473,7 +473,20 @@ impl Image {
|
|||||||
| (ColorType::CMYK(n), _)
|
| (ColorType::CMYK(n), _)
|
||||||
| (ColorType::Gray(n), _)
|
| (ColorType::Gray(n), _)
|
||||||
if usize::from(n) == buffer.byte_len() * 8 => {}
|
if usize::from(n) == buffer.byte_len() * 8 => {}
|
||||||
(ColorType::Gray(n), DecodingBuffer::U8(_)) if n <= 8 => {}
|
(ColorType::Gray(n), DecodingBuffer::U8(_)) if n < 8 => match self.predictor {
|
||||||
|
Predictor::None => {}
|
||||||
|
Predictor::Horizontal => {
|
||||||
|
return Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::HorizontalPredictor(color_type),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Predictor::FloatingPoint => {
|
||||||
|
return Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::FloatingPointPredictor(color_type),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Predictor::__NonExhaustive => unreachable!(),
|
||||||
|
},
|
||||||
(type_, _) => {
|
(type_, _) => {
|
||||||
return Err(TiffError::UnsupportedError(
|
return Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::UnsupportedColorType(type_),
|
TiffUnsupportedError::UnsupportedColorType(type_),
|
||||||
@@ -481,10 +494,25 @@ impl Image {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let compressed_bytes = self
|
// Validate that the predictor is supported for the sample type.
|
||||||
.chunk_bytes
|
match (self.predictor, &buffer) {
|
||||||
.get(chunk_index)
|
(Predictor::Horizontal, DecodingBuffer::F32(_))
|
||||||
.ok_or(TiffError::FormatError(
|
| (Predictor::Horizontal, DecodingBuffer::F64(_)) => {
|
||||||
|
return Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::HorizontalPredictor(color_type),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
(Predictor::FloatingPoint, DecodingBuffer::F32(_))
|
||||||
|
| (Predictor::FloatingPoint, DecodingBuffer::F64(_)) => {}
|
||||||
|
(Predictor::FloatingPoint, _) => {
|
||||||
|
return Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::FloatingPointPredictor(color_type),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let compressed_bytes = self.chunk_bytes.get(chunk).ok_or(TiffError::FormatError(
|
||||||
TiffFormatError::InconsistentSizesEncountered,
|
TiffFormatError::InconsistentSizesEncountered,
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
@@ -507,10 +535,33 @@ impl Image {
|
|||||||
let tile = &mut buffer.as_bytes_mut()[..total_samples * byte_len];
|
let tile = &mut buffer.as_bytes_mut()[..total_samples * byte_len];
|
||||||
reader.read_exact(tile)?;
|
reader.read_exact(tile)?;
|
||||||
|
|
||||||
super::fix_endianness(&mut buffer.subrange(0..total_samples), byte_order);
|
for row in 0..(chunk_dimensions.1 - padding.1) {
|
||||||
|
let row_start = row * output_width * samples;
|
||||||
|
let row_end = (row + 1) * output_width * samples;
|
||||||
|
let row = buffer.subrange(row_start..row_end);
|
||||||
|
super::fix_endianness_and_predict(row, samples, byte_order, predictor);
|
||||||
|
}
|
||||||
if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
||||||
super::invert_colors(&mut buffer.subrange(0..total_samples), color_type);
|
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.
|
||||||
|
let mut encoded = vec![0u8; chunk_dimensions.0 * samples * byte_len];
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 {
|
} else {
|
||||||
for row in 0..chunk_info.data_height {
|
for row in 0..chunk_info.data_height {
|
||||||
let row_start = row * output_width * samples;
|
let row_start = row * output_width * samples;
|
||||||
@@ -525,29 +576,14 @@ impl Image {
|
|||||||
io::copy(&mut reader.by_ref().take(len), &mut io::sink())?;
|
io::copy(&mut reader.by_ref().take(len), &mut io::sink())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
super::fix_endianness(&mut buffer.subrange(row_start..row_end), byte_order);
|
let mut row = buffer.subrange(row_start..row_end);
|
||||||
|
super::fix_endianness_and_predict(row.copy(), samples, byte_order, predictor);
|
||||||
if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
||||||
super::invert_colors(&mut buffer.subrange(row_start..row_end), color_type);
|
super::invert_colors(&mut row, color_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match predictor {
|
|
||||||
Predictor::None => {}
|
|
||||||
Predictor::Horizontal => {
|
|
||||||
super::rev_hpredict(
|
|
||||||
buffer,
|
|
||||||
(
|
|
||||||
u32::try_from(chunk_info.data_width)?,
|
|
||||||
u32::try_from(chunk_info.data_height)?,
|
|
||||||
),
|
|
||||||
output_width,
|
|
||||||
color_type,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
Predictor::__NonExhaustive => unreachable!(),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+64
-81
@@ -360,95 +360,78 @@ impl Wrapping for i64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rev_hpredict_nsamp<T>(
|
fn rev_hpredict_nsamp<T: Copy + Wrapping>(image: &mut [T], samples: usize) {
|
||||||
image: &mut [T],
|
for col in samples..image.len() {
|
||||||
size: (u32, u32), // Size of the block
|
image[col] = image[col].wrapping_add(image[col - samples]);
|
||||||
img_width: usize, // Width of the image (this distinction is needed for tiles)
|
|
||||||
samples: usize,
|
|
||||||
) -> TiffResult<()>
|
|
||||||
where
|
|
||||||
T: Copy + Wrapping,
|
|
||||||
{
|
|
||||||
let width = usize::try_from(size.0)?;
|
|
||||||
let height = usize::try_from(size.1)?;
|
|
||||||
for row in 0..height {
|
|
||||||
for col in samples..width * samples {
|
|
||||||
let prev_pixel = image[(row * img_width * samples + col - samples)];
|
|
||||||
let pixel = &mut image[(row * img_width * samples + col)];
|
|
||||||
*pixel = pixel.wrapping_add(prev_pixel);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rev_hpredict(
|
pub fn fp_predict_f32(input: &mut [u8], output: &mut [f32], samples: usize) {
|
||||||
image: DecodingBuffer,
|
rev_hpredict_nsamp(input, samples);
|
||||||
size: (u32, u32),
|
for i in 0..output.len() {
|
||||||
img_width: usize,
|
// TODO: use f32::from_be_bytes() when we can (version 1.40)
|
||||||
color_type: ColorType,
|
output[i] = f32::from_bits(u32::from_be_bytes([
|
||||||
) -> TiffResult<()> {
|
input[input.len() / 4 * 0 + i],
|
||||||
// TODO: use bits_per_sample.len() after implementing type 3 predictor
|
input[input.len() / 4 * 1 + i],
|
||||||
let samples = match color_type {
|
input[input.len() / 4 * 2 + i],
|
||||||
ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) | ColorType::Gray(64) => 1,
|
input[input.len() / 4 * 3 + i],
|
||||||
ColorType::RGB(8) | ColorType::RGB(16) | ColorType::RGB(32) | ColorType::RGB(64) => 3,
|
]));
|
||||||
ColorType::RGBA(8)
|
|
||||||
| ColorType::RGBA(16)
|
|
||||||
| ColorType::RGBA(32)
|
|
||||||
| ColorType::RGBA(64)
|
|
||||||
| ColorType::CMYK(8)
|
|
||||||
| ColorType::CMYK(16)
|
|
||||||
| ColorType::CMYK(32)
|
|
||||||
| ColorType::CMYK(64) => 4,
|
|
||||||
_ => {
|
|
||||||
return Err(TiffError::UnsupportedError(
|
|
||||||
TiffUnsupportedError::HorizontalPredictor(color_type),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
byte_order: ByteOrder,
|
||||||
|
predictor: Predictor,
|
||||||
|
) {
|
||||||
|
match predictor {
|
||||||
|
Predictor::None => {
|
||||||
|
fix_endianness(&mut image, byte_order);
|
||||||
|
}
|
||||||
|
Predictor::Horizontal => {
|
||||||
|
fix_endianness(&mut image, byte_order);
|
||||||
match image {
|
match image {
|
||||||
DecodingBuffer::U8(buf) => {
|
DecodingBuffer::U8(buf) => rev_hpredict_nsamp(buf, samples),
|
||||||
rev_hpredict_nsamp(buf, size, img_width, samples)?;
|
DecodingBuffer::U16(buf) => rev_hpredict_nsamp(buf, samples),
|
||||||
}
|
DecodingBuffer::U32(buf) => rev_hpredict_nsamp(buf, samples),
|
||||||
DecodingBuffer::U16(buf) => {
|
DecodingBuffer::U64(buf) => rev_hpredict_nsamp(buf, samples),
|
||||||
rev_hpredict_nsamp(buf, size, img_width, samples)?;
|
DecodingBuffer::I8(buf) => rev_hpredict_nsamp(buf, samples),
|
||||||
}
|
DecodingBuffer::I16(buf) => rev_hpredict_nsamp(buf, samples),
|
||||||
DecodingBuffer::U32(buf) => {
|
DecodingBuffer::I32(buf) => rev_hpredict_nsamp(buf, samples),
|
||||||
rev_hpredict_nsamp(buf, size, img_width, samples)?;
|
DecodingBuffer::I64(buf) => rev_hpredict_nsamp(buf, samples),
|
||||||
}
|
DecodingBuffer::F32(_) | DecodingBuffer::F64(_) => {
|
||||||
DecodingBuffer::U64(buf) => {
|
unreachable!("Caller should have validated arguments. Please file a bug.")
|
||||||
rev_hpredict_nsamp(buf, size, img_width, samples)?;
|
|
||||||
}
|
|
||||||
DecodingBuffer::F32(_buf) => {
|
|
||||||
// FIXME: check how this is defined.
|
|
||||||
// See issue #89.
|
|
||||||
// rev_hpredict_nsamp(buf, size, img_width,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, img_width,samples)?;
|
|
||||||
return Err(TiffError::UnsupportedError(
|
|
||||||
TiffUnsupportedError::HorizontalPredictor(color_type),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
DecodingBuffer::I8(buf) => {
|
|
||||||
rev_hpredict_nsamp(buf, size, img_width, samples)?;
|
|
||||||
}
|
|
||||||
DecodingBuffer::I16(buf) => {
|
|
||||||
rev_hpredict_nsamp(buf, size, img_width, samples)?;
|
|
||||||
}
|
|
||||||
DecodingBuffer::I32(buf) => {
|
|
||||||
rev_hpredict_nsamp(buf, size, img_width, samples)?;
|
|
||||||
}
|
|
||||||
DecodingBuffer::I64(buf) => {
|
|
||||||
rev_hpredict_nsamp(buf, size, img_width, samples)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
}
|
||||||
|
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!("Caller should have validated arguments. Please file a bug."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Predictor::__NonExhaustive => unreachable!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invert_colors_unsigned<T>(buffer: &mut [T], max: T)
|
fn invert_colors_unsigned<T>(buffer: &mut [T], max: T)
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ impl fmt::Display for TiffFormatError {
|
|||||||
/// stability guarantees.
|
/// stability guarantees.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum TiffUnsupportedError {
|
pub enum TiffUnsupportedError {
|
||||||
|
FloatingPointPredictor(ColorType),
|
||||||
HorizontalPredictor(ColorType),
|
HorizontalPredictor(ColorType),
|
||||||
InterpretationWithBits(PhotometricInterpretation, Vec<u8>),
|
InterpretationWithBits(PhotometricInterpretation, Vec<u8>),
|
||||||
UnknownInterpretation,
|
UnknownInterpretation,
|
||||||
@@ -156,6 +157,11 @@ impl fmt::Display for TiffUnsupportedError {
|
|||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
use self::TiffUnsupportedError::*;
|
use self::TiffUnsupportedError::*;
|
||||||
match *self {
|
match *self {
|
||||||
|
FloatingPointPredictor(color_type) => write!(
|
||||||
|
fmt,
|
||||||
|
"Floating point predictor for {:?} is unsupported.",
|
||||||
|
color_type
|
||||||
|
),
|
||||||
HorizontalPredictor(color_type) => write!(
|
HorizontalPredictor(color_type) => write!(
|
||||||
fmt,
|
fmt,
|
||||||
"Horizontal predictor for {:?} is unsupported.",
|
"Horizontal predictor for {:?} is unsupported.",
|
||||||
|
|||||||
@@ -214,6 +214,7 @@ tags! {
|
|||||||
pub enum Predictor(u16) {
|
pub enum Predictor(u16) {
|
||||||
None = 1,
|
None = 1,
|
||||||
Horizontal = 2,
|
Horizontal = 2,
|
||||||
|
FloatingPoint = 3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -494,3 +494,13 @@ fn timeout() {
|
|||||||
fn test_no_rows_per_strip() {
|
fn test_no_rows_per_strip() {
|
||||||
test_image_sum_u8("no_rows_per_strip.tiff", ColorType::RGB(8), 99448840);
|
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.
Reference in new issue
Block a user