1#![forbid(unsafe_code)]
19#![allow(dead_code)]
20#![allow(clippy::doc_markdown)]
21#![allow(clippy::unused_self)]
22#![allow(clippy::cast_possible_truncation)]
23#![allow(clippy::trivially_copy_pass_by_ref)]
24#![allow(clippy::cast_sign_loss)]
25#![allow(clippy::unnecessary_cast)]
26#![allow(clippy::identity_op)]
27#![allow(clippy::if_not_else)]
28#![allow(clippy::missing_errors_doc)]
29
30use super::sequence::SequenceHeader;
31use crate::error::{CodecError, CodecResult};
32use oximedia_io::BitReader;
33
34pub const MAX_LOOP_FILTER_LEVEL: u8 = 63;
40
41pub const MAX_SHARPNESS_LEVEL: u8 = 7;
43
44pub const MAX_MODE_LF_DELTAS: usize = 2;
46
47pub const TOTAL_REFS_PER_FRAME: usize = 8;
49
50pub const LF_LEVEL_BITS: u8 = 6;
52
53pub const LF_DELTA_BITS: u8 = 6;
55
56pub const DEFAULT_REF_DELTAS: [i8; TOTAL_REFS_PER_FRAME] = [1, 0, 0, 0, 0, -1, -1, -1];
58
59pub const DEFAULT_MODE_DELTAS: [i8; MAX_MODE_LF_DELTAS] = [0, 0];
61
62#[derive(Clone, Debug)]
68pub struct LoopFilterParams {
69 pub level: [u8; 4],
71 pub sharpness: u8,
73 pub delta_enabled: bool,
75 pub delta_update: bool,
77 pub ref_deltas: [i8; TOTAL_REFS_PER_FRAME],
79 pub mode_deltas: [i8; MAX_MODE_LF_DELTAS],
81}
82
83impl Default for LoopFilterParams {
84 fn default() -> Self {
85 Self {
86 level: [0; 4],
87 sharpness: 0,
88 delta_enabled: true,
89 delta_update: true,
90 ref_deltas: DEFAULT_REF_DELTAS,
91 mode_deltas: DEFAULT_MODE_DELTAS,
92 }
93 }
94}
95
96impl LoopFilterParams {
97 #[must_use]
99 pub fn new() -> Self {
100 Self::default()
101 }
102
103 #[allow(clippy::cast_possible_truncation)]
109 pub fn parse(
110 reader: &mut BitReader<'_>,
111 seq: &SequenceHeader,
112 frame_is_intra: bool,
113 ) -> CodecResult<Self> {
114 let mut lf = Self::new();
115
116 lf.level[0] = reader.read_bits(LF_LEVEL_BITS).map_err(CodecError::Core)? as u8;
118 lf.level[1] = reader.read_bits(LF_LEVEL_BITS).map_err(CodecError::Core)? as u8;
119
120 if !seq.color_config.mono_chrome && (lf.level[0] > 0 || lf.level[1] > 0) {
122 lf.level[2] = reader.read_bits(LF_LEVEL_BITS).map_err(CodecError::Core)? as u8;
123 lf.level[3] = reader.read_bits(LF_LEVEL_BITS).map_err(CodecError::Core)? as u8;
124 }
125
126 lf.sharpness = reader.read_bits(3).map_err(CodecError::Core)? as u8;
128
129 lf.delta_enabled = reader.read_bit().map_err(CodecError::Core)? != 0;
131
132 if lf.delta_enabled {
133 lf.delta_update = reader.read_bit().map_err(CodecError::Core)? != 0;
134
135 if lf.delta_update {
136 for i in 0..TOTAL_REFS_PER_FRAME {
138 let update = reader.read_bit().map_err(CodecError::Core)? != 0;
139 if update {
140 lf.ref_deltas[i] = Self::read_delta(reader)?;
141 }
142 }
143
144 if !frame_is_intra {
146 for i in 0..MAX_MODE_LF_DELTAS {
147 let update = reader.read_bit().map_err(CodecError::Core)? != 0;
148 if update {
149 lf.mode_deltas[i] = Self::read_delta(reader)?;
150 }
151 }
152 }
153 }
154 }
155
156 Ok(lf)
157 }
158
159 #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
161 fn read_delta(reader: &mut BitReader<'_>) -> CodecResult<i8> {
162 let abs_value = reader.read_bits(LF_DELTA_BITS).map_err(CodecError::Core)? as i8;
163 if abs_value != 0 {
164 let sign = reader.read_bit().map_err(CodecError::Core)?;
165 if sign != 0 {
166 Ok(-abs_value)
167 } else {
168 Ok(abs_value)
169 }
170 } else {
171 Ok(0)
172 }
173 }
174
175 #[must_use]
177 pub const fn level_y_v(&self) -> u8 {
178 self.level[0]
179 }
180
181 #[must_use]
183 pub const fn level_y_h(&self) -> u8 {
184 self.level[1]
185 }
186
187 #[must_use]
189 pub const fn level_u(&self) -> u8 {
190 self.level[2]
191 }
192
193 #[must_use]
195 pub const fn level_v(&self) -> u8 {
196 self.level[3]
197 }
198
199 #[must_use]
201 pub fn is_enabled(&self) -> bool {
202 self.level.iter().any(|&l| l > 0)
203 }
204
205 #[must_use]
212 pub fn get_level(&self, plane: usize, direction: usize) -> u8 {
213 match (plane, direction) {
214 (0, 0) => self.level[0],
215 (0, 1) => self.level[1],
216 (1, _) => self.level[2],
217 (2, _) => self.level[3],
218 _ => 0,
219 }
220 }
221
222 #[must_use]
233 #[allow(clippy::cast_sign_loss, clippy::cast_possible_wrap)]
234 pub fn compute_level(
235 &self,
236 base_level: u8,
237 ref_frame: usize,
238 mode: usize,
239 segment_delta: i16,
240 ) -> u8 {
241 if base_level == 0 || !self.delta_enabled {
242 return base_level;
243 }
244
245 let mut level = i32::from(base_level);
246
247 level += i32::from(segment_delta);
249
250 if ref_frame < TOTAL_REFS_PER_FRAME {
252 level += i32::from(self.ref_deltas[ref_frame]);
253 }
254
255 if ref_frame > 0 && mode < MAX_MODE_LF_DELTAS {
257 level += i32::from(self.mode_deltas[mode]);
258 }
259
260 level.clamp(0, i32::from(MAX_LOOP_FILTER_LEVEL)) as u8
261 }
262
263 #[must_use]
267 pub fn get_limit(&self, level: u8) -> u8 {
268 if self.sharpness > 0 {
269 let block_limit = (9 - self.sharpness).max(1);
270 let shift = (self.sharpness + 3) >> 2;
271 ((level >> shift) as u8).min(block_limit)
272 } else {
273 ((level >> 0) as u8).max(1)
274 }
275 }
276
277 #[must_use]
279 pub fn get_threshold(&self, level: u8) -> u8 {
280 self.get_limit(level) >> 1
282 }
283
284 #[must_use]
286 pub const fn get_hev_threshold(&self, level: u8) -> u8 {
287 if level >= 40 {
288 2
289 } else if level >= 20 {
290 1
291 } else {
292 0
293 }
294 }
295
296 #[must_use]
298 pub const fn has_delta_updates(&self) -> bool {
299 self.delta_enabled && self.delta_update
300 }
301
302 pub fn reset_deltas(&mut self) {
304 self.ref_deltas = DEFAULT_REF_DELTAS;
305 self.mode_deltas = DEFAULT_MODE_DELTAS;
306 }
307
308 pub fn set_level_all(&mut self, level: u8) {
310 self.level = [level; 4];
311 }
312
313 #[must_use]
315 pub fn to_bytes(&self, seq: &SequenceHeader, frame_is_intra: bool) -> Vec<u8> {
316 let mut bits: Vec<u8> = Vec::new();
317
318 bits.push(self.level[0]);
322 bits.push(self.level[1]);
323
324 if !seq.color_config.mono_chrome && (self.level[0] > 0 || self.level[1] > 0) {
325 bits.push(self.level[2]);
326 bits.push(self.level[3]);
327 }
328
329 bits.push(self.sharpness);
330 bits.push(u8::from(self.delta_enabled));
331
332 if self.delta_enabled {
333 bits.push(u8::from(self.delta_update));
334
335 if self.delta_update {
336 for &delta in &self.ref_deltas {
337 #[allow(clippy::cast_sign_loss)]
338 bits.push(delta.unsigned_abs());
339 }
340 if !frame_is_intra {
341 for &delta in &self.mode_deltas {
342 #[allow(clippy::cast_sign_loss)]
343 bits.push(delta.unsigned_abs());
344 }
345 }
346 }
347 }
348
349 bits
350 }
351}
352
353#[derive(Clone, Copy, Debug, Default)]
355pub struct LoopFilterEdge {
356 pub direction: u8,
358 pub level: u8,
360 pub limit: u8,
362 pub threshold: u8,
364 pub hev_threshold: u8,
366}
367
368impl LoopFilterEdge {
369 #[must_use]
371 pub fn new(params: &LoopFilterParams, level: u8, direction: u8) -> Self {
372 Self {
373 direction,
374 level,
375 limit: params.get_limit(level),
376 threshold: params.get_threshold(level),
377 hev_threshold: params.get_hev_threshold(level),
378 }
379 }
380
381 #[must_use]
383 pub const fn should_filter(&self) -> bool {
384 self.level > 0
385 }
386}
387
388#[derive(Clone, Debug, Default)]
392pub struct LoopFilterMask {
393 pub left_y: [u64; 4],
395 pub above_y: [u64; 4],
397 pub left_u: [u16; 4],
399 pub above_u: [u16; 4],
401 pub left_v: [u16; 4],
403 pub above_v: [u16; 4],
405}
406
407impl LoopFilterMask {
408 #[must_use]
410 pub fn new() -> Self {
411 Self::default()
412 }
413
414 pub fn set_left_y(&mut self, tx_size: usize, row: usize, col: usize, sb_size: usize) {
416 if tx_size < 4 && row < sb_size && col < sb_size {
417 let bit = row * sb_size + col;
418 if bit < 64 {
419 self.left_y[tx_size] |= 1u64 << bit;
420 }
421 }
422 }
423
424 pub fn set_above_y(&mut self, tx_size: usize, row: usize, col: usize, sb_size: usize) {
426 if tx_size < 4 && row < sb_size && col < sb_size {
427 let bit = row * sb_size + col;
428 if bit < 64 {
429 self.above_y[tx_size] |= 1u64 << bit;
430 }
431 }
432 }
433
434 pub fn clear(&mut self) {
436 *self = Self::default();
437 }
438
439 #[must_use]
441 pub fn has_edges(&self) -> bool {
442 self.left_y.iter().any(|&m| m != 0)
443 || self.above_y.iter().any(|&m| m != 0)
444 || self.left_u.iter().any(|&m| m != 0)
445 || self.above_u.iter().any(|&m| m != 0)
446 || self.left_v.iter().any(|&m| m != 0)
447 || self.above_v.iter().any(|&m| m != 0)
448 }
449}
450
451#[derive(Clone, Debug, Default)]
453pub struct LoopFilterContext {
454 level_lookup: [[u8; MAX_MODE_LF_DELTAS + 1]; TOTAL_REFS_PER_FRAME],
456}
457
458impl LoopFilterContext {
459 #[must_use]
461 pub fn new() -> Self {
462 Self::default()
463 }
464
465 #[allow(clippy::cast_sign_loss, clippy::cast_possible_wrap)]
467 pub fn init(&mut self, params: &LoopFilterParams, base_level: u8) {
468 if base_level == 0 || !params.delta_enabled {
469 for ref_frame in 0..TOTAL_REFS_PER_FRAME {
470 for mode in 0..=MAX_MODE_LF_DELTAS {
471 self.level_lookup[ref_frame][mode] = base_level;
472 }
473 }
474 return;
475 }
476
477 for ref_frame in 0..TOTAL_REFS_PER_FRAME {
478 for mode in 0..=MAX_MODE_LF_DELTAS {
479 let mut level = i32::from(base_level);
480 level += i32::from(params.ref_deltas[ref_frame]);
481 if ref_frame > 0 && mode < MAX_MODE_LF_DELTAS {
482 level += i32::from(params.mode_deltas[mode]);
483 }
484 self.level_lookup[ref_frame][mode] =
485 level.clamp(0, i32::from(MAX_LOOP_FILTER_LEVEL)) as u8;
486 }
487 }
488 }
489
490 #[must_use]
492 pub fn get_level(&self, ref_frame: usize, mode: usize) -> u8 {
493 if ref_frame < TOTAL_REFS_PER_FRAME && mode <= MAX_MODE_LF_DELTAS {
494 self.level_lookup[ref_frame][mode]
495 } else {
496 0
497 }
498 }
499}
500
501#[cfg(test)]
506mod tests {
507 use super::*;
508
509 #[test]
510 fn test_loop_filter_default() {
511 let lf = LoopFilterParams::default();
512 assert_eq!(lf.level[0], 0);
513 assert_eq!(lf.sharpness, 0);
514 assert!(lf.delta_enabled);
515 assert!(!lf.is_enabled());
516 }
517
518 #[test]
519 fn test_loop_filter_is_enabled() {
520 let mut lf = LoopFilterParams::default();
521 assert!(!lf.is_enabled());
522
523 lf.level[0] = 10;
524 assert!(lf.is_enabled());
525
526 lf.level[0] = 0;
527 lf.level[2] = 5;
528 assert!(lf.is_enabled());
529 }
530
531 #[test]
532 fn test_loop_filter_get_level() {
533 let mut lf = LoopFilterParams::default();
534 lf.level = [10, 20, 30, 40];
535
536 assert_eq!(lf.get_level(0, 0), 10); assert_eq!(lf.get_level(0, 1), 20); assert_eq!(lf.get_level(1, 0), 30); assert_eq!(lf.get_level(1, 1), 30); assert_eq!(lf.get_level(2, 0), 40); assert_eq!(lf.get_level(3, 0), 0); }
543
544 #[test]
545 fn test_loop_filter_accessors() {
546 let mut lf = LoopFilterParams::default();
547 lf.level = [10, 20, 30, 40];
548
549 assert_eq!(lf.level_y_v(), 10);
550 assert_eq!(lf.level_y_h(), 20);
551 assert_eq!(lf.level_u(), 30);
552 assert_eq!(lf.level_v(), 40);
553 }
554
555 #[test]
556 fn test_compute_level() {
557 let mut lf = LoopFilterParams::default();
558 lf.delta_enabled = true;
559 lf.ref_deltas = [1, -1, 2, 0, 0, 0, 0, 0];
560 lf.mode_deltas = [0, -2];
561
562 let level = lf.compute_level(30, 0, 0, 0);
564 assert_eq!(level, 31); let level = lf.compute_level(30, 1, 1, 0);
568 assert_eq!(level, 27); let level = lf.compute_level(30, 0, 0, 10);
572 assert_eq!(level, 41); let level = lf.compute_level(60, 0, 0, 10);
576 assert_eq!(level, 63); }
578
579 #[test]
580 fn test_compute_level_disabled() {
581 let mut lf = LoopFilterParams::default();
582 lf.delta_enabled = false;
583 lf.ref_deltas[0] = 10;
584
585 let level = lf.compute_level(30, 0, 0, 0);
587 assert_eq!(level, 30);
588
589 let level = lf.compute_level(0, 0, 0, 0);
591 assert_eq!(level, 0);
592 }
593
594 #[test]
595 fn test_get_limit() {
596 let mut lf = LoopFilterParams::default();
597
598 lf.sharpness = 0;
600 assert_eq!(lf.get_limit(30), 30);
601
602 lf.sharpness = 4;
604 let limit = lf.get_limit(32);
605 assert!(limit <= 32);
606 assert!(limit <= 9 - 4);
607 }
608
609 #[test]
610 fn test_get_threshold() {
611 let lf = LoopFilterParams::default();
612 let limit = lf.get_limit(20);
613 let threshold = lf.get_threshold(20);
614 assert_eq!(threshold, limit >> 1);
615 }
616
617 #[test]
618 fn test_get_hev_threshold() {
619 let lf = LoopFilterParams::default();
620
621 assert_eq!(lf.get_hev_threshold(10), 0);
622 assert_eq!(lf.get_hev_threshold(25), 1);
623 assert_eq!(lf.get_hev_threshold(45), 2);
624 }
625
626 #[test]
627 fn test_has_delta_updates() {
628 let mut lf = LoopFilterParams::default();
629 lf.delta_enabled = true;
630 lf.delta_update = true;
631 assert!(lf.has_delta_updates());
632
633 lf.delta_enabled = false;
634 assert!(!lf.has_delta_updates());
635
636 lf.delta_enabled = true;
637 lf.delta_update = false;
638 assert!(!lf.has_delta_updates());
639 }
640
641 #[test]
642 fn test_reset_deltas() {
643 let mut lf = LoopFilterParams::default();
644 lf.ref_deltas = [10; TOTAL_REFS_PER_FRAME];
645 lf.mode_deltas = [5; MAX_MODE_LF_DELTAS];
646
647 lf.reset_deltas();
648
649 assert_eq!(lf.ref_deltas, DEFAULT_REF_DELTAS);
650 assert_eq!(lf.mode_deltas, DEFAULT_MODE_DELTAS);
651 }
652
653 #[test]
654 fn test_set_level_all() {
655 let mut lf = LoopFilterParams::default();
656 lf.set_level_all(25);
657
658 assert_eq!(lf.level, [25, 25, 25, 25]);
659 }
660
661 #[test]
662 fn test_loop_filter_edge() {
663 let params = LoopFilterParams::default();
664 let edge = LoopFilterEdge::new(¶ms, 30, 0);
665
666 assert_eq!(edge.level, 30);
667 assert_eq!(edge.direction, 0);
668 assert!(edge.should_filter());
669
670 let edge_zero = LoopFilterEdge::new(¶ms, 0, 1);
671 assert!(!edge_zero.should_filter());
672 }
673
674 #[test]
675 fn test_loop_filter_mask() {
676 let mut mask = LoopFilterMask::new();
677 assert!(!mask.has_edges());
678
679 mask.set_left_y(0, 0, 0, 8);
680 assert!(mask.has_edges());
681 assert_eq!(mask.left_y[0], 1);
682
683 mask.set_above_y(1, 2, 3, 8);
684 assert_eq!(mask.above_y[1], 1u64 << (2 * 8 + 3));
685
686 mask.clear();
687 assert!(!mask.has_edges());
688 }
689
690 #[test]
691 fn test_loop_filter_context() {
692 let mut params = LoopFilterParams::default();
693 params.delta_enabled = true;
694 params.ref_deltas = [2, -1, 0, 0, 0, 0, 0, 0];
695 params.mode_deltas = [0, -3];
696
697 let mut ctx = LoopFilterContext::new();
698 ctx.init(¶ms, 30);
699
700 assert_eq!(ctx.get_level(0, 0), 32); assert_eq!(ctx.get_level(1, 0), 29); assert_eq!(ctx.get_level(1, 1), 26); }
704
705 #[test]
706 fn test_loop_filter_context_disabled() {
707 let mut params = LoopFilterParams::default();
708 params.delta_enabled = false;
709
710 let mut ctx = LoopFilterContext::new();
711 ctx.init(¶ms, 30);
712
713 assert_eq!(ctx.get_level(0, 0), 30);
715 assert_eq!(ctx.get_level(1, 1), 30);
716 }
717
718 #[test]
719 fn test_default_deltas() {
720 assert_eq!(DEFAULT_REF_DELTAS, [1, 0, 0, 0, 0, -1, -1, -1]);
721 assert_eq!(DEFAULT_MODE_DELTAS, [0, 0]);
722 }
723
724 #[test]
725 fn test_constants() {
726 assert_eq!(MAX_LOOP_FILTER_LEVEL, 63);
727 assert_eq!(MAX_SHARPNESS_LEVEL, 7);
728 assert_eq!(LF_LEVEL_BITS, 6);
729 assert_eq!(LF_DELTA_BITS, 6);
730 }
731}