#![allow(dead_code)]
#![allow(clippy::needless_range_loop)]
extern crate alloc;
use alloc::vec;
use alloc::vec::Vec;
use super::histogram::collect_histogram_with_offset;
use super::prediction::{make_chroma8_preds, make_luma16_preds};
use super::{
BPS, MAX_INTRA16_MODE, MAX_UV_MODE, PRED_SIZE_ENC, U_OFF_ENC, V_OFF_ENC, VP8_I16_MODE_OFFSETS,
VP8_UV_MODE_OFFSETS, Y_OFF_ENC, YUV_SIZE_ENC,
};
#[inline]
fn import_block(src: &[u8], src_stride: usize, dst: &mut [u8], w: usize, h: usize, size: usize) {
for y in 0..h {
dst[y * BPS..y * BPS + w].copy_from_slice(&src[y * src_stride..y * src_stride + w]);
if w < size {
let last_pixel = dst[y * BPS + w - 1];
for x in w..size {
dst[y * BPS + x] = last_pixel;
}
}
}
if h < size {
let last_row_start = (h - 1) * BPS;
let mut last_row = [0u8; 16]; last_row[..size].copy_from_slice(&dst[last_row_start..last_row_start + size]);
for y in h..size {
dst[y * BPS..y * BPS + size].copy_from_slice(&last_row[..size]);
}
}
}
#[inline]
fn import_line(src: &[u8], src_stride: usize, dst: &mut [u8], len: usize, total_len: usize) {
for i in 0..len {
dst[i] = src[i * src_stride];
}
let last = dst[len.saturating_sub(1)];
for i in len..total_len {
dst[i] = last;
}
}
pub struct AnalysisIterator {
pub x: usize,
pub y: usize,
pub mb_w: usize,
pub mb_h: usize,
pub width: usize,
pub height: usize,
pub yuv_in: Vec<u8>,
pub yuv_p: Vec<u8>,
pub y_left: [u8; 17],
pub u_left: [u8; 9],
pub v_left: [u8; 9],
pub y_top: Vec<u8>,
pub uv_top: Vec<u8>,
}
impl AnalysisIterator {
pub fn new(width: usize, height: usize) -> Self {
let mb_w = width.div_ceil(16);
let mb_h = height.div_ceil(16);
Self {
x: 0,
y: 0,
mb_w,
mb_h,
width,
height,
yuv_in: vec![0u8; YUV_SIZE_ENC],
yuv_p: vec![0u8; PRED_SIZE_ENC],
y_left: [129u8; 17],
u_left: [129u8; 9],
v_left: [129u8; 9],
y_top: vec![127u8; mb_w * 16 + 4],
uv_top: vec![127u8; mb_w * 16],
}
}
pub fn reset(&mut self) {
self.x = 0;
self.y = 0;
self.init_left();
self.init_top();
}
fn init_left(&mut self) {
let corner = if self.y > 0 { 129u8 } else { 127u8 };
self.y_left[0] = corner;
self.u_left[0] = corner;
self.v_left[0] = corner;
for i in 1..17 {
self.y_left[i] = 129;
}
for i in 1..9 {
self.u_left[i] = 129;
self.v_left[i] = 129;
}
}
fn init_top(&mut self) {
self.y_top.fill(127);
self.uv_top.fill(127);
}
pub fn set_row(&mut self, y: usize) {
self.x = 0;
self.y = y;
self.init_left();
}
pub fn import(
&mut self,
y_src: &[u8],
u_src: &[u8],
v_src: &[u8],
y_stride: usize,
uv_stride: usize,
) {
let x = self.x;
let y = self.y;
let y_offset = y * 16 * y_stride + x * 16;
let uv_offset = y * 8 * uv_stride + x * 8;
let w = (self.width - x * 16).min(16);
let h = (self.height - y * 16).min(16);
let uv_w = w.div_ceil(2);
let uv_h = h.div_ceil(2);
import_block(
&y_src[y_offset..],
y_stride,
&mut self.yuv_in[Y_OFF_ENC..],
w,
h,
16,
);
import_block(
&u_src[uv_offset..],
uv_stride,
&mut self.yuv_in[U_OFF_ENC..],
uv_w,
uv_h,
8,
);
import_block(
&v_src[uv_offset..],
uv_stride,
&mut self.yuv_in[V_OFF_ENC..],
uv_w,
uv_h,
8,
);
if x == 0 {
self.init_left();
} else {
if y == 0 {
self.y_left[0] = 127;
self.u_left[0] = 127;
self.v_left[0] = 127;
} else {
self.y_left[0] = y_src[y_offset - 1 - y_stride];
self.u_left[0] = u_src[uv_offset - 1 - uv_stride];
self.v_left[0] = v_src[uv_offset - 1 - uv_stride];
}
import_line(
&y_src[y_offset - 1..],
y_stride,
&mut self.y_left[1..],
h,
16,
);
import_line(
&u_src[uv_offset - 1..],
uv_stride,
&mut self.u_left[1..],
uv_h,
8,
);
import_line(
&v_src[uv_offset - 1..],
uv_stride,
&mut self.v_left[1..],
uv_h,
8,
);
}
if y == 0 {
for i in 0..16 {
self.y_top[x * 16 + i] = 127;
}
for i in 0..8 {
self.uv_top[x * 16 + i] = 127;
self.uv_top[x * 16 + 8 + i] = 127;
}
} else {
for i in 0..w {
self.y_top[x * 16 + i] = y_src[y_offset - y_stride + i];
}
let last_y = self.y_top[x * 16 + w - 1];
for i in w..16 {
self.y_top[x * 16 + i] = last_y;
}
for i in 0..uv_w {
self.uv_top[x * 16 + i] = u_src[uv_offset - uv_stride + i];
self.uv_top[x * 16 + 8 + i] = v_src[uv_offset - uv_stride + i];
}
let last_u = self.uv_top[x * 16 + uv_w - 1];
let last_v = self.uv_top[x * 16 + 8 + uv_w - 1];
for i in uv_w..8 {
self.uv_top[x * 16 + i] = last_u;
self.uv_top[x * 16 + 8 + i] = last_v;
}
}
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> bool {
self.x += 1;
if self.x >= self.mb_w {
self.set_row(self.y + 1);
}
self.y < self.mb_h
}
pub fn is_done(&self) -> bool {
self.y >= self.mb_h
}
#[allow(dead_code)]
fn get_y_left_with_corner(&self) -> Option<&[u8]> {
if self.x > 0 {
Some(&self.y_left[..])
} else {
None
}
}
#[allow(dead_code)]
fn get_y_top(&self) -> Option<&[u8]> {
if self.y > 0 {
Some(&self.y_top[self.x * 16..self.x * 16 + 16])
} else {
None
}
}
#[allow(dead_code)]
fn get_u_left_with_corner(&self) -> Option<&[u8]> {
if self.x > 0 {
Some(&self.u_left[..])
} else {
None
}
}
#[allow(dead_code)]
fn get_v_left_with_corner(&self) -> Option<&[u8]> {
if self.x > 0 {
Some(&self.v_left[..])
} else {
None
}
}
#[allow(dead_code)]
fn get_uv_top(&self) -> Option<&[u8]> {
if self.y > 0 {
Some(&self.uv_top[self.x * 16..self.x * 16 + 16])
} else {
None
}
}
pub fn analyze_best_intra16_mode(&mut self) -> (i32, usize) {
let mut best_alpha = -1i32;
let mut best_mode = 0usize;
let has_left = self.x > 0;
let has_top = self.y > 0;
let y_left_copy: [u8; 17] = self.y_left;
let y_top_start = self.x * 16;
let y_left_opt = if has_left {
Some(&y_left_copy[..])
} else {
None
};
let y_top_opt = if has_top {
Some(&self.y_top[y_top_start..y_top_start + 16])
} else {
None
};
make_luma16_preds(&mut self.yuv_p, y_left_opt, y_top_opt);
for mode in 0..MAX_INTRA16_MODE {
let pred_offset = VP8_I16_MODE_OFFSETS[mode];
let histo = collect_histogram_with_offset(
&self.yuv_in,
Y_OFF_ENC,
&self.yuv_p,
pred_offset,
0,
16,
);
let alpha = histo.get_alpha();
if alpha > best_alpha {
best_alpha = alpha;
best_mode = mode;
}
}
(best_alpha, best_mode)
}
pub fn analyze_best_uv_mode(&mut self) -> (i32, usize) {
let mut best_alpha = -1i32;
let mut smallest_alpha = i32::MAX;
let mut best_mode = 0usize;
let has_left = self.x > 0;
let has_top = self.y > 0;
let u_left_copy: [u8; 9] = self.u_left;
let v_left_copy: [u8; 9] = self.v_left;
let uv_top_start = self.x * 16;
let u_left_opt = if has_left {
Some(&u_left_copy[..])
} else {
None
};
let v_left_opt = if has_left {
Some(&v_left_copy[..])
} else {
None
};
let uv_top_opt = if has_top {
Some(&self.uv_top[uv_top_start..uv_top_start + 16])
} else {
None
};
make_chroma8_preds(&mut self.yuv_p, u_left_opt, v_left_opt, uv_top_opt);
for mode in 0..MAX_UV_MODE {
let pred_offset = VP8_UV_MODE_OFFSETS[mode];
let histo = collect_histogram_with_offset(
&self.yuv_in,
U_OFF_ENC,
&self.yuv_p,
pred_offset,
16,
24,
);
let alpha = histo.get_alpha();
if alpha > best_alpha {
best_alpha = alpha;
}
if mode == 0 || alpha < smallest_alpha {
smallest_alpha = alpha;
best_mode = mode;
}
}
(best_alpha, best_mode)
}
}