From ca6a317012a13ee84b58a69e14e74c94c5b158ff Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 09:06:07 -0400 Subject: [PATCH 01/30] Minor --- src/hb-font.cc | 6 +++--- src/hb-font.h | 8 ++++---- src/hb-font.hh | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/hb-font.cc b/src/hb-font.cc index fa5e6614b..2a40aeb57 100644 --- a/src/hb-font.cc +++ b/src/hb-font.cc @@ -807,7 +807,7 @@ hb_font_get_glyph_v_advance (hb_font_t *font, **/ void hb_font_get_glyph_h_advances (hb_font_t* font, - unsigned count, + unsigned int count, const hb_codepoint_t *first_glyph, unsigned glyph_stride, hb_position_t *first_advance, @@ -825,7 +825,7 @@ hb_font_get_glyph_h_advances (hb_font_t* font, **/ void hb_font_get_glyph_v_advances (hb_font_t* font, - unsigned count, + unsigned int count, const hb_codepoint_t *first_glyph, unsigned glyph_stride, hb_position_t *first_advance, @@ -1052,7 +1052,7 @@ hb_font_get_glyph_advance_for_direction (hb_font_t *font, HB_EXTERN void hb_font_get_glyph_advances_for_direction (hb_font_t* font, hb_direction_t direction, - unsigned count, + unsigned int count, const hb_codepoint_t *first_glyph, unsigned glyph_stride, hb_position_t *first_advance, diff --git a/src/hb-font.h b/src/hb-font.h index 48450e99d..6ee6b2a8b 100644 --- a/src/hb-font.h +++ b/src/hb-font.h @@ -133,7 +133,7 @@ typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_h_advance_func_t; typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_v_advance_func_t; typedef void (*hb_font_get_glyph_advances_func_t) (hb_font_t* font, void* font_data, - unsigned count, + unsigned int count, const hb_codepoint_t *first_glyph, unsigned glyph_stride, hb_position_t *first_advance, @@ -423,14 +423,14 @@ hb_font_get_glyph_v_advance (hb_font_t *font, HB_EXTERN void hb_font_get_glyph_h_advances (hb_font_t* font, - unsigned count, + unsigned int count, const hb_codepoint_t *first_glyph, unsigned glyph_stride, hb_position_t *first_advance, unsigned advance_stride); HB_EXTERN void hb_font_get_glyph_v_advances (hb_font_t* font, - unsigned count, + unsigned int count, const hb_codepoint_t *first_glyph, unsigned glyph_stride, hb_position_t *first_advance, @@ -486,7 +486,7 @@ hb_font_get_glyph_advance_for_direction (hb_font_t *font, HB_EXTERN void hb_font_get_glyph_advances_for_direction (hb_font_t* font, hb_direction_t direction, - unsigned count, + unsigned int count, const hb_codepoint_t *first_glyph, unsigned glyph_stride, hb_position_t *first_advance, diff --git a/src/hb-font.hh b/src/hb-font.hh index 47f5261e8..3d00496f9 100644 --- a/src/hb-font.hh +++ b/src/hb-font.hh @@ -377,7 +377,7 @@ struct hb_font_t *y = get_glyph_v_advance (glyph); } inline void get_glyph_advances_for_direction (hb_direction_t direction, - unsigned count, + unsigned int count, const hb_codepoint_t *first_glyph, unsigned glyph_stride, hb_position_t *first_advance, From b314c4e9abf4236c6650a63d1287471b61f64885 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 09:23:51 -0400 Subject: [PATCH 02/30] [font] Add get_nominal_glyphs() callback (note the plural) Unused as of now. To be wired up to normalizer, which would remove overhead and allow hb-ot-font initialization to become a no-op, so we can enable it by default. --- docs/harfbuzz-sections.txt | 3 +++ src/hb-font.cc | 33 +++++++++++++++++++++++++++++++++ src/hb-font.h | 24 ++++++++++++++++++++++++ src/hb-font.hh | 13 +++++++++++++ 4 files changed, 73 insertions(+) diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt index 2ee14a964..4cb8d42be 100644 --- a/docs/harfbuzz-sections.txt +++ b/docs/harfbuzz-sections.txt @@ -237,6 +237,7 @@ hb_font_funcs_set_glyph_v_advance_func hb_font_funcs_set_glyph_v_advances_func hb_font_funcs_set_glyph_v_origin_func hb_font_funcs_set_nominal_glyph_func +hb_font_funcs_set_nominal_glyphs_func hb_font_funcs_set_user_data hb_font_funcs_set_variation_glyph_func hb_font_funcs_t @@ -273,6 +274,8 @@ hb_font_get_glyph_v_origin hb_font_get_glyph_v_origin_func_t hb_font_get_nominal_glyph hb_font_get_nominal_glyph_func_t +hb_font_get_nominal_glyphs +hb_font_get_nominal_glyphs_func_t hb_font_get_parent hb_font_get_ppem hb_font_get_ptem diff --git a/src/hb-font.cc b/src/hb-font.cc index 2a40aeb57..fcf86f07b 100644 --- a/src/hb-font.cc +++ b/src/hb-font.cc @@ -101,9 +101,42 @@ hb_font_get_nominal_glyph_default (hb_font_t *font, hb_codepoint_t *glyph, void *user_data HB_UNUSED) { + if (font->has_nominal_glyphs_func ()) + { + return font->get_nominal_glyphs (1, &unicode, 0, glyph, 0); + } return font->parent->get_nominal_glyph (unicode, glyph); } +#define hb_font_get_nominal_glyphs_nil hb_font_get_nominal_glyphs_default +static unsigned int +hb_font_get_nominal_glyphs_default (hb_font_t *font, + void *font_data HB_UNUSED, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data HB_UNUSED) +{ + if (font->has_nominal_glyph_func ()) + { + for (unsigned int i = 0; i < count; i++) + { + if (!font->get_nominal_glyph (*first_unicode, first_glyph)) + return i; + + first_unicode = &StructAtOffset (first_unicode, unicode_stride); + first_glyph = &StructAtOffset (first_glyph, glyph_stride); + } + return count; + } + + return font->parent->get_nominal_glyphs (count, + first_unicode, unicode_stride, + first_glyph, glyph_stride); +} + static hb_bool_t hb_font_get_variation_glyph_nil (hb_font_t *font HB_UNUSED, void *font_data HB_UNUSED, diff --git a/src/hb-font.h b/src/hb-font.h index 6ee6b2a8b..dc88f26c3 100644 --- a/src/hb-font.h +++ b/src/hb-font.h @@ -125,6 +125,14 @@ typedef hb_bool_t (*hb_font_get_variation_glyph_func_t) (hb_font_t *font, void * hb_codepoint_t *glyph, void *user_data); +typedef unsigned int (*hb_font_get_nominal_glyphs_func_t) (hb_font_t *font, void *font_data, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data); + typedef hb_position_t (*hb_font_get_glyph_advance_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, @@ -220,6 +228,22 @@ hb_font_funcs_set_nominal_glyph_func (hb_font_funcs_t *ffuncs, hb_font_get_nominal_glyph_func_t func, void *user_data, hb_destroy_func_t destroy); +/** + * hb_font_funcs_set_nominal_glyphs_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: REPLACEME + **/ +HB_EXTERN void +hb_font_funcs_set_nominal_glyphs_func (hb_font_funcs_t *ffuncs, + hb_font_get_nominal_glyphs_func_t func, + void *user_data, hb_destroy_func_t destroy); + /** * hb_font_funcs_set_variation_glyph_func: * @ffuncs: font functions. diff --git a/src/hb-font.hh b/src/hb-font.hh index 3d00496f9..ce140ab63 100644 --- a/src/hb-font.hh +++ b/src/hb-font.hh @@ -43,6 +43,7 @@ HB_FONT_FUNC_IMPLEMENT (font_h_extents) \ HB_FONT_FUNC_IMPLEMENT (font_v_extents) \ HB_FONT_FUNC_IMPLEMENT (nominal_glyph) \ + HB_FONT_FUNC_IMPLEMENT (nominal_glyphs) \ HB_FONT_FUNC_IMPLEMENT (variation_glyph) \ HB_FONT_FUNC_IMPLEMENT (glyph_h_advance) \ HB_FONT_FUNC_IMPLEMENT (glyph_v_advance) \ @@ -212,6 +213,18 @@ struct hb_font_t unicode, glyph, klass->user_data.nominal_glyph); } + inline unsigned int get_nominal_glyphs ( unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride) + { + return klass->get.f.nominal_glyphs (this, user_data, + count, + first_unicode, unicode_stride, + first_glyph, glyph_stride, + klass->user_data.nominal_glyphs); + } inline hb_bool_t get_variation_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector, hb_codepoint_t *glyph) From b5371f18effbeb91565fd8c554c120b911641f0b Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 14:12:59 -0400 Subject: [PATCH 03/30] Inline decompose_cluster Towards separating the common case into its own loop. --- src/hb-ot-shape-normalize.cc | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc index 0e13707ed..9436cb5ff 100644 --- a/src/hb-ot-shape-normalize.cc +++ b/src/hb-ot-shape-normalize.cc @@ -264,15 +264,6 @@ decompose_multi_char_cluster (const hb_ot_shape_normalize_context_t *c, unsigned decompose_current_character (c, short_circuit); } -static inline void -decompose_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end, bool might_short_circuit, bool always_short_circuit) -{ - if (likely (c->buffer->idx + 1 == end)) - decompose_current_character (c, might_short_circuit); - else - decompose_multi_char_cluster (c, end, always_short_circuit); -} - static int compare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) @@ -337,7 +328,10 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end])))) break; - decompose_cluster (&c, end, might_short_circuit, always_short_circuit); + if (likely (c.buffer->idx + 1 == end)) + decompose_current_character (&c, might_short_circuit); + else + decompose_multi_char_cluster (&c, end, always_short_circuit); } buffer->swap_buffers (); From 24382debe893450088acd1e4b387ac31145d4553 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 14:33:24 -0400 Subject: [PATCH 04/30] Rewrite main normalizer loop to isolate runs of simple clusters --- src/hb-ot-shape-normalize.cc | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc index 9436cb5ff..98830e499 100644 --- a/src/hb-ot-shape-normalize.cc +++ b/src/hb-ot-shape-normalize.cc @@ -321,18 +321,32 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, buffer->clear_output (); count = buffer->len; - for (buffer->idx = 0; buffer->idx < count && buffer->successful;) + buffer->idx = 0; + do { unsigned int end; for (end = buffer->idx + 1; end < count; end++) - if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end])))) + if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end])))) break; - if (likely (c.buffer->idx + 1 == end)) + if (end < count) + end--; /* Leave one base for the marks to cluster with. */ + + /* From i to end are simple clusters. */ + while (buffer->idx < end && buffer->successful) decompose_current_character (&c, might_short_circuit); - else - decompose_multi_char_cluster (&c, end, always_short_circuit); + + if (buffer->idx == count || !buffer->successful) + break; + + /* Find all the marks now. */ + for (end = buffer->idx + 1; end < count; end++) + if (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end]))) + break; + + decompose_multi_char_cluster (&c, end, always_short_circuit); } + while (buffer->idx < count && buffer->successful); buffer->swap_buffers (); From 9f79365c3b183278d14352ba6241c7d4ec274984 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 14:36:25 -0400 Subject: [PATCH 05/30] Fix warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit How come this one is not generated by clang everything bot?! ../../../test/api/test-multithread.c:37:26: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] static char *font_path = "fonts/Inconsolata-Regular.abc.ttf"; ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ../../../test/api/test-multithread.c:38:21: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] --- test/api/test-multithread.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/api/test-multithread.c b/test/api/test-multithread.c index 58a637cae..af5e8f9a6 100644 --- a/test/api/test-multithread.c +++ b/test/api/test-multithread.c @@ -34,8 +34,8 @@ #include #include -static char *font_path = "fonts/Inconsolata-Regular.abc.ttf"; -static char *text = "abc"; +static const char *font_path = "fonts/Inconsolata-Regular.abc.ttf"; +static const char *text = "abc"; static int num_threads = 30; static int num_iters = 200; From 30c114ffec335770452e60729224b1634586c5b0 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 14:37:08 -0400 Subject: [PATCH 06/30] Avoid sort and recompose stages if all clusters simple Even has measurable speedup... --- src/hb-ot-shape-normalize.cc | 52 ++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc index 98830e499..5df0aa0bc 100644 --- a/src/hb-ot-shape-normalize.cc +++ b/src/hb-ot-shape-normalize.cc @@ -319,6 +319,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, /* First round, decompose */ + bool all_simple = true; buffer->clear_output (); count = buffer->len; buffer->idx = 0; @@ -332,49 +333,53 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, if (end < count) end--; /* Leave one base for the marks to cluster with. */ - /* From i to end are simple clusters. */ + /* From idx to end are simple clusters. */ while (buffer->idx < end && buffer->successful) decompose_current_character (&c, might_short_circuit); if (buffer->idx == count || !buffer->successful) break; + all_simple = false; + /* Find all the marks now. */ for (end = buffer->idx + 1; end < count; end++) if (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end]))) break; + /* idx to end is one non-simple cluster. */ decompose_multi_char_cluster (&c, end, always_short_circuit); } while (buffer->idx < count && buffer->successful); buffer->swap_buffers (); - /* Second round, reorder (inplace) */ - - count = buffer->len; - for (unsigned int i = 0; i < count; i++) + if (!all_simple) { - if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]) == 0) - continue; + count = buffer->len; + for (unsigned int i = 0; i < count; i++) + { + if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]) == 0) + continue; - unsigned int end; - for (end = i + 1; end < count; end++) - if (_hb_glyph_info_get_modified_combining_class (&buffer->info[end]) == 0) - break; + unsigned int end; + for (end = i + 1; end < count; end++) + if (_hb_glyph_info_get_modified_combining_class (&buffer->info[end]) == 0) + break; + + /* We are going to do a O(n^2). Only do this if the sequence is short. */ + if (end - i > HB_OT_SHAPE_COMPLEX_MAX_COMBINING_MARKS) { + i = end; + continue; + } + + buffer->sort (i, end, compare_combining_class); + + if (plan->shaper->reorder_marks) + plan->shaper->reorder_marks (plan, buffer, i, end); - /* We are going to do a O(n^2). Only do this if the sequence is short. */ - if (end - i > HB_OT_SHAPE_COMPLEX_MAX_COMBINING_MARKS) { i = end; - continue; } - - buffer->sort (i, end, compare_combining_class); - - if (plan->shaper->reorder_marks) - plan->shaper->reorder_marks (plan, buffer, i, end); - - i = end; } if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_CGJ) { @@ -393,8 +398,9 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, /* Third round, recompose */ - if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS || - mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT) + if (!all_simple && + (mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS || + mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT)) { /* As noted in the comment earlier, we don't try to combine * ccc=0 chars with their previous Starter. */ From 8008bca83b0bb310fc434dbdd339545af951193b Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 14:38:23 -0400 Subject: [PATCH 07/30] Whitespace --- src/hb-font.hh | 2 +- src/hb-ot-shape-normalize.cc | 52 +++++++++++++++++++----------------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/hb-font.hh b/src/hb-font.hh index ce140ab63..e10d56745 100644 --- a/src/hb-font.hh +++ b/src/hb-font.hh @@ -213,7 +213,7 @@ struct hb_font_t unicode, glyph, klass->user_data.nominal_glyph); } - inline unsigned int get_nominal_glyphs ( unsigned int count, + inline unsigned int get_nominal_glyphs (unsigned int count, const hb_codepoint_t *first_unicode, unsigned int unicode_stride, hb_codepoint_t *first_glyph, diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc index 5df0aa0bc..3d62e44b4 100644 --- a/src/hb-ot-shape-normalize.cc +++ b/src/hb-ot-shape-normalize.cc @@ -320,40 +320,44 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, /* First round, decompose */ bool all_simple = true; - buffer->clear_output (); - count = buffer->len; - buffer->idx = 0; - do { - unsigned int end; - for (end = buffer->idx + 1; end < count; end++) - if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end])))) - break; + buffer->clear_output (); + count = buffer->len; + buffer->idx = 0; + do + { + unsigned int end; + for (end = buffer->idx + 1; end < count; end++) + if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end])))) + break; - if (end < count) - end--; /* Leave one base for the marks to cluster with. */ + if (end < count) + end--; /* Leave one base for the marks to cluster with. */ - /* From idx to end are simple clusters. */ - while (buffer->idx < end && buffer->successful) - decompose_current_character (&c, might_short_circuit); + /* From idx to end are simple clusters. */ + while (buffer->idx < end && buffer->successful) + decompose_current_character (&c, might_short_circuit); - if (buffer->idx == count || !buffer->successful) - break; + if (buffer->idx == count || !buffer->successful) + break; - all_simple = false; + all_simple = false; - /* Find all the marks now. */ - for (end = buffer->idx + 1; end < count; end++) - if (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end]))) - break; + /* Find all the marks now. */ + for (end = buffer->idx + 1; end < count; end++) + if (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end]))) + break; - /* idx to end is one non-simple cluster. */ - decompose_multi_char_cluster (&c, end, always_short_circuit); + /* idx to end is one non-simple cluster. */ + decompose_multi_char_cluster (&c, end, always_short_circuit); + } + while (buffer->idx < count && buffer->successful); + buffer->swap_buffers (); } - while (buffer->idx < count && buffer->successful); - buffer->swap_buffers (); + /* Second round, reorder (inplace) */ + if (!all_simple) { count = buffer->len; From e883f52732a25f5495ec30656489954afd8cc3a4 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 14:50:57 -0400 Subject: [PATCH 08/30] Call get_nominal_glyphs() for runs of simple clusters at a time Even without FT or OT font funcs implementing get_nominal_glyphs(), there's measurable speedup. --- src/hb-buffer.hh | 20 +++++++++++++++++++- src/hb-ot-shape-normalize.cc | 9 +++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/hb-buffer.hh b/src/hb-buffer.hh index 9126822eb..9628ddae0 100644 --- a/src/hb-buffer.hh +++ b/src/hb-buffer.hh @@ -260,7 +260,8 @@ struct hb_buffer_t { if (have_output) { - if (unlikely (out_info != info || out_len != idx)) { + if (out_info != info || out_len != idx) + { if (unlikely (!make_room_for (1, 1))) return; out_info[out_len] = info[idx]; } @@ -269,6 +270,23 @@ struct hb_buffer_t idx++; } + /* Copies n glyphs at idx to output and advance idx. + * If there's no output, just advance idx. */ + inline void + next_glyphs (unsigned int n) + { + if (have_output) + { + if (out_info != info || out_len != idx) + { + if (unlikely (!make_room_for (n, n))) return; + memmove (out_info + out_len, info + idx, n * sizeof (out_info[0])); + } + out_len += n; + } + + idx += n; + } /* Advance idx without copying to output. */ inline void skip_glyph (void) { diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc index 3d62e44b4..a8229a98d 100644 --- a/src/hb-ot-shape-normalize.cc +++ b/src/hb-ot-shape-normalize.cc @@ -335,6 +335,15 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, end--; /* Leave one base for the marks to cluster with. */ /* From idx to end are simple clusters. */ + if (might_short_circuit) + { + unsigned int done = font->get_nominal_glyphs (end - buffer->idx, + &buffer->cur().codepoint, + sizeof (buffer->info[0]), + &buffer->cur().glyph_index(), + sizeof (buffer->info[0])); + buffer->next_glyphs (done); + } while (buffer->idx < end && buffer->successful) decompose_current_character (&c, might_short_circuit); From ec84460e46fdaa5f8a3c16c8d48dabe2b0c869da Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 15:07:04 -0400 Subject: [PATCH 09/30] [ot/ft] Implement get_nominal_glyphs() callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some more measurable speedup. The recent commits' speedups are as follows: Testing with Roboto, ****when disabling kern and liga****: Before: FT --features=-kern,-liga user↦ 0m0.521s OT --features=-liga,-kern user↦ 0m0.568s After: FT --features=-liga,-kern user↦ 0m0.428s OT --features=-liga,-kern user↦ 0m0.470s So, 17% speedup. Note that FT callbacks are faster than OT these days since we added an advance cache to FT. I don't think the difference is enough to justify adding a cache to OT. When not disabling kern, the thing is three times slower, so the speedups are three times less impressive... Still, 5% not bad for a codebase that I otherwise thought is optimized out. Note that, because of this and other optimiztions in our main shaper, disabling kern and liga, the OT shaper is now *faster* than the fallback shaper. So, that's my recommendation to clients that need the absolute fastest... --- src/hb-ft.cc | 26 ++++++++++++++++++++++++++ src/hb-ot-font.cc | 24 ++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/hb-ft.cc b/src/hb-ft.cc index 79ce8f49a..d293693cd 100644 --- a/src/hb-ft.cc +++ b/src/hb-ft.cc @@ -200,6 +200,31 @@ hb_ft_get_nominal_glyph (hb_font_t *font HB_UNUSED, return true; } +static unsigned int +hb_ft_get_nominal_glyphs (hb_font_t *font HB_UNUSED, + void *font_data, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + unsigned int done; + for (done = 0; + done < count && (*first_glyph = FT_Get_Char_Index (ft_font->ft_face, *first_unicode)); + done++) + { + first_unicode = &StructAtOffset (first_unicode, unicode_stride); + first_glyph = &StructAtOffset (first_glyph, glyph_stride); + } + /* We don't need to do ft_font->symbol dance here, since HB calls the singular + * nominal_glyph() for what we don't handle here. */ + return done; +} + + static hb_bool_t hb_ft_get_variation_glyph (hb_font_t *font HB_UNUSED, void *font_data, @@ -444,6 +469,7 @@ static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_tcmap.get_relaxed()->get_nominal_glyph (unicode, glyph); } +static unsigned int +hb_ot_get_nominal_glyphs (hb_font_t *font HB_UNUSED, + void *font_data, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data HB_UNUSED) +{ + const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data; + const OT::cmap_accelerator_t &cmap = *ot_face->cmap.get_relaxed (); + unsigned int done; + for (done = 0; + done < count && cmap.get_nominal_glyph (*first_unicode, first_glyph); + done++) + { + first_unicode = &StructAtOffset (first_unicode, unicode_stride); + first_glyph = &StructAtOffset (first_glyph, glyph_stride); + } + return done; +} + static hb_bool_t hb_ot_get_variation_glyph (hb_font_t *font HB_UNUSED, void *font_data, @@ -187,6 +210,7 @@ static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t Date: Tue, 9 Oct 2018 15:12:09 -0400 Subject: [PATCH 10/30] [ot-font] Don't pre-load cmap table Now that we have get_h_advances() and get_nominal_glyphs() implemented, the overhead of doing a proper atomic load would be once per run, NOT once per glyph. So, no need to pre-load the tables to avoid that overhead. As such, hb_ot_font_set_funcs() has become really cheap. Can *finally* make it be default font functions on all newly created fonts! --- src/hb-ot-font.cc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc index 1498911de..0631453b7 100644 --- a/src/hb-ot-font.cc +++ b/src/hb-ot-font.cc @@ -48,7 +48,7 @@ hb_ot_get_nominal_glyph (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data; - return ot_face->cmap.get_relaxed()->get_nominal_glyph (unicode, glyph); + return ot_face->cmap.get ()->get_nominal_glyph (unicode, glyph); } static unsigned int @@ -62,7 +62,7 @@ hb_ot_get_nominal_glyphs (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data; - const OT::cmap_accelerator_t &cmap = *ot_face->cmap.get_relaxed (); + const OT::cmap_accelerator_t &cmap = *ot_face->cmap.get (); unsigned int done; for (done = 0; done < count && cmap.get_nominal_glyph (*first_unicode, first_glyph); @@ -83,7 +83,7 @@ hb_ot_get_variation_glyph (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data; - return ot_face->cmap.get_relaxed ()->get_variation_glyph (unicode, variation_selector, glyph); + return ot_face->cmap.get ()->get_variation_glyph (unicode, variation_selector, glyph); } static void @@ -257,9 +257,6 @@ hb_ot_font_set_funcs (hb_font_t *font) if (unlikely (!hb_ot_shaper_face_data_ensure (font->face))) return; hb_ot_face_data_t *ot_face = hb_ot_face_data (font->face); - /* Load them lazy. We access it with get_relaxed() for performance. */ - ot_face->cmap.get (); - hb_font_set_funcs (font, _hb_ot_get_font_funcs (), ot_face, From 07899435b8065d494e563f83e0a35300c828eefe Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 15:39:51 -0400 Subject: [PATCH 11/30] Install ot-funcs on newly created funcs **Finally**! Casual users can stop caring about font-funcs completely now, like they haven't been needing to care re unicode-funcs for a few years. --- src/hb-font.cc | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/hb-font.cc b/src/hb-font.cc index fcf86f07b..fd0e097a8 100644 --- a/src/hb-font.cc +++ b/src/hb-font.cc @@ -31,6 +31,8 @@ #include "hb-font.hh" #include "hb-machinery.hh" +#include "hb-ot.h" + /* * hb_font_funcs_t @@ -1304,18 +1306,8 @@ DEFINE_NULL_INSTANCE (hb_font_t) = }; -/** - * hb_font_create: (Xconstructor) - * @face: a face. - * - * - * - * Return value: (transfer full): - * - * Since: 0.9.2 - **/ -hb_font_t * -hb_font_create (hb_face_t *face) +static hb_font_t * +_hb_font_create (hb_face_t *face) { hb_font_t *font; @@ -1334,6 +1326,27 @@ hb_font_create (hb_face_t *face) return font; } +/** + * hb_font_create: (Xconstructor) + * @face: a face. + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_create (hb_face_t *face) +{ + hb_font_t *font = _hb_font_create (face); + + /* Install our in-house, very lightweight, funcs. */ + hb_ot_font_set_funcs (font); + + return font; +} + /** * hb_font_create_sub_font: * @parent: parent font. @@ -1350,7 +1363,7 @@ hb_font_create_sub_font (hb_font_t *parent) if (unlikely (!parent)) parent = hb_font_get_empty (); - hb_font_t *font = hb_font_create (parent->face); + hb_font_t *font = _hb_font_create (parent->face); if (unlikely (hb_object_is_inert (font))) return font; From 7003b601afd02b0ba7e839510a7d0b886da09aaa Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 15:55:26 -0400 Subject: [PATCH 12/30] Minor --- src/hb-ot-shape-complex-arabic.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc index 2cdd7ba8a..0e011e252 100644 --- a/src/hb-ot-shape-complex-arabic.cc +++ b/src/hb-ot-shape-complex-arabic.cc @@ -599,7 +599,7 @@ postprocess_glyphs_arabic (const hb_ot_shape_plan_t *plan, HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); } -/* https://unicode.org/reports/tr53/tr53-1.pdf */ +/* http://www.unicode.org/reports/tr53/ */ static hb_codepoint_t modifier_combining_marks[] = From d18c3c5861d40291077eb8b8667dc2f12b649cf2 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 16:07:55 -0400 Subject: [PATCH 13/30] [ft] Remove (probably) stale comment --- src/hb-ft.cc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/hb-ft.cc b/src/hb-ft.cc index d293693cd..90e8073bf 100644 --- a/src/hb-ft.cc +++ b/src/hb-ft.cc @@ -45,21 +45,16 @@ * In general, this file does a fine job of what it's supposed to do. * There are, however, things that need more work: * - * - I remember seeing FT_Get_Advance() without the NO_HINTING flag to be buggy. - * Have not investigated. - * * - FreeType works in 26.6 mode. Clients can decide to use that mode, and everything * would work fine. However, we also abuse this API for performing in font-space, * but don't pass the correct flags to FreeType. We just abuse the no-hinting mode * for that, such that no rounding etc happens. As such, we don't set ppem, and * pass NO_HINTING as load_flags. Would be much better to use NO_SCALE, and scale - * ourselves, like we do in uniscribe, etc. + * ourselves. * * - We don't handle / allow for emboldening / obliqueing. * * - In the future, we should add constructors to create fonts in font space? - * - * - FT_Load_Glyph() is extremely costly. Do something about it? */ From be2f148da474d6dd30132c22dd467ea33a942edf Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 16:24:50 -0400 Subject: [PATCH 14/30] [ft] Use mutex to lock access to FT_Face Makes our FT-backed hb_font_t safe to use from multiple threads. Still, the underlying FT_Face should NOT be used from other threads by client or other libraries. Maybe I add a lock()/unlock() public API ala PangoFT2 and cairo-ft. Maybe not. --- src/hb-ft.cc | 18 +++++++++++++++++- src/hb-mutex.hh | 8 ++++++++ test/api/test-multithread.c | 7 ++----- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/hb-ft.cc b/src/hb-ft.cc index 90e8073bf..18fb72a73 100644 --- a/src/hb-ft.cc +++ b/src/hb-ft.cc @@ -60,6 +60,7 @@ struct hb_ft_font_t { + mutable hb_mutex_t lock; FT_Face ft_face; int load_flags; bool symbol; /* Whether selected cmap is symbol cmap. */ @@ -77,6 +78,7 @@ _hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref) if (unlikely (!ft_font)) return nullptr; + ft_font->lock.init (); ft_font->ft_face = ft_face; ft_font->symbol = symbol; ft_font->unref = unref; @@ -105,6 +107,8 @@ _hb_ft_font_destroy (void *data) if (ft_font->unref) _hb_ft_face_destroy (ft_font->ft_face); + ft_font->lock.fini (); + free (ft_font); } @@ -172,6 +176,7 @@ hb_ft_get_nominal_glyph (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); unsigned int g = FT_Get_Char_Index (ft_font->ft_face, unicode); if (unlikely (!g)) @@ -206,6 +211,7 @@ hb_ft_get_nominal_glyphs (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); unsigned int done; for (done = 0; done < count && (*first_glyph = FT_Get_Char_Index (ft_font->ft_face, *first_unicode)); @@ -229,6 +235,7 @@ hb_ft_get_variation_glyph (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); unsigned int g = FT_Face_GetCharVariantIndex (ft_font->ft_face, unicode, variation_selector); if (unlikely (!g)) @@ -248,6 +255,7 @@ hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; int load_flags = ft_font->load_flags; int mult = font->x_scale < 0 ? -1 : +1; @@ -285,6 +293,7 @@ hb_ft_get_glyph_v_advance (hb_font_t *font, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); FT_Fixed v; if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags | FT_LOAD_VERTICAL_LAYOUT, &v))) @@ -307,6 +316,7 @@ hb_ft_get_glyph_v_origin (hb_font_t *font, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) @@ -333,6 +343,7 @@ hb_ft_get_glyph_extents (hb_font_t *font, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) @@ -365,6 +376,7 @@ hb_ft_get_glyph_contour_point (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) @@ -390,8 +402,10 @@ hb_ft_get_glyph_name (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; - hb_bool_t ret = !FT_Get_Glyph_Name (ft_font->ft_face, glyph, name, size); + hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size); if (ret && (size && !*name)) ret = false; @@ -406,6 +420,7 @@ hb_ft_get_glyph_from_name (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; if (len < 0) @@ -438,6 +453,7 @@ hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; metrics->ascender = ft_face->size->metrics.ascender; metrics->descender = ft_face->size->metrics.descender; diff --git a/src/hb-mutex.hh b/src/hb-mutex.hh index d8cdf4b64..c49d7a9e5 100644 --- a/src/hb-mutex.hh +++ b/src/hb-mutex.hh @@ -137,5 +137,13 @@ struct hb_mutex_t inline void fini (void) { hb_mutex_impl_finish (&m); } }; +struct hb_lock_t +{ + hb_lock_t (hb_mutex_t &mutex_) : mutex (mutex_) { mutex.lock (); } + ~hb_lock_t (void) { mutex.unlock (); } + private: + hb_mutex_t &mutex; +}; + #endif /* HB_MUTEX_HH */ diff --git a/test/api/test-multithread.c b/test/api/test-multithread.c index af5e8f9a6..779b762dc 100644 --- a/test/api/test-multithread.c +++ b/test/api/test-multithread.c @@ -164,11 +164,8 @@ main (int argc, char **argv) test_body (); - /* hb-font backed by FreeType functions can only be used from - * one thread at a time, because that's FT_Face's MT guarantee. - * So, disable this, even though it works "most of the time". */ - //hb_ft_font_set_funcs (font); - //test_body (); + hb_ft_font_set_funcs (font); + test_body (); hb_buffer_destroy (ref_buffer); From 210f899acad1959d60892538ac1968a36dbbb51b Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 21:56:37 -0400 Subject: [PATCH 15/30] [kern] Sanitize 4 bytes, not 2 --- src/hb-ot-kern-table.hh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh index 0ab9322f5..ee2370201 100644 --- a/src/hb-ot-kern-table.hh +++ b/src/hb-ot-kern-table.hh @@ -430,7 +430,7 @@ struct kern inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.major.sanitize (c)) return_trace (false); + if (!u.version32.sanitize (c)) return_trace (false); switch (u.major) { case 0: return_trace (u.ot.sanitize (c)); case 1: return_trace (u.aat.sanitize (c)); @@ -480,13 +480,13 @@ struct kern protected: union { - HBUINT16 major; HBUINT32 version32; + HBUINT16 major; KernOT ot; KernAAT aat; } u; public: - DEFINE_SIZE_UNION (2, major); + DEFINE_SIZE_UNION (4, version32); }; struct kern_accelerator_t : kern::accelerator_t {}; From 60318f87153b559e5da103f0bfcce6bad8bab3b0 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 22:25:53 -0400 Subject: [PATCH 16/30] Minor --- src/hb-mutex.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hb-mutex.hh b/src/hb-mutex.hh index c49d7a9e5..75b89addb 100644 --- a/src/hb-mutex.hh +++ b/src/hb-mutex.hh @@ -139,8 +139,8 @@ struct hb_mutex_t struct hb_lock_t { - hb_lock_t (hb_mutex_t &mutex_) : mutex (mutex_) { mutex.lock (); } - ~hb_lock_t (void) { mutex.unlock (); } + inline hb_lock_t (hb_mutex_t &mutex_) : mutex (mutex_) { mutex.lock (); } + inline ~hb_lock_t (void) { mutex.unlock (); } private: hb_mutex_t &mutex; }; From 53e55945000347fb19168bb4c13a470d30d46251 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 22:35:22 -0400 Subject: [PATCH 17/30] [kerx] Implement Format0 apply() Not hooked up to be called yet. --- src/hb-aat-layout-common.hh | 6 ++++-- src/hb-aat-layout-kerx-table.hh | 29 ++++++++++++++++++++++++++--- src/hb-aat-layout.cc | 6 ++++-- src/hb-aat-layout.hh | 12 +++++++----- src/hb-ot-shape.cc | 2 +- 5 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/hb-aat-layout-common.hh b/src/hb-aat-layout-common.hh index d6e597a40..22c84255b 100644 --- a/src/hb-aat-layout-common.hh +++ b/src/hb-aat-layout-common.hh @@ -518,6 +518,7 @@ struct hb_aat_apply_context_t : static return_t default_return_value (void) { return false; } bool stop_sublookup_iteration (return_t r) const { return r; } + hb_ot_shape_plan_t *plan; hb_font_t *font; hb_face_t *face; hb_buffer_t *buffer; @@ -527,10 +528,11 @@ struct hb_aat_apply_context_t : unsigned int lookup_index; unsigned int debug_depth; - inline hb_aat_apply_context_t (hb_font_t *font_, + inline hb_aat_apply_context_t (hb_ot_shape_plan_t *plan_, + hb_font_t *font_, hb_buffer_t *buffer_, hb_blob_t *table) : - font (font_), face (font->face), buffer (buffer_), + plan (plan_), font (font_), face (font->face), buffer (buffer_), sanitizer (), lookup_index (0), debug_depth (0) { sanitizer.init (table); diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh index 49d188c36..42181bf64 100644 --- a/src/hb-aat-layout-kerx-table.hh +++ b/src/hb-aat-layout-kerx-table.hh @@ -59,7 +59,9 @@ struct KerxSubTableFormat0 { TRACE_APPLY (this); - /* TODO */ + hb_kern_machine_t machine (*this); + + machine.kern (c->font, c->buffer, c->plan->kern_mask); return_trace (true); } @@ -111,8 +113,6 @@ struct KerxSubTableFormat2 unsigned int r = *(this+rightClassTable).get_value (right, num_glyphs); unsigned int offset = l + r; const FWORD *arr = &(this+array); - if (unlikely ((const void *) arr < (const void *) this || (const void *) arr >= (const void *) end)) - return 0; const FWORD *v = &StructAtOffset (arr, offset); if (unlikely ((const void *) v < (const void *) arr || (const void *) (v + 1) > (const void *) end)) return 0; @@ -124,6 +124,13 @@ struct KerxSubTableFormat2 TRACE_APPLY (this); /* TODO */ +#if 0 + accelerator_t accel (*this, + c->blob->data + c->blob->len, + c->face->get_num_glyphs ()); + hb_kern_machine_t machine (accel); + machine.kern (c->font, c->buffer, c->plan->kern_mask); +#endif return_trace (true); } @@ -138,6 +145,22 @@ struct KerxSubTableFormat2 array.sanitize (c, this))); } + struct accelerator_t + { + const KerxSubTableFormat2 &table; + const char *end; + unsigned int num_glyphs; + + inline accelerator_t (const KerxSubTableFormat2 &table_, + const char *end_, unsigned int num_glyphs_) + : table (table_), end (end_), num_glyphs (num_glyphs_) {} + + inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + return table.get_kerning (left, right, end, num_glyphs); + } + }; + protected: HBUINT32 rowWidth; /* The width, in bytes, of a row in the table. */ LOffsetTo > diff --git a/src/hb-aat-layout.cc b/src/hb-aat-layout.cc index faf418d6e..5cd9621f1 100644 --- a/src/hb-aat-layout.cc +++ b/src/hb-aat-layout.cc @@ -62,12 +62,14 @@ hb_aat_layout_has_substitution (hb_face_t *face) } void -hb_aat_layout_substitute (hb_font_t *font, hb_buffer_t *buffer) +hb_aat_layout_substitute (hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) { hb_blob_t *blob; const AAT::morx& morx = _get_morx (font->face, &blob); - AAT::hb_aat_apply_context_t c (font, buffer, blob); + AAT::hb_aat_apply_context_t c (plan, font, buffer, blob); morx.apply (&c); } diff --git a/src/hb-aat-layout.hh b/src/hb-aat-layout.hh index 553832f1e..8b12833d9 100644 --- a/src/hb-aat-layout.hh +++ b/src/hb-aat-layout.hh @@ -29,17 +29,19 @@ #include "hb.hh" -#include "hb-font.hh" -#include "hb-buffer.hh" -#include "hb-open-type.hh" +#include "hb-ot-shape.hh" HB_INTERNAL hb_bool_t hb_aat_layout_has_substitution (hb_face_t *face); HB_INTERNAL void -hb_aat_layout_substitute (hb_font_t *font, hb_buffer_t *buffer); +hb_aat_layout_substitute (hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); HB_INTERNAL void -hb_aat_layout_position (hb_font_t *font, hb_buffer_t *buffer); +hb_aat_layout_position (hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); #endif /* HB_AAT_LAYOUT_HH */ diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc index a9c0844e8..6753ceb29 100644 --- a/src/hb-ot-shape.cc +++ b/src/hb-ot-shape.cc @@ -668,7 +668,7 @@ hb_ot_substitute_complex (const hb_ot_shape_context_t *c) hb_synthesize_glyph_classes (c); if (unlikely (c->plan->apply_morx)) - hb_aat_layout_substitute (c->font, c->buffer); + hb_aat_layout_substitute (c->plan, c->font, c->buffer); else c->plan->substitute (c->font, buffer); } From 8bff1d2994876a5202c8605d1cc37522431c6c84 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 22:39:31 -0400 Subject: [PATCH 18/30] [kern] Minor --- src/hb-ot-kern-table.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh index ee2370201..04be9827b 100644 --- a/src/hb-ot-kern-table.hh +++ b/src/hb-ot-kern-table.hh @@ -283,7 +283,7 @@ struct KernSubTableWrapper TRACE_SANITIZE (this); return_trace (c->check_struct (thiz()) && thiz()->length >= T::min_size && - c->check_array (thiz(), thiz()->length, 1) && + c->check_range (thiz(), thiz()->length) && thiz()->subtable.sanitize (c, thiz()->format)); } }; From 4df8eb200def767d342762654b96d5d9314e5b21 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 22:46:12 -0400 Subject: [PATCH 19/30] [kern] Use kern subtable length for sanitizing in the accelerator --- src/hb-ot-kern-table.hh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh index 04be9827b..f9aec05e9 100644 --- a/src/hb-ot-kern-table.hh +++ b/src/hb-ot-kern-table.hh @@ -294,7 +294,7 @@ struct KernTable /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ inline const T* thiz (void) const { return static_cast (this); } - inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, unsigned int table_length) const + inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const { int v = 0; const typename T::SubTableWrapper *st = CastP (&thiz()->dataZ); @@ -303,7 +303,7 @@ struct KernTable { if (st->is_override ()) v = 0; - v += st->get_h_kerning (left, right, table_length + (const char *) this); + v += st->get_h_kerning (left, right, st->length + (const char *) st); st = &StructAfter (*st); } return v; @@ -337,6 +337,7 @@ struct KernOT : KernTable struct SubTableWrapper : KernSubTableWrapper { + friend struct KernTable; friend struct KernSubTableWrapper; enum Coverage @@ -378,6 +379,7 @@ struct KernAAT : KernTable struct SubTableWrapper : KernSubTableWrapper { + friend struct KernTable; friend struct KernSubTableWrapper; enum Coverage @@ -418,11 +420,11 @@ struct kern inline bool has_data (void) const { return u.version32 != 0; } - inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, unsigned int table_length) const + inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const { switch (u.major) { - case 0: return u.ot.get_h_kerning (left, right, table_length); - case 1: return u.aat.get_h_kerning (left, right, table_length); + case 0: return u.ot.get_h_kerning (left, right); + case 1: return u.aat.get_h_kerning (left, right); default:return 0; } } @@ -444,7 +446,6 @@ struct kern { blob = hb_sanitize_context_t().reference_table (face); table = blob->as (); - table_length = blob->length; } inline void fini (void) { @@ -455,7 +456,7 @@ struct kern { return table->has_data (); } inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const - { return table->get_h_kerning (left, right, table_length); } + { return table->get_h_kerning (left, right); } inline int get_kerning (hb_codepoint_t first, hb_codepoint_t second) const { return get_h_kerning (first, second); } @@ -475,7 +476,6 @@ struct kern private: hb_blob_t *blob; const kern *table; - unsigned int table_length; }; protected: From 4b461785bdf356e4b5586cb6e5b226f47da04b7d Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 22:46:30 -0400 Subject: [PATCH 20/30] Fix check --- src/hb-aat-layout-kerx-table.hh | 2 ++ src/hb-aat-layout.cc | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh index 42181bf64..c73183473 100644 --- a/src/hb-aat-layout-kerx-table.hh +++ b/src/hb-aat-layout-kerx-table.hh @@ -113,6 +113,8 @@ struct KerxSubTableFormat2 unsigned int r = *(this+rightClassTable).get_value (right, num_glyphs); unsigned int offset = l + r; const FWORD *arr = &(this+array); + if (unlikely ((const void *) arr < (const void *) this || (const void *) arr >= (const void *) end)) + return 0; const FWORD *v = &StructAtOffset (arr, offset); if (unlikely ((const void *) v < (const void *) arr || (const void *) (v + 1) > (const void *) end)) return 0; diff --git a/src/hb-aat-layout.cc b/src/hb-aat-layout.cc index 5cd9621f1..71932e798 100644 --- a/src/hb-aat-layout.cc +++ b/src/hb-aat-layout.cc @@ -74,7 +74,9 @@ hb_aat_layout_substitute (hb_ot_shape_plan_t *plan, } void -hb_aat_layout_position (hb_font_t *font, hb_buffer_t *buffer) +hb_aat_layout_position (hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) { #if 0 hb_blob_t *blob; From c66f7f8c5deaac109e34bf65fc16cff92b74e69a Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 22:49:33 -0400 Subject: [PATCH 21/30] [kerx] Implement Format2 apply() Still, not hooked. --- src/hb-aat-layout-kerx-table.hh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh index c73183473..feaee7085 100644 --- a/src/hb-aat-layout-kerx-table.hh +++ b/src/hb-aat-layout-kerx-table.hh @@ -125,14 +125,11 @@ struct KerxSubTableFormat2 { TRACE_APPLY (this); - /* TODO */ -#if 0 accelerator_t accel (*this, - c->blob->data + c->blob->len, + c->sanitizer.end, /* XXX Use SubTable length? */ c->face->get_num_glyphs ()); hb_kern_machine_t machine (accel); machine.kern (c->font, c->buffer, c->plan->kern_mask); -#endif return_trace (true); } From 27db859416c0362f211e2b42dc9a2ebb53b0e0f4 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 22:52:41 -0400 Subject: [PATCH 22/30] [kern/kerx] Adjust bounds check --- src/hb-aat-layout-kerx-table.hh | 8 +++----- src/hb-ot-kern-table.hh | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh index feaee7085..2c84758b4 100644 --- a/src/hb-aat-layout-kerx-table.hh +++ b/src/hb-aat-layout-kerx-table.hh @@ -112,11 +112,9 @@ struct KerxSubTableFormat2 unsigned int l = *(this+leftClassTable).get_value (left, num_glyphs); unsigned int r = *(this+rightClassTable).get_value (right, num_glyphs); unsigned int offset = l + r; - const FWORD *arr = &(this+array); - if (unlikely ((const void *) arr < (const void *) this || (const void *) arr >= (const void *) end)) - return 0; - const FWORD *v = &StructAtOffset (arr, offset); - if (unlikely ((const void *) v < (const void *) arr || (const void *) (v + 1) > (const void *) end)) + const FWORD *v = &StructAtOffset (&(this+array), offset); + if (unlikely ((const char *) v < (const char *) &array || + (const char *) v > (const char *) end - 2)) return 0; return *v; } diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh index f9aec05e9..c4e53fa6e 100644 --- a/src/hb-ot-kern-table.hh +++ b/src/hb-ot-kern-table.hh @@ -194,11 +194,9 @@ struct KernSubTableFormat2 unsigned int l = (this+leftClassTable).get_class (left); unsigned int r = (this+rightClassTable).get_class (right); unsigned int offset = l + r; - const FWORD *arr = &(this+array); - if (unlikely ((const void *) arr < (const void *) this || (const void *) arr >= (const void *) end)) - return 0; - const FWORD *v = &StructAtOffset (arr, offset); - if (unlikely ((const void *) v < (const void *) arr || (const void *) (v + 1) > (const void *) end)) + const FWORD *v = &StructAtOffset (&(this+array), offset); + if (unlikely ((const char *) v < (const char *) &array || + (const char *) v > (const char *) end - 2)) return 0; return *v; } From db9600bbe15035cea6c2d4e8e9d184a5e23e357e Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 22:55:47 -0400 Subject: [PATCH 23/30] [kerx] Remove junk --- src/hb-aat-layout-kerx-table.hh | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh index 2c84758b4..590ec4917 100644 --- a/src/hb-aat-layout-kerx-table.hh +++ b/src/hb-aat-layout-kerx-table.hh @@ -292,22 +292,6 @@ public: DEFINE_SIZE_MIN (12); }; -struct SubtableXXX -{ - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); - } - - protected: - HBUINT32 length; - HBUINT32 coverage; - HBUINT32 tupleCount; - public: - DEFINE_SIZE_STATIC (12); -}; - /* * The 'kerx' Table From ad763074861da60ed51211931788ca5b27fc1512 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 22:57:00 -0400 Subject: [PATCH 24/30] [kerx] Comment --- src/hb-aat-layout-kerx-table.hh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh index 590ec4917..dd6e81768 100644 --- a/src/hb-aat-layout-kerx-table.hh +++ b/src/hb-aat-layout-kerx-table.hh @@ -328,7 +328,10 @@ struct kerx if (reverse) c->buffer->reverse (); - /* XXX Reverse-kern is not working yet... */ + /* XXX Reverse-kern is not working yet... + * hb_kern_machine_t would need to know that it's reverse-kerning. + * Or better yet, make it work in reverse as well, so we don't have + * to reverse and reverse back? */ table->dispatch (c); if (reverse) From 329f2401082011007d9ce12b15ce0225cd267c57 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 23:02:53 -0400 Subject: [PATCH 25/30] [aat] Set embedded sanitizer max ops really high Since we consume it legitimately during shaping. --- src/hb-aat-layout-common.hh | 1 + src/hb-machinery.hh | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/hb-aat-layout-common.hh b/src/hb-aat-layout-common.hh index 22c84255b..3d562e795 100644 --- a/src/hb-aat-layout-common.hh +++ b/src/hb-aat-layout-common.hh @@ -538,6 +538,7 @@ struct hb_aat_apply_context_t : sanitizer.init (table); sanitizer.set_num_glyphs (face->get_num_glyphs ()); sanitizer.start_processing (); + sanitizer.set_max_ops ((unsigned int) -1); } inline void set_lookup_index (unsigned int i) { lookup_index = i; } diff --git a/src/hb-machinery.hh b/src/hb-machinery.hh index 9c73df2ca..5eaec759c 100644 --- a/src/hb-machinery.hh +++ b/src/hb-machinery.hh @@ -252,6 +252,8 @@ struct hb_sanitize_context_t : } inline unsigned int get_num_glyphs (void) { return num_glyphs; } + inline void set_max_ops (unsigned int max_ops_) { max_ops = max_ops_; } + inline void start_processing (void) { this->start = this->blob->data; From 948f59a13a4c643ae310f5fc643e29fefd6c3787 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 23:07:47 -0400 Subject: [PATCH 26/30] [kerx] Use subtable range for runtime checks --- src/hb-aat-layout-kerx-table.hh | 4 +++- src/hb-machinery.hh | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh index dd6e81768..c7cb367f6 100644 --- a/src/hb-aat-layout-kerx-table.hh +++ b/src/hb-aat-layout-kerx-table.hh @@ -124,7 +124,7 @@ struct KerxSubTableFormat2 TRACE_APPLY (this); accelerator_t accel (*this, - c->sanitizer.end, /* XXX Use SubTable length? */ + c->sanitizer.end, c->face->get_num_glyphs ()); hb_kern_machine_t machine (accel); machine.kern (c->font, c->buffer, c->plan->kern_mask); @@ -328,6 +328,8 @@ struct kerx if (reverse) c->buffer->reverse (); + c->sanitizer.set_object (*table); + /* XXX Reverse-kern is not working yet... * hb_kern_machine_t would need to know that it's reverse-kerning. * Or better yet, make it work in reverse as well, so we don't have diff --git a/src/hb-machinery.hh b/src/hb-machinery.hh index 5eaec759c..98808894b 100644 --- a/src/hb-machinery.hh +++ b/src/hb-machinery.hh @@ -254,6 +254,14 @@ struct hb_sanitize_context_t : inline void set_max_ops (unsigned int max_ops_) { max_ops = max_ops_; } + template + inline void set_object (const T& obj) + { + this->start = (const char *) &obj; + this->end = (const char *) &obj + obj.get_size (); + assert (this->start <= this->end); /* Must not overflow. */ + } + inline void start_processing (void) { this->start = this->blob->data; From d35315cc028e70dd1b3ffc8cb079a2336b22a0c3 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 23:17:32 -0400 Subject: [PATCH 27/30] [aat] Fixup recent commit For 329f2401082011007d9ce12b15ce0225cd267c57 max_ops is signed. --- src/hb-aat-layout-common.hh | 2 +- src/hb-machinery.hh | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/hb-aat-layout-common.hh b/src/hb-aat-layout-common.hh index 3d562e795..5845ab516 100644 --- a/src/hb-aat-layout-common.hh +++ b/src/hb-aat-layout-common.hh @@ -538,7 +538,7 @@ struct hb_aat_apply_context_t : sanitizer.init (table); sanitizer.set_num_glyphs (face->get_num_glyphs ()); sanitizer.start_processing (); - sanitizer.set_max_ops ((unsigned int) -1); + sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX); } inline void set_lookup_index (unsigned int i) { lookup_index = i; } diff --git a/src/hb-machinery.hh b/src/hb-machinery.hh index 98808894b..8feb3773c 100644 --- a/src/hb-machinery.hh +++ b/src/hb-machinery.hh @@ -217,6 +217,9 @@ struct hb_dispatch_context_t #ifndef HB_SANITIZE_MAX_OPS_MIN #define HB_SANITIZE_MAX_OPS_MIN 16384 #endif +#ifndef HB_SANITIZE_MAX_OPS_MAX +#define HB_SANITIZE_MAX_OPS_MAX 0x3FFFFFFF +#endif struct hb_sanitize_context_t : hb_dispatch_context_t @@ -252,7 +255,7 @@ struct hb_sanitize_context_t : } inline unsigned int get_num_glyphs (void) { return num_glyphs; } - inline void set_max_ops (unsigned int max_ops_) { max_ops = max_ops_; } + inline void set_max_ops (int max_ops_) { max_ops = max_ops_; } template inline void set_object (const T& obj) From 54c9ecb92d196e62901eef3f8bc025c024ed16bb Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 23:18:19 -0400 Subject: [PATCH 28/30] [morx] Use subtable range for embedded sanitizer here as well --- src/hb-aat-layout-morx-table.hh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hb-aat-layout-morx-table.hh b/src/hb-aat-layout-morx-table.hh index cf08d7961..68d82dd10 100644 --- a/src/hb-aat-layout-morx-table.hh +++ b/src/hb-aat-layout-morx-table.hh @@ -886,6 +886,8 @@ struct Chain if (reverse) c->buffer->reverse (); + c->sanitizer.set_object (*subtable); + subtable->dispatch (c); if (reverse) From 362d3241195bb7054c395fb4b029b6d55da4612a Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 23:27:00 -0400 Subject: [PATCH 29/30] [aat] Rename --- src/hb-aat-layout-kerx-table.hh | 4 ++-- src/hb-aat-layout-morx-table.hh | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh index c7cb367f6..d6747f9a8 100644 --- a/src/hb-aat-layout-kerx-table.hh +++ b/src/hb-aat-layout-kerx-table.hh @@ -242,7 +242,7 @@ struct KerxTable Vertical = 0x80000000, /* Set if table has vertical kerning values. */ CrossStream = 0x40000000, /* Set if table has cross-stream kerning values. */ Variation = 0x20000000, /* Set if table has variation kerning values. */ - ProcessDirection = 0x10000000, /* If clear, process the glyphs forwards, that + Backwards = 0x10000000, /* If clear, process the glyphs forwards, that * is, from first to last in the glyph stream. * If we, process them from last to first. * This flag only applies to state-table based @@ -319,7 +319,7 @@ struct kerx if (table->coverage & KerxTable::CrossStream) goto skip; /* We do NOT handle cross-stream kerning. */ - reverse = bool (table->coverage & KerxTable::ProcessDirection) != + reverse = bool (table->coverage & KerxTable::Backwards) != HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); if (!c->buffer->message (c->font, "start kerx subtable %d", c->lookup_index)) diff --git a/src/hb-aat-layout-morx-table.hh b/src/hb-aat-layout-morx-table.hh index 68d82dd10..0020750ce 100644 --- a/src/hb-aat-layout-morx-table.hh +++ b/src/hb-aat-layout-morx-table.hh @@ -752,7 +752,7 @@ struct ChainSubtable Vertical = 0x80000000, /* If set, this subtable will only be applied * to vertical text. If clear, this subtable * will only be applied to horizontal text. */ - Descending = 0x40000000, /* If set, this subtable will process glyphs + Backwards = 0x40000000, /* If set, this subtable will process glyphs * in descending order. If clear, it will * process the glyphs in ascending order. */ AllDirections = 0x20000000, /* If set, this subtable will be applied to @@ -876,8 +876,8 @@ struct Chain may be right-to-left or left-to-right). */ reverse = subtable->coverage & ChainSubtable::Logical ? - bool (subtable->coverage & ChainSubtable::Descending) : - bool (subtable->coverage & ChainSubtable::Descending) != + bool (subtable->coverage & ChainSubtable::Backwards) : + bool (subtable->coverage & ChainSubtable::Backwards) != HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index)) From 0537a40193e803d50a99cd6b993d6d9301e84ebf Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 9 Oct 2018 23:35:07 -0400 Subject: [PATCH 30/30] [kerx] Comment --- src/hb-aat-layout-kerx-table.hh | 2 +- src/hb-ot-kern-table.hh | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh index d6747f9a8..e5934c39f 100644 --- a/src/hb-aat-layout-kerx-table.hh +++ b/src/hb-aat-layout-kerx-table.hh @@ -317,7 +317,7 @@ struct kerx goto skip; if (table->coverage & KerxTable::CrossStream) - goto skip; /* We do NOT handle cross-stream kerning. */ + goto skip; /* We do NOT handle cross-stream kerning. None of Apple fonts use it. */ reverse = bool (table->coverage & KerxTable::Backwards) != HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh index c4e53fa6e..ccb666e8c 100644 --- a/src/hb-ot-kern-table.hh +++ b/src/hb-ot-kern-table.hh @@ -463,6 +463,7 @@ struct kern hb_buffer_t *buffer, hb_mask_t kern_mask) const { + /* We only apply horizontal kerning in this table. */ if (!HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) return;