#[inline]
pub(crate) fn inverse_sample(y: i32, cg: i32, co: i32) -> (i32, i32, i32) {
let t = y - (cg >> 1);
let g = cg + t;
let b = t - (co >> 1);
let r = b + co;
(g, b, r)
}
#[cfg(test)]
#[inline]
pub(crate) fn forward_sample(r0: i32, r1: i32, r2: i32) -> (i32, i32, i32) {
let co = r2 - r1;
let t = r1 + (co >> 1);
let cg = r0 - t;
let y = t + (cg >> 1);
(y, cg, co)
}
pub(crate) fn inverse_block(r0: &mut [i32], r1: &mut [i32], r2: &mut [i32], n: usize) {
let r0 = &mut r0[..n];
let r1 = &mut r1[..n];
let r2 = &mut r2[..n];
for ((r0, r1), r2) in r0.iter_mut().zip(r1.iter_mut()).zip(r2.iter_mut()) {
let (g, b, r) = inverse_sample(*r0, *r1, *r2);
*r0 = g;
*r1 = b;
*r2 = r;
}
}
#[cfg(test)]
pub(crate) fn inverse_block_i16(r0: &mut [i16], r1: &mut [i16], r2: &mut [i16], n: usize) {
let r0 = &mut r0[..n];
let r1 = &mut r1[..n];
let r2 = &mut r2[..n];
for ((r0, r1), r2) in r0.iter_mut().zip(r1.iter_mut()).zip(r2.iter_mut()) {
let (g, b, r) = inverse_sample(*r0 as i32, *r1 as i32, *r2 as i32);
*r0 = g.clamp(i16::MIN as i32, i16::MAX as i32) as i16;
*r1 = b.clamp(i16::MIN as i32, i16::MAX as i32) as i16;
*r2 = r.clamp(i16::MIN as i32, i16::MAX as i32) as i16;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ycgco_r_is_reversible() {
for r0 in (-300..=300).step_by(7) {
for r1 in (-300..=300).step_by(11) {
for r2 in (-300..=300).step_by(13) {
let (y, cg, co) = forward_sample(r0, r1, r2);
assert_eq!(inverse_sample(y, cg, co), (r0, r1, r2));
}
}
}
}
#[test]
fn zero_maps_to_zero() {
assert_eq!(inverse_sample(0, 0, 0), (0, 0, 0));
assert_eq!(forward_sample(0, 0, 0), (0, 0, 0));
}
#[test]
fn block_matches_per_sample() {
let mut a = vec![10i32, -5, 3, 100];
let mut b = vec![7i32, 2, -8, -100];
let mut c = vec![1i32, 4, -2, 50];
let expect: Vec<_> = (0..4).map(|i| inverse_sample(a[i], b[i], c[i])).collect();
inverse_block(&mut a, &mut b, &mut c, 4);
for i in 0..4 {
assert_eq!((a[i], b[i], c[i]), expect[i]);
}
}
#[test]
fn i16_block_lossless_within_range() {
let orig: Vec<(i32, i32, i32)> = (0..8).map(|i| (i - 4, (i * 3) - 12, 10 - i)).collect();
let mut y = vec![0i16; 8];
let mut cg = vec![0i16; 8];
let mut co = vec![0i16; 8];
for (i, &(r0, r1, r2)) in orig.iter().enumerate() {
let (a, b, cc) = forward_sample(r0, r1, r2);
y[i] = a as i16;
cg[i] = b as i16;
co[i] = cc as i16;
}
inverse_block_i16(&mut y, &mut cg, &mut co, 8);
for (i, &(r0, r1, r2)) in orig.iter().enumerate() {
assert_eq!((y[i] as i32, cg[i] as i32, co[i] as i32), (r0, r1, r2));
}
}
}