Make decoding internally operate on byte buffers (#230)
This commit is contained in:
1 parent
307cd8be97
commit
6dc7a266d3
4 files changed
+218
-260
No files matched your search
+72
-60
@@ -1,7 +1,7 @@
|
|||||||
use super::ifd::{Directory, Value};
|
use super::ifd::{Directory, Value};
|
||||||
use super::stream::{ByteOrder, DeflateReader, LZWReader, PackBitsReader};
|
use super::stream::{ByteOrder, DeflateReader, LZWReader, PackBitsReader};
|
||||||
use super::tag_reader::TagReader;
|
use super::tag_reader::TagReader;
|
||||||
use super::{fp_predict_f32, fp_predict_f64, DecodingBuffer, Limits};
|
use super::{predict_f32, predict_f64, Limits};
|
||||||
use super::{stream::SmartReader, ChunkType};
|
use super::{stream::SmartReader, ChunkType};
|
||||||
use crate::tags::{
|
use crate::tags::{
|
||||||
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, SampleFormat, Tag,
|
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, SampleFormat, Tag,
|
||||||
@@ -64,7 +64,6 @@ pub(crate) struct Image {
|
|||||||
pub width: u32,
|
pub width: u32,
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
pub bits_per_sample: u8,
|
pub bits_per_sample: u8,
|
||||||
#[allow(unused)]
|
|
||||||
pub samples: u16,
|
pub samples: u16,
|
||||||
pub sample_format: SampleFormat,
|
pub sample_format: SampleFormat,
|
||||||
pub photometric_interpretation: PhotometricInterpretation,
|
pub photometric_interpretation: PhotometricInterpretation,
|
||||||
@@ -171,8 +170,9 @@ impl Image {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This library (and libtiff) do not support mixed sample formats.
|
// This library (and libtiff) do not support mixed sample formats and zero bits per sample
|
||||||
if bits_per_sample.iter().any(|&b| b != bits_per_sample[0]) {
|
// doesn't make sense.
|
||||||
|
if bits_per_sample.iter().any(|&b| b != bits_per_sample[0]) || bits_per_sample[0] == 0 {
|
||||||
return Err(TiffUnsupportedError::InconsistentBitsPerSample(bits_per_sample).into());
|
return Err(TiffUnsupportedError::InconsistentBitsPerSample(bits_per_sample).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,35 +546,29 @@ impl Image {
|
|||||||
pub(crate) fn expand_chunk(
|
pub(crate) fn expand_chunk(
|
||||||
&self,
|
&self,
|
||||||
reader: impl Read,
|
reader: impl Read,
|
||||||
mut buffer: DecodingBuffer,
|
buf: &mut [u8],
|
||||||
output_width: usize,
|
output_row_stride: usize,
|
||||||
byte_order: ByteOrder,
|
byte_order: ByteOrder,
|
||||||
chunk_index: u32,
|
chunk_index: u32,
|
||||||
limits: &Limits,
|
limits: &Limits,
|
||||||
) -> TiffResult<()> {
|
) -> TiffResult<()> {
|
||||||
// Validate that the provided buffer is of the expected type.
|
// Validate that the color type is supported.
|
||||||
let color_type = self.colortype()?;
|
let color_type = self.colortype()?;
|
||||||
match (color_type, &buffer) {
|
match color_type {
|
||||||
(ColorType::RGB(n), _)
|
ColorType::RGB(n)
|
||||||
| (ColorType::RGBA(n), _)
|
| ColorType::RGBA(n)
|
||||||
| (ColorType::CMYK(n), _)
|
| ColorType::CMYK(n)
|
||||||
| (ColorType::YCbCr(n), _)
|
| ColorType::YCbCr(n)
|
||||||
| (ColorType::Gray(n), _)
|
| ColorType::Gray(n)
|
||||||
| (
|
| ColorType::Multiband {
|
||||||
ColorType::Multiband {
|
|
||||||
bit_depth: n,
|
bit_depth: n,
|
||||||
num_samples: _,
|
num_samples: _,
|
||||||
},
|
} if n == 8 || n == 16 || n == 32 || n == 64 => {}
|
||||||
_,
|
|
||||||
) if usize::from(n) == buffer.byte_len() * 8 => {}
|
|
||||||
(
|
|
||||||
ColorType::Gray(n)
|
ColorType::Gray(n)
|
||||||
| ColorType::Multiband {
|
| ColorType::Multiband {
|
||||||
bit_depth: n,
|
bit_depth: n,
|
||||||
num_samples: _,
|
num_samples: _,
|
||||||
},
|
} if n < 8 => match self.predictor {
|
||||||
DecodingBuffer::U8(_),
|
|
||||||
) if n < 8 => match self.predictor {
|
|
||||||
Predictor::None => {}
|
Predictor::None => {}
|
||||||
Predictor::Horizontal => {
|
Predictor::Horizontal => {
|
||||||
return Err(TiffError::UnsupportedError(
|
return Err(TiffError::UnsupportedError(
|
||||||
@@ -587,7 +581,7 @@ impl Image {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(type_, _) => {
|
type_ => {
|
||||||
return Err(TiffError::UnsupportedError(
|
return Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::UnsupportedColorType(type_),
|
TiffUnsupportedError::UnsupportedColorType(type_),
|
||||||
));
|
));
|
||||||
@@ -595,15 +589,14 @@ impl Image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate that the predictor is supported for the sample type.
|
// Validate that the predictor is supported for the sample type.
|
||||||
match (self.predictor, &buffer) {
|
match (self.predictor, self.sample_format) {
|
||||||
(Predictor::Horizontal, DecodingBuffer::F32(_))
|
(Predictor::Horizontal, SampleFormat::Int | SampleFormat::Uint) => {}
|
||||||
| (Predictor::Horizontal, DecodingBuffer::F64(_)) => {
|
(Predictor::Horizontal, _) => {
|
||||||
return Err(TiffError::UnsupportedError(
|
return Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::HorizontalPredictor(color_type),
|
TiffUnsupportedError::HorizontalPredictor(color_type),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
(Predictor::FloatingPoint, DecodingBuffer::F32(_))
|
(Predictor::FloatingPoint, SampleFormat::IEEEFP) => {}
|
||||||
| (Predictor::FloatingPoint, DecodingBuffer::F64(_)) => {}
|
|
||||||
(Predictor::FloatingPoint, _) => {
|
(Predictor::FloatingPoint, _) => {
|
||||||
return Err(TiffError::UnsupportedError(
|
return Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::FloatingPointPredictor(color_type),
|
TiffUnsupportedError::FloatingPointPredictor(color_type),
|
||||||
@@ -622,7 +615,6 @@ impl Image {
|
|||||||
return Err(TiffError::LimitsExceeded);
|
return Err(TiffError::LimitsExceeded);
|
||||||
}
|
}
|
||||||
|
|
||||||
let byte_len = buffer.byte_len();
|
|
||||||
let compression_method = self.compression_method;
|
let compression_method = self.compression_method;
|
||||||
let photometric_interpretation = self.photometric_interpretation;
|
let photometric_interpretation = self.photometric_interpretation;
|
||||||
let predictor = self.predictor;
|
let predictor = self.predictor;
|
||||||
@@ -631,7 +623,19 @@ impl Image {
|
|||||||
let chunk_dims = self.chunk_dimensions()?;
|
let chunk_dims = self.chunk_dimensions()?;
|
||||||
let data_dims = self.chunk_data_dimensions(chunk_index)?;
|
let data_dims = self.chunk_data_dimensions(chunk_index)?;
|
||||||
|
|
||||||
let padding_right = chunk_dims.0 - data_dims.0;
|
let chunk_row_bits = (u64::from(chunk_dims.0) * u64::from(self.bits_per_sample))
|
||||||
|
.checked_mul(samples as u64)
|
||||||
|
.ok_or(TiffError::LimitsExceeded)?;
|
||||||
|
let chunk_row_bytes: usize = ((chunk_row_bits + 7) / 8).try_into()?;
|
||||||
|
|
||||||
|
let data_row_bits = (u64::from(data_dims.0) * u64::from(self.bits_per_sample))
|
||||||
|
.checked_mul(samples as u64)
|
||||||
|
.ok_or(TiffError::LimitsExceeded)?;
|
||||||
|
let data_row_bytes: usize = ((data_row_bits + 7) / 8).try_into()?;
|
||||||
|
|
||||||
|
// TODO: Should these return errors instead?
|
||||||
|
assert!(output_row_stride >= data_row_bytes);
|
||||||
|
assert!(buf.len() >= output_row_stride * (data_dims.1 as usize - 1) + data_row_bytes);
|
||||||
|
|
||||||
let mut reader = Self::create_reader(
|
let mut reader = Self::create_reader(
|
||||||
reader,
|
reader,
|
||||||
@@ -641,57 +645,65 @@ impl Image {
|
|||||||
self.jpeg_tables.as_deref().map(|a| &**a),
|
self.jpeg_tables.as_deref().map(|a| &**a),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if output_width == data_dims.0 as usize && padding_right == 0 {
|
if output_row_stride == chunk_row_bytes as usize {
|
||||||
let total_samples = data_dims.0 as usize * data_dims.1 as usize * samples;
|
let tile = &mut buf[..chunk_row_bytes * data_dims.1 as usize];
|
||||||
let tile = &mut buffer.as_bytes_mut()[..total_samples * byte_len];
|
|
||||||
reader.read_exact(tile)?;
|
reader.read_exact(tile)?;
|
||||||
|
|
||||||
for row in 0..data_dims.1 as usize {
|
for row in tile.chunks_mut(chunk_row_bytes as usize) {
|
||||||
let row_start = row * output_width * samples;
|
super::fix_endianness_and_predict(
|
||||||
let row_end = (row + 1) * output_width * samples;
|
row,
|
||||||
let row = buffer.subrange(row_start..row_end);
|
color_type.bit_depth(),
|
||||||
super::fix_endianness_and_predict(row, samples, byte_order, predictor);
|
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(tile, color_type, self.sample_format);
|
||||||
}
|
}
|
||||||
} else if padding_right > 0 && self.predictor == Predictor::FloatingPoint {
|
} else if chunk_row_bytes > data_row_bytes && self.predictor == Predictor::FloatingPoint {
|
||||||
// The floating point predictor shuffles the padding bytes into the encoded output, so
|
// The floating point predictor shuffles the padding bytes into the encoded output, so
|
||||||
// this case is handled specially when needed.
|
// this case is handled specially when needed.
|
||||||
let mut encoded = vec![0u8; chunk_dims.0 as usize * samples * byte_len];
|
let mut encoded = vec![0u8; chunk_row_bytes];
|
||||||
|
for row in buf.chunks_mut(output_row_stride).take(data_dims.1 as usize) {
|
||||||
for row in 0..data_dims.1 as usize {
|
|
||||||
let row_start = row * output_width * samples;
|
|
||||||
let row_end = row_start + data_dims.0 as usize * samples;
|
|
||||||
|
|
||||||
reader.read_exact(&mut encoded)?;
|
reader.read_exact(&mut encoded)?;
|
||||||
match buffer.subrange(row_start..row_end) {
|
|
||||||
DecodingBuffer::F32(buf) => fp_predict_f32(&mut encoded, buf, samples),
|
let row = &mut row[..data_row_bytes];
|
||||||
DecodingBuffer::F64(buf) => fp_predict_f64(&mut encoded, buf, samples),
|
match color_type.bit_depth() {
|
||||||
|
32 => predict_f32(&mut encoded, row, samples),
|
||||||
|
64 => predict_f64(&mut encoded, row, samples),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
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(row, color_type, self.sample_format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for row in 0..data_dims.1 as usize {
|
for (i, row) in buf
|
||||||
let row_start = row * output_width * samples;
|
.chunks_mut(output_row_stride)
|
||||||
let row_end = row_start + data_dims.0 as usize * samples;
|
.take(data_dims.1 as usize)
|
||||||
|
.enumerate()
|
||||||
let row = &mut buffer.as_bytes_mut()[(row_start * byte_len)..(row_end * byte_len)];
|
{
|
||||||
|
let row = &mut row[..data_row_bytes];
|
||||||
reader.read_exact(row)?;
|
reader.read_exact(row)?;
|
||||||
|
|
||||||
|
println!("chunk={chunk_index}, index={i}");
|
||||||
|
|
||||||
// Skip horizontal padding
|
// Skip horizontal padding
|
||||||
if padding_right > 0 {
|
if chunk_row_bytes > data_row_bytes {
|
||||||
let len = u64::try_from(padding_right as usize * samples * byte_len)?;
|
let len = u64::try_from(chunk_row_bytes - data_row_bytes)?;
|
||||||
io::copy(&mut reader.by_ref().take(len), &mut io::sink())?;
|
io::copy(&mut reader.by_ref().take(len), &mut io::sink())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut row = buffer.subrange(row_start..row_end);
|
super::fix_endianness_and_predict(
|
||||||
super::fix_endianness_and_predict(row.copy(), samples, byte_order, predictor);
|
row,
|
||||||
|
color_type.bit_depth(),
|
||||||
|
samples,
|
||||||
|
byte_order,
|
||||||
|
predictor,
|
||||||
|
);
|
||||||
if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
||||||
super::invert_colors(&mut row, color_type);
|
super::invert_colors(row, color_type, self.sample_format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+130
-200
@@ -1,6 +1,5 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::io::{self, Read, Seek};
|
use std::io::{self, Read, Seek};
|
||||||
use std::ops::Range;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bytecast, ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError,
|
bytecast, ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError,
|
||||||
@@ -167,57 +166,6 @@ pub enum DecodingBuffer<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DecodingBuffer<'a> {
|
impl<'a> DecodingBuffer<'a> {
|
||||||
fn byte_len(&self) -> usize {
|
|
||||||
match *self {
|
|
||||||
DecodingBuffer::U8(_) => 1,
|
|
||||||
DecodingBuffer::U16(_) => 2,
|
|
||||||
DecodingBuffer::U32(_) => 4,
|
|
||||||
DecodingBuffer::U64(_) => 8,
|
|
||||||
DecodingBuffer::F32(_) => 4,
|
|
||||||
DecodingBuffer::F64(_) => 8,
|
|
||||||
DecodingBuffer::I8(_) => 1,
|
|
||||||
DecodingBuffer::I16(_) => 2,
|
|
||||||
DecodingBuffer::I32(_) => 4,
|
|
||||||
DecodingBuffer::I64(_) => 8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn copy<'b>(&'b mut self) -> DecodingBuffer<'b>
|
|
||||||
where
|
|
||||||
'a: 'b,
|
|
||||||
{
|
|
||||||
match *self {
|
|
||||||
DecodingBuffer::U8(ref mut buf) => DecodingBuffer::U8(buf),
|
|
||||||
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),
|
|
||||||
DecodingBuffer::I8(ref mut buf) => DecodingBuffer::I8(buf),
|
|
||||||
DecodingBuffer::I16(ref mut buf) => DecodingBuffer::I16(buf),
|
|
||||||
DecodingBuffer::I32(ref mut buf) => DecodingBuffer::I32(buf),
|
|
||||||
DecodingBuffer::I64(ref mut buf) => DecodingBuffer::I64(buf),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn subrange<'b>(&'b mut self, range: Range<usize>) -> DecodingBuffer<'b>
|
|
||||||
where
|
|
||||||
'a: 'b,
|
|
||||||
{
|
|
||||||
match *self {
|
|
||||||
DecodingBuffer::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[range]),
|
|
||||||
DecodingBuffer::U16(ref mut buf) => DecodingBuffer::U16(&mut buf[range]),
|
|
||||||
DecodingBuffer::U32(ref mut buf) => DecodingBuffer::U32(&mut buf[range]),
|
|
||||||
DecodingBuffer::U64(ref mut buf) => DecodingBuffer::U64(&mut buf[range]),
|
|
||||||
DecodingBuffer::F32(ref mut buf) => DecodingBuffer::F32(&mut buf[range]),
|
|
||||||
DecodingBuffer::F64(ref mut buf) => DecodingBuffer::F64(&mut buf[range]),
|
|
||||||
DecodingBuffer::I8(ref mut buf) => DecodingBuffer::I8(&mut buf[range]),
|
|
||||||
DecodingBuffer::I16(ref mut buf) => DecodingBuffer::I16(&mut buf[range]),
|
|
||||||
DecodingBuffer::I32(ref mut buf) => DecodingBuffer::I32(&mut buf[range]),
|
|
||||||
DecodingBuffer::I64(ref mut buf) => DecodingBuffer::I64(&mut buf[range]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_bytes_mut(&mut self) -> &mut [u8] {
|
fn as_bytes_mut(&mut self) -> &mut [u8] {
|
||||||
match self {
|
match self {
|
||||||
DecodingBuffer::U8(ref mut buf) => buf,
|
DecodingBuffer::U8(ref mut buf) => buf,
|
||||||
@@ -307,82 +255,62 @@ where
|
|||||||
image: Image,
|
image: Image,
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Wrapping {
|
fn rev_hpredict_nsamp(buf: &mut [u8], bit_depth: u8, samples: usize) {
|
||||||
fn wrapping_add(&self, other: Self) -> Self;
|
match bit_depth {
|
||||||
|
0..=8 => {
|
||||||
|
for i in samples..buf.len() {
|
||||||
|
buf[i] = buf[i].wrapping_add(buf[i - samples]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
9..=16 => {
|
||||||
|
for i in (samples * 2..buf.len()).step_by(2) {
|
||||||
|
let v = u16::from_ne_bytes(buf[i..][..2].try_into().unwrap());
|
||||||
|
let p = u16::from_ne_bytes(buf[i - samples..][..2].try_into().unwrap());
|
||||||
|
buf[i..][..2].copy_from_slice(&(v.wrapping_add(p)).to_ne_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
17..=32 => {
|
||||||
|
for i in (samples * 4..buf.len()).step_by(4) {
|
||||||
|
let v = u32::from_ne_bytes(buf[i..][..4].try_into().unwrap());
|
||||||
|
let p = u32::from_ne_bytes(buf[i - samples..][..4].try_into().unwrap());
|
||||||
|
buf[i..][..4].copy_from_slice(&(v.wrapping_add(p)).to_ne_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
33..=64 => {
|
||||||
|
for i in (samples * 8..buf.len()).step_by(8) {
|
||||||
|
let v = u64::from_ne_bytes(buf[i..][..8].try_into().unwrap());
|
||||||
|
let p = u64::from_ne_bytes(buf[i - samples..][..8].try_into().unwrap());
|
||||||
|
buf[i..][..8].copy_from_slice(&(v.wrapping_add(p)).to_ne_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unreachable!("Caller should have validated arguments. Please file a bug.")
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wrapping for u8 {
|
|
||||||
fn wrapping_add(&self, other: Self) -> Self {
|
|
||||||
u8::wrapping_add(*self, other)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wrapping for u16 {
|
fn predict_f32(input: &mut [u8], output: &mut [u8], samples: usize) {
|
||||||
fn wrapping_add(&self, other: Self) -> Self {
|
for i in samples..input.len() {
|
||||||
u16::wrapping_add(*self, other)
|
input[i] = input[i].wrapping_add(input[i - samples]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wrapping for u32 {
|
for (i, chunk) in output.chunks_mut(4).enumerate() {
|
||||||
fn wrapping_add(&self, other: Self) -> Self {
|
chunk.copy_from_slice(&u32::to_ne_bytes(u32::from_be_bytes([
|
||||||
u32::wrapping_add(*self, other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Wrapping for u64 {
|
|
||||||
fn wrapping_add(&self, other: Self) -> Self {
|
|
||||||
u64::wrapping_add(*self, other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Wrapping for i8 {
|
|
||||||
fn wrapping_add(&self, other: Self) -> Self {
|
|
||||||
i8::wrapping_add(*self, other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Wrapping for i16 {
|
|
||||||
fn wrapping_add(&self, other: Self) -> Self {
|
|
||||||
i16::wrapping_add(*self, other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Wrapping for i32 {
|
|
||||||
fn wrapping_add(&self, other: Self) -> Self {
|
|
||||||
i32::wrapping_add(*self, other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Wrapping for i64 {
|
|
||||||
fn wrapping_add(&self, other: Self) -> Self {
|
|
||||||
i64::wrapping_add(*self, other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rev_hpredict_nsamp<T: Copy + Wrapping>(image: &mut [T], samples: usize) {
|
|
||||||
for col in samples..image.len() {
|
|
||||||
image[col] = image[col].wrapping_add(image[col - samples]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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[i],
|
input[i],
|
||||||
input[input.len() / 4 + i],
|
input[input.len() / 4 + i],
|
||||||
input[input.len() / 4 * 2 + i],
|
input[input.len() / 4 * 2 + i],
|
||||||
input[input.len() / 4 * 3 + i],
|
input[input.len() / 4 * 3 + i],
|
||||||
]));
|
])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fp_predict_f64(input: &mut [u8], output: &mut [f64], samples: usize) {
|
fn predict_f64(input: &mut [u8], output: &mut [u8], samples: usize) {
|
||||||
rev_hpredict_nsamp(input, samples);
|
for i in samples..input.len() {
|
||||||
for i in 0..output.len() {
|
input[i] = input[i].wrapping_add(input[i - samples]);
|
||||||
// TODO: use f64::from_be_bytes() when we can (version 1.40)
|
}
|
||||||
output[i] = f64::from_bits(u64::from_be_bytes([
|
|
||||||
|
for (i, chunk) in output.chunks_mut(8).enumerate() {
|
||||||
|
chunk.copy_from_slice(&u64::to_ne_bytes(u64::from_be_bytes([
|
||||||
input[i],
|
input[i],
|
||||||
input[input.len() / 8 + i],
|
input[input.len() / 8 + i],
|
||||||
input[input.len() / 8 * 2 + i],
|
input[input.len() / 8 * 2 + i],
|
||||||
@@ -391,122 +319,103 @@ pub fn fp_predict_f64(input: &mut [u8], output: &mut [f64], samples: usize) {
|
|||||||
input[input.len() / 8 * 5 + i],
|
input[input.len() / 8 * 5 + i],
|
||||||
input[input.len() / 8 * 6 + i],
|
input[input.len() / 8 * 6 + i],
|
||||||
input[input.len() / 8 * 7 + i],
|
input[input.len() / 8 * 7 + i],
|
||||||
]));
|
])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fix_endianness_and_predict(
|
fn fix_endianness_and_predict(
|
||||||
mut image: DecodingBuffer,
|
buf: &mut [u8],
|
||||||
|
bit_depth: u8,
|
||||||
samples: usize,
|
samples: usize,
|
||||||
byte_order: ByteOrder,
|
byte_order: ByteOrder,
|
||||||
predictor: Predictor,
|
predictor: Predictor,
|
||||||
) {
|
) {
|
||||||
match predictor {
|
match predictor {
|
||||||
Predictor::None => {
|
Predictor::None => {
|
||||||
fix_endianness(&mut image, byte_order);
|
fix_endianness(buf, byte_order, bit_depth);
|
||||||
}
|
}
|
||||||
Predictor::Horizontal => {
|
Predictor::Horizontal => {
|
||||||
fix_endianness(&mut image, byte_order);
|
fix_endianness(buf, byte_order, bit_depth);
|
||||||
match image {
|
rev_hpredict_nsamp(buf, bit_depth, samples);
|
||||||
DecodingBuffer::U8(buf) => rev_hpredict_nsamp(buf, samples),
|
|
||||||
DecodingBuffer::U16(buf) => rev_hpredict_nsamp(buf, samples),
|
|
||||||
DecodingBuffer::U32(buf) => rev_hpredict_nsamp(buf, samples),
|
|
||||||
DecodingBuffer::U64(buf) => rev_hpredict_nsamp(buf, samples),
|
|
||||||
DecodingBuffer::I8(buf) => rev_hpredict_nsamp(buf, samples),
|
|
||||||
DecodingBuffer::I16(buf) => rev_hpredict_nsamp(buf, samples),
|
|
||||||
DecodingBuffer::I32(buf) => rev_hpredict_nsamp(buf, samples),
|
|
||||||
DecodingBuffer::I64(buf) => rev_hpredict_nsamp(buf, samples),
|
|
||||||
DecodingBuffer::F32(_) | DecodingBuffer::F64(_) => {
|
|
||||||
unreachable!("Caller should have validated arguments. Please file a bug.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Predictor::FloatingPoint => {
|
Predictor::FloatingPoint => {
|
||||||
let mut buffer_copy = image.as_bytes_mut().to_vec();
|
let mut buffer_copy = buf.to_vec();
|
||||||
match image {
|
match bit_depth {
|
||||||
DecodingBuffer::F32(buf) => fp_predict_f32(&mut buffer_copy, buf, samples),
|
32 => predict_f32(&mut buffer_copy, buf, samples),
|
||||||
DecodingBuffer::F64(buf) => fp_predict_f64(&mut buffer_copy, buf, samples),
|
64 => predict_f64(&mut buffer_copy, buf, samples),
|
||||||
_ => unreachable!("Caller should have validated arguments. Please file a bug."),
|
_ => unreachable!("Caller should have validated arguments. Please file a bug."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invert_colors_unsigned<T>(buffer: &mut [T], max: T)
|
fn invert_colors(buf: &mut [u8], color_type: ColorType, sample_format: SampleFormat) {
|
||||||
where
|
match (color_type, sample_format) {
|
||||||
T: std::ops::Sub<T> + std::ops::Sub<Output = T> + Copy,
|
(ColorType::Gray(8), SampleFormat::Uint) => {
|
||||||
{
|
for x in buf {
|
||||||
for datum in buffer.iter_mut() {
|
*x = 0xff - *x;
|
||||||
*datum = max - *datum
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
(ColorType::Gray(16), SampleFormat::Uint) => {
|
||||||
fn invert_colors_fp<T>(buffer: &mut [T], max: T)
|
for x in buf.chunks_mut(2) {
|
||||||
where
|
let v = u16::from_ne_bytes(x.try_into().unwrap());
|
||||||
T: std::ops::Sub<T> + std::ops::Sub<Output = T> + Copy,
|
x.copy_from_slice(&(0xffff - v).to_ne_bytes());
|
||||||
{
|
|
||||||
for datum in buffer.iter_mut() {
|
|
||||||
// FIXME: assumes [0, 1) range for floats
|
|
||||||
*datum = max - *datum
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
(ColorType::Gray(32), SampleFormat::Uint) => {
|
||||||
fn invert_colors(buf: &mut DecodingBuffer, color_type: ColorType) {
|
for x in buf.chunks_mut(4) {
|
||||||
match (color_type, buf) {
|
let v = u32::from_ne_bytes(x.try_into().unwrap());
|
||||||
(ColorType::Gray(64), DecodingBuffer::U64(ref mut buffer)) => {
|
x.copy_from_slice(&(0xffff_ffff - v).to_ne_bytes());
|
||||||
invert_colors_unsigned(buffer, 0xffff_ffff_ffff_ffff);
|
|
||||||
}
|
}
|
||||||
(ColorType::Gray(32), DecodingBuffer::U32(ref mut buffer)) => {
|
|
||||||
invert_colors_unsigned(buffer, 0xffff_ffff);
|
|
||||||
}
|
}
|
||||||
(ColorType::Gray(16), DecodingBuffer::U16(ref mut buffer)) => {
|
(ColorType::Gray(64), SampleFormat::Uint) => {
|
||||||
invert_colors_unsigned(buffer, 0xffff);
|
for x in buf.chunks_mut(8) {
|
||||||
|
let v = u64::from_ne_bytes(x.try_into().unwrap());
|
||||||
|
x.copy_from_slice(&(0xffff_ffff_ffff_ffff - v).to_ne_bytes());
|
||||||
}
|
}
|
||||||
(ColorType::Gray(n), DecodingBuffer::U8(ref mut buffer)) if n <= 8 => {
|
|
||||||
invert_colors_unsigned(buffer, 0xff);
|
|
||||||
}
|
}
|
||||||
(ColorType::Gray(32), DecodingBuffer::F32(ref mut buffer)) => {
|
(ColorType::Gray(32), SampleFormat::IEEEFP) => {
|
||||||
invert_colors_fp(buffer, 1.0);
|
for x in buf.chunks_mut(4) {
|
||||||
|
let v = f32::from_ne_bytes(x.try_into().unwrap());
|
||||||
|
x.copy_from_slice(&(1.0 - v).to_ne_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(ColorType::Gray(64), SampleFormat::IEEEFP) => {
|
||||||
|
for x in buf.chunks_mut(8) {
|
||||||
|
let v = f64::from_ne_bytes(x.try_into().unwrap());
|
||||||
|
x.copy_from_slice(&(1.0 - v).to_ne_bytes());
|
||||||
}
|
}
|
||||||
(ColorType::Gray(64), DecodingBuffer::F64(ref mut buffer)) => {
|
|
||||||
invert_colors_fp(buffer, 1.0);
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fix endianness. If `byte_order` matches the host, then conversion is a no-op.
|
/// Fix endianness. If `byte_order` matches the host, then conversion is a no-op.
|
||||||
fn fix_endianness(buf: &mut DecodingBuffer, byte_order: ByteOrder) {
|
fn fix_endianness(buf: &mut [u8], byte_order: ByteOrder, bit_depth: u8) {
|
||||||
match byte_order {
|
match byte_order {
|
||||||
ByteOrder::LittleEndian => match buf {
|
ByteOrder::LittleEndian => match bit_depth {
|
||||||
DecodingBuffer::U8(_) | DecodingBuffer::I8(_) => {}
|
0..=8 => {}
|
||||||
DecodingBuffer::U16(b) => b.iter_mut().for_each(|v| *v = u16::from_le(*v)),
|
9..=16 => buf.chunks_exact_mut(2).for_each(|v| {
|
||||||
DecodingBuffer::I16(b) => b.iter_mut().for_each(|v| *v = i16::from_le(*v)),
|
v.copy_from_slice(&u16::from_le_bytes((*v).try_into().unwrap()).to_ne_bytes())
|
||||||
DecodingBuffer::U32(b) => b.iter_mut().for_each(|v| *v = u32::from_le(*v)),
|
}),
|
||||||
DecodingBuffer::I32(b) => b.iter_mut().for_each(|v| *v = i32::from_le(*v)),
|
17..=32 => buf.chunks_exact_mut(4).for_each(|v| {
|
||||||
DecodingBuffer::U64(b) => b.iter_mut().for_each(|v| *v = u64::from_le(*v)),
|
v.copy_from_slice(&u32::from_le_bytes((*v).try_into().unwrap()).to_ne_bytes())
|
||||||
DecodingBuffer::I64(b) => b.iter_mut().for_each(|v| *v = i64::from_le(*v)),
|
}),
|
||||||
DecodingBuffer::F32(b) => b
|
_ => buf.chunks_exact_mut(8).for_each(|v| {
|
||||||
.iter_mut()
|
v.copy_from_slice(&u64::from_le_bytes((*v).try_into().unwrap()).to_ne_bytes())
|
||||||
.for_each(|v| *v = f32::from_bits(u32::from_le(v.to_bits()))),
|
}),
|
||||||
DecodingBuffer::F64(b) => b
|
|
||||||
.iter_mut()
|
|
||||||
.for_each(|v| *v = f64::from_bits(u64::from_le(v.to_bits()))),
|
|
||||||
},
|
},
|
||||||
ByteOrder::BigEndian => match buf {
|
ByteOrder::BigEndian => match bit_depth {
|
||||||
DecodingBuffer::U8(_) | DecodingBuffer::I8(_) => {}
|
0..=8 => {}
|
||||||
DecodingBuffer::U16(b) => b.iter_mut().for_each(|v| *v = u16::from_be(*v)),
|
9..=16 => buf.chunks_exact_mut(2).for_each(|v| {
|
||||||
DecodingBuffer::I16(b) => b.iter_mut().for_each(|v| *v = i16::from_be(*v)),
|
v.copy_from_slice(&u16::from_be_bytes((*v).try_into().unwrap()).to_ne_bytes())
|
||||||
DecodingBuffer::U32(b) => b.iter_mut().for_each(|v| *v = u32::from_be(*v)),
|
}),
|
||||||
DecodingBuffer::I32(b) => b.iter_mut().for_each(|v| *v = i32::from_be(*v)),
|
17..=32 => buf.chunks_exact_mut(4).for_each(|v| {
|
||||||
DecodingBuffer::U64(b) => b.iter_mut().for_each(|v| *v = u64::from_be(*v)),
|
v.copy_from_slice(&u32::from_be_bytes((*v).try_into().unwrap()).to_ne_bytes())
|
||||||
DecodingBuffer::I64(b) => b.iter_mut().for_each(|v| *v = i64::from_be(*v)),
|
}),
|
||||||
DecodingBuffer::F32(b) => b
|
_ => buf.chunks_exact_mut(8).for_each(|v| {
|
||||||
.iter_mut()
|
v.copy_from_slice(&u64::from_be_bytes((*v).try_into().unwrap()).to_ne_bytes())
|
||||||
.for_each(|v| *v = f32::from_bits(u32::from_be(v.to_bits()))),
|
}),
|
||||||
DecodingBuffer::F64(b) => b
|
|
||||||
.iter_mut()
|
|
||||||
.for_each(|v| *v = f64::from_bits(u64::from_be(v.to_bits()))),
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1042,10 +951,15 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
let byte_order = self.reader.byte_order;
|
let byte_order = self.reader.byte_order;
|
||||||
|
|
||||||
|
let output_row_stride = (output_width as u64)
|
||||||
|
.saturating_mul(self.image.samples_per_pixel() as u64)
|
||||||
|
.saturating_mul(self.image.bits_per_sample as u64)
|
||||||
|
/ 8;
|
||||||
|
|
||||||
self.image.expand_chunk(
|
self.image.expand_chunk(
|
||||||
&mut self.reader,
|
&mut self.reader,
|
||||||
buffer.copy(),
|
buffer.as_bytes_mut(),
|
||||||
output_width,
|
output_row_stride.try_into()?,
|
||||||
byte_order,
|
byte_order,
|
||||||
chunk_index,
|
chunk_index,
|
||||||
&self.limits,
|
&self.limits,
|
||||||
@@ -1146,8 +1060,23 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let output_row_bits = (width as u64 * self.image.bits_per_sample as u64)
|
||||||
|
.checked_mul(samples as u64)
|
||||||
|
.ok_or(TiffError::LimitsExceeded)?;
|
||||||
|
let output_row_stride: usize = ((output_row_bits + 7) / 8).try_into()?;
|
||||||
|
|
||||||
|
let chunk_row_bits = (chunk_dimensions.0 as u64 * self.image.bits_per_sample as u64)
|
||||||
|
.checked_mul(samples as u64)
|
||||||
|
.ok_or(TiffError::LimitsExceeded)?;
|
||||||
|
let chunk_row_bytes: usize = ((chunk_row_bits + 7) / 8).try_into()?;
|
||||||
|
|
||||||
let chunks_across = ((width - 1) / chunk_dimensions.0 + 1) as usize;
|
let chunks_across = ((width - 1) / chunk_dimensions.0 + 1) as usize;
|
||||||
let strip_samples = width as usize * chunk_dimensions.1 as usize * samples;
|
|
||||||
|
if chunks_across > 1 && chunk_row_bits % 8 != 0 {
|
||||||
|
return Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::MisalignedTileBoundaries,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let image_chunks = self.image().chunk_offsets.len() / self.image().strips_per_pixel();
|
let image_chunks = self.image().chunk_offsets.len() / self.image().strips_per_pixel();
|
||||||
// For multi-band images, only the first band is read.
|
// For multi-band images, only the first band is read.
|
||||||
@@ -1159,12 +1088,13 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
let x = chunk % chunks_across;
|
let x = chunk % chunks_across;
|
||||||
let y = chunk / chunks_across;
|
let y = chunk / chunks_across;
|
||||||
let buffer_offset = y * strip_samples + x * chunk_dimensions.0 as usize * samples;
|
let buffer_offset =
|
||||||
|
y * output_row_stride * chunk_dimensions.1 as usize + x * chunk_row_bytes;
|
||||||
let byte_order = self.reader.byte_order;
|
let byte_order = self.reader.byte_order;
|
||||||
self.image.expand_chunk(
|
self.image.expand_chunk(
|
||||||
&mut self.reader,
|
&mut self.reader,
|
||||||
result.as_buffer(buffer_offset).copy(),
|
&mut result.as_buffer(0).as_bytes_mut()[buffer_offset..],
|
||||||
width as usize,
|
output_row_stride,
|
||||||
byte_order,
|
byte_order,
|
||||||
chunk as u32,
|
chunk as u32,
|
||||||
&self.limits,
|
&self.limits,
|
||||||
|
|||||||
@@ -165,6 +165,7 @@ pub enum TiffUnsupportedError {
|
|||||||
UnsupportedDataType,
|
UnsupportedDataType,
|
||||||
UnsupportedInterpretation(PhotometricInterpretation),
|
UnsupportedInterpretation(PhotometricInterpretation),
|
||||||
UnsupportedJpegFeature(UnsupportedFeature),
|
UnsupportedJpegFeature(UnsupportedFeature),
|
||||||
|
MisalignedTileBoundaries,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for TiffUnsupportedError {
|
impl fmt::Display for TiffUnsupportedError {
|
||||||
@@ -223,6 +224,7 @@ impl fmt::Display for TiffUnsupportedError {
|
|||||||
UnsupportedJpegFeature(ref unsupported_feature) => {
|
UnsupportedJpegFeature(ref unsupported_feature) => {
|
||||||
write!(fmt, "Unsupported JPEG feature {:?}", unsupported_feature)
|
write!(fmt, "Unsupported JPEG feature {:?}", unsupported_feature)
|
||||||
}
|
}
|
||||||
|
MisalignedTileBoundaries => write!(fmt, "Tile rows are not aligned to byte boundaries"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
@@ -44,3 +44,17 @@ pub enum ColorType {
|
|||||||
/// Pixel has multiple bands/channels
|
/// Pixel has multiple bands/channels
|
||||||
Multiband { bit_depth: u8, num_samples: u16 },
|
Multiband { bit_depth: u8, num_samples: u16 },
|
||||||
}
|
}
|
||||||
|
impl ColorType {
|
||||||
|
fn bit_depth(&self) -> u8 {
|
||||||
|
match *self {
|
||||||
|
ColorType::Gray(b)
|
||||||
|
| ColorType::RGB(b)
|
||||||
|
| ColorType::Palette(b)
|
||||||
|
| ColorType::GrayA(b)
|
||||||
|
| ColorType::RGBA(b)
|
||||||
|
| ColorType::CMYK(b)
|
||||||
|
| ColorType::YCbCr(b)
|
||||||
|
| ColorType::Multiband { bit_depth: b, .. } => b,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user