The Birdfont Source Code


All Repositories / birdfont.git / commit – RSS feed

Merge branch 'master' of github.com:johanmattssonm/birdfont

These changes was commited to the Birdfont repository Thu, 17 Dec 2015 23:13:31 +0000.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git
author Johan Mattsson <johan.mattsson.m@gmail.com>
Thu, 17 Dec 2015 23:13:31 +0000 (00:13 +0100)
committer Johan Mattsson <johan.mattsson.m@gmail.com>
Thu, 17 Dec 2015 23:13:31 +0000 (00:13 +0100)
commit 8061186c83a62c59a993e826f195477cd2cb685d
tree 82c51f1c4b8cb58b2f55759df9397b707c498016
parent 0e2d1709ec7e3b9aadcfdd83afa0e526756bc769
parent 8928962902af44e60817e531d67f8e01d81ee45f
Merge branch 'master' of github.com:johanmattssonm/birdfont

libbirdfont/BirdFont.vala
libbirdfont/FileDialogTab.vala
libbirdfont/GlyphRange.vala
libbirdfont/LabelTool.vala
libbirdfont/MainWindow.vala
libbirdfont/OverView.vala
libbirdfont/OverViewItem.vala
--- a/libbirdfont/BirdFont.vala +++ b/libbirdfont/BirdFont.vala @@ -259,7 +259,7 @@ if (has_argument ("--codepages")) { codepage_bits = new CodePageBits (); codepage_bits.generate_codepage_database (); - } + } } public static bool has_logging () {
--- a/libbirdfont/FileDialogTab.vala +++ b/libbirdfont/FileDialogTab.vala @@ -225,7 +225,12 @@ if (selected_filename == "") { action.cancel (); } else { - f = get_child (current_dir, selected_filename); + if (selected_filename.index_of (":\\") != -1) { + f = File.new_for_path (selected_filename); + } else { + f = get_child (current_dir, selected_filename); + } + action.file_selected ((!)f.get_path ()); } });
--- a/libbirdfont/GlyphRange.vala +++ b/libbirdfont/GlyphRange.vala @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 2014 Johan Mattsson + Copyright (C) 2012 2014 2015 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 @@ -26,11 +26,38 @@ uint32 len = 0; bool range_is_class = false; + uint32* range_index = null; + int index_size = 0; + int index_hint = 0; public GlyphRange () { ranges = new Gee.ArrayList<UniRange> (); unassigned = new Gee.ArrayList<string> (); name = "No name"; + } + + ~GlyphRange () { + if (range_index != null) { + delete range_index; + } + } + + private void generate_unirange_index () { + if (range_index != null) { + delete range_index; + } + + index_size = ranges.size; + range_index = new uint32[index_size]; + + int i = 0; + uint32 next_index = 0; + + foreach (UniRange range in ranges) { + range_index[i] = next_index; + next_index += (uint32) range.length (); + i++; + } } public void add_unassigned (string glyph_name) { @@ -53,13 +80,14 @@ unassigned.clear (); ranges.clear (); len = 0; + generate_unirange_index (); } public unowned Gee.ArrayList<UniRange> get_ranges () { return ranges; } - // TODO: complete localized alphabetical sort åäö is not the right order for example. + // sort by unicode value public void sort () { ranges.sort ((a, b) => { UniRange first, next; @@ -73,6 +101,8 @@ return (r) ? 1 : -1; }); + + generate_unirange_index (); } public void add_single (unichar c) { @@ -122,6 +152,8 @@ } } } + + generate_unirange_index (); } /** Parse ranges on the form a-z. Single characters can be added as well as @@ -131,6 +163,7 @@ */ public void parse_ranges (string ranges) throws MarkupError { parse_range_string (ranges); + generate_unirange_index (); } private void parse_range_string (string ranges) throws MarkupError { @@ -200,11 +233,11 @@ first = false; } + return s.str; } public static string serialize (string s) { - if (s == "space") { return s; } @@ -351,7 +384,7 @@ private void append_range (unichar start, unichar stop) { UniRange r; r = insert_range (start, stop); // insert a unique range - merge_range (r); // join connecting ranges + merge_range (r); } private void merge_range (UniRange r) { @@ -393,50 +426,105 @@ merge_range (r); } } + + /** Find a range which containd index. */ + private void get_unirange_index (uint32 index, out UniRange? range, out uint32 range_start_index) { + int lower = 0; + int upper = index_size - 1; + int i; + int end = index_size - 1; + + if (index_hint >= 0 && index_hint < index_size) { + i = index_hint; + } else { + i = (lower + upper) / 2; + } + + range_start_index = -1; + range = null; + + if (unlikely (ranges.size != index_size)) { + warning (@"Range size does not match index size: $(ranges.size) != $index_size"); + } + + while (true) { + if (i == end) { + range_start_index = range_index[i]; + range = ranges.get (i); + index_hint = i; + break; + } else if (range_index[i] <= index && range_index[i + 1] > index) { + range_start_index = range_index[i]; + range = ranges.get (i); + index_hint = i; + break; + } + + if (lower >= upper) { + break; + } + if (range_index[i] < index) { + lower = i + 1; + } else { + upper = i - 1; + } + + i = (lower + upper) / 2; + } + } + public string get_char (uint32 index) { + StringBuilder sb; + + sb = new StringBuilder (); + sb.append_unichar (get_character (index)); + + return sb.str; + } + + public unichar get_character (uint32 index) { int64 ti; string chr; UniRange r; - StringBuilder sb; unichar c; + UniRange? range; + uint32 range_start_index; - if (index > len + unassigned.size) { - return "\0".dup(); + if (unlikely (index > len + unassigned.size)) { + return '\0'; } if (index >= len) { - if (index - len >= unassigned.size) { - return "\0".dup(); + if (unlikely (index - len >= unassigned.size)) { + return '\0'; } chr = unassigned.get ((int) (index - len)); - return chr; + return chr.get_char (); } - - r = ranges.get (0); - ti = index; - - foreach (UniRange u in ranges) { - ti -= u.length (); - - if (ti < 0) { - r = u; - break; - } + + get_unirange_index (index, out range, out range_start_index); + + if (unlikely (range == null)) { + warning (@"No range found for index $index"); + return '\0'; + } + + if (unlikely (range_start_index > index || range_start_index == -1)) { + warning ("Index out of bounds in glyph range."); + return '\0'; } - - sb = new StringBuilder (); - c = r.get_char ((unichar) (ti + r.length ())); + + r = (!) range; + c = r.get_char ((unichar) (index - range_start_index)); if (unlikely (!c.validate ())) { warning ("Not a valid unicode character."); - return ""; + return '\0'; } - sb.append_unichar (c); - - return sb.str; + return c; } public uint32 length () {
--- a/libbirdfont/LabelTool.vala +++ b/libbirdfont/LabelTool.vala @@ -24,11 +24,24 @@ } set { + clear_cache (); label_text.set_text (value); } } - public string number { get; set; } + public string number { + get { + return counter_number; + } + + set { + clear_cache (); + counter_number = value; + } + } + + string counter_number = ""; + public bool has_counter { get; set; } public bool has_delete_button { get; set; } public signal void delete_action (LabelTool self); @@ -38,6 +51,9 @@ double counter_box_height = 11 * Toolbox.get_scale (); Text label_text; + + Surface? selected_cache = null; + Surface? deselected_cache = null; public LabelTool (string label) { double text_height; @@ -61,19 +77,59 @@ delete_action (this); } }); + } + + void clear_cache () { + selected_cache = null; + deselected_cache = null; } public override void draw_tool (Context cr, double px, double py) { + double x = this.x - px; + double y = this.y - py; + + if (is_selected ()) { + + if (selected_cache == null) { + selected_cache = Screen.create_background_surface ((int) w, (int) h + 2); + Context c = new Context ((!) selected_cache); + c.scale (1 / Screen.get_scale (), 1 / Screen.get_scale ()); + draw_tool_surface (c, x, 2, true); + } + + cr.save (); + cr.set_antialias (Cairo.Antialias.NONE); + cr.set_source_surface ((!) selected_cache, 0, (int) y - 2); + cr.paint (); + cr.restore (); + } else { + + if (deselected_cache == null) { + deselected_cache = Screen.create_background_surface ((int) w, (int) h + 2); + Context c = new Context ((!) deselected_cache); + c.scale (1 / Screen.get_scale (), 1 / Screen.get_scale ()); + draw_tool_surface (c, x, 2, false); + } + + cr.save (); + cr.set_antialias (Cairo.Antialias.NONE); + cr.set_source_surface ((!) deselected_cache, 0, (int) y - 2); + cr.paint (); + cr.restore (); + } + } + + public void draw_tool_surface (Context cr, double px, double py, bool selected) { Text glyph_count; double bgx, bgy; double center_x, center_y; - double x = this.x - px; - double y = this.y - py; double text_height; double text_width; + double x = px; + double y = py; // background - if (is_selected ()) { + if (selected) { cr.save (); Theme.color (cr, "Menu Background"); cr.rectangle (0, y - 2 * Toolbox.get_scale (), w, h); // labels overlap with 2 pixels @@ -84,11 +140,7 @@ // tab label cr.save (); - if (is_selected ()) { - Theme.text_color (label_text, "Text Tool Box"); - } else { - Theme.text_color (label_text, "Text Tool Box"); - } + Theme.text_color (label_text, "Text Tool Box"); text_width = Toolbox.allocation_width; @@ -101,7 +153,6 @@ } label_text.truncate (text_width); - label_text.draw_at_top (cr, x, y); cr.restore ();
--- a/libbirdfont/MainWindow.vala +++ b/libbirdfont/MainWindow.vala @@ -170,6 +170,7 @@ public void set_native (NativeWindow nw) { native_window = nw; + OverViewItem.start_thumbnail_processing (); } public static FontDisplay get_current_display () {
--- a/libbirdfont/OverView.vala +++ b/libbirdfont/OverView.vala @@ -20,7 +20,7 @@ public class OverView : FontDisplay { public WidgetAllocation allocation = new WidgetAllocation (); - public OverViewItem selected_item = new OverViewItem (null, '\0', 0, 0); + public OverViewItem selected_item = new OverViewItem (); public Gee.ArrayList<GlyphCollection> copied_glyphs = new Gee.ArrayList<GlyphCollection> (); public Gee.ArrayList<GlyphCollection> selected_items = new Gee.ArrayList<GlyphCollection> (); @@ -75,6 +75,8 @@ double scroll_size = 1; const double UCD_LINE_HEIGHT = 17 * 1.3; + + private bool update_scheduled = true; public OverView (GlyphRange? range = null, bool open_selected = true, bool default_character_set = true) { @@ -488,7 +490,7 @@ OverViewItem get_selected_item () { if (visible_items.size == 0) { - return new OverViewItem (null, '\0', 0, 0); + return new OverViewItem (); } if (!(0 <= selected < visible_items.size)) { @@ -512,7 +514,11 @@ return i - 1; } - public void update_item_list (int item_list_length = -1) { + public void update_item_list () { + update_scheduled = true; + } + + public void process_item_list_update () { string character_string; Font f = BirdFont.get_current_font (); GlyphCollection? glyphs = null; @@ -522,80 +528,132 @@ unichar character; Glyph glyph; double tab_with; + int item_list_length; + int visible_size; + + Test test = new Test.time("update_item_list"); - tab_with = allocation.width - 30; // scrollbar + tab_with = allocation.width - 30; // scrollbar items_per_row = get_items_per_row (); rows = (int) (allocation.height / OverViewItem.full_height ()) + 2; - if (item_list_length == -1) { - item_list_length = items_per_row * rows; + item_list_length = items_per_row * rows; + + foreach (OverViewItem overview_item in visible_items) { + overview_item.cancel_thumbnail_rendering (); } visible_items.clear (); - visible_items = new Gee.ArrayList<OverViewItem> (); - - // update item list + Test test3 = new Test.time("two conditions"); + index = (uint32) first_visible; x = OverViewItem.margin; y = OverViewItem.margin; - for (int i = 0; i < item_list_length; i++) { - if (all_available) { - if (! (0 <= index < f.length ())) { - break; - } - + + if (all_available) { + uint font_length = f.length (); + + for (int i = 0; i < item_list_length && index < font_length; i++) { glyphs = f.get_glyph_collection_index ((uint32) index); return_if_fail (glyphs != null); glyph = ((!) glyphs).get_current (); character_string = glyph.name; character = glyph.unichar_code; - } else { - if (!(0 <= index < glyph_range.get_length ())) { - break; - } - character_string = glyph_range.get_char ((uint32) index); - glyphs = f.get_glyph_collection_by_name (character_string); - character = character_string.get_char (0); + item = new OverViewItem (); + item.set_glyphs (glyphs); + item.set_character (character); + item.x = x; + item.y = y; + visible_items.add (item); + index++; } + } else { + + Test test_mk = new Test.time("mk items"); + uint32 glyph_range_size = glyph_range.get_length (); - item = new OverViewItem (glyphs, character, x, y); - item.adjust_scale (); + for (int i = 0; i < item_list_length && index < glyph_range_size; i++) { + item = new OverViewItem (); + visible_items.add (item); + } + warning (test_mk.get_test_time ()); - x += OverViewItem.full_width (); + Test test_gl = new Test.time("get_glyph"); + visible_size = visible_items.size; + for (int i = 0; i < visible_size; i++) { + item = visible_items.get (i); + glyphs = f.get_glyph_collection_by_name ((!) item.character.to_string ()); + item.set_glyphs (glyphs); + } + warning (test_gl.get_test_time ()); + + Test test_a = new Test.time("get_char"); - if (x + OverViewItem.full_width () >= tab_with) { - x = OverViewItem.margin; - y += OverViewItem.full_height (); + visible_size = visible_items.size; + for (int i = 0; i < visible_size; i++) { + item = visible_items.get (i); + character = glyph_range.get_character ((uint32) index); + item.set_character (character); + index++; } - - item.selected = (i == selected); + warning (test_a.get_test_time ()); + } + + Test test_b = new Test.time("adjust pos"); + x = OverViewItem.margin; + y = OverViewItem.margin; + + visible_size = visible_items.size; + int selected_index; + bool selected_item; + double full_width = OverViewItem.full_width (); + + for (int i = 0; i < visible_size; i++) { + item = visible_items.get (i); + + selected_item = false; if (glyphs != null) { - item.selected |= selected_items.index_of ((!) glyphs) != -1; + selected_index = selected_items.index_of ((!) glyphs); + selected_item = (selected_index != -1); } - visible_items.add (item); - index++; + item.adjust_scale (); + + selected_item |= (i == selected); + item.selected = selected_item; + + item.x = x + view_offset_y; + item.y = y + view_offset_x; + + x += full_width; + + if (x + full_width >= tab_with) { + x = OverViewItem.margin; + y += OverViewItem.full_height (); + } } - - foreach (OverViewItem i in visible_items) { - i.y += view_offset_y; - i.x += view_offset_x; - } + + warning (test_b.get_test_time ()); + + warning (test3.get_test_time ()); + warning (test.get_test_time ()); + update_scheduled = false; } public override void draw (WidgetAllocation allocation, Context cr) { - - if (this.allocation.width != allocation.width + if (update_scheduled + || this.allocation.width != allocation.width || this.allocation.height != allocation.height || this.allocation.width == 0) { this.allocation = allocation; - update_item_list (); + process_item_list_update (); } + Test test = new Test.time("Overview.draw"); this.allocation = allocation; // clear canvas @@ -616,6 +674,8 @@ if (unlikely (character_info != null)) { draw_character_info (cr); } + + warning (test.get_test_time ()); } void draw_empty_canvas (WidgetAllocation allocation, Context cr) {
--- a/libbirdfont/OverViewItem.vala +++ b/libbirdfont/OverViewItem.vala @@ -37,23 +37,42 @@ public VersionList version_menu; Text label; + + private Surface? cache = null; public static Surface? label_background = null; public static Surface? selected_label_background = null; public static Surface? label_background_no_menu = null; public static Surface? selected_label_background_no_menu = null; - public OverViewItem (GlyphCollection? glyphs, unichar character, double x, double y) { - this.x = x; - this.y = y; - this.character = character; - this.glyphs = glyphs; - this.info = new CharacterInfo (character, glyphs); + private static Task thumbnail_task; + private static Gee.PriorityQueue<OverViewItem> thumbnail_queue; + private static Cond has_thumnail_task = new Cond (); + private static Mutex thumbnail_mutex = new Mutex (); + + private bool cancel_thumbnail = false; + + public OverViewItem () { + } - label = new Text ((!) character.to_string (), 17); - truncate_label (); - - if (glyphs != null) { + public void set_character (unichar character) { + this.character = character; + info = new CharacterInfo (character, glyphs); + + if (glyphs == null) { + label = new Text (); + } else { + label = new Text ((!) character.to_string (), 17); + truncate_label (); + } + + draw_background (); + } + + public void set_glyphs (GlyphCollection? gc) { + glyphs = gc; + + if (glyphs != null) { version_menu = new VersionList ((!) glyphs); version_menu.add_glyph_item.connect ((glyph) => { ((!) glyphs).insert_glyph (glyph, true); @@ -65,8 +84,144 @@ v.update_item_list (); GlyphCanvas.redraw (); }); + } + + /* + thumbnail_mutex.lock (); + if (!is_null (thumbnail_queue)) { + thumbnail_queue.offer (this); + has_thumnail_task.signal (); + } + thumbnail_mutex.unlock (); + */ + } + + public static void start_thumbnail_processing () { + //thumbnail_task = new Task (process_thumbnails, true); + thumbnail_queue = new Gee.PriorityQueue<OverViewItem> (); + //MainWindow.native_window.run_non_blocking_background_thread (thumbnail_task); + } + + public void cancel_thumbnail_rendering () { + thumbnail_mutex.lock (); + cancel_thumbnail = true; + thumbnail_mutex.unlock (); + } + + private static void process_thumbnails () { + bool cancel = false; + + while (!thumbnail_task.is_cancelled ()) { + OverViewItem item; + + thumbnail_mutex.lock (); + item = thumbnail_queue.poll (); + thumbnail_mutex.unlock (); + + if (!is_null (item)) { + thumbnail_mutex.lock (); + cancel = item.cancel_thumbnail; + thumbnail_mutex.unlock (); + + if (!cancel) { + item.draw_background (); + } + } else { + thumbnail_mutex.lock (); + has_thumnail_task.wait (thumbnail_mutex); + thumbnail_mutex.unlock (); + } + } + } + + public void draw_glyph_from_font () { + if (glyphs == null) { + return; + } + + Glyph g; + Font font; + double gx, gy; + double x1, x2, y1, y2; + double scale_box; + double w, h; + double glyph_width, glyph_height; + Surface s; + Context c; + Color color = Color.black (); + + w = width; + h = height; + + scale_box = width / DEFAULT_WIDTH; + + s = Screen.create_background_surface ((int) width, (int) height - 20); + c = new Context (s); + + g = ((!) glyphs).get_current (); + + c.save (); + g.boundaries (out x1, out y1, out x2, out y2); + + glyph_width = x2 - x1; + glyph_height = y2 - y1; + + c.save (); + c.scale (glyph_scale * Screen.get_scale (), glyph_scale * Screen.get_scale ()); + + g.add_help_lines (); + + 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); + c.restore (); + + cache = s; + GlyphCanvas.redraw (); + } + + public void draw_background () { + Glyph g; + Font font; + double gx, gy; + double x1, x2, y1, y2; + double scale_box; + double w, h; + double glyph_width, glyph_height; + Surface s; + Context c; + Color color = Color.black (); + + w = width; + h = height; + + scale_box = width / DEFAULT_WIDTH; + + s = Screen.create_background_surface ((int) width, (int) height - 20); + c = new Context (s); + + if (glyphs != null) { // FIXME: lock + draw_glyph_from_font (); } else { - version_menu = new VersionList (new GlyphCollection (character, (!) character.to_string ())); + c.scale (Screen.get_scale (), Screen.get_scale ()); + + c.save (); + Text fallback = new Text (); + fallback.set_use_cache (false); + Theme.text_color (fallback, "Overview Glyph"); + fallback.set_text ((!) character.to_string ()); + double font_size = height * 0.8; + fallback.set_font_size (font_size); + gx = (width - fallback.get_extent ()) / 2.0; + gy = height - 30; + fallback.draw_at_baseline (c, gx, gy); + c.restore (); + + cache = s; + GlyphCanvas.redraw (); } } @@ -143,7 +298,7 @@ if (!is_on_screen (allocation)) { return; } - + cr.save (); Theme.color (cr, "Background 1"); cr.rectangle (x, y, width, height); @@ -157,9 +312,10 @@ cr.stroke (); cr.restore (); - draw_thumbnail (cr, glyphs, x, y + height); draw_caption (cr); draw_menu (cr); + + draw_thumbnail (cr, x, y + height); } public void adjust_scale () { @@ -189,72 +345,15 @@ } } - private void draw_thumbnail (Context cr, GlyphCollection? gl, double x, double y) { - Glyph g; - Font font; - double gx, gy; - double x1, x2, y1, y2; - double scale_box; - double w, h; - double glyph_width, glyph_height; - Surface s; - Context c; - Color color = Color.black (); - - w = width; - h = height; - - scale_box = width / DEFAULT_WIDTH; - - s = Screen.create_background_surface ((int) width, (int) height - 20); - c = new Context (s); - - if (gl != null) { - font = BirdFont.get_current_font (); - g = ((!) gl).get_interpolated_fast (OverviewTools.current_master_size); - - c.save (); - g.boundaries (out x1, out y1, out x2, out y2); - - glyph_width = x2 - x1; - glyph_height = y2 - y1; - - c.save (); - c.scale (glyph_scale * Screen.get_scale (), glyph_scale * Screen.get_scale ()); - - g.add_help_lines (); - - 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); - c.restore (); - } else { - c.scale (Screen.get_scale (), Screen.get_scale ()); - - c.save (); - Text fallback = new Text (); - fallback.set_use_cache (false); - Theme.text_color (fallback, "Overview Glyph"); - fallback.set_text ((!) character.to_string ()); - double font_size = height * 0.8; - fallback.set_font_size (font_size); - - gx = (width - fallback.get_extent ()) / 2.0; - gy = height - 30; - fallback.set_font_size (font_size); - fallback.draw_at_baseline (c, gx, gy); - c.restore (); + private void draw_thumbnail (Context cr, double x, double y) { + if (cache != null) { + cr.save (); + cr.set_antialias (Cairo.Antialias.NONE); + cr.scale (1 / Screen.get_scale (), 1 / Screen.get_scale ()); + cr.set_source_surface ((!) cache, (int) (x * Screen.get_scale ()), (int) ((y - height)) * Screen.get_scale ()); + cr.paint (); + cr.restore (); } - - cr.save (); - cr.set_antialias (Cairo.Antialias.NONE); - cr.scale (1 / Screen.get_scale (), 1 / Screen.get_scale ()); - cr.set_source_surface (s, (int) (x * Screen.get_scale ()), (int) ((y - h)) * Screen.get_scale ()); - cr.paint (); - cr.restore (); } public bool has_icons () { @@ -321,7 +420,7 @@ selected_label_background = (!) cache; - // unselected item without menu icon + // deselected item without menu icon cache = Screen.create_background_surface ((int) width, 20); cc = new Context (cache); cc.scale(Screen.get_scale(), Screen.get_scale());