[librsvg/rustification] Start a branch to port bits of librsvg to Rust



commit f27a8c908ac23f5b7560d2279acec44e41b91a25
Author: Federico Mena Quintero <federico gnome org>
Date:   Tue Oct 25 10:21:34 2016 -0500

    Start a branch to port bits of librsvg to Rust
    
    I want parts of librsvg to be rewritten in Rust so that the library's
    internals are written in a safe language.  It should be possible to
    preserve the public C API, while internally parts of librsvg are
    written in Rust.
    
    I don't know how to integrate the Rust-built library into the C one yet,
    so this is a work in progress.
    
    To begin, we have path_to_segments() from rsvg-marker.c.  The rest of
    the marker-drawing machinery is still missing; and the ported version
    of that function needs some tests - it doesn't do the right thing with a
    closepath operator.

 rust/Cargo.toml    |   10 ++
 rust/src/marker.rs |  237 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 247 insertions(+), 0 deletions(-)
---
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
new file mode 100644
index 0000000..e308994
--- /dev/null
+++ b/rust/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "marker"
+version = "0.0.1"
+authors = ["Federico Mena Quintero <federico gnome org>"]
+
+[dependencies]
+cairo-rs = "^0"
+
+[[bin]]
+name = "marker"
diff --git a/rust/src/marker.rs b/rust/src/marker.rs
new file mode 100644
index 0000000..c373733
--- /dev/null
+++ b/rust/src/marker.rs
@@ -0,0 +1,237 @@
+extern crate cairo;
+
+struct Segment {
+    is_degenerate: bool, /* If true, only (p1x, p1y) are valid.  If false, all are valid */
+    p1x: f64, p1y: f64,
+    p2x: f64, p2y: f64,
+    p3x: f64, p3y: f64,
+    p4x: f64, p4y: f64
+}
+
+enum SegmentState {
+    Start,
+    End
+}
+
+/* This converts a cairo_path_t into a list of curveto-like segments.  Each segment can be:
+ *
+ * 1. segment.is_degenerate = TRUE => the segment is actually a single point (segment.p1x, segment.p1y)
+ *
+ * 2. segment.is_degenerate = FALSE => either a lineto or a curveto (or the effective lineto that results 
from a closepath).
+ *    We have the following points:
+ *       P1 = (p1x, p1y)
+ *       P2 = (p2x, p2y)
+ *       P3 = (p3x, p3y)
+ *       P4 = (p4x, p4y)
+ *
+ *    The start and end points are P1 and P4, respectively.
+ *    The tangent at the start point is given by the vector (P2 - P1).
+ *    The tangent at the end point is given by the vector (P4 - P3).
+ *    The tangents also work if the segment refers to a lineto (they will both just point in the same 
direction).
+ */
+
+const EPSILON: f64 = 1e-10;
+
+fn double_equals (a: f64, b: f64) -> bool {
+    (a - b).abs () < EPSILON
+}
+
+fn path_to_segments (path: cairo::Path) -> Vec<Segment> {
+    let mut last_x: f64;
+    let mut last_y: f64;
+    let mut cur_x: f64;
+    let mut cur_y: f64;
+    let mut subpath_start_x: f64;
+    let mut subpath_start_y: f64;
+    let mut has_first_segment : bool;
+    let mut segment_num : usize;
+    let mut segments: Vec<Segment>;
+    let mut state: SegmentState;
+
+    cur_x = 0.0;
+    cur_y = 0.0;
+    subpath_start_x = 0.0;
+    subpath_start_y = 0.0;
+
+    has_first_segment = false;
+    segment_num = 0;
+    segments = Vec::new ();
+    state = SegmentState::End;
+
+    for cairo_segment in path.iter () {
+        last_x = cur_x;
+        last_y = cur_y;
+
+        match cairo_segment {
+            cairo::PathSegment::MoveTo ((x, y)) => {
+                if has_first_segment {
+                    segment_num += 1;
+                } else {
+                    has_first_segment = true;
+                }
+
+                cur_x = x;
+                cur_y = y;
+
+                subpath_start_x = cur_x;
+                subpath_start_y = cur_y;
+
+                let seg = Segment {
+                    is_degenerate: true,
+                    p1x: x,
+                    p1y: y,
+                    p2x: 0.0, p2y: 0.0, p3x: 0.0, p3y: 0.0, p4x: 0.0, p4y: 0.0 // these are set in the next 
iteration
+                };
+
+                segments.push (seg);
+
+                state = SegmentState::Start;
+            },
+
+            cairo::PathSegment::LineTo ((x, y)) => {
+                cur_x = x;
+                cur_y = y;
+
+                match state {
+                    SegmentState::Start => {
+                        segments[segment_num].is_degenerate = false;
+                        state = SegmentState::End;
+                    },
+
+                    SegmentState::End => {
+                        segment_num += 1;
+
+                        let seg = Segment {
+                            is_degenerate: false,
+                            p1x: last_x,
+                            p1y: last_y,
+                            p2x: 0.0, p2y: 0.0, p3x: 0.0, p3y: 0.0, p4x: 0.0, p4y: 0.0  // these are set 
below
+                        };
+
+                        segments.push (seg);
+                    }
+                }
+
+                segments[segment_num].p2x = cur_x;
+                segments[segment_num].p2y = cur_y;
+
+                segments[segment_num].p3x = last_x;
+                segments[segment_num].p3y = last_y;
+
+                segments[segment_num].p4x = cur_x;
+                segments[segment_num].p4y = cur_y;
+            },
+
+            cairo::PathSegment::CurveTo ((p2x, p2y), (p3x, p3y), (p4x, p4y)) => {
+                cur_x = p4x;
+                cur_y = p4y;
+
+                match state {
+                    SegmentState::Start => {
+                        segments[segment_num as usize].is_degenerate = false;
+                        state = SegmentState::End;
+                    },
+
+                    SegmentState::End => {
+                        segment_num += 1;
+
+                        let seg = Segment {
+                            is_degenerate: false,
+                            p1x: last_x,
+                            p1y: last_y,
+                            p2x: 0.0, p2y: 0.0, p3x: 0.0, p3y: 0.0, p4x: 0.0, p4y: 0.0 // these are set below
+                        };
+
+                        segments.push (seg);
+                    }
+                }
+
+                segments[segment_num].p2x = p2x;
+                segments[segment_num].p2y = p2y;
+
+                segments[segment_num].p3x = p3x;
+                segments[segment_num].p3y = p3y;
+
+                segments[segment_num].p4x = cur_x;
+                segments[segment_num].p4y = cur_y;
+
+                /* Fix the tangents for when the middle control points coincide with their respective 
endpoints */
+
+                if double_equals (segments[segment_num].p2x, segments[segment_num].p1x)
+                    && double_equals (segments[segment_num].p2y, segments[segment_num].p1y) {
+                    segments[segment_num].p2x = segments[segment_num].p3x;
+                    segments[segment_num].p2y = segments[segment_num].p3y;
+                }
+
+                if double_equals (segments[segment_num].p3x, segments[segment_num].p4x)
+                    && double_equals (segments[segment_num].p3y, segments[segment_num].p4y) {
+                    segments[segment_num].p3x = segments[segment_num].p2x;
+                    segments[segment_num].p3y = segments[segment_num].p2y;
+                }
+            }
+
+            cairo::PathSegment::ClosePath => {
+                cur_x = subpath_start_x;
+                cur_y = subpath_start_y;
+
+                match state {
+                    SegmentState::Start => {
+                        segments[segment_num].is_degenerate = false;
+
+                        segments[segment_num].p2x = cur_x;
+                        segments[segment_num].p2y = cur_y;
+
+                        segments[segment_num].p3x = last_x;
+                        segments[segment_num].p3y = last_y;
+
+                        segments[segment_num].p4x = cur_x;
+                        segments[segment_num].p4y = cur_y;
+
+                        state = SegmentState::End;
+                    },
+
+                    SegmentState::End => {
+                        /* nothing; closepath after moveto (or a single lone closepath) does nothing */
+                    }
+                }
+            }
+        }
+    }
+
+    segments
+}
+
+//#[cfg(test)]
+//mod tests {
+//    #[test]
+//    fn it_works() {
+fn main () {
+    let surf = cairo::ImageSurface::create (cairo::Format::Rgb24, 256, 256);
+
+    let cr = cairo::Context::new (&surf);
+
+    cr.move_to (10.0, 10.0);
+    cr.curve_to (20.0, 20.0, 30.0, 20.0, 40.0, 20.0);
+    cr.close_path ();
+//    cr.move_to (30.0, 30.0);
+
+    let path = cr.copy_path ();
+
+    let segments = path_to_segments (path);
+
+    let mut i : usize = 0;
+
+    for s in segments {
+        println! ("segment {}: is_degenerate={}, ({}, {}) ({}, {}) ({}, {}) ({}, {})",
+                  i,
+                  s.is_degenerate,
+                  s.p1x, s.p1y,
+                  s.p2x, s.p2y,
+                  s.p3x, s.p3y,
+                  s.p4x, s.p4y);
+        i += 1;
+    }
+}
+
+//    }
+//}


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