veridian_kernel/graphics/
gl_compositor.rs1#![allow(dead_code)]
12
13use alloc::{collections::BTreeMap, vec, vec::Vec};
14
15use super::texture_atlas::{AtlasRegion, ShelfAllocator};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
23pub enum BlendMode {
24 #[default]
26 Over,
27 Add,
29 Multiply,
31}
32
33pub type SurfaceId = u32;
39
40#[derive(Debug, Clone)]
42pub struct GlSurface {
43 pub id: SurfaceId,
45 pub atlas_region: AtlasRegion,
47 pub z_order: i32,
49 pub visible: bool,
51 pub opacity: u8,
53 pub blend_mode: BlendMode,
55 pub x: i32,
57 pub y: i32,
59 pub pixels: Vec<u32>,
61}
62
63#[derive(Debug)]
70pub struct GlCompositor {
71 surfaces: BTreeMap<SurfaceId, GlSurface>,
73 atlas: ShelfAllocator,
75 viewport_width: u32,
77 viewport_height: u32,
79 back_buffer: Vec<u32>,
81 next_id: SurfaceId,
83 clear_color: u32,
85}
86
87impl GlCompositor {
88 pub fn new(viewport_width: u32, viewport_height: u32) -> Self {
90 let pixel_count = (viewport_width as usize) * (viewport_height as usize);
91 Self {
92 surfaces: BTreeMap::new(),
93 atlas: ShelfAllocator::new(viewport_width.max(2048), viewport_height.max(2048)),
94 viewport_width,
95 viewport_height,
96 back_buffer: vec![0xFF000000u32; pixel_count],
97 next_id: 1,
98 clear_color: 0xFF000000,
99 }
100 }
101
102 pub fn set_clear_color(&mut self, color: u32) {
104 self.clear_color = color;
105 }
106
107 pub fn width(&self) -> u32 {
109 self.viewport_width
110 }
111
112 pub fn height(&self) -> u32 {
114 self.viewport_height
115 }
116
117 pub fn surface_count(&self) -> usize {
119 self.surfaces.len()
120 }
121
122 pub fn create_surface(&mut self, width: u32, height: u32) -> Option<SurfaceId> {
126 let region = self.atlas.allocate(width, height)?;
127 let pixel_count = (width as usize) * (height as usize);
128 let id = self.next_id;
129 self.next_id += 1;
130
131 let surface = GlSurface {
132 id,
133 atlas_region: region,
134 z_order: 0,
135 visible: true,
136 opacity: 255,
137 blend_mode: BlendMode::Over,
138 x: 0,
139 y: 0,
140 pixels: vec![0x00000000u32; pixel_count],
141 };
142
143 self.surfaces.insert(id, surface);
144 Some(id)
145 }
146
147 pub fn destroy_surface(&mut self, id: SurfaceId) -> bool {
149 if let Some(surface) = self.surfaces.remove(&id) {
150 self.atlas.deallocate(&surface.atlas_region);
151 true
152 } else {
153 false
154 }
155 }
156
157 pub fn update_surface(&mut self, id: SurfaceId, pixels: &[u32]) -> bool {
161 if let Some(surface) = self.surfaces.get_mut(&id) {
162 let expected =
163 (surface.atlas_region.width as usize) * (surface.atlas_region.height as usize);
164 if pixels.len() == expected {
165 surface.pixels.clear();
166 surface.pixels.extend_from_slice(pixels);
167 true
168 } else {
169 false
170 }
171 } else {
172 false
173 }
174 }
175
176 pub fn set_surface_position(&mut self, id: SurfaceId, x: i32, y: i32) {
178 if let Some(surface) = self.surfaces.get_mut(&id) {
179 surface.x = x;
180 surface.y = y;
181 }
182 }
183
184 pub fn set_surface_z_order(&mut self, id: SurfaceId, z: i32) {
186 if let Some(surface) = self.surfaces.get_mut(&id) {
187 surface.z_order = z;
188 }
189 }
190
191 pub fn set_surface_visible(&mut self, id: SurfaceId, visible: bool) {
193 if let Some(surface) = self.surfaces.get_mut(&id) {
194 surface.visible = visible;
195 }
196 }
197
198 pub fn set_surface_opacity(&mut self, id: SurfaceId, opacity: u8) {
200 if let Some(surface) = self.surfaces.get_mut(&id) {
201 surface.opacity = opacity;
202 }
203 }
204
205 pub fn composite(&mut self) {
210 for px in self.back_buffer.iter_mut() {
212 *px = self.clear_color;
213 }
214
215 let mut order: Vec<SurfaceId> = self
217 .surfaces
218 .values()
219 .filter(|s| s.visible)
220 .map(|s| s.id)
221 .collect();
222
223 order.sort_by(|a, b| {
225 let za = self.surfaces.get(a).map_or(0, |s| s.z_order);
226 let zb = self.surfaces.get(b).map_or(0, |s| s.z_order);
227 za.cmp(&zb)
228 });
229
230 let vw = self.viewport_width as i32;
231 let vh = self.viewport_height as i32;
232
233 for sid in &order {
234 let surface = match self.surfaces.get(sid) {
235 Some(s) => s,
236 None => continue,
237 };
238
239 let sw = surface.atlas_region.width as i32;
240 let sh = surface.atlas_region.height as i32;
241 let sx = surface.x;
242 let sy = surface.y;
243 let opacity = surface.opacity;
244 let blend = surface.blend_mode;
245
246 for row in 0..sh {
247 let dst_y = sy + row;
248 if dst_y < 0 || dst_y >= vh {
249 continue;
250 }
251 for col in 0..sw {
252 let dst_x = sx + col;
253 if dst_x < 0 || dst_x >= vw {
254 continue;
255 }
256
257 let src_pixel = surface.pixels[(row * sw + col) as usize];
258 let dst_idx = (dst_y * vw + dst_x) as usize;
259
260 let blended = blend_pixel(src_pixel, self.back_buffer[dst_idx], opacity, blend);
261 self.back_buffer[dst_idx] = blended;
262 }
263 }
264 }
265 }
266
267 pub fn present(&self) -> &[u32] {
269 &self.back_buffer
270 }
271
272 pub fn get_surface(&self, id: SurfaceId) -> Option<&GlSurface> {
274 self.surfaces.get(&id)
275 }
276
277 pub fn get_surface_mut(&mut self, id: SurfaceId) -> Option<&mut GlSurface> {
279 self.surfaces.get_mut(&id)
280 }
281}
282
283fn blend_pixel(src: u32, dst: u32, opacity: u8, mode: BlendMode) -> u32 {
292 let sa = (src >> 24) & 0xFF;
293 let sr = (src >> 16) & 0xFF;
294 let sg = (src >> 8) & 0xFF;
295 let sb = src & 0xFF;
296
297 let da = (dst >> 24) & 0xFF;
298 let dr = (dst >> 16) & 0xFF;
299 let dg = (dst >> 8) & 0xFF;
300 let db = dst & 0xFF;
301
302 let alpha = (sa * opacity as u32) / 255;
304 let inv_alpha = 255 - alpha;
305
306 let (ra, rr, rg, rb) = match mode {
307 BlendMode::Over => {
308 let oa = alpha + (da * inv_alpha) / 255;
309 let or = (sr * alpha + dr * inv_alpha) / 255;
310 let og = (sg * alpha + dg * inv_alpha) / 255;
311 let ob = (sb * alpha + db * inv_alpha) / 255;
312 (oa.min(255), or.min(255), og.min(255), ob.min(255))
313 }
314 BlendMode::Add => {
315 let oa = (alpha + da).min(255);
316 let or = (sr * alpha / 255 + dr).min(255);
317 let og = (sg * alpha / 255 + dg).min(255);
318 let ob = (sb * alpha / 255 + db).min(255);
319 (oa, or, og, ob)
320 }
321 BlendMode::Multiply => {
322 let oa = (da * alpha) / 255;
323 let or = (dr * sr) / 255;
324 let og = (dg * sg) / 255;
325 let ob = (db * sb) / 255;
326 (oa.min(255), or.min(255), og.min(255), ob.min(255))
327 }
328 };
329
330 (ra << 24) | (rr << 16) | (rg << 8) | rb
331}
332
333#[cfg(test)]
338mod tests {
339 use super::*;
340
341 #[test]
342 fn test_create_surface() {
343 let mut comp = GlCompositor::new(640, 480);
344 let id = comp.create_surface(100, 50);
345 assert!(id.is_some());
346 assert_eq!(comp.surface_count(), 1);
347 }
348
349 #[test]
350 fn test_destroy_surface() {
351 let mut comp = GlCompositor::new(640, 480);
352 let id = comp.create_surface(100, 50).unwrap();
353 assert!(comp.destroy_surface(id));
354 assert_eq!(comp.surface_count(), 0);
355 assert!(!comp.destroy_surface(id)); }
357
358 #[test]
359 fn test_update_surface() {
360 let mut comp = GlCompositor::new(640, 480);
361 let id = comp.create_surface(4, 4).unwrap();
362 let pixels = vec![0xFFFF0000u32; 16];
363 assert!(comp.update_surface(id, &pixels));
364 let bad = vec![0u32; 10];
366 assert!(!comp.update_surface(id, &bad));
367 }
368
369 #[test]
370 fn test_composite_empty() {
371 let mut comp = GlCompositor::new(8, 8);
372 comp.set_clear_color(0xFF112233);
373 comp.composite();
374 assert_eq!(comp.present()[0], 0xFF112233);
375 }
376
377 #[test]
378 fn test_composite_opaque_surface() {
379 let mut comp = GlCompositor::new(8, 8);
380 comp.set_clear_color(0xFF000000);
381 let id = comp.create_surface(4, 4).unwrap();
382 let pixels = vec![0xFFFF0000u32; 16]; comp.update_surface(id, &pixels);
384 comp.set_surface_position(id, 0, 0);
385 comp.composite();
386 assert_eq!(comp.present()[0], 0xFFFF0000);
388 assert_eq!(comp.present()[4], 0xFF000000);
390 }
391
392 #[test]
393 fn test_z_order() {
394 let mut comp = GlCompositor::new(8, 8);
395 let id1 = comp.create_surface(4, 4).unwrap();
396 let id2 = comp.create_surface(4, 4).unwrap();
397 comp.update_surface(id1, &vec![0xFFFF0000u32; 16]);
398 comp.update_surface(id2, &vec![0xFF00FF00u32; 16]);
399 comp.set_surface_position(id1, 0, 0);
400 comp.set_surface_position(id2, 0, 0);
401 comp.set_surface_z_order(id1, 0);
402 comp.set_surface_z_order(id2, 1);
403 comp.composite();
404 assert_eq!(comp.present()[0], 0xFF00FF00);
406 }
407
408 #[test]
409 fn test_visibility() {
410 let mut comp = GlCompositor::new(8, 8);
411 comp.set_clear_color(0xFF000000);
412 let id = comp.create_surface(4, 4).unwrap();
413 comp.update_surface(id, &vec![0xFFFF0000u32; 16]);
414 comp.set_surface_visible(id, false);
415 comp.composite();
416 assert_eq!(comp.present()[0], 0xFF000000);
417 }
418
419 #[test]
420 fn test_blend_over_semitransparent() {
421 let result = blend_pixel(0x80FF0000, 0xFF000000, 255, BlendMode::Over);
423 let r = (result >> 16) & 0xFF;
424 assert!(r > 100 && r < 140);
426 }
427
428 #[test]
429 fn test_blend_add() {
430 let result = blend_pixel(0xFF800000, 0xFF400000, 255, BlendMode::Add);
431 let r = (result >> 16) & 0xFF;
432 assert_eq!(r, 192); }
434
435 #[test]
436 fn test_blend_multiply() {
437 let result = blend_pixel(0xFF800000, 0xFF800000, 255, BlendMode::Multiply);
438 let r = (result >> 16) & 0xFF;
439 assert!(r > 60 && r < 68);
441 }
442}