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