From d106900bfd61ed45fbd4ffd93875d167d86e01e4 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Tue, 28 Jan 2020 15:26:13 +0330 Subject: [PATCH] [draw][glyf] Implement quadratic to cubic call translation --- src/hb-draw.cc | 7 +---- src/hb-draw.h | 3 +++ src/hb-ot-glyf-table.hh | 58 ++++++++++++++++++++++++++++++++++++----- test/api/test-draw.c | 27 +++++++++++++++++-- 4 files changed, 81 insertions(+), 14 deletions(-) diff --git a/src/hb-draw.cc b/src/hb-draw.cc index ca34b71b0..84ece13da 100644 --- a/src/hb-draw.cc +++ b/src/hb-draw.cc @@ -123,11 +123,6 @@ _move_to_nil (hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, void * static void _line_to_nil (hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, void *user_data HB_UNUSED) {} -static void -_quadratic_to_nil (hb_position_t control_x HB_UNUSED, hb_position_t control_y HB_UNUSED, - hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, - void *user_data HB_UNUSED) {} - static void _cubic_to_nil (hb_position_t control1_x HB_UNUSED, hb_position_t control1_y HB_UNUSED, hb_position_t control2_x HB_UNUSED, hb_position_t control2_y HB_UNUSED, @@ -153,7 +148,7 @@ hb_draw_funcs_create () funcs->move_to = (hb_draw_move_to_func_t) _move_to_nil; funcs->line_to = (hb_draw_line_to_func_t) _line_to_nil; - funcs->quadratic_to = (hb_draw_quadratic_to_func_t) _quadratic_to_nil; + funcs->quadratic_to = nullptr; funcs->cubic_to = (hb_draw_cubic_to_func_t) _cubic_to_nil; funcs->close_path = (hb_draw_close_path_func_t) _close_path_nil; return funcs; diff --git a/src/hb-draw.h b/src/hb-draw.h index 5b9414adb..165d7ab2b 100644 --- a/src/hb-draw.h +++ b/src/hb-draw.h @@ -49,6 +49,9 @@ typedef void (*hb_draw_close_path_func_t) (void *user_data); * * Glyph decompose callbacks. * + * _move_to, _line_to and _cubic_to are nessecary to be defined but we + * can translate _quadratic_to calls to _cubic_to in case isn't defined. + * * Since: REPLACEME **/ typedef struct hb_draw_funcs_t hb_draw_funcs_t; diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index f9294602e..3eb2b56d6 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -1043,6 +1043,36 @@ struct glyf add_gid_and_children (item.glyphIndex, gids_to_retain, depth); } + static void + _normal_quadratic_to_call (hb_font_t *font, const hb_draw_funcs_t *funcs, + float from_x HB_UNUSED, float from_y HB_UNUSED, + float control_x, float control_y, + float to_x, float to_y, + void *user_data) + { + funcs->quadratic_to (font->em_scalef_x (control_x), font->em_scalef_y (control_y), + font->em_scalef_x (to_x), font->em_scalef_y (to_y), + user_data); + } + + static void + _translate_quadratic_to_cubic (hb_font_t *font, const hb_draw_funcs_t *funcs, + float from_x, float from_y, + float control_x, float control_y, + float to_x, float to_y, + void *user_data) + { + /* based on https://github.com/fonttools/fonttools/blob/a37dab3/Lib/fontTools/pens/basePen.py#L218 */ + float mid1_x = from_x + 0.6666666667f * (control_x - from_x); + float mid1_y = from_y + 0.6666666667f * (control_y - from_y); + float mid2_x = to_x + 0.6666666667f * (control_x - to_x); + float mid2_y = to_y + 0.6666666667f * (control_y - to_y); + funcs->cubic_to (font->em_scalef_x (mid1_x), font->em_scalef_y (mid1_y), + font->em_scalef_x (mid2_x), font->em_scalef_y (mid2_y), + font->em_scalef_x (to_x), font->em_scalef_y (to_y), + user_data); + } + bool get_path (hb_font_t *font, hb_codepoint_t gid, const hb_draw_funcs_t *funcs, void *user_data) const @@ -1055,10 +1085,15 @@ struct glyf if (unlikely (!get_points (font, gid, all_points))) return false; hb_array_t points = all_points.sub_array (0, all_points.length - 4); + void (*quad_to) (hb_font_t *, const hb_draw_funcs_t *, + float, float, float, float, float, float, + void *) = funcs->quadratic_to ? _normal_quadratic_to_call : _translate_quadratic_to_cubic; + unsigned contour_start = 0; /* Learnt from https://github.com/opentypejs/opentype.js/blob/4e0bb99/src/tables/glyf.js#L222 */ while (contour_start < points.length) { + float prev_x = 0; float prev_y = 0; unsigned contour_length = 0; for (unsigned i = contour_start; i < points.length; ++i) { @@ -1070,15 +1105,23 @@ struct glyf contour_point_t *next = &points[contour_start]; if (curr->flag & Glyph::FLAG_ON_CURVE) - funcs->move_to (font->em_scalef_x (curr->x), font->em_scalef_y (curr->y), user_data); + { + prev_x = curr->x; prev_y = curr->y; + funcs->move_to (font->em_scalef_x (prev_x), font->em_scalef_y (prev_y), user_data); + } else { if (next->flag & Glyph::FLAG_ON_CURVE) - funcs->move_to (font->em_scalef_x (next->x), font->em_scalef_y (next->y), user_data); + { + prev_x = next->x; prev_y = next->y; + funcs->move_to (font->em_scalef_x (prev_x), font->em_scalef_y (prev_y), user_data); + } else + { + prev_x = (curr->x + next->x) / 2.f; prev_y = (curr->y + next->y) / 2.f; /* If both first and last points are off-curve, start at their middle. */ - funcs->move_to (font->em_scalef_x ((curr->x + next->x) / 2.f), - font->em_scalef_y ((curr->y + next->y) / 2.f), user_data); + funcs->move_to (font->em_scalef_x (prev_x), font->em_scalef_y (prev_y), user_data); + } } for (unsigned i = 0; i < contour_length; ++i) @@ -1087,14 +1130,17 @@ struct glyf next = &points[contour_start + ((i + 1) % contour_length)]; if (curr->flag & Glyph::FLAG_ON_CURVE) + { + prev_x = curr->x; prev_y = curr->y; funcs->line_to (font->em_scalef_x (curr->x), font->em_scalef_y (curr->y), user_data); + } else { float to_x, to_y; if (next->flag & Glyph::FLAG_ON_CURVE) { to_x = next->x; to_y = next->y; } else { to_x = (curr->x + next->x) / 2.f; to_y = (curr->y + next->y) / 2.f; } - funcs->quadratic_to (font->em_scalef_x (curr->x), font->em_scalef_y (curr->y), - font->em_scalef_x (to_x), font->em_scalef_y (to_y), user_data); + quad_to (font, funcs, prev_x, prev_y, curr->x, curr->y, to_x, to_y, user_data); + prev_x = to_x; prev_y = to_y; } } contour_start += contour_length; diff --git a/test/api/test-draw.c b/test/api/test-draw.c index a86e863a4..086f4e710 100644 --- a/test/api/test-draw.c +++ b/test/api/test-draw.c @@ -160,6 +160,7 @@ close_path (user_data_t *user_data) } static hb_draw_funcs_t *funcs; +static hb_draw_funcs_t *funcs2; /* this one translates quadratic calls to cubic ones */ static void test_hb_glyph_empty (void) @@ -183,6 +184,7 @@ test_hb_glyph_glyf (void) user_data.consumed = 0; g_assert (!hb_font_draw_glyph (font, 4, funcs, &user_data)); + user_data.consumed = 0; g_assert (hb_font_draw_glyph (font, 3, funcs, &user_data)); char expected[] = "M275,442L275,442Q232,442 198,420Q164,397 145,353Q126,309 126,245L126,245" @@ -193,6 +195,20 @@ test_hb_glyph_glyf (void) "Q378,321 367,334Q355,347 350,366L350,366L325,454L371,417Q346,430 321,436Q296,442 275,442Z"; g_assert_cmpmem (str, user_data.consumed, expected, sizeof (expected) - 1); + /* Test translating quadratic calls to cubic by a _draw_funcs_t that doesn't set the callback */ + user_data.consumed = 0; + g_assert (hb_font_draw_glyph (font, 3, funcs2, &user_data)); + char expected2[] = "M275,442L275,442C246,442 221,435 198,420C175,405 158,382 145,353C132,324 126,288 126,245" + "L126,245C126,203 133,168 147,139C160,110 179,88 204,73C228,58 256,50 287,50L287,50" + "C316,50 342,57 367,70C392,83 412,103 427,128L427,128L451,116C438,75 415,43 384,21" + "C352,-2 313,-13 266,-13L266,-13C221,-13 181,-3 148,18C114,38 88,67 70,104" + "C52,141 43,185 43,236L43,236C43,288 54,333 76,371C97,408 125,437 160,457" + "C195,477 232,487 272,487L272,487C301,487 329,481 354,470C379,459 400,443 417,424" + "C434,405 444,383 448,358L448,358C443,333 428,321 403,321L403,321" + "C386,321 374,325 367,334C359,343 353,353 350,366L350,366L325,454" + "L371,417C354,426 338,432 321,436C304,440 289,442 275,442Z"; + g_assert_cmpmem (str, user_data.consumed, expected2, sizeof (expected2) - 1); + hb_variation_t var; var.tag = HB_TAG ('w','g','h','t'); var.value = 800; @@ -200,13 +216,13 @@ test_hb_glyph_glyf (void) user_data.consumed = 0; g_assert (hb_font_draw_glyph (font, 3, funcs, &user_data)); - char expected2[] = "M323,448L323,448Q297,448 271,430Q244,412 227,371" + char expected3[] = "M323,448L323,448Q297,448 271,430Q244,412 227,371" "Q209,330 209,261L209,261Q209,204 226,166Q242,127 273,107Q303,86 344,86L344,86Q378,86 404,101" "Q430,115 451,137L451,137L488,103Q458,42 404,13Q350,-16 279,-16L279,-16Q211,-16 153,13Q95,41 60,99" "Q25,156 25,241L25,241Q25,323 62,382Q99,440 163,471Q226,501 303,501L303,501Q357,501 399,481" "Q440,460 464,426Q488,392 492,352L492,352Q475,297 420,297L420,297Q390,297 366,320" "Q342,342 339,401L339,401L333,469L411,427Q387,438 368,443Q348,448 323,448Z"; - g_assert_cmpmem (str, user_data.consumed, expected2, sizeof (expected2) - 1); + g_assert_cmpmem (str, user_data.consumed, expected3, sizeof (expected3) - 1); hb_font_destroy (font); } @@ -754,6 +770,12 @@ main (int argc, char **argv) hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) cubic_to); hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) close_path); + funcs2 = hb_draw_funcs_create (); + hb_draw_funcs_set_move_to_func (funcs2, (hb_draw_move_to_func_t) move_to); + hb_draw_funcs_set_line_to_func (funcs2, (hb_draw_line_to_func_t) line_to); + hb_draw_funcs_set_cubic_to_func (funcs2, (hb_draw_cubic_to_func_t) cubic_to); + hb_draw_funcs_set_close_path_func (funcs2, (hb_draw_close_path_func_t) close_path); + hb_test_init (&argc, &argv); hb_test_add (test_itoa); hb_test_add (test_hb_glyph_empty); @@ -767,5 +789,6 @@ main (int argc, char **argv) unsigned result = hb_test_run (); hb_draw_funcs_destroy (funcs); + hb_draw_funcs_destroy (funcs2); return result; }