Skip to content

Commit a31e67a

Browse files
author
Andreas Molzer
authored
Merge pull request #527 from anforowicz/unfiltered-rows-buffer
Extract a separate `unfiltered_rows_buffer` module.
2 parents e912074 + 70a217d commit a31e67a

File tree

4 files changed

+147
-61
lines changed

4 files changed

+147
-61
lines changed

src/decoder/mod.rs

Lines changed: 17 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ mod interlace_info;
22
mod read_decoder;
33
pub(crate) mod stream;
44
pub(crate) mod transform;
5+
mod unfiltering_buffer;
56
mod zlib;
67

78
use self::read_decoder::{ImageDataCompletionStatus, ReadDecoder};
89
use self::stream::{DecodeOptions, DecodingError, FormatErrorInner, CHUNK_BUFFER_SIZE};
910
use self::transform::{create_transform_fn, TransformFn};
11+
use self::unfiltering_buffer::UnfilteringBuffer;
1012

1113
use std::io::Read;
1214
use std::mem;
@@ -15,7 +17,6 @@ use crate::adam7::{self, Adam7Info};
1517
use crate::common::{
1618
BitDepth, BytesPerPixel, ColorType, Info, ParameterErrorKind, Transformations,
1719
};
18-
use crate::filter::{unfilter, FilterType};
1920
use crate::FrameControl;
2021

2122
pub use interlace_info::InterlaceInfo;
@@ -192,9 +193,7 @@ impl<R: Read> Decoder<R> {
192193
bpp: BytesPerPixel::One,
193194
subframe: SubframeInfo::not_yet_init(),
194195
remaining_frames: 0, // Temporary value - fixed below after reading `acTL` and `fcTL`.
195-
data_stream: Vec::new(),
196-
prev_start: 0,
197-
current_start: 0,
196+
unfiltering_buffer: UnfilteringBuffer::new(),
198197
transform: self.transform,
199198
transform_fn: None,
200199
scratch_buffer: Vec::new(),
@@ -288,12 +287,8 @@ pub struct Reader<R: Read> {
288287
subframe: SubframeInfo,
289288
/// How many frames remain to be decoded. Decremented after each `IDAT` or `fdAT` sequence.
290289
remaining_frames: usize,
291-
/// Vec containing the uncompressed image data currently being processed.
292-
data_stream: Vec<u8>,
293-
/// Index in `data_stream` where the previous row starts.
294-
prev_start: usize,
295-
/// Index in `data_stream` where the current row starts.
296-
current_start: usize,
290+
/// Buffer with not-yet-`unfilter`-ed image rows
291+
unfiltering_buffer: UnfilteringBuffer,
297292
/// Output transformations
298293
transform: Transformations,
299294
/// Function that can transform decompressed, unfiltered rows into final output.
@@ -362,16 +357,12 @@ impl<R: Read> Reader<R> {
362357

363358
self.subframe = SubframeInfo::new(self.info());
364359
self.bpp = self.info().bpp_in_prediction();
365-
self.data_stream.clear();
366-
self.current_start = 0;
367-
self.prev_start = 0;
360+
self.unfiltering_buffer = UnfilteringBuffer::new();
368361

369362
// Allocate output buffer.
370363
let buflen = self.output_line_size(self.subframe.width);
371364
self.decoder.reserve_bytes(buflen)?;
372365

373-
self.prev_start = self.current_start;
374-
375366
Ok(())
376367
}
377368

@@ -499,7 +490,7 @@ impl<R: Read> Reader<R> {
499490
Some(interlace) => *interlace,
500491
};
501492
if interlace.line_number() == 0 {
502-
self.prev_start = self.current_start;
493+
self.unfiltering_buffer.reset_prev_row();
503494
}
504495
let rowlen = match interlace {
505496
InterlaceInfo::Null(_) => self.subframe.rowlen,
@@ -537,9 +528,7 @@ impl<R: Read> Reader<R> {
537528
}
538529

539530
self.remaining_frames = 0;
540-
self.data_stream.clear();
541-
self.current_start = 0;
542-
self.prev_start = 0;
531+
self.unfiltering_buffer = UnfilteringBuffer::new();
543532
self.decoder.read_until_end_of_input()?;
544533

545534
self.finished = true;
@@ -553,8 +542,8 @@ impl<R: Read> Reader<R> {
553542
output_buffer: &mut [u8],
554543
) -> Result<(), DecodingError> {
555544
self.next_raw_interlaced_row(rowlen)?;
556-
assert_eq!(self.current_start - self.prev_start, rowlen - 1);
557-
let row = &self.data_stream[self.prev_start..self.current_start];
545+
let row = self.unfiltering_buffer.prev_row();
546+
assert_eq!(row.len(), rowlen - 1);
558547

559548
// Apply transformations and write resulting data to buffer.
560549
let transform_fn = {
@@ -619,51 +608,26 @@ impl<R: Read> Reader<R> {
619608
color.raw_row_length_from_width(depth, width) - 1
620609
}
621610

622-
/// Write the next raw interlaced row into `self.prev`.
623-
///
624-
/// The scanline is filtered against the previous scanline according to the specification.
611+
/// Unfilter the next raw interlaced row into `self.unfiltering_buffer`.
625612
fn next_raw_interlaced_row(&mut self, rowlen: usize) -> Result<(), DecodingError> {
626613
// Read image data until we have at least one full row (but possibly more than one).
627-
while self.data_stream.len() - self.current_start < rowlen {
614+
while self.unfiltering_buffer.curr_row_len() < rowlen {
628615
if self.subframe.consumed_and_flushed {
629616
return Err(DecodingError::Format(
630617
FormatErrorInner::NoMoreImageData.into(),
631618
));
632619
}
633620

634-
// Clear the current buffer before appending more data.
635-
if self.prev_start > 0 {
636-
self.data_stream.copy_within(self.prev_start.., 0);
637-
self.data_stream
638-
.truncate(self.data_stream.len() - self.prev_start);
639-
self.current_start -= self.prev_start;
640-
self.prev_start = 0;
641-
}
642-
643-
match self.decoder.decode_image_data(&mut self.data_stream)? {
621+
match self
622+
.decoder
623+
.decode_image_data(self.unfiltering_buffer.as_mut_vec())?
624+
{
644625
ImageDataCompletionStatus::ExpectingMoreData => (),
645626
ImageDataCompletionStatus::Done => self.mark_subframe_as_consumed_and_flushed(),
646627
}
647628
}
648629

649-
// Get a reference to the current row and point scan_start to the next one.
650-
let (prev, row) = self.data_stream.split_at_mut(self.current_start);
651-
652-
// Unfilter the row.
653-
let filter = FilterType::from_u8(row[0]).ok_or(DecodingError::Format(
654-
FormatErrorInner::UnknownFilterMethod(row[0]).into(),
655-
))?;
656-
unfilter(
657-
filter,
658-
self.bpp,
659-
&prev[self.prev_start..],
660-
&mut row[1..rowlen],
661-
);
662-
663-
self.prev_start = self.current_start + 1;
664-
self.current_start += rowlen;
665-
666-
Ok(())
630+
self.unfiltering_buffer.unfilter_curr_row(rowlen, self.bpp)
667631
}
668632
}
669633

src/decoder/read_decoder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ impl<R: Read> ReadDecoder<R> {
173173
}
174174

175175
#[derive(Debug, Eq, PartialEq)]
176-
pub enum ImageDataCompletionStatus {
176+
pub(crate) enum ImageDataCompletionStatus {
177177
ExpectingMoreData,
178178
Done,
179179
}

src/decoder/stream.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2436,17 +2436,16 @@ mod tests {
24362436
/// Creates a ready-to-test [`Reader`] which decodes an animated PNG that contains:
24372437
/// IHDR, acTL, fcTL, IDAT, fcTL, fdAT, IEND. (i.e. IDAT is part of the animation)
24382438
fn create_reader_of_ihdr_actl_fctl_idat_fctl_fdat() -> Reader<VecDeque<u8>> {
2439-
let width = 16;
2440-
let frame_data = generate_rgba8_with_width_and_height(width, width);
2439+
let idat_width = 16;
24412440
let mut fctl = crate::FrameControl {
2442-
width,
2443-
height: width,
2441+
width: idat_width,
2442+
height: idat_width, // same height and width
24442443
..Default::default()
24452444
};
24462445

24472446
let mut png = VecDeque::new();
24482447
write_png_sig(&mut png);
2449-
write_rgba8_ihdr_with_width(&mut png, width);
2448+
write_rgba8_ihdr_with_width(&mut png, idat_width);
24502449
write_actl(
24512450
&mut png,
24522451
&crate::AnimationControl {
@@ -2456,10 +2455,21 @@ mod tests {
24562455
);
24572456
fctl.sequence_number = 0;
24582457
write_fctl(&mut png, &fctl);
2459-
write_chunk(&mut png, b"IDAT", &frame_data);
2458+
// Using `fctl.height + 1` means that the `IDAT` will have "left-over" data after
2459+
// processing. This helps to verify that `Reader.read_until_image_data` discards the
2460+
// left-over data when resetting `UnfilteredRowsBuffer`.
2461+
let idat_data = generate_rgba8_with_width_and_height(fctl.width, fctl.height + 1);
2462+
write_chunk(&mut png, b"IDAT", &idat_data);
2463+
2464+
let fdat_width = 10;
24602465
fctl.sequence_number = 1;
2466+
// Using different width in `IDAT` and `fDAT` frames helps to catch problems that
2467+
// may arise when `Reader.read_until_image_data` doesn't properly reset
2468+
// `UnfilteredRowsBuffer`.
2469+
fctl.width = fdat_width;
24612470
write_fctl(&mut png, &fctl);
2462-
write_fdat(&mut png, 2, &frame_data);
2471+
let fdat_data = generate_rgba8_with_width_and_height(fctl.width, fctl.height);
2472+
write_fdat(&mut png, 2, &fdat_data);
24632473
write_iend(&mut png);
24642474

24652475
Decoder::new(png).read_info().unwrap()

src/decoder/unfiltering_buffer.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
use super::stream::{DecodingError, FormatErrorInner};
2+
use crate::common::BytesPerPixel;
3+
use crate::filter::{unfilter, FilterType};
4+
5+
// Buffer for temporarily holding decompressed, not-yet-`unfilter`-ed rows.
6+
pub(crate) struct UnfilteringBuffer {
7+
/// Vec containing the uncompressed image data currently being processed.
8+
data_stream: Vec<u8>,
9+
/// Index in `data_stream` where the previous row starts.
10+
/// This excludes the filter type byte - it points at the first byte of actual pixel data.
11+
/// The pixel data is already-`unfilter`-ed.
12+
/// If `prev_start == current_start` then it means that there is no previous row.
13+
prev_start: usize,
14+
/// Index in `data_stream` where the current row starts.
15+
/// This points at the filter type byte of the current row (i.e. the actual pixel data starts at `current_start + 1`)
16+
/// The pixel data is not-yet-`unfilter`-ed.
17+
current_start: usize,
18+
}
19+
20+
impl UnfilteringBuffer {
21+
/// Asserts in debug builds that all the invariants hold. No-op in release
22+
/// builds. Intended to be called after creating or mutating `self` to
23+
/// ensure that the final state preserves the invariants.
24+
fn debug_assert_invariants(&self) {
25+
debug_assert!(self.prev_start <= self.current_start);
26+
debug_assert!(self.prev_start <= self.data_stream.len());
27+
debug_assert!(self.current_start <= self.data_stream.len());
28+
}
29+
30+
pub fn new() -> Self {
31+
let result = Self {
32+
data_stream: Vec::new(),
33+
prev_start: 0,
34+
current_start: 0,
35+
};
36+
result.debug_assert_invariants();
37+
result
38+
}
39+
40+
/// Called to indicate that there is no previous row (e.g. when the current
41+
/// row is the first scanline of a given Adam7 pass).
42+
pub fn reset_prev_row(&mut self) {
43+
self.prev_start = self.current_start;
44+
self.debug_assert_invariants();
45+
}
46+
47+
/// Returns the previous (already `unfilter`-ed) row.
48+
pub fn prev_row(&self) -> &[u8] {
49+
// No point calling this if there is no previous row.
50+
debug_assert!(self.prev_start < self.current_start);
51+
52+
&self.data_stream[self.prev_start..self.current_start]
53+
}
54+
55+
/// Returns how many bytes of the current row are present in the buffer.
56+
pub fn curr_row_len(&self) -> usize {
57+
self.data_stream.len() - self.current_start
58+
}
59+
60+
/// Returns a `&mut Vec<u8>` suitable for passing to
61+
/// `ReadDecoder.decode_image_data` or `StreamingDecoder.update`.
62+
///
63+
/// Invariants of `self` depend on the assumption that the caller will only
64+
/// append new bytes to the returned vector (which is indeed the behavior of
65+
/// `ReadDecoder` and `StreamingDecoder`). TODO: Consider protecting the
66+
/// invariants by returning an append-only view of the vector
67+
/// (`FnMut(&[u8])`??? or maybe `std::io::Write`???).
68+
pub fn as_mut_vec(&mut self) -> &mut Vec<u8> {
69+
// Opportunistically compact the current buffer by discarding bytes
70+
// before `prev_start`.
71+
if self.prev_start > 0 {
72+
self.data_stream.copy_within(self.prev_start.., 0);
73+
self.data_stream
74+
.truncate(self.data_stream.len() - self.prev_start);
75+
self.current_start -= self.prev_start;
76+
self.prev_start = 0;
77+
self.debug_assert_invariants();
78+
}
79+
80+
&mut self.data_stream
81+
}
82+
83+
/// Runs `unfilter` on the current row, and then shifts rows so that the current row becomes the previous row.
84+
///
85+
/// Will panic if `self.curr_row_len() < rowlen`.
86+
pub fn unfilter_curr_row(
87+
&mut self,
88+
rowlen: usize,
89+
bpp: BytesPerPixel,
90+
) -> Result<(), DecodingError> {
91+
debug_assert!(rowlen >= 2); // 1 byte for `FilterType` and at least 1 byte of pixel data.
92+
93+
let (prev, row) = self.data_stream.split_at_mut(self.current_start);
94+
let prev: &[u8] = prev; // `prev` is immutable
95+
let prev = &prev[self.prev_start..];
96+
debug_assert!(prev.is_empty() || prev.len() == (rowlen - 1));
97+
98+
// Get the filter type.
99+
let filter = FilterType::from_u8(row[0]).ok_or(DecodingError::Format(
100+
FormatErrorInner::UnknownFilterMethod(row[0]).into(),
101+
))?;
102+
let row = &mut row[1..rowlen];
103+
104+
unfilter(filter, bpp, prev, row);
105+
106+
self.prev_start = self.current_start + 1;
107+
self.current_start += rowlen;
108+
self.debug_assert_invariants();
109+
110+
Ok(())
111+
}
112+
}

0 commit comments

Comments
 (0)