X-Git-Url: https://git.nihav.org/?p=nihav.git;a=blobdiff_plain;f=nihav-duck%2Fsrc%2Fcodecs%2Fvp7enc%2Fcoder.rs;fp=nihav-duck%2Fsrc%2Fcodecs%2Fvp7enc%2Fcoder.rs;h=fcd885ac2d749b1a937ee874e6713e3793c0c2a4;hp=0000000000000000000000000000000000000000;hb=c5d5793c1fd18882a32acabb8141a221b0a97b61;hpb=b922b48d3b003b2f4b84755541fd9dc4be8f22f6 diff --git a/nihav-duck/src/codecs/vp7enc/coder.rs b/nihav-duck/src/codecs/vp7enc/coder.rs new file mode 100644 index 0000000..fcd885a --- /dev/null +++ b/nihav-duck/src/codecs/vp7enc/coder.rs @@ -0,0 +1,601 @@ +use nihav_core::codecs::EncoderResult; +use nihav_codec_support::codecs::{MV, ZERO_MV}; +use crate::codecs::vpenc::coder::*; +use super::super::vpcommon::*; +use super::super::vp78::*; +use super::super::vp78data::*; +use super::super::vp7data::*; +use super::blocks::MBType; +use super::models::*; +pub use crate::codecs::vpenc::coder::{BoolEncoder, Estimator}; + +const KF_Y_MODE_TREE: &[TokenSeq] = &[ + bit_seq!(PredMode::BPred; F; 0), + bit_seq!(PredMode::DCPred; T, F, F; 0, 1, 2), + bit_seq!(PredMode::VPred; T, F, T; 0, 1, 2), + bit_seq!(PredMode::HPred; T, T, F; 0, 1, 3), + bit_seq!(PredMode::TMPred; T, T, T; 0, 1, 3), +]; + +const Y_MODE_TREE: &[TokenSeq] = &[ + bit_seq!(PredMode::DCPred; F; 0), + bit_seq!(PredMode::VPred; T, F, F; 0, 1, 2), + bit_seq!(PredMode::HPred; T, F, T; 0, 1, 2), + bit_seq!(PredMode::TMPred; T, T, F; 0, 1, 3), + bit_seq!(PredMode::BPred; T, T, T; 0, 1, 3), +]; + +const UV_MODE_TREE: &[TokenSeq] = &[ + bit_seq!(PredMode::DCPred; F; 0), + bit_seq!(PredMode::VPred; T, F; 0, 1), + bit_seq!(PredMode::HPred; T, T, F; 0, 1, 2), + bit_seq!(PredMode::TMPred; T, T, T; 0, 1, 2), +]; + +const B_MODE_TREE: &[TokenSeq] = &[ + bit_seq!(PredMode::DCPred; F; 0), + bit_seq!(PredMode::TMPred; T, F; 0, 1), + bit_seq!(PredMode::VPred; T, T, F; 0, 1, 2), + bit_seq!(PredMode::HPred; T, T, T, F, F; 0, 1, 2, 3, 4), + bit_seq!(PredMode::RDPred; T, T, T, F, T, F; 0, 1, 2, 3, 4, 5), + bit_seq!(PredMode::VRPred; T, T, T, F, T, T; 0, 1, 2, 3, 4, 5), + bit_seq!(PredMode::LDPred; T, T, T, T, F; 0, 1, 2, 3, 6), + bit_seq!(PredMode::VLPred; T, T, T, T, T, F; 0, 1, 2, 3, 6, 7), + bit_seq!(PredMode::HDPred; T, T, T, T, T, T, F; 0, 1, 2, 3, 6, 7, 8), + bit_seq!(PredMode::HUPred; T, T, T, T, T, T, T; 0, 1, 2, 3, 6, 7, 8), +]; + +const MV_REF_TREE: &[TokenSeq] = &[ + bit_seq!(VPMBType::InterNoMV; F; 0), + bit_seq!(VPMBType::InterNearest; T, F; 0, 1), + bit_seq!(VPMBType::InterNear; T, T, F; 0, 1, 2), + bit_seq!(VPMBType::InterMV; T, T, T, F; 0, 1, 2, 3), + bit_seq!(VPMBType::InterFourMV; T, T, T, T; 0, 1, 2, 3), +]; + +const COEF_TREE: &[TokenSeq] = &[ + bit_seq!(DCTToken::EOB; F; 0), + bit_seq!(DCTToken::Zero; T, F; 0, 1), + bit_seq!(DCTToken::One; T, T, F; 0, 1, 2), + bit_seq!(DCTToken::Two; T, T, T, F, F; 0, 1, 2, 3, 4), + bit_seq!(DCTToken::Three; T, T, T, F, T, F; 0, 1, 2, 3, 4, 5), + bit_seq!(DCTToken::Four; T, T, T, F, T, T; 0, 1, 2, 3, 4, 5), + bit_seq!(DCTToken::Cat1; T, T, T, T, F, F; 0, 1, 2, 3, 6, 7), + bit_seq!(DCTToken::Cat2; T, T, T, T, F, T; 0, 1, 2, 3, 6, 7), + bit_seq!(DCTToken::Cat3; T, T, T, T, T, F, F; 0, 1, 2, 3, 6, 8, 9), + bit_seq!(DCTToken::Cat4; T, T, T, T, T, F, T; 0, 1, 2, 3, 6, 8, 9), + bit_seq!(DCTToken::Cat5; T, T, T, T, T, T, F; 0, 1, 2, 3, 6, 8, 10), + bit_seq!(DCTToken::Cat6; T, T, T, T, T, T, T; 0, 1, 2, 3, 6, 8, 10), +]; + +const MV_TREE: &[TokenSeq] = &[ + bit_seq!(0; F, F, F, F; 0, 2, 3, 4), + bit_seq!(1; F, F, F, T; 0, 2, 3, 4), + bit_seq!(2; F, F, T, F; 0, 2, 3, 5), + bit_seq!(3; F, F, T, T; 0, 2, 3, 5), + bit_seq!(4; F, T, F, F; 0, 2, 6, 7), + bit_seq!(5; F, T, F, T; 0, 2, 6, 7), + bit_seq!(6; F, T, T, F; 0, 2, 6, 8), + bit_seq!(7; F, T, T, T; 0, 2, 6, 8), + bit_seq!(8; T; 0), +]; + +const MV_SPLIT_MODE_TREE: &[TokenSeq] = &[ + bit_seq!(MVSplitMode::Sixteenths; F; 0), + bit_seq!(MVSplitMode::Quarters; T, F; 0, 1), + bit_seq!(MVSplitMode::TopBottom; T, T, F; 0, 1, 2), + bit_seq!(MVSplitMode::LeftRight; T, T, T; 0, 1, 2), +]; + +const SUB_MV_REF_TREE: &[TokenSeq] = &[ + bit_seq!(SubMVRef::Left; F; 0), + bit_seq!(SubMVRef::Above; T, F; 0, 1), + bit_seq!(SubMVRef::Zero; T, T, F; 0, 1, 2), + bit_seq!(SubMVRef::New; T, T, T; 0, 1, 2), +]; + +const FEATURE_TREE: &[TokenSeq] = &[ + bit_seq!(0; F, F; 0, 1), + bit_seq!(1; F, T; 0, 1), + bit_seq!(2; T, F; 0, 2), + bit_seq!(3; T, T; 0, 2) +]; + +pub trait VP7BoolEncoder { + fn put_byte(&mut self, val: u8) -> EncoderResult<()>; + fn write_large_coef(&mut self, val: i16, cat: usize) -> EncoderResult<()>; + fn encode_subblock(&mut self, blk: &[i16; 16], ctype: usize, pctx: u8, models: &VP7Models) -> EncoderResult<()>; + fn encode_mv_component(&mut self, val: i16, probs: &[u8; 17]) -> EncoderResult<()>; + fn encode_mv(&mut self, mv: MV, models: &VP7Models) -> EncoderResult<()>; + fn encode_sub_mv(&mut self, stype: SubMVRef, mv: MV, models: &VP7Models) -> EncoderResult<()>; + fn encode_feature(&mut self, id: usize, feat: Option, models: &VP7Models) -> EncoderResult<()>; + fn encode_mb_type(&mut self, is_intra: bool, mb_type: &MBType, models: &VP7Models) -> EncoderResult<()>; +} + +impl<'a, 'b> VP7BoolEncoder for BoolEncoder<'a, 'b> { + fn put_byte(&mut self, val: u8) -> EncoderResult<()> { + self.put_bits(u32::from(val), 8) + } + fn write_large_coef(&mut self, val: i16, cat: usize) -> EncoderResult<()> { + let base = VP56_COEF_BASE[cat]; + let mut probs = VP56_COEF_ADD_PROBS[cat].iter(); + let add = val.abs() - base; + let mut mask = 1 << (VP6_COEF_ADD_BITS[cat] - 1); + while mask != 0 { + self.put_bool((add & mask) != 0, *probs.next().unwrap())?; + mask >>= 1; + } + self.put_bool(val < 0, 128)?; + + Ok(()) + } + fn encode_subblock(&mut self, blk: &[i16; 16], ctype: usize, pctx: u8, models: &VP7Models) -> EncoderResult<()> { + let probs = &models.coef_probs[ctype]; + + let start = if ctype != 0 { 0 } else { 1 }; + let mut cval = pctx as usize; + + let mut last = 16; + for &idx in DEFAULT_SCAN_ORDER.iter().skip(start) { + if blk[idx] != 0 { + last = idx; + } + } + + if last == 16 { + self.write_el(DCTToken::EOB, COEF_TREE, &probs[COEF_BANDS[start]][cval])?; + return Ok(()); + } + + for i in start..16 { + let val = blk[DEFAULT_SCAN_ORDER[i]]; + let token = match val.abs() { + 0 => DCTToken::Zero, + 1 => DCTToken::One, + 2 => DCTToken::Two, + 3 => DCTToken::Three, + 4 => DCTToken::Four, + 5..=6 => DCTToken::Cat1, + 7 ..=10 => DCTToken::Cat2, + 11..=18 => DCTToken::Cat3, + 19..=34 => DCTToken::Cat4, + 35..=66 => DCTToken::Cat5, + _ => DCTToken::Cat6, + }; + self.write_el(token, COEF_TREE, &probs[COEF_BANDS[i]][cval])?; + match token { + DCTToken::Zero => {}, + DCTToken::One | + DCTToken::Two | + DCTToken::Three | + DCTToken::Four => self.put_bool(val < 0, 128)?, + DCTToken::Cat1 => self.write_large_coef(val, 0)?, + DCTToken::Cat2 => self.write_large_coef(val, 1)?, + DCTToken::Cat3 => self.write_large_coef(val, 2)?, + DCTToken::Cat4 => self.write_large_coef(val, 3)?, + DCTToken::Cat5 => self.write_large_coef(val, 4)?, + DCTToken::Cat6 => self.write_large_coef(val, 5)?, + DCTToken::EOB => {}, + }; + cval = val.abs().min(2) as usize; + + if DEFAULT_SCAN_ORDER[i] == last { + if DEFAULT_SCAN_ORDER[i] != 15 { + self.write_el(DCTToken::EOB, COEF_TREE, &probs[COEF_BANDS[i + 1]][cval])?; + } + break; + } + } + Ok(()) + } + fn encode_mv_component(&mut self, val: i16, probs: &[u8; 17]) -> EncoderResult<()> { + let aval = val.abs(); + self.write_el(aval.min(8), MV_TREE, probs)?; + if aval >= 8 { + for &ord in LONG_VECTOR_ORDER.iter() { + self.put_bool(((aval >> ord) & 1) != 0, probs[ord + 9])?; + } + if (aval & 0xF0) != 0 { + self.put_bool((aval & (1 << 3)) != 0, probs[3 + 9])?; + } + } + if val != 0 { + self.put_bool(val < 0, probs[1])?; + } + Ok(()) + } + fn encode_mv(&mut self, mv: MV, models: &VP7Models) -> EncoderResult<()> { + self.encode_mv_component(mv.y, &models.mv_probs[0])?; + self.encode_mv_component(mv.x, &models.mv_probs[1])?; + Ok(()) + } + fn encode_sub_mv(&mut self, stype: SubMVRef, mv: MV, models: &VP7Models) -> EncoderResult<()> { + self.write_el(stype, SUB_MV_REF_TREE, &SUB_MV_REF_PROBS)?; + if stype == SubMVRef::New { + self.encode_mv_component(mv.y, &models.mv_probs[0])?; + self.encode_mv_component(mv.x, &models.mv_probs[1])?; + } + Ok(()) + } + fn encode_feature(&mut self, id: usize, feat: Option, models: &VP7Models) -> EncoderResult<()> { + self.put_bool(feat.is_some(), models.feature_present[id])?; + if let Some(num) = feat { + self.write_el(num, FEATURE_TREE, &models.feature_tree_probs[id])?; + } + Ok(()) + } + fn encode_mb_type(&mut self, is_intra: bool, mb_type: &MBType, models: &VP7Models) -> EncoderResult<()> { + if !is_intra { + self.put_bool(!mb_type.is_intra(), models.prob_intra_pred)?; + if !mb_type.is_intra() { + let last = mb_type.get_last(); + self.put_bool(!last, models.prob_last_pred)?; + } + } + match *mb_type { + MBType::Intra(ymode, uvmode) => { + if is_intra { + self.write_el(ymode, KF_Y_MODE_TREE, KF_Y_MODE_TREE_PROBS)?; + self.write_el(uvmode, UV_MODE_TREE, KF_UV_MODE_TREE_PROBS)?; + } else { + self.write_el(ymode, Y_MODE_TREE, &models.kf_ymode_prob)?; + self.write_el(uvmode, UV_MODE_TREE, &models.kf_uvmode_prob)?; + } + }, + MBType::Intra4x4(ymodes, yctx, uvmode) => { + if is_intra { + self.write_el(PredMode::BPred, KF_Y_MODE_TREE, KF_Y_MODE_TREE_PROBS)?; + for (&ypred, &yctx) in ymodes.iter().zip(yctx.iter()) { + let top_idx = (yctx / 10) as usize; + let left_idx = (yctx % 10) as usize; + self.write_el(ypred, B_MODE_TREE, &KF_B_MODE_TREE_PROBS[top_idx][left_idx])?; + } + self.write_el(uvmode, UV_MODE_TREE, KF_UV_MODE_TREE_PROBS)?; + } else { + self.write_el(PredMode::BPred, Y_MODE_TREE, &models.kf_ymode_prob)?; + for &ypred in ymodes.iter() { + self.write_el(ypred, B_MODE_TREE, B_MODE_TREE_PROBS)?; + } + self.write_el(uvmode, UV_MODE_TREE, &models.kf_uvmode_prob)?; + } + }, + MBType::InterNoMV(_last, ref mv_probs) => { + self.write_el(VPMBType::InterNoMV, MV_REF_TREE, mv_probs)?; + }, + MBType::InterNearest(_last, ref mv_probs) => { + self.write_el(VPMBType::InterNearest, MV_REF_TREE, mv_probs)?; + }, + MBType::InterNear(_last, ref mv_probs) => { + self.write_el(VPMBType::InterNear, MV_REF_TREE, mv_probs)?; + }, + MBType::InterMV(_last, ref mv_probs, mv) => { + self.write_el(VPMBType::InterMV, MV_REF_TREE, mv_probs)?; + self.encode_mv(mv, models)?; + }, + MBType::InterSplitMV(_last, ref mv_probs, split_mode, stypes, mvs) => { + self.write_el(VPMBType::InterFourMV, MV_REF_TREE, mv_probs)?; + self.write_el(split_mode, MV_SPLIT_MODE_TREE, &MV_SPLIT_MODE_PROBS)?; + match split_mode { + MVSplitMode::TopBottom | MVSplitMode::LeftRight => { + for (&stype, &mv) in stypes.iter().zip(mvs.iter()).take(2) { + self.encode_sub_mv(stype, mv, models)?; + } + }, + MVSplitMode::Quarters => { + for (&stype, &mv) in stypes.iter().zip(mvs.iter()).take(4) { + self.encode_sub_mv(stype, mv, models)?; + } + }, + MVSplitMode::Sixteenths => { + for (&stype, &mv) in stypes.iter().zip(mvs.iter()) { + self.encode_sub_mv(stype, mv, models)?; + } + }, + }; + }, + }; + Ok(()) + } +} + +pub fn encode_dct_coef_prob_upd(bc: &mut BoolEncoder, coef_probs: &[[[[u8; 11]; 3]; 8]; 4], prev: &[[[[u8; 11]; 3]; 8]; 4]) -> EncoderResult<()> { + for ((new, old), upd) in coef_probs.iter().zip(prev.iter()).zip(DCT_UPDATE_PROBS.iter()) { + for ((new, old), upd) in new.iter().zip(old.iter()).zip(upd.iter()) { + for ((new, old), upd) in new.iter().zip(old.iter()).zip(upd.iter()) { + for ((&new, &old), &upd) in new.iter().zip(old.iter()).zip(upd.iter()) { + bc.put_bool(new != old, upd)?; + if new != old { + bc.put_byte(new)?; + } + } + } + } + } + Ok(()) +} + +pub fn encode_mv_prob_upd(bc: &mut BoolEncoder, mv_probs: &[[u8; 17]; 2], prev: &[[u8; 17]; 2]) -> EncoderResult<()> { + for ((new, old), upd) in mv_probs.iter().zip(prev.iter()).zip(MV_UPDATE_PROBS.iter()) { + for ((&new, &old), &upd) in new.iter().zip(old.iter()).zip(upd.iter()) { + bc.put_bool(new != old, upd)?; + if new != old { + bc.put_bits(u32::from(new) >> 1, 7)?; + } + } + } + Ok(()) +} + +pub trait VP7Estimator { + fn estimate_subblock(&self, blk: &[i16; 16], ctype: usize, pctx: u8, models: &mut VP7ModelsStat); + fn estimate_mv_component(&self, val: i16, probs: &mut [ProbCounter; 17]); + fn estimate_mv(&self, mv: MV, models: &mut VP7ModelsStat); + fn estimate_sub_mv(&self, stype: SubMVRef, mv: MV, models: &mut VP7ModelsStat); + fn estimate_mb_type(&self, is_intra: bool, mb_type: &MBType, models: &mut VP7ModelsStat); + fn estimate_feature(&self, id: usize, feat: Option, models: &mut VP7ModelsStat); +} + +impl VP7Estimator for Estimator { + fn estimate_subblock(&self, blk: &[i16; 16], ctype: usize, pctx: u8, models: &mut VP7ModelsStat) { + let probs = &mut models.coef_probs[ctype]; + + let start = if ctype != 0 { 0 } else { 1 }; + let mut cval = pctx as usize; + + let mut last = 16; + for &idx in DEFAULT_SCAN_ORDER.iter().skip(start) { + if blk[idx] != 0 { + last = idx; + } + } + + if last == 16 { + self.write_el(DCTToken::EOB, COEF_TREE, &mut probs[COEF_BANDS[start]][cval]); + return; + } + + for i in start..16 { + let val = blk[DEFAULT_SCAN_ORDER[i]]; + let token = match val.abs() { + 0 => DCTToken::Zero, + 1 => DCTToken::One, + 2 => DCTToken::Two, + 3 => DCTToken::Three, + 4 => DCTToken::Four, + 5..=6 => DCTToken::Cat1, + 7 ..=10 => DCTToken::Cat2, + 11..=18 => DCTToken::Cat3, + 19..=34 => DCTToken::Cat4, + 35..=66 => DCTToken::Cat5, + _ => DCTToken::Cat6, + }; + self.write_el(token, COEF_TREE, &mut probs[COEF_BANDS[i]][cval]); + cval = val.abs().min(2) as usize; + + if DEFAULT_SCAN_ORDER[i] == last { + if DEFAULT_SCAN_ORDER[i] != 15 { + self.write_el(DCTToken::EOB, COEF_TREE, &mut probs[COEF_BANDS[i + 1]][cval]); + } + break; + } + } + } + fn estimate_mv_component(&self, val: i16, probs: &mut [ProbCounter; 17]) { + let aval = val.abs(); + self.write_el(aval.min(8), MV_TREE, probs); + if aval >= 8 { + for &ord in LONG_VECTOR_ORDER.iter() { + probs[ord + 9].add(((aval >> ord) & 1) != 0); + } + if (aval & 0xF0) != 0 { + probs[3 + 9].add((aval & (1 << 3)) != 0); + } + } + if val != 0 { + probs[1].add(val < 0); + } + } + fn estimate_mv(&self, mv: MV, models: &mut VP7ModelsStat) { + self.estimate_mv_component(mv.y, &mut models.mv_probs[0]); + self.estimate_mv_component(mv.x, &mut models.mv_probs[1]); + } + fn estimate_sub_mv(&self, stype: SubMVRef, mv: MV, models: &mut VP7ModelsStat) { + if stype == SubMVRef::New { + self.estimate_mv_component(mv.y, &mut models.mv_probs[0]); + self.estimate_mv_component(mv.x, &mut models.mv_probs[1]); + } + } + fn estimate_mb_type(&self, is_intra: bool, mb_type: &MBType, models: &mut VP7ModelsStat) { + if !is_intra { + models.prob_intra_pred.add(!mb_type.is_intra()); + if !mb_type.is_intra() { + let last = mb_type.get_last(); + models.prob_last_pred.add(!last); + } + } + match *mb_type { + MBType::Intra(ymode, cmode) => { + if !is_intra { + self.write_el(ymode, Y_MODE_TREE, &mut models.kf_ymode_prob); + self.write_el(cmode, UV_MODE_TREE, &mut models.kf_uvmode_prob); + } + }, + MBType::Intra4x4(_, _, cmode) => { + if !is_intra { + self.write_el(PredMode::BPred, Y_MODE_TREE, &mut models.kf_ymode_prob); + self.write_el(cmode, UV_MODE_TREE, &mut models.kf_uvmode_prob); + } + }, + MBType::InterMV(_last, _, mv) => { + self.estimate_mv(mv, models); + }, + MBType::InterSplitMV(_last, _, split_mode, stypes, mvs) => { + match split_mode { + MVSplitMode::TopBottom | MVSplitMode::LeftRight => { + for (&stype, &mv) in stypes.iter().zip(mvs.iter()).take(2) { + self.estimate_sub_mv(stype, mv, models); + } + }, + MVSplitMode::Quarters => { + for (&stype, &mv) in stypes.iter().zip(mvs.iter()).take(4) { + self.estimate_sub_mv(stype, mv, models); + } + }, + MVSplitMode::Sixteenths => { + for (&stype, &mv) in stypes.iter().zip(mvs.iter()) { + self.estimate_sub_mv(stype, mv, models); + } + }, + }; + }, + _ => {}, + }; + } + fn estimate_feature(&self, id: usize, feat: Option, models: &mut VP7ModelsStat) { + models.feature_present[id].add(feat.is_some()); + if let Some(num) = feat { + self.write_el(num, FEATURE_TREE, &mut models.feature_tree_probs[id]); + } + } +} + +fn code_nits(el: T, tree: &[TokenSeq], probs: &[u8]) -> u32 { + let mut nits = 0; + for entry in tree.iter() { + if entry.val == el { + for seq in entry.seq.iter() { + nits += Estimator::est_nits(seq.bit, probs[seq.idx as usize]); + } + return nits; + } + } + 0 +} +pub fn b_mode_nits(mode: PredMode) -> u32 { + code_nits(mode, B_MODE_TREE, &KF_B_MODE_TREE_PROBS[2][2]) // todo find better context +} +fn mv_component_nits(val: i16, probs: &[u8; 17]) -> u32 { + let aval = val.abs(); + let mut nits = code_nits(aval.min(8), MV_TREE, probs); + if aval >= 8 { + for &ord in LONG_VECTOR_ORDER.iter() { + nits += Estimator::est_nits(((aval >> ord) & 1) != 0, probs[ord + 9]); + } + if (aval & 0xF0) != 0 { + nits += Estimator::est_nits((aval & (1 << 3)) != 0, probs[3 + 9]); + } + } + if val != 0 { + nits += u32::from(PROB_BITS[128]); + } + nits +} +pub fn inter_mv_nits(mv: MV, mvprobs: &[u8; 4], nearest_mv: MV, near_mv: MV, pred_mv: MV, models: &VP7Models) -> u32 { + if mv == ZERO_MV { + code_nits(VPMBType::InterNoMV, MV_REF_TREE, mvprobs) + } else if mv == nearest_mv { + code_nits(VPMBType::InterNearest, MV_REF_TREE, mvprobs) + } else if mv == near_mv { + code_nits(VPMBType::InterNear, MV_REF_TREE, mvprobs) + } else { + let dmv = mv - pred_mv; + let mut nits = code_nits(VPMBType::InterMV, MV_REF_TREE, mvprobs); + nits += mv_component_nits(dmv.y, &models.mv_probs[0]); + nits += mv_component_nits(dmv.x, &models.mv_probs[1]); + nits + } +} +pub fn sub_mv_mode_nits(mode: MVSplitMode) -> u32 { + code_nits(mode, MV_SPLIT_MODE_TREE, &MV_SPLIT_MODE_PROBS) +} +pub fn sub_mv_nits(mv: MV, left_mv: MV, top_mv: MV, pred_mv: MV, models: &VP7Models) -> (SubMVRef, u32) { + if mv == ZERO_MV { + (SubMVRef::Zero, code_nits(SubMVRef::Zero, SUB_MV_REF_TREE, &SUB_MV_REF_PROBS)) + } else if mv == left_mv { + (SubMVRef::Left, code_nits(SubMVRef::Left, SUB_MV_REF_TREE, &SUB_MV_REF_PROBS)) + } else if mv == top_mv { + (SubMVRef::Above, code_nits(SubMVRef::Above, SUB_MV_REF_TREE, &SUB_MV_REF_PROBS)) + } else { + let dmv = mv - pred_mv; + let mut nits = code_nits(SubMVRef::New, SUB_MV_REF_TREE, &SUB_MV_REF_PROBS); + nits += mv_component_nits(dmv.y, &models.mv_probs[0]); + nits += mv_component_nits(dmv.x, &models.mv_probs[1]); + (SubMVRef::New, nits) + } +} +fn est_large_coef(val: i16, cat: usize) -> u32 { + let base = VP56_COEF_BASE[cat]; + let mut probs = VP56_COEF_ADD_PROBS[cat].iter(); + let add = val.abs() - base; + let mut mask = 1 << (VP6_COEF_ADD_BITS[cat] - 1); + let mut nits = 0; + while mask != 0 { + nits += Estimator::est_nits((add & mask) != 0, *probs.next().unwrap()); + mask >>= 1; + } + nits += u32::from(PROB_BITS[128]); + + nits +} + +pub fn estimate_subblock_nits(blk: &[i16; 16], ctype: usize, pctx: u8, probs: &[[[u8; 11]; 3]; 8]) -> u32 { + let start = if ctype != 0 { 0 } else { 1 }; + let mut cval = pctx as usize; + + let mut last = 16; + for &idx in DEFAULT_SCAN_ORDER.iter().skip(start) { + if blk[idx] != 0 { + last = idx; + } + } + + if last == 16 { + return code_nits(DCTToken::EOB, COEF_TREE, &probs[COEF_BANDS[start]][cval]); + } + + let mut nits = 0; + for i in start..16 { + let val = blk[DEFAULT_SCAN_ORDER[i]]; + let token = match val.abs() { + 0 => DCTToken::Zero, + 1 => DCTToken::One, + 2 => DCTToken::Two, + 3 => DCTToken::Three, + 4 => DCTToken::Four, + 5..=6 => DCTToken::Cat1, + 7 ..=10 => DCTToken::Cat2, + 11..=18 => DCTToken::Cat3, + 19..=34 => DCTToken::Cat4, + 35..=66 => DCTToken::Cat5, + _ => DCTToken::Cat6, + }; + nits += code_nits(token, COEF_TREE, &probs[COEF_BANDS[i]][cval]); + nits += match token { + DCTToken::Zero => 0, + DCTToken::One | + DCTToken::Two | + DCTToken::Three | + DCTToken::Four => u32::from(PROB_BITS[128]), + DCTToken::Cat1 => est_large_coef(val, 0), + DCTToken::Cat2 => est_large_coef(val, 1), + DCTToken::Cat3 => est_large_coef(val, 2), + DCTToken::Cat4 => est_large_coef(val, 3), + DCTToken::Cat5 => est_large_coef(val, 4), + DCTToken::Cat6 => est_large_coef(val, 5), + DCTToken::EOB => 0, + }; + cval = val.abs().min(2) as usize; + + if DEFAULT_SCAN_ORDER[i] == last { + if DEFAULT_SCAN_ORDER[i] != 15 { + nits += code_nits(DCTToken::EOB, COEF_TREE, &probs[COEF_BANDS[i + 1]][cval]); + } + break; + } + } + nits +} + +const COEF_BANDS: [usize; 16] = [ 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7 ]; +const VP6_COEF_ADD_BITS: [u8; 6] = [ 1, 2, 3, 4, 5, 11 ]; +const LONG_VECTOR_ORDER: [usize; 7] = [ 0, 1, 2, 7, 6, 5, 4 ];