[librsvg] Change ImageSurfaceDataShared to a safe version
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg] Change ImageSurfaceDataShared to a safe version
- Date: Fri, 22 Jun 2018 14:10:23 +0000 (UTC)
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(¶ms_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]