#![forbid(unsafe_code)]
use crate::include::dav1d::headers::Rav1dPixelLayout;
use crate::src::enum_map::DefaultValue;
use crate::src::levels::BlockLevel;
use bitflags::bitflags;
use std::ops::Shr;
bitflags! {
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
pub(crate) struct EdgeFlags: u8 {
const I444_TOP_HAS_RIGHT = 1 << 0;
const I422_TOP_HAS_RIGHT = 1 << 1;
const I420_TOP_HAS_RIGHT = 1 << 2;
const I444_LEFT_HAS_BOTTOM = 1 << 3;
const I422_LEFT_HAS_BOTTOM = 1 << 4;
const I420_LEFT_HAS_BOTTOM = 1 << 5;
}
}
impl EdgeFlags {
pub const ALL_LEFT_HAS_BOTTOM: Self = Self::union_all([
Self::I444_LEFT_HAS_BOTTOM,
Self::I422_LEFT_HAS_BOTTOM,
Self::I420_LEFT_HAS_BOTTOM,
]);
pub const ALL_TOP_HAS_RIGHT: Self = Self::union_all([
Self::I444_TOP_HAS_RIGHT,
Self::I422_TOP_HAS_RIGHT,
Self::I420_TOP_HAS_RIGHT,
]);
pub const ALL_TR_AND_BL: Self =
Self::union_all([Self::ALL_LEFT_HAS_BOTTOM, Self::ALL_TOP_HAS_RIGHT]);
pub const fn union_all<const N: usize>(flags: [Self; N]) -> Self {
let mut i = 0;
let mut output = Self::empty();
while i < N {
output = output.union(flags[i]);
i += 1;
}
output
}
pub(crate) const fn select(&self, select: bool) -> Self {
if select { *self } else { Self::empty() }
}
}
impl Shr<Rav1dPixelLayout> for EdgeFlags {
type Output = Self;
fn shr(self, rhs: Rav1dPixelLayout) -> Self::Output {
Self::from_bits_retain(self.bits() >> (rhs as u32).wrapping_sub(1))
}
}
const B: usize = 4;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum EdgeKind {
Tip,
Branch,
}
#[derive(Clone, Copy)]
pub struct EdgeIndex {
index: u8,
kind: EdgeKind,
}
impl EdgeIndex {
pub const fn root() -> Self {
Self {
index: 0,
kind: EdgeKind::Branch,
}
}
#[must_use]
pub const fn pop_front(mut self) -> (Self, Self) {
let front = self;
self.index = self.index.wrapping_add(1);
(front, self)
}
}
#[repr(C)]
pub struct EdgeNode {
pub o: EdgeFlags,
pub h: [EdgeFlags; 2],
pub v: [EdgeFlags; 2],
}
#[repr(C)]
pub struct EdgeTip {
pub node: EdgeNode,
pub split: [EdgeFlags; 3],
}
#[repr(C)]
pub struct EdgeBranch {
pub node: EdgeNode,
pub h4: EdgeFlags,
pub v4: EdgeFlags,
pub split: [EdgeIndex; B],
}
impl EdgeTip {
const fn new(edge_flags: EdgeFlags) -> Self {
let o = edge_flags;
let h = [
edge_flags.union(EdgeFlags::ALL_LEFT_HAS_BOTTOM),
edge_flags.intersection(EdgeFlags::union_all([
EdgeFlags::ALL_LEFT_HAS_BOTTOM,
EdgeFlags::I420_TOP_HAS_RIGHT,
])),
];
let v = [
edge_flags.union(EdgeFlags::ALL_TOP_HAS_RIGHT),
edge_flags.intersection(EdgeFlags::union_all([
EdgeFlags::ALL_TOP_HAS_RIGHT,
EdgeFlags::I420_LEFT_HAS_BOTTOM,
EdgeFlags::I422_LEFT_HAS_BOTTOM,
])),
];
let node = EdgeNode { o, h, v };
let split = [
edge_flags
.intersection(EdgeFlags::ALL_TOP_HAS_RIGHT)
.union(EdgeFlags::I422_LEFT_HAS_BOTTOM),
edge_flags.union(EdgeFlags::I444_TOP_HAS_RIGHT),
edge_flags.intersection(EdgeFlags::union_all([
EdgeFlags::I420_TOP_HAS_RIGHT,
EdgeFlags::I420_LEFT_HAS_BOTTOM,
EdgeFlags::I422_LEFT_HAS_BOTTOM,
])),
];
Self { node, split }
}
}
impl EdgeBranch {
const fn new(edge_flags: EdgeFlags, bl: BlockLevel) -> Self {
let o = edge_flags;
let h = [
edge_flags.union(EdgeFlags::ALL_LEFT_HAS_BOTTOM),
edge_flags.intersection(EdgeFlags::ALL_LEFT_HAS_BOTTOM),
];
let v = [
edge_flags.union(EdgeFlags::ALL_TOP_HAS_RIGHT),
edge_flags.intersection(EdgeFlags::ALL_TOP_HAS_RIGHT),
];
let node = EdgeNode { o, h, v };
let h4 = edge_flags
.intersection(EdgeFlags::I420_TOP_HAS_RIGHT)
.select(matches!(bl, BlockLevel::Bl16x16))
.union(EdgeFlags::ALL_LEFT_HAS_BOTTOM);
let v4 = edge_flags
.intersection(EdgeFlags::union_all([
EdgeFlags::I420_LEFT_HAS_BOTTOM,
EdgeFlags::I422_LEFT_HAS_BOTTOM,
]))
.select(matches!(bl, BlockLevel::Bl16x16))
.union(EdgeFlags::ALL_TOP_HAS_RIGHT);
let split = [EdgeIndex::root(); 4];
Self {
node,
h4,
v4,
split,
}
}
}
impl DefaultValue for EdgeTip {
const DEFAULT: Self = Self::new(EdgeFlags::empty());
}
impl DefaultValue for EdgeBranch {
const DEFAULT: Self = Self::new(EdgeFlags::empty(), BlockLevel::Bl128x128);
}
struct EdgeIndices {
branch: [EdgeIndex; 3],
tip: EdgeIndex,
}
#[repr(C)]
struct IntraEdge<const SB128: bool, const N_BRANCH: usize, const N_TIP: usize> {
branch: [EdgeBranch; N_BRANCH],
tip: [EdgeTip; N_TIP],
}
const fn level_index(mut level: u8) -> u8 {
let mut level_size = 1;
let mut index = 0;
while level > 0 {
index += level_size;
level_size *= B;
level -= 1;
}
index as u8
}
impl<const SB128: bool, const N_BRANCH: usize, const N_TIP: usize>
IntraEdge<SB128, N_BRANCH, N_TIP>
{
#[must_use]
const fn init_mode_node(
mut self,
branch_index: EdgeIndex,
bl: BlockLevel,
mut indices: EdgeIndices,
top_has_right: bool,
left_has_bottom: bool,
) -> (Self, EdgeIndices) {
let mut branch = EdgeBranch::new(
EdgeFlags::union_all([
EdgeFlags::ALL_TOP_HAS_RIGHT.select(top_has_right),
EdgeFlags::ALL_LEFT_HAS_BOTTOM.select(left_has_bottom),
]),
bl,
);
if matches!(bl, BlockLevel::Bl16x16) {
let mut n = 0;
while n < B as u8 {
let (tip, next) = indices.tip.pop_front();
indices.tip = next;
branch.split[n as usize] = tip;
self.tip[tip.index as usize] = EdgeTip::new(EdgeFlags::union_all([
EdgeFlags::ALL_TOP_HAS_RIGHT.select(!(n == 3 || (n == 1 && !top_has_right))),
EdgeFlags::ALL_LEFT_HAS_BOTTOM.select(n == 0 || (n == 2 && left_has_bottom)),
]));
n += 1;
}
} else {
let mut n = 0;
while n < B as u8 {
let (child_branch, next) = indices.branch[bl as usize].pop_front();
indices.branch[bl as usize] = next;
branch.split[n as usize] = child_branch;
(self, indices) = self.init_mode_node(
child_branch,
match bl.decrease() {
None => panic!("BlockLevel::BL_8X8 should never make it here"),
Some(next_bl) => next_bl,
},
indices,
!(n == 3 || (n == 1 && !top_has_right)),
n == 0 || (n == 2 && left_has_bottom),
);
n += 1;
}
};
self.branch[branch_index.index as usize] = branch;
(self, indices)
}
const fn init(mut self) -> Self {
let mut indices = EdgeIndices {
branch: [EdgeIndex {
index: 0,
kind: EdgeKind::Branch,
}; 3],
tip: EdgeIndex {
index: 0,
kind: EdgeKind::Tip,
},
};
let sb128 = SB128 as u8;
let mut bl = BlockLevel::Bl128x128 as u8;
while bl <= BlockLevel::Bl32x32 as u8 {
indices.branch[bl as usize].index = level_index(bl as u8 + sb128);
bl += 1;
}
let bl = if SB128 {
BlockLevel::Bl128x128
} else {
BlockLevel::Bl64x64
};
(self, indices) = self.init_mode_node(EdgeIndex::root(), bl, indices, true, false);
let mut bl = BlockLevel::Bl128x128 as u8;
while bl <= BlockLevel::Bl32x32 as u8 {
let index = indices.branch[bl as usize].index;
if index != 0 {
assert!(index == level_index(1 + bl + sb128));
}
bl += 1;
}
assert!(indices.tip.index == self.tip.len() as u8);
self
}
const fn check_indices(self) -> Self {
let mut i = 0;
while i < self.branch.len() {
let mut j = 0;
while j < B {
let edge = self.branch[i].split[j];
let index = edge.index as usize;
match edge.kind {
EdgeKind::Branch => assert!(index < self.branch.len()),
EdgeKind::Tip => assert!(index < self.tip.len()),
}
j += 1;
}
i += 1;
}
self
}
const fn new() -> Self {
Self {
branch: [EdgeBranch::DEFAULT; N_BRANCH],
tip: [EdgeTip::DEFAULT; N_TIP],
}
.init()
.check_indices()
}
fn edge<E, const N: usize>(edges: &[E; N], edge: EdgeIndex, kind: EdgeKind) -> &E {
assert!(edge.kind == kind);
&edges[edge.index as usize]
}
pub fn branch(&self, branch: EdgeIndex) -> &EdgeBranch {
Self::edge(&self.branch, branch, EdgeKind::Branch)
}
pub fn tip(&self, tip: EdgeIndex) -> &EdgeTip {
Self::edge(&self.tip, tip, EdgeKind::Tip)
}
pub fn node(&self, node: EdgeIndex) -> &EdgeNode {
match node.kind {
EdgeKind::Branch => &self.branch(node).node,
EdgeKind::Tip => &self.tip(node).node,
}
}
}
#[repr(C)]
pub struct IntraEdges {
sb128: IntraEdge<true, 85, 256>,
sb64: IntraEdge<false, 21, 64>,
}
impl IntraEdges {
#[inline(always)]
const fn new() -> Self {
Self {
sb128: IntraEdge::new(),
sb64: IntraEdge::new(),
}
}
pub fn branch(&self, sb128: bool, branch: EdgeIndex) -> &EdgeBranch {
assert!(branch.kind == EdgeKind::Branch); if sb128 {
self.sb128.branch(branch)
} else {
self.sb64.branch(branch)
}
}
pub fn tip(&self, sb128: bool, tip: EdgeIndex) -> &EdgeTip {
assert!(tip.kind == EdgeKind::Tip); if sb128 {
self.sb128.tip(tip)
} else {
self.sb64.tip(tip)
}
}
pub fn node(&self, sb128: bool, node: EdgeIndex) -> &EdgeNode {
if sb128 {
self.sb128.node(node)
} else {
self.sb64.node(node)
}
}
}
impl DefaultValue for IntraEdges {
const DEFAULT: Self = Self::new();
}