use super::*;
use crate::Speed;
#[allow(clippy::too_many_arguments)]
fn dispatch_intra_pred(
m: usize,
adelta: i32,
ab: &[i32],
lf: &[i32],
corner: i32,
have_top: bool,
have_left: bool,
max_w: i32,
max_h: i32,
) -> Vec<f32> {
use directional::Dir::*;
let dir = match m {
1 => return intrapred::smooth(32, ab, lf),
2 => return intrapred::smooth_v(32, ab, lf),
3 => return intrapred::smooth_h(32, ab, lf),
4 => return intrapred::paeth(32, ab, lf, corner),
5 => V,
6 => H,
7 => D45,
8 => D135,
9 => D113,
10 => D157,
11 => D203,
_ => D67,
};
directional::directional(
dir, adelta, 32, ab, lf, corner, true, have_top, have_left, false, max_w, max_h,
)
}
#[allow(clippy::too_many_arguments)]
pub(super) fn predict_luma(
recy: &[f32],
pw: usize,
width: usize,
height: usize,
i: usize,
y0: usize,
x0: usize,
m: usize,
adelta: i32,
neutral: f32,
) -> Vec<f32> {
if m == 0 {
return vec![dc_pred(recy, pw, y0, x0, 32, neutral); 1024];
}
let have_above = y0 > 0;
let have_left = x0 > 0;
let mi_col_end = (((width + 63) & !63) >> 2) as i64;
let mi_row_end = (((height + 63) & !63) >> 2) as i64;
let sb_y0 = (y0 / 64) * 64;
let sb_x0 = (x0 / 64) * 64;
let mi_row = (sb_y0 >> 2) as i64;
let mi_col = (sb_x0 >> 2) as i64;
let (row_off, col_off) = ((y0 - sb_y0) as i64 / 4, (x0 - sb_x0) as i64 / 4);
let (lx, ly) = ((x0 - sb_x0) as i64, (y0 - sb_y0) as i64); let xr = ((mi_col_end - mi_col - 16) << 2) + 32 - lx;
let yd = ((mi_row_end - mi_row - 16) << 2) + 32 - ly;
let right_available = (mi_col + col_off + 8) < mi_col_end;
let bottom_available = (yd > 0) && ((mi_row + row_off + 8) < mi_row_end);
let tr_ok = matches!(i, 0..=2) && have_above && right_available && xr > 0;
let tr_px = if tr_ok { xr.min(32).max(0) as usize } else { 0 };
let bl_ok = i == 0 && have_left && bottom_available && yd > 0;
let bl_px = if bl_ok {
(yd.min(32)).max(0) as usize
} else {
0
};
let (ab, lf, corner) = intrapred::build_refs(
recy, pw, y0, x0, 32, have_above, have_left, tr_px, bl_px, neutral,
);
dispatch_intra_pred(
m,
adelta,
&ab,
&lf,
corner,
have_above,
have_left,
32 + tr_px as i32,
32 + bl_px as i32,
)
}
pub(crate) fn part_lambda(qstep: i32, c: f64) -> f64 {
c * (qstep as f64) * (qstep as f64)
}
fn project_luma_rdoq(
luma: &Basis,
resid: &[f32],
scan: &[u16],
qc: usize,
cost: &mut f64,
lambda: f64,
) -> Vec<f32> {
if lambda > 0.0 {
let (mut l, prm) = luma.project_scan_with_prm(resid, scan);
*cost += coder::rdoq_luma(&prm, &mut l, qc, scan, 1024, lambda);
l
} else {
let l = luma.project(resid, 0.0);
*cost += l
.iter()
.filter(|&&v| v != 0.0)
.map(|&v| 2.0 + 2.0 * ((v.abs() as f64) + 1.0).log2())
.sum::<f64>();
l
}
}
#[allow(clippy::too_many_arguments)]
pub(super) fn encode_luma_sb(
recy: &mut [f32],
yp: &[f32],
pw: usize,
width: usize,
height: usize,
sb_y: usize,
sb_x: usize,
luma: &Basis,
qstep: i32,
resid_scale: f32,
scan: &[u16],
neutral: f32,
qc: usize,
rdoq_lambda: f64,
speed: Speed,
bd: i32,
allow_dir: bool,
) -> ([Vec<Coeff>; 4], usize, i8) {
const POS: [(usize, usize); 4] = [(0, 0), (0, 32), (32, 0), (32, 32)];
let mut best_cost = f64::INFINITY;
let mut best_mode = 0usize;
let mut best_delta = 0i32;
let mut best_tus: [Vec<Coeff>; 4] = [Vec::new(), Vec::new(), Vec::new(), Vec::new()];
let mut best_region = vec![0f32; 64 * 64];
let base_modes: &[usize] = if speed.reduced_modes() {
if allow_dir {
&[0usize, 1, 2, 5, 6, 7, 8, 9, 10, 11, 12]
} else {
&[0usize, 1, 2]
}
} else if allow_dir {
&[0usize, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
} else {
&[0usize, 1, 2, 3, 4]
};
let dir_deltas: &[i32] = if speed.try_angle_deltas() {
&[0, -1, 1, -2, 2, -3, 3]
} else {
&[0]
};
let mut cands: Vec<(usize, i32)> = Vec::new();
for &m in base_modes {
if m >= 5 {
for &d in dir_deltas {
cands.push((m, d));
}
} else {
cands.push((m, 0));
}
}
let search_lambda = if speed.per_candidate_rdoq() {
rdoq_lambda
} else {
0.0
};
let resid_buf = std::cell::RefCell::new(vec![0f32; 1024]);
let encode_mode =
|recy: &mut [f32], m: usize, adelta: i32, lambda: f64| -> ([Vec<Coeff>; 4], f64) {
let mut resid = resid_buf.borrow_mut();
let mut cost = 0f64;
let mut tus: [Vec<Coeff>; 4] = [Vec::new(), Vec::new(), Vec::new(), Vec::new()];
for (i, &(ty, tx)) in POS.iter().enumerate() {
let (y0, x0) = (sb_y + ty, sb_x + tx);
let pblk = predict_luma(recy, pw, width, height, i, y0, x0, m, adelta, neutral);
for r in 0..32 {
let base = (y0 + r) * pw + x0;
let src = &yp[base..base + 32];
let pred = &pblk[r * 32..r * 32 + 32];
let dst = &mut resid[r * 32..r * 32 + 32];
for ((d, &s), &p) in dst.iter_mut().zip(src).zip(pred) {
*d = (s - p) * resid_scale;
}
}
let lev = if lambda > 0.0 {
let (mut l, prm) = luma.project_with_prm(&resid[..]);
cost += coder::rdoq_luma(&prm, &mut l, qc, scan, 1024, lambda);
l
} else {
let l = luma.project(&resid[..], 0.0);
cost += l
.iter()
.filter(|&&v| v != 0.0)
.map(|&v| 2.0 + 2.0 * ((v.abs() as f64) + 1.0).log2())
.sum::<f64>();
l
};
let rb = reconstruct_luma(&pblk, &lev, qstep, scan, bd);
put_block(recy, pw, y0, x0, 32, &rb);
tus[i] = levels_to_coeffs(&lev);
}
if m != 0 {
cost += if m >= 5 { 9.0 } else { 6.0 };
}
(tus, cost)
};
for &(m, d) in &cands {
let (tus, cost) = encode_mode(recy, m, d, search_lambda);
if cost < best_cost {
best_cost = cost;
best_mode = m;
best_delta = d;
best_tus = tus;
for ry in 0..64 {
let dst = ry * 64;
let src = (sb_y + ry) * pw + sb_x;
best_region[dst..dst + 64].copy_from_slice(&recy[src..src + 64]);
}
}
}
if speed.per_candidate_rdoq() || rdoq_lambda <= 0.0 {
for ry in 0..64 {
let src = ry * 64;
let dst = (sb_y + ry) * pw + sb_x;
recy[dst..dst + 64].copy_from_slice(&best_region[src..src + 64]);
}
} else {
let (tus, _) = encode_mode(recy, best_mode, best_delta, rdoq_lambda);
best_tus = tus;
}
(best_tus, best_mode, best_delta as i8)
}
#[allow(clippy::too_many_arguments)]
pub(super) fn predict_luma_leaf32(
recy: &[f32],
pw: usize,
mi_cols: i64,
_mi_rows: i64,
sb_y: usize,
sb_x: usize,
ti: usize,
m: usize,
neutral: f32,
) -> Vec<f32> {
let (y0, x0) = (sb_y, sb_x + ti * 32);
if m == 0 {
return vec![dc_pred(recy, pw, y0, x0, 32, neutral); 1024];
}
let have_above = y0 > 0;
let have_left = x0 > 0;
let mi_col = (sb_x >> 2) as i64;
let lx = (ti * 32) as i64;
let col_off = lx / 4;
let xr = ((mi_cols - mi_col - 16) << 2) + 32 - lx;
let right_available = (mi_col + col_off + 8) < mi_cols;
let tr_ok = have_above && right_available && xr > 0;
let tr_px = if tr_ok { xr.min(32).max(0) as usize } else { 0 };
let (ab, lf, corner) = intrapred::build_refs(
recy, pw, y0, x0, 32, have_above, have_left, tr_px, 0, neutral,
);
dispatch_intra_pred(
m,
0,
&ab,
&lf,
corner,
have_above,
have_left,
32 + tr_px as i32,
32,
)
}
#[allow(clippy::too_many_arguments)]
pub(super) fn encode_luma_leaf32(
recy: &mut [f32],
yp: &[f32],
pw: usize,
mi_cols: i64,
mi_rows: i64,
sb_y: usize,
sb_x: usize,
luma: &Basis,
qstep: i32,
scan: &[u16],
neutral: f32,
qc: usize,
rdoq_lambda: f64,
speed: Speed,
bd: i32,
) -> ([Vec<Coeff>; 2], usize) {
let mut best_cost = f64::INFINITY;
let mut best_mode = 0usize;
let mut best_tus: [Vec<Coeff>; 2] = [Vec::new(), Vec::new()];
let mut best_region = [0f32; 64 * 32];
let cands: &[usize] = if speed.reduced_modes() {
&[0usize, 1, 2]
} else {
&[0usize, 1, 2, 3, 4]
};
let search_lambda = if speed.per_candidate_rdoq() {
rdoq_lambda
} else {
0.0
};
let encode_mode = |recy: &mut [f32], m: usize, lambda: f64| -> ([Vec<Coeff>; 2], f64) {
let mut resid = [0f32; 1024];
let mut cost = 0f64;
let mut tus: [Vec<Coeff>; 2] = [Vec::new(), Vec::new()];
for (ti, tu) in tus.iter_mut().enumerate() {
let (y0, x0) = (sb_y, sb_x + ti * 32);
let pblk = predict_luma_leaf32(recy, pw, mi_cols, mi_rows, sb_y, sb_x, ti, m, neutral);
for r in 0..32 {
let base = (y0 + r) * pw + x0;
for c in 0..32 {
resid[r * 32 + c] =
(yp[base + c] - pblk[r * 32 + c]) * (luma.qstep as f32 / qstep as f32);
}
}
let lev = project_luma_rdoq(luma, &resid, scan, qc, &mut cost, lambda);
let rb = reconstruct_luma(&pblk, &lev, qstep, scan, bd);
put_block(recy, pw, y0, x0, 32, &rb);
*tu = levels_to_coeffs(&lev);
}
if m != 0 {
cost += 6.0;
}
(tus, cost)
};
for &m in cands {
let (tus, cost) = encode_mode(recy, m, search_lambda);
if cost < best_cost {
best_cost = cost;
best_mode = m;
best_tus = tus;
for ry in 0..32 {
let src = (sb_y + ry) * pw + sb_x;
best_region[ry * 64..ry * 64 + 64].copy_from_slice(&recy[src..src + 64]);
}
}
}
if speed.per_candidate_rdoq() || rdoq_lambda <= 0.0 {
for ry in 0..32 {
let dst = (sb_y + ry) * pw + sb_x;
recy[dst..dst + 64].copy_from_slice(&best_region[ry * 64..ry * 64 + 64]);
}
} else {
let (tus, _) = encode_mode(recy, best_mode, rdoq_lambda);
best_tus = tus;
}
(best_tus, best_mode)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn predict_luma_leaf_tu(
recy: &[f32],
pw: usize,
mc: i64,
mr: i64,
sb_y: usize,
sb_x: usize,
ty: usize,
tx: usize,
i: usize,
m: usize,
neutral: f32,
split_leaf: bool,
) -> Vec<f32> {
let (y0, x0) = (sb_y + ty, sb_x + tx);
if m == 0 {
return vec![dc_pred(recy, pw, y0, x0, 32, neutral); 1024];
}
let have_above = y0 > 0;
let have_left = x0 > 0;
let sb_x0 = (x0 / 64) * 64;
let sb_y0 = (y0 / 64) * 64;
let is_right = (x0 - sb_x0) >= 32;
let is_bottom = (y0 - sb_y0) >= 32;
let mi_col = (sb_x >> 2) as i64;
let mi_row = (sb_y >> 2) as i64;
let (lx, ly) = (tx as i64, ty as i64);
let (col_off, row_off) = (lx / 4, ly / 4);
let xr = ((mc - mi_col - 16) << 2) + 32 - lx;
let yd = ((mr - mi_row - 16) << 2) + 32 - ly;
#[allow(clippy::overly_complex_bool_expr)]
let tr_avail = !(is_right && is_bottom) && !is_bottom && (mi_col + col_off + 8) < mc;
#[allow(clippy::overly_complex_bool_expr)]
let bl_avail = !is_right && !is_bottom && (mi_row + row_off + 8) < mr;
let need_tr = matches!(m, 1 | 3 | 7) || m >= 12;
let need_bl = matches!(m, 1 | 2 | 11);
let tr_ok = need_tr && matches!(i, 0..=2) && have_above && tr_avail && xr > 0;
let tr_px = if tr_ok { xr.min(32).max(0) as usize } else { 0 };
let bl_ok = need_bl
&& i == 0
&& have_left
&& (bl_avail || (split_leaf && is_right && !is_bottom))
&& yd > 0;
let bl_px = if bl_ok { yd.min(32).max(0) as usize } else { 0 };
let (ab, lf, corner) = intrapred::build_refs(
recy, pw, y0, x0, 32, have_above, have_left, tr_px, bl_px, neutral,
);
dispatch_intra_pred(
m,
0,
&ab,
&lf,
corner,
have_above,
have_left,
32 + tr_px as i32,
32 + bl_px as i32,
)
}
#[allow(clippy::too_many_arguments)]
pub(super) fn encode_luma_leaf_v32x64(
recy: &mut [f32],
yp: &[f32],
pw: usize,
mc: i64,
mr: i64,
sb_y: usize,
sb_x: usize,
luma: &Basis,
qstep: i32,
scan: &[u16],
neutral: f32,
qc: usize,
rdoq_lambda: f64,
speed: Speed,
bd: i32,
) -> ([Vec<Coeff>; 2], usize) {
let tu_i = [(0usize, 0usize), (32usize, 2usize)]; let mut best_cost = f64::INFINITY;
let mut best_mode = 0usize;
let mut best_tus: [Vec<Coeff>; 2] = [Vec::new(), Vec::new()];
let mut best_region = vec![0f32; 32 * 64];
let cands: &[usize] = if speed.reduced_modes() {
&[0usize, 1, 2]
} else {
&[0usize, 1, 2, 3, 4]
};
let search_lambda = if speed.per_candidate_rdoq() {
rdoq_lambda
} else {
0.0
};
let encode_mode = |recy: &mut [f32], m: usize, lambda: f64| -> ([Vec<Coeff>; 2], f64) {
let mut resid = vec![0f32; 1024];
let mut cost = 0f64;
let mut tus: [Vec<Coeff>; 2] = [Vec::new(), Vec::new()];
for (k, &(ty, i)) in tu_i.iter().enumerate() {
let (y0, x0) = (sb_y + ty, sb_x);
let pblk =
predict_luma_leaf_tu(recy, pw, mc, mr, sb_y, sb_x, ty, 0, i, m, neutral, false);
for r in 0..32 {
let base = (y0 + r) * pw + x0;
for c in 0..32 {
resid[r * 32 + c] =
(yp[base + c] - pblk[r * 32 + c]) * (luma.qstep as f32 / qstep as f32);
}
}
let lev = project_luma_rdoq(luma, &resid, scan, qc, &mut cost, lambda);
let rb = reconstruct_luma(&pblk, &lev, qstep, scan, bd);
put_block(recy, pw, y0, x0, 32, &rb);
tus[k] = levels_to_coeffs(&lev);
}
if m != 0 {
cost += 6.0;
}
(tus, cost)
};
for &m in cands {
let (tus, cost) = encode_mode(recy, m, search_lambda);
if cost < best_cost {
best_cost = cost;
best_mode = m;
best_tus = tus;
for ry in 0..64 {
let src = (sb_y + ry) * pw + sb_x;
best_region[ry * 32..ry * 32 + 32].copy_from_slice(&recy[src..src + 32]);
}
}
}
if speed.per_candidate_rdoq() || rdoq_lambda <= 0.0 {
for ry in 0..64 {
let dst = (sb_y + ry) * pw + sb_x;
recy[dst..dst + 32].copy_from_slice(&best_region[ry * 32..ry * 32 + 32]);
}
} else {
let (tus, _) = encode_mode(recy, best_mode, rdoq_lambda);
best_tus = tus;
}
(best_tus, best_mode)
}
#[allow(clippy::too_many_arguments)]
pub(super) fn encode_luma_leaf_s32x32(
recy: &mut [f32],
yp: &[f32],
pw: usize,
mc: i64,
mr: i64,
sb_y: usize,
sb_x: usize,
luma: &Basis,
qstep: i32,
scan: &[u16],
neutral: f32,
qc: usize,
rdoq_lambda: f64,
speed: Speed,
bd: i32,
) -> (Vec<Coeff>, usize) {
let cands: &[usize] = if speed.reduced_modes() {
&[0usize, 1, 2]
} else {
&[0usize, 1, 2, 3, 4]
};
let search_lambda = if speed.per_candidate_rdoq() {
rdoq_lambda
} else {
0.0
};
let encode_mode = |recy: &[f32], m: usize, lambda: f64| -> ([f32; 1024], Vec<Coeff>, f64) {
let pblk = predict_luma_leaf_tu(recy, pw, mc, mr, sb_y, sb_x, 0, 0, 0, m, neutral, true);
let mut resid = [0f32; 1024];
for r in 0..32 {
let base = (sb_y + r) * pw + sb_x;
for c in 0..32 {
resid[r * 32 + c] =
(yp[base + c] - pblk[r * 32 + c]) * (luma.qstep as f32 / qstep as f32);
}
}
let mut cost = 0f64;
let lev = project_luma_rdoq(luma, &resid, scan, qc, &mut cost, lambda);
if m != 0 {
cost += 6.0;
}
let rb = reconstruct_luma(&pblk, &lev, qstep, scan, bd);
(rb, levels_to_coeffs(&lev), cost)
};
let mut best_cost = f64::INFINITY;
let mut best_mode = 0usize;
let mut best_tu: Vec<Coeff> = Vec::new();
let mut best_region = vec![0f32; 32 * 32];
for &m in cands {
let (rb, tu, cost) = encode_mode(recy, m, search_lambda);
if cost < best_cost {
best_cost = cost;
best_mode = m;
best_tu = tu;
best_region.copy_from_slice(&rb);
}
}
if !speed.per_candidate_rdoq() && rdoq_lambda > 0.0 {
let (rb, tu, _) = encode_mode(recy, best_mode, rdoq_lambda);
best_tu = tu;
best_region.copy_from_slice(&rb);
}
put_block(recy, pw, sb_y, sb_x, 32, &best_region);
(best_tu, best_mode)
}