From 247039de5afebacf0a84b4c59ae814c9743c23c5 Mon Sep 17 00:00:00 2001 From: Qunxin Liu Date: Fri, 26 Apr 2024 13:36:52 -0700 Subject: [PATCH] [instancer] partial instantiating support for COLRv1 Also add subset support for COLRv1 VarStore/DeltaSetIndexMap --- src/OT/Color/COLR/COLR.hh | 201 +++++++++++++++++++++++++++--- src/hb-subset-plan-member-list.hh | 4 + src/hb-subset-plan.cc | 56 ++++++++- 3 files changed, 240 insertions(+), 21 deletions(-) diff --git a/src/OT/Color/COLR/COLR.hh b/src/OT/Color/COLR/COLR.hh index d84e07039..ecbf12b67 100644 --- a/src/OT/Color/COLR/COLR.hh +++ b/src/OT/Color/COLR/COLR.hh @@ -269,8 +269,18 @@ struct Variable if (c->plan->all_axes_pinned) return_trace (true); - //TODO: update varIdxBase for partial-instancing - return_trace (c->serializer->embed (varIdxBase)); + VarIdx new_varidx; + new_varidx = varIdxBase; + if (varIdxBase != VarIdx::NO_VARIATION) + { + hb_pair_t *new_varidx_delta; + if (!c->plan->colrv1_variation_idx_delta_map.has (varIdxBase, &new_varidx_delta)) + return_trace (false); + + new_varidx = hb_first (*new_varidx_delta); + } + + return_trace (c->serializer->embed (new_varidx)); } bool sanitize (hb_sanitize_context_t *c) const @@ -1993,6 +2003,76 @@ struct LayerList : Array32OfOffset32To } }; +struct delta_set_index_map_subset_plan_t +{ + unsigned get_inner_bit_count () const { return inner_bit_count; } + unsigned get_width () const { return ((outer_bit_count + inner_bit_count + 7) / 8); } + hb_array_t get_output_map () const { return output_map.as_array (); } + + delta_set_index_map_subset_plan_t (const hb_map_t &new_deltaset_idx_varidx_map) + { + map_count = 0; + outer_bit_count = 0; + inner_bit_count = 1; + output_map.init (); + + /* search backwards */ + unsigned count = new_deltaset_idx_varidx_map.get_population (); + if (!count) return; + + unsigned last_idx = (unsigned)-1; + unsigned last_varidx = (unsigned)-1; + + for (unsigned i = count; i; i--) + { + unsigned delta_set_idx = i - 1; + unsigned var_idx = new_deltaset_idx_varidx_map.get (delta_set_idx); + if (i == count) + { + last_idx = delta_set_idx; + last_varidx = var_idx; + continue; + } + if (var_idx != last_varidx) + break; + last_idx = delta_set_idx; + } + + map_count = last_idx + 1; + } + + bool remap (const hb_map_t &new_deltaset_idx_varidx_map) + { + /* recalculate bit_count */ + outer_bit_count = 1; + inner_bit_count = 1; + + if (unlikely (!output_map.resize (map_count, false))) return false; + + for (unsigned idx = 0; idx < map_count; idx++) + { + unsigned *var_idx; + if (!new_deltaset_idx_varidx_map.has (idx, &var_idx)) return false; + output_map.arrayZ[idx] = *var_idx; + + unsigned outer = (*var_idx) >> 16; + unsigned bit_count = (outer == 0) ? 1 : hb_bit_storage (outer); + outer_bit_count = hb_max (bit_count, outer_bit_count); + + unsigned inner = (*var_idx) & 0xFFFF; + bit_count = (inner == 0) ? 1 : hb_bit_storage (inner); + inner_bit_count = hb_max (bit_count, inner_bit_count); + } + return true; + } + + private: + unsigned map_count; + unsigned outer_bit_count; + unsigned inner_bit_count; + hb_vector_t output_map; +}; + struct COLR { static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR; @@ -2055,6 +2135,12 @@ struct COLR const ItemVariationStore &get_var_store () const { return colr->get_var_store (); } + bool has_delta_set_index_map () const + { return colr->has_delta_set_index_map (); } + + const DeltaSetIndexMap &get_delta_set_index_map () const + { return colr->get_delta_set_index_map (); } + private: hb_blob_ptr_t colr; }; @@ -2141,6 +2227,9 @@ struct COLR bool has_delta_set_index_map () const { return version >= 1 && varIdxMap != 0; } + const DeltaSetIndexMap &get_delta_set_index_map () const + { return (version == 0 || varIdxMap == 0) ? Null (DeltaSetIndexMap) : this+varIdxMap; } + const ItemVariationStore &get_var_store () const { return (version == 0 || varStore == 0) ? Null (ItemVariationStore) : this+varStore; } @@ -2219,6 +2308,88 @@ struct COLR return record; } + bool downgrade_to_V0 (const hb_set_t &glyphset) const + { + //no more COLRv1 glyphs, downgrade to version 0 + for (const BaseGlyphPaintRecord& _ : get_baseglyphList ()) + if (glyphset.has (_.glyphId)) + return false; + + return true; + } + + bool subset_varstore (hb_subset_context_t *c, + COLR* out /* OUT */) const + { + TRACE_SUBSET (this); + if (!varStore || c->plan->all_axes_pinned || + !c->plan->colrv1_variation_idx_delta_map) + return_trace (true); + + const ItemVariationStore& var_store = this+varStore; + if (c->plan->normalized_coords) + { + item_variations_t item_vars; + /* turn off varstore optimization when varIdxMap is null, so we maintain + * original var_idx sequence */ + bool optimize = (varIdxMap != 0) ? true : false; + if (!item_vars.instantiate (var_store, c->plan, + optimize, /* optimization */ + optimize, /* use_no_variation_idx = false */ + c->plan->colrv1_varstore_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, update colrv1_new_deltaset_idx_varidx_map in + * subset plan */ + if (optimize) + { + const hb_map_t &varidx_map = item_vars.get_varidx_map (); + for (auto _ : c->plan->colrv1_new_deltaset_idx_varidx_map.iter_ref ()) + { + uint32_t varidx = _.second; + uint32_t *new_varidx; + if (varidx_map.has (varidx, &new_varidx)) + _.second = *new_varidx; + else + _.second = VarIdx::NO_VARIATION; + } + } + } + else + { + if (unlikely (!out->varStore.serialize_serialize (c->serializer, + &var_store, + c->plan->colrv1_varstore_inner_maps.as_array ()))) + return_trace (false); + } + + return_trace (true); + } + + bool subset_delta_set_index_map (hb_subset_context_t *c, + COLR* out /* OUT */) const + { + TRACE_SUBSET (this); + if (!varIdxMap || c->plan->all_axes_pinned || + !c->plan->colrv1_new_deltaset_idx_varidx_map) + return_trace (true); + + const hb_map_t &deltaset_idx_varidx_map = c->plan->colrv1_new_deltaset_idx_varidx_map; + delta_set_index_map_subset_plan_t index_map_plan (deltaset_idx_varidx_map); + + if (unlikely (!index_map_plan.remap (deltaset_idx_varidx_map))) + return_trace (false); + + return_trace (out->varIdxMap.serialize_serialize (c->serializer, index_map_plan)); + } + bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); @@ -2287,34 +2458,28 @@ struct COLR auto *colr_prime = c->serializer->start_embed (); if (unlikely (!c->serializer->extend_min (colr_prime))) return_trace (false); - if (version == 0) - return_trace (colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)); + if (version == 0 || downgrade_to_V0 (glyphset)) + return_trace (colr_prime->serialize_V0 (c->serializer, 0, base_it, layer_it)); - auto snap = c->serializer->snapshot (); + //start version 1 if (!c->serializer->allocate_size (5 * HBUINT32::static_size)) return_trace (false); + if (!colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)) return_trace (false); + + /* subset ItemVariationStore first, cause varidx_map needs to be updated + * after instancing */ + if (!subset_varstore (c, colr_prime)) return_trace (false); ItemVarStoreInstancer instancer (varStore ? &(this+varStore) : nullptr, varIdxMap ? &(this+varIdxMap) : nullptr, c->plan->normalized_coords.as_array ()); if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this, instancer)) - { - if (c->serializer->in_error ()) return_trace (false); - //no more COLRv1 glyphs: downgrade to version 0 - c->serializer->revert (snap); - return_trace (colr_prime->serialize_V0 (c->serializer, 0, base_it, layer_it)); - } - - if (!colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)) return_trace (false); + return_trace (false); colr_prime->layerList.serialize_subset (c, layerList, this, instancer); colr_prime->clipList.serialize_subset (c, clipList, this, instancer); - if (!varStore || c->plan->all_axes_pinned) - return_trace (true); - colr_prime->varIdxMap.serialize_copy (c->serializer, varIdxMap, this); - colr_prime->varStore.serialize_copy (c->serializer, varStore, this); - return_trace (true); + return_trace (subset_delta_set_index_map (c, colr_prime)); } const Paint *get_base_glyph_paint (hb_codepoint_t glyph) const diff --git a/src/hb-subset-plan-member-list.hh b/src/hb-subset-plan-member-list.hh index b02c33e92..ade8278c4 100644 --- a/src/hb-subset-plan-member-list.hh +++ b/src/hb-subset-plan-member-list.hh @@ -104,6 +104,10 @@ HB_SUBSET_PLAN_MEMBER (hb_map_t, colrv1_layers) HB_SUBSET_PLAN_MEMBER (hb_map_t, colr_palettes) //colrv1 varstore retained varidx mapping HB_SUBSET_PLAN_MEMBER (hb_vector_t, colrv1_varstore_inner_maps) +//colrv1 retained varidx -> (new varidx, delta) mapping +HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E()>), colrv1_variation_idx_delta_map) +//colrv1 retained new delta set index -> new varidx mapping +HB_SUBSET_PLAN_MEMBER (hb_map_t, colrv1_new_deltaset_idx_varidx_map) //Old layout item variation index -> (New varidx, delta) mapping HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E()>), layout_variation_idx_delta_map) diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc index b13e3a917..feea1285a 100644 --- a/src/hb-subset-plan.cc +++ b/src/hb-subset-plan.cc @@ -513,6 +513,30 @@ _cmap_closure (hb_face_t *face, cmap.table->closure_glyphs (unicodes, glyphset); } +static void +_remap_colrv1_delta_set_index_indices (const OT::DeltaSetIndexMap &index_map, + const hb_set_t &delta_set_idxes, + hb_hashmap_t> &variation_idx_delta_map, /* IN/OUT */ + hb_map_t &new_deltaset_idx_varidx_map /* OUT */) +{ + if (!index_map.get_map_count ()) + return; + + hb_hashmap_t> delta_set_idx_delta_map; + unsigned new_delta_set_idx = 0; + for (unsigned delta_set_idx : delta_set_idxes) + { + unsigned var_idx = index_map.map (delta_set_idx); + hb_pair_t *new_varidx_delta; + if (!variation_idx_delta_map.has (var_idx, &new_varidx_delta)) continue; + + new_deltaset_idx_varidx_map.set (new_delta_set_idx, hb_first (*new_varidx_delta)); + delta_set_idx_delta_map.set (delta_set_idx, hb_pair_t (new_delta_set_idx, hb_second (*new_varidx_delta))); + new_delta_set_idx++; + } + variation_idx_delta_map = std::move (delta_set_idx_delta_map); +} + static void _colr_closure (hb_subset_plan_t* plan, hb_set_t *glyphs_colred) { @@ -535,10 +559,36 @@ static void _colr_closure (hb_subset_plan_t* plan, _remap_indexes (&layer_indices, &plan->colrv1_layers); _remap_palette_indexes (&palette_indices, &plan->colr_palettes); - if (!colr.has_var_store ()) return; - - unsigned subtable_count = colr.get_var_store ().get_sub_table_count (); + if (!colr.has_var_store () || !variation_indices) return; + + const OT::ItemVariationStore &var_store = colr.get_var_store (); + // generated inner_maps is used by ItemVariationStore serialize(), which is subset only + unsigned subtable_count = var_store.get_sub_table_count (); _generate_varstore_inner_maps (variation_indices, subtable_count, plan->colrv1_varstore_inner_maps); + + /* colr variation indices mapping during planning phase: + * generate colrv1_variation_idx_delta_map. When delta set index map is not + * included, it's a mapping from varIdx-> (new varIdx,delta). Otherwise, it's + * a mapping from old delta set idx-> (new delta set idx, delta). Mapping + * delta set indices is the same as gid mapping. + * Besides, we need to generate a delta set idx-> new var_idx map for updating + * delta set index map if exists. This map will be updated again after + * instancing. */ + if (!plan->all_axes_pinned) + { + _remap_variation_indices (var_store, + variation_indices, + plan->normalized_coords, + false, /* no need to calculate delta for COLR during planning */ + plan->all_axes_pinned, + plan->colrv1_variation_idx_delta_map); + + if (colr.has_delta_set_index_map ()) + _remap_colrv1_delta_set_index_indices (colr.get_delta_set_index_map (), + delta_set_indices, + plan->colrv1_variation_idx_delta_map, + plan->colrv1_new_deltaset_idx_varidx_map); + } } static inline void