pub fn buffer_size_i420(width: u32, height: u32) -> usize {
let w = width as usize;
let h = height as usize;
let cw = w.div_ceil(2);
let ch = h.div_ceil(2);
w * h + 2 * cw * ch
}
fn planes_mut(buf: &mut [u8], width: u32, height: u32) -> (&mut [u8], &mut [u8], &mut [u8]) {
let w = width as usize;
let h = height as usize;
let cw = w.div_ceil(2);
let ch = h.div_ceil(2);
let (y, rest) = buf.split_at_mut(w * h);
let (u, v) = rest.split_at_mut(cw * ch);
(y, u, v)
}
pub fn gradient(width: u32, height: u32, t: u32) -> Vec<u8> {
let mut buf = vec![0u8; buffer_size_i420(width, height)];
let (w, h) = (width as usize, height as usize);
let cw = w.div_ceil(2);
let (y, u, v) = planes_mut(&mut buf, width, height);
for row in 0..h {
for col in 0..w {
y[row * w + col] = (col + row + t as usize) as u8;
}
}
let ch = h.div_ceil(2);
for row in 0..ch {
for col in 0..cw {
u[row * cw + col] = (col * 2 + t as usize) as u8;
v[row * cw + col] = (row * 2 + t as usize) as u8;
}
}
buf
}
pub fn moving_box(width: u32, height: u32, t: u32) -> Vec<u8> {
const BOX: usize = 64;
const BG: u8 = 128;
const FG: u8 = 255;
let mut buf = vec![0u8; buffer_size_i420(width, height)];
let (w, h) = (width as usize, height as usize);
let (y, u, v) = planes_mut(&mut buf, width, height);
let x0 = (t as usize * 2) % w;
let y0 = (t as usize * 2) % h;
for row in 0..h {
for col in 0..w {
let in_x = ((col + w - x0) % w) < BOX;
let in_y = ((row + h - y0) % h) < BOX;
y[row * w + col] = if in_x && in_y { FG } else { BG };
}
}
for c in u.iter_mut() {
*c = 128;
}
for c in v.iter_mut() {
*c = 128;
}
buf
}
pub fn noise(width: u32, height: u32, t: u32, seed: u64) -> Vec<u8> {
let mut buf = vec![0u8; buffer_size_i420(width, height)];
let mut state = seed ^ (t as u64).wrapping_mul(0x9E37_79B9_7F4A_7C15) ^ 0x2545_F491_4F6C_DD1D;
if state == 0 {
state = 0xDEAD_BEEF_CAFE_F00D;
}
for byte in buf.iter_mut() {
*byte = (xorshift64(&mut state) >> 33) as u8;
}
buf
}
pub fn busy(width: u32, height: u32, t: u32) -> Vec<u8> {
const TEXTURE_AMPLITUDE: i32 = 18;
let mut buf = vec![0u8; buffer_size_i420(width, height)];
let (w, h) = (width as usize, height as usize);
let (y, u, v) = planes_mut(&mut buf, width, height);
for row in 0..h {
for col in 0..w {
let bg = 64 + ((row as i32 + col as i32) / 8).min(127);
let mut state = (row as u64).wrapping_mul(2_654_435_761)
^ (col as u64).wrapping_mul(40_503)
^ (t as u64).wrapping_mul(0x9E37_79B9_7F4A_7C15);
if state == 0 {
state = 0x1234_5678_9ABC_DEF0;
}
let noise = ((xorshift64(&mut state) >> 33) as i32 & 0xff) - 128;
let val = bg + noise * TEXTURE_AMPLITUDE / 128;
y[row * w + col] = val.clamp(0, 255) as u8;
}
}
for c in u.iter_mut().chain(v.iter_mut()) {
*c = 128;
}
buf
}
fn xorshift64(state: &mut u64) -> u64 {
let mut x = *state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
*state = x;
x
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generators_produce_correct_length() {
for &(w, h) in &[(640u32, 480u32), (64, 64), (636, 476)] {
let expected = buffer_size_i420(w, h);
assert_eq!(gradient(w, h, 0).len(), expected);
assert_eq!(moving_box(w, h, 5).len(), expected);
assert_eq!(noise(w, h, 5, 42).len(), expected);
}
}
#[test]
fn buffer_size_even_matches_classic_formula() {
assert_eq!(buffer_size_i420(640, 480), 640 * 480 * 3 / 2);
}
#[test]
fn generators_are_deterministic() {
assert_eq!(gradient(64, 64, 3), gradient(64, 64, 3));
assert_eq!(moving_box(64, 64, 3), moving_box(64, 64, 3));
assert_eq!(noise(64, 64, 3, 7), noise(64, 64, 3, 7));
assert_ne!(noise(64, 64, 3, 7), noise(64, 64, 3, 8));
}
#[test]
fn moving_box_moves() {
assert_ne!(moving_box(128, 128, 0), moving_box(128, 128, 10));
}
#[test]
fn busy_is_deterministic_and_changes_each_frame() {
assert_eq!(busy(320, 240, 4), busy(320, 240, 4));
assert_ne!(busy(320, 240, 4), busy(320, 240, 5));
}
#[test]
fn busy_correct_length_and_neutral_chroma() {
for &(w, h) in &[(320u32, 240u32), (636, 476)] {
let b = busy(w, h, 3);
assert_eq!(b.len(), buffer_size_i420(w, h));
assert!(b[(w * h) as usize..].iter().all(|&c| c == 128));
}
}
}