| 1 | use nihav_core::frame::NASimpleVideoFrame; |
| 2 | use super::types::SliceState; |
| 3 | use super::dsp::*; |
| 4 | |
| 5 | const ALPHA: [i16; 52] = [ |
| 6 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 7 | 4, 4, 5, 6, 7, 8, 9, 10, 12, 13, 15, 17, 20, 22, 25, 28, |
| 8 | 32, 36, 40, 45, 50, 56, 63, 71, 80, 90, 100, 113, 127, 144, 162, 182, |
| 9 | 203, 226, 255, 255 |
| 10 | ]; |
| 11 | const BETA: [i16; 52] = [ |
| 12 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 13 | 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 6, 6, 7, 7, 8, 8, |
| 14 | 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, |
| 15 | 17, 17, 18, 18 |
| 16 | ]; |
| 17 | |
| 18 | const TC0: [[u8; 3]; 52] = [ |
| 19 | [ 0, 0, 0], [ 0, 0, 0], [ 0, 0, 0], [ 0, 0, 0], |
| 20 | [ 0, 0, 0], [ 0, 0, 0], [ 0, 0, 0], [ 0, 0, 0], |
| 21 | [ 0, 0, 0], [ 0, 0, 0], [ 0, 0, 0], [ 0, 0, 0], |
| 22 | [ 0, 0, 0], [ 0, 0, 0], [ 0, 0, 0], [ 0, 0, 0], |
| 23 | [ 0, 0, 0], [ 0, 0, 1], [ 0, 0, 1], [ 0, 0, 1], |
| 24 | [ 0, 0, 1], [ 0, 1, 1], [ 0, 1, 1], [ 1, 1, 1], |
| 25 | [ 1, 1, 1], [ 1, 1, 1], [ 1, 1, 1], [ 1, 1, 2], |
| 26 | [ 1, 1, 2], [ 1, 1, 2], [ 1, 1, 2], [ 1, 2, 3], |
| 27 | [ 1, 2, 3], [ 2, 2, 3], [ 2, 2, 4], [ 2, 3, 4], |
| 28 | [ 2, 3, 4], [ 3, 3, 5], [ 3, 4, 6], [ 3, 4, 6], |
| 29 | [ 4, 5, 7], [ 4, 5, 8], [ 4, 6, 9], [ 5, 7, 10], |
| 30 | [ 6, 8, 11], [ 6, 8, 13], [ 7, 10, 14], [ 8, 11, 16], |
| 31 | [ 9, 12, 18], [10, 13, 20], [11, 15, 23], [13, 17, 25] |
| 32 | ]; |
| 33 | |
| 34 | fn get_lf_idx(qp0: u8, qp1: u8, off: i8) -> usize { |
| 35 | (i16::from((qp0 + qp1 + 1) >> 1) + i16::from(off)).max(0).min(51) as usize |
| 36 | } |
| 37 | |
| 38 | macro_rules! filter_edge_func { |
| 39 | ($funcname: ident, $edgefilter: ident, $normfilter: ident) => { |
| 40 | fn $funcname(dst: &mut [u8], off: usize, stride: usize, dmode: u8, quants: [u8; 2], alpha_off: i8, beta_off: i8) { |
| 41 | let q = quants[0]; |
| 42 | let qleft = quants[1]; |
| 43 | if dmode != 0 { |
| 44 | let index_a = get_lf_idx(q, qleft, alpha_off); |
| 45 | let alpha = ALPHA[index_a]; |
| 46 | let beta = BETA[get_lf_idx(q, qleft, beta_off)]; |
| 47 | if dmode == 4 { |
| 48 | $edgefilter(dst, off, stride, alpha, beta); |
| 49 | } else { |
| 50 | let tc0 = i16::from(TC0[index_a][(dmode - 1) as usize]); |
| 51 | $normfilter(dst, off, stride, alpha, beta, tc0); |
| 52 | } |
| 53 | } |
| 54 | } |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | filter_edge_func!(filter_edge_y_v, loop_filter_lumaedge_v, loop_filter_lumanormal_v); |
| 59 | filter_edge_func!(filter_edge_y_h, loop_filter_lumaedge_h, loop_filter_lumanormal_h); |
| 60 | filter_edge_func!(filter_edge_c_v, loop_filter_chromaedge_v, loop_filter_chromanormal_v); |
| 61 | filter_edge_func!(filter_edge_c_h, loop_filter_chromaedge_h, loop_filter_chromanormal_h); |
| 62 | |
| 63 | pub fn loop_filter_mb(frm: &mut NASimpleVideoFrame<u8>, sstate: &SliceState, alpha_off: i8, beta_off: i8) { |
| 64 | let yoff = frm.offset[0] + sstate.mb_x * 16 + sstate.mb_y * 16 * frm.stride[0]; |
| 65 | let uoff = frm.offset[1] + sstate.mb_x * 8 + sstate.mb_y * 8 * frm.stride[1]; |
| 66 | let voff = frm.offset[2] + sstate.mb_x * 8 + sstate.mb_y * 8 * frm.stride[2]; |
| 67 | let mb_idx = sstate.mb.xpos + sstate.mb_x; |
| 68 | |
| 69 | let lqy = sstate.mb.data[mb_idx - 1].qp_y; |
| 70 | let lqu = sstate.mb.data[mb_idx - 1].qp_u; |
| 71 | let lqv = sstate.mb.data[mb_idx - 1].qp_v; |
| 72 | let qy = sstate.mb.data[mb_idx].qp_y; |
| 73 | let qu = sstate.mb.data[mb_idx].qp_u; |
| 74 | let qv = sstate.mb.data[mb_idx].qp_v; |
| 75 | |
| 76 | for (y, dmodes) in sstate.deblock.chunks(4).enumerate() { |
| 77 | filter_edge_y_v(frm.data, yoff + y * 4 * frm.stride[0], frm.stride[0], dmodes[0] & 0xF, [qy, lqy], alpha_off, beta_off); |
| 78 | for x in 1..4 { |
| 79 | filter_edge_y_v(frm.data, yoff + x * 4 + y * 4 * frm.stride[0], frm.stride[0], dmodes[x] & 0xF, [qy, qy], alpha_off, beta_off); |
| 80 | } |
| 81 | filter_edge_c_v(frm.data, uoff + y * 2 * frm.stride[1], frm.stride[1], dmodes[0] & 0xF, [qu, lqu], alpha_off, beta_off); |
| 82 | filter_edge_c_v(frm.data, uoff + y * 2 * frm.stride[1] + 4, frm.stride[1], dmodes[2] & 0xF, [qu, qu], alpha_off, beta_off); |
| 83 | filter_edge_c_v(frm.data, voff + y * 2 * frm.stride[2], frm.stride[2], dmodes[0] & 0xF, [qv, lqv], alpha_off, beta_off); |
| 84 | filter_edge_c_v(frm.data, voff + y * 2 * frm.stride[2] + 4, frm.stride[2], dmodes[2] & 0xF, [qv, qv], alpha_off, beta_off); |
| 85 | } |
| 86 | |
| 87 | let tqy = sstate.mb.data[mb_idx - sstate.mb.stride].qp_y; |
| 88 | let tqu = sstate.mb.data[mb_idx - sstate.mb.stride].qp_u; |
| 89 | let tqv = sstate.mb.data[mb_idx - sstate.mb.stride].qp_v; |
| 90 | |
| 91 | let dmodes = &sstate.deblock; |
| 92 | for x in 0..4 { |
| 93 | filter_edge_y_h(frm.data, yoff + x * 4, frm.stride[0], dmodes[x] >> 4, [qy, tqy], alpha_off, beta_off); |
| 94 | } |
| 95 | for x in 0..4 { |
| 96 | filter_edge_c_h(frm.data, uoff + x * 2, frm.stride[1], dmodes[x] >> 4, [qu, tqu], alpha_off, beta_off); |
| 97 | filter_edge_c_h(frm.data, voff + x * 2, frm.stride[2], dmodes[x] >> 4, [qv, tqv], alpha_off, beta_off); |
| 98 | } |
| 99 | |
| 100 | for (y, dmodes) in sstate.deblock.chunks(4).enumerate().skip(1) { |
| 101 | for x in 0..4 { |
| 102 | filter_edge_y_h(frm.data, yoff + x * 4 + y * 4 * frm.stride[0], frm.stride[0], dmodes[x] >> 4, [qy, qy], alpha_off, beta_off); |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | let dmodes = &sstate.deblock[4 * 2..]; |
| 107 | for x in 0..4 { |
| 108 | filter_edge_c_h(frm.data, uoff + x * 2 + frm.stride[1] * 4, frm.stride[1], dmodes[x] >> 4, [qu, qu], alpha_off, beta_off); |
| 109 | filter_edge_c_h(frm.data, voff + x * 2 + frm.stride[2] * 4, frm.stride[2], dmodes[x] >> 4, [qv, qv], alpha_off, beta_off); |
| 110 | } |
| 111 | } |