1
//! Lighting filters and light nodes.
2

            
3
use cssparser::{Color, RGBA};
4
use float_cmp::approx_eq;
5
use markup5ever::{expanded_name, local_name, namespace_url, ns};
6
use nalgebra::{Vector2, Vector3};
7
use num_traits::identities::Zero;
8
use rayon::prelude::*;
9
use std::cmp::max;
10

            
11
use crate::color::color_to_rgba;
12
use crate::document::AcquiredNodes;
13
use crate::drawing_ctx::DrawingCtx;
14
use crate::element::{set_attribute, ElementData, ElementTrait};
15
use crate::filters::{
16
    bounds::BoundsBuilder,
17
    context::{FilterContext, FilterOutput},
18
    FilterEffect, FilterError, FilterResolveError, Input, Primitive, PrimitiveParams,
19
    ResolvedPrimitive,
20
};
21
use crate::node::{CascadedValues, Node, NodeBorrow};
22
use crate::paint_server::resolve_color;
23
use crate::parsers::{NonNegative, NumberOptionalNumber, ParseValue};
24
use crate::properties::ColorInterpolationFilters;
25
use crate::rect::IRect;
26
use crate::rsvg_log;
27
use crate::session::Session;
28
use crate::surface_utils::{
29
    shared_surface::{ExclusiveImageSurface, SharedImageSurface, SurfaceType},
30
    ImageSurfaceDataExt, Pixel,
31
};
32
use crate::transform::Transform;
33
use crate::unit_interval::UnitInterval;
34
use crate::util::clamp;
35
use crate::xml::Attributes;
36

            
37
/// The `feDiffuseLighting` filter primitives.
38
42
#[derive(Default)]
39
pub struct FeDiffuseLighting {
40
42
    base: Primitive,
41
42
    params: DiffuseLightingParams,
42
}
43

            
44
100
#[derive(Clone)]
45
pub struct DiffuseLightingParams {
46
50
    in1: Input,
47
50
    surface_scale: f64,
48
50
    kernel_unit_length: Option<(f64, f64)>,
49
50
    diffuse_constant: NonNegative,
50
}
51

            
52
impl Default for DiffuseLightingParams {
53
42
    fn default() -> Self {
54
42
        Self {
55
42
            in1: Default::default(),
56
            surface_scale: 1.0,
57
42
            kernel_unit_length: None,
58
42
            diffuse_constant: NonNegative(1.0),
59
        }
60
42
    }
61
}
62

            
63
/// The `feSpecularLighting` filter primitives.
64
30
#[derive(Default)]
65
pub struct FeSpecularLighting {
66
30
    base: Primitive,
67
30
    params: SpecularLightingParams,
68
}
69

            
70
80
#[derive(Clone)]
71
pub struct SpecularLightingParams {
72
40
    in1: Input,
73
40
    surface_scale: f64,
74
40
    kernel_unit_length: Option<(f64, f64)>,
75
40
    specular_constant: NonNegative,
76
40
    specular_exponent: f64,
77
}
78

            
79
impl Default for SpecularLightingParams {
80
30
    fn default() -> Self {
81
30
        Self {
82
30
            in1: Default::default(),
83
            surface_scale: 1.0,
84
30
            kernel_unit_length: None,
85
30
            specular_constant: NonNegative(1.0),
86
            specular_exponent: 1.0,
87
        }
88
30
    }
89
}
90

            
91
/// Resolved `feDiffuseLighting` primitive for rendering.
92
pub struct DiffuseLighting {
93
    params: DiffuseLightingParams,
94
    light: Light,
95
}
96

            
97
/// Resolved `feSpecularLighting` primitive for rendering.
98
pub struct SpecularLighting {
99
    params: SpecularLightingParams,
100
    light: Light,
101
}
102

            
103
/// A light source before applying affine transformations, straight out of the SVG.
104
6
#[derive(Debug, PartialEq)]
105
enum UntransformedLightSource {
106
1
    Distant(FeDistantLight),
107
1
    Point(FePointLight),
108
1
    Spot(FeSpotLight),
109
}
110

            
111
/// A light source with affine transformations applied.
112
enum LightSource {
113
    Distant {
114
        azimuth: f64,
115
        elevation: f64,
116
    },
117
    Point {
118
        origin: Vector3<f64>,
119
    },
120
    Spot {
121
        origin: Vector3<f64>,
122
        direction: Vector3<f64>,
123
        specular_exponent: f64,
124
        limiting_cone_angle: Option<f64>,
125
    },
126
}
127

            
128
impl UntransformedLightSource {
129
87
    fn transform(&self, paffine: Transform) -> LightSource {
130
87
        match *self {
131
63
            UntransformedLightSource::Distant(ref l) => l.transform(),
132
11
            UntransformedLightSource::Point(ref l) => l.transform(paffine),
133
13
            UntransformedLightSource::Spot(ref l) => l.transform(paffine),
134
        }
135
87
    }
136
}
137

            
138
struct Light {
139
    source: UntransformedLightSource,
140
    lighting_color: Color,
141
    color_interpolation_filters: ColorInterpolationFilters,
142
}
143

            
144
/// Returns the color and unit (or null) vector from the image sample to the light.
145
#[inline]
146
209136
fn color_and_vector(
147
    lighting_color: &RGBA,
148
    source: &LightSource,
149
    x: f64,
150
    y: f64,
151
    z: f64,
152
) -> (cssparser::RGBA, Vector3<f64>) {
153
209136
    let vector = match *source {
154
129340
        LightSource::Distant { azimuth, elevation } => {
155
129340
            let azimuth = azimuth.to_radians();
156
129340
            let elevation = elevation.to_radians();
157
129340
            Vector3::new(
158
129340
                azimuth.cos() * elevation.cos(),
159
129340
                azimuth.sin() * elevation.cos(),
160
129340
                elevation.sin(),
161
            )
162
        }
163
79796
        LightSource::Point { origin } | LightSource::Spot { origin, .. } => {
164
79796
            let mut v = origin - Vector3::new(x, y, z);
165
79796
            let _ = v.try_normalize_mut(0.0);
166
79796
            v
167
79796
        }
168
    };
169

            
170
209136
    let color = match *source {
171
        LightSource::Spot {
172
38273
            direction,
173
38273
            specular_exponent,
174
38273
            limiting_cone_angle,
175
            ..
176
        } => {
177
38273
            let transparent_color = cssparser::RGBA::new(Some(0), Some(0), Some(0), Some(0.0));
178
38273
            let minus_l_dot_s = -vector.dot(&direction);
179
38273
            match limiting_cone_angle {
180
38273
                _ if minus_l_dot_s <= 0.0 => transparent_color,
181
32264
                Some(a) if minus_l_dot_s < a.to_radians().cos() => transparent_color,
182
                _ => {
183
21048
                    let factor = minus_l_dot_s.powf(specular_exponent);
184
82223
                    let compute = |x| (clamp(f64::from(x) * factor, 0.0, 255.0) + 0.5) as u8;
185

            
186
21048
                    cssparser::RGBA {
187
21048
                        red: Some(compute(lighting_color.red.unwrap_or(0))),
188
21048
                        green: Some(compute(lighting_color.green.unwrap_or(0))),
189
21048
                        blue: Some(compute(lighting_color.blue.unwrap_or(0))),
190
21048
                        alpha: Some(1.0),
191
                    }
192
21048
                }
193
            }
194
        }
195
170863
        _ => *lighting_color,
196
    };
197

            
198
209136
    (color, vector)
199
209136
}
200

            
201
226
#[derive(Clone, Debug, Default, PartialEq)]
202
pub struct FeDistantLight {
203
113
    azimuth: f64,
204
113
    elevation: f64,
205
}
206

            
207
impl FeDistantLight {
208
63
    fn transform(&self) -> LightSource {
209
63
        LightSource::Distant {
210
63
            azimuth: self.azimuth,
211
63
            elevation: self.elevation,
212
        }
213
63
    }
214
}
215

            
216
impl ElementTrait for FeDistantLight {
217
48
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
218
144
        for (attr, value) in attrs.iter() {
219
96
            match attr.expanded() {
220
                expanded_name!("", "azimuth") => {
221
48
                    set_attribute(&mut self.azimuth, attr.parse(value), session)
222
                }
223
                expanded_name!("", "elevation") => {
224
48
                    set_attribute(&mut self.elevation, attr.parse(value), session)
225
                }
226
                _ => (),
227
            }
228
96
        }
229
48
    }
230
}
231

            
232
46
#[derive(Clone, Debug, Default, PartialEq)]
233
pub struct FePointLight {
234
23
    x: f64,
235
23
    y: f64,
236
23
    z: f64,
237
}
238

            
239
impl FePointLight {
240
11
    fn transform(&self, paffine: Transform) -> LightSource {
241
11
        let (x, y) = paffine.transform_point(self.x, self.y);
242
11
        let z = transform_dist(paffine, self.z);
243

            
244
11
        LightSource::Point {
245
11
            origin: Vector3::new(x, y, z),
246
        }
247
11
    }
248
}
249

            
250
impl ElementTrait for FePointLight {
251
10
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
252
40
        for (attr, value) in attrs.iter() {
253
30
            match attr.expanded() {
254
10
                expanded_name!("", "x") => set_attribute(&mut self.x, attr.parse(value), session),
255
10
                expanded_name!("", "y") => set_attribute(&mut self.y, attr.parse(value), session),
256
10
                expanded_name!("", "z") => set_attribute(&mut self.z, attr.parse(value), session),
257
                _ => (),
258
            }
259
30
        }
260
10
    }
261
}
262

            
263
30
#[derive(Clone, Debug, PartialEq)]
264
pub struct FeSpotLight {
265
15
    x: f64,
266
15
    y: f64,
267
15
    z: f64,
268
15
    points_at_x: f64,
269
15
    points_at_y: f64,
270
15
    points_at_z: f64,
271
15
    specular_exponent: f64,
272
15
    limiting_cone_angle: Option<f64>,
273
}
274

            
275
// We need this because, per the spec, the initial values for all fields are 0.0
276
// except for specular_exponent, which is 1.
277
impl Default for FeSpotLight {
278
14
    fn default() -> FeSpotLight {
279
14
        FeSpotLight {
280
            x: 0.0,
281
            y: 0.0,
282
            z: 0.0,
283
            points_at_x: 0.0,
284
            points_at_y: 0.0,
285
            points_at_z: 0.0,
286
            specular_exponent: 1.0,
287
14
            limiting_cone_angle: None,
288
        }
289
14
    }
290
}
291

            
292
impl FeSpotLight {
293
13
    fn transform(&self, paffine: Transform) -> LightSource {
294
13
        let (x, y) = paffine.transform_point(self.x, self.y);
295
13
        let z = transform_dist(paffine, self.z);
296
13
        let (points_at_x, points_at_y) =
297
13
            paffine.transform_point(self.points_at_x, self.points_at_y);
298
13
        let points_at_z = transform_dist(paffine, self.points_at_z);
299

            
300
13
        let origin = Vector3::new(x, y, z);
301
13
        let mut direction = Vector3::new(points_at_x, points_at_y, points_at_z) - origin;
302
13
        let _ = direction.try_normalize_mut(0.0);
303

            
304
13
        LightSource::Spot {
305
            origin,
306
13
            direction,
307
13
            specular_exponent: self.specular_exponent,
308
13
            limiting_cone_angle: self.limiting_cone_angle,
309
        }
310
13
    }
311
}
312

            
313
impl ElementTrait for FeSpotLight {
314
14
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
315
123
        for (attr, value) in attrs.iter() {
316
109
            match attr.expanded() {
317
14
                expanded_name!("", "x") => set_attribute(&mut self.x, attr.parse(value), session),
318
14
                expanded_name!("", "y") => set_attribute(&mut self.y, attr.parse(value), session),
319
14
                expanded_name!("", "z") => set_attribute(&mut self.z, attr.parse(value), session),
320
                expanded_name!("", "pointsAtX") => {
321
14
                    set_attribute(&mut self.points_at_x, attr.parse(value), session)
322
                }
323
                expanded_name!("", "pointsAtY") => {
324
14
                    set_attribute(&mut self.points_at_y, attr.parse(value), session)
325
                }
326
                expanded_name!("", "pointsAtZ") => {
327
14
                    set_attribute(&mut self.points_at_z, attr.parse(value), session)
328
                }
329

            
330
                expanded_name!("", "specularExponent") => {
331
14
                    set_attribute(&mut self.specular_exponent, attr.parse(value), session);
332
                }
333

            
334
                expanded_name!("", "limitingConeAngle") => {
335
11
                    set_attribute(&mut self.limiting_cone_angle, attr.parse(value), session);
336
                }
337

            
338
                _ => (),
339
            }
340
109
        }
341
14
    }
342
}
343

            
344
/// Applies the `primitiveUnits` coordinate transformation to a non-x or y distance.
345
#[inline]
346
37
fn transform_dist(t: Transform, d: f64) -> f64 {
347
37
    d * (t.xx.powi(2) + t.yy.powi(2)).sqrt() / std::f64::consts::SQRT_2
348
37
}
349

            
350
impl ElementTrait for FeDiffuseLighting {
351
42
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
352
42
        self.params.in1 = self.base.parse_one_input(attrs, session);
353

            
354
203
        for (attr, value) in attrs.iter() {
355
161
            match attr.expanded() {
356
                expanded_name!("", "surfaceScale") => {
357
40
                    set_attribute(&mut self.params.surface_scale, attr.parse(value), session);
358
                }
359
                expanded_name!("", "kernelUnitLength") => {
360
2
                    let v: Result<NumberOptionalNumber<f64>, _> = attr.parse(value);
361
2
                    match v {
362
2
                        Ok(NumberOptionalNumber(x, y)) => {
363
2
                            self.params.kernel_unit_length = Some((x, y));
364
2
                        }
365

            
366
                        Err(e) => {
367
                            rsvg_log!(session, "ignoring attribute with invalid value: {}", e);
368
                        }
369
                    }
370
                }
371
                expanded_name!("", "diffuseConstant") => {
372
40
                    set_attribute(
373
40
                        &mut self.params.diffuse_constant,
374
40
                        attr.parse(value),
375
                        session,
376
                    );
377
                }
378
                _ => (),
379
            }
380
161
        }
381
42
    }
382
}
383

            
384
impl DiffuseLighting {
385
    #[inline]
386
90498
    fn compute_factor(&self, normal: Normal, light_vector: Vector3<f64>) -> f64 {
387
90498
        let k = if normal.normal.is_zero() {
388
            // Common case of (0, 0, 1) normal.
389
75060
            light_vector.z
390
        } else {
391
30876
            let mut n = normal
392
                .normal
393
45146
                .map(|x| f64::from(x) * self.params.surface_scale / 255.);
394
15438
            n.component_mul_assign(&normal.factor);
395
15438
            let normal = Vector3::new(n.x, n.y, 1.0);
396

            
397
15438
            normal.dot(&light_vector) / normal.norm()
398
        };
399

            
400
90498
        self.params.diffuse_constant.0 * k
401
90498
    }
402
}
403

            
404
impl ElementTrait for FeSpecularLighting {
405
30
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
406
30
        self.params.in1 = self.base.parse_one_input(attrs, session);
407

            
408
167
        for (attr, value) in attrs.iter() {
409
137
            match attr.expanded() {
410
                expanded_name!("", "surfaceScale") => {
411
29
                    set_attribute(&mut self.params.surface_scale, attr.parse(value), session);
412
                }
413
                expanded_name!("", "kernelUnitLength") => {
414
                    let v: Result<NumberOptionalNumber<f64>, _> = attr.parse(value);
415
                    match v {
416
                        Ok(NumberOptionalNumber(x, y)) => {
417
                            self.params.kernel_unit_length = Some((x, y));
418
                        }
419

            
420
                        Err(e) => {
421
                            rsvg_log!(session, "ignoring attribute with invalid value: {}", e);
422
                        }
423
                    }
424
                }
425
                expanded_name!("", "specularConstant") => {
426
29
                    set_attribute(
427
29
                        &mut self.params.specular_constant,
428
29
                        attr.parse(value),
429
                        session,
430
                    );
431
                }
432
                expanded_name!("", "specularExponent") => {
433
29
                    set_attribute(
434
29
                        &mut self.params.specular_exponent,
435
29
                        attr.parse(value),
436
                        session,
437
                    );
438
                }
439
                _ => (),
440
            }
441
137
        }
442
30
    }
443
}
444

            
445
impl SpecularLighting {
446
    #[inline]
447
104866
    fn compute_factor(&self, normal: Normal, light_vector: Vector3<f64>) -> f64 {
448
104866
        let h = light_vector + Vector3::new(0.0, 0.0, 1.0);
449
104866
        let h_norm = h.norm();
450

            
451
104866
        if h_norm == 0.0 {
452
3
            return 0.0;
453
        }
454

            
455
104863
        let n_dot_h = if normal.normal.is_zero() {
456
            // Common case of (0, 0, 1) normal.
457
87544
            h.z / h_norm
458
        } else {
459
34638
            let mut n = normal
460
                .normal
461
54237
                .map(|x| f64::from(x) * self.params.surface_scale / 255.);
462
17319
            n.component_mul_assign(&normal.factor);
463
17319
            let normal = Vector3::new(n.x, n.y, 1.0);
464
17319
            normal.dot(&h) / normal.norm() / h_norm
465
        };
466

            
467
104863
        if approx_eq!(f64, self.params.specular_exponent, 1.0) {
468
37986
            self.params.specular_constant.0 * n_dot_h
469
        } else {
470
66877
            self.params.specular_constant.0 * n_dot_h.powf(self.params.specular_exponent)
471
        }
472
104866
    }
473
}
474

            
475
macro_rules! impl_lighting_filter {
476
    ($lighting_type:ty, $params_name:ident, $alpha_func:ident) => {
477
        impl $params_name {
478
87
            pub fn render(
479
                &self,
480
                bounds_builder: BoundsBuilder,
481
                ctx: &FilterContext,
482
                acquired_nodes: &mut AcquiredNodes<'_>,
483
                draw_ctx: &mut DrawingCtx,
484
            ) -> Result<FilterOutput, FilterError> {
485
174
                let input_1 = ctx.get_input(
486
                    acquired_nodes,
487
                    draw_ctx,
488
87
                    &self.params.in1,
489
87
                    self.light.color_interpolation_filters,
490
                )?;
491
87
                let mut bounds: IRect = bounds_builder
492
                    .add_input(&input_1)
493
                    .compute(ctx)
494
                    .clipped
495
                    .into();
496
87
                let original_bounds = bounds;
497

            
498
87
                let scale = self
499
                    .params
500
                    .kernel_unit_length
501
2
                    .and_then(|(x, y)| {
502
2
                        if x <= 0.0 || y <= 0.0 {
503
                            None
504
                        } else {
505
2
                            Some((x, y))
506
                        }
507
2
                    })
508
89
                    .map(|(dx, dy)| ctx.paffine().transform_distance(dx, dy));
509

            
510
87
                let mut input_surface = input_1.surface().clone();
511

            
512
89
                if let Some((ox, oy)) = scale {
513
                    // Scale the input surface to match kernel_unit_length.
514
2
                    let (new_surface, new_bounds) =
515
2
                        input_surface.scale(bounds, 1.0 / ox, 1.0 / oy)?;
516

            
517
2
                    input_surface = new_surface;
518
2
                    bounds = new_bounds;
519
2
                }
520

            
521
87
                let (bounds_w, bounds_h) = bounds.size();
522

            
523
                // Check if the surface is too small for normal computation. This case is
524
                // unspecified; WebKit doesn't render anything in this case.
525
87
                if bounds_w < 2 || bounds_h < 2 {
526
                    return Err(FilterError::LightingInputTooSmall);
527
                }
528

            
529
87
                let (ox, oy) = scale.unwrap_or((1.0, 1.0));
530

            
531
87
                let source = self.light.source.transform(ctx.paffine());
532

            
533
87
                let mut surface = ExclusiveImageSurface::new(
534
87
                    input_surface.width(),
535
87
                    input_surface.height(),
536
87
                    SurfaceType::from(self.light.color_interpolation_filters),
537
                )?;
538

            
539
87
                let lighting_color = color_to_rgba(&self.light.lighting_color);
540

            
541
                {
542
87
                    let output_stride = surface.stride() as usize;
543
87
                    let mut output_data = surface.data();
544
87
                    let output_slice = &mut *output_data;
545

            
546
                    let compute_output_pixel =
547
211679
                        |output_slice: &mut [u8], base_y, x, y, normal: Normal| {
548
211592
                            let pixel = input_surface.get_pixel(x, y);
549

            
550
211592
                            let scaled_x = f64::from(x) * ox;
551
211592
                            let scaled_y = f64::from(y) * oy;
552
211592
                            let z = f64::from(pixel.a) / 255.0 * self.params.surface_scale;
553

            
554
211592
                            let (color, vector) =
555
211592
                                color_and_vector(&lighting_color, &source, scaled_x, scaled_y, z);
556

            
557
                            // compute the factor just once for the three colors
558
211592
                            let factor = self.compute_factor(normal, vector);
559
                            let compute =
560
753105
                                |x| (clamp(factor * f64::from(x), 0.0, 255.0) + 0.5) as u8;
561

            
562
211592
                            let r = compute(color.red.unwrap_or(0));
563
211592
                            let g = compute(color.green.unwrap_or(0));
564
211592
                            let b = compute(color.blue.unwrap_or(0));
565
211592
                            let a = $alpha_func(r, g, b);
566

            
567
211592
                            let output_pixel = Pixel { r, g, b, a };
568

            
569
211592
                            output_slice.set_pixel(output_stride, output_pixel, x, y - base_y);
570
211592
                        };
571

            
572
                    // Top left.
573
87
                    compute_output_pixel(
574
                        output_slice,
575
                        0,
576
87
                        bounds.x0 as u32,
577
87
                        bounds.y0 as u32,
578
87
                        Normal::top_left(&input_surface, bounds),
579
                    );
580

            
581
                    // Top right.
582
87
                    compute_output_pixel(
583
                        output_slice,
584
                        0,
585
87
                        bounds.x1 as u32 - 1,
586
87
                        bounds.y0 as u32,
587
87
                        Normal::top_right(&input_surface, bounds),
588
                    );
589

            
590
                    // Bottom left.
591
87
                    compute_output_pixel(
592
                        output_slice,
593
                        0,
594
87
                        bounds.x0 as u32,
595
87
                        bounds.y1 as u32 - 1,
596
87
                        Normal::bottom_left(&input_surface, bounds),
597
                    );
598

            
599
                    // Bottom right.
600
87
                    compute_output_pixel(
601
                        output_slice,
602
                        0,
603
87
                        bounds.x1 as u32 - 1,
604
87
                        bounds.y1 as u32 - 1,
605
87
                        Normal::bottom_right(&input_surface, bounds),
606
                    );
607

            
608
87
                    if bounds_w >= 3 {
609
                        // Top row.
610
4503
                        for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
611
4416
                            compute_output_pixel(
612
                                output_slice,
613
                                0,
614
                                x,
615
4416
                                bounds.y0 as u32,
616
4416
                                Normal::top_row(&input_surface, bounds, x),
617
                            );
618
                        }
619

            
620
                        // Bottom row.
621
4503
                        for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
622
4416
                            compute_output_pixel(
623
                                output_slice,
624
                                0,
625
                                x,
626
4416
                                bounds.y1 as u32 - 1,
627
4416
                                Normal::bottom_row(&input_surface, bounds, x),
628
                            );
629
                        }
630
                    }
631

            
632
87
                    if bounds_h >= 3 {
633
                        // Left column.
634
3350
                        for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
635
3263
                            compute_output_pixel(
636
                                output_slice,
637
                                0,
638
3263
                                bounds.x0 as u32,
639
                                y,
640
3263
                                Normal::left_column(&input_surface, bounds, y),
641
                            );
642
                        }
643

            
644
                        // Right column.
645
3350
                        for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
646
3263
                            compute_output_pixel(
647
                                output_slice,
648
                                0,
649
3263
                                bounds.x1 as u32 - 1,
650
                                y,
651
3263
                                Normal::right_column(&input_surface, bounds, y),
652
                            );
653
                        }
654
                    }
655

            
656
87
                    if bounds_w >= 3 && bounds_h >= 3 {
657
                        // Interior pixels.
658
87
                        let first_row = bounds.y0 as u32 + 1;
659
87
                        let one_past_last_row = bounds.y1 as u32 - 1;
660
87
                        let first_pixel = (first_row as usize) * output_stride;
661
87
                        let one_past_last_pixel = (one_past_last_row as usize) * output_stride;
662

            
663
87
                        output_slice[first_pixel..one_past_last_pixel]
664
87
                            .par_chunks_mut(output_stride)
665
87
                            .zip(first_row..one_past_last_row)
666
3347
                            .for_each(|(slice, y)| {
667
189982
                                for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
668
373444
                                    compute_output_pixel(
669
                                        slice,
670
                                        y,
671
                                        x,
672
                                        y,
673
186722
                                        Normal::interior(&input_surface, bounds, x, y),
674
                                    );
675
                                }
676
3260
                            });
677
                    }
678
87
                }
679

            
680
87
                let mut surface = surface.share()?;
681

            
682
89
                if let Some((ox, oy)) = scale {
683
                    // Scale the output surface back.
684
2
                    surface = surface.scale_to(
685
2
                        ctx.source_graphic().width(),
686
2
                        ctx.source_graphic().height(),
687
                        original_bounds,
688
                        ox,
689
                        oy,
690
                    )?;
691

            
692
2
                    bounds = original_bounds;
693
                }
694

            
695
87
                Ok(FilterOutput { surface, bounds })
696
87
            }
697
        }
698

            
699
        impl FilterEffect for $lighting_type {
700
90
            fn resolve(
701
                &self,
702
                _acquired_nodes: &mut AcquiredNodes<'_>,
703
                node: &Node,
704
            ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> {
705
360
                let mut sources = node.children().rev().filter(|c| {
706
270
                    c.is_element()
707
90
                        && matches!(
708
90
                            *c.borrow_element_data(),
709
                            ElementData::FeDistantLight(_)
710
                                | ElementData::FePointLight(_)
711
                                | ElementData::FeSpotLight(_)
712
                        )
713
270
                });
714

            
715
90
                let source_node = sources.next();
716
90
                if source_node.is_none() || sources.next().is_some() {
717
                    return Err(FilterResolveError::InvalidLightSourceCount);
718
                }
719

            
720
90
                let source_node = source_node.unwrap();
721

            
722
90
                let source = match &*source_node.borrow_element_data() {
723
64
                    ElementData::FeDistantLight(l) => {
724
64
                        UntransformedLightSource::Distant((**l).clone())
725
64
                    }
726
12
                    ElementData::FePointLight(l) => UntransformedLightSource::Point((**l).clone()),
727
14
                    ElementData::FeSpotLight(l) => UntransformedLightSource::Spot((**l).clone()),
728
                    _ => unreachable!(),
729
90
                };
730

            
731
90
                let cascaded = CascadedValues::new_from_node(node);
732
90
                let values = cascaded.get();
733

            
734
180
                Ok(vec![ResolvedPrimitive {
735
90
                    primitive: self.base.clone(),
736
90
                    params: PrimitiveParams::$params_name($params_name {
737
90
                        params: self.params.clone(),
738
90
                        light: Light {
739
90
                            source,
740
90
                            lighting_color: resolve_color(
741
90
                                &values.lighting_color().0,
742
90
                                UnitInterval::clamp(1.0),
743
90
                                &values.color().0,
744
                            ),
745
90
                            color_interpolation_filters: values.color_interpolation_filters(),
746
                        },
747
                    }),
748
                }])
749
90
            }
750
        }
751
    };
752
}
753

            
754
85506
const fn diffuse_alpha(_r: u8, _g: u8, _b: u8) -> u8 {
755
    255
756
85506
}
757

            
758
103100
fn specular_alpha(r: u8, g: u8, b: u8) -> u8 {
759
103100
    max(max(r, g), b)
760
103100
}
761

            
762
impl_lighting_filter!(FeDiffuseLighting, DiffuseLighting, diffuse_alpha);
763

            
764
impl_lighting_filter!(FeSpecularLighting, SpecularLighting, specular_alpha);
765

            
766
/// 2D normal and factor stored separately.
767
///
768
/// The normal needs to be multiplied by `surface_scale * factor / 255` and
769
/// normalized with 1 as the z component.
770
/// pub for the purpose of accessing this from benchmarks.
771
#[derive(Debug, Clone, Copy)]
772
pub struct Normal {
773
    pub factor: Vector2<f64>,
774
    pub normal: Vector2<i16>,
775
}
776

            
777
impl Normal {
778
    #[inline]
779
209687
    fn new(factor_x: f64, nx: i16, factor_y: f64, ny: i16) -> Normal {
780
        // Negative nx and ny to account for the different coordinate system.
781
209687
        Normal {
782
209687
            factor: Vector2::new(factor_x, factor_y),
783
209687
            normal: Vector2::new(-nx, -ny),
784
        }
785
209687
    }
786

            
787
    /// Computes and returns the normal vector for the top left pixel for light filters.
788
    #[inline]
789
87
    pub fn top_left(surface: &SharedImageSurface, bounds: IRect) -> Normal {
790
        // Surface needs to be at least 2×2.
791
87
        assert!(bounds.width() >= 2);
792
87
        assert!(bounds.height() >= 2);
793

            
794
435
        let get = |x, y| i16::from(surface.get_pixel(x, y).a);
795
87
        let (x, y) = (bounds.x0 as u32, bounds.y0 as u32);
796

            
797
87
        let center = get(x, y);
798
87
        let right = get(x + 1, y);
799
87
        let bottom = get(x, y + 1);
800
87
        let bottom_right = get(x + 1, y + 1);
801

            
802
87
        Self::new(
803
            2. / 3.,
804
87
            -2 * center + 2 * right - bottom + bottom_right,
805
            2. / 3.,
806
87
            -2 * center - right + 2 * bottom + bottom_right,
807
        )
808
87
    }
809

            
810
    /// Computes and returns the normal vector for the top row pixels for light filters.
811
    #[inline]
812
4416
    pub fn top_row(surface: &SharedImageSurface, bounds: IRect, x: u32) -> Normal {
813
4416
        assert!(x as i32 > bounds.x0);
814
4416
        assert!((x as i32) + 1 < bounds.x1);
815
4416
        assert!(bounds.height() >= 2);
816

            
817
30903
        let get = |x, y| i16::from(surface.get_pixel(x, y).a);
818
4416
        let y = bounds.y0 as u32;
819

            
820
4416
        let left = get(x - 1, y);
821
4416
        let center = get(x, y);
822
4416
        let right = get(x + 1, y);
823
4416
        let bottom_left = get(x - 1, y + 1);
824
4416
        let bottom = get(x, y + 1);
825
4416
        let bottom_right = get(x + 1, y + 1);
826

            
827
4416
        Self::new(
828
            1. / 3.,
829
4416
            -2 * left + 2 * right - bottom_left + bottom_right,
830
            1. / 2.,
831
4416
            -left - 2 * center - right + bottom_left + 2 * bottom + bottom_right,
832
        )
833
4416
    }
834

            
835
    /// Computes and returns the normal vector for the top right pixel for light filters.
836
    #[inline]
837
87
    pub fn top_right(surface: &SharedImageSurface, bounds: IRect) -> Normal {
838
        // Surface needs to be at least 2×2.
839
87
        assert!(bounds.width() >= 2);
840
87
        assert!(bounds.height() >= 2);
841

            
842
435
        let get = |x, y| i16::from(surface.get_pixel(x, y).a);
843
87
        let (x, y) = (bounds.x1 as u32 - 1, bounds.y0 as u32);
844

            
845
87
        let left = get(x - 1, y);
846
87
        let center = get(x, y);
847
87
        let bottom_left = get(x - 1, y + 1);
848
87
        let bottom = get(x, y + 1);
849

            
850
87
        Self::new(
851
            2. / 3.,
852
87
            -2 * left + 2 * center - bottom_left + bottom,
853
            2. / 3.,
854
87
            -left - 2 * center + bottom_left + 2 * bottom,
855
        )
856
87
    }
857

            
858
    /// Computes and returns the normal vector for the left column pixels for light filters.
859
    #[inline]
860
3263
    pub fn left_column(surface: &SharedImageSurface, bounds: IRect, y: u32) -> Normal {
861
3263
        assert!(y as i32 > bounds.y0);
862
3263
        assert!((y as i32) + 1 < bounds.y1);
863
3263
        assert!(bounds.width() >= 2);
864

            
865
22841
        let get = |x, y| i16::from(surface.get_pixel(x, y).a);
866
3263
        let x = bounds.x0 as u32;
867

            
868
3263
        let top = get(x, y - 1);
869
3263
        let top_right = get(x + 1, y - 1);
870
3263
        let center = get(x, y);
871
3263
        let right = get(x + 1, y);
872
3263
        let bottom = get(x, y + 1);
873
3263
        let bottom_right = get(x + 1, y + 1);
874

            
875
3263
        Self::new(
876
            1. / 2.,
877
3263
            -top + top_right - 2 * center + 2 * right - bottom + bottom_right,
878
            1. / 3.,
879
3263
            -2 * top - top_right + 2 * bottom + bottom_right,
880
        )
881
3263
    }
882

            
883
    /// Computes and returns the normal vector for the interior pixels for light filters.
884
    #[inline]
885
188570
    pub fn interior(surface: &SharedImageSurface, bounds: IRect, x: u32, y: u32) -> Normal {
886
188570
        assert!(x as i32 > bounds.x0);
887
188570
        assert!((x as i32) + 1 < bounds.x1);
888
188570
        assert!(y as i32 > bounds.y0);
889
188570
        assert!((y as i32) + 1 < bounds.y1);
890

            
891
1568842
        let get = |x, y| i16::from(surface.get_pixel(x, y).a);
892

            
893
188570
        let top_left = get(x - 1, y - 1);
894
188570
        let top = get(x, y - 1);
895
188570
        let top_right = get(x + 1, y - 1);
896
188570
        let left = get(x - 1, y);
897
188570
        let right = get(x + 1, y);
898
188570
        let bottom_left = get(x - 1, y + 1);
899
188570
        let bottom = get(x, y + 1);
900
188570
        let bottom_right = get(x + 1, y + 1);
901

            
902
188570
        Self::new(
903
            1. / 4.,
904
188570
            -top_left + top_right - 2 * left + 2 * right - bottom_left + bottom_right,
905
            1. / 4.,
906
188570
            -top_left - 2 * top - top_right + bottom_left + 2 * bottom + bottom_right,
907
        )
908
188570
    }
909

            
910
    /// Computes and returns the normal vector for the right column pixels for light filters.
911
    #[inline]
912
3263
    pub fn right_column(surface: &SharedImageSurface, bounds: IRect, y: u32) -> Normal {
913
3263
        assert!(y as i32 > bounds.y0);
914
3263
        assert!((y as i32) + 1 < bounds.y1);
915
3263
        assert!(bounds.width() >= 2);
916

            
917
22841
        let get = |x, y| i16::from(surface.get_pixel(x, y).a);
918
3263
        let x = bounds.x1 as u32 - 1;
919

            
920
3263
        let top_left = get(x - 1, y - 1);
921
3263
        let top = get(x, y - 1);
922
3263
        let left = get(x - 1, y);
923
3263
        let center = get(x, y);
924
3263
        let bottom_left = get(x - 1, y + 1);
925
3263
        let bottom = get(x, y + 1);
926

            
927
3263
        Self::new(
928
            1. / 2.,
929
3263
            -top_left + top - 2 * left + 2 * center - bottom_left + bottom,
930
            1. / 3.,
931
3263
            -top_left - 2 * top + bottom_left + 2 * bottom,
932
        )
933
3263
    }
934

            
935
    /// Computes and returns the normal vector for the bottom left pixel for light filters.
936
    #[inline]
937
87
    pub fn bottom_left(surface: &SharedImageSurface, bounds: IRect) -> Normal {
938
        // Surface needs to be at least 2×2.
939
87
        assert!(bounds.width() >= 2);
940
87
        assert!(bounds.height() >= 2);
941

            
942
435
        let get = |x, y| i16::from(surface.get_pixel(x, y).a);
943
87
        let (x, y) = (bounds.x0 as u32, bounds.y1 as u32 - 1);
944

            
945
87
        let top = get(x, y - 1);
946
87
        let top_right = get(x + 1, y - 1);
947
87
        let center = get(x, y);
948
87
        let right = get(x + 1, y);
949

            
950
87
        Self::new(
951
            2. / 3.,
952
87
            -top + top_right - 2 * center + 2 * right,
953
            2. / 3.,
954
87
            -2 * top - top_right + 2 * center + right,
955
        )
956
87
    }
957

            
958
    /// Computes and returns the normal vector for the bottom row pixels for light filters.
959
    #[inline]
960
4415
    pub fn bottom_row(surface: &SharedImageSurface, bounds: IRect, x: u32) -> Normal {
961
4415
        assert!(x as i32 > bounds.x0);
962
4415
        assert!((x as i32) + 1 < bounds.x1);
963
4415
        assert!(bounds.height() >= 2);
964

            
965
30903
        let get = |x, y| i16::from(surface.get_pixel(x, y).a);
966
4415
        let y = bounds.y1 as u32 - 1;
967

            
968
4415
        let top_left = get(x - 1, y - 1);
969
4415
        let top = get(x, y - 1);
970
4415
        let top_right = get(x + 1, y - 1);
971
4415
        let left = get(x - 1, y);
972
4415
        let center = get(x, y);
973
4415
        let right = get(x + 1, y);
974

            
975
4415
        Self::new(
976
            1. / 3.,
977
4415
            -top_left + top_right - 2 * left + 2 * right,
978
            1. / 2.,
979
4415
            -top_left - 2 * top - top_right + left + 2 * center + right,
980
        )
981
4415
    }
982

            
983
    /// Computes and returns the normal vector for the bottom right pixel for light filters.
984
    #[inline]
985
87
    pub fn bottom_right(surface: &SharedImageSurface, bounds: IRect) -> Normal {
986
        // Surface needs to be at least 2×2.
987
87
        assert!(bounds.width() >= 2);
988
87
        assert!(bounds.height() >= 2);
989

            
990
435
        let get = |x, y| i16::from(surface.get_pixel(x, y).a);
991
87
        let (x, y) = (bounds.x1 as u32 - 1, bounds.y1 as u32 - 1);
992

            
993
87
        let top_left = get(x - 1, y - 1);
994
87
        let top = get(x, y - 1);
995
87
        let left = get(x - 1, y);
996
87
        let center = get(x, y);
997

            
998
87
        Self::new(
999
            2. / 3.,
87
            -top_left + top - 2 * left + 2 * center,
            2. / 3.,
87
            -top_left - 2 * top + left + 2 * center,
        )
87
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    use crate::borrow_element_as;
    use crate::document::Document;
    #[test]
2
    fn extracts_light_source() {
1
        let document = Document::load_from_bytes(
            br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg">
  <filter id="filter">
    <feDiffuseLighting id="diffuse_distant">
      <feDistantLight azimuth="0.0" elevation="45.0"/>
    </feDiffuseLighting>
    <feSpecularLighting id="specular_point">
      <fePointLight x="1.0" y="2.0" z="3.0"/>
    </feSpecularLighting>
    <feDiffuseLighting id="diffuse_spot">
      <feSpotLight x="1.0" y="2.0" z="3.0"
                   pointsAtX="4.0" pointsAtY="5.0" pointsAtZ="6.0"
                   specularExponent="7.0" limitingConeAngle="8.0"/>
    </feDiffuseLighting>
  </filter>
</svg>
"#,
        );
1
        let mut acquired_nodes = AcquiredNodes::new(&document);
1
        let node = document.lookup_internal_node("diffuse_distant").unwrap();
1
        let lighting = borrow_element_as!(node, FeDiffuseLighting);
1
        let resolved = lighting.resolve(&mut acquired_nodes, &node).unwrap();
1
        let ResolvedPrimitive { params, .. } = resolved.first().unwrap();
1
        let diffuse_lighting = match params {
1
            PrimitiveParams::DiffuseLighting(l) => l,
            _ => unreachable!(),
        };
1
        assert_eq!(
            diffuse_lighting.light.source,
            UntransformedLightSource::Distant(FeDistantLight {
                azimuth: 0.0,
                elevation: 45.0,
            })
        );
1
        let node = document.lookup_internal_node("specular_point").unwrap();
1
        let lighting = borrow_element_as!(node, FeSpecularLighting);
1
        let resolved = lighting.resolve(&mut acquired_nodes, &node).unwrap();
1
        let ResolvedPrimitive { params, .. } = resolved.first().unwrap();
1
        let specular_lighting = match params {
1
            PrimitiveParams::SpecularLighting(l) => l,
            _ => unreachable!(),
        };
1
        assert_eq!(
            specular_lighting.light.source,
            UntransformedLightSource::Point(FePointLight {
                x: 1.0,
                y: 2.0,
                z: 3.0,
            })
        );
1
        let node = document.lookup_internal_node("diffuse_spot").unwrap();
1
        let lighting = borrow_element_as!(node, FeDiffuseLighting);
1
        let resolved = lighting.resolve(&mut acquired_nodes, &node).unwrap();
1
        let ResolvedPrimitive { params, .. } = resolved.first().unwrap();
1
        let diffuse_lighting = match params {
1
            PrimitiveParams::DiffuseLighting(l) => l,
            _ => unreachable!(),
        };
1
        assert_eq!(
            diffuse_lighting.light.source,
            UntransformedLightSource::Spot(FeSpotLight {
                x: 1.0,
                y: 2.0,
                z: 3.0,
                points_at_x: 4.0,
                points_at_y: 5.0,
                points_at_z: 6.0,
                specular_exponent: 7.0,
                limiting_cone_angle: Some(8.0),
            })
        );
2
    }
}