1use crate::color::Color;
6use crate::error::{DrawError, Result};
7use crate::font::TextStyle;
8use crate::paint::{Paint, PaintStyle};
9use crate::point::Point;
10use crate::rect::Rect;
11use crate::surface::Surface;
12use image::Rgba;
13use imageproc::drawing::{
14 draw_filled_rect_mut, draw_hollow_rect_mut, draw_line_segment_mut, draw_text_mut,
15};
16use imageproc::rect::Rect as ImageprocRect;
17
18pub struct Canvas {
22 surface: Surface,
24}
25
26impl Canvas {
27 pub fn new(width: u32, height: u32) -> Result<Self> {
29 let surface = Surface::new(width, height)?;
30 Ok(Self { surface })
31 }
32
33 pub fn new_with_color(width: u32, height: u32, color: Color) -> Result<Self> {
35 let surface = Surface::new_with_color(width, height, color.to_rgba())?;
36 Ok(Self { surface })
37 }
38
39 pub fn from_surface(surface: Surface) -> Self {
41 Self { surface }
42 }
43
44 pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
46 let surface = Surface::from_file(path)?;
47 Ok(Self { surface })
48 }
49
50 pub fn width(&self) -> u32 {
52 self.surface.width()
53 }
54
55 pub fn height(&self) -> u32 {
57 self.surface.height()
58 }
59
60 pub fn dimensions(&self) -> (u32, u32) {
62 self.surface.dimensions()
63 }
64
65 pub fn clear(&mut self, color: Color) {
67 self.surface.clear(color.to_rgba());
68 }
69
70 pub fn save<P: AsRef<std::path::Path>>(&self, path: P) -> Result<()> {
72 self.surface.save(path)?;
73 Ok(())
74 }
75
76 pub fn to_png_bytes(&self) -> Result<Vec<u8>> {
78 Ok(self.surface.to_png_bytes()?)
79 }
80
81 pub fn to_jpeg_bytes(&self, quality: u8) -> Result<Vec<u8>> {
83 Ok(self.surface.to_jpeg_bytes(quality)?)
84 }
85
86 pub fn surface(&self) -> &Surface {
88 &self.surface
89 }
90
91 pub fn surface_mut(&mut self) -> &mut Surface {
93 &mut self.surface
94 }
95
96 pub fn draw_point(&mut self, point: Point, paint: &Paint) {
98 let x = point.x;
99 let y = point.y;
100
101 if x >= 0 && y >= 0 {
102 let x = x as u32;
103 let y = y as u32;
104 if x < self.width() && y < self.height() {
105 self.surface.put_pixel(x, y, paint.color().to_rgba());
106 }
107 }
108 }
109
110 pub fn draw_line(&mut self, start: Point, end: Point, paint: &Paint) {
112 let start_f = (start.x as f32, start.y as f32);
113 let end_f = (end.x as f32, end.y as f32);
114
115 draw_line_segment_mut(
116 self.surface.image_mut(),
117 start_f,
118 end_f,
119 paint.color().to_rgba(),
120 );
121 }
122
123 pub fn draw_rect(&mut self, rect: Rect, paint: &Paint) {
125 let imageproc_rect = ImageprocRect::at(rect.x, rect.y).of_size(rect.width, rect.height);
126
127 match paint.style() {
128 PaintStyle::Fill => {
129 draw_filled_rect_mut(
130 self.surface.image_mut(),
131 imageproc_rect,
132 paint.color().to_rgba(),
133 );
134 }
135 PaintStyle::Stroke => {
136 draw_hollow_rect_mut(
138 self.surface.image_mut(),
139 imageproc_rect,
140 paint.color().to_rgba(),
141 );
142
143 let stroke_width = paint.stroke_width() as i32;
145 for i in 1..stroke_width {
146 let inner_rect = ImageprocRect::at(rect.x + i, rect.y + i).of_size(
147 rect.width.saturating_sub((i * 2) as u32),
148 rect.height.saturating_sub((i * 2) as u32),
149 );
150 if inner_rect.width() > 0 && inner_rect.height() > 0 {
151 draw_hollow_rect_mut(
152 self.surface.image_mut(),
153 inner_rect,
154 paint.color().to_rgba(),
155 );
156 }
157 }
158 }
159 PaintStyle::FillAndStroke => {
160 draw_filled_rect_mut(
162 self.surface.image_mut(),
163 imageproc_rect,
164 paint.color().to_rgba(),
165 );
166 draw_hollow_rect_mut(
168 self.surface.image_mut(),
169 imageproc_rect,
170 paint.color().to_rgba(),
171 );
172 }
173 }
174 }
175
176 pub fn draw_rounded_rect(&mut self, rect: Rect, radius: f32, paint: &Paint) {
178 let r = radius.min((rect.width as f32 / 2.0).min(rect.height as f32 / 2.0));
180
181 if r <= 0.0 {
182 self.draw_rect(rect, paint);
183 return;
184 }
185
186 let color = paint.color().to_rgba();
187
188 match paint.style() {
190 PaintStyle::Fill | PaintStyle::FillAndStroke => {
191 let center_rect = ImageprocRect::at(rect.x, rect.y + r as i32)
193 .of_size(rect.width, rect.height - (r * 2.0) as u32);
194 draw_filled_rect_mut(self.surface.image_mut(), center_rect, color);
195
196 let top_rect = ImageprocRect::at(rect.x + r as i32, rect.y)
198 .of_size(rect.width - (r * 2.0) as u32, r as u32);
199 draw_filled_rect_mut(self.surface.image_mut(), top_rect, color);
200
201 let bottom_rect =
202 ImageprocRect::at(rect.x + r as i32, rect.y + rect.height as i32 - r as i32)
203 .of_size(rect.width - (r * 2.0) as u32, r as u32);
204 draw_filled_rect_mut(self.surface.image_mut(), bottom_rect, color);
205
206 self.draw_circle_corner(rect.x + r as i32, rect.y + r as i32, r, color, true);
208 self.draw_circle_corner(
209 rect.x + rect.width as i32 - r as i32,
210 rect.y + r as i32,
211 r,
212 color,
213 true,
214 );
215 self.draw_circle_corner(
216 rect.x + r as i32,
217 rect.y + rect.height as i32 - r as i32,
218 r,
219 color,
220 true,
221 );
222 self.draw_circle_corner(
223 rect.x + rect.width as i32 - r as i32,
224 rect.y + rect.height as i32 - r as i32,
225 r,
226 color,
227 true,
228 );
229 }
230 PaintStyle::Stroke => {
231 self.draw_rect(rect, paint);
233 }
234 }
235 }
236
237 fn draw_circle_corner(&mut self, cx: i32, cy: i32, radius: f32, color: Rgba<u8>, fill: bool) {
239 let r = radius as i32;
240 for dy in -r..=r {
241 for dx in -r..=r {
242 let dist_sq = dx * dx + dy * dy;
243 let r_sq = (radius * radius) as i32;
244
245 if fill && dist_sq <= r_sq {
246 let x = cx + dx;
247 let y = cy + dy;
248 if x >= 0 && y >= 0 {
249 self.surface.put_pixel(x as u32, y as u32, color);
250 }
251 }
252 }
253 }
254 }
255
256 pub fn draw_circle(&mut self, center: Point, radius: f32, paint: &Paint) {
258 let color = paint.color().to_rgba();
259 let r = radius as i32;
260
261 match paint.style() {
262 PaintStyle::Fill | PaintStyle::FillAndStroke => {
263 for dy in -r..=r {
265 for dx in -r..=r {
266 let dist_sq = dx * dx + dy * dy;
267 let r_sq = (radius * radius) as i32;
268
269 if dist_sq <= r_sq {
270 let x = center.x + dx;
271 let y = center.y + dy;
272 if x >= 0 && y >= 0 {
273 self.surface.put_pixel(x as u32, y as u32, color);
274 }
275 }
276 }
277 }
278 }
279 PaintStyle::Stroke => {
280 let stroke_width = paint.stroke_width();
282 let inner_r = (radius - stroke_width).max(0.0);
283 let inner_r_sq = (inner_r * inner_r) as i32;
284 let outer_r_sq = (radius * radius) as i32;
285
286 for dy in -r..=r {
287 for dx in -r..=r {
288 let dist_sq = dx * dx + dy * dy;
289
290 if dist_sq <= outer_r_sq && dist_sq >= inner_r_sq {
291 let x = center.x + dx;
292 let y = center.y + dy;
293 if x >= 0 && y >= 0 {
294 self.surface.put_pixel(x as u32, y as u32, color);
295 }
296 }
297 }
298 }
299 }
300 }
301 }
302
303 pub fn draw_oval(&mut self, rect: Rect, paint: &Paint) {
305 let cx = rect.x + rect.width as i32 / 2;
306 let cy = rect.y + rect.height as i32 / 2;
307 let rx = rect.width as f32 / 2.0;
308 let ry = rect.height as f32 / 2.0;
309
310 let color = paint.color().to_rgba();
311
312 match paint.style() {
313 PaintStyle::Fill | PaintStyle::FillAndStroke => {
314 for y in rect.y..(rect.y + rect.height as i32) {
316 for x in rect.x..(rect.x + rect.width as i32) {
317 let dx = (x - cx) as f32;
318 let dy = (y - cy) as f32;
319
320 if (dx * dx) / (rx * rx) + (dy * dy) / (ry * ry) <= 1.0 {
321 if x >= 0 && y >= 0 {
322 self.surface.put_pixel(x as u32, y as u32, color);
323 }
324 }
325 }
326 }
327 }
328 PaintStyle::Stroke => {
329 let stroke_width = paint.stroke_width();
331 let inner_rx = (rx - stroke_width).max(0.0);
332 let inner_ry = (ry - stroke_width).max(0.0);
333
334 for y in rect.y..(rect.y + rect.height as i32) {
335 for x in rect.x..(rect.x + rect.width as i32) {
336 let dx = (x - cx) as f32;
337 let dy = (y - cy) as f32;
338
339 let outer = (dx * dx) / (rx * rx) + (dy * dy) / (ry * ry);
340 let inner =
341 (dx * dx) / (inner_rx * inner_rx) + (dy * dy) / (inner_ry * inner_ry);
342
343 if outer <= 1.0 && inner >= 1.0 {
344 if x >= 0 && y >= 0 {
345 self.surface.put_pixel(x as u32, y as u32, color);
346 }
347 }
348 }
349 }
350 }
351 }
352 }
353
354 pub fn draw_text(
356 &mut self,
357 text: &str,
358 position: Point,
359 text_style: &TextStyle,
360 paint: &Paint,
361 ) {
362 let scale = text_style.scale();
363 let font = text_style.font.inner();
364 let color = paint.color().to_rgba();
365
366 draw_text_mut(
367 self.surface.image_mut(),
368 color,
369 position.x,
370 position.y,
371 scale,
372 font,
373 text,
374 );
375 }
376
377 pub fn draw_image(&mut self, image: &Surface, position: Point) -> Result<()> {
379 self.draw_image_rect(
380 image,
381 None,
382 Rect::new(position.x, position.y, image.width(), image.height()),
383 )
384 }
385
386 pub fn draw_image_rect(
388 &mut self,
389 image: &Surface,
390 src_rect: Option<Rect>,
391 dst_rect: Rect,
392 ) -> Result<()> {
393 let src = src_rect.unwrap_or_else(|| Rect::new(0, 0, image.width(), image.height()));
395
396 if src.x < 0
398 || src.y < 0
399 || src.x as u32 + src.width > image.width()
400 || src.y as u32 + src.height > image.height()
401 {
402 return Err(DrawError::InvalidCoordinates { x: src.x, y: src.y });
403 }
404
405 let src_image = if src.width != dst_rect.width || src.height != dst_rect.height {
407 let cropped = image.crop(src.x as u32, src.y as u32, src.width, src.height)?;
409 cropped.resize(dst_rect.width, dst_rect.height)?
411 } else {
412 image.crop(src.x as u32, src.y as u32, src.width, src.height)?
414 };
415
416 for y in 0..dst_rect.height {
418 for x in 0..dst_rect.width {
419 let src_pixel = src_image.get_pixel(x, y).unwrap();
420 let dst_x = dst_rect.x + x as i32;
421 let dst_y = dst_rect.y + y as i32;
422
423 if dst_x >= 0
424 && dst_y >= 0
425 && (dst_x as u32) < self.width()
426 && (dst_y as u32) < self.height()
427 {
428 self.blend_pixel(dst_x as u32, dst_y as u32, src_pixel);
430 }
431 }
432 }
433
434 Ok(())
435 }
436
437 fn blend_pixel(&mut self, x: u32, y: u32, src: Rgba<u8>) {
439 if let Some(dst) = self.surface.get_pixel(x, y) {
440 let src_alpha = src[3] as f32 / 255.0;
441 let dst_alpha = dst[3] as f32 / 255.0;
442
443 if src_alpha == 0.0 {
444 return;
445 }
446
447 if src_alpha == 1.0 {
448 self.surface.put_pixel(x, y, src);
449 return;
450 }
451
452 let out_alpha = src_alpha + dst_alpha * (1.0 - src_alpha);
454
455 if out_alpha == 0.0 {
456 return;
457 }
458
459 let r = ((src[0] as f32 * src_alpha + dst[0] as f32 * dst_alpha * (1.0 - src_alpha))
460 / out_alpha) as u8;
461 let g = ((src[1] as f32 * src_alpha + dst[1] as f32 * dst_alpha * (1.0 - src_alpha))
462 / out_alpha) as u8;
463 let b = ((src[2] as f32 * src_alpha + dst[2] as f32 * dst_alpha * (1.0 - src_alpha))
464 / out_alpha) as u8;
465 let a = (out_alpha * 255.0) as u8;
466
467 self.surface.put_pixel(x, y, Rgba([r, g, b, a]));
468 }
469 }
470
471 pub fn draw_image_nine(&mut self, image: &Surface, center: Rect, dst: Rect) -> Result<()> {
497 if center.x < 0
499 || center.y < 0
500 || center.x as u32 >= image.width()
501 || center.y as u32 >= image.height()
502 || center.x as u32 + center.width > image.width()
503 || center.y as u32 + center.height > image.height()
504 {
505 return Err(DrawError::InvalidCoordinates {
506 x: center.x,
507 y: center.y,
508 });
509 }
510
511 let src_left = center.x as u32;
513 let src_top = center.y as u32;
514 let src_right = image.width() - (center.x as u32 + center.width);
515 let src_bottom = image.height() - (center.y as u32 + center.height);
516 let src_center_width = center.width;
517 let src_center_height = center.height;
518
519 let dst_left = src_left.min(dst.width / 3);
522 let dst_top = src_top.min(dst.height / 3);
523 let dst_right = src_right.min(dst.width / 3);
524 let dst_bottom = src_bottom.min(dst.height / 3);
525
526 let dst_center_width = dst.width.saturating_sub(dst_left + dst_right);
527 let dst_center_height = dst.height.saturating_sub(dst_top + dst_bottom);
528
529 if dst_left > 0 && dst_top > 0 {
533 let src_rect = Rect::new(0, 0, src_left, src_top);
534 let dst_rect = Rect::new(dst.x, dst.y, dst_left, dst_top);
535 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
536 }
537
538 if dst_center_width > 0 && dst_top > 0 {
540 let src_rect = Rect::new(src_left as i32, 0, src_center_width, src_top);
541 let dst_rect = Rect::new(dst.x + dst_left as i32, dst.y, dst_center_width, dst_top);
542 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
543 }
544
545 if dst_right > 0 && dst_top > 0 {
547 let src_rect = Rect::new((image.width() - src_right) as i32, 0, src_right, src_top);
548 let dst_rect = Rect::new(
549 dst.x + (dst.width - dst_right) as i32,
550 dst.y,
551 dst_right,
552 dst_top,
553 );
554 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
555 }
556
557 if dst_left > 0 && dst_center_height > 0 {
559 let src_rect = Rect::new(0, src_top as i32, src_left, src_center_height);
560 let dst_rect = Rect::new(dst.x, dst.y + dst_top as i32, dst_left, dst_center_height);
561 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
562 }
563
564 if dst_center_width > 0 && dst_center_height > 0 {
566 let src_rect = Rect::new(
567 src_left as i32,
568 src_top as i32,
569 src_center_width,
570 src_center_height,
571 );
572 let dst_rect = Rect::new(
573 dst.x + dst_left as i32,
574 dst.y + dst_top as i32,
575 dst_center_width,
576 dst_center_height,
577 );
578 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
579 }
580
581 if dst_right > 0 && dst_center_height > 0 {
583 let src_rect = Rect::new(
584 (image.width() - src_right) as i32,
585 src_top as i32,
586 src_right,
587 src_center_height,
588 );
589 let dst_rect = Rect::new(
590 dst.x + (dst.width - dst_right) as i32,
591 dst.y + dst_top as i32,
592 dst_right,
593 dst_center_height,
594 );
595 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
596 }
597
598 if dst_left > 0 && dst_bottom > 0 {
600 let src_rect = Rect::new(
601 0,
602 (image.height() - src_bottom) as i32,
603 src_left,
604 src_bottom,
605 );
606 let dst_rect = Rect::new(
607 dst.x,
608 dst.y + (dst.height - dst_bottom) as i32,
609 dst_left,
610 dst_bottom,
611 );
612 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
613 }
614
615 if dst_center_width > 0 && dst_bottom > 0 {
617 let src_rect = Rect::new(
618 src_left as i32,
619 (image.height() - src_bottom) as i32,
620 src_center_width,
621 src_bottom,
622 );
623 let dst_rect = Rect::new(
624 dst.x + dst_left as i32,
625 dst.y + (dst.height - dst_bottom) as i32,
626 dst_center_width,
627 dst_bottom,
628 );
629 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
630 }
631
632 if dst_right > 0 && dst_bottom > 0 {
634 let src_rect = Rect::new(
635 (image.width() - src_right) as i32,
636 (image.height() - src_bottom) as i32,
637 src_right,
638 src_bottom,
639 );
640 let dst_rect = Rect::new(
641 dst.x + (dst.width - dst_right) as i32,
642 dst.y + (dst.height - dst_bottom) as i32,
643 dst_right,
644 dst_bottom,
645 );
646 self.draw_image_rect(image, Some(src_rect), dst_rect)?;
647 }
648
649 Ok(())
650 }
651
652 pub fn measure_text(&self, text: &str, text_style: &TextStyle) -> (u32, u32) {
654 let scale = text_style.scale();
655 let font = text_style.font.inner();
656
657 let v_metrics = font.v_metrics(scale);
658 let height = (v_metrics.ascent - v_metrics.descent).ceil() as u32;
659
660 let width = font
661 .layout(text, scale, rusttype::point(0.0, 0.0))
662 .map(|g| g.position().x + g.unpositioned().h_metrics().advance_width)
663 .last()
664 .unwrap_or(0.0)
665 .ceil() as u32;
666
667 (width, height)
668 }
669
670 pub fn draw_dashed_line(&mut self, start: Point, end: Point, paint: &Paint) {
696 if paint.dash_style().is_solid() {
698 self.draw_line(start, end, paint);
699 return;
700 }
701
702 let pattern = match paint.dash_pattern() {
704 Some(p) => p,
705 None => {
706 self.draw_line(start, end, paint);
707 return;
708 }
709 };
710
711 if pattern.is_empty() {
712 self.draw_line(start, end, paint);
713 return;
714 }
715
716 let dx = end.x - start.x;
718 let dy = end.y - start.y;
719 let length = ((dx * dx + dy * dy) as f32).sqrt();
720
721 if length == 0.0 {
722 return;
723 }
724
725 let unit_dx = dx as f32 / length;
727 let unit_dy = dy as f32 / length;
728
729 let mut current_pos = 0.0;
731 let mut pattern_index = 0;
732 let mut is_dash = true; while current_pos < length {
735 let segment_length = pattern[pattern_index % pattern.len()];
736 let next_pos = (current_pos + segment_length).min(length);
737
738 if is_dash {
739 let seg_start = Point::new(
741 start.x + (unit_dx * current_pos) as i32,
742 start.y + (unit_dy * current_pos) as i32,
743 );
744 let seg_end = Point::new(
745 start.x + (unit_dx * next_pos) as i32,
746 start.y + (unit_dy * next_pos) as i32,
747 );
748 self.draw_line(seg_start, seg_end, paint);
749 }
750
751 current_pos = next_pos;
752 pattern_index += 1;
753 is_dash = !is_dash; }
755 }
756
757 pub fn draw_dashed_rect(&mut self, rect: Rect, paint: &Paint) {
781 if paint.dash_style().is_solid() {
783 self.draw_rect(rect, paint);
784 return;
785 }
786
787 self.draw_dashed_line(
790 Point::new(rect.x, rect.y),
791 Point::new(rect.x + rect.width as i32, rect.y),
792 paint,
793 );
794
795 self.draw_dashed_line(
797 Point::new(rect.x + rect.width as i32, rect.y),
798 Point::new(rect.x + rect.width as i32, rect.y + rect.height as i32),
799 paint,
800 );
801
802 self.draw_dashed_line(
804 Point::new(rect.x + rect.width as i32, rect.y + rect.height as i32),
805 Point::new(rect.x, rect.y + rect.height as i32),
806 paint,
807 );
808
809 self.draw_dashed_line(
811 Point::new(rect.x, rect.y + rect.height as i32),
812 Point::new(rect.x, rect.y),
813 paint,
814 );
815 }
816
817 pub fn draw_dashed_rounded_rect(&mut self, rect: Rect, radius: f32, paint: &Paint) {
843 let r = radius.min((rect.width as f32 / 2.0).min(rect.height as f32 / 2.0));
845
846 if r <= 0.0 {
847 self.draw_dashed_rect(rect, paint);
848 return;
849 }
850
851 if paint.dash_style().is_solid() {
853 self.draw_rounded_rect(rect, r, paint);
854 return;
855 }
856
857 self.draw_dashed_line(
860 Point::new(rect.x + r as i32, rect.y),
861 Point::new(rect.x + rect.width as i32 - r as i32, rect.y),
862 paint,
863 );
864
865 self.draw_dashed_line(
867 Point::new(rect.x + rect.width as i32, rect.y + r as i32),
868 Point::new(
869 rect.x + rect.width as i32,
870 rect.y + rect.height as i32 - r as i32,
871 ),
872 paint,
873 );
874
875 self.draw_dashed_line(
877 Point::new(
878 rect.x + rect.width as i32 - r as i32,
879 rect.y + rect.height as i32,
880 ),
881 Point::new(rect.x + r as i32, rect.y + rect.height as i32),
882 paint,
883 );
884
885 self.draw_dashed_line(
887 Point::new(rect.x, rect.y + rect.height as i32 - r as i32),
888 Point::new(rect.x, rect.y + r as i32),
889 paint,
890 );
891
892 self.draw_dashed_arc(
895 Point::new(rect.x + r as i32, rect.y + r as i32),
896 r,
897 180.0,
898 270.0,
899 paint,
900 );
901
902 self.draw_dashed_arc(
904 Point::new(rect.x + rect.width as i32 - r as i32, rect.y + r as i32),
905 r,
906 270.0,
907 360.0,
908 paint,
909 );
910
911 self.draw_dashed_arc(
913 Point::new(
914 rect.x + rect.width as i32 - r as i32,
915 rect.y + rect.height as i32 - r as i32,
916 ),
917 r,
918 0.0,
919 90.0,
920 paint,
921 );
922
923 self.draw_dashed_arc(
925 Point::new(rect.x + r as i32, rect.y + rect.height as i32 - r as i32),
926 r,
927 90.0,
928 180.0,
929 paint,
930 );
931 }
932
933 fn draw_dashed_arc(
945 &mut self,
946 center: Point,
947 radius: f32,
948 start_angle: f32,
949 end_angle: f32,
950 paint: &Paint,
951 ) {
952 let pattern = match paint.dash_pattern() {
954 Some(p) if !p.is_empty() => p,
955 _ => {
956 self.draw_arc_solid(center, radius, start_angle, end_angle, paint);
958 return;
959 }
960 };
961
962 let angle_range = (end_angle - start_angle).abs();
964 let arc_length = (angle_range.to_radians() * radius) as f32;
965
966 if arc_length <= 0.0 {
967 return;
968 }
969
970 let mut current_length = 0.0;
972 let mut pattern_index = 0;
973 let mut is_dash = true;
974
975 while current_length < arc_length {
976 let segment_length = pattern[pattern_index % pattern.len()];
977 let next_length = (current_length + segment_length).min(arc_length);
978
979 if is_dash {
980 let seg_start_angle = start_angle + (current_length / arc_length) * angle_range;
982 let seg_end_angle = start_angle + (next_length / arc_length) * angle_range;
983
984 self.draw_arc_segment(center, radius, seg_start_angle, seg_end_angle, paint);
986 }
987
988 current_length = next_length;
989 pattern_index += 1;
990 is_dash = !is_dash;
991 }
992 }
993
994 fn draw_arc_solid(
996 &mut self,
997 center: Point,
998 radius: f32,
999 start_angle: f32,
1000 end_angle: f32,
1001 paint: &Paint,
1002 ) {
1003 self.draw_arc_segment(center, radius, start_angle, end_angle, paint);
1004 }
1005
1006 fn draw_arc_segment(
1008 &mut self,
1009 center: Point,
1010 radius: f32,
1011 start_angle: f32,
1012 end_angle: f32,
1013 paint: &Paint,
1014 ) {
1015 let angle_range = (end_angle - start_angle).abs();
1018 let arc_length = angle_range.to_radians() * radius;
1019
1020 let points_per_pixel = 4.0; let num_points = (arc_length * points_per_pixel)
1024 .ceil()
1025 .max(angle_range * 2.0)
1026 .max(10.0) as i32;
1027
1028 let mut points = Vec::new();
1030 for i in 0..=num_points {
1031 let t = i as f32 / num_points as f32;
1032 let angle = start_angle + t * (end_angle - start_angle);
1033 let rad = angle.to_radians();
1034
1035 let x = center.x + (radius * rad.cos()) as i32;
1036 let y = center.y + (radius * rad.sin()) as i32;
1037 points.push(Point::new(x, y));
1038 }
1039
1040 for i in 0..points.len() - 1 {
1042 self.draw_line(points[i], points[i + 1], paint);
1043 }
1044 }
1045
1046 pub fn draw_custom_border_rect(
1076 &mut self,
1077 rect: Rect,
1078 border: &crate::border::Border,
1079 radius: &crate::border::BorderRadius,
1080 ) {
1081 let radius = radius.clamp(rect.width as f32, rect.height as f32);
1083
1084 if radius.is_zero() {
1086 self.draw_custom_border_rect_no_radius(rect, border);
1087 return;
1088 }
1089
1090 if border.top.width > 0.0 {
1093 let mut paint = crate::Paint::stroke(border.top.color, border.top.width);
1094 paint.set_dash_style(border.top.dash_style.clone());
1095
1096 let start = Point::new(rect.x + radius.top_left as i32, rect.y);
1097 let end = Point::new(rect.x + rect.width as i32 - radius.top_right as i32, rect.y);
1098 self.draw_dashed_line(start, end, &paint);
1099 }
1100
1101 if border.right.width > 0.0 {
1103 let mut paint = crate::Paint::stroke(border.right.color, border.right.width);
1104 paint.set_dash_style(border.right.dash_style.clone());
1105
1106 let start = Point::new(rect.x + rect.width as i32, rect.y + radius.top_right as i32);
1107 let end = Point::new(
1108 rect.x + rect.width as i32,
1109 rect.y + rect.height as i32 - radius.bottom_right as i32,
1110 );
1111 self.draw_dashed_line(start, end, &paint);
1112 }
1113
1114 if border.bottom.width > 0.0 {
1116 let mut paint = crate::Paint::stroke(border.bottom.color, border.bottom.width);
1117 paint.set_dash_style(border.bottom.dash_style.clone());
1118
1119 let start = Point::new(
1120 rect.x + rect.width as i32 - radius.bottom_right as i32,
1121 rect.y + rect.height as i32,
1122 );
1123 let end = Point::new(
1124 rect.x + radius.bottom_left as i32,
1125 rect.y + rect.height as i32,
1126 );
1127 self.draw_dashed_line(start, end, &paint);
1128 }
1129
1130 if border.left.width > 0.0 {
1132 let mut paint = crate::Paint::stroke(border.left.color, border.left.width);
1133 paint.set_dash_style(border.left.dash_style.clone());
1134
1135 let start = Point::new(
1136 rect.x,
1137 rect.y + rect.height as i32 - radius.bottom_left as i32,
1138 );
1139 let end = Point::new(rect.x, rect.y + radius.top_left as i32);
1140 self.draw_dashed_line(start, end, &paint);
1141 }
1142
1143 if radius.top_left > 0.0 {
1146 let corner_border = self.blend_corner_style(&border.top, &border.left);
1148 let mut paint = crate::Paint::stroke(corner_border.color, corner_border.width);
1149 paint.set_dash_style(corner_border.dash_style);
1150
1151 self.draw_dashed_arc(
1152 Point::new(
1153 rect.x + radius.top_left as i32,
1154 rect.y + radius.top_left as i32,
1155 ),
1156 radius.top_left,
1157 180.0,
1158 270.0,
1159 &paint,
1160 );
1161 }
1162
1163 if radius.top_right > 0.0 {
1165 let corner_border = self.blend_corner_style(&border.top, &border.right);
1166 let mut paint = crate::Paint::stroke(corner_border.color, corner_border.width);
1167 paint.set_dash_style(corner_border.dash_style);
1168
1169 self.draw_dashed_arc(
1170 Point::new(
1171 rect.x + rect.width as i32 - radius.top_right as i32,
1172 rect.y + radius.top_right as i32,
1173 ),
1174 radius.top_right,
1175 270.0,
1176 360.0,
1177 &paint,
1178 );
1179 }
1180
1181 if radius.bottom_right > 0.0 {
1183 let corner_border = self.blend_corner_style(&border.right, &border.bottom);
1184 let mut paint = crate::Paint::stroke(corner_border.color, corner_border.width);
1185 paint.set_dash_style(corner_border.dash_style);
1186
1187 self.draw_dashed_arc(
1188 Point::new(
1189 rect.x + rect.width as i32 - radius.bottom_right as i32,
1190 rect.y + rect.height as i32 - radius.bottom_right as i32,
1191 ),
1192 radius.bottom_right,
1193 0.0,
1194 90.0,
1195 &paint,
1196 );
1197 }
1198
1199 if radius.bottom_left > 0.0 {
1201 let corner_border = self.blend_corner_style(&border.bottom, &border.left);
1202 let mut paint = crate::Paint::stroke(corner_border.color, corner_border.width);
1203 paint.set_dash_style(corner_border.dash_style);
1204
1205 self.draw_dashed_arc(
1206 Point::new(
1207 rect.x + radius.bottom_left as i32,
1208 rect.y + rect.height as i32 - radius.bottom_left as i32,
1209 ),
1210 radius.bottom_left,
1211 90.0,
1212 180.0,
1213 &paint,
1214 );
1215 }
1216 }
1217
1218 fn draw_custom_border_rect_no_radius(&mut self, rect: Rect, border: &crate::border::Border) {
1220 if border.top.width > 0.0 {
1222 let mut paint = crate::Paint::stroke(border.top.color, border.top.width);
1223 paint.set_dash_style(border.top.dash_style.clone());
1224 self.draw_dashed_line(
1225 Point::new(rect.x, rect.y),
1226 Point::new(rect.x + rect.width as i32, rect.y),
1227 &paint,
1228 );
1229 }
1230
1231 if border.right.width > 0.0 {
1233 let mut paint = crate::Paint::stroke(border.right.color, border.right.width);
1234 paint.set_dash_style(border.right.dash_style.clone());
1235 self.draw_dashed_line(
1236 Point::new(rect.x + rect.width as i32, rect.y),
1237 Point::new(rect.x + rect.width as i32, rect.y + rect.height as i32),
1238 &paint,
1239 );
1240 }
1241
1242 if border.bottom.width > 0.0 {
1244 let mut paint = crate::Paint::stroke(border.bottom.color, border.bottom.width);
1245 paint.set_dash_style(border.bottom.dash_style.clone());
1246 self.draw_dashed_line(
1247 Point::new(rect.x + rect.width as i32, rect.y + rect.height as i32),
1248 Point::new(rect.x, rect.y + rect.height as i32),
1249 &paint,
1250 );
1251 }
1252
1253 if border.left.width > 0.0 {
1255 let mut paint = crate::Paint::stroke(border.left.color, border.left.width);
1256 paint.set_dash_style(border.left.dash_style.clone());
1257 self.draw_dashed_line(
1258 Point::new(rect.x, rect.y + rect.height as i32),
1259 Point::new(rect.x, rect.y),
1260 &paint,
1261 );
1262 }
1263 }
1264
1265 fn blend_corner_style(
1267 &self,
1268 side1: &crate::border::BorderSide,
1269 side2: &crate::border::BorderSide,
1270 ) -> crate::border::BorderSide {
1271 if side1.width == 0.0 {
1273 return side2.clone();
1274 }
1275 if side2.width == 0.0 {
1276 return side1.clone();
1277 }
1278
1279 let color = crate::Color::rgba(
1281 ((side1.color.r as u16 + side2.color.r as u16) / 2) as u8,
1282 ((side1.color.g as u16 + side2.color.g as u16) / 2) as u8,
1283 ((side1.color.b as u16 + side2.color.b as u16) / 2) as u8,
1284 ((side1.color.a as u16 + side2.color.a as u16) / 2) as u8,
1285 );
1286
1287 let width = (side1.width + side2.width) / 2.0;
1289
1290 let dash_style = side1.dash_style.clone();
1292
1293 crate::border::BorderSide::new(color, width, dash_style)
1294 }
1295}