1use crate::console::graphics::{ClampedInto, RasterOps};
19use crate::console::{PixelsXY, SizeInPixels};
20use crate::gfx::lcd::fonts::Font;
21use std::convert::TryFrom;
22use std::io;
23
24fn draw_line_low<R>(rasops: &mut R, x1: i32, y1: i32, x2: i32, y2: i32) -> io::Result<()>
26where
27 R: RasterOps,
28{
29 let dx = x2 - x1;
30 let mut dy = y2 - y1;
31
32 let mut yi = 1;
33 if dy < 0 {
34 yi = -1;
35 dy = -dy;
36 }
37 let mut d = (2 * dy) - dx;
38 let mut y = y1;
39
40 for x in x1..(x2 + 1) {
41 if cfg!(debug_assertions) {
42 rasops.draw_pixel(PixelsXY {
43 x: i16::try_from(x).expect("Coordinate must fit after computations"),
44 y: i16::try_from(y).expect("Coordinate must fit after computations"),
45 })?;
46 } else {
47 rasops.draw_pixel(PixelsXY { x: x as i16, y: y as i16 })?;
48 }
49 if d > 0 {
50 y += yi;
51 d += 2 * (dy - dx);
52 } else {
53 d += 2 * dy;
54 }
55 }
56
57 Ok(())
58}
59
60fn draw_line_high<R>(rasops: &mut R, x1: i32, y1: i32, x2: i32, y2: i32) -> io::Result<()>
62where
63 R: RasterOps,
64{
65 let mut dx = x2 - x1;
66 let dy = y2 - y1;
67
68 let mut xi = 1;
69 if dx < 0 {
70 xi = -1;
71 dx = -dx;
72 }
73 let mut d = (2 * dx) - dy;
74 let mut x = x1;
75
76 for y in y1..(y2 + 1) {
77 if cfg!(debug_assertions) {
78 rasops.draw_pixel(PixelsXY {
79 x: i16::try_from(x).expect("Coordinate must fit after computations"),
80 y: i16::try_from(y).expect("Coordinate must fit after computations"),
81 })?;
82 } else {
83 rasops.draw_pixel(PixelsXY { x: x as i16, y: y as i16 })?;
84 }
85 if d > 0 {
86 x += xi;
87 d += 2 * (dx - dy);
88 } else {
89 d += 2 * dx;
90 }
91 }
92
93 Ok(())
94}
95
96pub fn draw_line<R>(rasops: &mut R, x1y1: PixelsXY, x2y2: PixelsXY) -> io::Result<()>
101where
102 R: RasterOps,
103{
104 let x1 = i32::from(x1y1.x);
106 let y1 = i32::from(x1y1.y);
107 let x2 = i32::from(x2y2.x);
108 let y2 = i32::from(x2y2.y);
109
110 if (y2 - y1).abs() < (x2 - x1).abs() {
111 if x1y1.x > x2y2.x {
112 draw_line_low(rasops, x2, y2, x1, y1)
113 } else {
114 draw_line_low(rasops, x1, y1, x2, y2)
115 }
116 } else {
117 if x1y1.y > x2y2.y {
118 draw_line_high(rasops, x2, y2, x1, y1)
119 } else {
120 draw_line_high(rasops, x1, y1, x2, y2)
121 }
122 }
123}
124
125pub fn bucket_fill<R>(rasops: &mut R, xy: PixelsXY, fill_color: u8) -> io::Result<()>
127where
128 R: RasterOps,
129{
130 let seed_color = match rasops.peek_pixel(xy)? {
131 Some(color) => color,
132 None => return Ok(()),
133 };
134 if seed_color == fill_color {
135 return Ok(());
136 }
137
138 let mut pending = vec![xy];
139 while let Some(xy) = pending.pop() {
140 if rasops.peek_pixel(xy)? != Some(seed_color) {
141 continue;
142 }
143
144 rasops.draw_pixel(xy)?;
145
146 if xy.x > i16::MIN {
147 pending.push(PixelsXY::new(xy.x - 1, xy.y));
148 }
149 if xy.x < i16::MAX {
150 pending.push(PixelsXY::new(xy.x + 1, xy.y));
151 }
152 if xy.y > i16::MIN {
153 pending.push(PixelsXY::new(xy.x, xy.y - 1));
154 }
155 if xy.y < i16::MAX {
156 pending.push(PixelsXY::new(xy.x, xy.y + 1));
157 }
158 }
159
160 Ok(())
161}
162
163pub fn draw_circle<R>(rasops: &mut R, center: PixelsXY, radius: u16) -> io::Result<()>
168where
169 R: RasterOps,
170{
171 fn point<R: RasterOps>(rasops: &mut R, x: i16, y: i16) -> io::Result<()> {
172 rasops.draw_pixel(PixelsXY { x, y })
173 }
174
175 if radius == 0 {
176 return Ok(());
177 } else if radius == 1 {
178 return rasops.draw_pixel(center);
179 }
180
181 let (diameter, radius): (i16, i16) = match radius.checked_mul(2) {
182 Some(d) => match i16::try_from(d) {
183 Ok(d) => (d, radius as i16),
184 Err(_) => return Err(io::Error::new(io::ErrorKind::InvalidInput, "Radius is too big")),
185 },
186 None => return Err(io::Error::new(io::ErrorKind::InvalidInput, "Radius is too big")),
187 };
188
189 let mut x: i16 = radius - 1;
190 let mut y: i16 = 0;
191 let mut tx: i16 = 1;
192 let mut ty: i16 = 1;
193 let mut e: i16 = tx - diameter;
194
195 while x >= y {
196 point(rasops, center.x + x, center.y - y)?;
197 point(rasops, center.x + x, center.y + y)?;
198 point(rasops, center.x - x, center.y - y)?;
199 point(rasops, center.x - x, center.y + y)?;
200 point(rasops, center.x + y, center.y - x)?;
201 point(rasops, center.x + y, center.y + x)?;
202 point(rasops, center.x - y, center.y - x)?;
203 point(rasops, center.x - y, center.y + x)?;
204
205 if e <= 0 {
206 y += 1;
207 e += ty;
208 ty += 2;
209 }
210
211 if e > 0 {
212 x -= 1;
213 tx += 2;
214 e += tx - diameter;
215 }
216 }
217
218 Ok(())
219}
220
221pub fn draw_circle_filled<R>(rasops: &mut R, center: PixelsXY, radius: u16) -> io::Result<()>
226where
227 R: RasterOps,
228{
229 fn line<R: RasterOps>(rasops: &mut R, x1: i16, y1: i16, x2: i16, y2: i16) -> io::Result<()> {
230 rasops.draw_line(PixelsXY { x: x1, y: y1 }, PixelsXY { x: x2, y: y2 })
231 }
232
233 if radius == 0 {
234 return Ok(());
235 } else if radius == 1 {
236 return rasops.draw_pixel(center);
237 }
238
239 let (diameter, radius): (i16, i16) = match radius.checked_mul(2) {
240 Some(d) => match i16::try_from(d) {
241 Ok(d) => (d, radius as i16),
242
243 Err(_) => return Err(io::Error::new(io::ErrorKind::InvalidInput, "Radius is too big")),
244 },
245 None => return Err(io::Error::new(io::ErrorKind::InvalidInput, "Radius is too big")),
246 };
247
248 let mut x: i16 = radius - 1;
249 let mut y: i16 = 0;
250 let mut tx: i16 = 1;
251 let mut ty: i16 = 1;
252 let mut e: i16 = tx - diameter;
253
254 while x >= y {
255 line(rasops, center.x + x, center.y - y, center.x + x, center.y + y)?;
256 line(rasops, center.x - x, center.y - y, center.x - x, center.y + y)?;
257 line(rasops, center.x + y, center.y - x, center.x + y, center.y + x)?;
258 line(rasops, center.x - y, center.y - x, center.x - y, center.y + x)?;
259
260 if e <= 0 {
261 y += 1;
262 e += ty;
263 ty += 2;
264 }
265
266 if e > 0 {
267 x -= 1;
268 tx += 2;
269 e += tx - diameter;
270 }
271 }
272
273 Ok(())
274}
275
276pub fn draw_poly<R>(rasops: &mut R, points: &[PixelsXY]) -> io::Result<()>
278where
279 R: RasterOps + ?Sized,
280{
281 if points.len() < 2 {
282 return Ok(());
283 }
284
285 for i in 1..points.len() {
286 rasops.draw_line(points[i - 1], points[i])?;
287 }
288 rasops.draw_line(*points.last().expect("At least 2 points are present"), points[0])?;
289 Ok(())
290}
291
292fn edge_intersection(y: i32, p1: PixelsXY, p2: PixelsXY) -> Option<i32> {
302 let y1 = i32::from(p1.y);
303 let y2 = i32::from(p2.y);
304 if y1 == y2 {
305 return None;
306 }
307
308 let ymin = y1.min(y2);
309 let ymax = y1.max(y2);
310 if y < ymin || y >= ymax {
313 return None;
314 }
315
316 let x1 = i32::from(p1.x);
317 let x2 = i32::from(p2.x);
318 Some(x1 + (y - y1) * (x2 - x1) / (y2 - y1))
319}
320
321fn fill_polygon<R>(rasops: &mut R, points: &[PixelsXY]) -> io::Result<()>
323where
324 R: RasterOps + ?Sized,
325{
326 if points.is_empty() {
327 return Ok(());
328 }
329
330 let min_y = points.iter().map(|p| i32::from(p.y)).min().expect("Points are not empty");
331 let max_y = points.iter().map(|p| i32::from(p.y)).max().expect("Points are not empty");
332
333 for y in min_y..=max_y {
334 let mut xs = vec![];
335 for i in 0..points.len() {
336 let next = (i + 1) % points.len();
337 if let Some(x) = edge_intersection(y, points[i], points[next]) {
338 xs.push(x);
339 }
340 }
341
342 if xs.len() < 2 {
343 continue;
344 }
345
346 xs.sort_unstable();
347 for pair in xs.chunks_exact(2) {
348 rasops.draw_line(
349 PixelsXY::new(pair[0].clamped_into(), y.clamped_into()),
350 PixelsXY::new(pair[1].clamped_into(), y.clamped_into()),
351 )?;
352 }
353 }
354
355 Ok(())
356}
357
358pub fn draw_poly_filled<R>(rasops: &mut R, points: &[PixelsXY]) -> io::Result<()>
360where
361 R: RasterOps + ?Sized,
362{
363 fill_polygon(rasops, points)?;
364 draw_poly(rasops, points)
365}
366
367pub fn draw_rect<R>(rasops: &mut R, x1y1: PixelsXY, size: SizeInPixels) -> io::Result<()>
369where
370 R: RasterOps,
371{
372 let x2y2 = PixelsXY {
373 x: (i32::from(x1y1.x) + i32::from(size.width - 1)).clamped_into(),
374 y: (i32::from(x1y1.y) + i32::from(size.height - 1)).clamped_into(),
375 };
376 rasops.draw_line(PixelsXY { x: x1y1.x, y: x1y1.y }, PixelsXY { x: x2y2.x, y: x1y1.y })?;
377 rasops.draw_line(PixelsXY { x: x2y2.x, y: x1y1.y }, PixelsXY { x: x2y2.x, y: x2y2.y })?;
378 rasops.draw_line(PixelsXY { x: x2y2.x, y: x2y2.y }, PixelsXY { x: x1y1.x, y: x2y2.y })?;
379 rasops.draw_line(PixelsXY { x: x1y1.x, y: x2y2.y }, PixelsXY { x: x1y1.x, y: x1y1.y })?;
380 Ok(())
381}
382
383fn draw_char<R>(rasops: &mut R, font: &Font, pos: PixelsXY, ch: char) -> io::Result<()>
385where
386 R: RasterOps,
387{
388 let glyph = font.glyph(ch);
389 for j in 0..font.glyph_size.height {
390 for k in 0..font.stride {
391 let row = glyph[j * font.stride + k];
392 let mut mask = 0x80;
393 for i in 0..font.glyph_size.width {
394 let bit = row & mask;
395 if bit != 0 {
396 let x = pos.x + i as i16 + k as i16 * 8;
400 let y = pos.y + j as i16;
401 rasops.draw_pixel(PixelsXY { x, y })?;
402 }
403 mask >>= 1;
404 }
405 }
406 }
407 Ok(())
408}
409
410pub fn draw_text<R>(rasops: &mut R, font: &Font, xy: PixelsXY, text: &str) -> io::Result<()>
412where
413 R: RasterOps,
414{
415 let mut pos = xy;
416 for ch in text.chars() {
417 draw_char(rasops, font, pos, ch)?;
418 pos.x += i16::try_from(font.glyph_size.width).expect("Must fit");
419 }
420 Ok(())
421}
422
423pub fn draw_tri<R>(rasops: &mut R, x1y1: PixelsXY, x2y2: PixelsXY, x3y3: PixelsXY) -> io::Result<()>
425where
426 R: RasterOps,
427{
428 rasops.draw_line(x1y1, x2y2)?;
429 rasops.draw_line(x2y2, x3y3)?;
430 rasops.draw_line(x3y3, x1y1)?;
431 Ok(())
432}
433
434pub fn draw_tri_filled<R>(
436 rasops: &mut R,
437 x1y1: PixelsXY,
438 x2y2: PixelsXY,
439 x3y3: PixelsXY,
440) -> io::Result<()>
441where
442 R: RasterOps,
443{
444 fill_polygon(rasops, &[x1y1, x2y2, x3y3])?;
445 draw_tri(rasops, x1y1, x2y2, x3y3)
446}
447
448#[cfg(test)]
449mod testutils {
450 use super::*;
451 use crate::console::graphics::RasterInfo;
452 use crate::console::{RGB, SizeInPixels, rgb_to_ansi_color};
453 use std::collections::HashMap;
454
455 #[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
457 pub(crate) enum CapturedRasop {
458 DrawLine(i16, i16, i16, i16),
459 DrawPixel(i16, i16),
460 }
461
462 #[derive(Default)]
467 pub(crate) struct RecordingRasops {
468 pub(crate) ops: Vec<CapturedRasop>,
469 pub(crate) draw_color: Option<u8>,
470 pub(crate) pixels: HashMap<PixelsXY, Option<u8>>,
471 }
472
473 impl RecordingRasops {
474 pub(crate) fn set_pixel(&mut self, xy: PixelsXY, color: Option<u8>) {
475 self.pixels.insert(xy, color);
476 }
477 }
478
479 impl RasterOps for RecordingRasops {
480 type ID = u8;
481
482 fn get_info(&self) -> RasterInfo {
483 unimplemented!();
484 }
485
486 fn set_draw_color(&mut self, color: RGB) {
487 self.draw_color = rgb_to_ansi_color(color);
488 }
489
490 fn clear(&mut self) -> io::Result<()> {
491 unimplemented!();
492 }
493
494 fn set_sync(&mut self, _enabled: bool) {
495 unimplemented!();
496 }
497
498 fn present_canvas(&mut self) -> io::Result<()> {
499 unimplemented!();
500 }
501
502 fn peek_pixel(&self, xy: PixelsXY) -> io::Result<Option<u8>> {
503 Ok(self.pixels.get(&xy).copied().unwrap_or(None))
504 }
505
506 fn read_pixels(&mut self, _xy: PixelsXY, _size: SizeInPixels) -> io::Result<Self::ID> {
507 unimplemented!();
508 }
509
510 fn put_pixels(&mut self, _xy: PixelsXY, _data: &Self::ID) -> io::Result<()> {
511 unimplemented!();
512 }
513
514 fn move_pixels(
515 &mut self,
516 _x1y1: PixelsXY,
517 _x2y2: PixelsXY,
518 _size: SizeInPixels,
519 ) -> io::Result<()> {
520 unimplemented!();
521 }
522
523 fn write_text(&mut self, _xy: PixelsXY, _text: &str) -> io::Result<()> {
524 unimplemented!();
525 }
526
527 fn draw_circle(&mut self, _center: PixelsXY, _radius: u16) -> io::Result<()> {
528 unimplemented!();
529 }
530
531 fn draw_circle_filled(&mut self, _center: PixelsXY, _radius: u16) -> io::Result<()> {
532 unimplemented!();
533 }
534
535 fn draw_line(&mut self, x1y1: PixelsXY, x2y2: PixelsXY) -> io::Result<()> {
536 self.ops.push(CapturedRasop::DrawLine(x1y1.x, x1y1.y, x2y2.x, x2y2.y));
537 Ok(())
538 }
539
540 fn draw_pixel(&mut self, xy: PixelsXY) -> io::Result<()> {
541 self.ops.push(CapturedRasop::DrawPixel(xy.x, xy.y));
542 self.set_pixel(xy, self.draw_color);
543 Ok(())
544 }
545
546 fn draw_poly(&mut self, points: &[PixelsXY]) -> io::Result<()> {
547 draw_poly(self, points)
548 }
549
550 fn draw_poly_filled(&mut self, points: &[PixelsXY]) -> io::Result<()> {
551 draw_poly_filled(self, points)
552 }
553
554 fn draw_rect(&mut self, _xy: PixelsXY, _size: SizeInPixels) -> io::Result<()> {
555 unimplemented!();
556 }
557
558 fn draw_rect_filled(&mut self, _xy: PixelsXY, _size: SizeInPixels) -> io::Result<()> {
559 unimplemented!();
560 }
561
562 fn draw_tri(
563 &mut self,
564 _x1y1: PixelsXY,
565 _x2y2: PixelsXY,
566 _x3y3: PixelsXY,
567 ) -> io::Result<()> {
568 unimplemented!();
569 }
570
571 fn draw_tri_filled(
572 &mut self,
573 _x1y1: PixelsXY,
574 _x2y2: PixelsXY,
575 _x3y3: PixelsXY,
576 ) -> io::Result<()> {
577 unimplemented!();
578 }
579 }
580}
581
582#[cfg(test)]
583mod tests {
584 use super::testutils::*;
585 use super::*;
586 use crate::console::ansi_color_to_rgb;
587
588 #[test]
589 fn test_bucket_fill_connected_region() {
590 let mut rasops = RecordingRasops::default();
591 rasops.set_draw_color(ansi_color_to_rgb(7));
592 for xy in [
593 PixelsXY::new(0, 0),
594 PixelsXY::new(1, 0),
595 PixelsXY::new(0, 1),
596 PixelsXY::new(2, 1),
597 PixelsXY::new(1, 2),
598 ] {
599 rasops.set_pixel(xy, Some(2));
600 }
601
602 bucket_fill(&mut rasops, PixelsXY::new(0, 0), 7).unwrap();
603
604 rasops.ops.sort();
605 assert_eq!(
606 [
607 CapturedRasop::DrawPixel(0, 0),
608 CapturedRasop::DrawPixel(0, 1),
609 CapturedRasop::DrawPixel(1, 0),
610 ],
611 rasops.ops.as_slice()
612 );
613 }
614
615 #[test]
616 fn test_bucket_fill_diagonal_is_not_connected() {
617 let mut rasops = RecordingRasops::default();
618 rasops.set_draw_color(ansi_color_to_rgb(7));
619 rasops.set_pixel(PixelsXY::new(0, 0), Some(2));
620 rasops.set_pixel(PixelsXY::new(1, 1), Some(2));
621
622 bucket_fill(&mut rasops, PixelsXY::new(0, 0), 7).unwrap();
623
624 assert_eq!([CapturedRasop::DrawPixel(0, 0)], rasops.ops.as_slice());
625 assert_eq!(Some(2), rasops.peek_pixel(PixelsXY::new(1, 1)).unwrap());
626 }
627
628 #[test]
629 fn test_bucket_fill_out_of_bounds_is_noop() {
630 let mut rasops = RecordingRasops::default();
631 rasops.set_draw_color(ansi_color_to_rgb(7));
632 bucket_fill(&mut rasops, PixelsXY::new(10, 20), 7).unwrap();
633 assert!(rasops.ops.is_empty());
634 }
635
636 #[test]
637 fn test_bucket_fill_same_color_is_noop() {
638 let mut rasops = RecordingRasops::default();
639 rasops.set_draw_color(ansi_color_to_rgb(7));
640 rasops.set_pixel(PixelsXY::new(10, 20), Some(7));
641
642 bucket_fill(&mut rasops, PixelsXY::new(10, 20), 7).unwrap();
643
644 assert!(rasops.ops.is_empty());
645 }
646
647 #[test]
648 fn test_draw_circle_zero() {
649 let mut rasops = RecordingRasops::default();
650 draw_circle(&mut rasops, PixelsXY::new(10, 20), 0).unwrap();
651 assert!(rasops.ops.is_empty());
652 }
653
654 #[test]
655 fn test_draw_circle_dot() {
656 let mut rasops = RecordingRasops::default();
657 draw_circle(&mut rasops, PixelsXY::new(10, 20), 1).unwrap();
658 assert_eq!([CapturedRasop::DrawPixel(10, 20)], rasops.ops.as_slice());
659 }
660
661 #[test]
662 fn test_draw_circle_larger() {
663 let mut rasops = RecordingRasops::default();
664 draw_circle(&mut rasops, PixelsXY::new(10, 20), 4).unwrap();
665 rasops.ops.sort();
666 assert_eq!(
667 [
668 CapturedRasop::DrawPixel(7, 18),
669 CapturedRasop::DrawPixel(7, 19),
670 CapturedRasop::DrawPixel(7, 20),
671 CapturedRasop::DrawPixel(7, 20),
672 CapturedRasop::DrawPixel(7, 21),
673 CapturedRasop::DrawPixel(7, 22),
674 CapturedRasop::DrawPixel(8, 17),
675 CapturedRasop::DrawPixel(8, 23),
676 CapturedRasop::DrawPixel(9, 17),
677 CapturedRasop::DrawPixel(9, 23),
678 CapturedRasop::DrawPixel(10, 17),
679 CapturedRasop::DrawPixel(10, 17),
680 CapturedRasop::DrawPixel(10, 23),
681 CapturedRasop::DrawPixel(10, 23),
682 CapturedRasop::DrawPixel(11, 17),
683 CapturedRasop::DrawPixel(11, 23),
684 CapturedRasop::DrawPixel(12, 17),
685 CapturedRasop::DrawPixel(12, 23),
686 CapturedRasop::DrawPixel(13, 18),
687 CapturedRasop::DrawPixel(13, 19),
688 CapturedRasop::DrawPixel(13, 20),
689 CapturedRasop::DrawPixel(13, 20),
690 CapturedRasop::DrawPixel(13, 21),
691 CapturedRasop::DrawPixel(13, 22),
692 ],
693 rasops.ops.as_slice()
694 );
695 }
696
697 #[test]
698 fn test_draw_circle_corners() {
699 for corner in
700 [PixelsXY::TOP_LEFT, PixelsXY::TOP_RIGHT, PixelsXY::BOTTOM_LEFT, PixelsXY::BOTTOM_RIGHT]
701 {
702 let mut rasops = RecordingRasops::default();
703 draw_circle(&mut rasops, corner, 1).unwrap();
704 assert_eq!([CapturedRasop::DrawPixel(corner.x, corner.y)], rasops.ops.as_slice());
705 }
706 }
707
708 #[test]
709 fn test_draw_circle_filled_zero() {
710 let mut rasops = RecordingRasops::default();
711 draw_circle_filled(&mut rasops, PixelsXY::new(10, 20), 0).unwrap();
712 assert!(rasops.ops.is_empty());
713 }
714
715 #[test]
716 fn test_draw_circle_filled_dot() {
717 let mut rasops = RecordingRasops::default();
718 draw_circle_filled(&mut rasops, PixelsXY::new(10, 20), 1).unwrap();
719 assert_eq!([CapturedRasop::DrawPixel(10, 20)], rasops.ops.as_slice());
720 }
721
722 #[test]
723 fn test_draw_circle_filled_larger() {
724 let mut rasops = RecordingRasops::default();
725 draw_circle_filled(&mut rasops, PixelsXY::new(10, 20), 4).unwrap();
726 rasops.ops.sort();
727 assert_eq!(
728 [
729 CapturedRasop::DrawLine(7, 18, 7, 22),
730 CapturedRasop::DrawLine(7, 19, 7, 21),
731 CapturedRasop::DrawLine(7, 20, 7, 20),
732 CapturedRasop::DrawLine(8, 17, 8, 23),
733 CapturedRasop::DrawLine(9, 17, 9, 23),
734 CapturedRasop::DrawLine(10, 17, 10, 23),
735 CapturedRasop::DrawLine(10, 17, 10, 23),
736 CapturedRasop::DrawLine(11, 17, 11, 23),
737 CapturedRasop::DrawLine(12, 17, 12, 23),
738 CapturedRasop::DrawLine(13, 18, 13, 22),
739 CapturedRasop::DrawLine(13, 19, 13, 21),
740 CapturedRasop::DrawLine(13, 20, 13, 20),
741 ],
742 rasops.ops.as_slice()
743 );
744 }
745
746 #[test]
747 fn test_draw_circle_filled_corners() {
748 for corner in
749 [PixelsXY::TOP_LEFT, PixelsXY::TOP_RIGHT, PixelsXY::BOTTOM_LEFT, PixelsXY::BOTTOM_RIGHT]
750 {
751 let mut rasops = RecordingRasops::default();
752 draw_circle_filled(&mut rasops, corner, 1).unwrap();
753 assert_eq!([CapturedRasop::DrawPixel(corner.x, corner.y)], rasops.ops.as_slice());
754 }
755 }
756
757 #[test]
758 fn test_draw_line_dot() {
759 let mut rasops = RecordingRasops::default();
760 draw_line(&mut rasops, PixelsXY::new(10, 20), PixelsXY::new(10, 20)).unwrap();
761 assert_eq!([CapturedRasop::DrawPixel(10, 20)], rasops.ops.as_slice());
762 }
763
764 #[test]
765 fn test_draw_line_horizontal_right() {
766 let mut rasops = RecordingRasops::default();
767 draw_line(&mut rasops, PixelsXY::new(100, 0), PixelsXY::new(105, 0)).unwrap();
768 assert_eq!(
769 [
770 CapturedRasop::DrawPixel(100, 0),
771 CapturedRasop::DrawPixel(101, 0),
772 CapturedRasop::DrawPixel(102, 0),
773 CapturedRasop::DrawPixel(103, 0),
774 CapturedRasop::DrawPixel(104, 0),
775 CapturedRasop::DrawPixel(105, 0),
776 ],
777 rasops.ops.as_slice()
778 );
779 }
780
781 #[test]
782 fn test_draw_line_horizontal_left() {
783 let mut rasops = RecordingRasops::default();
784 draw_line(&mut rasops, PixelsXY::new(105, 0), PixelsXY::new(100, 0)).unwrap();
785 assert_eq!(
786 [
787 CapturedRasop::DrawPixel(100, 0),
788 CapturedRasop::DrawPixel(101, 0),
789 CapturedRasop::DrawPixel(102, 0),
790 CapturedRasop::DrawPixel(103, 0),
791 CapturedRasop::DrawPixel(104, 0),
792 CapturedRasop::DrawPixel(105, 0),
793 ],
794 rasops.ops.as_slice()
795 );
796 }
797
798 #[test]
799 fn test_draw_line_vertical_down() {
800 let mut rasops = RecordingRasops::default();
801 draw_line(&mut rasops, PixelsXY::new(0, 100), PixelsXY::new(0, 105)).unwrap();
802 assert_eq!(
803 [
804 CapturedRasop::DrawPixel(0, 100),
805 CapturedRasop::DrawPixel(0, 101),
806 CapturedRasop::DrawPixel(0, 102),
807 CapturedRasop::DrawPixel(0, 103),
808 CapturedRasop::DrawPixel(0, 104),
809 CapturedRasop::DrawPixel(0, 105),
810 ],
811 rasops.ops.as_slice()
812 );
813 }
814
815 #[test]
816 fn test_draw_line_vertical_up() {
817 let mut rasops = RecordingRasops::default();
818 draw_line(&mut rasops, PixelsXY::new(0, 105), PixelsXY::new(0, 100)).unwrap();
819 assert_eq!(
820 [
821 CapturedRasop::DrawPixel(0, 100),
822 CapturedRasop::DrawPixel(0, 101),
823 CapturedRasop::DrawPixel(0, 102),
824 CapturedRasop::DrawPixel(0, 103),
825 CapturedRasop::DrawPixel(0, 104),
826 CapturedRasop::DrawPixel(0, 105),
827 ],
828 rasops.ops.as_slice()
829 );
830 }
831
832 #[test]
833 fn test_draw_line_horizontal_slope_right() {
834 let mut rasops = RecordingRasops::default();
835 draw_line(&mut rasops, PixelsXY::new(100, 0), PixelsXY::new(105, 3)).unwrap();
836 assert_eq!(
837 [
838 CapturedRasop::DrawPixel(100, 0),
839 CapturedRasop::DrawPixel(101, 1),
840 CapturedRasop::DrawPixel(102, 1),
841 CapturedRasop::DrawPixel(103, 2),
842 CapturedRasop::DrawPixel(104, 2),
843 CapturedRasop::DrawPixel(105, 3),
844 ],
845 rasops.ops.as_slice()
846 );
847 }
848
849 #[test]
850 fn test_draw_line_horizontal_slope_left() {
851 let mut rasops = RecordingRasops::default();
852 draw_line(&mut rasops, PixelsXY::new(105, 0), PixelsXY::new(100, 3)).unwrap();
853 assert_eq!(
854 [
855 CapturedRasop::DrawPixel(100, 3),
856 CapturedRasop::DrawPixel(101, 2),
857 CapturedRasop::DrawPixel(102, 2),
858 CapturedRasop::DrawPixel(103, 1),
859 CapturedRasop::DrawPixel(104, 1),
860 CapturedRasop::DrawPixel(105, 0),
861 ],
862 rasops.ops.as_slice()
863 );
864 }
865
866 #[test]
867 fn test_draw_line_vertical_slope_up() {
868 let mut rasops = RecordingRasops::default();
869 draw_line(&mut rasops, PixelsXY::new(0, 100), PixelsXY::new(3, 105)).unwrap();
870 assert_eq!(
871 [
872 CapturedRasop::DrawPixel(0, 100),
873 CapturedRasop::DrawPixel(1, 101),
874 CapturedRasop::DrawPixel(1, 102),
875 CapturedRasop::DrawPixel(2, 103),
876 CapturedRasop::DrawPixel(2, 104),
877 CapturedRasop::DrawPixel(3, 105),
878 ],
879 rasops.ops.as_slice()
880 );
881 }
882
883 #[test]
884 fn test_draw_line_vertical_slope_down() {
885 let mut rasops = RecordingRasops::default();
886 draw_line(&mut rasops, PixelsXY::new(0, 105), PixelsXY::new(3, 100)).unwrap();
887 assert_eq!(
888 [
889 CapturedRasop::DrawPixel(3, 100),
890 CapturedRasop::DrawPixel(2, 101),
891 CapturedRasop::DrawPixel(2, 102),
892 CapturedRasop::DrawPixel(1, 103),
893 CapturedRasop::DrawPixel(1, 104),
894 CapturedRasop::DrawPixel(0, 105),
895 ],
896 rasops.ops.as_slice()
897 );
898 }
899
900 #[test]
901 fn test_draw_line_corners() {
902 for corner in
903 [PixelsXY::TOP_LEFT, PixelsXY::TOP_RIGHT, PixelsXY::BOTTOM_LEFT, PixelsXY::BOTTOM_RIGHT]
904 {
905 let mut rasops = RecordingRasops::default();
906 draw_line(&mut rasops, corner, corner).unwrap();
907 assert_eq!([CapturedRasop::DrawPixel(corner.x, corner.y)], rasops.ops.as_slice());
908 }
909 }
910
911 #[test]
912 fn test_draw_line_perimeter() {
913 for corners in [
914 (PixelsXY::TOP_LEFT, PixelsXY::TOP_RIGHT),
915 (PixelsXY::TOP_RIGHT, PixelsXY::BOTTOM_RIGHT),
916 (PixelsXY::BOTTOM_RIGHT, PixelsXY::BOTTOM_LEFT),
917 (PixelsXY::BOTTOM_LEFT, PixelsXY::TOP_LEFT),
918 ] {
919 let mut rasops = RecordingRasops::default();
920 draw_line(&mut rasops, corners.0, corners.1).unwrap();
921 assert_eq!(usize::from(u16::MAX) + 1, rasops.ops.len());
922 }
923 }
924
925 #[test]
926 fn test_draw_line_diagonals() {
927 for corners in [
928 (PixelsXY::TOP_LEFT, PixelsXY::BOTTOM_RIGHT),
929 (PixelsXY::BOTTOM_RIGHT, PixelsXY::TOP_LEFT),
930 (PixelsXY::TOP_RIGHT, PixelsXY::BOTTOM_LEFT),
931 (PixelsXY::BOTTOM_LEFT, PixelsXY::TOP_RIGHT),
932 ] {
933 let mut rasops = RecordingRasops::default();
934 draw_line(&mut rasops, corners.0, corners.1).unwrap();
935 assert_eq!(usize::from(u16::MAX) + 1, rasops.ops.len());
936 }
937 }
938
939 #[test]
940 fn test_draw_poly() {
941 let mut rasops = RecordingRasops::default();
942 draw_poly(
943 &mut rasops,
944 &[
945 PixelsXY::new(10, 20),
946 PixelsXY::new(20, 20),
947 PixelsXY::new(15, 30),
948 PixelsXY::new(8, 24),
949 ],
950 )
951 .unwrap();
952 assert_eq!(
953 [
954 CapturedRasop::DrawLine(10, 20, 20, 20),
955 CapturedRasop::DrawLine(20, 20, 15, 30),
956 CapturedRasop::DrawLine(15, 30, 8, 24),
957 CapturedRasop::DrawLine(8, 24, 10, 20),
958 ],
959 rasops.ops.as_slice()
960 );
961 }
962
963 #[test]
964 fn test_draw_poly_filled() {
965 let mut rasops = RecordingRasops::default();
966 draw_poly_filled(
967 &mut rasops,
968 &[
969 PixelsXY::new(10, 20),
970 PixelsXY::new(20, 20),
971 PixelsXY::new(18, 24),
972 PixelsXY::new(12, 24),
973 ],
974 )
975 .unwrap();
976 assert_eq!(
977 [
978 CapturedRasop::DrawLine(10, 20, 20, 20),
979 CapturedRasop::DrawLine(11, 21, 20, 21),
980 CapturedRasop::DrawLine(11, 22, 19, 22),
981 CapturedRasop::DrawLine(12, 23, 19, 23),
982 CapturedRasop::DrawLine(10, 20, 20, 20),
983 CapturedRasop::DrawLine(20, 20, 18, 24),
984 CapturedRasop::DrawLine(18, 24, 12, 24),
985 CapturedRasop::DrawLine(12, 24, 10, 20),
986 ],
987 rasops.ops.as_slice()
988 );
989 }
990
991 #[test]
992 fn test_draw_poly_filled_concave() {
993 let mut rasops = RecordingRasops::default();
994 draw_poly_filled(
995 &mut rasops,
996 &[
997 PixelsXY::new(10, 20),
998 PixelsXY::new(20, 20),
999 PixelsXY::new(20, 24),
1000 PixelsXY::new(15, 22),
1001 PixelsXY::new(10, 24),
1002 ],
1003 )
1004 .unwrap();
1005 assert_eq!(
1006 [
1007 CapturedRasop::DrawLine(10, 20, 20, 20),
1008 CapturedRasop::DrawLine(10, 21, 20, 21),
1009 CapturedRasop::DrawLine(10, 22, 15, 22),
1010 CapturedRasop::DrawLine(15, 22, 20, 22),
1011 CapturedRasop::DrawLine(10, 23, 13, 23),
1012 CapturedRasop::DrawLine(18, 23, 20, 23),
1013 CapturedRasop::DrawLine(10, 20, 20, 20),
1014 CapturedRasop::DrawLine(20, 20, 20, 24),
1015 CapturedRasop::DrawLine(20, 24, 15, 22),
1016 CapturedRasop::DrawLine(15, 22, 10, 24),
1017 CapturedRasop::DrawLine(10, 24, 10, 20),
1018 ],
1019 rasops.ops.as_slice()
1020 );
1021 }
1022
1023 #[test]
1024 fn test_draw_rect_dot() {
1025 let mut rasops = RecordingRasops::default();
1026 draw_rect(&mut rasops, PixelsXY::new(10, 20), SizeInPixels::new(1, 1)).unwrap();
1027 assert_eq!(
1028 [
1029 CapturedRasop::DrawLine(10, 20, 10, 20),
1030 CapturedRasop::DrawLine(10, 20, 10, 20),
1031 CapturedRasop::DrawLine(10, 20, 10, 20),
1032 CapturedRasop::DrawLine(10, 20, 10, 20),
1033 ],
1034 rasops.ops.as_slice()
1035 );
1036 }
1037
1038 #[test]
1039 fn test_draw_rect_horizontal_line() {
1040 let mut rasops = RecordingRasops::default();
1041 draw_rect(&mut rasops, PixelsXY::new(10, 20), SizeInPixels::new(6, 1)).unwrap();
1042 assert_eq!(
1043 [
1044 CapturedRasop::DrawLine(10, 20, 15, 20),
1045 CapturedRasop::DrawLine(15, 20, 15, 20),
1046 CapturedRasop::DrawLine(15, 20, 10, 20),
1047 CapturedRasop::DrawLine(10, 20, 10, 20),
1048 ],
1049 rasops.ops.as_slice()
1050 );
1051 }
1052
1053 #[test]
1054 fn test_draw_rect_vertical_line() {
1055 let mut rasops = RecordingRasops::default();
1056 draw_rect(&mut rasops, PixelsXY::new(10, 20), SizeInPixels::new(1, 6)).unwrap();
1057 assert_eq!(
1058 [
1059 CapturedRasop::DrawLine(10, 20, 10, 20),
1060 CapturedRasop::DrawLine(10, 20, 10, 25),
1061 CapturedRasop::DrawLine(10, 25, 10, 25),
1062 CapturedRasop::DrawLine(10, 25, 10, 20),
1063 ],
1064 rasops.ops.as_slice()
1065 );
1066 }
1067
1068 #[test]
1069 fn test_draw_rect_2d() {
1070 let mut rasops = RecordingRasops::default();
1071 draw_rect(&mut rasops, PixelsXY::new(10, 20), SizeInPixels::new(6, 7)).unwrap();
1072 assert_eq!(
1073 [
1074 CapturedRasop::DrawLine(10, 20, 15, 20),
1075 CapturedRasop::DrawLine(15, 20, 15, 26),
1076 CapturedRasop::DrawLine(15, 26, 10, 26),
1077 CapturedRasop::DrawLine(10, 26, 10, 20),
1078 ],
1079 rasops.ops.as_slice()
1080 );
1081 }
1082
1083 #[test]
1084 fn test_draw_rect_corners() {
1085 let mut rasops = RecordingRasops::default();
1086 draw_rect(&mut rasops, PixelsXY::TOP_LEFT, SizeInPixels::MAX).unwrap();
1087 assert_eq!(
1088 [
1089 CapturedRasop::DrawLine(i16::MIN, i16::MIN, i16::MAX - 1, i16::MIN),
1090 CapturedRasop::DrawLine(i16::MAX - 1, i16::MIN, i16::MAX - 1, i16::MAX - 1),
1091 CapturedRasop::DrawLine(i16::MAX - 1, i16::MAX - 1, i16::MIN, i16::MAX - 1),
1092 CapturedRasop::DrawLine(i16::MIN, i16::MAX - 1, i16::MIN, i16::MIN),
1093 ],
1094 rasops.ops.as_slice()
1095 );
1096 }
1097
1098 #[test]
1099 fn test_draw_text_empty() {
1100 let mut rasops = RecordingRasops::default();
1101 draw_text(&mut rasops, &crate::gfx::lcd::fonts::FONT_5X8, PixelsXY::new(10, 20), "")
1102 .unwrap();
1103 assert!(rasops.ops.is_empty());
1104 }
1105
1106 #[test]
1107 fn test_draw_text_one_char() {
1108 let mut rasops = RecordingRasops::default();
1109 draw_text(&mut rasops, &crate::gfx::lcd::fonts::FONT_5X8, PixelsXY::new(10, 20), "!")
1110 .unwrap();
1111 assert_eq!(
1112 [
1113 CapturedRasop::DrawPixel(12, 20),
1114 CapturedRasop::DrawPixel(12, 21),
1115 CapturedRasop::DrawPixel(12, 22),
1116 CapturedRasop::DrawPixel(12, 23),
1117 CapturedRasop::DrawPixel(12, 25),
1118 ],
1119 rasops.ops.as_slice()
1120 );
1121 }
1122
1123 #[test]
1124 fn test_draw_text_two_chars() {
1125 let mut rasops = RecordingRasops::default();
1126 draw_text(&mut rasops, &crate::gfx::lcd::fonts::FONT_5X8, PixelsXY::new(10, 20), "!!")
1127 .unwrap();
1128 assert_eq!(
1129 [
1130 CapturedRasop::DrawPixel(12, 20),
1131 CapturedRasop::DrawPixel(12, 21),
1132 CapturedRasop::DrawPixel(12, 22),
1133 CapturedRasop::DrawPixel(12, 23),
1134 CapturedRasop::DrawPixel(12, 25),
1135 CapturedRasop::DrawPixel(17, 20),
1136 CapturedRasop::DrawPixel(17, 21),
1137 CapturedRasop::DrawPixel(17, 22),
1138 CapturedRasop::DrawPixel(17, 23),
1139 CapturedRasop::DrawPixel(17, 25),
1140 ],
1141 rasops.ops.as_slice()
1142 );
1143 }
1144
1145 #[test]
1146 fn test_draw_tri() {
1147 let mut rasops = RecordingRasops::default();
1148 draw_tri(&mut rasops, PixelsXY::new(10, 20), PixelsXY::new(20, 20), PixelsXY::new(15, 30))
1149 .unwrap();
1150 assert_eq!(
1151 [
1152 CapturedRasop::DrawLine(10, 20, 20, 20),
1153 CapturedRasop::DrawLine(20, 20, 15, 30),
1154 CapturedRasop::DrawLine(15, 30, 10, 20),
1155 ],
1156 rasops.ops.as_slice()
1157 );
1158 }
1159
1160 #[test]
1161 fn test_draw_tri_filled() {
1162 let mut rasops = RecordingRasops::default();
1163 draw_tri_filled(
1164 &mut rasops,
1165 PixelsXY::new(10, 20),
1166 PixelsXY::new(20, 20),
1167 PixelsXY::new(15, 24),
1168 )
1169 .unwrap();
1170 assert_eq!(
1171 [
1172 CapturedRasop::DrawLine(10, 20, 20, 20),
1173 CapturedRasop::DrawLine(12, 21, 19, 21),
1174 CapturedRasop::DrawLine(13, 22, 18, 22),
1175 CapturedRasop::DrawLine(14, 23, 17, 23),
1176 CapturedRasop::DrawLine(10, 20, 20, 20),
1177 CapturedRasop::DrawLine(20, 20, 15, 24),
1178 CapturedRasop::DrawLine(15, 24, 10, 20),
1179 ],
1180 rasops.ops.as_slice()
1181 );
1182 }
1183}