Skip to main content

simple_render/wayland/
canvas.rs

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