1#![allow(dead_code)]
9
10use alloc::{string::String, vec::Vec};
11
12use super::{
13 css_parser::fp_to_px,
14 layout::{LayoutBox, Rect},
15 style::{BorderStyle, Display, Overflow, Visibility},
16};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum BorderSide {
21 Top,
22 Right,
23 Bottom,
24 Left,
25}
26
27#[derive(Debug, Clone)]
29pub enum DisplayCommand {
30 SolidColor(u32, PixelRect),
32 Text(String, i32, i32, u32, i32),
34 Border(u32, PixelRect, i32, BorderSide),
36 ClipRect(PixelRect),
38 PopClip,
40}
41
42#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
44pub struct PixelRect {
45 pub x: i32,
46 pub y: i32,
47 pub width: i32,
48 pub height: i32,
49}
50
51impl PixelRect {
52 pub fn new(x: i32, y: i32, width: i32, height: i32) -> Self {
53 Self {
54 x,
55 y,
56 width,
57 height,
58 }
59 }
60
61 pub fn from_fp(rect: &Rect) -> Self {
63 Self {
64 x: fp_to_px(rect.x),
65 y: fp_to_px(rect.y),
66 width: fp_to_px(rect.width),
67 height: fp_to_px(rect.height),
68 }
69 }
70
71 pub fn intersect(&self, other: &PixelRect) -> Option<PixelRect> {
73 let x1 = core::cmp::max(self.x, other.x);
74 let y1 = core::cmp::max(self.y, other.y);
75 let x2 = core::cmp::min(self.x + self.width, other.x + other.width);
76 let y2 = core::cmp::min(self.y + self.height, other.y + other.height);
77 if x2 > x1 && y2 > y1 {
78 Some(PixelRect::new(x1, y1, x2 - x1, y2 - y1))
79 } else {
80 None
81 }
82 }
83
84 pub fn contains(&self, x: i32, y: i32) -> bool {
86 x >= self.x && x < self.x + self.width && y >= self.y && y < self.y + self.height
87 }
88}
89
90#[derive(Debug, Clone, Default)]
92pub struct DisplayList {
93 pub commands: Vec<DisplayCommand>,
94}
95
96impl DisplayList {
97 pub fn new() -> Self {
98 Self {
99 commands: Vec::new(),
100 }
101 }
102
103 pub fn push(&mut self, cmd: DisplayCommand) {
104 self.commands.push(cmd);
105 }
106}
107
108pub fn build_display_list(layout: &LayoutBox, scroll_y: i32) -> DisplayList {
110 let mut list = DisplayList::new();
111 render_layout_box(&mut list, layout, scroll_y);
112 list
113}
114
115fn render_layout_box(list: &mut DisplayList, layout: &LayoutBox, scroll_y: i32) {
117 if layout.style.display == Display::None {
118 return;
119 }
120 if layout.style.visibility == Visibility::Hidden {
121 for child in &layout.children {
123 render_layout_box(list, child, scroll_y);
124 }
125 return;
126 }
127
128 render_background(list, layout, scroll_y);
130
131 render_borders(list, layout, scroll_y);
133
134 let needs_clip = matches!(layout.style.overflow, Overflow::Hidden | Overflow::Scroll);
136 if needs_clip {
137 let content_rect = PixelRect::from_fp(&layout.dimensions.content);
138 list.push(DisplayCommand::ClipRect(PixelRect::new(
139 content_rect.x,
140 content_rect.y - scroll_y,
141 content_rect.width,
142 content_rect.height,
143 )));
144 }
145
146 for line in &layout.line_boxes {
148 let line_y = fp_to_px(line.y) - scroll_y;
149 for frag in &line.fragments {
150 list.push(DisplayCommand::Text(
151 frag.text.clone(),
152 fp_to_px(line.x) + fp_to_px(frag.width), line_y,
154 frag.color,
155 fp_to_px(frag.font_size),
156 ));
157 }
158 }
159
160 for child in &layout.children {
162 render_layout_box(list, child, scroll_y);
163 }
164
165 if needs_clip {
166 list.push(DisplayCommand::PopClip);
167 }
168}
169
170fn render_background(list: &mut DisplayList, layout: &LayoutBox, scroll_y: i32) {
172 let bg = layout.style.background_color;
173 if bg == 0 || (bg >> 24) == 0 {
174 return; }
176
177 let border_box = layout.dimensions.border_box();
178 let rect = PixelRect::new(
179 fp_to_px(border_box.x),
180 fp_to_px(border_box.y) - scroll_y,
181 fp_to_px(border_box.width),
182 fp_to_px(border_box.height),
183 );
184
185 if rect.width > 0 && rect.height > 0 {
186 list.push(DisplayCommand::SolidColor(bg, rect));
187 }
188}
189
190fn render_borders(list: &mut DisplayList, layout: &LayoutBox, scroll_y: i32) {
192 let d = &layout.dimensions;
193 let bb = d.border_box();
194 let bb_px = PixelRect::from_fp(&bb);
195 let y_offset = scroll_y;
196
197 if layout.style.border_top_style != BorderStyle::None && d.border.top > 0 {
199 let rect = PixelRect::new(
200 bb_px.x,
201 bb_px.y - y_offset,
202 bb_px.width,
203 fp_to_px(d.border.top),
204 );
205 list.push(DisplayCommand::Border(
206 layout.style.border_top_color,
207 rect,
208 fp_to_px(d.border.top),
209 BorderSide::Top,
210 ));
211 }
212
213 if layout.style.border_right_style != BorderStyle::None && d.border.right > 0 {
215 let rect = PixelRect::new(
216 bb_px.x + bb_px.width - fp_to_px(d.border.right),
217 bb_px.y - y_offset,
218 fp_to_px(d.border.right),
219 bb_px.height,
220 );
221 list.push(DisplayCommand::Border(
222 layout.style.border_right_color,
223 rect,
224 fp_to_px(d.border.right),
225 BorderSide::Right,
226 ));
227 }
228
229 if layout.style.border_bottom_style != BorderStyle::None && d.border.bottom > 0 {
231 let rect = PixelRect::new(
232 bb_px.x,
233 bb_px.y - y_offset + bb_px.height - fp_to_px(d.border.bottom),
234 bb_px.width,
235 fp_to_px(d.border.bottom),
236 );
237 list.push(DisplayCommand::Border(
238 layout.style.border_bottom_color,
239 rect,
240 fp_to_px(d.border.bottom),
241 BorderSide::Bottom,
242 ));
243 }
244
245 if layout.style.border_left_style != BorderStyle::None && d.border.left > 0 {
247 let rect = PixelRect::new(
248 bb_px.x,
249 bb_px.y - y_offset,
250 fp_to_px(d.border.left),
251 bb_px.height,
252 );
253 list.push(DisplayCommand::Border(
254 layout.style.border_left_color,
255 rect,
256 fp_to_px(d.border.left),
257 BorderSide::Left,
258 ));
259 }
260}
261
262pub struct Painter {
264 pub width: usize,
265 pub height: usize,
266 pub pixels: Vec<u32>,
267 clip_stack: Vec<PixelRect>,
268}
269
270impl Painter {
271 pub fn new(width: usize, height: usize) -> Self {
273 let size = width.checked_mul(height).unwrap_or(0);
274 Self {
275 width,
276 height,
277 pixels: alloc::vec![0xFFFFFFFF; size], clip_stack: Vec::new(),
279 }
280 }
281
282 pub fn paint(&mut self, display_list: &DisplayList) {
284 for cmd in &display_list.commands {
285 match cmd {
286 DisplayCommand::SolidColor(color, rect) => {
287 self.fill_rect(*color, rect);
288 }
289 DisplayCommand::Text(text, x, y, color, font_size) => {
290 self.draw_text(text, *x, *y, *color, *font_size);
291 }
292 DisplayCommand::Border(color, rect, _width, _side) => {
293 self.fill_rect(*color, rect);
294 }
295 DisplayCommand::ClipRect(rect) => {
296 self.clip_stack.push(*rect);
297 }
298 DisplayCommand::PopClip => {
299 self.clip_stack.pop();
300 }
301 }
302 }
303 }
304
305 pub fn fill_rect(&mut self, color: u32, rect: &PixelRect) {
307 let clipped = self.clip_rect(rect);
308 let clipped = match clipped {
309 Some(r) => r,
310 None => return,
311 };
312
313 let alpha = (color >> 24) & 0xFF;
314 if alpha == 0 {
315 return;
316 }
317
318 for py in clipped.y..(clipped.y + clipped.height) {
319 if py < 0 || py >= self.height as i32 {
320 continue;
321 }
322 for px in clipped.x..(clipped.x + clipped.width) {
323 if px < 0 || px >= self.width as i32 {
324 continue;
325 }
326 let idx = py as usize * self.width + px as usize;
327 if idx < self.pixels.len() {
328 if alpha == 255 {
329 self.pixels[idx] = color;
330 } else {
331 self.pixels[idx] = alpha_blend(color, self.pixels[idx]);
332 }
333 }
334 }
335 }
336 }
337
338 pub fn draw_text(&mut self, text: &str, x: i32, y: i32, color: u32, _font_size: i32) {
340 let mut cx = x;
341 for ch in text.chars() {
342 self.draw_char(ch, cx, y, color);
343 cx += 8; }
345 }
346
347 fn draw_char(&mut self, ch: char, x: i32, y: i32, color: u32) {
349 let glyph = get_glyph(ch);
350 for (row, &bits) in glyph.iter().enumerate() {
351 for col in 0..8 {
352 if (bits >> (7 - col)) & 1 != 0 {
353 let px = x + col;
354 let py = y + row as i32;
355 if px >= 0 && px < self.width as i32 && py >= 0 && py < self.height as i32 {
356 let idx = py as usize * self.width + px as usize;
357 if idx < self.pixels.len() {
358 let alpha = (color >> 24) & 0xFF;
359 if alpha == 255 {
360 self.pixels[idx] = color;
361 } else {
362 self.pixels[idx] = alpha_blend(color, self.pixels[idx]);
363 }
364 }
365 }
366 }
367 }
368 }
369 }
370
371 fn clip_rect(&self, rect: &PixelRect) -> Option<PixelRect> {
373 let viewport = PixelRect::new(0, 0, self.width as i32, self.height as i32);
374 let mut clipped = rect.intersect(&viewport)?;
375
376 for clip in &self.clip_stack {
377 clipped = clipped.intersect(clip)?;
378 }
379
380 Some(clipped)
381 }
382
383 pub fn get_pixel(&self, x: usize, y: usize) -> u32 {
385 if x < self.width && y < self.height {
386 self.pixels[y * self.width + x]
387 } else {
388 0
389 }
390 }
391
392 pub fn clear(&mut self, color: u32) {
394 for pixel in &mut self.pixels {
395 *pixel = color;
396 }
397 }
398
399 pub fn as_bytes(&self) -> &[u32] {
401 &self.pixels
402 }
403}
404
405pub fn alpha_blend(src: u32, dst: u32) -> u32 {
408 let sa = (src >> 24) & 0xFF;
409 let sr = (src >> 16) & 0xFF;
410 let sg = (src >> 8) & 0xFF;
411 let sb = src & 0xFF;
412
413 let da = (dst >> 24) & 0xFF;
414 let dr = (dst >> 16) & 0xFF;
415 let dg = (dst >> 8) & 0xFF;
416 let db = dst & 0xFF;
417
418 let inv_sa = 255 - sa;
419 let out_r = (sr * sa + dr * inv_sa) / 255;
420 let out_g = (sg * sa + dg * inv_sa) / 255;
421 let out_b = (sb * sa + db * inv_sa) / 255;
422 let out_a = sa + (da * inv_sa) / 255;
423
424 (out_a << 24) | (out_r << 16) | (out_g << 8) | out_b
425}
426
427fn get_glyph(ch: char) -> [u8; 16] {
430 let code = ch as u32;
432 if !(32..=126).contains(&code) {
433 return [0; 16]; }
435
436 match ch {
438 'A' => [
439 0x00, 0x18, 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
440 0x00, 0x00,
441 ],
442 'B' => [
443 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00,
444 0x00, 0x00,
445 ],
446 'H' => [
447 0x00, 0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00,
448 0x00, 0x00,
449 ],
450 'e' => [
451 0x00, 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
452 0x00, 0x00,
453 ],
454 'l' => [
455 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
456 0x00, 0x00,
457 ],
458 'o' => [
459 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
460 0x00, 0x00,
461 ],
462 _ => {
463 [
465 0x00, 0x00, 0x7E, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x00, 0x00, 0x00, 0x00,
466 0x00, 0x00,
467 ]
468 }
469 }
470}
471
472#[cfg(test)]
473mod tests {
474 #[allow(unused_imports)]
475 use alloc::vec;
476
477 use super::*;
478 use crate::browser::css_parser::px_to_fp;
479
480 #[test]
481 fn test_pixel_rect_new() {
482 let r = PixelRect::new(10, 20, 100, 50);
483 assert_eq!(r.x, 10);
484 assert_eq!(r.y, 20);
485 assert_eq!(r.width, 100);
486 assert_eq!(r.height, 50);
487 }
488
489 #[test]
490 fn test_pixel_rect_contains() {
491 let r = PixelRect::new(10, 10, 100, 50);
492 assert!(r.contains(50, 30));
493 assert!(!r.contains(5, 5));
494 assert!(!r.contains(200, 200));
495 }
496
497 #[test]
498 fn test_pixel_rect_intersect() {
499 let a = PixelRect::new(0, 0, 100, 100);
500 let b = PixelRect::new(50, 50, 100, 100);
501 let c = a.intersect(&b).unwrap();
502 assert_eq!(c.x, 50);
503 assert_eq!(c.y, 50);
504 assert_eq!(c.width, 50);
505 assert_eq!(c.height, 50);
506 }
507
508 #[test]
509 fn test_pixel_rect_no_intersect() {
510 let a = PixelRect::new(0, 0, 10, 10);
511 let b = PixelRect::new(20, 20, 10, 10);
512 assert!(a.intersect(&b).is_none());
513 }
514
515 #[test]
516 fn test_painter_new() {
517 let p = Painter::new(100, 100);
518 assert_eq!(p.width, 100);
519 assert_eq!(p.height, 100);
520 assert_eq!(p.pixels.len(), 10000);
521 assert_eq!(p.pixels[0], 0xFFFFFFFF);
523 }
524
525 #[test]
526 fn test_painter_fill_rect() {
527 let mut p = Painter::new(100, 100);
528 let rect = PixelRect::new(10, 10, 20, 20);
529 p.fill_rect(0xFFFF0000, &rect);
530 assert_eq!(p.get_pixel(15, 15), 0xFFFF0000);
531 assert_eq!(p.get_pixel(0, 0), 0xFFFFFFFF);
532 }
533
534 #[test]
535 fn test_painter_clear() {
536 let mut p = Painter::new(10, 10);
537 p.clear(0xFF000000);
538 assert_eq!(p.get_pixel(5, 5), 0xFF000000);
539 }
540
541 #[test]
542 fn test_alpha_blend_opaque() {
543 let result = alpha_blend(0xFFFF0000, 0xFF0000FF);
544 assert_eq!(result, 0xFFFF0000);
545 }
546
547 #[test]
548 fn test_alpha_blend_transparent() {
549 let result = alpha_blend(0x00FF0000, 0xFF0000FF);
550 assert_eq!(result & 0x00FFFFFF, 0x0000FF); }
552
553 #[test]
554 fn test_alpha_blend_half() {
555 let result = alpha_blend(0x80FF0000, 0xFF0000FF);
556 let r = (result >> 16) & 0xFF;
557 let b = result & 0xFF;
558 assert!(r > 60 && r < 200);
560 assert!(b > 60 && b < 200);
561 }
562
563 #[test]
564 fn test_display_list_new() {
565 let dl = DisplayList::new();
566 assert!(dl.commands.is_empty());
567 }
568
569 #[test]
570 fn test_display_list_push() {
571 let mut dl = DisplayList::new();
572 dl.push(DisplayCommand::SolidColor(
573 0xFFFF0000,
574 PixelRect::new(0, 0, 10, 10),
575 ));
576 assert_eq!(dl.commands.len(), 1);
577 }
578
579 #[test]
580 fn test_painter_draw_text() {
581 let mut p = Painter::new(100, 100);
582 p.draw_text("A", 10, 10, 0xFF000000, 16);
583 let mut has_black = false;
585 for y in 10..26 {
586 for x in 10..18 {
587 if p.get_pixel(x, y) == 0xFF000000 {
588 has_black = true;
589 }
590 }
591 }
592 assert!(has_black);
593 }
594
595 #[test]
596 fn test_get_glyph_a() {
597 let g = get_glyph('A');
598 assert_eq!(g[1], 0x18);
600 }
601
602 #[test]
603 fn test_get_glyph_nonprintable() {
604 let g = get_glyph('\0');
605 assert_eq!(g, [0; 16]);
606 }
607
608 #[test]
609 fn test_painter_clip() {
610 let mut p = Painter::new(100, 100);
611 p.clip_stack.push(PixelRect::new(20, 20, 60, 60));
612 let rect = PixelRect::new(0, 0, 100, 100);
613 p.fill_rect(0xFFFF0000, &rect);
614 assert_eq!(p.get_pixel(10, 10), 0xFFFFFFFF);
616 assert_eq!(p.get_pixel(30, 30), 0xFFFF0000);
618 }
619
620 #[test]
621 fn test_build_display_list_empty() {
622 let layout = LayoutBox::default();
623 let dl = build_display_list(&layout, 0);
624 assert!(dl.commands.is_empty() || !dl.commands.is_empty());
626 }
627
628 #[test]
629 fn test_pixel_rect_from_fp() {
630 let fp_rect = Rect {
631 x: px_to_fp(10),
632 y: px_to_fp(20),
633 width: px_to_fp(100),
634 height: px_to_fp(50),
635 };
636 let pr = PixelRect::from_fp(&fp_rect);
637 assert_eq!(pr.x, 10);
638 assert_eq!(pr.y, 20);
639 assert_eq!(pr.width, 100);
640 assert_eq!(pr.height, 50);
641 }
642
643 #[test]
644 fn test_painter_out_of_bounds() {
645 let p = Painter::new(10, 10);
646 assert_eq!(p.get_pixel(100, 100), 0);
647 }
648
649 #[test]
650 fn test_fill_rect_outside_viewport() {
651 let mut p = Painter::new(10, 10);
652 let rect = PixelRect::new(20, 20, 10, 10);
653 p.fill_rect(0xFFFF0000, &rect);
654 assert_eq!(p.get_pixel(0, 0), 0xFFFFFFFF);
656 }
657}