1//! Does loop filtering on webp lossy images
2
3#[inline]
4fn c(val: i32) -> i32 {
5 val.clamp(min:-128, max:127)
6}
7
8//unsigned to signed
9#[inline]
10fn u2s(val: u8) -> i32 {
11 i32::from(val) - 128
12}
13
14//signed to unsigned
15#[inline]
16fn s2u(val: i32) -> u8 {
17 (c(val) + 128) as u8
18}
19
20#[inline]
21const fn diff(val1: u8, val2: u8) -> u8 {
22 if val1 > val2 {
23 val1 - val2
24 } else {
25 val2 - val1
26 }
27}
28
29//15.2
30fn common_adjust(use_outer_taps: bool, pixels: &mut [u8], point: usize, stride: usize) -> i32 {
31 let p1: i32 = u2s(val:pixels[point - 2 * stride]);
32 let p0: i32 = u2s(val:pixels[point - stride]);
33 let q0: i32 = u2s(val:pixels[point]);
34 let q1: i32 = u2s(val:pixels[point + stride]);
35
36 //value for the outer 2 pixels
37 let outer: i32 = if use_outer_taps { c(val:p1 - q1) } else { 0 };
38
39 let mut a: i32 = c(val:outer + 3 * (q0 - p0));
40
41 let b: i32 = (c(val:a + 3)) >> 3;
42
43 a = (c(val:a + 4)) >> 3;
44
45 pixels[point] = s2u(val:q0 - a);
46 pixels[point - stride] = s2u(val:p0 + b);
47
48 a
49}
50
51fn simple_threshold(filter_limit: i32, pixels: &[u8], point: usize, stride: usize) -> bool {
52 i32::from(diff(val1:pixels[point - stride], val2:pixels[point])) * 2
53 + i32::from(diff(val1:pixels[point - 2 * stride], val2:pixels[point + stride])) / 2
54 <= filter_limit
55}
56
57fn should_filter(
58 interior_limit: u8,
59 edge_limit: u8,
60 pixels: &[u8],
61 point: usize,
62 stride: usize,
63) -> bool {
64 simple_threshold(filter_limit:i32::from(edge_limit), pixels, point, stride)
65 && diff(val1:pixels[point - 4 * stride], val2:pixels[point - 3 * stride]) <= interior_limit
66 && diff(val1:pixels[point - 3 * stride], val2:pixels[point - 2 * stride]) <= interior_limit
67 && diff(val1:pixels[point - 2 * stride], val2:pixels[point - stride]) <= interior_limit
68 && diff(val1:pixels[point + 3 * stride], val2:pixels[point + 2 * stride]) <= interior_limit
69 && diff(val1:pixels[point + 2 * stride], val2:pixels[point + stride]) <= interior_limit
70 && diff(val1:pixels[point + stride], val2:pixels[point]) <= interior_limit
71}
72
73fn high_edge_variance(threshold: u8, pixels: &[u8], point: usize, stride: usize) -> bool {
74 diff(val1:pixels[point - 2 * stride], val2:pixels[point - stride]) > threshold
75 || diff(val1:pixels[point + stride], val2:pixels[point]) > threshold
76}
77
78//simple filter
79//effects 4 pixels on an edge(2 each side)
80pub(crate) fn simple_segment(edge_limit: u8, pixels: &mut [u8], point: usize, stride: usize) {
81 if simple_threshold(filter_limit:i32::from(edge_limit), pixels, point, stride) {
82 common_adjust(use_outer_taps:true, pixels, point, stride);
83 }
84}
85
86//normal filter
87//works on the 8 pixels on the edges between subblocks inside a macroblock
88pub(crate) fn subblock_filter(
89 hev_threshold: u8,
90 interior_limit: u8,
91 edge_limit: u8,
92 pixels: &mut [u8],
93 point: usize,
94 stride: usize,
95) {
96 if should_filter(interior_limit, edge_limit, pixels, point, stride) {
97 let hv: bool = high_edge_variance(hev_threshold, pixels, point, stride);
98
99 let a: i32 = (common_adjust(use_outer_taps:hv, pixels, point, stride) + 1) >> 1;
100
101 if !hv {
102 pixels[point + stride] = s2u(val:u2s(val:pixels[point + stride]) - a);
103 pixels[point - 2 * stride] = s2u(val:u2s(val:pixels[point - 2 * stride]) - a);
104 }
105 }
106}
107
108//normal filter
109//works on the 8 pixels on the edges between macroblocks
110pub(crate) fn macroblock_filter(
111 hev_threshold: u8,
112 interior_limit: u8,
113 edge_limit: u8,
114 pixels: &mut [u8],
115 point: usize,
116 stride: usize,
117) {
118 let mut spixels = [0i32; 8];
119 for i in 0..8 {
120 spixels[i] = u2s(pixels[point + i * stride - 4 * stride]);
121 }
122
123 if should_filter(interior_limit, edge_limit, pixels, point, stride) {
124 if !high_edge_variance(hev_threshold, pixels, point, stride) {
125 let w = c(c(spixels[2] - spixels[5]) + 3 * (spixels[4] - spixels[3]));
126
127 let mut a = c((27 * w + 63) >> 7);
128
129 pixels[point] = s2u(spixels[4] - a);
130 pixels[point - stride] = s2u(spixels[3] + a);
131
132 a = c((18 * w + 63) >> 7);
133
134 pixels[point + stride] = s2u(spixels[5] - a);
135 pixels[point - 2 * stride] = s2u(spixels[2] + a);
136
137 a = c((9 * w + 63) >> 7);
138
139 pixels[point + 2 * stride] = s2u(spixels[6] - a);
140 pixels[point - 3 * stride] = s2u(spixels[1] + a);
141 } else {
142 common_adjust(true, pixels, point, stride);
143 }
144 }
145}
146