use std::vec;
use std::vec::Vec;
pub struct Fixture {
pub name: &'static str,
pub width: u32,
pub height: u32,
pub rgba: Vec<u8>,
}
pub fn all() -> Vec<Fixture> {
vec![
flat("flat", 32, 32),
checker("checker", 32, 32),
gradient("gradient", 64, 48),
text_blocks("text-blocks", 64, 48),
noise("noise", 64, 48),
lowpass_noise("lowpass-noise", 64, 48),
soft_alpha("alpha-soft", 64, 48),
hard_alpha("alpha-hard", 64, 48),
soft_alpha("alpha-odd", 17, 31),
lowpass_noise("photo-large", 1024, 768),
gradient("one-pixel", 1, 1),
gradient("single-column", 1, 33),
gradient("single-row", 33, 1),
text_blocks("odd-size", 17, 31),
]
}
fn flat(name: &'static str, width: u32, height: u32) -> Fixture {
let mut rgba = Vec::with_capacity(pixel_bytes(width, height));
for _ in 0..width * height {
rgba.extend_from_slice(&[96, 128, 160, 255]);
}
Fixture {
name,
width,
height,
rgba,
}
}
fn checker(name: &'static str, width: u32, height: u32) -> Fixture {
let mut rgba = Vec::with_capacity(pixel_bytes(width, height));
for y in 0..height {
for x in 0..width {
let value = if (x / 2 + y / 2) % 2 == 0 { 0 } else { 255 };
rgba.extend_from_slice(&[value, value, value, 255]);
}
}
Fixture {
name,
width,
height,
rgba,
}
}
fn gradient(name: &'static str, width: u32, height: u32) -> Fixture {
let mut rgba = Vec::with_capacity(pixel_bytes(width, height));
for y in 0..height {
for x in 0..width {
rgba.push(ramp(x, width));
rgba.push(ramp(y, height));
rgba.push(ramp(x + y, width + height - 1));
rgba.push(255);
}
}
Fixture {
name,
width,
height,
rgba,
}
}
fn text_blocks(name: &'static str, width: u32, height: u32) -> Fixture {
let mut rgba = Vec::with_capacity(pixel_bytes(width, height));
for y in 0..height {
for x in 0..width {
let stem = x % 7 < 2 && y % 11 < 8;
let bar = y % 11 == 8 && x % 14 < 9;
let value = if stem || bar { 24 } else { 240 };
rgba.push(value);
rgba.push(value);
rgba.push(value);
rgba.push(255);
}
}
Fixture {
name,
width,
height,
rgba,
}
}
pub(crate) fn noise(name: &'static str, width: u32, height: u32) -> Fixture {
let mut rng = Rng::seeded(name);
let mut rgba = Vec::with_capacity(pixel_bytes(width, height));
for _ in 0..width * height {
rgba.push(rng.next_byte());
rgba.push(rng.next_byte());
rgba.push(rng.next_byte());
rgba.push(255);
}
Fixture {
name,
width,
height,
rgba,
}
}
fn lowpass_noise(name: &'static str, width: u32, height: u32) -> Fixture {
let mut rng = Rng::seeded(name);
let count = (width * height) as usize;
let mut planes: Vec<Vec<u8>> = (0..3)
.map(|_| (0..count).map(|_| rng.next_byte()).collect())
.collect();
for plane in &mut planes {
*plane = blur(plane, width, height);
*plane = blur(plane, width, height);
}
let mut rgba = Vec::with_capacity(pixel_bytes(width, height));
for ((red, green), blue) in planes[0].iter().zip(&planes[1]).zip(&planes[2]) {
rgba.push(*red);
rgba.push(*green);
rgba.push(*blue);
rgba.push(255);
}
Fixture {
name,
width,
height,
rgba,
}
}
fn soft_alpha(name: &'static str, width: u32, height: u32) -> Fixture {
let mut fixture = gradient(name, width, height);
let span_x = i64::from(width) - 1;
let span_y = i64::from(height) - 1;
let corner = span_x * span_x + span_y * span_y;
for y in 0..height {
for x in 0..width {
let dx = 2 * i64::from(x) - span_x;
let dy = 2 * i64::from(y) - span_y;
let alpha = if corner == 0 {
255
} else {
255 - ((dx * dx + dy * dy) * 255 / corner).min(255)
};
let index = (y as usize * width as usize + x as usize) * 4 + 3;
fixture.rgba[index] = alpha as u8;
}
}
fixture
}
fn hard_alpha(name: &'static str, width: u32, height: u32) -> Fixture {
let mut fixture = gradient(name, width, height);
for y in 0..height {
for x in 0..width {
let inside =
x * 4 >= width && x * 4 < width * 3 && y * 4 >= height && y * 4 < height * 3;
let index = (y as usize * width as usize + x as usize) * 4 + 3;
fixture.rgba[index] = if inside { 255 } else { 0 };
}
}
fixture
}
fn blur(plane: &[u8], width: u32, height: u32) -> Vec<u8> {
let mut out = Vec::with_capacity(plane.len());
let last_x = i64::from(width) - 1;
let last_y = i64::from(height) - 1;
for y in 0..height {
for x in 0..width {
let mut sum: u32 = 0;
for dy in -1..=1_i64 {
for dx in -1..=1_i64 {
let sx = (i64::from(x) + dx).clamp(0, last_x) as usize;
let sy = (i64::from(y) + dy).clamp(0, last_y) as usize;
sum += u32::from(plane[sy * width as usize + sx]);
}
}
out.push((sum / 9) as u8);
}
}
out
}
fn ramp(index: u32, count: u32) -> u8 {
if count <= 1 {
0
} else {
(index * 255 / (count - 1)) as u8
}
}
fn pixel_bytes(width: u32, height: u32) -> usize {
width as usize * height as usize * 4
}
struct Rng(u32);
impl Rng {
fn seeded(name: &str) -> Self {
let mut state: u32 = 0x811c_9dc5;
for byte in name.as_bytes() {
state ^= u32::from(*byte);
state = state.wrapping_mul(0x0100_0193);
}
Self(state | 1)
}
fn next_u32(&mut self) -> u32 {
let mut state = self.0;
state ^= state << 13;
state ^= state >> 17;
state ^= state << 5;
self.0 = state;
state
}
fn next_byte(&mut self) -> u8 {
(self.next_u32() >> 24) as u8
}
}