diff --git a/src/hb-ot-var-common.hh b/src/hb-ot-var-common.hh index 9e813f6d2..caddfe416 100644 --- a/src/hb-ot-var-common.hh +++ b/src/hb-ot-var-common.hh @@ -449,6 +449,9 @@ struct tuple_delta_t hb_vector_t compiled_tuple_header; hb_vector_t compiled_deltas; + /* compiled peak coords, empty for non-gvar tuples */ + hb_vector_t compiled_peak_coords; + tuple_delta_t () = default; tuple_delta_t (const tuple_delta_t& o) = default; @@ -552,6 +555,36 @@ struct tuple_delta_t return out; } + bool compile_peak_coords (const hb_map_t& axes_index_map, + const hb_map_t& axes_old_index_tag_map) + { + unsigned axis_count = axes_index_map.get_population (); + if (unlikely (!compiled_peak_coords.alloc (axis_count * F2DOT14::static_size))) + return false; + + unsigned orig_axis_count = axes_old_index_tag_map.get_population (); + for (unsigned i = 0; i < orig_axis_count; i++) + { + if (!axes_index_map.has (i)) + continue; + + hb_tag_t axis_tag = axes_old_index_tag_map.get (i); + Triple *coords; + F2DOT14 peak_coord; + if (axis_tuples.has (axis_tag, &coords)) + peak_coord.set_float (coords->middle); + else + peak_coord.set_int (0); + + /* push F2DOT14 value into char vector */ + int16_t val = peak_coord.to_int (); + compiled_peak_coords.push (static_cast (val >> 8)); + compiled_peak_coords.push (static_cast (val & 0xFF)); + } + + return !compiled_peak_coords.in_error (); + } + /* deltas should be compiled already before we compile tuple * variation header cause we need to fill in the size of the * serialized data for this tuple variation */ diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index b5099ac07..66da68295 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -76,6 +76,111 @@ struct contour_point_vector_t : hb_vector_t struct GlyphVariationData : TupleVariationData {}; +struct glyph_variations_t +{ + using tuple_variations_t = TupleVariationData::tuple_variations_t; + hb_vector_t glyph_variations; + + hb_vector_t compiled_shared_tuples; + private: + unsigned shared_tuples_count = 0; + + /* shared coords-> index map after instantiation */ + hb_hashmap_t*, unsigned> shared_tuples_idx_map; + + public: + unsigned compiled_shared_tuples_count () const + { return shared_tuples_count; } + + bool compile_shared_tuples (const hb_map_t& axes_index_map, + const hb_map_t& axes_old_index_tag_map) + { + /* key is pointer to compiled_peak_coords inside each tuple, hashing + * function will always deref pointers first */ + hb_hashmap_t*, unsigned> coords_count_map; + + /* count the num of shared coords */ + for (tuple_variations_t& vars: glyph_variations) + { + for (tuple_delta_t& var : vars.tuple_vars) + { + if (!var.compile_peak_coords (axes_index_map, axes_old_index_tag_map)) + return false; + unsigned* count; + if (coords_count_map.has (&(var.compiled_peak_coords), &count)) + coords_count_map.set (&(var.compiled_peak_coords), *count + 1); + else + coords_count_map.set (&(var.compiled_peak_coords), 1); + } + } + + if (!coords_count_map || coords_count_map.in_error ()) + return false; + + /* add only those coords that are used more than once into the vector and sort */ + hb_vector_t*> shared_coords; + if (unlikely (!shared_coords.alloc (coords_count_map.get_population ()))) + return false; + + for (const auto _ : coords_count_map.iter ()) + { + if (_.second == 1) continue; + shared_coords.push (_.first); + } + + /* no shared tuples: no coords are used more than once */ + if (!shared_coords) return true; + /* sorting based on the coords frequency first (high to low), then compare + * the coords bytes */ + hb_qsort (shared_coords.arrayZ, shared_coords.length, sizeof (hb_vector_t*), _cmp_coords, (void *) (&coords_count_map)); + + /* build shared_coords->idx map and shared tuples byte array */ + + shared_tuples_count = hb_min (0xFFFu + 1, shared_coords.length); + unsigned len = shared_tuples_count * (shared_coords[0]->length); + if (unlikely (!compiled_shared_tuples.alloc (len))) + return false; + + for (unsigned i = 0; i < shared_tuples_count; i++) + { + shared_tuples_idx_map.set (shared_coords[i], i); + /* add a concat() in hb_vector_t? */ + for (char c : shared_coords[i]->iter ()) + compiled_shared_tuples.push (c); + } + + return true; + } + + static int _cmp_coords (const void *pa, const void *pb, void *arg) + { + const hb_hashmap_t*, unsigned>* coords_count_map = + reinterpret_cast*, unsigned>*> (arg); + + /* shared_coords is hb_vector_t*> so casting pa/pb + * to be a pointer to a pointer */ + const hb_vector_t** a = reinterpret_cast**> (const_cast(pa)); + const hb_vector_t** b = reinterpret_cast**> (const_cast(pb)); + + bool has_a = coords_count_map->has (*a); + bool has_b = coords_count_map->has (*b); + + if (has_a && has_b) + { + unsigned a_num = coords_count_map->get (*a); + unsigned b_num = coords_count_map->get (*b); + + if (a_num != b_num) + return b_num - a_num; + + return (*b)->as_array().cmp ((*a)->as_array ()); + } + else if (has_a) return -1; + else if (has_b) return 1; + else return 0; + } +}; + struct gvar { static constexpr hb_tag_t tableTag = HB_OT_TAG_gvar;