1use crate::common::BytesPerPixel;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[repr(u8)]
10pub enum FilterType {
11 NoFilter = 0,
12 Sub = 1,
13 Up = 2,
14 Avg = 3,
15 Paeth = 4,
16}
17
18impl Default for FilterType {
19 fn default() -> Self {
20 FilterType::Sub
21 }
22}
23
24impl FilterType {
25 pub fn from_u8(n: u8) -> Option<FilterType> {
27 match n {
28 0 => Some(FilterType::NoFilter),
29 1 => Some(FilterType::Sub),
30 2 => Some(FilterType::Up),
31 3 => Some(FilterType::Avg),
32 4 => Some(FilterType::Paeth),
33 _ => None,
34 }
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45#[repr(u8)]
46pub enum AdaptiveFilterType {
47 Adaptive,
48 NonAdaptive,
49}
50
51impl Default for AdaptiveFilterType {
52 fn default() -> Self {
53 AdaptiveFilterType::NonAdaptive
54 }
55}
56
57fn filter_paeth(a: u8, b: u8, c: u8) -> u8 {
58 let ia = i16::from(a);
59 let ib = i16::from(b);
60 let ic = i16::from(c);
61
62 let p = ia + ib - ic;
63
64 let pa = (p - ia).abs();
65 let pb = (p - ib).abs();
66 let pc = (p - ic).abs();
67
68 if pa <= pb && pa <= pc {
69 a
70 } else if pb <= pc {
71 b
72 } else {
73 c
74 }
75}
76
77fn filter_internal(
78 method: FilterType,
79 bpp: usize,
80 len: usize,
81 previous: &[u8],
82 current: &mut [u8],
83) -> FilterType {
84 use self::FilterType::*;
85
86 match method {
87 NoFilter => NoFilter,
88 Sub => {
89 for i in (bpp..len).rev() {
90 current[i] = current[i].wrapping_sub(current[i - bpp]);
91 }
92 Sub
93 }
94 Up => {
95 for i in 0..len {
96 current[i] = current[i].wrapping_sub(previous[i]);
97 }
98 Up
99 }
100 Avg => {
101 for i in (bpp..len).rev() {
102 current[i] = current[i].wrapping_sub(
103 ((u16::from(current[i - bpp]) + u16::from(previous[i])) / 2) as u8,
104 );
105 }
106
107 for i in 0..bpp {
108 current[i] = current[i].wrapping_sub(previous[i] / 2);
109 }
110 Avg
111 }
112 Paeth => {
113 for i in (bpp..len).rev() {
114 current[i] = current[i].wrapping_sub(filter_paeth(
115 current[i - bpp],
116 previous[i],
117 previous[i - bpp],
118 ));
119 }
120
121 for i in 0..bpp {
122 current[i] = current[i].wrapping_sub(filter_paeth(0, previous[i], 0));
123 }
124 Paeth
125 }
126 }
127}
128
129pub(crate) fn filter(
130 method: FilterType,
131 adaptive: AdaptiveFilterType,
132 bpp: BytesPerPixel,
133 previous: &[u8],
134 current: &mut [u8],
135) -> FilterType {
136 use FilterType::*;
137 let bpp = bpp.into_usize();
138 let len = current.len();
139
140 match adaptive {
141 AdaptiveFilterType::NonAdaptive => filter_internal(method, bpp, len, previous, current),
142 AdaptiveFilterType::Adaptive => {
143 let mut filtered_buffer = vec![0; len];
147 filtered_buffer.copy_from_slice(current);
148 let mut scratch = vec![0; len];
149
150 let mut min_sum: usize = sum_buffer(&filtered_buffer);
152 let mut filter_choice = FilterType::NoFilter;
153
154 for &filter in [Sub, Up, Avg, Paeth].iter() {
155 scratch.copy_from_slice(current);
156 filter_internal(filter, bpp, len, previous, &mut scratch);
157 let sum = sum_buffer(&scratch);
158 if sum < min_sum {
159 min_sum = sum;
160 filter_choice = filter;
161 core::mem::swap(&mut filtered_buffer, &mut scratch);
162 }
163 }
164
165 current.copy_from_slice(&filtered_buffer);
166
167 filter_choice
168 }
169 }
170}
171
172fn sum_buffer(buf: &[u8]) -> usize {
174 buf.iter().fold(0, |acc, &x| {
175 acc.saturating_add(i16::from(x as i8).abs() as usize)
176 })
177}