1use piet_common::{kurbo, Color, LineCap, Piet, RenderContext, StrokeStyle};
6use plotters_backend::{BackendColor, BackendCoord, DrawingBackend, DrawingErrorKind};
7
8#[derive(Debug, PartialEq, Eq)]
9pub struct Error {}
10
11impl std::fmt::Display for Error {
12 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
13 writeln!(f, "plotters-piet error")
14 }
15}
16
17impl std::error::Error for Error {}
18
19pub struct PietBackend<'a, 'b> {
23 pub size: (u32, u32),
24 pub render_ctx: &'a mut Piet<'b>,
25}
26
27impl<'a, 'b> std::fmt::Debug for PietBackend<'a, 'b> {
28 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
29 fmt.debug_struct("PietBackend")
30 .field("size", &self.size)
31 .field("render_ctx", &"(not printable)")
32 .finish()
33 }
34}
35
36impl<'a, 'b> DrawingBackend for PietBackend<'a, 'b> {
37 type ErrorType = Error;
38
39 fn get_size(&self) -> (u32, u32) {
40 self.size
41 }
42
43 fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
44 Ok(())
45 }
46
47 fn present(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
48 self.render_ctx
49 .finish()
50 .map_err(|_| DrawingErrorKind::DrawingError(Error {}))
51 }
52
53 fn draw_pixel(
54 &mut self,
55 point: BackendCoord,
56 color: BackendColor,
57 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
58 let x = point.0 as f64;
59 let y = point.1 as f64;
60 self.render_ctx.fill(
61 kurbo::Rect::new(x, y, x + 1., y + 1.),
62 &plotters_color_to_piet(&color),
63 );
64 Ok(())
65 }
66
67 fn draw_line<S: plotters_backend::BackendStyle>(
68 &mut self,
69 from: BackendCoord,
70 to: BackendCoord,
71 style: &S,
72 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
73 let from = plotters_point_to_kurbo_mid(from);
74 let to = plotters_point_to_kurbo_mid(to);
75
76 self.render_ctx.stroke_styled(
77 kurbo::Line::new(from, to),
78 &plotters_color_to_piet(&style.color()),
79 style.stroke_width() as f64,
80 &STROKE_STYLE_SQUARE_CAP,
81 );
82 Ok(())
83 }
84
85 fn draw_rect<S: plotters_backend::BackendStyle>(
86 &mut self,
87 upper_left: BackendCoord,
88 bottom_right: BackendCoord,
89 style: &S,
90 fill: bool,
91 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
92 let color = plotters_color_to_piet(&style.color());
93
94 if fill {
95 let upper_left = plotters_point_to_kurbo_corner(upper_left);
96 let mut bottom_right = plotters_point_to_kurbo_corner(bottom_right);
97 bottom_right.x += 1.;
98 bottom_right.y += 1.;
99 let rect = kurbo::Rect::new(upper_left.x, upper_left.y, bottom_right.x, bottom_right.y);
100
101 self.render_ctx.fill(rect, &color);
102 } else {
103 let upper_left = plotters_point_to_kurbo_mid(upper_left);
104 let bottom_right = plotters_point_to_kurbo_mid(bottom_right);
105 let rect = kurbo::Rect::new(upper_left.x, upper_left.y, bottom_right.x, bottom_right.y);
106
107 self.render_ctx
108 .stroke(rect, &color, style.stroke_width() as f64);
109 }
110
111 Ok(())
112 }
113
114 fn draw_path<S: plotters_backend::BackendStyle, I: IntoIterator<Item = BackendCoord>>(
115 &mut self,
116 path: I,
117 style: &S,
118 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
119 if style.color().alpha == 0.0 {
120 return Ok(());
121 }
122
123 let path: Vec<kurbo::PathEl> = plotters_path_to_kurbo(path).collect();
124
125 self.render_ctx.stroke_styled(
126 &*path,
127 &plotters_color_to_piet(&style.color()),
128 style.stroke_width() as f64,
129 &STROKE_STYLE_SQUARE_CAP,
130 );
131 Ok(())
132 }
133
134 fn draw_circle<S: plotters_backend::BackendStyle>(
135 &mut self,
136 center: BackendCoord,
137 radius: u32,
138 style: &S,
139 fill: bool,
140 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
141 let center = plotters_point_to_kurbo_mid(center);
142 let color = plotters_color_to_piet(&style.color());
143 let circle = kurbo::Circle::new(center, radius as f64);
144
145 if fill {
146 self.render_ctx.fill(circle, &color);
147 } else {
148 self.render_ctx
149 .stroke(circle, &color, style.stroke_width() as f64);
150 }
151 Ok(())
152 }
153
154 fn fill_polygon<S: plotters_backend::BackendStyle, I: IntoIterator<Item = BackendCoord>>(
155 &mut self,
156 vert: I,
157 style: &S,
158 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
159 if style.color().alpha == 0.0 {
160 return Ok(());
161 }
162
163 let path: Vec<kurbo::PathEl> = plotters_path_to_kurbo(vert)
164 .chain(std::iter::once(kurbo::PathEl::ClosePath))
165 .collect();
166 self.render_ctx
167 .fill(&*path, &plotters_color_to_piet(&style.color()));
168 Ok(())
169 }
170
171 }
223
224fn plotters_color_to_piet(col: &BackendColor) -> piet_common::Color {
225 Color::rgba8(col.rgb.0, col.rgb.1, col.rgb.2, (col.alpha * 256.) as u8)
226}
227
228fn plotters_point_to_kurbo_mid((x, y): BackendCoord) -> kurbo::Point {
229 kurbo::Point {
230 x: x as f64 + 0.5,
231 y: y as f64 + 0.5,
232 }
233}
234
235fn plotters_point_to_kurbo_corner((x, y): BackendCoord) -> kurbo::Point {
236 kurbo::Point {
237 x: x as f64,
238 y: y as f64,
239 }
240}
241
242struct PlottersPathToKurbo<I> {
248 iter: I,
249 first: bool,
250}
251
252impl<I> PlottersPathToKurbo<I> {
253 fn new(path: I) -> PlottersPathToKurbo<I> {
254 PlottersPathToKurbo {
255 iter: path,
256 first: true,
257 }
258 }
259}
260
261impl<I> Iterator for PlottersPathToKurbo<I>
262where
263 I: Iterator<Item = BackendCoord>,
264{
265 type Item = kurbo::PathEl;
266
267 fn next(&mut self) -> Option<Self::Item> {
268 self.iter.next().map(|point| {
269 let point = plotters_point_to_kurbo_mid(point);
270
271 if self.first {
272 self.first = false;
273 kurbo::PathEl::MoveTo(point)
274 } else {
275 kurbo::PathEl::LineTo(point)
276 }
277 })
278 }
279}
280
281fn plotters_path_to_kurbo(
282 path: impl IntoIterator<Item = BackendCoord>,
283) -> impl Iterator<Item = kurbo::PathEl> {
284 PlottersPathToKurbo::new(path.into_iter())
285}
286
287const STROKE_STYLE_SQUARE_CAP: StrokeStyle = StrokeStyle::new().line_cap(LineCap::Square);
288
289#[cfg(test)]
290mod tests {
291 use super::*;
292 use piet_common::RenderContext;
293 use plotters::prelude::*;
294
295 #[test]
296 fn fill_root_white() {
297 let width = 3;
298 let height = 2;
299
300 let mut device = piet_common::Device::new().unwrap();
301 let mut bitmap = device.bitmap_target(width, height, 1.0).unwrap();
302
303 {
304 let mut render_ctx = bitmap.render_context();
305
306 let piet_backend = PietBackend {
307 size: (width as u32, height as u32),
308 render_ctx: &mut render_ctx,
309 };
310
311 let root = piet_backend.into_drawing_area();
312 root.fill(&WHITE).unwrap();
313
314 render_ctx.finish().unwrap();
315 }
316
317 let mut buf = [0; 6 * 4];
318 bitmap
319 .copy_raw_pixels(piet_common::ImageFormat::RgbaPremul, &mut buf)
320 .unwrap();
321
322 assert_eq!(buf, [255; 6 * 4]);
323 }
324
325 #[test]
326 fn test_plotters_path_to_kurbo() {
327 let path = vec![(1, 2), (3, 4), (5, 6)];
328
329 let kurbo_path: Vec<kurbo::PathEl> = plotters_path_to_kurbo(path).collect();
330
331 assert_eq!(
332 kurbo_path,
333 vec![
334 kurbo::PathEl::MoveTo(kurbo::Point { x: 1.5, y: 2.5 }),
335 kurbo::PathEl::LineTo(kurbo::Point { x: 3.5, y: 4.5 }),
336 kurbo::PathEl::LineTo(kurbo::Point { x: 5.5, y: 6.5 }),
337 ]
338 );
339 }
340}