1#![forbid(unsafe_code)]
8#![allow(clippy::unreadable_literal)]
9#![allow(clippy::items_after_statements)]
10#![allow(clippy::unnecessary_wraps)]
11#![allow(clippy::struct_excessive_bools)]
12#![allow(clippy::identity_op)]
13#![allow(clippy::range_plus_one)]
14#![allow(clippy::needless_range_loop)]
15#![allow(clippy::useless_conversion)]
16#![allow(clippy::redundant_closure_for_method_calls)]
17#![allow(clippy::single_match_else)]
18#![allow(dead_code)]
19#![allow(clippy::doc_markdown)]
20#![allow(clippy::unused_self)]
21#![allow(clippy::trivially_copy_pass_by_ref)]
22#![allow(clippy::cast_possible_truncation)]
23#![allow(clippy::cast_sign_loss)]
24#![allow(clippy::cast_possible_wrap)]
25#![allow(clippy::missing_errors_doc)]
26#![allow(clippy::too_many_arguments)]
27#![allow(clippy::similar_names)]
28#![allow(clippy::many_single_char_names)]
29#![allow(clippy::cast_precision_loss)]
30#![allow(clippy::cast_lossless)]
31
32use super::pipeline::FrameContext;
33use super::{FrameBuffer, PlaneBuffer, ReconstructResult};
34
35pub const MAX_BOUNDARY_STRENGTH: u8 = 4;
41
42pub const MIN_DEBLOCK_SIZE: usize = 4;
44
45pub const NORMAL_FILTER_TAPS: usize = 4;
47
48pub const STRONG_FILTER_TAPS: usize = 8;
50
51#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
57pub struct FilterStrength {
58 pub bs: u8,
60 pub alpha: u8,
62 pub beta: u8,
64 pub tc0: u8,
66}
67
68impl FilterStrength {
69 #[must_use]
71 pub const fn new(bs: u8) -> Self {
72 Self {
73 bs,
74 alpha: 0,
75 beta: 0,
76 tc0: 0,
77 }
78 }
79
80 #[must_use]
82 pub fn from_qp(qp: u8, bs: u8) -> Self {
83 let idx = qp.min(51) as usize;
84
85 const ALPHA: [u8; 52] = [
87 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 5, 6, 7, 8, 9, 10, 12, 13, 15,
88 17, 20, 22, 25, 28, 32, 36, 40, 45, 50, 56, 63, 71, 80, 90, 101, 113, 127, 144, 162,
89 182, 203, 226, 255, 255,
90 ];
91
92 const BETA: [u8; 52] = [
94 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 6, 6, 7,
95 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18,
96 ];
97
98 const TC0: [[u8; 3]; 52] = [
100 [0, 0, 0],
101 [0, 0, 0],
102 [0, 0, 0],
103 [0, 0, 0],
104 [0, 0, 0],
105 [0, 0, 0],
106 [0, 0, 0],
107 [0, 0, 0],
108 [0, 0, 0],
109 [0, 0, 0],
110 [0, 0, 0],
111 [0, 0, 0],
112 [0, 0, 0],
113 [0, 0, 0],
114 [0, 0, 0],
115 [0, 0, 0],
116 [0, 0, 0],
117 [0, 0, 1],
118 [0, 0, 1],
119 [0, 0, 1],
120 [0, 0, 1],
121 [0, 1, 1],
122 [0, 1, 1],
123 [1, 1, 1],
124 [1, 1, 1],
125 [1, 1, 1],
126 [1, 1, 1],
127 [1, 1, 2],
128 [1, 1, 2],
129 [1, 1, 2],
130 [1, 1, 2],
131 [1, 2, 3],
132 [1, 2, 3],
133 [2, 2, 3],
134 [2, 2, 4],
135 [2, 3, 4],
136 [2, 3, 4],
137 [3, 3, 5],
138 [3, 4, 6],
139 [3, 4, 6],
140 [4, 5, 7],
141 [4, 5, 8],
142 [4, 6, 9],
143 [5, 7, 10],
144 [6, 8, 11],
145 [6, 8, 13],
146 [7, 10, 14],
147 [8, 11, 16],
148 [9, 12, 18],
149 [10, 13, 20],
150 [11, 15, 23],
151 [13, 17, 25],
152 ];
153
154 let tc0 = if bs > 0 && bs < 4 {
155 TC0[idx][(bs - 1) as usize]
156 } else {
157 0
158 };
159
160 Self {
161 bs,
162 alpha: ALPHA[idx],
163 beta: BETA[idx],
164 tc0,
165 }
166 }
167
168 #[must_use]
170 pub const fn should_filter(&self) -> bool {
171 self.bs > 0
172 }
173
174 #[must_use]
176 pub const fn is_strong(&self) -> bool {
177 self.bs >= 4
178 }
179
180 #[must_use]
182 pub const fn tc(&self) -> i16 {
183 if self.bs >= 4 {
184 0 } else {
186 self.tc0 as i16
187 }
188 }
189}
190
191#[derive(Clone, Debug, Default)]
197pub struct DeblockParams {
198 pub disable_deblock: bool,
200 pub alpha_offset: i8,
202 pub beta_offset: i8,
204 pub qp: u8,
206}
207
208impl DeblockParams {
209 #[must_use]
211 pub fn new(qp: u8) -> Self {
212 Self {
213 qp,
214 ..Default::default()
215 }
216 }
217
218 #[must_use]
220 pub const fn with_alpha_offset(mut self, offset: i8) -> Self {
221 self.alpha_offset = offset;
222 self
223 }
224
225 #[must_use]
227 pub const fn with_beta_offset(mut self, offset: i8) -> Self {
228 self.beta_offset = offset;
229 self
230 }
231
232 #[must_use]
234 pub fn effective_qp_alpha(&self) -> u8 {
235 ((self.qp as i16) + i16::from(self.alpha_offset)).clamp(0, 51) as u8
236 }
237
238 #[must_use]
240 pub fn effective_qp_beta(&self) -> u8 {
241 ((self.qp as i16) + i16::from(self.beta_offset)).clamp(0, 51) as u8
242 }
243
244 #[must_use]
246 pub fn create_strength(&self, bs: u8) -> FilterStrength {
247 FilterStrength::from_qp(self.qp, bs)
248 }
249}
250
251#[derive(Clone, Copy, Debug, Default)]
257pub struct BlockInfo {
258 pub is_intra: bool,
260 pub has_coeffs: bool,
262 pub ref_frame: u8,
264 pub mv_x: i16,
266 pub mv_y: i16,
268}
269
270impl BlockInfo {
271 #[must_use]
273 pub const fn intra() -> Self {
274 Self {
275 is_intra: true,
276 has_coeffs: true,
277 ref_frame: 0,
278 mv_x: 0,
279 mv_y: 0,
280 }
281 }
282
283 #[must_use]
285 pub const fn inter(ref_frame: u8, mv_x: i16, mv_y: i16, has_coeffs: bool) -> Self {
286 Self {
287 is_intra: false,
288 has_coeffs,
289 ref_frame,
290 mv_x,
291 mv_y,
292 }
293 }
294}
295
296#[must_use]
298pub fn calculate_boundary_strength(p: &BlockInfo, q: &BlockInfo) -> u8 {
299 if p.is_intra || q.is_intra {
301 return 4;
302 }
303
304 if p.has_coeffs || q.has_coeffs {
306 return 2;
307 }
308
309 if p.ref_frame != q.ref_frame {
311 return 1;
312 }
313
314 if (p.mv_x - q.mv_x).abs() >= 4 || (p.mv_y - q.mv_y).abs() >= 4 {
316 return 1;
317 }
318
319 0
321}
322
323#[derive(Debug)]
329pub struct DeblockFilter {
330 params: DeblockParams,
332 block_size: usize,
334}
335
336impl Default for DeblockFilter {
337 fn default() -> Self {
338 Self::new()
339 }
340}
341
342impl DeblockFilter {
343 #[must_use]
345 pub fn new() -> Self {
346 Self {
347 params: DeblockParams::default(),
348 block_size: 8,
349 }
350 }
351
352 #[must_use]
354 pub fn with_params(params: DeblockParams) -> Self {
355 Self {
356 params,
357 block_size: 8,
358 }
359 }
360
361 pub fn set_params(&mut self, params: DeblockParams) {
363 self.params = params;
364 }
365
366 #[must_use]
368 pub fn params(&self) -> &DeblockParams {
369 &self.params
370 }
371
372 pub fn apply(
378 &mut self,
379 frame: &mut FrameBuffer,
380 _context: &FrameContext,
381 ) -> ReconstructResult<()> {
382 if self.params.disable_deblock {
383 return Ok(());
384 }
385
386 let bd = frame.bit_depth();
387
388 self.filter_plane(frame.y_plane_mut(), bd, false)?;
390
391 if let Some(u) = frame.u_plane_mut() {
393 self.filter_plane(u, bd, true)?;
394 }
395 if let Some(v) = frame.v_plane_mut() {
396 self.filter_plane(v, bd, true)?;
397 }
398
399 Ok(())
400 }
401
402 fn filter_plane(
404 &self,
405 plane: &mut PlaneBuffer,
406 bd: u8,
407 is_chroma: bool,
408 ) -> ReconstructResult<()> {
409 let width = plane.width() as usize;
410 let height = plane.height() as usize;
411 let block_size = if is_chroma { 4 } else { self.block_size };
412
413 let strength = self.params.create_strength(2);
416
417 for by in 0..(height / block_size) {
419 for bx in 1..(width / block_size) {
420 let x = (bx * block_size) as u32;
421 let y = (by * block_size) as u32;
422 self.filter_edge_vertical(plane, x, y, block_size, &strength, bd);
423 }
424 }
425
426 for by in 1..(height / block_size) {
428 for bx in 0..(width / block_size) {
429 let x = (bx * block_size) as u32;
430 let y = (by * block_size) as u32;
431 self.filter_edge_horizontal(plane, x, y, block_size, &strength, bd);
432 }
433 }
434
435 Ok(())
436 }
437
438 fn filter_edge_vertical(
440 &self,
441 plane: &mut PlaneBuffer,
442 x: u32,
443 y: u32,
444 length: usize,
445 strength: &FilterStrength,
446 bd: u8,
447 ) {
448 if !strength.should_filter() {
449 return;
450 }
451
452 for i in 0..length {
453 let py = y + i as u32;
454
455 let p2 = plane.get(x.saturating_sub(3), py);
457 let p1 = plane.get(x.saturating_sub(2), py);
458 let p0 = plane.get(x.saturating_sub(1), py);
459 let q0 = plane.get(x, py);
460 let q1 = plane.get(x + 1, py);
461 let q2 = plane.get(x + 2, py);
462
463 if !self.should_filter_edge(p0, p1, q0, q1, strength) {
465 continue;
466 }
467
468 let (new_p0, new_p1, new_q0, new_q1) = if strength.is_strong() {
470 self.strong_filter(p0, p1, p2, q0, q1, q2, bd)
471 } else {
472 self.normal_filter(p0, p1, q0, q1, strength, bd)
473 };
474
475 plane.set(x.saturating_sub(1), py, new_p0);
477 plane.set(x.saturating_sub(2), py, new_p1);
478 plane.set(x, py, new_q0);
479 plane.set(x + 1, py, new_q1);
480 }
481 }
482
483 fn filter_edge_horizontal(
485 &self,
486 plane: &mut PlaneBuffer,
487 x: u32,
488 y: u32,
489 length: usize,
490 strength: &FilterStrength,
491 bd: u8,
492 ) {
493 if !strength.should_filter() {
494 return;
495 }
496
497 for i in 0..length {
498 let px = x + i as u32;
499
500 let p2 = plane.get(px, y.saturating_sub(3));
502 let p1 = plane.get(px, y.saturating_sub(2));
503 let p0 = plane.get(px, y.saturating_sub(1));
504 let q0 = plane.get(px, y);
505 let q1 = plane.get(px, y + 1);
506 let q2 = plane.get(px, y + 2);
507
508 if !self.should_filter_edge(p0, p1, q0, q1, strength) {
510 continue;
511 }
512
513 let (new_p0, new_p1, new_q0, new_q1) = if strength.is_strong() {
515 self.strong_filter(p0, p1, p2, q0, q1, q2, bd)
516 } else {
517 self.normal_filter(p0, p1, q0, q1, strength, bd)
518 };
519
520 plane.set(px, y.saturating_sub(1), new_p0);
522 plane.set(px, y.saturating_sub(2), new_p1);
523 plane.set(px, y, new_q0);
524 plane.set(px, y + 1, new_q1);
525 }
526 }
527
528 fn should_filter_edge(
530 &self,
531 p0: i16,
532 p1: i16,
533 q0: i16,
534 q1: i16,
535 strength: &FilterStrength,
536 ) -> bool {
537 let alpha = i16::from(strength.alpha);
538 let beta = i16::from(strength.beta);
539
540 (p0 - q0).abs() < alpha && (p1 - p0).abs() < beta && (q1 - q0).abs() < beta
542 }
543
544 fn normal_filter(
546 &self,
547 p0: i16,
548 p1: i16,
549 q0: i16,
550 q1: i16,
551 strength: &FilterStrength,
552 bd: u8,
553 ) -> (i16, i16, i16, i16) {
554 let max_val = (1i16 << bd) - 1;
555 let tc = strength.tc();
556
557 let delta0 = ((((q0 - p0) << 2) + (p1 - q1) + 4) >> 3).clamp(-tc, tc);
559
560 let new_p0 = (p0 + delta0).clamp(0, max_val);
561 let new_q0 = (q0 - delta0).clamp(0, max_val);
562
563 let (new_p1, new_q1) = if tc > 0 {
565 let delta_p1 = ((p2_avg(p0, p1) - p1 + delta0) >> 1).clamp(-tc, tc);
566 let delta_q1 = ((p2_avg(q0, q1) - q1 - delta0) >> 1).clamp(-tc, tc);
567 (
568 (p1 + delta_p1).clamp(0, max_val),
569 (q1 + delta_q1).clamp(0, max_val),
570 )
571 } else {
572 (p1, q1)
573 };
574
575 (new_p0, new_p1, new_q0, new_q1)
576 }
577
578 fn strong_filter(
580 &self,
581 p0: i16,
582 p1: i16,
583 p2: i16,
584 q0: i16,
585 q1: i16,
586 q2: i16,
587 bd: u8,
588 ) -> (i16, i16, i16, i16) {
589 let max_val = (1i16 << bd) - 1;
590
591 let new_p0 = ((p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3).clamp(0, max_val);
593 let new_p1 = ((p2 + p1 + p0 + q0 + 2) >> 2).clamp(0, max_val);
594 let new_q0 = ((p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3).clamp(0, max_val);
595 let new_q1 = ((p0 + q0 + q1 + q2 + 2) >> 2).clamp(0, max_val);
596
597 (new_p0, new_p1, new_q0, new_q1)
598 }
599}
600
601#[inline]
603fn p2_avg(p0: i16, p1: i16) -> i16 {
604 (p0 + p1 + 1) >> 1
605}
606
607#[cfg(test)]
612mod tests {
613 use super::*;
614 use crate::reconstruct::ChromaSubsampling;
615
616 #[test]
617 fn test_filter_strength_new() {
618 let strength = FilterStrength::new(2);
619 assert_eq!(strength.bs, 2);
620 assert!(strength.should_filter());
621 assert!(!strength.is_strong());
622
623 let strength_zero = FilterStrength::new(0);
624 assert!(!strength_zero.should_filter());
625 }
626
627 #[test]
628 fn test_filter_strength_from_qp() {
629 let strength = FilterStrength::from_qp(26, 2);
630 assert_eq!(strength.bs, 2);
631 assert!(strength.alpha > 0);
632 assert!(strength.beta > 0);
633
634 let strength_strong = FilterStrength::from_qp(26, 4);
635 assert!(strength_strong.is_strong());
636 }
637
638 #[test]
639 fn test_deblock_params() {
640 let params = DeblockParams::new(26)
641 .with_alpha_offset(2)
642 .with_beta_offset(-2);
643
644 assert_eq!(params.qp, 26);
645 assert_eq!(params.effective_qp_alpha(), 28);
646 assert_eq!(params.effective_qp_beta(), 24);
647 }
648
649 #[test]
650 fn test_block_info() {
651 let intra = BlockInfo::intra();
652 assert!(intra.is_intra);
653
654 let inter = BlockInfo::inter(1, 10, 20, true);
655 assert!(!inter.is_intra);
656 assert_eq!(inter.ref_frame, 1);
657 assert_eq!(inter.mv_x, 10);
658 }
659
660 #[test]
661 fn test_boundary_strength_calculation() {
662 let intra = BlockInfo::intra();
663 let inter = BlockInfo::inter(0, 0, 0, false);
664
665 assert_eq!(calculate_boundary_strength(&intra, &inter), 4);
667
668 let inter2 = BlockInfo::inter(1, 0, 0, false);
670 assert_eq!(calculate_boundary_strength(&inter, &inter2), 1);
671
672 let inter3 = BlockInfo::inter(0, 10, 0, false);
674 assert_eq!(calculate_boundary_strength(&inter, &inter3), 1);
675
676 assert_eq!(calculate_boundary_strength(&inter, &inter), 0);
678 }
679
680 #[test]
681 fn test_boundary_strength_with_coeffs() {
682 let inter_coeffs = BlockInfo::inter(0, 0, 0, true);
683 let inter_no_coeffs = BlockInfo::inter(0, 0, 0, false);
684
685 assert_eq!(
686 calculate_boundary_strength(&inter_coeffs, &inter_no_coeffs),
687 2
688 );
689 }
690
691 #[test]
692 fn test_deblock_filter_creation() {
693 let filter = DeblockFilter::new();
694 assert!(!filter.params().disable_deblock);
695 }
696
697 #[test]
698 fn test_deblock_filter_with_params() {
699 let params = DeblockParams::new(26);
700 let filter = DeblockFilter::with_params(params);
701 assert_eq!(filter.params().qp, 26);
702 }
703
704 #[test]
705 fn test_deblock_filter_apply_disabled() {
706 let mut frame = FrameBuffer::new(64, 64, 8, ChromaSubsampling::Cs420);
707 let context = FrameContext::new(64, 64);
708
709 let mut params = DeblockParams::new(26);
710 params.disable_deblock = true;
711 let mut filter = DeblockFilter::with_params(params);
712
713 let result = filter.apply(&mut frame, &context);
714 assert!(result.is_ok());
715 }
716
717 #[test]
718 fn test_deblock_filter_apply() {
719 let mut frame = FrameBuffer::new(64, 64, 8, ChromaSubsampling::Cs420);
720
721 for y in 0..64 {
723 for x in 0..8 {
724 frame.y_plane_mut().set(x, y as u32, 100);
725 }
726 for x in 8..64 {
727 frame.y_plane_mut().set(x as u32, y as u32, 150);
728 }
729 }
730
731 let context = FrameContext::new(64, 64);
732 let params = DeblockParams::new(26);
733 let mut filter = DeblockFilter::with_params(params);
734
735 let result = filter.apply(&mut frame, &context);
736 assert!(result.is_ok());
737 }
738
739 #[test]
740 fn test_strong_filter() {
741 let filter = DeblockFilter::new();
742
743 let (new_p0, _new_p1, new_q0, _new_q1) =
744 filter.strong_filter(100, 95, 90, 150, 155, 160, 8);
745
746 assert!((new_p0 - new_q0).abs() < (100 - 150i16).abs());
748 assert!(new_p0 >= 0 && new_p0 <= 255);
749 assert!(new_q0 >= 0 && new_q0 <= 255);
750 }
751
752 #[test]
753 fn test_normal_filter() {
754 let filter = DeblockFilter::new();
755 let strength = FilterStrength::from_qp(26, 2);
756
757 let (new_p0, _new_p1, new_q0, _new_q1) =
758 filter.normal_filter(100, 95, 150, 155, &strength, 8);
759
760 assert!((new_p0 - new_q0).abs() <= (100 - 150i16).abs());
762 }
763
764 #[test]
765 fn test_constants() {
766 assert_eq!(MAX_BOUNDARY_STRENGTH, 4);
767 assert_eq!(MIN_DEBLOCK_SIZE, 4);
768 assert_eq!(NORMAL_FILTER_TAPS, 4);
769 assert_eq!(STRONG_FILTER_TAPS, 8);
770 }
771}