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
+58
-23
+58
-23
@@ -9,6 +9,10 @@ use crate::tags::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
ColorType, Directory, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError,
|
ColorType, Directory, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "fax")]
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use std::io::{self, Cursor, Read, Seek};
|
use std::io::{self, Cursor, Read, Seek};
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -381,12 +385,12 @@ impl Image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_reader<'r, R: 'r + Read>(
|
fn create_reader<'r, R: 'r + Read>(
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
reader: R,
|
reader: R,
|
||||||
compression_method: CompressionMethod,
|
compression_method: CompressionMethod,
|
||||||
compressed_length: u64,
|
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>> {
|
) -> TiffResult<Box<dyn Read + 'r>> {
|
||||||
Ok(match compression_method {
|
Ok(match compression_method {
|
||||||
CompressionMethod::None => Box::new(reader),
|
CompressionMethod::None => Box::new(reader),
|
||||||
@@ -454,26 +458,58 @@ impl Image {
|
|||||||
|
|
||||||
Box::new(Cursor::new(data))
|
Box::new(Cursor::new(data))
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "fax")]
|
||||||
CompressionMethod::Fax4 => {
|
CompressionMethod::Fax4 => {
|
||||||
let width = u16::try_from(width)?;
|
let width = u16::try_from(dimensions.0)?;
|
||||||
let height = u16::try_from(height)?;
|
let height = u16::try_from(dimensions.1)?;
|
||||||
let mut out: Vec<u8> = Vec::with_capacity(usize::from(width) * usize::from(height));
|
|
||||||
|
|
||||||
// all extant tiff/fax4 decoders I've found always assume that the photometric interpretation
|
struct Group4Reader<R2> {
|
||||||
// is `WhiteIsZero`, ignoring the tag. ImageMagick appears to generate fax4-encoded tiffs
|
decoder: fax::decoder::Group4Decoder<std::io::Bytes<R2>>,
|
||||||
// with the tag incorrectly set to `BlackIsZero`.
|
line_buf: VecDeque<u8>,
|
||||||
fax::decoder::decode_g4(
|
y: u16,
|
||||||
reader.bytes().map(|b| b.unwrap()),
|
height: u16,
|
||||||
width,
|
width: u16,
|
||||||
Some(height),
|
}
|
||||||
|transitions| {
|
|
||||||
out.extend(fax::decoder::pels(transitions, width).map(|c| match c {
|
impl<R2: Read> Read for Group4Reader<R2> {
|
||||||
fax::Color::Black => 255,
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
fax::Color::White => 0,
|
if self.line_buf.is_empty() && self.y < self.height {
|
||||||
}))
|
let next = self
|
||||||
},
|
.decoder
|
||||||
);
|
.advance()
|
||||||
Box::new(Cursor::new(out))
|
.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::pels(self.decoder.transition(), self.width)
|
||||||
|
.map(|c| match c {
|
||||||
|
fax::Color::Black => 255,
|
||||||
|
fax::Color::White => 0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 => {
|
method => {
|
||||||
return Err(TiffError::UnsupportedError(
|
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);
|
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(
|
||||||
self.width,
|
|
||||||
self.height,
|
|
||||||
reader,
|
reader,
|
||||||
compression_method,
|
compression_method,
|
||||||
*compressed_bytes,
|
*compressed_bytes,
|
||||||
self.jpeg_tables.as_deref().map(|a| &**a),
|
self.jpeg_tables.as_deref().map(|a| &**a),
|
||||||
|
chunk_dims,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if output_row_stride == chunk_row_bytes {
|
if output_row_stride == chunk_row_bytes {
|
||||||
|
|||||||
Reference in new issue
Block a user