1use std::rc::Rc;
6
7use rgb::{FromSlice, RGBA8};
8use tiny_skia::IntRect;
9use usvg::{ApproxEqUlps, ApproxZeroUlps};
10
11mod box_blur;
12mod color_matrix;
13mod component_transfer;
14mod composite;
15mod convolve_matrix;
16mod displacement_map;
17mod iir_blur;
18mod lighting;
19mod morphology;
20mod turbulence;
21
22#[derive(Clone, Copy)]
31pub struct ImageRef<'a> {
32 data: &'a [RGBA8],
33 width: u32,
34 height: u32,
35}
36
37impl<'a> ImageRef<'a> {
38 #[inline]
42 pub fn new(width: u32, height: u32, data: &'a [RGBA8]) -> Self {
43 ImageRef {
44 data,
45 width,
46 height,
47 }
48 }
49
50 #[inline]
51 fn alpha_at(&self, x: u32, y: u32) -> i16 {
52 self.data[(self.width * y + x) as usize].a as i16
53 }
54}
55
56pub struct ImageRefMut<'a> {
58 data: &'a mut [RGBA8],
59 width: u32,
60 height: u32,
61}
62
63impl<'a> ImageRefMut<'a> {
64 #[inline]
68 pub fn new(width: u32, height: u32, data: &'a mut [RGBA8]) -> Self {
69 ImageRefMut {
70 data,
71 width,
72 height,
73 }
74 }
75
76 #[inline]
77 fn pixel_at(&self, x: u32, y: u32) -> RGBA8 {
78 self.data[(self.width * y + x) as usize]
79 }
80
81 #[inline]
82 fn pixel_at_mut(&mut self, x: u32, y: u32) -> &mut RGBA8 {
83 &mut self.data[(self.width * y + x) as usize]
84 }
85}
86
87#[derive(Debug)]
88pub(crate) enum Error {
89 InvalidRegion,
90 NoResults,
91}
92
93trait PixmapExt: Sized {
94 fn try_create(width: u32, height: u32) -> Result<tiny_skia::Pixmap, Error>;
95 fn copy_region(&self, region: IntRect) -> Result<tiny_skia::Pixmap, Error>;
96 fn clear(&mut self);
97 fn into_srgb(&mut self);
98 fn into_linear_rgb(&mut self);
99}
100
101impl PixmapExt for tiny_skia::Pixmap {
102 fn try_create(width: u32, height: u32) -> Result<tiny_skia::Pixmap, Error> {
103 tiny_skia::Pixmap::new(width, height).ok_or(Error::InvalidRegion)
104 }
105
106 fn copy_region(&self, region: IntRect) -> Result<tiny_skia::Pixmap, Error> {
107 let rect = IntRect::from_xywh(region.x(), region.y(), region.width(), region.height())
108 .ok_or(Error::InvalidRegion)?;
109 self.clone_rect(rect).ok_or(Error::InvalidRegion)
110 }
111
112 fn clear(&mut self) {
113 self.fill(tiny_skia::Color::TRANSPARENT);
114 }
115
116 fn into_srgb(&mut self) {
117 demultiply_alpha(self.data_mut().as_rgba_mut());
118 from_linear_rgb(self.data_mut().as_rgba_mut());
119 multiply_alpha(self.data_mut().as_rgba_mut());
120 }
121
122 fn into_linear_rgb(&mut self) {
123 demultiply_alpha(self.data_mut().as_rgba_mut());
124 into_linear_rgb(self.data_mut().as_rgba_mut());
125 multiply_alpha(self.data_mut().as_rgba_mut());
126 }
127}
128
129fn multiply_alpha(data: &mut [RGBA8]) {
131 for p in data {
132 let a = p.a as f32 / 255.0;
133 p.b = (p.b as f32 * a + 0.5) as u8;
134 p.g = (p.g as f32 * a + 0.5) as u8;
135 p.r = (p.r as f32 * a + 0.5) as u8;
136 }
137}
138
139fn demultiply_alpha(data: &mut [RGBA8]) {
141 for p in data {
142 let a = p.a as f32 / 255.0;
143 p.b = (p.b as f32 / a + 0.5) as u8;
144 p.g = (p.g as f32 / a + 0.5) as u8;
145 p.r = (p.r as f32 / a + 0.5) as u8;
146 }
147}
148
149#[rustfmt::skip]
163const SRGB_TO_LINEAR_RGB_TABLE: &[u8; 256] = &[
164 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
165 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3,
166 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7,
167 8, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13,
168 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 17, 18, 18, 19, 19, 20,
169 20, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29,
170 30, 30, 31, 32, 32, 33, 34, 35, 35, 36, 37, 37, 38, 39, 40, 41,
171 41, 42, 43, 44, 45, 45, 46, 47, 48, 49, 50, 51, 51, 52, 53, 54,
172 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
173 71, 72, 73, 74, 76, 77, 78, 79, 80, 81, 82, 84, 85, 86, 87, 88,
174 90, 91, 92, 93, 95, 96, 97, 99, 100, 101, 103, 104, 105, 107, 108, 109,
175 111, 112, 114, 115, 116, 118, 119, 121, 122, 124, 125, 127, 128, 130, 131, 133,
176 134, 136, 138, 139, 141, 142, 144, 146, 147, 149, 151, 152, 154, 156, 157, 159,
177 161, 163, 164, 166, 168, 170, 171, 173, 175, 177, 179, 181, 183, 184, 186, 188,
178 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220,
179 222, 224, 226, 229, 231, 233, 235, 237, 239, 242, 244, 246, 248, 250, 253, 255,
180];
181
182#[rustfmt::skip]
196const LINEAR_RGB_TO_SRGB_TABLE: &[u8; 256] = &[
197 0, 13, 22, 28, 34, 38, 42, 46, 50, 53, 56, 59, 61, 64, 66, 69,
198 71, 73, 75, 77, 79, 81, 83, 85, 86, 88, 90, 92, 93, 95, 96, 98,
199 99, 101, 102, 104, 105, 106, 108, 109, 110, 112, 113, 114, 115, 117, 118, 119,
200 120, 121, 122, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136,
201 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149, 150, 151,
202 152, 153, 154, 155, 155, 156, 157, 158, 159, 159, 160, 161, 162, 163, 163, 164,
203 165, 166, 167, 167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175, 175, 176,
204 177, 178, 178, 179, 180, 180, 181, 182, 182, 183, 184, 185, 185, 186, 187, 187,
205 188, 189, 189, 190, 190, 191, 192, 192, 193, 194, 194, 195, 196, 196, 197, 197,
206 198, 199, 199, 200, 200, 201, 202, 202, 203, 203, 204, 205, 205, 206, 206, 207,
207 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216,
208 216, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224,
209 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233,
210 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 238, 239, 239, 240, 240,
211 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 246, 247, 247, 248,
212 248, 249, 249, 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255, 255,
213];
214
215fn into_linear_rgb(data: &mut [RGBA8]) {
221 for p in data {
222 p.r = SRGB_TO_LINEAR_RGB_TABLE[p.r as usize];
223 p.g = SRGB_TO_LINEAR_RGB_TABLE[p.g as usize];
224 p.b = SRGB_TO_LINEAR_RGB_TABLE[p.b as usize];
225 }
226}
227
228fn from_linear_rgb(data: &mut [RGBA8]) {
234 for p in data {
235 p.r = LINEAR_RGB_TO_SRGB_TABLE[p.r as usize];
236 p.g = LINEAR_RGB_TO_SRGB_TABLE[p.g as usize];
237 p.b = LINEAR_RGB_TO_SRGB_TABLE[p.b as usize];
238 }
239}
240
241#[inline]
243fn f32_bound(min: f32, val: f32, max: f32) -> f32 {
244 debug_assert!(min.is_finite());
245 debug_assert!(val.is_finite());
246 debug_assert!(max.is_finite());
247
248 if val > max {
249 max
250 } else if val < min {
251 min
252 } else {
253 val
254 }
255}
256
257#[derive(Clone)]
258struct Image {
259 image: Rc<tiny_skia::Pixmap>,
263
264 region: IntRect,
272
273 color_space: usvg::filter::ColorInterpolation,
275}
276
277impl Image {
278 fn from_image(image: tiny_skia::Pixmap, color_space: usvg::filter::ColorInterpolation) -> Self {
279 let (w, h) = (image.width(), image.height());
280 Image {
281 image: Rc::new(image),
282 region: IntRect::from_xywh(0, 0, w, h).unwrap(),
283 color_space,
284 }
285 }
286
287 fn into_color_space(
288 self,
289 color_space: usvg::filter::ColorInterpolation,
290 ) -> Result<Self, Error> {
291 if color_space != self.color_space {
292 let region = self.region;
293
294 let mut image = self.take()?;
295
296 match color_space {
297 usvg::filter::ColorInterpolation::SRGB => image.into_srgb(),
298 usvg::filter::ColorInterpolation::LinearRGB => image.into_linear_rgb(),
299 }
300
301 Ok(Image {
302 image: Rc::new(image),
303 region,
304 color_space,
305 })
306 } else {
307 Ok(self)
308 }
309 }
310
311 fn take(self) -> Result<tiny_skia::Pixmap, Error> {
312 match Rc::try_unwrap(self.image) {
313 Ok(v) => Ok(v),
314 Err(v) => Ok((*v).clone()),
315 }
316 }
317
318 fn width(&self) -> u32 {
319 self.image.width()
320 }
321
322 fn height(&self) -> u32 {
323 self.image.height()
324 }
325
326 fn as_ref(&self) -> &tiny_skia::Pixmap {
327 &self.image
328 }
329}
330
331struct FilterResult {
332 name: String,
333 image: Image,
334}
335
336pub fn apply(
337 filter: &usvg::filter::Filter,
338 ts: tiny_skia::Transform,
339 source: &mut tiny_skia::Pixmap,
340) {
341 let result = apply_inner(filter, ts, source);
342 let result = result.and_then(|image| apply_to_canvas(image, source));
343
344 if result.is_err() {
346 source.fill(tiny_skia::Color::TRANSPARENT);
347 }
348
349 match result {
350 Ok(_) => {}
351 Err(Error::InvalidRegion) => {
352 log::warn!("Filter has an invalid region.");
353 }
354 Err(Error::NoResults) => {}
355 }
356}
357
358fn apply_inner(
359 filter: &usvg::filter::Filter,
360 ts: usvg::Transform,
361 source: &mut tiny_skia::Pixmap,
362) -> Result<Image, Error> {
363 let region = filter
364 .rect()
365 .transform(ts)
366 .map(|r| r.to_int_rect())
367 .ok_or(Error::InvalidRegion)?;
368
369 let mut results: Vec<FilterResult> = Vec::new();
370
371 for primitive in filter.primitives() {
372 let mut subregion = primitive
373 .rect()
374 .transform(ts)
375 .map(|r| r.to_int_rect())
376 .ok_or(Error::InvalidRegion)?;
377
378 if let usvg::filter::Kind::Offset(ref fe) = primitive.kind() {
380 if let usvg::filter::Input::Reference(ref name) = fe.input() {
381 if let Some(res) = results.iter().rev().find(|v| v.name == *name) {
382 subregion = res.image.region;
383 }
384 }
385 }
386
387 let cs = primitive.color_interpolation();
388
389 let mut result = match primitive.kind() {
390 usvg::filter::Kind::Blend(ref fe) => {
391 let input1 = get_input(fe.input1(), region, source, &results)?;
392 let input2 = get_input(fe.input2(), region, source, &results)?;
393 apply_blend(fe, cs, region, input1, input2)
394 }
395 usvg::filter::Kind::DropShadow(ref fe) => {
396 let input = get_input(fe.input(), region, source, &results)?;
397 apply_drop_shadow(fe, cs, ts, input)
398 }
399 usvg::filter::Kind::Flood(ref fe) => apply_flood(fe, region),
400 usvg::filter::Kind::GaussianBlur(ref fe) => {
401 let input = get_input(fe.input(), region, source, &results)?;
402 apply_blur(fe, cs, ts, input)
403 }
404 usvg::filter::Kind::Offset(ref fe) => {
405 let input = get_input(fe.input(), region, source, &results)?;
406 apply_offset(fe, ts, input)
407 }
408 usvg::filter::Kind::Composite(ref fe) => {
409 let input1 = get_input(fe.input1(), region, source, &results)?;
410 let input2 = get_input(fe.input2(), region, source, &results)?;
411 apply_composite(fe, cs, region, input1, input2)
412 }
413 usvg::filter::Kind::Merge(ref fe) => apply_merge(fe, cs, region, source, &results),
414 usvg::filter::Kind::Tile(ref fe) => {
415 let input = get_input(fe.input(), region, source, &results)?;
416 apply_tile(input, region)
417 }
418 usvg::filter::Kind::Image(ref fe) => apply_image(fe, region, subregion, ts),
419 usvg::filter::Kind::ComponentTransfer(ref fe) => {
420 let input = get_input(fe.input(), region, source, &results)?;
421 apply_component_transfer(fe, cs, input)
422 }
423 usvg::filter::Kind::ColorMatrix(ref fe) => {
424 let input = get_input(fe.input(), region, source, &results)?;
425 apply_color_matrix(fe, cs, input)
426 }
427 usvg::filter::Kind::ConvolveMatrix(ref fe) => {
428 let input = get_input(fe.input(), region, source, &results)?;
429 apply_convolve_matrix(fe, cs, input)
430 }
431 usvg::filter::Kind::Morphology(ref fe) => {
432 let input = get_input(fe.input(), region, source, &results)?;
433 apply_morphology(fe, cs, ts, input)
434 }
435 usvg::filter::Kind::DisplacementMap(ref fe) => {
436 let input1 = get_input(fe.input1(), region, source, &results)?;
437 let input2 = get_input(fe.input2(), region, source, &results)?;
438 apply_displacement_map(fe, region, cs, ts, input1, input2)
439 }
440 usvg::filter::Kind::Turbulence(ref fe) => apply_turbulence(fe, region, cs, ts),
441 usvg::filter::Kind::DiffuseLighting(ref fe) => {
442 let input = get_input(fe.input(), region, source, &results)?;
443 apply_diffuse_lighting(fe, region, cs, ts, input)
444 }
445 usvg::filter::Kind::SpecularLighting(ref fe) => {
446 let input = get_input(fe.input(), region, source, &results)?;
447 apply_specular_lighting(fe, region, cs, ts, input)
448 }
449 }?;
450
451 if region != subregion {
452 let subregion2 = if let usvg::filter::Kind::Offset(..) = primitive.kind() {
456 region.translate_to(0, 0)
458 } else {
459 subregion.translate(-region.x(), -region.y())
460 }
461 .unwrap();
462
463 let color_space = result.color_space;
464
465 let pixmap = {
466 let mut paint = tiny_skia::Paint::default();
468 paint.set_color(tiny_skia::Color::BLACK);
469 paint.blend_mode = tiny_skia::BlendMode::Clear;
470
471 let mut pixmap = result.take()?;
472 let w = pixmap.width() as f32;
473 let h = pixmap.height() as f32;
474
475 if let Some(rect) = tiny_skia::Rect::from_xywh(0.0, 0.0, w, subregion2.y() as f32) {
476 pixmap.fill_rect(rect, &paint, tiny_skia::Transform::identity(), None);
477 }
478
479 if let Some(rect) = tiny_skia::Rect::from_xywh(0.0, 0.0, subregion2.x() as f32, h) {
480 pixmap.fill_rect(rect, &paint, tiny_skia::Transform::identity(), None);
481 }
482
483 if let Some(rect) = tiny_skia::Rect::from_xywh(subregion2.right() as f32, 0.0, w, h)
484 {
485 pixmap.fill_rect(rect, &paint, tiny_skia::Transform::identity(), None);
486 }
487
488 if let Some(rect) =
489 tiny_skia::Rect::from_xywh(0.0, subregion2.bottom() as f32, w, h)
490 {
491 pixmap.fill_rect(rect, &paint, tiny_skia::Transform::identity(), None);
492 }
493
494 pixmap
495 };
496
497 result = Image {
498 image: Rc::new(pixmap),
499 region: subregion,
500 color_space,
501 };
502 }
503
504 results.push(FilterResult {
505 name: primitive.result().to_string(),
506 image: result,
507 });
508 }
509
510 if let Some(res) = results.pop() {
511 Ok(res.image)
512 } else {
513 Err(Error::NoResults)
514 }
515}
516
517fn get_input(
518 input: &usvg::filter::Input,
519 region: IntRect,
520 source: &tiny_skia::Pixmap,
521 results: &[FilterResult],
522) -> Result<Image, Error> {
523 match input {
524 usvg::filter::Input::SourceGraphic => {
525 let image = source.clone();
526
527 Ok(Image {
528 image: Rc::new(image),
529 region,
530 color_space: usvg::filter::ColorInterpolation::SRGB,
531 })
532 }
533 usvg::filter::Input::SourceAlpha => {
534 let mut image = source.clone();
535 for p in image.data_mut().as_rgba_mut() {
537 p.r = 0;
538 p.g = 0;
539 p.b = 0;
540 }
541
542 Ok(Image {
543 image: Rc::new(image),
544 region,
545 color_space: usvg::filter::ColorInterpolation::SRGB,
546 })
547 }
548 usvg::filter::Input::Reference(ref name) => {
549 if let Some(v) = results.iter().rev().find(|v| v.name == *name) {
550 Ok(v.image.clone())
551 } else {
552 log::warn!("Unknown filter primitive reference '{}'.", name);
554 get_input(&usvg::filter::Input::SourceGraphic, region, source, results)
555 }
556 }
557 }
558}
559
560trait PixmapToImageRef<'a> {
561 fn as_image_ref(&'a self) -> ImageRef<'a>;
562 fn as_image_ref_mut(&'a mut self) -> ImageRefMut<'a>;
563}
564
565impl<'a> PixmapToImageRef<'a> for tiny_skia::Pixmap {
566 fn as_image_ref(&'a self) -> ImageRef<'a> {
567 ImageRef::new(self.width(), self.height(), self.data().as_rgba())
568 }
569
570 fn as_image_ref_mut(&'a mut self) -> ImageRefMut<'a> {
571 ImageRefMut::new(self.width(), self.height(), self.data_mut().as_rgba_mut())
572 }
573}
574
575fn apply_drop_shadow(
576 fe: &usvg::filter::DropShadow,
577 cs: usvg::filter::ColorInterpolation,
578 ts: usvg::Transform,
579 input: Image,
580) -> Result<Image, Error> {
581 let (dx, dy) = match scale_coordinates(fe.dx(), fe.dy(), ts) {
582 Some(v) => v,
583 None => return Ok(input),
584 };
585
586 let mut pixmap = tiny_skia::Pixmap::try_create(input.width(), input.height())?;
587 let input_pixmap = input.into_color_space(cs)?.take()?;
588 let mut shadow_pixmap = input_pixmap.clone();
589
590 if let Some((std_dx, std_dy, use_box_blur)) =
591 resolve_std_dev(fe.std_dev_x().get(), fe.std_dev_y().get(), ts)
592 {
593 if use_box_blur {
594 box_blur::apply(std_dx, std_dy, shadow_pixmap.as_image_ref_mut());
595 } else {
596 iir_blur::apply(std_dx, std_dy, shadow_pixmap.as_image_ref_mut());
597 }
598 }
599
600 let color = tiny_skia::Color::from_rgba8(
602 fe.color().red,
603 fe.color().green,
604 fe.color().blue,
605 fe.opacity().to_u8(),
606 );
607 for p in shadow_pixmap.pixels_mut() {
608 let mut color = color;
609 color.apply_opacity(p.alpha() as f32 / 255.0);
610 *p = color.premultiply().to_color_u8();
611 }
612
613 match cs {
614 usvg::filter::ColorInterpolation::SRGB => shadow_pixmap.into_srgb(),
615 usvg::filter::ColorInterpolation::LinearRGB => shadow_pixmap.into_linear_rgb(),
616 }
617
618 pixmap.draw_pixmap(
619 dx as i32,
620 dy as i32,
621 shadow_pixmap.as_ref(),
622 &tiny_skia::PixmapPaint::default(),
623 tiny_skia::Transform::identity(),
624 None,
625 );
626
627 pixmap.draw_pixmap(
628 0,
629 0,
630 input_pixmap.as_ref(),
631 &tiny_skia::PixmapPaint::default(),
632 tiny_skia::Transform::identity(),
633 None,
634 );
635
636 Ok(Image::from_image(pixmap, cs))
637}
638
639fn apply_blur(
640 fe: &usvg::filter::GaussianBlur,
641 cs: usvg::filter::ColorInterpolation,
642 ts: usvg::Transform,
643 input: Image,
644) -> Result<Image, Error> {
645 let (std_dx, std_dy, use_box_blur) =
646 match resolve_std_dev(fe.std_dev_x().get(), fe.std_dev_y().get(), ts) {
647 Some(v) => v,
648 None => return Ok(input),
649 };
650
651 let mut pixmap = input.into_color_space(cs)?.take()?;
652
653 if use_box_blur {
654 box_blur::apply(std_dx, std_dy, pixmap.as_image_ref_mut());
655 } else {
656 iir_blur::apply(std_dx, std_dy, pixmap.as_image_ref_mut());
657 }
658
659 Ok(Image::from_image(pixmap, cs))
660}
661
662fn apply_offset(
663 fe: &usvg::filter::Offset,
664 ts: usvg::Transform,
665 input: Image,
666) -> Result<Image, Error> {
667 let (dx, dy) = match scale_coordinates(fe.dx(), fe.dy(), ts) {
668 Some(v) => v,
669 None => return Ok(input),
670 };
671
672 if dx.approx_zero_ulps(4) && dy.approx_zero_ulps(4) {
673 return Ok(input);
674 }
675
676 let mut pixmap = tiny_skia::Pixmap::try_create(input.width(), input.height())?;
677 pixmap.draw_pixmap(
678 dx as i32,
679 dy as i32,
680 input.as_ref().as_ref(),
681 &tiny_skia::PixmapPaint::default(),
682 tiny_skia::Transform::identity(),
683 None,
684 );
685
686 Ok(Image::from_image(pixmap, input.color_space))
687}
688
689fn apply_blend(
690 fe: &usvg::filter::Blend,
691 cs: usvg::filter::ColorInterpolation,
692 region: IntRect,
693 input1: Image,
694 input2: Image,
695) -> Result<Image, Error> {
696 let input1 = input1.into_color_space(cs)?;
697 let input2 = input2.into_color_space(cs)?;
698
699 let mut pixmap = tiny_skia::Pixmap::try_create(region.width(), region.height())?;
700
701 pixmap.draw_pixmap(
702 0,
703 0,
704 input2.as_ref().as_ref(),
705 &tiny_skia::PixmapPaint::default(),
706 tiny_skia::Transform::identity(),
707 None,
708 );
709
710 pixmap.draw_pixmap(
711 0,
712 0,
713 input1.as_ref().as_ref(),
714 &tiny_skia::PixmapPaint {
715 blend_mode: crate::render::convert_blend_mode(fe.mode()),
716 ..tiny_skia::PixmapPaint::default()
717 },
718 tiny_skia::Transform::identity(),
719 None,
720 );
721
722 Ok(Image::from_image(pixmap, cs))
723}
724
725fn apply_composite(
726 fe: &usvg::filter::Composite,
727 cs: usvg::filter::ColorInterpolation,
728 region: IntRect,
729 input1: Image,
730 input2: Image,
731) -> Result<Image, Error> {
732 use usvg::filter::CompositeOperator as Operator;
733
734 let input1 = input1.into_color_space(cs)?;
735 let input2 = input2.into_color_space(cs)?;
736
737 let mut pixmap = tiny_skia::Pixmap::try_create(region.width(), region.height())?;
738
739 if let Operator::Arithmetic { k1, k2, k3, k4 } = fe.operator() {
740 let pixmap1 = input1.take()?;
741 let pixmap2 = input2.take()?;
742
743 composite::arithmetic(
744 k1,
745 k2,
746 k3,
747 k4,
748 pixmap1.as_image_ref(),
749 pixmap2.as_image_ref(),
750 pixmap.as_image_ref_mut(),
751 );
752
753 return Ok(Image::from_image(pixmap, cs));
754 }
755
756 pixmap.draw_pixmap(
757 0,
758 0,
759 input2.as_ref().as_ref(),
760 &tiny_skia::PixmapPaint::default(),
761 tiny_skia::Transform::identity(),
762 None,
763 );
764
765 let blend_mode = match fe.operator() {
766 Operator::Over => tiny_skia::BlendMode::SourceOver,
767 Operator::In => tiny_skia::BlendMode::SourceIn,
768 Operator::Out => tiny_skia::BlendMode::SourceOut,
769 Operator::Atop => tiny_skia::BlendMode::SourceAtop,
770 Operator::Xor => tiny_skia::BlendMode::Xor,
771 Operator::Arithmetic { .. } => tiny_skia::BlendMode::SourceOver,
772 };
773
774 pixmap.draw_pixmap(
775 0,
776 0,
777 input1.as_ref().as_ref(),
778 &tiny_skia::PixmapPaint {
779 blend_mode,
780 ..tiny_skia::PixmapPaint::default()
781 },
782 tiny_skia::Transform::identity(),
783 None,
784 );
785
786 Ok(Image::from_image(pixmap, cs))
787}
788
789fn apply_merge(
790 fe: &usvg::filter::Merge,
791 cs: usvg::filter::ColorInterpolation,
792 region: IntRect,
793 source: &tiny_skia::Pixmap,
794 results: &[FilterResult],
795) -> Result<Image, Error> {
796 let mut pixmap = tiny_skia::Pixmap::try_create(region.width(), region.height())?;
797
798 for input in fe.inputs() {
799 let input = get_input(input, region, source, results)?;
800 let input = input.into_color_space(cs)?;
801 pixmap.draw_pixmap(
802 0,
803 0,
804 input.as_ref().as_ref(),
805 &tiny_skia::PixmapPaint::default(),
806 tiny_skia::Transform::identity(),
807 None,
808 );
809 }
810
811 Ok(Image::from_image(pixmap, cs))
812}
813
814fn apply_flood(fe: &usvg::filter::Flood, region: IntRect) -> Result<Image, Error> {
815 let c = fe.color();
816
817 let mut pixmap = tiny_skia::Pixmap::try_create(region.width(), region.height())?;
818 pixmap.fill(tiny_skia::Color::from_rgba8(
819 c.red,
820 c.green,
821 c.blue,
822 fe.opacity().to_u8(),
823 ));
824
825 Ok(Image::from_image(
826 pixmap,
827 usvg::filter::ColorInterpolation::SRGB,
828 ))
829}
830
831fn apply_tile(input: Image, region: IntRect) -> Result<Image, Error> {
832 let subregion = input.region.translate(-region.x(), -region.y()).unwrap();
833
834 let tile_pixmap = input.image.copy_region(subregion)?;
835 let mut paint = tiny_skia::Paint::default();
836 paint.shader = tiny_skia::Pattern::new(
837 tile_pixmap.as_ref(),
838 tiny_skia::SpreadMode::Repeat,
839 tiny_skia::FilterQuality::Bicubic,
840 1.0,
841 tiny_skia::Transform::from_translate(subregion.x() as f32, subregion.y() as f32),
842 );
843
844 let mut pixmap = tiny_skia::Pixmap::try_create(region.width(), region.height())?;
845 let rect = tiny_skia::Rect::from_xywh(0.0, 0.0, region.width() as f32, region.height() as f32)
846 .unwrap();
847 pixmap.fill_rect(rect, &paint, tiny_skia::Transform::identity(), None);
848
849 Ok(Image::from_image(
850 pixmap,
851 usvg::filter::ColorInterpolation::SRGB,
852 ))
853}
854
855fn apply_image(
856 fe: &usvg::filter::Image,
857 region: IntRect,
858 subregion: IntRect,
859 ts: usvg::Transform,
860) -> Result<Image, Error> {
861 let mut pixmap = tiny_skia::Pixmap::try_create(region.width(), region.height())?;
862
863 let (sx, sy) = ts.get_scale();
864 let transform = tiny_skia::Transform::from_row(
865 sx,
866 0.0,
867 0.0,
868 sy,
869 subregion.x() as f32,
870 subregion.y() as f32,
871 );
872
873 let ctx = crate::render::Context {
874 max_bbox: tiny_skia::IntRect::from_xywh(0, 0, region.width(), region.height()).unwrap(),
875 };
876
877 crate::render::render_nodes(fe.root(), &ctx, transform, &mut pixmap.as_mut());
878
879 Ok(Image::from_image(
880 pixmap,
881 usvg::filter::ColorInterpolation::SRGB,
882 ))
883}
884
885fn apply_component_transfer(
886 fe: &usvg::filter::ComponentTransfer,
887 cs: usvg::filter::ColorInterpolation,
888 input: Image,
889) -> Result<Image, Error> {
890 let mut pixmap = input.into_color_space(cs)?.take()?;
891
892 demultiply_alpha(pixmap.data_mut().as_rgba_mut());
893 component_transfer::apply(fe, pixmap.as_image_ref_mut());
894 multiply_alpha(pixmap.data_mut().as_rgba_mut());
895
896 Ok(Image::from_image(pixmap, cs))
897}
898
899fn apply_color_matrix(
900 fe: &usvg::filter::ColorMatrix,
901 cs: usvg::filter::ColorInterpolation,
902 input: Image,
903) -> Result<Image, Error> {
904 let mut pixmap = input.into_color_space(cs)?.take()?;
905
906 demultiply_alpha(pixmap.data_mut().as_rgba_mut());
907 color_matrix::apply(fe.kind(), pixmap.as_image_ref_mut());
908 multiply_alpha(pixmap.data_mut().as_rgba_mut());
909
910 Ok(Image::from_image(pixmap, cs))
911}
912
913fn apply_convolve_matrix(
914 fe: &usvg::filter::ConvolveMatrix,
915 cs: usvg::filter::ColorInterpolation,
916 input: Image,
917) -> Result<Image, Error> {
918 let mut pixmap = input.into_color_space(cs)?.take()?;
919
920 if fe.preserve_alpha() {
921 demultiply_alpha(pixmap.data_mut().as_rgba_mut());
922 }
923
924 convolve_matrix::apply(fe, pixmap.as_image_ref_mut());
925
926 Ok(Image::from_image(pixmap, cs))
927}
928
929fn apply_morphology(
930 fe: &usvg::filter::Morphology,
931 cs: usvg::filter::ColorInterpolation,
932 ts: usvg::Transform,
933 input: Image,
934) -> Result<Image, Error> {
935 let mut pixmap = input.into_color_space(cs)?.take()?;
936
937 let (rx, ry) = match scale_coordinates(fe.radius_x().get(), fe.radius_y().get(), ts) {
938 Some(v) => v,
939 None => return Ok(Image::from_image(pixmap, cs)),
940 };
941
942 if !(rx > 0.0 && ry > 0.0) {
943 pixmap.clear();
944 return Ok(Image::from_image(pixmap, cs));
945 }
946
947 morphology::apply(fe.operator(), rx, ry, pixmap.as_image_ref_mut());
948
949 Ok(Image::from_image(pixmap, cs))
950}
951
952fn apply_displacement_map(
953 fe: &usvg::filter::DisplacementMap,
954 region: IntRect,
955 cs: usvg::filter::ColorInterpolation,
956 ts: usvg::Transform,
957 input1: Image,
958 input2: Image,
959) -> Result<Image, Error> {
960 let pixmap1 = input1.into_color_space(cs)?.take()?;
961 let pixmap2 = input2.into_color_space(cs)?.take()?;
962
963 let mut pixmap = tiny_skia::Pixmap::try_create(region.width(), region.height())?;
964
965 let (sx, sy) = match scale_coordinates(fe.scale(), fe.scale(), ts) {
966 Some(v) => v,
967 None => return Ok(Image::from_image(pixmap1, cs)),
968 };
969
970 displacement_map::apply(
971 fe,
972 sx,
973 sy,
974 pixmap1.as_image_ref(),
975 pixmap2.as_image_ref(),
976 pixmap.as_image_ref_mut(),
977 );
978
979 Ok(Image::from_image(pixmap, cs))
980}
981
982fn apply_turbulence(
983 fe: &usvg::filter::Turbulence,
984 region: IntRect,
985 cs: usvg::filter::ColorInterpolation,
986 ts: usvg::Transform,
987) -> Result<Image, Error> {
988 let mut pixmap = tiny_skia::Pixmap::try_create(region.width(), region.height())?;
989
990 let (sx, sy) = ts.get_scale();
991 if sx.approx_zero_ulps(4) || sy.approx_zero_ulps(4) {
992 return Ok(Image::from_image(pixmap, cs));
993 }
994
995 turbulence::apply(
996 region.x() as f64 - ts.tx as f64,
997 region.y() as f64 - ts.ty as f64,
998 sx as f64,
999 sy as f64,
1000 fe.base_frequency_x().get() as f64,
1001 fe.base_frequency_y().get() as f64,
1002 fe.num_octaves(),
1003 fe.seed(),
1004 fe.stitch_tiles(),
1005 fe.kind() == usvg::filter::TurbulenceKind::FractalNoise,
1006 pixmap.as_image_ref_mut(),
1007 );
1008
1009 multiply_alpha(pixmap.data_mut().as_rgba_mut());
1010
1011 Ok(Image::from_image(pixmap, cs))
1012}
1013
1014fn apply_diffuse_lighting(
1015 fe: &usvg::filter::DiffuseLighting,
1016 region: IntRect,
1017 cs: usvg::filter::ColorInterpolation,
1018 ts: usvg::Transform,
1019 input: Image,
1020) -> Result<Image, Error> {
1021 let mut pixmap = tiny_skia::Pixmap::try_create(region.width(), region.height())?;
1022
1023 let light_source = transform_light_source(fe.light_source(), region, ts);
1024
1025 lighting::diffuse_lighting(
1026 fe,
1027 light_source,
1028 input.as_ref().as_image_ref(),
1029 pixmap.as_image_ref_mut(),
1030 );
1031
1032 Ok(Image::from_image(pixmap, cs))
1033}
1034
1035fn apply_specular_lighting(
1036 fe: &usvg::filter::SpecularLighting,
1037 region: IntRect,
1038 cs: usvg::filter::ColorInterpolation,
1039 ts: usvg::Transform,
1040 input: Image,
1041) -> Result<Image, Error> {
1042 let mut pixmap = tiny_skia::Pixmap::try_create(region.width(), region.height())?;
1043
1044 let light_source = transform_light_source(fe.light_source(), region, ts);
1045
1046 lighting::specular_lighting(
1047 fe,
1048 light_source,
1049 input.as_ref().as_image_ref(),
1050 pixmap.as_image_ref_mut(),
1051 );
1052
1053 Ok(Image::from_image(pixmap, cs))
1054}
1055
1056fn transform_light_source(
1058 mut source: usvg::filter::LightSource,
1059 region: IntRect,
1060 ts: usvg::Transform,
1061) -> usvg::filter::LightSource {
1062 use std::f32::consts::SQRT_2;
1063 use usvg::filter::LightSource;
1064
1065 match source {
1066 LightSource::DistantLight(..) => {}
1067 LightSource::PointLight(ref mut light) => {
1068 let mut point = tiny_skia::Point::from_xy(light.x, light.y);
1069 ts.map_point(&mut point);
1070 light.x = point.x - region.x() as f32;
1071 light.y = point.y - region.y() as f32;
1072 light.z = light.z * (ts.sx * ts.sx + ts.sy * ts.sy).sqrt() / SQRT_2;
1073 }
1074 LightSource::SpotLight(ref mut light) => {
1075 let sz = (ts.sx * ts.sx + ts.sy * ts.sy).sqrt() / SQRT_2;
1076
1077 let mut point = tiny_skia::Point::from_xy(light.x, light.y);
1078 ts.map_point(&mut point);
1079 light.x = point.x - region.x() as f32;
1080 light.y = point.y - region.x() as f32;
1081 light.z *= sz;
1082
1083 let mut point = tiny_skia::Point::from_xy(light.points_at_x, light.points_at_y);
1084 ts.map_point(&mut point);
1085 light.points_at_x = point.x - region.x() as f32;
1086 light.points_at_y = point.y - region.x() as f32;
1087 light.points_at_z *= sz;
1088 }
1089 }
1090
1091 source
1092}
1093
1094fn apply_to_canvas(input: Image, pixmap: &mut tiny_skia::Pixmap) -> Result<(), Error> {
1095 let input = input.into_color_space(usvg::filter::ColorInterpolation::SRGB)?;
1096
1097 pixmap.fill(tiny_skia::Color::TRANSPARENT);
1098 pixmap.draw_pixmap(
1099 0,
1100 0,
1101 input.as_ref().as_ref(),
1102 &tiny_skia::PixmapPaint::default(),
1103 tiny_skia::Transform::identity(),
1104 None,
1105 );
1106
1107 Ok(())
1108}
1109
1110fn resolve_std_dev(std_dx: f32, std_dy: f32, ts: usvg::Transform) -> Option<(f64, f64, bool)> {
1114 let (mut std_dx, mut std_dy) = scale_coordinates(std_dx, std_dy, ts)?;
1115
1116 if std_dx.approx_eq_ulps(&0.0, 4) && std_dy.approx_eq_ulps(&0.0, 4) {
1119 return None;
1120 }
1121
1122 if std_dx < 0.05 {
1124 std_dx = 0.0;
1125 }
1126
1127 if std_dy < 0.05 {
1128 std_dy = 0.0;
1129 }
1130
1131 const BLUR_SIGMA_THRESHOLD: f32 = 2.0;
1132 let box_blur = std_dx >= BLUR_SIGMA_THRESHOLD || std_dy >= BLUR_SIGMA_THRESHOLD;
1134
1135 Some((std_dx as f64, std_dy as f64, box_blur))
1136}
1137
1138fn scale_coordinates(x: f32, y: f32, ts: usvg::Transform) -> Option<(f32, f32)> {
1139 let (sx, sy) = ts.get_scale();
1140 Some((x * sx, y * sy))
1141}