[draw][glyf] Implement quadratic to cubic call translation

This commit is contained in:
Ebrahim Byagowi 2020-01-28 15:26:13 +03:30
parent 74fdcdcac8
commit d106900bfd
4 changed files with 81 additions and 14 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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<contour_point_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;

View File

@ -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;
}