[librsvg: 1/2] port RsvgPaintServer to Rust



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]