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