1#[inline]
4fn c(val: i32) -> i32 {
5 val.clamp(-128, 127)
6}
7
8#[inline]
10fn u2s(val: u8) -> i32 {
11 i32::from(val) - 128
12}
13
14#[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 u8::abs_diff(val1, val2)
23}
24
25fn common_adjust_vertical(
29 use_outer_taps: bool,
30 pixels: &mut [u8],
31 point: usize,
32 stride: usize,
33) -> i32 {
34 let p1 = u2s(pixels[point - 2 * stride]);
35 let p0 = u2s(pixels[point - stride]);
36 let q0 = u2s(pixels[point]);
37 let q1 = u2s(pixels[point + stride]);
38
39 let outer = if use_outer_taps { c(p1 - q1) } else { 0 };
41
42 let a = c(outer + 3 * (q0 - p0));
43
44 let b = (c(a + 3)) >> 3;
45
46 let a = (c(a + 4)) >> 3;
47
48 pixels[point] = s2u(q0 - a);
49 pixels[point - stride] = s2u(p0 + b);
50
51 a
52}
53
54fn common_adjust_horizontal(use_outer_taps: bool, pixels: &mut [u8]) -> i32 {
58 let p1 = u2s(pixels[2]);
59 let p0 = u2s(pixels[3]);
60 let q0 = u2s(pixels[4]);
61 let q1 = u2s(pixels[5]);
62
63 let outer = if use_outer_taps { c(p1 - q1) } else { 0 };
65
66 let a = c(outer + 3 * (q0 - p0));
67
68 let b = (c(a + 3)) >> 3;
69
70 let a = (c(a + 4)) >> 3;
71
72 pixels[4] = s2u(q0 - a);
73 pixels[3] = s2u(p0 + b);
74
75 a
76}
77
78#[inline]
79fn simple_threshold_vertical(
80 filter_limit: i32,
81 pixels: &[u8],
82 point: usize,
83 stride: usize,
84) -> bool {
85 i32::from(diff(pixels[point - stride], pixels[point])) * 2
86 + i32::from(diff(pixels[point - 2 * stride], pixels[point + stride])) / 2
87 <= filter_limit
88}
89
90#[inline]
91fn simple_threshold_horizontal(filter_limit: i32, pixels: &[u8]) -> bool {
92 assert!(pixels.len() >= 6); i32::from(diff(pixels[3], pixels[4])) * 2 + i32::from(diff(pixels[2], pixels[5])) / 2
94 <= filter_limit
95}
96
97fn should_filter_vertical(
98 interior_limit: u8,
99 edge_limit: u8,
100 pixels: &[u8],
101 point: usize,
102 stride: usize,
103) -> bool {
104 simple_threshold_vertical(i32::from(edge_limit), pixels, point, stride)
105 && diff(pixels[point - 4 * stride], pixels[point - 3 * stride]) <= interior_limit
109 && diff(pixels[point - 3 * stride], pixels[point - 2 * stride]) <= interior_limit
110 && diff(pixels[point - 2 * stride], pixels[point - stride]) <= interior_limit
111 && diff(pixels[point + 3 * stride], pixels[point + 2 * stride]) <= interior_limit
112 && diff(pixels[point + 2 * stride], pixels[point + stride]) <= interior_limit
113 && diff(pixels[point + stride], pixels[point]) <= interior_limit
114}
115
116fn should_filter_horizontal(interior_limit: u8, edge_limit: u8, pixels: &[u8]) -> bool {
117 assert!(pixels.len() >= 8); simple_threshold_horizontal(i32::from(edge_limit), pixels)
119 && diff(pixels[0], pixels[1]) <= interior_limit
123 && diff(pixels[1], pixels[2]) <= interior_limit
124 && diff(pixels[2], pixels[3]) <= interior_limit
125 && diff(pixels[7], pixels[6]) <= interior_limit
126 && diff(pixels[6], pixels[5]) <= interior_limit
127 && diff(pixels[5], pixels[4]) <= interior_limit
128}
129
130#[inline]
131fn high_edge_variance_vertical(threshold: u8, pixels: &[u8], point: usize, stride: usize) -> bool {
132 diff(pixels[point - 2 * stride], pixels[point - stride]) > threshold
133 || diff(pixels[point + stride], pixels[point]) > threshold
134}
135
136#[inline]
137fn high_edge_variance_horizontal(threshold: u8, pixels: &[u8]) -> bool {
138 diff(pixels[2], pixels[3]) > threshold || diff(pixels[5], pixels[4]) > threshold
139}
140
141pub(crate) fn simple_segment_vertical(
145 edge_limit: u8,
146 pixels: &mut [u8],
147 point: usize,
148 stride: usize,
149) {
150 if simple_threshold_vertical(i32::from(edge_limit), pixels, point, stride) {
151 common_adjust_vertical(true, pixels, point, stride);
152 }
153}
154
155pub(crate) fn simple_segment_horizontal(edge_limit: u8, pixels: &mut [u8]) {
159 if simple_threshold_horizontal(i32::from(edge_limit), pixels) {
160 common_adjust_horizontal(true, pixels);
161 }
162}
163
164pub(crate) fn subblock_filter_vertical(
168 hev_threshold: u8,
169 interior_limit: u8,
170 edge_limit: u8,
171 pixels: &mut [u8],
172 point: usize,
173 stride: usize,
174) {
175 if should_filter_vertical(interior_limit, edge_limit, pixels, point, stride) {
176 let hv = high_edge_variance_vertical(hev_threshold, pixels, point, stride);
177
178 let a = (common_adjust_vertical(hv, pixels, point, stride) + 1) >> 1;
179
180 if !hv {
181 pixels[point + stride] = s2u(u2s(pixels[point + stride]) - a);
182 pixels[point - 2 * stride] = s2u(u2s(pixels[point - 2 * stride]) + a);
183 }
184 }
185}
186
187pub(crate) fn subblock_filter_horizontal(
191 hev_threshold: u8,
192 interior_limit: u8,
193 edge_limit: u8,
194 pixels: &mut [u8],
195) {
196 if should_filter_horizontal(interior_limit, edge_limit, pixels) {
197 let hv = high_edge_variance_horizontal(hev_threshold, pixels);
198
199 let a = (common_adjust_horizontal(hv, pixels) + 1) >> 1;
200
201 if !hv {
202 pixels[5] = s2u(u2s(pixels[5]) - a);
203 pixels[2] = s2u(u2s(pixels[2]) + a);
204 }
205 }
206}
207
208pub(crate) fn macroblock_filter_vertical(
213 hev_threshold: u8,
214 interior_limit: u8,
215 edge_limit: u8,
216 pixels: &mut [u8],
217 point: usize,
218 stride: usize,
219) {
220 if should_filter_vertical(interior_limit, edge_limit, pixels, point, stride) {
221 if !high_edge_variance_vertical(hev_threshold, pixels, point, stride) {
222 let p2 = u2s(pixels[point - 3 * stride]);
224 let p1 = u2s(pixels[point - 2 * stride]);
225 let p0 = u2s(pixels[point - stride]);
226 let q0 = u2s(pixels[point]);
228 let q1 = u2s(pixels[point + stride]);
229 let q2 = u2s(pixels[point + 2 * stride]);
230
231 let w = c(c(p1 - q1) + 3 * (q0 - p0));
232
233 let a = c((27 * w + 63) >> 7);
234
235 pixels[point] = s2u(q0 - a);
236 pixels[point - stride] = s2u(p0 + a);
237
238 let a = c((18 * w + 63) >> 7);
239
240 pixels[point + stride] = s2u(q1 - a);
241 pixels[point - 2 * stride] = s2u(p1 + a);
242
243 let a = c((9 * w + 63) >> 7);
244
245 pixels[point + 2 * stride] = s2u(q2 - a);
246 pixels[point - 3 * stride] = s2u(p2 + a);
247 } else {
248 common_adjust_vertical(true, pixels, point, stride);
249 }
250 }
251}
252
253pub(crate) fn macroblock_filter_horizontal(
258 hev_threshold: u8,
259 interior_limit: u8,
260 edge_limit: u8,
261 pixels: &mut [u8],
262) {
263 assert!(pixels.len() >= 8);
264 if should_filter_horizontal(interior_limit, edge_limit, pixels) {
265 if !high_edge_variance_horizontal(hev_threshold, pixels) {
266 let p2 = u2s(pixels[1]);
268 let p1 = u2s(pixels[2]);
269 let p0 = u2s(pixels[3]);
270 let q0 = u2s(pixels[4]);
272 let q1 = u2s(pixels[5]);
273 let q2 = u2s(pixels[6]);
274
275 let w = c(c(p1 - q1) + 3 * (q0 - p0));
276
277 let a = c((27 * w + 63) >> 7);
278
279 pixels[4] = s2u(q0 - a);
280 pixels[3] = s2u(p0 + a);
281
282 let a = c((18 * w + 63) >> 7);
283
284 pixels[5] = s2u(q1 - a);
285 pixels[2] = s2u(p1 + a);
286
287 let a = c((9 * w + 63) >> 7);
288
289 pixels[6] = s2u(q2 - a);
290 pixels[1] = s2u(p2 + a);
291 } else {
292 common_adjust_horizontal(true, pixels);
293 }
294 }
295}
296
297#[cfg(all(test, feature = "_benchmarks"))]
298mod benches {
299 use super::*;
300 use test::{black_box, Bencher};
301
302 #[rustfmt::skip]
303 const TEST_DATA: [u8; 8 * 8] = [
304 177, 192, 179, 181, 185, 174, 186, 193,
305 185, 180, 175, 179, 175, 190, 189, 190,
306 185, 181, 177, 190, 190, 174, 176, 188,
307 192, 179, 186, 175, 190, 184, 190, 175,
308 175, 183, 183, 190, 187, 186, 176, 181,
309 183, 177, 182, 185, 183, 179, 178, 181,
310 191, 183, 188, 181, 180, 193, 185, 180,
311 177, 182, 177, 178, 179, 178, 191, 178,
312 ];
313
314 #[bench]
315 fn measure_horizontal_macroblock_filter(b: &mut Bencher) {
316 let hev_threshold = 5;
317 let interior_limit = 15;
318 let edge_limit = 15;
319
320 let mut data = TEST_DATA.clone();
321 let stride = 8;
322
323 b.iter(|| {
324 for y in 0..8 {
325 black_box(macroblock_filter_horizontal(
326 hev_threshold,
327 interior_limit,
328 edge_limit,
329 &mut data[y * stride..][..8],
330 ));
331 }
332 });
333 }
334
335 #[bench]
336 fn measure_vertical_macroblock_filter(b: &mut Bencher) {
337 let hev_threshold = 5;
338 let interior_limit = 15;
339 let edge_limit = 15;
340
341 let mut data = TEST_DATA.clone();
342 let stride = 8;
343
344 b.iter(|| {
345 for x in 0..8 {
346 black_box(macroblock_filter_vertical(
347 hev_threshold,
348 interior_limit,
349 edge_limit,
350 &mut data,
351 4 * stride + x,
352 stride,
353 ));
354 }
355 });
356 }
357
358 #[bench]
359 fn measure_horizontal_subblock_filter(b: &mut Bencher) {
360 let hev_threshold = 5;
361 let interior_limit = 15;
362 let edge_limit = 15;
363
364 let mut data = TEST_DATA.clone();
365 let stride = 8;
366
367 b.iter(|| {
368 for y in 0usize..8 {
369 black_box(subblock_filter_horizontal(
370 hev_threshold,
371 interior_limit,
372 edge_limit,
373 &mut data[y * stride..][..8],
374 ))
375 }
376 });
377 }
378
379 #[bench]
380 fn measure_vertical_subblock_filter(b: &mut Bencher) {
381 let hev_threshold = 5;
382 let interior_limit = 15;
383 let edge_limit = 15;
384
385 let mut data = TEST_DATA.clone();
386 let stride = 8;
387
388 b.iter(|| {
389 for x in 0..8 {
390 black_box(subblock_filter_vertical(
391 hev_threshold,
392 interior_limit,
393 edge_limit,
394 &mut data,
395 4 * stride + x,
396 stride,
397 ))
398 }
399 });
400 }
401
402 #[bench]
403 fn measure_simple_segment_horizontal_filter(b: &mut Bencher) {
404 let edge_limit = 15;
405
406 let mut data = TEST_DATA.clone();
407 let stride = 8;
408
409 b.iter(|| {
410 for y in 0usize..8 {
411 black_box(simple_segment_horizontal(
412 edge_limit,
413 &mut data[y * stride..][..8],
414 ))
415 }
416 });
417 }
418
419 #[bench]
420 fn measure_simple_segment_vertical_filter(b: &mut Bencher) {
421 let edge_limit = 15;
422
423 let mut data = TEST_DATA.clone();
424 let stride = 8;
425
426 b.iter(|| {
427 for x in 0usize..16 {
428 black_box(simple_segment_vertical(
429 edge_limit,
430 &mut data,
431 4 * stride + x,
432 stride,
433 ))
434 }
435 });
436 }
437}