freya_plotters_backend/
lib.rs

1use std::error::Error;
2
3use freya_engine::prelude::*;
4use plotters_backend::{
5    BackendCoord,
6    BackendStyle,
7    BackendTextStyle,
8    DrawingBackend,
9    DrawingErrorKind,
10    rasterizer,
11    text_anchor::{
12        HPos,
13        VPos,
14    },
15};
16
17#[derive(Debug)]
18pub struct PlotSkiaBackendError;
19
20impl std::fmt::Display for PlotSkiaBackendError {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        f.write_str("Skia backend error.")
23    }
24}
25
26impl Error for PlotSkiaBackendError {}
27
28pub struct PlotSkiaBackend<'a> {
29    size: (i32, i32),
30    canvas: &'a Canvas,
31    font_collection: &'a mut FontCollection,
32}
33
34impl<'a> PlotSkiaBackend<'a> {
35    pub fn new(
36        canvas: &'a Canvas,
37        font_collection: &'a mut FontCollection,
38        size: (i32, i32),
39    ) -> Self {
40        Self {
41            canvas,
42            font_collection,
43            size,
44        }
45    }
46}
47
48impl DrawingBackend for PlotSkiaBackend<'_> {
49    type ErrorType = PlotSkiaBackendError;
50
51    fn draw_line<S: BackendStyle>(
52        &mut self,
53        from: BackendCoord,
54        to: BackendCoord,
55        style: &S,
56    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
57        let mut paint = Paint::default();
58        let color = style.color();
59        paint.set_color(Color::from_argb(
60            (255. * color.alpha) as u8,
61            color.rgb.0,
62            color.rgb.1,
63            color.rgb.2,
64        ));
65        paint.set_stroke_width(style.stroke_width() as f32);
66        self.canvas.draw_line(from, to, &paint);
67        Ok(())
68    }
69
70    fn draw_rect<S: BackendStyle>(
71        &mut self,
72        upper_left: BackendCoord,
73        bottom_right: BackendCoord,
74        style: &S,
75        fill: bool,
76    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
77        let mut paint = Paint::default();
78        let color = style.color();
79        paint.set_color(Color::from_argb(
80            (255. * color.alpha) as u8,
81            color.rgb.0,
82            color.rgb.1,
83            color.rgb.2,
84        ));
85        paint.set_style(if fill {
86            PaintStyle::Fill
87        } else {
88            PaintStyle::Stroke
89        });
90        paint.set_stroke_width(style.stroke_width() as f32);
91        let rect = Rect::new(
92            upper_left.0 as f32,
93            upper_left.1 as f32,
94            bottom_right.0 as f32,
95            bottom_right.1 as f32,
96        );
97        self.canvas.draw_rect(rect, &paint);
98        Ok(())
99    }
100
101    fn draw_path<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(
102        &mut self,
103        path: I,
104        style: &S,
105    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
106        if style.color().alpha == 0.0 {
107            return Ok(());
108        }
109
110        // Code based on the SVG backend implementation
111        if style.stroke_width() == 1 {
112            let mut begin: Option<BackendCoord> = None;
113            for end in path.into_iter() {
114                if let Some(begin) = begin {
115                    let result = self.draw_line(begin, end, style);
116                    #[allow(clippy::question_mark)]
117                    if result.is_err() {
118                        return result;
119                    }
120                }
121                begin = Some(end);
122            }
123        } else {
124            let p: Vec<_> = path.into_iter().collect();
125            let v = rasterizer::polygonize(&p[..], style.stroke_width());
126            return self.fill_polygon(v, &style.color());
127        }
128        Ok(())
129    }
130
131    fn draw_circle<S: BackendStyle>(
132        &mut self,
133        center: BackendCoord,
134        radius: u32,
135        style: &S,
136        fill: bool,
137    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
138        let radius = radius as f32;
139
140        let mut paint = Paint::default();
141        let color = style.color();
142        paint.set_anti_alias(true);
143        paint.set_style(if fill {
144            PaintStyle::Fill
145        } else {
146            PaintStyle::Stroke
147        });
148        paint.set_color(Color::from_argb(
149            (255.0 * color.alpha) as u8,
150            color.rgb.0,
151            color.rgb.1,
152            color.rgb.2,
153        ));
154
155        if !fill {
156            paint.set_stroke_width(1.0);
157        }
158
159        self.canvas.draw_circle(center, radius, &paint);
160
161        Ok(())
162    }
163
164    fn fill_polygon<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(
165        &mut self,
166        vert: I,
167        style: &S,
168    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
169        let vert_buf: Vec<_> = vert.into_iter().collect();
170
171        let mut paint = Paint::default();
172        let color = style.color();
173        paint.set_color(Color::from_argb(
174            (255. * color.alpha) as u8,
175            color.rgb.0,
176            color.rgb.1,
177            color.rgb.2,
178        ));
179        let mut path = PathBuilder::new();
180        let first = vert_buf[0];
181        path.move_to(first);
182
183        for pos in &vert_buf[1..] {
184            path.line_to(*pos);
185        }
186        let path = path.detach();
187        self.canvas.draw_path(&path, &paint);
188
189        Ok(())
190    }
191
192    fn draw_text<TStyle: BackendTextStyle>(
193        &mut self,
194        text: &str,
195        style: &TStyle,
196        pos: BackendCoord,
197    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
198        let mut builder =
199            ParagraphBuilder::new(&ParagraphStyle::default(), self.font_collection.clone());
200        let mut text_style = TextStyle::new();
201        let color = style.color();
202        text_style.set_color(Color::from_argb(
203            (255. * color.alpha) as u8,
204            color.rgb.0,
205            color.rgb.1,
206            color.rgb.2,
207        ));
208        text_style.set_font_families(&[style.family().as_str()]);
209        text_style.set_font_size(style.size() as f32);
210        builder.push_style(&text_style);
211        builder.add_text(text);
212        let mut paragraph = builder.build();
213        paragraph.layout(f32::MAX);
214
215        let mut pos = (pos.0 as f32, pos.1 as f32);
216        match style.anchor().h_pos {
217            HPos::Left => {}
218            HPos::Center => {
219                pos.0 -= paragraph.max_intrinsic_width() / 2.0;
220            }
221            HPos::Right => {
222                pos.0 -= paragraph.max_intrinsic_width();
223            }
224        }
225        match style.anchor().v_pos {
226            VPos::Top => {}
227            VPos::Center => {
228                pos.1 -= paragraph.height() / 2.0;
229            }
230            VPos::Bottom => {
231                pos.1 -= paragraph.height();
232            }
233        }
234
235        paragraph.paint(self.canvas, pos);
236        Ok(())
237    }
238
239    fn estimate_text_size<TStyle: BackendTextStyle>(
240        &self,
241        text: &str,
242        style: &TStyle,
243    ) -> Result<(u32, u32), DrawingErrorKind<Self::ErrorType>> {
244        let mut builder =
245            ParagraphBuilder::new(&ParagraphStyle::default(), self.font_collection.clone());
246        let mut text_style = TextStyle::new();
247        let color = style.color();
248        text_style.set_color(Color::from_argb(
249            (255. * color.alpha) as u8,
250            color.rgb.0,
251            color.rgb.1,
252            color.rgb.2,
253        ));
254        text_style.set_font_families(&[style.family().as_str()]);
255        text_style.set_font_size(style.size() as f32);
256        builder.push_style(&text_style);
257        builder.add_text(text);
258        let mut paragraph = builder.build();
259        paragraph.layout(f32::MAX);
260        Ok((
261            paragraph.max_intrinsic_width() as u32,
262            paragraph.height() as u32,
263        ))
264    }
265
266    fn draw_pixel(
267        &mut self,
268        _point: plotters_backend::BackendCoord,
269        _color: plotters_backend::BackendColor,
270    ) -> Result<(), plotters_backend::DrawingErrorKind<Self::ErrorType>> {
271        todo!()
272    }
273
274    fn get_size(&self) -> (u32, u32) {
275        (self.size.0 as u32, self.size.1 as u32)
276    }
277
278    fn ensure_prepared(
279        &mut self,
280    ) -> Result<(), plotters_backend::DrawingErrorKind<Self::ErrorType>> {
281        Ok(())
282    }
283
284    fn present(&mut self) -> Result<(), plotters_backend::DrawingErrorKind<Self::ErrorType>> {
285        Ok(())
286    }
287}