1#![allow(clippy::many_single_char_names)]
2#![allow(unused_imports)]
3#![allow(unused_variables)]
4#![allow(dead_code)]
5#![cfg(not(target_arch = "wasm32"))]
6
7use crate::{
8 BaseLine, CanvasContext, Color, Direction, Gradient, GradientType, LineCap, LineJoin,
9 LinearGradient, PatternExtend, Point, RadialGradient, Rect, RgbaColor, Size, TextAlign,
10 TextMetrics, TextStyle, TextWeight,
11};
12use cairo::{self, FontFace, FontSlant, FontWeight, ImageSurface, Surface, SurfacePattern};
13use std::{any::Any, cell::RefCell};
14
15#[derive(Debug, Clone)]
16pub struct Pattern {
17 pub extend: PatternExtend,
18 pub inner: cairo::SurfacePattern,
19}
20
21impl Pattern {
22 pub fn new(extend: PatternExtend, surface: &cairo::Surface) -> Self {
24 Self {
25 extend,
26 inner: SurfacePattern::create(surface),
27 }
28 }
29}
30
31#[derive(Clone)]
32enum Paint {
33 None,
34 Solid(Color),
35 Gradient(Gradient),
36 Pattern(Pattern),
37}
38
39impl Default for Paint {
40 fn default() -> Self {
41 Paint::None
42 }
43}
44
45#[derive(Default, Clone)]
46struct CanvasState {
47 stroke: Paint,
48 fill: Paint,
49}
50
51pub struct Canvas<'a> {
52 ctx: &'a cairo::Context,
53 state: RefCell<CanvasState>,
54}
55
56impl<'a> Canvas<'a> {
57 #[allow(dead_code)]
58 pub fn new(ctx: &'a cairo::Context) -> Self {
59 Self {
60 ctx,
61 state: Default::default(),
62 }
63 }
64
65 fn handle_paint(&self, paint: &Paint) -> bool {
66 match paint {
67 Paint::Solid(value) => {
68 let value = *value;
69 let color: RgbaColor = value.into();
70 self.ctx.set_source_rgba(
71 color.red as f64 / 255.,
72 color.green as f64 / 255.,
73 color.blue as f64 / 255.,
74 color.alpha as f64 / 255.,
75 );
76
77 true
78 }
79 Paint::Gradient(value) => {
80 match value.kind {
81 GradientType::Linear(params) => {
82 let LinearGradient { x0, y0, x1, y1 } = params;
83 let gradient = cairo::LinearGradient::new(x0, y0, x1, y1);
84 let stops = value.stops.borrow();
85 for stop in stops.iter() {
86 let RgbaColor {
87 red,
88 green,
89 blue,
90 alpha,
91 } = stop.color.into();
92
93 gradient.add_color_stop_rgba(
94 stop.offset,
95 red as f64 / 255.,
96 green as f64 / 255.,
97 blue as f64 / 255.,
98 alpha as f64 / 255.,
99 );
100 }
101 self.ctx.set_source(&gradient);
102 }
103 GradientType::Radial(params) => {
104 let RadialGradient {
105 x0,
106 y0,
107 r0,
108 x1,
109 y1,
110 r1,
111 } = params;
112 let gradient = cairo::RadialGradient::new(x0, y0, r0, x1, y1, r1);
113 let stops = value.stops.borrow();
114 for stop in stops.iter() {
115 let RgbaColor {
116 red,
117 green,
118 blue,
119 alpha,
120 } = stop.color.into();
121
122 gradient.add_color_stop_rgba(
123 stop.offset,
124 red as f64 / 255.,
125 green as f64 / 255.,
126 blue as f64 / 255.,
127 alpha as f64 / 255.,
128 );
129 }
130 self.ctx.set_source(&gradient);
131 }
132 }
133
134 true
135 }
136 Paint::Pattern(value) => {
137 let extend = match value.extend {
138 PatternExtend::None => cairo::Extend::None,
139 PatternExtend::Repeat => cairo::Extend::Repeat,
140 PatternExtend::Reflect => cairo::Extend::Reflect,
141 PatternExtend::Pad => cairo::Extend::Pad,
142 };
143
144 value.inner.set_extend(extend);
145 self.ctx.set_source(&value.inner);
146
147 true
148 }
149 Paint::None => false,
150 }
151 }
152}
153
154impl<'a> CanvasContext for Canvas<'a> {
155 type Pattern = Pattern;
156 fn get_direction(&self) -> Direction {
164 unimplemented!()
165 }
166
167 fn set_direction(&self, value: Direction) -> String {
168 info!("NOT IMPLEMENTED");
169 unimplemented!()
170 }
171
172 fn set_fill_color(&self, value: Color) {
173 let mut state = self.state.borrow_mut();
174 state.fill = Paint::Solid(value);
175
176 let RgbaColor {
177 red,
178 green,
179 blue,
180 alpha,
181 } = value.into();
182 self.ctx.set_source_rgba(
183 red as f64 / 255.,
184 green as f64 / 255.,
185 blue as f64 / 255.,
186 alpha as f64 / 255.,
187 );
188 }
189
190 fn set_fill_gradient(&self, value: &Gradient) {
191 let mut state = self.state.borrow_mut();
192 state.fill = Paint::Gradient(value.clone());
193
194 match value.kind {
195 GradientType::Linear(params) => {
196 let LinearGradient { x0, y0, x1, y1 } = params;
197 let gradient = cairo::LinearGradient::new(x0, y0, x1, y1);
198 let stops = value.stops.borrow();
199 for stop in stops.iter() {
200 let RgbaColor {
201 red,
202 green,
203 blue,
204 alpha,
205 } = stop.color.into();
206
207 gradient.add_color_stop_rgba(
208 stop.offset,
209 red as f64 / 255.,
210 green as f64 / 255.,
211 blue as f64 / 255.,
212 alpha as f64 / 255.,
213 );
214 }
215 self.ctx.set_source(&gradient);
216 }
217 GradientType::Radial(params) => {
218 let RadialGradient {
219 x0,
220 y0,
221 r0,
222 x1,
223 y1,
224 r1,
225 } = params;
226 let gradient = cairo::RadialGradient::new(x0, y0, r0, x1, y1, r1);
227 let stops = value.stops.borrow();
228 for stop in stops.iter() {
229 let RgbaColor {
230 red,
231 green,
232 blue,
233 alpha,
234 } = stop.color.into();
235
236 gradient.add_color_stop_rgba(
237 stop.offset,
238 red as f64 / 255.,
239 green as f64 / 255.,
240 blue as f64 / 255.,
241 alpha as f64 / 255.,
242 );
243 }
244 self.ctx.set_source(&gradient);
245 }
246 }
247 }
248
249 fn set_fill_pattern(&self, value: &Self::Pattern) {
250 let mut state = self.state.borrow_mut();
251 state.fill = Paint::Pattern(value.clone());
252
253 let extend = match value.extend {
254 PatternExtend::None => cairo::Extend::None,
255 PatternExtend::Repeat => cairo::Extend::Repeat,
256 PatternExtend::Reflect => cairo::Extend::Reflect,
257 PatternExtend::Pad => cairo::Extend::Pad,
258 };
259
260 value.inner.set_extend(extend);
261 self.ctx.set_source(&value.inner);
262 }
263
264 fn get_filter(&self) -> String {
265 unimplemented!()
266 }
267
268 fn set_filter(&self, value: &str) {
269 unimplemented!()
270 }
271
272 fn get_font(&self) -> String {
273 unimplemented!()
275 }
276
277 fn set_font(&self, family: &str, style: TextStyle, weight: TextWeight, size: f64) {
278 let slant = match style {
279 TextStyle::Italic => FontSlant::Italic,
280 TextStyle::Normal => FontSlant::Normal,
281 TextStyle::Oblique => FontSlant::Oblique,
282 };
283
284 let weight = match weight {
285 TextWeight::Bold => FontWeight::Bold,
286 _ => FontWeight::Normal,
287 };
288
289 self.ctx.select_font_face(family, slant, weight);
290 self.ctx.set_font_size(size);
291 }
292
293 fn get_global_alpha(&self) -> f64 {
294 unimplemented!()
295 }
296
297 fn set_global_alpha(&self, value: f64) {
298 unimplemented!()
299 }
300
301 fn get_global_composite_operation(&self) -> String {
302 unimplemented!()
303 }
304
305 fn set_global_composite_operation(&self, value: &str) {
306 unimplemented!()
307 }
308
309 fn is_image_smoothing_enabled(&self) -> bool {
312 unimplemented!()
313 }
314
315 fn set_image_smoothing(&self, value: bool) {
316 unimplemented!()
317 }
318
319 fn get_line_cap(&self) -> LineCap {
328 match self.ctx.get_line_cap() {
329 cairo::LineCap::Round => LineCap::Round,
330 cairo::LineCap::Square => LineCap::Square,
331 _ => LineCap::Butt, }
333 }
334
335 fn set_line_cap(&self, value: LineCap) {
336 let value = match value {
337 LineCap::Round => cairo::LineCap::Round,
338 LineCap::Square => cairo::LineCap::Square,
339 LineCap::Butt => cairo::LineCap::Butt,
340 };
341 self.ctx.set_line_cap(value)
342 }
343
344 fn get_line_dash_offset(&self) -> f64 {
345 let (_, result) = self.ctx.get_dash();
346 result
347 }
348
349 fn set_line_dash_offset(&self, value: f64) {
350 let (dash, _) = self.ctx.get_dash();
351 self.ctx.set_dash(&dash, value);
352 }
353
354 fn get_line_join(&self) -> LineJoin {
355 match self.ctx.get_line_join() {
356 cairo::LineJoin::Bevel => LineJoin::Bevel,
357 cairo::LineJoin::Round => LineJoin::Round,
358 _ => LineJoin::Miter,
359 }
360 }
361
362 fn set_line_join(&self, value: LineJoin) {
363 let value = match value {
364 LineJoin::Bevel => cairo::LineJoin::Bevel,
365 LineJoin::Round => cairo::LineJoin::Round,
366 LineJoin::Miter => cairo::LineJoin::Miter,
367 };
368 self.ctx.set_line_join(value)
369 }
370
371 fn get_line_width(&self) -> f64 {
372 self.ctx.get_line_width()
373 }
374
375 fn set_line_width(&self, value: f64) {
376 self.ctx.set_line_width(value);
377 }
378
379 fn get_miter_limit(&self) -> f64 {
380 self.ctx.get_miter_limit()
381 }
382
383 fn set_miter_limit(&self, value: f64) {
384 self.ctx.set_miter_limit(value);
385 }
386
387 fn get_shadow_blur(&self) -> f64 {
388 unimplemented!()
389 }
390
391 fn set_shadow_blur(&self, value: f64) {
392 unimplemented!()
393 }
394
395 fn get_shadow_color(&self) -> Color {
396 unimplemented!()
397 }
398 fn set_shadow_color(&self, value: Color) {
399 unimplemented!()
400 }
401
402 fn get_shadow_offset_x(&self) -> f64 {
403 unimplemented!()
404 }
405 fn set_shadow_offset_x(&self, value: f64) {
406 unimplemented!()
407 }
408
409 fn get_shadow_offset_y(&self) -> f64 {
410 unimplemented!()
411 }
412
413 fn set_shadow_offset_y(&self, value: f64) {
414 unimplemented!()
415 }
416
417 fn set_stroke_color(&self, value: Color) {
418 let mut state = self.state.borrow_mut();
419 state.stroke = Paint::Solid(value);
420 }
421
422 fn set_stroke_gradient(&self, value: &Gradient) {
423 let mut state = self.state.borrow_mut();
424 state.stroke = Paint::Gradient(value.clone());
425 }
426
427 fn set_stroke_pattern(&self, value: &Self::Pattern) {
428 let mut state = self.state.borrow_mut();
429 state.stroke = Paint::Pattern(value.clone());
430 }
431
432 fn get_text_align(&self) -> TextAlign {
433 unimplemented!()
434 }
435
436 fn set_text_align(&self, value: TextAlign) {
437 }
439
440 fn get_text_baseline(&self) -> BaseLine {
441 unimplemented!()
442 }
443
444 fn set_text_baseline(&self, value: BaseLine) {
445 }
447
448 fn arc(
450 &self,
451 x: f64,
452 y: f64,
453 radius: f64,
454 start_angle: f64,
455 end_angle: f64,
456 anticlockwise: bool,
457 ) {
458 if anticlockwise {
459 self.ctx.arc_negative(x, y, radius, start_angle, end_angle);
460 } else {
461 self.ctx.arc(x, y, radius, start_angle, end_angle);
462 }
463 }
464
465 fn arc_to(&self, x1: f64, y1: f64, x2: f64, y2: f64, radius: f64) {
466 unimplemented!()
467 }
468
469 fn begin_path(&self) {
470 self.ctx.new_path();
471 }
472
473 fn bezier_curve_to(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
474 self.ctx.curve_to(cp1x, cp1y, cp2x, cp2y, x, y);
475 }
476
477 fn clear_rect(&self, x: f64, y: f64, width: f64, height: f64) {
479 self.ctx.new_path();
480 self.ctx.save();
481 self.ctx.set_source_rgba(0., 0., 0., 0.);
482 self.ctx.set_operator(cairo::Operator::Clear);
483 self.ctx.rectangle(x, y, width, height);
484 self.ctx.fill();
485 self.ctx.restore();
486 }
487
488 fn close_path(&self) {
491 self.ctx.close_path();
492 }
493
494 fn ellipse(
520 &self,
521 x: f64,
522 y: f64,
523 radius_x: f64,
524 radius_y: f64,
525 rotation: f64,
526 start_angle: f64,
527 end_angle: f64,
528 anticlockwise: bool,
529 ) {
530 self.ctx.save();
531
532 self.ctx.translate(x, y);
533 self.ctx.scale(1., radius_y / radius_x);
534 self.ctx.rotate(rotation);
535 self.ctx.translate(-x, -y);
536
537 self.ctx.arc(x, y, radius_x, start_angle, end_angle);
538
539 self.ctx.restore();
540 }
542
543 fn fill(&self) {
547 let state = self.state.borrow();
548 if self.handle_paint(&state.fill) {
549 self.ctx.fill_preserve();
550 }
551 }
552
553 fn fill_rect(&self, x: f64, y: f64, width: f64, height: f64) {
554 let state = self.state.borrow();
555 if self.handle_paint(&state.fill) {
556 self.ctx.new_path();
557 self.ctx.rectangle(x, y, width, height);
558 self.ctx.fill();
559 }
560 }
561
562 fn fill_text(&self, text: &str, x: f64, y: f64) {
564 let state = self.state.borrow();
565 if self.handle_paint(&state.fill) {
566 self.ctx.new_path();
567 self.ctx.save();
568 self.ctx.move_to(x, y);
569 self.ctx.text_path(text);
570 self.ctx.fill();
571 self.ctx.restore();
572 }
573 }
574
575 fn get_line_dash(&self) -> Vec<f64> {
581 let (result, _) = self.ctx.get_dash();
582 result
583 }
584
585 fn line_to(&self, x: f64, y: f64) {
591 self.ctx.line_to(x, y);
592 }
593
594 fn measure_text(&self, text: &str) -> TextMetrics {
595 let ext = self.ctx.text_extents(text);
596 TextMetrics {
597 width: ext.width,
598 height: ext.height,
599 }
600 }
601
602 fn move_to(&self, x: f64, y: f64) {
603 self.ctx.move_to(x, y);
604 }
605
606 fn quadratic_curve_to(&self, cpx: f64, cpy: f64, x: f64, y: f64) {
609 let (x0, y0) = self.ctx.get_current_point();
618 self.ctx.curve_to(
619 2.0 / 3.0 * cpx + 1.0 / 3.0 * x0,
620 2.0 / 3.0 * cpy + 1.0 / 3.0 * y0,
621 2.0 / 3.0 * cpx + 1.0 / 3.0 * x,
622 2.0 / 3.0 * cpy + 1.0 / 3.0 * y,
623 x,
624 y,
625 );
626 }
627
628 fn rect(&self, x: f64, y: f64, width: f64, height: f64) {
629 self.ctx.rectangle(x, y, width, height);
630 }
631
632 fn reset_transform(&self) {
633 unimplemented!()
634 }
635
636 fn restore(&self) {
637 self.ctx.restore();
638 }
639
640 fn rotate(&self, angle: f64) {
641 self.ctx.rotate(angle);
642 }
643
644 fn save(&self) {
645 self.ctx.save();
646 }
647
648 fn scale(&self, x: f64, y: f64) {
649 self.ctx.scale(x, y);
650 }
651
652 fn set_line_dash(&self, dash: &Vec<f64>) {
656 let (_, offset) = self.ctx.get_dash();
657 self.ctx.set_dash(dash, offset);
658 }
659
660 fn set_transform(&self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) {
661 let m = cairo::Matrix::new(a, b, c, d, e, f);
662 self.ctx.transform(m);
663 }
664
665 fn stroke(&self) {
666 let state = self.state.borrow();
667 if self.handle_paint(&state.stroke) {
668 self.ctx.stroke_preserve();
669 }
670 }
671
672 fn stroke_rect(&self, x: f64, y: f64, width: f64, height: f64) {
673 let state = self.state.borrow();
674 if self.handle_paint(&state.stroke) {
675 self.ctx.new_path();
676 self.ctx.rectangle(x, y, width, height);
677 self.ctx.stroke();
678 }
679 }
680
681 fn stroke_text(&self, text: &str, x: f64, y: f64) {
682 let state = self.state.borrow();
683 if self.handle_paint(&state.stroke) {
684 self.ctx.new_path();
685 self.ctx.save();
686 self.ctx.move_to(x, y);
687 self.ctx.text_path(text);
688 self.ctx.stroke();
689 self.ctx.restore();
690 }
691 }
692
693 fn transform(&self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) {
694 let m = cairo::Matrix::new(a, b, c, d, e, f);
695 self.ctx.transform(m);
696 }
697
698 fn translate(&self, x: f64, y: f64) {
699 self.ctx.translate(x, y);
700 }
701}