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}