diff --git a/gfx/harfbuzz/NEWS b/gfx/harfbuzz/NEWS index 626dafe9a7a5..9154938d70f3 100644 --- a/gfx/harfbuzz/NEWS +++ b/gfx/harfbuzz/NEWS @@ -1,3 +1,14 @@ +Overview of changes leading to 8.2.2 +Wednesday, October 18, 2023 +“From the river to the sea, Palestine will be free” +==================================== +- Fix regression from 8.1.0 in shaping fonts with duplicate feature tags. +- Fix regression from 8.2.0 in parsing CSS-style feature strings. +- Variable fonts instanciation now handles more tables. +- Various CMake build improvements. +- various fixes to build without errors with gcc 4.9.2. + + Overview of changes leading to 8.2.1 Monday, September 18, 2023 ==================================== diff --git a/gfx/harfbuzz/configure.ac b/gfx/harfbuzz/configure.ac index eece69107bea..fbcd25747cdd 100644 --- a/gfx/harfbuzz/configure.ac +++ b/gfx/harfbuzz/configure.ac @@ -1,6 +1,6 @@ AC_PREREQ([2.64]) AC_INIT([HarfBuzz], - [8.2.1], + [8.2.2], [https://github.com/harfbuzz/harfbuzz/issues/new], [harfbuzz], [http://harfbuzz.org/]) diff --git a/gfx/harfbuzz/moz.yaml b/gfx/harfbuzz/moz.yaml index 0e68a768af1c..7427031b5267 100644 --- a/gfx/harfbuzz/moz.yaml +++ b/gfx/harfbuzz/moz.yaml @@ -20,11 +20,11 @@ origin: # Human-readable identifier for this version/release # Generally "version NNN", "tag SSS", "bookmark SSS" - release: 8.2.1 (2023-09-18T21:18:49+03:00). + release: 8.2.2 (2023-10-18T01:18:14+03:00). # Revision to pull in # Must be a long or short commit SHA (long preferred) - revision: 8.2.1 + revision: 8.2.2 # The package's license, where possible using the mnemonic from # https://spdx.org/licenses/ diff --git a/gfx/harfbuzz/src/Makefile.am b/gfx/harfbuzz/src/Makefile.am index 430d44a453b8..ff6a6d6f3708 100644 --- a/gfx/harfbuzz/src/Makefile.am +++ b/gfx/harfbuzz/src/Makefile.am @@ -14,7 +14,7 @@ check_PROGRAMS = EXTRA_DIST += harfbuzz.cc harfbuzz-subset.cc EXTRA_DIST += meson.build -EXTRA_DIST += fix_get_types.py +EXTRA_DIST += fix_get_types.py relative_to.py # Convenience targets: lib: $(BUILT_SOURCES) libharfbuzz.la @@ -458,6 +458,7 @@ COMPILED_TESTS = \ test-classdef-graph \ test-instancer-solver \ test-tuple-varstore \ + test-item-varstore \ $(NULL) COMPILED_TESTS_CPPFLAGS = $(HBCFLAGS) -DMAIN -UNDEBUG COMPILED_TESTS_LDADD = libharfbuzz.la $(HBLIBS) @@ -536,6 +537,10 @@ test_tuple_varstore_SOURCES = test-tuple-varstore.cc hb-subset-instancer-solver. test_tuple_varstore_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) test_tuple_varstore_LDADD = $(COMPILED_TESTS_LDADD) +test_item_varstore_SOURCES = test-item-varstore.cc hb-subset-instancer-solver.cc hb-static.cc +test_item_varstore_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_item_varstore_LDADD = $(COMPILED_TESTS_LDADD) + dist_check_SCRIPTS = \ check-c-linkage-decls.py \ check-externs.py \ diff --git a/gfx/harfbuzz/src/OT/Layout/GDEF/GDEF.hh b/gfx/harfbuzz/src/OT/Layout/GDEF/GDEF.hh index 4f85d3ce5ece..dd025c1284d7 100644 --- a/gfx/harfbuzz/src/OT/Layout/GDEF/GDEF.hh +++ b/gfx/harfbuzz/src/OT/Layout/GDEF/GDEF.hh @@ -29,7 +29,7 @@ #ifndef OT_LAYOUT_GDEF_GDEF_HH #define OT_LAYOUT_GDEF_GDEF_HH -#include "../../../hb-ot-layout-common.hh" +#include "../../../hb-ot-var-common.hh" #include "../../../hb-font.hh" #include "../../../hb-cache.hh" @@ -204,17 +204,19 @@ struct CaretValueFormat3 if (!c->serializer->embed (coordinate)) return_trace (false); unsigned varidx = (this+deviceTable).get_variation_index (); - if (c->plan->layout_variation_idx_delta_map.has (varidx)) + hb_pair_t *new_varidx_delta; + if (!c->plan->layout_variation_idx_delta_map.has (varidx, &new_varidx_delta)) + return_trace (false); + + uint32_t new_varidx = hb_first (*new_varidx_delta); + int delta = hb_second (*new_varidx_delta); + if (delta != 0) { - int delta = hb_second (c->plan->layout_variation_idx_delta_map.get (varidx)); - if (delta != 0) - { - if (!c->serializer->check_assign (out->coordinate, coordinate + delta, HB_SERIALIZE_ERROR_INT_OVERFLOW)) - return_trace (false); - } + if (!c->serializer->check_assign (out->coordinate, coordinate + delta, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); } - if (c->plan->all_axes_pinned) + if (new_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX) return_trace (c->serializer->check_assign (out->caretValueFormat, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW)); if (!c->serializer->embed (deviceTable)) @@ -602,6 +604,26 @@ struct GDEFVersion1_2 (version.to_int () < 0x00010003u || varStore.sanitize (c, this))); } + static void remap_varidx_after_instantiation (const hb_map_t& varidx_map, + hb_hashmap_t>& layout_variation_idx_delta_map /* IN/OUT */) + { + /* varidx_map is empty which means varstore is empty after instantiation, + * no variations, map all varidx to HB_OT_LAYOUT_NO_VARIATIONS_INDEX. + * varidx_map doesn't have original varidx, indicating delta row is all + * zeros, map varidx to HB_OT_LAYOUT_NO_VARIATIONS_INDEX */ + for (auto _ : layout_variation_idx_delta_map.iter_ref ()) + { + /* old_varidx->(varidx, delta) mapping generated for subsetting, then this + * varidx is used as key of varidx_map during instantiation */ + uint32_t varidx = _.second.first; + uint32_t *new_varidx; + if (varidx_map.has (varidx, &new_varidx)) + _.second.first = *new_varidx; + else + _.second.first = HB_OT_LAYOUT_NO_VARIATIONS_INDEX; + } + } + bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); @@ -624,6 +646,22 @@ struct GDEFVersion1_2 { if (c->plan->all_axes_pinned) out->varStore = 0; + else if (c->plan->normalized_coords) + { + if (varStore) + { + item_variations_t item_vars; + if (item_vars.instantiate (this+varStore, c->plan, true, true, + c->plan->gdef_varstore_inner_maps.as_array ())) + subset_varstore = out->varStore.serialize_serialize (c->serializer, + item_vars.has_long_word (), + c->plan->axis_tags, + item_vars.get_region_list (), + item_vars.get_vardata_encodings ()); + remap_varidx_after_instantiation (item_vars.get_varidx_map (), + c->plan->layout_variation_idx_delta_map); + } + } else subset_varstore = out->varStore.serialize_subset (c, varStore, this, c->plan->gdef_varstore_inner_maps.as_array ()); } @@ -922,17 +960,32 @@ struct GDEF { get_lig_caret_list ().collect_variation_indices (c); } void remap_layout_variation_indices (const hb_set_t *layout_variation_indices, + const hb_vector_t& normalized_coords, + bool calculate_delta, /* not pinned at default */ + bool no_variations, /* all axes pinned */ hb_hashmap_t> *layout_variation_idx_delta_map /* OUT */) const { if (!has_var_store ()) return; - if (layout_variation_indices->is_empty ()) return; - + const VariationStore &var_store = get_var_store (); + float *store_cache = var_store.create_cache (); + unsigned new_major = 0, new_minor = 0; unsigned last_major = (layout_variation_indices->get_min ()) >> 16; for (unsigned idx : layout_variation_indices->iter ()) { + int delta = 0; + if (calculate_delta) + delta = roundf (var_store.get_delta (idx, normalized_coords.arrayZ, + normalized_coords.length, store_cache)); + + if (no_variations) + { + layout_variation_idx_delta_map->set (idx, hb_pair_t (HB_OT_LAYOUT_NO_VARIATIONS_INDEX, delta)); + continue; + } + uint16_t major = idx >> 16; - if (major >= get_var_store ().get_sub_table_count ()) break; + if (major >= var_store.get_sub_table_count ()) break; if (major != last_major) { new_minor = 0; @@ -940,14 +993,11 @@ struct GDEF } unsigned new_idx = (new_major << 16) + new_minor; - if (!layout_variation_idx_delta_map->has (idx)) - continue; - int delta = hb_second (layout_variation_idx_delta_map->get (idx)); - layout_variation_idx_delta_map->set (idx, hb_pair_t (new_idx, delta)); ++new_minor; last_major = major; } + var_store.destroy_cache (store_cache); } protected: diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh index 8684f60ca5dc..56eda4a5778b 100644 --- a/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh @@ -52,9 +52,14 @@ struct AnchorFormat3 if (unlikely (!c->serializer->embed (yCoordinate))) return_trace (false); unsigned x_varidx = xDeviceTable ? (this+xDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX; - if (c->plan->layout_variation_idx_delta_map.has (x_varidx)) + if (x_varidx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX) { - int delta = hb_second (c->plan->layout_variation_idx_delta_map.get (x_varidx)); + hb_pair_t *new_varidx_delta; + if (!c->plan->layout_variation_idx_delta_map.has (x_varidx, &new_varidx_delta)) + return_trace (false); + + x_varidx = hb_first (*new_varidx_delta); + int delta = hb_second (*new_varidx_delta); if (delta != 0) { if (!c->serializer->check_assign (out->xCoordinate, xCoordinate + delta, @@ -64,9 +69,14 @@ struct AnchorFormat3 } unsigned y_varidx = yDeviceTable ? (this+yDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX; - if (c->plan->layout_variation_idx_delta_map.has (y_varidx)) + if (y_varidx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX) { - int delta = hb_second (c->plan->layout_variation_idx_delta_map.get (y_varidx)); + hb_pair_t *new_varidx_delta; + if (!c->plan->layout_variation_idx_delta_map.has (y_varidx, &new_varidx_delta)) + return_trace (false); + + y_varidx = hb_first (*new_varidx_delta); + int delta = hb_second (*new_varidx_delta); if (delta != 0) { if (!c->serializer->check_assign (out->yCoordinate, yCoordinate + delta, @@ -75,7 +85,10 @@ struct AnchorFormat3 } } - if (c->plan->all_axes_pinned) + /* in case that all axes are pinned or no variations after instantiation, + * both var_idxes will be mapped to HB_OT_LAYOUT_NO_VARIATIONS_INDEX */ + if (x_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX && + y_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX) return_trace (c->serializer->check_assign (out->format, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW)); if (!c->serializer->embed (xDeviceTable)) return_trace (false); diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/MarkLigPosFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/MarkLigPosFormat1.hh index af8b4723aeff..d6bee277c70c 100644 --- a/gfx/harfbuzz/src/OT/Layout/GPOS/MarkLigPosFormat1.hh +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/MarkLigPosFormat1.hh @@ -169,7 +169,7 @@ struct MarkLigPosFormat1_2 { TRACE_SUBSET (this); const hb_set_t &glyphset = *c->plan->glyphset_gsub (); - const hb_map_t &glyph_map = *c->plan->glyph_map; + const hb_map_t &glyph_map = c->plan->glyph_map_gsub; auto *out = c->serializer->start_embed (*this); if (unlikely (!c->serializer->extend_min (out))) return_trace (false); @@ -202,8 +202,9 @@ struct MarkLigPosFormat1_2 auto new_ligature_coverage = + hb_iter (this + ligatureCoverage) - | hb_filter (glyphset) + | hb_take ((this + ligatureArray).len) | hb_map_retains_sorting (glyph_map) + | hb_filter ([] (hb_codepoint_t glyph) { return glyph != HB_MAP_VALUE_INVALID; }) ; if (!out->ligatureCoverage.serialize_serialize (c->serializer, new_ligature_coverage)) diff --git a/gfx/harfbuzz/src/graph/graph.hh b/gfx/harfbuzz/src/graph/graph.hh index 49424488ebb4..2b4e1b2d3f38 100644 --- a/gfx/harfbuzz/src/graph/graph.hh +++ b/gfx/harfbuzz/src/graph/graph.hh @@ -566,7 +566,7 @@ struct graph_t update_distances (); - hb_priority_queue_t queue; + hb_priority_queue_t queue; hb_vector_t &sorted_graph = vertices_scratch_; if (unlikely (!check_success (sorted_graph.resize (vertices_.length)))) return; hb_vector_t id_map; @@ -1369,7 +1369,7 @@ struct graph_t vertices_.arrayZ[i].distance = hb_int_max (int64_t); vertices_.tail ().distance = 0; - hb_priority_queue_t queue; + hb_priority_queue_t queue; queue.insert (0, vertices_.length - 1); hb_vector_t visited; diff --git a/gfx/harfbuzz/src/graph/markbasepos-graph.hh b/gfx/harfbuzz/src/graph/markbasepos-graph.hh index e10e1cf3e3c4..ae5ebd0d1675 100644 --- a/gfx/harfbuzz/src/graph/markbasepos-graph.hh +++ b/gfx/harfbuzz/src/graph/markbasepos-graph.hh @@ -217,7 +217,7 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2::min_size + + OT::Layout::GPOS_impl::MarkBasePosFormat1_2::min_size + MarkArray::min_size + AnchorMatrix::min_size + c.graph.vertices_[base_coverage_id].table_size (); @@ -484,7 +484,7 @@ struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); #ifndef HB_NO_BEYOND_64K case 2: HB_FALLTHROUGH; - // Don't split 24bit PairPos's. + // Don't split 24bit MarkBasePos's. #endif default: return hb_vector_t (); diff --git a/gfx/harfbuzz/src/harfbuzz-config.cmake.in b/gfx/harfbuzz/src/harfbuzz-config.cmake.in index 95b5d0afd271..6abe2d62dce0 100644 --- a/gfx/harfbuzz/src/harfbuzz-config.cmake.in +++ b/gfx/harfbuzz/src/harfbuzz-config.cmake.in @@ -1,69 +1,32 @@ -set(_harfbuzz_libdir "@libdir@") -set(_harfbuzz_includedir "@includedir@") +@PACKAGE_INIT@ -# Extract version information from libtool. -set(_harfbuzz_version_info "@HB_LIBTOOL_VERSION_INFO@") -string(REPLACE ":" ";" _harfbuzz_version_info "${_harfbuzz_version_info}") -list(GET _harfbuzz_version_info 0 - _harfbuzz_current) -list(GET _harfbuzz_version_info 1 - _harfbuzz_revision) -list(GET _harfbuzz_version_info 2 - _harfbuzz_age) -unset(_harfbuzz_version_info) - -if ("@default_library@" MATCHES "static") - set(_harfbuzz_lib_prefix "lib") - set(_harfbuzz_lib_suffix ".a") -else () - if (APPLE) - set(_harfbuzz_lib_prefix "${CMAKE_SHARED_LIBRARY_PREFIX}") - set(_harfbuzz_lib_suffix ".0${CMAKE_SHARED_LIBRARY_SUFFIX}") - elseif (UNIX) - set(_harfbuzz_lib_prefix "${CMAKE_SHARED_LIBRARY_PREFIX}") - set(_harfbuzz_lib_suffix "${CMAKE_SHARED_LIBRARY_SUFFIX}.0.${_harfbuzz_current}.${_harfbuzz_revision}") - elseif (WIN32) - set(_harfbuzz_lib_prefix "${CMAKE_IMPORT_LIBRARY_PREFIX}") - set(_harfbuzz_lib_suffix "${CMAKE_IMPORT_LIBRARY_SUFFIX}") - else () - # Unsupported. - set(harfbuzz_FOUND 0) - endif () -endif () +set_and_check(HARFBUZZ_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@") # Add the libraries. -add_library(harfbuzz::harfbuzz SHARED IMPORTED) +add_library(harfbuzz::harfbuzz @HB_LIBRARY_TYPE@ IMPORTED) set_target_properties(harfbuzz::harfbuzz PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_includedir}/harfbuzz" - IMPORTED_LOCATION "${_harfbuzz_libdir}/${_harfbuzz_lib_prefix}harfbuzz${_harfbuzz_lib_suffix}") + INTERFACE_INCLUDE_DIRECTORIES "@PACKAGE_INCLUDE_INSTALL_DIR@" + IMPORTED_LOCATION "@PACKAGE_CMAKE_INSTALL_LIBDIR@/@HB_LIB_PREFIX@harfbuzz@HB_LIB_SUFFIX@") -add_library(harfbuzz::icu SHARED IMPORTED) +add_library(harfbuzz::icu @HB_LIBRARY_TYPE@ IMPORTED) set_target_properties(harfbuzz::icu PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_includedir}/harfbuzz" + INTERFACE_INCLUDE_DIRECTORIES "@PACKAGE_INCLUDE_INSTALL_DIR@" INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz" - IMPORTED_LOCATION "${_harfbuzz_libdir}/${_harfbuzz_lib_prefix}harfbuzz-icu${_harfbuzz_lib_suffix}") + IMPORTED_LOCATION "@PACKAGE_CMAKE_INSTALL_LIBDIR@/@HB_LIB_PREFIX@harfbuzz-icu@HB_LIB_SUFFIX@") -add_library(harfbuzz::subset SHARED IMPORTED) +add_library(harfbuzz::subset @HB_LIBRARY_TYPE@ IMPORTED) set_target_properties(harfbuzz::subset PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_includedir}/harfbuzz" + INTERFACE_INCLUDE_DIRECTORIES "@PACKAGE_INCLUDE_INSTALL_DIR@" INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz" - IMPORTED_LOCATION "${_harfbuzz_libdir}/${_harfbuzz_lib_prefix}harfbuzz-subset${_harfbuzz_lib_suffix}") + IMPORTED_LOCATION "@PACKAGE_CMAKE_INSTALL_LIBDIR@/@HB_LIB_PREFIX@harfbuzz-subset@HB_LIB_SUFFIX@") # Only add the gobject library if it was built. -set(_harfbuzz_have_gobject "@have_gobject@") -if (_harfbuzz_have_gobject) - add_library(harfbuzz::gobject SHARED IMPORTED) +if (@HB_HAVE_GOBJECT@) + add_library(harfbuzz::gobject @HB_LIBRARY_TYPE@ IMPORTED) set_target_properties(harfbuzz::gobject PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_includedir}/harfbuzz" + INTERFACE_INCLUDE_DIRECTORIES "@PACKAGE_INCLUDE_INSTALL_DIR@" INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz" - IMPORTED_LOCATION "${_harfbuzz_libdir}/${_harfbuzz_lib_prefix}harfbuzz-gobject${_harfbuzz_lib_suffix}") + IMPORTED_LOCATION "@PACKAGE_CMAKE_INSTALL_LIBDIR@/@HB_LIB_PREFIX@harfbuzz-gobject@HB_LIB_SUFFIX@") endif () -# Clean out variables we used in our scope. -unset(_harfbuzz_lib_prefix) -unset(_harfbuzz_lib_suffix) -unset(_harfbuzz_current) -unset(_harfbuzz_revision) -unset(_harfbuzz_age) -unset(_harfbuzz_includedir) -unset(_harfbuzz_libdir) +check_required_components(harfbuzz) diff --git a/gfx/harfbuzz/src/hb-common.cc b/gfx/harfbuzz/src/hb-common.cc index bd4cd88e34f9..0c13c7d171fc 100644 --- a/gfx/harfbuzz/src/hb-common.cc +++ b/gfx/harfbuzz/src/hb-common.cc @@ -815,7 +815,7 @@ parse_tag (const char **pp, const char *end, hb_tag_t *tag) } const char *p = *pp; - while (*pp < end && (**pp != ' ' && **pp != '=' && **pp != '[')) + while (*pp < end && (**pp != ' ' && **pp != '=' && **pp != '[' && **pp != quote)) (*pp)++; if (p == *pp || *pp - p > 4) diff --git a/gfx/harfbuzz/src/hb-map.hh b/gfx/harfbuzz/src/hb-map.hh index 6ea416658990..13d62054c112 100644 --- a/gfx/harfbuzz/src/hb-map.hh +++ b/gfx/harfbuzz/src/hb-map.hh @@ -104,12 +104,12 @@ struct hb_hashmap_t hb_pair_t get_pair_ref() { return hb_pair_t (key, value); } uint32_t total_hash () const - { return (hash * 31) + hb_hash (value); } + { return (hash * 31u) + hb_hash (value); } - static constexpr bool is_trivial = std::is_trivially_constructible::value && - std::is_trivially_destructible::value && - std::is_trivially_constructible::value && - std::is_trivially_destructible::value; + static constexpr bool is_trivial = hb_is_trivially_constructible(K) && + hb_is_trivially_destructible(K) && + hb_is_trivially_constructible(V) && + hb_is_trivially_destructible(V); }; hb_object_header_t header; @@ -398,37 +398,37 @@ struct hb_hashmap_t auto iter_items () const HB_AUTO_RETURN ( - + hb_iter (items, size ()) + + hb_iter (items, this->size ()) | hb_filter (&item_t::is_real) ) auto iter_ref () const HB_AUTO_RETURN ( - + iter_items () + + this->iter_items () | hb_map (&item_t::get_pair_ref) ) auto iter () const HB_AUTO_RETURN ( - + iter_items () + + this->iter_items () | hb_map (&item_t::get_pair) ) auto keys_ref () const HB_AUTO_RETURN ( - + iter_items () + + this->iter_items () | hb_map (&item_t::get_key) ) auto keys () const HB_AUTO_RETURN ( - + keys_ref () + + this->keys_ref () | hb_map (hb_ridentity) ) auto values_ref () const HB_AUTO_RETURN ( - + iter_items () + + this->iter_items () | hb_map (&item_t::get_value) ) auto values () const HB_AUTO_RETURN ( - + values_ref () + + this->values_ref () | hb_map (hb_ridentity) ) diff --git a/gfx/harfbuzz/src/hb-ot-layout-common.hh b/gfx/harfbuzz/src/hb-ot-layout-common.hh index 2cc50980233a..e869d8eceab2 100644 --- a/gfx/harfbuzz/src/hb-ot-layout-common.hh +++ b/gfx/harfbuzz/src/hb-ot-layout-common.hh @@ -191,27 +191,15 @@ struct hb_collect_variation_indices_context_t : static return_t default_return_value () { return hb_empty_t (); } hb_set_t *layout_variation_indices; - hb_hashmap_t> *varidx_delta_map; - hb_vector_t *normalized_coords; - const VariationStore *var_store; const hb_set_t *glyph_set; const hb_map_t *gpos_lookups; - float *store_cache; hb_collect_variation_indices_context_t (hb_set_t *layout_variation_indices_, - hb_hashmap_t> *varidx_delta_map_, - hb_vector_t *normalized_coords_, - const VariationStore *var_store_, const hb_set_t *glyph_set_, - const hb_map_t *gpos_lookups_, - float *store_cache_) : + const hb_map_t *gpos_lookups_) : layout_variation_indices (layout_variation_indices_), - varidx_delta_map (varidx_delta_map_), - normalized_coords (normalized_coords_), - var_store (var_store_), glyph_set (glyph_set_), - gpos_lookups (gpos_lookups_), - store_cache (store_cache_) {} + gpos_lookups (gpos_lookups_) {} }; template @@ -2300,6 +2288,158 @@ static inline bool ClassDef_serialize (hb_serialize_context_t *c, * Item Variation Store */ +/* ported from fonttools (class _Encoding) */ +struct delta_row_encoding_t +{ + /* each byte represents a region, value is one of 0/1/2/4, which means bytes + * needed for this region */ + hb_vector_t chars; + unsigned width = 0; + hb_vector_t columns; + unsigned overhead = 0; + hb_vector_t*> items; + + delta_row_encoding_t () = default; + delta_row_encoding_t (hb_vector_t&& chars_, + const hb_vector_t* row = nullptr) : + delta_row_encoding_t () + + { + chars = std::move (chars_); + width = get_width (); + columns = get_columns (); + overhead = get_chars_overhead (columns); + if (row) items.push (row); + } + + bool is_empty () const + { return !items; } + + static hb_vector_t get_row_chars (const hb_vector_t& row) + { + hb_vector_t ret; + if (!ret.alloc (row.length)) return ret; + + bool long_words = false; + + /* 0/1/2 byte encoding */ + for (int i = row.length - 1; i >= 0; i--) + { + int v = row.arrayZ[i]; + if (v == 0) + ret.push (0); + else if (v > 32767 || v < -32768) + { + long_words = true; + break; + } + else if (v > 127 || v < -128) + ret.push (2); + else + ret.push (1); + } + + if (!long_words) + return ret; + + /* redo, 0/2/4 bytes encoding */ + ret.reset (); + for (int i = row.length - 1; i >= 0; i--) + { + int v = row.arrayZ[i]; + if (v == 0) + ret.push (0); + else if (v > 32767 || v < -32768) + ret.push (4); + else + ret.push (2); + } + return ret; + } + + inline unsigned get_width () + { + unsigned ret = + hb_iter (chars) + | hb_reduce (hb_add, 0u) + ; + return ret; + } + + hb_vector_t get_columns () + { + hb_vector_t cols; + cols.alloc (chars.length); + for (auto v : chars) + { + uint8_t flag = v ? 1 : 0; + cols.push (flag); + } + return cols; + } + + static inline unsigned get_chars_overhead (const hb_vector_t& cols) + { + unsigned c = 4 + 6; // 4 bytes for LOffset, 6 bytes for VarData header + unsigned cols_bit_count = 0; + for (auto v : cols) + if (v) cols_bit_count++; + return c + cols_bit_count * 2; + } + + unsigned get_gain () const + { + int count = items.length; + return hb_max (0, (int) overhead - count); + } + + int gain_from_merging (const delta_row_encoding_t& other_encoding) const + { + int combined_width = 0; + for (unsigned i = 0; i < chars.length; i++) + combined_width += hb_max (chars.arrayZ[i], other_encoding.chars.arrayZ[i]); + + hb_vector_t combined_columns; + combined_columns.alloc (columns.length); + for (unsigned i = 0; i < columns.length; i++) + combined_columns.push (columns.arrayZ[i] | other_encoding.columns.arrayZ[i]); + + int combined_overhead = get_chars_overhead (combined_columns); + int combined_gain = (int) overhead + (int) other_encoding.overhead - combined_overhead + - (combined_width - (int) width) * items.length + - (combined_width - (int) other_encoding.width) * other_encoding.items.length; + + return combined_gain; + } + + static int cmp (const void *pa, const void *pb) + { + const delta_row_encoding_t *a = (const delta_row_encoding_t *)pa; + const delta_row_encoding_t *b = (const delta_row_encoding_t *)pb; + + int gain_a = a->get_gain (); + int gain_b = b->get_gain (); + + if (gain_a != gain_b) + return gain_a - gain_b; + + return (b->chars).as_array ().cmp ((a->chars).as_array ()); + } + + static int cmp_width (const void *pa, const void *pb) + { + const delta_row_encoding_t *a = (const delta_row_encoding_t *)pa; + const delta_row_encoding_t *b = (const delta_row_encoding_t *)pb; + + if (a->width != b->width) + return (int) a->width - (int) b->width; + + return (b->chars).as_array ().cmp ((a->chars).as_array ()); + } + + bool add_row (const hb_vector_t* row) + { return items.push (row); } +}; + struct VarRegionAxis { float evaluate (int coord) const @@ -2334,6 +2474,12 @@ struct VarRegionAxis * have to do that at runtime. */ } + bool serialize (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + public: F2DOT14 startCoord; F2DOT14 peakCoord; @@ -2391,6 +2537,47 @@ struct VarRegionList return_trace (c->check_struct (this) && axesZ.sanitize (c, axisCount * regionCount)); } + bool serialize (hb_serialize_context_t *c, + const hb_vector_t& axis_tags, + const hb_vector_t*>& regions) + { + TRACE_SERIALIZE (this); + unsigned axis_count = axis_tags.length; + unsigned region_count = regions.length; + if (!axis_count || !region_count) return_trace (false); + if (unlikely (hb_unsigned_mul_overflows (axis_count * region_count, + VarRegionAxis::static_size))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); + axisCount = axis_count; + regionCount = region_count; + + for (unsigned r = 0; r < region_count; r++) + { + const auto& region = regions[r]; + for (unsigned i = 0; i < axis_count; i++) + { + hb_tag_t tag = axis_tags.arrayZ[i]; + VarRegionAxis var_region_rec; + Triple *coords; + if (region->has (tag, &coords)) + { + var_region_rec.startCoord.set_float (coords->minimum); + var_region_rec.peakCoord.set_float (coords->middle); + var_region_rec.endCoord.set_float (coords->maximum); + } + else + { + var_region_rec.startCoord.set_int (0); + var_region_rec.peakCoord.set_int (0); + var_region_rec.endCoord.set_int (0); + } + if (!var_region_rec.serialize (c)) + return_trace (false); + } + } + return_trace (true); + } + bool serialize (hb_serialize_context_t *c, const VarRegionList *src, const hb_inc_bimap_t ®ion_map) { TRACE_SERIALIZE (this); @@ -2411,6 +2598,45 @@ struct VarRegionList return_trace (true); } + bool get_var_region (unsigned region_index, + const hb_map_t& axes_old_index_tag_map, + hb_hashmap_t& axis_tuples /* OUT */) const + { + if (region_index >= regionCount) return false; + const VarRegionAxis* axis_region = axesZ.arrayZ + (region_index * axisCount); + for (unsigned i = 0; i < axisCount; i++) + { + hb_tag_t *axis_tag; + if (!axes_old_index_tag_map.has (i, &axis_tag)) + return false; + + float min_val = axis_region->startCoord.to_float (); + float def_val = axis_region->peakCoord.to_float (); + float max_val = axis_region->endCoord.to_float (); + + if (def_val != 0.f) + axis_tuples.set (*axis_tag, Triple (min_val, def_val, max_val)); + axis_region++; + } + return !axis_tuples.in_error (); + } + + bool get_var_regions (const hb_map_t& axes_old_index_tag_map, + hb_vector_t>& regions /* OUT */) const + { + if (!regions.alloc (regionCount)) + return false; + + for (unsigned i = 0; i < regionCount; i++) + { + hb_hashmap_t axis_tuples; + if (!get_var_region (i, axes_old_index_tag_map, axis_tuples)) + return false; + regions.push (std::move (axis_tuples)); + } + return !regions.in_error (); + } + unsigned int get_size () const { return min_size + VarRegionAxis::static_size * axisCount * regionCount; } public: @@ -2430,6 +2656,9 @@ struct VarData unsigned int get_region_index_count () const { return regionIndices.len; } + + unsigned get_region_index (unsigned i) const + { return i >= regionIndices.len ? -1 : regionIndices[i]; } unsigned int get_row_size () const { return (wordCount () + regionIndices.len) * (longWords () ? 2 : 1); } @@ -2505,6 +2734,81 @@ struct VarData get_row_size ())); } + bool serialize (hb_serialize_context_t *c, + bool has_long, + const hb_vector_t*>& rows) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + unsigned row_count = rows.length; + itemCount = row_count; + + int min_threshold = has_long ? -65536 : -128; + int max_threshold = has_long ? +65535 : +127; + enum delta_size_t { kZero=0, kNonWord, kWord }; + hb_vector_t delta_sz; + unsigned num_regions = rows[0]->length; + if (!delta_sz.resize (num_regions)) + return_trace (false); + + unsigned word_count = 0; + for (unsigned r = 0; r < num_regions; r++) + { + for (unsigned i = 0; i < row_count; i++) + { + int delta = rows[i]->arrayZ[r]; + if (delta < min_threshold || delta > max_threshold) + { + delta_sz[r] = kWord; + word_count++; + break; + } + else if (delta != 0) + { + delta_sz[r] = kNonWord; + } + } + } + + /* reorder regions: words and then non-words*/ + unsigned word_index = 0; + unsigned non_word_index = word_count; + hb_map_t ri_map; + for (unsigned r = 0; r < num_regions; r++) + { + if (!delta_sz[r]) continue; + unsigned new_r = (delta_sz[r] == kWord)? word_index++ : non_word_index++; + if (!ri_map.set (new_r, r)) + return_trace (false); + } + + wordSizeCount = word_count | (has_long ? 0x8000u /* LONG_WORDS */ : 0); + + unsigned ri_count = ri_map.get_population (); + regionIndices.len = ri_count; + if (unlikely (!c->extend (this))) return_trace (false); + + for (unsigned r = 0; r < ri_count; r++) + { + hb_codepoint_t *idx; + if (!ri_map.has (r, &idx)) + return_trace (false); + regionIndices[r] = *idx; + } + + HBUINT8 *delta_bytes = get_delta_bytes (); + unsigned row_size = get_row_size (); + for (unsigned int i = 0; i < row_count; i++) + { + for (unsigned int r = 0; r < ri_count; r++) + { + int delta = rows[i]->arrayZ[ri_map[r]]; + set_item_delta_fast (i, r, delta, delta_bytes, row_size); + } + } + return_trace (true); + } + bool serialize (hb_serialize_context_t *c, const VarData *src, const hb_inc_bimap_t &inner_map, @@ -2625,13 +2929,15 @@ struct VarData } } - protected: + public: const HBUINT8 *get_delta_bytes () const { return &StructAfter (regionIndices); } + protected: HBUINT8 *get_delta_bytes () { return &StructAfter (regionIndices); } + public: int32_t get_item_delta_fast (unsigned int item, unsigned int region, const HBUINT8 *delta_bytes, unsigned row_size) const { @@ -2662,6 +2968,7 @@ struct VarData get_row_size ()); } + protected: void set_item_delta_fast (unsigned int item, unsigned int region, int32_t delta, HBUINT8 *delta_bytes, unsigned row_size) { @@ -2704,6 +3011,7 @@ struct VarData struct VariationStore { + friend struct item_variations_t; using cache_t = VarRegionList::cache_t; cache_t *create_cache () const @@ -2774,6 +3082,36 @@ struct VariationStore dataSets.sanitize (c, this)); } + bool serialize (hb_serialize_context_t *c, + bool has_long, + const hb_vector_t& axis_tags, + const hb_vector_t*>& region_list, + const hb_vector_t& vardata_encodings) + { + TRACE_SERIALIZE (this); +#ifdef HB_NO_VAR + return_trace (false); +#endif + if (unlikely (!c->extend_min (this))) return_trace (false); + + format = 1; + if (!regions.serialize_serialize (c, axis_tags, region_list)) + return_trace (false); + + unsigned num_var_data = vardata_encodings.length; + if (!num_var_data) return_trace (false); + if (unlikely (!c->check_assign (dataSets.len, num_var_data, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) + return_trace (false); + + if (unlikely (!c->extend (dataSets))) return_trace (false); + for (unsigned i = 0; i < num_var_data; i++) + if (!dataSets[i].serialize_serialize (c, has_long, vardata_encodings[i].items)) + return_trace (false); + + return_trace (true); + } + bool serialize (hb_serialize_context_t *c, const VariationStore *src, const hb_array_t &inner_maps) @@ -2903,6 +3241,22 @@ struct VariationStore return dataSets.len; } + const VarData& get_sub_table (unsigned i) const + { +#ifdef HB_NO_VAR + return Null (VarData); +#endif + return this+dataSets[i]; + } + + const VarRegionList& get_region_list () const + { +#ifdef HB_NO_VAR + return Null (VarRegionList); +#endif + return this+regions; + } + protected: HBUINT16 format; Offset32To regions; @@ -3614,23 +3968,13 @@ struct VariationDevice auto *out = c->embed (this); if (unlikely (!out)) return_trace (nullptr); - unsigned new_idx = hb_first (*v); - out->varIdx = new_idx; + if (!c->check_assign (out->varIdx, hb_first (*v), HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (nullptr); return_trace (out); } void collect_variation_index (hb_collect_variation_indices_context_t *c) const - { - c->layout_variation_indices->add (varIdx); - int delta = 0; - if (c->normalized_coords && c->var_store) - delta = roundf (c->var_store->get_delta (varIdx, c->normalized_coords->arrayZ, - c->normalized_coords->length, c->store_cache)); - - /* set new varidx to HB_OT_LAYOUT_NO_VARIATIONS_INDEX here, will remap - * varidx later*/ - c->varidx_delta_map->set (varIdx, hb_pair_t (HB_OT_LAYOUT_NO_VARIATIONS_INDEX, delta)); - } + { c->layout_variation_indices->add (varIdx); } bool sanitize (hb_sanitize_context_t *c) const { diff --git a/gfx/harfbuzz/src/hb-ot-layout.cc b/gfx/harfbuzz/src/hb-ot-layout.cc index 5ce36693a80b..2eb8535db5a0 100644 --- a/gfx/harfbuzz/src/hb-ot-layout.cc +++ b/gfx/harfbuzz/src/hb-ot-layout.cc @@ -1308,11 +1308,16 @@ hb_ot_layout_collect_features_map (hb_face_t *face, unsigned int count = l.get_feature_indexes (0, nullptr, nullptr); feature_map->alloc (count); - for (unsigned int i = 0; i < count; i++) + /* Loop in reverse, such that earlier entries win. That emulates + * a linear search, which seems to be what other implementations do. + * We found that with arialuni_t.ttf, the "ur" language system has + * duplicate features, and the earlier ones work but not later ones. + */ + for (unsigned int i = count; i; i--) { unsigned feature_index = 0; unsigned feature_count = 1; - l.get_feature_indexes (i, &feature_count, &feature_index); + l.get_feature_indexes (i - 1, &feature_count, &feature_index); if (!feature_count) break; hb_tag_t feature_tag = g.get_feature_tag (feature_index); diff --git a/gfx/harfbuzz/src/hb-ot-os2-table.hh b/gfx/harfbuzz/src/hb-ot-os2-table.hh index 72cb662473d3..19330b9bd4e8 100644 --- a/gfx/harfbuzz/src/hb-ot-os2-table.hh +++ b/gfx/harfbuzz/src/hb-ot-os2-table.hh @@ -246,24 +246,19 @@ struct OS2 } #endif - if (c->plan->user_axes_location.has (HB_TAG ('w','g','h','t')) && - !c->plan->pinned_at_default) + Triple *axis_range; + if (c->plan->user_axes_location.has (HB_TAG ('w','g','h','t'), &axis_range)) { - float weight_class = c->plan->user_axes_location.get (HB_TAG ('w','g','h','t')).middle; - if (!c->serializer->check_assign (os2_prime->usWeightClass, - roundf (hb_clamp (weight_class, 1.0f, 1000.0f)), - HB_SERIALIZE_ERROR_INT_OVERFLOW)) - return_trace (false); + unsigned weight_class = static_cast (roundf (hb_clamp (axis_range->middle, 1.0f, 1000.0f))); + if (os2_prime->usWeightClass != weight_class) + os2_prime->usWeightClass = weight_class; } - if (c->plan->user_axes_location.has (HB_TAG ('w','d','t','h')) && - !c->plan->pinned_at_default) + if (c->plan->user_axes_location.has (HB_TAG ('w','d','t','h'), &axis_range)) { - float width = c->plan->user_axes_location.get (HB_TAG ('w','d','t','h')).middle; - if (!c->serializer->check_assign (os2_prime->usWidthClass, - roundf (map_wdth_to_widthclass (width)), - HB_SERIALIZE_ERROR_INT_OVERFLOW)) - return_trace (false); + unsigned width_class = static_cast (roundf (map_wdth_to_widthclass (axis_range->middle))); + if (os2_prime->usWidthClass != width_class) + os2_prime->usWidthClass = width_class; } if (c->plan->flags & HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES) diff --git a/gfx/harfbuzz/src/hb-ot-post-table.hh b/gfx/harfbuzz/src/hb-ot-post-table.hh index 761e49d1198e..aaecc348ee1a 100644 --- a/gfx/harfbuzz/src/hb-ot-post-table.hh +++ b/gfx/harfbuzz/src/hb-ot-post-table.hh @@ -113,12 +113,12 @@ struct post } #endif - if (c->plan->user_axes_location.has (HB_TAG ('s','l','n','t')) && - !c->plan->pinned_at_default) + Triple *axis_range; + if (c->plan->user_axes_location.has (HB_TAG ('s','l','n','t'), &axis_range)) { - float italic_angle = c->plan->user_axes_location.get (HB_TAG ('s','l','n','t')).middle; - italic_angle = hb_max (-90.f, hb_min (italic_angle, 90.f)); - post_prime->italicAngle.set_float (italic_angle); + float italic_angle = hb_max (-90.f, hb_min (axis_range->middle, 90.f)); + if (post_prime->italicAngle.to_float () != italic_angle) + post_prime->italicAngle.set_float (italic_angle); } if (glyph_names && version.major == 2) diff --git a/gfx/harfbuzz/src/hb-ot-var-common.hh b/gfx/harfbuzz/src/hb-ot-var-common.hh index 23d7f7cc3d0b..b6fe13f11374 100644 --- a/gfx/harfbuzz/src/hb-ot-var-common.hh +++ b/gfx/harfbuzz/src/hb-ot-var-common.hh @@ -27,6 +27,7 @@ #define HB_OT_VAR_COMMON_HH #include "hb-ot-layout-common.hh" +#include "hb-priority-queue.hh" namespace OT { @@ -1171,14 +1172,71 @@ struct TupleVariationData return true; } + bool create_from_item_var_data (const VarData &var_data, + const hb_vector_t>& regions, + const hb_map_t& axes_old_index_tag_map, + const hb_inc_bimap_t* inner_map = nullptr) + { + /* NULL offset, to keep original varidx valid, just return */ + if (&var_data == &Null (VarData)) + return true; + + unsigned num_regions = var_data.get_region_index_count (); + if (!tuple_vars.alloc (num_regions)) return false; + + unsigned item_count = inner_map ? inner_map->get_population () : var_data.get_item_count (); + unsigned row_size = var_data.get_row_size (); + const HBUINT8 *delta_bytes = var_data.get_delta_bytes (); + + for (unsigned r = 0; r < num_regions; r++) + { + /* In VarData, deltas are organized in rows, convert them into + * column(region) based tuples, resize deltas_x first */ + tuple_delta_t tuple; + if (!tuple.deltas_x.resize (item_count, false) || + !tuple.indices.resize (item_count, false)) + return false; + + for (unsigned i = 0; i < item_count; i++) + { + tuple.indices.arrayZ[i] = true; + tuple.deltas_x.arrayZ[i] = var_data.get_item_delta_fast (inner_map ? inner_map->backward (i) : i, + r, delta_bytes, row_size); + } + + unsigned region_index = var_data.get_region_index (r); + if (region_index >= regions.length) return false; + tuple.axis_tuples = regions.arrayZ[region_index]; + + tuple_vars.push (std::move (tuple)); + } + return !tuple_vars.in_error (); + } + private: - void change_tuple_variations_axis_limits (const hb_hashmap_t& normalized_axes_location, + static int _cmp_axis_tag (const void *pa, const void *pb) + { + const hb_tag_t *a = (const hb_tag_t*) pa; + const hb_tag_t *b = (const hb_tag_t*) pb; + return (int)(*a) - (int)(*b); + } + + bool change_tuple_variations_axis_limits (const hb_hashmap_t& normalized_axes_location, const hb_hashmap_t& axes_triple_distances) { - for (auto _ : normalized_axes_location) + /* sort axis_tag/axis_limits, make result deterministic */ + hb_vector_t axis_tags; + if (!axis_tags.alloc (normalized_axes_location.get_population ())) + return false; + for (auto t : normalized_axes_location.keys ()) + axis_tags.push (t); + + axis_tags.qsort (_cmp_axis_tag); + for (auto axis_tag : axis_tags) { - hb_tag_t axis_tag = _.first; - Triple axis_limit = _.second; + Triple *axis_limit; + if (!normalized_axes_location.has (axis_tag, &axis_limit)) + return false; TripleDistances axis_triple_distances{1.f, 1.f}; if (axes_triple_distances.has (axis_tag)) axis_triple_distances = axes_triple_distances.get (axis_tag); @@ -1186,12 +1244,13 @@ struct TupleVariationData hb_vector_t new_vars; for (const tuple_delta_t& var : tuple_vars) { - hb_vector_t out = var.change_tuple_var_axis_limit (axis_tag, axis_limit, axis_triple_distances); + hb_vector_t out = var.change_tuple_var_axis_limit (axis_tag, *axis_limit, axis_triple_distances); if (!out) continue; + unsigned new_len = new_vars.length + out.length; if (unlikely (!new_vars.alloc (new_len, false))) - { fini (); return;} + { fini (); return false;} for (unsigned i = 0; i < out.length; i++) new_vars.push (std::move (out[i])); @@ -1199,6 +1258,7 @@ struct TupleVariationData tuple_vars.fini (); tuple_vars = std::move (new_vars); } + return true; } /* merge tuple variations with overlapping tents */ @@ -1382,7 +1442,8 @@ struct TupleVariationData contour_point_vector_t* contour_points = nullptr) { if (!tuple_vars) return true; - change_tuple_variations_axis_limits (normalized_axes_location, axes_triple_distances); + if (!change_tuple_variations_axis_limits (normalized_axes_location, axes_triple_distances)) + return false; /* compute inferred deltas only for gvar */ if (contour_points) if (!calc_inferred_deltas (*contour_points)) @@ -1705,6 +1766,463 @@ struct TupleVariationData DEFINE_SIZE_MIN (4); }; +using tuple_variations_t = TupleVariationData::tuple_variations_t; +struct item_variations_t +{ + using region_t = const hb_hashmap_t*; + private: + /* each subtable is decompiled into a tuple_variations_t, in which all tuples + * have the same num of deltas (rows) */ + hb_vector_t vars; + + /* original region list, decompiled from item varstore, used when rebuilding + * region list after instantiation */ + hb_vector_t> orig_region_list; + + /* region list: vector of Regions, maintain the original order for the regions + * that existed before instantiate (), append the new regions at the end. + * Regions are stored in each tuple already, save pointers only. + * When converting back to item varstore, unused regions will be pruned */ + hb_vector_t region_list; + + /* region -> idx map after instantiation and pruning unused regions */ + hb_hashmap_t region_map; + + /* all delta rows after instantiation */ + hb_vector_t> delta_rows; + /* final optimized vector of encoding objects used to assemble the varstore */ + hb_vector_t encodings; + + /* old varidxes -> new var_idxes map */ + hb_map_t varidx_map; + + /* has long words */ + bool has_long = false; + + public: + bool has_long_word () const + { return has_long; } + + const hb_vector_t& get_region_list () const + { return region_list; } + + const hb_vector_t& get_vardata_encodings () const + { return encodings; } + + const hb_map_t& get_varidx_map () const + { return varidx_map; } + + bool instantiate (const VariationStore& varStore, + const hb_subset_plan_t *plan, + bool optimize=true, + bool use_no_variation_idx=true, + const hb_array_t inner_maps = hb_array_t ()) + { + if (!create_from_item_varstore (varStore, plan->axes_old_index_tag_map, inner_maps)) + return false; + if (!instantiate_tuple_vars (plan->axes_location, plan->axes_triple_distances)) + return false; + return as_item_varstore (optimize, use_no_variation_idx); + } + + /* keep below APIs public only for unit test: test-item-varstore */ + bool create_from_item_varstore (const VariationStore& varStore, + const hb_map_t& axes_old_index_tag_map, + const hb_array_t inner_maps = hb_array_t ()) + { + const VarRegionList& regionList = varStore.get_region_list (); + if (!regionList.get_var_regions (axes_old_index_tag_map, orig_region_list)) + return false; + + unsigned num_var_data = varStore.get_sub_table_count (); + if (inner_maps && inner_maps.length != num_var_data) return false; + if (!vars.alloc (num_var_data)) return false; + + for (unsigned i = 0; i < num_var_data; i++) + { + if (inner_maps && !inner_maps.arrayZ[i].get_population ()) + continue; + tuple_variations_t var_data_tuples; + if (!var_data_tuples.create_from_item_var_data (varStore.get_sub_table (i), + orig_region_list, + axes_old_index_tag_map, + inner_maps ? &(inner_maps.arrayZ[i]) : nullptr)) + return false; + + vars.push (std::move (var_data_tuples)); + } + return !vars.in_error (); + } + + bool instantiate_tuple_vars (const hb_hashmap_t& normalized_axes_location, + const hb_hashmap_t& axes_triple_distances) + { + for (tuple_variations_t& tuple_vars : vars) + if (!tuple_vars.instantiate (normalized_axes_location, axes_triple_distances)) + return false; + + if (!build_region_list ()) return false; + return true; + } + + bool build_region_list () + { + /* scan all tuples and collect all unique regions, prune unused regions */ + hb_hashmap_t all_regions; + hb_hashmap_t used_regions; + + /* use a vector when inserting new regions, make result deterministic */ + hb_vector_t all_unique_regions; + for (const tuple_variations_t& sub_table : vars) + { + for (const tuple_delta_t& tuple : sub_table.tuple_vars) + { + region_t r = &(tuple.axis_tuples); + if (!used_regions.has (r)) + { + bool all_zeros = true; + for (float d : tuple.deltas_x) + { + int delta = (int) roundf (d); + if (delta != 0) + { + all_zeros = false; + break; + } + } + if (!all_zeros) + { + if (!used_regions.set (r, 1)) + return false; + } + } + if (all_regions.has (r)) + continue; + if (!all_regions.set (r, 1)) + return false; + all_unique_regions.push (r); + } + } + + if (!all_regions || !all_unique_regions) return false; + if (!region_list.alloc (all_regions.get_population ())) + return false; + + unsigned idx = 0; + /* append the original regions that pre-existed */ + for (const auto& r : orig_region_list) + { + if (!all_regions.has (&r) || !used_regions.has (&r)) + continue; + + region_list.push (&r); + if (!region_map.set (&r, idx)) + return false; + all_regions.del (&r); + idx++; + } + + /* append the new regions at the end */ + for (const auto& r: all_unique_regions) + { + if (!all_regions.has (r) || !used_regions.has (r)) + continue; + region_list.push (r); + if (!region_map.set (r, idx)) + return false; + all_regions.del (r); + idx++; + } + return (!region_list.in_error ()) && (!region_map.in_error ()); + } + + /* main algorithm ported from fonttools VarStore_optimize() method, optimize + * varstore by default */ + + struct combined_gain_idx_tuple_t + { + int gain; + unsigned idx_1; + unsigned idx_2; + + combined_gain_idx_tuple_t () = default; + combined_gain_idx_tuple_t (int gain_, unsigned i, unsigned j) + :gain (gain_), idx_1 (i), idx_2 (j) {} + + bool operator < (const combined_gain_idx_tuple_t& o) + { + if (gain != o.gain) + return gain < o.gain; + + if (idx_1 != o.idx_1) + return idx_1 < o.idx_1; + + return idx_2 < o.idx_2; + } + + bool operator <= (const combined_gain_idx_tuple_t& o) + { + if (*this < o) return true; + return gain == o.gain && idx_1 == o.idx_1 && idx_2 == o.idx_2; + } + }; + + bool as_item_varstore (bool optimize=true, bool use_no_variation_idx=true) + { + if (!region_list) return false; + unsigned num_cols = region_list.length; + /* pre-alloc a 2D vector for all sub_table's VarData rows */ + unsigned total_rows = 0; + for (unsigned major = 0; major < vars.length; major++) + { + const tuple_variations_t& tuples = vars[major]; + /* all tuples in each sub_table should have same num of deltas(num rows) */ + total_rows += tuples.tuple_vars[0].deltas_x.length; + } + + if (!delta_rows.resize (total_rows)) return false; + /* init all rows to [0]*num_cols */ + for (unsigned i = 0; i < total_rows; i++) + if (!(delta_rows[i].resize (num_cols))) return false; + + /* old VarIdxes -> full encoding_row mapping */ + hb_hashmap_t*> front_mapping; + unsigned start_row = 0; + hb_vector_t encoding_objs; + hb_hashmap_t, unsigned> chars_idx_map; + + /* delta_rows map, used for filtering out duplicate rows */ + hb_hashmap_t*, unsigned> delta_rows_map; + for (unsigned major = 0; major < vars.length; major++) + { + /* deltas are stored in tuples(column based), convert them back into items + * (row based) delta */ + const tuple_variations_t& tuples = vars[major]; + unsigned num_rows = tuples.tuple_vars[0].deltas_x.length; + for (const tuple_delta_t& tuple: tuples.tuple_vars) + { + if (tuple.deltas_x.length != num_rows) + return false; + + /* skip unused regions */ + unsigned *col_idx; + if (!region_map.has (&(tuple.axis_tuples), &col_idx)) + continue; + + for (unsigned i = 0; i < num_rows; i++) + { + int rounded_delta = roundf (tuple.deltas_x[i]); + delta_rows[start_row + i][*col_idx] += rounded_delta; + if ((!has_long) && (rounded_delta < -65536 || rounded_delta > 65535)) + has_long = true; + } + } + + if (!optimize) + { + /* assemble a delta_row_encoding_t for this subtable, skip optimization so + * chars is not initialized, we only need delta rows for serialization */ + delta_row_encoding_t obj; + for (unsigned r = start_row; r < start_row + num_rows; r++) + obj.add_row (&(delta_rows.arrayZ[r])); + + encodings.push (std::move (obj)); + start_row += num_rows; + continue; + } + + for (unsigned minor = 0; minor < num_rows; minor++) + { + const hb_vector_t& row = delta_rows[start_row + minor]; + if (use_no_variation_idx) + { + bool all_zeros = true; + for (int delta : row) + { + if (delta != 0) + { + all_zeros = false; + break; + } + } + if (all_zeros) + continue; + } + + if (!front_mapping.set ((major<<16) + minor, &row)) + return false; + + hb_vector_t chars = delta_row_encoding_t::get_row_chars (row); + if (!chars) return false; + + if (delta_rows_map.has (&row)) + continue; + + delta_rows_map.set (&row, 1); + unsigned *obj_idx; + if (chars_idx_map.has (chars, &obj_idx)) + { + delta_row_encoding_t& obj = encoding_objs[*obj_idx]; + if (!obj.add_row (&row)) + return false; + } + else + { + if (!chars_idx_map.set (chars, encoding_objs.length)) + return false; + delta_row_encoding_t obj (std::move (chars), &row); + encoding_objs.push (std::move (obj)); + } + } + + start_row += num_rows; + } + + /* return directly if no optimization, maintain original VariationIndex so + * varidx_map would be empty */ + if (!optimize) return !encodings.in_error (); + + /* sort encoding_objs */ + encoding_objs.qsort (); + + /* main algorithm: repeatedly pick 2 best encodings to combine, and combine + * them */ + hb_priority_queue_t queue; + unsigned num_todos = encoding_objs.length; + for (unsigned i = 0; i < num_todos; i++) + { + for (unsigned j = i + 1; j < num_todos; j++) + { + int combining_gain = encoding_objs.arrayZ[i].gain_from_merging (encoding_objs.arrayZ[j]); + if (combining_gain > 0) + queue.insert (combined_gain_idx_tuple_t (-combining_gain, i, j), 0); + } + } + + hb_set_t removed_todo_idxes; + while (queue) + { + auto t = queue.pop_minimum ().first; + unsigned i = t.idx_1; + unsigned j = t.idx_2; + + if (removed_todo_idxes.has (i) || removed_todo_idxes.has (j)) + continue; + + delta_row_encoding_t& encoding = encoding_objs.arrayZ[i]; + delta_row_encoding_t& other_encoding = encoding_objs.arrayZ[j]; + + removed_todo_idxes.add (i); + removed_todo_idxes.add (j); + + hb_vector_t combined_chars; + if (!combined_chars.alloc (encoding.chars.length)) + return false; + + for (unsigned idx = 0; idx < encoding.chars.length; idx++) + { + uint8_t v = hb_max (encoding.chars.arrayZ[idx], other_encoding.chars.arrayZ[idx]); + combined_chars.push (v); + } + + delta_row_encoding_t combined_encoding_obj (std::move (combined_chars)); + for (const auto& row : hb_concat (encoding.items, other_encoding.items)) + combined_encoding_obj.add_row (row); + + for (unsigned idx = 0; idx < encoding_objs.length; idx++) + { + if (removed_todo_idxes.has (idx)) continue; + + const delta_row_encoding_t& obj = encoding_objs.arrayZ[idx]; + if (obj.chars == combined_chars) + { + for (const auto& row : obj.items) + combined_encoding_obj.add_row (row); + + removed_todo_idxes.add (idx); + continue; + } + + int combined_gain = combined_encoding_obj.gain_from_merging (obj); + if (combined_gain > 0) + queue.insert (combined_gain_idx_tuple_t (-combined_gain, idx, encoding_objs.length), 0); + } + + encoding_objs.push (std::move (combined_encoding_obj)); + } + + int num_final_encodings = (int) encoding_objs.length - (int) removed_todo_idxes.get_population (); + if (num_final_encodings <= 0) return false; + + if (!encodings.alloc (num_final_encodings)) return false; + for (unsigned i = 0; i < encoding_objs.length; i++) + { + if (removed_todo_idxes.has (i)) continue; + encodings.push (std::move (encoding_objs.arrayZ[i])); + } + + /* sort again based on width, make result deterministic */ + encodings.qsort (delta_row_encoding_t::cmp_width); + + return compile_varidx_map (front_mapping); + } + + private: + /* compile varidx_map for one VarData subtable (index specified by major) */ + bool compile_varidx_map (const hb_hashmap_t*>& front_mapping) + { + /* full encoding_row -> new VarIdxes mapping */ + hb_hashmap_t*, unsigned> back_mapping; + + for (unsigned major = 0; major < encodings.length; major++) + { + delta_row_encoding_t& encoding = encodings[major]; + /* just sanity check, this shouldn't happen */ + if (encoding.is_empty ()) + return false; + + unsigned num_rows = encoding.items.length; + + /* sort rows, make result deterministic */ + encoding.items.qsort (_cmp_row); + + /* compile old to new var_idxes mapping */ + for (unsigned minor = 0; minor < num_rows; minor++) + { + unsigned new_varidx = (major << 16) + minor; + back_mapping.set (encoding.items.arrayZ[minor], new_varidx); + } + } + + for (auto _ : front_mapping.iter ()) + { + unsigned old_varidx = _.first; + unsigned *new_varidx; + if (back_mapping.has (_.second, &new_varidx)) + varidx_map.set (old_varidx, *new_varidx); + else + varidx_map.set (old_varidx, HB_OT_LAYOUT_NO_VARIATIONS_INDEX); + } + return !varidx_map.in_error (); + } + + static int _cmp_row (const void *pa, const void *pb) + { + /* compare pointers of vectors(const hb_vector_t*) that represent a row */ + const hb_vector_t** a = (const hb_vector_t**) pa; + const hb_vector_t** b = (const hb_vector_t**) pb; + + for (unsigned i = 0; i < (*b)->length; i++) + { + int va = (*a)->arrayZ[i]; + int vb = (*b)->arrayZ[i]; + if (va != vb) + return va < vb ? -1 : 1; + } + return 0; + } +}; + } /* namespace OT */ diff --git a/gfx/harfbuzz/src/hb-ot-var-cvar-table.hh b/gfx/harfbuzz/src/hb-ot-var-cvar-table.hh index adb81769bdc5..381ae3c616be 100644 --- a/gfx/harfbuzz/src/hb-ot-var-cvar-table.hh +++ b/gfx/harfbuzz/src/hb-ot-var-cvar-table.hh @@ -54,14 +54,14 @@ struct cvar bool decompile_tuple_variations (unsigned axis_count, unsigned point_count, + hb_blob_t *blob, bool is_gvar, const hb_map_t *axes_old_index_tag_map, TupleVariationData::tuple_variations_t& tuple_variations /* OUT */) const { hb_vector_t shared_indices; TupleVariationData::tuple_iterator_t iterator; - unsigned var_data_length = tupleVariationData.get_size (axis_count); - hb_bytes_t var_data_bytes = hb_bytes_t (reinterpret_cast (get_tuple_var_data ()), var_data_length); + hb_bytes_t var_data_bytes = blob->as_bytes ().sub_array (4); if (!TupleVariationData::get_tuple_iterator (var_data_bytes, axis_count, this, shared_indices, &iterator)) return false; @@ -143,19 +143,6 @@ struct cvar if (c->plan->all_axes_pinned) return_trace (false); - /* subset() for cvar is called by partial instancing only, we always pass - * through cvar table in other cases */ - if (!c->plan->normalized_coords) - { - unsigned axis_count = c->plan->source->table.fvar->get_axis_count (); - unsigned total_size = min_size + tupleVariationData.get_size (axis_count); - char *out = c->serializer->allocate_size (total_size); - if (unlikely (!out)) return_trace (false); - - hb_memcpy (out, this, total_size); - return_trace (true); - } - OT::TupleVariationData::tuple_variations_t tuple_variations; unsigned axis_count = c->plan->axes_old_index_tag_map.get_population (); @@ -164,7 +151,8 @@ struct cvar unsigned point_count = hb_blob_get_length (cvt_blob) / FWORD::static_size; hb_blob_destroy (cvt_blob); - if (!decompile_tuple_variations (axis_count, point_count, false, + if (!decompile_tuple_variations (axis_count, point_count, + c->source_blob, false, &(c->plan->axes_old_index_tag_map), tuple_variations)) return_trace (false); diff --git a/gfx/harfbuzz/src/hb-ot-var-hvar-table.hh b/gfx/harfbuzz/src/hb-ot-var-hvar-table.hh index 490f883fccec..ca1ea1ca8c28 100644 --- a/gfx/harfbuzz/src/hb-ot-var-hvar-table.hh +++ b/gfx/harfbuzz/src/hb-ot-var-hvar-table.hh @@ -134,6 +134,36 @@ struct index_map_subset_plan_t } } + bool remap_after_instantiation (const hb_subset_plan_t *plan, + const hb_map_t& varidx_map) + { + /* recalculate bit_count after remapping */ + outer_bit_count = 1; + inner_bit_count = 1; + + for (const auto &_ : plan->new_to_old_gid_list) + { + hb_codepoint_t new_gid = _.first; + if (unlikely (new_gid >= map_count)) break; + + uint32_t v = output_map.arrayZ[new_gid]; + uint32_t *new_varidx; + if (!varidx_map.has (v, &new_varidx)) + return false; + + output_map.arrayZ[new_gid] = *new_varidx; + + unsigned outer = (*new_varidx) >> 16; + unsigned bit_count = (outer == 0) ? 1 : hb_bit_storage (outer); + outer_bit_count = hb_max (bit_count, outer_bit_count); + + unsigned inner = (*new_varidx) & 0xFFFF; + bit_count = (inner == 0) ? 1 : hb_bit_storage (inner); + inner_bit_count = hb_max (bit_count, inner_bit_count); + } + return true; + } + unsigned int get_inner_bit_count () const { return inner_bit_count; } unsigned int get_width () const { return ((outer_bit_count + inner_bit_count + 7) / 8); } unsigned int get_map_count () const { return map_count; } @@ -211,6 +241,16 @@ struct hvarvvar_subset_plan_t index_map_plans[i].remap (index_maps[i], outer_map, inner_maps, plan); } + /* remap */ + bool remap_index_map_plans (const hb_subset_plan_t *plan, + const hb_map_t& varidx_map) + { + for (unsigned i = 0; i < index_map_plans.length; i++) + if (!index_map_plans[i].remap_after_instantiation (plan, varidx_map)) + return false; + return true; + } + void fini () { for (unsigned int i = 0; i < inner_sets.length; i++) @@ -289,6 +329,9 @@ struct HVARVVAR bool _subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); + if (c->plan->all_axes_pinned) + return_trace (false); + hvarvvar_subset_plan_t hvar_plan; hb_vector_t index_maps; @@ -302,11 +345,37 @@ struct HVARVVAR out->version.major = 1; out->version.minor = 0; - if (unlikely (!out->varStore - .serialize_serialize (c->serializer, - hvar_plan.var_store, - hvar_plan.inner_maps.as_array ()))) + if (c->plan->normalized_coords) + { + item_variations_t item_vars; + if (!item_vars.instantiate (this+varStore, c->plan, + advMap == 0 ? false : true, + false, /* use_no_variation_idx = false */ + hvar_plan.inner_maps.as_array ())) + return_trace (false); + + if (!out->varStore.serialize_serialize (c->serializer, + item_vars.has_long_word (), + c->plan->axis_tags, + item_vars.get_region_list (), + item_vars.get_vardata_encodings ())) + return_trace (false); + + /* if varstore is optimized, remap output_map */ + if (advMap) + { + if (!hvar_plan.remap_index_map_plans (c->plan, item_vars.get_varidx_map ())) + return_trace (false); + } + } + else + { + if (unlikely (!out->varStore + .serialize_serialize (c->serializer, + hvar_plan.var_store, + hvar_plan.inner_maps.as_array ()))) return_trace (false); + } return_trace (out->T::serialize_index_maps (c->serializer, hvar_plan.index_map_plans.as_array ())); diff --git a/gfx/harfbuzz/src/hb-ot-var-mvar-table.hh b/gfx/harfbuzz/src/hb-ot-var-mvar-table.hh index d27ebb39c0a0..ceabc9a3e296 100644 --- a/gfx/harfbuzz/src/hb-ot-var-mvar-table.hh +++ b/gfx/harfbuzz/src/hb-ot-var-mvar-table.hh @@ -27,7 +27,7 @@ #ifndef HB_OT_VAR_MVAR_TABLE_HH #define HB_OT_VAR_MVAR_TABLE_HH -#include "hb-ot-layout-common.hh" +#include "hb-ot-var-common.hh" namespace OT { @@ -41,6 +41,19 @@ struct VariationValueRecord return_trace (c->check_struct (this)); } + bool subset (hb_subset_context_t *c, + const hb_map_t& varidx_map) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + hb_codepoint_t *new_idx; + return_trace (c->serializer->check_assign (out->varIdx, + (varidx_map.has (varIdx, &new_idx)) ? *new_idx : HB_OT_LAYOUT_NO_VARIATIONS_INDEX, + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + public: Tag valueTag; /* Four-byte tag identifying a font-wide measure. */ VarIdx varIdx; /* Outer/inner index into VariationStore item. */ @@ -73,6 +86,47 @@ struct MVAR valueRecordSize)); } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); +#ifdef HB_NO_VAR + return_trace (false); +#endif + + if (c->plan->all_axes_pinned) + return_trace (false); + + MVAR *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->version = version; + out->reserved = reserved; + out->valueRecordSize = valueRecordSize; + out->valueRecordCount = valueRecordCount; + + item_variations_t item_vars; + const VariationStore& src_var_store = this+varStore; + + if (!item_vars.instantiate (src_var_store, c->plan)) + return_trace (false); + + /* serialize varstore */ + if (!out->varStore.serialize_serialize (c->serializer, item_vars.has_long_word (), + c->plan->axis_tags, + item_vars.get_region_list (), + item_vars.get_vardata_encodings ())) + return_trace (false); + + /* serialize value records array */ + unsigned value_rec_count = valueRecordCount; + const VariationValueRecord *record = reinterpret_cast (valuesZ.arrayZ); + for (unsigned i = 0; i < value_rec_count; i++) + { + if (!record->subset (c, item_vars.get_varidx_map ())) return_trace (false); + record++; + } + return_trace (true); + } + float get_var (hb_tag_t tag, const int *coords, unsigned int coord_count) const { diff --git a/gfx/harfbuzz/src/hb-priority-queue.hh b/gfx/harfbuzz/src/hb-priority-queue.hh index baac7e1e6957..2c8ccbfb6885 100644 --- a/gfx/harfbuzz/src/hb-priority-queue.hh +++ b/gfx/harfbuzz/src/hb-priority-queue.hh @@ -42,10 +42,11 @@ * priority of its children. The heap is stored in an array, with the * children of node i stored at indices 2i + 1 and 2i + 2. */ +template struct hb_priority_queue_t { private: - typedef hb_pair_t item_t; + typedef hb_pair_t item_t; hb_vector_t heap; public: @@ -57,7 +58,7 @@ struct hb_priority_queue_t #ifndef HB_OPTIMIZE_SIZE HB_ALWAYS_INLINE #endif - void insert (int64_t priority, unsigned value) + void insert (K priority, unsigned value) { heap.push (item_t (priority, value)); if (unlikely (heap.in_error ())) return; diff --git a/gfx/harfbuzz/src/hb-subset-input.cc b/gfx/harfbuzz/src/hb-subset-input.cc index aade5585fa43..0277d3d3d6a2 100644 --- a/gfx/harfbuzz/src/hb-subset-input.cc +++ b/gfx/harfbuzz/src/hb-subset-input.cc @@ -74,7 +74,6 @@ hb_subset_input_t::hb_subset_input_t () HB_TAG ('p', 'r', 'e', 'p'), HB_TAG ('V', 'D', 'M', 'X'), HB_TAG ('D', 'S', 'I', 'G'), - HB_TAG ('M', 'V', 'A', 'R'), }; sets.no_subset_tables->add_array (default_no_subset_tables, ARRAY_LENGTH (default_no_subset_tables)); diff --git a/gfx/harfbuzz/src/hb-subset-plan-member-list.hh b/gfx/harfbuzz/src/hb-subset-plan-member-list.hh index 01d8b0f8bea2..8bc1fcb56820 100644 --- a/gfx/harfbuzz/src/hb-subset-plan-member-list.hh +++ b/gfx/harfbuzz/src/hb-subset-plan-member-list.hh @@ -92,7 +92,7 @@ HB_SUBSET_PLAN_MEMBER (hb_map_t, colrv1_layers) HB_SUBSET_PLAN_MEMBER (hb_map_t, colr_palettes) //Old layout item variation index -> (New varidx, delta) mapping -HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E()>), layout_variation_idx_delta_map) +HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E()>), layout_variation_idx_delta_map) //gdef varstore retained varidx mapping HB_SUBSET_PLAN_MEMBER (hb_vector_t, gdef_varstore_inner_maps) @@ -113,6 +113,8 @@ HB_SUBSET_PLAN_MEMBER (hb_map_t, axes_index_map) //axis_index->axis_tag mapping in fvar axis array HB_SUBSET_PLAN_MEMBER (hb_map_t, axes_old_index_tag_map) +//vector of retained axis tags in the order of axes given in the 'fvar' table +HB_SUBSET_PLAN_MEMBER (hb_vector_t, axis_tags) //hmtx metrics map: new gid->(advance, lsb) HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E()>), hmtx_map) diff --git a/gfx/harfbuzz/src/hb-subset-plan.cc b/gfx/harfbuzz/src/hb-subset-plan.cc index c293ba3bafdd..c688b7187b2c 100644 --- a/gfx/harfbuzz/src/hb-subset-plan.cc +++ b/gfx/harfbuzz/src/hb-subset-plan.cc @@ -399,34 +399,20 @@ _collect_layout_variation_indices (hb_subset_plan_t* plan) return; } - const OT::VariationStore *var_store = nullptr; hb_set_t varidx_set; - float *store_cache = nullptr; - bool collect_delta = plan->pinned_at_default ? false : true; - if (collect_delta) - { - if (gdef->has_var_store ()) - { - var_store = &(gdef->get_var_store ()); - store_cache = var_store->create_cache (); - } - } - OT::hb_collect_variation_indices_context_t c (&varidx_set, - &plan->layout_variation_idx_delta_map, - plan->normalized_coords ? &(plan->normalized_coords) : nullptr, - var_store, &plan->_glyphset_gsub, - &plan->gpos_lookups, - store_cache); + &plan->gpos_lookups); gdef->collect_variation_indices (&c); if (hb_ot_layout_has_positioning (plan->source)) gpos->collect_variation_indices (&c); - var_store->destroy_cache (store_cache); - - gdef->remap_layout_variation_indices (&varidx_set, &plan->layout_variation_idx_delta_map); + gdef->remap_layout_variation_indices (&varidx_set, + plan->normalized_coords, + !plan->pinned_at_default, + plan->all_axes_pinned, + &plan->layout_variation_idx_delta_map); unsigned subtable_count = gdef->has_var_store () ? gdef->get_var_store ().get_sub_table_count () : 0; _generate_varstore_inner_maps (varidx_set, subtable_count, plan->gdef_varstore_inner_maps); @@ -927,6 +913,7 @@ _normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan) { axis_not_pinned = true; plan->axes_index_map.set (old_axis_idx, new_axis_idx); + plan->axis_tags.push (axis_tag); new_axis_idx++; } diff --git a/gfx/harfbuzz/src/hb-subset.cc b/gfx/harfbuzz/src/hb-subset.cc index 100ce87d50c7..229b1c30884c 100644 --- a/gfx/harfbuzz/src/hb-subset.cc +++ b/gfx/harfbuzz/src/hb-subset.cc @@ -55,6 +55,7 @@ #include "hb-ot-var-fvar-table.hh" #include "hb-ot-var-gvar-table.hh" #include "hb-ot-var-hvar-table.hh" +#include "hb-ot-var-mvar-table.hh" #include "hb-ot-math-table.hh" #include "hb-ot-stat-table.hh" #include "hb-repacker.hh" @@ -460,6 +461,8 @@ _dependencies_satisfied (hb_subset_plan_t *plan, hb_tag_t tag, case HB_OT_TAG_vmtx: case HB_OT_TAG_maxp: return !plan->normalized_coords || !pending_subset_tags.has (HB_OT_TAG_glyf); + case HB_OT_TAG_GPOS: + return !plan->normalized_coords || plan->all_axes_pinned || !pending_subset_tags.has (HB_OT_TAG_GDEF); default: return true; } @@ -514,6 +517,8 @@ _subset_table (hb_subset_plan_t *plan, case HB_OT_TAG_HVAR: return _subset (plan, buf); case HB_OT_TAG_VVAR: return _subset (plan, buf); #endif + +#ifndef HB_NO_VAR case HB_OT_TAG_fvar: if (plan->user_axes_location.is_empty ()) return _passthrough (plan, tag); return _subset (plan, buf); @@ -523,6 +528,11 @@ _subset_table (hb_subset_plan_t *plan, case HB_OT_TAG_cvar: if (plan->user_axes_location.is_empty ()) return _passthrough (plan, tag); return _subset (plan, buf); + case HB_OT_TAG_MVAR: + if (plan->user_axes_location.is_empty ()) return _passthrough (plan, tag); + return _subset (plan, buf); +#endif + case HB_OT_TAG_STAT: if (!plan->user_axes_location.is_empty ()) return _subset (plan, buf); else return _passthrough (plan, tag); diff --git a/gfx/harfbuzz/src/hb-vector.hh b/gfx/harfbuzz/src/hb-vector.hh index 66e687a1a20e..13cfe56b59f9 100644 --- a/gfx/harfbuzz/src/hb-vector.hh +++ b/gfx/harfbuzz/src/hb-vector.hh @@ -460,7 +460,7 @@ struct hb_vector_t Type pop () { if (!length) return Null (Type); - Type v {std::move (arrayZ[length - 1])}; + Type v (std::move (arrayZ[length - 1])); arrayZ[length - 1].~Type (); length--; return v; diff --git a/gfx/harfbuzz/src/hb-version.h b/gfx/harfbuzz/src/hb-version.h index 73fa16cd8659..cd7257e45cfa 100644 --- a/gfx/harfbuzz/src/hb-version.h +++ b/gfx/harfbuzz/src/hb-version.h @@ -53,14 +53,14 @@ HB_BEGIN_DECLS * * The micro component of the library version available at compile-time. */ -#define HB_VERSION_MICRO 1 +#define HB_VERSION_MICRO 2 /** * HB_VERSION_STRING: * * A string literal containing the library version available at compile-time. */ -#define HB_VERSION_STRING "8.2.1" +#define HB_VERSION_STRING "8.2.2" /** * HB_VERSION_ATLEAST: diff --git a/gfx/harfbuzz/src/meson.build b/gfx/harfbuzz/src/meson.build index 87e8962d5404..5401e5df6e96 100644 --- a/gfx/harfbuzz/src/meson.build +++ b/gfx/harfbuzz/src/meson.build @@ -1,3 +1,5 @@ +fs = import('fs') + hb_version_h = configure_file( command: [find_program('gen-hb-version.py'), meson.project_version(), '@OUTPUT@', '@INPUT@'], input: 'hb-version.h.in', @@ -703,13 +705,14 @@ if get_option('tests').enabled() 'test-serialize': ['test-serialize.cc', 'hb-static.cc'], 'test-set': ['test-set.cc', 'hb-static.cc'], 'test-tuple-varstore': ['test-tuple-varstore.cc', 'hb-subset-instancer-solver.cc', 'hb-static.cc'], + 'test-item-varstore': ['test-item-varstore.cc', 'hb-subset-instancer-solver.cc', 'hb-static.cc'], 'test-use-table': 'test-use-table.cc', 'test-vector': ['test-vector.cc', 'hb-static.cc'], } foreach name, source : noinst_programs executable(name, source, include_directories: incconfig, - cpp_args: cpp_args, + cpp_args: cpp_args + ['-UNDEBUG'], dependencies: libharfbuzz_dep, install: false, ) @@ -789,15 +792,95 @@ endif have_gobject = conf.get('HAVE_GOBJECT', 0) == 1 +# This code (especially PACKAGE_INIT) kept similar to what CMake's own +# configure_package_config_file() generates, see +# https://cmake.org/cmake/help/latest/module/CMakePackageConfigHelpers.html#command:configure_package_config_file + cmake_config = configuration_data() -cmake_config.set('libdir', get_option('prefix') / get_option('libdir')) -cmake_config.set('includedir', get_option('prefix') / get_option('includedir')) -cmake_config.set('HB_LIBTOOL_VERSION_INFO', hb_libtool_version_info) -cmake_config.set('have_gobject', '@0@'.format(have_gobject)) +cmake_config_dir = cmake_package_install_dir / 'harfbuzz' + +have_fs_relative_to = meson.version().version_compare('>=1.3.0') + +if not have_fs_relative_to + relative_to = find_program('relative_to.py') +endif + +if have_fs_relative_to + cmake_package_prefix_dir = fs.relative_to(get_option('prefix'), get_option('prefix') / cmake_config_dir) +else + cmake_package_prefix_dir = run_command(relative_to, get_option('prefix'), get_option('prefix') / cmake_config_dir, check: true).stdout().strip() +endif + +cmake_package_prefix_dir = '${CMAKE_CURRENT_LIST_DIR}/@0@'.format(cmake_package_prefix_dir) + +# Make all the relevant paths relative to our prefix, so we can later append +# them onto ${PACKAGE_PREFIX_DIR} to get the correct paths. + +cmake_install_includedir = get_option('includedir') + +if fs.is_absolute(cmake_install_includedir) + if have_fs_relative_to + cmake_install_includedir = fs.relative_to(cmake_install_includedir, get_option('prefix')) + else + cmake_install_includedir = run_command(relative_to, cmake_install_includedir, get_option('prefix'), check: true).stdout().strip() + endif +endif + +cmake_install_libdir = get_option('libdir') + +if fs.is_absolute(cmake_install_libdir) + if have_fs_relative_to + cmake_install_libdir = fs.relative_to(cmake_install_libdir, get_option('prefix')) + else + cmake_install_libdir = run_command(relative_to, cmake_install_libdir, get_option('prefix'), check: true).stdout().strip() + endif +endif + +cmake_config.set('PACKAGE_INIT', ''' +get_filename_component(PACKAGE_PREFIX_DIR "@0@" ABSOLUTE) + +macro(set_and_check _var _file) + set(${_var} "${_file}") + if(NOT EXISTS "${_file}") + message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !") + endif() +endmacro() + +macro(check_required_components _NAME) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(NOT ${_NAME}_${comp}_FOUND) + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_NAME}_FOUND FALSE) + endif() + endif() + endforeach() +endmacro() +'''.format(cmake_package_prefix_dir)) + +cmake_config.set('PACKAGE_CMAKE_INSTALL_INCLUDEDIR', '${PACKAGE_PREFIX_DIR}/@0@'.format(cmake_install_includedir)) +cmake_config.set('PACKAGE_CMAKE_INSTALL_LIBDIR', '${PACKAGE_PREFIX_DIR}/@0@'.format(cmake_install_libdir)) +cmake_config.set('PACKAGE_INCLUDE_INSTALL_DIR', '${PACKAGE_PREFIX_DIR}/@0@/@1@'.format(cmake_install_includedir, meson.project_name())) +cmake_config.set('HB_HAVE_GOBJECT', have_gobject ? 'YES' : 'NO') +cmake_config.set('HB_LIBRARY_TYPE', get_option('default_library') == 'static' ? 'STATIC' : 'SHARED') + +if get_option('default_library') == 'static' + cmake_config.set('HB_LIB_PREFIX', '${CMAKE_STATIC_LIBRARY_PREFIX}') + cmake_config.set('HB_LIB_SUFFIX', '${CMAKE_STATIC_LIBRARY_SUFFIX}') +elif host_machine.system() == 'darwin' + cmake_config.set('HB_LIB_PREFIX', '${CMAKE_SHARED_LIBRARY_PREFIX}') + cmake_config.set('HB_LIB_SUFFIX', '.@0@.${CMAKE_SHARED_LIBRARY_SUFFIX}'.format(hb_so_version)) +elif host_machine.system() == 'windows' + cmake_config.set('HB_LIB_PREFIX', '${CMAKE_IMPORT_LIBRARY_PREFIX}') + cmake_config.set('HB_LIB_SUFFIX', '${CMAKE_IMPORT_LIBRARY_SUFFIX}') +else + cmake_config.set('HB_LIB_PREFIX', '${CMAKE_SHARED_LIBRARY_PREFIX}') + cmake_config.set('HB_LIB_SUFFIX', '${CMAKE_SHARED_LIBRARY_SUFFIX}.@0@'.format(version)) +endif + configure_file(input: 'harfbuzz-config.cmake.in', output: 'harfbuzz-config.cmake', configuration: cmake_config, - install_dir: get_option('libdir') / 'cmake' / 'harfbuzz', + install_dir: cmake_config_dir, ) gobject_enums_c = [] diff --git a/gfx/harfbuzz/src/relative_to.py b/gfx/harfbuzz/src/relative_to.py new file mode 100755 index 000000000000..8a676bf8f383 --- /dev/null +++ b/gfx/harfbuzz/src/relative_to.py @@ -0,0 +1,6 @@ +#!/usr/bin/python3 + +import sys +from os import path + +print(path.relpath(sys.argv[1], sys.argv[2]))