[librsvg: 1/4] Move composition code in surface_utils.



commit d78a352bec288314c85636ea091753f016fd1940
Author: Paolo Borelli <pborelli gnome org>
Date:   Fri Dec 27 22:18:06 2019 +0100

    Move composition code in surface_utils.
    
    Move pixel operations in the surface_utils module.

 rsvg_internals/benches/composite.rs                |   5 +-
 rsvg_internals/src/filters/composite.rs            | 113 ++----------------
 rsvg_internals/src/surface_utils/shared_surface.rs | 131 ++++++++++++++++++++-
 3 files changed, 140 insertions(+), 109 deletions(-)
---
diff --git a/rsvg_internals/benches/composite.rs b/rsvg_internals/benches/composite.rs
index 48a69097..bdcc581f 100644
--- a/rsvg_internals/benches/composite.rs
+++ b/rsvg_internals/benches/composite.rs
@@ -5,9 +5,10 @@ use criterion::{black_box, Criterion};
 use cairo;
 use rsvg_internals;
 
-use rsvg_internals::filters::composite::composite_arithmetic;
 use rsvg_internals::rect::IRect;
-use rsvg_internals::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
+use rsvg_internals::surface_utils::shared_surface::{
+    composite_arithmetic, SharedImageSurface, SurfaceType,
+};
 
 const SURFACE_SIDE: i32 = 512;
 const BOUNDS: IRect = IRect {
diff --git a/rsvg_internals/src/filters/composite.rs b/rsvg_internals/src/filters/composite.rs
index da3bc3f1..138bbcf4 100644
--- a/rsvg_internals/src/filters/composite.rs
+++ b/rsvg_internals/src/filters/composite.rs
@@ -1,4 +1,3 @@
-use cairo::{self, ImageSurface};
 use cssparser::Parser;
 use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
@@ -7,14 +6,6 @@ use crate::error::*;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
 use crate::parsers::{Parse, ParseValue};
 use crate::property_bag::PropertyBag;
-use crate::rect::IRect;
-use crate::surface_utils::{
-    iterators::Pixels,
-    shared_surface::SharedImageSurface,
-    ImageSurfaceDataExt,
-    Pixel,
-};
-use crate::util::clamp;
 
 use super::context::{FilterContext, FilterOutput, FilterResult};
 use super::input::Input;
@@ -80,55 +71,6 @@ impl NodeTrait for FeComposite {
     }
 }
 
-/// Performs the arithmetic composite operation. Public for benchmarking.
-#[inline]
-pub fn composite_arithmetic(
-    input_surface: &SharedImageSurface,
-    input_2_surface: &SharedImageSurface,
-    output_surface: &mut cairo::ImageSurface,
-    bounds: IRect,
-    k1: f64,
-    k2: f64,
-    k3: f64,
-    k4: f64,
-) {
-    let output_stride = output_surface.get_stride() as usize;
-    {
-        let mut output_data = output_surface.get_data().unwrap();
-
-        for (x, y, pixel, pixel_2) in Pixels::new(input_surface, bounds)
-            .map(|(x, y, p)| (x, y, p, input_2_surface.get_pixel(x, y)))
-        {
-            let i1a = f64::from(pixel.a) / 255f64;
-            let i2a = f64::from(pixel_2.a) / 255f64;
-            let oa = k1 * i1a * i2a + k2 * i1a + k3 * i2a + k4;
-            let oa = clamp(oa, 0f64, 1f64);
-
-            // Contents of image surfaces are transparent by default, so if the resulting pixel is
-            // transparent there's no need to do anything.
-            if oa > 0f64 {
-                let compute = |i1, i2| {
-                    let i1 = f64::from(i1) / 255f64;
-                    let i2 = f64::from(i2) / 255f64;
-
-                    let o = k1 * i1 * i2 + k2 * i1 + k3 * i2 + k4;
-                    let o = clamp(o, 0f64, oa);
-
-                    ((o * 255f64) + 0.5) as u8
-                };
-
-                let output_pixel = Pixel {
-                    r: compute(pixel.r, pixel_2.r),
-                    g: compute(pixel.g, pixel_2.g),
-                    b: compute(pixel.b, pixel_2.b),
-                    a: ((oa * 255f64) + 0.5) as u8,
-                };
-                output_data.set_pixel(output_stride, output_pixel, x, y);
-            }
-        }
-    }
-}
-
 impl FilterEffect for FeComposite {
     fn render(
         &self,
@@ -145,63 +87,26 @@ impl FilterEffect for FeComposite {
             .add_input(&input_2)
             .into_irect(draw_ctx);
 
-        // If we're combining two alpha-only surfaces, the result is alpha-only. Otherwise the
-        // result is whatever the non-alpha-only type we're working on (which can be either sRGB or
-        // linear sRGB depending on color-interpolation-filters).
-        let surface_type = if input.surface().is_alpha_only() {
-            input_2.surface().surface_type()
-        } else {
-            if !input_2.surface().is_alpha_only() {
-                // All surface types should match (this is enforced by get_input()).
-                assert_eq!(
-                    input_2.surface().surface_type(),
-                    input.surface().surface_type()
-                );
-            }
-
-            input.surface().surface_type()
-        };
-
-        let output_surface = if self.operator == Operator::Arithmetic {
-            let mut output_surface = ImageSurface::create(
-                cairo::Format::ARgb32,
-                input.surface().width(),
-                input.surface().height(),
-            )?;
-
-            composite_arithmetic(
-                input.surface(),
+        let surface = if self.operator == Operator::Arithmetic {
+            input.surface().compose_arithmetic(
                 input_2.surface(),
-                &mut output_surface,
                 bounds,
                 self.k1,
                 self.k2,
                 self.k3,
                 self.k4,
-            );
-
-            output_surface
+            )?
         } else {
-            let output_surface = input_2.surface().copy_surface(bounds)?;
-
-            let cr = cairo::Context::new(&output_surface);
-            let r = cairo::Rectangle::from(bounds);
-            cr.rectangle(r.x, r.y, r.width, r.height);
-            cr.clip();
-
-            input.surface().set_as_source_surface(&cr, 0f64, 0f64);
-            cr.set_operator(self.operator.into());
-            cr.paint();
-
-            output_surface
+            input.surface().compose(
+                input_2.surface(),
+                bounds,
+                cairo::Operator::from(self.operator),
+            )?
         };
 
         Ok(FilterResult {
             name: self.base.result.clone(),
-            output: FilterOutput {
-                surface: SharedImageSurface::new(output_surface, surface_type)?,
-                bounds,
-            },
+            output: FilterOutput { surface, bounds },
         })
     }
 
diff --git a/rsvg_internals/src/surface_utils/shared_surface.rs 
b/rsvg_internals/src/surface_utils/shared_surface.rs
index 53a5b040..0e4aba1f 100644
--- a/rsvg_internals/src/surface_utils/shared_surface.rs
+++ b/rsvg_internals/src/surface_utils/shared_surface.rs
@@ -16,9 +16,7 @@ use crate::util::clamp;
 
 use super::{
     iterators::{PixelRectangle, Pixels},
-    EdgeMode,
-    ImageSurfaceDataExt,
-    Pixel,
+    EdgeMode, ImageSurfaceDataExt, Pixel,
 };
 
 /// Types of pixel data in a `SharedImageSurface`.
@@ -35,6 +33,25 @@ pub enum SurfaceType {
     AlphaOnly,
 }
 
+impl SurfaceType {
+    /// Combines surface types
+    ///
+    /// If combining two alpha-only surfaces, the result is alpha-only.
+    /// If one is alpha-only, the result is the other.
+    /// If none is alpha-only, the types should be the same.
+    ///
+    /// # Panics
+    /// Panics if the surface types are not alpha-only and differ.
+    pub fn combine(self, other: SurfaceType) -> SurfaceType {
+        match (self, other) {
+            (SurfaceType::AlphaOnly, t) => t,
+            (t, SurfaceType::AlphaOnly) => t,
+            (t1, t2) if t1 == t2 => t1,
+            _ => panic!(),
+        }
+    }
+}
+
 /// Wrapper for a Cairo image surface that allows shared access.
 ///
 /// There doesn't seem to be any good way of making safe shared access to `ImageSurface` pixel
@@ -886,6 +903,65 @@ impl SharedImageSurface {
         SharedImageSurface::new(output_surface, self.surface_type)
     }
 
+    /// Performs the combination of two input surfaces using Porter-Duff
+    /// compositing operators
+    ///
+    /// # Panics
+    /// Panics if the two surface types are not compatible.
+    #[inline]
+    pub fn compose(
+        &self,
+        other: &SharedImageSurface,
+        bounds: IRect,
+        operator: cairo::Operator,
+    ) -> Result<SharedImageSurface, cairo::Status> {
+        let output_surface = other.copy_surface(bounds)?;
+
+        {
+            let cr = cairo::Context::new(&output_surface);
+            let r = cairo::Rectangle::from(bounds);
+            cr.rectangle(r.x, r.y, r.width, r.height);
+            cr.clip();
+
+            self.set_as_source_surface(&cr, 0.0, 0.0);
+            cr.set_operator(operator);
+            cr.paint();
+        }
+
+        SharedImageSurface::new(
+            output_surface,
+            self.surface_type.combine(other.surface_type),
+        )
+    }
+
+    /// Performs the combination of two input surfaces.
+    ///
+    /// Each pixel of the resulting image is computed using the following formula:
+    /// `res = k1*i1*i2 + k2*i1 + k3*i2 + k4`
+    ///
+    /// # Panics
+    /// Panics if the two surface types are not compatible.
+    #[inline]
+    pub fn compose_arithmetic(
+        &self,
+        other: &SharedImageSurface,
+        bounds: IRect,
+        k1: f64,
+        k2: f64,
+        k3: f64,
+        k4: f64,
+    ) -> Result<SharedImageSurface, cairo::Status> {
+        let mut output_surface =
+            cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
+
+        composite_arithmetic(self, other, &mut output_surface, bounds, k1, k2, k3, k4);
+
+        SharedImageSurface::new(
+            output_surface,
+            self.surface_type.combine(other.surface_type),
+        )
+    }
+
     /// Returns a raw pointer to the underlying surface.
     ///
     /// # Safety
@@ -896,6 +972,55 @@ impl SharedImageSurface {
     }
 }
 
+/// Performs the arithmetic composite operation. Public for benchmarking.
+#[inline]
+pub fn composite_arithmetic(
+    surface1: &SharedImageSurface,
+    surface2: &SharedImageSurface,
+    output_surface: &mut cairo::ImageSurface,
+    bounds: IRect,
+    k1: f64,
+    k2: f64,
+    k3: f64,
+    k4: f64,
+) {
+    let output_stride = output_surface.get_stride() as usize;
+    {
+        let mut output_data = output_surface.get_data().unwrap();
+
+        for (x, y, pixel, pixel_2) in
+            Pixels::new(surface1, bounds).map(|(x, y, p)| (x, y, p, surface2.get_pixel(x, y)))
+        {
+            let i1a = f64::from(pixel.a) / 255f64;
+            let i2a = f64::from(pixel_2.a) / 255f64;
+            let oa = k1 * i1a * i2a + k2 * i1a + k3 * i2a + k4;
+            let oa = clamp(oa, 0f64, 1f64);
+
+            // Contents of image surfaces are transparent by default, so if the resulting pixel is
+            // transparent there's no need to do anything.
+            if oa > 0f64 {
+                let compute = |i1, i2| {
+                    let i1 = f64::from(i1) / 255f64;
+                    let i2 = f64::from(i2) / 255f64;
+
+                    let o = k1 * i1 * i2 + k2 * i1 + k3 * i2 + k4;
+                    let o = clamp(o, 0f64, oa);
+
+                    ((o * 255f64) + 0.5) as u8
+                };
+
+                let output_pixel = Pixel {
+                    r: compute(pixel.r, pixel_2.r),
+                    g: compute(pixel.g, pixel_2.g),
+                    b: compute(pixel.b, pixel_2.b),
+                    a: ((oa * 255f64) + 0.5) as u8,
+                };
+                output_data.set_pixel(output_stride, output_pixel, x, y);
+            }
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;


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