[instancer] partial instantiating support for COLRv1

Also add subset support for COLRv1 VarStore/DeltaSetIndexMap
pull/4691/head
Qunxin Liu 10 months ago
parent 625a9a963a
commit 247039de5a
  1. 201
      src/OT/Color/COLR/COLR.hh
  2. 4
      src/hb-subset-plan-member-list.hh
  3. 56
      src/hb-subset-plan.cc

@ -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<unsigned, int> *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<Paint>
}
};
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<const uint32_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<uint32_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> 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<COLR> ();
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<void> (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

@ -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<hb_inc_bimap_t>, colrv1_varstore_inner_maps)
//colrv1 retained varidx -> (new varidx, delta) mapping
HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E(<unsigned, hb_pair_t E(<unsigned, int>)>), 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(<unsigned, hb_pair_t E(<unsigned, int>)>), layout_variation_idx_delta_map)

@ -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<unsigned, hb_pair_t<unsigned, int>> &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<unsigned, hb_pair_t<unsigned, int>> 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<unsigned, int> *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<unsigned, int> (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

Loading…
Cancel
Save