[librsvg] Merge diffuse and specular lighting code



commit d25d396f31514098d3b2fad6fccbc7d42d84a859
Author: Ivan Molodetskikh <yalterz gmail com>
Date:   Mon Jul 30 12:31:44 2018 +0300

    Merge diffuse and specular lighting code
    
    It's very similar, no need to duplicate it.

 Makefile.am                                        |   3 +-
 .../light/{diffuse_lighting.rs => lighting.rs}     | 195 ++++++++++++----
 rsvg_internals/src/filters/light/mod.rs            |   3 +-
 .../src/filters/light/specular_lighting.rs         | 249 ---------------------
 rsvg_internals/src/filters/mod.rs                  |   3 +-
 rsvg_internals/src/load.rs                         |  10 +-
 6 files changed, 163 insertions(+), 300 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 691f5e8c..02c57393 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -67,9 +67,8 @@ RUST_SRC =                                                    \
        rsvg_internals/src/filters/flood.rs                     \
        rsvg_internals/src/filters/gaussian_blur.rs             \
        rsvg_internals/src/filters/image.rs                     \
-       rsvg_internals/src/filters/light/diffuse_lighting.rs    \
        rsvg_internals/src/filters/light/light_source.rs        \
-       rsvg_internals/src/filters/light/specular_lighting.rs   \
+       rsvg_internals/src/filters/light/lighting.rs            \
        rsvg_internals/src/filters/light/mod.rs                 \
        rsvg_internals/src/filters/merge.rs                     \
        rsvg_internals/src/filters/morphology.rs                \
diff --git a/rsvg_internals/src/filters/light/diffuse_lighting.rs 
b/rsvg_internals/src/filters/light/lighting.rs
similarity index 50%
rename from rsvg_internals/src/filters/light/diffuse_lighting.rs
rename to rsvg_internals/src/filters/light/lighting.rs
index 1b4f7c58..26bc65cd 100644
--- a/rsvg_internals/src/filters/light/diffuse_lighting.rs
+++ b/rsvg_internals/src/filters/light/lighting.rs
@@ -1,7 +1,9 @@
 use std::cell::Cell;
+use std::cmp::max;
 
 use cairo::{self, ImageSurface, MatrixTrait};
 use cssparser;
+use nalgebra::Vector3;
 
 use attributes::Attribute;
 use drawing_ctx::DrawingCtx;
@@ -26,28 +28,51 @@ use surface_utils::{
 };
 use util::clamp;
 
-/// The `feDiffuseLighting` filter primitive.
-pub struct DiffuseLighting {
+/// Properties specific to either diffuse or specular lighting.
+enum Data {
+    Diffuse {
+        diffuse_constant: Cell<f64>,
+    },
+    Specular {
+        specular_constant: Cell<f64>,
+        specular_exponent: Cell<f64>,
+    },
+}
+
+/// The `feDiffuseLighting` and `feSpecularLighting` filter primitives.
+pub struct Lighting {
     base: PrimitiveWithInput,
     surface_scale: Cell<f64>,
-    diffuse_constant: Cell<f64>,
     kernel_unit_length: Cell<Option<(f64, f64)>>,
+    data: Data,
 }
 
-impl DiffuseLighting {
-    /// Constructs a new `DiffuseLighting` with empty properties.
+impl Lighting {
+    /// Constructs a new diffuse `Lighting` with empty properties.
     #[inline]
-    pub fn new() -> DiffuseLighting {
-        DiffuseLighting {
-            base: PrimitiveWithInput::new::<Self>(),
-            surface_scale: Cell::new(1.0),
-            diffuse_constant: Cell::new(1.0),
-            kernel_unit_length: Cell::new(None),
+    pub fn new_diffuse() -> Lighting {
+        Lighting {
+            data: Data::Diffuse {
+                diffuse_constant: Cell::new(1.0),
+            },
+            ..Self::default()
+        }
+    }
+
+    /// Constructs a new specular `Lighting` with empty properties.
+    #[inline]
+    pub fn new_specular() -> Lighting {
+        Lighting {
+            data: Data::Specular {
+                specular_constant: Cell::new(1.0),
+                specular_exponent: Cell::new(1.0),
+            },
+            ..Self::default()
         }
     }
 }
 
-impl NodeTrait for DiffuseLighting {
+impl NodeTrait for Lighting {
     fn set_atts(
         &self,
         node: &RsvgNode,
@@ -61,20 +86,6 @@ impl NodeTrait for DiffuseLighting {
                 Attribute::SurfaceScale => self
                     .surface_scale
                     .set(parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?),
-                Attribute::DiffuseConstant => self.diffuse_constant.set(
-                    parsers::number(value)
-                        .map_err(|err| NodeError::parse_error(attr, err))
-                        .and_then(|x| {
-                            if x >= 0.0 {
-                                Ok(x)
-                            } else {
-                                Err(NodeError::value_error(
-                                    attr,
-                                    "diffuseConstant can't be negative",
-                                ))
-                            }
-                        })?,
-                ),
                 Attribute::KernelUnitLength => self.kernel_unit_length.set(Some(
                     parsers::number_optional_number(value)
                         .map_err(|err| NodeError::parse_error(attr, err))
@@ -93,11 +104,75 @@ impl NodeTrait for DiffuseLighting {
             }
         }
 
+        match self.data {
+            Data::Diffuse {
+                ref diffuse_constant,
+            } => {
+                for (_key, attr, value) in pbag.iter() {
+                    match attr {
+                        Attribute::DiffuseConstant => diffuse_constant.set(
+                            parsers::number(value)
+                                .map_err(|err| NodeError::parse_error(attr, err))
+                                .and_then(|x| {
+                                    if x >= 0.0 {
+                                        Ok(x)
+                                    } else {
+                                        Err(NodeError::value_error(
+                                            attr,
+                                            "diffuseConstant can't be negative",
+                                        ))
+                                    }
+                                })?,
+                        ),
+                        _ => (),
+                    }
+                }
+            }
+            Data::Specular {
+                ref specular_constant,
+                ref specular_exponent,
+            } => {
+                for (_key, attr, value) in pbag.iter() {
+                    match attr {
+                        Attribute::SpecularConstant => specular_constant.set(
+                            parsers::number(value)
+                                .map_err(|err| NodeError::parse_error(attr, err))
+                                .and_then(|x| {
+                                    if x >= 0.0 {
+                                        Ok(x)
+                                    } else {
+                                        Err(NodeError::value_error(
+                                            attr,
+                                            "specularConstant can't be negative",
+                                        ))
+                                    }
+                                })?,
+                        ),
+                        Attribute::SpecularExponent => specular_exponent.set(
+                            parsers::number(value)
+                                .map_err(|err| NodeError::parse_error(attr, err))
+                                .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(())
     }
 }
 
-impl Filter for DiffuseLighting {
+impl Filter for Lighting {
     fn render(
         &self,
         node: &RsvgNode,
@@ -118,7 +193,6 @@ impl Filter for DiffuseLighting {
             .map(|(dx, dy)| ctx.paffine().transform_distance(dx, dy));
 
         let surface_scale = self.surface_scale.get();
-        let diffuse_constant = self.diffuse_constant.get();
 
         let cascaded = node.get_cascaded_values();
         let values = cascaded.get();
@@ -168,19 +242,48 @@ impl Filter for DiffuseLighting {
                 let scaled_y = f64::from(y) * oy;
                 let z = f64::from(pixel.a) / 255.0 * surface_scale;
                 let light_vector = light_source.vector(scaled_x, scaled_y, z, ctx);
-
                 let light_color = light_source.color(lighting_color, light_vector, ctx);
 
-                let n_dot_l = normal.dot(&light_vector);
-                let compute =
-                    |x| clamp(diffuse_constant * n_dot_l * f64::from(x), 0.0, 255.0).round() as u8;
+                let output_pixel = match self.data {
+                    Data::Diffuse {
+                        ref diffuse_constant,
+                    } => {
+                        let n_dot_l = normal.dot(&light_vector);
+                        let compute = |x| {
+                            clamp(diffuse_constant.get() * n_dot_l * f64::from(x), 0.0, 255.0)
+                                .round() as u8
+                        };
+
+                        Pixel {
+                            r: compute(light_color.red),
+                            g: compute(light_color.green),
+                            b: compute(light_color.blue),
+                            a: 255,
+                        }.premultiply()
+                    }
+                    Data::Specular {
+                        ref specular_constant,
+                        ref specular_exponent,
+                    } => {
+                        let mut h = light_vector + Vector3::new(0.0, 0.0, 1.0);
+                        let _ = h.try_normalize_mut(0.0);
+
+                        let n_dot_h = normal.dot(&h);
+                        let factor =
+                            specular_constant.get() * n_dot_h.powf(specular_exponent.get());
+                        let compute = |x| clamp(factor * f64::from(x), 0.0, 255.0).round() as u8;
+
+                        let mut output_pixel = Pixel {
+                            r: compute(light_color.red),
+                            g: compute(light_color.green),
+                            b: compute(light_color.blue),
+                            a: 0,
+                        };
+                        output_pixel.a = max(max(output_pixel.r, output_pixel.g), output_pixel.b);
+                        output_pixel
+                    }
+                };
 
-                let output_pixel = Pixel {
-                    r: compute(light_color.red),
-                    g: compute(light_color.green),
-                    b: compute(light_color.blue),
-                    a: 255,
-                }.premultiply();
                 output_data.set_pixel(output_stride, output_pixel, x, y);
             }
         }
@@ -224,3 +327,19 @@ impl Filter for DiffuseLighting {
         true
     }
 }
+
+impl Default for Lighting {
+    #[inline]
+    fn default() -> Self {
+        Self {
+            base: PrimitiveWithInput::new::<Self>(),
+            surface_scale: Cell::new(1.0),
+            kernel_unit_length: Cell::new(None),
+
+            // The data field is unused in this case.
+            data: Data::Diffuse {
+                diffuse_constant: Cell::new(1.0),
+            },
+        }
+    }
+}
diff --git a/rsvg_internals/src/filters/light/mod.rs b/rsvg_internals/src/filters/light/mod.rs
index c345c5f4..62f19fd7 100644
--- a/rsvg_internals/src/filters/light/mod.rs
+++ b/rsvg_internals/src/filters/light/mod.rs
@@ -4,9 +4,8 @@ use nalgebra::{Matrix3, Vector3};
 use filters::context::IRect;
 use surface_utils::{iterators::PixelRectangle, shared_surface::SharedImageSurface, EdgeMode};
 
-pub mod diffuse_lighting;
 pub mod light_source;
-pub mod specular_lighting;
+pub mod lighting;
 
 /// Computes and returns the normal vector for the light filters.
 fn normal(
diff --git a/rsvg_internals/src/filters/mod.rs b/rsvg_internals/src/filters/mod.rs
index 410fc6be..0961244c 100644
--- a/rsvg_internals/src/filters/mod.rs
+++ b/rsvg_internals/src/filters/mod.rs
@@ -299,8 +299,7 @@ pub fn render(
                     flood::Flood,
                     gaussian_blur::GaussianBlur,
                     image::Image,
-                    light::diffuse_lighting::DiffuseLighting,
-                    light::specular_lighting::SpecularLighting,
+                    light::lighting::Lighting,
                     merge::Merge,
                     morphology::Morphology,
                     offset::Offset,
diff --git a/rsvg_internals/src/load.rs b/rsvg_internals/src/load.rs
index efaa474f..537b2230 100644
--- a/rsvg_internals/src/load.rs
+++ b/rsvg_internals/src/load.rs
@@ -14,11 +14,7 @@ use filters::{
     flood::Flood,
     gaussian_blur::GaussianBlur,
     image::Image,
-    light::{
-        diffuse_lighting::DiffuseLighting,
-        light_source::LightSource,
-        specular_lighting::SpecularLighting,
-    },
+    light::{light_source::LightSource, lighting::Lighting},
     merge::{Merge, MergeNode},
     morphology::Morphology,
     node::NodeFilter,
@@ -97,7 +93,7 @@ node_create_fn!(create_defs, Defs, NodeDefs::new);
 node_create_fn!(
     create_diffuse_lighting,
     FilterPrimitiveDiffuseLighting,
-    DiffuseLighting::new
+    Lighting::new_diffuse
 );
 node_create_fn!(
     create_distant_light,
@@ -155,7 +151,7 @@ node_create_fn!(create_rect, Rect, NodeRect::new);
 node_create_fn!(
     create_specular_lighting,
     FilterPrimitiveSpecularLighting,
-    SpecularLighting::new
+    Lighting::new_specular
 );
 node_create_fn!(create_spot_light, LightSource, LightSource::new_spot_light);
 node_create_fn!(create_stop, Stop, NodeStop::new);


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