use crate::Speed;
use crate::dct::{
adst4x4_t, adst4x8_t, adst8x8_t, adst16x16_t, adstdct4x4_t, adstdct4x8_t, adstdct8x8_t,
adstdct16x16_t, dct16x8_t, dctadst4x4_t, dctadst4x8_t, dctadst8x8_t, dctadst16x16_t,
fidentity8x8_t,
};
use crate::idct::{
iadst_dequant_4x4, iadst_dequant_4x8, iadst_dequant_8x8, iadst_dequant_16x16,
iadstdct_dequant_4x4, iadstdct_dequant_4x8, iadstdct_dequant_8x8, iadstdct_dequant_16x16,
idct_dequant_4x4, idct_dequant_4x8, idct_dequant_8x8, idct_dequant_8x16, idct_dequant_16x8,
idct_dequant_16x16, idct_dequant_16x32, idct_dequant_32x32, idctadst_dequant_4x4,
idctadst_dequant_4x8, idctadst_dequant_8x8, idctadst_dequant_16x16, iidentity_dequant_8x8,
};
use crate::obu::{
frame_header_lossy_multitile, frame_header_lossy_multitile_th, temporal_delimiter,
wrap_obu_frame, wrap_obu_frame_split,
};
use crate::odec::OdEcEncoder;
#[cfg(test)]
pub(crate) static FORCE_SPLIT4: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(false);
#[cfg(not(test))]
pub(crate) static FORCE_SPLIT4: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(false);
pub(crate) static SPLIT4_ENABLED: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(true);
pub(crate) static FORCE_HORZ: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(false);
pub(crate) static HORZ_ENABLED: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(true);
pub(crate) static TUNE_SSIMULACRA2: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(true);
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Part16 {
None,
Horz,
Split,
}
use crate::trellis::{trellis_optimize, trellis_optimize_ctx};
use crate::coeffs::encode_tx16_coeffs_adapt;
use crate::coeffs::*;
use crate::cost::*;
use crate::intrapred::*;
use crate::quant::*;
use crate::tables::*;
#[derive(Clone, Copy, PartialEq, Eq)]
enum ChromaTx {
DctDct,
AdstAdst,
AdstDct,
DctAdst,
}
fn chroma_tx_for_mode(mode: usize) -> ChromaTx {
match mode {
m if m == PAETH_PRED || m == SMOOTH_PRED || m == D135_PRED => ChromaTx::AdstAdst,
m if m == SMOOTH_V_PRED || m == V_PRED || m == D113_PRED || m == VERT_LEFT_PRED => {
ChromaTx::AdstDct
}
m if m == SMOOTH_H_PRED || m == H_PRED || m == D157_PRED || m == D203_PRED => {
ChromaTx::DctAdst
}
_ => ChromaTx::DctDct,
}
}
fn fwd_chroma_8x8(tx: ChromaTx, resid: &[i32; 64], q: &impl Dct) -> ([i32; 64], [f64; 64]) {
match tx {
ChromaTx::DctDct => forward_dct_quant_8x8_t(resid, q),
ChromaTx::AdstAdst => adst8x8_t(resid, q),
ChromaTx::AdstDct => adstdct8x8_t(resid, q),
ChromaTx::DctAdst => dctadst8x8_t(resid, q),
}
}
fn inv_chroma_8x8(tx: ChromaTx, levels: &[i32; 64], q: &impl Dct) -> [i32; 64] {
match tx {
ChromaTx::DctDct => idct_dequant_8x8(levels, q),
ChromaTx::AdstAdst => iadst_dequant_8x8(levels, q),
ChromaTx::AdstDct => iadstdct_dequant_8x8(levels, q),
ChromaTx::DctAdst => idctadst_dequant_8x8(levels, q),
}
}
fn fwd_chroma_16x16(tx: ChromaTx, resid: &[i32; 256], q: &impl Dct) -> ([i32; 256], [f64; 256]) {
match tx {
ChromaTx::DctDct => forward_dct_quant_16x16_t(resid, q),
ChromaTx::AdstAdst => adst16x16_t(resid, q),
ChromaTx::AdstDct => adstdct16x16_t(resid, q),
ChromaTx::DctAdst => dctadst16x16_t(resid, q),
}
}
fn inv_chroma_16x16(tx: ChromaTx, levels: &[i32; 256], q: &impl Dct) -> [i32; 256] {
match tx {
ChromaTx::DctDct => idct_dequant_16x16(levels, q),
ChromaTx::AdstAdst => iadst_dequant_16x16(levels, q),
ChromaTx::AdstDct => iadstdct_dequant_16x16(levels, q),
ChromaTx::DctAdst => idctadst_dequant_16x16(levels, q),
}
}
fn fwd_chroma_4x4(tx: ChromaTx, resid: &[i32; 16], q: &impl Dct) -> ([i32; 16], [f64; 16]) {
match tx {
ChromaTx::DctDct => forward_dct_quant_4x4_t(resid, q),
ChromaTx::AdstAdst => adst4x4_t(resid, q),
ChromaTx::AdstDct => adstdct4x4_t(resid, q),
ChromaTx::DctAdst => dctadst4x4_t(resid, q),
}
}
fn inv_chroma_4x4(tx: ChromaTx, levels: &[i32; 16], q: &impl Dct) -> [i32; 16] {
match tx {
ChromaTx::DctDct => idct_dequant_4x4(levels, q),
ChromaTx::AdstAdst => iadst_dequant_4x4(levels, q),
ChromaTx::AdstDct => iadstdct_dequant_4x4(levels, q),
ChromaTx::DctAdst => idctadst_dequant_4x4(levels, q),
}
}
fn fwd_chroma_4x8(tx: ChromaTx, resid: &[i32; 32], q: &impl Dct) -> ([i32; 32], [f64; 32]) {
match tx {
ChromaTx::DctDct => forward_dct_quant_4x8_t(resid, q),
ChromaTx::AdstAdst => adst4x8_t(resid, q),
ChromaTx::AdstDct => adstdct4x8_t(resid, q),
ChromaTx::DctAdst => dctadst4x8_t(resid, q),
}
}
fn inv_chroma_4x8(tx: ChromaTx, levels: &[i32; 32], q: &impl Dct) -> [i32; 32] {
match tx {
ChromaTx::DctDct => idct_dequant_4x8(levels, q),
ChromaTx::AdstAdst => iadst_dequant_4x8(levels, q),
ChromaTx::AdstDct => iadstdct_dequant_4x8(levels, q),
ChromaTx::DctAdst => idctadst_dequant_4x8(levels, q),
}
}
pub(crate) struct Cdfs {
pub(crate) skip: Vec<Vec<u16>>, pub(crate) part_bl8: Vec<Vec<u16>>, pub(crate) part_split: Vec<Vec<Vec<u16>>>, pub(crate) kf_y: Vec<Vec<u16>>, pub(crate) uv_mode: Vec<Vec<u16>>, pub(crate) angle_delta: Vec<Vec<u16>>, pub(crate) cfl_sign: Vec<u16>, pub(crate) cfl_alpha: Vec<Vec<u16>>, pub(crate) txtp: Vec<Vec<u16>>, pub(crate) txtp4: Vec<Vec<u16>>, pub(crate) txtp16: Vec<Vec<u16>>, pub(crate) txb_skip: [Vec<Vec<u16>>; 4], pub(crate) base_tok: [[Vec<Vec<u16>>; 2]; 4], pub(crate) br_tok: [[Vec<Vec<u16>>; 2]; 4], pub(crate) eob_base: [[Vec<Vec<u16>>; 2]; 4], pub(crate) eob_hi: [[Vec<Vec<u16>>; 2]; 4], pub(crate) dc_sign: [Vec<Vec<u16>>; 2], pub(crate) eob_bin_16_c: Vec<u16>, pub(crate) eob_bin_16_l: Vec<u16>, pub(crate) eob_bin_32_c: Vec<u16>, pub(crate) eob_bin_64_l: Vec<u16>, pub(crate) eob_bin_64_c: Vec<u16>, pub(crate) eob_bin_256_l: Vec<u16>, pub(crate) eob_bin_256_c: Vec<u16>, pub(crate) eob_bin_128_c: Vec<u16>, pub(crate) eob_bin_128_l: Vec<u16>, pub(crate) eob_bin_1024_l: Vec<u16>, pub(crate) eob_bin_1024_c: Vec<u16>, pub(crate) eob_bin_512_c: Vec<u16>, pub(crate) delta_q: Vec<u16>, pub(crate) wiener_restore: Vec<u16>, }
impl Cdfs {
fn new(qctx: usize) -> Self {
use crate::coef_q as Q;
let rows = |t: &[[u16; 3]]| t.iter().map(|r| icdf(r)).collect::<Vec<_>>();
let rows2 = |t: &[[u16; 2]]| t.iter().map(|r| icdf(r)).collect::<Vec<_>>();
let his = |t: &[u16]| t.iter().map(|&v| icdf(&[v])).collect::<Vec<_>>();
let txb_skip = [
Q::SKIP_TX4[qctx]
.iter()
.map(|&v| icdf(&[v]))
.collect::<Vec<_>>(),
Q::SKIP_TX8[qctx]
.iter()
.map(|&v| icdf(&[v]))
.collect::<Vec<_>>(),
Q::SKIP_TX16[qctx]
.iter()
.map(|&v| icdf(&[v]))
.collect::<Vec<_>>(),
Q::SKIP_TX32[qctx]
.iter()
.map(|&v| icdf(&[v]))
.collect::<Vec<_>>(),
];
let base_tok = [
[
rows(&Q::BASE_TOK_TX4_LUMA_Q[qctx]),
rows(&Q::BASE_TOK_TX4_CHROMA_Q[qctx]),
],
[
rows(&Q::BASE_TOK_TX8_LUMA_Q[qctx]),
rows(&Q::BASE_TOK_TX8_CHROMA_Q[qctx]),
],
[
rows(&Q::BASE_TOK_TX16_LUMA_Q[qctx]),
rows(&Q::BASE_TOK_TX16_CHROMA_Q[qctx]),
],
[
rows(&Q::BASE_TOK_TX32_LUMA_Q[qctx]),
rows(&Q::BASE_TOK_TX32_CHROMA_Q[qctx]),
],
];
let br_tok = [
[
rows(&Q::BR_TOK_TX4_LUMA_Q[qctx]),
rows(&Q::BR_TOK_TX4_CHROMA_Q[qctx]),
],
[
rows(&Q::BR_TOK_TX8_LUMA_Q[qctx]),
rows(&Q::BR_TOK_TX8_CHROMA_Q[qctx]),
],
[
rows(&Q::BR_TOK_TX16_LUMA_Q[qctx]),
rows(&Q::BR_TOK_TX16_CHROMA_Q[qctx]),
],
[
rows(&Q::BR_TOK_TX32_LUMA_Q[qctx]),
rows(&Q::BR_TOK_TX32_CHROMA_Q[qctx]),
],
];
let eob_base = [
[
rows2(&Q::EOB_BASE_TX4_LUMA_Q[qctx]),
rows2(&Q::EOB_BASE_TX4_CHROMA_Q[qctx]),
],
[
rows2(&Q::EOB_BASE_TX8_LUMA_Q[qctx]),
rows2(&Q::EOB_BASE_TX8_CHROMA_Q[qctx]),
],
[
rows2(&Q::EOB_BASE_TX16_LUMA_Q[qctx]),
rows2(&Q::EOB_BASE_TX16_CHROMA_Q[qctx]),
],
[
rows2(&Q::EOB_BASE_TX32_LUMA_Q[qctx]),
rows2(&Q::EOB_BASE_TX32_CHROMA_Q[qctx]),
],
];
let eob_hi = [
[
his(&Q::EOB_HI_TX4_LUMA[qctx]),
his(&Q::EOB_HI_TX4_CHROMA[qctx]),
],
[
his(&Q::EOB_HI_TX8_LUMA[qctx]),
his(&Q::EOB_HI_TX8_CHROMA[qctx]),
],
[
his(&Q::EOB_HI_TX16_LUMA[qctx]),
his(&Q::EOB_HI_TX16_CHROMA[qctx]),
],
[
his(&Q::EOB_HI_TX32_LUMA[qctx]),
his(&Q::EOB_HI_TX32_CHROMA[qctx]),
],
];
Cdfs {
skip: SKIP_CDF.iter().map(|&v| icdf(&[v])).collect(),
part_bl8: PART_BL8_CDF.iter().map(|r| icdf(r)).collect(),
part_split: PART_SPLIT_CDF
.iter()
.map(|lvl| lvl.iter().map(|r| icdf(r)).collect())
.collect(),
kf_y: {
let mut v = Vec::with_capacity(25);
#[allow(clippy::needless_range_loop)]
for a in 0..5 {
for l in 0..5 {
v.push(icdf(&KF_Y_MODE_CDF[a][l]));
}
}
v
},
angle_delta: ANGLE_DELTA_CDF.iter().map(|r| icdf(r)).collect(),
cfl_sign: icdf(&CFL_SIGN_CDF),
cfl_alpha: CFL_ALPHA_CDF.iter().map(|r| icdf(r)).collect(),
uv_mode: {
let mut v = Vec::with_capacity(26);
#[allow(clippy::needless_range_loop)]
for m in 0..13 {
v.push(icdf(&UV_MODE_NOCFL_CDF[m]));
}
#[allow(clippy::needless_range_loop)]
for m in 0..13 {
v.push(icdf(&UV_MODE_CFL_CDF[m]));
}
v
},
txtp: TXTP_INTRA1_TX8.iter().map(|r| icdf(r)).collect(),
txtp16: TXTP_INTRA2_TX16.iter().map(|r| icdf(r)).collect(),
txtp4: TXTP_INTRA1_TX4.iter().map(|r| icdf(r)).collect(),
txb_skip,
base_tok,
br_tok,
eob_base,
eob_hi,
dc_sign: [
Q::DC_SIGN_Q[qctx][0].iter().map(|&v| icdf(&[v])).collect(),
Q::DC_SIGN_Q[qctx][1].iter().map(|&v| icdf(&[v])).collect(),
],
eob_bin_16_c: icdf(&Q::EOB_BIN_16_CHROMA[qctx]),
eob_bin_16_l: icdf(&Q::EOB_BIN_16_LUMA[qctx]),
eob_bin_32_c: icdf(&Q::EOB_BIN_32_CHROMA[qctx]),
eob_bin_64_l: icdf(&Q::EOB_BIN_64_LUMA[qctx]),
eob_bin_64_c: icdf(&Q::EOB_BIN_64_CHROMA[qctx]),
eob_bin_256_l: icdf(&Q::EOB_BIN_256_LUMA[qctx]),
eob_bin_256_c: icdf(&Q::EOB_BIN_256_CHROMA[qctx]),
eob_bin_128_c: icdf(&Q::EOB_BIN_128_CHROMA[qctx]),
eob_bin_128_l: icdf(&Q::EOB_BIN_128_LUMA[qctx]),
eob_bin_1024_l: icdf(&Q::EOB_BIN_1024_LUMA[qctx]),
eob_bin_1024_c: icdf(&Q::EOB_BIN_1024_CHROMA[qctx]),
eob_bin_512_c: icdf(&Q::EOB_BIN_512_CHROMA[qctx]),
delta_q: icdf(&[28160, 32120, 32677]),
wiener_restore: icdf(&[11570]),
}
}
}
const AQ_RES_LOG2: u8 = 2;
pub(crate) const AQ_DELTA_Q_RES_LOG2: u8 = AQ_RES_LOG2;
const AQ_MAX_STEPS: i32 = 12;
const AQ_SLOPE: f32 = 5.0;
const AQ_MAX_DELTA: f32 = 28.0;
#[derive(Clone, Copy, Debug)]
pub(crate) struct VarianceBoost {
pub enabled: bool,
pub octile: u8,
pub strength: f32,
pub boost_only: bool,
}
impl VarianceBoost {
pub(crate) const fn off() -> Self {
VarianceBoost {
enabled: false,
octile: 6,
strength: 1.0,
boost_only: false,
}
}
pub(crate) fn on() -> Self {
VarianceBoost {
enabled: true,
octile: 6,
strength: 0.6,
boost_only: true,
}
}
}
fn sb_activity(
yp: &[i32],
pw: usize,
sb_y: usize,
sb_x: usize,
width: usize,
height: usize,
) -> f32 {
let h = height.saturating_sub(sb_y).min(64);
let w = width.saturating_sub(sb_x).min(64);
if h == 0 || w == 0 {
return 0.0;
}
let mut sum = 0i64;
let mut sum2 = 0i64;
for r in 0..h {
let base = (sb_y + r) * pw + sb_x;
for &c in &yp[base..base + w] {
let v = c as i64;
sum += v;
sum2 += v * v;
}
}
let n = (h * w) as f64;
let mean = sum as f64 / n;
let var = (sum2 as f64 / n - mean * mean).max(0.0);
(1.0 + var).ln() as f32
}
fn tile_ref_activity(yp: &[i32], pw: usize, w: usize, h: usize) -> f32 {
let mut sum = 0f32;
let mut cnt = 0f32;
for sb_y in (0..h).step_by(64) {
for sb_x in (0..w).step_by(64) {
sum += sb_activity(yp, pw, sb_y, sb_x, w, h);
cnt += 1.0;
}
}
if cnt > 0.0 { sum / cnt } else { 5.0 }
}
fn aq_params() -> (f32, f32, f32) {
(AQ_SLOPE, AQ_MAX_DELTA, 1.0)
}
fn aq_target_qidx(base_q: i32, activity: f32, ref_act: f32) -> i32 {
let (slope, maxd, coarsen) = aq_params();
let mut delta = (activity - ref_act) * slope;
if delta > 0.0 {
delta *= coarsen;
}
let delta = delta.clamp(-maxd, maxd);
(base_q + delta.round() as i32).clamp(1, 255)
}
fn aq_sb_subblock_variances(
yp: &[i32],
pw: usize,
sb_y: usize,
sb_x: usize,
width: usize,
height: usize,
out: &mut [f32; 64],
) -> usize {
let mut filled = 0usize;
let mut acc = 0f64;
for by in 0..8 {
for bx in 0..8 {
let y0 = sb_y + by * 8;
let x0 = sb_x + bx * 8;
let h = height.saturating_sub(y0).min(8);
let w = width.saturating_sub(x0).min(8);
let idx = by * 8 + bx;
if h == 0 || w == 0 {
out[idx] = f32::NAN; continue;
}
let mut sum = 0i64;
let mut sum2 = 0i64;
for r in 0..h {
let base = (y0 + r) * pw + x0;
for &v in &yp[base..base + w] {
let v = v as i64;
sum += v;
sum2 += v * v;
}
}
let n = (h * w) as f64;
let mean = sum as f64 / n;
let var = (sum2 as f64 / n - mean * mean).max(0.0) as f32;
out[idx] = var;
acc += var as f64;
filled += 1;
}
}
if filled == 0 {
out.iter_mut().for_each(|v| *v = 0.0);
return 0;
}
let mean = (acc / filled as f64) as f32;
for v in out.iter_mut() {
if v.is_nan() {
*v = mean;
}
}
filled
}
fn aq_sb_octile_variance(subvars: &mut [f32; 64], octile: u8) -> f32 {
subvars.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
let o = octile.clamp(1, 8) as usize;
let idx = (o * 8 - 1).min(63);
subvars[idx]
}
fn aq_variance_boost_delta(picked_var: f32, ref_log: f32, strength: f32, boost_only: bool) -> i32 {
let v_log = (1.0 + picked_var).ln();
const LOW_LOG: f32 = 5.549_076; const MAX_BOOST: f32 = 18.0;
const MAX_CUT: f32 = 10.0;
const BOOST_SLOPE: f32 = 5.0;
const CUT_SLOPE: f32 = 3.0;
if v_log < LOW_LOG {
let d = ((LOW_LOG - v_log) * BOOST_SLOPE * strength).min(MAX_BOOST);
-(d.round() as i32)
} else if boost_only {
0
} else {
let over = (v_log - ref_log.max(LOW_LOG)).max(0.0);
let d = (over * CUT_SLOPE * strength).min(MAX_CUT);
d.round() as i32
}
}
struct AqCtx {
enabled: bool,
base_q: u8,
res_log2: u8,
cur_qidx: i32,
ref_act: f32,
read_deltas: bool,
pending: i32,
vb_enabled: bool,
vb_octile: u8,
vb_strength: f32,
vb_boost_only: bool,
}
impl AqCtx {
fn off() -> Self {
AqCtx {
enabled: false,
base_q: 0,
res_log2: 0,
cur_qidx: 0,
ref_act: 0.0,
read_deltas: false,
pending: 0,
vb_enabled: false,
vb_octile: 6,
vb_strength: 1.0,
vb_boost_only: false,
}
}
}
struct LossyTile<'a> {
bd: u8,
quant: Quant,
cquant: Quant,
w: usize,
h: usize,
cw: usize, ss422: bool, ss420: bool, mono: bool, src: &'a [Vec<i32>; 3],
recon: [Vec<i32>; 3],
a_coef: [Vec<u8>; 3], l_coef: [Vec<u8>; 3], a_part: Vec<u8>, l_part: Vec<u8>, a_skip: Vec<u8>, l_skip: Vec<u8>, a_mode: Vec<u8>, l_mode: Vec<u8>, blk4: Vec<u8>, blk4h: Vec<u8>, skip8: Vec<bool>, enc: OdEcEncoder,
cdfs: Cdfs,
speed: Speed,
aq: AqCtx,
wiener: Option<crate::wiener::WienerUnit>,
lr_ref_h: [i32; 3],
lr_ref_v: [i32; 3],
frame_x0: usize,
frame_y0: usize,
frame_w: usize,
frame_h: usize,
base_q_idx: u8,
}
impl<'a> LossyTile<'a> {
fn new(q: u8, bd: u8, w: usize, h: usize, src: &'a [Vec<i32>; 3]) -> Self {
LossyTile {
bd,
quant: Quant::new(q, bd),
cquant: Quant::new_chroma(q, bd),
w,
h,
cw: w,
ss422: false,
ss420: false,
mono: false,
src,
recon: [vec![0; w * h], vec![0; w * h], vec![0; w * h]],
a_coef: [vec![0x40; w / 4], vec![0x40; w / 4], vec![0x40; w / 4]],
l_coef: [vec![0x40; h / 4], vec![0x40; h / 4], vec![0x40; h / 4]],
a_part: vec![0; w / 8],
l_part: vec![0; h / 8],
a_skip: vec![0; w / 4],
l_skip: vec![0; h / 4],
a_mode: vec![0; w / 4],
l_mode: vec![0; h / 4],
blk4: vec![0; (w / 4) * (h / 4)],
blk4h: vec![0; (w / 4) * (h / 4)],
skip8: vec![true; w.div_ceil(8) * h.div_ceil(8)],
enc: OdEcEncoder::new(),
cdfs: Cdfs::new(crate::coef_q::qcat(q)),
speed: Speed::Slow,
aq: AqCtx::off(),
wiener: None,
lr_ref_h: crate::wiener::WIENER_TAPS_MID,
lr_ref_v: crate::wiener::WIENER_TAPS_MID,
frame_x0: 0,
frame_y0: 0,
frame_w: w,
frame_h: h,
base_q_idx: q,
}
}
fn new_mono(q: u8, bd: u8, w: usize, h: usize, src: &'a [Vec<i32>; 3]) -> Self {
LossyTile {
bd,
quant: Quant::new(q, bd),
cquant: Quant::new_chroma(q, bd),
w,
h,
cw: w,
ss422: false,
ss420: false,
mono: true,
src,
recon: [vec![0; w * h], Vec::new(), Vec::new()],
a_coef: [vec![0x40; w / 4], Vec::new(), Vec::new()],
l_coef: [vec![0x40; h / 4], Vec::new(), Vec::new()],
a_part: vec![0; w / 8],
l_part: vec![0; h / 8],
a_skip: vec![0; w / 4],
l_skip: vec![0; h / 4],
a_mode: vec![0; w / 4],
l_mode: vec![0; h / 4],
blk4: vec![0; (w / 4) * (h / 4)],
blk4h: vec![0; (w / 4) * (h / 4)],
skip8: vec![true; w.div_ceil(8) * h.div_ceil(8)],
enc: OdEcEncoder::new(),
cdfs: Cdfs::new(crate::coef_q::qcat(q)),
speed: Speed::Slow,
aq: AqCtx::off(),
wiener: None,
lr_ref_h: crate::wiener::WIENER_TAPS_MID,
lr_ref_v: crate::wiener::WIENER_TAPS_MID,
frame_x0: 0,
frame_y0: 0,
frame_w: w,
frame_h: h,
base_q_idx: q,
}
}
fn new_422(q: u8, bd: u8, w: usize, h: usize, src: &'a [Vec<i32>; 3]) -> Self {
let cw = w / 2;
LossyTile {
bd,
quant: Quant::new(q, bd),
cquant: Quant::new_chroma(q, bd),
w,
h,
cw,
ss422: true,
ss420: false,
mono: false,
src,
recon: [vec![0; w * h], vec![0; cw * h], vec![0; cw * h]],
a_coef: [vec![0x40; w / 4], vec![0x40; cw / 4], vec![0x40; cw / 4]],
l_coef: [vec![0x40; h / 4], vec![0x40; h / 4], vec![0x40; h / 4]],
a_part: vec![0; w / 8],
l_part: vec![0; h / 8],
a_skip: vec![0; w / 4],
l_skip: vec![0; h / 4],
a_mode: vec![0; w / 4],
l_mode: vec![0; h / 4],
blk4: vec![0; (w / 4) * (h / 4)],
blk4h: vec![0; (w / 4) * (h / 4)],
skip8: vec![true; w.div_ceil(8) * h.div_ceil(8)],
enc: OdEcEncoder::new(),
cdfs: Cdfs::new(crate::coef_q::qcat(q)),
speed: Speed::Slow,
aq: AqCtx::off(),
wiener: None,
lr_ref_h: crate::wiener::WIENER_TAPS_MID,
lr_ref_v: crate::wiener::WIENER_TAPS_MID,
frame_x0: 0,
frame_y0: 0,
frame_w: w,
frame_h: h,
base_q_idx: q,
}
}
fn new_420(q: u8, bd: u8, w: usize, h: usize, src: &'a [Vec<i32>; 3]) -> Self {
let (cw, ch) = (w / 2, h / 2);
LossyTile {
bd,
quant: Quant::new(q, bd),
cquant: Quant::new_chroma(q, bd),
w,
h,
cw,
ss422: false,
ss420: true,
mono: false,
src,
recon: [vec![0; w * h], vec![0; cw * ch], vec![0; cw * ch]],
a_coef: [vec![0x40; w / 4], vec![0x40; cw / 4], vec![0x40; cw / 4]],
l_coef: [vec![0x40; h / 4], vec![0x40; ch / 4], vec![0x40; ch / 4]],
a_part: vec![0; w / 8],
l_part: vec![0; h / 8],
a_skip: vec![0; w / 4],
l_skip: vec![0; h / 4],
a_mode: vec![0; w / 4],
l_mode: vec![0; h / 4],
blk4: vec![0; (w / 4) * (h / 4)],
blk4h: vec![0; (w / 4) * (h / 4)],
skip8: vec![true; w.div_ceil(8) * h.div_ceil(8)],
enc: OdEcEncoder::new(),
cdfs: Cdfs::new(crate::coef_q::qcat(q)),
speed: Speed::Slow,
aq: AqCtx::off(),
wiener: None,
lr_ref_h: crate::wiener::WIENER_TAPS_MID,
lr_ref_v: crate::wiener::WIENER_TAPS_MID,
frame_x0: 0,
frame_y0: 0,
frame_w: w,
frame_h: h,
base_q_idx: q,
}
}
fn skip_ctx(&self, plane: usize, bx4: usize, by4: usize, chroma: bool) -> usize {
if !chroma {
0 } else {
let a = &self.a_coef[plane];
let l = &self.l_coef[plane];
let ca = (a[bx4] != 0x40 || a[bx4 + 1] != 0x40) as usize;
let cl = (l[by4] != 0x40 || l[by4 + 1] != 0x40) as usize;
7 + ca + cl
}
}
fn dc_sign_ctx(&self, plane: usize, bx4: usize, by4: usize) -> usize {
let a = &self.a_coef[plane];
let l = &self.l_coef[plane];
let suma = (a[bx4] >> 6) as i32 + (a[bx4 + 1] >> 6) as i32;
let suml = (l[by4] >> 6) as i32 + (l[by4 + 1] >> 6) as i32;
let s = suma + suml - 4;
(s != 0) as usize + (s > 0) as usize
}
fn skip_ctx_16(&self, plane: usize, bx4: usize, by4: usize, chroma: bool) -> usize {
if !chroma {
0
} else {
let a = &self.a_coef[plane];
let l = &self.l_coef[plane];
let ca = a[bx4..bx4 + 4].iter().any(|&x| x != 0x40) as usize;
let cl = l[by4..by4 + 4].iter().any(|&x| x != 0x40) as usize;
7 + ca + cl
}
}
fn dc_sign_ctx_16(&self, plane: usize, bx4: usize, by4: usize) -> usize {
let a = &self.a_coef[plane];
let l = &self.l_coef[plane];
let suma: i32 = a[bx4..bx4 + 4].iter().map(|&x| (x >> 6) as i32).sum();
let suml: i32 = l[by4..by4 + 4].iter().map(|&x| (x >> 6) as i32).sum();
let s = suma + suml - 8;
(s != 0) as usize + (s > 0) as usize
}
fn skip_ctx_16x8_luma(&self) -> usize {
0
}
fn dc_sign_ctx_16x8_luma(&self, bx4: usize, by4: usize) -> usize {
let a = &self.a_coef[0];
let l = &self.l_coef[0];
let suma: i32 = a[bx4..bx4 + 4].iter().map(|&x| (x >> 6) as i32).sum();
let suml: i32 = l[by4..by4 + 2].iter().map(|&x| (x >> 6) as i32).sum();
let s = suma + suml - 6;
(s != 0) as usize + (s > 0) as usize
}
fn skip_ctx_16x8_chroma(&self, plane: usize, bx4: usize, by4: usize) -> usize {
let a = &self.a_coef[plane];
let l = &self.l_coef[plane];
let ca = a[bx4..bx4 + 4].iter().any(|&x| x != 0x40) as usize;
let cl = l[by4..by4 + 2].iter().any(|&x| x != 0x40) as usize;
7 + ca + cl
}
fn dc_sign_ctx_16x8_chroma(&self, plane: usize, bx4: usize, by4: usize) -> usize {
let a = &self.a_coef[plane];
let l = &self.l_coef[plane];
let suma: i32 = a[bx4..bx4 + 4].iter().map(|&x| (x >> 6) as i32).sum();
let suml: i32 = l[by4..by4 + 2].iter().map(|&x| (x >> 6) as i32).sum();
let s = suma + suml - 6;
(s != 0) as usize + (s > 0) as usize
}
fn block_luma_range(&self, x8: usize, y8: usize, dim: usize) -> i32 {
let (px, py) = (x8 * 8, y8 * 8);
let mut lo = i32::MAX;
let mut hi = i32::MIN;
for ry in 0..dim {
let base = (py + ry) * self.w + px;
for &s in &self.src[0][base..base + dim] {
if s < lo {
lo = s;
}
if s > hi {
hi = s;
}
}
}
hi - lo
}
fn prefer_8x8_none(&self, x8: usize, y8: usize) -> bool {
if self.mono || self.ss422 {
return true;
}
let (px, py) = (x8 * 8, y8 * 8);
let maxv = (1 << self.bd) - 1;
let (dcq, acq) = (self.quant.dc_q() as f64, self.quant.ac_q() as f64);
let lam = trellis_lambda();
let mlam = mode_lambda() * acq * acq;
let modes = if self.speed.reduced_modes() {
fast_nd_modes()
} else {
nd_modes()
};
let mut eff8 = f64::INFINITY;
for &m in modes {
let mut pred = [0i32; 64];
if m == DC_PRED {
let d = dc_pred_8x8(&self.recon[0], self.w, px, py, self.bd as i32);
pred = [d; 64];
} else {
intra_predict_nd(
m,
&self.recon[0],
self.w,
px,
py,
8,
8,
false,
false,
self.w,
self.h,
&mut pred,
self.bd,
);
}
let mut resid = [0i32; 64];
for ry in 0..8 {
let srow = &self.src[0][(py + ry) * self.w + px..];
for rx in 0..8 {
resid[ry * 8 + rx] = srow[rx] - pred[ry * 8 + rx];
}
}
let (mut cf, tf) = forward_dct_quant_8x8_t(&resid, &self.quant);
trellis_optimize(&mut cf, &tf, dcq, acq, &SCAN_8X8, lam);
let rr = idct_dequant_8x8(&cf, &self.quant);
let mut sse = 0i64;
for ry in 0..8 {
let srow = &self.src[0][(py + ry) * self.w + px..];
for rx in 0..8 {
let r = (pred[ry * 8 + rx] + rr[ry * 8 + rx]).clamp(0, maxv);
let d = srow[rx] - r;
sse += (d * d) as i64;
}
}
let eff = sse as f64 + mlam * block_rate_bits(&cf, &SCAN_8X8);
if eff < eff8 {
eff8 = eff;
}
}
let mut eff4_sum = mlam * 2.0; for (sx, sy) in [(0usize, 0usize), (4, 0), (0, 4), (4, 4)] {
let (bx, by) = (px + sx, py + sy);
let mut best = f64::INFINITY;
for &m in modes {
let mut pred = [0i32; 16];
if m == DC_PRED {
let d = dc_pred_4x4(&self.recon[0], self.w, bx, by, self.bd as i32);
pred = [d; 16];
} else {
intra_predict_nd(
m,
&self.recon[0],
self.w,
bx,
by,
4,
4,
false,
false,
self.w,
self.h,
&mut pred,
self.bd,
);
}
let mut resid = [0i32; 16];
for ry in 0..4 {
let srow = &self.src[0][(by + ry) * self.w + bx..];
for rx in 0..4 {
resid[ry * 4 + rx] = srow[rx] - pred[ry * 4 + rx];
}
}
let (mut cf, tf) = forward_dct_quant_4x4_t(&resid, &self.quant);
trellis_optimize(&mut cf, &tf, dcq, acq, &SCAN_4X4, lam);
let rr = idct_dequant_4x4(&cf, &self.quant);
let mut sse = 0i64;
for ry in 0..4 {
let srow = &self.src[0][(by + ry) * self.w + bx..];
for rx in 0..4 {
let r = (pred[ry * 4 + rx] + rr[ry * 4 + rx]).clamp(0, maxv);
let d = srow[rx] - r;
sse += (d * d) as i64;
}
}
let eff = sse as f64 + mlam * (block_rate_bits(&cf, &SCAN_4X4) + 4.0);
if eff < best {
best = eff;
}
}
eff4_sum += best;
}
eff8 <= eff4_sum
}
fn luma_variance(&self, px: usize, py: usize, w: usize, h: usize) -> f64 {
let mut sum = 0i64;
let mut sqsum = 0i64;
for ry in 0..h {
let row = &self.src[0][(py + ry) * self.w + px..];
for &s in &row[..w] {
sum += s as i64;
sqsum += (s as i64) * (s as i64);
}
}
let n = (w * h) as f64;
let mean = sum as f64 / n;
(sqsum as f64 / n) - mean * mean
}
fn partition_choice_16(&self, x8: usize, y8: usize) -> Part16 {
if self.mono {
return Part16::Split; }
if self.ss422 {
return Part16::Split;
}
if self.block_luma_range(x8, y8, 16) < LF_BAND_SMOOTH_RANGE {
return Part16::Split;
}
let (px, py) = (x8 * 8, y8 * 8);
let acq = self.quant.ac_q() as f64;
let horz_on = !self.ss420
&& !self.ss422
&& HORZ_ENABLED.load(std::sync::atomic::Ordering::Relaxed)
&& self.quant.ac_q() >= AC_Q_HORZ_MIN;
let block_var = self.luma_variance(px, py, 16, 16);
let mut prune_horz = !horz_on;
if horz_on {
let vh0 = self.luma_variance(px, py, 16, 8);
let vh1 = self.luma_variance(px, py + 8, 16, 8);
let mean_half = 0.5 * (vh0 + vh1);
if block_var <= 1.0 || mean_half >= 0.85 * block_var {
prune_horz = true;
}
}
let prdo = self.perceptual_rd_scale(px, py, 16);
let tune = TUNE_SSIMULACRA2.load(std::sync::atomic::Ordering::Relaxed);
let prdo = prdo * crate::cost::mode_lambda_weight(self.base_q_idx, tune);
let part_lam = mode_lambda() * acq * acq * prdo;
let rd_none = self.rd_cost_square(px, py, 16, false, false, prdo);
let mut rd_split = part_lam * SPLIT_SIGNAL_BITS;
for (sx, sy) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
rd_split += self.rd_cost_square(px + sx, py + sy, 8, false, false, prdo);
}
let rd_horz = if prune_horz {
f64::INFINITY
} else {
self.rd_cost_horz(px, py, prdo)
};
if rd_none <= rd_split && rd_none <= rd_horz {
Part16::None
} else if rd_horz < rd_none && rd_horz <= rd_split {
Part16::Horz
} else {
Part16::Split
}
}
fn with_speed(mut self, speed: Speed) -> Self {
self.speed = speed;
self
}
fn enable_aq(&mut self, base_q: u8, ref_act: f32, vb: &VarianceBoost) {
self.aq = AqCtx {
enabled: true,
base_q,
res_log2: AQ_RES_LOG2,
cur_qidx: base_q as i32,
ref_act,
read_deltas: false,
pending: 0,
vb_enabled: vb.enabled,
vb_octile: vb.octile.clamp(1, 8),
vb_strength: vb.strength.max(0.0),
vb_boost_only: vb.boost_only,
};
}
fn emit_lr_sb(&mut self, sb_x: usize, sb_y: usize) {
let Some(unit) = self.wiener else {
return;
};
const UNIT: usize = 64;
const MI: usize = 4;
let count_units = |frame: usize| -> usize { (1).max((frame + (UNIT >> 1)) / UNIT) };
let unit_rows = count_units(self.frame_h);
let unit_cols = count_units(self.frame_w);
let r = (self.frame_y0 + sb_y) / MI;
let c = (self.frame_x0 + sb_x) / MI;
let sb_mi = UNIT / MI; let urs = (r * MI).div_ceil(UNIT);
let ure = unit_rows.min(((r + sb_mi) * MI).div_ceil(UNIT));
let ucs = (c * MI).div_ceil(UNIT);
let uce = unit_cols.min(((c + sb_mi) * MI).div_ceil(UNIT));
for _ur in urs..ure {
for _uc in ucs..uce {
self.emit_lr_unit(&unit);
}
}
}
fn emit_lr_unit(&mut self, unit: &crate::wiener::WienerUnit) {
use crate::wiener::{WIENER_TAPS_K, WIENER_TAPS_MAX, WIENER_TAPS_MIN};
self.enc.encode_symbol(1, &mut self.cdfs.wiener_restore);
for axis in 0..2 {
let (taps, refs) = if axis == 0 {
(unit.v, &mut self.lr_ref_v)
} else {
(unit.h, &mut self.lr_ref_h)
};
for j in 0..3usize {
let lo = WIENER_TAPS_MIN[j];
let hi = WIENER_TAPS_MAX[j] + 1; let k = WIENER_TAPS_K[j] as u32;
self.enc
.encode_signed_subexp_with_ref(taps[j], lo, hi, k, refs[j]);
refs[j] = taps[j];
}
}
}
fn aq_begin_sb(&mut self, sb_x: usize, sb_y: usize) {
if !self.aq.enabled {
return;
}
let target = if self.aq.vb_enabled {
let mut subvars = [0f32; 64];
let filled = aq_sb_subblock_variances(
&self.src[0],
self.w,
sb_y,
sb_x,
self.w,
self.h,
&mut subvars,
);
if filled == 0 {
self.aq.base_q as i32
} else {
let picked = aq_sb_octile_variance(&mut subvars, self.aq.vb_octile);
let delta = aq_variance_boost_delta(
picked,
self.aq.ref_act,
self.aq.vb_strength,
self.aq.vb_boost_only,
);
(self.aq.base_q as i32 + delta).clamp(1, 255)
}
} else {
let act = sb_activity(&self.src[0], self.w, sb_y, sb_x, self.w, self.h);
aq_target_qidx(self.aq.base_q as i32, act, self.aq.ref_act)
};
let step = 1i32 << self.aq.res_log2;
let steps = (((target - self.aq.cur_qidx) as f32) / step as f32)
.round()
.clamp(-(AQ_MAX_STEPS as f32), AQ_MAX_STEPS as f32) as i32;
let newq = (self.aq.cur_qidx + steps * step).clamp(1, 255);
self.aq.cur_qidx = newq;
self.aq.pending = steps;
self.aq.read_deltas = true;
self.quant = Quant::new(newq as u8, self.bd);
let frame_dc_delta = chroma_dc_delta(self.aq.base_q);
self.cquant = Quant::new_chroma_with_delta(newq as u8, frame_dc_delta, self.bd);
}
fn code_delta_q_if_armed(&mut self) {
if !self.aq.read_deltas {
return;
}
self.aq.read_deltas = false;
let m = self.aq.pending.unsigned_abs() as i32;
const DELTA_Q_SMALL: usize = 3;
if m < DELTA_Q_SMALL as i32 {
self.enc.encode_symbol(m as usize, &mut self.cdfs.delta_q);
} else {
self.enc
.encode_symbol(DELTA_Q_SMALL, &mut self.cdfs.delta_q);
let v = (m - 1) as u32;
let rem = 31 - v.leading_zeros(); let abs_bits = v - (1 << rem);
self.enc.encode_literal(rem - 1, 3); self.enc.encode_literal(abs_bits, rem); }
if m != 0 {
self.enc.encode_literal((self.aq.pending < 0) as u32, 1); }
}
fn perceptual_rd_scale(&self, px: usize, py: usize, dim: usize) -> f64 {
let k = prdo_k();
if k == 0.0 {
return 1.0;
}
let refa = self.aq.ref_act;
if refa <= 0.0 {
return 1.0;
}
let bw = dim.min(self.w.saturating_sub(px));
let bh = dim.min(self.h.saturating_sub(py));
if bw == 0 || bh == 0 {
return 1.0;
}
let yp = &self.src[0];
let (mut sum, mut sum2) = (0i64, 0i64);
for r in 0..bh {
let base = (py + r) * self.w + px;
for &c in &yp[base..base + bw] {
let v = c as i64;
sum += v;
sum2 += v * v;
}
}
let n = (bw * bh) as f64;
let mean = sum as f64 / n;
let var = (sum2 as f64 / n - mean * mean).max(0.0);
let act = (1.0 + var).ln() as f32;
let c = prdo_clamp();
((k * (act - refa) as f64).exp()).clamp(1.0 / c, c)
}
fn rd_cost_square(
&self,
px: usize,
py: usize,
dim: usize,
have_tr: bool,
have_bl: bool,
prdo: f64,
) -> f64 {
let acq = self.quant.ac_q() as f64;
let dcq = self.quant.dc_q() as f64;
let lam = trellis_lambda();
let mlam = mode_lambda() * acq * acq;
let (lam, mlam) = (lam * prdo, mlam * prdo);
let modes: &[usize] = &[DC_PRED, SMOOTH_PRED, PAETH_PRED];
let mut best = f64::INFINITY;
match dim {
8 => {
let scan = &SCAN_8X8;
for &m in modes {
let mut pred = [0i32; 64];
if m == DC_PRED {
let d = dc_pred_8x8(&self.recon[0], self.w, px, py, self.bd as i32);
pred = [d; 64];
} else {
intra_predict_nd(
m,
&self.recon[0],
self.w,
px,
py,
8,
8,
have_tr,
have_bl,
self.w,
self.h,
&mut pred,
self.bd,
);
}
let mut resid = [0i32; 64];
for (ry, (rrow, prow)) in resid
.as_chunks_mut::<8>()
.0
.iter_mut()
.zip(pred.as_chunks::<8>().0.iter())
.enumerate()
{
let srow = &self.src[0][(py + ry) * self.w + px..];
for (r, (&p, &s)) in rrow.iter_mut().zip(prow.iter().zip(srow.iter())) {
*r = s - p;
}
}
let (mut cf, tf) = forward_dct_quant_8x8_t(&resid, &self.quant);
trellis_optimize(&mut cf, &tf, dcq, acq, scan, lam);
let rr = idct_dequant_8x8(&cf, &self.quant);
let sse = sse_recon::<64, 8>(&pred, &rr, &self.src[0], self.w, px, py, self.bd);
let bits = block_rate_bits(&cf, scan) + mode_signal_bits(m);
let cost = sse as f64 + mlam * bits;
if cost < best {
best = cost;
}
}
}
16 => {
let scan = &SCAN_16X16;
for &m in modes {
let mut pred = [0i32; 256];
if m == DC_PRED {
let d = dc_pred_16x16(&self.recon[0], self.w, px, py, self.bd as i32);
pred = [d; 256];
} else {
intra_predict_nd(
m,
&self.recon[0],
self.w,
px,
py,
16,
16,
have_tr,
have_bl,
self.w,
self.h,
&mut pred,
self.bd,
);
}
let mut resid = [0i32; 256];
for (ry, (rrow, prow)) in resid
.as_chunks_mut::<16>()
.0
.iter_mut()
.zip(pred.as_chunks::<16>().0.iter())
.enumerate()
{
let srow = &self.src[0][(py + ry) * self.w + px..];
for (r, (&p, &s)) in rrow.iter_mut().zip(prow.iter().zip(srow.iter())) {
*r = s - p;
}
}
let (mut cf, tf) = forward_dct_quant_16x16_t(&resid, &self.quant);
trellis_optimize(&mut cf, &tf, dcq, acq, scan, lam);
let rr = idct_dequant_16x16(&cf, &self.quant);
let sse =
sse_recon::<256, 16>(&pred, &rr, &self.src[0], self.w, px, py, self.bd);
let bits = block_rate_bits(&cf, scan) + mode_signal_bits(m);
let cost = sse as f64 + mlam * bits;
if cost < best {
best = cost;
}
}
}
32 => {
let scan = &SCAN_32X32;
for &m in modes {
let mut pred = [0i32; 1024];
if m == DC_PRED {
let d = dc_pred_32x32(&self.recon[0], self.w, px, py, self.bd as i32);
pred = [d; 1024];
} else {
intra_predict_nd(
m,
&self.recon[0],
self.w,
px,
py,
32,
32,
have_tr,
have_bl,
self.w,
self.h,
&mut pred,
self.bd,
);
}
let mut resid = [0i32; 1024];
for (ry, (rrow, prow)) in resid
.as_chunks_mut::<32>()
.0
.iter_mut()
.zip(pred.as_chunks::<32>().0.iter())
.enumerate()
{
let srow = &self.src[0][(py + ry) * self.w + px..];
for (r, (&p, &s)) in rrow.iter_mut().zip(prow.iter().zip(srow.iter())) {
*r = s - p;
}
}
let (mut cf, tf) = forward_dct_quant_32x32_t(&resid, &self.quant);
trellis_optimize(&mut cf, &tf, dcq, acq, scan, lam);
let rr = idct_dequant_32x32(&cf, &self.quant);
let sse =
sse_recon::<1024, 32>(&pred, &rr, &self.src[0], self.w, px, py, self.bd);
let bits = block_rate_bits(&cf, scan) + mode_signal_bits(m);
let cost = sse as f64 + mlam * bits;
if cost < best {
best = cost;
}
}
}
_ => unreachable!("rd_cost_square dim {}", dim),
}
best
}
fn rd_cost_horz(&self, px: usize, py: usize, prdo: f64) -> f64 {
let acq = self.quant.ac_q() as f64;
let dcq = self.quant.dc_q() as f64;
let lam = trellis_lambda();
let (lam, mlam) = (lam * prdo, mode_lambda() * acq * acq * prdo);
let maxv = (1 << self.bd) - 1;
let mut total = mlam * SPLIT_SIGNAL_BITS; for half in 0..2 {
let sy = py + half * 8;
let dc = dc_pred_16x8(&self.recon[0], self.w, px, sy, self.bd as i32);
let mut resid = [0i32; 128];
for ry in 0..8 {
let srow = &self.src[0][(sy + ry) * self.w + px..];
for cx in 0..16 {
resid[ry * 16 + cx] = srow[cx] - dc;
}
}
let (mut cf, tf) = dct16x8_t(&resid, &self.quant);
trellis_optimize(&mut cf, &tf, dcq, acq, &SCAN_16X8, lam);
let rr = idct_dequant_16x8(&cf, &self.quant);
let mut sse = 0i64;
for ry in 0..8 {
let srow = &self.src[0][(sy + ry) * self.w + px..];
for cx in 0..16 {
let r = (dc + rr[ry * 16 + cx]).clamp(0, maxv);
let d = (srow[cx] - r) as i64;
sse += d * d;
}
}
let bits = block_rate_bits(&cf, &SCAN_16X8);
total += sse as f64 + mlam * bits;
}
total
}
fn code_block16_horz_444(&mut self, x8: usize, y8: usize) {
let maxval = (1 << self.bd) - 1;
let lam = trellis_lambda();
let (dcq, acq) = (self.quant.dc_q() as f64, self.quant.ac_q() as f64);
let (cdcq, cacq) = (self.cquant.dc_q() as f64, self.cquant.ac_q() as f64);
for half in 0..2 {
let (px, py) = (x8 * 8, y8 * 8 + half * 8);
let (bx4, by4) = (px / 4, py / 4); let lpred = dc_pred_16x8(&self.recon[0], self.w, px, py, self.bd as i32);
let mut lresid = [0i32; 128];
for ry in 0..8 {
let srow = &self.src[0][(py + ry) * self.w + px..];
for cx in 0..16 {
lresid[ry * 16 + cx] = srow[cx] - lpred;
}
}
let (mut lcf, ltf) = dct16x8_t(&lresid, &self.quant);
trellis_optimize(&mut lcf, <f, dcq, acq, &SCAN_16X8, lam);
let mean_l = lresid.iter().sum::<i32>() / 128;
if lcf[0] == 0 && mean_l.abs() >= 8 {
lcf[0] = if mean_l > 0 { 1 } else { -1 };
}
let mut ccf = [[0i32; 128]; 2];
let mut cpred = [0i32; 2];
for ci in 0..2 {
let plane = ci + 1;
let dc = dc_pred_16x8(&self.recon[plane], self.w, px, py, self.bd as i32);
cpred[ci] = dc;
let mut resid = [0i32; 128];
for ry in 0..8 {
let srow = &self.src[plane][(py + ry) * self.w + px..];
for cx in 0..16 {
resid[ry * 16 + cx] = srow[cx] - dc;
}
}
let (mut q, qt) = dct16x8_t(&resid, &self.cquant);
trellis_optimize(&mut q, &qt, cdcq, cacq, &SCAN_16X8, lam);
let mean_c = resid.iter().sum::<i32>() / 128;
if q[0] == 0 && mean_c.abs() >= 8 {
q[0] = if mean_c > 0 { 1 } else { -1 };
}
ccf[ci] = q;
}
let luma_zero = lcf.iter().all(|&v| v == 0);
let chroma_zero = ccf[0].iter().all(|&v| v == 0) && ccf[1].iter().all(|&v| v == 0);
let block_skip = luma_zero && chroma_zero;
let sctx = (self.a_skip[bx4] + self.l_skip[by4]) as usize;
self.enc
.encode_symbol(block_skip as usize, &mut self.cdfs.skip[sctx]);
self.code_delta_q_if_armed();
self.record_blk_rect(x8, y8 + half, 4, 2);
self.mark_skip8_rect(x8, y8 + half, 2, 1, block_skip);
let yctx = INTRA_MODE_CTX[self.a_mode[bx4] as usize] * 5
+ INTRA_MODE_CTX[self.l_mode[by4] as usize];
self.enc.encode_symbol(DC_PRED, &mut self.cdfs.kf_y[yctx]);
self.emit_uv_mode(DC_PRED, DC_PRED, None);
let sv = block_skip as u8;
self.a_skip[bx4..bx4 + 4].fill(sv);
self.l_skip[by4..by4 + 2].fill(sv);
self.a_mode[bx4..bx4 + 4].fill(DC_PRED as u8);
self.l_mode[by4..by4 + 2].fill(DC_PRED as u8);
let lres_ctx = if block_skip {
0x40
} else {
let sk = self.skip_ctx_16x8_luma();
let ds = self.dc_sign_ctx_16x8_luma(bx4, by4);
encode_16x8_luma_coeffs(&mut self.enc, &mut self.cdfs, &lcf, sk, ds, DC_PRED, 1)
};
self.a_coef[0][bx4..bx4 + 4].fill(lres_ctx);
self.l_coef[0][by4..by4 + 2].fill(lres_ctx);
let lrr = if block_skip {
[0i32; 128]
} else {
idct_dequant_16x8(&lcf, &self.quant)
};
for ry in 0..8 {
let drow = &mut self.recon[0][(py + ry) * self.w + px..];
for cx in 0..16 {
drow[cx] = (lpred + lrr[ry * 16 + cx]).clamp(0, maxval);
}
}
for ci in 0..2 {
let plane = ci + 1;
let cres_ctx = if block_skip {
0x40
} else {
let sk = self.skip_ctx_16x8_chroma(plane, bx4, by4);
let ds = self.dc_sign_ctx_16x8_chroma(plane, bx4, by4);
encode_16x8_chroma_coeffs(&mut self.enc, &mut self.cdfs, &ccf[ci], sk, ds)
};
self.a_coef[plane][bx4..bx4 + 4].fill(cres_ctx);
self.l_coef[plane][by4..by4 + 2].fill(cres_ctx);
let rr = if block_skip {
[0i32; 128]
} else {
idct_dequant_16x8(&ccf[ci], &self.cquant)
};
for ry in 0..8 {
let drow = &mut self.recon[plane][(py + ry) * self.w + px..];
for cx in 0..16 {
drow[cx] = (cpred[ci] + rr[ry * 16 + cx]).clamp(0, maxval);
}
}
}
}
}
fn code_block16(&mut self, x8: usize, y8: usize, have_tr: bool, have_bl: bool) {
self.record_blk(x8, y8, 4);
let (px, py) = (x8 * 8, y8 * 8);
let (dcq, acq, lam) = (
self.quant.dc_q() as f64,
self.quant.ac_q() as f64,
trellis_lambda(),
);
let dcs16 = self.dc_sign_ctx_16(0, px / 4, py / 4);
let mlam = mode_lambda() * acq * acq;
let prdo = self.perceptual_rd_scale(px, py, 16);
let (lam, mlam) = (lam * prdo, mlam * prdo);
let mut best_mode = DC_PRED;
let mut txtp16: u8 = 0; let mut lpred_arr = [0i32; 256];
let mut lcf = [0i32; 256];
let mut best_eff = f64::INFINITY;
let mut best_dct_sse = 0i64;
let mut best_dct_bits = 0f64;
let mut ltf = [0f64; 256]; let modes = if self.speed.reduced_modes() {
fast_nd_modes()
} else {
nd_modes()
};
for &m in modes {
let mut pred = [0i32; 256];
if m == DC_PRED {
let d = dc_pred_16x16(&self.recon[0], self.w, px, py, self.bd as i32);
pred = [d; 256];
} else {
intra_predict_nd(
m,
&self.recon[0],
self.w,
px,
py,
16,
16,
have_tr,
have_bl,
self.w,
self.h,
&mut pred,
self.bd,
);
}
let mut resid = [0i32; 256];
for (ry, (rrow, prow)) in resid
.as_chunks_mut::<16>()
.0
.iter_mut()
.zip(pred.as_chunks::<16>().0.iter())
.enumerate()
{
let srow = &self.src[0][(py + ry) * self.w + px..];
for (r, (&p, &s)) in rrow.iter_mut().zip(prow.iter().zip(srow.iter())) {
*r = s - p;
}
}
let blk_sse16 = |rr: &[i32; 256]| -> i64 {
let mut sse = 0i64;
for (ry, (prow, rrow)) in pred
.as_chunks::<16>()
.0
.iter()
.zip(rr.as_chunks::<16>().0.iter())
.enumerate()
{
let srow = &self.src[0][(py + ry) * self.w + px..];
for ((&p, &rv), &s) in prow.iter().zip(rrow.iter()).zip(srow.iter()) {
let r = (p + rv).clamp(0, (1 << self.bd) - 1);
let d = s - r;
sse += (d * d) as i64;
}
}
sse
};
let (mut cf, tf) = forward_dct_quant_16x16_t(&resid, &self.quant);
if self.speed.per_candidate_rdoq() {
trellis_optimize_ctx(
&mut cf,
&tf,
dcq,
acq,
&SCAN_16X16,
lam,
16,
&self.cdfs,
2,
0,
&self.cdfs.eob_bin_256_l,
dcs16,
);
}
let sse = blk_sse16(&idct_dequant_16x16(&cf, &self.quant));
let bits = block_rate_bits(&cf, &SCAN_16X16);
let cost = sse as f64 + mlam * (bits + mode_signal_bits(m));
if cost < best_eff {
best_eff = cost;
best_mode = m;
lpred_arr = pred;
lcf = cf;
ltf = tf;
best_dct_sse = sse;
best_dct_bits = bits;
}
}
let mut best_delta: i32 = 0;
if angle_delta_enabled()
&& (D45_PRED..=VERT_LEFT_PRED).contains(&best_mode)
&& best_mode != V_PRED
&& best_mode != H_PRED
{
let ad_cdf = self.cdfs.angle_delta[best_mode - V_PRED].clone();
let mut best_ad_cost =
best_dct_sse as f64 + mlam * (best_dct_bits + cdf_cost(&ad_cdf, 3));
for d in [-3i32, -2, -1, 1, 2, 3] {
let mut pred = [0i32; 256];
intra_predict_nd_ad(
best_mode,
d,
&self.recon[0],
self.w,
px,
py,
16,
16,
have_tr,
have_bl,
self.w,
self.h,
&mut pred,
self.bd,
);
let mut resid = [0i32; 256];
for ry in 0..16 {
let srow = &self.src[0][(py + ry) * self.w + px..];
for rx in 0..16 {
resid[ry * 16 + rx] = srow[rx] - pred[ry * 16 + rx];
}
}
let (mut cf, tf) = forward_dct_quant_16x16_t(&resid, &self.quant);
if self.speed.per_candidate_rdoq() {
trellis_optimize_ctx(
&mut cf,
&tf,
dcq,
acq,
&SCAN_16X16,
lam,
16,
&self.cdfs,
2,
0,
&self.cdfs.eob_bin_256_l,
dcs16,
);
}
let rr = idct_dequant_16x16(&cf, &self.quant);
let mut sse = 0i64;
for ry in 0..16 {
let srow = &self.src[0][(py + ry) * self.w + px..];
for rx in 0..16 {
let r =
(pred[ry * 16 + rx] + rr[ry * 16 + rx]).clamp(0, (1 << self.bd) - 1);
let dd = srow[rx] - r;
sse += (dd * dd) as i64;
}
}
let bits = block_rate_bits(&cf, &SCAN_16X16);
let cost = sse as f64 + mlam * (bits + cdf_cost(&ad_cdf, (d + 3) as usize));
if cost < best_ad_cost {
best_ad_cost = cost;
best_delta = d;
lpred_arr = pred;
lcf = cf;
ltf = tf;
best_dct_sse = sse;
best_dct_bits = bits;
}
}
}
if !self.speed.per_candidate_rdoq() {
trellis_optimize_ctx(
&mut lcf,
<f,
dcq,
acq,
&SCAN_16X16,
lam,
16,
&self.cdfs,
2,
0,
&self.cdfs.eob_bin_256_l,
dcs16,
);
}
if self.speed.try_adst() {
let mut resid = [0i32; 256];
for (ry, rrow) in resid.as_chunks_mut::<16>().0.iter_mut().enumerate() {
let srow = &self.src[0][(py + ry) * self.w + px..];
let prow = &lpred_arr[ry * 16..ry * 16 + 16];
for (r, (&p, &s)) in rrow.iter_mut().zip(prow.iter().zip(srow.iter())) {
*r = s - p;
}
}
let (mut acf, atf) = adst16x16_t(&resid, &self.quant);
trellis_optimize_ctx(
&mut acf,
&atf,
dcq,
acq,
&SCAN_16X16,
lam,
16,
&self.cdfs,
2,
0,
&self.cdfs.eob_bin_256_l,
dcs16,
);
let rr = iadst_dequant_16x16(&acf, &self.quant);
let mut asse = 0i64;
for (ry, rrow) in rr.as_chunks::<16>().0.iter().enumerate() {
let srow = &self.src[0][(py + ry) * self.w + px..];
let prow = &lpred_arr[ry * 16..ry * 16 + 16];
for ((&p, &rv), &s) in prow.iter().zip(rrow.iter()).zip(srow.iter()) {
let r = (p + rv).clamp(0, (1 << self.bd) - 1);
let d = s - r;
asse += (d * d) as i64;
}
}
let abits = block_rate_bits(&acf, &SCAN_16X16);
if asse <= best_dct_sse + (best_dct_sse >> 5)
&& asse as f64 + mlam * abits < best_dct_sse as f64 + mlam * best_dct_bits
{
lcf = acf;
txtp16 = 1;
}
}
if self.speed.try_adst() && asym_adst_enabled() {
let mut best_txtp16_sse = if txtp16 == 1 { i64::MAX } else { best_dct_sse };
let mut best_txtp16_bits = best_dct_bits;
if txtp16 == 1 {
let rr = iadst_dequant_16x16(&lcf, &self.quant);
let mut s = 0i64;
for (ry, rrow) in rr.as_chunks::<16>().0.iter().enumerate() {
let srow = &self.src[0][(py + ry) * self.w + px..];
let prow = &lpred_arr[ry * 16..ry * 16 + 16];
for ((&p, &rv), &sv) in prow.iter().zip(rrow.iter()).zip(srow.iter()) {
let r = (p + rv).clamp(0, (1 << self.bd) - 1);
let d = sv - r;
s += (d * d) as i64;
}
}
best_txtp16_sse = s;
best_txtp16_bits = block_rate_bits(&lcf, &SCAN_16X16);
}
for (fwd_dctadst, inv_dctadst) in [(false, false), (true, true)] {
let mut resid = [0i32; 256];
for (ry, rrow) in resid.as_chunks_mut::<16>().0.iter_mut().enumerate() {
let srow = &self.src[0][(py + ry) * self.w + px..];
let prow = &lpred_arr[ry * 16..ry * 16 + 16];
for (r, (&p, &s)) in rrow.iter_mut().zip(prow.iter().zip(srow.iter())) {
*r = s - p;
}
}
let (mut acf, atf) = if fwd_dctadst {
dctadst16x16_t(&resid, &self.quant)
} else {
adstdct16x16_t(&resid, &self.quant)
};
trellis_optimize_ctx(
&mut acf,
&atf,
dcq,
acq,
&SCAN_16X16,
lam,
16,
&self.cdfs,
2,
0,
&self.cdfs.eob_bin_256_l,
dcs16,
);
let rr = if inv_dctadst {
idctadst_dequant_16x16(&acf, &self.quant)
} else {
iadstdct_dequant_16x16(&acf, &self.quant)
};
let mut asse = 0i64;
for (ry, rrow) in rr.as_chunks::<16>().0.iter().enumerate() {
let srow = &self.src[0][(py + ry) * self.w + px..];
let prow = &lpred_arr[ry * 16..ry * 16 + 16];
for ((&p, &rv), &s) in prow.iter().zip(rrow.iter()).zip(srow.iter()) {
let r = (p + rv).clamp(0, (1 << self.bd) - 1);
let d = s - r;
asse += (d * d) as i64;
}
}
let abits = block_rate_bits(&acf, &SCAN_16X16);
if asse <= best_dct_sse + (best_dct_sse >> 5)
&& asse as f64 + mlam * abits < best_txtp16_sse as f64 + mlam * best_txtp16_bits
{
lcf = acf;
txtp16 = if inv_dctadst { 3 } else { 2 };
best_txtp16_sse = asse;
best_txtp16_bits = abits;
}
}
}
let luma_zero = lcf.iter().all(|&c| c == 0);
if self.ss420 {
self.code_block16_420(
x8, y8, &lcf, &lpred_arr, best_mode, luma_zero, txtp16, best_delta,
);
} else if self.ss422 {
self.code_block16_422(
x8, y8, &lcf, &lpred_arr, best_mode, luma_zero, txtp16, best_delta,
);
} else {
self.code_block16_444(
x8, y8, &lcf, &lpred_arr, best_mode, luma_zero, txtp16, best_delta,
);
}
}
fn emit_uv_mode(&mut self, y_mode: usize, uv_mode: usize, cfl: Option<[i32; 2]>) {
match cfl {
Some(a) => {
let su = if a[0] == 0 {
0
} else if a[0] < 0 {
1
} else {
2
};
let sv = if a[1] == 0 {
0
} else if a[1] < 0 {
1
} else {
2
};
let sign = su * 3 + sv;
if sign == 0 {
self.enc
.encode_symbol(DC_PRED, &mut self.cdfs.uv_mode[13 + y_mode]);
return;
}
self.enc
.encode_symbol(CFL_PRED, &mut self.cdfs.uv_mode[13 + y_mode]);
self.enc.encode_symbol(sign - 1, &mut self.cdfs.cfl_sign);
if su != 0 {
let c = (su == 2) as usize * 3 + sv;
self.enc
.encode_symbol((a[0].abs() - 1) as usize, &mut self.cdfs.cfl_alpha[c]);
}
if sv != 0 {
let c = (sv == 2) as usize * 3 + su;
self.enc
.encode_symbol((a[1].abs() - 1) as usize, &mut self.cdfs.cfl_alpha[c]);
}
}
None => {
self.enc
.encode_symbol(uv_mode, &mut self.cdfs.uv_mode[13 + y_mode]);
if (V_PRED..=VERT_LEFT_PRED).contains(&uv_mode) {
self.enc
.encode_symbol(3, &mut self.cdfs.angle_delta[uv_mode - V_PRED]);
}
}
}
}
#[allow(clippy::too_many_arguments)]
fn code_header_luma16(
&mut self,
x8: usize,
y8: usize,
lcf: &[i32; 256],
lpred: &[i32; 256],
y_mode: usize,
block_skip: bool,
uv_mode: usize,
cfl: Option<[i32; 2]>,
txtp16: u8,
angle_delta: i32,
) {
let (px, py) = (x8 * 8, y8 * 8);
let (bx4, by4) = (px / 4, py / 4);
let sctx = (self.a_skip[bx4] + self.l_skip[by4]) as usize;
self.enc
.encode_symbol(block_skip as usize, &mut self.cdfs.skip[sctx]);
self.code_delta_q_if_armed();
self.mark_skip8(x8, y8, 2, block_skip);
let yctx = INTRA_MODE_CTX[self.a_mode[bx4] as usize] * 5
+ INTRA_MODE_CTX[self.l_mode[by4] as usize];
self.enc.encode_symbol(y_mode, &mut self.cdfs.kf_y[yctx]);
if (V_PRED..=VERT_LEFT_PRED).contains(&y_mode) {
self.enc.encode_symbol(
(angle_delta + 3) as usize,
&mut self.cdfs.angle_delta[y_mode - V_PRED],
);
}
self.emit_uv_mode(y_mode, uv_mode, cfl);
let sv = block_skip as u8;
let mv = y_mode as u8;
self.a_skip[bx4..bx4 + 4].fill(sv);
self.l_skip[by4..by4 + 4].fill(sv);
self.a_mode[bx4..bx4 + 4].fill(mv);
self.l_mode[by4..by4 + 4].fill(mv);
let lres_ctx = if block_skip {
0x40
} else {
let sk = self.skip_ctx_16(0, bx4, by4, false);
let ds = self.dc_sign_ctx_16(0, bx4, by4);
encode_tx16_coeffs_adapt(
&mut self.enc,
&mut self.cdfs,
lcf,
false,
sk,
ds,
y_mode,
match txtp16 {
1 => ADST_ADST_TX16_IDX,
2 => ADST_DCT_TX16_IDX,
3 => DCT_ADST_TX16_IDX,
_ => 1,
},
)
};
self.a_coef[0][bx4..bx4 + 4].fill(lres_ctx);
self.l_coef[0][by4..by4 + 4].fill(lres_ctx);
let lrr = if block_skip {
[0i32; 256]
} else {
match txtp16 {
1 => iadst_dequant_16x16(lcf, &self.quant),
2 => iadstdct_dequant_16x16(lcf, &self.quant),
3 => idctadst_dequant_16x16(lcf, &self.quant),
_ => idct_dequant_16x16(lcf, &self.quant),
}
};
for (ry, (prow, rrow)) in lpred
.as_chunks::<16>()
.0
.iter()
.zip(lrr.as_chunks::<16>().0.iter())
.enumerate()
{
let drow = &mut self.recon[0][(py + ry) * self.w + px..];
for ((dv, &p), &rv) in drow.iter_mut().zip(prow.iter()).zip(rrow.iter()) {
*dv = (p + rv).clamp(0, (1 << self.bd) - 1);
}
}
}
#[allow(clippy::too_many_arguments)]
fn code_block16_444(
&mut self,
x8: usize,
y8: usize,
lcf: &[i32; 256],
lpred: &[i32; 256],
y_mode: usize,
luma_zero: bool,
txtp16: u8,
angle_delta: i32,
) {
let (px, py) = (x8 * 8, y8 * 8);
let (bx4, by4) = (px / 4, py / 4);
let mut ccf = [[0i32; 256]; 2];
let mut cpred = [0i32; 2];
for ci in 0..2 {
let plane = ci + 1;
let pred = dc_pred_16x16(&self.recon[plane], self.w, px, py, self.bd as i32);
cpred[ci] = pred;
let mut resid = [0i32; 256];
for (ry, drow) in resid.as_chunks_mut::<16>().0.iter_mut().enumerate() {
let srow = &self.src[plane][(py + ry) * self.w + px..];
for (dv, &s) in drow.iter_mut().zip(srow.iter()) {
*dv = s - pred;
}
}
let (q, qt) = forward_dct_quant_16x16_t(&resid, &self.cquant);
ccf[ci] = q;
trellis_optimize(
&mut ccf[ci],
&qt,
self.cquant.dc_q() as f64,
self.cquant.ac_q() as f64,
&SCAN_16X16,
trellis_lambda(),
);
let mean_resid_dc = resid.iter().sum::<i32>() / 256;
if ccf[ci][0] == 0 && mean_resid_dc.abs() >= 8 {
ccf[ci][0] = if mean_resid_dc > 0 { 1 } else { -1 };
}
}
let mut cpred16 = [[0i32; 256]; 2];
let mut cfl_opt: Option<[i32; 2]> = None;
{
let lrr_cfl = match txtp16 {
1 => iadst_dequant_16x16(lcf, &self.quant),
2 => iadstdct_dequant_16x16(lcf, &self.quant),
3 => idctadst_dequant_16x16(lcf, &self.quant),
_ => idct_dequant_16x16(lcf, &self.quant),
};
let mut luma_rec = [0i32; 256];
for i in 0..256 {
luma_rec[i] = (lpred[i] + lrr_cfl[i]).clamp(0, (1 << self.bd) - 1);
}
let mut ac = [0i32; 256];
cfl_ac_444(&luma_rec, 16, 16, &mut ac);
let (dcq, acq, lam) = (
self.cquant.dc_q() as f64,
self.cquant.ac_q() as f64,
trellis_lambda(),
);
let mlam = mode_lambda() * acq * acq;
let mut cfl_ccf = [[0i32; 256]; 2];
let mut cfl_a = [0i32; 2];
let (mut dc_sse, mut dc_bits) = ([0i64; 2], [0f64; 2]);
let (mut cfl_sse, mut cfl_bits) = ([0i64; 2], [0f64; 2]);
for ci in 0..2 {
let plane = ci + 1;
let dc = cpred[ci];
let mut src = [0i32; 256];
for (ry, drow) in src.as_chunks_mut::<16>().0.iter_mut().enumerate() {
drow.copy_from_slice(&self.src[plane][(py + ry) * self.w + px..][..16]);
}
let dcrr = idct_dequant_16x16(&ccf[ci], &self.cquant);
let mut s = 0i64;
for i in 0..256 {
let r = (dc + dcrr[i]).clamp(0, (1 << self.bd) - 1);
let d = src[i] - r;
s += (d * d) as i64;
}
dc_sse[ci] = s;
dc_bits[ci] = block_rate_bits(&ccf[ci], &SCAN_16X16);
let a = cfl_best_alpha(&ac, &src, dc, 256, self.bd);
cfl_a[ci] = a;
let mut cpr = [0i32; 256];
let mut resid = [0i32; 256];
for i in 0..256 {
cpr[i] = cfl_pred_pixel(dc, ac[i], a, self.bd);
resid[i] = src[i] - cpr[i];
}
let (mut q, qt) = forward_dct_quant_16x16_t(&resid, &self.cquant);
trellis_optimize(&mut q, &qt, dcq, acq, &SCAN_16X16, lam);
let rr = idct_dequant_16x16(&q, &self.cquant);
let mut s2 = 0i64;
for i in 0..256 {
let r = (cpr[i] + rr[i]).clamp(0, (1 << self.bd) - 1);
let d = src[i] - r;
s2 += (d * d) as i64;
}
cfl_ccf[ci] = q;
cfl_sse[ci] = s2;
cfl_bits[ci] = block_rate_bits(&q, &SCAN_16X16);
cpred16[ci] = cpr;
}
let sig =
4.0 + if cfl_a[0] != 0 { 4.0 } else { 0.0 } + if cfl_a[1] != 0 { 4.0 } else { 0.0 };
let dc_total = (dc_sse[0] + dc_sse[1]) as f64 + mlam * (dc_bits[0] + dc_bits[1]);
let cfl_total =
(cfl_sse[0] + cfl_sse[1]) as f64 + mlam * (cfl_bits[0] + cfl_bits[1] + sig);
if cfl_total < dc_total && (cfl_a[0] != 0 || cfl_a[1] != 0) {
cfl_opt = Some(cfl_a);
ccf[..2].copy_from_slice(&cfl_ccf[..2]);
} else {
for ci in 0..2 {
cpred16[ci] = [cpred[ci]; 256];
}
}
}
let mut chosen_uv_16 = DC_PRED;
{
let (dcq, acq, lam) = (
self.cquant.dc_q() as f64,
self.cquant.ac_q() as f64,
trellis_lambda(),
);
let mlam = mode_lambda() * acq * acq;
let maxv = (1 << self.bd) - 1;
let mut cur_total = 0f64;
if let Some(a) = cfl_opt {
cur_total += mlam
* (4.0 + if a[0] != 0 { 4.0 } else { 0.0 } + if a[1] != 0 { 4.0 } else { 0.0 });
}
for ci in 0..2 {
let plane = ci + 1;
let rr = idct_dequant_16x16(&ccf[ci], &self.cquant);
let mut sse = 0i64;
for (ry, rrow) in rr.as_chunks::<16>().0.iter().enumerate() {
let srow = &self.src[plane][(py + ry) * self.w + px..];
let prow = &cpred16[ci][ry * 16..];
for ((&s, &p), &r) in srow[..16].iter().zip(prow.iter()).zip(rrow.iter()) {
let d = s - (p + r).clamp(0, maxv);
sse += (d * d) as i64;
}
}
cur_total += sse as f64 + mlam * block_rate_bits(&ccf[ci], &SCAN_16X16);
}
let mut best_total = cur_total;
let mut best_mode_uv = DC_PRED;
let mut best_ccf16 = [[0i32; 256]; 2];
let mut best_pred16 = [[0i32; 256]; 2];
for &cand in &[
SMOOTH_V_PRED,
PAETH_PRED,
SMOOTH_PRED,
SMOOTH_H_PRED,
V_PRED,
H_PRED,
D135_PRED,
D113_PRED,
D157_PRED,
] {
if (V_PRED..=VERT_LEFT_PRED).contains(&cand) && !self.speed.try_chroma_directional()
{
continue;
}
let tx = chroma_tx_for_mode(cand);
let mut cand_ccf = [[0i32; 256]; 2];
let mut cand_pred = [[0i32; 256]; 2];
let sig_bits = if (V_PRED..=VERT_LEFT_PRED).contains(&cand) {
7.0
} else {
4.0
};
let mut cand_total = mlam * sig_bits;
for ci in 0..2 {
let plane = ci + 1;
intra_predict_nd(
cand,
&self.recon[plane],
self.w,
px,
py,
16,
16,
false,
false,
self.w,
self.h,
&mut cand_pred[ci],
self.bd,
);
let mut resid = [0i32; 256];
for (ry, drow) in resid.as_chunks_mut::<16>().0.iter_mut().enumerate() {
let srow = &self.src[plane][(py + ry) * self.w + px..];
let prow = &cand_pred[ci][ry * 16..];
for (dv, (&s, &p)) in drow.iter_mut().zip(srow.iter().zip(prow.iter())) {
*dv = s - p;
}
}
let (mut q, qt) = fwd_chroma_16x16(tx, &resid, &self.cquant);
trellis_optimize(&mut q, &qt, dcq, acq, &SCAN_16X16, lam);
let mean_resid = resid.iter().sum::<i32>() / 256;
if q[0] == 0 && mean_resid.abs() >= 8 {
q[0] = if mean_resid > 0 { 1 } else { -1 };
}
cand_ccf[ci] = q;
let rr = inv_chroma_16x16(tx, &q, &self.cquant);
let mut sse = 0i64;
for (ry, rrow) in rr.as_chunks::<16>().0.iter().enumerate() {
let srow = &self.src[plane][(py + ry) * self.w + px..];
let prow = &cand_pred[ci][ry * 16..];
for ((&s, &p), &r) in srow[..16].iter().zip(prow.iter()).zip(rrow.iter()) {
let d = s - (p + r).clamp(0, maxv);
sse += (d * d) as i64;
}
}
cand_total += sse as f64 + mlam * block_rate_bits(&q, &SCAN_16X16);
}
if cand_total < best_total {
best_total = cand_total;
best_mode_uv = cand;
best_ccf16 = cand_ccf;
best_pred16 = cand_pred;
}
}
if best_mode_uv != DC_PRED {
ccf[..2].copy_from_slice(&best_ccf16[..2]);
cpred16[..2].copy_from_slice(&best_pred16[..2]);
cfl_opt = None; chosen_uv_16 = best_mode_uv;
}
} let block_skip =
luma_zero && ccf[0].iter().all(|&c| c == 0) && ccf[1].iter().all(|&c| c == 0);
self.code_header_luma16(
x8,
y8,
lcf,
lpred,
y_mode,
block_skip,
chosen_uv_16,
cfl_opt,
txtp16,
angle_delta,
);
for ci in 0..2 {
let plane = ci + 1;
let res_ctx = if block_skip {
0x40
} else {
let sk = self.skip_ctx_16(plane, bx4, by4, true);
let ds = self.dc_sign_ctx_16(plane, bx4, by4);
encode_tx16_coeffs_adapt(
&mut self.enc,
&mut self.cdfs,
&ccf[ci],
true,
sk,
ds,
0,
1,
)
};
self.a_coef[plane][bx4..bx4 + 4].fill(res_ctx);
self.l_coef[plane][by4..by4 + 4].fill(res_ctx);
let rr = if block_skip {
[0i32; 256]
} else if chosen_uv_16 != DC_PRED {
inv_chroma_16x16(chroma_tx_for_mode(chosen_uv_16), &ccf[ci], &self.cquant)
} else {
idct_dequant_16x16(&ccf[ci], &self.cquant)
};
for (ry, rrow) in rr.as_chunks::<16>().0.iter().enumerate() {
let drow = &mut self.recon[plane][(py + ry) * self.w + px..];
if cfl_opt.is_some() || chosen_uv_16 != DC_PRED {
let prow = &cpred16[ci][ry * 16..];
for ((dv, &rv), &p) in drow.iter_mut().zip(rrow.iter()).zip(prow.iter()) {
*dv = (p + rv).clamp(0, (1 << self.bd) - 1);
}
} else {
for (dv, &rv) in drow.iter_mut().zip(rrow.iter()) {
*dv = (cpred[ci] + rv).clamp(0, (1 << self.bd) - 1);
}
}
}
}
}
#[allow(clippy::too_many_arguments)]
fn code_block16_420(
&mut self,
x8: usize,
y8: usize,
lcf: &[i32; 256],
lpred: &[i32; 256],
y_mode: usize,
luma_zero: bool,
txtp16: u8,
angle_delta: i32,
) {
let (px, py) = (x8 * 8, y8 * 8);
let (cx, cy) = (px / 2, py / 2);
let (bx4c, by4c) = (cx / 4, cy / 4);
let (dcq, acq, lam) = (
self.cquant.dc_q() as f64,
self.cquant.ac_q() as f64,
trellis_lambda(),
);
let maxval = (1 << self.bd) - 1;
let mut ccf_dc = [[0i32; 64]; 2];
let mut dc_preds = [0i32; 2];
for ci in 0..2 {
let plane = ci + 1;
let dc = dc_pred_8x8(&self.recon[plane], self.cw, cx, cy, self.bd as i32);
dc_preds[ci] = dc;
let mut resid = [0i32; 64];
for (ry, drow) in resid.as_chunks_mut::<8>().0.iter_mut().enumerate() {
let srow = &self.src[plane][(cy + ry) * self.cw + cx..];
for (dv, &s) in drow.iter_mut().zip(srow.iter()) {
*dv = s - dc;
}
}
let (q, qt) = forward_dct_quant_8x8_t(&resid, &self.cquant);
ccf_dc[ci] = q;
trellis_optimize(&mut ccf_dc[ci], &qt, dcq, acq, &SCAN_8X8, lam);
let mean_resid_dc = resid.iter().sum::<i32>() / 64;
if ccf_dc[ci][0] == 0 && mean_resid_dc.abs() >= 8 {
ccf_dc[ci][0] = if mean_resid_dc > 0 { 1 } else { -1 };
}
}
let mlam = mode_lambda() * acq * acq;
let mut rr_dc = [[0i32; 64]; 2];
let mut dc_total = 0f64;
for ci in 0..2 {
let plane = ci + 1;
rr_dc[ci] = idct_dequant_8x8(&ccf_dc[ci], &self.cquant);
let dc = dc_preds[ci];
let mut sse = 0i64;
for (ry, rrow) in rr_dc[ci].as_chunks::<8>().0.iter().enumerate() {
let srow = &self.src[plane][(cy + ry) * self.cw + cx..];
for (&s, &r) in srow[..8].iter().zip(rrow.iter()) {
let d = s - (dc + r).clamp(0, maxval);
sse += (d * d) as i64;
}
}
dc_total += sse as f64 + mlam * block_rate_bits(&ccf_dc[ci], &SCAN_8X8);
}
let mut best_total = dc_total;
let mut chosen_uv = DC_PRED;
let mut best_ccf = ccf_dc;
let mut best_rr = rr_dc;
let mut best_pred = [[0i32; 64]; 2];
for &cand in &[
SMOOTH_V_PRED,
PAETH_PRED,
SMOOTH_PRED,
SMOOTH_H_PRED,
V_PRED,
H_PRED,
D135_PRED,
D113_PRED,
D157_PRED,
] {
if (V_PRED..=VERT_LEFT_PRED).contains(&cand) && !self.speed.try_chroma_directional() {
continue;
}
let tx = chroma_tx_for_mode(cand);
let mut cand_ccf = [[0i32; 64]; 2];
let mut cand_rr = [[0i32; 64]; 2];
let mut cand_pred = [[0i32; 64]; 2];
let sig_bits = if (V_PRED..=VERT_LEFT_PRED).contains(&cand) {
7.0
} else {
4.0
};
let mut cand_total = mlam * sig_bits;
for ci in 0..2 {
let plane = ci + 1;
intra_predict_nd(
cand,
&self.recon[plane],
self.cw,
cx,
cy,
8,
8,
false,
false,
self.cw,
self.h,
&mut cand_pred[ci],
self.bd,
);
let mut resid = [0i32; 64];
for (ry, drow) in resid.as_chunks_mut::<8>().0.iter_mut().enumerate() {
let srow = &self.src[plane][(cy + ry) * self.cw + cx..];
let prow = &cand_pred[ci][ry * 8..];
for (dv, (&s, &p)) in drow.iter_mut().zip(srow.iter().zip(prow.iter())) {
*dv = s - p;
}
}
let (mut q, qt) = fwd_chroma_8x8(tx, &resid, &self.cquant);
trellis_optimize(&mut q, &qt, dcq, acq, &SCAN_8X8, lam);
let mean_resid = resid.iter().sum::<i32>() / 64;
if q[0] == 0 && mean_resid.abs() >= 8 {
q[0] = if mean_resid > 0 { 1 } else { -1 };
}
cand_ccf[ci] = q;
cand_rr[ci] = inv_chroma_8x8(tx, &q, &self.cquant);
let mut sse = 0i64;
for (ry, rrow) in cand_rr[ci].as_chunks::<8>().0.iter().enumerate() {
let srow = &self.src[plane][(cy + ry) * self.cw + cx..];
let prow = &cand_pred[ci][ry * 8..];
for ((&s, &p), &r) in srow[..8].iter().zip(prow.iter()).zip(rrow.iter()) {
let d = s - (p + r).clamp(0, maxval);
sse += (d * d) as i64;
}
}
cand_total += sse as f64 + mlam * block_rate_bits(&q, &SCAN_8X8);
}
if cand_total < best_total {
best_total = cand_total;
chosen_uv = cand;
best_ccf = cand_ccf;
best_rr = cand_rr;
best_pred = cand_pred;
}
}
let (ccf, rr_cache) = (best_ccf, best_rr);
let sv_preds = best_pred;
let use_nondc = chosen_uv != DC_PRED;
let block_skip =
luma_zero && ccf[0].iter().all(|&c| c == 0) && ccf[1].iter().all(|&c| c == 0);
self.code_header_luma16(
x8,
y8,
lcf,
lpred,
y_mode,
block_skip,
chosen_uv,
None,
txtp16,
angle_delta,
);
for ci in 0..2 {
let plane = ci + 1;
let res_ctx = if block_skip {
0x40
} else {
let sk = self.skip_ctx(plane, bx4c, by4c, true);
let ds = self.dc_sign_ctx(plane, bx4c, by4c);
encode_tx8_coeffs_adapt(&mut self.enc, &mut self.cdfs, &ccf[ci], true, sk, ds, 0, 1)
};
self.a_coef[plane][bx4c..bx4c + 2].fill(res_ctx);
self.l_coef[plane][by4c..by4c + 2].fill(res_ctx);
let rr = if block_skip { [0i32; 64] } else { rr_cache[ci] };
for (ry, rrow) in rr.as_chunks::<8>().0.iter().enumerate() {
let drow = &mut self.recon[plane][(cy + ry) * self.cw + cx..];
if use_nondc {
let prow = &sv_preds[ci][ry * 8..];
for ((dv, &rv), &prow) in
drow[..8].iter_mut().zip(rrow.iter()).zip(prow[..8].iter())
{
*dv = (prow + rv).clamp(0, maxval);
}
} else {
let dc = dc_preds[ci];
for (dv, &rv) in drow[..8].iter_mut().zip(rrow.iter()) {
*dv = (dc + rv).clamp(0, maxval);
}
}
}
}
}
#[allow(clippy::too_many_arguments)]
fn code_block16_422(
&mut self,
x8: usize,
y8: usize,
lcf: &[i32; 256],
lpred: &[i32; 256],
y_mode: usize,
luma_zero: bool,
txtp16: u8,
angle_delta: i32,
) {
let (px, py) = (x8 * 8, y8 * 8);
let cx = px / 2;
let (bx4c, by4c) = (cx / 4, py / 4);
let maxv = (1 << self.bd) - 1;
let (dcq, acq, lam) = (
self.cquant.dc_q() as f64,
self.cquant.ac_q() as f64,
trellis_lambda(),
);
let mlam = mode_lambda() * acq * acq;
let mut ccf = [[0i32; 128]; 2];
let mut cpred = [0i32; 2];
let mut cpred_px = [[0i32; 128]; 2];
let mut src_planes = [[0i32; 128]; 2];
let mut dc_ccf = [[0i32; 128]; 2];
let mut dc_sse = [0i64; 2];
let mut dc_bits = [0f64; 2];
for ci in 0..2 {
let plane = ci + 1;
let pred = dc_pred_8x16(&self.recon[plane], self.cw, cx, py, self.bd as i32);
cpred[ci] = pred;
let mut src = [0i32; 128];
let mut resid = [0i32; 128];
for (ry, (drow, srow_dst)) in resid
.as_chunks_mut::<8>()
.0
.iter_mut()
.zip(src.as_chunks_mut::<8>().0.iter_mut())
.enumerate()
{
let srow = &self.src[plane][(py + ry) * self.cw + cx..];
for ((dv, sd), &s) in drow.iter_mut().zip(srow_dst.iter_mut()).zip(srow.iter()) {
*dv = s - pred;
*sd = s;
}
}
src_planes[ci] = src;
let (mut q, qt) = forward_dct_quant_8x16_t(&resid, &self.cquant);
trellis_optimize(&mut q, &qt, dcq, acq, &SCAN_8X16, lam);
let rr = idct_dequant_8x16(&q, &self.cquant);
let mut sse = 0i64;
for i in 0..128 {
let r = (pred + rr[i]).clamp(0, maxv);
let d = src[i] - r;
sse += (d * d) as i64;
}
dc_ccf[ci] = q;
dc_sse[ci] = sse;
dc_bits[ci] = block_rate_bits(&q, &SCAN_8X16);
}
let mut use_cfl = false;
let mut cfl_alpha_uv = [0i32; 2];
{
let lrr_cfl = match txtp16 {
1 => iadst_dequant_16x16(lcf, &self.quant),
2 => iadstdct_dequant_16x16(lcf, &self.quant),
3 => idctadst_dequant_16x16(lcf, &self.quant),
_ => idct_dequant_16x16(lcf, &self.quant),
};
let mut luma_rec = [0i32; 256];
for i in 0..256 {
luma_rec[i] = (lpred[i] + lrr_cfl[i]).clamp(0, maxv);
}
let mut ac = [0i32; 128];
cfl_ac_sub(&luma_rec, 16, 8, 16, true, false, &mut ac);
let mut cfl_ccf = [[0i32; 128]; 2];
let mut cfl_a = [0i32; 2];
let mut cfl_sse = [0i64; 2];
let mut cfl_bits = [0f64; 2];
for ci in 0..2 {
let dc = cpred[ci];
let src = src_planes[ci];
let a = cfl_best_alpha(&ac, &src, dc, 128, self.bd);
cfl_a[ci] = a;
let mut cpr = [0i32; 128];
let mut resid = [0i32; 128];
for i in 0..128 {
cpr[i] = cfl_pred_pixel(dc, ac[i], a, self.bd);
resid[i] = src[i] - cpr[i];
}
let (mut q, qt) = forward_dct_quant_8x16_t(&resid, &self.cquant);
trellis_optimize(&mut q, &qt, dcq, acq, &SCAN_8X16, lam);
let rr = idct_dequant_8x16(&q, &self.cquant);
let mut sse = 0i64;
for i in 0..128 {
let r = (cpr[i] + rr[i]).clamp(0, maxv);
let d = src[i] - r;
sse += (d * d) as i64;
}
cfl_ccf[ci] = q;
cfl_sse[ci] = sse;
cfl_bits[ci] = block_rate_bits(&q, &SCAN_8X16);
cpred_px[ci] = cpr;
}
let sig =
4.0 + if cfl_a[0] != 0 { 4.0 } else { 0.0 } + if cfl_a[1] != 0 { 4.0 } else { 0.0 };
let dc_total = (dc_sse[0] + dc_sse[1]) as f64 + mlam * (dc_bits[0] + dc_bits[1]);
let cfl_total =
(cfl_sse[0] + cfl_sse[1]) as f64 + mlam * (cfl_bits[0] + cfl_bits[1] + sig);
if cfl_total < dc_total && (cfl_a[0] != 0 || cfl_a[1] != 0) {
use_cfl = true;
cfl_alpha_uv = cfl_a;
ccf[..2].copy_from_slice(&cfl_ccf[..2]);
}
}
if !use_cfl {
for ci in 0..2 {
ccf[ci] = dc_ccf[ci];
cpred_px[ci] = [cpred[ci]; 128];
}
}
let block_skip =
luma_zero && ccf[0].iter().all(|&c| c == 0) && ccf[1].iter().all(|&c| c == 0);
self.code_header_luma16(
x8,
y8,
lcf,
lpred,
y_mode,
block_skip,
if use_cfl { CFL_PRED } else { DC_PRED },
if use_cfl { Some(cfl_alpha_uv) } else { None },
txtp16,
angle_delta,
);
for ci in 0..2 {
let plane = ci + 1;
let res_ctx = if block_skip {
0x40
} else {
let sk = self.skip_ctx_8x16_422(plane, bx4c, by4c);
let ds = self.dc_sign_ctx_8x16_422(plane, bx4c, by4c);
encode_8x16_chroma_coeffs(&mut self.enc, &mut self.cdfs, &ccf[ci], sk, ds)
};
self.a_coef[plane][bx4c..bx4c + 2].fill(res_ctx);
self.l_coef[plane][by4c..by4c + 4].fill(res_ctx);
let rr = if block_skip {
[0i32; 128]
} else {
idct_dequant_8x16(&ccf[ci], &self.cquant)
};
for (ry, rrow) in rr.as_chunks::<8>().0.iter().enumerate() {
let drow = &mut self.recon[plane][(py + ry) * self.cw + cx..];
let prow = &cpred_px[ci][ry * 8..];
for ((dv, &rv), &p) in drow.iter_mut().zip(rrow.iter()).zip(prow.iter()) {
*dv = (p + rv).clamp(0, maxv);
}
}
}
}
fn code_block_split4_dc(&mut self, x8: usize, y8: usize) {
let (px, py) = (x8 * 8, y8 * 8);
let maxv = (1 << self.bd) - 1;
let (dcq, acq) = (self.quant.dc_q() as f64, self.quant.ac_q() as f64);
let lam = trellis_lambda();
let nc4 = self.w / 4;
for uy in 0..2 {
for ux in 0..2 {
self.blk4[(y8 * 2 + uy) * nc4 + (x8 * 2 + ux)] = 1;
self.blk4h[(y8 * 2 + uy) * nc4 + (x8 * 2 + ux)] = 1;
}
}
let ss420 = self.ss420;
let (cx420, cy420) = (px / 2, py / 2);
let sub = [(0usize, 0usize), (1, 0), (0, 1), (1, 1)];
for (si, &(sx, sy)) in sub.iter().enumerate() {
let (bx, by) = (px + sx * 4, py + sy * 4);
let (bx4, by4) = (bx / 4, by / 4);
let has_chroma = !self.mono && (!ss420 || si == 3);
let (chx, chy) = if ss420 { (cx420, cy420) } else { (bx, by) };
let cstride = self.cw;
let mlam = mode_lambda() * acq * acq;
let modes = fast_nd_modes();
let mut best_mode = DC_PRED;
let mut lpred = [0i32; 16];
let mut lcf = [0i32; 16];
let mut best_eff = f64::INFINITY;
for &m in modes {
let mut pred = [0i32; 16];
if m == DC_PRED {
let d = dc_pred_4x4(&self.recon[0], self.w, bx, by, self.bd as i32);
pred = [d; 16];
} else {
intra_predict_nd(
m,
&self.recon[0],
self.w,
bx,
by,
4,
4,
false,
false,
self.w,
self.h,
&mut pred,
self.bd,
);
}
let mut resid = [0i32; 16];
for ry in 0..4 {
let srow = &self.src[0][(by + ry) * self.w + bx..];
for rx in 0..4 {
resid[ry * 4 + rx] = srow[rx] - pred[ry * 4 + rx];
}
}
let (mut cf, tf) = forward_dct_quant_4x4_t(&resid, &self.quant);
trellis_optimize(&mut cf, &tf, dcq, acq, &SCAN_4X4, lam);
let rr = idct_dequant_4x4(&cf, &self.quant);
let mut sse = 0i64;
for ry in 0..4 {
let srow = &self.src[0][(by + ry) * self.w + bx..];
for rx in 0..4 {
let r = (pred[ry * 4 + rx] + rr[ry * 4 + rx]).clamp(0, maxv);
let d = srow[rx] - r;
sse += (d * d) as i64;
}
}
let bits = block_rate_bits(&cf, &SCAN_4X4);
let eff = sse as f64 + mlam * bits;
if eff < best_eff {
best_eff = eff;
best_mode = m;
lpred = pred;
lcf = cf;
}
}
let luma_zero = lcf.iter().all(|&c| c == 0);
let mut ccf = [[0i32; 16]; 2];
let mut cpred = [0i32; 2]; let mut cpred_px = [[0i32; 16]; 2];
let mut chroma_zero = true;
let mut use_cfl = false;
let mut cfl_alpha_uv = [0i32; 2];
if has_chroma {
let (cdcq, cacq) = (self.cquant.dc_q() as f64, self.cquant.ac_q() as f64);
let mut dc_ccf = [[0i32; 16]; 2];
let mut dc_sse = [0i64; 2];
let mut dc_bits = [0f64; 2];
let mut src_planes = [[0i32; 16]; 2];
for ci in 0..2 {
let plane = ci + 1;
let dc = dc_pred_4x4(&self.recon[plane], cstride, chx, chy, self.bd as i32);
cpred[ci] = dc;
let mut src = [0i32; 16];
for ry in 0..4 {
let srow = &self.src[plane][(chy + ry) * cstride + chx..];
for rx in 0..4 {
src[ry * 4 + rx] = srow[rx];
}
}
src_planes[ci] = src;
let mut cres = [0i32; 16];
for i in 0..16 {
cres[i] = src[i] - dc;
}
let (mut q, qt) = forward_dct_quant_4x4_t(&cres, &self.cquant);
trellis_optimize(&mut q, &qt, cdcq, cacq, &SCAN_4X4, lam);
let rr = idct_dequant_4x4(&q, &self.cquant);
let mut sse = 0i64;
for i in 0..16 {
let r = (dc + rr[i]).clamp(0, maxv);
let d = src[i] - r;
sse += (d * d) as i64;
}
dc_ccf[ci] = q;
dc_sse[ci] = sse;
dc_bits[ci] = block_rate_bits(&q, &SCAN_4X4);
}
let cfl_eligible = !ss420;
let mut cfl_ccf = [[0i32; 16]; 2];
let mut cfl_a = [0i32; 2];
let mut cfl_px = [[0i32; 16]; 2];
let mut cfl_sse = [0i64; 2];
let mut cfl_bits = [0f64; 2];
if cfl_eligible {
let lrr_cfl = idct_dequant_4x4(&lcf, &self.quant);
let mut luma_rec = [0i32; 16];
for i in 0..16 {
luma_rec[i] = (lpred[i] + lrr_cfl[i]).clamp(0, maxv);
}
let mut ac = [0i32; 16];
cfl_ac_444(&luma_rec, 4, 4, &mut ac);
for ci in 0..2 {
let dc = cpred[ci];
let src = src_planes[ci];
let a = cfl_best_alpha(&ac, &src, dc, 16, self.bd);
cfl_a[ci] = a;
let mut cpr = [0i32; 16];
let mut resid = [0i32; 16];
for i in 0..16 {
cpr[i] = cfl_pred_pixel(dc, ac[i], a, self.bd);
resid[i] = src[i] - cpr[i];
}
let (mut q, qt) = forward_dct_quant_4x4_t(&resid, &self.cquant);
trellis_optimize(&mut q, &qt, cdcq, cacq, &SCAN_4X4, lam);
let rr = idct_dequant_4x4(&q, &self.cquant);
let mut sse = 0i64;
for i in 0..16 {
let r = (cpr[i] + rr[i]).clamp(0, maxv);
let d = src[i] - r;
sse += (d * d) as i64;
}
cfl_ccf[ci] = q;
cfl_a[ci] = a;
cfl_px[ci] = cpr;
cfl_sse[ci] = sse;
cfl_bits[ci] = block_rate_bits(&q, &SCAN_4X4);
}
}
let sig = 4.0
+ if cfl_a[0] != 0 { 4.0 } else { 0.0 }
+ if cfl_a[1] != 0 { 4.0 } else { 0.0 };
let dc_total = (dc_sse[0] + dc_sse[1]) as f64 + mlam * (dc_bits[0] + dc_bits[1]);
let cfl_total =
(cfl_sse[0] + cfl_sse[1]) as f64 + mlam * (cfl_bits[0] + cfl_bits[1] + sig);
if cfl_eligible && cfl_total < dc_total && (cfl_a[0] != 0 || cfl_a[1] != 0) {
use_cfl = true;
cfl_alpha_uv = cfl_a;
for ci in 0..2 {
ccf[ci] = cfl_ccf[ci];
cpred_px[ci] = cfl_px[ci];
if !cfl_ccf[ci].iter().all(|&c| c == 0) {
chroma_zero = false;
}
}
} else {
for ci in 0..2 {
ccf[ci] = dc_ccf[ci];
cpred_px[ci] = [cpred[ci]; 16];
if !dc_ccf[ci].iter().all(|&c| c == 0) {
chroma_zero = false;
}
}
}
}
let block_skip = if has_chroma {
luma_zero && chroma_zero
} else {
luma_zero
};
let sctx = (self.a_skip[bx4] + self.l_skip[by4]) as usize;
self.enc
.encode_symbol(block_skip as usize, &mut self.cdfs.skip[sctx]);
self.code_delta_q_if_armed();
let yctx = INTRA_MODE_CTX[self.a_mode[bx4] as usize] * 5
+ INTRA_MODE_CTX[self.l_mode[by4] as usize];
self.enc.encode_symbol(best_mode, &mut self.cdfs.kf_y[yctx]);
if has_chroma {
if use_cfl {
self.emit_uv_mode(best_mode, CFL_PRED, Some(cfl_alpha_uv));
} else {
self.emit_uv_mode(best_mode, DC_PRED, None);
}
}
let lres_ctx = if block_skip {
0x40
} else {
let ds = self.dc_sign_ctx_420(0, bx4, by4);
encode_tx4_luma_coeffs_adapt(
&mut self.enc,
&mut self.cdfs,
&lcf,
0, ds,
best_mode,
1, )
};
self.a_coef[0][bx4] = lres_ctx;
self.l_coef[0][by4] = lres_ctx;
let lrr = if block_skip {
[0i32; 16]
} else {
idct_dequant_4x4(&lcf, &self.quant)
};
for ry in 0..4 {
let drow = &mut self.recon[0][(by + ry) * self.w + bx..];
for rx in 0..4 {
drow[rx] = (lpred[ry * 4 + rx] + lrr[ry * 4 + rx]).clamp(0, maxv);
}
}
if has_chroma {
let (bx4c, by4c) = (chx / 4, chy / 4);
for ci in 0..2 {
let plane = ci + 1;
let res_ctx = if block_skip {
0x40
} else {
let sk = self.skip_ctx_420(plane, bx4c, by4c);
let ds = self.dc_sign_ctx_420(plane, bx4c, by4c);
encode_4x4_chroma_coeffs(&mut self.enc, &mut self.cdfs, &ccf[ci], sk, ds)
};
self.a_coef[plane][bx4c] = res_ctx;
self.l_coef[plane][by4c] = res_ctx;
let rr = if block_skip {
[0i32; 16]
} else {
idct_dequant_4x4(&ccf[ci], &self.cquant)
};
for ry in 0..4 {
let drow = &mut self.recon[plane][(chy + ry) * cstride + chx..];
for rx in 0..4 {
drow[rx] = (cpred_px[ci][ry * 4 + rx] + rr[ry * 4 + rx]).clamp(0, maxv);
}
}
}
}
self.a_skip[bx4] = block_skip as u8;
self.l_skip[by4] = block_skip as u8;
self.a_mode[bx4] = best_mode as u8;
self.l_mode[by4] = best_mode as u8;
}
}
fn code_block(&mut self, x8: usize, y8: usize, have_tr: bool, have_bl: bool) {
self.record_blk(x8, y8, 2);
let (px, py) = (x8 * 8, y8 * 8);
let (bx4, by4) = (px / 4, py / 4);
let cx = px / 2;
let (dcq, acq, lam) = (
self.quant.dc_q() as f64,
self.quant.ac_q() as f64,
trellis_lambda(),
);
let mlam = mode_lambda() * acq * acq;
let prdo = self.perceptual_rd_scale(px, py, 8);
let (lam, mlam) = (lam * prdo, mlam * prdo);
let mut best_mode = DC_PRED;
let mut best_is_adst = false;
let mut best_is_idtx = false;
let mut best_is_adstdct = false;
let mut best_is_dctadst = false;
let mut lpred_arr = [0i32; 64];
let mut lcf = [0i32; 64];
let mut best_eff = f64::INFINITY;
let mut best_dct_sse = 0i64;
let mut best_dct_bits = 0f64;
let dc_sgn = self.dc_sign_ctx(0, px / 4, py / 4);
let mut ltf = [0f64; 64]; let modes = if self.speed.reduced_modes() {
fast_nd_modes()
} else {
nd_modes()
};
for &m in modes {
let mut pred = [0i32; 64];
if m == DC_PRED {
let d = dc_pred_8x8(&self.recon[0], self.w, px, py, self.bd as i32);
pred = [d; 64];
} else {
intra_predict_nd(
m,
&self.recon[0],
self.w,
px,
py,
8,
8,
have_tr,
have_bl,
self.w,
self.h,
&mut pred,
self.bd,
);
}
let mut resid = [0i32; 64];
for (ry, (rrow, prow)) in resid
.as_chunks_mut::<8>()
.0
.iter_mut()
.zip(pred.as_chunks::<8>().0.iter())
.enumerate()
{
let srow = &self.src[0][(py + ry) * self.w + px..];
for (r, (&p, &s)) in rrow.iter_mut().zip(prow.iter().zip(srow.iter())) {
*r = s - p;
}
}
let blk_sse = |rr: &[i32; 64]| -> i64 {
let mut sse = 0i64;
for (ry, (prow, rrow)) in pred
.as_chunks::<8>()
.0
.iter()
.zip(rr.as_chunks::<8>().0.iter())
.enumerate()
{
let srow = &self.src[0][(py + ry) * self.w + px..];
for ((&p, &rv), &s) in prow.iter().zip(rrow.iter()).zip(srow.iter()) {
let r = (p + rv).clamp(0, (1 << self.bd) - 1);
let d = s - r;
sse += (d * d) as i64;
}
}
sse
};
let (mut cf, tf) = forward_dct_quant_8x8_t(&resid, &self.quant);
if self.speed.per_candidate_rdoq() {
trellis_optimize_ctx(
&mut cf,
&tf,
dcq,
acq,
&SCAN_8X8,
lam,
8,
&self.cdfs,
1,
0,
&self.cdfs.eob_bin_64_l,
dc_sgn,
);
}
let sse = blk_sse(&idct_dequant_8x8(&cf, &self.quant));
let bits = block_rate_bits(&cf, &SCAN_8X8);
let cost = sse as f64 + mlam * (bits + mode_signal_bits(m));
if cost < best_eff {
best_eff = cost;
best_mode = m;
lpred_arr = pred;
lcf = cf;
ltf = tf;
best_dct_sse = sse;
best_dct_bits = bits;
}
}
let mut best_delta: i32 = 0;
if angle_delta_enabled()
&& (D45_PRED..=VERT_LEFT_PRED).contains(&best_mode)
&& best_mode != V_PRED
&& best_mode != H_PRED
{
let ad_cdf = self.cdfs.angle_delta[best_mode - V_PRED].clone();
let mut best_ad_cost =
best_dct_sse as f64 + mlam * (best_dct_bits + cdf_cost(&ad_cdf, 3));
for d in [-3i32, -2, -1, 1, 2, 3] {
let mut pred = [0i32; 64];
intra_predict_nd_ad(
best_mode,
d,
&self.recon[0],
self.w,
px,
py,
8,
8,
have_tr,
have_bl,
self.w,
self.h,
&mut pred,
self.bd,
);
let mut resid = [0i32; 64];
for ry in 0..8 {
let srow = &self.src[0][(py + ry) * self.w + px..];
for rx in 0..8 {
resid[ry * 8 + rx] = srow[rx] - pred[ry * 8 + rx];
}
}
let (mut cf, tf) = forward_dct_quant_8x8_t(&resid, &self.quant);
if self.speed.per_candidate_rdoq() {
trellis_optimize_ctx(
&mut cf,
&tf,
dcq,
acq,
&SCAN_8X8,
lam,
8,
&self.cdfs,
1,
0,
&self.cdfs.eob_bin_64_l,
dc_sgn,
);
}
let rr = idct_dequant_8x8(&cf, &self.quant);
let mut sse = 0i64;
for ry in 0..8 {
let srow = &self.src[0][(py + ry) * self.w + px..];
for rx in 0..8 {
let r = (pred[ry * 8 + rx] + rr[ry * 8 + rx]).clamp(0, (1 << self.bd) - 1);
let dd = srow[rx] - r;
sse += (dd * dd) as i64;
}
}
let bits = block_rate_bits(&cf, &SCAN_8X8);
let cost = sse as f64 + mlam * (bits + cdf_cost(&ad_cdf, (d + 3) as usize));
if cost < best_ad_cost {
best_ad_cost = cost;
best_delta = d;
lpred_arr = pred;
lcf = cf;
ltf = tf;
best_dct_sse = sse;
best_dct_bits = bits;
}
}
}
if !self.speed.per_candidate_rdoq() {
trellis_optimize_ctx(
&mut lcf,
<f,
dcq,
acq,
&SCAN_8X8,
lam,
8,
&self.cdfs,
1,
0,
&self.cdfs.eob_bin_64_l,
dc_sgn,
);
}
let mut best_txtp_sse = best_dct_sse;
let mut best_txtp_bits = best_dct_bits;
if self.speed.try_adst() {
let mut resid = [0i32; 64];
for (ry, rrow) in resid.as_chunks_mut::<8>().0.iter_mut().enumerate() {
let srow = &self.src[0][(py + ry) * self.w + px..];
let prow = &lpred_arr[ry * 8..ry * 8 + 8];
for (r, (&p, &s)) in rrow.iter_mut().zip(prow.iter().zip(srow.iter())) {
*r = s - p;
}
}
let (mut acf, atf) = adst8x8_t(&resid, &self.quant);
trellis_optimize_ctx(
&mut acf,
&atf,
dcq,
acq,
&SCAN_8X8,
lam,
8,
&self.cdfs,
1,
0,
&self.cdfs.eob_bin_64_l,
dc_sgn,
);
let rr = iadst_dequant_8x8(&acf, &self.quant);
let mut asse = 0i64;
for (ry, rrow) in rr.as_chunks::<8>().0.iter().enumerate() {
let srow = &self.src[0][(py + ry) * self.w + px..];
let prow = &lpred_arr[ry * 8..ry * 8 + 8];
for ((&p, &rv), &s) in prow.iter().zip(rrow.iter()).zip(srow.iter()) {
let r = (p + rv).clamp(0, (1 << self.bd) - 1);
let d = s - r;
asse += (d * d) as i64;
}
}
let abits = block_rate_bits(&acf, &SCAN_8X8);
if asse <= best_dct_sse + (best_dct_sse >> 5)
&& asse as f64 + mlam * abits < best_dct_sse as f64 + mlam * best_dct_bits
{
lcf = acf;
best_is_adst = true;
best_txtp_sse = asse;
best_txtp_bits = abits;
}
}
if self.speed.try_adst() && asym_adst_enabled() {
for (fwd_t, inv_is_dctadst) in [(false, false), (true, true)] {
let mut resid = [0i32; 64];
for (ry, rrow) in resid.as_chunks_mut::<8>().0.iter_mut().enumerate() {
let srow = &self.src[0][(py + ry) * self.w + px..];
let prow = &lpred_arr[ry * 8..ry * 8 + 8];
for (r, (&p, &s)) in rrow.iter_mut().zip(prow.iter().zip(srow.iter())) {
*r = s - p;
}
}
let (mut acf, atf) = if fwd_t {
dctadst8x8_t(&resid, &self.quant)
} else {
adstdct8x8_t(&resid, &self.quant)
};
trellis_optimize_ctx(
&mut acf,
&atf,
dcq,
acq,
&SCAN_8X8,
lam,
8,
&self.cdfs,
1,
0,
&self.cdfs.eob_bin_64_l,
dc_sgn,
);
let rr = if inv_is_dctadst {
idctadst_dequant_8x8(&acf, &self.quant)
} else {
iadstdct_dequant_8x8(&acf, &self.quant)
};
let mut asse = 0i64;
for (ry, rrow) in rr.as_chunks::<8>().0.iter().enumerate() {
let srow = &self.src[0][(py + ry) * self.w + px..];
let prow = &lpred_arr[ry * 8..ry * 8 + 8];
for ((&p, &rv), &s) in prow.iter().zip(rrow.iter()).zip(srow.iter()) {
let r = (p + rv).clamp(0, (1 << self.bd) - 1);
let d = s - r;
asse += (d * d) as i64;
}
}
let abits = block_rate_bits(&acf, &SCAN_8X8);
if asse <= best_dct_sse + (best_dct_sse >> 5)
&& asse as f64 + mlam * abits < best_txtp_sse as f64 + mlam * best_txtp_bits
{
lcf = acf;
best_is_adst = false;
best_is_idtx = false;
best_is_adstdct = !inv_is_dctadst;
best_is_dctadst = inv_is_dctadst;
best_txtp_sse = asse;
best_txtp_bits = abits;
}
}
}
if self.speed.try_adst() {
let mut resid = [0i32; 64];
for (ry, rrow) in resid.as_chunks_mut::<8>().0.iter_mut().enumerate() {
let srow = &self.src[0][(py + ry) * self.w + px..];
let prow = &lpred_arr[ry * 8..ry * 8 + 8];
for (r, (&p, &s)) in rrow.iter_mut().zip(prow.iter().zip(srow.iter())) {
*r = s - p;
}
}
let (icf, _itf) = fidentity8x8_t(&resid, &self.quant);
let rr = iidentity_dequant_8x8(&icf, &self.quant);
let mut isse = 0i64;
for (ry, rrow) in rr.as_chunks::<8>().0.iter().enumerate() {
let srow = &self.src[0][(py + ry) * self.w + px..];
let prow = &lpred_arr[ry * 8..ry * 8 + 8];
for ((&p, &rv), &s) in prow.iter().zip(rrow.iter()).zip(srow.iter()) {
let r = (p + rv).clamp(0, (1 << self.bd) - 1);
let d = s - r;
isse += (d * d) as i64;
}
}
let ibits = block_rate_bits(&icf, &SCAN_8X8);
if isse <= best_txtp_sse + (best_txtp_sse >> 5)
&& isse as f64 + mlam * ibits < best_txtp_sse as f64 + mlam * best_txtp_bits
{
lcf = icf;
best_is_adst = false;
best_is_idtx = true;
best_is_adstdct = false;
best_is_dctadst = false;
}
}
let mut ccf8 = [[0i32; 64]; 2];
let mut ccf48 = [[0i32; 32]; 2];
let mut ccf44 = [[0i32; 16]; 2];
let mut cpred = [0i32; 2];
let cy = py / 2; for ci in 0..(if self.mono { 0 } else { 2 }) {
let plane = ci + 1;
if self.ss420 {
let pred = dc_pred_4x4(&self.recon[plane], self.cw, cx, cy, self.bd as i32);
cpred[ci] = pred;
let mut resid = [0i32; 16];
for (ry, drow) in resid.as_chunks_mut::<4>().0.iter_mut().enumerate() {
let srow = &self.src[plane][(cy + ry) * self.cw + cx..];
for (dv, &s) in drow.iter_mut().zip(srow.iter()) {
*dv = s - pred;
}
}
let (q, qt) = forward_dct_quant_4x4_t(&resid, &self.cquant);
ccf44[ci] = q;
trellis_optimize(&mut ccf44[ci], &qt, dcq, acq, &SCAN_4X4, lam);
} else if self.ss422 {
let pred = dc_pred_4x8(&self.recon[plane], self.cw, cx, py, self.bd as i32);
cpred[ci] = pred;
let mut resid = [0i32; 32];
for (ry, drow) in resid.as_chunks_mut::<4>().0.iter_mut().enumerate() {
let srow = &self.src[plane][(py + ry) * self.cw + cx..];
for (dv, &s) in drow.iter_mut().zip(srow.iter()) {
*dv = s - pred;
}
}
let (q, qt) = forward_dct_quant_4x8_t(&resid, &self.cquant);
ccf48[ci] = q;
trellis_optimize(&mut ccf48[ci], &qt, dcq, acq, &SCAN_4X8, lam);
} else {
let pred = dc_pred_8x8(&self.recon[plane], self.w, px, py, self.bd as i32);
cpred[ci] = pred;
let mut resid = [0i32; 64];
for (ry, drow) in resid.as_chunks_mut::<8>().0.iter_mut().enumerate() {
let srow = &self.src[plane][(py + ry) * self.w + px..];
for (dv, &s) in drow.iter_mut().zip(srow.iter()) {
*dv = s - pred;
}
}
let (q, qt) = forward_dct_quant_8x8_t(&resid, &self.cquant);
ccf8[ci] = q;
trellis_optimize(&mut ccf8[ci], &qt, dcq, acq, &SCAN_8X8, lam);
}
}
let mut cpred444 = [[0i32; 64]; 2];
let mut cpred420 = [[0i32; 16]; 2];
let mut cpred422 = [[0i32; 32]; 2];
let mut use_cfl = false;
let mut cfl_alpha_uv = [0i32; 2];
if !self.mono && !self.ss420 && !self.ss422 {
let lrr_cfl = if best_is_idtx {
iidentity_dequant_8x8(&lcf, &self.quant)
} else if best_is_adst {
iadst_dequant_8x8(&lcf, &self.quant)
} else if best_is_adstdct {
iadstdct_dequant_8x8(&lcf, &self.quant)
} else if best_is_dctadst {
idctadst_dequant_8x8(&lcf, &self.quant)
} else {
idct_dequant_8x8(&lcf, &self.quant)
};
let mut luma_rec = [0i32; 64];
for i in 0..64 {
luma_rec[i] = (lpred_arr[i] + lrr_cfl[i]).clamp(0, (1 << self.bd) - 1);
}
let mut ac = [0i32; 64];
cfl_ac_444(&luma_rec, 8, 8, &mut ac);
let mut cfl_ccf = [[0i32; 64]; 2];
let mut cfl_a = [0i32; 2];
let (mut dc_sse, mut dc_bits) = ([0i64; 2], [0f64; 2]);
let (mut cfl_sse, mut cfl_bits) = ([0i64; 2], [0f64; 2]);
for ci in 0..2 {
let plane = ci + 1;
let dc = cpred[ci];
let mut src = [0i32; 64];
for (ry, drow) in src.as_chunks_mut::<8>().0.iter_mut().enumerate() {
drow.copy_from_slice(&self.src[plane][(py + ry) * self.w + px..][..8]);
}
let dcrr = idct_dequant_8x8(&ccf8[ci], &self.cquant);
let mut s = 0i64;
for i in 0..64 {
let r = (dc + dcrr[i]).clamp(0, (1 << self.bd) - 1);
let d = src[i] - r;
s += (d * d) as i64;
}
dc_sse[ci] = s;
dc_bits[ci] = block_rate_bits(&ccf8[ci], &SCAN_8X8);
let a = cfl_best_alpha(&ac, &src, dc, 64, self.bd);
cfl_a[ci] = a;
let mut cpr = [0i32; 64];
let mut resid = [0i32; 64];
for i in 0..64 {
cpr[i] = cfl_pred_pixel(dc, ac[i], a, self.bd);
resid[i] = src[i] - cpr[i];
}
let (mut q, qt) = forward_dct_quant_8x8_t(&resid, &self.cquant);
trellis_optimize(&mut q, &qt, dcq, acq, &SCAN_8X8, lam);
let rr = idct_dequant_8x8(&q, &self.cquant);
let mut s2 = 0i64;
for i in 0..64 {
let r = (cpr[i] + rr[i]).clamp(0, (1 << self.bd) - 1);
let d = src[i] - r;
s2 += (d * d) as i64;
}
cfl_ccf[ci] = q;
cfl_sse[ci] = s2;
cfl_bits[ci] = block_rate_bits(&q, &SCAN_8X8);
cpred444[ci] = cpr;
}
let sig =
4.0 + if cfl_a[0] != 0 { 4.0 } else { 0.0 } + if cfl_a[1] != 0 { 4.0 } else { 0.0 };
let dc_total = (dc_sse[0] + dc_sse[1]) as f64 + mlam * (dc_bits[0] + dc_bits[1]);
let cfl_total =
(cfl_sse[0] + cfl_sse[1]) as f64 + mlam * (cfl_bits[0] + cfl_bits[1] + sig);
if cfl_total < dc_total && (cfl_a[0] != 0 || cfl_a[1] != 0) {
use_cfl = true;
cfl_alpha_uv = cfl_a;
ccf8[..2].copy_from_slice(&cfl_ccf[..2]);
} else {
for ci in 0..2 {
cpred444[ci] = [cpred[ci]; 64];
}
}
}
let mut chosen_uv_444 = if use_cfl { CFL_PRED } else { DC_PRED };
let mut paeth_pred444 = [[0i32; 64]; 2];
if !self.mono && !self.ss420 && !self.ss422 && !use_cfl {
let maxv = (1 << self.bd) - 1;
let mut dc_total = 0f64;
let mut src_planes = [[0i32; 64]; 2];
for ci in 0..2 {
let plane = ci + 1;
let mut src = [0i32; 64];
for (ry, drow) in src.as_chunks_mut::<8>().0.iter_mut().enumerate() {
drow.copy_from_slice(&self.src[plane][(py + ry) * self.w + px..][..8]);
}
src_planes[ci] = src;
let dcrr = idct_dequant_8x8(&ccf8[ci], &self.cquant);
let mut sse = 0i64;
for i in 0..64 {
let r = (cpred[ci] + dcrr[i]).clamp(0, maxv);
let d = src[i] - r;
sse += (d * d) as i64;
}
dc_total += sse as f64 + mlam * block_rate_bits(&ccf8[ci], &SCAN_8X8);
}
let mut best_total = dc_total;
let mut best_mode_uv = DC_PRED;
let mut best_ccf = ccf8;
let mut best_pred = [[0i32; 64]; 2];
for &cand in &[
PAETH_PRED,
SMOOTH_PRED,
SMOOTH_V_PRED,
SMOOTH_H_PRED,
V_PRED,
H_PRED,
D135_PRED,
D113_PRED,
D157_PRED,
] {
if (V_PRED..=VERT_LEFT_PRED).contains(&cand) && !self.speed.try_chroma_directional()
{
continue;
}
let tx = chroma_tx_for_mode(cand);
let sig_bits = if (V_PRED..=VERT_LEFT_PRED).contains(&cand) {
7.0
} else {
4.0
};
let mut cand_ccf = [[0i32; 64]; 2];
let mut cand_pred = [[0i32; 64]; 2];
let mut cand_total = mlam * sig_bits;
for ci in 0..2 {
let plane = ci + 1;
let mut pp = [0i32; 64];
intra_predict_nd(
cand,
&self.recon[plane],
self.w,
px,
py,
8,
8,
false,
false,
self.w,
self.h,
&mut pp,
self.bd,
);
let mut resid = [0i32; 64];
for i in 0..64 {
resid[i] = src_planes[ci][i] - pp[i];
}
let (mut q, qt) = fwd_chroma_8x8(tx, &resid, &self.cquant);
trellis_optimize(&mut q, &qt, dcq, acq, &SCAN_8X8, lam);
let rr = inv_chroma_8x8(tx, &q, &self.cquant);
let mut sse = 0i64;
for i in 0..64 {
let r = (pp[i] + rr[i]).clamp(0, maxv);
let d = src_planes[ci][i] - r;
sse += (d * d) as i64;
}
cand_total += sse as f64 + mlam * block_rate_bits(&q, &SCAN_8X8);
cand_ccf[ci] = q;
cand_pred[ci] = pp;
}
if cand_total < best_total {
best_total = cand_total;
best_mode_uv = cand;
best_ccf = cand_ccf;
best_pred = cand_pred;
}
}
if best_mode_uv != DC_PRED {
chosen_uv_444 = best_mode_uv;
ccf8[..2].copy_from_slice(&best_ccf[..2]);
paeth_pred444[..2].copy_from_slice(&best_pred[..2]);
}
}
let chroma_zero = |ci: usize| {
if self.ss420 {
ccf44[ci].iter().all(|&c| c == 0)
} else if self.ss422 {
ccf48[ci].iter().all(|&c| c == 0)
} else {
ccf8[ci].iter().all(|&c| c == 0)
}
};
let block_skip =
lcf.iter().all(|&c| c == 0) && (self.mono || (chroma_zero(0) && chroma_zero(1)));
let sctx = (self.a_skip[bx4] + self.l_skip[by4]) as usize;
self.enc
.encode_symbol(block_skip as usize, &mut self.cdfs.skip[sctx]);
self.code_delta_q_if_armed();
self.mark_skip8(x8, y8, 1, block_skip);
let yctx = INTRA_MODE_CTX[self.a_mode[bx4] as usize] * 5
+ INTRA_MODE_CTX[self.l_mode[by4] as usize];
self.enc.encode_symbol(best_mode, &mut self.cdfs.kf_y[yctx]);
if (V_PRED..=VERT_LEFT_PRED).contains(&best_mode) {
self.enc.encode_symbol(
(best_delta + 3) as usize,
&mut self.cdfs.angle_delta[best_mode - V_PRED],
);
}
let smooth_v_active_ss420 = false; let mut sv_preds_420 = [[0i32; 16]; 2];
let mut chosen_uv_block = DC_PRED;
let mut chosen_uv_420 = DC_PRED;
let mut paeth_pred420 = [[0i32; 16]; 2];
let mut chosen_uv_422 = DC_PRED;
let mut paeth_pred422 = [[0i32; 32]; 2];
if !self.mono && self.ss420 && smooth_v_active_ss420 {
let (dcq2, acq2, lam2) = (
self.cquant.dc_q() as f64,
self.cquant.ac_q() as f64,
trellis_lambda(),
);
let mut sv_ccf44_2 = [[0i32; 16]; 2];
let mut sse_cur = 0i64;
let mut sse_sv = 0i64;
for ci in 0..2 {
let plane = ci + 1;
let dc = cpred[ci];
for ry in 0..4 {
let srow = &self.src[plane][(cy + ry) * self.cw + cx..];
for &sr in srow[..4].iter() {
let d = sr - dc;
sse_cur += (d * d) as i64;
}
}
intra_predict_nd(
SMOOTH_V_PRED,
&self.recon[plane],
self.cw,
cx,
cy,
4,
4,
false,
false,
self.cw,
self.h,
&mut sv_preds_420[ci],
self.bd,
);
let mut resid = [0i32; 16];
for (ry, drow) in resid.as_chunks_mut::<4>().0.iter_mut().enumerate() {
let srow = &self.src[plane][(cy + ry) * self.cw + cx..];
let prow = &sv_preds_420[ci][ry * 4..];
for (dv, (&s, &p)) in drow.iter_mut().zip(srow.iter().zip(prow.iter())) {
*dv = s - p;
}
}
let (q, qt) = forward_dct_quant_4x4_t(&resid, &self.cquant);
sv_ccf44_2[ci] = q;
trellis_optimize(&mut sv_ccf44_2[ci], &qt, dcq2, acq2, &SCAN_4X4, lam2);
for ry in 0..4 {
let srow = &self.src[plane][(cy + ry) * self.cw + cx..];
let prow = &sv_preds_420[ci][ry * 4..];
for j in 0..4 {
let d = srow[j] - prow[j];
sse_sv += (d * d) as i64;
}
}
}
if sse_sv < sse_cur {
ccf44[..2].copy_from_slice(&sv_ccf44_2[..2]);
chosen_uv_block = SMOOTH_V_PRED;
}
}
if !self.mono && self.ss420 {
let (dcq2, acq2, lam2) = (
self.cquant.dc_q() as f64,
self.cquant.ac_q() as f64,
trellis_lambda(),
);
let lrr = if best_is_idtx {
iidentity_dequant_8x8(&lcf, &self.quant)
} else if best_is_adst {
iadst_dequant_8x8(&lcf, &self.quant)
} else if best_is_adstdct {
iadstdct_dequant_8x8(&lcf, &self.quant)
} else if best_is_dctadst {
idctadst_dequant_8x8(&lcf, &self.quant)
} else {
idct_dequant_8x8(&lcf, &self.quant)
};
let mut luma_rec = [0i32; 64];
for i in 0..64 {
luma_rec[i] = (lpred_arr[i] + lrr[i]).clamp(0, (1 << self.bd) - 1);
}
let mut ac = [0i32; 16];
cfl_ac_sub(&luma_rec, 8, 4, 4, true, true, &mut ac);
let mut cfl_ccf = [[0i32; 16]; 2];
let mut cfl_a = [0i32; 2];
let (mut cur_sse, mut cfl_sse) = (0i64, 0i64);
let (mut cur_bits, mut cfl_bits) = (0f64, 0f64);
let maxv = (1 << self.bd) - 1;
for ci in 0..2 {
let plane = ci + 1;
let dc = cpred[ci];
let mut src = [0i32; 16];
for (ry, drow) in src.as_chunks_mut::<4>().0.iter_mut().enumerate() {
drow.copy_from_slice(&self.src[plane][(cy + ry) * self.cw + cx..][..4]);
}
let curr = idct_dequant_4x4(&ccf44[ci], &self.cquant);
for i in 0..16 {
let p = if chosen_uv_block == SMOOTH_V_PRED {
sv_preds_420[ci][i]
} else {
dc
};
let r = (p + curr[i]).clamp(0, maxv);
let d = src[i] - r;
cur_sse += (d * d) as i64;
}
cur_bits += block_rate_bits(&ccf44[ci], &SCAN_4X4);
let a = cfl_best_alpha(&ac, &src, dc, 16, self.bd);
cfl_a[ci] = a;
let mut cpr = [0i32; 16];
let mut resid = [0i32; 16];
for i in 0..16 {
cpr[i] = cfl_pred_pixel(dc, ac[i], a, self.bd);
resid[i] = src[i] - cpr[i];
}
let (mut q, qt) = forward_dct_quant_4x4_t(&resid, &self.cquant);
trellis_optimize(&mut q, &qt, dcq2, acq2, &SCAN_4X4, lam2);
let rr = idct_dequant_4x4(&q, &self.cquant);
for i in 0..16 {
let r = (cpr[i] + rr[i]).clamp(0, maxv);
let d = src[i] - r;
cfl_sse += (d * d) as i64;
}
cfl_bits += block_rate_bits(&q, &SCAN_4X4);
cfl_ccf[ci] = q;
cpred420[ci] = cpr;
}
let sig =
4.0 + if cfl_a[0] != 0 { 4.0 } else { 0.0 } + if cfl_a[1] != 0 { 4.0 } else { 0.0 };
let cur_total = cur_sse as f64 + mlam * cur_bits;
let cfl_total = cfl_sse as f64 + mlam * (cfl_bits + sig);
if cfl_total < cur_total && (cfl_a[0] != 0 || cfl_a[1] != 0) {
use_cfl = true;
cfl_alpha_uv = cfl_a;
ccf44[..2].copy_from_slice(&cfl_ccf[..2]);
}
}
if !self.mono && self.ss420 && !use_cfl && self.cquant.ac_q() < 120 {
let maxv = (1 << self.bd) - 1;
let mut src_planes = [[0i32; 16]; 2];
let mut dc_total = 0f64;
for ci in 0..2 {
let plane = ci + 1;
let mut src = [0i32; 16];
for (ry, drow) in src.as_chunks_mut::<4>().0.iter_mut().enumerate() {
drow.copy_from_slice(&self.src[plane][(cy + ry) * self.cw + cx..][..4]);
}
src_planes[ci] = src;
let dcrr = idct_dequant_4x4(&ccf44[ci], &self.cquant);
let mut sse = 0i64;
for i in 0..16 {
let r = (cpred[ci] + dcrr[i]).clamp(0, maxv);
let d = src[i] - r;
sse += (d * d) as i64;
}
dc_total += sse as f64 + mlam * block_rate_bits(&ccf44[ci], &SCAN_4X4);
}
let mut best_total = dc_total;
let mut best_mode_uv = DC_PRED;
let mut best_ccf = ccf44;
let mut best_pred = [[0i32; 16]; 2];
for &cand in &[
PAETH_PRED,
SMOOTH_PRED,
SMOOTH_V_PRED,
SMOOTH_H_PRED,
V_PRED,
H_PRED,
D135_PRED,
D113_PRED,
D157_PRED,
] {
if (V_PRED..=VERT_LEFT_PRED).contains(&cand) && !self.speed.try_chroma_directional()
{
continue;
}
let tx = chroma_tx_for_mode(cand);
let mut cand_ccf = [[0i32; 16]; 2];
let mut cand_pred = [[0i32; 16]; 2];
let sig_bits = if (V_PRED..=VERT_LEFT_PRED).contains(&cand) {
7.0
} else {
4.0
};
let mut cand_total = mlam * sig_bits; for ci in 0..2 {
let plane = ci + 1;
let mut pp = [0i32; 16];
intra_predict_nd(
cand,
&self.recon[plane],
self.cw,
cx,
cy,
4,
4,
false,
false,
self.cw,
self.h,
&mut pp,
self.bd,
);
let mut resid = [0i32; 16];
for i in 0..16 {
resid[i] = src_planes[ci][i] - pp[i];
}
let (mut q, qt) = fwd_chroma_4x4(tx, &resid, &self.cquant);
trellis_optimize(&mut q, &qt, dcq, acq, &SCAN_4X4, lam);
let rr = inv_chroma_4x4(tx, &q, &self.cquant);
let mut sse = 0i64;
for i in 0..16 {
let r = (pp[i] + rr[i]).clamp(0, maxv);
let d = src_planes[ci][i] - r;
sse += (d * d) as i64;
}
cand_total += sse as f64 + mlam * block_rate_bits(&q, &SCAN_4X4);
cand_ccf[ci] = q;
cand_pred[ci] = pp;
}
if cand_total < best_total {
best_total = cand_total;
best_mode_uv = cand;
best_ccf = cand_ccf;
best_pred = cand_pred;
}
}
if best_mode_uv != DC_PRED {
chosen_uv_420 = best_mode_uv;
ccf44[..2].copy_from_slice(&best_ccf[..2]);
paeth_pred420[..2].copy_from_slice(&best_pred[..2]);
}
}
if !self.mono && self.ss422 {
let (dcq2, acq2, lam2) = (
self.cquant.dc_q() as f64,
self.cquant.ac_q() as f64,
trellis_lambda(),
);
let lrr = if best_is_idtx {
iidentity_dequant_8x8(&lcf, &self.quant)
} else if best_is_adst {
iadst_dequant_8x8(&lcf, &self.quant)
} else if best_is_adstdct {
iadstdct_dequant_8x8(&lcf, &self.quant)
} else if best_is_dctadst {
idctadst_dequant_8x8(&lcf, &self.quant)
} else {
idct_dequant_8x8(&lcf, &self.quant)
};
let mut luma_rec = [0i32; 64];
for i in 0..64 {
luma_rec[i] = (lpred_arr[i] + lrr[i]).clamp(0, (1 << self.bd) - 1);
}
let mut ac = [0i32; 32];
cfl_ac_sub(&luma_rec, 8, 4, 8, true, false, &mut ac);
let mut cfl_ccf = [[0i32; 32]; 2];
let mut cfl_a = [0i32; 2];
let (mut cur_sse, mut cfl_sse) = (0i64, 0i64);
let (mut cur_bits, mut cfl_bits) = (0f64, 0f64);
let maxv = (1 << self.bd) - 1;
for ci in 0..2 {
let plane = ci + 1;
let dc = cpred[ci];
let mut src = [0i32; 32];
for (ry, drow) in src.as_chunks_mut::<4>().0.iter_mut().enumerate() {
drow.copy_from_slice(&self.src[plane][(py + ry) * self.cw + cx..][..4]);
}
let curr = idct_dequant_4x8(&ccf48[ci], &self.cquant);
for i in 0..32 {
let r = (dc + curr[i]).clamp(0, maxv);
let d = src[i] - r;
cur_sse += (d * d) as i64;
}
cur_bits += block_rate_bits(&ccf48[ci], &SCAN_4X8);
let a = cfl_best_alpha(&ac, &src, dc, 32, self.bd);
cfl_a[ci] = a;
let mut cpr = [0i32; 32];
let mut resid = [0i32; 32];
for i in 0..32 {
cpr[i] = cfl_pred_pixel(dc, ac[i], a, self.bd);
resid[i] = src[i] - cpr[i];
}
let (mut q, qt) = forward_dct_quant_4x8_t(&resid, &self.cquant);
trellis_optimize(&mut q, &qt, dcq2, acq2, &SCAN_4X8, lam2);
let rr = idct_dequant_4x8(&q, &self.cquant);
for i in 0..32 {
let r = (cpr[i] + rr[i]).clamp(0, maxv);
let d = src[i] - r;
cfl_sse += (d * d) as i64;
}
cfl_bits += block_rate_bits(&q, &SCAN_4X8);
cfl_ccf[ci] = q;
cpred422[ci] = cpr;
}
let sig =
4.0 + if cfl_a[0] != 0 { 4.0 } else { 0.0 } + if cfl_a[1] != 0 { 4.0 } else { 0.0 };
let cur_total = cur_sse as f64 + mlam * cur_bits;
let cfl_total = cfl_sse as f64 + mlam * (cfl_bits + sig);
if cfl_total < cur_total && (cfl_a[0] != 0 || cfl_a[1] != 0) {
use_cfl = true;
cfl_alpha_uv = cfl_a;
ccf48[..2].copy_from_slice(&cfl_ccf[..2]);
}
}
if !self.mono && self.ss422 && !use_cfl && self.cquant.ac_q() < 120 {
let maxv = (1 << self.bd) - 1;
let mut src_planes = [[0i32; 32]; 2];
let mut dc_total = 0f64;
for ci in 0..2 {
let plane = ci + 1;
let mut src = [0i32; 32];
for (ry, drow) in src.as_chunks_mut::<4>().0.iter_mut().enumerate() {
drow.copy_from_slice(&self.src[plane][(py + ry) * self.cw + cx..][..4]);
}
src_planes[ci] = src;
let dcrr = idct_dequant_4x8(&ccf48[ci], &self.cquant);
let mut sse = 0i64;
for i in 0..32 {
let r = (cpred[ci] + dcrr[i]).clamp(0, maxv);
let d = src[i] - r;
sse += (d * d) as i64;
}
dc_total += sse as f64 + mlam * block_rate_bits(&ccf48[ci], &SCAN_4X8);
}
let mut best_total = dc_total;
let mut best_mode_uv = DC_PRED;
let mut best_ccf = ccf48;
let mut best_pred = [[0i32; 32]; 2];
for &cand in &[
PAETH_PRED,
SMOOTH_PRED,
SMOOTH_V_PRED,
SMOOTH_H_PRED,
V_PRED,
H_PRED,
D135_PRED,
D113_PRED,
D157_PRED,
] {
if (V_PRED..=VERT_LEFT_PRED).contains(&cand) && !self.speed.try_chroma_directional()
{
continue;
}
let tx = chroma_tx_for_mode(cand);
let mut cand_ccf = [[0i32; 32]; 2];
let mut cand_pred = [[0i32; 32]; 2];
let sig_bits = if (V_PRED..=VERT_LEFT_PRED).contains(&cand) {
7.0
} else {
4.0
};
let mut cand_total = mlam * sig_bits;
for ci in 0..2 {
let plane = ci + 1;
let mut pp = [0i32; 32];
intra_predict_nd(
cand,
&self.recon[plane],
self.cw,
cx,
py,
4,
8,
false,
false,
self.cw,
self.h,
&mut pp,
self.bd,
);
let mut resid = [0i32; 32];
for i in 0..32 {
resid[i] = src_planes[ci][i] - pp[i];
}
let (mut q, qt) = fwd_chroma_4x8(tx, &resid, &self.cquant);
trellis_optimize(&mut q, &qt, dcq, acq, &SCAN_4X8, lam);
let rr = inv_chroma_4x8(tx, &q, &self.cquant);
let mut sse = 0i64;
for i in 0..32 {
let r = (pp[i] + rr[i]).clamp(0, maxv);
let d = src_planes[ci][i] - r;
sse += (d * d) as i64;
}
cand_total += sse as f64 + mlam * block_rate_bits(&q, &SCAN_4X8);
cand_ccf[ci] = q;
cand_pred[ci] = pp;
}
if cand_total < best_total {
best_total = cand_total;
best_mode_uv = cand;
best_ccf = cand_ccf;
best_pred = cand_pred;
}
}
if best_mode_uv != DC_PRED {
chosen_uv_422 = best_mode_uv;
ccf48[..2].copy_from_slice(&best_ccf[..2]);
paeth_pred422[..2].copy_from_slice(&best_pred[..2]);
}
}
if !self.mono {
let uv_mode_sym = if !self.ss420 && !self.ss422 {
chosen_uv_444
} else if self.ss420 {
if chosen_uv_420 != DC_PRED {
chosen_uv_420
} else {
chosen_uv_block
}
} else if self.ss422 {
if chosen_uv_422 != DC_PRED {
chosen_uv_422
} else {
chosen_uv_block
}
} else {
chosen_uv_block
};
self.emit_uv_mode(
best_mode,
uv_mode_sym,
if use_cfl { Some(cfl_alpha_uv) } else { None },
);
}
let sv = block_skip as u8;
self.a_skip[bx4] = sv;
self.a_skip[bx4 + 1] = sv;
self.l_skip[by4] = sv;
self.l_skip[by4 + 1] = sv;
let mv = best_mode as u8;
self.a_mode[bx4] = mv;
self.a_mode[bx4 + 1] = mv;
self.l_mode[by4] = mv;
self.l_mode[by4 + 1] = mv;
let lres_ctx = if block_skip {
0x40
} else {
let sk = self.skip_ctx(0, bx4, by4, false);
let ds = self.dc_sign_ctx(0, bx4, by4);
encode_tx8_coeffs_adapt(
&mut self.enc,
&mut self.cdfs,
&lcf,
false,
sk,
ds,
best_mode,
if best_is_idtx {
0
} else if best_is_adst {
ADST_ADST_TX8_IDX
} else if best_is_adstdct {
ADST_DCT_TX8_IDX
} else if best_is_dctadst {
DCT_ADST_TX8_IDX
} else {
1
},
)
};
self.a_coef[0][bx4] = lres_ctx;
self.a_coef[0][bx4 + 1] = lres_ctx;
self.l_coef[0][by4] = lres_ctx;
self.l_coef[0][by4 + 1] = lres_ctx;
let lrr = if block_skip {
[0i32; 64]
} else if best_is_idtx {
iidentity_dequant_8x8(&lcf, &self.quant)
} else if best_is_adst {
iadst_dequant_8x8(&lcf, &self.quant)
} else if best_is_adstdct {
iadstdct_dequant_8x8(&lcf, &self.quant)
} else if best_is_dctadst {
idctadst_dequant_8x8(&lcf, &self.quant)
} else {
idct_dequant_8x8(&lcf, &self.quant)
};
for (ry, (prow, rrow)) in lpred_arr
.as_chunks::<8>()
.0
.iter()
.zip(lrr.as_chunks::<8>().0.iter())
.enumerate()
{
let drow = &mut self.recon[0][(py + ry) * self.w + px..];
for ((dv, &p), &rv) in drow.iter_mut().zip(prow.iter()).zip(rrow.iter()) {
*dv = (p + rv).clamp(0, (1 << self.bd) - 1);
}
}
for ci in 0..(if self.mono { 0 } else { 2 }) {
let plane = ci + 1;
if self.ss420 {
let (bx4c, by4c) = (cx / 4, cy / 4);
let res_ctx = if block_skip {
0x40
} else {
let sk = self.skip_ctx_420(plane, bx4c, by4c);
let ds = self.dc_sign_ctx_420(plane, bx4c, by4c);
encode_4x4_chroma_coeffs(&mut self.enc, &mut self.cdfs, &ccf44[ci], sk, ds)
};
self.a_coef[plane][bx4c] = res_ctx;
self.l_coef[plane][by4c] = res_ctx;
let paeth420 = chosen_uv_420 != DC_PRED;
let rr = if block_skip {
[0i32; 16]
} else if paeth420 {
inv_chroma_4x4(chroma_tx_for_mode(chosen_uv_420), &ccf44[ci], &self.cquant)
} else {
idct_dequant_4x4(&ccf44[ci], &self.cquant)
};
for (ry, rrow) in rr.as_chunks::<4>().0.iter().enumerate() {
let drow = &mut self.recon[plane][(cy + ry) * self.cw + cx..];
if use_cfl {
let prow = &cpred420[ci][ry * 4..];
for ((dv, &rv), &p) in
drow[..4].iter_mut().zip(rrow.iter()).zip(prow.iter())
{
*dv = (p + rv).clamp(0, (1 << self.bd) - 1);
}
} else if chosen_uv_block == SMOOTH_V_PRED {
let prow = &sv_preds_420[ci][ry * 4..];
for ((dv, &rv), &prow) in
drow[..4].iter_mut().zip(rrow.iter()).zip(prow.iter())
{
*dv = (prow + rv).clamp(0, (1 << self.bd) - 1);
}
} else if paeth420 {
let prow = &paeth_pred420[ci][ry * 4..];
for ((dv, &rv), &p) in
drow[..4].iter_mut().zip(rrow.iter()).zip(prow.iter())
{
*dv = (p + rv).clamp(0, (1 << self.bd) - 1);
}
} else {
for (dv, &rv) in drow.iter_mut().zip(rrow.iter()) {
*dv = (cpred[ci] + rv).clamp(0, (1 << self.bd) - 1);
}
}
}
} else if self.ss422 {
let (bx4c, by4c) = (cx / 4, py / 4);
let res_ctx = if block_skip {
0x40
} else {
let sk = self.skip_ctx_422(plane, bx4c, by4c);
let ds = self.dc_sign_ctx_422(plane, bx4c, by4c);
encode_4x8_chroma_coeffs(&mut self.enc, &mut self.cdfs, &ccf48[ci], sk, ds)
};
self.a_coef[plane][bx4c] = res_ctx;
self.l_coef[plane][by4c] = res_ctx;
self.l_coef[plane][by4c + 1] = res_ctx;
let paeth422 = chosen_uv_422 != DC_PRED;
let rr = if block_skip {
[0i32; 32]
} else if paeth422 {
inv_chroma_4x8(chroma_tx_for_mode(chosen_uv_422), &ccf48[ci], &self.cquant)
} else {
idct_dequant_4x8(&ccf48[ci], &self.cquant)
};
for (ry, rrow) in rr.as_chunks::<4>().0.iter().enumerate() {
let drow = &mut self.recon[plane][(py + ry) * self.cw + cx..];
if use_cfl {
let prow = &cpred422[ci][ry * 4..];
for ((dv, &rv), &p) in drow.iter_mut().zip(rrow.iter()).zip(prow.iter()) {
*dv = (p + rv).clamp(0, (1 << self.bd) - 1);
}
} else if paeth422 {
let prow = &paeth_pred422[ci][ry * 4..];
for ((dv, &rv), &p) in drow.iter_mut().zip(rrow.iter()).zip(prow.iter()) {
*dv = (p + rv).clamp(0, (1 << self.bd) - 1);
}
} else {
for (dv, &rv) in drow.iter_mut().zip(rrow.iter()) {
*dv = (cpred[ci] + rv).clamp(0, (1 << self.bd) - 1);
}
}
}
} else {
let res_ctx = if block_skip {
0x40
} else {
let sk = self.skip_ctx(plane, bx4, by4, true);
let ds = self.dc_sign_ctx(plane, bx4, by4);
encode_tx8_coeffs_adapt(
&mut self.enc,
&mut self.cdfs,
&ccf8[ci],
true,
sk,
ds,
0,
1,
)
};
self.a_coef[plane][bx4] = res_ctx;
self.a_coef[plane][bx4 + 1] = res_ctx;
self.l_coef[plane][by4] = res_ctx;
self.l_coef[plane][by4 + 1] = res_ctx;
let paeth = chosen_uv_444 != DC_PRED && chosen_uv_444 != CFL_PRED;
let rr = if block_skip {
[0i32; 64]
} else if paeth {
inv_chroma_8x8(chroma_tx_for_mode(chosen_uv_444), &ccf8[ci], &self.cquant)
} else {
idct_dequant_8x8(&ccf8[ci], &self.cquant)
};
for (ry, rrow) in rr.as_chunks::<8>().0.iter().enumerate() {
let drow = &mut self.recon[plane][(py + ry) * self.w + px..];
if use_cfl {
let prow = &cpred444[ci][ry * 8..];
for ((dv, &rv), &p) in drow.iter_mut().zip(rrow.iter()).zip(prow.iter()) {
*dv = (p + rv).clamp(0, (1 << self.bd) - 1);
}
} else if paeth {
let prow = &paeth_pred444[ci][ry * 8..];
for ((dv, &rv), &p) in drow.iter_mut().zip(rrow.iter()).zip(prow.iter()) {
*dv = (p + rv).clamp(0, (1 << self.bd) - 1);
}
} else {
for (dv, &rv) in drow.iter_mut().zip(rrow.iter()) {
*dv = (cpred[ci] + rv).clamp(0, (1 << self.bd) - 1);
}
}
}
}
}
}
fn skip_ctx_422(&self, plane: usize, bx4c: usize, by4c: usize) -> usize {
let a = &self.a_coef[plane];
let l = &self.l_coef[plane];
let ca = (a[bx4c] != 0x40) as usize;
let cl = (l[by4c] != 0x40 || l[by4c + 1] != 0x40) as usize;
7 + ca + cl
}
fn dc_sign_ctx_422(&self, plane: usize, bx4c: usize, by4c: usize) -> usize {
let a = &self.a_coef[plane];
let l = &self.l_coef[plane];
let s = (a[bx4c] >> 6) as i32 + (l[by4c] >> 6) as i32 + (l[by4c + 1] >> 6) as i32 - 3;
(s != 0) as usize + (s > 0) as usize
}
fn skip_ctx_8x16_422(&self, plane: usize, bx4c: usize, by4c: usize) -> usize {
let a = &self.a_coef[plane];
let l = &self.l_coef[plane];
let ca = (a[bx4c] != 0x40 || a[bx4c + 1] != 0x40) as usize;
let cl =
(l[by4c] != 0x40 || l[by4c + 1] != 0x40 || l[by4c + 2] != 0x40 || l[by4c + 3] != 0x40)
as usize;
7 + ca + cl
}
fn dc_sign_ctx_8x16_422(&self, plane: usize, bx4c: usize, by4c: usize) -> usize {
let a = &self.a_coef[plane];
let l = &self.l_coef[plane];
let s = (a[bx4c] >> 6) as i32
+ (a[bx4c + 1] >> 6) as i32
+ (l[by4c] >> 6) as i32
+ (l[by4c + 1] >> 6) as i32
+ (l[by4c + 2] >> 6) as i32
+ (l[by4c + 3] >> 6) as i32
- 6;
(s != 0) as usize + (s > 0) as usize
}
fn skip_ctx_16x32_422(&self, plane: usize, bx4c: usize, by4c: usize) -> usize {
let a = &self.a_coef[plane];
let l = &self.l_coef[plane];
let ca = a[bx4c..bx4c + 4].iter().any(|&x| x != 0x40) as usize;
let cl = l[by4c..by4c + 8].iter().any(|&x| x != 0x40) as usize;
7 + ca + cl
}
fn dc_sign_ctx_16x32_422(&self, plane: usize, bx4c: usize, by4c: usize) -> usize {
let a = &self.a_coef[plane];
let l = &self.l_coef[plane];
let suma: i32 = a[bx4c..bx4c + 4].iter().map(|x| (x >> 6) as i32).sum();
let suml: i32 = l[by4c..by4c + 8].iter().map(|x| (x >> 6) as i32).sum();
let s = suma + suml - 12;
(s != 0) as usize + (s > 0) as usize
}
fn skip_ctx_420(&self, plane: usize, bx4c: usize, by4c: usize) -> usize {
let a = &self.a_coef[plane];
let l = &self.l_coef[plane];
7 + (a[bx4c] != 0x40) as usize + (l[by4c] != 0x40) as usize
}
fn dc_sign_ctx_420(&self, plane: usize, bx4c: usize, by4c: usize) -> usize {
let a = &self.a_coef[plane];
let l = &self.l_coef[plane];
let s = (a[bx4c] >> 6) as i32 + (l[by4c] >> 6) as i32 - 2;
(s != 0) as usize + (s > 0) as usize
}
fn dc_sign_ctx_32(&self, plane: usize, bx4: usize, by4: usize) -> usize {
let a = &self.a_coef[plane];
let l = &self.l_coef[plane];
let suma: i32 = (0..8).map(|k| (a[bx4 + k] >> 6) as i32).sum();
let suml: i32 = (0..8).map(|k| (l[by4 + k] >> 6) as i32).sum();
let s = suma + suml - 16;
(s != 0) as usize + (s > 0) as usize
}
fn skip_ctx_32(&self, plane: usize, bx4: usize, by4: usize, chroma: bool) -> usize {
if !chroma {
0
} else {
let a = &self.a_coef[plane];
let l = &self.l_coef[plane];
let ca = (0..8).any(|k| a[bx4 + k] != 0x40) as usize;
let cl = (0..8).any(|k| l[by4 + k] != 0x40) as usize;
7 + ca + cl
}
}
fn prefer_32x32(&self, _x8: usize, _y8: usize) -> bool {
let policy = tx32_policy();
if policy == 0 || self.mono {
return false;
}
if policy == 1 && self.block_luma_range(_x8, _y8, 32) < tx32_smooth_gate() {
return false;
}
let (px, py) = (_x8 * 8, _y8 * 8);
let lpred = dc_pred_32x32(&self.recon[0], self.w, px, py, self.bd as i32);
let mut r32 = [0i32; 1024];
for (ry, drow) in r32.as_chunks_mut::<32>().0.iter_mut().enumerate() {
let srow = &self.src[0][(py + ry) * self.w + px..];
for (dv, &s) in drow.iter_mut().zip(srow.iter()) {
*dv = s - lpred;
}
}
forward_dct_quant_32x32(&mut r32, &self.quant);
let cost32: u32 = est_block_bits(&r32, &SCAN_32X32) + OVERHEAD_16;
let mut cost16 = 0u32;
for (sx, sy) in [(0usize, 0usize), (16, 0), (0, 16), (16, 16)] {
let pred = dc_pred_16x16(&self.recon[0], self.w, px + sx, py + sy, self.bd as i32);
let mut r16 = [0i32; 256];
for (ry, drow) in r16.as_chunks_mut::<16>().0.iter_mut().enumerate() {
let srow = &self.src[0][(py + sy + ry) * self.w + px + sx..];
for (dv, &s) in drow.iter_mut().zip(srow.iter()) {
*dv = s - pred;
}
}
forward_dct_quant_16x16(&mut r16, &self.quant);
cost16 += est_block_bits(&r16, &SCAN_16X16) + OVERHEAD_16;
}
cost32 + (cost16 >> 4) <= cost16
}
fn code_block32(&mut self, x8: usize, y8: usize, have_tr: bool, have_bl: bool) {
self.record_blk(x8, y8, 8);
let (px, py) = (x8 * 8, y8 * 8);
let (dcq, acq, lam) = (
self.quant.dc_q() as f64,
self.quant.ac_q() as f64,
trellis_lambda(),
);
let mlam = mode_lambda() * acq * acq;
let prdo = self.perceptual_rd_scale(px, py, 32);
let (lam, mlam) = (lam * prdo, mlam * prdo);
let mut best_mode = DC_PRED;
let mut lpred = [0i32; 1024];
let mut lcf = [0i32; 1024];
let mut best_eff = f64::INFINITY;
let mut ltf = [0f64; 1024]; let modes = if self.speed.reduced_modes() {
fast_nd_modes()
} else {
nd_modes()
};
for &m in modes {
let mut pred = [0i32; 1024];
if m == DC_PRED {
let d = dc_pred_32x32(&self.recon[0], self.w, px, py, self.bd as i32);
pred = [d; 1024];
} else {
intra_predict_nd(
m,
&self.recon[0],
self.w,
px,
py,
32,
32,
have_tr,
have_bl,
self.w,
self.h,
&mut pred,
self.bd,
);
}
let mut resid = [0i32; 1024];
for (ry, (rrow, prow)) in resid
.as_chunks_mut::<32>()
.0
.iter_mut()
.zip(pred.as_chunks::<32>().0.iter())
.enumerate()
{
let srow = &self.src[0][(py + ry) * self.w + px..];
for (r, (&p, &s)) in rrow.iter_mut().zip(prow.iter().zip(srow.iter())) {
*r = s - p;
}
}
let (mut cf, tf) = forward_dct_quant_32x32_t(&resid, &self.quant);
if self.speed.per_candidate_rdoq() {
trellis_optimize_ctx(
&mut cf,
&tf,
dcq,
acq,
&SCAN_32X32,
lam,
32,
&self.cdfs,
3,
0,
&self.cdfs.eob_bin_1024_l,
self.dc_sign_ctx_32(0, px / 4, py / 4),
);
}
let rr = idct_dequant_32x32(&cf, &self.quant);
let mut sse = 0i64;
for (ry, (prow, rrow)) in pred
.as_chunks::<32>()
.0
.iter()
.zip(rr.as_chunks::<32>().0.iter())
.enumerate()
{
let srow = &self.src[0][(py + ry) * self.w + px..];
for ((&p, &rv), &s) in prow.iter().zip(rrow.iter()).zip(srow.iter()) {
let r = (p + rv).clamp(0, (1 << self.bd) - 1);
let d = s - r;
sse += (d * d) as i64;
}
}
let bits = block_rate_bits(&cf, &SCAN_32X32) + mode_signal_bits(m);
let cost = sse as f64 + mlam * bits;
if cost < best_eff {
best_eff = cost;
best_mode = m;
lpred = pred;
lcf = cf;
ltf = tf;
}
}
let mut best_delta: i32 = 0;
if angle_delta_enabled()
&& (D45_PRED..=VERT_LEFT_PRED).contains(&best_mode)
&& best_mode != V_PRED
&& best_mode != H_PRED
{
let ad_cdf = self.cdfs.angle_delta[best_mode - V_PRED].clone();
let ds = self.dc_sign_ctx_32(0, px / 4, py / 4);
let wrr = idct_dequant_32x32(&lcf, &self.quant);
let mut wsse = 0i64;
for ry in 0..32 {
let srow = &self.src[0][(py + ry) * self.w + px..];
for rx in 0..32 {
let r = (lpred[ry * 32 + rx] + wrr[ry * 32 + rx]).clamp(0, (1 << self.bd) - 1);
let dd = srow[rx] - r;
wsse += (dd * dd) as i64;
}
}
let wbits = block_rate_bits(&lcf, &SCAN_32X32);
let mut best_ad_cost = wsse as f64 + mlam * (wbits + cdf_cost(&ad_cdf, 3));
for d in [-3i32, -2, -1, 1, 2, 3] {
let mut pred = [0i32; 1024];
intra_predict_nd_ad(
best_mode,
d,
&self.recon[0],
self.w,
px,
py,
32,
32,
have_tr,
have_bl,
self.w,
self.h,
&mut pred,
self.bd,
);
let mut resid = [0i32; 1024];
for ry in 0..32 {
let srow = &self.src[0][(py + ry) * self.w + px..];
for rx in 0..32 {
resid[ry * 32 + rx] = srow[rx] - pred[ry * 32 + rx];
}
}
let (mut cf, tf) = forward_dct_quant_32x32_t(&resid, &self.quant);
if self.speed.per_candidate_rdoq() {
trellis_optimize_ctx(
&mut cf,
&tf,
dcq,
acq,
&SCAN_32X32,
lam,
32,
&self.cdfs,
3,
0,
&self.cdfs.eob_bin_1024_l,
ds,
);
}
let rr = idct_dequant_32x32(&cf, &self.quant);
let mut sse = 0i64;
for ry in 0..32 {
let srow = &self.src[0][(py + ry) * self.w + px..];
for rx in 0..32 {
let r =
(pred[ry * 32 + rx] + rr[ry * 32 + rx]).clamp(0, (1 << self.bd) - 1);
let dd = srow[rx] - r;
sse += (dd * dd) as i64;
}
}
let bits = block_rate_bits(&cf, &SCAN_32X32);
let cost = sse as f64 + mlam * (bits + cdf_cost(&ad_cdf, (d + 3) as usize));
if cost < best_ad_cost {
best_ad_cost = cost;
best_delta = d;
lpred = pred;
lcf = cf;
ltf = tf;
}
}
}
if !self.speed.per_candidate_rdoq() {
trellis_optimize_ctx(
&mut lcf,
<f,
dcq,
acq,
&SCAN_32X32,
lam,
32,
&self.cdfs,
3,
0,
&self.cdfs.eob_bin_1024_l,
self.dc_sign_ctx_32(0, px / 4, py / 4),
);
}
let luma_zero = lcf.iter().all(|&c| c == 0);
if self.ss420 {
self.code_block32_420(x8, y8, &lcf, &lpred, best_mode, luma_zero, best_delta);
} else if self.ss422 {
self.code_block32_422(x8, y8, &lcf, &lpred, best_mode, luma_zero, best_delta);
} else {
self.code_block32_444(x8, y8, &lcf, &lpred, best_mode, luma_zero, best_delta);
}
}
#[allow(clippy::too_many_arguments)]
fn code_header_luma32(
&mut self,
x8: usize,
y8: usize,
lcf: &[i32; 1024],
lpred: &[i32; 1024],
y_mode: usize,
block_skip: bool,
uv_mode: usize,
cfl: Option<[i32; 2]>,
angle_delta: i32,
) {
let (px, py) = (x8 * 8, y8 * 8);
let (bx4, by4) = (px / 4, py / 4);
let sctx = (self.a_skip[bx4] + self.l_skip[by4]) as usize;
self.enc
.encode_symbol(block_skip as usize, &mut self.cdfs.skip[sctx]);
self.code_delta_q_if_armed();
self.mark_skip8(x8, y8, 4, block_skip);
let yctx = INTRA_MODE_CTX[self.a_mode[bx4] as usize] * 5
+ INTRA_MODE_CTX[self.l_mode[by4] as usize];
self.enc.encode_symbol(y_mode, &mut self.cdfs.kf_y[yctx]);
if (V_PRED..=VERT_LEFT_PRED).contains(&y_mode) {
self.enc.encode_symbol(
(angle_delta + 3) as usize,
&mut self.cdfs.angle_delta[y_mode - V_PRED],
);
}
self.emit_uv_mode(y_mode, uv_mode, cfl);
let sv = block_skip as u8;
let mv = y_mode as u8;
self.a_skip[bx4..bx4 + 8].fill(sv);
self.l_skip[by4..by4 + 8].fill(sv);
self.a_mode[bx4..bx4 + 8].fill(mv);
self.l_mode[by4..by4 + 8].fill(mv);
let lres = if block_skip {
0x40
} else {
let sk = self.skip_ctx_32(0, bx4, by4, false);
let ds = self.dc_sign_ctx_32(0, bx4, by4);
encode_tx32_coeffs_adapt(&mut self.enc, &mut self.cdfs, lcf, false, sk, ds)
};
self.a_coef[0][bx4..bx4 + 8].fill(lres);
self.l_coef[0][by4..by4 + 8].fill(lres);
let lrr = if block_skip {
[0i32; 1024]
} else {
idct_dequant_32x32(lcf, &self.quant)
};
for (ry, (prow, rrow)) in lpred
.as_chunks::<32>()
.0
.iter()
.zip(lrr.as_chunks::<32>().0.iter())
.enumerate()
{
let drow = &mut self.recon[0][(py + ry) * self.w + px..];
for ((dv, &p), &rv) in drow.iter_mut().zip(prow.iter()).zip(rrow.iter()) {
*dv = (p + rv).clamp(0, (1 << self.bd) - 1);
}
}
}
#[allow(clippy::too_many_arguments)]
fn code_block32_444(
&mut self,
x8: usize,
y8: usize,
lcf: &[i32; 1024],
lpred: &[i32; 1024],
y_mode: usize,
luma_zero: bool,
angle_delta: i32,
) {
let (px, py) = (x8 * 8, y8 * 8);
let (bx4, by4) = (px / 4, py / 4);
let (dcq, acq, lam) = (
self.cquant.dc_q() as f64,
self.cquant.ac_q() as f64,
trellis_lambda(),
);
let mut ccf = [[0i32; 1024]; 2];
let mut cdc = [0i32; 2];
for ci in 0..2 {
let plane = ci + 1;
let dc = dc_pred_32x32(&self.recon[plane], self.w, px, py, self.bd as i32);
cdc[ci] = dc;
let mut cresid = [0i32; 1024];
for (ry, drow) in cresid.as_chunks_mut::<32>().0.iter_mut().enumerate() {
let srow = &self.src[plane][(py + ry) * self.w + px..];
for (dv, &s) in drow.iter_mut().zip(srow.iter()) {
*dv = s - dc;
}
}
let (q, qt) = forward_dct_quant_32x32_t(&cresid, &self.cquant);
ccf[ci] = q;
trellis_optimize(&mut ccf[ci], &qt, dcq, acq, &SCAN_32X32, lam);
let mean_resid_dc = cresid.iter().sum::<i32>() / 1024;
if ccf[ci][0] == 0 && mean_resid_dc.abs() >= 8 {
ccf[ci][0] = if mean_resid_dc > 0 { 1 } else { -1 };
}
}
let mut cfl_ccf = [[0i32; 1024]; 2];
let mut cfl_pred = [[0i32; 1024]; 2];
let mut cfl_a = [0i32; 2];
let (mut dc_cost, mut cfl_cost) = ([0f64; 2], [0f64; 2]);
let mlam = mode_lambda() * acq * acq;
{
let lrr_cfl = idct_dequant_32x32(lcf, &self.quant);
let mut luma_rec = [0i32; 1024];
for i in 0..1024 {
luma_rec[i] = (lpred[i] + lrr_cfl[i]).clamp(0, (1 << self.bd) - 1);
}
let mut ac = [0i32; 1024];
cfl_ac_444(&luma_rec, 32, 32, &mut ac);
for ci in 0..2 {
let plane = ci + 1;
let dc = cdc[ci];
let mut src = [0i32; 1024];
for (ry, drow) in src.as_chunks_mut::<32>().0.iter_mut().enumerate() {
drow.copy_from_slice(&self.src[plane][(py + ry) * self.w + px..][..32]);
}
let dcrr = idct_dequant_32x32(&ccf[ci], &self.cquant);
let mut s = 0i64;
for i in 0..1024 {
let d = src[i] - (dc + dcrr[i]).clamp(0, (1 << self.bd) - 1);
s += (d * d) as i64;
}
dc_cost[ci] = s as f64 + mlam * block_rate_bits(&ccf[ci], &SCAN_32X32);
let a = cfl_best_alpha(&ac, &src, dc, 1024, self.bd);
cfl_a[ci] = a;
let mut cpr = [0i32; 1024];
let mut resid = [0i32; 1024];
for i in 0..1024 {
cpr[i] = cfl_pred_pixel(dc, ac[i], a, self.bd);
resid[i] = src[i] - cpr[i];
}
let (mut q, qt) = forward_dct_quant_32x32_t(&resid, &self.cquant);
trellis_optimize(&mut q, &qt, dcq, acq, &SCAN_32X32, lam);
let rr = idct_dequant_32x32(&q, &self.cquant);
let mut s2 = 0i64;
for i in 0..1024 {
let d = src[i] - (cpr[i] + rr[i]).clamp(0, (1 << self.bd) - 1);
s2 += (d * d) as i64;
}
cfl_ccf[ci] = q;
cfl_pred[ci] = cpr;
cfl_cost[ci] = s2 as f64 + mlam * block_rate_bits(&q, &SCAN_32X32);
}
}
let cfl_sig =
4.0 + if cfl_a[0] != 0 { 4.0 } else { 0.0 } + if cfl_a[1] != 0 { 4.0 } else { 0.0 };
let use_cfl = acq > 300.0
&& (cfl_a[0] != 0 || cfl_a[1] != 0)
&& cfl_cost[0] + cfl_cost[1] + mlam * cfl_sig < dc_cost[0] + dc_cost[1];
#[allow(unused_mut)] let (cf_use, pred_dc, mut cfl_opt): (
&[[i32; 1024]; 2],
[i32; 2],
Option<[i32; 2]>,
) = if use_cfl {
(&cfl_ccf, cdc, Some(cfl_a))
} else {
(&ccf, cdc, None)
};
#[allow(unused_mut)] let mut cf_use_owned: [[i32; 1024]; 2];
let mut sv_preds32 = [[0i32; 1024]; 2];
let (final_cf, chosen_uv_32) = 'sv: {
let dcq2 = self.cquant.dc_q() as f64;
let acq2 = self.cquant.ac_q() as f64;
let lam2 = trellis_lambda();
let mlam = mode_lambda() * acq2 * acq2;
let maxv = (1 << self.bd) - 1;
let mut cur_total = 0f64;
if use_cfl {
let a = cfl_a;
cur_total += mlam
* (4.0 + if a[0] != 0 { 4.0 } else { 0.0 } + if a[1] != 0 { 4.0 } else { 0.0 });
}
for ci in 0..2 {
let plane = ci + 1;
let rr = idct_dequant_32x32(&cf_use[ci], &self.cquant);
let mut sse = 0i64;
for (ry, rrow) in rr.as_chunks::<32>().0.iter().enumerate() {
let srow = &self.src[plane][(py + ry) * self.w + px..];
for (j, (&s, &r)) in srow[..32].iter().zip(rrow.iter()).enumerate() {
let p = if use_cfl {
cfl_pred[ci][ry * 32 + j]
} else {
pred_dc[ci]
};
let d = s - (p + r).clamp(0, maxv);
sse += (d * d) as i64;
}
}
cur_total += sse as f64 + mlam * block_rate_bits(&cf_use[ci], &SCAN_32X32);
}
let mut best_total = cur_total;
let mut best_mode = DC_PRED;
let mut best_ccf = [[0i32; 1024]; 2];
let mut best_pred = [[0i32; 1024]; 2];
for &cand in &[
SMOOTH_V_PRED,
PAETH_PRED,
SMOOTH_PRED,
SMOOTH_H_PRED,
V_PRED,
H_PRED,
D135_PRED,
D113_PRED,
D157_PRED,
] {
if (V_PRED..=VERT_LEFT_PRED).contains(&cand) && !self.speed.try_chroma_directional()
{
continue;
}
let mut cand_ccf = [[0i32; 1024]; 2];
let mut cand_pred = [[0i32; 1024]; 2];
let sig_bits = if (V_PRED..=VERT_LEFT_PRED).contains(&cand) {
7.0
} else {
4.0
};
let mut cand_total = mlam * sig_bits;
for ci in 0..2 {
let plane = ci + 1;
intra_predict_nd(
cand,
&self.recon[plane],
self.w,
px,
py,
32,
32,
false,
false,
self.w,
self.h,
&mut cand_pred[ci],
self.bd,
);
let mut resid = [0i32; 1024];
for (ry, drow) in resid.as_chunks_mut::<32>().0.iter_mut().enumerate() {
let srow = &self.src[plane][(py + ry) * self.w + px..];
let prow = &cand_pred[ci][ry * 32..];
for (dv, (&s, &p)) in drow.iter_mut().zip(srow.iter().zip(prow.iter())) {
*dv = s - p;
}
}
let (mut q, qt) = forward_dct_quant_32x32_t(&resid, &self.cquant);
trellis_optimize(&mut q, &qt, dcq2, acq2, &SCAN_32X32, lam2);
let mean_resid = resid.iter().sum::<i32>() / 1024;
if q[0] == 0 && mean_resid.abs() >= 8 {
q[0] = if mean_resid > 0 { 1 } else { -1 };
}
cand_ccf[ci] = q;
let rr = idct_dequant_32x32(&q, &self.cquant);
let mut sse = 0i64;
for (ry, rrow) in rr.as_chunks::<32>().0.iter().enumerate() {
let srow = &self.src[plane][(py + ry) * self.w + px..];
let prow = &cand_pred[ci][ry * 32..];
for ((&s, &p), &r) in srow[..32].iter().zip(prow.iter()).zip(rrow.iter()) {
let d = s - (p + r).clamp(0, maxv);
sse += (d * d) as i64;
}
}
cand_total += sse as f64 + mlam * block_rate_bits(&q, &SCAN_32X32);
}
if cand_total < best_total {
best_total = cand_total;
best_mode = cand;
best_ccf = cand_ccf;
best_pred = cand_pred;
}
}
if best_mode != DC_PRED {
cfl_opt = None; cf_use_owned = best_ccf;
sv_preds32 = best_pred;
break 'sv (&cf_use_owned, best_mode);
}
(cf_use, DC_PRED)
};
let block_skip =
luma_zero && final_cf[0].iter().all(|&c| c == 0) && final_cf[1].iter().all(|&c| c == 0);
self.code_header_luma32(
x8,
y8,
lcf,
lpred,
y_mode,
block_skip,
chosen_uv_32,
cfl_opt,
angle_delta,
);
for ci in 0..2 {
let plane = ci + 1;
let cres = if block_skip {
0x40
} else {
let sk = self.skip_ctx_32(plane, bx4, by4, true);
let ds = self.dc_sign_ctx_32(plane, bx4, by4);
encode_tx32_coeffs_adapt(&mut self.enc, &mut self.cdfs, &final_cf[ci], true, sk, ds)
};
self.a_coef[plane][bx4..bx4 + 8].fill(cres);
self.l_coef[plane][by4..by4 + 8].fill(cres);
let crr = if block_skip {
[0i32; 1024]
} else {
idct_dequant_32x32(&final_cf[ci], &self.cquant)
};
for (ry, rrow) in crr.as_chunks::<32>().0.iter().enumerate() {
let drow = &mut self.recon[plane][(py + ry) * self.w + px..];
if chosen_uv_32 != DC_PRED {
let prow = &sv_preds32[ci][ry * 32..];
for (j, (dv, &rv)) in drow[..32].iter_mut().zip(rrow.iter()).enumerate() {
*dv = (prow[j] + rv).clamp(0, (1 << self.bd) - 1);
}
} else {
let base = if use_cfl {
cfl_pred[ci][ry * 32..][0]
} else {
pred_dc[ci]
};
for (dv, (&cp, &rv)) in drow[..32]
.iter_mut()
.zip(cfl_pred[ci][ry * 32..].iter().zip(rrow.iter()))
{
let b = if use_cfl { cp } else { base };
*dv = (b + rv).clamp(0, (1 << self.bd) - 1);
}
}
}
}
}
#[allow(clippy::too_many_arguments)]
fn code_block32_420(
&mut self,
x8: usize,
y8: usize,
lcf: &[i32; 1024],
lpred: &[i32; 1024],
y_mode: usize,
luma_zero: bool,
angle_delta: i32,
) {
let (px, py) = (x8 * 8, y8 * 8);
let (cx, cy) = (px / 2, py / 2);
let (bx4c, by4c) = (cx / 4, cy / 4);
let (dcq, acq, lam) = (
self.cquant.dc_q() as f64,
self.cquant.ac_q() as f64,
trellis_lambda(),
);
let maxval = (1 << self.bd) - 1;
let mut ccf_dc = [[0i32; 256]; 2];
let mut dc_preds = [0i32; 2];
for ci in 0..2 {
let plane = ci + 1;
let dc = dc_pred_16x16(&self.recon[plane], self.cw, cx, cy, self.bd as i32);
dc_preds[ci] = dc;
let mut resid = [0i32; 256];
for (ry, drow) in resid.as_chunks_mut::<16>().0.iter_mut().enumerate() {
let srow = &self.src[plane][(cy + ry) * self.cw + cx..];
for (dv, &s) in drow.iter_mut().zip(srow.iter()) {
*dv = s - dc;
}
}
let (q, qt) = forward_dct_quant_16x16_t(&resid, &self.cquant);
ccf_dc[ci] = q;
trellis_optimize(&mut ccf_dc[ci], &qt, dcq, acq, &SCAN_16X16, lam);
let mean_resid_dc = resid.iter().sum::<i32>() / 256;
if ccf_dc[ci][0] == 0 && mean_resid_dc.abs() >= 8 {
ccf_dc[ci][0] = if mean_resid_dc > 0 { 1 } else { -1 };
}
}
let mlam = mode_lambda() * acq * acq;
let mut rr_dc = [[0i32; 256]; 2];
let mut dc_total = 0f64;
for ci in 0..2 {
let plane = ci + 1;
rr_dc[ci] = idct_dequant_16x16(&ccf_dc[ci], &self.cquant);
let dc = dc_preds[ci];
let mut sse = 0i64;
for (ry, rrow) in rr_dc[ci].as_chunks::<16>().0.iter().enumerate() {
let srow = &self.src[plane][(cy + ry) * self.cw + cx..];
for (&s, &r) in srow[..16].iter().zip(rrow.iter()) {
let d = s - (dc + r).clamp(0, maxval);
sse += (d * d) as i64;
}
}
dc_total += sse as f64 + mlam * block_rate_bits(&ccf_dc[ci], &SCAN_16X16);
}
let mut best_total = dc_total;
let mut chosen_uv = DC_PRED;
let mut best_ccf = ccf_dc;
let mut best_rr = rr_dc;
let mut sv_preds = [[0i32; 256]; 2];
for &cand in &[
SMOOTH_V_PRED,
PAETH_PRED,
SMOOTH_PRED,
SMOOTH_H_PRED,
V_PRED,
H_PRED,
D135_PRED,
D113_PRED,
D157_PRED,
] {
if (V_PRED..=VERT_LEFT_PRED).contains(&cand) && !self.speed.try_chroma_directional() {
continue;
}
let tx = chroma_tx_for_mode(cand);
let mut cand_ccf = [[0i32; 256]; 2];
let mut cand_rr = [[0i32; 256]; 2];
let mut cand_pred = [[0i32; 256]; 2];
let sig_bits = if (V_PRED..=VERT_LEFT_PRED).contains(&cand) {
7.0
} else {
4.0
};
let mut cand_total = mlam * sig_bits;
for ci in 0..2 {
let plane = ci + 1;
intra_predict_nd(
cand,
&self.recon[plane],
self.cw,
cx,
cy,
16,
16,
false,
false,
self.cw,
self.h,
&mut cand_pred[ci],
self.bd,
);
let mut resid = [0i32; 256];
for (ry, drow) in resid.as_chunks_mut::<16>().0.iter_mut().enumerate() {
let srow = &self.src[plane][(cy + ry) * self.cw + cx..];
let prow = &cand_pred[ci][ry * 16..];
for (dv, (&s, &p)) in drow.iter_mut().zip(srow.iter().zip(prow.iter())) {
*dv = s - p;
}
}
let (mut q, qt) = fwd_chroma_16x16(tx, &resid, &self.cquant);
trellis_optimize(&mut q, &qt, dcq, acq, &SCAN_16X16, lam);
let mean_resid = resid.iter().sum::<i32>() / 256;
if q[0] == 0 && mean_resid.abs() >= 8 {
q[0] = if mean_resid > 0 { 1 } else { -1 };
}
cand_ccf[ci] = q;
cand_rr[ci] = inv_chroma_16x16(tx, &q, &self.cquant);
let mut sse = 0i64;
for (ry, rrow) in cand_rr[ci].as_chunks::<16>().0.iter().enumerate() {
let srow = &self.src[plane][(cy + ry) * self.cw + cx..];
let prow = &cand_pred[ci][ry * 16..];
for ((&s, &p), &r) in srow[..16].iter().zip(prow.iter()).zip(rrow.iter()) {
let d = s - (p + r).clamp(0, maxval);
sse += (d * d) as i64;
}
}
cand_total += sse as f64 + mlam * block_rate_bits(&q, &SCAN_16X16);
}
if cand_total < best_total {
best_total = cand_total;
chosen_uv = cand;
best_ccf = cand_ccf;
best_rr = cand_rr;
sv_preds = cand_pred;
}
}
let use_sv = chosen_uv != DC_PRED;
let (ccf, rr_cache) = (best_ccf, best_rr);
let block_skip =
luma_zero && ccf[0].iter().all(|&c| c == 0) && ccf[1].iter().all(|&c| c == 0);
self.code_header_luma32(
x8,
y8,
lcf,
lpred,
y_mode,
block_skip,
chosen_uv,
None,
angle_delta,
);
for ci in 0..2 {
let plane = ci + 1;
let res_ctx = if block_skip {
0x40
} else {
let sk = self.skip_ctx_16(plane, bx4c, by4c, true);
let ds = self.dc_sign_ctx_16(plane, bx4c, by4c);
encode_tx16_coeffs_adapt(
&mut self.enc,
&mut self.cdfs,
&ccf[ci],
true,
sk,
ds,
0,
1,
)
};
self.a_coef[plane][bx4c..bx4c + 4].fill(res_ctx);
self.l_coef[plane][by4c..by4c + 4].fill(res_ctx);
let rr = if block_skip {
[0i32; 256]
} else {
rr_cache[ci]
};
for (ry, rrow) in rr.as_chunks::<16>().0.iter().enumerate() {
let drow = &mut self.recon[plane][(cy + ry) * self.cw + cx..];
if use_sv {
let prow = &sv_preds[ci][ry * 16..];
for ((dv, &rv), &prow) in drow[..16]
.iter_mut()
.zip(rrow[..16].iter())
.zip(prow[..16].iter())
{
*dv = (prow + rv).clamp(0, maxval);
}
} else {
let dc = dc_preds[ci];
for (dv, &rv) in drow[..16].iter_mut().zip(rrow.iter()) {
*dv = (dc + rv).clamp(0, maxval);
}
}
}
}
}
#[allow(clippy::too_many_arguments)]
fn code_block32_422(
&mut self,
x8: usize,
y8: usize,
lcf: &[i32; 1024],
lpred: &[i32; 1024],
y_mode: usize,
luma_zero: bool,
angle_delta: i32,
) {
let (px, py) = (x8 * 8, y8 * 8);
let cx = px / 2;
let (bx4c, by4c) = (cx / 4, py / 4);
let maxv = (1 << self.bd) - 1;
let (dcq, acq, lam) = (
self.cquant.dc_q() as f64,
self.cquant.ac_q() as f64,
trellis_lambda(),
);
let mlam = mode_lambda() * acq * acq;
let mut ccf = [[0i32; 512]; 2];
let mut cpred = [0i32; 2];
let mut cpred_px = [[0i32; 512]; 2];
let mut src_planes = [[0i32; 512]; 2];
let mut dc_ccf = [[0i32; 512]; 2];
let mut dc_sse = [0i64; 2];
let mut dc_bits = [0f64; 2];
for ci in 0..2 {
let plane = ci + 1;
let pred = dc_pred_16x32(&self.recon[plane], self.cw, cx, py, self.bd as i32);
cpred[ci] = pred;
let mut src = [0i32; 512];
let mut resid = [0i32; 512];
for (ry, (drow, srow_dst)) in resid
.as_chunks_mut::<16>()
.0
.iter_mut()
.zip(src.as_chunks_mut::<16>().0.iter_mut())
.enumerate()
{
let srow = &self.src[plane][(py + ry) * self.cw + cx..];
for ((dv, sd), &s) in drow.iter_mut().zip(srow_dst.iter_mut()).zip(srow.iter()) {
*dv = s - pred;
*sd = s;
}
}
src_planes[ci] = src;
let (mut q, qt) = forward_dct_quant_16x32_t(&resid, &self.cquant);
trellis_optimize(&mut q, &qt, dcq, acq, &SCAN_16X32, lam);
let rr = idct_dequant_16x32(&q, &self.cquant);
let mut sse = 0i64;
for i in 0..512 {
let r = (pred + rr[i]).clamp(0, maxv);
let d = src[i] - r;
sse += (d * d) as i64;
}
dc_ccf[ci] = q;
dc_sse[ci] = sse;
dc_bits[ci] = block_rate_bits(&q, &SCAN_16X32);
}
let mut use_cfl = false;
let mut cfl_alpha_uv = [0i32; 2];
{
let lrr_cfl = idct_dequant_32x32(lcf, &self.quant);
let mut luma_rec = [0i32; 1024];
for i in 0..1024 {
luma_rec[i] = (lpred[i] + lrr_cfl[i]).clamp(0, maxv);
}
let mut ac = [0i32; 512];
cfl_ac_sub(&luma_rec, 32, 16, 32, true, false, &mut ac);
let mut cfl_ccf = [[0i32; 512]; 2];
let mut cfl_a = [0i32; 2];
let mut cfl_sse = [0i64; 2];
let mut cfl_bits = [0f64; 2];
for ci in 0..2 {
let dc = cpred[ci];
let src = src_planes[ci];
let a = cfl_best_alpha(&ac, &src, dc, 512, self.bd);
cfl_a[ci] = a;
let mut cpr = [0i32; 512];
let mut resid = [0i32; 512];
for i in 0..512 {
cpr[i] = cfl_pred_pixel(dc, ac[i], a, self.bd);
resid[i] = src[i] - cpr[i];
}
let (mut q, qt) = forward_dct_quant_16x32_t(&resid, &self.cquant);
trellis_optimize(&mut q, &qt, dcq, acq, &SCAN_16X32, lam);
let rr = idct_dequant_16x32(&q, &self.cquant);
let mut sse = 0i64;
for i in 0..512 {
let r = (cpr[i] + rr[i]).clamp(0, maxv);
let d = src[i] - r;
sse += (d * d) as i64;
}
cfl_ccf[ci] = q;
cfl_sse[ci] = sse;
cfl_bits[ci] = block_rate_bits(&q, &SCAN_16X32);
cpred_px[ci] = cpr;
}
let sig =
4.0 + if cfl_a[0] != 0 { 4.0 } else { 0.0 } + if cfl_a[1] != 0 { 4.0 } else { 0.0 };
let dc_total = (dc_sse[0] + dc_sse[1]) as f64 + mlam * (dc_bits[0] + dc_bits[1]);
let cfl_total =
(cfl_sse[0] + cfl_sse[1]) as f64 + mlam * (cfl_bits[0] + cfl_bits[1] + sig);
if cfl_total < dc_total && (cfl_a[0] != 0 || cfl_a[1] != 0) {
use_cfl = true;
cfl_alpha_uv = cfl_a;
ccf[..2].copy_from_slice(&cfl_ccf[..2]);
}
}
let mut chosen_uv = if use_cfl { CFL_PRED } else { DC_PRED };
{
let mut best_total = 0f64;
if use_cfl {
let a = cfl_alpha_uv;
best_total += mlam
* (4.0 + if a[0] != 0 { 4.0 } else { 0.0 } + if a[1] != 0 { 4.0 } else { 0.0 });
}
for ci in 0..2 {
let cur_ccf = if use_cfl { ccf[ci] } else { dc_ccf[ci] };
let rr = idct_dequant_16x32(&cur_ccf, &self.cquant);
let mut sse = 0i64;
for i in 0..512 {
let p = if use_cfl { cpred_px[ci][i] } else { cpred[ci] };
let d = src_planes[ci][i] - (p + rr[i]).clamp(0, maxv);
sse += (d * d) as i64;
}
best_total += sse as f64 + mlam * block_rate_bits(&cur_ccf, &SCAN_16X32);
}
for &cand in &[
SMOOTH_V_PRED,
PAETH_PRED,
SMOOTH_PRED,
SMOOTH_H_PRED,
V_PRED,
H_PRED,
D135_PRED,
D113_PRED,
D157_PRED,
] {
if (V_PRED..=VERT_LEFT_PRED).contains(&cand) && !self.speed.try_chroma_directional()
{
continue;
}
let mut cand_ccf = [[0i32; 512]; 2];
let mut cand_pred = [[0i32; 512]; 2];
let sig_bits = if (V_PRED..=VERT_LEFT_PRED).contains(&cand) {
7.0
} else {
4.0
};
let mut cand_total = mlam * sig_bits;
for ci in 0..2 {
let plane = ci + 1;
intra_predict_nd(
cand,
&self.recon[plane],
self.cw,
cx,
py,
16,
32,
false,
false,
self.cw,
self.h,
&mut cand_pred[ci],
self.bd,
);
let src = src_planes[ci];
let mut resid = [0i32; 512];
for i in 0..512 {
resid[i] = src[i] - cand_pred[ci][i];
}
let (mut q, qt) = forward_dct_quant_16x32_t(&resid, &self.cquant);
trellis_optimize(&mut q, &qt, dcq, acq, &SCAN_16X32, lam);
let mean_resid = resid.iter().sum::<i32>() / 512;
if q[0] == 0 && mean_resid.abs() >= 8 {
q[0] = if mean_resid > 0 { 1 } else { -1 };
}
cand_ccf[ci] = q;
let rr = idct_dequant_16x32(&q, &self.cquant);
let mut sse = 0i64;
for i in 0..512 {
let r = (cand_pred[ci][i] + rr[i]).clamp(0, maxv);
let d = src[i] - r;
sse += (d * d) as i64;
}
cand_total += sse as f64 + mlam * block_rate_bits(&q, &SCAN_16X32);
}
if cand_total < best_total {
best_total = cand_total;
chosen_uv = cand;
use_cfl = false;
ccf[..2].copy_from_slice(&cand_ccf[..2]);
cpred_px[..2].copy_from_slice(&cand_pred[..2]);
}
}
}
if chosen_uv == DC_PRED {
for ci in 0..2 {
ccf[ci] = dc_ccf[ci];
cpred_px[ci] = [cpred[ci]; 512];
}
}
let block_skip =
luma_zero && ccf[0].iter().all(|&c| c == 0) && ccf[1].iter().all(|&c| c == 0);
self.code_header_luma32(
x8,
y8,
lcf,
lpred,
y_mode,
block_skip,
chosen_uv,
if use_cfl { Some(cfl_alpha_uv) } else { None },
angle_delta,
);
for ci in 0..2 {
let plane = ci + 1;
let res_ctx = if block_skip {
0x40
} else {
let sk = self.skip_ctx_16x32_422(plane, bx4c, by4c);
let ds = self.dc_sign_ctx_16x32_422(plane, bx4c, by4c);
encode_16x32_chroma_coeffs(&mut self.enc, &mut self.cdfs, &ccf[ci], sk, ds)
};
self.a_coef[plane][bx4c..bx4c + 4].fill(res_ctx);
self.l_coef[plane][by4c..by4c + 8].fill(res_ctx);
let rr = if block_skip {
[0i32; 512]
} else {
idct_dequant_16x32(&ccf[ci], &self.cquant)
};
for (ry, rrow) in rr.as_chunks::<16>().0.iter().enumerate() {
let drow = &mut self.recon[plane][(py + ry) * self.cw + cx..];
let prow = &cpred_px[ci][ry * 16..];
for ((dv, &rv), &p) in drow.iter_mut().zip(rrow.iter()).zip(prow.iter()) {
*dv = (p + rv).clamp(0, maxv);
}
}
}
}
fn mark_skip8(&mut self, x8: usize, y8: usize, dim8: usize, skip: bool) {
let sb8w = self.w.div_ceil(8);
let sb8h = self.h.div_ceil(8);
for ry in 0..dim8 {
for rx in 0..dim8 {
let (bx, by) = (x8 + rx, y8 + ry);
if bx < sb8w && by < sb8h {
self.skip8[by * sb8w + bx] = skip;
}
}
}
}
fn mark_skip8_rect(&mut self, x8: usize, y8: usize, w8: usize, h8: usize, skip: bool) {
let sb8w = self.w.div_ceil(8);
let sb8h = self.h.div_ceil(8);
for ry in 0..h8 {
for rx in 0..w8 {
let (bx, by) = (x8 + rx, y8 + ry);
if bx < sb8w && by < sb8h {
self.skip8[by * sb8w + bx] = skip;
}
}
}
}
fn record_blk(&mut self, x8: usize, y8: usize, dim4: u8) {
let nc4 = self.w / 4;
let bx4 = x8 * 2;
let by4 = y8 * 2;
let d = dim4 as usize;
let nr4 = self.h / 4;
for r in by4..(by4 + d).min(nr4) {
for c in bx4..(bx4 + d).min(nc4) {
self.blk4[r * nc4 + c] = dim4;
self.blk4h[r * nc4 + c] = dim4;
}
}
}
fn record_blk_rect(&mut self, x8: usize, y8: usize, w4: u8, h4: u8) {
let nc4 = self.w / 4;
let nr4 = self.h / 4;
let bx4 = x8 * 2;
let by4 = y8 * 2;
for r in by4..(by4 + h4 as usize).min(nr4) {
for c in bx4..(bx4 + w4 as usize).min(nc4) {
self.blk4[r * nc4 + c] = w4;
self.blk4h[r * nc4 + c] = h4;
}
}
}
fn decode_sb(&mut self, bl: usize, x8: usize, y8: usize, sz8: usize, thr: bool, lhb: bool) {
if sz8 == 1 {
let ctx = get_partition_ctx(&self.a_part, &self.l_part, 4, x8, y8);
let split_eligible = !self.ss422 && !self.mono;
let want_split = split_eligible
&& (FORCE_SPLIT4.load(std::sync::atomic::Ordering::Relaxed)
|| (SPLIT4_ENABLED.load(std::sync::atomic::Ordering::Relaxed)
&& !self.prefer_8x8_none(x8, y8)));
if want_split {
self.enc.encode_symbol(3, &mut self.cdfs.part_bl8[ctx]); self.code_block_split4_dc(x8, y8);
self.a_part[x8] = 0x1f;
self.l_part[y8] = 0x1f;
return;
}
self.enc.encode_symbol(0, &mut self.cdfs.part_bl8[ctx]);
let have_tr = thr && y8 > 0 && (x8 * 8 + 8) < self.w;
let have_bl = lhb && x8 > 0 && (y8 * 8 + 8) < self.h;
self.code_block(x8, y8, have_tr, have_bl);
self.a_part[x8] = 0x1e;
self.l_part[y8] = 0x1e;
return;
}
if sz8 == 4 {
let full_h = (x8 + 4) * 8 <= self.w;
let full_v = (y8 + 4) * 8 <= self.h;
if full_h && full_v && self.prefer_32x32(x8, y8) {
let ctx = get_partition_ctx(&self.a_part, &self.l_part, bl, x8, y8);
self.enc
.encode_symbol(0, &mut self.cdfs.part_split[bl - 1][ctx]); let have_tr = thr && y8 > 0 && (x8 * 8 + 32) < self.w;
let have_bl = lhb && x8 > 0 && (y8 * 8 + 32) < self.h;
self.code_block32(x8, y8, have_tr, have_bl);
self.a_part[x8..x8 + 4].fill(0x18);
self.l_part[y8..y8 + 4].fill(0x18);
return;
}
}
if sz8 == 2 {
let have_h = (x8 + 1) * 8 < self.w;
let have_v = (y8 + 1) * 8 < self.h;
if have_h && have_v {
let forced_horz = !self.ss420
&& !self.ss422
&& !self.mono
&& FORCE_HORZ.load(std::sync::atomic::Ordering::Relaxed);
let choice = if forced_horz {
Part16::Horz
} else {
self.partition_choice_16(x8, y8)
};
match choice {
Part16::Horz => {
let ctx = get_partition_ctx(&self.a_part, &self.l_part, bl, x8, y8);
self.enc
.encode_symbol(1, &mut self.cdfs.part_split[bl - 1][ctx]); self.code_block16_horz_444(x8, y8);
self.a_part[x8..x8 + 2].fill(0x1c);
self.l_part[y8..y8 + 2].fill(0x1e);
return;
}
Part16::None => {
let ctx = get_partition_ctx(&self.a_part, &self.l_part, bl, x8, y8);
self.enc
.encode_symbol(0, &mut self.cdfs.part_split[bl - 1][ctx]); let have_tr = thr && y8 > 0 && (x8 * 8 + 16) < self.w;
let have_bl = lhb && x8 > 0 && (y8 * 8 + 16) < self.h;
self.code_block16(x8, y8, have_tr, have_bl);
self.a_part[x8..x8 + 2].fill(0x1c);
self.l_part[y8..y8 + 2].fill(0x1c);
return;
}
Part16::Split => { }
}
}
}
let hh = sz8 / 2;
let have_h = (x8 + hh) * 8 < self.w;
let have_v = (y8 + hh) * 8 < self.h;
let ctx = get_partition_ctx(&self.a_part, &self.l_part, bl, x8, y8);
if have_h && have_v {
self.enc
.encode_symbol(3, &mut self.cdfs.part_split[bl - 1][ctx]);
} else if have_h {
let p = gather_split_prob_icdf(&self.cdfs.part_split[bl - 1][ctx], true);
self.enc.encode_bool(true, p);
} else if have_v {
let p = gather_split_prob_icdf(&self.cdfs.part_split[bl - 1][ctx], false);
self.enc.encode_bool(true, p);
}
let children = [
(x8, y8, true, true),
(x8 + hh, y8, thr, false),
(x8, y8 + hh, true, lhb),
(x8 + hh, y8 + hh, false, false),
];
for (cx, cy, cthr, clhb) in children {
if cx * 8 < self.w && cy * 8 < self.h {
self.decode_sb(bl + 1, cx, cy, hh, cthr, clhb);
}
}
}
}
#[inline]
fn sse_recon<const N: usize, const D: usize>(
pred: &[i32; N],
resid: &[i32; N],
src: &[i32],
stride: usize,
px: usize,
py: usize,
bd: u8,
) -> i64 {
let maxv = (1 << bd) - 1;
let mut sse = 0i64;
for ry in 0..D {
let srow = &src[(py + ry) * stride + px..];
for c in 0..D {
let r = (pred[ry * D + c] + resid[ry * D + c]).clamp(0, maxv);
let d = (srow[c] - r) as i64;
sse += d * d;
}
}
sse
}
fn tx32_policy() -> u32 {
2
}
fn asym_adst_enabled() -> bool {
true
}
fn angle_delta_enabled() -> bool {
false
}
fn prdo_k() -> f64 {
0.0
}
fn prdo_clamp() -> f64 {
2.0
}
#[inline]
fn tx32_smooth_gate() -> i32 {
LF_BAND_SMOOTH_RANGE
}
const LF_BAND_SMOOTH_RANGE: i32 = 32;
const SPLIT_SIGNAL_BITS: f64 = 24.0;
const AC_Q_HORZ_MIN: i32 = 100;
pub(crate) fn align8(n: usize) -> usize {
(n + 7) & !7
}
pub(crate) fn pad_to_mult8<T: Copy>(src: &[T], w: usize, h: usize, w8: usize, h8: usize) -> Vec<T> {
let mut out = Vec::with_capacity(w8 * h8);
for y in 0..h {
let row = &src[y * w..y * w + w];
out.extend_from_slice(row);
out.resize(out.len() + (w8 - w), row[w - 1]);
}
for _ in h..h8 {
out.extend_from_within((h - 1) * w8..h * w8);
}
out
}
fn tile_log2(blk: u32, target: u32) -> u32 {
let mut k = 0;
while (blk << k) < target {
k += 1;
}
k
}
fn increment_bits(min: u32, max: u32, target: u32) -> Vec<bool> {
let mut v = Vec::new();
let mut cur = min;
while cur < max {
if cur < target {
v.push(true);
cur += 1;
} else {
v.push(false);
break;
}
}
v
}
struct Tiling {
tcl: u32,
trl: u32,
cols_incr: Vec<bool>,
rows_incr: Vec<bool>,
}
fn plan_tiling(sb_cols: u32, sb_rows: u32, target_tiles: usize) -> Tiling {
const MAX_TILE_WIDTH_SB: u32 = 4096 / 64; const MAX_TILE_AREA_SB: u32 = (4096 * 2304) / (64 * 64); let min_log2_tile_cols = tile_log2(MAX_TILE_WIDTH_SB, sb_cols);
let max_log2_tile_cols = tile_log2(1, sb_cols.min(64));
let max_log2_tile_rows = tile_log2(1, sb_rows.min(64));
let min_log2_tiles = min_log2_tile_cols.max(tile_log2(MAX_TILE_AREA_SB, sb_rows * sb_cols));
let mut tcl = min_log2_tile_cols.min(max_log2_tile_cols);
let mut trl = min_log2_tiles.saturating_sub(tcl).min(max_log2_tile_rows);
let target = target_tiles.max(1) as u32;
while (1u32 << (tcl + trl)) < target {
let can_col = tcl < max_log2_tile_cols;
let can_row = trl < max_log2_tile_rows;
if !can_col && !can_row {
break;
}
let col_span = sb_cols >> tcl; let row_span = sb_rows >> trl; if can_col && (!can_row || col_span >= row_span) {
tcl += 1;
} else {
trl += 1;
}
}
let cols_incr = increment_bits(min_log2_tile_cols, max_log2_tile_cols, tcl);
let min_log2_tile_rows = min_log2_tiles.saturating_sub(tcl);
let rows_incr = increment_bits(min_log2_tile_rows, max_log2_tile_rows, trl);
Tiling {
tcl,
trl,
cols_incr,
rows_incr,
}
}
fn tile_starts_sb(sbs: u32, log2: u32) -> Vec<u32> {
let size_sb = sbs.div_ceil(1 << log2);
let mut starts = Vec::new();
let mut s = 0;
while s < sbs {
starts.push(s);
s += size_sb;
}
starts
}
fn crop_plane<T: Copy>(
src: &[T],
full_w: usize,
x0: usize,
y0: usize,
tw: usize,
th: usize,
) -> Vec<T> {
let mut out = Vec::with_capacity(tw * th);
for r in 0..th {
let s = (y0 + r) * full_w + x0;
out.extend_from_slice(&src[s..s + tw]);
}
out
}
fn stitch_plane(
dst: &mut [i32],
full_w: usize,
x0: usize,
y0: usize,
tile: &[i32],
tw: usize,
th: usize,
) {
for r in 0..th {
let d = (y0 + r) * full_w + x0;
dst[d..d + tw].copy_from_slice(&tile[r * tw..(r + 1) * tw]);
}
}
#[derive(Clone, Copy)]
struct TileRect {
x0: usize,
y0: usize,
tw: usize,
th: usize,
cx0: usize,
cy0: usize,
ctw: usize,
cth: usize,
}
struct TileOut {
payload: Vec<u8>,
recon: [Vec<i32>; 3],
skip8: Vec<bool>, blk4: Vec<u8>, blk4h: Vec<u8>, }
#[allow(clippy::too_many_arguments)]
fn encode_one_tile(
base_q_idx: u8,
bd: u8,
full_w: usize,
full_h: usize,
cw8: usize,
sub_x: usize,
sub_y: usize,
mono: bool,
src: &[Vec<i32>; 3],
r: &TileRect,
speed: Speed,
aq: bool,
vb: &VarianceBoost,
wiener: Option<crate::wiener::WienerUnit>,
) -> TileOut {
let tsrc = if mono {
[
crop_plane(&src[0], full_w, r.x0, r.y0, r.tw, r.th),
Vec::new(),
Vec::new(),
]
} else {
[
crop_plane(&src[0], full_w, r.x0, r.y0, r.tw, r.th),
crop_plane(&src[1], cw8, r.cx0, r.cy0, r.ctw, r.cth),
crop_plane(&src[2], cw8, r.cx0, r.cy0, r.ctw, r.cth),
]
};
let mut tile = if mono {
LossyTile::new_mono(base_q_idx, bd, r.tw, r.th, &tsrc)
} else {
match (sub_x, sub_y) {
(0, 0) => LossyTile::new(base_q_idx, bd, r.tw, r.th, &tsrc),
(1, 0) => LossyTile::new_422(base_q_idx, bd, r.tw, r.th, &tsrc),
_ => LossyTile::new_420(base_q_idx, bd, r.tw, r.th, &tsrc),
}
}
.with_speed(speed);
tile.wiener = wiener;
tile.frame_x0 = r.x0;
tile.frame_y0 = r.y0;
tile.frame_w = full_w;
tile.frame_h = full_h;
if aq {
let ref_act = tile_ref_activity(&tile.src[0], tile.w, tile.w, tile.h);
tile.enable_aq(base_q_idx, ref_act, vb);
}
for sb_y in (0..r.th).step_by(64) {
for sb_x in (0..r.tw).step_by(64) {
tile.emit_lr_sb(sb_x, sb_y);
tile.aq_begin_sb(sb_x, sb_y);
tile.decode_sb(1, sb_x / 8, sb_y / 8, 8, true, false);
}
}
let skip8 = tile.skip8;
let blk4 = tile.blk4;
let blk4h = tile.blk4h;
let payload = tile.enc.done();
TileOut {
payload,
recon: tile.recon,
skip8,
blk4,
blk4h,
}
}
fn resolve_threads(threads: usize) -> usize {
if threads == 0 {
std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1)
} else {
threads
}
}
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
fn encode_lossy_tilegroup(
base_q_idx: u8,
bd: u8,
w8: usize,
h8: usize,
src: &[Vec<i32>; 3],
sub_x: usize,
sub_y: usize,
mono: bool,
threads: usize,
speed: Speed,
aq: bool,
vb: &VarianceBoost,
cdef_on: bool,
wiener_on: bool,
) -> (
Vec<u8>,
[Vec<i32>; 3],
Tiling,
Option<crate::obu::CdefParams>,
Option<crate::obu::LrParams>,
) {
let sb_cols = w8.div_ceil(64) as u32;
let sb_rows = h8.div_ceil(64) as u32;
let want = resolve_threads(threads);
let plan = plan_tiling(sb_cols, sb_rows, want);
let col_starts = tile_starts_sb(sb_cols, plan.tcl);
let row_starts = tile_starts_sb(sb_rows, plan.trl);
let (cw8, ch8) = (w8 >> sub_x, h8 >> sub_y);
let mut rects: Vec<TileRect> = Vec::with_capacity(col_starts.len() * row_starts.len());
for (ti, &rsb) in row_starts.iter().enumerate() {
let y0 = rsb as usize * 64;
let y1 = (row_starts.get(ti + 1).map_or(sb_rows, |&n| n) as usize * 64).min(h8);
let th = y1 - y0;
for (tj, &csb) in col_starts.iter().enumerate() {
let x0 = csb as usize * 64;
let x1 = (col_starts.get(tj + 1).map_or(sb_cols, |&n| n) as usize * 64).min(w8);
let tw = x1 - x0;
rects.push(TileRect {
x0,
y0,
tw,
th,
cx0: x0 >> sub_x,
cy0: y0 >> sub_y,
ctw: tw >> sub_x,
cth: th >> sub_y,
});
}
}
let n = rects.len();
let nthreads = want.clamp(1, n.max(1));
let encode_all = |wiener_unit: Option<crate::wiener::WienerUnit>| -> Vec<TileOut> {
if nthreads <= 1 || n <= 1 {
rects
.iter()
.map(|r| {
encode_one_tile(
base_q_idx,
bd,
w8,
h8,
cw8,
sub_x,
sub_y,
mono,
src,
r,
speed,
aq,
vb,
wiener_unit,
)
})
.collect()
} else {
let mut slots: Vec<Option<TileOut>> = (0..n).map(|_| None).collect();
let chunk = n.div_ceil(nthreads);
std::thread::scope(|scope| {
for (rs, os) in rects.chunks(chunk).zip(slots.chunks_mut(chunk)) {
scope.spawn(move || {
for (r, o) in rs.iter().zip(os.iter_mut()) {
*o = Some(encode_one_tile(
base_q_idx,
bd,
w8,
h8,
cw8,
sub_x,
sub_y,
mono,
src,
r,
speed,
aq,
vb,
wiener_unit,
));
}
});
}
});
slots.into_iter().map(|o| o.unwrap()).collect()
}
};
let outs: Vec<TileOut> = encode_all(None);
let mut recon = if mono {
[vec![0i32; w8 * h8], Vec::new(), Vec::new()]
} else {
[
vec![0i32; w8 * h8],
vec![0i32; cw8 * ch8],
vec![0i32; cw8 * ch8],
]
};
let mut payloads: Vec<Vec<u8>> = Vec::with_capacity(n);
let sb8w = w8.div_ceil(8);
let sb8h = h8.div_ceil(8);
let mut skip8 = vec![true; sb8w * sb8h];
let nc4f = w8 / 4;
let nr4f = h8 / 4;
let mut blk4f = vec![0u8; nc4f * nr4f];
let mut blk4hf = vec![0u8; nc4f * nr4f];
for (r, out) in rects.iter().zip(outs) {
let tsb8w = r.tw.div_ceil(8);
let (ox8, oy8) = (r.x0 / 8, r.y0 / 8);
for ty in 0..r.th.div_ceil(8) {
for tx in 0..tsb8w {
let (fx, fy) = (ox8 + tx, oy8 + ty);
if fx < sb8w && fy < sb8h {
skip8[fy * sb8w + fx] = out.skip8[ty * tsb8w + tx];
}
}
}
stitch_plane(&mut recon[0], w8, r.x0, r.y0, &out.recon[0], r.tw, r.th);
let tnc4 = r.tw / 4;
let (ox4, oy4) = (r.x0 / 4, r.y0 / 4);
for ty in 0..(r.th / 4) {
for tx in 0..tnc4 {
let (fx, fy) = (ox4 + tx, oy4 + ty);
if fx < nc4f && fy < nr4f {
blk4f[fy * nc4f + fx] = out.blk4[ty * tnc4 + tx];
blk4hf[fy * nc4f + fx] = out.blk4h[ty * tnc4 + tx];
}
}
}
if !mono {
stitch_plane(
&mut recon[1],
cw8,
r.cx0,
r.cy0,
&out.recon[1],
r.ctw,
r.cth,
);
stitch_plane(
&mut recon[2],
cw8,
r.cx0,
r.cy0,
&out.recon[2],
r.ctw,
r.cth,
);
}
payloads.push(out.payload);
}
let (lvl_y, lvl_uv) = crate::obu::loop_filter_levels(base_q_idx);
frame_deblock(
&mut recon, w8, h8, cw8, ch8, &blk4f, &blk4hf, nc4f, sub_x, sub_y, mono, lvl_y, lvl_uv, bd,
);
let cdef = if cdef_on && base_q_idx != 0 {
frame_cdef(
&mut recon, src, &skip8, sb8w, w8, h8, cw8, ch8, sub_x, sub_y, mono, base_q_idx, bd,
)
} else {
None
};
let lr = if wiener_on && base_q_idx != 0 {
if let Some(unit) = frame_wiener_search(&recon[0], &src[0], w8, h8, bd) {
let outs2 = encode_all(Some(unit));
payloads = outs2.into_iter().map(|o| o.payload).collect();
apply_frame_wiener(&mut recon[0], w8, h8, &unit, bd);
Some(crate::obu::LrParams { luma_wiener: true })
} else {
None
}
} else {
None
};
let tilegroup = assemble_tilegroup(payloads);
(tilegroup, recon, plan, cdef, lr)
}
fn cdef_damping(base_q_idx: u8) -> u8 {
3 + ((base_q_idx as u32) / 64).min(3) as u8
}
fn apply_frame_wiener(
plane: &mut [i32],
w: usize,
h: usize,
unit: &crate::wiener::WienerUnit,
bd: u8,
) {
use crate::wiener::{WienerKernel, wiener_filter_plane};
let hk = WienerKernel::from_coded(unit.h);
let vk = WienerKernel::from_coded(unit.v);
let src = plane.to_vec();
wiener_filter_plane(plane, &src, w, h, &hk, &vk, bd);
}
fn frame_wiener_search(
recon: &[i32],
src: &[i32],
w: usize,
h: usize,
bd: u8,
) -> Option<crate::wiener::WienerUnit> {
use crate::wiener::{WienerKernel, wiener_filter_plane};
let sse = |a: &[i32]| -> i64 {
let mut s = 0i64;
for i in 0..w * h {
let d = (a[i] - src[i]) as i64;
s += d * d;
}
s
};
let base = sse(recon);
const CANDS: [[i32; 3]; 4] = [[0, 0, 1], [-1, 2, 2], [0, 1, 3], [1, -3, 5]];
let mut best: Option<(i64, crate::wiener::WienerUnit)> = None;
let mut tmp = recon.to_vec();
for &h_taps in &CANDS {
for &v_taps in &CANDS {
let hk = WienerKernel::from_coded(h_taps);
let vk = WienerKernel::from_coded(v_taps);
wiener_filter_plane(&mut tmp, recon, w, h, &hk, &vk, bd);
let s = sse(&tmp);
if s < base && best.as_ref().is_none_or(|b| s < b.0) {
best = Some((
s,
crate::wiener::WienerUnit {
h: h_taps,
v: v_taps,
},
));
}
}
}
best.map(|b| b.1)
}
#[allow(clippy::too_many_arguments)]
fn frame_cdef(
recon: &mut [Vec<i32>; 3],
src: &[Vec<i32>; 3],
skip8: &[bool],
sb8w: usize,
w8: usize,
h8: usize,
cw8: usize,
ch8: usize,
sub_x: usize,
sub_y: usize,
mono: bool,
base_q_idx: u8,
bd: u8,
) -> Option<crate::obu::CdefParams> {
use crate::cdef;
let signalled_damping = cdef_damping(base_q_idx) as i32;
let damping = signalled_damping + (bd as i32 - 8);
let nbx = w8.div_ceil(8);
let nby = h8.div_ceil(8);
let mut ldirs = vec![0usize; nbx * nby];
let mut lvars = vec![0i32; nbx * nby];
for by in 0..nby {
for bx in 0..nbx {
if bx * 8 < w8 && by * 8 < h8 {
let (d, v) = cdef::cdef_direction(&recon[0], w8, bx * 8, by * 8, bd);
ldirs[by * nbx + bx] = d;
lvars[by * nbx + bx] = v;
}
}
}
let lskip: Vec<bool> = (0..nbx * nby)
.map(|i| {
let (bx, by) = (i % nbx, i / nbx);
skip8.get(by * sb8w + bx).copied().unwrap_or(true)
})
.collect();
let luma_margin: i64 = if base_q_idx >= 180 { 12 } else { 22 };
let (yp, ys) = cdef_search_plane(
&recon[0],
&src[0],
w8,
h8,
&ldirs,
&lvars,
&lskip,
nbx,
damping,
bd,
luma_margin,
1000,
);
let uv_dir: [usize; 8] = if sub_x == 1 && sub_y == 0 {
[7, 0, 2, 4, 5, 6, 6, 6] } else {
[0, 1, 2, 3, 4, 5, 6, 7] };
let chroma_damping = damping - 1;
let (up, us) = if mono {
(0, 0)
} else {
cdef_search_chroma(
&recon[1],
&src[1],
cw8,
ch8,
&ldirs,
&uv_dir,
skip8,
sb8w,
nbx,
nby,
sub_x,
sub_y,
chroma_damping,
bd,
)
};
if yp == 0 && ys == 0 && up == 0 && us == 0 {
return None;
}
apply_cdef_plane(
&mut recon[0],
w8,
h8,
&ldirs,
&lvars,
&lskip,
nbx,
yp,
ys,
damping,
bd,
);
if !mono && (up != 0 || us != 0) {
#[allow(clippy::needless_range_loop)]
for plane in 1..3 {
apply_cdef_chroma(
&mut recon[plane],
cw8,
ch8,
&ldirs,
&uv_dir,
skip8,
sb8w,
nbx,
nby,
sub_x,
sub_y,
up,
us,
chroma_damping,
bd,
);
}
}
Some(crate::obu::CdefParams {
bits: 0,
damping: signalled_damping as u8,
strengths: vec![(yp as u8, ys as u8, up as u8, us as u8)],
})
}
#[allow(clippy::too_many_arguments)]
fn cdef_search_plane(
recon: &[i32],
src: &[i32],
w: usize,
h: usize,
dirs: &[usize],
vars: &[i32],
skip: &[bool],
nbx: usize,
damping: i32,
bd: u8,
margin_num: i64,
margin_den: i64,
) -> (i32, i32) {
use crate::cdef;
let mut off_sse = 0i64;
for y in (0..h).step_by(8) {
for x in (0..w).step_by(8) {
off_sse += plane_block_sse(recon, src, w, h, x, y);
}
}
let threshold = off_sse - (off_sse.saturating_mul(margin_num) / margin_den.max(1));
let candidates: Vec<(i32, i32)> = cdef::PRI_CANDIDATES
.iter()
.flat_map(|&pri| cdef::SEC_CANDIDATES.iter().map(move |&sec| (pri, sec)))
.filter(|&(pri, sec)| !(pri == 0 && sec == 0))
.collect();
let eval = |pri: i32, sec: i32| -> i64 {
let mut tmp = recon.to_vec();
let mut sse = 0i64;
for y in (0..h).step_by(8) {
for x in (0..w).step_by(8) {
let bxi = x / 8;
let byi = y / 8;
let bi = byi * nbx + bxi;
if skip.get(bi).copied().unwrap_or(true) {
sse += plane_block_sse(recon, src, w, h, x, y);
continue;
}
let dir = dirs[bi];
let var = vars[bi];
let apri = cdef::adjust_pri(pri << (bd - 8), var);
cdef::cdef_filter_8x8(
&mut tmp,
recon,
w,
x,
y,
apri,
sec << (bd - 8),
dir,
damping,
bd,
);
sse += plane_block_sse(&tmp, src, w, h, x, y);
}
}
sse
};
let want = resolve_threads(0).clamp(1, candidates.len().max(1));
let mut sses: Vec<i64> = vec![0; candidates.len()];
if want <= 1 || candidates.len() <= 1 {
for (slot, &(pri, sec)) in sses.iter_mut().zip(candidates.iter()) {
*slot = eval(pri, sec);
}
} else {
let chunk = candidates.len().div_ceil(want);
std::thread::scope(|scope| {
for (cs, os) in candidates.chunks(chunk).zip(sses.chunks_mut(chunk)) {
let eval = &eval;
scope.spawn(move || {
for (&(pri, sec), o) in cs.iter().zip(os.iter_mut()) {
*o = eval(pri, sec);
}
});
}
});
}
let mut best = (0i32, 0i32);
let mut best_sse = off_sse;
for (&(pri, sec), &sse) in candidates.iter().zip(sses.iter()) {
if sse < best_sse && sse <= threshold {
best_sse = sse;
best = (pri, sec);
}
}
best
}
fn plane_block_sse(a: &[i32], b: &[i32], w: usize, h: usize, x: usize, y: usize) -> i64 {
let mut s = 0i64;
let yh = (y + 8).min(h);
let xw = (x + 8).min(w);
for yy in y..yh {
for xx in x..xw {
let d = (a[yy * w + xx] - b[yy * w + xx]) as i64;
s += d * d;
}
}
s
}
#[allow(clippy::too_many_arguments)]
fn apply_cdef_plane(
plane: &mut [i32],
w: usize,
h: usize,
dirs: &[usize],
vars: &[i32],
skip: &[bool],
nbx: usize,
pri: i32,
sec: i32,
damping: i32,
bd: u8,
) {
use crate::cdef;
let snapshot = plane.to_vec();
for y in (0..h).step_by(8) {
for x in (0..w).step_by(8) {
let bxi = x / 8;
let byi = y / 8;
let bi = byi * nbx + bxi;
if skip.get(bi).copied().unwrap_or(true) {
continue;
}
let dir = dirs[bi];
let var = vars[bi];
let apri = cdef::adjust_pri(pri << (bd - 8), var);
cdef::cdef_filter_8x8(
plane,
&snapshot,
w,
x,
y,
apri,
sec << (bd - 8),
dir,
damping,
bd,
);
}
}
}
#[allow(clippy::too_many_arguments)]
fn apply_cdef_chroma(
plane: &mut [i32],
cw: usize,
ch: usize,
ldirs: &[usize],
uv_dir: &[usize; 8],
skip8: &[bool],
sb8w: usize,
nbx: usize,
nby: usize,
sub_x: usize,
sub_y: usize,
pri: i32,
sec: i32,
damping: i32,
bd: u8,
) {
use crate::cdef;
let snapshot = plane.to_vec();
let cbw = 8 >> sub_x; let cbh = 8 >> sub_y;
for lby in 0..nby {
for lbx in 0..nbx {
if skip8.get(lby * sb8w + lbx).copied().unwrap_or(true) {
continue;
}
let cx = (lbx * 8) >> sub_x;
let cy = (lby * 8) >> sub_y;
if cx >= cw || cy >= ch {
continue;
}
let ld = ldirs.get(lby * nbx + lbx).copied().unwrap_or(0);
let dir = uv_dir[ld];
cdef::cdef_filter_block(
plane,
&snapshot,
cw,
cx,
cy,
cbw,
cbh,
pri << (bd - 8),
sec << (bd - 8),
dir,
damping,
bd,
);
}
}
}
#[allow(clippy::too_many_arguments)]
fn cdef_search_chroma(
recon: &[i32],
src: &[i32],
cw: usize,
ch: usize,
ldirs: &[usize],
uv_dir: &[usize; 8],
skip8: &[bool],
sb8w: usize,
nbx: usize,
nby: usize,
sub_x: usize,
sub_y: usize,
damping: i32,
bd: u8,
) -> (i32, i32) {
use crate::cdef;
let cbw = 8 >> sub_x;
let cbh = 8 >> sub_y;
let mut off_sse = 0i64;
for y in (0..ch).step_by(8) {
for x in (0..cw).step_by(8) {
off_sse += plane_block_sse(recon, src, cw, ch, x, y);
}
}
let candidates: Vec<(i32, i32)> = cdef::PRI_CANDIDATES
.iter()
.flat_map(|&pri| cdef::SEC_CANDIDATES.iter().map(move |&sec| (pri, sec)))
.filter(|&(pri, sec)| !(pri == 0 && sec == 0))
.collect();
let eval = |pri: i32, sec: i32| -> i64 {
let mut tmp = recon.to_vec();
for lby in 0..nby {
for lbx in 0..nbx {
if skip8.get(lby * sb8w + lbx).copied().unwrap_or(true) {
continue;
}
let cx = (lbx * 8) >> sub_x;
let cy = (lby * 8) >> sub_y;
if cx >= cw || cy >= ch {
continue;
}
let ld = ldirs.get(lby * nbx + lbx).copied().unwrap_or(0);
let dir = uv_dir[ld];
cdef::cdef_filter_block(
&mut tmp,
recon,
cw,
cx,
cy,
cbw,
cbh,
pri << (bd - 8),
sec << (bd - 8),
dir,
damping,
bd,
);
}
}
let mut sse = 0i64;
for y in (0..ch).step_by(8) {
for x in (0..cw).step_by(8) {
sse += plane_block_sse(&tmp, src, cw, ch, x, y);
}
}
sse
};
let want = resolve_threads(0).clamp(1, candidates.len().max(1));
let mut sses: Vec<i64> = vec![0; candidates.len()];
if want <= 1 || candidates.len() <= 1 {
for (slot, &(pri, sec)) in sses.iter_mut().zip(candidates.iter()) {
*slot = eval(pri, sec);
}
} else {
let chunk = candidates.len().div_ceil(want);
std::thread::scope(|scope| {
for (cs, os) in candidates.chunks(chunk).zip(sses.chunks_mut(chunk)) {
let eval = &eval;
scope.spawn(move || {
for (&(pri, sec), o) in cs.iter().zip(os.iter_mut()) {
*o = eval(pri, sec);
}
});
}
});
}
let mut best = (0i32, 0i32);
let mut best_sse = off_sse;
for (&(pri, sec), &sse) in candidates.iter().zip(sses.iter()) {
if sse < best_sse {
best_sse = sse;
best = (pri, sec);
}
}
best
}
#[allow(clippy::too_many_arguments)]
fn frame_deblock(
recon: &mut [Vec<i32>; 3],
w8: usize,
h8: usize,
cw8: usize,
ch8: usize,
blk4: &[u8], blk4h: &[u8], nc4: usize, sub_x: usize,
sub_y: usize,
mono: bool,
level_y: i32,
level_uv: i32,
bd: u8,
) {
if level_y > 0 {
crate::loopfilter::filter_plane(
&mut recon[0],
w8,
h8,
blk4,
blk4h,
nc4,
level_y,
true,
16, bd,
);
}
if mono || level_uv <= 0 {
return;
}
let ss_hor = sub_x;
let ss_ver = sub_y;
let cw = cw8;
let ch = ch8;
let cnc4 = cw / 4;
let cnr4 = ch / 4;
let mut cbw4 = vec![0u8; cnc4 * cnr4];
let mut cbh4 = vec![0u8; cnc4 * cnr4];
for cr in 0..cnr4 {
for cc in 0..cnc4 {
let lr = cr << ss_ver;
let lc = cc << ss_hor;
let dw = blk4[lr * nc4 + lc];
let dh = blk4h[lr * nc4 + lc];
cbw4[cr * cnc4 + cc] = (dw >> ss_hor).max(1);
cbh4[cr * cnc4 + cc] = (dh >> ss_ver).max(1);
}
}
let csb = 16 >> ss_ver;
#[allow(clippy::needless_range_loop)]
for plane in 1..3 {
crate::loopfilter::filter_plane(
&mut recon[plane],
cw,
ch,
&cbw4,
&cbh4,
cnc4,
level_uv,
false,
csb,
bd,
);
}
}
fn assemble_tilegroup(payloads: Vec<Vec<u8>>) -> Vec<u8> {
if payloads.len() == 1 {
return payloads.into_iter().next().unwrap();
}
let mut out = Vec::new();
out.push(0u8);
let last = payloads.len() - 1;
for (i, p) in payloads.iter().enumerate() {
if i != last {
let sz_minus_1 = (p.len() - 1) as u32; out.extend_from_slice(&sz_minus_1.to_le_bytes());
}
out.extend_from_slice(p);
}
out
}
fn assemble_frame_obus(
base_q_idx: u8,
plan: &Tiling,
tilegroup: &[u8],
mono: bool,
aq: bool,
cdef: Option<&crate::obu::CdefParams>,
lr: Option<&crate::obu::LrParams>,
) -> Vec<u8> {
if plan.tcl + plan.trl > 0 {
let fh = frame_header_lossy_multitile_th(
base_q_idx,
&plan.cols_incr,
&plan.rows_incr,
plan.tcl,
plan.trl,
mono,
aq,
cdef,
lr,
);
wrap_obu_frame_split(&fh, tilegroup)
} else {
let fh = frame_header_lossy_multitile(
base_q_idx,
&plan.cols_incr,
&plan.rows_incr,
0,
0,
mono,
aq,
cdef,
lr,
);
wrap_obu_frame(&fh, tilegroup)
}
}
pub(crate) fn encode_lossless_frame_obus(
bd: u8,
w8: usize,
h8: usize,
src: &[Vec<i16>; 3],
threads: usize,
) -> Vec<u8> {
let (tilegroup, plan) = encode_lossless_tilegroup(bd, w8, h8, src, threads);
assemble_lossless_frame_obus(&plan, &tilegroup)
}
fn encode_one_lossless_tile(
bd: u8,
full_w: usize,
src: &[Vec<i16>; 3],
r: &(usize, usize, usize, usize),
) -> Vec<u8> {
let (x0, y0, tw, th) = *r;
let p0 = crop_plane(&src[0], full_w, x0, y0, tw, th);
let p1 = crop_plane(&src[1], full_w, x0, y0, tw, th);
let p2 = crop_plane(&src[2], full_w, x0, y0, tw, th);
crate::tile::encode_tile_lossless(tw, th, bd, [&p0, &p1, &p2])
}
fn encode_lossless_tilegroup(
bd: u8,
w8: usize,
h8: usize,
src: &[Vec<i16>; 3],
threads: usize,
) -> (Vec<u8>, Tiling) {
let sb_cols = w8.div_ceil(64) as u32;
let sb_rows = h8.div_ceil(64) as u32;
let want = resolve_threads(threads);
let plan = plan_tiling(sb_cols, sb_rows, want);
let col_starts = tile_starts_sb(sb_cols, plan.tcl);
let row_starts = tile_starts_sb(sb_rows, plan.trl);
let mut rects: Vec<(usize, usize, usize, usize)> =
Vec::with_capacity(col_starts.len() * row_starts.len());
for (ti, &rsb) in row_starts.iter().enumerate() {
let y0 = rsb as usize * 64;
let y1 = (row_starts.get(ti + 1).map_or(sb_rows, |&n| n) as usize * 64).min(h8);
for (tj, &csb) in col_starts.iter().enumerate() {
let x0 = csb as usize * 64;
let x1 = (col_starts.get(tj + 1).map_or(sb_cols, |&n| n) as usize * 64).min(w8);
rects.push((x0, y0, x1 - x0, y1 - y0));
}
}
let n = rects.len();
let nthreads = want.clamp(1, n.max(1));
let payloads: Vec<Vec<u8>> = if nthreads <= 1 || n <= 1 {
rects
.iter()
.map(|r| encode_one_lossless_tile(bd, w8, src, r))
.collect()
} else {
let mut slots: Vec<Option<Vec<u8>>> = (0..n).map(|_| None).collect();
let chunk = n.div_ceil(nthreads);
std::thread::scope(|scope| {
for (rs, os) in rects.chunks(chunk).zip(slots.chunks_mut(chunk)) {
scope.spawn(move || {
for (r, o) in rs.iter().zip(os.iter_mut()) {
*o = Some(encode_one_lossless_tile(bd, w8, src, r));
}
});
}
});
slots.into_iter().map(|o| o.unwrap()).collect()
};
(assemble_tilegroup(payloads), plan)
}
fn assemble_lossless_frame_obus(plan: &Tiling, tilegroup: &[u8]) -> Vec<u8> {
if plan.tcl + plan.trl > 0 {
let fh = crate::obu::frame_header_lossless_multitile_th(
&plan.cols_incr,
&plan.rows_incr,
plan.tcl,
plan.trl,
);
wrap_obu_frame_split(&fh, tilegroup)
} else {
let fh =
crate::obu::frame_header_lossless_multitile(&plan.cols_incr, &plan.rows_incr, 0, 0);
wrap_obu_frame(&fh, tilegroup)
}
}
fn encode_one_lossless_tile_mono(
bd: u8,
full_w: usize,
luma: &[i16],
r: &(usize, usize, usize, usize),
) -> Vec<u8> {
let (x0, y0, tw, th) = *r;
let p0 = crop_plane(luma, full_w, x0, y0, tw, th);
crate::tile::encode_tile_lossless_mono(tw, th, bd, &p0)
}
fn encode_lossless_mono_tilegroup(
bd: u8,
w8: usize,
h8: usize,
luma: &[i16],
threads: usize,
) -> (Vec<u8>, Tiling) {
let sb_cols = w8.div_ceil(64) as u32;
let sb_rows = h8.div_ceil(64) as u32;
let want = resolve_threads(threads);
let plan = plan_tiling(sb_cols, sb_rows, want);
let col_starts = tile_starts_sb(sb_cols, plan.tcl);
let row_starts = tile_starts_sb(sb_rows, plan.trl);
let mut rects: Vec<(usize, usize, usize, usize)> =
Vec::with_capacity(col_starts.len() * row_starts.len());
for (ti, &rsb) in row_starts.iter().enumerate() {
let y0 = rsb as usize * 64;
let y1 = (row_starts.get(ti + 1).map_or(sb_rows, |&n| n) as usize * 64).min(h8);
for (tj, &csb) in col_starts.iter().enumerate() {
let x0 = csb as usize * 64;
let x1 = (col_starts.get(tj + 1).map_or(sb_cols, |&n| n) as usize * 64).min(w8);
rects.push((x0, y0, x1 - x0, y1 - y0));
}
}
let n = rects.len();
let nthreads = want.clamp(1, n.max(1));
let payloads: Vec<Vec<u8>> = if nthreads <= 1 || n <= 1 {
rects
.iter()
.map(|r| encode_one_lossless_tile_mono(bd, w8, luma, r))
.collect()
} else {
let mut slots: Vec<Option<Vec<u8>>> = (0..n).map(|_| None).collect();
let chunk = n.div_ceil(nthreads);
std::thread::scope(|scope| {
for (rs, os) in rects.chunks(chunk).zip(slots.chunks_mut(chunk)) {
scope.spawn(move || {
for (r, o) in rs.iter().zip(os.iter_mut()) {
*o = Some(encode_one_lossless_tile_mono(bd, w8, luma, r));
}
});
}
});
slots.into_iter().map(|o| o.unwrap()).collect()
};
(assemble_tilegroup(payloads), plan)
}
fn assemble_lossless_mono_frame_obus(plan: &Tiling, tilegroup: &[u8]) -> Vec<u8> {
if plan.tcl + plan.trl > 0 {
let fh = crate::obu::frame_header_lossless_mono_multitile_th(
&plan.cols_incr,
&plan.rows_incr,
plan.tcl,
plan.trl,
);
wrap_obu_frame_split(&fh, tilegroup)
} else {
let fh = crate::obu::frame_header_lossless_mono_multitile(
&plan.cols_incr,
&plan.rows_incr,
0,
0,
);
wrap_obu_frame(&fh, tilegroup)
}
}
pub(crate) fn encode_lossless_mono_frame_obus(
bd: u8,
w8: usize,
h8: usize,
luma: &[i16],
threads: usize,
) -> Vec<u8> {
let (tilegroup, plan) = encode_lossless_mono_tilegroup(bd, w8, h8, luma, threads);
assemble_lossless_mono_frame_obus(&plan, &tilegroup)
}
pub(crate) fn encode_av1_mono_lossless_image(
bd: u8,
w: usize,
h: usize,
luma: &[i16],
full_range: bool,
threads: usize,
) -> Vec<u8> {
assert_eq!(luma.len(), w * h, "luma plane must be w*h");
assert!(w > 0 && h > 0, "width/height must be non-zero");
let (w8, h8) = (align8(w), align8(h));
let padded = pad_to_mult8(luma, w, h, w8, h8);
let mut bytes = Vec::new();
bytes.extend_from_slice(&temporal_delimiter());
bytes.extend_from_slice(&crate::obu::sequence_header_mono(
w as u32, h as u32, bd, full_range,
));
bytes.extend_from_slice(&encode_lossless_mono_frame_obus(
bd, w8, h8, &padded, threads,
));
bytes
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn encode_av1_lossy_image_cs(
base_q_idx: u8,
bd: u8,
w: usize,
h: usize,
luma: &[i32],
u: &[i32],
v: &[i32],
color: Option<&crate::color::Cicp>,
threads: usize,
speed: Speed,
aq: bool,
vb: VarianceBoost,
cdef: bool,
wiener: bool,
) -> Vec<u8> {
encode_av1_lossy_image_cs_recon_dbg(
base_q_idx, bd, w, h, luma, u, v, color, threads, speed, aq, &vb, cdef, wiener,
)
.0
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn encode_av1_lossy_image_cs_recon_dbg(
base_q_idx: u8,
bd: u8,
w: usize,
h: usize,
luma: &[i32],
u: &[i32],
v: &[i32],
color: Option<&crate::color::Cicp>,
threads: usize,
speed: Speed,
aq: bool,
vb: &VarianceBoost,
cdef: bool,
wiener: bool,
) -> (Vec<u8>, [Vec<i32>; 3], (usize, usize)) {
assert_eq!(luma.len(), w * h);
assert!(w > 0 && h > 0, "width/height must be non-zero");
let (w8, h8) = (align8(w), align8(h));
let src = [
pad_to_mult8(luma, w, h, w8, h8),
pad_to_mult8(u, w, h, w8, h8),
pad_to_mult8(v, w, h, w8, h8),
];
let (payload, recon, plan, cdefp, lrp) = encode_lossy_tilegroup(
base_q_idx, bd, w8, h8, &src, 0, 0, false, threads, speed, aq, vb, cdef, wiener,
);
let profile = if bd == 12 { 2 } else { 1 };
let mut bytes = Vec::new();
bytes.extend_from_slice(&temporal_delimiter());
bytes.extend_from_slice(&crate::obu::sequence_header_cicp(
w as u32, h as u32, profile, bd, color,
));
bytes.extend_from_slice(&assemble_frame_obus(
base_q_idx,
&plan,
&payload,
false,
aq,
cdefp.as_ref(),
lrp.as_ref(),
));
(bytes, recon, (w8, h8))
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn encode_av1_lossy_image_422(
base_q_idx: u8,
bd: u8,
w: usize,
h: usize,
luma: &[i32],
u: &[i32],
v: &[i32],
color: Option<&crate::color::Cicp>,
threads: usize,
speed: Speed,
aq: bool,
vb: VarianceBoost,
cdef: bool,
wiener: bool,
) -> Vec<u8> {
encode_av1_lossy_image_422_recon_dbg(
base_q_idx, bd, w, h, luma, u, v, color, threads, speed, aq, vb, cdef, wiener,
)
.0
}
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
pub(crate) fn encode_av1_lossy_image_422_recon_dbg(
base_q_idx: u8,
bd: u8,
w: usize,
h: usize,
luma: &[i32],
u: &[i32],
v: &[i32],
color: Option<&crate::color::Cicp>,
threads: usize,
speed: Speed,
aq: bool,
vb: VarianceBoost,
cdef: bool,
wiener: bool,
) -> (Vec<u8>, [Vec<i32>; 3], (usize, usize, usize)) {
assert_eq!(luma.len(), w * h);
assert!(w > 0 && h > 0, "width/height must be non-zero");
let cw = w.div_ceil(2);
assert_eq!(u.len(), cw * h);
assert_eq!(v.len(), cw * h);
let (w8, h8) = (align8(w), align8(h));
let cw8 = w8 / 2;
let luma_p: Vec<i32> = pad_to_mult8(luma, w, h, w8, h8);
let pad_c = |p: &[i32]| -> Vec<i32> { pad_to_mult8(p, cw, h, cw8, h8) };
let src = [luma_p, pad_c(u), pad_c(v)];
let (payload, recon, plan, cdefp, lrp) = encode_lossy_tilegroup(
base_q_idx, bd, w8, h8, &src, 1, 0, false, threads, speed, aq, &vb, cdef, wiener,
);
let mut bytes = Vec::new();
bytes.extend_from_slice(&temporal_delimiter());
bytes.extend_from_slice(&crate::obu::sequence_header_cicp_ss(
w as u32, h as u32, 2, bd, color, 1, 0,
));
bytes.extend_from_slice(&assemble_frame_obus(
base_q_idx,
&plan,
&payload,
false,
aq,
cdefp.as_ref(),
lrp.as_ref(),
));
(bytes, recon, (w8, h8, cw8))
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn encode_av1_lossy_image_420(
base_q_idx: u8,
bd: u8,
w: usize,
h: usize,
luma: &[i32],
u: &[i32],
v: &[i32],
color: Option<&crate::color::Cicp>,
threads: usize,
speed: Speed,
aq: bool,
vb: VarianceBoost,
cdef: bool,
wiener: bool,
) -> Vec<u8> {
encode_av1_lossy_image_420_recon_dbg(
base_q_idx, bd, w, h, luma, u, v, color, threads, speed, aq, vb, cdef, wiener,
)
.0
}
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
pub(crate) fn encode_av1_lossy_image_420_recon_dbg(
base_q_idx: u8,
bd: u8,
w: usize,
h: usize,
luma: &[i32],
u: &[i32],
v: &[i32],
color: Option<&crate::color::Cicp>,
threads: usize,
speed: Speed,
aq: bool,
vb: VarianceBoost,
cdef: bool,
wiener: bool,
) -> (Vec<u8>, [Vec<i32>; 3], (usize, usize, usize, usize)) {
assert_eq!(luma.len(), w * h);
assert!(w > 0 && h > 0, "width/height must be non-zero");
let (cw, ch) = (w.div_ceil(2), h.div_ceil(2));
assert_eq!(u.len(), cw * ch);
assert_eq!(v.len(), cw * ch);
let (w8, h8) = (align8(w), align8(h));
let (cw8, ch8) = (w8 / 2, h8 / 2);
let luma_p: Vec<i32> = pad_to_mult8(luma, w, h, w8, h8);
let pad_c = |p: &[i32]| -> Vec<i32> { pad_to_mult8(p, cw, ch, cw8, ch8) };
let src = [luma_p, pad_c(u), pad_c(v)];
let (payload, recon, plan, cdefp, lrp) = encode_lossy_tilegroup(
base_q_idx, bd, w8, h8, &src, 1, 1, false, threads, speed, aq, &vb, cdef, wiener,
);
let profile = if bd == 12 { 2 } else { 0 };
let mut bytes = Vec::new();
bytes.extend_from_slice(&temporal_delimiter());
bytes.extend_from_slice(&crate::obu::sequence_header_cicp_ss(
w as u32, h as u32, profile, bd, color, 1, 1,
));
bytes.extend_from_slice(&assemble_frame_obus(
base_q_idx,
&plan,
&payload,
false,
aq,
cdefp.as_ref(),
lrp.as_ref(),
));
(bytes, recon, (w8, h8, cw8, ch8))
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn encode_av1_mono_image(
base_q_idx: u8,
bd: u8,
w: usize,
h: usize,
luma: &[i32],
full_range: bool,
threads: usize,
speed: Speed,
aq: bool,
vb: VarianceBoost,
cdef: bool,
wiener: bool,
) -> Vec<u8> {
let (bytes, _recon, _w8, _h8) = encode_av1_mono_image_recon_dbg(
base_q_idx, bd, w, h, luma, full_range, threads, speed, aq, vb, cdef, wiener,
);
bytes
}
#[doc(hidden)]
#[allow(clippy::too_many_arguments)]
pub(crate) fn encode_av1_mono_image_recon_dbg(
base_q_idx: u8,
bd: u8,
w: usize,
h: usize,
luma: &[i32],
full_range: bool,
threads: usize,
speed: Speed,
aq: bool,
vb: VarianceBoost,
cdef: bool,
wiener: bool,
) -> (Vec<u8>, Vec<i32>, usize, usize) {
assert_eq!(luma.len(), w * h, "luma plane must be w*h");
assert!(w > 0 && h > 0, "width/height must be non-zero");
let (w8, h8) = (align8(w), align8(h));
let src = [pad_to_mult8(luma, w, h, w8, h8), Vec::new(), Vec::new()];
let (payload, recon, plan, cdefp, lrp) = encode_lossy_tilegroup(
base_q_idx, bd, w8, h8, &src, 0, 0, true, threads, speed, aq, &vb, cdef, wiener,
);
let mut bytes = Vec::new();
bytes.extend_from_slice(&temporal_delimiter());
bytes.extend_from_slice(&crate::obu::sequence_header_mono(
w as u32, h as u32, bd, full_range,
));
bytes.extend_from_slice(&assemble_frame_obus(
base_q_idx,
&plan,
&payload,
true,
aq,
cdefp.as_ref(),
lrp.as_ref(),
));
let [luma_recon, _, _] = recon;
(bytes, luma_recon, w8, h8)
}