1use std::marker::PhantomData;
2
3use tiny_skia::*;
4
5use embedded_graphics_core::{
6 draw_target::*,
7 geometry::{Point, Size},
8 pixelcolor::*,
9 prelude::Dimensions,
10 prelude::*,
11 primitives::Rectangle,
12 Pixel,
13};
14
15pub struct TinySkiaDisplay<C>
34where
35 C: PixelColor + From<<C as PixelColor>::Raw>,
36{
37 pix_map: Pixmap,
38 size: Size,
39 _pixel_color: PhantomData<C>,
40}
41
42impl<C> DrawTarget for TinySkiaDisplay<C>
43where
44 C: PixelColor + From<<C as PixelColor>::Raw> + Into<Rgb888>,
45{
46 type Color = C;
47
48 type Error = String;
49
50 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
51 where
52 I: IntoIterator<Item = embedded_graphics_core::Pixel<Self::Color>>,
53 {
54 for Pixel(p, color) in pixels.into_iter() {
57 self.draw_pixel(p, color);
58 }
59
60 Ok(())
63 }
64
65 fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
66 let area = area.intersection(&self.bounding_box());
67
68 if let Some(rect) = Rect::from_xywh(
69 area.top_left.x as f32,
70 area.top_left.y as f32,
71 area.size.width as f32,
72 area.size.height as f32,
73 ) {
74 self.pix_map.fill_rect(
75 rect,
76 &convert_color_to_paint(color),
77 Transform::identity(),
78 None,
79 );
80 } else {
81 return Err(String::from("Cannot create tiny-skia rect"));
82 }
83
84 Ok(())
87 }
88
89 fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
90 where
91 I: IntoIterator<Item = Self::Color>,
92 {
93 let area = area.intersection(&self.bounding_box());
97
98 if area.size != Size::zero() {
100 self.draw_iter(
101 area.points()
102 .zip(colors)
103 .filter(|(pos, _color)| area.contains(*pos))
104 .map(|(pos, color)| Pixel(pos, color)),
105 )?;
106 }
107
108 Ok(())
109 }
110}
111
112impl<C> OriginDimensions for TinySkiaDisplay<C>
113where
114 C: PixelColor + From<<C as PixelColor>::Raw>,
115{
116 fn size(&self) -> Size {
117 self.size
118 }
119}
120
121impl<C> TinySkiaDisplay<C>
122where
123 C: PixelColor + From<<C as PixelColor>::Raw> + Into<Rgb888>,
124{
125 pub fn new(width: u32, height: u32) -> Result<Self, String> {
127 Ok(TinySkiaDisplay {
128 pix_map: Pixmap::new(width, height).ok_or("Cannot create tiny-skia Pixmap")?,
129 size: Size::new(width, height),
130 _pixel_color: PhantomData::default(),
132 })
133 }
134
135 pub fn data(&self) -> &[u8] {
137 self.pix_map.data()
138 }
139
140 pub fn draw_pixel(&mut self, position: Point, color: C) {
141 let (r, g, b, a) = rgba(color);
142
143 if position.x >= 0
144 && position.y >= 0
145 && position.x < self.size.width as i32
146 && position.y < self.size.height as i32
147 {
148 let index = (position.y as usize * self.size.width as usize + position.x as usize) * 4;
149
150 self.pix_map.data_mut()[index] = r;
151 self.pix_map.data_mut()[index + 1] = g;
152 self.pix_map.data_mut()[index + 2] = b;
153 self.pix_map.data_mut()[index + 3] = a;
154 }
155 }
156
157 pub fn flip(&mut self, surface: &mut [u8]) {
159 surface.copy_from_slice(self.pix_map.data_mut());
160 }
161}
162
163#[cfg(not(target_arch = "wasm32"))]
164fn rgba<C: PixelColor + Into<Rgb888>>(color: C) -> (u8, u8, u8, u8) {
165 let color: Rgb888 = color.into();
166
167 (color.b(), color.g(), color.r(), 255)
168}
169
170#[cfg(target_arch = "wasm32")]
171fn rgba<C: PixelColor + Into<Rgb888>>(color: C) -> (u8, u8, u8, u8) {
172 let color: Rgb888 = color.into();
173
174 (color.r(), color.g(), color.b(), 255)
175}
176
177fn convert_color_to_paint<'a, C: PixelColor + Into<Rgb888>>(color: C) -> Paint<'a> {
179 let (r, g, b, a) = rgba(color);
180
181 let mut paint = Paint {
182 anti_alias: true,
183 ..Default::default()
184 };
185 paint.set_color_rgba8(r, g, b, a);
186 paint
187}