#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BitDepth {
Eight,
Ten,
Twelve,
}
impl BitDepth {
pub fn from_bits(bits: u8) -> Self {
match bits {
8 => BitDepth::Eight,
10 => BitDepth::Ten,
12 => BitDepth::Twelve,
other => panic!("unsupported bit depth: {other} (only 8, 10, 12 supported)"),
}
}
pub fn bits(self) -> u8 {
match self {
BitDepth::Eight => 8,
BitDepth::Ten => 10,
BitDepth::Twelve => 12,
}
}
pub fn minus8(self) -> u8 {
self.bits() - 8
}
pub fn max_val(self) -> u16 {
(1u16 << self.bits()) - 1
}
pub fn neutral(self) -> u16 {
1u16 << (self.bits() - 1)
}
pub fn qp_bd_offset(self) -> u8 {
6 * self.minus8()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ChromaFormat {
Monochrome,
Yuv420,
Yuv422,
Yuv444,
}
impl ChromaFormat {
pub fn idc(self) -> u32 {
match self {
ChromaFormat::Monochrome => 0,
ChromaFormat::Yuv420 => 1,
ChromaFormat::Yuv422 => 2,
ChromaFormat::Yuv444 => 3,
}
}
pub fn is_monochrome(self) -> bool {
matches!(self, ChromaFormat::Monochrome)
}
pub fn sub_w(self) -> usize {
match self {
ChromaFormat::Yuv420 | ChromaFormat::Yuv422 => 2,
ChromaFormat::Yuv444 | ChromaFormat::Monochrome => 1,
}
}
pub fn sub_h(self) -> usize {
match self {
ChromaFormat::Yuv420 => 2,
ChromaFormat::Yuv422 | ChromaFormat::Yuv444 | ChromaFormat::Monochrome => 1,
}
}
pub fn chroma_tb_size(self) -> usize {
match self {
ChromaFormat::Yuv420 | ChromaFormat::Yuv422 => 4,
ChromaFormat::Yuv444 => 8,
ChromaFormat::Monochrome => 0,
}
}
pub fn chroma_tbs_per_cu(self) -> usize {
match self {
ChromaFormat::Monochrome => 0,
ChromaFormat::Yuv420 => 1,
ChromaFormat::Yuv422 => 2,
ChromaFormat::Yuv444 => 1,
}
}
}
#[derive(Clone, Debug)]
pub enum ImageBuffer {
Luma8(Vec<u8>), Luma16(Vec<u16>), Rgb8(Vec<u8>), Rgb16(Vec<u16>), }
impl ImageBuffer {
pub fn as_u8(&self) -> Option<&[u8]> {
match self {
Self::Luma8(v) | Self::Rgb8(v) => Some(v),
_ => None,
}
}
pub fn as_u16(&self) -> Option<&[u16]> {
match self {
Self::Luma16(v) | Self::Rgb16(v) => Some(v),
_ => None,
}
}
pub fn channels(&self) -> usize {
match self {
Self::Luma8(_) | Self::Luma16(_) => 1,
_ => 3,
}
}
pub fn len(&self) -> usize {
match self {
Self::Luma8(v) => v.len(),
Self::Luma16(v) => v.len(),
Self::Rgb8(v) => v.len(),
Self::Rgb16(v) => v.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn is_luma(&self) -> bool {
matches!(self, Self::Luma8(_) | Self::Luma16(_))
}
}
#[derive(Clone, Debug)]
pub enum SampleBuf {
U8(Vec<u8>),
U16(Vec<u16>),
}
impl SampleBuf {
pub fn as_u8(&self) -> Option<&[u8]> {
if let Self::U8(v) = self {
Some(v)
} else {
None
}
}
pub fn as_u16(&self) -> Option<&[u16]> {
if let Self::U16(v) = self {
Some(v)
} else {
None
}
}
pub fn len(&self) -> usize {
match self {
Self::U8(v) => v.len(),
Self::U16(v) => v.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bitdepth_derived_quantities() {
assert_eq!(BitDepth::Eight.bits(), 8);
assert_eq!(BitDepth::Ten.bits(), 10);
assert_eq!(BitDepth::Twelve.bits(), 12);
assert_eq!(BitDepth::Eight.minus8(), 0);
assert_eq!(BitDepth::Ten.minus8(), 2);
assert_eq!(BitDepth::Twelve.minus8(), 4);
assert_eq!(BitDepth::Eight.max_val(), 255);
assert_eq!(BitDepth::Ten.max_val(), 1023);
assert_eq!(BitDepth::Twelve.max_val(), 4095);
assert_eq!(BitDepth::Eight.neutral(), 128);
assert_eq!(BitDepth::Ten.neutral(), 512);
assert_eq!(BitDepth::Twelve.neutral(), 2048);
assert_eq!(BitDepth::Eight.qp_bd_offset(), 0);
assert_eq!(BitDepth::Ten.qp_bd_offset(), 12);
assert_eq!(BitDepth::Twelve.qp_bd_offset(), 24);
}
#[test]
fn bitdepth_from_bits_roundtrip() {
assert_eq!(BitDepth::from_bits(8), BitDepth::Eight);
assert_eq!(BitDepth::from_bits(10), BitDepth::Ten);
assert_eq!(BitDepth::from_bits(12), BitDepth::Twelve);
}
#[test]
#[should_panic]
fn bitdepth_rejects_unsupported() {
let _ = BitDepth::from_bits(16);
}
}