1#![forbid(unsafe_code)]
16
17pub mod diff;
18pub mod paint;
19pub mod presentation;
20pub mod tinyskia_backend;
21pub mod vello_backend;
22
23pub use paint::{
24 FontResource, GlyphInstance, GlyphRun, GradientStop, GradientStops, PaintCommand, PaintList,
25 PathBuilder,
26};
27pub use presentation::{
28 nonzero as presentation_nonzero, present_rgba_to_softbuffer, rgba_to_softbuffer,
29 PresentationError, SoftbufferPresenter,
30};
31pub use tinyskia_backend::TinySkiaBackend;
32pub use vello_backend::VelloRenderer;
33
34pub use kurbo::{BezPath, Point, Rect};
37
38pub trait RenderBackend: Send + 'static {
65 fn render(&mut self, paint_list: &PaintList);
88}
89
90#[cfg(test)]
91mod tests {
92 use super::{PaintCommand, PaintList, RenderBackend};
93 use kurbo::{Point, Rect};
94
95 struct MockBackend {
97 received_count: usize,
98 }
99
100 impl MockBackend {
101 fn new() -> Self {
102 Self { received_count: 0 }
103 }
104 }
105
106 impl RenderBackend for MockBackend {
107 fn render(&mut self, paint_list: &PaintList) {
108 self.received_count = paint_list.commands.len();
109 }
110 }
111
112 #[test]
113 fn new_creates_empty_commands() {
114 let list = PaintList::new();
115 assert!(list.commands.is_empty());
116 }
117
118 #[test]
119 fn default_creates_empty_commands() {
120 let list = PaintList::default();
121 assert!(list.commands.is_empty());
122 }
123
124 #[test]
125 fn clear_empties_non_empty_list() {
126 let mut list = PaintList::new();
127 list.commands
128 .push(PaintCommand::FillRect(Rect::ZERO, [255, 0, 0, 255]));
129 assert_eq!(list.commands.len(), 1);
130 list.clear();
131 assert!(list.commands.is_empty());
132 }
133
134 #[test]
135 fn commands_appear_in_order() {
136 let mut list = PaintList::new();
137 list.commands
138 .push(PaintCommand::FillRect(Rect::ZERO, [255, 0, 0, 255]));
139 list.commands
140 .push(PaintCommand::StrokeRect(Rect::ZERO, 1.0, [0, 255, 0, 255]));
141 list.commands.push(PaintCommand::DrawText(
142 Point::ZERO,
143 "hi".to_string(),
144 12.0,
145 [0, 0, 255, 255],
146 ));
147 assert_eq!(list.commands.len(), 3);
148 assert!(matches!(list.commands[0], PaintCommand::FillRect(..)));
149 assert!(matches!(list.commands[1], PaintCommand::StrokeRect(..)));
150 assert!(matches!(list.commands[2], PaintCommand::DrawText(..)));
151 }
152
153 #[test]
154 fn mock_backend_receives_commands() {
155 let mut list = PaintList::new();
156 list.commands
157 .push(PaintCommand::FillRect(Rect::ZERO, [255, 0, 0, 255]));
158 list.commands
159 .push(PaintCommand::StrokeRect(Rect::ZERO, 1.0, [0, 255, 0, 255]));
160
161 let mut backend = MockBackend::new();
162 assert_eq!(backend.received_count, 0);
163 backend.render(&list);
164 assert_eq!(backend.received_count, 2);
165
166 list.clear();
167 backend.render(&list);
168 assert_eq!(backend.received_count, 0);
169 }
170}