Commit 26e337b
Use libsvgbird for drawing in libbirdfont
Changed files (15)
modifiedlibbirdfont/BirdFont.valamodifiedlibbirdfont/BirdFontFile.valaaddedlibbirdfont/CanvasSettings.valamodifiedlibbirdfont/EmbeddedSvg.valamodifiedlibbirdfont/Glyph.valamodifiedlibbirdfont/KerningDisplay.valamodifiedlibbirdfont/LayerUtils.valamodifiedlibbirdfont/OverViewItem.valamodifiedlibbirdfont/Path.valamodifiedlibbirdfont/PathObject.valamodifiedlibbirdfont/PenTool.valamodifiedlibbirdfont/SettingsTab.valamodifiedlibbirdfont/TrackTool.valamodifiedlibsvgbird/Layer.valamodifiedlibsvgbird/SvgStyle.vala
Diff
@@ -90,7 +90,6 @@ public class BirdFont { public static Font current_font; public static GlyphCollection current_glyph_collection;- public static Drawing? drawing = null; public static string? settings_subdirectory = null;@@ -166,6 +165,8 @@ public class BirdFont { Process.exit (0); } #endif+ + CanvasSettings.init (); Preferences.load (); // always load default theme when names in theme does change@@ -506,7 +506,7 @@ class BirdFontFile : GLib.Object { void write_layer (Layer layer, DataOutputStream os) throws GLib.Error { os.put_string (@"\t\t<layer name= \"$(layer.name)\" visible=\"$(layer.visible)\">\n"); - foreach (SvgBird.Object o in layer.get_all_objects ().objects) {+ foreach (SvgBird.Object o in layer.objects.objects) { if (o is EmbeddedSvg) { write_embedded_svg ((EmbeddedSvg) o, os);new file mode 100644@@ -0,0 +1,92 @@+/*+ Copyright (C) 2016 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.+*/++using B;+using Cairo;++namespace BirdFont {++public class CanvasSettings {++ public static double stroke_width {+ get {+ settings_mutex.lock ();+ double r = stroke_width_setting;+ + if (unlikely (stroke_width_setting < 1)) {+ string width = Preferences.get ("stroke_width");+ if (width != "") {+ stroke_width_setting = double.parse (width);+ }+ }+ + if (stroke_width_setting < 1) {+ stroke_width_setting = 1;+ }+ + settings_mutex.unlock ();+ return r;+ }+ + set {+ settings_mutex.lock ();+ stroke_width_setting = value;+ settings_mutex.unlock ();+ }+ }+ + public static bool show_all_line_handles {+ get {+ settings_mutex.lock ();+ bool r = show_all_line_handles_setting;+ settings_mutex.unlock ();+ return r;+ }+ + set {+ settings_mutex.lock ();+ show_all_line_handles_setting = value;+ settings_mutex.unlock ();+ }+ }+ + public static bool fill_open_path {+ get { + settings_mutex.lock ();+ bool r = fill_open_path_setting;+ settings_mutex.unlock ();+ return r;+ }+ + set {+ settings_mutex.lock ();+ fill_open_path_setting = value;+ settings_mutex.unlock ();+ }+ }+ + static Mutex settings_mutex;++ /** The stroke of an outline when the path is not filled. */+ static double stroke_width_setting = 0;+ static bool show_all_line_handles_setting = true;+ static bool fill_open_path_setting = false;++ public static void init () {+ settings_mutex = new Mutex ();+ }++}++}@@ -76,13 +76,16 @@ public class EmbeddedSvg : SvgBird.Object { && (this.y - drawing.height <= y <= this.y); } - public override void draw_outline (Context cr) {+ public void draw_embedded_svg (Context cr) { cr.save (); cr.translate (Glyph.xc () + x, Glyph.yc () - y); drawing.draw (cr); cr.restore (); } + public override void draw_outline (Context cr) {+ }+ public override SvgBird.Object copy () { EmbeddedSvg svg = new EmbeddedSvg (drawing); svg.svg_data = svg_data;@@ -180,7 +180,7 @@ public class Glyph : FontDisplay { } public Gee.ArrayList<SvgBird.Object> get_visible_objects () {- return layers.get_visible_objects ().objects;+ return LayerUtils.get_visible_objects (layers); } public Gee.ArrayList<Path> get_visible_paths () {@@ -192,7 +192,7 @@ public class Glyph : FontDisplay { } public Gee.ArrayList<SvgBird.Object> get_objects_in_current_layer () {- return get_current_layer ().get_all_objects ().objects;+ return get_current_layer ().objects.objects; } public Gee.ArrayList<Path> get_paths_in_current_layer () {@@ -252,6 +252,10 @@ public class Glyph : FontDisplay { public override void close () { undo_list.clear (); redo_list.clear ();+ + foreach (Path path in get_all_paths ()) {+ path.set_editable (false);+ } } public void set_empty_ttf (bool e) {@@ -1331,7 +1335,7 @@ public class Glyph : FontDisplay { } public void open_path () {- foreach (Path p in get_visible_paths ()) {+ foreach (Path p in get_all_paths ()) { p.set_editable (true); p.recalculate_linear_handles (); @@ -1576,17 +1580,74 @@ public class Glyph : FontDisplay { cr.stroke (); } - /** Draw filled paths. */- public void draw_paths (Context cr, Color? c = null) {- layers.draw (cr);+ public void draw_layers (Context cr) {+ foreach (SvgBird.Object object in layers.objects) {+ if (object is Layer) {+ draw_layer (cr, (Layer) object);+ }+ }+ + draw_bird_font_paths (cr); } - public void draw_path (Context cr) {- layers.draw (cr);+ public void draw_layer (Context cr, Layer sublayers) {+ foreach (SvgBird.Object object in sublayers.objects) {+ if (object is EmbeddedSvg) {+ EmbeddedSvg svg = (EmbeddedSvg) object;+ svg.draw_embedded_svg (cr);+ }+ } }+ + public void draw_bird_font_paths (Context cr) {+ Tool selected_tool = MainWindow.get_toolbox ().get_current_tool ();+ + bool draw_control_points = (selected_tool is PenTool)+ || (selected_tool is PointTool)+ || (selected_tool is TrackTool)+ || (selected_tool is BezierTool);++ bool has_path = false;+ foreach (SvgBird.Object object in get_visible_objects ()) {+ if (object is PathObject && object.stroke > 0) {+ has_path = true;+ PathObject object_path = (PathObject) object; + object_path.draw_path (cr);+ }+ } - private Color get_path_fill_color () {- return Theme.get_color ("Fill Color");+ if (has_path) {+ cr.set_fill_rule (FillRule.WINDING);+ cr.set_source_rgba (0, 0, 0, 1);+ cr.fill ();+ }++ has_path = false;+ foreach (SvgBird.Object object in get_visible_objects ()) {+ if (object is PathObject && object.stroke == 0) {+ has_path = true;+ PathObject object_path = (PathObject) object; + object_path.draw_path (cr);+ }+ }++ if (has_path) {+ cr.set_fill_rule (FillRule.EVEN_ODD);+ cr.set_source_rgba (0, 0, 0, 1);+ cr.fill ();+ }+ + if (draw_control_points) {+ foreach (SvgBird.Object object in get_visible_objects ()) {+ if (object is PathObject) {+ PathObject object_path = (PathObject) object;+ Glyph g = MainWindow.get_current_glyph ();+ cr.set_line_width (CanvasSettings.stroke_width / g.view_zoom);+ object_path.path.draw_outline (cr);+ object_path.path.draw_control_points (cr);+ }+ }+ } } public void draw_background_color (Context cr, double opacity) {@@ -1647,7 +1708,7 @@ public class Glyph : FontDisplay { cmp.save (); cmp.scale (view_zoom, view_zoom); cmp.translate (-view_offset_x, -view_offset_y);- draw_path (cmp); + draw_layers (cmp); cmp.restore (); cmp.save ();@@ -193,7 +193,7 @@ public class KerningDisplay : FontDisplay { cr.save (); glyph.add_help_lines (); cr.translate (kern + x - glyph.get_lsb () - Glyph.xc (), glyph.get_baseline () + y - Glyph.yc ());- glyph.draw_paths (cr);+ glyph.draw_layers (cr); cr.restore (); w = glyph.get_width ();@@ -17,9 +17,31 @@ using SvgBird; namespace BirdFont { public class LayerUtils {++ public static Gee.ArrayList<SvgBird.Object> get_visible_objects (Layer layer) {+ ObjectGroup group = new ObjectGroup ();+ add_visible_objects (layer, group);+ return group.objects;+ }++ public static void add_visible_objects (Layer layer, ObjectGroup objects) {+ foreach (SvgBird.Object o in layer.objects) {+ if (o is Layer) {+ Layer sublayer = (Layer) o;+ + if (sublayer.visible) {+ add_visible_objects (sublayer, objects);+ }+ } else {+ if (o.visible) {+ objects.add (o);+ }+ }+ }+ } public static PathList get_all_paths (Layer layer) {- ObjectGroup objects = layer.get_all_objects ();+ ObjectGroup objects = layer.objects; PathList paths = new PathList (); foreach (SvgBird.Object o in objects) {@@ -33,19 +55,22 @@ public class LayerUtils { } public static PathList get_visible_paths (Layer layer) {- ObjectGroup objects = layer.get_visible_objects (); PathList paths = new PathList ();- - foreach (SvgBird.Object o in objects) {+ add_visible_paths_to_group (layer, paths);+ return paths;+ }++ public static void add_visible_paths_to_group (Layer layer, PathList paths) {+ foreach (SvgBird.Object o in layer.objects) { if (o is PathObject) { PathObject p = (PathObject) o; paths.add (p.get_path ());+ } else if (o is Layer) {+ add_visible_paths_to_group ((Layer) o, paths); } }- - return paths; }- + public static void add_path (Layer layer, Path path) { PathObject p = new PathObject.for_path (path); layer.add_object (p);@@ -106,7 +106,6 @@ public class OverViewItem : GLib.Object { double glyph_width, glyph_height; Surface s; Context c;- Color color = Color.black (); g = ((!) glyphs).get_current (); @@ -137,7 +136,7 @@ public class OverViewItem : GLib.Object { gx = ((w / glyph_scale) - glyph_width) / 2 - g.get_left_side_bearing (); gy = (h / glyph_scale) - 25 / glyph_scale; c.translate (gx - Glyph.xc () - g.get_lsb (), g.get_baseline () + gy - Glyph.yc ());- g.draw_paths (c, color);+ g.draw_layers (c); c.restore (); @@ -1,5 +1,5 @@ /*- Copyright (C) 2012 2013 2014 2015 Johan Mattsson+ Copyright (C) 2012 - 2016 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 @@ -84,15 +84,9 @@ public class Path : GLib.Object { bool clockwise_direction = true; // Iterate over each pixel in a path- public delegate bool RasterIterator (double x, double y, double step);- + public delegate bool RasterIterator (double x, double y, double step); public delegate bool SegmentIterator (EditPoint start, EditPoint stop); - /** The stroke of an outline when the path is not filled. */- public static double stroke_width = 0;- public static bool show_all_line_handles = true;- public static bool fill_open_path { get; set; }- public double rotation = 0; public double skew = 0; @@ -108,18 +102,6 @@ public class Path : GLib.Object { public Gradient? gradient = null; public Path () { - string width;- - if (unlikely (stroke_width < 1)) {- width = Preferences.get ("stroke_width");- if (width != "") {- stroke_width = double.parse (width);- }- }-- if (stroke_width < 1) {- stroke_width = 1;- } } public bool is_filled () {@@ -201,7 +183,7 @@ public class Path : GLib.Object { i++; } - // close path+ // closed path if (!is_open () && n != null) { if (highlight_last_segment) { cr.stroke ();@@ -226,20 +208,21 @@ public class Path : GLib.Object { } } - public void draw_edit_points (Context cr) { - if (is_editable ()) {- // control points for curvature- foreach (EditPoint e in points) {- if (show_all_line_handles || e.selected_point || e.selected_handle > 0) {- draw_edit_point_handles (e, cr);- }- }- - // control points- foreach (EditPoint e in points) {- draw_edit_point (e, cr);+ public void draw_control_points (Context cr) { + // control points for curvature+ foreach (EditPoint e in points) {+ if (CanvasSettings.show_all_line_handles + || e.selected_point+ || e.selected_handle > 0) {+ + draw_edit_point_handles (e, cr); } }+ + // on curve control points+ foreach (EditPoint e in points) {+ draw_edit_point (e, cr);+ } } /** Add all control points for a path to the cairo context.@@ -334,12 +317,9 @@ public class Path : GLib.Object { cr.translate (x, y); double inverted_zoom = Glyph.ivz (); cr.rotate (-angle);- cr.translate (-x, -y); - + cr.translate (-x, -y); cr.scale (inverted_zoom, inverted_zoom);- arrow_icon.draw_at_baseline (cr, x, y);- cr.restore (); } }@@ -373,7 +353,6 @@ public class Path : GLib.Object { } private static void draw_curve (EditPoint e, EditPoint en, Context cr, bool highlighted = false, double alpha = 1) {- Glyph g = MainWindow.get_current_glyph (); double xa, ya, xb, yb, xc, yc, xd, yd; PointType t = e.get_right_handle ().type; PointType u = en.get_left_handle ().type;@@ -386,8 +365,6 @@ public class Path : GLib.Object { Theme.color (cr, "Highlighted Guide"); } - cr.set_line_width (stroke_width / g.view_zoom);- cr.line_to (xa, ya); // this point makes sense only if it is in the first or last position if (t == PointType.QUADRATIC || t == PointType.LINE_QUADRATIC || t == PointType.DOUBLE_CURVE || u == PointType.QUADRATIC || u == PointType.LINE_QUADRATIC || u == PointType.DOUBLE_CURVE) {@@ -453,7 +430,7 @@ public class Path : GLib.Object { get_line_points (e, en, out ax, out ay, out bx, out by); Theme.color (cr, "Handle Color");- cr.set_line_width (1.7 * (stroke_width / g.view_zoom));+ cr.set_line_width (1.7 * (CanvasSettings.stroke_width / g.view_zoom)); cr.line_to (ax, ay); cr.line_to (bx, by);@@ -583,7 +560,7 @@ public class Path : GLib.Object { public static void draw_control_point (Context cr, double x, double y, Color color, double size = 3.5) { Glyph g = MainWindow.get_current_glyph (); double ivz = 1 / g.view_zoom;- double width = size * Math.sqrt (stroke_width) * ivz;+ double width = size * Math.sqrt (CanvasSettings.stroke_width) * ivz; double xc = g.allocation.width / 2.0; double yc = g.allocation.height / 2.0; @@ -1,5 +1,5 @@ /*- Copyright (C) 2015 Johan Mattsson+ Copyright (C) 2015 2016 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 @@ -19,7 +19,18 @@ namespace BirdFont { public class PathObject : SvgBird.Object { - Path path;+ public Path path;+ + public override double stroke {+ get {+ return path.stroke;+ }+ + set {+ path.stroke = value;+ }+ }+ // FIXME: flip y axis public override double xmin {@@ -69,8 +80,9 @@ public class PathObject : SvgBird.Object { default = Glyph.CANVAS_MIN; }- + public PathObject () {+ base (); path = new Path (); update_region_boundaries (); }@@ -81,36 +93,33 @@ public class PathObject : SvgBird.Object { } public PathObject.for_path (Path path) {+ base (); this.path = path; } public override bool is_over (double x, double y) { return path.is_over_coordinate (x, y); }-+ public override void draw_outline (Context cr) { draw_path (cr); }- - public void draw_path (Context cr, Color? c = null) {++ private SvgBird.Color get_path_fill_color () {+ Color c = Theme.get_color ("Fill Color");+ return new SvgBird.Color (c.r, c.g, c.b, c.a);+ }++ public void draw_path (Context cr) { // FIXME: delete PathList path_stroke;- Color path_color; bool open; - cr.save ();- - if (c != null) {- path_color = (!) c;- } else if (color != null) {- path_color = new Color.create_copy ((!) color);- } else {- path_color = Color.black ();- }- if (path.stroke > 0) {+ cr.set_fill_rule (FillRule.WINDING); path_stroke = path.get_stroke_fast ();- draw_path_list (path_stroke, cr, path_color);+ draw_path_list (path_stroke, cr); } else {+ cr.set_fill_rule (FillRule.EVEN_ODD); open = path.is_open (); if (open) {@@ -118,19 +127,17 @@ public class PathObject : SvgBird.Object { path.recalculate_linear_handles (); } - path.draw_path (cr, path_color);+ path.draw_path (cr); if (open) { path.reopen (); } }-- cr.restore (); } - public static void draw_path_list (PathList pl, Context cr, Color? c = null) {+ public static void draw_path_list (PathList pl, Context cr) { foreach (Path p in pl.paths) {- p.draw_path (cr, c);+ p.draw_path (cr); } } @@ -1924,7 +1924,7 @@ public class PenTool : Tool { Glyph g = MainWindow.get_current_glyph (); double distance_to_edit_point = g.view_zoom * get_distance_to_closest_edit_point (event_x, event_y); - if (!Path.show_all_line_handles) {+ if (!CanvasSettings.show_all_line_handles) { foreach (PointSelection selected_corner in selected_points) { if (is_close_to_handle (selected_corner.point, event_x, event_y, distance_to_edit_point)) { return true;@@ -1994,7 +1994,7 @@ public class PenTool : Tool { foreach (Path p in g.get_paths_in_current_layer ()) { foreach (EditPoint ep in p.points) {- if (ep.is_selected () || Path.show_all_line_handles) {+ if (ep.is_selected () || CanvasSettings.show_all_line_handles) { left = ep.get_left_handle (); right = ep.get_right_handle (); @@ -43,13 +43,13 @@ public class SettingsTab : SettingsDisplay { stroke_width.new_value_action.connect ((self) => { Glyph g = MainWindow.get_current_glyph ();- Path.stroke_width = stroke_width.get_value ();+ CanvasSettings.stroke_width = stroke_width.get_value (); g.redraw_area (0, 0, g.allocation.width, g.allocation.height); Preferences.set ("stroke_width_for_open_paths", stroke_width.get_display_value ()); MainWindow.get_toolbox ().redraw ((int) stroke_width.x, (int) stroke_width.y, 70, 70); }); - Path.stroke_width = stroke_width.get_value ();+ CanvasSettings.stroke_width = stroke_width.get_value (); // adjust precision string precision_value = Preferences.get ("precision");@@ -81,7 +81,7 @@ public class SettingsTab : SettingsDisplay { Tool show_all_line_handles = new Tool ("show_all_line_handles"); show_all_line_handles.select_action.connect((self) => {- Path.show_all_line_handles = !Path.show_all_line_handles;+ CanvasSettings.show_all_line_handles = !CanvasSettings.show_all_line_handles; Glyph g = MainWindow.get_current_glyph (); g.redraw_area (0, 0, g.allocation.width, g.allocation.height); });@@ -89,11 +89,11 @@ public class SettingsTab : SettingsDisplay { Tool fill_open_path = new Tool ("fill_open_path"); fill_open_path.select_action.connect((self) => {- Path.fill_open_path = true; + CanvasSettings.fill_open_path = true; }); fill_open_path.deselect_action.connect((self) => {- Path.fill_open_path = false; + CanvasSettings.fill_open_path = false; }); tools.add (new SettingsItem (fill_open_path, t_("Fill open paths."))); @@ -232,12 +232,17 @@ public class TrackTool : Tool { } } }- - // FIXME: double check+ void set_tie () { Glyph glyph = MainWindow.get_current_glyph (); var paths = glyph.get_visible_paths ();- Path p = paths.get (paths.size - 1);+ Path p;+ + if (paths.size == 0) {+ return;+ }+ + p = paths.get (paths.size - 1); foreach (EditPoint ep in p.points) { if (ep.get_right_handle ().is_line () || ep.get_left_handle ().is_line ()) {@@ -42,9 +42,14 @@ public class Layer : Object { ClipPath clipping = (!) object.clip_path; clipping.apply (cr); }-- object.draw_outline (cr);- object.paint (cr);+ + if (object is Layer) {+ Layer sublayer = (Layer) object;+ sublayer.draw (cr); + } else {+ object.draw_outline (cr);+ object.paint (cr);+ } cr.restore (); } @@ -52,7 +57,6 @@ public class Layer : Object { } public override void draw_outline (Context cr) {- cr.save (); apply_transform (cr); foreach (Object object in objects) {@@ -68,14 +72,6 @@ public class Layer : Object { public int index_of (Layer sublayer) { return objects.index_of (sublayer); }-- public ObjectGroup get_all_objects () {- return objects;- }- - public ObjectGroup get_visible_objects () {- return objects; // FIXME: remove this- } public void add_layer (Layer layer) { objects.add (layer);@@ -111,39 +107,7 @@ public class Layer : Object { Object.copy_attributes (this, layer); return layer; }-- public void get_boundaries (out double x, out double y, out double w, out double h) {- double px, py, px2, py2;- - px = double.MAX;- py = double.MAX;- px2 = -double.MAX;- py2 = -double.MAX;- - foreach (Object p in get_all_objects ().objects) {- if (px > p.xmin) {- px = p.xmin;- } -- if (py > p.ymin) {- py = p.ymin;- }-- if (px2 < p.xmax) {- px2 = p.xmax;- }- - if (py2 < p.ymax) {- py2 = p.ymax;- }- }- - w = px2 - px;- h = py2 - py;- x = px;- y = py2;- }-+ public Gee.ArrayList<Layer> get_sublayers () { Gee.ArrayList<Layer> sublayers = new Gee.ArrayList<Layer> (); @@ -32,7 +32,16 @@ public class SvgStyle : GLib.Object { public SvgStyle () { }+ + public SvgStyle.for_properties (Defs? defs, string style) {+ parse_key_value_pairs (style);+ set_style_properties (defs, this);+ } + public void set_style (string property, string style) {+ this.style.set (property, style);+ }+ public bool property_equals (string property, string value) { string? p = get_css_property (property); @@ -89,11 +98,6 @@ public class SvgStyle : GLib.Object { inheritance_mutex.unlock (); } - public SvgStyle.for_properties (Defs? defs, string style) {- parse_key_value_pairs (style);- set_style_properties (defs, this);- }- public SvgStyle copy () { SvgStyle style = new SvgStyle (); style.apply (this);