Refactor Group4 reader into stream module

This commit is contained in:
Stephen Judkins authored and Aurelia Molzer committed 2025-08-31 01:25:43 +02:00
1 parent c75aa98ce2
commit 80ce77190e
5 files changed
+74 -67

No files matched your search

+1 -1
View File
@@ -20,7 +20,7 @@ exclude = ["tests/images/*", "tests/fuzz_images/*"]
half = { version = "2.4.1" } half = { version = "2.4.1" }
quick-error = "2.0.1" quick-error = "2.0.1"
fax = { version = "0.2.2", optional = true } fax = { version = "0.2.4", optional = true }
flate2 = { version = "1.0.20", optional = true } flate2 = { version = "1.0.20", optional = true }
weezl = { version = "0.1.10", optional = true } weezl = { version = "0.1.10", optional = true }
zstd = { version = "0.13", optional = true } zstd = { version = "0.13", optional = true }
+5 -64
View File
@@ -10,8 +10,6 @@ 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;
@@ -458,68 +456,11 @@ impl Image {
Box::new(Cursor::new(data)) Box::new(Cursor::new(data))
} }
#[cfg(feature = "fax")] #[cfg(feature = "fax")]
CompressionMethod::Fax4 => { CompressionMethod::Fax4 => Box::new(super::stream::Group4Reader::new(
let width = u16::try_from(dimensions.0)?; dimensions,
let height = u16::try_from(dimensions.1)?; reader,
compressed_length,
struct Group4Reader<R2> { )?),
decoder: fax::decoder::Group4Decoder<std::io::Bytes<R2>>,
bits: bitvec::vec::BitVec<u8, bitvec::prelude::Msb0>,
byte_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.byte_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 => {
// eprintln!("DONE!");
self.byte_buf.extend(self.bits.as_raw_slice())
}
fax::decoder::DecodeStatus::Incomplete => {
self.y += 1;
// eprintln!("{:?}", self.y);
for c in
fax::decoder::pels(self.decoder.transition(), self.width)
{
self.bits.push(match c {
fax::Color::Black => true,
fax::Color::White => false,
});
if self.bits.len() == 8 {
self.byte_buf.extend(self.bits.as_raw_slice());
self.bits.clear()
}
}
}
}
}
// eprintln!("{:?}: {:?} / {:?}", self.y, self.byte_buf.len(), self.bits.len());
self.byte_buf.read(buf)
}
}
Box::new(Group4Reader {
decoder: fax::decoder::Group4Decoder::new(
reader.take(compressed_length).bytes(),
width,
)?,
bits: bitvec::vec::BitVec::new(),
byte_buf: VecDeque::new(),
y: 0,
width: width,
height: height,
})
}
method => { method => {
return Err(TiffError::UnsupportedError( return Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedCompressionMethod(method), TiffUnsupportedError::UnsupportedCompressionMethod(method),
-1
View File
@@ -547,7 +547,6 @@ fn fix_endianness(buf: &mut [u8], byte_order: ByteOrder, bit_depth: u8) {
} }
impl<R: Read + Seek> Decoder<R> { impl<R: Read + Seek> Decoder<R> {
/// Create a new decoder that decodes from the stream ```r```
pub fn new(mut r: R) -> TiffResult<Decoder<R>> { pub fn new(mut r: R) -> TiffResult<Decoder<R>> {
let mut endianess = Vec::with_capacity(2); let mut endianess = Vec::with_capacity(2);
(&mut r).take(2).read_to_end(&mut endianess)?; (&mut r).take(2).read_to_end(&mut endianess)?;
+67
View File
@@ -1,5 +1,7 @@
//! All IO functionality needed for TIFF decoding //! All IO functionality needed for TIFF decoding
#[cfg(feature = "fax")]
use std::collections::VecDeque;
use std::io::{self, BufRead, BufReader, Read, Seek, Take}; use std::io::{self, BufRead, BufReader, Read, Seek, Take};
/// Byte order of the TIFF file. /// Byte order of the TIFF file.
@@ -276,6 +278,71 @@ impl<R: Read> Read for PackBitsReader<R> {
} }
} }
#[cfg(feature = "fax")]
pub struct Group4Reader<R: Read> {
decoder: fax::decoder::Group4Decoder<io::Bytes<io::Take<R>>>,
bits: bitvec::vec::BitVec<u8, bitvec::prelude::Msb0>,
byte_buf: VecDeque<u8>,
height: u16,
width: u16,
y: u16,
}
#[cfg(feature = "fax")]
impl<R: Read> Group4Reader<R> {
pub fn new(
dimensions: (u32, u32),
reader: R,
compressed_length: u64,
) -> crate::TiffResult<Self> {
let width = u16::try_from(dimensions.0)?;
let height = u16::try_from(dimensions.1)?;
Ok(Self {
decoder: fax::decoder::Group4Decoder::new(
reader.take(compressed_length).bytes(),
width,
)?,
bits: bitvec::vec::BitVec::new(),
byte_buf: VecDeque::new(),
width: width,
height: height,
y: 0,
})
}
pub fn dump_bits(&mut self) -> () {
self.byte_buf.extend(self.bits.as_raw_slice());
self.bits.clear()
}
}
#[cfg(feature = "fax")]
impl<R: Read> Read for Group4Reader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.byte_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 => self.dump_bits(),
fax::decoder::DecodeStatus::Incomplete => {
self.y += 1;
for c in fax::decoder::pels(self.decoder.transition(), self.width) {
self.bits.push(match c {
fax::Color::Black => true,
fax::Color::White => false,
});
}
self.dump_bits();
}
}
}
self.byte_buf.read(buf)
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
+1 -1
View File
@@ -676,5 +676,5 @@ fn bytes_gray_f32() {
#[test] #[test]
#[cfg(feature = "fax")] #[cfg(feature = "fax")]
fn test_fax4() { fn test_fax4() {
test_image_sum_u8("fax4.tiff", ColorType::Gray(1), 62384985); test_image_sum_u8("fax4.tiff", ColorType::Gray(1), 7802706);
} }