tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

hb-subset-plan-var.cc (16357B)


      1 /*
      2 * Copyright © 2023  Google, Inc.
      3 *
      4 *  This is part of HarfBuzz, a text shaping library.
      5 *
      6 * Permission is hereby granted, without written agreement and without
      7 * license or royalty fees, to use, copy, modify, and distribute this
      8 * software and its documentation for any purpose, provided that the
      9 * above copyright notice and the following two paragraphs appear in
     10 * all copies of this software.
     11 *
     12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
     13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
     14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
     15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
     16 * DAMAGE.
     17 *
     18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
     19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
     20 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
     21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
     22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
     23 *
     24 * Google Author(s): Garret Rieger, Qunxin Liu, Roderick Sheeter
     25 */
     26 
     27 #include "hb-ot-layout-common.hh"
     28 #include "hb-subset-plan.hh"
     29 
     30 #include "hb-ot-var-common.hh"
     31 #include "hb-ot-layout-base-table.hh"
     32 #include "hb-ot-glyf-table.hh"
     33 #include "hb-ot-var-fvar-table.hh"
     34 #include "hb-ot-var-avar-table.hh"
     35 #include "hb-ot-cff2-table.hh"
     36 
     37 #ifndef HB_NO_VAR
     38 
     39 void
     40 generate_varstore_inner_maps (const hb_set_t& varidx_set,
     41                               unsigned subtable_count,
     42                               hb_vector_t<hb_inc_bimap_t> &inner_maps /* OUT */)
     43 {
     44   if (varidx_set.is_empty () || subtable_count == 0) return;
     45 
     46   if (unlikely (!inner_maps.resize (subtable_count))) return;
     47   for (unsigned idx : varidx_set)
     48   {
     49     uint16_t major = idx >> 16;
     50     uint16_t minor = idx & 0xFFFF;
     51 
     52     if (major >= subtable_count)
     53       continue;
     54     inner_maps[major].add (minor);
     55   }
     56 }
     57 
     58 static inline hb_font_t*
     59 _get_hb_font_with_variations (const hb_subset_plan_t *plan)
     60 {
     61   hb_font_t *font = hb_font_create (plan->source);
     62 
     63   hb_vector_t<hb_variation_t> vars;
     64   if (!vars.alloc (plan->user_axes_location.get_population ())) {
     65     hb_font_destroy (font);
     66     return nullptr;
     67   }
     68 
     69   for (auto _ : plan->user_axes_location)
     70   {
     71     hb_variation_t var;
     72     var.tag = _.first;
     73     var.value = _.second.middle;
     74     vars.push (var);
     75   }
     76 
     77   hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location.get_population ());
     78   return font;
     79 }
     80 
     81 template<typename ItemVarStore>
     82 void
     83 remap_variation_indices (const ItemVarStore &var_store,
     84                          const hb_set_t &variation_indices,
     85                          const hb_vector_t<int>& normalized_coords,
     86                          bool calculate_delta, /* not pinned at default */
     87                          bool no_variations, /* all axes pinned */
     88                          hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> &variation_idx_delta_map /* OUT */)
     89 {
     90   if (&var_store == &Null (OT::ItemVariationStore)) return;
     91   unsigned subtable_count = var_store.get_sub_table_count ();
     92   auto *store_cache = var_store.create_cache ();
     93 
     94   unsigned new_major = 0, new_minor = 0;
     95   unsigned last_major = (variation_indices.get_min ()) >> 16;
     96   for (unsigned idx : variation_indices)
     97   {
     98     int delta = 0;
     99     if (calculate_delta)
    100       delta = roundf (var_store.get_delta (idx, normalized_coords.arrayZ,
    101                                            normalized_coords.length, store_cache));
    102 
    103     if (no_variations)
    104     {
    105       variation_idx_delta_map.set (idx, hb_pair_t<unsigned, int> (HB_OT_LAYOUT_NO_VARIATIONS_INDEX, delta));
    106       continue;
    107     }
    108 
    109     uint16_t major = idx >> 16;
    110     if (major >= subtable_count) break;
    111     if (major != last_major)
    112     {
    113       new_minor = 0;
    114       ++new_major;
    115     }
    116 
    117     unsigned new_idx = (new_major << 16) + new_minor;
    118     variation_idx_delta_map.set (idx, hb_pair_t<unsigned, int> (new_idx, delta));
    119     ++new_minor;
    120     last_major = major;
    121   }
    122   var_store.destroy_cache (store_cache);
    123 }
    124 
    125 template
    126 void
    127 remap_variation_indices<OT::ItemVariationStore> (const OT::ItemVariationStore &var_store,
    128                          const hb_set_t &variation_indices,
    129                          const hb_vector_t<int>& normalized_coords,
    130                          bool calculate_delta, /* not pinned at default */
    131                          bool no_variations, /* all axes pinned */
    132                          hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> &variation_idx_delta_map /* OUT */);
    133 
    134 #ifndef HB_NO_BASE
    135 void
    136 collect_base_variation_indices (hb_subset_plan_t* plan)
    137 {
    138   hb_blob_ptr_t<OT::BASE> base = plan->source_table<OT::BASE> ();
    139   if (!base->has_var_store ())
    140   {
    141     base.destroy ();
    142     return;
    143   }
    144 
    145   hb_set_t varidx_set;
    146   base->collect_variation_indices (plan, varidx_set);
    147   const OT::ItemVariationStore &var_store = base->get_var_store ();
    148   unsigned subtable_count = var_store.get_sub_table_count ();
    149 
    150 
    151   remap_variation_indices (var_store, varidx_set,
    152                             plan->normalized_coords,
    153                             !plan->pinned_at_default,
    154                             plan->all_axes_pinned,
    155                             plan->base_variation_idx_map);
    156   generate_varstore_inner_maps (varidx_set, subtable_count, plan->base_varstore_inner_maps);
    157 
    158   base.destroy ();
    159 }
    160 
    161 #endif
    162 
    163 bool
    164 normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan)
    165 {
    166  if (plan->user_axes_location.is_empty ())
    167    return true;
    168 
    169  hb_array_t<const OT::AxisRecord> axes = face->table.fvar->get_axes ();
    170  if (!plan->check_success (plan->normalized_coords.resize (axes.length)))
    171    return false;
    172 
    173  bool has_avar = face->table.avar->has_data ();
    174  hb_vector_t<float> normalized_mins;
    175  hb_vector_t<float> normalized_defaults;
    176  hb_vector_t<float> normalized_maxs;
    177  if (has_avar)
    178  {
    179    if (!plan->check_success (normalized_mins.resize (axes.length)) ||
    180        !plan->check_success (normalized_defaults.resize (axes.length)) ||
    181        !plan->check_success (normalized_maxs.resize (axes.length)))
    182      return false;
    183  }
    184 
    185  bool axis_not_pinned = false;
    186  unsigned new_axis_idx = 0;
    187  unsigned last_idx = 0;
    188  for (const auto& _ : + hb_enumerate (axes))
    189  {
    190    unsigned i = _.first;
    191    const OT::AxisRecord &axis = _.second;
    192    hb_tag_t axis_tag = axis.get_axis_tag ();
    193    plan->axes_old_index_tag_map.set (i, axis_tag);
    194 
    195    if (!plan->user_axes_location.has (axis_tag) ||
    196        !plan->user_axes_location.get (axis_tag).is_point ())
    197    {
    198      axis_not_pinned = true;
    199      plan->axes_index_map.set (i, new_axis_idx);
    200      plan->axis_tags.push (axis_tag);
    201      new_axis_idx++;
    202    }
    203 
    204    Triple *axis_range;
    205    if (plan->user_axes_location.has (axis_tag, &axis_range))
    206    {
    207      plan->axes_triple_distances.set (axis_tag, axis.get_triple_distances ());
    208 
    209      float normalized_min = axis.normalize_axis_value (axis_range->minimum);
    210      float normalized_default = axis.normalize_axis_value (axis_range->middle);
    211      float normalized_max = axis.normalize_axis_value (axis_range->maximum);
    212 
    213      // TODO(behdad): Spec says axis normalization should be done in 16.16;
    214      // We used to do it in 2.14, but that's not correct.  I fixed this in
    215      // the fvar/avar code, but keeping 2.14 here for now to keep tests
    216      // happy. We might need to adjust fonttools as well.
    217      // I'm only fairly confident in the above statement. Anyway,
    218      // we should look deeper into this, and also update fonttools if
    219      // needed.
    220 
    221      // Round to 2.14
    222      normalized_min = roundf (normalized_min * 16384.f) / 16384.f;
    223      normalized_default = roundf (normalized_default * 16384.f) / 16384.f;
    224      normalized_max = roundf (normalized_max * 16384.f) / 16384.f;
    225 
    226      if (has_avar)
    227      {
    228        normalized_mins[i] = normalized_min;
    229        normalized_defaults[i] = normalized_default;
    230        normalized_maxs[i] = normalized_max;
    231        last_idx = i;
    232      }
    233      else
    234      {
    235        plan->axes_location.set (axis_tag, Triple ((double) normalized_min,
    236                                                   (double) normalized_default,
    237                                                   (double) normalized_max));
    238        if (normalized_default == -0.f)
    239          normalized_default = 0.f; // Normalize -0 to 0
    240        if (normalized_default != 0.f)
    241          plan->pinned_at_default = false;
    242 
    243        plan->normalized_coords[i] = roundf (normalized_default * 16384.f);
    244      }
    245    }
    246  }
    247  plan->all_axes_pinned = !axis_not_pinned;
    248 
    249  // TODO: use avar map_coords_16_16() when normalization is changed to 16.16
    250  // in fonttools
    251  if (has_avar)
    252  {
    253    const OT::avar* avar_table = face->table.avar;
    254    if (avar_table->has_v2_data () && !plan->all_axes_pinned)
    255    {
    256      DEBUG_MSG (SUBSET, nullptr, "Partial-instancing avar2 table is not supported.");
    257      return false;
    258    }
    259 
    260    unsigned coords_len = last_idx + 1;
    261    if (!plan->check_success (avar_table->map_coords_2_14 (normalized_mins.arrayZ, coords_len)) ||
    262        !plan->check_success (avar_table->map_coords_2_14 (normalized_defaults.arrayZ, coords_len)) ||
    263        !plan->check_success (avar_table->map_coords_2_14 (normalized_maxs.arrayZ, coords_len)))
    264      return false;
    265 
    266    for (const auto& _ : + hb_enumerate (axes))
    267    {
    268      unsigned i = _.first;
    269      hb_tag_t axis_tag = _.second.get_axis_tag ();
    270      if (plan->user_axes_location.has (axis_tag))
    271      {
    272        plan->axes_location.set (axis_tag, Triple ((double) normalized_mins[i],
    273                                                   (double) normalized_defaults[i],
    274                                                   (double) normalized_maxs[i]));
    275        float normalized_default = normalized_defaults[i];
    276        if (normalized_default == -0.f)
    277          normalized_default = 0.f; // Normalize -0 to 0
    278        if (normalized_default != 0.f)
    279          plan->pinned_at_default = false;
    280 
    281        plan->normalized_coords[i] = roundf (normalized_default * 16384.f);
    282      }
    283    }
    284  }
    285  return true;
    286 }
    287 
    288 void
    289 update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan)
    290 {
    291  if (!plan->normalized_coords) return;
    292  OT::cff2::accelerator_t cff2 (plan->source);
    293  if (!cff2.is_valid ()) return;
    294 
    295  hb_font_t *font = _get_hb_font_with_variations (plan);
    296  if (unlikely (!plan->check_success (font != nullptr)))
    297  {
    298    hb_font_destroy (font);
    299    return;
    300  }
    301 
    302  hb_glyph_extents_t extents = {0x7FFF, -0x7FFF};
    303  OT::hmtx_accelerator_t _hmtx (plan->source);
    304  OT::hb_scalar_cache_t *hvar_store_cache = nullptr;
    305  if (_hmtx.has_data () && _hmtx.var_table.get_length ())
    306    hvar_store_cache = _hmtx.var_table->get_var_store ().create_cache ();
    307 
    308  OT::vmtx_accelerator_t _vmtx (plan->source);
    309  OT::hb_scalar_cache_t *vvar_store_cache = nullptr;
    310  if (_vmtx.has_data () && _vmtx.var_table.get_length ())
    311    vvar_store_cache = _vmtx.var_table->get_var_store ().create_cache ();
    312 
    313  for (auto p : *plan->glyph_map)
    314  {
    315    hb_codepoint_t old_gid = p.first;
    316    hb_codepoint_t new_gid = p.second;
    317    if (!cff2.get_extents (font, old_gid, &extents)) continue;
    318    bool has_bounds_info = true;
    319    if (extents.x_bearing == 0 && extents.width == 0 &&
    320        extents.height == 0 && extents.y_bearing == 0)
    321      has_bounds_info = false;
    322 
    323    if (has_bounds_info)
    324    {
    325      plan->head_maxp_info.xMin = hb_min (plan->head_maxp_info.xMin, extents.x_bearing);
    326      plan->head_maxp_info.xMax = hb_max (plan->head_maxp_info.xMax, extents.x_bearing + extents.width);
    327      plan->head_maxp_info.yMax = hb_max (plan->head_maxp_info.yMax, extents.y_bearing);
    328      plan->head_maxp_info.yMin = hb_min (plan->head_maxp_info.yMin, extents.y_bearing + extents.height);
    329    }
    330 
    331    if (_hmtx.has_data ())
    332    {
    333      int hori_aw = _hmtx.get_advance_without_var_unscaled (old_gid);
    334      if (_hmtx.var_table.get_length ())
    335        hori_aw += (int) roundf (_hmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords,
    336                                                                              hvar_store_cache));
    337      int lsb = extents.x_bearing;
    338      if (!has_bounds_info)
    339      {
    340        _hmtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb);
    341      }
    342      plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb));
    343      plan->bounds_width_vec[new_gid] = extents.width;
    344    }
    345 
    346    if (_vmtx.has_data ())
    347    {
    348      int vert_aw = _vmtx.get_advance_without_var_unscaled (old_gid);
    349      if (_vmtx.var_table.get_length ())
    350        vert_aw += (int) roundf (_vmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords,
    351                                                                              vvar_store_cache));
    352      hb_position_t vorg_x = 0;
    353      hb_position_t vorg_y = 0;
    354      int tsb = 0;
    355      if (has_bounds_info &&
    356           hb_font_get_glyph_v_origin (font, old_gid, &vorg_x, &vorg_y))
    357      {
    358        tsb = vorg_y - extents.y_bearing;
    359      } else {
    360        _vmtx.get_leading_bearing_without_var_unscaled (old_gid, &tsb);
    361      }
    362 
    363      plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb));
    364      plan->bounds_height_vec[new_gid] = extents.height;
    365    }
    366  }
    367  hb_font_destroy (font);
    368  if (hvar_store_cache)
    369    _hmtx.var_table->get_var_store ().destroy_cache (hvar_store_cache);
    370  if (vvar_store_cache)
    371    _vmtx.var_table->get_var_store ().destroy_cache (vvar_store_cache);
    372 }
    373 
    374 bool
    375 get_instance_glyphs_contour_points (hb_subset_plan_t *plan)
    376 {
    377  /* contour_points vector only needed for updating gvar table (infer delta and
    378   * iup delta optimization) during partial instancing */
    379  if (plan->user_axes_location.is_empty () || plan->all_axes_pinned)
    380    return true;
    381 
    382  OT::glyf_accelerator_t glyf (plan->source);
    383 
    384  for (auto &_ : plan->new_to_old_gid_list)
    385  {
    386    hb_codepoint_t new_gid = _.first;
    387    contour_point_vector_t all_points;
    388    if (new_gid == 0 && !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
    389    {
    390      if (unlikely (!plan->new_gid_contour_points_map.set (new_gid, all_points)))
    391        return false;
    392      continue;
    393    }
    394 
    395    hb_codepoint_t old_gid = _.second;
    396    auto glyph = glyf.glyph_for_gid (old_gid);
    397    if (unlikely (!glyph.get_all_points_without_var (plan->source, all_points)))
    398      return false;
    399    if (unlikely (!plan->new_gid_contour_points_map.set (new_gid, all_points)))
    400      return false;
    401 
    402    /* composite new gids are only needed by iup delta optimization */
    403    if ((plan->flags & HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS) && glyph.is_composite ())
    404      plan->composite_new_gids.add (new_gid);
    405  }
    406  return true;
    407 }
    408 
    409 template<typename DeltaSetIndexMap>
    410 void
    411 remap_colrv1_delta_set_index_indices (const DeltaSetIndexMap &index_map,
    412                                      const hb_set_t &delta_set_idxes,
    413                                      hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> &variation_idx_delta_map, /* IN/OUT */
    414                                      hb_map_t &new_deltaset_idx_varidx_map /* OUT */)
    415 {
    416  if (!index_map.get_map_count ())
    417    return;
    418 
    419  hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> delta_set_idx_delta_map;
    420  unsigned new_delta_set_idx = 0;
    421  for (unsigned delta_set_idx : delta_set_idxes)
    422  {
    423    unsigned var_idx = index_map.map (delta_set_idx);
    424    unsigned new_varidx = HB_OT_LAYOUT_NO_VARIATIONS_INDEX;
    425    int delta = 0;
    426 
    427    if (var_idx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX)
    428    {
    429      hb_pair_t<unsigned, int> *new_varidx_delta;
    430      if (!variation_idx_delta_map.has (var_idx, &new_varidx_delta)) continue;
    431 
    432      new_varidx = hb_first (*new_varidx_delta);
    433      delta = hb_second (*new_varidx_delta);
    434    }
    435 
    436    new_deltaset_idx_varidx_map.set (new_delta_set_idx, new_varidx);
    437    delta_set_idx_delta_map.set (delta_set_idx, hb_pair_t<unsigned, int> (new_delta_set_idx, delta));
    438    new_delta_set_idx++;
    439  }
    440  variation_idx_delta_map = std::move (delta_set_idx_delta_map);
    441 }
    442 
    443 template void
    444 remap_colrv1_delta_set_index_indices<OT::DeltaSetIndexMap> (const OT::DeltaSetIndexMap &index_map,
    445                                      const hb_set_t &delta_set_idxes,
    446                                      hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> &variation_idx_delta_map, /* IN/OUT */
    447                                      hb_map_t &new_deltaset_idx_varidx_map /* OUT */);
    448 
    449 #endif