Skip to main content

truce_gui_types/
render.rs

1//! Render backend trait for abstracting over CPU and GPU rendering.
2//!
3//! Widgets draw through this trait, making them backend-agnostic.
4
5use crate::theme::Color;
6
7/// Opaque handle to a backend-registered image.
8///
9/// Returned by [`RenderBackend::register_image`]; passed to
10/// [`RenderBackend::draw_image`]. Valid until `unregister_image` or the
11/// backend is dropped. Two backends do not share ids - ids from one
12/// backend must not be used with another.
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub struct ImageId(pub u32);
15
16impl ImageId {
17    /// Sentinel value returned by the default trait impls when a backend
18    /// does not override `register_image`.
19    pub const INVALID: Self = Self(u32::MAX);
20}
21
22/// Abstraction over rendering backends (CPU via tiny-skia, GPU via wgpu).
23///
24/// **Coordinate contract.** All coordinates and font sizes are in
25/// **logical points**. Backends own the display scale factor
26/// internally and multiply inputs by it at raster time, so a widget
27/// drawn at `(100, 100)` with `size = 14.0` has the same apparent
28/// size on 1× and 2× displays. Callers must not pre-multiply by
29/// scale.
30pub trait RenderBackend {
31    /// Clear the entire surface with a solid color.
32    fn clear(&mut self, color: Color);
33
34    /// Fill a rectangle.
35    fn fill_rect(&mut self, x: f32, y: f32, w: f32, h: f32, color: Color);
36
37    /// Fill a circle.
38    fn fill_circle(&mut self, cx: f32, cy: f32, radius: f32, color: Color);
39
40    /// Stroke a circle outline.
41    fn stroke_circle(&mut self, cx: f32, cy: f32, radius: f32, color: Color, width: f32);
42
43    /// Stroke an arc (portion of a circle).
44    fn stroke_arc(
45        &mut self,
46        cx: f32,
47        cy: f32,
48        radius: f32,
49        start_angle: f32,
50        end_angle: f32,
51        color: Color,
52        width: f32,
53    );
54
55    /// Draw a line between two points.
56    fn draw_line(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, color: Color, width: f32);
57
58    /// Draw text using the embedded TrueType font (fontdue).
59    fn draw_text(&mut self, text: &str, x: f32, y: f32, size: f32, color: Color);
60
61    /// Measure the width of a text string in logical points, at the
62    /// given logical-point font size.
63    fn text_width(&self, text: &str, size: f32) -> f32;
64
65    /// Flush rendering to the display surface.
66    ///
67    /// No-op for CPU backends (pixels are read directly from the buffer).
68    /// GPU backends submit their command buffer and present here.
69    fn present(&mut self) {}
70
71    /// Register an RGBA8 image (premultiplied alpha, row-major, tightly packed).
72    ///
73    /// Returned id is valid until `unregister_image` is called or the backend
74    /// is dropped. Returns [`ImageId::INVALID`] if the backend does not
75    /// support images.
76    fn register_image(&mut self, _rgba: &[u8], _width: u32, _height: u32) -> ImageId {
77        ImageId::INVALID
78    }
79
80    /// Remove a previously-registered image. No-op if the id is invalid
81    /// or already unregistered.
82    fn unregister_image(&mut self, _id: ImageId) {}
83
84    /// Draw a previously-registered image at `(x, y)` sized `w × h`.
85    ///
86    /// Sampling is linear. The image is scaled to fill the target rect.
87    /// The default impl draws a magenta rect so missing backend support
88    /// is visually obvious.
89    fn draw_image(&mut self, _id: ImageId, x: f32, y: f32, w: f32, h: f32) {
90        self.fill_rect(x, y, w, h, Color::rgb(1.0, 0.0, 1.0));
91    }
92}