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}