Skip to main content

agpu/
painter.rs

1//! AgpuPainter — implements `crate::paint::Painter` using wgpu.
2//!
3//! Collects all drawing commands during a frame, maintaining a clip stack.
4//! The actual GPU rendering happens when `finish()` is called with the
5//! render pass.
6
7use crate::core::{Color, Position, Rect, Size, TextStyle};
8use crate::paint::{Gradient, ImageHandle, Painter, Shadow};
9use crate::renderer::ShapeRenderer;
10use crate::text::TextEngine;
11
12/// A [`Painter`] backed by agpu's wgpu-based 2D renderer.
13pub struct AgpuPainter<'a> {
14    shapes: &'a mut ShapeRenderer,
15    text: &'a mut TextEngine,
16    clip_stack: Vec<Rect>,
17}
18
19impl<'a> AgpuPainter<'a> {
20    /// Create a new painter wrapping the shape renderer and text engine.
21    pub fn new(shapes: &'a mut ShapeRenderer, text: &'a mut TextEngine) -> Self {
22        Self {
23            shapes,
24            text,
25            clip_stack: Vec::new(),
26        }
27    }
28
29    /// Get the current clip rectangle, if any.
30    fn current_clip(&self) -> Option<Rect> {
31        self.clip_stack.last().copied()
32    }
33}
34
35impl Painter for AgpuPainter<'_> {
36    fn fill_rect(&mut self, rect: Rect, color: Color, corner_radius: f32) {
37        self.shapes.fill_rect(rect, color, corner_radius);
38    }
39
40    fn stroke_rect(&mut self, rect: Rect, color: Color, width: f32, corner_radius: f32) {
41        self.shapes.stroke_rect(rect, color, width, corner_radius);
42    }
43
44    fn stroke_rounded_rect(
45        &mut self,
46        rect: Rect,
47        color: Color,
48        width: f32,
49        corner_radius: f32,
50        bg_color: Color,
51    ) {
52        self.shapes
53            .stroke_rounded_rect(rect, color, width, corner_radius, bg_color);
54    }
55
56    fn fill_circle(&mut self, center: Position, radius: f32, color: Color) {
57        self.shapes.fill_circle(center, radius, color);
58    }
59
60    fn stroke_circle(&mut self, center: Position, radius: f32, color: Color, width: f32) {
61        self.shapes.stroke_circle(center, radius, color, width);
62    }
63
64    fn line(&mut self, from: Position, to: Position, color: Color, width: f32) {
65        self.shapes.line(from, to, color, width);
66    }
67
68    fn text(&mut self, pos: Position, text: &str, style: &TextStyle) {
69        let clip = self.current_clip();
70        self.text.draw(pos, text, style, clip);
71    }
72
73    fn measure_text(&self, text: &str, style: &TextStyle) -> Size {
74        // Estimation fallback when only &self is available.
75        let w = style.font_size * 0.6 * text.len() as f32;
76        Size::new(w, style.font_size * 1.2)
77    }
78
79    fn measure_text_mut(&mut self, text: &str, style: &TextStyle) -> Size {
80        self.text.measure(text, style)
81    }
82
83    fn push_clip(&mut self, rect: Rect) {
84        self.clip_stack.push(rect);
85    }
86
87    fn pop_clip(&mut self) {
88        self.clip_stack.pop();
89    }
90
91    fn fill_rect_gradient(&mut self, rect: Rect, gradient: &Gradient, corner_radius: f32) {
92        self.shapes
93            .fill_rect_gradient(rect, gradient, corner_radius);
94    }
95
96    fn shadow_rect(&mut self, rect: Rect, shadow: &Shadow, corner_radius: f32) {
97        self.shapes.shadow_rect(rect, shadow, corner_radius);
98    }
99
100    fn draw_image(&mut self, handle: &ImageHandle, rect: Rect) {
101        self.shapes.draw_image(handle, rect);
102    }
103}