Skip to main content

simple_render/wayland/
canvas.rs

1use super::*;
2
3pub struct Canvas<'a> {
4    pub(in crate::wayland) pixels: &'a mut [u8],
5    pub(in crate::wayland) width: u32,
6    pub(in crate::wayland) height: u32,
7    pub(in crate::wayland) stride: u32,
8    pub(in crate::wayland) scale: u32,
9    pub(in crate::wayland) damage: Option<DamageRect>,
10}
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct DamageRect {
14    pub x: u32,
15    pub y: u32,
16    pub width: u32,
17    pub height: u32,
18}
19
20impl<'a> Canvas<'a> {
21    pub fn from_bgra_pixels(
22        pixels: &'a mut [u8],
23        width: u32,
24        height: u32,
25        stride: u32,
26        scale: u32,
27    ) -> Option<Self> {
28        if stride < width.checked_mul(4)? {
29            return None;
30        }
31        let required = if height == 0 {
32            0
33        } else {
34            stride
35                .checked_mul(height.saturating_sub(1))?
36                .checked_add(width.checked_mul(4)?)?
37        };
38        if pixels.len() < required as usize {
39            return None;
40        }
41
42        Some(Self {
43            pixels,
44            width,
45            height,
46            stride,
47            scale: scale.max(1),
48            damage: None,
49        })
50    }
51
52    pub fn pixels(&self) -> &[u8] {
53        self.pixels
54    }
55
56    pub fn pixels_mut(&mut self) -> &mut [u8] {
57        self.damage_all();
58        self.pixels
59    }
60
61    pub fn width(&self) -> u32 {
62        self.width
63    }
64
65    pub fn height(&self) -> u32 {
66        self.height
67    }
68
69    pub fn stride(&self) -> u32 {
70        self.stride
71    }
72
73    pub fn scale(&self) -> u32 {
74        self.scale
75    }
76
77    pub fn clear(&mut self, rgba: [u8; 4]) {
78        let bgra = rgba_to_bgra(rgba);
79        for pixel in self.pixels.chunks_exact_mut(4) {
80            pixel.copy_from_slice(&bgra);
81        }
82        self.damage_all();
83    }
84
85    pub fn put_pixel(&mut self, x: u32, y: u32, rgba: [u8; 4]) {
86        if x >= self.width || y >= self.height {
87            return;
88        }
89
90        let index = ((y * self.stride) + (x * 4)) as usize;
91        self.pixels[index..index + 4].copy_from_slice(&rgba_to_bgra(rgba));
92        self.damage_rect(DamageRect::new(x, y, 1, 1));
93    }
94
95    pub fn blend_pixel(&mut self, x: u32, y: u32, rgba: [u8; 4]) {
96        if x >= self.width || y >= self.height || rgba[3] == 0 {
97            return;
98        }
99
100        let index = ((y * self.stride) + (x * 4)) as usize;
101        let background = bgra_to_rgba([
102            self.pixels[index],
103            self.pixels[index + 1],
104            self.pixels[index + 2],
105            self.pixels[index + 3],
106        ]);
107        self.pixels[index..index + 4].copy_from_slice(&rgba_to_bgra(blend_color(background, rgba)));
108        self.damage_rect(DamageRect::new(x, y, 1, 1));
109    }
110
111    pub fn damage(&self) -> Option<DamageRect> {
112        self.damage
113    }
114
115    pub fn damage_rect(&mut self, rect: DamageRect) {
116        let Some(rect) = rect.clamp(self.width, self.height) else {
117            return;
118        };
119        self.damage = Some(match self.damage {
120            Some(damage) => damage.union(rect),
121            None => rect,
122        });
123    }
124
125    pub fn damage_all(&mut self) {
126        self.damage_rect(DamageRect::new(0, 0, self.width, self.height));
127    }
128}
129
130impl fmt::Debug for Canvas<'_> {
131    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
132        formatter
133            .debug_struct("Canvas")
134            .field("width", &self.width)
135            .field("height", &self.height)
136            .field("stride", &self.stride)
137            .finish_non_exhaustive()
138    }
139}
140
141impl DamageRect {
142    pub const fn new(x: u32, y: u32, width: u32, height: u32) -> Self {
143        Self {
144            x,
145            y,
146            width,
147            height,
148        }
149    }
150
151    fn right(self) -> u32 {
152        self.x.saturating_add(self.width)
153    }
154
155    fn bottom(self) -> u32 {
156        self.y.saturating_add(self.height)
157    }
158
159    fn union(self, other: Self) -> Self {
160        let x = self.x.min(other.x);
161        let y = self.y.min(other.y);
162        let right = self.right().max(other.right());
163        let bottom = self.bottom().max(other.bottom());
164        Self {
165            x,
166            y,
167            width: right.saturating_sub(x),
168            height: bottom.saturating_sub(y),
169        }
170    }
171
172    fn clamp(self, width: u32, height: u32) -> Option<Self> {
173        let x = self.x.min(width);
174        let y = self.y.min(height);
175        let right = self.right().min(width);
176        let bottom = self.bottom().min(height);
177        let width = right.saturating_sub(x);
178        let height = bottom.saturating_sub(y);
179        (width > 0 && height > 0).then_some(Self {
180            x,
181            y,
182            width,
183            height,
184        })
185    }
186}
187
188fn rgba_to_bgra([red, green, blue, alpha]: [u8; 4]) -> [u8; 4] {
189    [blue, green, red, alpha]
190}
191
192fn bgra_to_rgba([blue, green, red, alpha]: [u8; 4]) -> [u8; 4] {
193    [red, green, blue, alpha]
194}
195
196pub(in crate::wayland) fn blend_color(background: [u8; 4], foreground: [u8; 4]) -> [u8; 4] {
197    let foreground_alpha = u32::from(foreground[3]);
198    if foreground_alpha == 0 {
199        return background;
200    }
201    if foreground_alpha == 255 {
202        return foreground;
203    }
204
205    let background_alpha = u32::from(background[3]);
206    let inverse_foreground_alpha = 255 - foreground_alpha;
207    let output_alpha = foreground_alpha + background_alpha * inverse_foreground_alpha / 255;
208    if output_alpha == 0 {
209        return [0, 0, 0, 0];
210    }
211
212    let blend_component = |foreground: u8, background: u8| {
213        let foreground = u32::from(foreground) * foreground_alpha;
214        let background = u32::from(background) * background_alpha * inverse_foreground_alpha / 255;
215        ((foreground + background) / output_alpha).min(255) as u8
216    };
217
218    [
219        blend_component(foreground[0], background[0]),
220        blend_component(foreground[1], background[1]),
221        blend_component(foreground[2], background[2]),
222        output_alpha.min(255) as u8,
223    ]
224}