1use float_cmp::approx_eq;
4use markup5ever::{expanded_name, local_name, ns};
5use nalgebra::{Vector2, Vector3};
6use num_traits::identities::Zero;
7use rayon::prelude::*;
8use std::cmp::max;
9
10use crate::color::{Color, RGBA, color_to_rgba, resolve_color};
11use crate::document::AcquiredNodes;
12use crate::element::{ElementData, ElementTrait, set_attribute};
13use crate::filters::{
14 FilterEffect, FilterError, FilterResolveError, Input, InputRequirements, Primitive,
15 PrimitiveParams, ResolvedPrimitive,
16 bounds::BoundsBuilder,
17 context::{FilterContext, FilterOutput},
18};
19use crate::node::{CascadedValues, Node, NodeBorrow};
20use crate::parsers::{NonNegative, ParseValue};
21use crate::properties::ColorInterpolationFilters;
22use crate::rect::IRect;
23use crate::session::Session;
24use crate::surface_utils::{
25 ImageSurfaceDataExt, Pixel,
26 shared_surface::{ExclusiveImageSurface, SharedImageSurface, SurfaceType},
27};
28use crate::transform::Transform;
29use crate::unit_interval::UnitInterval;
30use crate::util::clamp;
31use crate::xml::Attributes;
32
33use super::convolve_matrix::KernelUnitLength;
34
35#[derive(Default)]
37pub struct FeDiffuseLighting {
38 base: Primitive,
39 params: DiffuseLightingParams,
40}
41
42#[derive(Clone)]
43pub struct DiffuseLightingParams {
44 in1: Input,
45 surface_scale: f64,
46 kernel_unit_length: KernelUnitLength,
47 diffuse_constant: NonNegative,
48}
49
50impl Default for DiffuseLightingParams {
51 fn default() -> Self {
52 Self {
53 in1: Default::default(),
54 surface_scale: 1.0,
55 kernel_unit_length: KernelUnitLength::default(),
56 diffuse_constant: NonNegative(1.0),
57 }
58 }
59}
60
61#[derive(Default)]
63pub struct FeSpecularLighting {
64 base: Primitive,
65 params: SpecularLightingParams,
66}
67
68#[derive(Clone)]
69pub struct SpecularLightingParams {
70 in1: Input,
71 surface_scale: f64,
72 kernel_unit_length: KernelUnitLength,
73 specular_constant: NonNegative,
74 specular_exponent: f64,
75}
76
77impl Default for SpecularLightingParams {
78 fn default() -> Self {
79 Self {
80 in1: Default::default(),
81 surface_scale: 1.0,
82 kernel_unit_length: KernelUnitLength::default(),
83 specular_constant: NonNegative(1.0),
84 specular_exponent: 1.0,
85 }
86 }
87}
88
89pub struct DiffuseLighting {
91 params: DiffuseLightingParams,
92 light: Light,
93}
94
95pub struct SpecularLighting {
97 params: SpecularLightingParams,
98 light: Light,
99}
100
101#[derive(Debug, PartialEq)]
103enum UntransformedLightSource {
104 Distant(FeDistantLight),
105 Point(FePointLight),
106 Spot(FeSpotLight),
107}
108
109enum LightSource {
111 Distant {
112 azimuth: f64,
113 elevation: f64,
114 },
115 Point {
116 origin: Vector3<f64>,
117 },
118 Spot {
119 origin: Vector3<f64>,
120 direction: Vector3<f64>,
121 specular_exponent: f64,
122 limiting_cone_angle: Option<f64>,
123 },
124}
125
126impl UntransformedLightSource {
127 fn transform(&self, paffine: Transform) -> LightSource {
128 match *self {
129 UntransformedLightSource::Distant(ref l) => l.transform(),
130 UntransformedLightSource::Point(ref l) => l.transform(paffine),
131 UntransformedLightSource::Spot(ref l) => l.transform(paffine),
132 }
133 }
134}
135
136struct Light {
137 source: UntransformedLightSource,
138 lighting_color: Color,
139 color_interpolation_filters: ColorInterpolationFilters,
140}
141
142#[inline]
144fn color_and_vector(
145 lighting_color: &RGBA,
146 source: &LightSource,
147 x: f64,
148 y: f64,
149 z: f64,
150) -> (RGBA, Vector3<f64>) {
151 let vector = match *source {
152 LightSource::Distant { azimuth, elevation } => {
153 let azimuth = azimuth.to_radians();
154 let elevation = elevation.to_radians();
155 Vector3::new(
156 azimuth.cos() * elevation.cos(),
157 azimuth.sin() * elevation.cos(),
158 elevation.sin(),
159 )
160 }
161 LightSource::Point { origin } | LightSource::Spot { origin, .. } => {
162 let mut v = origin - Vector3::new(x, y, z);
163 let _ = v.try_normalize_mut(0.0);
164 v
165 }
166 };
167
168 let color = match *source {
169 LightSource::Spot {
170 direction,
171 specular_exponent,
172 limiting_cone_angle,
173 ..
174 } => {
175 let transparent_color = RGBA::new(0, 0, 0, 0.0);
176 let minus_l_dot_s = -vector.dot(&direction);
177 match limiting_cone_angle {
178 _ if minus_l_dot_s <= 0.0 => transparent_color,
179 Some(a) if minus_l_dot_s < a.to_radians().cos() => transparent_color,
180 _ => {
181 let factor = minus_l_dot_s.powf(specular_exponent);
182 let compute = |x| (clamp(f64::from(x) * factor, 0.0, 255.0) + 0.5) as u8;
183
184 RGBA {
185 red: compute(lighting_color.red),
186 green: compute(lighting_color.green),
187 blue: compute(lighting_color.blue),
188 alpha: 1.0,
189 }
190 }
191 }
192 }
193 _ => *lighting_color,
194 };
195
196 (color, vector)
197}
198
199#[derive(Clone, Debug, Default, PartialEq)]
200pub struct FeDistantLight {
201 azimuth: f64,
202 elevation: f64,
203}
204
205impl FeDistantLight {
206 fn transform(&self) -> LightSource {
207 LightSource::Distant {
208 azimuth: self.azimuth,
209 elevation: self.elevation,
210 }
211 }
212}
213
214impl ElementTrait for FeDistantLight {
215 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
216 for (attr, value) in attrs.iter() {
217 match attr.expanded() {
218 expanded_name!("", "azimuth") => {
219 set_attribute(&mut self.azimuth, attr.parse(value), session)
220 }
221 expanded_name!("", "elevation") => {
222 set_attribute(&mut self.elevation, attr.parse(value), session)
223 }
224 _ => (),
225 }
226 }
227 }
228}
229
230#[derive(Clone, Debug, Default, PartialEq)]
231pub struct FePointLight {
232 x: f64,
233 y: f64,
234 z: f64,
235}
236
237impl FePointLight {
238 fn transform(&self, paffine: Transform) -> LightSource {
239 let (x, y) = paffine.transform_point(self.x, self.y);
240 let z = transform_dist(paffine, self.z);
241
242 LightSource::Point {
243 origin: Vector3::new(x, y, z),
244 }
245 }
246}
247
248impl ElementTrait for FePointLight {
249 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
250 for (attr, value) in attrs.iter() {
251 match attr.expanded() {
252 expanded_name!("", "x") => set_attribute(&mut self.x, attr.parse(value), session),
253 expanded_name!("", "y") => set_attribute(&mut self.y, attr.parse(value), session),
254 expanded_name!("", "z") => set_attribute(&mut self.z, attr.parse(value), session),
255 _ => (),
256 }
257 }
258 }
259}
260
261#[derive(Clone, Debug, PartialEq)]
262pub struct FeSpotLight {
263 x: f64,
264 y: f64,
265 z: f64,
266 points_at_x: f64,
267 points_at_y: f64,
268 points_at_z: f64,
269 specular_exponent: f64,
270 limiting_cone_angle: Option<f64>,
271}
272
273impl Default for FeSpotLight {
276 fn default() -> FeSpotLight {
277 FeSpotLight {
278 x: 0.0,
279 y: 0.0,
280 z: 0.0,
281 points_at_x: 0.0,
282 points_at_y: 0.0,
283 points_at_z: 0.0,
284 specular_exponent: 1.0,
285 limiting_cone_angle: None,
286 }
287 }
288}
289
290impl FeSpotLight {
291 fn transform(&self, paffine: Transform) -> LightSource {
292 let (x, y) = paffine.transform_point(self.x, self.y);
293 let z = transform_dist(paffine, self.z);
294 let (points_at_x, points_at_y) =
295 paffine.transform_point(self.points_at_x, self.points_at_y);
296 let points_at_z = transform_dist(paffine, self.points_at_z);
297
298 let origin = Vector3::new(x, y, z);
299 let mut direction = Vector3::new(points_at_x, points_at_y, points_at_z) - origin;
300 let _ = direction.try_normalize_mut(0.0);
301
302 LightSource::Spot {
303 origin,
304 direction,
305 specular_exponent: self.specular_exponent,
306 limiting_cone_angle: self.limiting_cone_angle,
307 }
308 }
309}
310
311impl ElementTrait for FeSpotLight {
312 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
313 for (attr, value) in attrs.iter() {
314 match attr.expanded() {
315 expanded_name!("", "x") => set_attribute(&mut self.x, attr.parse(value), session),
316 expanded_name!("", "y") => set_attribute(&mut self.y, attr.parse(value), session),
317 expanded_name!("", "z") => set_attribute(&mut self.z, attr.parse(value), session),
318 expanded_name!("", "pointsAtX") => {
319 set_attribute(&mut self.points_at_x, attr.parse(value), session)
320 }
321 expanded_name!("", "pointsAtY") => {
322 set_attribute(&mut self.points_at_y, attr.parse(value), session)
323 }
324 expanded_name!("", "pointsAtZ") => {
325 set_attribute(&mut self.points_at_z, attr.parse(value), session)
326 }
327
328 expanded_name!("", "specularExponent") => {
329 set_attribute(&mut self.specular_exponent, attr.parse(value), session);
330 }
331
332 expanded_name!("", "limitingConeAngle") => {
333 set_attribute(&mut self.limiting_cone_angle, attr.parse(value), session);
334 }
335
336 _ => (),
337 }
338 }
339 }
340}
341
342#[inline]
344fn transform_dist(t: Transform, d: f64) -> f64 {
345 d * (t.xx.powi(2) + t.yy.powi(2)).sqrt() / std::f64::consts::SQRT_2
346}
347
348impl ElementTrait for FeDiffuseLighting {
349 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
350 self.params.in1 = self.base.parse_one_input(attrs, session);
351
352 for (attr, value) in attrs.iter() {
353 match attr.expanded() {
354 expanded_name!("", "surfaceScale") => {
355 set_attribute(&mut self.params.surface_scale, attr.parse(value), session);
356 }
357 expanded_name!("", "kernelUnitLength") => {
358 set_attribute(
359 &mut self.params.kernel_unit_length,
360 attr.parse(value),
361 session,
362 );
363 }
364 expanded_name!("", "diffuseConstant") => {
365 set_attribute(
366 &mut self.params.diffuse_constant,
367 attr.parse(value),
368 session,
369 );
370 }
371 _ => (),
372 }
373 }
374 }
375}
376
377impl DiffuseLighting {
378 #[inline]
379 fn compute_factor(&self, normal: Normal, light_vector: Vector3<f64>) -> f64 {
380 let k = if normal.normal.is_zero() {
381 light_vector.z
383 } else {
384 let mut n = normal
385 .normal
386 .map(|x| f64::from(x) * self.params.surface_scale / 255.);
387 n.component_mul_assign(&normal.factor);
388 let normal = Vector3::new(n.x, n.y, 1.0);
389
390 normal.dot(&light_vector) / normal.norm()
391 };
392
393 self.params.diffuse_constant.0 * k
394 }
395}
396
397impl ElementTrait for FeSpecularLighting {
398 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
399 self.params.in1 = self.base.parse_one_input(attrs, session);
400
401 for (attr, value) in attrs.iter() {
402 match attr.expanded() {
403 expanded_name!("", "surfaceScale") => {
404 set_attribute(&mut self.params.surface_scale, attr.parse(value), session);
405 }
406 expanded_name!("", "kernelUnitLength") => {
407 set_attribute(
408 &mut self.params.kernel_unit_length,
409 attr.parse(value),
410 session,
411 );
412 }
413 expanded_name!("", "specularConstant") => {
414 set_attribute(
415 &mut self.params.specular_constant,
416 attr.parse(value),
417 session,
418 );
419 }
420 expanded_name!("", "specularExponent") => {
421 set_attribute(
422 &mut self.params.specular_exponent,
423 attr.parse(value),
424 session,
425 );
426 }
427 _ => (),
428 }
429 }
430 }
431}
432
433impl SpecularLighting {
434 #[inline]
435 fn compute_factor(&self, normal: Normal, light_vector: Vector3<f64>) -> f64 {
436 let h = light_vector + Vector3::new(0.0, 0.0, 1.0);
437 let h_norm = h.norm();
438
439 if h_norm == 0.0 {
440 return 0.0;
441 }
442
443 let n_dot_h = if normal.normal.is_zero() {
444 h.z / h_norm
446 } else {
447 let mut n = normal
448 .normal
449 .map(|x| f64::from(x) * self.params.surface_scale / 255.);
450 n.component_mul_assign(&normal.factor);
451 let normal = Vector3::new(n.x, n.y, 1.0);
452 normal.dot(&h) / normal.norm() / h_norm
453 };
454
455 if approx_eq!(f64, self.params.specular_exponent, 1.0) {
456 self.params.specular_constant.0 * n_dot_h
457 } else {
458 self.params.specular_constant.0 * n_dot_h.powf(self.params.specular_exponent)
459 }
460 }
461}
462
463macro_rules! impl_lighting_filter {
464 ($lighting_type:ty, $params_name:ident, $alpha_func:ident) => {
465 impl $params_name {
466 pub fn render(
467 &self,
468 bounds_builder: BoundsBuilder,
469 ctx: &FilterContext,
470 ) -> Result<FilterOutput, FilterError> {
471 let input_1 =
472 ctx.get_input(&self.params.in1, self.light.color_interpolation_filters)?;
473 let mut bounds: IRect = bounds_builder
474 .add_input(&input_1)
475 .compute(ctx)
476 .clipped
477 .into();
478 let original_bounds = bounds;
479
480 let scale = self
481 .params
482 .kernel_unit_length
483 .0
484 .map(|(dx, dy)| ctx.paffine().transform_distance(dx, dy));
485
486 let mut input_surface = input_1.surface().clone();
487
488 if let Some((ox, oy)) = scale {
489 let (new_surface, new_bounds) =
491 input_surface.scale(bounds, 1.0 / ox, 1.0 / oy)?;
492
493 input_surface = new_surface;
494 bounds = new_bounds;
495 }
496
497 let (bounds_w, bounds_h) = bounds.size();
498
499 if bounds_w < 2 || bounds_h < 2 {
502 return Err(FilterError::LightingInputTooSmall);
503 }
504
505 let (ox, oy) = scale.unwrap_or((1.0, 1.0));
506
507 let source = self.light.source.transform(ctx.paffine());
508
509 let mut surface = ExclusiveImageSurface::new(
510 input_surface.width(),
511 input_surface.height(),
512 SurfaceType::from(self.light.color_interpolation_filters),
513 )?;
514
515 let lighting_color = color_to_rgba(&self.light.lighting_color);
516
517 {
518 let output_stride = surface.stride() as usize;
519 let mut output_data = surface.data();
520 let output_slice = &mut *output_data;
521
522 let compute_output_pixel =
523 |output_slice: &mut [u8], base_y, x, y, normal: Normal| {
524 let pixel = input_surface.get_pixel(x, y);
525
526 let scaled_x = f64::from(x) * ox;
527 let scaled_y = f64::from(y) * oy;
528 let z = f64::from(pixel.a) / 255.0 * self.params.surface_scale;
529
530 let (color, vector) =
531 color_and_vector(&lighting_color, &source, scaled_x, scaled_y, z);
532
533 let factor = self.compute_factor(normal, vector);
535 let compute =
536 |x| (clamp(factor * f64::from(x), 0.0, 255.0) + 0.5) as u8;
537
538 let r = compute(color.red);
539 let g = compute(color.green);
540 let b = compute(color.blue);
541 let a = $alpha_func(r, g, b);
542
543 let output_pixel = Pixel { r, g, b, a };
544
545 output_slice.set_pixel(output_stride, output_pixel, x, y - base_y);
546 };
547
548 compute_output_pixel(
550 output_slice,
551 0,
552 bounds.x0 as u32,
553 bounds.y0 as u32,
554 Normal::top_left(&input_surface, bounds),
555 );
556
557 compute_output_pixel(
559 output_slice,
560 0,
561 bounds.x1 as u32 - 1,
562 bounds.y0 as u32,
563 Normal::top_right(&input_surface, bounds),
564 );
565
566 compute_output_pixel(
568 output_slice,
569 0,
570 bounds.x0 as u32,
571 bounds.y1 as u32 - 1,
572 Normal::bottom_left(&input_surface, bounds),
573 );
574
575 compute_output_pixel(
577 output_slice,
578 0,
579 bounds.x1 as u32 - 1,
580 bounds.y1 as u32 - 1,
581 Normal::bottom_right(&input_surface, bounds),
582 );
583
584 if bounds_w >= 3 {
585 for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
587 compute_output_pixel(
588 output_slice,
589 0,
590 x,
591 bounds.y0 as u32,
592 Normal::top_row(&input_surface, bounds, x),
593 );
594 }
595
596 for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
598 compute_output_pixel(
599 output_slice,
600 0,
601 x,
602 bounds.y1 as u32 - 1,
603 Normal::bottom_row(&input_surface, bounds, x),
604 );
605 }
606 }
607
608 if bounds_h >= 3 {
609 for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
611 compute_output_pixel(
612 output_slice,
613 0,
614 bounds.x0 as u32,
615 y,
616 Normal::left_column(&input_surface, bounds, y),
617 );
618 }
619
620 for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
622 compute_output_pixel(
623 output_slice,
624 0,
625 bounds.x1 as u32 - 1,
626 y,
627 Normal::right_column(&input_surface, bounds, y),
628 );
629 }
630 }
631
632 if bounds_w >= 3 && bounds_h >= 3 {
633 let first_row = bounds.y0 as u32 + 1;
635 let one_past_last_row = bounds.y1 as u32 - 1;
636 let first_pixel = (first_row as usize) * output_stride;
637 let one_past_last_pixel = (one_past_last_row as usize) * output_stride;
638
639 output_slice[first_pixel..one_past_last_pixel]
640 .par_chunks_mut(output_stride)
641 .zip(first_row..one_past_last_row)
642 .for_each(|(slice, y)| {
643 for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
644 compute_output_pixel(
645 slice,
646 y,
647 x,
648 y,
649 Normal::interior(&input_surface, bounds, x, y),
650 );
651 }
652 });
653 }
654 }
655
656 let mut surface = surface.share()?;
657
658 if let Some((ox, oy)) = scale {
659 surface = surface.scale_to(
661 ctx.source_graphic().width(),
662 ctx.source_graphic().height(),
663 original_bounds,
664 ox,
665 oy,
666 )?;
667
668 bounds = original_bounds;
669 }
670
671 Ok(FilterOutput { surface, bounds })
672 }
673
674 pub fn get_input_requirements(&self) -> InputRequirements {
675 self.params.in1.get_requirements()
676 }
677 }
678
679 impl FilterEffect for $lighting_type {
680 fn resolve(
681 &self,
682 _acquired_nodes: &mut AcquiredNodes<'_>,
683 node: &Node,
684 ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> {
685 let mut sources = node.children().rev().filter(|c| {
686 c.is_element()
687 && matches!(
688 *c.borrow_element_data(),
689 ElementData::FeDistantLight(_)
690 | ElementData::FePointLight(_)
691 | ElementData::FeSpotLight(_)
692 )
693 });
694
695 let source_node = sources.next();
696 if source_node.is_none() || sources.next().is_some() {
697 return Err(FilterResolveError::InvalidLightSourceCount);
698 }
699
700 let source_node = source_node.unwrap();
701
702 let source = match &*source_node.borrow_element_data() {
703 ElementData::FeDistantLight(l) => {
704 UntransformedLightSource::Distant((**l).clone())
705 }
706 ElementData::FePointLight(l) => UntransformedLightSource::Point((**l).clone()),
707 ElementData::FeSpotLight(l) => UntransformedLightSource::Spot((**l).clone()),
708 _ => unreachable!(),
709 };
710
711 let cascaded = CascadedValues::new_from_node(node);
712 let values = cascaded.get();
713
714 Ok(vec![ResolvedPrimitive {
715 primitive: self.base.clone(),
716 params: PrimitiveParams::$params_name($params_name {
717 params: self.params.clone(),
718 light: Light {
719 source,
720 lighting_color: resolve_color(
721 &values.lighting_color().0,
722 UnitInterval::clamp(1.0),
723 &values.color().0,
724 ),
725 color_interpolation_filters: values.color_interpolation_filters(),
726 },
727 }),
728 }])
729 }
730 }
731 };
732}
733
734const fn diffuse_alpha(_r: u8, _g: u8, _b: u8) -> u8 {
735 255
736}
737
738fn specular_alpha(r: u8, g: u8, b: u8) -> u8 {
739 max(max(r, g), b)
740}
741
742impl_lighting_filter!(FeDiffuseLighting, DiffuseLighting, diffuse_alpha);
743
744impl_lighting_filter!(FeSpecularLighting, SpecularLighting, specular_alpha);
745
746#[derive(Debug, Clone, Copy)]
752pub struct Normal {
753 pub factor: Vector2<f64>,
754 pub normal: Vector2<i16>,
755}
756
757impl Normal {
758 #[inline]
759 fn new(factor_x: f64, nx: i16, factor_y: f64, ny: i16) -> Normal {
760 Normal {
762 factor: Vector2::new(factor_x, factor_y),
763 normal: Vector2::new(-nx, -ny),
764 }
765 }
766
767 #[inline]
769 pub fn top_left(surface: &SharedImageSurface, bounds: IRect) -> Normal {
770 assert!(bounds.width() >= 2);
772 assert!(bounds.height() >= 2);
773
774 let get = |x, y| i16::from(surface.get_pixel(x, y).a);
775 let (x, y) = (bounds.x0 as u32, bounds.y0 as u32);
776
777 let center = get(x, y);
778 let right = get(x + 1, y);
779 let bottom = get(x, y + 1);
780 let bottom_right = get(x + 1, y + 1);
781
782 Self::new(
783 2. / 3.,
784 -2 * center + 2 * right - bottom + bottom_right,
785 2. / 3.,
786 -2 * center - right + 2 * bottom + bottom_right,
787 )
788 }
789
790 #[inline]
792 pub fn top_row(surface: &SharedImageSurface, bounds: IRect, x: u32) -> Normal {
793 assert!(x as i32 > bounds.x0);
794 assert!((x as i32) + 1 < bounds.x1);
795 assert!(bounds.height() >= 2);
796
797 let get = |x, y| i16::from(surface.get_pixel(x, y).a);
798 let y = bounds.y0 as u32;
799
800 let left = get(x - 1, y);
801 let center = get(x, y);
802 let right = get(x + 1, y);
803 let bottom_left = get(x - 1, y + 1);
804 let bottom = get(x, y + 1);
805 let bottom_right = get(x + 1, y + 1);
806
807 Self::new(
808 1. / 3.,
809 -2 * left + 2 * right - bottom_left + bottom_right,
810 1. / 2.,
811 -left - 2 * center - right + bottom_left + 2 * bottom + bottom_right,
812 )
813 }
814
815 #[inline]
817 pub fn top_right(surface: &SharedImageSurface, bounds: IRect) -> Normal {
818 assert!(bounds.width() >= 2);
820 assert!(bounds.height() >= 2);
821
822 let get = |x, y| i16::from(surface.get_pixel(x, y).a);
823 let (x, y) = (bounds.x1 as u32 - 1, bounds.y0 as u32);
824
825 let left = get(x - 1, y);
826 let center = get(x, y);
827 let bottom_left = get(x - 1, y + 1);
828 let bottom = get(x, y + 1);
829
830 Self::new(
831 2. / 3.,
832 -2 * left + 2 * center - bottom_left + bottom,
833 2. / 3.,
834 -left - 2 * center + bottom_left + 2 * bottom,
835 )
836 }
837
838 #[inline]
840 pub fn left_column(surface: &SharedImageSurface, bounds: IRect, y: u32) -> Normal {
841 assert!(y as i32 > bounds.y0);
842 assert!((y as i32) + 1 < bounds.y1);
843 assert!(bounds.width() >= 2);
844
845 let get = |x, y| i16::from(surface.get_pixel(x, y).a);
846 let x = bounds.x0 as u32;
847
848 let top = get(x, y - 1);
849 let top_right = get(x + 1, y - 1);
850 let center = get(x, y);
851 let right = get(x + 1, y);
852 let bottom = get(x, y + 1);
853 let bottom_right = get(x + 1, y + 1);
854
855 Self::new(
856 1. / 2.,
857 -top + top_right - 2 * center + 2 * right - bottom + bottom_right,
858 1. / 3.,
859 -2 * top - top_right + 2 * bottom + bottom_right,
860 )
861 }
862
863 #[inline]
865 pub fn interior(surface: &SharedImageSurface, bounds: IRect, x: u32, y: u32) -> Normal {
866 assert!(x as i32 > bounds.x0);
867 assert!((x as i32) + 1 < bounds.x1);
868 assert!(y as i32 > bounds.y0);
869 assert!((y as i32) + 1 < bounds.y1);
870
871 let get = |x, y| i16::from(surface.get_pixel(x, y).a);
872
873 let top_left = get(x - 1, y - 1);
874 let top = get(x, y - 1);
875 let top_right = get(x + 1, y - 1);
876 let left = get(x - 1, y);
877 let right = get(x + 1, y);
878 let bottom_left = get(x - 1, y + 1);
879 let bottom = get(x, y + 1);
880 let bottom_right = get(x + 1, y + 1);
881
882 Self::new(
883 1. / 4.,
884 -top_left + top_right - 2 * left + 2 * right - bottom_left + bottom_right,
885 1. / 4.,
886 -top_left - 2 * top - top_right + bottom_left + 2 * bottom + bottom_right,
887 )
888 }
889
890 #[inline]
892 pub fn right_column(surface: &SharedImageSurface, bounds: IRect, y: u32) -> Normal {
893 assert!(y as i32 > bounds.y0);
894 assert!((y as i32) + 1 < bounds.y1);
895 assert!(bounds.width() >= 2);
896
897 let get = |x, y| i16::from(surface.get_pixel(x, y).a);
898 let x = bounds.x1 as u32 - 1;
899
900 let top_left = get(x - 1, y - 1);
901 let top = get(x, y - 1);
902 let left = get(x - 1, y);
903 let center = get(x, y);
904 let bottom_left = get(x - 1, y + 1);
905 let bottom = get(x, y + 1);
906
907 Self::new(
908 1. / 2.,
909 -top_left + top - 2 * left + 2 * center - bottom_left + bottom,
910 1. / 3.,
911 -top_left - 2 * top + bottom_left + 2 * bottom,
912 )
913 }
914
915 #[inline]
917 pub fn bottom_left(surface: &SharedImageSurface, bounds: IRect) -> Normal {
918 assert!(bounds.width() >= 2);
920 assert!(bounds.height() >= 2);
921
922 let get = |x, y| i16::from(surface.get_pixel(x, y).a);
923 let (x, y) = (bounds.x0 as u32, bounds.y1 as u32 - 1);
924
925 let top = get(x, y - 1);
926 let top_right = get(x + 1, y - 1);
927 let center = get(x, y);
928 let right = get(x + 1, y);
929
930 Self::new(
931 2. / 3.,
932 -top + top_right - 2 * center + 2 * right,
933 2. / 3.,
934 -2 * top - top_right + 2 * center + right,
935 )
936 }
937
938 #[inline]
940 pub fn bottom_row(surface: &SharedImageSurface, bounds: IRect, x: u32) -> Normal {
941 assert!(x as i32 > bounds.x0);
942 assert!((x as i32) + 1 < bounds.x1);
943 assert!(bounds.height() >= 2);
944
945 let get = |x, y| i16::from(surface.get_pixel(x, y).a);
946 let y = bounds.y1 as u32 - 1;
947
948 let top_left = get(x - 1, y - 1);
949 let top = get(x, y - 1);
950 let top_right = get(x + 1, y - 1);
951 let left = get(x - 1, y);
952 let center = get(x, y);
953 let right = get(x + 1, y);
954
955 Self::new(
956 1. / 3.,
957 -top_left + top_right - 2 * left + 2 * right,
958 1. / 2.,
959 -top_left - 2 * top - top_right + left + 2 * center + right,
960 )
961 }
962
963 #[inline]
965 pub fn bottom_right(surface: &SharedImageSurface, bounds: IRect) -> Normal {
966 assert!(bounds.width() >= 2);
968 assert!(bounds.height() >= 2);
969
970 let get = |x, y| i16::from(surface.get_pixel(x, y).a);
971 let (x, y) = (bounds.x1 as u32 - 1, bounds.y1 as u32 - 1);
972
973 let top_left = get(x - 1, y - 1);
974 let top = get(x, y - 1);
975 let left = get(x - 1, y);
976 let center = get(x, y);
977
978 Self::new(
979 2. / 3.,
980 -top_left + top - 2 * left + 2 * center,
981 2. / 3.,
982 -top_left - 2 * top + left + 2 * center,
983 )
984 }
985}
986
987#[cfg(test)]
988mod tests {
989 use super::*;
990
991 use crate::borrow_element_as;
992 use crate::document::Document;
993
994 #[test]
995 fn extracts_light_source() {
996 let document = Document::load_from_bytes(
997 br#"<?xml version="1.0" encoding="UTF-8"?>
998<svg xmlns="http://www.w3.org/2000/svg">
999 <filter id="filter">
1000 <feDiffuseLighting id="diffuse_distant">
1001 <feDistantLight azimuth="0.0" elevation="45.0"/>
1002 </feDiffuseLighting>
1003
1004 <feSpecularLighting id="specular_point">
1005 <fePointLight x="1.0" y="2.0" z="3.0"/>
1006 </feSpecularLighting>
1007
1008 <feDiffuseLighting id="diffuse_spot">
1009 <feSpotLight x="1.0" y="2.0" z="3.0"
1010 pointsAtX="4.0" pointsAtY="5.0" pointsAtZ="6.0"
1011 specularExponent="7.0" limitingConeAngle="8.0"/>
1012 </feDiffuseLighting>
1013 </filter>
1014</svg>
1015"#,
1016 );
1017 let mut acquired_nodes = AcquiredNodes::new(&document, None::<gio::Cancellable>);
1018
1019 let node = document.lookup_internal_node("diffuse_distant").unwrap();
1020 let lighting = borrow_element_as!(node, FeDiffuseLighting);
1021 let resolved = lighting.resolve(&mut acquired_nodes, &node).unwrap();
1022 let ResolvedPrimitive { params, .. } = resolved.first().unwrap();
1023 let diffuse_lighting = match params {
1024 PrimitiveParams::DiffuseLighting(l) => l,
1025 _ => unreachable!(),
1026 };
1027 assert_eq!(
1028 diffuse_lighting.light.source,
1029 UntransformedLightSource::Distant(FeDistantLight {
1030 azimuth: 0.0,
1031 elevation: 45.0,
1032 })
1033 );
1034
1035 let node = document.lookup_internal_node("specular_point").unwrap();
1036 let lighting = borrow_element_as!(node, FeSpecularLighting);
1037 let resolved = lighting.resolve(&mut acquired_nodes, &node).unwrap();
1038 let ResolvedPrimitive { params, .. } = resolved.first().unwrap();
1039 let specular_lighting = match params {
1040 PrimitiveParams::SpecularLighting(l) => l,
1041 _ => unreachable!(),
1042 };
1043 assert_eq!(
1044 specular_lighting.light.source,
1045 UntransformedLightSource::Point(FePointLight {
1046 x: 1.0,
1047 y: 2.0,
1048 z: 3.0,
1049 })
1050 );
1051
1052 let node = document.lookup_internal_node("diffuse_spot").unwrap();
1053 let lighting = borrow_element_as!(node, FeDiffuseLighting);
1054 let resolved = lighting.resolve(&mut acquired_nodes, &node).unwrap();
1055 let ResolvedPrimitive { params, .. } = resolved.first().unwrap();
1056 let diffuse_lighting = match params {
1057 PrimitiveParams::DiffuseLighting(l) => l,
1058 _ => unreachable!(),
1059 };
1060 assert_eq!(
1061 diffuse_lighting.light.source,
1062 UntransformedLightSource::Spot(FeSpotLight {
1063 x: 1.0,
1064 y: 2.0,
1065 z: 3.0,
1066 points_at_x: 4.0,
1067 points_at_y: 5.0,
1068 points_at_z: 6.0,
1069 specular_exponent: 7.0,
1070 limiting_cone_angle: Some(8.0),
1071 })
1072 );
1073 }
1074}