Skip to main content

png/filter/
mod.rs

1use core::convert::TryInto;
2
3use crate::{common::BytesPerPixel, Compression};
4
5mod paeth;
6
7#[cfg(feature = "unstable")]
8mod simd;
9
10/// The byte level filter applied to scanlines to prepare them for compression.
11///
12/// Compression in general benefits from repetitive data. The filter is a content-aware method of
13/// compressing the range of occurring byte values to help the compression algorithm. Note that
14/// this does not operate on pixels but on raw bytes of a scanline.
15///
16/// Details on how each filter works can be found in the [PNG Book](http://www.libpng.org/pub/png/book/chapter09.html).
17///
18/// The default filter is `Adaptive`, which uses heuristics to select the best filter for every row.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[non_exhaustive]
21pub enum Filter {
22    NoFilter,
23    Sub,
24    Up,
25    Avg,
26    Paeth,
27    Adaptive,
28    MinEntropy,
29}
30
31impl Default for Filter {
32    fn default() -> Self {
33        Filter::Adaptive
34    }
35}
36
37impl From<RowFilter> for Filter {
38    fn from(value: RowFilter) -> Self {
39        match value {
40            RowFilter::NoFilter => Filter::NoFilter,
41            RowFilter::Sub => Filter::Sub,
42            RowFilter::Up => Filter::Up,
43            RowFilter::Avg => Filter::Avg,
44            RowFilter::Paeth => Filter::Paeth,
45        }
46    }
47}
48
49impl Filter {
50    pub(crate) fn from_simple(compression: Compression) -> Self {
51        match compression {
52            Compression::NoCompression => Filter::NoFilter, // with no DEFLATE filtering would only waste time
53            Compression::Fastest => Filter::Up, // pairs well with FdeflateUltraFast, producing much smaller files while being very fast
54            Compression::Fast => Filter::Adaptive,
55            Compression::Balanced => Filter::Adaptive,
56            Compression::High => Filter::Adaptive,
57        }
58    }
59}
60
61/// Unlike the public [Filter], does not include the "Adaptive" option
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63#[repr(u8)]
64pub(crate) enum RowFilter {
65    NoFilter = 0,
66    Sub = 1,
67    Up = 2,
68    Avg = 3,
69    Paeth = 4,
70}
71
72impl Default for RowFilter {
73    fn default() -> Self {
74        RowFilter::Up
75    }
76}
77
78impl RowFilter {
79    pub fn from_u8(n: u8) -> Option<Self> {
80        match n {
81            0 => Some(Self::NoFilter),
82            1 => Some(Self::Sub),
83            2 => Some(Self::Up),
84            3 => Some(Self::Avg),
85            4 => Some(Self::Paeth),
86            _ => None,
87        }
88    }
89
90    pub fn from_method(strat: Filter) -> Option<Self> {
91        match strat {
92            Filter::NoFilter => Some(Self::NoFilter),
93            Filter::Sub => Some(Self::Sub),
94            Filter::Up => Some(Self::Up),
95            Filter::Avg => Some(Self::Avg),
96            Filter::Paeth => Some(Self::Paeth),
97            Filter::Adaptive | Filter::MinEntropy => None,
98        }
99    }
100}
101
102pub(crate) fn unfilter(
103    mut filter: RowFilter,
104    tbpp: BytesPerPixel,
105    previous: &[u8],
106    current: &mut [u8],
107) {
108    use self::RowFilter::*;
109
110    // If the previous row is empty, then treat it as if it were filled with zeros.
111    if previous.is_empty() {
112        if filter == Paeth {
113            filter = Sub;
114        } else if filter == Up {
115            filter = NoFilter;
116        }
117    }
118
119    match filter {
120        NoFilter => {}
121        Sub => match tbpp {
122            BytesPerPixel::One => {
123                current.iter_mut().reduce(|&mut prev, curr| {
124                    *curr = curr.wrapping_add(prev);
125                    curr
126                });
127            }
128            BytesPerPixel::Two => {
129                let mut prev = [0; 2];
130                for chunk in current.chunks_exact_mut(2) {
131                    let new_chunk = [
132                        chunk[0].wrapping_add(prev[0]),
133                        chunk[1].wrapping_add(prev[1]),
134                    ];
135                    *TryInto::<&mut [u8; 2]>::try_into(chunk).unwrap() = new_chunk;
136                    prev = new_chunk;
137                }
138            }
139            BytesPerPixel::Three => {
140                let mut prev = [0; 3];
141                for chunk in current.chunks_exact_mut(3) {
142                    let new_chunk = [
143                        chunk[0].wrapping_add(prev[0]),
144                        chunk[1].wrapping_add(prev[1]),
145                        chunk[2].wrapping_add(prev[2]),
146                    ];
147                    *TryInto::<&mut [u8; 3]>::try_into(chunk).unwrap() = new_chunk;
148                    prev = new_chunk;
149                }
150            }
151            BytesPerPixel::Four => {
152                let mut prev = [0; 4];
153                for chunk in current.chunks_exact_mut(4) {
154                    let new_chunk = [
155                        chunk[0].wrapping_add(prev[0]),
156                        chunk[1].wrapping_add(prev[1]),
157                        chunk[2].wrapping_add(prev[2]),
158                        chunk[3].wrapping_add(prev[3]),
159                    ];
160                    *TryInto::<&mut [u8; 4]>::try_into(chunk).unwrap() = new_chunk;
161                    prev = new_chunk;
162                }
163            }
164            BytesPerPixel::Six => {
165                let mut prev = [0; 6];
166                for chunk in current.chunks_exact_mut(6) {
167                    let new_chunk = [
168                        chunk[0].wrapping_add(prev[0]),
169                        chunk[1].wrapping_add(prev[1]),
170                        chunk[2].wrapping_add(prev[2]),
171                        chunk[3].wrapping_add(prev[3]),
172                        chunk[4].wrapping_add(prev[4]),
173                        chunk[5].wrapping_add(prev[5]),
174                    ];
175                    *TryInto::<&mut [u8; 6]>::try_into(chunk).unwrap() = new_chunk;
176                    prev = new_chunk;
177                }
178            }
179            BytesPerPixel::Eight => {
180                let mut prev = [0; 8];
181                for chunk in current.chunks_exact_mut(8) {
182                    let new_chunk = [
183                        chunk[0].wrapping_add(prev[0]),
184                        chunk[1].wrapping_add(prev[1]),
185                        chunk[2].wrapping_add(prev[2]),
186                        chunk[3].wrapping_add(prev[3]),
187                        chunk[4].wrapping_add(prev[4]),
188                        chunk[5].wrapping_add(prev[5]),
189                        chunk[6].wrapping_add(prev[6]),
190                        chunk[7].wrapping_add(prev[7]),
191                    ];
192                    *TryInto::<&mut [u8; 8]>::try_into(chunk).unwrap() = new_chunk;
193                    prev = new_chunk;
194                }
195            }
196        },
197        Up => {
198            for (curr, &above) in current.iter_mut().zip(previous) {
199                *curr = curr.wrapping_add(above);
200            }
201        }
202        Avg if previous.is_empty() => match tbpp {
203            BytesPerPixel::One => {
204                current.iter_mut().reduce(|&mut prev, curr| {
205                    *curr = curr.wrapping_add(prev / 2);
206                    curr
207                });
208            }
209            BytesPerPixel::Two => {
210                let mut prev = [0; 2];
211                for chunk in current.chunks_exact_mut(2) {
212                    let new_chunk = [
213                        chunk[0].wrapping_add(prev[0] / 2),
214                        chunk[1].wrapping_add(prev[1] / 2),
215                    ];
216                    *TryInto::<&mut [u8; 2]>::try_into(chunk).unwrap() = new_chunk;
217                    prev = new_chunk;
218                }
219            }
220            BytesPerPixel::Three => {
221                let mut prev = [0; 3];
222                for chunk in current.chunks_exact_mut(3) {
223                    let new_chunk = [
224                        chunk[0].wrapping_add(prev[0] / 2),
225                        chunk[1].wrapping_add(prev[1] / 2),
226                        chunk[2].wrapping_add(prev[2] / 2),
227                    ];
228                    *TryInto::<&mut [u8; 3]>::try_into(chunk).unwrap() = new_chunk;
229                    prev = new_chunk;
230                }
231            }
232            BytesPerPixel::Four => {
233                let mut prev = [0; 4];
234                for chunk in current.chunks_exact_mut(4) {
235                    let new_chunk = [
236                        chunk[0].wrapping_add(prev[0] / 2),
237                        chunk[1].wrapping_add(prev[1] / 2),
238                        chunk[2].wrapping_add(prev[2] / 2),
239                        chunk[3].wrapping_add(prev[3] / 2),
240                    ];
241                    *TryInto::<&mut [u8; 4]>::try_into(chunk).unwrap() = new_chunk;
242                    prev = new_chunk;
243                }
244            }
245            BytesPerPixel::Six => {
246                let mut prev = [0; 6];
247                for chunk in current.chunks_exact_mut(6) {
248                    let new_chunk = [
249                        chunk[0].wrapping_add(prev[0] / 2),
250                        chunk[1].wrapping_add(prev[1] / 2),
251                        chunk[2].wrapping_add(prev[2] / 2),
252                        chunk[3].wrapping_add(prev[3] / 2),
253                        chunk[4].wrapping_add(prev[4] / 2),
254                        chunk[5].wrapping_add(prev[5] / 2),
255                    ];
256                    *TryInto::<&mut [u8; 6]>::try_into(chunk).unwrap() = new_chunk;
257                    prev = new_chunk;
258                }
259            }
260            BytesPerPixel::Eight => {
261                let mut prev = [0; 8];
262                for chunk in current.chunks_exact_mut(8) {
263                    let new_chunk = [
264                        chunk[0].wrapping_add(prev[0] / 2),
265                        chunk[1].wrapping_add(prev[1] / 2),
266                        chunk[2].wrapping_add(prev[2] / 2),
267                        chunk[3].wrapping_add(prev[3] / 2),
268                        chunk[4].wrapping_add(prev[4] / 2),
269                        chunk[5].wrapping_add(prev[5] / 2),
270                        chunk[6].wrapping_add(prev[6] / 2),
271                        chunk[7].wrapping_add(prev[7] / 2),
272                    ];
273                    *TryInto::<&mut [u8; 8]>::try_into(chunk).unwrap() = new_chunk;
274                    prev = new_chunk;
275                }
276            }
277        },
278        Avg => match tbpp {
279            BytesPerPixel::One => {
280                let mut lprev = [0; 1];
281                for (chunk, above) in current.chunks_exact_mut(1).zip(previous.chunks_exact(1)) {
282                    let new_chunk =
283                        [chunk[0].wrapping_add(((above[0] as u16 + lprev[0] as u16) / 2) as u8)];
284                    *TryInto::<&mut [u8; 1]>::try_into(chunk).unwrap() = new_chunk;
285                    lprev = new_chunk;
286                }
287            }
288            BytesPerPixel::Two => {
289                let mut lprev = [0; 2];
290                for (chunk, above) in current.chunks_exact_mut(2).zip(previous.chunks_exact(2)) {
291                    let new_chunk = [
292                        chunk[0].wrapping_add(((above[0] as u16 + lprev[0] as u16) / 2) as u8),
293                        chunk[1].wrapping_add(((above[1] as u16 + lprev[1] as u16) / 2) as u8),
294                    ];
295                    *TryInto::<&mut [u8; 2]>::try_into(chunk).unwrap() = new_chunk;
296                    lprev = new_chunk;
297                }
298            }
299            BytesPerPixel::Three => {
300                let mut lprev = [0; 3];
301                for (chunk, above) in current.chunks_exact_mut(3).zip(previous.chunks_exact(3)) {
302                    let new_chunk = [
303                        chunk[0].wrapping_add(((above[0] as u16 + lprev[0] as u16) / 2) as u8),
304                        chunk[1].wrapping_add(((above[1] as u16 + lprev[1] as u16) / 2) as u8),
305                        chunk[2].wrapping_add(((above[2] as u16 + lprev[2] as u16) / 2) as u8),
306                    ];
307                    *TryInto::<&mut [u8; 3]>::try_into(chunk).unwrap() = new_chunk;
308                    lprev = new_chunk;
309                }
310            }
311            BytesPerPixel::Four => {
312                let mut lprev = [0; 4];
313                for (chunk, above) in current.chunks_exact_mut(4).zip(previous.chunks_exact(4)) {
314                    let new_chunk = [
315                        chunk[0].wrapping_add(((above[0] as u16 + lprev[0] as u16) / 2) as u8),
316                        chunk[1].wrapping_add(((above[1] as u16 + lprev[1] as u16) / 2) as u8),
317                        chunk[2].wrapping_add(((above[2] as u16 + lprev[2] as u16) / 2) as u8),
318                        chunk[3].wrapping_add(((above[3] as u16 + lprev[3] as u16) / 2) as u8),
319                    ];
320                    *TryInto::<&mut [u8; 4]>::try_into(chunk).unwrap() = new_chunk;
321                    lprev = new_chunk;
322                }
323            }
324            BytesPerPixel::Six => {
325                let mut lprev = [0; 6];
326                for (chunk, above) in current.chunks_exact_mut(6).zip(previous.chunks_exact(6)) {
327                    let new_chunk = [
328                        chunk[0].wrapping_add(((above[0] as u16 + lprev[0] as u16) / 2) as u8),
329                        chunk[1].wrapping_add(((above[1] as u16 + lprev[1] as u16) / 2) as u8),
330                        chunk[2].wrapping_add(((above[2] as u16 + lprev[2] as u16) / 2) as u8),
331                        chunk[3].wrapping_add(((above[3] as u16 + lprev[3] as u16) / 2) as u8),
332                        chunk[4].wrapping_add(((above[4] as u16 + lprev[4] as u16) / 2) as u8),
333                        chunk[5].wrapping_add(((above[5] as u16 + lprev[5] as u16) / 2) as u8),
334                    ];
335                    *TryInto::<&mut [u8; 6]>::try_into(chunk).unwrap() = new_chunk;
336                    lprev = new_chunk;
337                }
338            }
339            BytesPerPixel::Eight => {
340                let mut lprev = [0; 8];
341                for (chunk, above) in current.chunks_exact_mut(8).zip(previous.chunks_exact(8)) {
342                    let new_chunk = [
343                        chunk[0].wrapping_add(((above[0] as u16 + lprev[0] as u16) / 2) as u8),
344                        chunk[1].wrapping_add(((above[1] as u16 + lprev[1] as u16) / 2) as u8),
345                        chunk[2].wrapping_add(((above[2] as u16 + lprev[2] as u16) / 2) as u8),
346                        chunk[3].wrapping_add(((above[3] as u16 + lprev[3] as u16) / 2) as u8),
347                        chunk[4].wrapping_add(((above[4] as u16 + lprev[4] as u16) / 2) as u8),
348                        chunk[5].wrapping_add(((above[5] as u16 + lprev[5] as u16) / 2) as u8),
349                        chunk[6].wrapping_add(((above[6] as u16 + lprev[6] as u16) / 2) as u8),
350                        chunk[7].wrapping_add(((above[7] as u16 + lprev[7] as u16) / 2) as u8),
351                    ];
352                    *TryInto::<&mut [u8; 8]>::try_into(chunk).unwrap() = new_chunk;
353                    lprev = new_chunk;
354                }
355            }
356        },
357        Paeth => paeth::unfilter(tbpp, previous, current),
358    }
359}
360
361fn filter_internal(
362    method: RowFilter,
363    bpp: usize,
364    len: usize,
365    previous: &[u8],
366    current: &[u8],
367    output: &mut [u8],
368) -> RowFilter {
369    use self::RowFilter::*;
370
371    // This value was chosen experimentally based on what achieved the best performance. The
372    // Rust compiler does auto-vectorization, and 32-bytes per loop iteration seems to enable
373    // the fastest code when doing so.
374    const CHUNK_SIZE: usize = 32;
375
376    match method {
377        NoFilter => {
378            output.copy_from_slice(current);
379            NoFilter
380        }
381        Sub => {
382            let mut out_chunks = output[bpp..].chunks_exact_mut(CHUNK_SIZE);
383            let mut cur_chunks = current[bpp..].chunks_exact(CHUNK_SIZE);
384            let mut prev_chunks = current[..len - bpp].chunks_exact(CHUNK_SIZE);
385
386            for ((out, cur), prev) in (&mut out_chunks).zip(&mut cur_chunks).zip(&mut prev_chunks) {
387                for i in 0..CHUNK_SIZE {
388                    out[i] = cur[i].wrapping_sub(prev[i]);
389                }
390            }
391
392            for ((out, cur), &prev) in out_chunks
393                .into_remainder()
394                .iter_mut()
395                .zip(cur_chunks.remainder())
396                .zip(prev_chunks.remainder())
397            {
398                *out = cur.wrapping_sub(prev);
399            }
400
401            output[..bpp].copy_from_slice(&current[..bpp]);
402            Sub
403        }
404        Up => {
405            let mut out_chunks = output.chunks_exact_mut(CHUNK_SIZE);
406            let mut cur_chunks = current.chunks_exact(CHUNK_SIZE);
407            let mut prev_chunks = previous.chunks_exact(CHUNK_SIZE);
408
409            for ((out, cur), prev) in (&mut out_chunks).zip(&mut cur_chunks).zip(&mut prev_chunks) {
410                for i in 0..CHUNK_SIZE {
411                    out[i] = cur[i].wrapping_sub(prev[i]);
412                }
413            }
414
415            for ((out, cur), &prev) in out_chunks
416                .into_remainder()
417                .iter_mut()
418                .zip(cur_chunks.remainder())
419                .zip(prev_chunks.remainder())
420            {
421                *out = cur.wrapping_sub(prev);
422            }
423            Up
424        }
425        Avg => {
426            let mut out_chunks = output[bpp..].chunks_exact_mut(CHUNK_SIZE);
427            let mut cur_chunks = current[bpp..].chunks_exact(CHUNK_SIZE);
428            let mut cur_minus_bpp_chunks = current[..len - bpp].chunks_exact(CHUNK_SIZE);
429            let mut prev_chunks = previous[bpp..].chunks_exact(CHUNK_SIZE);
430
431            for (((out, cur), cur_minus_bpp), prev) in (&mut out_chunks)
432                .zip(&mut cur_chunks)
433                .zip(&mut cur_minus_bpp_chunks)
434                .zip(&mut prev_chunks)
435            {
436                for i in 0..CHUNK_SIZE {
437                    // Bitwise average of two integers without overflow and
438                    // without converting to a wider bit-width. See:
439                    // http://aggregate.org/MAGIC/#Average%20of%20Integers
440                    // If this is unrolled by component, consider reverting to
441                    // `((cur_minus_bpp[i] as u16 + prev[i] as u16) / 2) as u8`
442                    out[i] = cur[i].wrapping_sub(
443                        (cur_minus_bpp[i] & prev[i]) + ((cur_minus_bpp[i] ^ prev[i]) >> 1),
444                    );
445                }
446            }
447
448            for (((out, cur), &cur_minus_bpp), &prev) in out_chunks
449                .into_remainder()
450                .iter_mut()
451                .zip(cur_chunks.remainder())
452                .zip(cur_minus_bpp_chunks.remainder())
453                .zip(prev_chunks.remainder())
454            {
455                *out = cur.wrapping_sub((cur_minus_bpp & prev) + ((cur_minus_bpp ^ prev) >> 1));
456            }
457
458            for i in 0..bpp {
459                output[i] = current[i].wrapping_sub(previous[i] / 2);
460            }
461            Avg
462        }
463        Paeth => {
464            let mut out_chunks = output[bpp..].chunks_exact_mut(CHUNK_SIZE);
465            let mut cur_chunks = current[bpp..].chunks_exact(CHUNK_SIZE);
466            let mut a_chunks = current[..len - bpp].chunks_exact(CHUNK_SIZE);
467            let mut b_chunks = previous[bpp..].chunks_exact(CHUNK_SIZE);
468            let mut c_chunks = previous[..len - bpp].chunks_exact(CHUNK_SIZE);
469
470            for ((((out, cur), a), b), c) in (&mut out_chunks)
471                .zip(&mut cur_chunks)
472                .zip(&mut a_chunks)
473                .zip(&mut b_chunks)
474                .zip(&mut c_chunks)
475            {
476                for i in 0..CHUNK_SIZE {
477                    out[i] = cur[i].wrapping_sub(paeth::filter_paeth_fpnge(a[i], b[i], c[i]));
478                }
479            }
480
481            for ((((out, cur), &a), &b), &c) in out_chunks
482                .into_remainder()
483                .iter_mut()
484                .zip(cur_chunks.remainder())
485                .zip(a_chunks.remainder())
486                .zip(b_chunks.remainder())
487                .zip(c_chunks.remainder())
488            {
489                *out = cur.wrapping_sub(paeth::filter_paeth_fpnge(a, b, c));
490            }
491
492            for i in 0..bpp {
493                output[i] = current[i].wrapping_sub(paeth::filter_paeth_fpnge(0, previous[i], 0));
494            }
495            Paeth
496        }
497    }
498}
499
500fn adaptive_filter(
501    f: impl Fn(&[u8]) -> u64,
502    bpp: usize,
503    len: usize,
504    previous: &[u8],
505    current: &[u8],
506    output: &mut [u8],
507) -> RowFilter {
508    use RowFilter::*;
509
510    let mut min_cost: u64 = u64::MAX;
511    let mut filter_choice = RowFilter::NoFilter;
512    for &filter in [Up, Sub, Avg, Paeth].iter() {
513        filter_internal(filter, bpp, len, previous, current, output);
514        let cost = f(output);
515        if cost <= min_cost {
516            min_cost = cost;
517            filter_choice = filter;
518
519            if cost == 0 {
520                return filter_choice;
521            }
522        }
523    }
524    if filter_choice != Paeth {
525        filter_internal(filter_choice, bpp, len, previous, current, output);
526    }
527    filter_choice
528}
529
530pub(crate) fn filter(
531    method: Filter,
532    bpp: BytesPerPixel,
533    previous: &[u8],
534    current: &[u8],
535    output: &mut [u8],
536) -> RowFilter {
537    let bpp = bpp.into_usize();
538    let len = current.len();
539
540    match method {
541        Filter::Adaptive => adaptive_filter(sum_buffer, bpp, len, previous, current, output),
542        Filter::MinEntropy => adaptive_filter(entropy, bpp, len, previous, current, output),
543        _ => {
544            let filter = RowFilter::from_method(method).unwrap();
545            filter_internal(filter, bpp, len, previous, current, output)
546        }
547    }
548}
549
550/// Estimate the value of i * log2(i) without using floating point operations,
551/// implementation originally from oxipng.
552fn ilog2i(i: u32) -> u32 {
553    let log = 32 - i.leading_zeros() - 1;
554    i * log + ((i - (1 << log)) << 1)
555}
556
557fn entropy(buf: &[u8]) -> u64 {
558    let mut counts = [[0_u32; 256]; 4];
559    let mut total = 0;
560
561    // Count the number of occurrences of each byte value.
562    let mut chunks = buf.chunks_exact(8);
563    for chunk in &mut chunks {
564        // Runs of zeros are common and very compressible, so treat them as free.
565        if chunk == [0; 8] {
566            continue;
567        }
568
569        // Scatter the counts into 4 separate arrays to reduce contention.
570        for j in 0..2 {
571            counts[0][chunk[j * 4] as usize] += 1;
572            counts[1][chunk[1 + j * 4] as usize] += 1;
573            counts[2][chunk[2 + j * 4] as usize] += 1;
574            counts[3][chunk[3 + j * 4] as usize] += 1;
575        }
576        total += 8;
577    }
578    for &lit in chunks.remainder() {
579        counts[0][lit as usize] += 1;
580        total += 1;
581    }
582
583    // If the input is entirely zeros, short-circuit the entropy calculation.
584    if counts[0][0] == total {
585        return 0;
586    }
587
588    // Consolidate the counts.
589    //
590    // Upstream bug: <https://github.com/rust-lang/rust-clippy/issues/11529>
591    #[allow(clippy::needless_range_loop)]
592    for i in 0..256 {
593        counts[0][i] += counts[1][i] + counts[2][i] + counts[3][i];
594    }
595
596    // Compute the entropy.
597    let mut entropy = ilog2i(total);
598    for &count in &counts[0] {
599        if count > 0 {
600            entropy = entropy.saturating_sub(ilog2i(count));
601        }
602    }
603
604    entropy as u64
605}
606
607// Helper function for Adaptive filter buffer summation
608fn sum_buffer(buf: &[u8]) -> u64 {
609    const CHUNK_SIZE: usize = 32;
610
611    let mut buf_chunks = buf.chunks_exact(CHUNK_SIZE);
612    let mut sum = 0_u64;
613
614    for chunk in &mut buf_chunks {
615        // At most, `acc` can be `32 * (i8::MIN as u8) = 32 * 128 = 4096`.
616        let mut acc = 0;
617        for &b in chunk {
618            acc += u64::from((b as i8).unsigned_abs());
619        }
620        sum = sum.saturating_add(acc);
621    }
622
623    let mut acc = 0;
624    for &b in buf_chunks.remainder() {
625        acc += u64::from((b as i8).unsigned_abs());
626    }
627
628    sum.saturating_add(acc)
629}
630
631#[cfg(test)]
632mod test {
633    use super::*;
634    use core::iter;
635
636    #[test]
637    fn roundtrip() {
638        // A multiple of 8, 6, 4, 3, 2, 1
639        const LEN: u8 = 240;
640        let previous: Vec<_> = iter::repeat(1).take(LEN.into()).collect();
641        let current: Vec<_> = (0..LEN).collect();
642        let expected = current.clone();
643
644        let roundtrip = |kind: RowFilter, bpp: BytesPerPixel| {
645            let mut output = vec![0; LEN.into()];
646            filter(kind.into(), bpp, &previous, &current, &mut output);
647            unfilter(kind, bpp, &previous, &mut output);
648            assert_eq!(
649                output, expected,
650                "Filtering {:?} with {:?} does not roundtrip",
651                bpp, kind
652            );
653        };
654
655        let filters = [
656            RowFilter::NoFilter,
657            RowFilter::Sub,
658            RowFilter::Up,
659            RowFilter::Avg,
660            RowFilter::Paeth,
661        ];
662
663        let bpps = [
664            BytesPerPixel::One,
665            BytesPerPixel::Two,
666            BytesPerPixel::Three,
667            BytesPerPixel::Four,
668            BytesPerPixel::Six,
669            BytesPerPixel::Eight,
670        ];
671
672        for &filter in filters.iter() {
673            for &bpp in bpps.iter() {
674                roundtrip(filter, bpp);
675            }
676        }
677    }
678
679    #[test]
680    fn roundtrip_ascending_previous_line() {
681        // A multiple of 8, 6, 4, 3, 2, 1
682        const LEN: u8 = 240;
683        let previous: Vec<_> = (0..LEN).collect();
684        let current: Vec<_> = (0..LEN).collect();
685        let expected = current.clone();
686
687        let roundtrip = |kind: RowFilter, bpp: BytesPerPixel| {
688            let mut output = vec![0; LEN.into()];
689            filter(kind.into(), bpp, &previous, &current, &mut output);
690            unfilter(kind, bpp, &previous, &mut output);
691            assert_eq!(
692                output, expected,
693                "Filtering {:?} with {:?} does not roundtrip",
694                bpp, kind
695            );
696        };
697
698        let filters = [
699            RowFilter::NoFilter,
700            RowFilter::Sub,
701            RowFilter::Up,
702            RowFilter::Avg,
703            RowFilter::Paeth,
704        ];
705
706        let bpps = [
707            BytesPerPixel::One,
708            BytesPerPixel::Two,
709            BytesPerPixel::Three,
710            BytesPerPixel::Four,
711            BytesPerPixel::Six,
712            BytesPerPixel::Eight,
713        ];
714
715        for &filter in filters.iter() {
716            for &bpp in bpps.iter() {
717                roundtrip(filter, bpp);
718            }
719        }
720    }
721
722    #[test]
723    // This tests that converting u8 to i8 doesn't overflow when taking the
724    // absolute value for adaptive filtering: -128_i8.abs() will panic in debug
725    // or produce garbage in release mode. The sum of 0..=255u8 should equal the
726    // sum of the absolute values of -128_i8..=127, or abs(-128..=0) + 1..=127.
727    fn sum_buffer_test() {
728        let sum = (0..=128).sum::<u64>() + (1..=127).sum::<u64>();
729        let buf: Vec<u8> = (0_u8..=255).collect();
730
731        assert_eq!(sum, crate::filter::sum_buffer(&buf));
732    }
733}