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 (skrifa, via
59    /// `truce_font::raster`).
60    fn draw_text(&mut self, text: &str, x: f32, y: f32, size: f32, color: Color);
61
62    /// Measure the width of a text string in logical points, at the
63    /// given logical-point font size.
64    fn text_width(&self, text: &str, size: f32) -> f32;
65
66    /// Flush rendering to the display surface.
67    ///
68    /// No-op for CPU backends (pixels are read directly from the buffer).
69    /// GPU backends submit their command buffer and present here.
70    fn present(&mut self) {}
71
72    /// Register an RGBA8 image (premultiplied alpha, row-major, tightly packed).
73    ///
74    /// Returned id is valid until `unregister_image` is called or the backend
75    /// is dropped. Returns [`ImageId::INVALID`] if the backend does not
76    /// support images.
77    fn register_image(&mut self, _rgba: &[u8], _width: u32, _height: u32) -> ImageId {
78        ImageId::INVALID
79    }
80
81    /// Remove a previously-registered image. No-op if the id is invalid
82    /// or already unregistered.
83    fn unregister_image(&mut self, _id: ImageId) {}
84
85    /// Draw a previously-registered image at `(x, y)` sized `w × h`.
86    ///
87    /// Sampling is linear. The image is scaled to fill the target rect.
88    /// The default impl draws a magenta rect so missing backend support
89    /// is visually obvious.
90    fn draw_image(&mut self, _id: ImageId, x: f32, y: f32, w: f32, h: f32) {
91        self.fill_rect(x, y, w, h, Color::rgb(1.0, 0.0, 1.0));
92    }
93}