1use crate::error::{CodecError, CodecResult};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16#[repr(u8)]
17pub enum FilterType {
18 None = 0,
20 Sub = 1,
22 Up = 2,
24 Average = 3,
26 Paeth = 4,
28}
29
30impl FilterType {
31 pub fn from_u8(value: u8) -> CodecResult<Self> {
37 match value {
38 0 => Ok(Self::None),
39 1 => Ok(Self::Sub),
40 2 => Ok(Self::Up),
41 3 => Ok(Self::Average),
42 4 => Ok(Self::Paeth),
43 _ => Err(CodecError::InvalidData(format!(
44 "Invalid filter type: {value}"
45 ))),
46 }
47 }
48
49 #[must_use]
51 pub const fn to_u8(self) -> u8 {
52 self as u8
53 }
54}
55
56#[allow(clippy::needless_pass_by_value)]
69pub fn apply_filter(
70 filter_type: FilterType,
71 scanline: &[u8],
72 prev_scanline: Option<&[u8]>,
73 bytes_per_pixel: usize,
74) -> Vec<u8> {
75 match filter_type {
76 FilterType::None => scanline.to_vec(),
77 FilterType::Sub => apply_sub_filter(scanline, bytes_per_pixel),
78 FilterType::Up => apply_up_filter(scanline, prev_scanline.unwrap_or(&[])),
79 FilterType::Average => {
80 apply_average_filter(scanline, prev_scanline.unwrap_or(&[]), bytes_per_pixel)
81 }
82 FilterType::Paeth => {
83 apply_paeth_filter(scanline, prev_scanline.unwrap_or(&[]), bytes_per_pixel)
84 }
85 }
86}
87
88fn apply_sub_filter(scanline: &[u8], bytes_per_pixel: usize) -> Vec<u8> {
90 let mut filtered = Vec::with_capacity(scanline.len());
91
92 for i in 0..scanline.len() {
93 let left = if i >= bytes_per_pixel {
94 scanline[i - bytes_per_pixel]
95 } else {
96 0
97 };
98 filtered.push(scanline[i].wrapping_sub(left));
99 }
100
101 filtered
102}
103
104fn apply_up_filter(scanline: &[u8], prev_scanline: &[u8]) -> Vec<u8> {
106 let mut filtered = Vec::with_capacity(scanline.len());
107
108 for i in 0..scanline.len() {
109 let above = if i < prev_scanline.len() {
110 prev_scanline[i]
111 } else {
112 0
113 };
114 filtered.push(scanline[i].wrapping_sub(above));
115 }
116
117 filtered
118}
119
120fn apply_average_filter(scanline: &[u8], prev_scanline: &[u8], bytes_per_pixel: usize) -> Vec<u8> {
122 let mut filtered = Vec::with_capacity(scanline.len());
123
124 for i in 0..scanline.len() {
125 let left = if i >= bytes_per_pixel {
126 scanline[i - bytes_per_pixel]
127 } else {
128 0
129 };
130 let above = if i < prev_scanline.len() {
131 prev_scanline[i]
132 } else {
133 0
134 };
135 let avg = ((u16::from(left) + u16::from(above)) / 2) as u8;
136 filtered.push(scanline[i].wrapping_sub(avg));
137 }
138
139 filtered
140}
141
142fn apply_paeth_filter(scanline: &[u8], prev_scanline: &[u8], bytes_per_pixel: usize) -> Vec<u8> {
144 let mut filtered = Vec::with_capacity(scanline.len());
145
146 for i in 0..scanline.len() {
147 let left = if i >= bytes_per_pixel {
148 scanline[i - bytes_per_pixel]
149 } else {
150 0
151 };
152 let above = if i < prev_scanline.len() {
153 prev_scanline[i]
154 } else {
155 0
156 };
157 let upper_left = if i >= bytes_per_pixel && i < prev_scanline.len() {
158 prev_scanline[i - bytes_per_pixel]
159 } else {
160 0
161 };
162
163 let paeth = paeth_predictor(left, above, upper_left);
164 filtered.push(scanline[i].wrapping_sub(paeth));
165 }
166
167 filtered
168}
169
170#[allow(clippy::cast_possible_wrap)]
174fn paeth_predictor(a: u8, b: u8, c: u8) -> u8 {
175 let a = i32::from(a);
176 let b = i32::from(b);
177 let c = i32::from(c);
178
179 let p = a + b - c;
180 let pa = (p - a).abs();
181 let pb = (p - b).abs();
182 let pc = (p - c).abs();
183
184 if pa <= pb && pa <= pc {
185 a as u8
186 } else if pb <= pc {
187 b as u8
188 } else {
189 c as u8
190 }
191}
192
193pub fn unfilter(
206 filter_type: FilterType,
207 filtered: &[u8],
208 prev_scanline: Option<&[u8]>,
209 bytes_per_pixel: usize,
210) -> CodecResult<Vec<u8>> {
211 match filter_type {
212 FilterType::None => Ok(filtered.to_vec()),
213 FilterType::Sub => Ok(unfilter_sub(filtered, bytes_per_pixel)),
214 FilterType::Up => Ok(unfilter_up(filtered, prev_scanline.unwrap_or(&[]))),
215 FilterType::Average => Ok(unfilter_average(
216 filtered,
217 prev_scanline.unwrap_or(&[]),
218 bytes_per_pixel,
219 )),
220 FilterType::Paeth => Ok(unfilter_paeth(
221 filtered,
222 prev_scanline.unwrap_or(&[]),
223 bytes_per_pixel,
224 )),
225 }
226}
227
228fn unfilter_sub(filtered: &[u8], bytes_per_pixel: usize) -> Vec<u8> {
230 let mut unfiltered = Vec::with_capacity(filtered.len());
231
232 for i in 0..filtered.len() {
233 let left = if i >= bytes_per_pixel {
234 unfiltered[i - bytes_per_pixel]
235 } else {
236 0
237 };
238 unfiltered.push(filtered[i].wrapping_add(left));
239 }
240
241 unfiltered
242}
243
244fn unfilter_up(filtered: &[u8], prev_scanline: &[u8]) -> Vec<u8> {
246 let mut unfiltered = Vec::with_capacity(filtered.len());
247
248 for i in 0..filtered.len() {
249 let above = if i < prev_scanline.len() {
250 prev_scanline[i]
251 } else {
252 0
253 };
254 unfiltered.push(filtered[i].wrapping_add(above));
255 }
256
257 unfiltered
258}
259
260fn unfilter_average(filtered: &[u8], prev_scanline: &[u8], bytes_per_pixel: usize) -> Vec<u8> {
262 let mut unfiltered = Vec::with_capacity(filtered.len());
263
264 for i in 0..filtered.len() {
265 let left = if i >= bytes_per_pixel {
266 unfiltered[i - bytes_per_pixel]
267 } else {
268 0
269 };
270 let above = if i < prev_scanline.len() {
271 prev_scanline[i]
272 } else {
273 0
274 };
275 let avg = ((u16::from(left) + u16::from(above)) / 2) as u8;
276 unfiltered.push(filtered[i].wrapping_add(avg));
277 }
278
279 unfiltered
280}
281
282fn unfilter_paeth(filtered: &[u8], prev_scanline: &[u8], bytes_per_pixel: usize) -> Vec<u8> {
284 let mut unfiltered = Vec::with_capacity(filtered.len());
285
286 for i in 0..filtered.len() {
287 let left = if i >= bytes_per_pixel {
288 unfiltered[i - bytes_per_pixel]
289 } else {
290 0
291 };
292 let above = if i < prev_scanline.len() {
293 prev_scanline[i]
294 } else {
295 0
296 };
297 let upper_left = if i >= bytes_per_pixel && i < prev_scanline.len() {
298 prev_scanline[i - bytes_per_pixel]
299 } else {
300 0
301 };
302
303 let paeth = paeth_predictor(left, above, upper_left);
304 unfiltered.push(filtered[i].wrapping_add(paeth));
305 }
306
307 unfiltered
308}
309
310#[must_use]
314pub fn sum_abs_diff(filtered: &[u8]) -> u64 {
315 filtered.iter().map(|&b| u64::from(b.abs_diff(128))).sum()
316}
317
318#[must_use]
332pub fn select_best_filter(
333 scanline: &[u8],
334 prev_scanline: Option<&[u8]>,
335 bytes_per_pixel: usize,
336) -> (FilterType, Vec<u8>) {
337 let mut best_filter = FilterType::None;
338 let mut best_data = scanline.to_vec();
339 let mut best_score = sum_abs_diff(&best_data);
340
341 let filters = [
342 FilterType::None,
343 FilterType::Sub,
344 FilterType::Up,
345 FilterType::Average,
346 FilterType::Paeth,
347 ];
348
349 for &filter in &filters {
350 let filtered = apply_filter(filter, scanline, prev_scanline, bytes_per_pixel);
351 let score = sum_abs_diff(&filtered);
352
353 if score < best_score {
354 best_score = score;
355 best_filter = filter;
356 best_data = filtered;
357 }
358 }
359
360 (best_filter, best_data)
361}
362
363#[must_use]
367pub fn select_fast_filter(
368 scanline: &[u8],
369 prev_scanline: Option<&[u8]>,
370 bytes_per_pixel: usize,
371) -> (FilterType, Vec<u8>) {
372 let mut best_filter = FilterType::None;
373 let mut best_data = scanline.to_vec();
374 let mut best_score = sum_abs_diff(&best_data);
375
376 let sub_filtered = apply_sub_filter(scanline, bytes_per_pixel);
378 let sub_score = sum_abs_diff(&sub_filtered);
379 if sub_score < best_score {
380 best_score = sub_score;
381 best_filter = FilterType::Sub;
382 best_data = sub_filtered;
383 }
384
385 if let Some(prev) = prev_scanline {
387 let up_filtered = apply_up_filter(scanline, prev);
388 let up_score = sum_abs_diff(&up_filtered);
389 if up_score < best_score {
390 best_filter = FilterType::Up;
391 best_data = up_filtered;
392 }
393 }
394
395 (best_filter, best_data)
396}
397
398#[derive(Debug, Clone, Copy, PartialEq, Eq)]
400pub enum FilterStrategy {
401 None,
403 Sub,
405 Up,
407 Average,
409 Paeth,
411 Fast,
413 Best,
415}
416
417impl FilterStrategy {
418 #[must_use]
424 pub fn apply(
425 &self,
426 scanline: &[u8],
427 prev_scanline: Option<&[u8]>,
428 bytes_per_pixel: usize,
429 ) -> (FilterType, Vec<u8>) {
430 match self {
431 Self::None => (FilterType::None, scanline.to_vec()),
432 Self::Sub => (FilterType::Sub, apply_sub_filter(scanline, bytes_per_pixel)),
433 Self::Up => (
434 FilterType::Up,
435 apply_up_filter(scanline, prev_scanline.unwrap_or(&[])),
436 ),
437 Self::Average => (
438 FilterType::Average,
439 apply_average_filter(scanline, prev_scanline.unwrap_or(&[]), bytes_per_pixel),
440 ),
441 Self::Paeth => (
442 FilterType::Paeth,
443 apply_paeth_filter(scanline, prev_scanline.unwrap_or(&[]), bytes_per_pixel),
444 ),
445 Self::Fast => select_fast_filter(scanline, prev_scanline, bytes_per_pixel),
446 Self::Best => select_best_filter(scanline, prev_scanline, bytes_per_pixel),
447 }
448 }
449}
450
451impl Default for FilterStrategy {
452 fn default() -> Self {
453 Self::Fast
454 }
455}