[librsvg: 1/2] port RsvgPaintServer to Rust
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg: 1/2] port RsvgPaintServer to Rust
- Date: Thu, 8 Feb 2018 21:09:54 +0000 (UTC)
commit 2e714d43cda40e0a7bf9fb933ba0dade13b33165
Author: Dmitry Kontsevoy <dmitry kontsevoy gmail com>
Date: Wed Feb 7 17:16:16 2018 +0300
port RsvgPaintServer to Rust
closes #192
Makefile.am | 1 -
rsvg-cairo-draw.c | 80 +-----------
rsvg-paint-server.c | 209 ------------------------------
rsvg-paint-server.h | 39 +-----
rust/src/lib.rs | 7 +
rust/src/paint_server.rs | 324 ++++++++++++++++++++++++++++++++++++++++++++++-
6 files changed, 341 insertions(+), 319 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index db10410..e7a7ae7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -46,7 +46,6 @@ librsvg_@RSVG_API_MAJOR_VERSION@_la_SOURCES = \
rsvg-io.h \
rsvg-marker.h \
rsvg-mask.h \
- rsvg-paint-server.c \
rsvg-paint-server.h \
rsvg-path-builder.h \
rsvg-private.h \
diff --git a/rsvg-cairo-draw.c b/rsvg-cairo-draw.c
index 24a6ae9..9555e9a 100644
--- a/rsvg-cairo-draw.c
+++ b/rsvg-cairo-draw.c
@@ -45,79 +45,13 @@
#include <pango/pangofc-fontmap.h>
#endif
-static void
-_set_source_rsvg_solid_color (RsvgDrawingCtx * ctx,
- RsvgSolidColor * color, guint8 opacity, guint32 current_color)
-{
- RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
- cairo_t *cr = render->cr;
- guint32 argb = color->argb;
- double r, g, b, a;
-
- if (color->currentcolor)
- argb = current_color;
-
- r = ((argb >> 16) & 0xff) / 255.0;
- g = ((argb >> 8) & 0xff) / 255.0;
- b = ((argb >> 0) & 0xff) / 255.0;
- a = (argb >> 24) / 255.0 * (opacity / 255.0);
-
- cairo_set_source_rgba (cr, r, g, b, a);
-}
-
-/* note: _set_source_rsvg_paint_server does not change cairo's CTM */
-static gboolean
-_set_source_rsvg_paint_server (RsvgDrawingCtx * ctx,
- RsvgPaintServer * ps,
- guint8 opacity,
- RsvgBbox bbox,
- guint32 current_color)
-{
- RsvgNode *node;
- gboolean had_paint_server;
- gboolean use_alternate;
-
- had_paint_server = FALSE;
-
- switch (ps->type) {
- case RSVG_PAINT_SERVER_IRI:
- use_alternate = FALSE;
-
- node = rsvg_drawing_ctx_acquire_node (ctx, ps->core.iri->iri_str);
- if (node == NULL) {
- use_alternate = TRUE;
- } else if (rsvg_node_get_type (node) == RSVG_NODE_TYPE_LINEAR_GRADIENT
- || rsvg_node_get_type (node) == RSVG_NODE_TYPE_RADIAL_GRADIENT) {
- if (gradient_resolve_fallbacks_and_set_pattern (node, ctx, opacity, bbox)) {
- had_paint_server = TRUE;
- } else {
- use_alternate = TRUE;
- }
- } else if (rsvg_node_get_type (node) == RSVG_NODE_TYPE_PATTERN) {
- if (pattern_resolve_fallbacks_and_set_pattern (node, ctx, bbox)) {
- had_paint_server = TRUE;
- } else {
- use_alternate = TRUE;
- }
- }
-
- if (use_alternate) {
- if (ps->core.iri->has_alternate) {
- _set_source_rsvg_solid_color (ctx, &ps->core.iri->alternate, opacity, current_color);
- had_paint_server = TRUE;
- }
- }
-
- rsvg_drawing_ctx_release_node (ctx, node);
- break;
- case RSVG_PAINT_SERVER_SOLID:
- _set_source_rsvg_solid_color (ctx, ps->core.color, opacity, current_color);
- had_paint_server = TRUE;
- break;
- }
-
- return had_paint_server;
-}
+/* Implemented in rust/src/paint_server.rs */
+G_GNUC_INTERNAL
+gboolean _set_source_rsvg_paint_server (RsvgDrawingCtx * ctx,
+ RsvgPaintServer * ps,
+ guint8 opacity,
+ RsvgBbox bbox,
+ guint32 current_color);
static void
_set_rsvg_affine (RsvgCairoRender * render, cairo_matrix_t *affine)
diff --git a/rsvg-paint-server.h b/rsvg-paint-server.h
index 54c664b..b2ce86c 100644
--- a/rsvg-paint-server.h
+++ b/rsvg-paint-server.h
@@ -32,8 +32,6 @@
G_BEGIN_DECLS
-typedef struct _RsvgSolidColor RsvgSolidColor;
-
typedef struct _RsvgPaintServer RsvgPaintServer;
/* Implemented in rust/src/gradient.rs */
@@ -60,43 +58,16 @@ gboolean pattern_resolve_fallbacks_and_set_pattern (RsvgNode *node,
RsvgDrawingCtx *draw_ctx,
RsvgBbox bbox);
-struct _RsvgSolidColor {
- gboolean currentcolor;
- guint32 argb;
-};
-
-typedef struct _RsvgSolidColor RsvgPaintServerColor;
-typedef enum _RsvgPaintServerType RsvgPaintServerType;
-typedef union _RsvgPaintServerCore RsvgPaintServerCore;
-typedef struct _RsvgPaintServerIri RsvgPaintServerIri;
-
-struct _RsvgPaintServerIri {
- char *iri_str;
- gboolean has_alternate;
- RsvgSolidColor alternate;
-};
-
-union _RsvgPaintServerCore {
- RsvgSolidColor *color;
- RsvgPaintServerIri *iri;
-};
-
-enum _RsvgPaintServerType {
- RSVG_PAINT_SERVER_SOLID,
- RSVG_PAINT_SERVER_IRI
-};
-
-struct _RsvgPaintServer {
- int refcnt;
- RsvgPaintServerType type;
- RsvgPaintServerCore core;
-};
-
/* Create a new paint server based on a specification string. */
+/* Implemented in rust/src/paint_server.rs */
G_GNUC_INTERNAL
RsvgPaintServer *rsvg_paint_server_parse (gboolean *inherit, const char *str);
+
+/* Implemented in rust/src/paint_server.rs */
G_GNUC_INTERNAL
void rsvg_paint_server_ref (RsvgPaintServer * ps);
+
+/* Implemented in rust/src/paint_server.rs */
G_GNUC_INTERNAL
void rsvg_paint_server_unref (RsvgPaintServer * ps);
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index 453dac8..95605b0 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -105,6 +105,13 @@ pub use opacity::{
rsvg_css_parse_opacity
};
+pub use paint_server::{
+ rsvg_paint_server_parse,
+ rsvg_paint_server_ref,
+ rsvg_paint_server_unref,
+ _set_source_rsvg_paint_server
+};
+
pub use parsers::{
rsvg_css_parse_number_list,
rsvg_css_parse_number_optional_number
diff --git a/rust/src/paint_server.rs b/rust/src/paint_server.rs
index 85eb948..b83003b 100644
--- a/rust/src/paint_server.rs
+++ b/rust/src/paint_server.rs
@@ -1,8 +1,21 @@
use cairo;
+use cssparser;
+use glib_sys;
+use glib::translate::*;
+use libc;
+use std::rc::Rc;
+use std::ptr;
+
+use bbox::RsvgBbox;
+use color::Color;
+use drawing_ctx;
use error::*;
-use parsers::Parse;
-use parsers::ParseError;
+use gradient::gradient_resolve_fallbacks_and_set_pattern;
+use node::NodeType;
+use parsers::{Parse, ParseError};
+use pattern::pattern_resolve_fallbacks_and_set_pattern;
+use util::utf8_cstr;
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct PaintServerSpread (pub cairo::enums::Extend);
@@ -27,6 +40,228 @@ impl Default for PaintServerSpread {
}
}
+#[derive(Debug, Clone, PartialEq)]
+pub enum PaintServer {
+ Iri {
+ iri: String,
+ alternate: Option<Color>,
+ },
+ SolidColor(Color),
+}
+
+impl PaintServer {
+ pub fn parse_input<'i, 't>(
+ input: &mut cssparser::Parser<'i, 't>,
+ ) -> Result<Self, AttributeError> {
+ if let Ok(url) = input.try(|i| i.expect_url()) {
+ Ok(PaintServer::Iri {
+ iri: String::from(url.as_ref()),
+ alternate: PaintServer::parse_fallback(input),
+ })
+ } else {
+ PaintServer::parse_color(input).map(|color| PaintServer::SolidColor(color))
+ }
+ }
+
+ fn parse_color<'i, 't>(input: &mut cssparser::Parser<'i, 't>) -> Result<Color, AttributeError> {
+ if input.try(|i| i.expect_ident_matching("inherit")).is_ok() {
+ Ok(Color::Inherit)
+ } else if input.try(|i| i.expect_ident_matching("currentColor")).is_ok() {
+ Ok(Color::CurrentColor)
+ } else {
+ input
+ .try(|i| cssparser::Color::parse(i))
+ .map(|c| Color::from(c))
+ .map_err(|e| AttributeError::from(e))
+ }
+ }
+
+ fn parse_fallback<'i, 't>(input: &mut cssparser::Parser<'i, 't>) -> Option<Color> {
+ if input.try(|i| i.expect_ident_matching("none")).is_ok() {
+ None
+ } else if input.try(|i| i.expect_ident_matching("currentColor")).is_ok() {
+ Some(Color::CurrentColor)
+ } else {
+ input.try(|i| cssparser::Color::parse(i)).ok().map(|i| {
+ Color::from(i)
+ })
+ }
+ }
+}
+
+impl Parse for PaintServer {
+ type Data = ();
+ type Err = AttributeError;
+
+ fn parse(s: &str, _: ()) -> Result<PaintServer, AttributeError> {
+ let mut input = cssparser::ParserInput::new(s);
+ PaintServer::parse_input(&mut cssparser::Parser::new(&mut input))
+ }
+}
+
+fn _set_source_rsvg_solid_color(
+ ctx: *mut drawing_ctx::RsvgDrawingCtx,
+ color: &Color,
+ opacity: u8,
+ current_color: u32,
+) {
+ let rgba_color = match *color {
+ Color::RGBA(rgba) => Some(rgba),
+ Color::CurrentColor => {
+ if let Color::RGBA(rgba) = Color::from(current_color) {
+ Some(rgba)
+ } else {
+ None
+ }
+ },
+
+ _ => None
+ };
+
+ if let Some(rgba) = rgba_color {
+ drawing_ctx::get_cairo_context(ctx).set_source_rgba(
+ f64::from(rgba.red_f32()),
+ f64::from(rgba.green_f32()),
+ f64::from(rgba.blue_f32()),
+ f64::from(rgba.alpha_f32()) * (f64::from(opacity) / 255.0)
+ );
+ }
+}
+
+/// Parses the paint specification, creating a new paint server object.
+/// Return value: (nullable): The newly created paint server, or NULL on error.
+///
+/// # Arguments
+///
+/// * `str` - The SVG paint specification string to parse.
+#[no_mangle]
+pub extern "C" fn rsvg_paint_server_parse(
+ inherit: *mut glib_sys::gboolean,
+ str: *const libc::c_char,
+) -> *const PaintServer {
+
+ if !inherit.is_null() {
+ unsafe {
+ *inherit = true.to_glib();
+ }
+ }
+
+ let mut paint_server = PaintServer::parse(unsafe { utf8_cstr(str) }, ());
+
+ if let Ok(PaintServer::SolidColor(ref mut color)) = paint_server {
+ if *color == Color::Inherit {
+ // FIXME: this is incorrect; we should inherit the paint server
+ if !inherit.is_null() {
+ unsafe {
+ *inherit = false.to_glib();
+ }
+ }
+
+ *color = Color::RGBA(
+ cssparser::RGBA{red: 0, green: 0, blue: 0, alpha: 255}
+ );
+ }
+ }
+
+ match paint_server {
+ Ok(m) => Rc::into_raw(Rc::new(m)),
+ Err(_) => ptr::null_mut(),
+ }
+}
+
+/// Increase references counter of `PaintServer`.
+///
+/// # Arguments
+///
+/// * `paint_server` - must be constructed with `rsvg_paint_server_parse`.
+#[no_mangle]
+pub extern "C" fn rsvg_paint_server_ref(paint_server: *const PaintServer) {
+ if paint_server.is_null() {
+ return;
+ }
+
+ let server: Rc<PaintServer> = unsafe { Rc::from_raw(paint_server) };
+
+ // forget about references
+ Rc::into_raw(server.clone());
+ Rc::into_raw(server);
+}
+
+/// Decrease references counter of `PaintServer`.
+///
+/// # Arguments
+///
+/// * `paint_server` - must be constructed with `rsvg_paint_server_parse`.
+#[no_mangle]
+pub extern "C" fn rsvg_paint_server_unref(paint_server: *const PaintServer) {
+ if paint_server.is_null() {
+ return;
+ }
+
+ // drop reference
+ unsafe { Rc::from_raw(paint_server) };
+}
+
+#[no_mangle]
+pub extern "C" fn _set_source_rsvg_paint_server(
+ c_ctx: *mut drawing_ctx::RsvgDrawingCtx,
+ c_ps: *const PaintServer,
+ opacity: u8,
+ c_bbox: RsvgBbox,
+ current_color: u32,
+) -> glib_sys::gboolean {
+ assert!(!c_ctx.is_null());
+ assert!(!c_ps.is_null());
+
+ let ps = unsafe { &*c_ps };
+ let mut had_paint_server = false;
+
+ match *ps {
+ PaintServer::Iri {
+ ref iri,
+ ref alternate,
+ } => {
+ let node_ptr = drawing_ctx::acquire_node(c_ctx, iri.as_str());
+
+ if !node_ptr.is_null() {
+ let node = unsafe { &*node_ptr };
+
+ if node.get_type() == NodeType::LinearGradient ||
+ node.get_type() == NodeType::RadialGradient
+ {
+ had_paint_server = gradient_resolve_fallbacks_and_set_pattern(
+ node_ptr,
+ c_ctx,
+ opacity,
+ c_bbox,
+ ).to_bool();
+ } else if node.get_type() == NodeType::Pattern {
+ had_paint_server = pattern_resolve_fallbacks_and_set_pattern(node_ptr, c_ctx,
c_bbox).to_bool();
+ }
+ }
+
+ if !had_paint_server && alternate.is_some() {
+ _set_source_rsvg_solid_color(
+ c_ctx,
+ alternate.as_ref().unwrap(),
+ opacity,
+ current_color,
+ );
+ had_paint_server = true;
+ }
+
+ drawing_ctx::release_node(c_ctx, node_ptr);
+ },
+
+ PaintServer::SolidColor(color) => {
+ _set_source_rsvg_solid_color(c_ctx, &color, opacity, current_color);
+ had_paint_server = true;
+ }
+ };
+
+ had_paint_server.to_glib()
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -44,4 +279,89 @@ mod tests {
assert! (PaintServerSpread::parse ("foobar", ()).is_err ());
}
+
+ #[test]
+ fn parses_solid_color() {
+ assert_eq!(
+ PaintServer::parse("rgb(255, 128, 64, 0.5)", ()),
+ Ok(PaintServer::SolidColor(Color::from(0x80ff8040)))
+ );
+
+ assert_eq!(
+ PaintServer::parse("inherit", ()),
+ Ok(PaintServer::SolidColor(Color::Inherit))
+ );
+
+ assert_eq!(
+ PaintServer::parse("currentColor", ()),
+ Ok(PaintServer::SolidColor(Color::CurrentColor))
+ );
+ }
+
+ #[test]
+ fn parses_iri() {
+ assert_eq!(
+ PaintServer::parse("url(#link)", ()),
+ Ok(PaintServer::Iri {
+ iri: "#link".to_string(),
+ alternate: None,
+ })
+ );
+
+ assert_eq!(
+ PaintServer::parse("url(#link) none", ()),
+ Ok(PaintServer::Iri {
+ iri: "#link".to_string(),
+ alternate: None,
+ })
+ );
+
+ assert_eq!(
+ PaintServer::parse("url(#link) #ff8040", ()),
+ Ok(PaintServer::Iri {
+ iri: "#link".to_string(),
+ alternate: Some(Color::from(0xffff8040)),
+ })
+ );
+
+ assert_eq!(
+ PaintServer::parse("url(#link) rgb(255, 128, 64, 0.5)", ()),
+ Ok(PaintServer::Iri {
+ iri: "#link".to_string(),
+ alternate: Some(Color::from(0x80ff8040)),
+ })
+ );
+
+ assert_eq!(
+ PaintServer::parse("url(#link) currentColor", ()),
+ Ok(PaintServer::Iri {
+ iri: "#link".to_string(),
+ alternate: Some(Color::CurrentColor),
+ })
+ );
+
+ assert_eq!(
+ PaintServer::parse("url(#link) inherit", ()),
+ Ok(PaintServer::Iri {
+ iri: "#link".to_string(),
+ alternate: None,
+ })
+ );
+ }
+
+ #[test]
+ fn paint_server_refs_and_unrefs() {
+ let rc = Rc::new(PaintServer::parse("#ffffff", ()).unwrap());
+ let weak = Rc::downgrade(&rc);
+ let ps = Rc::into_raw(rc);
+
+ rsvg_paint_server_ref(ps);
+ assert!(weak.upgrade().is_some());
+
+ rsvg_paint_server_unref(ps);
+ assert!(weak.upgrade().is_some());
+
+ rsvg_paint_server_unref(ps);
+ assert!(weak.upgrade().is_none());
+ }
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]