1use std::rc::Rc;
2
3use image::{codecs::png::PngEncoder, guess_format, load_from_memory_with_format, ImageBuffer, RgbaImage};
4use usvg::{Opacity, Paint};
5use wasm_bindgen::prelude::*;
6use base64::{prelude::BASE64_STANDARD, Engine};
7
8use crate::utils::interpolation::{inverse_lerp, lerp};
9
10use super::{console::log, point2d::Point2D};
11
12#[wasm_bindgen]
14#[derive(Debug, Clone, Copy)]
15pub struct Color {
16 pub red: u8,
18 pub green: u8,
20 pub blue: u8,
22 pub alpha: f32,
24}
25
26impl Default for Color {
27 fn default() -> Self {
28 Color {
29 red: 0,
30 green: 0,
31 blue: 0,
32 alpha: 0.0,
33 }
34 }
35}
36
37#[wasm_bindgen]
38impl Color {
39 #[wasm_bindgen(constructor, return_description = "A new color.")]
41 pub fn new(
42 #[wasm_bindgen(param_description = "The red component of the color.")]
43 red: u8,
44 #[wasm_bindgen(param_description = "The green component of the color.")]
45 green: u8,
46 #[wasm_bindgen(param_description = "The blue component of the color.")]
47 blue: u8,
48 #[wasm_bindgen(param_description = "The alpha component of the color.")]
49 alpha: f32
50 ) -> Color {
51 Color {
52 red,
53 green,
54 blue,
55 alpha,
56 }
57 }
58 #[wasm_bindgen(return_description = "The default color.")]
60 pub fn default_color() -> Color {
61 Color::default()
62 }
63 #[wasm_bindgen]
65 pub fn lerp(
66 #[wasm_bindgen(param_description = "The start color.")]
67 color1: &Color,
68 #[wasm_bindgen(param_description = "The end color.")]
69 color2: &Color,
70 #[wasm_bindgen(param_description = "The progress value.")]
71 t: f32
72 ) -> Color {
73 let red = lerp(color1.red as f32, color2.red as f32, t) as u8;
74 let green = lerp(color1.green as f32, color2.green as f32, t) as u8;
75 let blue = lerp(color1.blue as f32, color2.blue as f32, t) as u8;
76 let alpha = lerp(color1.alpha, color2.alpha, t);
77 Color {
78 red,
79 green,
80 blue,
81 alpha,
82 }
83 }
84}
85
86#[wasm_bindgen]
88#[derive(Debug, Clone, Copy)]
89pub struct ColorStop {
90 pub color: Color,
92 pub position: f32,
94}
95
96impl Default for ColorStop {
97 fn default() -> Self {
98 ColorStop {
99 color: Color::default(),
100 position: 0.0,
101 }
102 }
103}
104
105#[wasm_bindgen]
106impl ColorStop {
107 #[wasm_bindgen(constructor, return_description = "A new color stop.")]
109 pub fn new(
110 #[wasm_bindgen(param_description = "The color of the stop.")]
111 color: Color,
112 #[wasm_bindgen(param_description = "The position of the stop.")]
113 position: f32
114 ) -> ColorStop {
115 ColorStop {
116 color,
117 position,
118 }
119 }
120}
121
122#[wasm_bindgen]
124#[derive(Debug, Clone)]
125pub struct LinearGradient {
126 pub p1: Point2D,
128 pub p2: Point2D,
130 color_stops: Rc<Vec<ColorStop>>,
132}
133
134impl Default for LinearGradient {
135 fn default() -> Self {
136 LinearGradient {
137 p1: Point2D::default(),
138 p2: Point2D::default(),
139 color_stops: Rc::new(vec![]),
140 }
141 }
142}
143
144#[wasm_bindgen]
145impl LinearGradient {
146 #[wasm_bindgen(constructor, return_description = "A new linear gradient.")]
148 pub fn new(
149 #[wasm_bindgen(param_description = "The start point of the gradient.")]
150 p1: Point2D,
151 #[wasm_bindgen(param_description = "The end point of the gradient.")]
152 p2: Point2D,
153 #[wasm_bindgen(param_description = "The color stops of the gradient.")]
154 color_stops: Vec<ColorStop>
155 ) -> LinearGradient {
156 LinearGradient {
157 p1,
158 p2,
159 color_stops: Rc::new(color_stops),
160 }
161 }
162 #[wasm_bindgen(return_description = "The default linear gradient.")]
164 pub fn single_color_gradient(
165 #[wasm_bindgen(param_description = "The start point of the gradient.")]
166 p1: Point2D,
167 #[wasm_bindgen(param_description = "The end point of the gradient.")]
168 p2: Point2D,
169 #[wasm_bindgen(param_description = "The color of the gradient.")]
170 color: Color,
171 #[wasm_bindgen(param_description = "Number of times to repeat the color.")]
172 repeats: Option<usize>
173 ) -> LinearGradient {
174 let repeats = repeats.unwrap_or(2);
175 let mut color_stops = vec![];
176 for i in 0..repeats {
177 color_stops.push(ColorStop { color, position: i as f32 / (repeats - 1) as f32 });
178 }
179 LinearGradient {
180 p1,
181 p2,
182 color_stops: Rc::new(color_stops),
183 }
184 }
185 #[wasm_bindgen(return_description = "The default linear gradient.")]
187 pub fn default_linear_gradient() -> LinearGradient {
188 LinearGradient::default()
189 }
190 #[wasm_bindgen(getter, return_description = "The color stops of the gradient.")]
192 pub fn color_stops(&self) -> Vec<ColorStop> {
193 self.color_stops.to_vec()
194 }
195 #[wasm_bindgen(setter)]
197 pub fn set_color_stops(
198 &mut self,
199 #[wasm_bindgen(param_description = "The color stops of the gradient.")]
200 color_stops: Vec<ColorStop>
201 ) {
202 self.color_stops = Rc::new(color_stops);
203 }
204 #[wasm_bindgen(return_description = "The color at the given offset.")]
206 pub fn color_at_offset(
207 &self,
208 #[wasm_bindgen(param_description = "The offset to get the color at.")]
209 position: f32
210 ) -> Color {
211 let mut the_stops = Rc::clone(&self.color_stops);
212 let stops = Rc::make_mut(&mut the_stops);
213 stops.sort_by(|a, b| a.position.partial_cmp(&b.position).unwrap());
214 for i in 0..stops.len() {
215 if position < stops[i].position {
216 if i == 0 {
217 return stops[i].color;
218 }
219 let t = inverse_lerp(stops[i - 1].position, stops[i].position, position);
220 return Color::lerp(&stops[i - 1].color, &stops[i].color, t);
221 }
222 }
223 stops[stops.len() - 1].color
224 }
225 #[wasm_bindgen(return_description = "The color at the given point.")]
227 pub fn color_at(
228 &self,
229 #[wasm_bindgen(param_description = "The point to get the color at.")]
230 p: Point2D
231 ) -> Color {
232 let t = p.project_onto_line(&self.p1, &self.p2);
233 self.color_at_offset(t)
234 }
235 #[wasm_bindgen(return_description = "The interpolated linear gradient.")]
237 pub fn lerp(
238 #[wasm_bindgen(param_description = "The first linear gradient.")]
239 gradient1: &LinearGradient,
240 #[wasm_bindgen(param_description = "The second linear gradient.")]
241 gradient2: &LinearGradient,
242 #[wasm_bindgen(param_description = "The progress value.")]
243 t: f32
244 ) -> LinearGradient {
245 let p1 = Point2D::lerp(&gradient1.p1, &gradient2.p1, t);
246 let p2 = Point2D::lerp(&gradient1.p2, &gradient2.p2, t);
247 let length = gradient1.color_stops.len().max(gradient2.color_stops.len());
248 let mut color_stops = vec![];
249 for i in 0..length {
250 let color1 = if i < gradient1.color_stops.len() {
251 gradient1.color_stops[i].color
252 } else {
253 Color::default()
254 };
255 let color2 = if i < gradient2.color_stops.len() {
256 gradient2.color_stops[i].color
257 } else {
258 Color::default()
259 };
260 let color = Color::lerp(&color1, &color2, t);
261 let position = if i < gradient1.color_stops.len() {
262 lerp(gradient1.color_stops[i].position, gradient2.color_stops[i].position, t)
263 } else {
264 lerp(0.0, gradient2.color_stops[i].position, t)
265 };
266 color_stops.push(ColorStop { color, position });
267 }
268 LinearGradient {
269 p1,
270 p2,
271 color_stops: Rc::new(color_stops),
272 }
273 }
274}
275
276#[wasm_bindgen]
278#[derive(Debug, Clone)]
279pub struct RadialGradient {
280 pub f: Point2D,
282 pub c: Point2D,
284 pub r: f32,
286 color_stops: Rc<Vec<ColorStop>>,
288}
289
290impl Default for RadialGradient {
291 fn default() -> Self {
292 RadialGradient {
293 f: Point2D::default(),
294 c: Point2D::default(),
295 r: 0.0,
296 color_stops: Rc::new(vec![]),
297 }
298 }
299}
300
301#[wasm_bindgen]
302impl RadialGradient {
303 #[wasm_bindgen(constructor, return_description = "A new radial gradient.")]
305 pub fn new(
306 #[wasm_bindgen(param_description = "The start circle center point of the gradient.")]
307 f: Point2D,
308 #[wasm_bindgen(param_description = "The end circle center point of the gradient.")]
309 c: Point2D,
310 #[wasm_bindgen(param_description = "The radius of the gradient.")]
311 r: f32,
312 #[wasm_bindgen(param_description = "The color stops of the gradient.")]
313 color_stops: Vec<ColorStop>
314 ) -> RadialGradient {
315 RadialGradient {
316 f,
317 c,
318 r,
319 color_stops: Rc::new(color_stops),
320 }
321 }
322 #[wasm_bindgen(return_description = "The default radial gradient.")]
324 pub fn default_radial_gradient() -> RadialGradient {
325 RadialGradient::default()
326 }
327 #[wasm_bindgen(getter, return_description = "The color stops of the gradient.")]
329 pub fn color_stops(&self) -> Vec<ColorStop> {
330 self.color_stops.to_vec()
331 }
332 #[wasm_bindgen(setter)]
334 pub fn set_color_stops(
335 &mut self,
336 #[wasm_bindgen(param_description = "The color stops of the gradient.")]
337 color_stops: Vec<ColorStop>
338 ) {
339 self.color_stops = Rc::new(color_stops);
340 }
341 #[wasm_bindgen(return_description = "The color at the given offset.")]
343 pub fn color_at_offset(
344 &self,
345 #[wasm_bindgen(param_description = "The offset to get the color at.")]
346 position: f32
347 ) -> Color {
348 let mut stops = Rc::clone(&self.color_stops);
349 let stops = Rc::make_mut(&mut stops);
350 stops.sort_by(|a, b| a.position.partial_cmp(&b.position).unwrap());
351 for i in 0..stops.len() {
352 if position < stops[i].position {
353 if i == 0 {
354 return stops[i].color;
355 }
356 let t = inverse_lerp(stops[i - 1].position, stops[i].position, position);
357 return Color::lerp(&stops[i - 1].color, &stops[i].color, t);
358 }
359 }
360 stops[stops.len() - 1].color
361 }
362 #[wasm_bindgen(return_description = "The color at the given point.")]
364 pub fn color_at(
365 &self,
366 #[wasm_bindgen(param_description = "The point to get the color at.")]
367 p: Point2D
368 ) -> Color {
369 let a = (p.x - self.f.x).powi(2) + (p.y - self.f.y).powi(2);
377 let b = 2.0 * (self.f.x - self.c.x) * (p.x - self.f.x) + 2.0 * (self.f.y - self.c.y) * (p.y - self.f.y);
378 let c = (self.f.x - self.c.x).powi(2) + (self.f.y - self.c.y).powi(2) - self.r.powi(2);
379 if a == 0.0 {
381 return self.color_at_offset(0.0);
382 }
383 let t = (-b + (b.powi(2) - 4.0 * a * c).sqrt()) / (2.0 * a);
384 let projection = p.project_onto_line(&self.f, &(self.f + (p - self.f) * t));
386 self.color_at_offset(projection)
387 }
388 #[wasm_bindgen(return_description = "A single color radial gradient.")]
390 pub fn single_color_gradient(
391 #[wasm_bindgen(param_description = "The start circle center point of the gradient.")]
392 f: Point2D,
393 #[wasm_bindgen(param_description = "The end circle center point of the gradient.")]
394 c: Point2D,
395 #[wasm_bindgen(param_description = "The radius of the gradient.")]
396 r: f32,
397 #[wasm_bindgen(param_description = "The color of the gradient.")]
398 color: Color,
399 #[wasm_bindgen(param_description = "Number of times to repeat the color.")]
400 repeats: Option<usize>
401 ) -> RadialGradient {
402 let repeats = repeats.unwrap_or(2);
403 let mut color_stops = vec![];
404 for i in 0..repeats {
405 color_stops.push(ColorStop { color, position: i as f32 / (repeats - 1) as f32 });
406 }
407 RadialGradient {
408 f,
409 c,
410 r,
411 color_stops: Rc::new(color_stops),
412 }
413 }
414 #[wasm_bindgen(return_description = "The interpolated radial gradient.")]
416 pub fn lerp(
417 #[wasm_bindgen(param_description = "The first radial gradient.")]
418 gradient1: &RadialGradient,
419 #[wasm_bindgen(param_description = "The second radial gradient.")]
420 gradient2: &RadialGradient,
421 #[wasm_bindgen(param_description = "The progress value.")]
422 t: f32
423 ) -> RadialGradient {
424 let f = Point2D::lerp(&gradient1.f, &gradient2.f, t);
425 let c = Point2D::lerp(&gradient1.c, &gradient2.c, t);
426 let r = lerp(gradient1.r, gradient2.r, t);
427 let length = gradient1.color_stops.len().max(gradient2.color_stops.len());
428 let mut color_stops = vec![];
429 for i in 0..length {
430 let color1 = if i < gradient1.color_stops.len() {
431 gradient1.color_stops[i].color
432 } else {
433 Color::default()
434 };
435 let color2 = if i < gradient2.color_stops.len() {
436 gradient2.color_stops[i].color
437 } else {
438 Color::default()
439 };
440 let color = Color::lerp(&color1, &color2, t);
441 let position = if i < gradient1.color_stops.len() {
442 lerp(gradient1.color_stops[i].position, gradient2.color_stops[i].position, t)
443 } else {
444 lerp(0.0, gradient2.color_stops[i].position, t)
445 };
446 color_stops.push(ColorStop { color, position });
447 }
448 RadialGradient {
449 f,
450 c,
451 r,
452 color_stops: Rc::new(color_stops),
453 }
454 }
455}
456
457#[wasm_bindgen]
459#[derive(Debug, Clone)]
460pub struct ImageBitmap {
461 pub x: f32,
463 pub y: f32,
465 pub width: f32,
467 pub height: f32,
469 pub data_width: usize,
471 pub data_height: usize,
473 rgba_image: ImageBuffer<image::Rgba<u8>, Vec<u8>>,
475}
476
477impl Default for ImageBitmap {
478 fn default() -> Self {
479 ImageBitmap {
480 x: 0.0,
481 y: 0.0,
482 width: 0.0,
483 height: 0.0,
484 data_width: 0,
485 data_height: 0,
486 rgba_image: RgbaImage::new(0, 0)
487 }
488 }
489}
490
491#[wasm_bindgen]
492impl ImageBitmap {
493 #[wasm_bindgen(constructor, return_description = "A new image bitmap.")]
495 pub fn new(
496 #[wasm_bindgen(param_description = "The x coordinate of the bitmap.")]
497 x: f32,
498 #[wasm_bindgen(param_description = "The y coordinate of the bitmap.")]
499 y: f32,
500 #[wasm_bindgen(param_description = "The width of the bitmap.")]
501 width: f32,
502 #[wasm_bindgen(param_description = "The height of the bitmap.")]
503 height: f32,
504 #[wasm_bindgen(param_description = "Number of pixels in a row of the bitmap.")]
505 data_width: usize,
506 #[wasm_bindgen(param_description = "Number of pixels in a column of the bitmap.")]
507 data_height: usize,
508 #[wasm_bindgen(param_description = "The pixel data of the bitmap.")]
509 data: Vec<u8>
510 ) -> Result<ImageBitmap, JsError> {
511 let rgba_image = guess_format(&data).and_then(|format| {
512 let img = load_from_memory_with_format(&data, format)?;
513 let img = img.to_rgba8();
514 Ok(img)
515 });
516 if rgba_image.is_err() {
517 log("Failed to create image bitmap.");
518 return Err(JsError::new("Failed to create image bitmap."));
519 }
520 let rgba_image = rgba_image.unwrap();
521 Ok(ImageBitmap {
522 x,
523 y,
524 width,
525 height,
526 data_width,
527 data_height,
528 rgba_image,
529 })
530 }
531 #[wasm_bindgen(getter, return_description = "The pixel data of the bitmap.")]
533 pub fn data(&self) -> Vec<u8> {
534 self.rgba_image.clone().into_raw()
535 }
536 #[wasm_bindgen]
538 pub fn set_data(
539 &mut self,
540 #[wasm_bindgen(param_description = "The number of pixels in a row of the bitmap.")]
541 data_width: f32,
542 #[wasm_bindgen(param_description = "The number of pixels in a column of the bitmap.")]
543 data_height: f32,
544 #[wasm_bindgen(param_description = "The pixel data of the bitmap.")]
545 data: Vec<u8>
546 ) -> Result<(), JsError> {
547 self.data_width = data_width as usize;
548 self.data_height = data_height as usize;
549 self.rgba_image = RgbaImage::from_raw(data_width as u32, data_height as u32, data).ok_or(JsError::new("Failed to set image bitmap data."))?;
550 Ok(())
551 }
552 #[wasm_bindgen(return_description = "The default image bitmap.")]
554 pub fn default_image_bitmap() -> ImageBitmap {
555 ImageBitmap::default()
556 }
557 #[wasm_bindgen(return_description = "The color of the pixel.")]
559 pub fn get_pixel(
560 &self,
561 #[wasm_bindgen(param_description = "The point to get the pixel color at.")]
562 p: Point2D
563 ) -> Color {
564 let x = ((p.x - self.x) % self.width / self.width * self.data_width as f32) as u32;
565 let y = ((p.y - self.y) % self.height / self.height * self.data_height as f32) as u32;
566 let pixel = self.rgba_image.get_pixel(x, y);
567 Color::new(pixel[0], pixel[1], pixel[2], pixel[3] as f32 / 255.0)
568 }
569 pub fn set_pixel(
571 &mut self,
572 #[wasm_bindgen(param_description = "The point to set the pixel color at.")]
573 p: Point2D,
574 #[wasm_bindgen(param_description = "The color of the pixel.")]
575 color: &Color
576 ) {
577 let x = ((p.x - self.x) % self.width / self.width * self.data_width as f32) as u32;
578 let y = ((p.y - self.y) % self.height / self.height * self.data_height as f32) as u32;
579 self.rgba_image.put_pixel(x, y, image::Rgba([color.red, color.green, color.blue, (color.alpha * 255.0) as u8]));
580 }
581 #[wasm_bindgen(return_description = "The filled image bitmap.")]
583 pub fn fill(
584 #[wasm_bindgen(param_description = "The x coordinate of the bitmap.")]
585 x: f32,
586 #[wasm_bindgen(param_description = "The y coordinate of the bitmap.")]
587 y: f32,
588 #[wasm_bindgen(param_description = "The width of the bitmap.")]
589 width: f32,
590 #[wasm_bindgen(param_description = "The height of the bitmap.")]
591 height: f32,
592 #[wasm_bindgen(param_description = "Number of pixels in a row of the bitmap.")]
593 data_width: usize,
594 #[wasm_bindgen(param_description = "Number of pixels in a column of the bitmap.")]
595 data_height: usize,
596 #[wasm_bindgen(param_description = "The color to fill the bitmap with.")]
597 color: &Color
598 ) -> ImageBitmap {
599 let rgba_image = RgbaImage::from_pixel(data_width as u32, data_height as u32, image::Rgba([color.red, color.green, color.blue, (color.alpha * 255.0) as u8]));
600 ImageBitmap {
601 x,
602 y,
603 width,
604 height,
605 data_width,
606 data_height,
607 rgba_image: rgba_image.clone(),
608 }
609 }
610 #[wasm_bindgen(return_description = "The filled image bitmap.")]
612 pub fn fill_linear_gradient(
613 #[wasm_bindgen(param_description = "The x coordinate of the bitmap.")]
614 x: f32,
615 #[wasm_bindgen(param_description = "The y coordinate of the bitmap.")]
616 y: f32,
617 #[wasm_bindgen(param_description = "The width of the bitmap.")]
618 width: f32,
619 #[wasm_bindgen(param_description = "The height of the bitmap.")]
620 height: f32,
621 #[wasm_bindgen(param_description = "Number of pixels in a row of the bitmap.")]
622 data_width: usize,
623 #[wasm_bindgen(param_description = "Number of pixels in a column of the bitmap.")]
624 data_height: usize,
625 #[wasm_bindgen(param_description = "The linear gradient to fill the bitmap with.")]
626 gradient: &LinearGradient,
627 ) -> ImageBitmap {
628 let rgba_image = RgbaImage::from_fn(data_width as u32, data_height as u32, |x_raw, y_raw| {
629 let p = Point2D::new(x_raw as f32 / data_width as f32 * width + x, y_raw as f32 / data_height as f32 * height + y);
630 let color = gradient.color_at(p);
631 image::Rgba([color.red, color.green, color.blue, (color.alpha * 255.0) as u8])
632 });
633 ImageBitmap {
634 x,
635 y,
636 width,
637 height,
638 data_width,
639 data_height,
640 rgba_image: rgba_image.clone(),
641 }
642 }
643 #[wasm_bindgen(return_description = "The filled image bitmap.")]
645 pub fn fill_radial_gradient(
646 #[wasm_bindgen(param_description = "The x coordinate of the bitmap.")]
647 x: f32,
648 #[wasm_bindgen(param_description = "The y coordinate of the bitmap.")]
649 y: f32,
650 #[wasm_bindgen(param_description = "The width of the bitmap.")]
651 width: f32,
652 #[wasm_bindgen(param_description = "The height of the bitmap.")]
653 height: f32,
654 #[wasm_bindgen(param_description = "Number of pixels in a row of the bitmap.")]
655 data_width: usize,
656 #[wasm_bindgen(param_description = "Number of pixels in a column of the bitmap.")]
657 data_height: usize,
658 #[wasm_bindgen(param_description = "The radial gradient to fill the bitmap with.")]
659 gradient: &RadialGradient,
660 ) -> ImageBitmap {
661 let rgba_image = RgbaImage::from_fn(data_width as u32, data_height as u32, |x_raw, y_raw| {
662 let p = Point2D::new(x_raw as f32 / data_width as f32 * width + x, y_raw as f32 / data_height as f32 * height + y);
663 let color = gradient.color_at(p);
664 image::Rgba([color.red, color.green, color.blue, (color.alpha * 255.0) as u8])
665 });
666 ImageBitmap {
667 x,
668 y,
669 width,
670 height,
671 data_width,
672 data_height,
673 rgba_image: rgba_image.clone(),
674 }
675 }
676 #[wasm_bindgen(getter, return_description = "The base64 encoded string of the image bitmap.")]
678 pub fn base64(&self) -> Result<String, String> {
679 let mut png_data = vec![];
680 let encoder = PngEncoder::new(&mut png_data);
681 self.rgba_image.write_with_encoder(encoder).map_err(|e| e.to_string())?;
682 let base64 = BASE64_STANDARD.encode(&png_data);
683 Ok(base64)
684 }
685 #[wasm_bindgen(return_description = "The interpolated image bitmap.")]
687 pub fn lerp(
688 #[wasm_bindgen(param_description = "The first image bitmap.")]
689 bitmap1: &ImageBitmap,
690 #[wasm_bindgen(param_description = "The second image bitmap.")]
691 bitmap2: &ImageBitmap,
692 #[wasm_bindgen(param_description = "The progress value.")]
693 t: f32,
694 ) -> ImageBitmap {
695 let x = bitmap1.x.min(bitmap2.x);
696 let y = bitmap1.y.min(bitmap2.y);
697 let width = bitmap1.width.max(bitmap2.width);
698 let height = bitmap1.height.max(bitmap2.height);
699 let data_width = bitmap1.data_width.max(bitmap2.data_width);
700 let data_height = bitmap1.data_height.max(bitmap2.data_height);
701 let new_image = RgbaImage::from_fn(data_width as u32, data_height as u32, |x_raw, y_raw| {
702 let p = Point2D::new(x_raw as f32 / data_width as f32 * width + x, y_raw as f32 / data_height as f32 * height + y);
703 let color1 = bitmap1.get_pixel(p);
704 let color2 = bitmap2.get_pixel(p);
705 let color = Color::lerp(&color1, &color2, t);
706 image::Rgba([color.red, color.green, color.blue, (color.alpha * 255.0) as u8])
707 });
708 ImageBitmap {
709 x,
710 y,
711 width,
712 height,
713 data_width,
714 data_height,
715 rgba_image: new_image.clone(),
716 }
717 }
718}
719
720#[wasm_bindgen]
722#[derive(Debug, Clone)]
723pub struct Style {
724 color: Option<Color>,
726 linear_gradient: Option<LinearGradient>,
728 radial_gradient: Option<RadialGradient>,
730 image: Option<ImageBitmap>,
732}
733
734impl Default for Style {
735 fn default() -> Self {
736 Style {
737 color: Color::default().into(),
738 linear_gradient: None,
739 radial_gradient: None,
740 image: None,
741 }
742 }
743}
744
745#[wasm_bindgen]
746impl Style {
747 #[wasm_bindgen(constructor, return_description = "A new style.")]
749 pub fn new(
750 #[wasm_bindgen(param_description = "The color of the style, if provided.")]
751 color: Option<Color>,
752 #[wasm_bindgen(param_description = "The linear gradient of the style, if provided.")]
753 linear_gradient: Option<LinearGradient>,
754 #[wasm_bindgen(param_description = "The radial gradient of the style, if provided.")]
755 radial_gradient: Option<RadialGradient>,
756 #[wasm_bindgen(param_description = "The image of the style, if provided.")]
757 image: Option<ImageBitmap>
758 ) -> Result<Style, JsError> {
759 let mut not_none = 0;
760 if color.is_some() {
761 not_none += 1;
762 }
763 if linear_gradient.is_some() {
764 not_none += 1;
765 }
766 if radial_gradient.is_some() {
767 not_none += 1;
768 }
769 if image.is_some() {
770 not_none += 1;
771 }
772 if not_none != 1 {
773 let err = JsError::new("Exactly one of color, linear_gradient, radial_gradient, or image must be provided.");
774 return Err(err);
775 }
776 Ok(Style {
777 color,
778 linear_gradient,
779 radial_gradient,
780 image,
781 })
782 }
783 #[wasm_bindgen(js_name = clone, return_description = "The cloned style.")]
785 pub fn clone_js(&self) -> Style {
786 self.clone()
787 }
788 #[wasm_bindgen(return_description = "A new style from the color.")]
790 pub fn from_color(
791 #[wasm_bindgen(param_description = "The color of the style.")]
792 color: Color
793 ) -> Style {
794 Style::new(Some(color), None, None, None).unwrap()
795 }
796 #[wasm_bindgen(return_description = "A new style from the linear gradient.")]
798 pub fn from_linear_gradient(
799 #[wasm_bindgen(param_description = "The linear gradient of the style.")]
800 gradient: LinearGradient
801 ) -> Style {
802 Style::new(None, Some(gradient), None, None).unwrap()
803 }
804 #[wasm_bindgen(return_description = "A new style from the radial gradient.")]
806 pub fn from_radial_gradient(gradient: RadialGradient) -> Style {
807 Style::new(None, None, Some(gradient), None).unwrap()
808 }
809 #[wasm_bindgen(return_description = "A new style from the image.")]
811 pub fn from_image(
812 #[wasm_bindgen(param_description = "The image of the style.")]
813 image: ImageBitmap
814 ) -> Style {
815 Style::new(None, None, None, Some(image)).unwrap()
816 }
817 #[wasm_bindgen(return_description = "The default style.")]
819 pub fn default_style() -> Style {
820 Style::default()
821 }
822 #[wasm_bindgen(return_description = "The faded style.")]
824 pub fn fade(
825 &self,
826 #[wasm_bindgen(param_description = "The amount to fade the style by.")]
827 amount: f32
828 ) -> Style {
829 let mut color = self.color.clone();
830 let mut linear_gradient = self.linear_gradient.clone();
831 let mut radial_gradient = self.radial_gradient.clone();
832 let mut image = self.image.clone();
833 if let Some(color) = &mut color {
834 color.alpha = color.alpha * (1.0 - amount);
835 }
836 if let Some(linear_gradient) = &mut linear_gradient {
837 for stop in Rc::make_mut(&mut linear_gradient.color_stops) {
838 stop.color.alpha = stop.color.alpha * (1.0 - amount);
839 }
840 }
841 if let Some(radial_gradient) = &mut radial_gradient {
842 for stop in Rc::make_mut(&mut radial_gradient.color_stops) {
843 stop.color.alpha = stop.color.alpha * (1.0 - amount);
844 }
845 }
846 if let Some(image) = &mut image {
847 for pixel in image.rgba_image.pixels_mut() {
848 pixel[3] = (pixel[3] as f32 * (1.0 - amount)) as u8;
849 }
850 }
851 Style {
852 color,
853 linear_gradient,
854 radial_gradient,
855 image,
856 }
857 }
858 #[wasm_bindgen(getter, return_description = "The color of the style.")]
860 pub fn color(&self) -> Option<Color> {
861 self.color.clone()
862 }
863 #[wasm_bindgen(setter)]
865 pub fn set_color(
866 &mut self,
867 #[wasm_bindgen(param_description = "The color of the style.")]
868 color: Color
869 ) {
870 self.color = Some(color);
871 self.linear_gradient = None;
872 self.radial_gradient = None;
873 self.image = None;
874 }
875 #[wasm_bindgen(getter, return_description = "The linear gradient of the style.")]
877 pub fn linear_gradient(&self) -> Option<LinearGradient> {
878 self.linear_gradient.clone()
879 }
880 #[wasm_bindgen(setter)]
882 pub fn set_linear_gradient(
883 &mut self,
884 #[wasm_bindgen(param_description = "The linear gradient of the style.")]
885 linear_gradient: LinearGradient
886 ) {
887 self.color = None;
888 self.linear_gradient = Some(linear_gradient);
889 self.radial_gradient = None;
890 self.image = None;
891 }
892 #[wasm_bindgen(getter, return_description = "The radial gradient of the style.")]
894 pub fn radial_gradient(&self) -> Option<RadialGradient> {
895 self.radial_gradient.clone()
896 }
897 #[wasm_bindgen(setter)]
899 pub fn set_radial_gradient(
900 &mut self,
901 #[wasm_bindgen(param_description = "The radial gradient of the style.")]
902 radial_gradient: RadialGradient
903 ) {
904 self.color = None;
905 self.linear_gradient = None;
906 self.radial_gradient = Some(radial_gradient);
907 self.image = None;
908 }
909 #[wasm_bindgen(getter, return_description = "The image of the style.")]
911 pub fn image(&self) -> Option<ImageBitmap> {
912 self.image.clone()
913 }
914 #[wasm_bindgen(setter)]
916 pub fn set_image(&mut self, image: ImageBitmap) {
917 self.color = None;
918 self.linear_gradient = None;
919 self.radial_gradient = None;
920 self.image = Some(image);
921 }
922 #[wasm_bindgen(return_description = "The color at the given point.")]
924 pub fn color_at(
925 &self,
926 #[wasm_bindgen(param_description = "The x coordinate of the point.")]
927 p: Point2D
928 ) -> Color {
929 if let Some(color) = self.color() {
930 return color;
931 }
932 if let Some(linear_gradient) = self.linear_gradient() {
933 return linear_gradient.color_at(p);
934 }
935 if let Some(radial_gradient) = self.radial_gradient() {
936 return radial_gradient.color_at(p);
937 }
938 if let Some(image) = self.image() {
939 return image.get_pixel(p);
940 }
941 Color::default()
942 }
943 #[wasm_bindgen(return_description = "The interpolated style.")]
945 pub fn lerp(
946 #[wasm_bindgen(param_description = "The first style.")]
947 style1: &Style,
948 #[wasm_bindgen(param_description = "The second style.")]
949 style2: &Style,
950 #[wasm_bindgen(param_description = "The progress value.")]
951 t: f32,
952 #[wasm_bindgen(param_description = "Top left x coordinate of the bitmap. Must be provided if both styles are images.")]
953 x: Option<f32>,
954 #[wasm_bindgen(param_description = "Top left y coordinate of the bitmap. Must be provided if both styles are images.")]
955 y: Option<f32>,
956 #[wasm_bindgen(param_description = "Width of the bitmap. Must be provided if both styles are images.")]
957 width: Option<f32>,
958 #[wasm_bindgen(param_description = "Height of the bitmap. Must be provided if both styles are images.")]
959 height: Option<f32>,
960 #[wasm_bindgen(param_description = "Number of pixels in a row of the bitmap. Must be provided if both styles are different kinds of gradients or one of them is an image.")]
961 data_width: Option<usize>,
962 #[wasm_bindgen(param_description = "Number of pixels in a column of the bitmap. Must be provided if both styles are different kinds of gradients or one of them is an image.")]
963 data_height: Option<usize>
964 ) -> Result<Style, String> {
965 let color1 = style1.color();
966 let color2 = style2.color();
967 let linear_gradient1 = style1.linear_gradient();
968 let linear_gradient2 = style2.linear_gradient();
969 let radial_gradient1 = style1.radial_gradient();
970 let radial_gradient2 = style2.radial_gradient();
971 let image1 = style1.image();
972 let image2 = style2.image();
973 if color1.is_some() && color2.is_some() {
974 let color = Color::lerp(&color1.unwrap(), &color2.unwrap(), t);
975 return Ok(Style::from_color(color));
976 }
977 if linear_gradient1.is_some() && linear_gradient2.is_some() {
978 let linear_gradient = LinearGradient::lerp(&linear_gradient1.unwrap(), &linear_gradient2.unwrap(), t);
979 return Ok(Style::from_linear_gradient(linear_gradient));
980 }
981 if radial_gradient1.is_some() && radial_gradient2.is_some() {
982 let radial_gradient = RadialGradient::lerp(&radial_gradient1.unwrap(), &radial_gradient2.unwrap(), t);
983 return Ok(Style::from_radial_gradient(radial_gradient));
984 }
985 if image1.is_some() && image2.is_some() {
986 let image = ImageBitmap::lerp(&image1.unwrap(), &image2.unwrap(), t);
987 return Ok(Style::from_image(image));
988 }
989 if color1.is_some() {
990 let color = color1.unwrap();
991 if linear_gradient2.is_some() {
992 let linear_gradient2 = linear_gradient2.unwrap();
993 let linear_gradient1 = LinearGradient::single_color_gradient(linear_gradient2.p1, linear_gradient2.p2, color, Some(linear_gradient2.color_stops.len()));
994 return Style::lerp(&Style::from_linear_gradient(linear_gradient1), &Style::from_linear_gradient(linear_gradient2), t, x, y, width, height, data_width, data_height);
995 }
996 if radial_gradient2.is_some() {
997 let radial_gradient2 = radial_gradient2.unwrap();
998 let radial_gradient1 = RadialGradient::single_color_gradient(radial_gradient2.f, radial_gradient2.c, radial_gradient2.r, color, Some(radial_gradient2.color_stops.len()));
999 return Style::lerp(&Style::from_radial_gradient(radial_gradient1), &Style::from_radial_gradient(radial_gradient2), t, x, y, width, height, data_width, data_height);
1000 }
1001 if image2.is_some() {
1002 if x.is_none() || y.is_none() || width.is_none() || height.is_none() {
1003 return Err("Bitmap data must be provided if one of the styles is an image.".to_string());
1004 }
1005 let image2 = image2.unwrap();
1006 let image1 = ImageBitmap::fill(x.unwrap(), y.unwrap(), width.unwrap(), height.unwrap(), data_width.unwrap(), data_height.unwrap(), &color);
1007 return Style::lerp(&Style::from_image(image1), &Style::from_image(image2), t, x, y, width, height, data_width, data_height);
1008 }
1009 }
1010 if linear_gradient1.is_some() {
1011 let linear_gradient1 = linear_gradient1.unwrap();
1012 if color2.is_some() {
1013 let color2 = color2.unwrap();
1014 let linear_gradient2 = LinearGradient::single_color_gradient(linear_gradient1.p1, linear_gradient1.p2, color2, Some(linear_gradient1.color_stops.len()));
1015 return Style::lerp(&Style::from_linear_gradient(linear_gradient1), &Style::from_linear_gradient(linear_gradient2), t, x, y, width, height, data_width, data_height);
1016 }
1017 if radial_gradient2.is_some() {
1018 if x.is_none() || y.is_none() || width.is_none() || height.is_none() {
1019 return Err("Bitmap data must be provided if both styles are different kinds of gradients.".to_string());
1020 }
1021 let radial_gradient2 = radial_gradient2.unwrap();
1022 let image2 = ImageBitmap::fill_radial_gradient(x.unwrap(), y.unwrap(), width.unwrap(), height.unwrap(), data_width.unwrap(), data_height.unwrap(), &radial_gradient2);
1023 let image1 = ImageBitmap::fill_linear_gradient(x.unwrap(), y.unwrap(), width.unwrap(), height.unwrap(), data_width.unwrap(), data_height.unwrap(), &linear_gradient1);
1024 return Style::lerp(&Style::from_image(image1), &Style::from_image(image2), t, x, y, width, height, data_width, data_height);
1025 }
1026 if image2.is_some() {
1027 if x.is_none() || y.is_none() || width.is_none() || height.is_none() {
1028 return Err("Bitmap data must be provided if both styles are different kinds of gradients.".to_string());
1029 }
1030 let image2 = image2.unwrap();
1031 let image1 = ImageBitmap::fill_linear_gradient(x.unwrap(), y.unwrap(), width.unwrap(), height.unwrap(), data_width.unwrap(), data_height.unwrap(), &linear_gradient1);
1032 return Style::lerp(&Style::from_image(image1), &Style::from_image(image2), t, x, y, width, height, data_width, data_height);
1033 }
1034 }
1035 if radial_gradient1.is_some() {
1036 let radial_gradient1 = radial_gradient1.unwrap();
1037 if color2.is_some() {
1038 let color2 = color2.unwrap();
1039 let radial_gradient2 = RadialGradient::single_color_gradient(radial_gradient1.f, radial_gradient1.c, radial_gradient1.r, color2, Some(radial_gradient1.color_stops.len()));
1040 return Style::lerp(&Style::from_radial_gradient(radial_gradient1), &Style::from_radial_gradient(radial_gradient2), t, x, y, width, height, data_width, data_height);
1041 }
1042 if linear_gradient2.is_some() {
1043 if x.is_none() || y.is_none() || width.is_none() || height.is_none() {
1044 return Err("Bitmap data must be provided if both styles are different kinds of gradients.".to_string());
1045 }
1046 let linear_gradient2 = linear_gradient2.unwrap();
1047 let image2 = ImageBitmap::fill_linear_gradient(x.unwrap(), y.unwrap(), width.unwrap(), height.unwrap(), data_width.unwrap(), data_height.unwrap(), &linear_gradient2);
1048 let image1 = ImageBitmap::fill_radial_gradient(x.unwrap(), y.unwrap(), width.unwrap(), height.unwrap(), data_width.unwrap(), data_height.unwrap(), &radial_gradient1);
1049 return Style::lerp(&Style::from_image(image1), &Style::from_image(image2), t, x, y, width, height, data_width, data_height);
1050 }
1051 if image2.is_some() {
1052 if x.is_none() || y.is_none() || width.is_none() || height.is_none() {
1053 return Err("Bitmap data must be provided if both styles are different kinds of gradients.".to_string());
1054 }
1055 let image2 = image2.unwrap();
1056 let image1 = ImageBitmap::fill_radial_gradient(x.unwrap(), y.unwrap(), width.unwrap(), height.unwrap(), data_width.unwrap(), data_height.unwrap(), &radial_gradient1);
1057 return Style::lerp(&Style::from_image(image1), &Style::from_image(image2), t, x, y, width, height, data_width, data_height);
1058 }
1059 }
1060 if image1.is_some() {
1061 let image1 = image1.unwrap();
1062 if color2.is_some() {
1063 if x.is_none() || y.is_none() || width.is_none() || height.is_none() {
1064 return Err("Bitmap data must be provided if one of the styles is an image.".to_string());
1065 }
1066 let color2 = color2.unwrap();
1067 let image2 = ImageBitmap::fill(x.unwrap(), y.unwrap(), width.unwrap(), height.unwrap(), data_width.unwrap(), data_height.unwrap(), &color2);
1068 return Style::lerp(&Style::from_image(image1), &Style::from_image(image2), t, x, y, width, height, data_width, data_height);
1069 }
1070 if linear_gradient2.is_some() {
1071 if x.is_none() || y.is_none() || width.is_none() || height.is_none() {
1072 return Err("Bitmap data must be provided if one of the styles is an image.".to_string());
1073 }
1074 let linear_gradient2 = linear_gradient2.unwrap();
1075 let image2 = ImageBitmap::fill_linear_gradient(x.unwrap(), y.unwrap(), width.unwrap(), height.unwrap(), data_width.unwrap(), data_height.unwrap(), &linear_gradient2);
1076 return Style::lerp(&Style::from_image(image1), &Style::from_image(image2), t, x, y, width, height, data_width, data_height);
1077 }
1078 if radial_gradient2.is_some() {
1079 if x.is_none() || y.is_none() || width.is_none() || height.is_none() {
1080 return Err("Bitmap data must be provided if one of the styles is an image.".to_string());
1081 }
1082 let radial_gradient2 = radial_gradient2.unwrap();
1083 let image2 = ImageBitmap::fill_radial_gradient(x.unwrap(), y.unwrap(), width.unwrap(), height.unwrap(), data_width.unwrap(), data_height.unwrap(), &radial_gradient2);
1084 return Style::lerp(&Style::from_image(image1), &Style::from_image(image2), t, x, y, width, height, data_width, data_height);
1085 }
1086 }
1087 Err("Exactly one of color, linear_gradient, radial_gradient, or image must be provided.".to_string())
1088 }
1089}
1090
1091impl Style {
1092 pub fn from_paint_and_opacity(
1094 paint: &Paint,
1095 opacity: &Opacity,
1096 ) -> Style {
1097 match &paint {
1098 Paint::Color(color) => {
1099 let color = Color::new(color.red, color.green, color.blue, opacity.get());
1100 Style::from_color(color)
1101 }
1102 Paint::LinearGradient(gradient) => {
1103 let start = Point2D::new(gradient.x1(), gradient.y1());
1104 let end = Point2D::new(gradient.x2(), gradient.y2());
1105 let linear_gradient = LinearGradient::new(start, end, gradient.stops().iter().map(|stop| {
1106 ColorStop {
1107 color: Color::new(stop.color().red, stop.color().green, stop.color().blue, stop.opacity().get() * opacity.get()),
1108 position: stop.offset().get(),
1109 }
1110 }).collect());
1111 Style::from_linear_gradient(linear_gradient)
1112 }
1113 Paint::RadialGradient(gradient) => {
1114 let start = Point2D::new(gradient.fx(), gradient.fy());
1115 let end = Point2D::new(gradient.cx(), gradient.cy());
1116 let radial_gradient = RadialGradient::new(start, end, gradient.r().get(), gradient.stops().iter().map(|stop| {
1117 ColorStop {
1118 color: Color::new(stop.color().red, stop.color().green, stop.color().blue, stop.opacity().get() * opacity.get()),
1119 position: stop.offset().get(),
1120 }
1121 }).collect());
1122 Style::from_radial_gradient(radial_gradient)
1123 }
1124 Paint::Pattern(pattern) => {
1125 let root = pattern.root();
1126 let bounding_box = pattern.rect();
1127 let x = bounding_box.x();
1128 let y = bounding_box.y();
1129 let width = bounding_box.width();
1130 let height = bounding_box.height();
1131 for child in root.children() {
1132 return Style::from_pattern_child(&child, x, y, width, height);
1133 }
1134 log("Unsupported pattern. Fallback to default style (fully transparent black).");
1135 Style::default()
1136 }
1137 }
1138 }
1139 pub fn from_pattern_child(
1140 child: &usvg::Node,
1141 x: f32,
1142 y: f32,
1143 width: f32,
1144 height: f32,
1145 ) -> Style {
1146 match &child {
1147 usvg::Node::Image(image) => {
1148 let kind = image.kind();
1149 let size = image.size();
1150 let data_width = size.width().round() as usize;
1151 let data_height = size.height().round() as usize;
1152 match &kind {
1153 usvg::ImageKind::JPEG(data) => {
1154 let new_data = data.to_vec();
1155 let image = ImageBitmap::new(x, y, width, height, data_width, data_height, new_data);
1156 if image.is_err() {
1157 log("Failed to create image bitmap.");
1158 return Style::default();
1159 }
1160 return Style::from_image(image.unwrap());
1161 }
1162 usvg::ImageKind::PNG(data) => {
1163 let new_data = data.to_vec();
1164 let image = ImageBitmap::new(x, y, width, height, data_width, data_height, new_data);
1165 if image.is_err() {
1166 log("Failed to create image bitmap.");
1167 return Style::default();
1168 }
1169 return Style::from_image(image.unwrap());
1170 }
1171 usvg::ImageKind::WEBP(data) => {
1172 let new_data = data.to_vec();
1173 let image = ImageBitmap::new(x, y, width, height, data_width, data_height, new_data);
1174 if image.is_err() {
1175 log("Failed to create image bitmap.");
1176 return Style::default();
1177 }
1178 return Style::from_image(image.unwrap());
1179 }
1180 _ => {
1181 log("Unsupported image format. Fallback to default style (fully transparent black).");
1182 return Style::default();
1183 }
1184 }
1185 }
1186 usvg::Node::Group(group) => {
1187 for child in group.children() {
1188 return Style::from_pattern_child(&child, x, y, width, height);
1189 }
1190 log("Unsupported pattern. Fallback to default style (fully transparent black).");
1191 return Style::default();
1192 }
1193 _ => {
1194 log("Unsupported pattern. Fallback to default style (fully transparent black).");
1195 return Style::default();
1196 }
1197 }
1198 }
1199}