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}