[librsvg/rustification] Start a branch to port bits of librsvg to Rust
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg/rustification] Start a branch to port bits of librsvg to Rust
- Date: Tue, 25 Oct 2016 15:30:43 +0000 (UTC)
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]