Skip to content

Commit 7c6c327

Browse files
authored
Merge pull request #229 from stephenjudkins/sdj/fax4-support
CCITT group 4 (Fax4) decoding support
2 parents 8cc6bc6 + 9f1de91 commit 7c6c327

File tree

6 files changed

+99
-3
lines changed

6 files changed

+99
-3
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ exclude = ["tests/images/*", "tests/fuzz_images/*"]
2020
half = { version = "2.4.1" }
2121
quick-error = "2.0.1"
2222

23+
# Rename to avoid confusion with the feature name.
24+
fax34 = { package = "fax", version = "0.2.4", optional = true }
2325
flate2 = { version = "1.0.20", optional = true }
2426
weezl = { version = "0.1.10", optional = true }
2527
zstd = { version = "0.13", optional = true }
@@ -35,6 +37,7 @@ default = ["deflate", "jpeg", "lzw"]
3537

3638
# Compression algorithms
3739
deflate = ["dep:flate2"]
40+
fax = ["dep:fax34"]
3841
jpeg = ["dep:zune-jpeg"]
3942
lzw = ["dep:weezl"]
4043
zstd = ["dep:zstd"]

src/decoder/image.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::tags::{
99
use crate::{
1010
ColorType, Directory, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError,
1111
};
12+
1213
use std::io::{self, Cursor, Read, Seek};
1314
use std::num::NonZeroUsize;
1415
use std::sync::Arc;
@@ -384,7 +385,9 @@ impl Image {
384385
reader: R,
385386
compression_method: CompressionMethod,
386387
compressed_length: u64,
387-
#[allow(unused_variables)] jpeg_tables: Option<&[u8]>,
388+
// FIXME: these should be `expect` attributes or we choose another way of passing them.
389+
#[cfg_attr(not(feature = "jpeg"), allow(unused_variables))] jpeg_tables: Option<&[u8]>,
390+
#[cfg_attr(not(feature = "fax"), allow(unused_variables))] dimensions: (u32, u32),
388391
) -> TiffResult<Box<dyn Read + 'r>> {
389392
Ok(match compression_method {
390393
CompressionMethod::None => Box::new(reader),
@@ -452,6 +455,12 @@ impl Image {
452455

453456
Box::new(Cursor::new(data))
454457
}
458+
#[cfg(feature = "fax")]
459+
CompressionMethod::Fax4 => Box::new(super::stream::Group4Reader::new(
460+
dimensions,
461+
reader,
462+
compressed_length,
463+
)?),
455464
method => {
456465
return Err(TiffError::UnsupportedError(
457466
TiffUnsupportedError::UnsupportedCompressionMethod(method),
@@ -658,6 +667,7 @@ impl Image {
658667
compression_method,
659668
*compressed_bytes,
660669
self.jpeg_tables.as_deref().map(|a| &**a),
670+
chunk_dims,
661671
)?;
662672

663673
if output_row_stride == chunk_row_bytes {

src/decoder/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,6 @@ fn fix_endianness(buf: &mut [u8], byte_order: ByteOrder, bit_depth: u8) {
547547
}
548548

549549
impl<R: Read + Seek> Decoder<R> {
550-
/// Create a new decoder that decodes from the stream ```r```
551550
pub fn new(mut r: R) -> TiffResult<Decoder<R>> {
552551
let mut endianess = Vec::with_capacity(2);
553552
(&mut r).take(2).read_to_end(&mut endianess)?;

src/decoder/stream.rs

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//! All IO functionality needed for TIFF decoding
2-
32
use std::io::{self, BufRead, BufReader, Read, Seek, Take};
43

54
/// Byte order of the TIFF file.
@@ -276,6 +275,85 @@ impl<R: Read> Read for PackBitsReader<R> {
276275
}
277276
}
278277

278+
#[cfg(feature = "fax")]
279+
pub struct Group4Reader<R: Read> {
280+
decoder: fax34::decoder::Group4Decoder<io::Bytes<io::BufReader<io::Take<R>>>>,
281+
line_buf: io::Cursor<Vec<u8>>,
282+
height: u16,
283+
width: u16,
284+
y: u16,
285+
}
286+
287+
#[cfg(feature = "fax")]
288+
impl<R: Read> Group4Reader<R> {
289+
pub fn new(
290+
dimensions: (u32, u32),
291+
reader: R,
292+
compressed_length: u64,
293+
) -> crate::TiffResult<Self> {
294+
let width = u16::try_from(dimensions.0)?;
295+
let height = u16::try_from(dimensions.1)?;
296+
297+
Ok(Self {
298+
decoder: fax34::decoder::Group4Decoder::new(
299+
io::BufReader::new(reader.take(compressed_length)).bytes(),
300+
width,
301+
)?,
302+
line_buf: io::Cursor::new(Vec::with_capacity(width.into())),
303+
width,
304+
height,
305+
y: 0,
306+
})
307+
}
308+
}
309+
310+
#[cfg(feature = "fax")]
311+
impl<R: Read> Read for Group4Reader<R> {
312+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
313+
// Either we have not read any line or we are at the end of a line.
314+
if self.line_buf.position() as usize == self.line_buf.get_ref().len()
315+
&& self.y < self.height
316+
{
317+
let next = self.decoder.advance().map_err(std::io::Error::other)?;
318+
319+
match next {
320+
fax34::decoder::DecodeStatus::End => (),
321+
fax34::decoder::DecodeStatus::Incomplete => {
322+
self.y += 1;
323+
324+
// We known `transitions` yields exactly `self.width` items (per doc).
325+
let transitions = fax34::decoder::pels(self.decoder.transition(), self.width);
326+
327+
let buffer = self.line_buf.get_mut();
328+
buffer.resize(usize::from(self.width).div_ceil(8), 0u8);
329+
330+
let target = &mut buffer[..];
331+
332+
let mut bits = transitions.map(|c| match c {
333+
fax34::Color::Black => true,
334+
fax34::Color::White => false,
335+
});
336+
337+
// Assemble bits in MSB as per our library representation for buffer.
338+
for byte in target {
339+
let mut val = 0;
340+
341+
for (idx, bit) in bits.by_ref().take(8).enumerate() {
342+
val |= u8::from(bit) << (7 - idx % 8);
343+
}
344+
345+
*byte = val;
346+
}
347+
348+
self.line_buf.set_position(0);
349+
}
350+
}
351+
}
352+
353+
self.line_buf.read(buf)
354+
}
355+
}
356+
279357
#[cfg(test)]
280358
mod test {
281359
use super::*;

tests/decode_images.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,3 +672,9 @@ fn bytes_gray_f32() {
672672
byte_order_u32,
673673
);
674674
}
675+
676+
#[test]
677+
#[cfg(feature = "fax")]
678+
fn test_fax4() {
679+
test_image_sum_u8("fax4.tiff", ColorType::Gray(1), 7802706);
680+
}

tests/images/fax4.tiff

32.8 KB
Binary file not shown.

0 commit comments

Comments
 (0)