pub(crate) fn forward(pixels: &mut [u32]) {
for pixel in pixels.iter_mut() {
let green = (*pixel >> 8) & 0xff;
let red = ((*pixel >> 16) & 0xff).wrapping_sub(green) & 0xff;
let blue = (*pixel & 0xff).wrapping_sub(green) & 0xff;
*pixel = (*pixel & 0xff00_ff00) | (red << 16) | blue;
}
}
pub(crate) fn inverse(pixels: &mut [u32]) {
inverse_row(pixels);
}
pub(crate) fn inverse_row(row: &mut [u32]) {
for pixel in row.iter_mut() {
let green = (*pixel >> 8) & 0xff;
let red_blue = (*pixel & 0x00ff_00ff).wrapping_add((green << 16) | green) & 0x00ff_00ff;
*pixel = (*pixel & 0xff00_ff00) | red_blue;
}
}
#[cfg(test)]
mod tests {
use super::{forward, inverse, inverse_row};
use proptest::prelude::*;
#[test]
fn inverse_adds_green_to_red_and_blue_with_wrap() {
let mut px = [0x0010_0220, 0xffff_03fe];
inverse(&mut px);
assert_eq!(px, [0x0012_0222, 0xff02_0301]);
}
#[test]
fn forward_matches_known_vector() {
let mut px = [0xff6e_6478];
forward(&mut px);
assert_eq!(px, [0xff0a_6414]);
}
#[test]
fn forward_does_not_propagate_borrow_from_blue_to_red() {
let mut px = [0x0005_0100];
forward(&mut px);
assert_eq!(px, [0x0004_01ff]);
}
fn lcg_pixels(seed: u32) -> Vec<u32> {
let mut state = seed;
(0..1024)
.map(|_| {
state = state.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
state
})
.collect()
}
#[test]
fn forward_then_inverse_is_identity_over_lcg() {
let original = lcg_pixels(0x1234_5678);
let mut px = original.clone();
forward(&mut px);
inverse(&mut px);
assert_eq!(px, original);
}
#[test]
fn whole_buffer_inverse_equals_looping_inverse_row() {
let coded = lcg_pixels(0x0bad_cafe);
let width = 7usize;
let usable = coded.len() / width * width;
let coded = &coded[..usable];
let mut batch = coded.to_vec();
inverse(&mut batch);
let mut rows = coded.to_vec();
for row in rows.chunks_mut(width) {
inverse_row(row);
}
assert_eq!(batch, rows);
}
#[test]
fn inverse_then_forward_is_identity_over_lcg() {
let original = lcg_pixels(0x9e37_79b9);
let mut px = original.clone();
inverse(&mut px);
forward(&mut px);
assert_eq!(px, original);
}
proptest! {
#[test]
fn forward_then_inverse_is_identity(
pixels in prop::collection::vec(any::<u32>(), 0..256)
) {
let mut px = pixels.clone();
forward(&mut px);
inverse(&mut px);
prop_assert_eq!(px, pixels);
}
}
}