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, ParseValue};
24
use crate::properties::ColorInterpolationFilters;
25
use crate::rect::IRect;
26
use crate::session::Session;
27
use crate::surface_utils::{
28
    shared_surface::{ExclusiveImageSurface, SharedImageSurface, SurfaceType},
29
    ImageSurfaceDataExt, Pixel,
30
};
31
use crate::transform::Transform;
32
use crate::unit_interval::UnitInterval;
33
use crate::util::clamp;
34
use crate::xml::Attributes;
35

            
36
use super::convolve_matrix::KernelUnitLength;
37

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
199
209415
    (color, vector)
200
209415
}
201

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
355
203
        for (attr, value) in attrs.iter() {
356
161
            match attr.expanded() {
357
                expanded_name!("", "surfaceScale") => {
358
40
                    set_attribute(&mut self.params.surface_scale, attr.parse(value), session);
359
                }
360
2
                expanded_name!("", "kernelUnitLength") => {
361
2
                    self.params.kernel_unit_length =
362
2
                        KernelUnitLength::from_attribute(&attr, value, session).unwrap_or_default();
363
                }
364
                expanded_name!("", "diffuseConstant") => {
365
40
                    set_attribute(
366
40
                        &mut self.params.diffuse_constant,
367
40
                        attr.parse(value),
368
                        session,
369
                    );
370
                }
371
                _ => (),
372
            }
373
161
        }
374
42
    }
375
}
376

            
377
impl DiffuseLighting {
378
    #[inline]
379
90683
    fn compute_factor(&self, normal: Normal, light_vector: Vector3<f64>) -> f64 {
380
90683
        let k = if normal.normal.is_zero() {
381
            // Common case of (0, 0, 1) normal.
382
75226
            light_vector.z
383
        } else {
384
15457
            let mut n = normal
385
                .normal
386
29755
                .map(|x| f64::from(x) * self.params.surface_scale / 255.);
387
15457
            n.component_mul_assign(&normal.factor);
388
15457
            let normal = Vector3::new(n.x, n.y, 1.0);
389

            
390
15457
            normal.dot(&light_vector) / normal.norm()
391
        };
392

            
393
90683
        self.params.diffuse_constant.0 * k
394
90683
    }
395
}
396

            
397
impl ElementTrait for FeSpecularLighting {
398
30
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
399
30
        self.params.in1 = self.base.parse_one_input(attrs, session);
400

            
401
167
        for (attr, value) in attrs.iter() {
402
137
            match attr.expanded() {
403
                expanded_name!("", "surfaceScale") => {
404
29
                    set_attribute(&mut self.params.surface_scale, attr.parse(value), session);
405
                }
406
                expanded_name!("", "kernelUnitLength") => {
407
                    self.params.kernel_unit_length =
408
                        KernelUnitLength::from_attribute(&attr, value, session).unwrap_or_default();
409
                }
410
                expanded_name!("", "specularConstant") => {
411
29
                    set_attribute(
412
29
                        &mut self.params.specular_constant,
413
29
                        attr.parse(value),
414
                        session,
415
                    );
416
                }
417
                expanded_name!("", "specularExponent") => {
418
29
                    set_attribute(
419
29
                        &mut self.params.specular_exponent,
420
29
                        attr.parse(value),
421
                        session,
422
                    );
423
                }
424
                _ => (),
425
            }
426
137
        }
427
30
    }
428
}
429

            
430
impl SpecularLighting {
431
    #[inline]
432
104338
    fn compute_factor(&self, normal: Normal, light_vector: Vector3<f64>) -> f64 {
433
104338
        let h = light_vector + Vector3::new(0.0, 0.0, 1.0);
434
104338
        let h_norm = h.norm();
435

            
436
104338
        if h_norm == 0.0 {
437
3
            return 0.0;
438
        }
439

            
440
104335
        let n_dot_h = if normal.normal.is_zero() {
441
            // Common case of (0, 0, 1) normal.
442
87460
            h.z / h_norm
443
        } else {
444
16875
            let mut n = normal
445
                .normal
446
36865
                .map(|x| f64::from(x) * self.params.surface_scale / 255.);
447
16875
            n.component_mul_assign(&normal.factor);
448
16875
            let normal = Vector3::new(n.x, n.y, 1.0);
449
16875
            normal.dot(&h) / normal.norm() / h_norm
450
        };
451

            
452
104335
        if approx_eq!(f64, self.params.specular_exponent, 1.0) {
453
37715
            self.params.specular_constant.0 * n_dot_h
454
        } else {
455
66620
            self.params.specular_constant.0 * n_dot_h.powf(self.params.specular_exponent)
456
        }
457
104338
    }
458
}
459

            
460
macro_rules! impl_lighting_filter {
461
    ($lighting_type:ty, $params_name:ident, $alpha_func:ident) => {
462
        impl $params_name {
463
89
            pub fn render(
464
                &self,
465
                bounds_builder: BoundsBuilder,
466
                ctx: &FilterContext,
467
                acquired_nodes: &mut AcquiredNodes<'_>,
468
                draw_ctx: &mut DrawingCtx,
469
            ) -> Result<FilterOutput, FilterError> {
470
178
                let input_1 = ctx.get_input(
471
                    acquired_nodes,
472
                    draw_ctx,
473
89
                    &self.params.in1,
474
89
                    self.light.color_interpolation_filters,
475
                )?;
476
89
                let mut bounds: IRect = bounds_builder
477
                    .add_input(&input_1)
478
                    .compute(ctx)
479
                    .clipped
480
                    .into();
481
87
                let original_bounds = bounds;
482

            
483
87
                let scale = self
484
                    .params
485
                    .kernel_unit_length
486
                    .0
487
2
                    .map(|(dx, dy)| ctx.paffine().transform_distance(dx, dy));
488

            
489
87
                let mut input_surface = input_1.surface().clone();
490

            
491
89
                if let Some((ox, oy)) = scale {
492
                    // Scale the input surface to match kernel_unit_length.
493
2
                    let (new_surface, new_bounds) =
494
2
                        input_surface.scale(bounds, 1.0 / ox, 1.0 / oy)?;
495

            
496
2
                    input_surface = new_surface;
497
2
                    bounds = new_bounds;
498
2
                }
499

            
500
87
                let (bounds_w, bounds_h) = bounds.size();
501

            
502
                // Check if the surface is too small for normal computation. This case is
503
                // unspecified; WebKit doesn't render anything in this case.
504
87
                if bounds_w < 2 || bounds_h < 2 {
505
                    return Err(FilterError::LightingInputTooSmall);
506
                }
507

            
508
87
                let (ox, oy) = scale.unwrap_or((1.0, 1.0));
509

            
510
87
                let source = self.light.source.transform(ctx.paffine());
511

            
512
87
                let mut surface = ExclusiveImageSurface::new(
513
87
                    input_surface.width(),
514
87
                    input_surface.height(),
515
87
                    SurfaceType::from(self.light.color_interpolation_filters),
516
                )?;
517

            
518
87
                let lighting_color = color_to_rgba(&self.light.lighting_color);
519

            
520
                {
521
87
                    let output_stride = surface.stride() as usize;
522
87
                    let mut output_data = surface.data();
523
87
                    let output_slice = &mut *output_data;
524

            
525
                    let compute_output_pixel =
526
210379
                        |output_slice: &mut [u8], base_y, x, y, normal: Normal| {
527
210292
                            let pixel = input_surface.get_pixel(x, y);
528

            
529
210292
                            let scaled_x = f64::from(x) * ox;
530
210292
                            let scaled_y = f64::from(y) * oy;
531
210292
                            let z = f64::from(pixel.a) / 255.0 * self.params.surface_scale;
532

            
533
210292
                            let (color, vector) =
534
210292
                                color_and_vector(&lighting_color, &source, scaled_x, scaled_y, z);
535

            
536
                            // compute the factor just once for the three colors
537
210292
                            let factor = self.compute_factor(normal, vector);
538
                            let compute =
539
751029
                                |x| (clamp(factor * f64::from(x), 0.0, 255.0) + 0.5) as u8;
540

            
541
210292
                            let r = compute(color.red.unwrap_or(0));
542
210292
                            let g = compute(color.green.unwrap_or(0));
543
210292
                            let b = compute(color.blue.unwrap_or(0));
544
210292
                            let a = $alpha_func(r, g, b);
545

            
546
210292
                            let output_pixel = Pixel { r, g, b, a };
547

            
548
210292
                            output_slice.set_pixel(output_stride, output_pixel, x, y - base_y);
549
210292
                        };
550

            
551
                    // Top left.
552
87
                    compute_output_pixel(
553
                        output_slice,
554
                        0,
555
87
                        bounds.x0 as u32,
556
87
                        bounds.y0 as u32,
557
87
                        Normal::top_left(&input_surface, bounds),
558
                    );
559

            
560
                    // Top right.
561
87
                    compute_output_pixel(
562
                        output_slice,
563
                        0,
564
87
                        bounds.x1 as u32 - 1,
565
87
                        bounds.y0 as u32,
566
87
                        Normal::top_right(&input_surface, bounds),
567
                    );
568

            
569
                    // Bottom left.
570
87
                    compute_output_pixel(
571
                        output_slice,
572
                        0,
573
87
                        bounds.x0 as u32,
574
87
                        bounds.y1 as u32 - 1,
575
87
                        Normal::bottom_left(&input_surface, bounds),
576
                    );
577

            
578
                    // Bottom right.
579
87
                    compute_output_pixel(
580
                        output_slice,
581
                        0,
582
87
                        bounds.x1 as u32 - 1,
583
87
                        bounds.y1 as u32 - 1,
584
87
                        Normal::bottom_right(&input_surface, bounds),
585
                    );
586

            
587
87
                    if bounds_w >= 3 {
588
                        // Top row.
589
4502
                        for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
590
4416
                            compute_output_pixel(
591
                                output_slice,
592
                                0,
593
                                x,
594
4417
                                bounds.y0 as u32,
595
4417
                                Normal::top_row(&input_surface, bounds, x),
596
                            );
597
                        }
598

            
599
                        // Bottom row.
600
4503
                        for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
601
4416
                            compute_output_pixel(
602
                                output_slice,
603
                                0,
604
                                x,
605
4416
                                bounds.y1 as u32 - 1,
606
4416
                                Normal::bottom_row(&input_surface, bounds, x),
607
                            );
608
                        }
609
                    }
610

            
611
89
                    if bounds_h >= 3 {
612
                        // Left column.
613
3350
                        for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
614
3263
                            compute_output_pixel(
615
                                output_slice,
616
                                0,
617
3263
                                bounds.x0 as u32,
618
                                y,
619
3263
                                Normal::left_column(&input_surface, bounds, y),
620
                            );
621
                        }
622

            
623
                        // Right column.
624
3350
                        for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
625
3263
                            compute_output_pixel(
626
                                output_slice,
627
                                0,
628
3263
                                bounds.x1 as u32 - 1,
629
                                y,
630
3263
                                Normal::right_column(&input_surface, bounds, y),
631
                            );
632
                        }
633
                    }
634

            
635
89
                    if bounds_w >= 3 && bounds_h >= 3 {
636
                        // Interior pixels.
637
87
                        let first_row = bounds.y0 as u32 + 1;
638
87
                        let one_past_last_row = bounds.y1 as u32 - 1;
639
87
                        let first_pixel = (first_row as usize) * output_stride;
640
87
                        let one_past_last_pixel = (one_past_last_row as usize) * output_stride;
641

            
642
87
                        output_slice[first_pixel..one_past_last_pixel]
643
                            .par_chunks_mut(output_stride)
644
                            .zip(first_row..one_past_last_row)
645
3349
                            .for_each(|(slice, y)| {
646
196014
                                for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
647
385504
                                    compute_output_pixel(
648
                                        slice,
649
                                        y,
650
                                        x,
651
                                        y,
652
192752
                                        Normal::interior(&input_surface, bounds, x, y),
653
                                    );
654
                                }
655
3262
                            });
656
                    }
657
89
                }
658

            
659
87
                let mut surface = surface.share()?;
660

            
661
89
                if let Some((ox, oy)) = scale {
662
                    // Scale the output surface back.
663
2
                    surface = surface.scale_to(
664
2
                        ctx.source_graphic().width(),
665
2
                        ctx.source_graphic().height(),
666
                        original_bounds,
667
                        ox,
668
                        oy,
669
                    )?;
670

            
671
2
                    bounds = original_bounds;
672
                }
673

            
674
87
                Ok(FilterOutput { surface, bounds })
675
87
            }
676
        }
677

            
678
        impl FilterEffect for $lighting_type {
679
90
            fn resolve(
680
                &self,
681
                _acquired_nodes: &mut AcquiredNodes<'_>,
682
                node: &Node,
683
            ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> {
684
360
                let mut sources = node.children().rev().filter(|c| {
685
270
                    c.is_element()
686
90
                        && matches!(
687
90
                            *c.borrow_element_data(),
688
                            ElementData::FeDistantLight(_)
689
                                | ElementData::FePointLight(_)
690
                                | ElementData::FeSpotLight(_)
691
                        )
692
270
                });
693

            
694
90
                let source_node = sources.next();
695
90
                if source_node.is_none() || sources.next().is_some() {
696
                    return Err(FilterResolveError::InvalidLightSourceCount);
697
                }
698

            
699
90
                let source_node = source_node.unwrap();
700

            
701
90
                let source = match &*source_node.borrow_element_data() {
702
64
                    ElementData::FeDistantLight(l) => {
703
64
                        UntransformedLightSource::Distant((**l).clone())
704
64
                    }
705
12
                    ElementData::FePointLight(l) => UntransformedLightSource::Point((**l).clone()),
706
14
                    ElementData::FeSpotLight(l) => UntransformedLightSource::Spot((**l).clone()),
707
                    _ => unreachable!(),
708
90
                };
709

            
710
90
                let cascaded = CascadedValues::new_from_node(node);
711
90
                let values = cascaded.get();
712

            
713
180
                Ok(vec![ResolvedPrimitive {
714
90
                    primitive: self.base.clone(),
715
90
                    params: PrimitiveParams::$params_name($params_name {
716
90
                        params: self.params.clone(),
717
90
                        light: Light {
718
90
                            source,
719
90
                            lighting_color: resolve_color(
720
90
                                &values.lighting_color().0,
721
90
                                UnitInterval::clamp(1.0),
722
90
                                &values.color().0,
723
                            ),
724
90
                            color_interpolation_filters: values.color_interpolation_filters(),
725
                        },
726
                    }),
727
                }])
728
90
            }
729
        }
730
    };
731
}
732

            
733
86273
const fn diffuse_alpha(_r: u8, _g: u8, _b: u8) -> u8 {
734
    255
735
86273
}
736

            
737
103457
fn specular_alpha(r: u8, g: u8, b: u8) -> u8 {
738
103457
    max(max(r, g), b)
739
103457
}
740

            
741
impl_lighting_filter!(FeDiffuseLighting, DiffuseLighting, diffuse_alpha);
742

            
743
impl_lighting_filter!(FeSpecularLighting, SpecularLighting, specular_alpha);
744

            
745
/// 2D normal and factor stored separately.
746
///
747
/// The normal needs to be multiplied by `surface_scale * factor / 255` and
748
/// normalized with 1 as the z component.
749
/// pub for the purpose of accessing this from benchmarks.
750
#[derive(Debug, Clone, Copy)]
751
pub struct Normal {
752
    pub factor: Vector2<f64>,
753
    pub normal: Vector2<i16>,
754
}
755

            
756
impl Normal {
757
    #[inline]
758
209992
    fn new(factor_x: f64, nx: i16, factor_y: f64, ny: i16) -> Normal {
759
        // Negative nx and ny to account for the different coordinate system.
760
209992
        Normal {
761
209992
            factor: Vector2::new(factor_x, factor_y),
762
209992
            normal: Vector2::new(-nx, -ny),
763
        }
764
209992
    }
765

            
766
    /// Computes and returns the normal vector for the top left pixel for light filters.
767
    #[inline]
768
87
    pub fn top_left(surface: &SharedImageSurface, bounds: IRect) -> Normal {
769
        // Surface needs to be at least 2×2.
770
87
        assert!(bounds.width() >= 2);
771
87
        assert!(bounds.height() >= 2);
772

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

            
776
87
        let center = get(x, y);
777
87
        let right = get(x + 1, y);
778
87
        let bottom = get(x, y + 1);
779
87
        let bottom_right = get(x + 1, y + 1);
780

            
781
87
        Self::new(
782
            2. / 3.,
783
87
            -2 * center + 2 * right - bottom + bottom_right,
784
            2. / 3.,
785
87
            -2 * center - right + 2 * bottom + bottom_right,
786
        )
787
87
    }
788

            
789
    /// Computes and returns the normal vector for the top row pixels for light filters.
790
    #[inline]
791
4415
    pub fn top_row(surface: &SharedImageSurface, bounds: IRect, x: u32) -> Normal {
792
4415
        assert!(x as i32 > bounds.x0);
793
4415
        assert!((x as i32) + 1 < bounds.x1);
794
4415
        assert!(bounds.height() >= 2);
795

            
796
30908
        let get = |x, y| i16::from(surface.get_pixel(x, y).a);
797
4415
        let y = bounds.y0 as u32;
798

            
799
4415
        let left = get(x - 1, y);
800
4415
        let center = get(x, y);
801
4415
        let right = get(x + 1, y);
802
4415
        let bottom_left = get(x - 1, y + 1);
803
4415
        let bottom = get(x, y + 1);
804
4415
        let bottom_right = get(x + 1, y + 1);
805

            
806
4415
        Self::new(
807
            1. / 3.,
808
4415
            -2 * left + 2 * right - bottom_left + bottom_right,
809
            1. / 2.,
810
4415
            -left - 2 * center - right + bottom_left + 2 * bottom + bottom_right,
811
        )
812
4415
    }
813

            
814
    /// Computes and returns the normal vector for the top right pixel for light filters.
815
    #[inline]
816
87
    pub fn top_right(surface: &SharedImageSurface, bounds: IRect) -> Normal {
817
        // Surface needs to be at least 2×2.
818
87
        assert!(bounds.width() >= 2);
819
87
        assert!(bounds.height() >= 2);
820

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

            
824
87
        let left = get(x - 1, y);
825
87
        let center = get(x, y);
826
87
        let bottom_left = get(x - 1, y + 1);
827
87
        let bottom = get(x, y + 1);
828

            
829
87
        Self::new(
830
            2. / 3.,
831
87
            -2 * left + 2 * center - bottom_left + bottom,
832
            2. / 3.,
833
87
            -left - 2 * center + bottom_left + 2 * bottom,
834
        )
835
87
    }
836

            
837
    /// Computes and returns the normal vector for the left column pixels for light filters.
838
    #[inline]
839
3263
    pub fn left_column(surface: &SharedImageSurface, bounds: IRect, y: u32) -> Normal {
840
3263
        assert!(y as i32 > bounds.y0);
841
3263
        assert!((y as i32) + 1 < bounds.y1);
842
3263
        assert!(bounds.width() >= 2);
843

            
844
22841
        let get = |x, y| i16::from(surface.get_pixel(x, y).a);
845
3263
        let x = bounds.x0 as u32;
846

            
847
3263
        let top = get(x, y - 1);
848
3263
        let top_right = get(x + 1, y - 1);
849
3263
        let center = get(x, y);
850
3263
        let right = get(x + 1, y);
851
3263
        let bottom = get(x, y + 1);
852
3263
        let bottom_right = get(x + 1, y + 1);
853

            
854
3263
        Self::new(
855
            1. / 2.,
856
3263
            -top + top_right - 2 * center + 2 * right - bottom + bottom_right,
857
            1. / 3.,
858
3263
            -2 * top - top_right + 2 * bottom + bottom_right,
859
        )
860
3263
    }
861

            
862
    /// Computes and returns the normal vector for the interior pixels for light filters.
863
    #[inline]
864
192404
    pub fn interior(surface: &SharedImageSurface, bounds: IRect, x: u32, y: u32) -> Normal {
865
192404
        assert!(x as i32 > bounds.x0);
866
192404
        assert!((x as i32) + 1 < bounds.x1);
867
192404
        assert!(y as i32 > bounds.y0);
868
192404
        assert!((y as i32) + 1 < bounds.y1);
869

            
870
1591111
        let get = |x, y| i16::from(surface.get_pixel(x, y).a);
871

            
872
192404
        let top_left = get(x - 1, y - 1);
873
192404
        let top = get(x, y - 1);
874
192404
        let top_right = get(x + 1, y - 1);
875
192404
        let left = get(x - 1, y);
876
192404
        let right = get(x + 1, y);
877
192404
        let bottom_left = get(x - 1, y + 1);
878
192404
        let bottom = get(x, y + 1);
879
192404
        let bottom_right = get(x + 1, y + 1);
880

            
881
192404
        Self::new(
882
            1. / 4.,
883
192404
            -top_left + top_right - 2 * left + 2 * right - bottom_left + bottom_right,
884
            1. / 4.,
885
192404
            -top_left - 2 * top - top_right + bottom_left + 2 * bottom + bottom_right,
886
        )
887
192404
    }
888

            
889
    /// Computes and returns the normal vector for the right column pixels for light filters.
890
    #[inline]
891
3263
    pub fn right_column(surface: &SharedImageSurface, bounds: IRect, y: u32) -> Normal {
892
3263
        assert!(y as i32 > bounds.y0);
893
3263
        assert!((y as i32) + 1 < bounds.y1);
894
3263
        assert!(bounds.width() >= 2);
895

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

            
899
3263
        let top_left = get(x - 1, y - 1);
900
3263
        let top = get(x, y - 1);
901
3263
        let left = get(x - 1, y);
902
3263
        let center = get(x, y);
903
3263
        let bottom_left = get(x - 1, y + 1);
904
3263
        let bottom = get(x, y + 1);
905

            
906
3263
        Self::new(
907
            1. / 2.,
908
3263
            -top_left + top - 2 * left + 2 * center - bottom_left + bottom,
909
            1. / 3.,
910
3263
            -top_left - 2 * top + bottom_left + 2 * bottom,
911
        )
912
3263
    }
913

            
914
    /// Computes and returns the normal vector for the bottom left pixel for light filters.
915
    #[inline]
916
87
    pub fn bottom_left(surface: &SharedImageSurface, bounds: IRect) -> Normal {
917
        // Surface needs to be at least 2×2.
918
87
        assert!(bounds.width() >= 2);
919
87
        assert!(bounds.height() >= 2);
920

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

            
924
87
        let top = get(x, y - 1);
925
87
        let top_right = get(x + 1, y - 1);
926
87
        let center = get(x, y);
927
87
        let right = get(x + 1, y);
928

            
929
87
        Self::new(
930
            2. / 3.,
931
87
            -top + top_right - 2 * center + 2 * right,
932
            2. / 3.,
933
87
            -2 * top - top_right + 2 * center + right,
934
        )
935
87
    }
936

            
937
    /// Computes and returns the normal vector for the bottom row pixels for light filters.
938
    #[inline]
939
4416
    pub fn bottom_row(surface: &SharedImageSurface, bounds: IRect, x: u32) -> Normal {
940
4416
        assert!(x as i32 > bounds.x0);
941
4416
        assert!((x as i32) + 1 < bounds.x1);
942
4416
        assert!(bounds.height() >= 2);
943

            
944
30901
        let get = |x, y| i16::from(surface.get_pixel(x, y).a);
945
4416
        let y = bounds.y1 as u32 - 1;
946

            
947
4416
        let top_left = get(x - 1, y - 1);
948
4416
        let top = get(x, y - 1);
949
4416
        let top_right = get(x + 1, y - 1);
950
4416
        let left = get(x - 1, y);
951
4416
        let center = get(x, y);
952
4416
        let right = get(x + 1, y);
953

            
954
4416
        Self::new(
955
            1. / 3.,
956
4416
            -top_left + top_right - 2 * left + 2 * right,
957
            1. / 2.,
958
4416
            -top_left - 2 * top - top_right + left + 2 * center + right,
959
        )
960
4416
    }
961

            
962
    /// Computes and returns the normal vector for the bottom right pixel for light filters.
963
    #[inline]
964
87
    pub fn bottom_right(surface: &SharedImageSurface, bounds: IRect) -> Normal {
965
        // Surface needs to be at least 2×2.
966
87
        assert!(bounds.width() >= 2);
967
87
        assert!(bounds.height() >= 2);
968

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

            
972
87
        let top_left = get(x - 1, y - 1);
973
87
        let top = get(x, y - 1);
974
87
        let left = get(x - 1, y);
975
87
        let center = get(x, y);
976

            
977
87
        Self::new(
978
            2. / 3.,
979
87
            -top_left + top - 2 * left + 2 * center,
980
            2. / 3.,
981
87
            -top_left - 2 * top + left + 2 * center,
982
        )
983
87
    }
984
}
985

            
986
#[cfg(test)]
987
mod tests {
988
    use super::*;
989

            
990
    use crate::borrow_element_as;
991
    use crate::document::Document;
992

            
993
    #[test]
994
2
    fn extracts_light_source() {
995
1
        let document = Document::load_from_bytes(
996
            br#"<?xml version="1.0" encoding="UTF-8"?>
997
<svg xmlns="http://www.w3.org/2000/svg">
998
  <filter id="filter">
999
    <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, None::<gio::Cancellable>);
1
        let node = document.lookup_internal_node("diffuse_distant").unwrap();
        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();
        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();
        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
    }
}