Commit 17f5c30
Render filled paths without parsing SVG This commit makes the stroke code five times faster.
Changed files (7)
modifiedbirdfont/GtkWindow.valaaddedlibbirdfont/Color.valamodifiedlibbirdfont/Glyph.valamodifiedlibbirdfont/GlyphRange.valamodifiedlibbirdfont/Path.valamodifiedlibbirdfont/StrokeTool.valamodifiedlibbirdfont/Test.vala
Diff
@@ -352,7 +352,7 @@ public class GtkWindow : Gtk.Window, NativeWindow { public ColorWindow (ColorTool color_tool) { color_selection.color_changed.connect (() => {- Color c;+ Gdk.Color c; color_selection.get_current_color (out c); color_tool.color_r = (double) c.red / uint16.MAX; color_tool.color_g = (double) c.green / uint16.MAX;new file mode 100644@@ -0,0 +1,35 @@+/*+ Copyright (C) 2014 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details.+*/++namespace BirdFont {++public class Color {+ public double r;+ public double g;+ public double b;+ public double a;+ + public Color (double r, double g, double b, double a) {+ this.r = r;+ this.g = g;+ this.b = b;+ this.a = a;+ }+ + public static Color black () {+ return new Color (0, 0, 0, 1);+ }+}++}@@ -1336,64 +1336,78 @@ public class Glyph : FontDisplay { } public void draw_path (Context cr) {- double left, baseline;- Font font = BirdFont.get_current_font ();- - baseline = font.base_line;- left = get_line ("left").pos;- - if (!is_open ()) {- cr.save ();- cr.set_source_rgba (0, 0, 0, 1);- - Svg.draw_svg_path (cr, get_svg_data (), Glyph.xc () + left, Glyph.yc () - baseline);- - cr.restore ();- }- if (is_open () && Path.fill_open_path) {+ cr.save ();+ cr.new_path (); foreach (unowned Path p in path_list) {- cr.save ();- cr.set_source_rgba (Path.fill_color_r, Path.fill_color_g, Path.fill_color_b, Path.fill_color_a);- Svg.draw_svg_path (cr, get_svg_data (), Glyph.xc () + left, Glyph.yc () - baseline);- cr.restore ();+ if (p.stroke > 0) {+ draw_path_list (StrokeTool.get_stroke (p, p.stroke), cr, get_path_fill_color ());+ }++ p.draw_path (cr, get_path_fill_color ()); }+ cr.fill ();+ cr.restore (); } if (is_open ()) {+ cr.save (); foreach (unowned Path p in path_list) {- p.draw_outline (cr, allocation, view_zoom);- p.draw_edit_points (cr, allocation, view_zoom); - if (p.stroke > 0) { draw_outline_for_paths (StrokeTool.get_stroke (p, p.stroke), cr); }++ p.draw_outline (cr);+ p.draw_edit_points (cr); }+ cr.restore (); } if (!is_open ()) {+ // This was good for testing but it is way too slow:+ // Svg.draw_svg_path (cr, get_svg_data (), Glyph.xc () + left, Glyph.yc () - baseline);+ + cr.save ();+ cr.new_path ();+ foreach (unowned Path p in path_list) {+ if (p.stroke == 0) {+ p.draw_path (cr, Color.black ());+ } else {+ draw_path_list (StrokeTool.get_stroke (p, p.stroke), cr, Color.black ());+ }+ }+ cr.fill ();+ cr.restore ();+ foreach (unowned Path p in active_paths) {+ cr.save (); if (p.stroke == 0) {- p.fill_path (cr, allocation, view_zoom);+ p.draw_path (cr); } else {- fill_paths (StrokeTool.get_stroke (p, p.stroke), cr);+ draw_path_list (StrokeTool.get_stroke (p, p.stroke), cr); }+ cr.fill ();+ cr.restore (); } } } + private Color get_path_fill_color () {+ return new Color (Path.fill_color_r, Path.fill_color_g, Path.fill_color_b, Path.fill_color_a);+ }+ private void draw_outline_for_paths (PathList pl, Context cr) {- foreach (Path p in pl.paths) {- p.draw_outline (cr, allocation, view_zoom);+ foreach (Path p in pl.paths) { + p.draw_outline (cr); } } - private void fill_paths (PathList pl, Context cr) {+ private void draw_path_list (PathList pl, Context cr, Color? c = null) { foreach (Path p in pl.paths) {- p.fill_path (cr, allocation, view_zoom);+ p.draw_path (cr, c); }- } - + }+ private void draw_zoom_area(Context cr) { cr.save (); cr.set_line_width (2.0);@@ -1443,7 +1457,7 @@ public class Glyph : FontDisplay { if (unlikely (Preferences.draw_boundaries)) { foreach (unowned Path p in path_list) {- p.draw_boundaries (cr, allocation, view_zoom);+ p.draw_boundaries (cr); } } @@ -183,8 +183,29 @@ public class GlyphRange { } public static string serialize (string s) {+ + if (s == "space") {+ return s;+ }++ if (s == "divis") {+ return s;+ }++ if (s == "null") {+ return s;+ }++ if (s == "quote") {+ return s;+ }++ if (s == "ampersand") {+ return s;+ }+ if (s.char_count () > 1) {- warning ("Expecting a single glyph");+ warning (@"Expecting a single glyph ($s)"); return s; } @@ -169,7 +169,7 @@ public class Path { stroke = width; } - public void draw_boundaries (Context cr, WidgetAllocation allocation, double view_zoom) {+ public void draw_boundaries (Context cr) { double x = Glyph.reverse_path_coordinate_x (xmin); double y = Glyph.reverse_path_coordinate_y (ymin); double x2 = Glyph.reverse_path_coordinate_x (xmax);@@ -185,7 +185,7 @@ public class Path { cr.restore (); } - public void draw_outline (Context cr, WidgetAllocation allocation, double view_zoom) {+ public void draw_outline (Context cr) { unowned List<EditPoint> ep = points; unowned EditPoint? n = null;@@ -218,7 +218,7 @@ public class Path { cr.stroke (); } - public void draw_edit_points (Context cr, WidgetAllocation allocation, double view_zoom) {+ public void draw_edit_points (Context cr) { unowned List<EditPoint> ep = points; if (is_editable ()) {@@ -236,14 +236,17 @@ public class Path { } } - public void fill_path (Context cr, WidgetAllocation allocation, double view_zoom) {+ /** Add all control points for a path to the cairo context.+ * Call Context.new_path (); before this method and Context.fill ()+ * to show the path.+ */+ public void draw_path (Context cr, Color? color = null) { unowned List<EditPoint> ep = points;- unowned EditPoint? n = null; unowned EditPoint en; unowned EditPoint em;- cr.new_path ();- + Color c;+ // draw lines foreach (EditPoint e in ep) { if (n != null) {@@ -263,14 +266,17 @@ public class Path { // fill path cr.close_path ();- - if (is_clockwise ()) {- cr.set_source_rgba (80/255.0, 95/255.0, 137/255.0, 0.5);+ + if (color != null) {+ c = (!) color;+ cr.set_source_rgba (c.r, c.g, c.b, c.a); } else {- cr.set_source_rgba (144/255.0, 145/255.0, 236/255.0, 0.5);+ if (is_clockwise ()) {+ cr.set_source_rgba (80/255.0, 95/255.0, 137/255.0, 0.5);+ } else {+ cr.set_source_rgba (144/255.0, 145/255.0, 236/255.0, 0.5);+ } }- - cr.fill (); } private void draw_next (EditPoint e, EditPoint en, Context cr) {@@ -752,7 +758,7 @@ public class Path { Glyph.path_coordinate_x (y2)); } - private static double get_length_from (EditPoint a, EditPoint b) {+ public static double get_length_from (EditPoint a, EditPoint b) { double x, y; x = Math.fabs (a.x - a.get_right_handle ().x ());@@ -58,8 +58,10 @@ public class StrokeTool : Tool { outline.close (); paths.add (outline); } else if (!p.is_open () && !p.is_filled ()) {+ outline = create_stroke (p, thickness); counter = create_stroke (p, -1 * thickness);+ paths.add (outline); paths.add (counter); @@ -91,7 +93,7 @@ public class StrokeTool : Tool { warning ("Can not create stroke."); paths.add (p); }- + return paths; } @@ -170,11 +172,11 @@ public class StrokeTool : Tool { Path new_path; Path stroked; - new_path = tangents (p);+ new_path = add_tangent_points (p); if (new_path.points.length () >= 2) { stroked = change_stroke_width (new_path, thickness);-+ if (!p.is_open ()) { stroked.reverse (); stroked.close ();@@ -188,14 +190,6 @@ public class StrokeTool : Tool { return stroked; } - static Path tangents (Path p) {- Path pn = p;- for (int i = 0; i < 3; i++) {- pn = add_tangent_points (pn);- }- return pn;- }- static Path add_tangent_points (Path p) { Path path = p.copy (); int steps = 2;@@ -206,14 +200,14 @@ public class StrokeTool : Tool { path.all_segments ((start, stop) => { EditPoint? prev = null;+ double length = Path.get_length_from (start, stop); - if (start.type == PointType.DOUBLE_CURVE) {- steps = 1;- } else {- steps = 2;- }- - Path.all_of (start, stop, (x, y, step) => {+ steps = (start.type == PointType.DOUBLE_CURVE) ?+ (int) (length / 10) : + (int) (length / 5);++ if (steps > 1) {+ Path.all_of (start, stop, (x, y, step) => { EditPoint ep = new EditPoint (); if (0 < step < 1) {@@ -251,10 +245,12 @@ public class StrokeTool : Tool { } return true;- }, steps);- - return true;- });+ }, steps); + }+ + return true;+ });+ while (new_points.length () > 0) { nep = new_points.first ().data;@@ -42,6 +42,11 @@ class Test : Object { public bool is_benchmark () { return benchmark; }+ + public static void print_time (string mess, double start_time) {+ double stop_time = GLib.get_real_time ();+ print (@"$mess $((stop_time - start_time) / 1000000.0)\n");+ } } }