1use crate::clip::{ClipOp, ClipStack};
6use crate::color::Color;
7use crate::error::{DrawError, Result};
8use crate::font::TextStyle;
9use crate::matrix::{CanvasState, Matrix};
10use crate::paint::{Paint, PaintStyle};
11use crate::path::Path;
12use crate::point::Point;
13use crate::rect::Rect;
14use crate::surface::Surface;
15use image::Rgba;
16use imageproc::drawing::{
17 draw_filled_rect_mut, draw_hollow_rect_mut, draw_line_segment_mut, draw_text_mut,
18};
19use imageproc::rect::Rect as ImageprocRect;
20
21pub struct Canvas {
25 surface: Surface,
27 state_stack: Vec<CanvasState>,
29 current_matrix: Matrix,
31 clip_stack: ClipStack,
33}
34
35impl Canvas {
36 pub fn new(width: u32, height: u32) -> Result<Self> {
38 let surface = Surface::new(width, height)?;
39 Ok(Self {
40 surface,
41 state_stack: Vec::new(),
42 current_matrix: Matrix::identity(),
43 clip_stack: ClipStack::new(),
44 })
45 }
46
47 pub fn new_with_color(width: u32, height: u32, color: Color) -> Result<Self> {
49 let surface = Surface::new_with_color(width, height, color.to_rgba())?;
50 Ok(Self {
51 surface,
52 state_stack: Vec::new(),
53 current_matrix: Matrix::identity(),
54 clip_stack: ClipStack::new(),
55 })
56 }
57
58 pub fn from_surface(surface: Surface) -> Self {
60 Self {
61 surface,
62 state_stack: Vec::new(),
63 current_matrix: Matrix::identity(),
64 clip_stack: ClipStack::new(),
65 }
66 }
67
68 pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
70 let surface = Surface::from_file(path)?;
71 Ok(Self {
72 surface,
73 state_stack: Vec::new(),
74 current_matrix: Matrix::identity(),
75 clip_stack: ClipStack::new(),
76 })
77 }
78
79 pub fn width(&self) -> u32 {
81 self.surface.width()
82 }
83
84 pub fn height(&self) -> u32 {
86 self.surface.height()
87 }
88
89 pub fn dimensions(&self) -> (u32, u32) {
91 self.surface.dimensions()
92 }
93
94 pub fn clear(&mut self, color: Color) {
96 self.surface.clear(color.to_rgba());
97 }
98
99 pub fn save_to_file<P: AsRef<std::path::Path>>(&self, path: P) -> Result<()> {
101 self.surface.save(path)?;
102 Ok(())
103 }
104
105 pub fn to_png_bytes(&self) -> Result<Vec<u8>> {
107 Ok(self.surface.to_png_bytes()?)
108 }
109
110 pub fn to_jpeg_bytes(&self, quality: u8) -> Result<Vec<u8>> {
112 Ok(self.surface.to_jpeg_bytes(quality)?)
113 }
114
115 pub fn surface(&self) -> &Surface {
117 &self.surface
118 }
119
120 pub fn surface_mut(&mut self) -> &mut Surface {
122 &mut self.surface
123 }
124
125 pub fn draw_point(&mut self, point: Point, paint: &Paint) {
127 let x = point.x;
128 let y = point.y;
129
130 if x >= 0 && y >= 0 {
131 let x = x as u32;
132 let y = y as u32;
133 if x < self.width() && y < self.height() {
134 self.surface.put_pixel(x, y, paint.color().to_rgba());
135 }
136 }
137 }
138
139 pub fn draw_line(&mut self, start: Point, end: Point, paint: &Paint) {
141 let start_f = (start.x as f32, start.y as f32);
142 let end_f = (end.x as f32, end.y as f32);
143
144 draw_line_segment_mut(
145 self.surface.image_mut(),
146 start_f,
147 end_f,
148 paint.color().to_rgba(),
149 );
150 }
151
152 pub fn draw_rect(&mut self, rect: Rect, paint: &Paint) {
154 let imageproc_rect = ImageprocRect::at(rect.x, rect.y).of_size(rect.width, rect.height);
155
156 match paint.style() {
157 PaintStyle::Fill => {
158 draw_filled_rect_mut(
159 self.surface.image_mut(),
160 imageproc_rect,
161 paint.color().to_rgba(),
162 );
163 }
164 PaintStyle::Stroke => {
165 draw_hollow_rect_mut(
167 self.surface.image_mut(),
168 imageproc_rect,
169 paint.color().to_rgba(),
170 );
171
172 let stroke_width = paint.stroke_width() as i32;
174 for i in 1..stroke_width {
175 let inner_rect = ImageprocRect::at(rect.x + i, rect.y + i).of_size(
176 rect.width.saturating_sub((i * 2) as u32),
177 rect.height.saturating_sub((i * 2) as u32),
178 );
179 if inner_rect.width() > 0 && inner_rect.height() > 0 {
180 draw_hollow_rect_mut(
181 self.surface.image_mut(),
182 inner_rect,
183 paint.color().to_rgba(),
184 );
185 }
186 }
187 }
188 PaintStyle::FillAndStroke => {
189 draw_filled_rect_mut(
191 self.surface.image_mut(),
192 imageproc_rect,
193 paint.color().to_rgba(),
194 );
195 draw_hollow_rect_mut(
197 self.surface.image_mut(),
198 imageproc_rect,
199 paint.color().to_rgba(),
200 );
201 }
202 }
203 }
204
205 pub fn draw_rounded_rect(&mut self, rect: Rect, radius: f32, paint: &Paint) {
207 let r = radius.min((rect.width as f32 / 2.0).min(rect.height as f32 / 2.0));
209
210 if r <= 0.0 {
211 self.draw_rect(rect, paint);
212 return;
213 }
214
215 let color = paint.color().to_rgba();
216
217 match paint.style() {
219 PaintStyle::Fill | PaintStyle::FillAndStroke => {
220 let center_rect = ImageprocRect::at(rect.x, rect.y + r as i32)
222 .of_size(rect.width, rect.height - (r * 2.0) as u32);
223 draw_filled_rect_mut(self.surface.image_mut(), center_rect, color);
224
225 let top_rect = ImageprocRect::at(rect.x + r as i32, rect.y)
227 .of_size(rect.width - (r * 2.0) as u32, r as u32);
228 draw_filled_rect_mut(self.surface.image_mut(), top_rect, color);
229
230 let bottom_rect =
231 ImageprocRect::at(rect.x + r as i32, rect.y + rect.height as i32 - r as i32)
232 .of_size(rect.width - (r * 2.0) as u32, r as u32);
233 draw_filled_rect_mut(self.surface.image_mut(), bottom_rect, color);
234
235 self.draw_circle_corner(rect.x + r as i32, rect.y + r as i32, r, color, true);
237 self.draw_circle_corner(
238 rect.x + rect.width as i32 - r as i32,
239 rect.y + r as i32,
240 r,
241 color,
242 true,
243 );
244 self.draw_circle_corner(
245 rect.x + r as i32,
246 rect.y + rect.height as i32 - r as i32,
247 r,
248 color,
249 true,
250 );
251 self.draw_circle_corner(
252 rect.x + rect.width as i32 - r as i32,
253 rect.y + rect.height as i32 - r as i32,
254 r,
255 color,
256 true,
257 );
258 }
259 PaintStyle::Stroke => {
260 self.draw_rect(rect, paint);
262 }
263 }
264 }
265
266 fn draw_circle_corner(&mut self, cx: i32, cy: i32, radius: f32, color: Rgba<u8>, fill: bool) {
268 let r = radius as i32;
269 for dy in -r..=r {
270 for dx in -r..=r {
271 let dist_sq = dx * dx + dy * dy;
272 let r_sq = (radius * radius) as i32;
273
274 if fill && dist_sq <= r_sq {
275 let x = cx + dx;
276 let y = cy + dy;
277 if x >= 0 && y >= 0 {
278 self.surface.put_pixel(x as u32, y as u32, color);
279 }
280 }
281 }
282 }
283 }
284
285 pub fn draw_circle(&mut self, center: Point, radius: f32, paint: &Paint) {
287 let color = paint.color().to_rgba();
288 let r = radius as i32;
289
290 match paint.style() {
291 PaintStyle::Fill | PaintStyle::FillAndStroke => {
292 for dy in -r..=r {
294 for dx in -r..=r {
295 let dist_sq = dx * dx + dy * dy;
296 let r_sq = (radius * radius) as i32;
297
298 if dist_sq <= r_sq {
299 let x = center.x + dx;
300 let y = center.y + dy;
301 if x >= 0 && y >= 0 {
302 self.surface.put_pixel(x as u32, y as u32, color);
303 }
304 }
305 }
306 }
307 }
308 PaintStyle::Stroke => {
309 let stroke_width = paint.stroke_width();
311 let inner_r = (radius - stroke_width).max(0.0);
312 let inner_r_sq = (inner_r * inner_r) as i32;
313 let outer_r_sq = (radius * radius) as i32;
314
315 for dy in -r..=r {
316 for dx in -r..=r {
317 let dist_sq = dx * dx + dy * dy;
318
319 if dist_sq <= outer_r_sq && dist_sq >= inner_r_sq {
320 let x = center.x + dx;
321 let y = center.y + dy;
322 if x >= 0 && y >= 0 {
323 self.surface.put_pixel(x as u32, y as u32, color);
324 }
325 }
326 }
327 }
328 }
329 }
330 }
331
332 pub fn draw_oval(&mut self, rect: Rect, paint: &Paint) {
334 let cx = rect.x + rect.width as i32 / 2;
335 let cy = rect.y + rect.height as i32 / 2;
336 let rx = rect.width as f32 / 2.0;
337 let ry = rect.height as f32 / 2.0;
338
339 let color = paint.color().to_rgba();
340
341 match paint.style() {
342 PaintStyle::Fill | PaintStyle::FillAndStroke => {
343 for y in rect.y..(rect.y + rect.height as i32) {
345 for x in rect.x..(rect.x + rect.width as i32) {
346 let dx = (x - cx) as f32;
347 let dy = (y - cy) as f32;
348
349 if (dx * dx) / (rx * rx) + (dy * dy) / (ry * ry) <= 1.0 {
350 if x >= 0 && y >= 0 {
351 self.surface.put_pixel(x as u32, y as u32, color);
352 }
353 }
354 }
355 }
356 }
357 PaintStyle::Stroke => {
358 let stroke_width = paint.stroke_width();
360 let inner_rx = (rx - stroke_width).max(0.0);
361 let inner_ry = (ry - stroke_width).max(0.0);
362
363 for y in rect.y..(rect.y + rect.height as i32) {
364 for x in rect.x..(rect.x + rect.width as i32) {
365 let dx = (x - cx) as f32;
366 let dy = (y - cy) as f32;
367
368 let outer = (dx * dx) / (rx * rx) + (dy * dy) / (ry * ry);
369 let inner =
370 (dx * dx) / (inner_rx * inner_rx) + (dy * dy) / (inner_ry * inner_ry);
371
372 if outer <= 1.0 && inner >= 1.0 {
373 if x >= 0 && y >= 0 {
374 self.surface.put_pixel(x as u32, y as u32, color);
375 }
376 }
377 }
378 }
379 }
380 }
381 }
382
383 pub fn draw_text(
385 &mut self,
386 text: &str,
387 position: Point,
388 text_style: &TextStyle,
389 paint: &Paint,
390 ) {
391 let scale = text_style.scale();
392 let font = text_style.font.inner();
393 let color = paint.color().to_rgba();
394
395 draw_text_mut(
396 self.surface.image_mut(),
397 color,
398 position.x,
399 position.y,
400 scale,
401 font,
402 text,
403 );
404 }
405
406 pub fn draw_image(&mut self, image: &Surface, position: Point) -> Result<()> {
408 self.draw_image_rect(
409 image,
410 None,
411 Rect::new(position.x, position.y, image.width(), image.height()),
412 )
413 }
414
415 pub fn draw_image_rect(
417 &mut self,
418 image: &Surface,
419 src_rect: Option<Rect>,
420 dst_rect: Rect,
421 ) -> Result<()> {
422 let src = src_rect.unwrap_or_else(|| Rect::new(0, 0, image.width(), image.height()));
424
425 if src.x < 0
427 || src.y < 0
428 || src.x as u32 + src.width > image.width()
429 || src.y as u32 + src.height > image.height()
430 {
431 return Err(DrawError::InvalidCoordinates { x: src.x, y: src.y });
432 }
433
434 let src_image = if src.width != dst_rect.width || src.height != dst_rect.height {
436 let cropped = image.crop(src.x as u32, src.y as u32, src.width, src.height)?;
438 cropped.resize(dst_rect.width, dst_rect.height)?
440 } else {
441 image.crop(src.x as u32, src.y as u32, src.width, src.height)?
443 };
444
445 for y in 0..dst_rect.height {
447 for x in 0..dst_rect.width {
448 let src_pixel = src_image.get_pixel(x, y).unwrap();
449 let dst_x = dst_rect.x + x as i32;
450 let dst_y = dst_rect.y + y as i32;
451
452 if dst_x >= 0
453 && dst_y >= 0
454 && (dst_x as u32) < self.width()
455 && (dst_y as u32) < self.height()
456 {
457 self.blend_pixel(dst_x as u32, dst_y as u32, src_pixel);
459 }
460 }
461 }
462
463 Ok(())
464 }
465
466 fn blend_pixel(&mut self, x: u32, y: u32, src: Rgba<u8>) {
468 if let Some(dst) = self.surface.get_pixel(x, y) {
469 let src_alpha = src[3] as f32 / 255.0;
470 let dst_alpha = dst[3] as f32 / 255.0;
471
472 if src_alpha == 0.0 {
473 return;
474 }
475
476 if src_alpha == 1.0 {
477 self.surface.put_pixel(x, y, src);
478 return;
479 }
480
481 let out_alpha = src_alpha + dst_alpha * (1.0 - src_alpha);
483
484 if out_alpha == 0.0 {
485 return;
486 }
487
488 let r = ((src[0] as f32 * src_alpha + dst[0] as f32 * dst_alpha * (1.0 - src_alpha))
489 / out_alpha) as u8;
490 let g = ((src[1] as f32 * src_alpha + dst[1] as f32 * dst_alpha * (1.0 - src_alpha))
491 / out_alpha) as u8;
492 let b = ((src[2] as f32 * src_alpha + dst[2] as f32 * dst_alpha * (1.0 - src_alpha))
493 / out_alpha) as u8;
494 let a = (out_alpha * 255.0) as u8;
495
496 self.surface.put_pixel(x, y, Rgba([r, g, b, a]));
497 }
498 }
499
500 pub fn draw_image_nine(&mut self, image: &Surface, center: Rect, dst: Rect) -> Result<()> {
526 if center.x < 0
528 || center.y < 0
529 || center.x as u32 >= image.width()
530 || center.y as u32 >= image.height()
531 || center.x as u32 + center.width > image.width()
532 || center.y as u32 + center.height > image.height()
533 {
534 return Err(DrawError::InvalidCoordinates {
535 x: center.x,
536 y: center.y,
537 });
538 }
539
540 let src_left = center.x as u32;
542 let src_top = center.y as u32;
543 let src_right = image.width() - (center.x as u32 + center.width);
544 let src_bottom = image.height() - (center.y as u32 + center.height);
545 let src_center_width = center.width;
546 let src_center_height = center.height;
547
548 let dst_left = src_left.min(dst.width / 3);
551 let dst_top = src_top.min(dst.height / 3);
552 let dst_right = src_right.min(dst.width / 3);
553 let dst_bottom = src_bottom.min(dst.height / 3);
554
555 let dst_center_width = dst.width.saturating_sub(dst_left + dst_right);
556 let dst_center_height = dst.height.saturating_sub(dst_top + dst_bottom);
557
558 if dst_left > 0 && dst_top > 0 {
562 let src_rect = Rect::new(0, 0, src_left, src_top);
563 let dst_rect = Rect::new(dst.x, dst.y, dst_left, dst_top);
564 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
565 }
566
567 if dst_center_width > 0 && dst_top > 0 {
569 let src_rect = Rect::new(src_left as i32, 0, src_center_width, src_top);
570 let dst_rect = Rect::new(dst.x + dst_left as i32, dst.y, dst_center_width, dst_top);
571 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
572 }
573
574 if dst_right > 0 && dst_top > 0 {
576 let src_rect = Rect::new((image.width() - src_right) as i32, 0, src_right, src_top);
577 let dst_rect = Rect::new(
578 dst.x + (dst.width - dst_right) as i32,
579 dst.y,
580 dst_right,
581 dst_top,
582 );
583 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
584 }
585
586 if dst_left > 0 && dst_center_height > 0 {
588 let src_rect = Rect::new(0, src_top as i32, src_left, src_center_height);
589 let dst_rect = Rect::new(dst.x, dst.y + dst_top as i32, dst_left, dst_center_height);
590 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
591 }
592
593 if dst_center_width > 0 && dst_center_height > 0 {
595 let src_rect = Rect::new(
596 src_left as i32,
597 src_top as i32,
598 src_center_width,
599 src_center_height,
600 );
601 let dst_rect = Rect::new(
602 dst.x + dst_left as i32,
603 dst.y + dst_top as i32,
604 dst_center_width,
605 dst_center_height,
606 );
607 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
608 }
609
610 if dst_right > 0 && dst_center_height > 0 {
612 let src_rect = Rect::new(
613 (image.width() - src_right) as i32,
614 src_top as i32,
615 src_right,
616 src_center_height,
617 );
618 let dst_rect = Rect::new(
619 dst.x + (dst.width - dst_right) as i32,
620 dst.y + dst_top as i32,
621 dst_right,
622 dst_center_height,
623 );
624 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
625 }
626
627 if dst_left > 0 && dst_bottom > 0 {
629 let src_rect = Rect::new(
630 0,
631 (image.height() - src_bottom) as i32,
632 src_left,
633 src_bottom,
634 );
635 let dst_rect = Rect::new(
636 dst.x,
637 dst.y + (dst.height - dst_bottom) as i32,
638 dst_left,
639 dst_bottom,
640 );
641 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
642 }
643
644 if dst_center_width > 0 && dst_bottom > 0 {
646 let src_rect = Rect::new(
647 src_left as i32,
648 (image.height() - src_bottom) as i32,
649 src_center_width,
650 src_bottom,
651 );
652 let dst_rect = Rect::new(
653 dst.x + dst_left as i32,
654 dst.y + (dst.height - dst_bottom) as i32,
655 dst_center_width,
656 dst_bottom,
657 );
658 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
659 }
660
661 if dst_right > 0 && dst_bottom > 0 {
663 let src_rect = Rect::new(
664 (image.width() - src_right) as i32,
665 (image.height() - src_bottom) as i32,
666 src_right,
667 src_bottom,
668 );
669 let dst_rect = Rect::new(
670 dst.x + (dst.width - dst_right) as i32,
671 dst.y + (dst.height - dst_bottom) as i32,
672 dst_right,
673 dst_bottom,
674 );
675 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
676 }
677
678 Ok(())
679 }
680
681 pub fn measure_text(&self, text: &str, text_style: &TextStyle) -> (u32, u32) {
683 let scale = text_style.scale();
684 let font = text_style.font.inner();
685
686 let v_metrics = font.v_metrics(scale);
687 let height = (v_metrics.ascent - v_metrics.descent).ceil() as u32;
688
689 let width = font
690 .layout(text, scale, rusttype::point(0.0, 0.0))
691 .map(|g| g.position().x + g.unpositioned().h_metrics().advance_width)
692 .last()
693 .unwrap_or(0.0)
694 .ceil() as u32;
695
696 (width, height)
697 }
698
699 pub fn draw_dashed_line(&mut self, start: Point, end: Point, paint: &Paint) {
725 if paint.dash_style().is_solid() {
727 self.draw_line(start, end, paint);
728 return;
729 }
730
731 let pattern = match paint.dash_pattern() {
733 Some(p) => p,
734 None => {
735 self.draw_line(start, end, paint);
736 return;
737 }
738 };
739
740 if pattern.is_empty() {
741 self.draw_line(start, end, paint);
742 return;
743 }
744
745 let dx = end.x - start.x;
747 let dy = end.y - start.y;
748 let length = ((dx * dx + dy * dy) as f32).sqrt();
749
750 if length == 0.0 {
751 return;
752 }
753
754 let unit_dx = dx as f32 / length;
756 let unit_dy = dy as f32 / length;
757
758 let mut current_pos = 0.0;
760 let mut pattern_index = 0;
761 let mut is_dash = true; while current_pos < length {
764 let segment_length = pattern[pattern_index % pattern.len()];
765 let next_pos = (current_pos + segment_length).min(length);
766
767 if is_dash {
768 let seg_start = Point::new(
770 start.x + (unit_dx * current_pos) as i32,
771 start.y + (unit_dy * current_pos) as i32,
772 );
773 let seg_end = Point::new(
774 start.x + (unit_dx * next_pos) as i32,
775 start.y + (unit_dy * next_pos) as i32,
776 );
777 self.draw_line(seg_start, seg_end, paint);
778 }
779
780 current_pos = next_pos;
781 pattern_index += 1;
782 is_dash = !is_dash; }
784 }
785
786 pub fn draw_dashed_rect(&mut self, rect: Rect, paint: &Paint) {
810 if paint.dash_style().is_solid() {
812 self.draw_rect(rect, paint);
813 return;
814 }
815
816 self.draw_dashed_line(
819 Point::new(rect.x, rect.y),
820 Point::new(rect.x + rect.width as i32, rect.y),
821 paint,
822 );
823
824 self.draw_dashed_line(
826 Point::new(rect.x + rect.width as i32, rect.y),
827 Point::new(rect.x + rect.width as i32, rect.y + rect.height as i32),
828 paint,
829 );
830
831 self.draw_dashed_line(
833 Point::new(rect.x + rect.width as i32, rect.y + rect.height as i32),
834 Point::new(rect.x, rect.y + rect.height as i32),
835 paint,
836 );
837
838 self.draw_dashed_line(
840 Point::new(rect.x, rect.y + rect.height as i32),
841 Point::new(rect.x, rect.y),
842 paint,
843 );
844 }
845
846 pub fn draw_dashed_rounded_rect(&mut self, rect: Rect, radius: f32, paint: &Paint) {
872 let r = radius.min((rect.width as f32 / 2.0).min(rect.height as f32 / 2.0));
874
875 if r <= 0.0 {
876 self.draw_dashed_rect(rect, paint);
877 return;
878 }
879
880 if paint.dash_style().is_solid() {
882 self.draw_rounded_rect(rect, r, paint);
883 return;
884 }
885
886 self.draw_dashed_line(
889 Point::new(rect.x + r as i32, rect.y),
890 Point::new(rect.x + rect.width as i32 - r as i32, rect.y),
891 paint,
892 );
893
894 self.draw_dashed_line(
896 Point::new(rect.x + rect.width as i32, rect.y + r as i32),
897 Point::new(
898 rect.x + rect.width as i32,
899 rect.y + rect.height as i32 - r as i32,
900 ),
901 paint,
902 );
903
904 self.draw_dashed_line(
906 Point::new(
907 rect.x + rect.width as i32 - r as i32,
908 rect.y + rect.height as i32,
909 ),
910 Point::new(rect.x + r as i32, rect.y + rect.height as i32),
911 paint,
912 );
913
914 self.draw_dashed_line(
916 Point::new(rect.x, rect.y + rect.height as i32 - r as i32),
917 Point::new(rect.x, rect.y + r as i32),
918 paint,
919 );
920
921 self.draw_dashed_arc(
924 Point::new(rect.x + r as i32, rect.y + r as i32),
925 r,
926 180.0,
927 270.0,
928 paint,
929 );
930
931 self.draw_dashed_arc(
933 Point::new(rect.x + rect.width as i32 - r as i32, rect.y + r as i32),
934 r,
935 270.0,
936 360.0,
937 paint,
938 );
939
940 self.draw_dashed_arc(
942 Point::new(
943 rect.x + rect.width as i32 - r as i32,
944 rect.y + rect.height as i32 - r as i32,
945 ),
946 r,
947 0.0,
948 90.0,
949 paint,
950 );
951
952 self.draw_dashed_arc(
954 Point::new(rect.x + r as i32, rect.y + rect.height as i32 - r as i32),
955 r,
956 90.0,
957 180.0,
958 paint,
959 );
960 }
961
962 fn draw_dashed_arc(
974 &mut self,
975 center: Point,
976 radius: f32,
977 start_angle: f32,
978 end_angle: f32,
979 paint: &Paint,
980 ) {
981 let pattern = match paint.dash_pattern() {
983 Some(p) if !p.is_empty() => p,
984 _ => {
985 self.draw_arc_solid(center, radius, start_angle, end_angle, paint);
987 return;
988 }
989 };
990
991 let angle_range = (end_angle - start_angle).abs();
993 let arc_length = (angle_range.to_radians() * radius) as f32;
994
995 if arc_length <= 0.0 {
996 return;
997 }
998
999 let mut current_length = 0.0;
1001 let mut pattern_index = 0;
1002 let mut is_dash = true;
1003
1004 while current_length < arc_length {
1005 let segment_length = pattern[pattern_index % pattern.len()];
1006 let next_length = (current_length + segment_length).min(arc_length);
1007
1008 if is_dash {
1009 let seg_start_angle = start_angle + (current_length / arc_length) * angle_range;
1011 let seg_end_angle = start_angle + (next_length / arc_length) * angle_range;
1012
1013 self.draw_arc_segment(center, radius, seg_start_angle, seg_end_angle, paint);
1015 }
1016
1017 current_length = next_length;
1018 pattern_index += 1;
1019 is_dash = !is_dash;
1020 }
1021 }
1022
1023 fn draw_arc_solid(
1025 &mut self,
1026 center: Point,
1027 radius: f32,
1028 start_angle: f32,
1029 end_angle: f32,
1030 paint: &Paint,
1031 ) {
1032 self.draw_arc_segment(center, radius, start_angle, end_angle, paint);
1033 }
1034
1035 fn draw_arc_segment(
1037 &mut self,
1038 center: Point,
1039 radius: f32,
1040 start_angle: f32,
1041 end_angle: f32,
1042 paint: &Paint,
1043 ) {
1044 let angle_range = (end_angle - start_angle).abs();
1047 let arc_length = angle_range.to_radians() * radius;
1048
1049 let points_per_pixel = 4.0; let num_points = (arc_length * points_per_pixel)
1053 .ceil()
1054 .max(angle_range * 2.0)
1055 .max(10.0) as i32;
1056
1057 let mut points = Vec::new();
1059 for i in 0..=num_points {
1060 let t = i as f32 / num_points as f32;
1061 let angle = start_angle + t * (end_angle - start_angle);
1062 let rad = angle.to_radians();
1063
1064 let x = center.x + (radius * rad.cos()) as i32;
1065 let y = center.y + (radius * rad.sin()) as i32;
1066 points.push(Point::new(x, y));
1067 }
1068
1069 for i in 0..points.len() - 1 {
1071 self.draw_line(points[i], points[i + 1], paint);
1072 }
1073 }
1074
1075 pub fn draw_custom_border_rect(
1105 &mut self,
1106 rect: Rect,
1107 border: &crate::border::Border,
1108 radius: &crate::border::BorderRadius,
1109 ) {
1110 let radius = radius.clamp(rect.width as f32, rect.height as f32);
1112
1113 if radius.is_zero() {
1115 self.draw_custom_border_rect_no_radius(rect, border);
1116 return;
1117 }
1118
1119 if border.top.width > 0.0 {
1122 let mut paint = crate::Paint::stroke(border.top.color, border.top.width);
1123 paint.set_dash_style(border.top.dash_style.clone());
1124
1125 let start = Point::new(rect.x + radius.top_left as i32, rect.y);
1126 let end = Point::new(rect.x + rect.width as i32 - radius.top_right as i32, rect.y);
1127 self.draw_dashed_line(start, end, &paint);
1128 }
1129
1130 if border.right.width > 0.0 {
1132 let mut paint = crate::Paint::stroke(border.right.color, border.right.width);
1133 paint.set_dash_style(border.right.dash_style.clone());
1134
1135 let start = Point::new(rect.x + rect.width as i32, rect.y + radius.top_right as i32);
1136 let end = Point::new(
1137 rect.x + rect.width as i32,
1138 rect.y + rect.height as i32 - radius.bottom_right as i32,
1139 );
1140 self.draw_dashed_line(start, end, &paint);
1141 }
1142
1143 if border.bottom.width > 0.0 {
1145 let mut paint = crate::Paint::stroke(border.bottom.color, border.bottom.width);
1146 paint.set_dash_style(border.bottom.dash_style.clone());
1147
1148 let start = Point::new(
1149 rect.x + rect.width as i32 - radius.bottom_right as i32,
1150 rect.y + rect.height as i32,
1151 );
1152 let end = Point::new(
1153 rect.x + radius.bottom_left as i32,
1154 rect.y + rect.height as i32,
1155 );
1156 self.draw_dashed_line(start, end, &paint);
1157 }
1158
1159 if border.left.width > 0.0 {
1161 let mut paint = crate::Paint::stroke(border.left.color, border.left.width);
1162 paint.set_dash_style(border.left.dash_style.clone());
1163
1164 let start = Point::new(
1165 rect.x,
1166 rect.y + rect.height as i32 - radius.bottom_left as i32,
1167 );
1168 let end = Point::new(rect.x, rect.y + radius.top_left as i32);
1169 self.draw_dashed_line(start, end, &paint);
1170 }
1171
1172 if radius.top_left > 0.0 {
1175 let corner_border = self.blend_corner_style(&border.top, &border.left);
1177 let mut paint = crate::Paint::stroke(corner_border.color, corner_border.width);
1178 paint.set_dash_style(corner_border.dash_style);
1179
1180 self.draw_dashed_arc(
1181 Point::new(
1182 rect.x + radius.top_left as i32,
1183 rect.y + radius.top_left as i32,
1184 ),
1185 radius.top_left,
1186 180.0,
1187 270.0,
1188 &paint,
1189 );
1190 }
1191
1192 if radius.top_right > 0.0 {
1194 let corner_border = self.blend_corner_style(&border.top, &border.right);
1195 let mut paint = crate::Paint::stroke(corner_border.color, corner_border.width);
1196 paint.set_dash_style(corner_border.dash_style);
1197
1198 self.draw_dashed_arc(
1199 Point::new(
1200 rect.x + rect.width as i32 - radius.top_right as i32,
1201 rect.y + radius.top_right as i32,
1202 ),
1203 radius.top_right,
1204 270.0,
1205 360.0,
1206 &paint,
1207 );
1208 }
1209
1210 if radius.bottom_right > 0.0 {
1212 let corner_border = self.blend_corner_style(&border.right, &border.bottom);
1213 let mut paint = crate::Paint::stroke(corner_border.color, corner_border.width);
1214 paint.set_dash_style(corner_border.dash_style);
1215
1216 self.draw_dashed_arc(
1217 Point::new(
1218 rect.x + rect.width as i32 - radius.bottom_right as i32,
1219 rect.y + rect.height as i32 - radius.bottom_right as i32,
1220 ),
1221 radius.bottom_right,
1222 0.0,
1223 90.0,
1224 &paint,
1225 );
1226 }
1227
1228 if radius.bottom_left > 0.0 {
1230 let corner_border = self.blend_corner_style(&border.bottom, &border.left);
1231 let mut paint = crate::Paint::stroke(corner_border.color, corner_border.width);
1232 paint.set_dash_style(corner_border.dash_style);
1233
1234 self.draw_dashed_arc(
1235 Point::new(
1236 rect.x + radius.bottom_left as i32,
1237 rect.y + rect.height as i32 - radius.bottom_left as i32,
1238 ),
1239 radius.bottom_left,
1240 90.0,
1241 180.0,
1242 &paint,
1243 );
1244 }
1245 }
1246
1247 fn draw_custom_border_rect_no_radius(&mut self, rect: Rect, border: &crate::border::Border) {
1249 if border.top.width > 0.0 {
1251 let mut paint = crate::Paint::stroke(border.top.color, border.top.width);
1252 paint.set_dash_style(border.top.dash_style.clone());
1253 self.draw_dashed_line(
1254 Point::new(rect.x, rect.y),
1255 Point::new(rect.x + rect.width as i32, rect.y),
1256 &paint,
1257 );
1258 }
1259
1260 if border.right.width > 0.0 {
1262 let mut paint = crate::Paint::stroke(border.right.color, border.right.width);
1263 paint.set_dash_style(border.right.dash_style.clone());
1264 self.draw_dashed_line(
1265 Point::new(rect.x + rect.width as i32, rect.y),
1266 Point::new(rect.x + rect.width as i32, rect.y + rect.height as i32),
1267 &paint,
1268 );
1269 }
1270
1271 if border.bottom.width > 0.0 {
1273 let mut paint = crate::Paint::stroke(border.bottom.color, border.bottom.width);
1274 paint.set_dash_style(border.bottom.dash_style.clone());
1275 self.draw_dashed_line(
1276 Point::new(rect.x + rect.width as i32, rect.y + rect.height as i32),
1277 Point::new(rect.x, rect.y + rect.height as i32),
1278 &paint,
1279 );
1280 }
1281
1282 if border.left.width > 0.0 {
1284 let mut paint = crate::Paint::stroke(border.left.color, border.left.width);
1285 paint.set_dash_style(border.left.dash_style.clone());
1286 self.draw_dashed_line(
1287 Point::new(rect.x, rect.y + rect.height as i32),
1288 Point::new(rect.x, rect.y),
1289 &paint,
1290 );
1291 }
1292 }
1293
1294 fn blend_corner_style(
1296 &self,
1297 side1: &crate::border::BorderSide,
1298 side2: &crate::border::BorderSide,
1299 ) -> crate::border::BorderSide {
1300 if side1.width == 0.0 {
1302 return side2.clone();
1303 }
1304 if side2.width == 0.0 {
1305 return side1.clone();
1306 }
1307
1308 let color = crate::Color::rgba(
1310 ((side1.color.r as u16 + side2.color.r as u16) / 2) as u8,
1311 ((side1.color.g as u16 + side2.color.g as u16) / 2) as u8,
1312 ((side1.color.b as u16 + side2.color.b as u16) / 2) as u8,
1313 ((side1.color.a as u16 + side2.color.a as u16) / 2) as u8,
1314 );
1315
1316 let width = (side1.width + side2.width) / 2.0;
1318
1319 let dash_style = side1.dash_style.clone();
1321
1322 crate::border::BorderSide::new(color, width, dash_style)
1323 }
1324
1325 pub fn draw_path(&mut self, path: &Path, paint: &Paint) {
1336 let transformed_path = path.transform(&self.current_matrix);
1338
1339 match paint.style() {
1340 PaintStyle::Fill | PaintStyle::FillAndStroke => {
1341 self.fill_path(&transformed_path, paint);
1342 }
1343 PaintStyle::Stroke => {
1344 self.stroke_path(&transformed_path, paint);
1345 }
1346 }
1347 }
1348
1349 fn fill_path(&mut self, path: &Path, paint: &Paint) {
1351 let color = paint.color().to_rgba();
1352
1353 if let Some(bounds) = path.bounds() {
1355 for y in bounds.y..(bounds.y + bounds.height as i32) {
1357 for x in bounds.x..(bounds.x + bounds.width as i32) {
1358 if path.contains(x as f32, y as f32) {
1359 if x >= 0 && y >= 0 && x < self.width() as i32 && y < self.height() as i32 {
1360 if self.clip_stack.contains_point(x, y) {
1362 self.surface.put_pixel(x as u32, y as u32, color);
1363 }
1364 }
1365 }
1366 }
1367 }
1368 }
1369 }
1370
1371 fn stroke_path(&mut self, path: &Path, paint: &Paint) {
1373 let commands = path.commands();
1375 let mut current_point = None;
1376
1377 for cmd in commands {
1378 match cmd {
1379 crate::path::PathCommand::MoveTo(p) => {
1380 current_point = Some(*p);
1381 }
1382 crate::path::PathCommand::LineTo(p) => {
1383 if let Some(start) = current_point {
1384 self.draw_line(
1385 Point::new(start.x as i32, start.y as i32),
1386 Point::new(p.x as i32, p.y as i32),
1387 paint,
1388 );
1389 }
1390 current_point = Some(*p);
1391 }
1392 crate::path::PathCommand::QuadTo { control, end } => {
1393 if let Some(start) = current_point {
1394 self.draw_quad_bezier(start, *control, *end, paint);
1396 }
1397 current_point = Some(*end);
1398 }
1399 crate::path::PathCommand::CubicTo {
1400 control1,
1401 control2,
1402 end,
1403 } => {
1404 if let Some(start) = current_point {
1405 self.draw_cubic_bezier(start, *control1, *control2, *end, paint);
1407 }
1408 current_point = Some(*end);
1409 }
1410 crate::path::PathCommand::ArcTo { .. } => {
1411 }
1414 crate::path::PathCommand::Close => {
1415 }
1417 }
1418 }
1419 }
1420
1421 fn draw_quad_bezier(
1423 &mut self,
1424 start: crate::point::PointF,
1425 control: crate::point::PointF,
1426 end: crate::point::PointF,
1427 paint: &Paint,
1428 ) {
1429 let steps = 20;
1430 let mut prev = start;
1431
1432 for i in 1..=steps {
1433 let t = i as f32 / steps as f32;
1434 let t2 = t * t;
1435 let mt = 1.0 - t;
1436 let mt2 = mt * mt;
1437
1438 let x = mt2 * start.x + 2.0 * mt * t * control.x + t2 * end.x;
1439 let y = mt2 * start.y + 2.0 * mt * t * control.y + t2 * end.y;
1440
1441 let curr = crate::point::PointF { x, y };
1442 self.draw_line(
1443 Point::new(prev.x as i32, prev.y as i32),
1444 Point::new(curr.x as i32, curr.y as i32),
1445 paint,
1446 );
1447 prev = curr;
1448 }
1449 }
1450
1451 fn draw_cubic_bezier(
1453 &mut self,
1454 start: crate::point::PointF,
1455 control1: crate::point::PointF,
1456 control2: crate::point::PointF,
1457 end: crate::point::PointF,
1458 paint: &Paint,
1459 ) {
1460 let steps = 30;
1461 let mut prev = start;
1462
1463 for i in 1..=steps {
1464 let t = i as f32 / steps as f32;
1465 let t2 = t * t;
1466 let t3 = t2 * t;
1467 let mt = 1.0 - t;
1468 let mt2 = mt * mt;
1469 let mt3 = mt2 * mt;
1470
1471 let x = mt3 * start.x
1472 + 3.0 * mt2 * t * control1.x
1473 + 3.0 * mt * t2 * control2.x
1474 + t3 * end.x;
1475 let y = mt3 * start.y
1476 + 3.0 * mt2 * t * control1.y
1477 + 3.0 * mt * t2 * control2.y
1478 + t3 * end.y;
1479
1480 let curr = crate::point::PointF { x, y };
1481 self.draw_line(
1482 Point::new(prev.x as i32, prev.y as i32),
1483 Point::new(curr.x as i32, curr.y as i32),
1484 paint,
1485 );
1486 prev = curr;
1487 }
1488 }
1489
1490 pub fn translate(&mut self, dx: f32, dy: f32) {
1501 self.current_matrix = self.current_matrix.pre_translate(dx, dy);
1502 }
1503
1504 pub fn scale(&mut self, sx: f32, sy: f32) {
1513 self.current_matrix = self.current_matrix.pre_scale(sx, sy);
1514 }
1515
1516 pub fn rotate(&mut self, degrees: f32) {
1524 self.current_matrix = self.current_matrix.pre_rotate(degrees);
1525 }
1526
1527 pub fn rotate_around(&mut self, degrees: f32, px: f32, py: f32) {
1535 self.translate(px, py);
1536 self.rotate(degrees);
1537 self.translate(-px, -py);
1538 }
1539
1540 pub fn set_matrix(&mut self, matrix: &Matrix) {
1548 self.current_matrix = *matrix;
1549 }
1550
1551 pub fn get_matrix(&self) -> &Matrix {
1553 &self.current_matrix
1554 }
1555
1556 pub fn concat(&mut self, matrix: &Matrix) {
1564 self.current_matrix = self.current_matrix.post_concat(matrix);
1565 }
1566
1567 pub fn reset_matrix(&mut self) {
1569 self.current_matrix = Matrix::identity();
1570 }
1571
1572 pub fn save(&mut self) {
1578 let state = CanvasState {
1579 matrix: self.current_matrix,
1580 clip_rect: self.clip_stack.get_bounds(),
1581 };
1582 self.state_stack.push(state);
1583 self.clip_stack.save();
1584 }
1585
1586 pub fn restore(&mut self) {
1590 if let Some(state) = self.state_stack.pop() {
1591 self.current_matrix = state.matrix;
1592 self.clip_stack.restore();
1593 }
1594 }
1595
1596 pub fn get_save_count(&self) -> usize {
1598 self.state_stack.len()
1599 }
1600
1601 pub fn restore_to_count(&mut self, count: usize) {
1607 while self.state_stack.len() > count {
1608 self.restore();
1609 }
1610 }
1611
1612 pub fn clip_rect(&mut self, rect: Rect, op: ClipOp) {
1623 let transformed_rect = self.transform_rect(rect);
1625 self.clip_stack.clip_rect(transformed_rect, op);
1626 }
1627
1628 pub fn clip_path(&mut self, path: &Path, op: ClipOp) {
1637 let transformed_path = path.transform(&self.current_matrix);
1639 self.clip_stack.clip_path(transformed_path, op);
1640 }
1641
1642 pub fn get_clip_bounds(&self) -> Option<Rect> {
1644 self.clip_stack.get_bounds()
1645 }
1646
1647 pub fn is_clipped(&self, x: i32, y: i32) -> bool {
1649 !self.clip_stack.contains_point(x, y)
1650 }
1651
1652 fn transform_rect(&self, rect: Rect) -> Rect {
1656 let (x1, y1) = self.current_matrix.map_point(rect.x as f32, rect.y as f32);
1657 let (x2, y2) = self.current_matrix.map_point(
1658 (rect.x + rect.width as i32) as f32,
1659 (rect.y + rect.height as i32) as f32,
1660 );
1661
1662 Rect::new(
1663 x1 as i32,
1664 y1 as i32,
1665 (x2 - x1).abs() as u32,
1666 (y2 - y1).abs() as u32,
1667 )
1668 }
1669}