1use embassy_time::{Duration, Timer};
9use embedded_graphics::{
10 Pixel,
11 mono_font::{MonoFont, MonoTextStyle},
12 pixelcolor::Rgb565,
13 prelude::*,
14 primitives::Rectangle,
15 text::{Alignment, Text},
16};
17use embedded_graphics_simulator::{
18 OutputSettings, OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window,
19 sdl2::{Keycode, MouseButton, MouseWheelDirection},
20};
21use std::{convert::Infallible, vec::Vec};
22use tiny_skia::{
23 Color as SkColor, FillRule, Mask, Paint, PathBuilder, Pixmap, Rect as SkRect, Stroke, Transform,
24};
25use zest_core::{
26 ButtonState, DirtyRegion, EncoderEvent, InputEvent, Key, KeyEvent, Platform,
27 PlatformCapabilities, RenderError, Renderer, TouchEvent, TouchPhase,
28};
29
30pub const DEFAULT_WIDTH: u32 = 320;
32pub const DEFAULT_HEIGHT: u32 = 240;
34pub const DEFAULT_SCALE: u32 = 2;
36pub const DEFAULT_PIXEL_SPACING: u32 = 0;
39pub const DEFAULT_POLL_MS: u64 = 16;
41
42pub struct SimulatorPlatformBuilder {
44 title: String,
45 size: Size,
46 scale: u32,
47 pixel_spacing: u32,
48 poll_ms: u64,
49 show_dirty: bool,
50}
51
52impl SimulatorPlatformBuilder {
53 #[must_use]
55 pub fn new(title: impl Into<String>) -> Self {
56 Self {
57 title: title.into(),
58 size: Size::new(DEFAULT_WIDTH, DEFAULT_HEIGHT),
59 scale: DEFAULT_SCALE,
60 pixel_spacing: DEFAULT_PIXEL_SPACING,
61 poll_ms: DEFAULT_POLL_MS,
62 show_dirty: false,
63 }
64 }
65
66 #[must_use]
68 pub fn size(mut self, size: Size) -> Self {
69 self.size = size;
70 self
71 }
72
73 #[must_use]
75 pub fn scale(mut self, scale: u32) -> Self {
76 self.scale = scale;
77 self
78 }
79
80 #[must_use]
87 pub fn pixel_spacing(mut self, pixel_spacing: u32) -> Self {
88 self.pixel_spacing = pixel_spacing;
89 self
90 }
91
92 #[must_use]
94 pub fn poll_ms(mut self, poll_ms: u64) -> Self {
95 self.poll_ms = poll_ms;
96 self
97 }
98
99 #[must_use]
101 pub fn show_dirty(mut self, show_dirty: bool) -> Self {
102 self.show_dirty = show_dirty;
103 self
104 }
105
106 #[must_use]
108 pub fn build(self) -> SimulatorPlatform {
109 let settings: OutputSettings = OutputSettingsBuilder::new()
110 .scale(self.scale)
111 .pixel_spacing(self.pixel_spacing)
112 .build();
113 let pixmap = Pixmap::new(self.size.width, self.size.height).expect("non-zero pixmap size");
114 SimulatorPlatform {
115 display: SimulatorDisplay::new(self.size),
116 window: Window::new(&self.title, &settings),
117 pixmap,
118 size: self.size,
119 mouse_down: false,
120 poll_ms: self.poll_ms,
121 show_dirty: self.show_dirty,
122 pending: None,
123 }
124 }
125}
126
127pub struct SimulatorPlatform {
129 display: SimulatorDisplay<Rgb565>,
130 window: Window,
131 pixmap: Pixmap,
132 size: Size,
133 mouse_down: bool,
134 poll_ms: u64,
135 show_dirty: bool,
136 pending: Option<InputEvent>,
140}
141
142impl SimulatorPlatform {
143 #[must_use]
145 pub fn new(title: impl Into<String>) -> Self {
146 SimulatorPlatformBuilder::new(title).build()
147 }
148
149 #[must_use]
151 pub fn builder(title: impl Into<String>) -> SimulatorPlatformBuilder {
152 SimulatorPlatformBuilder::new(title)
153 }
154
155 fn key_event(keycode: Keycode, repeat: bool, pressed: bool) -> Option<InputEvent> {
156 let state = if pressed {
157 if repeat {
158 ButtonState::Repeated
159 } else {
160 ButtonState::Pressed
161 }
162 } else {
163 ButtonState::Released
164 };
165
166 let key = match keycode {
167 Keycode::Return | Keycode::KpEnter => Key::Enter,
168 Keycode::Tab => Key::Tab,
169 Keycode::Backspace => Key::Backspace,
170 Keycode::Delete => Key::Delete,
171 Keycode::Left => Key::Left,
172 Keycode::Right => Key::Right,
173 Keycode::Up => Key::Up,
174 Keycode::Down => Key::Down,
175 Keycode::Home => Key::Home,
176 Keycode::End => Key::End,
177 Keycode::PageUp => Key::PageUp,
178 Keycode::PageDown => Key::PageDown,
179 Keycode::Space => Key::Char(' '),
180 Keycode::A => Key::Char('a'),
181 Keycode::B => Key::Char('b'),
182 Keycode::C => Key::Char('c'),
183 Keycode::D => Key::Char('d'),
184 Keycode::E => Key::Char('e'),
185 Keycode::F => Key::Char('f'),
186 Keycode::G => Key::Char('g'),
187 Keycode::H => Key::Char('h'),
188 Keycode::I => Key::Char('i'),
189 Keycode::J => Key::Char('j'),
190 Keycode::K => Key::Char('k'),
191 Keycode::L => Key::Char('l'),
192 Keycode::M => Key::Char('m'),
193 Keycode::N => Key::Char('n'),
194 Keycode::O => Key::Char('o'),
195 Keycode::P => Key::Char('p'),
196 Keycode::Q => Key::Char('q'),
197 Keycode::R => Key::Char('r'),
198 Keycode::S => Key::Char('s'),
199 Keycode::T => Key::Char('t'),
200 Keycode::U => Key::Char('u'),
201 Keycode::V => Key::Char('v'),
202 Keycode::W => Key::Char('w'),
203 Keycode::X => Key::Char('x'),
204 Keycode::Y => Key::Char('y'),
205 Keycode::Z => Key::Char('z'),
206 Keycode::Num0 | Keycode::Kp0 => Key::Char('0'),
207 Keycode::Num1 | Keycode::Kp1 => Key::Char('1'),
208 Keycode::Num2 | Keycode::Kp2 => Key::Char('2'),
209 Keycode::Num3 | Keycode::Kp3 => Key::Char('3'),
210 Keycode::Num4 | Keycode::Kp4 => Key::Char('4'),
211 Keycode::Num5 | Keycode::Kp5 => Key::Char('5'),
212 Keycode::Num6 | Keycode::Kp6 => Key::Char('6'),
213 Keycode::Num7 | Keycode::Kp7 => Key::Char('7'),
214 Keycode::Num8 | Keycode::Kp8 => Key::Char('8'),
215 Keycode::Num9 | Keycode::Kp9 => Key::Char('9'),
216 _ => return None,
217 };
218
219 Some(InputEvent::Key(KeyEvent { key, state }))
220 }
221
222 fn encoder_event(scroll_delta: Point, direction: MouseWheelDirection) -> Option<InputEvent> {
223 let axis = if scroll_delta.y != 0 {
224 scroll_delta.y
225 } else {
226 scroll_delta.x
227 };
228 if axis == 0 {
229 return None;
230 }
231
232 let delta = match direction {
233 MouseWheelDirection::Flipped => -axis,
234 _ => axis,
235 };
236 Some(InputEvent::Encoder(EncoderEvent { delta }))
237 }
238}
239
240impl Platform for SimulatorPlatform {
241 type Color = Rgb565;
242 type Error = Infallible;
243
244 async fn next_event(&mut self) -> Option<InputEvent> {
245 loop {
246 if let Some(ev) = self.pending.take() {
248 return Some(ev);
249 }
250
251 let mut latest_move: Option<Point> = None;
259 for sim_event in self.window.events() {
260 match sim_event {
261 SimulatorEvent::Quit => return None,
262 SimulatorEvent::MouseButtonDown {
263 mouse_btn: MouseButton::Left,
264 point,
265 } => {
266 self.mouse_down = true;
267 let down = InputEvent::Touch(TouchEvent {
268 phase: TouchPhase::Down,
269 point,
270 });
271 if let Some(p) = latest_move.take() {
272 self.pending = Some(down);
273 return Some(InputEvent::Touch(TouchEvent {
274 phase: TouchPhase::Moved,
275 point: p,
276 }));
277 }
278 return Some(down);
279 }
280 SimulatorEvent::MouseButtonUp {
281 mouse_btn: MouseButton::Left,
282 point,
283 } => {
284 self.mouse_down = false;
285 let up = InputEvent::Touch(TouchEvent {
286 phase: TouchPhase::Up,
287 point,
288 });
289 if let Some(p) = latest_move.take() {
290 self.pending = Some(up);
291 return Some(InputEvent::Touch(TouchEvent {
292 phase: TouchPhase::Moved,
293 point: p,
294 }));
295 }
296 return Some(up);
297 }
298 SimulatorEvent::MouseMove { point } if self.mouse_down => {
299 latest_move = Some(point);
300 }
301 SimulatorEvent::KeyDown {
302 keycode, repeat, ..
303 } => {
304 if let Some(event) = Self::key_event(keycode, repeat, true) {
305 return Some(event);
306 }
307 }
308 SimulatorEvent::KeyUp {
309 keycode, repeat, ..
310 } => {
311 if let Some(event) = Self::key_event(keycode, repeat, false) {
312 return Some(event);
313 }
314 }
315 SimulatorEvent::MouseWheel {
316 scroll_delta,
317 direction,
318 } => {
319 if let Some(event) = Self::encoder_event(scroll_delta, direction) {
320 return Some(event);
321 }
322 }
323 _ => {}
324 }
325 }
326 if let Some(point) = latest_move {
327 return Some(InputEvent::Touch(TouchEvent {
328 phase: TouchPhase::Moved,
329 point,
330 }));
331 }
332 Timer::after(Duration::from_millis(self.poll_ms)).await;
333 }
334 }
335
336 async fn render_with<F>(&mut self, draw: F) -> Result<(), Self::Error>
337 where
338 F: FnOnce(&mut dyn Renderer<Self::Color>) -> Result<(), RenderError>,
339 {
340 self.pixmap.fill(SkColor::BLACK);
343 {
344 let mut renderer = TinySkiaRenderer {
345 pixmap: &mut self.pixmap,
346 clip_mask: None,
347 clip_rect: None,
348 clip_stack: Vec::new(),
349 };
350 let _ = draw(&mut renderer);
351 }
352
353 let area = Rectangle::new(Point::zero(), self.size);
355 let data = self.pixmap.data();
356 let colors = (0..(self.size.width * self.size.height) as usize).map(|i| {
357 let off = i * 4;
358 rgba8_to_rgb565(data[off], data[off + 1], data[off + 2])
359 });
360 let _ = self.display.fill_contiguous(&area, colors);
361
362 self.window.update(&self.display);
363 Ok(())
364 }
365
366 async fn render_with_dirty<F>(
367 &mut self,
368 dirty: &DirtyRegion,
369 draw: F,
370 ) -> Result<(), Self::Error>
371 where
372 F: FnOnce(&mut dyn Renderer<Self::Color>) -> Result<(), RenderError>,
373 {
374 self.pixmap.fill(SkColor::BLACK);
375 {
376 let mut renderer = TinySkiaRenderer {
377 pixmap: &mut self.pixmap,
378 clip_mask: None,
379 clip_rect: None,
380 clip_stack: Vec::new(),
381 };
382 let _ = draw(&mut renderer);
383 }
384
385 match dirty {
386 DirtyRegion::None => {}
387 DirtyRegion::Full => self.blit_full(),
388 DirtyRegion::Rects(rects) => {
389 if self.show_dirty {
390 self.overlay_dirty(rects);
391 }
392 for rect in rects {
393 self.blit_rect(*rect);
394 }
395 }
396 }
397
398 self.window.update(&self.display);
399 Ok(())
400 }
401
402 fn viewport(&self) -> Size {
403 self.size
404 }
405
406 fn capabilities(&self) -> PlatformCapabilities {
407 PlatformCapabilities {
408 supports_clip: true,
409 supports_partial_flush: true,
410 supports_semantic_input: false,
411 prefers_full_redraw: false,
412 }
413 }
414}
415
416impl SimulatorPlatform {
417 fn blit_full(&mut self) {
418 let area = Rectangle::new(Point::zero(), self.size);
419 let data = self.pixmap.data();
420 let colors = (0..(self.size.width * self.size.height) as usize).map(|i| {
421 let off = i * 4;
422 rgba8_to_rgb565(data[off], data[off + 1], data[off + 2])
423 });
424 let _ = self.display.fill_contiguous(&area, colors);
425 }
426
427 fn blit_rect(&mut self, rect: Rectangle) {
428 let x0 = rect.top_left.x.max(0) as u32;
429 let y0 = rect.top_left.y.max(0) as u32;
430 let x1 = (rect.top_left.x + rect.size.width as i32).clamp(0, self.size.width as i32) as u32;
431 let y1 =
432 (rect.top_left.y + rect.size.height as i32).clamp(0, self.size.height as i32) as u32;
433 if x1 <= x0 || y1 <= y0 {
434 return;
435 }
436
437 let width = x1 - x0;
438 let height = y1 - y0;
439 let area = Rectangle::new(Point::new(x0 as i32, y0 as i32), Size::new(width, height));
440 let stride = self.size.width as usize * 4;
441 let data = self.pixmap.data();
442 let mut colors = Vec::with_capacity((width * height) as usize);
443 for y in y0..y1 {
444 let row = y as usize * stride;
445 for x in x0..x1 {
446 let off = row + x as usize * 4;
447 colors.push(rgba8_to_rgb565(data[off], data[off + 1], data[off + 2]));
448 }
449 }
450 let _ = self.display.fill_contiguous(&area, colors.into_iter());
451 }
452
453 fn overlay_dirty(&mut self, rects: &[Rectangle]) {
454 let mut renderer = TinySkiaRenderer {
455 pixmap: &mut self.pixmap,
456 clip_mask: None,
457 clip_rect: None,
458 clip_stack: Vec::new(),
459 };
460 for rect in rects {
461 let _ = renderer.stroke_rect(*rect, Rgb565::MAGENTA);
462 }
463 }
464}
465
466struct TinySkiaRenderer<'p> {
467 pixmap: &'p mut Pixmap,
468 clip_mask: Option<Mask>,
469 clip_rect: Option<Rectangle>,
470 clip_stack: Vec<(Option<Mask>, Option<Rectangle>)>,
471}
472
473impl<'p> Renderer<Rgb565> for TinySkiaRenderer<'p> {
474 fn fill_rect(&mut self, rect: Rectangle, color: Rgb565) -> Result<(), RenderError> {
475 let Some(sk_rect) = SkRect::from_xywh(
476 rect.top_left.x as f32,
477 rect.top_left.y as f32,
478 rect.size.width as f32,
479 rect.size.height as f32,
480 ) else {
481 return Ok(());
482 };
483 let mut paint = Paint::default();
484 paint.set_color(rgb565_to_skia(color));
485 paint.anti_alias = false;
487 self.pixmap.fill_rect(
488 sk_rect,
489 &paint,
490 Transform::identity(),
491 self.clip_mask.as_ref(),
492 );
493 Ok(())
494 }
495
496 fn stroke_rect(&mut self, rect: Rectangle, color: Rgb565) -> Result<(), RenderError> {
497 let Some(sk_rect) = SkRect::from_xywh(
498 rect.top_left.x as f32 + 0.5,
499 rect.top_left.y as f32 + 0.5,
500 rect.size.width.saturating_sub(1) as f32,
501 rect.size.height.saturating_sub(1) as f32,
502 ) else {
503 return Ok(());
504 };
505 let path = PathBuilder::from_rect(sk_rect);
506 let mut paint = Paint::default();
507 paint.set_color(rgb565_to_skia(color));
508 paint.anti_alias = false;
509 let mut stroke = Stroke::default();
510 stroke.width = 1.0;
511 self.pixmap.stroke_path(
512 &path,
513 &paint,
514 &stroke,
515 Transform::identity(),
516 self.clip_mask.as_ref(),
517 );
518 Ok(())
519 }
520
521 fn fill_circle(
522 &mut self,
523 center: Point,
524 radius: u32,
525 color: Rgb565,
526 ) -> Result<(), RenderError> {
527 if radius == 0 {
528 return Ok(());
529 }
530 let mut pb = PathBuilder::new();
531 pb.push_circle(center.x as f32 + 0.5, center.y as f32 + 0.5, radius as f32);
532 let Some(path) = pb.finish() else {
533 return Ok(());
534 };
535 let mut paint = Paint::default();
536 paint.set_color(rgb565_to_skia(color));
537 paint.anti_alias = true;
538 self.pixmap.fill_path(
539 &path,
540 &paint,
541 FillRule::Winding,
542 Transform::identity(),
543 self.clip_mask.as_ref(),
544 );
545 Ok(())
546 }
547
548 fn stroke_line(
549 &mut self,
550 start: Point,
551 end: Point,
552 color: Rgb565,
553 width: u32,
554 ) -> Result<(), RenderError> {
555 if width == 0 {
556 return Ok(());
557 }
558 let mut pb = PathBuilder::new();
559 pb.move_to(start.x as f32 + 0.5, start.y as f32 + 0.5);
560 pb.line_to(end.x as f32 + 0.5, end.y as f32 + 0.5);
561 let Some(path) = pb.finish() else {
562 return Ok(());
563 };
564 let mut paint = Paint::default();
565 paint.set_color(rgb565_to_skia(color));
566 paint.anti_alias = true;
567 let mut stroke = Stroke::default();
568 stroke.width = width as f32;
569 self.pixmap.stroke_path(
570 &path,
571 &paint,
572 &stroke,
573 Transform::identity(),
574 self.clip_mask.as_ref(),
575 );
576 Ok(())
577 }
578
579 fn stroke_arc(
580 &mut self,
581 center: Point,
582 radius: u32,
583 start_deg: i32,
584 sweep_deg: i32,
585 width: u32,
586 color: Rgb565,
587 ) -> Result<(), RenderError> {
588 if radius == 0 || width == 0 || sweep_deg == 0 {
589 return Ok(());
590 }
591 let Some(path) = arc_path(center, radius, start_deg, sweep_deg, false) else {
592 return Ok(());
593 };
594 let mut paint = Paint::default();
595 paint.set_color(rgb565_to_skia(color));
596 paint.anti_alias = true;
597 let mut stroke = Stroke::default();
598 stroke.width = width as f32;
599 stroke.line_cap = tiny_skia::LineCap::Round;
600 self.pixmap.stroke_path(
601 &path,
602 &paint,
603 &stroke,
604 Transform::identity(),
605 self.clip_mask.as_ref(),
606 );
607 Ok(())
608 }
609
610 fn fill_arc(
611 &mut self,
612 center: Point,
613 radius: u32,
614 start_deg: i32,
615 sweep_deg: i32,
616 color: Rgb565,
617 ) -> Result<(), RenderError> {
618 if radius == 0 || sweep_deg == 0 {
619 return Ok(());
620 }
621 let Some(path) = arc_path(center, radius, start_deg, sweep_deg, true) else {
622 return Ok(());
623 };
624 let mut paint = Paint::default();
625 paint.set_color(rgb565_to_skia(color));
626 paint.anti_alias = true;
627 self.pixmap.fill_path(
628 &path,
629 &paint,
630 FillRule::Winding,
631 Transform::identity(),
632 self.clip_mask.as_ref(),
633 );
634 Ok(())
635 }
636
637 fn draw_text(
638 &mut self,
639 text: &str,
640 position: Point,
641 font: &MonoFont<'_>,
642 color: Rgb565,
643 alignment: Alignment,
644 ) -> Result<(), RenderError> {
645 let style = MonoTextStyle::new(font, color);
646 let mut adapter = PixmapDrawTarget {
647 pixmap: &mut *self.pixmap,
648 clip: self.clip_rect,
649 };
650 Text::with_alignment(text, position, style, alignment)
651 .draw(&mut adapter)
652 .map(|_| ())
653 .map_err(|_| RenderError)
654 }
655
656 fn draw_image(
657 &mut self,
658 top_left: Point,
659 size: Size,
660 pixels: &[Rgb565],
661 ) -> Result<(), RenderError> {
662 let w = size.width as i32;
663 if w == 0 {
664 return Ok(());
665 }
666 let pw = self.pixmap.width() as i32;
667 let ph = self.pixmap.height() as i32;
668 let stride = self.pixmap.width() as usize * 4;
669 let (cx1, cy1, cx2, cy2) = match self.clip_rect {
670 Some(r) => (
671 r.top_left.x,
672 r.top_left.y,
673 r.top_left.x + r.size.width as i32,
674 r.top_left.y + r.size.height as i32,
675 ),
676 None => (0, 0, pw, ph),
677 };
678 let data = self.pixmap.data_mut();
679 for (i, color) in pixels.iter().enumerate() {
680 let x = top_left.x + (i as i32 % w);
681 let y = top_left.y + (i as i32 / w);
682 if x < cx1 || y < cy1 || x >= cx2 || y >= cy2 {
683 continue;
684 }
685 if x < 0 || y < 0 || x >= pw || y >= ph {
686 continue;
687 }
688 let off = y as usize * stride + x as usize * 4;
689 let (r, g, b) = rgb565_components(*color);
690 data[off] = r;
691 data[off + 1] = g;
692 data[off + 2] = b;
693 data[off + 3] = 255;
694 }
695 Ok(())
696 }
697
698 fn push_clip(&mut self, rect: Rectangle) {
699 let new_rect = match self.clip_rect {
700 Some(existing) => intersect(existing, rect),
701 None => rect,
702 };
703 let new_mask = if new_rect.size.width == 0 || new_rect.size.height == 0 {
704 Some(Mask::new(self.pixmap.width(), self.pixmap.height()).expect("mask"))
706 } else {
707 build_rect_mask(self.pixmap.width(), self.pixmap.height(), new_rect)
708 };
709 let prev_mask = core::mem::replace(&mut self.clip_mask, new_mask);
710 let prev_rect = self.clip_rect.replace(new_rect);
711 self.clip_stack.push((prev_mask, prev_rect));
712 }
713
714 fn pop_clip(&mut self) {
715 if let Some((mask, rect)) = self.clip_stack.pop() {
716 self.clip_mask = mask;
717 self.clip_rect = rect;
718 }
719 }
720}
721
722fn intersect(a: Rectangle, b: Rectangle) -> Rectangle {
723 let ax2 = a.top_left.x + a.size.width as i32;
724 let ay2 = a.top_left.y + a.size.height as i32;
725 let bx2 = b.top_left.x + b.size.width as i32;
726 let by2 = b.top_left.y + b.size.height as i32;
727 let x1 = a.top_left.x.max(b.top_left.x);
728 let y1 = a.top_left.y.max(b.top_left.y);
729 let x2 = ax2.min(bx2);
730 let y2 = ay2.min(by2);
731 if x2 <= x1 || y2 <= y1 {
732 Rectangle::new(Point::new(x1, y1), Size::zero())
733 } else {
734 Rectangle::new(
735 Point::new(x1, y1),
736 Size::new((x2 - x1) as u32, (y2 - y1) as u32),
737 )
738 }
739}
740
741fn arc_path(
748 center: Point,
749 radius: u32,
750 start_deg: i32,
751 sweep_deg: i32,
752 pie: bool,
753) -> Option<tiny_skia::Path> {
754 let total = sweep_deg.unsigned_abs().min(360);
755 let step: f32 = if sweep_deg >= 0 { 1.0 } else { -1.0 };
756 let r = radius as f32;
757 let cx = center.x as f32 + 0.5;
758 let cy = center.y as f32 + 0.5;
759
760 let point_at = |deg: f32| -> (f32, f32) {
761 let rad = deg * core::f32::consts::PI / 180.0;
762 (cx + rad.cos() * r, cy - rad.sin() * r)
764 };
765
766 let mut pb = PathBuilder::new();
767 if pie {
768 pb.move_to(cx, cy);
769 let (x0, y0) = point_at(start_deg as f32);
770 pb.line_to(x0, y0);
771 } else {
772 let (x0, y0) = point_at(start_deg as f32);
773 pb.move_to(x0, y0);
774 }
775 for i in 1..=total {
776 let (x, y) = point_at(start_deg as f32 + i as f32 * step);
777 pb.line_to(x, y);
778 }
779 if pie {
780 pb.close();
781 }
782 pb.finish()
783}
784
785fn build_rect_mask(pixmap_w: u32, pixmap_h: u32, rect: Rectangle) -> Option<Mask> {
786 let mut mask = Mask::new(pixmap_w, pixmap_h)?;
787 let sk_rect = SkRect::from_xywh(
788 rect.top_left.x as f32,
789 rect.top_left.y as f32,
790 rect.size.width as f32,
791 rect.size.height as f32,
792 )?;
793 let path = PathBuilder::from_rect(sk_rect);
794 mask.fill_path(&path, FillRule::Winding, false, Transform::identity());
795 Some(mask)
796}
797
798struct PixmapDrawTarget<'p> {
803 pixmap: &'p mut Pixmap,
804 clip: Option<Rectangle>,
805}
806
807impl<'p> OriginDimensions for PixmapDrawTarget<'p> {
808 fn size(&self) -> Size {
809 Size::new(self.pixmap.width(), self.pixmap.height())
810 }
811}
812
813impl<'p> DrawTarget for PixmapDrawTarget<'p> {
814 type Color = Rgb565;
815 type Error = Infallible;
816
817 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
818 where
819 I: IntoIterator<Item = Pixel<Self::Color>>,
820 {
821 let w = self.pixmap.width() as i32;
822 let h = self.pixmap.height() as i32;
823 let stride = self.pixmap.width() as usize * 4;
824 let (cx1, cy1, cx2, cy2) = match self.clip {
825 Some(r) => (
826 r.top_left.x,
827 r.top_left.y,
828 r.top_left.x + r.size.width as i32,
829 r.top_left.y + r.size.height as i32,
830 ),
831 None => (0, 0, w, h),
832 };
833 let data = self.pixmap.data_mut();
834 for Pixel(p, c) in pixels {
835 if p.x < cx1 || p.y < cy1 || p.x >= cx2 || p.y >= cy2 {
836 continue;
837 }
838 if p.x < 0 || p.y < 0 || p.x >= w || p.y >= h {
839 continue;
840 }
841 let off = p.y as usize * stride + p.x as usize * 4;
842 let (r, g, b) = rgb565_components(c);
843 data[off] = r;
844 data[off + 1] = g;
845 data[off + 2] = b;
846 data[off + 3] = 255;
847 }
848 Ok(())
849 }
850}
851
852fn rgb565_components(c: Rgb565) -> (u8, u8, u8) {
853 let r5 = c.r();
855 let g6 = c.g();
856 let b5 = c.b();
857 let r = (r5 << 3) | (r5 >> 2);
858 let g = (g6 << 2) | (g6 >> 4);
859 let b = (b5 << 3) | (b5 >> 2);
860 (r, g, b)
861}
862
863fn rgb565_to_skia(c: Rgb565) -> SkColor {
864 let (r, g, b) = rgb565_components(c);
865 SkColor::from_rgba8(r, g, b, 255)
866}
867
868fn rgba8_to_rgb565(r: u8, g: u8, b: u8) -> Rgb565 {
869 Rgb565::new(r >> 3, g >> 2, b >> 3)
870}