Skip to main content

winio/ui/
canvas.rs

1use image::DynamicImage;
2use winio_primitive::{DrawingFont, Point, Rect, Size, Transform};
3
4use crate::{sys, sys::Result};
5
6/// Drawing brush.
7pub trait Brush: sys::Brush {}
8
9impl<B: sys::Brush> Brush for B {}
10
11/// Drawing Pen.
12pub trait Pen: sys::Pen {}
13
14impl<P: sys::Pen> Pen for P {}
15
16/// Canvas compatible drawing image.
17pub struct DrawingImage(sys::DrawingImage);
18
19impl DrawingImage {
20    /// Size of the image.
21    pub fn size(&self) -> Result<Size> {
22        self.0.size()
23    }
24}
25
26/// Canvas drawing context.
27pub struct DrawingContext<'a>(sys::DrawingContext<'a>);
28
29#[inline]
30fn fix_rect(mut rect: Rect) -> Rect {
31    rect.size = fix_size(rect.size);
32    rect
33}
34
35#[inline]
36fn fix_size(mut size: Size) -> Size {
37    size.width = size.width.max(0.1);
38    size.height = size.height.max(0.1);
39    size
40}
41
42#[inline]
43fn fix_font(mut font: DrawingFont) -> DrawingFont {
44    font.size = font.size.max(0.1);
45    if font.family.is_empty() {
46        font.family = "Arial".to_string();
47    }
48    font
49}
50
51impl<'a> DrawingContext<'a> {
52    pub(crate) fn new(ctx: sys::DrawingContext<'a>) -> Self {
53        Self(ctx)
54    }
55
56    /// Close the context manually.
57    pub fn close(self) -> Result<()> {
58        self.0.close()
59    }
60
61    /// Set the transform matrix.
62    pub fn set_transform(&mut self, transform: Transform) -> Result<()> {
63        self.0.set_transform(transform)
64    }
65
66    /// Get the transform matrix.
67    pub fn transform(&self) -> Result<Transform> {
68        self.0.transform()
69    }
70
71    /// Draw a path.
72    pub fn draw_path(&mut self, pen: impl Pen, path: &DrawingPath) -> Result<()> {
73        self.0.draw_path(pen, &path.0)
74    }
75
76    /// Fill a path.
77    pub fn fill_path(&mut self, brush: impl Brush, path: &DrawingPath) -> Result<()> {
78        self.0.fill_path(brush, &path.0)
79    }
80
81    /// Draw an arc.
82    pub fn draw_arc(&mut self, pen: impl Pen, rect: Rect, start: f64, end: f64) -> Result<()> {
83        self.0.draw_arc(pen, fix_rect(rect), start, end)
84    }
85
86    /// Draw an arc.
87    pub fn draw_pie(&mut self, pen: impl Pen, rect: Rect, start: f64, end: f64) -> Result<()> {
88        self.0.draw_pie(pen, fix_rect(rect), start, end)
89    }
90
91    /// Fill a pie.
92    pub fn fill_pie(&mut self, brush: impl Brush, rect: Rect, start: f64, end: f64) -> Result<()> {
93        self.0.fill_pie(brush, fix_rect(rect), start, end)
94    }
95
96    /// Draw an ellipse.
97    pub fn draw_ellipse(&mut self, pen: impl Pen, rect: Rect) -> Result<()> {
98        self.0.draw_ellipse(pen, fix_rect(rect))
99    }
100
101    /// Fill an ellipse.
102    pub fn fill_ellipse(&mut self, brush: impl Brush, rect: Rect) -> Result<()> {
103        self.0.fill_ellipse(brush, fix_rect(rect))
104    }
105
106    /// Draw a line.
107    pub fn draw_line(&mut self, pen: impl Pen, start: Point, end: Point) -> Result<()> {
108        self.0.draw_line(pen, start, end)
109    }
110
111    /// Draw a rectangle.
112    pub fn draw_rect(&mut self, pen: impl Pen, rect: Rect) -> Result<()> {
113        self.0.draw_rect(pen, fix_rect(rect))
114    }
115
116    /// Fill a rectangle.
117    pub fn fill_rect(&mut self, brush: impl Brush, rect: Rect) -> Result<()> {
118        self.0.fill_rect(brush, fix_rect(rect))
119    }
120
121    /// Draw a rounded rectangle.
122    pub fn draw_round_rect(&mut self, pen: impl Pen, rect: Rect, round: Size) -> Result<()> {
123        self.0.draw_round_rect(pen, fix_rect(rect), round)
124    }
125
126    /// Fill a rounded rectangle.
127    pub fn fill_round_rect(&mut self, brush: impl Brush, rect: Rect, round: Size) -> Result<()> {
128        self.0.fill_round_rect(brush, fix_rect(rect), round)
129    }
130
131    /// Draw a string.
132    pub fn draw_str(
133        &mut self,
134        brush: impl Brush,
135        font: DrawingFont,
136        pos: Point,
137        text: impl AsRef<str>,
138    ) -> Result<()> {
139        self.0.draw_str(brush, fix_font(font), pos, text.as_ref())
140    }
141
142    /// Measure string size.
143    pub fn measure_str(&self, font: DrawingFont, text: &str) -> Result<Size> {
144        self.0.measure_str(fix_font(font), text)
145    }
146
147    /// Create a [`DrawingContext`]-compatible image from [`DynamicImage`].
148    pub fn create_image(&self, image: DynamicImage) -> Result<DrawingImage> {
149        Ok(DrawingImage(self.0.create_image(image)?))
150    }
151
152    /// Draw a image with RGBA format.
153    pub fn draw_image(
154        &mut self,
155        image: &DrawingImage,
156        rect: Rect,
157        clip: Option<Rect>,
158    ) -> Result<()> {
159        self.0.draw_image(&image.0, fix_rect(rect), clip)
160    }
161
162    /// Create [`DrawingPathBuilder`].
163    pub fn create_path_builder(&self, start: Point) -> Result<DrawingPathBuilder> {
164        Ok(DrawingPathBuilder(self.0.create_path_builder(start)?))
165    }
166}
167
168/// A drawing path.
169pub struct DrawingPath(sys::DrawingPath);
170
171/// Builder for [`DrawingPath`].
172pub struct DrawingPathBuilder(sys::DrawingPathBuilder);
173
174impl DrawingPathBuilder {
175    /// Line from current point to the target point.
176    pub fn add_line(&mut self, p: Point) -> Result<()> {
177        self.0.add_line(p)?;
178        Ok(())
179    }
180
181    /// Add arc. A line will be created implicitly if the start point is not the
182    /// current point.
183    pub fn add_arc(
184        &mut self,
185        center: Point,
186        radius: Size,
187        start: f64,
188        end: f64,
189        clockwise: bool,
190    ) -> Result<()> {
191        self.0
192            .add_arc(center, fix_size(radius), start, end, clockwise)?;
193        Ok(())
194    }
195
196    /// Add a cubic Bezier curve.
197    pub fn add_bezier(&mut self, p1: Point, p2: Point, p3: Point) -> Result<()> {
198        self.0.add_bezier(p1, p2, p3)?;
199        Ok(())
200    }
201
202    /// Build [`DrawingPath`].
203    pub fn build(self, close: bool) -> Result<DrawingPath> {
204        Ok(DrawingPath(self.0.build(close)?))
205    }
206}