From 49f9359632c78754b6e1eb32f2505b340cde55c8 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Tue, 26 Mar 2019 17:10:46 -0700 Subject: [PATCH] add support of anchor point & SCALED/UNSCALED_COMPONENT_OFFSET some code cleanup --- src/hb-ot-glyf-table.hh | 166 ++++++++++++------ src/hb-ot-var-gvar-table.hh | 38 +++- .../fonts/SourceSansVariable-Roman.anchor.ttf | Bin 0 -> 4708 bytes test/api/test-ot-metrics-tt-var.c | 33 ++++ 4 files changed, 179 insertions(+), 58 deletions(-) create mode 100644 test/api/fonts/SourceSansVariable-Roman.anchor.ttf diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index d4cae61b5..2c762bd3b 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -34,6 +34,8 @@ #include "hb-ot-var-gvar-table.hh" #include "hb-subset-glyf.hh" +#include + namespace OT { @@ -174,8 +176,50 @@ struct glyf return size; } - void transform_point (float &x, float &y) const + bool is_anchored () const { return (flags & ARGS_ARE_XY_VALUES) == 0; } + void get_anchor_points (unsigned int &point1, unsigned int &point2) const { + const HBUINT8 *p = &StructAfter (glyphIndex); + if (flags & ARG_1_AND_2_ARE_WORDS) + { + point1 = ((const HBUINT16 *)p)[0]; + point2 = ((const HBUINT16 *)p)[1]; + } + else + { + point1 = p[0]; + point2 = p[1]; + } + } + + void transform_points (contour_point_vector_t &points) const + { + float matrix[4]; + contour_point_t trans; + if (get_transformation (matrix, trans)) + { + if (scaled_offsets ()) + { + points.translate (trans); + points.transform (matrix); + } + else + { + points.transform (matrix); + points.translate (trans); + } + } + } + + protected: + bool scaled_offsets () const + { return (flags & (SCALED_COMPONENT_OFFSET|UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; } + + bool get_transformation (float (&matrix)[4], contour_point_t &trans) const + { + matrix[0] = matrix[3] = 1.f; + matrix[1] = matrix[2] = 0.f; + int tx, ty; const HBINT8 *p = &StructAfter (glyphIndex); if (flags & ARG_1_AND_2_ARE_WORDS) @@ -190,28 +234,33 @@ struct glyf tx = *p++; ty = *p++; } - if (!(flags & ARGS_ARE_XY_VALUES)) tx = ty = 0; /* TODO: anchor point unsupported for now */ + if (is_anchored ()) tx = ty = 0; + + trans.init ((float)tx, (float)ty); if (flags & WE_HAVE_A_SCALE) { - float scale = ((const F2DOT14*)p)->to_float (); - x *= scale; - y *= scale; + matrix[0] = matrix[3] = ((const F2DOT14*)p)->to_float (); + return true; } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) { - x *= ((const F2DOT14*)p)[0].to_float (); - y *= ((const F2DOT14*)p)[1].to_float (); + matrix[0] = ((const F2DOT14*)p)[0].to_float (); + matrix[3] = ((const F2DOT14*)p)[1].to_float (); + return true; } else if (flags & WE_HAVE_A_TWO_BY_TWO) { - float x_ = x * ((const F2DOT14*)p)[0].to_float () + y * ((const F2DOT14*)p)[1].to_float (); - y = x * ((const F2DOT14*)p)[2].to_float () + y * ((const F2DOT14*)p)[3].to_float (); - x = x_; + matrix[0] = ((const F2DOT14*)p)[0].to_float (); + matrix[1] = ((const F2DOT14*)p)[1].to_float (); + matrix[2] = ((const F2DOT14*)p)[2].to_float (); + matrix[3] = ((const F2DOT14*)p)[3].to_float (); + return true; } - if (tx | ty) { x += tx; y += ty; } + return tx || ty; } + public: struct Iterator { const char *glyph_start; @@ -362,7 +411,7 @@ struct glyf template static bool read_points (const HBUINT8 *&p /* IN/OUT */, - hb_vector_t &points_ /* IN/OUT */, + contour_point_vector_t &points_ /* IN/OUT */, const range_checker_t &checker) { T coord_setter; @@ -411,7 +460,7 @@ struct glyf * in both cases points trailed with four phantom points */ bool get_contour_points (hb_codepoint_t glyph, - hb_vector_t &points_ /* OUT */, + contour_point_vector_t &points_ /* OUT */, hb_vector_t &end_points_ /* OUT */, const bool phantom_only=false) const { @@ -493,10 +542,10 @@ struct glyf /* Note: Recursively calls itself. Who's checking recursively nested composite glyph BTW? */ bool get_var_metrics (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, - hb_array_t phantoms /* OUT */) const + contour_point_vector_t &phantoms /* OUT */) const { - hb_vector_t points; - hb_vector_t end_points; + contour_point_vector_t points; + hb_vector_t end_points; if (unlikely (!get_contour_points (glyph, points, end_points, true/*phantom_only*/))) return false; hb_array_t phantoms_array = points.sub_array (points.length-PHANTOM_COUNT, PHANTOM_COUNT); init_phantom_points (glyph, phantoms_array); @@ -513,9 +562,9 @@ struct glyf if (composite.current->flags & CompositeGlyphHeader::USE_MY_METRICS) { if (unlikely (!get_var_metrics (composite.current->glyphIndex, coords, coord_count, - phantoms.sub_array (0, 2)))) return false; - for (unsigned int j = 0; j < phantoms.length; j++) - composite.current->transform_point (phantoms[j].x, phantoms[j].y); + phantoms))) return false; + + composite.current->transform_points (phantoms); } } while (composite.move_to_next()); return true; @@ -533,28 +582,21 @@ struct glyf max.y = MAX (max.y, p.y); } - void offset (const contour_point_t &p) { min.offset (p); max.offset (p); } - - void merge (const contour_bounds_t &b) - { - if (empty ()) { *this = b; return; } - add (b.min); - add (b.max); - } - bool empty () const { return (min.x >= max.x) || (min.y >= max.y); } contour_point_t min; contour_point_t max; }; - /* Note: Recursively calls itself. Who's checking recursively nested composite glyph BTW? */ - bool get_bounds_var (hb_codepoint_t glyph, + /* Note: Recursively calls itself. + * all_points does not include phantom points + */ + bool get_points_var (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, - contour_bounds_t &bounds) const + contour_point_vector_t &all_points /* OUT */) const { - hb_vector_t points; - hb_vector_t end_points; + contour_point_vector_t points; + hb_vector_t end_points; if (unlikely (!get_contour_points (glyph, points, end_points))) return false; hb_array_t phantoms_array = points.sub_array (points.length-PHANTOM_COUNT, PHANTOM_COUNT); init_phantom_points (glyph, phantoms_array); @@ -565,34 +607,50 @@ struct glyf if (!get_composite (glyph, &composite)) { /* simple glyph */ - for (unsigned int i = 0; i + PHANTOM_COUNT < points.length; i++) - bounds.add (points[i]); /* TODO: need to check ON_CURVE or flatten? */ + if (likely (points.length >= PHANTOM_COUNT)) + all_points.extend (points.sub_array (0, points.length - PHANTOM_COUNT)); } else { /* composite glyph */ do { - contour_bounds_t comp_bounds; - if (unlikely (!get_bounds_var (composite.current->glyphIndex, coords, coord_count, comp_bounds))) return false; + contour_point_vector_t comp_points; + if (unlikely (!get_points_var (composite.current->glyphIndex, coords, coord_count, + comp_points))) return false; - /* Apply offset & scaling */ - composite.current->transform_point (comp_bounds.min.x, comp_bounds.min.y); - composite.current->transform_point (comp_bounds.max.x, comp_bounds.max.y); + /* Apply component transformation & translation */ + composite.current->transform_points (comp_points); - /* Apply offset adjustments from gvar */ - comp_bounds.offset (points[comp_index]); - - bounds.merge (comp_bounds); + /* Apply translatation from gvar */ + comp_points.translate (points[comp_index]); + + if (composite.current->is_anchored ()) + { + unsigned int p1, p2; + composite.current->get_anchor_points (p1, p2); + if (likely (p1 < all_points.length && p2 < comp_points.length)) + { + contour_point_t delta; + delta.init (all_points[p1].x - comp_points[p2].x, + all_points[p1].y - comp_points[p2].y); + + comp_points.translate (delta); + } + } + all_points.extend (comp_points.as_array ()); + comp_index++; } while (composite.move_to_next()); } - /* Shift bounds by the updated left side bearing (vertically too?) */ { - float x_delta = points[points.length - PHANTOM_COUNT + PHANTOM_LEFT].x; - bounds.min.x -= x_delta; - bounds.max.x -= x_delta; + /* Undocumented rasterizer behavior: + * Shift points horizontally by the updated left side bearing + */ + contour_point_t delta; + delta.init (points[points.length - PHANTOM_COUNT + PHANTOM_LEFT].x, 0.f); + if (delta.x != 0.f) all_points.translate (delta); } return true; @@ -602,8 +660,12 @@ struct glyf const int *coords, unsigned int coord_count, hb_glyph_extents_t *extents) const { + contour_point_vector_t all_points; + if (unlikely (!get_points_var (glyph, coords, coord_count, all_points))) return false; + contour_bounds_t bounds; - if (unlikely (!get_bounds_var (glyph, coords, coord_count, bounds))) return false; + for (unsigned int i = 0; i < all_points.length; i++) + bounds.add (all_points[i]); if (bounds.min.x >= bounds.max.x) { @@ -793,11 +855,11 @@ struct glyf bool vertical) const { bool success = false; - hb_vector_t phantoms; + contour_point_vector_t phantoms; phantoms.resize (PHANTOM_COUNT); if (likely (coord_count == gvar_accel.get_axis_count ())) - success = get_var_metrics (glyph, coords, coord_count, phantoms.as_array ()); + success = get_var_metrics (glyph, coords, coord_count, phantoms); if (unlikely (!success)) return vertical? vmtx_accel.get_advance (glyph): hmtx_accel.get_advance (glyph); @@ -810,7 +872,7 @@ struct glyf int get_side_bearing_var (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, bool vertical) const { - hb_vector_t phantoms; + contour_point_vector_t phantoms; phantoms.resize (PHANTOM_COUNT); if (unlikely (!get_var_metrics (glyph, coords, coord_count, phantoms))) diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index 50797bdcd..354edf99f 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -31,8 +31,6 @@ #include "hb-ot-glyf-table.hh" #include "hb-ot-var-fvar-table.hh" -#include - /* * gvar -- Glyph Variation Table * https://docs.microsoft.com/en-us/typography/opentype/spec/gvar @@ -43,14 +41,42 @@ namespace OT { struct contour_point_t { - void init () { flag = 0; x = y = 0.0f; } + void init (float x_=0.f, float y_=0.f) { flag = 0; x = x_; y = y_; } - void offset (const contour_point_t &p) { x += p.x; y += p.y; } + void translate (const contour_point_t &p) { x += p.x; y += p.y; } uint8_t flag; float x, y; }; +struct contour_point_vector_t : hb_vector_t +{ + void extend (const hb_array_t &a) + { + unsigned int old_len = length; + resize (old_len + a.length); + for (unsigned int i = 0; i < a.length; i++) + (*this)[old_len + i] = a[i]; + } + + void transform (const float (&matrix)[4]) + { + for (unsigned int i = 0; i < length; i++) + { + contour_point_t &p = (*this)[i]; + float x_ = p.x * matrix[0] + p.y * matrix[1]; + p.y = p.x * matrix[2] + p.y * matrix[3]; + p.x = x_; + } + } + + void translate (const contour_point_t& delta) + { + for (unsigned int i = 0; i < length; i++) + (*this)[i].translate (delta); + } +}; + struct range_checker_t { range_checker_t (const void *table_, unsigned int start_offset_, unsigned int end_offset_) @@ -564,7 +590,7 @@ struct gvar &iterator)) return false; - hb_vector_t deltas; /* flag is used to indicate referenced point */ + contour_point_vector_t deltas; /* flag is used to indicate referenced point */ deltas.resize (points.length); for (unsigned int i = 0; i < deltas.length; i++) deltas[i].init (); @@ -605,7 +631,7 @@ struct gvar } while (iterator.move_to_next ()); /* infer deltas for unreferenced points */ - hb_vector_t orig_points; + contour_point_vector_t orig_points; orig_points.resize (points.length); for (unsigned int i = 0; i < orig_points.length; i++) orig_points[i] = points[i]; diff --git a/test/api/fonts/SourceSansVariable-Roman.anchor.ttf b/test/api/fonts/SourceSansVariable-Roman.anchor.ttf new file mode 100644 index 0000000000000000000000000000000000000000..4e8dc9db4741fbdf45a1dfe4517a08f4c1e1fab5 GIT binary patch literal 4708 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 literal 0 HcmV?d00001 diff --git a/test/api/test-ot-metrics-tt-var.c b/test/api/test-ot-metrics-tt-var.c index 56d4b1159..3259a7910 100644 --- a/test/api/test-ot-metrics-tt-var.c +++ b/test/api/test-ot-metrics-tt-var.c @@ -133,6 +133,38 @@ test_advance_tt_var_hvarvvar (void) hb_font_destroy (font); } +static void +test_advance_tt_var_anchor (void) +{ + hb_face_t *face = hb_test_open_font_file ("fonts/SourceSansVariable-Roman.anchor.ttf"); + g_assert (face); + hb_font_t *font = hb_font_create (face); + hb_face_destroy (face); + g_assert (font); + hb_ot_font_set_funcs (font); + + hb_glyph_extents_t extents; + hb_bool_t result = hb_font_get_glyph_extents (font, 2, &extents); + g_assert (result); + + g_assert_cmpint (extents.x_bearing, ==, 56); + g_assert_cmpint (extents.y_bearing, ==, 672); + g_assert_cmpint (extents.width, ==, 556); + g_assert_cmpint (extents.height, ==, -684); + + float coords[1] = { 500.0f }; + hb_font_set_var_coords_design (font, coords, 1); + result = hb_font_get_glyph_extents (font, 2, &extents); + g_assert (result); + + g_assert_cmpint (extents.x_bearing, ==, 50); + g_assert_cmpint (extents.y_bearing, ==, 668); + g_assert_cmpint (extents.width, ==, 593); + g_assert_cmpint (extents.height, ==, -680); + + hb_font_destroy (font); +} + int main (int argc, char **argv) { @@ -141,6 +173,7 @@ main (int argc, char **argv) hb_test_add (test_extents_tt_var); hb_test_add (test_advance_tt_var_nohvar); hb_test_add (test_advance_tt_var_hvarvvar); + hb_test_add (test_advance_tt_var_anchor); return hb_test_run (); }