Skip to content

Commit d7798cd

Browse files
committed
Extract a separate struct PaethState and fn paeth_step.
This refactoring is desirable because: * It removes a little bit of duplication between `unfilter_paeth3` and `unfilter_paeth6` * It helps in a follow-up CL, where we need to use `paeth_step` from more places.
1 parent e6b0959 commit d7798cd

File tree

1 file changed

+41
-40
lines changed

1 file changed

+41
-40
lines changed

src/filter.rs

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ use crate::common::BytesPerPixel;
1010
#[cfg(feature = "unstable")]
1111
mod simd {
1212
use std::simd::{
13-
i16x4, i16x8, u8x4, u8x8, LaneCount, Simd, SimdInt, SimdOrd, SimdPartialEq, SimdUint,
14-
SupportedLaneCount,
13+
u8x4, u8x8, LaneCount, Simd, SimdInt, SimdOrd, SimdPartialEq, SimdUint, SupportedLaneCount,
1514
};
1615

1716
/// This is an equivalent of the `PaethPredictor` function from
@@ -54,6 +53,40 @@ mod simd {
5453
.select(a, smallest.simd_eq(pb).select(b, c))
5554
}
5655

56+
/// Memory of previous pixels (as needed to unfilter `FilterType::Paeth`).
57+
/// See also https://www.w3.org/TR/png/#filter-byte-positions
58+
#[derive(Default)]
59+
struct PaethState<const N: usize>
60+
where
61+
LaneCount<N>: SupportedLaneCount,
62+
{
63+
/// Previous pixel in the previous row.
64+
c: Simd<i16, N>,
65+
66+
/// Previous pixel in the current row.
67+
a: Simd<i16, N>,
68+
}
69+
70+
/// Mutates `x` as needed to unfilter `FilterType::Paeth`.
71+
///
72+
/// `b` is the current pixel in the previous row. `x` is the current pixel in the current row.
73+
/// See also https://www.w3.org/TR/png/#filter-byte-positions
74+
fn paeth_step<const N: usize>(state: &mut PaethState<N>, b: Simd<u8, N>, x: &mut Simd<u8, N>)
75+
where
76+
LaneCount<N>: SupportedLaneCount,
77+
{
78+
// Storing the inputs.
79+
let b = b.cast::<i16>();
80+
81+
// Calculating the new value of the current pixel.
82+
let predictor = paeth_predictor(state.a, b, state.c);
83+
*x += predictor.cast::<u8>();
84+
85+
// Preparing for the next step.
86+
state.c = b;
87+
state.a = x.cast::<i16>();
88+
}
89+
5790
fn load3(src: &[u8]) -> u8x4 {
5891
u8x4::from_array([src[0], src[1], src[2], 0])
5992
}
@@ -67,28 +100,12 @@ mod simd {
67100
debug_assert_eq!(prev_row.len(), curr_row.len());
68101
debug_assert_eq!(prev_row.len() % 3, 0);
69102

70-
// Paeth tries to predict pixel x using the pixel to the left of it, a,
71-
// and two pixels from the previous row, b and c:
72-
//
73-
// prev_row: c b
74-
// curr_row: a x
75-
//
76-
// The first pixel has no left context, and so uses an Up filter, p = b.
77-
// This works naturally with our main loop's p = a+b-c if we force a and c
78-
// to zero.
79-
let mut a = i16x4::default();
80-
let mut c = i16x4::default();
81-
103+
let mut state = PaethState::<4>::default();
82104
for (prev, curr) in prev_row.chunks_exact(3).zip(curr_row.chunks_exact_mut(3)) {
83-
let b = load3(prev).cast::<i16>();
105+
let b = load3(prev);
84106
let mut x = load3(curr);
85-
86-
let predictor = paeth_predictor(a, b, c);
87-
x += predictor.cast::<u8>();
107+
paeth_step(&mut state, b, &mut x);
88108
store3(x, curr);
89-
90-
c = b;
91-
a = x.cast::<i16>();
92109
}
93110
}
94111

@@ -105,28 +122,12 @@ mod simd {
105122
debug_assert_eq!(prev_row.len(), curr_row.len());
106123
debug_assert_eq!(prev_row.len() % 6, 0);
107124

108-
// Paeth tries to predict pixel x using the pixel to the left of it, a,
109-
// and two pixels from the previous row, b and c:
110-
//
111-
// prev_row: c b
112-
// curr_row: a x
113-
//
114-
// The first pixel has no left context, and so uses an Up filter, p = b.
115-
// This works naturally with our main loop's p = a+b-c if we force a and c
116-
// to zero.
117-
let mut a = i16x8::default();
118-
let mut c = i16x8::default();
119-
125+
let mut state = PaethState::<8>::default();
120126
for (prev, curr) in prev_row.chunks_exact(6).zip(curr_row.chunks_exact_mut(6)) {
121-
let b = load6(prev).cast::<i16>();
127+
let b = load6(prev);
122128
let mut x = load6(curr);
123-
124-
let predictor = paeth_predictor(a, b, c);
125-
x += predictor.cast::<u8>();
129+
paeth_step(&mut state, b, &mut x);
126130
store6(x, curr);
127-
128-
c = b;
129-
a = x.cast::<i16>();
130131
}
131132
}
132133
}

0 commit comments

Comments
 (0)