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}