use oxideav_core::{Error, Result};
use crate::bitstream::{BitReader, BitWriter};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum AlphaChannelType {
Eight,
Sixteen,
}
impl AlphaChannelType {
pub fn from_code(c: u8) -> Result<Option<Self>> {
match c {
0 => Ok(None),
1 => Ok(Some(AlphaChannelType::Eight)),
2 => Ok(Some(AlphaChannelType::Sixteen)),
_ => Err(Error::unsupported(format!(
"prores: alpha_channel_type {c} not supported"
))),
}
}
pub fn code(self) -> u8 {
match self {
AlphaChannelType::Eight => 1,
AlphaChannelType::Sixteen => 2,
}
}
pub fn mask(self) -> u32 {
match self {
AlphaChannelType::Eight => 0xFF,
AlphaChannelType::Sixteen => 0xFFFF,
}
}
}
const MAX_RUN: u32 = 2048;
pub fn decode_scanned_alpha(
data: &[u8],
num_values: usize,
act: AlphaChannelType,
) -> Result<Vec<u16>> {
let mask = act.mask();
let mut br = BitReader::new(data);
let mut out = vec![0u16; num_values];
let mut n: usize = 0;
let mut previous_alpha: i32 = -1;
while n < num_values {
let (diff, is_modulo) = read_alpha_difference(&mut br, act)?;
let mut alpha = previous_alpha.wrapping_add(diff);
if is_modulo {
alpha = (alpha as u32 & mask) as i32;
}
previous_alpha = alpha;
let run = read_run(&mut br)? as usize;
if run == 0 {
return Err(Error::invalid("prores alpha: zero run"));
}
if n + run > num_values {
return Err(Error::invalid(
"prores alpha: run overruns alphaValues array",
));
}
let alpha_u = (alpha as u32 & mask) as u16;
for _ in 0..run {
out[n] = alpha_u;
n += 1;
}
}
Ok(out)
}
pub fn encode_scanned_alpha(values: &[u16], act: AlphaChannelType) -> Result<Vec<u8>> {
let mask = act.mask();
let mut bw = BitWriter::new();
let mut n: usize = 0;
let mut previous_alpha: i32 = -1;
while n < values.len() {
let alpha = (values[n] as u32 & mask) as i32;
let mut run: u32 = 1;
while n + (run as usize) < values.len() && values[n + (run as usize)] == values[n] {
if run == MAX_RUN {
break;
}
run += 1;
}
let raw_diff: i32 = alpha - previous_alpha;
write_alpha_difference(&mut bw, raw_diff, alpha, mask, act);
write_run(&mut bw, run);
previous_alpha = alpha;
n += run as usize;
}
bw.align_byte();
Ok(bw.finish())
}
fn read_alpha_difference(br: &mut BitReader<'_>, act: AlphaChannelType) -> Result<(i32, bool)> {
let escape = br.read_bit()?;
if escape == 1 {
let bits = match act {
AlphaChannelType::Eight => 8,
AlphaChannelType::Sixteen => 16,
};
let raw = br.read_bits(bits)?;
Ok((raw as i32, true))
} else {
let mag_bits = match act {
AlphaChannelType::Eight => 3, AlphaChannelType::Sixteen => 6, };
let mag = br.read_bits(mag_bits)? + 1;
let sign = br.read_bit()?;
let val = if sign == 0 { mag as i32 } else { -(mag as i32) };
Ok((val, false))
}
}
fn write_alpha_difference(
bw: &mut BitWriter,
raw_diff: i32,
_alpha: i32,
mask: u32,
act: AlphaChannelType,
) {
let small_max = match act {
AlphaChannelType::Eight => 8,
AlphaChannelType::Sixteen => 64,
};
let mag_bits = match act {
AlphaChannelType::Eight => 3,
AlphaChannelType::Sixteen => 6,
};
let abs = raw_diff.unsigned_abs();
if abs >= 1 && abs <= small_max {
bw.write_bit(0);
bw.write_bits(abs - 1, mag_bits);
bw.write_bit(if raw_diff < 0 { 1 } else { 0 });
} else {
bw.write_bit(1);
let bits = match act {
AlphaChannelType::Eight => 8,
AlphaChannelType::Sixteen => 16,
};
let raw_u: u32 = (raw_diff as u32) & mask;
bw.write_bits(raw_u, bits);
}
}
fn read_run(br: &mut BitReader<'_>) -> Result<u32> {
let b0 = br.read_bit()?;
if b0 == 1 {
return Ok(1);
}
let nibble = br.read_bits(4)?;
if nibble != 0 {
return Ok(nibble + 1);
}
let flc = br.read_bits(11)?;
let run = flc + 1;
if !(17..=2048).contains(&run) {
return Err(Error::invalid("prores alpha: run length out of range"));
}
Ok(run)
}
fn write_run(bw: &mut BitWriter, run: u32) {
debug_assert!((1..=MAX_RUN).contains(&run));
if run == 1 {
bw.write_bit(1);
return;
}
if run <= 16 {
bw.write_bit(0);
bw.write_bits(run - 1, 4);
return;
}
bw.write_bits(0, 5); bw.write_bits(run - 1, 11);
}
#[cfg(test)]
mod tests {
use super::*;
fn roundtrip(values: &[u16], act: AlphaChannelType) {
let buf = encode_scanned_alpha(values, act).expect("encode");
let decoded = decode_scanned_alpha(&buf, values.len(), act).expect("decode");
assert_eq!(decoded, values, "alpha roundtrip mismatch");
}
#[test]
fn run_codeword_length_one() {
let mut bw = BitWriter::new();
write_run(&mut bw, 1);
bw.align_byte();
let buf = bw.finish();
assert_eq!(buf, vec![0b1000_0000]);
}
#[test]
fn run_codeword_short() {
let mut bw = BitWriter::new();
write_run(&mut bw, 5);
bw.align_byte();
let buf = bw.finish();
assert_eq!(buf, vec![0b0010_0000]);
}
#[test]
fn run_roundtrip_all_lengths() {
for r in 1u32..=MAX_RUN {
let mut bw = BitWriter::new();
write_run(&mut bw, r);
bw.align_byte();
let buf = bw.finish();
let mut br = BitReader::new(&buf);
let got = read_run(&mut br).expect("decode run");
assert_eq!(got, r, "run {r}");
}
}
#[test]
fn alpha_8bit_constant_value() {
let values = vec![0xFFu16; 16];
roundtrip(&values, AlphaChannelType::Eight);
}
#[test]
fn alpha_16bit_constant_value() {
let values = vec![0xFFFFu16; 32];
roundtrip(&values, AlphaChannelType::Sixteen);
}
#[test]
fn alpha_8bit_first_run_diff_minus_1() {
let values = vec![0u16, 0u16, 0u16];
roundtrip(&values, AlphaChannelType::Eight);
}
#[test]
fn alpha_8bit_alternating_two_values() {
let mut v = Vec::new();
for i in 0..32 {
v.push(if i & 1 == 0 { 0x10 } else { 0x20 });
}
roundtrip(&v, AlphaChannelType::Eight);
}
#[test]
fn alpha_8bit_escape_path_large_diff() {
let values = vec![0u16, 100u16, 100u16];
roundtrip(&values, AlphaChannelType::Eight);
}
#[test]
fn alpha_16bit_escape_path_large_diff() {
let values = vec![0u16, 5000u16, 5000u16, 5000u16];
roundtrip(&values, AlphaChannelType::Sixteen);
}
#[test]
fn alpha_8bit_random_pattern() {
let mut v = Vec::new();
for i in 0..256 {
v.push((255u16 - (i as u16 % 32)) & 0xFF);
}
roundtrip(&v, AlphaChannelType::Eight);
}
#[test]
fn alpha_16bit_long_runs() {
let mut v = vec![0x1234u16; 100];
v.extend(vec![0xABCDu16; 50]);
roundtrip(&v, AlphaChannelType::Sixteen);
}
#[test]
fn alpha_channel_type_codes() {
assert_eq!(AlphaChannelType::from_code(0).unwrap(), None);
assert_eq!(
AlphaChannelType::from_code(1).unwrap(),
Some(AlphaChannelType::Eight)
);
assert_eq!(
AlphaChannelType::from_code(2).unwrap(),
Some(AlphaChannelType::Sixteen)
);
assert!(AlphaChannelType::from_code(3).is_err());
assert_eq!(AlphaChannelType::Eight.mask(), 0xFF);
assert_eq!(AlphaChannelType::Sixteen.mask(), 0xFFFF);
}
}