1use super::{FFIResult, Ptr, Rid, std::destroy};
3use crate::alloc::Vec;
4use crate::imports::std::{encode, free_result, read_buffer};
5use serde::{Deserialize, Serialize};
6
7pub use crate::canvas::*;
8
9#[link(wasm_import_module = "canvas")]
10unsafe extern "C" {
11 fn new_context(width: f32, height: f32) -> Rid;
12
13 fn set_transform(
14 context: Rid,
15 translate_x: f32,
16 translate_y: f32,
17 scale_x: f32,
18 scale_y: f32,
19 rotate_angle: f32,
20 ) -> FFIResult;
21 fn copy_image(
22 context: Rid,
23 image: Rid,
24 src_x: f32,
25 src_y: f32,
26 src_width: f32,
27 src_height: f32,
28 dst_x: f32,
29 dst_y: f32,
30 dst_width: f32,
31 dst_height: f32,
32 ) -> FFIResult;
33 fn draw_image(
34 context: Rid,
35 image: Rid,
36 dst_x: f32,
37 dst_y: f32,
38 dst_width: f32,
39 dst_height: f32,
40 ) -> FFIResult;
41 fn fill(context: Rid, path: Ptr, r: f32, g: f32, b: f32, a: f32) -> FFIResult;
42 fn stroke(context: Rid, path: Ptr, style: Ptr) -> FFIResult;
43 fn draw_text(
44 context: Rid,
45 text: *const u8,
46 text_len: usize,
47 size: f32,
48 x: f32,
49 y: f32,
50 font: Rid,
51 r: f32,
52 g: f32,
53 b: f32,
54 a: f32,
55 ) -> FFIResult;
56 fn get_image(context: Rid) -> Rid;
57
58 fn new_font(name_ptr: *const u8, name_len: usize) -> FFIResult;
59 fn system_font(weight: u8) -> Rid;
60 fn load_font(url_ptr: *const u8, url_len: usize) -> FFIResult;
61
62 fn new_image(data_ptr: *const u8, data_len: usize) -> FFIResult;
63 fn get_image_data(image_rid: Rid) -> FFIResult;
64 fn get_image_width(image_rid: Rid) -> f32;
65 fn get_image_height(image_rid: Rid) -> f32;
66}
67
68#[derive(PartialEq, Eq, Debug, Clone)]
70pub enum CanvasError {
71 InvalidContext,
72 InvalidImagePointer,
73 InvalidImage,
74 InvalidSrcRect,
75 InvalidResult,
76 InvalidBounds,
77 InvalidPath,
78 InvalidStyle,
79 InvalidString,
80 InvalidFont,
81 FontLoadFailed,
82}
83
84impl CanvasError {
85 fn from(value: FFIResult) -> Option<Self> {
86 match value {
87 -1 => Some(Self::InvalidContext),
88 -2 => Some(Self::InvalidImagePointer),
89 -3 => Some(Self::InvalidImage),
90 -4 => Some(Self::InvalidSrcRect),
91 -5 => Some(Self::InvalidResult),
92 -6 => Some(Self::InvalidBounds),
93 -7 => Some(Self::InvalidPath),
94 -8 => Some(Self::InvalidStyle),
95 -9 => Some(Self::InvalidString),
96 -10 => Some(Self::InvalidFont),
97 -11 => Some(Self::FontLoadFailed),
98 _ => None,
99 }
100 }
101}
102
103#[derive(Debug)]
105pub struct ImageRef {
106 pub rid: Rid,
111 pub externally_managed: bool,
116}
117
118impl ImageRef {
119 pub(crate) fn from(rid: Rid, externally_managed: bool) -> Self {
120 ImageRef {
121 rid,
122 externally_managed,
123 }
124 }
125
126 pub fn new(data: &[u8]) -> Self {
127 let rid = unsafe { new_image(data.as_ptr(), data.len()) };
128 ImageRef::from(rid, false)
129 }
130
131 pub fn data(&self) -> Vec<u8> {
132 let result = unsafe { get_image_data(self.rid) };
133 if CanvasError::from(result).is_some() {
134 return Vec::new();
135 }
136 read_buffer(result).unwrap_or_default()
137 }
138
139 pub fn width(&self) -> f32 {
140 unsafe { get_image_width(self.rid) }
141 }
142
143 pub fn height(&self) -> f32 {
144 unsafe { get_image_height(self.rid) }
145 }
146}
147
148impl PartialEq for ImageRef {
149 fn eq(&self, other: &Self) -> bool {
150 self.rid == other.rid
151 }
152}
153
154impl Drop for ImageRef {
155 fn drop(&mut self) {
156 if !self.externally_managed {
157 unsafe { destroy(self.rid) }
158 }
159 }
160}
161
162impl Serialize for ImageRef {
163 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
164 where
165 S: serde::Serializer,
166 {
167 self.rid.serialize(serializer)
168 }
169}
170
171impl<'de> Deserialize<'de> for ImageRef {
172 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
173 where
174 D: serde::Deserializer<'de>,
175 {
176 Rid::deserialize(deserializer).map(|rid| ImageRef::from(rid, true))
178 }
179}
180
181impl Drop for Path {
182 fn drop(&mut self) {
183 if let Some(ptr) = self.ptr {
184 unsafe { free_result(ptr) };
185 }
186 }
187}
188
189#[derive(Debug)]
191pub struct Font {
192 rid: Rid,
193}
194
195impl Font {
196 pub fn new(font_family: &str) -> Result<Self, CanvasError> {
198 let rid = unsafe { new_font(font_family.as_ptr(), font_family.len()) };
199 if let Some(err) = CanvasError::from(rid) {
200 return Err(err);
201 }
202 Ok(Self { rid })
203 }
204
205 pub fn system(weight: FontWeight) -> Self {
207 let rid = unsafe { system_font(weight as u8) };
208 Self { rid }
209 }
210
211 pub fn load(url: &str) -> Result<Self, CanvasError> {
213 let rid = unsafe { load_font(url.as_ptr(), url.len()) };
214 if let Some(err) = CanvasError::from(rid) {
215 return Err(err);
216 }
217 Ok(Self { rid })
218 }
219}
220
221#[derive(Debug)]
223pub struct Canvas {
224 rid: Rid,
225}
226
227impl Canvas {
228 pub fn new(width: f32, height: f32) -> Self {
230 let rid = unsafe { new_context(width, height) };
231 Canvas { rid }
232 }
233
234 pub fn set_transform(&mut self, transform: &Transform) {
236 use num_traits::float::Float;
237
238 let scale_x = (transform.m11 * transform.m11 + transform.m12 * transform.m12).sqrt();
240 let scale_y = (transform.m21 * transform.m21 + transform.m22 * transform.m22).sqrt();
241 let rotate_angle = transform.m12.atan2(transform.m11);
242 let translate_x = (transform.m31 * Float::cos(rotate_angle)
243 + transform.m32 * Float::sin(rotate_angle))
244 / scale_x;
245 let translate_y = (transform.m32 * Float::cos(rotate_angle)
246 - transform.m31 * Float::sin(rotate_angle))
247 / scale_y;
248
249 unsafe {
250 set_transform(
251 self.rid,
252 translate_x,
253 translate_y,
254 scale_x,
255 scale_y,
256 rotate_angle,
257 );
258 }
259 }
260
261 pub fn draw_image(&mut self, image: &ImageRef, dst_rect: Rect) {
263 unsafe {
264 draw_image(
265 self.rid,
266 image.rid,
267 dst_rect.x,
268 dst_rect.y,
269 dst_rect.width,
270 dst_rect.height,
271 );
272 }
273 }
274
275 pub fn copy_image(&mut self, image: &ImageRef, src_rect: Rect, dst_rect: Rect) {
277 unsafe {
278 copy_image(
279 self.rid,
280 image.rid,
281 src_rect.x,
282 src_rect.y,
283 src_rect.width,
284 src_rect.height,
285 dst_rect.x,
286 dst_rect.y,
287 dst_rect.width,
288 dst_rect.height,
289 );
290 }
291 }
292
293 pub fn fill(&mut self, path: &Path, color: &Color) {
295 let Some(path_ptr) = path.ptr else { return };
296 unsafe {
297 fill(
298 self.rid,
299 path_ptr,
300 color.red,
301 color.green,
302 color.blue,
303 color.alpha,
304 );
305 }
306 }
307
308 pub fn stroke(&mut self, path: &Path, style: &StrokeStyle) {
310 let Some(path_ptr) = path.ptr else { return };
311 let style_ptr = unsafe { encode(style) };
312 unsafe {
313 stroke(self.rid, path_ptr, style_ptr);
314 }
315 unsafe {
316 free_result(style_ptr);
317 }
318 }
319
320 pub fn draw_text(&mut self, text: &str, size: f32, pos: &Point, font: &Font, color: &Color) {
322 unsafe {
323 draw_text(
324 self.rid,
325 text.as_ptr(),
326 text.len(),
327 size,
328 pos.x,
329 pos.y,
330 font.rid,
331 color.red,
332 color.green,
333 color.blue,
334 color.alpha,
335 );
336 }
337 }
338
339 pub fn get_image(self) -> ImageRef {
341 ImageRef::from(unsafe { get_image(self.rid) }, false)
342 }
343}
344
345impl Drop for Canvas {
346 fn drop(&mut self) {
347 unsafe { destroy(self.rid) }
348 }
349}