[librsvg: 1/3] filters: move cairo code out of the image filter



commit 11f3c2d12a7fbae5c916e20d009b4a305c45686a
Author: Paolo Borelli <pborelli gnome org>
Date:   Tue Jan 7 17:00:40 2020 +0100

    filters: move cairo code out of the image filter
    
    Factor out a paint_image method on SharedImageSurface

 rsvg_internals/src/drawing_ctx.rs                  |  26 ++--
 rsvg_internals/src/filters/error.rs                |  15 +++
 rsvg_internals/src/filters/image.rs                | 141 ++++++---------------
 rsvg_internals/src/surface_utils/shared_surface.rs |  42 +++++-
 4 files changed, 112 insertions(+), 112 deletions(-)
---
diff --git a/rsvg_internals/src/drawing_ctx.rs b/rsvg_internals/src/drawing_ctx.rs
index fd2d7881..0a17833b 100644
--- a/rsvg_internals/src/drawing_ctx.rs
+++ b/rsvg_internals/src/drawing_ctx.rs
@@ -935,30 +935,34 @@ impl DrawingCtx {
             .map_err(|_| RenderingError::InvalidHref)
     }
 
-    pub fn draw_node_on_surface(
+    pub fn draw_node_to_surface(
         &mut self,
         node: &RsvgNode,
         cascaded: &CascadedValues<'_>,
-        surface: &cairo::ImageSurface,
         affine: cairo::Matrix,
-        width: f64,
-        height: f64,
-    ) -> Result<BoundingBox, RenderingError> {
+        width: i32,
+        height: i32,
+    ) -> Result<SharedImageSurface, RenderingError> {
+        let surface = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
+
         let save_cr = self.cr.clone();
         let save_rect = self.rect;
 
-        let cr = cairo::Context::new(surface);
-        cr.set_matrix(affine);
+        {
+            let cr = cairo::Context::new(&surface);
+            cr.set_matrix(affine);
 
-        self.cr = cr;
-        self.rect = Rect::from_size(width, height);
+            self.cr = cr;
 
-        let res = self.draw_node_from_stack(cascaded, node, false);
+            self.rect = Rect::from_size(f64::from(width), f64::from(height));
+
+            let _ = self.draw_node_from_stack(cascaded, node, false)?;
+        }
 
         self.cr = save_cr;
         self.rect = save_rect;
 
-        res
+        Ok(SharedImageSurface::new(surface, SurfaceType::SRgb)?)
     }
 
     pub fn draw_node_from_stack(
diff --git a/rsvg_internals/src/filters/error.rs b/rsvg_internals/src/filters/error.rs
index f70e96b3..21055bf5 100644
--- a/rsvg_internals/src/filters/error.rs
+++ b/rsvg_internals/src/filters/error.rs
@@ -1,6 +1,8 @@
 use std::error::Error;
 use std::fmt;
 
+use crate::error::RenderingError;
+
 /// An enumeration of errors that can occur during filter primitive rendering.
 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
 pub enum FilterError {
@@ -60,3 +62,16 @@ impl From<cairo::Status> for FilterError {
         FilterError::CairoError(x)
     }
 }
+
+impl From<RenderingError> for FilterError {
+    #[inline]
+    fn from(e: RenderingError) -> Self {
+        if let RenderingError::Cairo(status) = e {
+            FilterError::CairoError(status)
+        } else {
+            // FIXME: this is just a dummy value; we should probably have a way to indicate
+            // an error in the underlying drawing process.
+            FilterError::CairoError(cairo::Status::InvalidStatus)
+        }
+    }
+}
diff --git a/rsvg_internals/src/filters/image.rs b/rsvg_internals/src/filters/image.rs
index d43a1694..fddcb084 100644
--- a/rsvg_internals/src/filters/image.rs
+++ b/rsvg_internals/src/filters/image.rs
@@ -8,7 +8,6 @@ use crate::node::{CascadedValues, NodeResult, NodeTrait, RsvgNode};
 use crate::parsers::ParseValue;
 use crate::property_bag::PropertyBag;
 use crate::rect::Rect;
-use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
 use crate::viewbox::ViewBox;
 
 use super::context::{FilterContext, FilterOutput, FilterResult};
@@ -41,56 +40,32 @@ impl FeImage {
         draw_ctx: &mut DrawingCtx,
         bounds: Rect,
         fragment: &Fragment,
-    ) -> Result<cairo::ImageSurface, FilterError> {
+    ) -> Result<FilterResult, FilterError> {
         let acquired_drawable = draw_ctx
             .acquire_node(fragment, &[])
             .map_err(|_| FilterError::InvalidInput)?;
         let drawable = acquired_drawable.get();
 
-        let surface = cairo::ImageSurface::create(
-            cairo::Format::ARgb32,
-            ctx.source_graphic().width(),
-            ctx.source_graphic().height(),
-        )?;
-
         let node_being_filtered_values = ctx.get_computed_values_from_node_being_filtered();
-
         let cascaded = CascadedValues::new_from_values(&drawable, node_being_filtered_values);
 
-        draw_ctx
-            .draw_node_on_surface(
-                &drawable,
-                &cascaded,
-                &surface,
-                ctx.paffine(),
-                f64::from(ctx.source_graphic().width()),
-                f64::from(ctx.source_graphic().height()),
-            )
-            .map_err(|e| {
-                if let RenderingError::Cairo(status) = e {
-                    FilterError::CairoError(status)
-                } else {
-                    // FIXME: this is just a dummy value; we should probably have a way to indicate
-                    // an error in the underlying drawing process.
-                    FilterError::CairoError(cairo::Status::InvalidStatus)
-                }
-            })?;
-
-        // Clip the output to bounds.
-        let output_surface = cairo::ImageSurface::create(
-            cairo::Format::ARgb32,
+        let image = draw_ctx.draw_node_to_surface(
+            &drawable,
+            &cascaded,
+            ctx.paffine(),
             ctx.source_graphic().width(),
             ctx.source_graphic().height(),
         )?;
 
-        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();
-        cr.set_source_surface(&surface, 0f64, 0f64);
-        cr.paint();
+        let surface = ctx.source_graphic().paint_image(bounds, &image, None)?;
 
-        Ok(output_surface)
+        Ok(FilterResult {
+            name: self.base.result.clone(),
+            output: FilterOutput {
+                surface,
+                bounds: bounds.into(),
+            },
+        })
     }
 
     /// Renders the filter if the source is an external image.
@@ -98,58 +73,35 @@ impl FeImage {
         &self,
         ctx: &FilterContext,
         draw_ctx: &DrawingCtx,
-        bounds: &Rect,
+        bounds: Rect,
         unclipped_bounds: &Rect,
-        href: &Href,
-    ) -> Result<cairo::ImageSurface, FilterError> {
-        let surface = if let Href::PlainUrl(ref url) = *href {
-            // FIXME: translate the error better here
-            draw_ctx
-                .lookup_image(&url)
-                .map_err(|_| FilterError::InvalidInput)?
-        } else {
-            unreachable!();
-        };
-
-        let output_surface = cairo::ImageSurface::create(
-            cairo::Format::ARgb32,
-            ctx.source_graphic().width(),
-            ctx.source_graphic().height(),
-        )?;
+        url: &str,
+    ) -> Result<FilterResult, FilterError> {
+        // FIXME: translate the error better here
+        let image = draw_ctx
+            .lookup_image(url)
+            .map_err(|_| FilterError::InvalidInput)?;
 
         // TODO: this goes through a f64->i32->f64 conversion.
-        let r = self.aspect.compute(
+        let rect = self.aspect.compute(
             &ViewBox(Rect::from_size(
-                f64::from(surface.width()),
-                f64::from(surface.height()),
+                f64::from(image.width()),
+                f64::from(image.height()),
             )),
             &unclipped_bounds,
         );
 
-        if r.is_empty() {
-            return Ok(output_surface);
-        }
-
-        let ptn = surface.to_cairo_pattern();
-        let mut matrix = cairo::Matrix::new(
-            r.width() / f64::from(surface.width()),
-            0.0,
-            0.0,
-            r.height() / f64::from(surface.height()),
-            r.x0,
-            r.y0,
-        );
-        matrix.invert();
-        ptn.set_matrix(matrix);
-
-        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();
-        cr.set_source(&ptn);
-        cr.paint();
-
-        Ok(output_surface)
+        let surface = ctx
+            .source_graphic()
+            .paint_image(bounds, &image, Some(rect))?;
+
+        Ok(FilterResult {
+            name: self.base.result.clone(),
+            output: FilterOutput {
+                surface,
+                bounds: bounds.into(),
+            },
+        })
     }
 }
 
@@ -190,24 +142,13 @@ impl FilterEffect for FeImage {
         let bounds_builder = self.base.get_bounds(ctx);
         let bounds = bounds_builder.into_rect(draw_ctx);
 
-        if let Some(href) = self.href.as_ref() {
-            let output_surface = match href {
-                Href::PlainUrl(_) => {
-                    let unclipped_bounds = bounds_builder.into_rect_without_clipping(draw_ctx);
-                    self.render_external_image(ctx, draw_ctx, &bounds, &unclipped_bounds, href)?
-                }
-                Href::WithFragment(ref frag) => self.render_node(ctx, draw_ctx, bounds, frag)?,
-            };
-
-            Ok(FilterResult {
-                name: self.base.result.clone(),
-                output: FilterOutput {
-                    surface: SharedImageSurface::new(output_surface, SurfaceType::SRgb)?,
-                    bounds: bounds.into(),
-                },
-            })
-        } else {
-            Err(FilterError::InvalidInput)
+        match self.href.as_ref() {
+            Some(Href::PlainUrl(url)) => {
+                let unclipped_bounds = bounds_builder.into_rect_without_clipping(draw_ctx);
+                self.render_external_image(ctx, draw_ctx, bounds, &unclipped_bounds, url)
+            }
+            Some(Href::WithFragment(ref frag)) => self.render_node(ctx, draw_ctx, bounds, frag),
+            _ => Err(FilterError::InvalidInput),
         }
     }
 
diff --git a/rsvg_internals/src/surface_utils/shared_surface.rs 
b/rsvg_internals/src/surface_utils/shared_surface.rs
index f36dd9da..9b4abb0f 100644
--- a/rsvg_internals/src/surface_utils/shared_surface.rs
+++ b/rsvg_internals/src/surface_utils/shared_surface.rs
@@ -8,7 +8,7 @@ use gdk_pixbuf::{Colorspace, Pixbuf};
 use glib::translate::{Stash, ToGlibPtr};
 use nalgebra::{storage::Storage, Dim, Matrix};
 
-use crate::rect::IRect;
+use crate::rect::{IRect, Rect};
 use crate::surface_utils::srgb;
 use crate::unit_interval::UnitInterval;
 use crate::util::clamp;
@@ -961,6 +961,46 @@ impl SharedImageSurface {
         SharedImageSurface::new(output_surface, self.surface_type)
     }
 
+    /// Returns a new surface of the same size, with the contents of the
+    /// specified image, optionally transformed to match a given box
+    #[inline]
+    pub fn paint_image(
+        &self,
+        bounds: Rect,
+        image: &SharedImageSurface,
+        rect: Option<Rect>,
+    ) -> Result<SharedImageSurface, cairo::Status> {
+        let output_surface =
+            cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
+
+        if rect.is_none() || !rect.unwrap().is_empty() {
+            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();
+
+            image.set_as_source_surface(&cr, 0f64, 0f64);
+
+            if let Some(rect) = rect {
+                let mut matrix = cairo::Matrix::new(
+                    rect.width() / f64::from(image.width()),
+                    0.0,
+                    0.0,
+                    rect.height() / f64::from(image.height()),
+                    rect.x0,
+                    rect.y0,
+                );
+                matrix.invert();
+
+                cr.get_source().set_matrix(matrix);
+            }
+
+            cr.paint();
+        }
+
+        SharedImageSurface::new(output_surface, image.surface_type)
+    }
+
     /// Performs the combination of two input surfaces using Porter-Duff
     /// compositing operators
     ///


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