#![forbid(unsafe_code)]
#![allow(dead_code)]
use super::mv::MvRefType;
pub const MAX_SEGMENTS: usize = 8;
pub const SEG_FEATURES: usize = 4;
pub const SEG_PRED_PROBS: usize = 3;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum SegmentFeature {
AltQ = 0,
AltLf = 1,
RefFrame = 2,
Skip = 3,
}
impl SegmentFeature {
pub const ALL: [SegmentFeature; SEG_FEATURES] = [
SegmentFeature::AltQ,
SegmentFeature::AltLf,
SegmentFeature::RefFrame,
SegmentFeature::Skip,
];
const MAX_DATA: [i16; SEG_FEATURES] = [255, 63, 3, 0];
const SIGNED: [bool; SEG_FEATURES] = [true, true, false, false];
const DATA_BITS: [u8; SEG_FEATURES] = [8, 6, 2, 0];
#[must_use]
pub const fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(Self::AltQ),
1 => Some(Self::AltLf),
2 => Some(Self::RefFrame),
3 => Some(Self::Skip),
_ => None,
}
}
#[must_use]
pub const fn max_data(&self) -> i16 {
Self::MAX_DATA[*self as usize]
}
#[must_use]
pub const fn is_signed(&self) -> bool {
Self::SIGNED[*self as usize]
}
#[must_use]
pub const fn data_bits(&self) -> u8 {
Self::DATA_BITS[*self as usize]
}
#[must_use]
pub const fn index(&self) -> usize {
*self as usize
}
}
impl From<SegmentFeature> for u8 {
fn from(value: SegmentFeature) -> Self {
value as u8
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct SegmentData {
pub feature_enabled: [bool; SEG_FEATURES],
pub feature_data: [i16; SEG_FEATURES],
}
impl SegmentData {
#[must_use]
pub const fn new() -> Self {
Self {
feature_enabled: [false; SEG_FEATURES],
feature_data: [0; SEG_FEATURES],
}
}
#[must_use]
pub const fn is_feature_enabled(&self, feature: SegmentFeature) -> bool {
self.feature_enabled[feature.index()]
}
#[must_use]
pub const fn feature_data(&self, feature: SegmentFeature) -> i16 {
self.feature_data[feature.index()]
}
pub fn enable_feature(&mut self, feature: SegmentFeature, data: i16) {
let idx = feature.index();
self.feature_enabled[idx] = true;
self.feature_data[idx] = data.clamp(-feature.max_data(), feature.max_data());
}
pub fn disable_feature(&mut self, feature: SegmentFeature) {
let idx = feature.index();
self.feature_enabled[idx] = false;
self.feature_data[idx] = 0;
}
#[must_use]
pub const fn is_skip_forced(&self) -> bool {
self.feature_enabled[SegmentFeature::Skip as usize]
}
#[must_use]
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
pub fn reference_frame(&self) -> Option<MvRefType> {
if self.feature_enabled[SegmentFeature::RefFrame as usize] {
let ref_idx = self.feature_data[SegmentFeature::RefFrame as usize] as u8;
MvRefType::from_u8(ref_idx)
} else {
None
}
}
#[must_use]
pub const fn qindex_delta(&self) -> Option<i16> {
if self.feature_enabled[SegmentFeature::AltQ as usize] {
Some(self.feature_data[SegmentFeature::AltQ as usize])
} else {
None
}
}
#[must_use]
pub const fn lf_delta(&self) -> Option<i16> {
if self.feature_enabled[SegmentFeature::AltLf as usize] {
Some(self.feature_data[SegmentFeature::AltLf as usize])
} else {
None
}
}
pub fn clear(&mut self) {
self.feature_enabled = [false; SEG_FEATURES];
self.feature_data = [0; SEG_FEATURES];
}
}
#[derive(Clone, Debug, Default)]
#[allow(clippy::struct_excessive_bools)]
pub struct Segmentation {
pub enabled: bool,
pub update_map: bool,
pub temporal_update: bool,
pub update_data: bool,
pub abs_delta: bool,
pub segments: [SegmentData; MAX_SEGMENTS],
pub pred_probs: [u8; SEG_PRED_PROBS],
pub tree_probs: [u8; MAX_SEGMENTS - 1],
}
impl Segmentation {
#[must_use]
pub fn new() -> Self {
Self {
enabled: false,
update_map: false,
temporal_update: false,
update_data: false,
abs_delta: false,
segments: [SegmentData::new(); MAX_SEGMENTS],
pred_probs: [255; SEG_PRED_PROBS],
tree_probs: [128; MAX_SEGMENTS - 1],
}
}
pub fn reset(&mut self) {
self.enabled = false;
self.update_map = false;
self.temporal_update = false;
self.update_data = false;
self.abs_delta = false;
for segment in &mut self.segments {
segment.clear();
}
self.pred_probs = [255; SEG_PRED_PROBS];
self.tree_probs = [128; MAX_SEGMENTS - 1];
}
pub fn clear_features(&mut self) {
for segment in &mut self.segments {
segment.clear();
}
}
#[must_use]
pub const fn is_active(&self) -> bool {
self.enabled
}
#[must_use]
pub const fn segment(&self, segment_id: u8) -> &SegmentData {
&self.segments[(segment_id as usize) & (MAX_SEGMENTS - 1)]
}
#[must_use]
pub fn segment_mut(&mut self, segment_id: u8) -> &mut SegmentData {
&mut self.segments[(segment_id as usize) & (MAX_SEGMENTS - 1)]
}
#[must_use]
pub fn any_segment_has_feature(&self, feature: SegmentFeature) -> bool {
self.segments.iter().any(|s| s.is_feature_enabled(feature))
}
#[must_use]
pub fn has_skip_segment(&self) -> bool {
self.any_segment_has_feature(SegmentFeature::Skip)
}
#[must_use]
pub fn has_ref_frame_segment(&self) -> bool {
self.any_segment_has_feature(SegmentFeature::RefFrame)
}
#[must_use]
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
pub fn get_qindex(&self, segment_id: u8, base_qindex: u8) -> u8 {
let segment = self.segment(segment_id);
if let Some(delta) = segment.qindex_delta() {
if self.abs_delta {
(delta as i32).clamp(0, 255) as u8
} else {
(i32::from(base_qindex) + i32::from(delta)).clamp(0, 255) as u8
}
} else {
base_qindex
}
}
#[must_use]
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
pub fn get_lf_level(&self, segment_id: u8, base_level: u8) -> u8 {
let segment = self.segment(segment_id);
if let Some(delta) = segment.lf_delta() {
if self.abs_delta {
(delta as i32).clamp(0, 63) as u8
} else {
(i32::from(base_level) + i32::from(delta)).clamp(0, 63) as u8
}
} else {
base_level
}
}
pub fn set_feature(&mut self, segment_id: u8, feature: SegmentFeature, data: i16) {
self.segment_mut(segment_id).enable_feature(feature, data);
}
pub fn clear_feature(&mut self, segment_id: u8, feature: SegmentFeature) {
self.segment_mut(segment_id).disable_feature(feature);
}
#[must_use]
pub fn pred_prob(&self, context: usize) -> u8 {
if context < SEG_PRED_PROBS {
self.pred_probs[context]
} else {
128
}
}
}
#[derive(Clone, Debug)]
pub struct SegmentMap {
mi_cols: usize,
mi_rows: usize,
ids: Vec<u8>,
}
impl SegmentMap {
#[must_use]
pub fn new(mi_cols: usize, mi_rows: usize) -> Self {
Self {
mi_cols,
mi_rows,
ids: vec![0; mi_cols * mi_rows],
}
}
#[must_use]
pub fn get(&self, mi_col: usize, mi_row: usize) -> u8 {
if mi_col < self.mi_cols && mi_row < self.mi_rows {
self.ids[mi_row * self.mi_cols + mi_col]
} else {
0
}
}
pub fn set(&mut self, mi_col: usize, mi_row: usize, segment_id: u8) {
if mi_col < self.mi_cols && mi_row < self.mi_rows {
self.ids[mi_row * self.mi_cols + mi_col] = segment_id & 7;
}
}
pub fn fill_block(&mut self, mi_col: usize, mi_row: usize, w: usize, h: usize, segment_id: u8) {
for row in mi_row..mi_row.saturating_add(h).min(self.mi_rows) {
for col in mi_col..mi_col.saturating_add(w).min(self.mi_cols) {
self.ids[row * self.mi_cols + col] = segment_id & 7;
}
}
}
pub fn clear(&mut self) {
self.ids.fill(0);
}
pub fn copy_from(&mut self, other: &SegmentMap) {
if self.mi_cols == other.mi_cols && self.mi_rows == other.mi_rows {
self.ids.copy_from_slice(&other.ids);
}
}
#[must_use]
pub const fn dimensions(&self) -> (usize, usize) {
(self.mi_cols, self.mi_rows)
}
}
impl Default for SegmentMap {
fn default() -> Self {
Self::new(0, 0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_segment_feature() {
assert_eq!(SegmentFeature::AltQ.index(), 0);
assert_eq!(SegmentFeature::Skip.index(), 3);
assert!(SegmentFeature::AltQ.is_signed());
assert!(!SegmentFeature::RefFrame.is_signed());
}
#[test]
fn test_segment_feature_from_u8() {
assert_eq!(SegmentFeature::from_u8(0), Some(SegmentFeature::AltQ));
assert_eq!(SegmentFeature::from_u8(3), Some(SegmentFeature::Skip));
assert_eq!(SegmentFeature::from_u8(4), None);
}
#[test]
fn test_segment_data() {
let mut data = SegmentData::new();
assert!(!data.is_feature_enabled(SegmentFeature::AltQ));
data.enable_feature(SegmentFeature::AltQ, -10);
assert!(data.is_feature_enabled(SegmentFeature::AltQ));
assert_eq!(data.feature_data(SegmentFeature::AltQ), -10);
assert_eq!(data.qindex_delta(), Some(-10));
data.disable_feature(SegmentFeature::AltQ);
assert!(!data.is_feature_enabled(SegmentFeature::AltQ));
}
#[test]
fn test_segment_data_skip() {
let mut data = SegmentData::new();
assert!(!data.is_skip_forced());
data.enable_feature(SegmentFeature::Skip, 0);
assert!(data.is_skip_forced());
}
#[test]
fn test_segment_data_ref_frame() {
let mut data = SegmentData::new();
assert!(data.reference_frame().is_none());
data.enable_feature(SegmentFeature::RefFrame, 1);
assert_eq!(data.reference_frame(), Some(MvRefType::Last));
}
#[test]
fn test_segmentation_new() {
let seg = Segmentation::new();
assert!(!seg.enabled);
assert!(!seg.is_active());
}
#[test]
fn test_segmentation_qindex() {
let mut seg = Segmentation::new();
seg.enabled = true;
seg.abs_delta = false;
seg.set_feature(1, SegmentFeature::AltQ, -20);
assert_eq!(seg.get_qindex(0, 100), 100); assert_eq!(seg.get_qindex(1, 100), 80); }
#[test]
fn test_segmentation_qindex_absolute() {
let mut seg = Segmentation::new();
seg.enabled = true;
seg.abs_delta = true;
seg.set_feature(2, SegmentFeature::AltQ, 50);
assert_eq!(seg.get_qindex(2, 100), 50); }
#[test]
fn test_segmentation_lf_level() {
let mut seg = Segmentation::new();
seg.enabled = true;
seg.set_feature(3, SegmentFeature::AltLf, 10);
assert_eq!(seg.get_lf_level(3, 30), 40); }
#[test]
fn test_segmentation_reset() {
let mut seg = Segmentation::new();
seg.enabled = true;
seg.set_feature(0, SegmentFeature::Skip, 0);
seg.reset();
assert!(!seg.enabled);
assert!(!seg.segment(0).is_skip_forced());
}
#[test]
fn test_segmentation_any_segment_has_feature() {
let mut seg = Segmentation::new();
assert!(!seg.has_skip_segment());
seg.set_feature(5, SegmentFeature::Skip, 0);
assert!(seg.has_skip_segment());
}
#[test]
fn test_segment_map() {
let mut map = SegmentMap::new(16, 16);
assert_eq!(map.get(0, 0), 0);
map.set(5, 5, 3);
assert_eq!(map.get(5, 5), 3);
}
#[test]
fn test_segment_map_fill_block() {
let mut map = SegmentMap::new(16, 16);
map.fill_block(2, 2, 4, 4, 5);
assert_eq!(map.get(2, 2), 5);
assert_eq!(map.get(5, 5), 5);
assert_eq!(map.get(1, 1), 0);
assert_eq!(map.get(6, 6), 0);
}
#[test]
fn test_segment_map_clear() {
let mut map = SegmentMap::new(16, 16);
map.set(5, 5, 7);
map.clear();
assert_eq!(map.get(5, 5), 0);
}
#[test]
fn test_segment_map_bounds() {
let map = SegmentMap::new(16, 16);
assert_eq!(map.get(100, 100), 0);
}
}