use super::Ink;
use super::shape::{SPAN, Shape};
const SUB: u32 = 8;
const SAMPLES: u32 = SUB * SUB;
const FULL: u32 = 255;
pub(super) fn rgba(shapes: &[Shape], size: u16) -> Vec<u8> {
let edge = i64::from(size);
let den = 2 * edge * i64::from(SUB);
let mut out = Vec::with_capacity(usize::from(size) * usize::from(size) * 4);
for y in 0..edge {
for x in 0..edge {
out.extend_from_slice(&pixel(shapes, x, y, den));
}
}
out
}
#[derive(Default)]
struct Seen {
red: u32,
green: u32,
blue: u32,
hits: u32,
}
impl Seen {
fn take(&mut self, ink: Ink) {
self.red += u32::from(ink.red);
self.green += u32::from(ink.green);
self.blue += u32::from(ink.blue);
self.hits += 1;
}
fn pixel(&self) -> [u8; 4] {
if self.hits == 0 {
return [0; 4];
}
let mean = |total: u32| byte((total + self.hits / 2) / self.hits);
[
mean(self.red),
mean(self.green),
mean(self.blue),
byte((self.hits * FULL + SAMPLES / 2) / SAMPLES),
]
}
}
fn pixel(shapes: &[Shape], x: i64, y: i64, den: i64) -> [u8; 4] {
let mut seen = Seen::default();
for sy in 0..SUB {
for sx in 0..SUB {
if let Some(ink) = topmost(shapes, at(x, sx), at(y, sy), den) {
seen.take(ink);
}
}
}
seen.pixel()
}
fn at(whole: i64, step: u32) -> i64 {
SPAN * (2 * (whole * i64::from(SUB) + i64::from(step)) + 1)
}
fn topmost(shapes: &[Shape], x: i64, y: i64, den: i64) -> Option<Ink> {
shapes
.iter()
.rev()
.find(|shape| shape.holds(x, y, den))
.map(Shape::ink)
}
fn byte(value: u32) -> u8 {
u8::try_from(value.min(FULL)).unwrap_or(u8::MAX)
}