use yog_data::Schema;
use yog_data::with::Raw;
use crate::color::Color;
use crate::image::{BYTES_PER_PIXEL, Image};
#[derive(Schema, Debug, Clone, PartialEq, Default)]
pub struct Mask {
pub width: u32,
pub height: u32,
#[data(with = "Raw")]
pub bits: Vec<u8>,
}
impl Mask {
pub fn row_bytes(width: u32) -> usize {
(width as usize).div_ceil(u8::BITS as usize)
}
pub fn get(&self, x: u32, y: u32) -> bool {
let at = y as usize * Self::row_bytes(self.width) + x as usize / u8::BITS as usize;
let bit = u8::BITS - 1 - x % u8::BITS;
self.bits.get(at).is_some_and(|b| b >> bit & 1 == 1)
}
pub fn of(image: &Image) -> Option<(Mask, Color)> {
let mut color = None;
let row = Self::row_bytes(image.width);
let mut bits = vec![0u8; row * image.height as usize];
for (i, p) in image.rgba.as_chunks::<BYTES_PER_PIXEL>().0.iter().enumerate() {
let alpha = p[BYTES_PER_PIXEL - 1];
if alpha == 0 {
continue;
}
if alpha != u8::MAX || *color.get_or_insert([p[0], p[1], p[2]]) != [p[0], p[1], p[2]] {
return None;
}
let (x, y) = (i % image.width as usize, i / image.width as usize);
bits[y * row + x / u8::BITS as usize] |=
1 << (u8::BITS as usize - 1 - x % u8::BITS as usize);
}
let [r, g, b] = color.unwrap_or_default();
let mask = Mask {
width: image.width,
height: image.height,
bits,
};
Some((mask, Color::from_rgba8([r, g, b, u8::MAX])))
}
pub fn image(&self, color: Color) -> Image {
let mut out = Image::new(self.width, self.height);
let px = color.to_rgba8();
for y in 0..self.height {
for x in 0..self.width {
if self.get(x, y) {
let at = (y * self.width + x) as usize * BYTES_PER_PIXEL;
out.rgba[at..at + BYTES_PER_PIXEL].copy_from_slice(&px);
}
}
}
out
}
}