1use yog_data::Schema;
5use yog_data::with::Raw;
6
7use crate::color::Color;
8
9use crate::image::{BYTES_PER_PIXEL, Image};
10
11#[derive(Schema, Debug, Clone, PartialEq, Default)]
14pub struct Mask {
15 pub width: u32,
16 pub height: u32,
17 #[data(with = "Raw")]
18 pub bits: Vec<u8>,
19}
20
21impl Mask {
22 pub fn row_bytes(width: u32) -> usize {
24 (width as usize).div_ceil(u8::BITS as usize)
25 }
26
27 pub fn get(&self, x: u32, y: u32) -> bool {
29 let at = y as usize * Self::row_bytes(self.width) + x as usize / u8::BITS as usize;
30 let bit = u8::BITS - 1 - x % u8::BITS;
31 self.bits.get(at).is_some_and(|b| b >> bit & 1 == 1)
32 }
33
34 pub fn of(image: &Image) -> Option<(Mask, Color)> {
38 let mut color = None;
39 let row = Self::row_bytes(image.width);
40 let mut bits = vec![0u8; row * image.height as usize];
41 for (i, p) in image.rgba.as_chunks::<BYTES_PER_PIXEL>().0.iter().enumerate() {
42 let alpha = p[BYTES_PER_PIXEL - 1];
43 if alpha == 0 {
44 continue;
45 }
46 if alpha != u8::MAX || *color.get_or_insert([p[0], p[1], p[2]]) != [p[0], p[1], p[2]] {
47 return None;
48 }
49 let (x, y) = (i % image.width as usize, i / image.width as usize);
50 bits[y * row + x / u8::BITS as usize] |=
51 1 << (u8::BITS as usize - 1 - x % u8::BITS as usize);
52 }
53 let [r, g, b] = color.unwrap_or_default();
54 let mask = Mask {
55 width: image.width,
56 height: image.height,
57 bits,
58 };
59 Some((mask, Color::from_rgba8([r, g, b, u8::MAX])))
60 }
61
62 pub fn image(&self, color: Color) -> Image {
64 let mut out = Image::new(self.width, self.height);
65 let px = color.to_rgba8();
66 for y in 0..self.height {
67 for x in 0..self.width {
68 if self.get(x, y) {
69 let at = (y * self.width + x) as usize * BYTES_PER_PIXEL;
70 out.rgba[at..at + BYTES_PER_PIXEL].copy_from_slice(&px);
71 }
72 }
73 }
74 out
75 }
76}