1use core::convert::TryInto;
2
3use crate::{common::BytesPerPixel, Compression};
4
5mod paeth;
6
7#[cfg(feature = "unstable")]
8mod simd;
9
10#[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, Compression::Fastest => Filter::Up, Compression::Fast => Filter::Adaptive,
55 Compression::Balanced => Filter::Adaptive,
56 Compression::High => Filter::Adaptive,
57 }
58 }
59}
60
61#[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 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 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(¤t[..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 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
550fn 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 let mut chunks = buf.chunks_exact(8);
563 for chunk in &mut chunks {
564 if chunk == [0; 8] {
566 continue;
567 }
568
569 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 counts[0][0] == total {
585 return 0;
586 }
587
588 #[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 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
607fn 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 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 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, ¤t, &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 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, ¤t, &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 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}