use crate::image::{Image3S, ImageB, ImageSB};
pub(crate) const STRATEGY_DCT: u8 = 0;
pub(crate) const STRATEGY_DCT16X8: u8 = 1;
pub(crate) const STRATEGY_DCT8X16: u8 = 2;
pub(crate) const STRATEGY_DCT16X16: u8 = 3;
pub(crate) const STRATEGY_DCT32X32: u8 = 4;
pub(crate) const STRATEGY_DCT4X4: u8 = 5;
pub(crate) const STRATEGY_DCT4X8: u8 = 6;
pub(crate) const STRATEGY_DCT8X4: u8 = 7;
pub(crate) const STRATEGY_DCT32X16: u8 = 8;
pub(crate) const STRATEGY_DCT16X32: u8 = 9;
pub(crate) const NUM_STRATEGIES: usize = 10;
pub(crate) static STRATEGY_CODE_LUT: [u8; NUM_STRATEGIES] = [0, 6, 7, 4, 5, 3, 12, 13, 10, 11];
const FIRST_BLOCK_BIT: u8 = 1;
pub(crate) struct AcStrategyImage {
xsize: usize,
ysize: usize,
cells: Vec<u8>,
}
impl AcStrategyImage {
pub(crate) fn new(xsize: usize, ysize: usize) -> Self {
Self {
xsize,
ysize,
cells: vec![(STRATEGY_DCT << 1) | FIRST_BLOCK_BIT; xsize * ysize],
}
}
#[inline]
pub(crate) fn xsize(&self) -> usize {
self.xsize
}
#[inline]
pub(crate) fn ysize(&self) -> usize {
self.ysize
}
pub(crate) fn copy_rows_from(&mut self, src: &AcStrategyImage, y0: usize, y1: usize) {
debug_assert_eq!(self.xsize, src.xsize);
let a = y0 * self.xsize;
let b = y1 * self.xsize;
self.cells[a..b].copy_from_slice(&src.cells[a..b]);
}
#[inline]
pub(crate) fn is_first_block(&self, x: usize, y: usize) -> bool {
self.cells[y * self.xsize + x] & FIRST_BLOCK_BIT != 0
}
#[inline]
pub(crate) fn raw_strategy(&self, x: usize, y: usize) -> u8 {
self.cells[y * self.xsize + x] >> 1
}
#[inline]
pub(crate) fn strategy_code(&self, x: usize, y: usize) -> u8 {
STRATEGY_CODE_LUT[self.raw_strategy(x, y) as usize]
}
#[inline]
pub(crate) fn covered_blocks_x_of(strategy: u8) -> usize {
static LUT: [u8; NUM_STRATEGIES] = [1, 1, 2, 2, 4, 1, 1, 1, 2, 4];
LUT[strategy as usize] as usize
}
#[inline]
pub(crate) fn covered_blocks_y_of(strategy: u8) -> usize {
static LUT: [u8; NUM_STRATEGIES] = [1, 2, 1, 2, 4, 1, 1, 1, 4, 2];
LUT[strategy as usize] as usize
}
pub(crate) fn set_first(&mut self, x: usize, y: usize, strategy: u8) {
let cx = Self::covered_blocks_x_of(strategy);
let cy = Self::covered_blocks_y_of(strategy);
assert!(
x + cx <= self.xsize && y + cy <= self.ysize,
"transform out of bounds: ({x},{y}) +{cx}x{cy} on {}x{}",
self.xsize,
self.ysize
);
self.cells[y * self.xsize + x] = (strategy << 1) | FIRST_BLOCK_BIT;
let v = strategy << 1;
let mut rows = self.cells.chunks_exact_mut(self.xsize).skip(y).take(cy);
if let Some(row) = rows.next() {
row[x + 1..x + cx].fill(v);
}
for row in rows {
row[x..x + cx].fill(v);
}
}
pub(crate) fn can_place_strategy(&self, x: usize, y: usize, strategy: u8) -> bool {
let cx = Self::covered_blocks_x_of(strategy);
let cy = Self::covered_blocks_y_of(strategy);
if x + cx > self.xsize || y + cy > self.ysize {
return false;
}
const GROUP: usize = 32;
if (x / GROUP) != ((x + cx - 1) / GROUP) {
return false;
}
if (y / GROUP) != ((y + cy - 1) / GROUP) {
return false;
}
const TILE: usize = 8;
if (x / TILE) != ((x + cx - 1) / TILE) {
return false;
}
if (y / TILE) != ((y + cy - 1) / TILE) {
return false;
}
true
}
pub(crate) fn iter_first_blocks(&self) -> impl Iterator<Item = (usize, usize, u8)> + '_ {
let xs = self.xsize;
let cells = &self.cells;
(0..self.ysize).flat_map(move |y| {
(0..xs).filter_map(move |x| {
let cell = cells[y * xs + x];
if cell & FIRST_BLOCK_BIT != 0 {
Some((x, y, cell >> 1))
} else {
None
}
})
})
}
pub(crate) fn count_first_blocks(&self) -> usize {
self.cells
.iter()
.filter(|&&c| c & FIRST_BLOCK_BIT != 0)
.count()
}
}
pub(crate) struct DcGroupData {
pub(crate) quant_dc: Image3S,
pub(crate) raw_quant_field: ImageB,
pub(crate) ac_strategy: AcStrategyImage,
pub(crate) ytox_map: ImageSB,
pub(crate) ytob_map: ImageSB,
pub(crate) dct4x4_benefit: f32,
}
const TILE_DIM_IN_BLOCKS: usize = 8;
impl DcGroupData {
pub(crate) fn new(xsize_blocks: usize, ysize_blocks: usize) -> Self {
let xtiles = xsize_blocks.div_ceil(TILE_DIM_IN_BLOCKS);
let ytiles = ysize_blocks.div_ceil(TILE_DIM_IN_BLOCKS);
Self {
quant_dc: Image3S::new(xsize_blocks, ysize_blocks),
raw_quant_field: ImageB::new_fill(xsize_blocks, ysize_blocks, 1),
ac_strategy: AcStrategyImage::new(xsize_blocks, ysize_blocks),
ytox_map: ImageSB::new_fill(xtiles, ytiles, 0),
ytob_map: ImageSB::new_fill(xtiles, ytiles, 0),
dct4x4_benefit: 0.0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_dct8_first_blocks() {
let a = AcStrategyImage::new(4, 4);
for y in 0..4 {
for x in 0..4 {
assert!(a.is_first_block(x, y));
assert_eq!(a.raw_strategy(x, y), STRATEGY_DCT);
assert_eq!(a.strategy_code(x, y), 0); }
}
assert_eq!(a.count_first_blocks(), 16);
}
#[test]
fn dct16x8_covers_1x2_blocks() {
let mut a = AcStrategyImage::new(4, 4);
a.set_first(0, 0, STRATEGY_DCT16X8);
assert!(a.is_first_block(0, 0));
assert_eq!(a.raw_strategy(0, 0), STRATEGY_DCT16X8);
assert_eq!(a.strategy_code(0, 0), 6); assert!(!a.is_first_block(0, 1));
assert_eq!(a.raw_strategy(0, 1), STRATEGY_DCT16X8);
assert!(a.is_first_block(1, 0));
assert!(a.is_first_block(0, 2));
assert_eq!(a.count_first_blocks(), 15);
}
#[test]
fn dct8x16_covers_2x1_blocks() {
let mut a = AcStrategyImage::new(4, 4);
a.set_first(0, 0, STRATEGY_DCT8X16);
assert!(a.is_first_block(0, 0));
assert_eq!(a.strategy_code(0, 0), 7); assert!(!a.is_first_block(1, 0));
assert_eq!(a.raw_strategy(1, 0), STRATEGY_DCT8X16);
assert!(a.is_first_block(0, 1));
assert_eq!(a.count_first_blocks(), 15);
}
#[test]
fn can_place_respects_bounds_and_groups() {
let a = AcStrategyImage::new(4, 4);
assert!(a.can_place_strategy(0, 0, STRATEGY_DCT16X8)); assert!(a.can_place_strategy(3, 0, STRATEGY_DCT16X8)); assert!(!a.can_place_strategy(0, 3, STRATEGY_DCT16X8)); assert!(a.can_place_strategy(0, 3, STRATEGY_DCT8X16)); assert!(!a.can_place_strategy(3, 0, STRATEGY_DCT8X16));
let big = AcStrategyImage::new(35, 35);
assert!(!big.can_place_strategy(0, 31, STRATEGY_DCT16X8)); assert!(big.can_place_strategy(0, 30, STRATEGY_DCT16X8)); assert!(!big.can_place_strategy(31, 0, STRATEGY_DCT8X16)); assert!(big.can_place_strategy(30, 0, STRATEGY_DCT8X16)); }
}