1use crate::texture::Texture;
2use crate::{Arc, Ellipse, ImageTexture, Pixels};
3use euclid::{Length, Point2D, Rect, Size2D};
4use qwac_sys::graphics;
5pub use qwac_sys::graphics::CompositeOperation;
6
7pub trait Canvas {
10    fn id(&self) -> i32;
11
12    fn draw_texture<T: Texture>(
13        &mut self,
14        texture: &T,
15        source: Rect<f32, Pixels>,
16        destination: Rect<f32, Pixels>,
17    ) {
18        unsafe {
19            graphics::canvas_draw_texture(
20                self.id(),
21                texture.id(),
22                source.origin.x,
23                source.origin.y,
24                source.size.width,
25                source.size.height,
26                destination.origin.x,
27                destination.origin.y,
28                destination.size.width,
29                destination.size.height,
30            );
31        }
32    }
33
34    fn draw_canvas<C: Canvas>(
35        &mut self,
36        canvas: &C,
37        source: Rect<f32, Pixels>,
38        destination: Rect<f32, Pixels>,
39    ) {
40        unsafe {
41            graphics::canvas_draw_canvas(
42                self.id(),
43                canvas.id(),
44                source.origin.x,
45                source.origin.y,
46                source.size.width,
47                source.size.height,
48                destination.origin.x,
49                destination.origin.y,
50                destination.size.width,
51                destination.size.height,
52            );
53        }
54    }
55
56    fn resize(&mut self, size: Size2D<u16, Pixels>) {
57        unsafe {
58            graphics::canvas_resize(self.id(), size.width.into(), size.height.into());
59        }
60    }
61
62    fn clear(&mut self) {
63        unsafe {
64            graphics::canvas_clear(self.id());
65        }
66    }
67
68    fn set_global_alpha(&mut self, alpha: f32) {
69        unsafe {
70            graphics::canvas_set_global_alpha(self.id(), alpha);
71        }
72    }
73
74    fn set_global_composite_operation(&mut self, operation: CompositeOperation) {
75        unsafe {
76            graphics::canvas_set_global_composite_operation(self.id(), operation);
77        }
78    }
79
80    fn size(&self) -> Size2D<u16, Pixels> {
81        let width = unsafe { graphics::canvas_width(self.id()) }
82            .try_into()
83            .expect("out of range");
84        let height = unsafe { graphics::canvas_height(self.id()) }
85            .try_into()
86            .expect("out of range");
87        Size2D::new(width, height)
88    }
89
90    fn set_fill_style<S>(&mut self, style: S)
91    where
92        S: AsRef<str>,
93    {
94        let style = style.as_ref();
95        let style_ptr = style.as_ptr();
96        let style_len = style.len().try_into().expect("out of range");
97        unsafe {
98            graphics::canvas_set_fill_style(self.id(), style_ptr, style_len);
99        }
100    }
101
102    fn set_stroke_style<S>(&mut self, style: S)
103    where
104        S: AsRef<str>,
105    {
106        let style = style.as_ref();
107        let style_ptr = style.as_ptr();
108        let style_len = style.len().try_into().expect("out of range");
109        unsafe {
110            graphics::canvas_set_stroke_style(self.id(), style_ptr, style_len);
111        }
112    }
113    fn set_font<S>(&mut self, font: S)
114    where
115        S: AsRef<str>,
116    {
117        let font = font.as_ref();
118        let font_ptr = font.as_ptr();
119        let font_len = font.len().try_into().expect("out of range");
120        unsafe {
121            graphics::canvas_set_font(self.id(), font_ptr, font_len);
122        }
123    }
124    fn set_line_width(&mut self, line_width: Length<f32, Pixels>) {
125        unsafe {
126            graphics::canvas_set_line_width(self.id(), line_width.get());
127        }
128    }
129
130    fn fill_text<S>(
131        &mut self,
132        text: S,
133        position: Point2D<f32, Pixels>,
134        max_width: Option<Length<f32, Pixels>>,
135    ) where
136        S: AsRef<str>,
137    {
138        let text = text.as_ref();
139        let text_ptr = text.as_ptr();
140        let text_len = text.len().try_into().expect("out of range");
141        unsafe {
142            graphics::canvas_fill_text(
143                self.id(),
144                text_ptr,
145                text_len,
146                position.x,
147                position.y,
148                max_width.unwrap_or_default().get(),
149            );
150        }
151    }
152    fn stroke_text<S>(
153        &mut self,
154        text: S,
155        position: Point2D<f32, Pixels>,
156        max_width: Option<Length<f32, Pixels>>,
157    ) where
158        S: AsRef<str>,
159    {
160        let text = text.as_ref();
161        let text_ptr = text.as_ptr();
162        let text_len = text.len().try_into().expect("out of range");
163        unsafe {
164            graphics::canvas_stroke_text(
165                self.id(),
166                text_ptr,
167                text_len,
168                position.x,
169                position.y,
170                max_width.unwrap_or_default().get(),
171            );
172        }
173    }
174    fn fill_rectangle(&mut self, rectangle: Rect<f32, Pixels>) {
175        unsafe {
176            graphics::canvas_fill_rectangle(
177                self.id(),
178                rectangle.origin.x,
179                rectangle.origin.y,
180                rectangle.size.width,
181                rectangle.size.height,
182            );
183        }
184    }
185    fn stroke_rectangle(&mut self, rectangle: Rect<f32, Pixels>) {
186        unsafe {
187            graphics::canvas_stroke_rectangle(
188                self.id(),
189                rectangle.origin.x,
190                rectangle.origin.y,
191                rectangle.size.width,
192                rectangle.size.height,
193            );
194        }
195    }
196    fn clear_rectangle(&mut self, rectangle: Rect<f32, Pixels>) {
197        unsafe {
198            graphics::canvas_clear_rectangle(
199                self.id(),
200                rectangle.origin.x,
201                rectangle.origin.y,
202                rectangle.size.width,
203                rectangle.size.height,
204            );
205        }
206    }
207}
208
209impl<D: Canvas> Canvas for &mut D {
210    fn id(&self) -> i32 {
211        (**self).id()
212    }
213}
214
215impl<D: Canvas> Canvas for &D {
216    fn id(&self) -> i32 {
217        (**self).id()
218    }
219}
220
221#[derive(Debug)]
222pub struct OffscreenCanvas {
223    pub(crate) id: i32,
224}
225
226impl OffscreenCanvas {
227    pub fn new() -> Self {
228        unsafe {
229            Self {
230                id: graphics::canvas_create(),
231            }
232        }
233    }
234
235    pub fn from_canvas<C>(canvas: &C) -> Self
236    where
237        C: Canvas,
238    {
239        let mut new = OffscreenCanvas::new();
240        let size = canvas.size();
241        let rect = Rect::new(
242            Point2D::zero(),
243            Size2D::new(
244                size.width.try_into().expect("out of range"),
245                size.height.try_into().expect("out of range"),
246            ),
247        );
248        new.resize(size);
249        new.draw_canvas(canvas, rect, rect);
250        new
251    }
252    pub fn from_texture<T>(texture: &T) -> Self
253    where
254        T: Texture,
255    {
256        let mut new = OffscreenCanvas::new();
257        let size = texture.size();
258        let rect = Rect::new(
259            Point2D::zero(),
260            Size2D::new(
261                size.width.try_into().expect("out of range"),
262                size.height.try_into().expect("out of range"),
263            ),
264        );
265        new.resize(size);
266        new.draw_texture(texture, rect, rect);
267        new
268    }
269}
270
271impl Canvas for OffscreenCanvas {
272    fn id(&self) -> i32 {
273        self.id
274    }
275}
276
277impl Drop for OffscreenCanvas {
278    fn drop(&mut self) {
279        unsafe {
280            graphics::canvas_drop(self.id);
281        }
282    }
283}
284
285impl Clone for OffscreenCanvas {
286    fn clone(&self) -> Self {
287        Self::from_canvas(&self)
288    }
289}
290
291#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
293pub struct Screen;
294
295impl Canvas for Screen {
296    fn id(&self) -> i32 {
297        0
298    }
299}
300
301impl From<&Screen> for OffscreenCanvas {
302    fn from(value: &Screen) -> Self {
303        Self::from_canvas(value)
304    }
305}
306
307impl From<Screen> for OffscreenCanvas {
308    fn from(value: Screen) -> Self {
309        Self::from_canvas(&value)
310    }
311}
312
313impl From<&ImageTexture> for OffscreenCanvas {
314    fn from(value: &ImageTexture) -> Self {
315        Self::from_texture(value)
316    }
317}
318
319impl From<ImageTexture> for OffscreenCanvas {
320    fn from(value: ImageTexture) -> Self {
321        Self::from_texture(&value)
322    }
323}
324
325#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
326enum PathType {
327    Fill,
328    Stroke,
329}
330
331#[derive(Debug)]
332pub struct Path<'a, C>
333where
334    C: Canvas + std::fmt::Debug,
335{
336    canvas: &'a mut C,
337    path_type: PathType,
338}
339
340impl<'a, C> Path<'a, C>
341where
342    C: Canvas + std::fmt::Debug,
343{
344    fn new(canvas: &'a mut C, path_type: PathType) -> Self {
345        unsafe {
346            graphics::canvas_path_begin(canvas.id());
347        }
348        Self { canvas, path_type }
349    }
350    pub fn fill(canvas: &'a mut C) -> Self {
351        Self::new(canvas, PathType::Fill)
352    }
353    pub fn stroke(canvas: &'a mut C) -> Self {
354        Self::new(canvas, PathType::Stroke)
355    }
356
357    pub fn close(&mut self) {
358        unsafe {
359            graphics::canvas_path_close(self.canvas.id());
360        }
361    }
362    pub fn move_to(&mut self, point: Point2D<f32, Pixels>) {
363        unsafe {
364            graphics::canvas_path_move_to(self.canvas.id(), point.x, point.y);
365        }
366    }
367
368    pub fn line_to(&mut self, point: Point2D<f32, Pixels>) {
369        unsafe {
370            graphics::canvas_path_line_to(self.canvas.id(), point.x, point.y);
371        }
372    }
373
374    pub fn rectangle(&mut self, rectangle: Rect<f32, Pixels>) {
375        unsafe {
376            graphics::canvas_path_rectangle(
377                self.canvas.id(),
378                rectangle.origin.x,
379                rectangle.origin.y,
380                rectangle.size.width,
381                rectangle.size.height,
382            );
383        }
384    }
385    pub fn ellipse(&mut self, ellipse: Ellipse) {
386        unsafe {
387            graphics::canvas_path_ellipse(
388                self.canvas.id(),
389                ellipse.center.x,
390                ellipse.center.y,
391                ellipse.radii.0.get(),
392                ellipse.radii.1.get(),
393                ellipse.rotation.radians,
394                ellipse.range.start().radians,
395                ellipse.range.end().radians,
396                ellipse.counter_clockwise.into(),
397            );
398        }
399    }
400    pub fn arc(&mut self, arc: Arc) {
401        unsafe {
402            graphics::canvas_path_arc(
403                self.canvas.id(),
404                arc.center.x,
405                arc.center.y,
406                arc.radius.get(),
407                arc.range.start().radians,
408                arc.range.end().radians,
409                arc.counter_clockwise.into(),
410            );
411        }
412    }
413    pub fn arc_to(
414        &mut self,
415        from: Point2D<f32, Pixels>,
416        to: Point2D<f32, Pixels>,
417        radius: Length<f32, Pixels>,
418    ) {
419        unsafe {
420            graphics::canvas_path_arc_to(
421                self.canvas.id(),
422                from.x,
423                from.y,
424                to.x,
425                to.y,
426                radius.get(),
427            );
428        }
429    }
430}
431
432impl<'a, C> Canvas for Path<'a, C>
433where
434    C: Canvas + std::fmt::Debug,
435{
436    fn id(&self) -> i32 {
437        self.canvas.id()
438    }
439}
440
441impl<'a, C> Drop for Path<'a, C>
442where
443    C: Canvas + std::fmt::Debug,
444{
445    fn drop(&mut self) {
446        unsafe {
447            match self.path_type {
448                PathType::Fill => graphics::canvas_path_fill(self.canvas.id()),
449                PathType::Stroke => graphics::canvas_path_stroke(self.canvas.id()),
450            }
451        }
452    }
453}
454
455#[derive(Debug)]
458pub struct CanvasState<'a, C>
459where
460    C: Canvas + std::fmt::Debug,
461{
462    canvas: &'a mut C,
463}
464
465impl<'a, C> CanvasState<'a, C>
466where
467    C: Canvas + std::fmt::Debug,
468{
469    pub fn new(canvas: &'a mut C) -> Self {
470        unsafe {
471            graphics::canvas_save(canvas.id());
472        }
473        Self { canvas }
474    }
475}
476
477impl<'a, C> Canvas for CanvasState<'a, C>
478where
479    C: Canvas + std::fmt::Debug,
480{
481    fn id(&self) -> i32 {
482        self.canvas.id()
483    }
484}
485
486impl<'a, C> Drop for CanvasState<'a, C>
487where
488    C: Canvas + std::fmt::Debug,
489{
490    fn drop(&mut self) {
491        unsafe {
492            graphics::canvas_restore(self.canvas.id());
493        }
494    }
495}