1use tiny_skia::{Paint, PathBuilder, Pixmap, Stroke, Transform};
6
7use crate::render::RenderBackend;
8use crate::theme::Color;
9
10pub struct CpuBackend {
15 pixmap: Pixmap,
16}
17
18impl CpuBackend {
19 pub fn new(width: u32, height: u32) -> Option<Self> {
21 Pixmap::new(width, height).map(|pixmap| Self { pixmap })
22 }
23
24 pub fn data(&self) -> &[u8] {
26 self.pixmap.data()
27 }
28
29 pub fn width(&self) -> u32 {
31 self.pixmap.width()
32 }
33
34 pub fn height(&self) -> u32 {
36 self.pixmap.height()
37 }
38}
39
40impl RenderBackend for CpuBackend {
41 fn clear(&mut self, color: Color) {
42 self.pixmap.fill(color.to_skia());
43 }
44
45 fn fill_rect(&mut self, x: f32, y: f32, w: f32, h: f32, color: Color) {
46 let rect = match tiny_skia::Rect::from_xywh(x, y, w, h) {
47 Some(r) => r,
48 None => return,
49 };
50 let mut paint = Paint::default();
51 paint.set_color(color.to_skia());
52 paint.anti_alias = true;
53 self.pixmap
54 .fill_rect(rect, &paint, Transform::identity(), None);
55 }
56
57 fn fill_circle(&mut self, cx: f32, cy: f32, radius: f32, color: Color) {
58 let mut pb = PathBuilder::new();
59 pb.push_circle(cx, cy, radius);
60 let path = match pb.finish() {
61 Some(p) => p,
62 None => return,
63 };
64 let mut paint = Paint::default();
65 paint.set_color(color.to_skia());
66 paint.anti_alias = true;
67 self.pixmap.fill_path(
68 &path,
69 &paint,
70 tiny_skia::FillRule::Winding,
71 Transform::identity(),
72 None,
73 );
74 }
75
76 fn stroke_circle(&mut self, cx: f32, cy: f32, radius: f32, color: Color, width: f32) {
77 let mut pb = PathBuilder::new();
78 pb.push_circle(cx, cy, radius);
79 let path = match pb.finish() {
80 Some(p) => p,
81 None => return,
82 };
83 let mut paint = Paint::default();
84 paint.set_color(color.to_skia());
85 paint.anti_alias = true;
86 let stroke = Stroke {
87 width,
88 ..Stroke::default()
89 };
90 self.pixmap
91 .stroke_path(&path, &paint, &stroke, Transform::identity(), None);
92 }
93
94 fn stroke_arc(
95 &mut self,
96 cx: f32,
97 cy: f32,
98 radius: f32,
99 start_angle: f32,
100 end_angle: f32,
101 color: Color,
102 width: f32,
103 ) {
104 let segments = 64;
105 let mut pb = PathBuilder::new();
106 let angle_range = end_angle - start_angle;
107 let step = angle_range / segments as f32;
108
109 for i in 0..=segments {
110 let angle = start_angle + step * i as f32;
111 let x = cx + radius * angle.cos();
112 let y = cy + radius * angle.sin();
113 if i == 0 {
114 pb.move_to(x, y);
115 } else {
116 pb.line_to(x, y);
117 }
118 }
119
120 let path = match pb.finish() {
121 Some(p) => p,
122 None => return,
123 };
124 let mut paint = Paint::default();
125 paint.set_color(color.to_skia());
126 paint.anti_alias = true;
127 let stroke = Stroke {
128 width,
129 line_cap: tiny_skia::LineCap::Round,
130 ..Stroke::default()
131 };
132 self.pixmap
133 .stroke_path(&path, &paint, &stroke, Transform::identity(), None);
134 }
135
136 fn draw_line(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, color: Color, width: f32) {
137 let mut pb = PathBuilder::new();
138 pb.move_to(x1, y1);
139 pb.line_to(x2, y2);
140 let path = match pb.finish() {
141 Some(p) => p,
142 None => return,
143 };
144 let mut paint = Paint::default();
145 paint.set_color(color.to_skia());
146 paint.anti_alias = true;
147 let stroke = Stroke {
148 width,
149 line_cap: tiny_skia::LineCap::Round,
150 ..Stroke::default()
151 };
152 self.pixmap
153 .stroke_path(&path, &paint, &stroke, Transform::identity(), None);
154 }
155
156 fn draw_text(&mut self, text: &str, x: f32, y: f32, size: f32, color: Color) {
157 let w = self.pixmap.width();
158 let h = self.pixmap.height();
159 crate::font::draw_text_fontdue(
160 self.pixmap.data_mut(),
161 w, h,
162 text, x, y, size,
163 color.r, color.g, color.b, color.a,
164 );
165 }
166
167 fn text_width(&self, text: &str, size: f32) -> f32 {
168 crate::font::text_width_fontdue(text, size)
169 }
170}