[librsvg] Change ImageSurfaceDataShared to a safe version



commit c84014cba2b3c13f0c505d45841c2a819600feba
Author: Ivan Molodetskikh <yalterz gmail com>
Date:   Thu Jun 21 16:56:31 2018 +0300

    Change ImageSurfaceDataShared to a safe version

 Makefile.am                                        | 124 ++++-----
 librsvg/filters/color_matrix.c                     |   1 -
 librsvg/filters/convolve_matrix.c                  |   1 -
 librsvg/filters/diffuse_lighting.c                 |   1 -
 librsvg/filters/displacement_map.c                 |   1 -
 librsvg/filters/erode.c                            |   1 -
 librsvg/filters/gaussian_blur.c                    |   1 -
 librsvg/filters/specular_lighting.c                |   1 -
 librsvg/filters/tile.c                             |   1 -
 librsvg/filters/turbulence.c                       |   1 -
 rsvg_internals/benches/pixel_iterators.rs          |  10 +-
 rsvg_internals/src/filters/blend.rs                |  30 ++-
 rsvg_internals/src/filters/component_transfer.rs   |  21 +-
 rsvg_internals/src/filters/composite.rs            |  28 +-
 rsvg_internals/src/filters/context.rs              |  79 +++---
 rsvg_internals/src/filters/ffi.rs                  |  19 +-
 rsvg_internals/src/filters/flood.rs                |   7 +-
 rsvg_internals/src/filters/image.rs                |  19 +-
 rsvg_internals/src/filters/iterators.rs            | 288 ---------------------
 rsvg_internals/src/filters/merge.rs                |   8 +-
 rsvg_internals/src/filters/mod.rs                  |   1 -
 rsvg_internals/src/filters/offset.rs               |  31 ++-
 rsvg_internals/src/lib.rs                          |   1 +
 rsvg_internals/src/srgb.rs                         |  26 +-
 rsvg_internals/src/surface_utils/iterators.rs      | 142 ++++++++++
 rsvg_internals/src/surface_utils/mod.rs            |  33 +++
 rsvg_internals/src/surface_utils/shared_surface.rs | 139 ++++++++++
 27 files changed, 532 insertions(+), 483 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 23181e7f..3d90467d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -65,68 +65,70 @@ librsvg_@RSVG_API_MAJOR_VERSION@_la_SOURCES =       \
        librsvg/rsvg.h                          \
        $(NULL)
 
-RUST_SRC =                                     \
-       Cargo.toml                              \
-       rsvg_internals/Cargo.toml               \
-       rsvg_internals/build.rs                 \
-       rsvg_internals/src/aspect_ratio.rs      \
-       rsvg_internals/src/attributes.rs        \
-       rsvg_internals/src/bbox.rs              \
-       rsvg_internals/src/clip_path.rs         \
-       rsvg_internals/src/cnode.rs             \
-       rsvg_internals/src/color.rs             \
-       rsvg_internals/src/cond.rs              \
-       rsvg_internals/src/coord_units.rs       \
-       rsvg_internals/src/defs.rs              \
-       rsvg_internals/src/draw.rs              \
-       rsvg_internals/src/drawing_ctx.rs       \
-       rsvg_internals/src/filters/bounds.rs    \
-       rsvg_internals/src/filters/blend.rs     \
+RUST_SRC =                                                     \
+       Cargo.toml                                              \
+       rsvg_internals/Cargo.toml                               \
+       rsvg_internals/build.rs                                 \
+       rsvg_internals/src/aspect_ratio.rs                      \
+       rsvg_internals/src/attributes.rs                        \
+       rsvg_internals/src/bbox.rs                              \
+       rsvg_internals/src/clip_path.rs                         \
+       rsvg_internals/src/cnode.rs                             \
+       rsvg_internals/src/color.rs                             \
+       rsvg_internals/src/cond.rs                              \
+       rsvg_internals/src/coord_units.rs                       \
+       rsvg_internals/src/defs.rs                              \
+       rsvg_internals/src/draw.rs                              \
+       rsvg_internals/src/drawing_ctx.rs                       \
+       rsvg_internals/src/filters/bounds.rs                    \
+       rsvg_internals/src/filters/blend.rs                     \
        rsvg_internals/src/filters/component_transfer.rs        \
-       rsvg_internals/src/filters/composite.rs \
-       rsvg_internals/src/filters/context.rs   \
-       rsvg_internals/src/filters/error.rs     \
-       rsvg_internals/src/filters/ffi.rs       \
-       rsvg_internals/src/filters/flood.rs     \
-       rsvg_internals/src/filters/image.rs     \
-       rsvg_internals/src/filters/merge.rs     \
-       rsvg_internals/src/filters/mod.rs       \
-       rsvg_internals/src/filters/node.rs      \
-       rsvg_internals/src/filters/input.rs     \
-       rsvg_internals/src/filters/iterators.rs \
-       rsvg_internals/src/filters/offset.rs    \
-       rsvg_internals/src/error.rs             \
-       rsvg_internals/src/float_eq_cairo.rs    \
-       rsvg_internals/src/gradient.rs          \
-       rsvg_internals/src/handle.rs            \
-       rsvg_internals/src/image.rs             \
-       rsvg_internals/src/iri.rs               \
-       rsvg_internals/src/length.rs            \
-       rsvg_internals/src/lib.rs               \
-       rsvg_internals/src/link.rs              \
-       rsvg_internals/src/load.rs              \
-       rsvg_internals/src/marker.rs            \
-       rsvg_internals/src/mask.rs              \
-       rsvg_internals/src/node.rs              \
-       rsvg_internals/src/paint_server.rs      \
-       rsvg_internals/src/parsers.rs           \
-       rsvg_internals/src/path_builder.rs      \
-       rsvg_internals/src/path_parser.rs       \
-       rsvg_internals/src/pattern.rs           \
-       rsvg_internals/src/property_bag.rs      \
-       rsvg_internals/src/property_macros.rs   \
-       rsvg_internals/src/rect.rs              \
-       rsvg_internals/src/shapes.rs            \
-       rsvg_internals/src/space.rs             \
-       rsvg_internals/src/srgb.rs              \
-       rsvg_internals/src/state.rs             \
-       rsvg_internals/src/stop.rs              \
-       rsvg_internals/src/structure.rs         \
-       rsvg_internals/src/text.rs              \
-       rsvg_internals/src/transform.rs         \
-       rsvg_internals/src/unitinterval.rs      \
-       rsvg_internals/src/util.rs              \
-       rsvg_internals/src/viewbox.rs           \
+       rsvg_internals/src/filters/composite.rs                 \
+       rsvg_internals/src/filters/context.rs                   \
+       rsvg_internals/src/filters/error.rs                     \
+       rsvg_internals/src/filters/ffi.rs                       \
+       rsvg_internals/src/filters/flood.rs                     \
+       rsvg_internals/src/filters/image.rs                     \
+       rsvg_internals/src/filters/merge.rs                     \
+       rsvg_internals/src/filters/mod.rs                       \
+       rsvg_internals/src/filters/node.rs                      \
+       rsvg_internals/src/filters/input.rs                     \
+       rsvg_internals/src/filters/offset.rs                    \
+       rsvg_internals/src/error.rs                             \
+       rsvg_internals/src/float_eq_cairo.rs                    \
+       rsvg_internals/src/gradient.rs                          \
+       rsvg_internals/src/handle.rs                            \
+       rsvg_internals/src/image.rs                             \
+       rsvg_internals/src/iri.rs                               \
+       rsvg_internals/src/length.rs                            \
+       rsvg_internals/src/lib.rs                               \
+       rsvg_internals/src/link.rs                              \
+       rsvg_internals/src/load.rs                              \
+       rsvg_internals/src/marker.rs                            \
+       rsvg_internals/src/mask.rs                              \
+       rsvg_internals/src/node.rs                              \
+       rsvg_internals/src/paint_server.rs                      \
+       rsvg_internals/src/parsers.rs                           \
+       rsvg_internals/src/path_builder.rs                      \
+       rsvg_internals/src/path_parser.rs                       \
+       rsvg_internals/src/pattern.rs                           \
+       rsvg_internals/src/property_bag.rs                      \
+       rsvg_internals/src/property_macros.rs                   \
+       rsvg_internals/src/rect.rs                              \
+       rsvg_internals/src/shapes.rs                            \
+       rsvg_internals/src/surface_utils/iterators.rs           \
+       rsvg_internals/src/surface_utils/mod.rs                 \
+       rsvg_internals/src/surface_utils/shared_surface.rs      \
+       rsvg_internals/src/space.rs                             \
+       rsvg_internals/src/srgb.rs                              \
+       rsvg_internals/src/state.rs                             \
+       rsvg_internals/src/stop.rs                              \
+       rsvg_internals/src/structure.rs                         \
+       rsvg_internals/src/text.rs                              \
+       rsvg_internals/src/transform.rs                         \
+       rsvg_internals/src/unitinterval.rs                      \
+       rsvg_internals/src/util.rs                              \
+       rsvg_internals/src/viewbox.rs                           \
        rsvg_internals/src/viewport.rs
 
 RUST_EXTRA =                                   \
diff --git a/librsvg/filters/color_matrix.c b/librsvg/filters/color_matrix.c
index 5d825499..a14cee21 100644
--- a/librsvg/filters/color_matrix.c
+++ b/librsvg/filters/color_matrix.c
@@ -135,7 +135,6 @@ rsvg_filter_primitive_color_matrix_render (RsvgNode *node, RsvgComputedValues *v
     /* rsvg_filter_store_result (primitive->result, output, ctx); */
 
     cairo_surface_destroy (in);
-    cairo_surface_destroy (output);
 }
 
 static void
diff --git a/librsvg/filters/convolve_matrix.c b/librsvg/filters/convolve_matrix.c
index 1d1baac4..d36f82a5 100644
--- a/librsvg/filters/convolve_matrix.c
+++ b/librsvg/filters/convolve_matrix.c
@@ -187,7 +187,6 @@ rsvg_filter_primitive_convolve_matrix_render (RsvgNode *node, RsvgComputedValues
     /* rsvg_filter_store_result (primitive->result, output, ctx); */
 
     cairo_surface_destroy (in);
-    cairo_surface_destroy (output);
 }
 
 static void
diff --git a/librsvg/filters/diffuse_lighting.c b/librsvg/filters/diffuse_lighting.c
index c4ef653c..f84c4c36 100644
--- a/librsvg/filters/diffuse_lighting.c
+++ b/librsvg/filters/diffuse_lighting.c
@@ -149,7 +149,6 @@ rsvg_filter_primitive_diffuse_lighting_render (RsvgNode *node, RsvgComputedValue
     /* rsvg_filter_store_result (primitive->result, output, ctx); */
 
     cairo_surface_destroy (in);
-    cairo_surface_destroy (output);
 }
 
 static void
diff --git a/librsvg/filters/displacement_map.c b/librsvg/filters/displacement_map.c
index f355600e..21643ea2 100644
--- a/librsvg/filters/displacement_map.c
+++ b/librsvg/filters/displacement_map.c
@@ -162,7 +162,6 @@ rsvg_filter_primitive_displacement_map_render (RsvgNode *node, RsvgComputedValue
 
     cairo_surface_destroy (in);
     cairo_surface_destroy (in2);
-    cairo_surface_destroy (output);
 }
 
 static void
diff --git a/librsvg/filters/erode.c b/librsvg/filters/erode.c
index 91666d10..b030ab4e 100644
--- a/librsvg/filters/erode.c
+++ b/librsvg/filters/erode.c
@@ -123,7 +123,6 @@ rsvg_filter_primitive_erode_render (RsvgNode *node, RsvgComputedValues *values,
     /* rsvg_filter_store_result (primitive->result, output, ctx); */
 
     cairo_surface_destroy (in);
-    cairo_surface_destroy (output);
 }
 
 static void
diff --git a/librsvg/filters/gaussian_blur.c b/librsvg/filters/gaussian_blur.c
index 01139a03..ca026b90 100644
--- a/librsvg/filters/gaussian_blur.c
+++ b/librsvg/filters/gaussian_blur.c
@@ -638,7 +638,6 @@ rsvg_filter_primitive_gaussian_blur_render (RsvgNode *node, RsvgComputedValues *
     rsvg_filter_store_output (primitive->result, op, ctx);
 
     cairo_surface_destroy (in);
-    cairo_surface_destroy (output);
 }
 
 static void
diff --git a/librsvg/filters/specular_lighting.c b/librsvg/filters/specular_lighting.c
index dd9ed2f4..96c37422 100644
--- a/librsvg/filters/specular_lighting.c
+++ b/librsvg/filters/specular_lighting.c
@@ -152,7 +152,6 @@ rsvg_filter_primitive_specular_lighting_render (RsvgNode *node, RsvgComputedValu
     /* rsvg_filter_store_result (primitive->result, output, ctx); */
 
     cairo_surface_destroy (in);
-    cairo_surface_destroy (output);
 }
 
 static void
diff --git a/librsvg/filters/tile.c b/librsvg/filters/tile.c
index 95383506..f46bb068 100644
--- a/librsvg/filters/tile.c
+++ b/librsvg/filters/tile.c
@@ -107,7 +107,6 @@ rsvg_filter_primitive_tile_render (RsvgNode *node, RsvgComputedValues *values, R
     /* rsvg_filter_store_result (primitive->result, output, ctx); */
 
     cairo_surface_destroy (in);
-    cairo_surface_destroy (output);
 }
 
 static void
diff --git a/librsvg/filters/turbulence.c b/librsvg/filters/turbulence.c
index d849ef83..fc86f425 100644
--- a/librsvg/filters/turbulence.c
+++ b/librsvg/filters/turbulence.c
@@ -348,7 +348,6 @@ rsvg_filter_primitive_turbulence_render (RsvgNode *node, RsvgComputedValues *val
     /* rsvg_filter_store_result (primitive->result, output, ctx); */
 
     cairo_surface_destroy (in);
-    cairo_surface_destroy (output);
 }
 
 static void
diff --git a/rsvg_internals/benches/pixel_iterators.rs b/rsvg_internals/benches/pixel_iterators.rs
index 576cab80..0613805f 100644
--- a/rsvg_internals/benches/pixel_iterators.rs
+++ b/rsvg_internals/benches/pixel_iterators.rs
@@ -9,7 +9,7 @@ extern crate test;
 mod tests {
     use super::*;
     use rsvg_internals::filters::context::IRect;
-    use rsvg_internals::filters::iterators;
+    use rsvg_internals::surface_utils::{iterators::Pixels, shared_surface::SharedImageSurface};
     use test::Bencher;
 
     const SURFACE_SIDE: i32 = 512;
@@ -52,7 +52,7 @@ mod tests {
     fn bench_straightforward_getpixel(b: &mut Bencher) {
         let surface =
             cairo::ImageSurface::create(cairo::Format::ARgb32, SURFACE_SIDE, SURFACE_SIDE).unwrap();
-        let data = iterators::ImageSurfaceDataShared::new(&surface).unwrap();
+        let surface = SharedImageSurface::new(surface).unwrap();
 
         b.iter(|| {
             let mut r = 0usize;
@@ -62,7 +62,7 @@ mod tests {
 
             for y in BOUNDS.y0..BOUNDS.y1 {
                 for x in BOUNDS.x0..BOUNDS.x1 {
-                    let pixel = data.get_pixel(x as usize, y as usize);
+                    let pixel = surface.get_pixel(x as u32, y as u32);
 
                     r += pixel.r as usize;
                     g += pixel.g as usize;
@@ -79,7 +79,7 @@ mod tests {
     fn bench_pixels(b: &mut Bencher) {
         let surface =
             cairo::ImageSurface::create(cairo::Format::ARgb32, SURFACE_SIDE, SURFACE_SIDE).unwrap();
-        let data = iterators::ImageSurfaceDataShared::new(&surface).unwrap();
+        let data = SharedImageSurface::new(surface).unwrap();
 
         b.iter(|| {
             let mut r = 0usize;
@@ -87,7 +87,7 @@ mod tests {
             let mut b = 0usize;
             let mut a = 0usize;
 
-            for (_x, _y, pixel) in iterators::Pixels::new(data, BOUNDS) {
+            for (_x, _y, pixel) in Pixels::new(&data, BOUNDS) {
                 r += pixel.r as usize;
                 g += pixel.g as usize;
                 b += pixel.b as usize;
diff --git a/rsvg_internals/src/filters/blend.rs b/rsvg_internals/src/filters/blend.rs
index 327d5c08..12289540 100644
--- a/rsvg_internals/src/filters/blend.rs
+++ b/rsvg_internals/src/filters/blend.rs
@@ -9,6 +9,7 @@ use node::{NodeResult, NodeTrait, RsvgCNodeImpl, RsvgNode};
 use parsers::ParseError;
 use property_bag::PropertyBag;
 use srgb::{linearize_surface, unlinearize_surface};
+use surface_utils::shared_surface::SharedImageSurface;
 
 use super::context::{FilterContext, FilterOutput, FilterResult};
 use super::input::Input;
@@ -90,26 +91,29 @@ impl Filter for Blend {
         let output_surface = linearize_surface(&input_2.surface(), bounds)
             .map_err(FilterError::BadInputSurfaceStatus)?;
 
-        let cr = cairo::Context::new(&output_surface);
-        cr.rectangle(
-            bounds.x0 as f64,
-            bounds.y0 as f64,
-            (bounds.x1 - bounds.x0) as f64,
-            (bounds.y1 - bounds.y0) as f64,
-        );
-        cr.clip();
-
-        cr.set_source_surface(&input_surface, 0f64, 0f64);
-        cr.set_operator(self.mode.get().into());
-        cr.paint();
+        {
+            let cr = cairo::Context::new(&output_surface);
+            cr.rectangle(
+                bounds.x0 as f64,
+                bounds.y0 as f64,
+                (bounds.x1 - bounds.x0) as f64,
+                (bounds.y1 - bounds.y0) as f64,
+            );
+            cr.clip();
+
+            cr.set_source_surface(&input_surface, 0f64, 0f64);
+            cr.set_operator(self.mode.get().into());
+            cr.paint();
+        }
 
+        let output_surface = SharedImageSurface::new(output_surface).unwrap();
         let output_surface = unlinearize_surface(&output_surface, bounds)
             .map_err(FilterError::OutputSurfaceCreation)?;
 
         Ok(FilterResult {
             name: self.base.result.borrow().clone(),
             output: FilterOutput {
-                surface: output_surface,
+                surface: SharedImageSurface::new(output_surface).unwrap(),
                 bounds,
             },
         })
diff --git a/rsvg_internals/src/filters/component_transfer.rs 
b/rsvg_internals/src/filters/component_transfer.rs
index 15cf8725..59c3409b 100644
--- a/rsvg_internals/src/filters/component_transfer.rs
+++ b/rsvg_internals/src/filters/component_transfer.rs
@@ -10,10 +10,15 @@ use node::{NodeResult, NodeTrait, NodeType, RsvgCNodeImpl, RsvgNode};
 use parsers::{self, ListLength, NumberListError, ParseError};
 use property_bag::PropertyBag;
 use srgb::{linearize_surface, unlinearize_surface};
+use surface_utils::{
+    iterators::Pixels,
+    shared_surface::SharedImageSurface,
+    ImageSurfaceDataExt,
+    Pixel,
+};
 use util::clamp;
 
 use super::context::{FilterContext, FilterOutput, FilterResult};
-use super::iterators::{ImageSurfaceDataExt, ImageSurfaceDataShared, Pixel, Pixels};
 use super::{make_result, Filter, FilterError, PrimitiveWithInput};
 
 /// The `feComponentTransfer` filter primitive.
@@ -282,8 +287,8 @@ impl Filter for ComponentTransfer {
         // Create the output surface.
         let mut output_surface = ImageSurface::create(
             cairo::Format::ARgb32,
-            ctx.source_graphic().get_width(),
-            ctx.source_graphic().get_height(),
+            ctx.source_graphic().width(),
+            ctx.source_graphic().height(),
         ).map_err(FilterError::OutputSurfaceCreation)?;
 
         // Enumerate all child <feFuncX> nodes.
@@ -344,15 +349,12 @@ impl Filter for ComponentTransfer {
         let compute_a = |alpha| compute_a(&params_a, alpha);
 
         // Do the actual processing.
-        let input_data = unsafe {
-            ImageSurfaceDataShared::new_unchecked(&input_surface)
-                .map_err(FilterError::BadInputSurfaceStatus)?
-        };
+        let input_surface = SharedImageSurface::new(input_surface).unwrap();
         let output_stride = output_surface.get_stride() as usize;
         {
             let mut output_data = output_surface.get_data().unwrap();
 
-            for (x, y, pixel) in Pixels::new(input_data, bounds) {
+            for (x, y, pixel) in Pixels::new(&input_surface, bounds) {
                 let alpha = f64::from(pixel.a) / 255f64;
                 let new_alpha = compute_a(alpha);
 
@@ -367,13 +369,14 @@ impl Filter for ComponentTransfer {
             }
         }
 
+        let output_surface = SharedImageSurface::new(output_surface).unwrap();
         let output_surface = unlinearize_surface(&output_surface, bounds)
             .map_err(FilterError::OutputSurfaceCreation)?;
 
         Ok(FilterResult {
             name: self.base.result.borrow().clone(),
             output: FilterOutput {
-                surface: output_surface,
+                surface: SharedImageSurface::new(output_surface).unwrap(),
                 bounds,
             },
         })
diff --git a/rsvg_internals/src/filters/composite.rs b/rsvg_internals/src/filters/composite.rs
index 9b4a995a..d2dd077d 100644
--- a/rsvg_internals/src/filters/composite.rs
+++ b/rsvg_internals/src/filters/composite.rs
@@ -10,11 +10,16 @@ use node::{NodeResult, NodeTrait, RsvgCNodeImpl, RsvgNode};
 use parsers::{self, parse, Parse};
 use property_bag::PropertyBag;
 use srgb::{linearize_surface, unlinearize_surface};
+use surface_utils::{
+    iterators::Pixels,
+    shared_surface::SharedImageSurface,
+    ImageSurfaceDataExt,
+    Pixel,
+};
 use util::clamp;
 
 use super::context::{FilterContext, FilterOutput, FilterResult};
 use super::input::Input;
-use super::iterators::{ImageSurfaceDataExt, ImageSurfaceDataShared, Pixel, Pixels};
 use super::{make_result, Filter, FilterError, PrimitiveWithInput};
 
 /// Enumeration of the possible compositing operations.
@@ -112,25 +117,19 @@ impl Filter for Composite {
             linearize_surface(input.surface(), bounds).map_err(FilterError::BadInputSurfaceStatus)?;
 
         let output_surface = if self.operator.get() == Operator::Arithmetic {
-            let input_data = unsafe {
-                ImageSurfaceDataShared::new_unchecked(&input_surface)
-                    .map_err(FilterError::BadInputSurfaceStatus)?
-            };
+            let input_surface = SharedImageSurface::new(input_surface).unwrap();
 
             // Not linearizing input_2 gives a better matching result than linearizing?..
             // Maybe it's due to some issue elsewhere? Am I missing something?
             //
             // let input_2_surface = linearize_surface(input_2.surface(), bounds)
             //     .map_err(FilterError::BadInputSurfaceStatus)?;
-            let input_2_data = unsafe {
-                ImageSurfaceDataShared::new_unchecked(&input_2.surface())
-                    .map_err(FilterError::BadInputSurfaceStatus)?
-            };
+            let input_2_surface = input_2.surface();
 
             let mut output_surface = ImageSurface::create(
                 cairo::Format::ARgb32,
-                input_data.width as i32,
-                input_data.height as i32,
+                input_surface.width(),
+                input_surface.height(),
             ).map_err(FilterError::OutputSurfaceCreation)?;
 
             let output_stride = output_surface.get_stride() as usize;
@@ -142,8 +141,8 @@ impl Filter for Composite {
                 let k3 = self.k3.get();
                 let k4 = self.k4.get();
 
-                for (x, y, pixel, pixel_2) in Pixels::new(input_data, bounds)
-                    .map(|(x, y, p)| (x, y, p, input_2_data.get_pixel(x, y)))
+                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;
@@ -196,13 +195,14 @@ impl Filter for Composite {
             output_surface
         };
 
+        let output_surface = SharedImageSurface::new(output_surface).unwrap();
         let output_surface = unlinearize_surface(&output_surface, bounds)
             .map_err(FilterError::OutputSurfaceCreation)?;
 
         Ok(FilterResult {
             name: self.base.result.borrow().clone(),
             output: FilterOutput {
-                surface: output_surface,
+                surface: SharedImageSurface::new(output_surface).unwrap(),
                 bounds,
             },
         })
diff --git a/rsvg_internals/src/filters/context.rs b/rsvg_internals/src/filters/context.rs
index 7aaab93c..b40ed6a1 100644
--- a/rsvg_internals/src/filters/context.rs
+++ b/rsvg_internals/src/filters/context.rs
@@ -6,7 +6,7 @@ use std::{mem, ptr};
 use cairo::prelude::SurfaceExt;
 use cairo::{self, MatrixTrait};
 use cairo_sys::cairo_surface_t;
-use glib::translate::{from_glib_none, ToGlibPtr};
+use glib::translate::{from_glib_full, from_glib_none};
 use glib_sys::*;
 
 use bbox::BoundingBox;
@@ -15,11 +15,11 @@ use drawing_ctx::{self, RsvgDrawingCtx};
 use length::RsvgLength;
 use node::{box_node, RsvgNode};
 use paint_server::{self, PaintServer};
+use surface_utils::{iterators::Pixels, shared_surface::SharedImageSurface, Pixel};
 use unitinterval::UnitInterval;
 
 use super::bounds::BoundsBuilder;
 use super::input::Input;
-use super::iterators::{ImageSurfaceDataShared, Pixel, Pixels};
 use super::node::NodeFilter;
 use super::RsvgFilterPrimitive;
 
@@ -48,7 +48,7 @@ pub struct RsvgFilterPrimitiveOutput {
 #[derive(Debug, Clone)]
 pub struct FilterOutput {
     /// The surface after the filter primitive was applied.
-    pub surface: cairo::ImageSurface,
+    pub surface: SharedImageSurface,
 
     /// The filter primitive subregion.
     pub bounds: IRect,
@@ -68,7 +68,7 @@ pub struct FilterResult {
 #[derive(Debug, Clone)]
 pub enum FilterInput {
     /// One of the standard inputs.
-    StandardInput(cairo::ImageSurface),
+    StandardInput(SharedImageSurface),
     /// Output of another filter primitive.
     PrimitiveOutput(FilterOutput),
 }
@@ -82,13 +82,13 @@ pub struct FilterContext {
     /// The node which referenced this filter.
     node_being_filtered: RsvgNode,
     /// The source graphic surface.
-    source_surface: cairo::ImageSurface,
+    source_surface: SharedImageSurface,
     /// Output of the last filter primitive.
     last_result: Option<FilterOutput>,
     /// Surfaces of the previous filter primitives by name.
     previous_results: HashMap<String, FilterOutput>,
     /// The background surface. Computed lazily.
-    background_surface: UnsafeCell<Option<Result<cairo::ImageSurface, cairo::Status>>>,
+    background_surface: UnsafeCell<Option<Result<SharedImageSurface, cairo::Status>>>,
     /// The drawing context.
     drawing_ctx: *mut RsvgDrawingCtx,
     /// The filter effects region.
@@ -121,20 +121,18 @@ pub struct FilterContext {
 
 /// Returns a surface with black background and alpha channel matching the input surface.
 fn extract_alpha(
-    surface: &cairo::ImageSurface,
+    surface: &SharedImageSurface,
     bounds: IRect,
 ) -> Result<cairo::ImageSurface, cairo::Status> {
-    let data = unsafe { ImageSurfaceDataShared::new_unchecked(surface).unwrap() };
-
     let mut output_surface =
-        cairo::ImageSurface::create(cairo::Format::ARgb32, data.width as i32, data.height as i32)?;
+        cairo::ImageSurface::create(cairo::Format::ARgb32, surface.width(), surface.height())?;
 
     let output_stride = output_surface.get_stride() as usize;
     {
         let mut output_data = output_surface.get_data().unwrap();
 
-        for (x, y, Pixel { a, .. }) in Pixels::new(data, bounds) {
-            output_data[y * output_stride + x * 4 + 3] = a;
+        for (x, y, Pixel { a, .. }) in Pixels::new(surface, bounds) {
+            output_data[y as usize * output_stride + x as usize * 4 + 3] = a;
         }
     }
 
@@ -225,7 +223,7 @@ impl FilterContext {
     pub fn new(
         filter_node: &RsvgNode,
         node_being_filtered: &RsvgNode,
-        source_surface: cairo::ImageSurface,
+        source_surface: SharedImageSurface,
         draw_ctx: *mut RsvgDrawingCtx,
         channelmap: [i32; 4],
     ) -> Self {
@@ -265,8 +263,8 @@ impl FilterContext {
             }
         };
 
-        let width = source_surface.get_width();
-        let height = source_surface.get_height();
+        let width = source_surface.width();
+        let height = source_surface.height();
 
         let mut rv = Self {
             node: filter_node.clone(),
@@ -318,7 +316,7 @@ impl FilterContext {
 
     /// Returns the surface corresponding to the source graphic.
     #[inline]
-    pub fn source_graphic(&self) -> &cairo::ImageSurface {
+    pub fn source_graphic(&self) -> &SharedImageSurface {
         &self.source_surface
     }
 
@@ -332,8 +330,8 @@ impl FilterContext {
     fn compute_background_image(&self) -> Result<cairo::ImageSurface, cairo::Status> {
         let surface = cairo::ImageSurface::create(
             cairo::Format::ARgb32,
-            self.source_surface.get_width(),
-            self.source_surface.get_height(),
+            self.source_surface.width(),
+            self.source_surface.height(),
         )?;
 
         let (x, y) = drawing_ctx::get_raw_offset(self.drawing_ctx);
@@ -354,7 +352,7 @@ impl FilterContext {
     }
 
     /// Returns the surface corresponding to the background image snapshot.
-    pub fn background_image(&self) -> Result<&cairo::ImageSurface, cairo::Status> {
+    pub fn background_image(&self) -> Result<&SharedImageSurface, cairo::Status> {
         {
             // At this point either no, or only immutable references to background_surface exist, so
             // it's ok to make an immutable reference.
@@ -371,7 +369,10 @@ impl FilterContext {
         // no references to it and we can create a mutable reference.
         let bg = unsafe { &mut *self.background_surface.get() };
 
-        *bg = Some(self.compute_background_image());
+        *bg = Some(
+            self.compute_background_image()
+                .and_then(SharedImageSurface::new),
+        );
 
         // Return the only existing reference as immutable.
         bg.as_ref().unwrap().as_ref().map_err(|&s| s)
@@ -393,7 +394,7 @@ impl FilterContext {
     /// Converts this `FilterContext` into the surface corresponding to the output of the filter
     /// chain.
     #[inline]
-    pub fn into_output(self) -> cairo::ImageSurface {
+    pub fn into_output(self) -> SharedImageSurface {
         self.last_result
             .map(|FilterOutput { surface, .. }| surface)
             .unwrap_or(self.source_surface)
@@ -463,8 +464,8 @@ impl FilterContext {
     ) -> Result<cairo::ImageSurface, cairo::Status> {
         let surface = cairo::ImageSurface::create(
             cairo::Format::ARgb32,
-            self.source_surface.get_width(),
-            self.source_surface.get_height(),
+            self.source_surface.width(),
+            self.source_surface.height(),
         )?;
 
         let cr_save = drawing_ctx::get_cairo_context(self.drawing_ctx);
@@ -509,6 +510,7 @@ impl FilterContext {
             Input::SourceAlpha => self
                 .source_alpha(self.effects_region().rect.unwrap().into())
                 .ok()
+                .map(|surface| SharedImageSurface::new(surface).unwrap())
                 .map(FilterInput::StandardInput),
             Input::BackgroundImage => self
                 .background_image()
@@ -518,15 +520,18 @@ impl FilterContext {
             Input::BackgroundAlpha => self
                 .background_alpha(self.effects_region().rect.unwrap().into())
                 .ok()
+                .map(|surface| SharedImageSurface::new(surface).unwrap())
                 .map(FilterInput::StandardInput),
 
             Input::FillPaint => self
                 .get_paint_server_surface(&values.fill.0, values.fill_opacity.0)
                 .ok()
+                .map(|surface| SharedImageSurface::new(surface).unwrap())
                 .map(FilterInput::StandardInput),
             Input::StrokePaint => self
                 .get_paint_server_surface(&values.stroke.0, values.stroke_opacity.0)
                 .ok()
+                .map(|surface| SharedImageSurface::new(surface).unwrap())
                 .map(FilterInput::StandardInput),
 
             Input::FilterOutput(ref name) => self
@@ -540,7 +545,7 @@ impl FilterContext {
 impl FilterInput {
     /// Retrieves the surface from `FilterInput`.
     #[inline]
-    pub fn surface(&self) -> &cairo::ImageSurface {
+    pub fn surface(&self) -> &SharedImageSurface {
         match *self {
             FilterInput::StandardInput(ref surface) => surface,
             FilterInput::PrimitiveOutput(FilterOutput { ref surface, .. }) => surface,
@@ -598,14 +603,14 @@ pub unsafe extern "C" fn rsvg_filter_context_get_drawing_ctx(
 pub unsafe extern "C" fn rsvg_filter_context_get_width(ctx: *const RsvgFilterContext) -> i32 {
     assert!(!ctx.is_null());
 
-    (*ctx).source_surface.get_width()
+    (*ctx).source_surface.width()
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn rsvg_filter_context_get_height(ctx: *const RsvgFilterContext) -> i32 {
     assert!(!ctx.is_null());
 
-    (*ctx).source_surface.get_height()
+    (*ctx).source_surface.height()
 }
 
 #[no_mangle]
@@ -707,9 +712,10 @@ pub unsafe extern "C" fn rsvg_filter_store_output(
 
     let name = from_glib_none((*name).str);
 
-    let surface: cairo::Surface = from_glib_none(result.surface);
+    let surface: cairo::Surface = from_glib_full(result.surface);
     assert_eq!(surface.get_type(), cairo::SurfaceType::Image);
     let surface = cairo::ImageSurface::from(surface).unwrap();
+    let surface = SharedImageSurface::new(surface).unwrap();
 
     let result = FilterResult {
         name: Some(name),
@@ -823,8 +829,8 @@ mod tests {
 
     #[test]
     fn test_extract_alpha() {
-        const WIDTH: usize = 32;
-        const HEIGHT: usize = 64;
+        const WIDTH: i32 = 32;
+        const HEIGHT: i32 = 64;
         const BOUNDS: IRect = IRect {
             x0: 8,
             x1: 24,
@@ -833,14 +839,13 @@ mod tests {
         };
         const FULL_BOUNDS: IRect = IRect {
             x0: 0,
-            x1: WIDTH as i32,
+            x1: WIDTH,
             y0: 0,
-            y1: HEIGHT as i32,
+            y1: HEIGHT,
         };
 
         let mut surface =
-            cairo::ImageSurface::create(cairo::Format::ARgb32, WIDTH as i32, HEIGHT as i32)
-                .unwrap();
+            cairo::ImageSurface::create(cairo::Format::ARgb32, WIDTH, HEIGHT).unwrap();
 
         // Fill the surface with some data.
         {
@@ -853,13 +858,11 @@ mod tests {
             }
         }
 
-        let alpha = extract_alpha(&surface, BOUNDS).unwrap();
-
-        let data = unsafe { ImageSurfaceDataShared::new(&surface).unwrap() };
-        let data_alpha = unsafe { ImageSurfaceDataShared::new(&alpha).unwrap() };
+        let surface = SharedImageSurface::new(surface).unwrap();
+        let alpha = SharedImageSurface::new(extract_alpha(&surface, BOUNDS).unwrap()).unwrap();
 
         for (x, y, p, pa) in
-            Pixels::new(data, FULL_BOUNDS).map(|(x, y, p)| (x, y, p, data_alpha.get_pixel(x, y)))
+            Pixels::new(&surface, FULL_BOUNDS).map(|(x, y, p)| (x, y, p, alpha.get_pixel(x, y)))
         {
             assert_eq!(pa.r, 0);
             assert_eq!(pa.g, 0);
diff --git a/rsvg_internals/src/filters/ffi.rs b/rsvg_internals/src/filters/ffi.rs
index 7bae7104..28d4b0eb 100644
--- a/rsvg_internals/src/filters/ffi.rs
+++ b/rsvg_internals/src/filters/ffi.rs
@@ -8,6 +8,7 @@ use drawing_ctx::RsvgDrawingCtx;
 use length::RsvgLength;
 use node::{NodeType, RsvgCNodeImpl, RsvgNode};
 use state::{ComputedValues, RsvgComputedValues};
+use surface_utils::shared_surface::SharedImageSurface;
 
 use super::context::{FilterContext, RsvgFilterContext};
 use super::{Filter, FilterError, FilterResult};
@@ -73,10 +74,24 @@ pub fn filter_render(
         }
     }
 
+    // The source surface has multiple references. We need to copy it to a new surface to have a
+    // unique reference to be able to safely access the pixel data.
+    let source_surface = cairo::ImageSurface::create(
+        cairo::Format::ARgb32,
+        source.get_width(),
+        source.get_height(),
+    ).unwrap();
+    {
+        let cr = cairo::Context::new(&source_surface);
+        cr.set_source_surface(source, 0f64, 0f64);
+        cr.paint();
+    }
+    let source_surface = SharedImageSurface::new(source_surface).unwrap();
+
     let mut filter_ctx = FilterContext::new(
         filter_node,
         node_being_filtered,
-        source.clone(),
+        source_surface,
         context,
         channelmap_arr,
     );
@@ -117,5 +132,5 @@ pub fn filter_render(
             }
         });
 
-    filter_ctx.into_output()
+    filter_ctx.into_output().into_image_surface()
 }
diff --git a/rsvg_internals/src/filters/flood.rs b/rsvg_internals/src/filters/flood.rs
index f5189d80..251bffc3 100644
--- a/rsvg_internals/src/filters/flood.rs
+++ b/rsvg_internals/src/filters/flood.rs
@@ -4,6 +4,7 @@ use cssparser;
 use handle::RsvgHandle;
 use node::{NodeResult, NodeTrait, RsvgCNodeImpl, RsvgNode};
 use property_bag::PropertyBag;
+use surface_utils::shared_surface::SharedImageSurface;
 
 use super::context::{FilterContext, FilterOutput, FilterResult};
 use super::{Filter, FilterError, Primitive};
@@ -46,8 +47,8 @@ impl Filter for Flood {
 
         let output_surface = ImageSurface::create(
             cairo::Format::ARgb32,
-            ctx.source_graphic().get_width(),
-            ctx.source_graphic().get_height(),
+            ctx.source_graphic().width(),
+            ctx.source_graphic().height(),
         ).map_err(FilterError::OutputSurfaceCreation)?;
 
         let cascaded = node.get_cascaded_values();
@@ -81,7 +82,7 @@ impl Filter for Flood {
         Ok(FilterResult {
             name: self.base.result.borrow().clone(),
             output: FilterOutput {
-                surface: output_surface,
+                surface: SharedImageSurface::new(output_surface).unwrap(),
                 bounds,
             },
         })
diff --git a/rsvg_internals/src/filters/image.rs b/rsvg_internals/src/filters/image.rs
index 7acfe9b7..4fc1e2e7 100644
--- a/rsvg_internals/src/filters/image.rs
+++ b/rsvg_internals/src/filters/image.rs
@@ -15,6 +15,7 @@ use handle::RsvgHandle;
 use node::{NodeResult, NodeTrait, RsvgCNodeImpl, RsvgNode};
 use parsers::parse;
 use property_bag::PropertyBag;
+use surface_utils::shared_surface::SharedImageSurface;
 
 use super::bounds::BoundsBuilder;
 use super::context::{FilterContext, FilterOutput, FilterResult, IRect};
@@ -58,8 +59,8 @@ impl Image {
 
         let surface = ImageSurface::create(
             cairo::Format::ARgb32,
-            ctx.source_graphic().get_width(),
-            ctx.source_graphic().get_height(),
+            ctx.source_graphic().width(),
+            ctx.source_graphic().height(),
         ).map_err(FilterError::OutputSurfaceCreation)?;
 
         drawing_ctx::get_cairo_context(ctx.drawing_context()).set_matrix(ctx.paffine());
@@ -68,15 +69,15 @@ impl Image {
             &drawable.get(),
             &ctx.get_node_being_filtered(),
             &surface,
-            f64::from(ctx.source_graphic().get_width()),
-            f64::from(ctx.source_graphic().get_height()),
+            f64::from(ctx.source_graphic().width()),
+            f64::from(ctx.source_graphic().height()),
         );
 
         // Clip the output to bounds.
         let output_surface = ImageSurface::create(
             cairo::Format::ARgb32,
-            ctx.source_graphic().get_width(),
-            ctx.source_graphic().get_height(),
+            ctx.source_graphic().width(),
+            ctx.source_graphic().height(),
         ).map_err(FilterError::OutputSurfaceCreation)?;
 
         let cr = cairo::Context::new(&output_surface);
@@ -129,8 +130,8 @@ impl Image {
 
         let output_surface = ImageSurface::create(
             cairo::Format::ARgb32,
-            ctx.source_graphic().get_width(),
-            ctx.source_graphic().get_height(),
+            ctx.source_graphic().width(),
+            ctx.source_graphic().height(),
         ).map_err(FilterError::OutputSurfaceCreation)?;
 
         // TODO: this goes through a f64->i32->f64 conversion.
@@ -228,7 +229,7 @@ impl Filter for Image {
         Ok(FilterResult {
             name: self.base.result.borrow().clone(),
             output: FilterOutput {
-                surface: output_surface,
+                surface: SharedImageSurface::new(output_surface).unwrap(),
                 bounds,
             },
         })
diff --git a/rsvg_internals/src/filters/merge.rs b/rsvg_internals/src/filters/merge.rs
index ef29f992..2b7409b6 100644
--- a/rsvg_internals/src/filters/merge.rs
+++ b/rsvg_internals/src/filters/merge.rs
@@ -7,6 +7,7 @@ use handle::RsvgHandle;
 use node::{NodeResult, NodeTrait, NodeType, RsvgCNodeImpl, RsvgNode};
 use property_bag::PropertyBag;
 use srgb::{linearize_surface, unlinearize_surface};
+use surface_utils::shared_surface::SharedImageSurface;
 
 use super::context::{FilterContext, FilterOutput, FilterResult, IRect};
 use super::input::Input;
@@ -138,12 +139,13 @@ impl Filter for Merge {
         }
 
         let output_surface = output_surface
+            .map(|surface| SharedImageSurface::new(surface).unwrap())
             .map(|surface| unlinearize_surface(&surface, bounds))
             .unwrap_or_else(|| {
                 ImageSurface::create(
                     cairo::Format::ARgb32,
-                    ctx.source_graphic().get_width(),
-                    ctx.source_graphic().get_height(),
+                    ctx.source_graphic().width(),
+                    ctx.source_graphic().height(),
                 )
             })
             .map_err(FilterError::OutputSurfaceCreation)?;
@@ -151,7 +153,7 @@ impl Filter for Merge {
         Ok(FilterResult {
             name: self.base.result.borrow().clone(),
             output: FilterOutput {
-                surface: output_surface,
+                surface: SharedImageSurface::new(output_surface).unwrap(),
                 bounds,
             },
         })
diff --git a/rsvg_internals/src/filters/mod.rs b/rsvg_internals/src/filters/mod.rs
index d7ab25a2..dab3e3c2 100644
--- a/rsvg_internals/src/filters/mod.rs
+++ b/rsvg_internals/src/filters/mod.rs
@@ -26,7 +26,6 @@ pub use self::ffi::{filter_render, RsvgFilterPrimitive};
 mod input;
 use self::input::Input;
 
-pub mod iterators;
 pub mod node;
 use self::node::NodeFilter;
 
diff --git a/rsvg_internals/src/filters/offset.rs b/rsvg_internals/src/filters/offset.rs
index 8a8b33cf..246fc7c5 100644
--- a/rsvg_internals/src/filters/offset.rs
+++ b/rsvg_internals/src/filters/offset.rs
@@ -8,6 +8,7 @@ use handle::RsvgHandle;
 use node::{NodeResult, NodeTrait, RsvgCNodeImpl, RsvgNode};
 use parsers;
 use property_bag::PropertyBag;
+use surface_utils::shared_surface::SharedImageSurface;
 use util::clamp;
 
 use super::context::{FilterContext, FilterOutput, FilterResult, IRect};
@@ -84,26 +85,28 @@ impl Filter for Offset {
 
         let output_surface = ImageSurface::create(
             cairo::Format::ARgb32,
-            ctx.source_graphic().get_width(),
-            ctx.source_graphic().get_height(),
+            ctx.source_graphic().width(),
+            ctx.source_graphic().height(),
         ).map_err(FilterError::OutputSurfaceCreation)?;
 
-        let cr = cairo::Context::new(&output_surface);
-        cr.rectangle(
-            output_bounds.x0 as f64,
-            output_bounds.y0 as f64,
-            (output_bounds.x1 - output_bounds.x0) as f64,
-            (output_bounds.y1 - output_bounds.y0) as f64,
-        );
-        cr.clip();
-
-        cr.set_source_surface(&input.surface(), ox, oy);
-        cr.paint();
+        {
+            let cr = cairo::Context::new(&output_surface);
+            cr.rectangle(
+                output_bounds.x0 as f64,
+                output_bounds.y0 as f64,
+                (output_bounds.x1 - output_bounds.x0) as f64,
+                (output_bounds.y1 - output_bounds.y0) as f64,
+            );
+            cr.clip();
+
+            input.surface().set_as_source_surface(&cr, ox, oy);
+            cr.paint();
+        }
 
         Ok(FilterResult {
             name: self.base.result.borrow().clone(),
             output: FilterOutput {
-                surface: output_surface,
+                surface: SharedImageSurface::new(output_surface).unwrap(),
                 bounds,
             },
         })
diff --git a/rsvg_internals/src/lib.rs b/rsvg_internals/src/lib.rs
index 610ce70a..62b11f58 100644
--- a/rsvg_internals/src/lib.rs
+++ b/rsvg_internals/src/lib.rs
@@ -160,6 +160,7 @@ mod srgb;
 mod state;
 mod stop;
 mod structure;
+pub mod surface_utils;
 mod text;
 mod transform;
 mod unitinterval;
diff --git a/rsvg_internals/src/srgb.rs b/rsvg_internals/src/srgb.rs
index be931955..136cafd7 100644
--- a/rsvg_internals/src/srgb.rs
+++ b/rsvg_internals/src/srgb.rs
@@ -4,7 +4,12 @@
 use cairo;
 
 use filters::context::IRect;
-use filters::iterators::{ImageSurfaceDataExt, ImageSurfaceDataShared, Pixel, Pixels};
+use surface_utils::{
+    iterators::Pixels,
+    shared_surface::SharedImageSurface,
+    ImageSurfaceDataExt,
+    Pixel,
+};
 
 /// Converts an sRGB color value to a linear sRGB color value (undoes the gamma correction).
 ///
@@ -34,29 +39,22 @@ pub fn unlinearize(c: f64) -> f64 {
 ///
 /// The returned surface is transparent everywhere except the rectangle defined by `bounds`.
 fn map_unpremultiplied_components<F>(
-    surface: &cairo::ImageSurface,
+    surface: &SharedImageSurface,
     bounds: IRect,
     f: F,
 ) -> Result<cairo::ImageSurface, cairo::Status>
 where
     F: Fn(f64) -> f64,
 {
-    let width = surface.get_width();
-    let height = surface.get_height();
-
-    assert!(bounds.x0 >= 0);
-    assert!(bounds.y0 >= 0);
-    assert!(bounds.x1 <= width);
-    assert!(bounds.y1 <= height);
-
-    let input_data = unsafe { ImageSurfaceDataShared::new_unchecked(surface)? };
+    let width = surface.width();
+    let height = surface.height();
 
     let mut output_surface = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
     let output_stride = output_surface.get_stride() as usize;
     {
         let mut output_data = output_surface.get_data().unwrap();
 
-        for (x, y, pixel) in Pixels::new(input_data, bounds) {
+        for (x, y, pixel) in Pixels::new(surface, bounds) {
             if pixel.a > 0 {
                 let alpha = f64::from(pixel.a) / 255f64;
 
@@ -87,7 +85,7 @@ where
 /// The returned surface is transparent everywhere except the rectangle defined by `bounds`.
 #[inline]
 pub fn linearize_surface(
-    surface: &cairo::ImageSurface,
+    surface: &SharedImageSurface,
     bounds: IRect,
 ) -> Result<cairo::ImageSurface, cairo::Status> {
     map_unpremultiplied_components(surface, bounds, linearize)
@@ -98,7 +96,7 @@ pub fn linearize_surface(
 /// The returned surface is transparent everywhere except the rectangle defined by `bounds`.
 #[inline]
 pub fn unlinearize_surface(
-    surface: &cairo::ImageSurface,
+    surface: &SharedImageSurface,
     bounds: IRect,
 ) -> Result<cairo::ImageSurface, cairo::Status> {
     map_unpremultiplied_components(surface, bounds, unlinearize)
diff --git a/rsvg_internals/src/surface_utils/iterators.rs b/rsvg_internals/src/surface_utils/iterators.rs
new file mode 100644
index 00000000..77cbf189
--- /dev/null
+++ b/rsvg_internals/src/surface_utils/iterators.rs
@@ -0,0 +1,142 @@
+//! Pixel iterators for `SharedImageSurface`.
+use filters::context::IRect;
+
+use super::shared_surface::SharedImageSurface;
+use super::Pixel;
+
+/// Iterator over pixels of a `SharedImageSurface`.
+#[derive(Debug, Clone, Copy)]
+pub struct Pixels<'a> {
+    surface: &'a SharedImageSurface,
+    bounds: IRect,
+    x: u32,
+    y: u32,
+}
+
+impl<'a> Pixels<'a> {
+    /// Creates an iterator over the image surface pixels, constrained within the given bounds.
+    #[inline]
+    pub fn new(surface: &'a SharedImageSurface, bounds: IRect) -> Self {
+        // Sanity checks.
+        assert!(bounds.x0 >= 0);
+        assert!(bounds.x0 <= surface.width());
+        assert!(bounds.x1 >= bounds.x0);
+        assert!(bounds.x1 <= surface.width());
+        assert!(bounds.y0 >= 0);
+        assert!(bounds.y0 <= surface.height());
+        assert!(bounds.y1 >= bounds.y0);
+        assert!(bounds.y1 <= surface.height());
+
+        Self {
+            surface,
+            bounds,
+            x: bounds.x0 as u32,
+            y: bounds.y0 as u32,
+        }
+    }
+}
+
+impl<'a> Iterator for Pixels<'a> {
+    type Item = (u32, u32, Pixel);
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        // This means we hit the end on the last iteration.
+        if self.x == self.bounds.x1 as u32 || self.y == self.bounds.y1 as u32 {
+            return None;
+        }
+
+        let rv = Some((self.x, self.y, self.surface.get_pixel(self.x, self.y)));
+
+        if self.x + 1 == self.bounds.x1 as u32 {
+            self.x = self.bounds.x0 as u32;
+            self.y += 1;
+        } else {
+            self.x += 1;
+        }
+
+        rv
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use cairo::{self, ImageSurface};
+
+    #[test]
+    fn pixels_count() {
+        const WIDTH: i32 = 32;
+        const HEIGHT: i32 = 64;
+
+        let surface = SharedImageSurface::new(
+            ImageSurface::create(cairo::Format::ARgb32, WIDTH, HEIGHT).unwrap(),
+        ).unwrap();
+
+        // Full image.
+        let bounds = IRect {
+            x0: 0,
+            y0: 0,
+            x1: WIDTH,
+            y1: HEIGHT,
+        };
+        assert_eq!(
+            Pixels::new(&surface, bounds).count(),
+            (WIDTH * HEIGHT) as usize
+        );
+
+        // 1-wide column.
+        let bounds = IRect {
+            x0: 0,
+            y0: 0,
+            x1: 1,
+            y1: HEIGHT,
+        };
+        assert_eq!(Pixels::new(&surface, bounds).count(), HEIGHT as usize);
+
+        // 1-tall row.
+        let bounds = IRect {
+            x0: 0,
+            y0: 0,
+            x1: WIDTH,
+            y1: 1,
+        };
+        assert_eq!(Pixels::new(&surface, bounds).count(), WIDTH as usize);
+
+        // 1×1.
+        let bounds = IRect {
+            x0: 0,
+            y0: 0,
+            x1: 1,
+            y1: 1,
+        };
+        assert_eq!(Pixels::new(&surface, bounds).count(), 1);
+
+        // Nothing (x0 == x1).
+        let bounds = IRect {
+            x0: 0,
+            y0: 0,
+            x1: 0,
+            y1: HEIGHT,
+        };
+        assert_eq!(Pixels::new(&surface, bounds).count(), 0);
+
+        // Nothing (y0 == y1).
+        let bounds = IRect {
+            x0: 0,
+            y0: 0,
+            x1: WIDTH,
+            y1: 0,
+        };
+        assert_eq!(Pixels::new(&surface, bounds).count(), 0);
+
+        // Nothing (x0 == x1, y0 == y1).
+        let bounds = IRect {
+            x0: 0,
+            y0: 0,
+            x1: 0,
+            y1: 0,
+        };
+        assert_eq!(Pixels::new(&surface, bounds).count(), 0);
+    }
+}
diff --git a/rsvg_internals/src/surface_utils/mod.rs b/rsvg_internals/src/surface_utils/mod.rs
new file mode 100644
index 00000000..b07c26aa
--- /dev/null
+++ b/rsvg_internals/src/surface_utils/mod.rs
@@ -0,0 +1,33 @@
+//! Various utilities for working with Cairo image surfaces.
+use std::ops::DerefMut;
+
+use cairo;
+
+pub mod iterators;
+pub mod shared_surface;
+
+/// A pixel consisting of R, G, B and A values.
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub struct Pixel {
+    pub r: u8,
+    pub g: u8,
+    pub b: u8,
+    pub a: u8,
+}
+
+/// Extension methods for `cairo::ImageSurfaceData`.
+pub trait ImageSurfaceDataExt: DerefMut<Target = [u8]> {
+    /// Sets the pixel at the given coordinates.
+    #[inline]
+    fn set_pixel(&mut self, stride: usize, pixel: Pixel, x: u32, y: u32) {
+        let value = ((pixel.a as u32) << 24)
+            | ((pixel.r as u32) << 16)
+            | ((pixel.g as u32) << 8)
+            | (pixel.b as u32);
+        unsafe {
+            *(&mut self[y as usize * stride + x as usize * 4] as *mut u8 as *mut u32) = value;
+        }
+    }
+}
+
+impl<'a> ImageSurfaceDataExt for cairo::ImageSurfaceData<'a> {}
diff --git a/rsvg_internals/src/surface_utils/shared_surface.rs 
b/rsvg_internals/src/surface_utils/shared_surface.rs
new file mode 100644
index 00000000..55fe0f6a
--- /dev/null
+++ b/rsvg_internals/src/surface_utils/shared_surface.rs
@@ -0,0 +1,139 @@
+//! Shared access to Cairo image surfaces.
+use std::ptr::NonNull;
+
+use cairo::prelude::SurfaceExt;
+use cairo::{self, ImageSurface};
+use cairo_sys;
+use glib::translate::{Stash, ToGlibPtr};
+
+use super::Pixel;
+
+/// 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
+/// data, since a read-only borrowed reference can still be cloned and then modified (for example,
+/// via a `Context`). We can't simply use `ImageSurface::get_data()` because in the filter code we
+/// have surfaces referenced from multiple places and it would probably add more complexity to
+/// remove that and start passing around references.
+///
+/// This wrapper asserts the uniqueness of its image surface and doesn't permit modifying it.
+///
+/// Note: originally I had an idea of using `Rc<RefCell<ImageSurface>>` here which allows to create
+/// both read-only and unique read-write accessors safely, however then I realized a read-write
+/// accessor isn't of much use if it can't expose a Cairo context interface. Cairo contexts have
+/// the very same issue that they can be cloned from a read-only reference and break all safety
+/// constraints in this way. Thus the only safe way of exposing a Cairo context seemed to be to
+/// manually add all Cairo context methods on the accessor forwarding to the underlying Cairo
+/// context (without exposing the context itself to prevent cloning), which would result in too
+/// much code. Unless it's absolutely required, I'd like to avoid that.
+///
+/// Having just read-only access simplifies things further dropping the need for `Rc<RefCell<>>`
+/// altogether.
+#[derive(Debug, Clone)]
+pub struct SharedImageSurface {
+    surface: ImageSurface,
+
+    data_ptr: NonNull<u8>, // *const.
+    width: i32,
+    height: i32,
+    stride: isize,
+}
+
+impl SharedImageSurface {
+    /// Creates a `SharedImageSurface` from a unique `ImageSurface`.
+    ///
+    /// # Panics
+    /// Panics if the `ImageSurface` is not unique, that is, its reference count isn't 1.
+    #[inline]
+    pub fn new(surface: ImageSurface) -> Result<Self, cairo::Status> {
+        let reference_count =
+            unsafe { cairo_sys::cairo_surface_get_reference_count(surface.to_raw_none()) };
+        assert_eq!(reference_count, 1);
+
+        surface.flush();
+        if surface.status() != cairo::Status::Success {
+            return Err(surface.status());
+        }
+
+        let data_ptr = NonNull::new(unsafe {
+            cairo_sys::cairo_image_surface_get_data(surface.to_raw_none())
+        }).unwrap();
+
+        let width = surface.get_width();
+        let height = surface.get_height();
+        let stride = surface.get_stride() as isize;
+
+        Ok(Self {
+            surface,
+            data_ptr,
+            width,
+            height,
+            stride,
+        })
+    }
+
+    /// Converts this `SharedImageSurface` back into a Cairo image surface.
+    ///
+    /// # Panics
+    /// Panics if the underlying Cairo image surface is not unique, that is, there are other
+    /// instances of `SharedImageSurface` pointing at the same Cairo image surface.
+    #[inline]
+    pub fn into_image_surface(self) -> ImageSurface {
+        let reference_count =
+            unsafe { cairo_sys::cairo_surface_get_reference_count(self.surface.to_raw_none()) };
+        assert_eq!(reference_count, 1);
+
+        self.surface
+    }
+
+    /// Returns the surface width.
+    #[inline]
+    pub fn width(&self) -> i32 {
+        self.width
+    }
+
+    /// Returns the surface height.
+    #[inline]
+    pub fn height(&self) -> i32 {
+        self.height
+    }
+
+    /// Retrieves the pixel value at the given coordinates.
+    // Making this just #[inline] AND making Pixels::next() #[inline] prevents this from being
+    // inlined in the benchmarks leading to significantly worse benchmark results. Making this
+    // #[inline(always)] and making Pixels::new() #[inline] leads to good benchmark results.
+    #[inline(always)]
+    pub fn get_pixel(&self, x: u32, y: u32) -> Pixel {
+        assert!(x < self.width as u32);
+        assert!(y < self.height as u32);
+
+        let value = unsafe {
+            *(self
+                .data_ptr
+                .as_ptr()
+                .offset(y as isize * self.stride + x as isize * 4) as *const u32)
+        };
+
+        Pixel {
+            r: ((value >> 16) & 0xFF) as u8,
+            g: ((value >> 8) & 0xFF) as u8,
+            b: (value & 0xFF) as u8,
+            a: ((value >> 24) & 0xFF) as u8,
+        }
+    }
+
+    /// Calls `set_source_surface()` on the given Cairo context.
+    #[inline]
+    pub fn set_as_source_surface(&self, cr: &cairo::Context, x: f64, y: f64) {
+        cr.set_source_surface(&self.surface, x, y);
+    }
+
+    /// Returns a raw pointer to the underlying surface.
+    ///
+    /// # Safety
+    /// The returned pointer must not be used to modify the surface.
+    #[inline]
+    pub unsafe fn to_glib_none(&self) -> Stash<*mut cairo_sys::cairo_surface_t, ImageSurface> {
+        self.surface.to_glib_none()
+    }
+}


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