use crate::cdf;
use gamut_bitstream::SymbolEncoder;
use gamut_color::Planar8;
const NUM_BASE_LEVELS: i32 = 2;
const COEFF_BASE_PLUS_RANGE: i32 = 14;
pub(crate) struct FrameEncoder<'a> {
planes: [&'a [u8]; 3],
width: usize,
height: usize,
mi_cols: usize,
mi_rows: usize,
coded_w: usize,
coded_h: usize,
sym: SymbolEncoder,
above_level: [Vec<u8>; 3],
above_dc: [Vec<u8>; 3],
left_level: [Vec<u8>; 3],
left_dc: [Vec<u8>; 3],
mi_bsl: Vec<u8>,
}
impl<'a> FrameEncoder<'a> {
pub(crate) fn new(planes: &'a Planar8) -> Self {
let width = planes.width() as usize;
let height = planes.height() as usize;
let mi_cols = 2 * ((width + 7) >> 3);
let mi_rows = 2 * ((height + 7) >> 3);
Self {
planes: [planes.plane(0), planes.plane(1), planes.plane(2)],
width,
height,
mi_cols,
mi_rows,
coded_w: mi_cols * 4,
coded_h: mi_rows * 4,
sym: SymbolEncoder::new(),
above_level: [vec![0; mi_cols], vec![0; mi_cols], vec![0; mi_cols]],
above_dc: [vec![0; mi_cols], vec![0; mi_cols], vec![0; mi_cols]],
left_level: [vec![0; mi_rows], vec![0; mi_rows], vec![0; mi_rows]],
left_dc: [vec![0; mi_rows], vec![0; mi_rows], vec![0; mi_rows]],
mi_bsl: vec![0; mi_cols * mi_rows],
}
}
pub(crate) fn encode(mut self) -> Vec<u8> {
const SB4: usize = 16; let mut r = 0;
while r < self.mi_rows {
for plane in 0..3 {
self.left_level[plane].iter_mut().for_each(|v| *v = 0);
self.left_dc[plane].iter_mut().for_each(|v| *v = 0);
}
let mut c = 0;
while c < self.mi_cols {
self.encode_partition(r, c, 64);
c += SB4;
}
r += SB4;
}
self.sym.finish()
}
fn sample(&self, plane: usize, x: usize, y: usize) -> i32 {
let xx = x.min(self.width - 1);
let yy = y.min(self.height - 1);
i32::from(self.planes[plane][yy * self.width + xx])
}
fn encode_partition(&mut self, r: usize, c: usize, bw: usize) {
if r >= self.mi_rows || c >= self.mi_cols {
return;
}
let num4x4 = bw / 4;
let half = num4x4 >> 1;
let has_rows = r + half < self.mi_rows;
let has_cols = c + half < self.mi_cols;
let bsl = num4x4.trailing_zeros() as usize;
let split = if bw < 8 {
false } else if has_rows && has_cols {
let ctx = self.partition_ctx(r, c, bsl);
self.sym.encode_symbol(0, partition_cdf(bsl, ctx)); false
} else if has_cols {
let ctx = self.partition_ctx(r, c, bsl);
let cdf2 = split_or_horz_cdf(partition_cdf(bsl, ctx));
self.sym.encode_symbol(1, &cdf2); true
} else if has_rows {
let ctx = self.partition_ctx(r, c, bsl);
let cdf2 = split_or_vert_cdf(partition_cdf(bsl, ctx));
self.sym.encode_symbol(1, &cdf2); true
} else {
true };
if !split {
self.encode_block(r, c, bw);
} else {
let h = bw / 2;
self.encode_partition(r, c, h);
self.encode_partition(r, c + half, h);
self.encode_partition(r + half, c, h);
self.encode_partition(r + half, c + half, h);
}
}
fn partition_ctx(&self, r: usize, c: usize, bsl: usize) -> usize {
let above = r > 0 && usize::from(self.mi_bsl[(r - 1) * self.mi_cols + c]) < bsl;
let left = c > 0 && usize::from(self.mi_bsl[r * self.mi_cols + (c - 1)]) < bsl;
usize::from(left) * 2 + usize::from(above)
}
fn encode_block(&mut self, r: usize, c: usize, bw: usize) {
let n4 = bw / 4;
let bsl = n4.trailing_zeros() as u8;
self.sym.encode_symbol(0, &cdf::SKIP[0]);
self.sym.encode_symbol(0, &cdf::INTRA_FRAME_Y_MODE_DC_DC);
let uv: &[u16] = if bw == 4 {
&cdf::UV_MODE_CFL_ALLOWED_DC
} else {
&cdf::UV_MODE_CFL_NOT_ALLOWED_DC
};
self.sym.encode_symbol(0, uv);
for y in 0..n4 {
for x in 0..n4 {
let (rr, cc) = (r + y, c + x);
if rr < self.mi_rows && cc < self.mi_cols {
self.mi_bsl[rr * self.mi_cols + cc] = bsl;
}
}
}
for plane in 0..3 {
for ty in 0..n4 {
for tx in 0..n4 {
let sx = c * 4 + tx * 4;
let sy = r * 4 + ty * 4;
if sx >= self.coded_w || sy >= self.coded_h {
continue; }
self.transform_block(plane, sx, sy, bw);
}
}
}
}
fn transform_block(&mut self, plane: usize, sx: usize, sy: usize, block_w: usize) {
let avg = self.dc_avg(plane, sx, sy);
let mut res = [0i32; 16];
for i in 0..4 {
for j in 0..4 {
res[i * 4 + j] = self.sample(plane, sx + j, sy + i) - avg;
}
}
let quant = gamut_dsp::fwht4x4(&res);
self.code_coeffs(plane, sx >> 2, sy >> 2, block_w, &quant);
}
fn dc_avg(&self, plane: usize, sx: usize, sy: usize) -> i32 {
let have_above = sy > 0;
let have_left = sx > 0;
match (have_above, have_left) {
(true, true) => {
let mut s = 0;
for k in 0..4 {
s += self.sample(plane, sx + k, sy - 1);
s += self.sample(plane, sx - 1, sy + k);
}
(s + 4) >> 3
}
(false, true) => {
let mut s = 0;
for k in 0..4 {
s += self.sample(plane, sx - 1, sy + k);
}
(s + 2) >> 2
}
(true, false) => {
let mut s = 0;
for k in 0..4 {
s += self.sample(plane, sx + k, sy - 1);
}
(s + 2) >> 2
}
(false, false) => 128, }
}
#[allow(clippy::too_many_lines)]
fn code_coeffs(
&mut self,
plane: usize,
x4: usize,
y4: usize,
block_w: usize,
quant: &[i32; 16],
) {
let ptype = usize::from(plane > 0);
let scan = &cdf::DEFAULT_SCAN_4X4;
let mut eob = 0usize;
for c in 0..16 {
if quant[scan[c]] != 0 {
eob = c + 1;
}
}
let txb_ctx = self.txb_skip_ctx(plane, x4, y4, block_w);
self.sym
.encode_symbol(usize::from(eob == 0), &cdf::TXB_SKIP[txb_ctx]);
if eob == 0 {
self.set_ctx(plane, x4, y4, 0, 0);
return;
}
let eobpt = eobpt_from_eob(eob);
self.sym.encode_symbol(eobpt - 1, &cdf::EOB_PT_16[ptype][0]);
if eobpt >= 3 {
let nbits = eobpt - 2;
let base = (1usize << (eobpt - 2)) + 1;
let extra = eob - base;
self.sym.encode_symbol(
(extra >> (nbits - 1)) & 1,
&cdf::EOB_EXTRA[ptype][eobpt - 3],
);
let mut i = nbits as isize - 2;
while i >= 0 {
self.sym.encode_literal(((extra >> i) & 1) as u32, 1);
i -= 1;
}
}
let mut levels = [0i32; 16];
for c in (0..eob).rev() {
let pos = scan[c];
let level = quant[pos].abs();
if c == eob - 1 {
let ctx = coeff_base_eob_ctx(c);
self.sym.encode_symbol(
(level.min(3) - 1) as usize,
&cdf::COEFF_BASE_EOB[ptype][ctx],
);
} else {
let ctx = coeff_base_ctx(pos, &levels);
self.sym
.encode_symbol(level.min(3) as usize, &cdf::COEFF_BASE[ptype][ctx]);
}
if level > NUM_BASE_LEVELS {
let br_ctx = coeff_br_ctx(pos, &levels);
let mut rem = level - 3;
for _ in 0..4 {
let brv = rem.min(3);
self.sym
.encode_symbol(brv as usize, &cdf::COEFF_BR[ptype][br_ctx]);
rem -= brv;
if brv < 3 {
break;
}
}
}
levels[pos] = level;
}
for (c, &pos) in scan.iter().enumerate().take(eob) {
let level = quant[pos].abs();
if level != 0 {
let neg = quant[pos] < 0;
if c == 0 {
let ctx = self.dc_sign_ctx(plane, x4, y4);
self.sym
.encode_symbol(usize::from(neg), &cdf::DC_SIGN[ptype][ctx]);
} else {
self.sym.encode_literal(u32::from(neg), 1);
}
if level > COEFF_BASE_PLUS_RANGE {
golomb(&mut self.sym, (level - COEFF_BASE_PLUS_RANGE) as u32);
}
}
}
let cul = levels.iter().sum::<i32>().min(63) as u8;
let dc_cat = if quant[0] == 0 {
0
} else if quant[0] < 0 {
1
} else {
2
};
self.set_ctx(plane, x4, y4, cul, dc_cat);
}
fn set_ctx(&mut self, plane: usize, x4: usize, y4: usize, cul: u8, dc: u8) {
self.above_level[plane][x4] = cul;
self.above_dc[plane][x4] = dc;
self.left_level[plane][y4] = cul;
self.left_dc[plane][y4] = dc;
}
fn txb_skip_ctx(&self, plane: usize, x4: usize, y4: usize, block_w: usize) -> usize {
if plane == 0 {
if block_w == 4 {
return 0;
}
let top = i32::from(self.above_level[0][x4]);
let left = i32::from(self.left_level[0][y4]);
if top == 0 && left == 0 {
1
} else if top == 0 || left == 0 {
2 + usize::from(top.max(left) > 3)
} else if top.max(left) <= 3 {
4
} else if top.min(left) <= 3 {
5
} else {
6
}
} else {
let above = self.above_level[plane][x4] | self.above_dc[plane][x4];
let left = self.left_level[plane][y4] | self.left_dc[plane][y4];
let mut ctx = usize::from(above != 0) + usize::from(left != 0) + 7;
if block_w * block_w > 16 {
ctx += 3;
}
ctx
}
}
fn dc_sign_ctx(&self, plane: usize, x4: usize, y4: usize) -> usize {
let mut s = 0i32;
for &cat in &[self.above_dc[plane][x4], self.left_dc[plane][y4]] {
if cat == 1 {
s -= 1;
} else if cat == 2 {
s += 1;
}
}
if s < 0 {
1
} else if s > 0 {
2
} else {
0
}
}
}
fn partition_cdf(bsl: usize, ctx: usize) -> &'static [u16] {
match bsl {
1 => &cdf::PARTITION_W8[ctx],
2 => &cdf::PARTITION_W16[ctx],
3 => &cdf::PARTITION_W32[ctx],
_ => &cdf::PARTITION_W64[ctx],
}
}
fn split_or_horz_cdf(p: &[u16]) -> [u16; 2] {
let psum = (p[2] - p[1])
+ (p[3] - p[2])
+ (p[4] - p[3])
+ (p[6] - p[5])
+ (p[7] - p[6])
+ (p[9] - p[8]);
[32768 - psum, 32768]
}
fn split_or_vert_cdf(p: &[u16]) -> [u16; 2] {
let psum = (p[1] - p[0])
+ (p[3] - p[2])
+ (p[4] - p[3])
+ (p[5] - p[4])
+ (p[6] - p[5])
+ (p[8] - p[7]);
[32768 - psum, 32768]
}
fn eobpt_from_eob(eob: usize) -> usize {
if eob <= 1 {
eob
} else {
(32 - ((eob - 1) as u32).leading_zeros()) as usize + 1
}
}
fn coeff_base_eob_ctx(c: usize) -> usize {
if c == 0 {
0
} else if c <= 2 {
1
} else if c <= 4 {
2
} else {
3
}
}
fn coeff_base_ctx(pos: usize, levels: &[i32; 16]) -> usize {
let (row, col) = (pos >> 2, pos & 3);
let mut mag = 0i32;
for &(dr, dc) in &cdf::SIG_REF_DIFF_OFFSET_2D {
let (rr, cc) = (row + dr, col + dc);
if rr < 4 && cc < 4 {
mag += levels[(rr << 2) + cc].abs().min(3);
}
}
let ctx = (((mag + 1) >> 1).min(4)) as usize;
if row == 0 && col == 0 {
return 0;
}
ctx + usize::from(cdf::COEFF_BASE_CTX_OFFSET_4X4[row.min(4)][col.min(4)])
}
fn coeff_br_ctx(pos: usize, levels: &[i32; 16]) -> usize {
let (row, col) = (pos >> 2, pos & 3);
let mut mag = 0i32;
for &(dr, dc) in &cdf::MAG_REF_OFFSET_2D {
let (rr, cc) = (row + dr, col + dc);
if rr < 4 && cc < 4 {
mag += levels[(rr << 2) + cc].abs().min(15);
}
}
let mag = (((mag + 1) >> 1).min(6)) as usize;
if pos == 0 {
mag
} else if row < 2 && col < 2 {
mag + 7
} else {
mag + 14
}
}
fn golomb(sym: &mut SymbolEncoder, x: u32) {
let len = 32 - x.leading_zeros(); for _ in 0..(len - 1) {
sym.encode_literal(0, 1);
}
sym.encode_literal(1, 1);
let mut i = len as isize - 2;
while i >= 0 {
sym.encode_literal((x >> i) & 1, 1);
i -= 1;
}
}