[librsvg/librsvg-2.46] lighting: split DiffuseLighting and SpecularLighting



commit 23df87171a769a49b50b4ac31a319c6b7ddfba67
Author: Paolo Borelli <pborelli gnome org>
Date:   Tue Oct 15 15:25:57 2019 +0200

    lighting: split DiffuseLighting and SpecularLighting
    
    Use two structs and a shared Lighting trait

 rsvg_internals/src/create_node.rs            |   6 +-
 rsvg_internals/src/filters/light/lighting.rs | 738 ++++++++++++++-------------
 2 files changed, 387 insertions(+), 357 deletions(-)
---
diff --git a/rsvg_internals/src/create_node.rs b/rsvg_internals/src/create_node.rs
index f1650cd6..c5256b6b 100644
--- a/rsvg_internals/src/create_node.rs
+++ b/rsvg_internals/src/create_node.rs
@@ -15,7 +15,7 @@ use crate::filters::{
     image::Image,
     light::{
         light_source::DistantLight, light_source::PointLight, light_source::SpotLight,
-        lighting::Lighting,
+        lighting::DiffuseLighting, lighting::SpecularLighting,
     },
     merge::{Merge, MergeNode},
     morphology::Morphology,
@@ -68,7 +68,7 @@ mod creators {
     n!(create_composite,                 FeComposite,                Composite::default);
     n!(create_convolve_matrix,           FeConvolveMatrix,           ConvolveMatrix::default);
     n!(create_defs,                      Defs,                       NodeNonRendering::default);
-    n!(create_diffuse_lighting,          FeDiffuseLighting,          Lighting::new_diffuse);
+    n!(create_diffuse_lighting,          FeDiffuseLighting,          DiffuseLighting::default);
     n!(create_distant_light,             FeDistantLight,             DistantLight::default);
     n!(create_displacement_map,          FeDisplacementMap,          DisplacementMap::default);
     n!(create_ellipse,                   Ellipse,                    NodeEllipse::default);
@@ -95,7 +95,7 @@ mod creators {
     n!(create_polyline,                  Polyline,                   NodePoly::new_open);
     n!(create_radial_gradient,           RadialGradient,             NodeRadialGradient::default);
     n!(create_rect,                      Rect,                       NodeRect::default);
-    n!(create_specular_lighting,         FeSpecularLighting,         Lighting::new_specular);
+    n!(create_specular_lighting,         FeSpecularLighting,         SpecularLighting::default);
     n!(create_spot_light,                FeSpotLight,                SpotLight::default);
     n!(create_stop,                      Stop,                       NodeStop::default);
     n!(create_style,                     Style,                      NodeStyle::default);
diff --git a/rsvg_internals/src/filters/light/lighting.rs b/rsvg_internals/src/filters/light/lighting.rs
index 0b50fb6c..ffdb8def 100644
--- a/rsvg_internals/src/filters/light/lighting.rs
+++ b/rsvg_internals/src/filters/light/lighting.rs
@@ -42,110 +42,26 @@ use crate::surface_utils::{
 };
 use crate::util::clamp;
 
-/// Properties specific to either diffuse or specular lighting.
-#[derive(Clone, Copy)]
-enum Data {
-    Diffuse {
-        diffuse_constant: f64,
-    },
-    Specular {
-        specular_constant: f64,
-        specular_exponent: f64,
-    },
+trait Lighting {
+    fn common(&self) -> &Common;
+
+    fn compute_factor(&self, normal: Normal, light_vector: Vector3<f64>) -> f64;
 }
 
-/// The `feDiffuseLighting` and `feSpecularLighting` filter primitives.
-pub struct Lighting {
+struct Common {
     base: PrimitiveWithInput,
     surface_scale: f64,
     kernel_unit_length: Option<(f64, f64)>,
-    data: Data,
 }
 
-impl Lighting {
-    /// Constructs a new diffuse `Lighting` with empty properties.
-    #[inline]
-    pub fn new_diffuse() -> Lighting {
-        Lighting {
-            data: Data::Diffuse {
-                diffuse_constant: 1.0,
-            },
-            ..Self::default()
-        }
-    }
-
-    /// Constructs a new specular `Lighting` with empty properties.
-    #[inline]
-    pub fn new_specular() -> Lighting {
-        Lighting {
-            data: Data::Specular {
-                specular_constant: 1.0,
-                specular_exponent: 1.0,
-            },
-            ..Self::default()
-        }
-    }
-
-    #[inline]
-    pub fn compute_factor(&self, normal: Normal, light_vector: Vector3<f64>) -> f64 {
-        match self.data {
-            Data::Diffuse { diffuse_constant } => {
-                let k = if normal.normal.is_zero() {
-                    // Common case of (0, 0, 1) normal.
-                    light_vector.z
-                } else {
-                    let mut n = normal
-                        .normal
-                        .map(|x| f64::from(x) * self.surface_scale / 255.);
-                    n.component_mul_assign(&normal.factor);
-                    let normal = Vector3::new(n.x, n.y, 1.0);
-
-                    normal.dot(&light_vector) / normal.norm()
-                };
-
-                diffuse_constant * k
-            }
-            Data::Specular {
-                specular_constant,
-                specular_exponent,
-            } => {
-                let h = light_vector + Vector3::new(0.0, 0.0, 1.0);
-                let h_norm = h.norm();
-                if h_norm == 0.0 {
-                    0.0
-                } else {
-                    let k = if normal.normal.is_zero() {
-                        // Common case of (0, 0, 1) normal.
-                        let n_dot_h = h.z / h_norm;
-                        if specular_exponent == 1.0 {
-                            n_dot_h
-                        } else {
-                            n_dot_h.powf(specular_exponent)
-                        }
-                    } else {
-                        let mut n = normal
-                            .normal
-                            .map(|x| f64::from(x) * self.surface_scale / 255.);
-                        n.component_mul_assign(&normal.factor);
-                        let normal = Vector3::new(n.x, n.y, 1.0);
-
-                        let n_dot_h = normal.dot(&h) / normal.norm() / h_norm;
-                        if specular_exponent == 1.0 {
-                            n_dot_h
-                        } else {
-                            n_dot_h.powf(specular_exponent)
-                        }
-                    };
-
-                    specular_constant * k
-                }
-            }
+impl Common {
+    fn new(base: PrimitiveWithInput) -> Self {
+        Self {
+            base,
+            surface_scale: 1.0,
+            kernel_unit_length: None,
         }
     }
-}
-
-impl NodeTrait for Lighting {
-    impl_node_as_filter!();
 
     fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
         self.base.set_atts(parent, pbag)?;
@@ -175,67 +91,48 @@ impl NodeTrait for Lighting {
             }
         }
 
-        match self.data {
-            Data::Diffuse {
-                ref mut diffuse_constant,
-            } => {
-                for (attr, value) in pbag.iter() {
-                    match attr {
-                        local_name!("diffuseConstant") => {
-                            *diffuse_constant = parsers::number(value)
-                                .attribute(attr.clone())
-                                .and_then(|x| {
-                                    if x >= 0.0 {
-                                        Ok(x)
-                                    } else {
-                                        Err(NodeError::value_error(
-                                            attr,
-                                            "diffuseConstant can't be negative",
-                                        ))
-                                    }
-                                })?;
-                        }
-                        _ => (),
-                    }
-                }
-            }
-            Data::Specular {
-                ref mut specular_constant,
-                ref mut specular_exponent,
-            } => {
-                for (attr, value) in pbag.iter() {
-                    match attr {
-                        local_name!("specularConstant") => {
-                            *specular_constant = parsers::number(value)
-                                .attribute(attr.clone())
-                                .and_then(|x| {
-                                    if x >= 0.0 {
-                                        Ok(x)
-                                    } else {
-                                        Err(NodeError::value_error(
-                                            attr,
-                                            "specularConstant can't be negative",
-                                        ))
-                                    }
-                                })?;
-                        }
-                        local_name!("specularExponent") => {
-                            *specular_exponent = parsers::number(value)
-                                .attribute(attr.clone())
-                                .and_then(|x| {
-                                    if x >= 1.0 && x <= 128.0 {
-                                        Ok(x)
-                                    } else {
-                                        Err(NodeError::value_error(
-                                            attr,
-                                            "specularExponent should be between 1.0 and 128.0",
-                                        ))
-                                    }
-                                })?;
-                        }
-                        _ => (),
-                    }
+        Ok(())
+    }
+}
+
+/// The `feDiffuseLighting` filter primitives.
+pub struct DiffuseLighting {
+    common: Common,
+    diffuse_constant: f64,
+}
+
+impl Default for DiffuseLighting {
+    fn default() -> Self {
+        Self {
+            common: Common::new(PrimitiveWithInput::new::<Self>()),
+            diffuse_constant: 1.0,
+        }
+    }
+}
+
+impl NodeTrait for DiffuseLighting {
+    impl_node_as_filter!();
+
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+        self.common.set_atts(parent, pbag)?;
+
+        for (attr, value) in pbag.iter() {
+            match attr {
+                local_name!("diffuseConstant") => {
+                    self.diffuse_constant = parsers::number(value)
+                        .attribute(attr.clone())
+                        .and_then(|x| {
+                            if x >= 0.0 {
+                                Ok(x)
+                            } else {
+                                Err(NodeError::value_error(
+                                    attr,
+                                    "diffuseConstant can't be negative",
+                                ))
+                            }
+                        })?;
                 }
+                _ => (),
             }
         }
 
@@ -243,237 +140,386 @@ impl NodeTrait for Lighting {
     }
 }
 
-impl Filter for Lighting {
-    fn render(
-        &self,
-        node: &RsvgNode,
-        ctx: &FilterContext,
-        draw_ctx: &mut DrawingCtx,
-    ) -> Result<FilterResult, FilterError> {
-        let input = self.base.get_input(ctx, draw_ctx)?;
-        let mut bounds = self
-            .base
-            .get_bounds(ctx)
-            .add_input(&input)
-            .into_irect(draw_ctx);
-        let original_bounds = bounds;
-
-        let scale = self
-            .kernel_unit_length
-            .map(|(dx, dy)| ctx.paffine().transform_distance(dx, dy));
-
-        let cascaded = CascadedValues::new_from_node(node);
-        let values = cascaded.get();
-        let lighting_color = match values.lighting_color.0 {
-            cssparser::Color::CurrentColor => values.color.0,
-            cssparser::Color::RGBA(rgba) => rgba,
+impl Lighting for DiffuseLighting {
+    #[inline]
+    fn common(&self) -> &Common {
+        &self.common
+    }
+
+    #[inline]
+    fn compute_factor(&self, normal: Normal, light_vector: Vector3<f64>) -> f64 {
+        let k = if normal.normal.is_zero() {
+            // Common case of (0, 0, 1) normal.
+            light_vector.z
+        } else {
+            let mut n = normal
+                .normal
+                .map(|x| f64::from(x) * self.common().surface_scale / 255.);
+            n.component_mul_assign(&normal.factor);
+            let normal = Vector3::new(n.x, n.y, 1.0);
+
+            normal.dot(&light_vector) / normal.norm()
         };
 
-        let light_source = find_light_source(node, ctx)?;
-        let mut input_surface = input.surface().clone();
+        self.diffuse_constant * k
+    }
+}
 
-        if let Some((ox, oy)) = scale {
-            // Scale the input surface to match kernel_unit_length.
-            let (new_surface, new_bounds) = input_surface.scale(bounds, 1.0 / ox, 1.0 / oy)?;
+/// The `feSpecularLighting` filter primitives.
+pub struct SpecularLighting {
+    common: Common,
+    specular_constant: f64,
+    specular_exponent: f64,
+}
 
-            input_surface = new_surface;
-            bounds = new_bounds;
+impl Default for SpecularLighting {
+    fn default() -> Self {
+        Self {
+            common: Common::new(PrimitiveWithInput::new::<Self>()),
+            specular_constant: 1.0,
+            specular_exponent: 1.0,
         }
+    }
+}
 
-        // Check if the surface is too small for normal computation. This case is unspecified;
-        // WebKit doesn't render anything in this case.
-        if bounds.x1 < bounds.x0 + 2 || bounds.y1 < bounds.y0 + 2 {
-            return Err(FilterError::LightingInputTooSmall);
+impl NodeTrait for SpecularLighting {
+    impl_node_as_filter!();
+
+    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
+        self.common.set_atts(parent, pbag)?;
+
+        for (attr, value) in pbag.iter() {
+            match attr {
+                local_name!("specularConstant") => {
+                    self.specular_constant = parsers::number(value)
+                        .attribute(attr.clone())
+                        .and_then(|x| {
+                            if x >= 0.0 {
+                                Ok(x)
+                            } else {
+                                Err(NodeError::value_error(
+                                    attr,
+                                    "specularConstant can't be negative",
+                                ))
+                            }
+                        })?;
+                }
+                local_name!("specularExponent") => {
+                    self.specular_exponent = parsers::number(value)
+                        .attribute(attr.clone())
+                        .and_then(|x| {
+                            if x >= 1.0 && x <= 128.0 {
+                                Ok(x)
+                            } else {
+                                Err(NodeError::value_error(
+                                    attr,
+                                    "specularExponent should be between 1.0 and 128.0",
+                                ))
+                            }
+                        })?;
+                }
+                _ => (),
+            }
         }
 
-        let (ox, oy) = scale.unwrap_or((1.0, 1.0));
-
-        let mut output_surface = ImageSurface::create(
-            cairo::Format::ARgb32,
-            input_surface.width(),
-            input_surface.height(),
-        )?;
-
-        let output_stride = output_surface.get_stride() as usize;
-        {
-            let mut output_data = output_surface.get_data().unwrap();
-            let output_slice = &mut *output_data;
-
-            let compute_output_pixel =
-                |mut output_slice: &mut [u8], base_y, x, y, normal: Normal| {
-                    let pixel = input_surface.get_pixel(x, y);
-
-                    let scaled_x = f64::from(x) * ox;
-                    let scaled_y = f64::from(y) * oy;
-                    let z = f64::from(pixel.a) / 255.0 * self.surface_scale;
-                    let light_vector = light_source.vector(scaled_x, scaled_y, z);
-                    let light_color = light_source.color(lighting_color, light_vector);
-
-                    let factor = self.compute_factor(normal, light_vector);
-                    let compute = |x| (clamp(factor * f64::from(x), 0.0, 255.0) + 0.5) as u8;
-
-                    let mut output_pixel = Pixel {
-                        r: compute(light_color.red),
-                        g: compute(light_color.green),
-                        b: compute(light_color.blue),
-                        a: 255,
-                    };
+        Ok(())
+    }
+}
 
-                    if let Data::Specular { .. } = self.data {
-                        output_pixel.a = max(max(output_pixel.r, output_pixel.g), output_pixel.b);
-                    }
+impl Lighting for SpecularLighting {
+    #[inline]
+    fn common(&self) -> &Common {
+        &self.common
+    }
+
+    #[inline]
+    fn compute_factor(&self, normal: Normal, light_vector: Vector3<f64>) -> f64 {
+        let h = light_vector + Vector3::new(0.0, 0.0, 1.0);
+        let h_norm = h.norm();
+
+        if h_norm == 0.0 {
+            return 0.0;
+        }
+
+        let k = if normal.normal.is_zero() {
+            // Common case of (0, 0, 1) normal.
+            let n_dot_h = h.z / h_norm;
+            if self.specular_exponent == 1.0 {
+                n_dot_h
+            } else {
+                n_dot_h.powf(self.specular_exponent)
+            }
+        } else {
+            let mut n = normal
+                .normal
+                .map(|x| f64::from(x) * self.common().surface_scale / 255.);
+            n.component_mul_assign(&normal.factor);
+            let normal = Vector3::new(n.x, n.y, 1.0);
+
+            let n_dot_h = normal.dot(&h) / normal.norm() / h_norm;
+            if self.specular_exponent == 1.0 {
+                n_dot_h
+            } else {
+                n_dot_h.powf(self.specular_exponent)
+            }
+        };
+
+        self.specular_constant * k
+    }
+}
 
-                    output_slice.set_pixel(output_stride, output_pixel, x, y - base_y);
+// We cannot use a blanket impl<T: Lighting> Filter for T because we do
+// not want to make the Lighting trait public, so we use a macro
+macro_rules! impl_lighting_filter {
+    ($lighting_type:ty, $alpha_func:ident) => {
+        impl Filter for $lighting_type {
+            fn render(
+                &self,
+                node: &RsvgNode,
+                ctx: &FilterContext,
+                draw_ctx: &mut DrawingCtx,
+            ) -> Result<FilterResult, FilterError> {
+                let input = self.common().base.get_input(ctx, draw_ctx)?;
+                let mut bounds = self
+                    .common()
+                    .base
+                    .get_bounds(ctx)
+                    .add_input(&input)
+                    .into_irect(draw_ctx);
+                let original_bounds = bounds;
+
+                let scale = self
+                    .common()
+                    .kernel_unit_length
+                    .map(|(dx, dy)| ctx.paffine().transform_distance(dx, dy));
+
+                let cascaded = CascadedValues::new_from_node(node);
+                let values = cascaded.get();
+                let lighting_color = match values.lighting_color.0 {
+                    cssparser::Color::CurrentColor => values.color.0,
+                    cssparser::Color::RGBA(rgba) => rgba,
                 };
 
-            // Top left.
-            compute_output_pixel(
-                output_slice,
-                0,
-                bounds.x0 as u32,
-                bounds.y0 as u32,
-                top_left_normal(&input_surface, bounds),
-            );
-
-            // Top right.
-            compute_output_pixel(
-                output_slice,
-                0,
-                bounds.x1 as u32 - 1,
-                bounds.y0 as u32,
-                top_right_normal(&input_surface, bounds),
-            );
-
-            // Bottom left.
-            compute_output_pixel(
-                output_slice,
-                0,
-                bounds.x0 as u32,
-                bounds.y1 as u32 - 1,
-                bottom_left_normal(&input_surface, bounds),
-            );
-
-            // Bottom right.
-            compute_output_pixel(
-                output_slice,
-                0,
-                bounds.x1 as u32 - 1,
-                bounds.y1 as u32 - 1,
-                bottom_right_normal(&input_surface, bounds),
-            );
-
-            if bounds.x1 - bounds.x0 >= 3 {
-                // Top row.
-                for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
+                let light_source = find_light_source(node, ctx)?;
+                let mut input_surface = input.surface().clone();
+
+                if let Some((ox, oy)) = scale {
+                    // Scale the input surface to match kernel_unit_length.
+                    let (new_surface, new_bounds) = input_surface.scale(bounds, 1.0 / ox, 1.0 / oy)?;
+
+                    input_surface = new_surface;
+                    bounds = new_bounds;
+                }
+
+                // Check if the surface is too small for normal computation. This case is unspecified;
+                // WebKit doesn't render anything in this case.
+                if bounds.x1 < bounds.x0 + 2 || bounds.y1 < bounds.y0 + 2 {
+                    return Err(FilterError::LightingInputTooSmall);
+                }
+
+                let (ox, oy) = scale.unwrap_or((1.0, 1.0));
+
+                let mut output_surface = ImageSurface::create(
+                    cairo::Format::ARgb32,
+                    input_surface.width(),
+                    input_surface.height(),
+                )?;
+
+                let output_stride = output_surface.get_stride() as usize;
+                {
+                    let mut output_data = output_surface.get_data().unwrap();
+                    let output_slice = &mut *output_data;
+
+                    let compute_output_pixel =
+                        |mut output_slice: &mut [u8], base_y, x, y, normal: Normal| {
+                            let pixel = input_surface.get_pixel(x, y);
+
+                            let scaled_x = f64::from(x) * ox;
+                            let scaled_y = f64::from(y) * oy;
+                            let z = f64::from(pixel.a) / 255.0 * self.common().surface_scale;
+                            let light_vector = light_source.vector(scaled_x, scaled_y, z);
+                            let light_color = light_source.color(lighting_color, light_vector);
+
+                            // compute the factor just once for the three colors
+                            let factor = self.compute_factor(normal, light_vector);
+                            let compute = |x| (clamp(factor * f64::from(x), 0.0, 255.0) + 0.5) as u8;
+
+                            let r = compute(light_color.red);
+                            let g = compute(light_color.green);
+                            let b = compute(light_color.blue);
+                            let a = $alpha_func(r, g, b);
+
+                            let output_pixel = Pixel {
+                                r,
+                                g,
+                                b,
+                                a,
+                            };
+
+                            output_slice.set_pixel(output_stride, output_pixel, x, y - base_y);
+                        };
+
+                    // Top left.
                     compute_output_pixel(
                         output_slice,
                         0,
-                        x,
+                        bounds.x0 as u32,
                         bounds.y0 as u32,
-                        top_row_normal(&input_surface, bounds, x),
+                        top_left_normal(&input_surface, bounds),
                     );
-                }
 
-                // Bottom row.
-                for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
+                    // Top right.
                     compute_output_pixel(
                         output_slice,
                         0,
-                        x,
-                        bounds.y1 as u32 - 1,
-                        bottom_row_normal(&input_surface, bounds, x),
+                        bounds.x1 as u32 - 1,
+                        bounds.y0 as u32,
+                        top_right_normal(&input_surface, bounds),
                     );
-                }
-            }
 
-            if bounds.y1 - bounds.y0 >= 3 {
-                // Left column.
-                for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
+                    // Bottom left.
                     compute_output_pixel(
                         output_slice,
                         0,
                         bounds.x0 as u32,
-                        y,
-                        left_column_normal(&input_surface, bounds, y),
+                        bounds.y1 as u32 - 1,
+                        bottom_left_normal(&input_surface, bounds),
                     );
-                }
 
-                // Right column.
-                for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
+                    // Bottom right.
                     compute_output_pixel(
                         output_slice,
                         0,
                         bounds.x1 as u32 - 1,
-                        y,
-                        right_column_normal(&input_surface, bounds, y),
+                        bounds.y1 as u32 - 1,
+                        bottom_right_normal(&input_surface, bounds),
                     );
-                }
-            }
 
-            if bounds.x1 - bounds.x0 >= 3 && bounds.y1 - bounds.y0 >= 3 {
-                // Interior pixels.
-                let first_row = bounds.y0 as u32 + 1;
-                let one_past_last_row = bounds.y1 as u32 - 1;
-                let first_pixel = (first_row as usize) * output_stride;
-                let one_past_last_pixel = (one_past_last_row as usize) * output_stride;
-
-                output_slice[first_pixel..one_past_last_pixel]
-                    .par_chunks_mut(output_stride)
-                    .zip(first_row..one_past_last_row)
-                    .for_each(|(slice, y)| {
+                    if bounds.x1 - bounds.x0 >= 3 {
+                        // Top row.
                         for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
                             compute_output_pixel(
-                                slice,
-                                y,
+                                output_slice,
+                                0,
+                                x,
+                                bounds.y0 as u32,
+                                top_row_normal(&input_surface, bounds, x),
+                            );
+                        }
+
+                        // Bottom row.
+                        for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
+                            compute_output_pixel(
+                                output_slice,
+                                0,
                                 x,
+                                bounds.y1 as u32 - 1,
+                                bottom_row_normal(&input_surface, bounds, x),
+                            );
+                        }
+                    }
+
+                    if bounds.y1 - bounds.y0 >= 3 {
+                        // Left column.
+                        for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
+                            compute_output_pixel(
+                                output_slice,
+                                0,
+                                bounds.x0 as u32,
+                                y,
+                                left_column_normal(&input_surface, bounds, y),
+                            );
+                        }
+
+                        // Right column.
+                        for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
+                            compute_output_pixel(
+                                output_slice,
+                                0,
+                                bounds.x1 as u32 - 1,
                                 y,
-                                interior_normal(&input_surface, bounds, x, y),
+                                right_column_normal(&input_surface, bounds, y),
                             );
                         }
-                    });
+                    }
+
+                    if bounds.x1 - bounds.x0 >= 3 && bounds.y1 - bounds.y0 >= 3 {
+                        // Interior pixels.
+                        let first_row = bounds.y0 as u32 + 1;
+                        let one_past_last_row = bounds.y1 as u32 - 1;
+                        let first_pixel = (first_row as usize) * output_stride;
+                        let one_past_last_pixel = (one_past_last_row as usize) * output_stride;
+
+                        output_slice[first_pixel..one_past_last_pixel]
+                            .par_chunks_mut(output_stride)
+                            .zip(first_row..one_past_last_row)
+                            .for_each(|(slice, y)| {
+                                for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
+                                    compute_output_pixel(
+                                        slice,
+                                        y,
+                                        x,
+                                        y,
+                                        interior_normal(&input_surface, bounds, x, y),
+                                    );
+                                }
+                            });
+                    }
+                }
+
+                let cascaded = CascadedValues::new_from_node(node);
+                let values = cascaded.get();
+                // The generated color values are in the color space determined by
+                // color-interpolation-filters.
+                let surface_type =
+                    if values.color_interpolation_filters == ColorInterpolationFilters::LinearRgb {
+                        SurfaceType::LinearRgb
+                    } else {
+                        SurfaceType::SRgb
+                    };
+                let mut output_surface = SharedImageSurface::new(output_surface, surface_type)?;
+
+                if let Some((ox, oy)) = scale {
+                    // Scale the output surface back.
+                    output_surface = output_surface.scale_to(
+                        ctx.source_graphic().width(),
+                        ctx.source_graphic().height(),
+                        original_bounds,
+                        ox,
+                        oy,
+                    )?;
+
+                    bounds = original_bounds;
+                }
+
+                Ok(FilterResult {
+                    name: self.common().base.result.clone(),
+                    output: FilterOutput {
+                        surface: output_surface,
+                        bounds,
+                    },
+                })
             }
-        }
 
-        let cascaded = CascadedValues::new_from_node(node);
-        let values = cascaded.get();
-        // The generated color values are in the color space determined by
-        // color-interpolation-filters.
-        let surface_type =
-            if values.color_interpolation_filters == ColorInterpolationFilters::LinearRgb {
-                SurfaceType::LinearRgb
-            } else {
-                SurfaceType::SRgb
-            };
-        let mut output_surface = SharedImageSurface::new(output_surface, surface_type)?;
-
-        if let Some((ox, oy)) = scale {
-            // Scale the output surface back.
-            output_surface = output_surface.scale_to(
-                ctx.source_graphic().width(),
-                ctx.source_graphic().height(),
-                original_bounds,
-                ox,
-                oy,
-            )?;
-
-            bounds = original_bounds;
+            #[inline]
+            fn is_affected_by_color_interpolation_filters(&self) -> bool {
+                true
+            }
         }
-
-        Ok(FilterResult {
-            name: self.base.result.clone(),
-            output: FilterOutput {
-                surface: output_surface,
-                bounds,
-            },
-        })
     }
+}
 
-    #[inline]
-    fn is_affected_by_color_interpolation_filters(&self) -> bool {
-        true
-    }
+const fn diffuse_alpha(_r: u8, _g: u8, _b: u8) -> u8 {
+    255
 }
 
+fn specular_alpha(r: u8, g: u8, b: u8) -> u8 {
+    max(max(r, g), b)
+}
+
+impl_lighting_filter!(DiffuseLighting, diffuse_alpha);
+impl_lighting_filter!(SpecularLighting, specular_alpha);
+
 fn find_light_source(node: &RsvgNode, ctx: &FilterContext) -> Result<LightSource, FilterError> {
     let mut light_sources = node
         .children()
@@ -502,19 +548,3 @@ fn find_light_source(node: &RsvgNode, ctx: &FilterContext) -> Result<LightSource
 
     Ok(light_source)
 }
-
-impl Default for Lighting {
-    #[inline]
-    fn default() -> Self {
-        Self {
-            base: PrimitiveWithInput::new::<Self>(),
-            surface_scale: 1.0,
-            kernel_unit_length: None,
-
-            // The data field is unused in this case.
-            data: Data::Diffuse {
-                diffuse_constant: 1.0,
-            },
-        }
-    }
-}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]