Reader instance for group 4-decoded images, that buffer one line at a time
This commit is contained in:
1 parent
9fcc2a3b65
commit
c1087f6f08
1 file changed
+52
-17
+52
-17
@@ -9,6 +9,10 @@ use crate::tags::{
|
||||
use crate::{
|
||||
ColorType, Directory, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError,
|
||||
};
|
||||
|
||||
#[cfg(feature = "fax")]
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use std::io::{self, Cursor, Read, Seek};
|
||||
use std::num::NonZeroUsize;
|
||||
use std::sync::Arc;
|
||||
@@ -381,12 +385,12 @@ impl Image {
|
||||
}
|
||||
|
||||
fn create_reader<'r, R: 'r + Read>(
|
||||
width: u32,
|
||||
height: u32,
|
||||
reader: R,
|
||||
compression_method: CompressionMethod,
|
||||
compressed_length: u64,
|
||||
#[allow(unused_variables)] jpeg_tables: Option<&[u8]>,
|
||||
// FIXME: these should be `expect` attributes or we choose another way of passing them.
|
||||
#[cfg_attr(not(feature = "jpeg"), allow(unused_variables))] jpeg_tables: Option<&[u8]>,
|
||||
#[cfg_attr(not(feature = "fax"), allow(unused_variables))] dimensions: (u32, u32),
|
||||
) -> TiffResult<Box<dyn Read + 'r>> {
|
||||
Ok(match compression_method {
|
||||
CompressionMethod::None => Box::new(reader),
|
||||
@@ -454,26 +458,58 @@ impl Image {
|
||||
|
||||
Box::new(Cursor::new(data))
|
||||
}
|
||||
#[cfg(feature = "fax")]
|
||||
CompressionMethod::Fax4 => {
|
||||
let width = u16::try_from(width)?;
|
||||
let height = u16::try_from(height)?;
|
||||
let mut out: Vec<u8> = Vec::with_capacity(usize::from(width) * usize::from(height));
|
||||
let width = u16::try_from(dimensions.0)?;
|
||||
let height = u16::try_from(dimensions.1)?;
|
||||
|
||||
struct Group4Reader<R2> {
|
||||
decoder: fax::decoder::Group4Decoder<std::io::Bytes<R2>>,
|
||||
line_buf: VecDeque<u8>,
|
||||
y: u16,
|
||||
height: u16,
|
||||
width: u16,
|
||||
}
|
||||
|
||||
impl<R2: Read> Read for Group4Reader<R2> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
if self.line_buf.is_empty() && self.y < self.height {
|
||||
let next = self
|
||||
.decoder
|
||||
.advance()
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
|
||||
match next {
|
||||
fax::decoder::DecodeStatus::End => (),
|
||||
fax::decoder::DecodeStatus::Incomplete => {
|
||||
self.y += 1;
|
||||
self.line_buf.extend(
|
||||
// all extant tiff/fax4 decoders I've found always assume that the photometric interpretation
|
||||
// is `WhiteIsZero`, ignoring the tag. ImageMagick appears to generate fax4-encoded tiffs
|
||||
// with the tag incorrectly set to `BlackIsZero`.
|
||||
fax::decoder::decode_g4(
|
||||
reader.bytes().map(|b| b.unwrap()),
|
||||
width,
|
||||
Some(height),
|
||||
|transitions| {
|
||||
out.extend(fax::decoder::pels(transitions, width).map(|c| match c {
|
||||
fax::decoder::pels(self.decoder.transition(), self.width)
|
||||
.map(|c| match c {
|
||||
fax::Color::Black => 255,
|
||||
fax::Color::White => 0,
|
||||
}))
|
||||
},
|
||||
}),
|
||||
);
|
||||
Box::new(Cursor::new(out))
|
||||
}
|
||||
}
|
||||
}
|
||||
self.line_buf.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
Box::new(Group4Reader {
|
||||
decoder: fax::decoder::Group4Decoder::new(
|
||||
reader.take(compressed_length).bytes(),
|
||||
width,
|
||||
)?,
|
||||
line_buf: VecDeque::with_capacity(usize::from(width)),
|
||||
y: 0,
|
||||
width: width,
|
||||
height: height,
|
||||
})
|
||||
}
|
||||
method => {
|
||||
return Err(TiffError::UnsupportedError(
|
||||
@@ -677,12 +713,11 @@ impl Image {
|
||||
assert!(buf.len() >= output_row_stride * (data_dims.1 as usize - 1) + data_row_bytes);
|
||||
|
||||
let mut reader = Self::create_reader(
|
||||
self.width,
|
||||
self.height,
|
||||
reader,
|
||||
compression_method,
|
||||
*compressed_bytes,
|
||||
self.jpeg_tables.as_deref().map(|a| &**a),
|
||||
chunk_dims,
|
||||
)?;
|
||||
|
||||
if output_row_stride == chunk_row_bytes {
|
||||
|
||||
Reference in new issue
Block a user