hb-ot-var-avar-table.hh (15095B)
1 /* 2 * Copyright © 2017 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): Behdad Esfahbod 25 */ 26 27 #ifndef HB_OT_VAR_AVAR_TABLE_HH 28 #define HB_OT_VAR_AVAR_TABLE_HH 29 30 #include "hb-open-type.hh" 31 #include "hb-ot-var-common.hh" 32 33 34 /* 35 * avar -- Axis Variations 36 * https://docs.microsoft.com/en-us/typography/opentype/spec/avar 37 */ 38 39 #define HB_OT_TAG_avar HB_TAG('a','v','a','r') 40 41 42 namespace OT { 43 44 45 /* "Spec": https://github.com/be-fonts/boring-expansion-spec/issues/14 */ 46 struct avarV2Tail 47 { 48 friend struct avar; 49 50 bool sanitize (hb_sanitize_context_t *c, 51 const void *base) const 52 { 53 TRACE_SANITIZE (this); 54 return_trace (varIdxMap.sanitize (c, base) && 55 varStore.sanitize (c, base)); 56 } 57 58 protected: 59 Offset32To<DeltaSetIndexMap> varIdxMap; /* Offset from the beginning of 'avar' table. */ 60 Offset32To<ItemVariationStore> varStore; /* Offset from the beginning of 'avar' table. */ 61 62 public: 63 DEFINE_SIZE_STATIC (8); 64 }; 65 66 67 struct AxisValueMap 68 { 69 bool sanitize (hb_sanitize_context_t *c) const 70 { 71 TRACE_SANITIZE (this); 72 return_trace (c->check_struct (this)); 73 } 74 75 void set_mapping (float from_coord, float to_coord) 76 { 77 coords[0].set_float (from_coord); 78 coords[1].set_float (to_coord); 79 } 80 81 bool is_outside_axis_range (const Triple& axis_range) const 82 { 83 double from_coord = (double) coords[0].to_float (); 84 return !axis_range.contains (from_coord); 85 } 86 87 bool must_include () const 88 { 89 float from_coord = coords[0].to_float (); 90 float to_coord = coords[1].to_float (); 91 return (from_coord == -1.f && to_coord == -1.f) || 92 (from_coord == 0.f && to_coord == 0.f) || 93 (from_coord == 1.f && to_coord == 1.f); 94 } 95 96 void instantiate (const Triple& axis_range, 97 const Triple& unmapped_range, 98 const TripleDistances& triple_distances) 99 { 100 float from_coord = coords[0].to_float (); 101 float to_coord = coords[1].to_float (); 102 103 from_coord = renormalizeValue ((double) from_coord, unmapped_range, triple_distances); 104 to_coord = renormalizeValue ((double) to_coord, axis_range, triple_distances); 105 106 coords[0].set_float (from_coord); 107 coords[1].set_float (to_coord); 108 } 109 110 HB_INTERNAL static int cmp (const void *pa, const void *pb) 111 { 112 const AxisValueMap *a = (const AxisValueMap *) pa; 113 const AxisValueMap *b = (const AxisValueMap *) pb; 114 115 int a_from = a->coords[0].to_int (); 116 int b_from = b->coords[0].to_int (); 117 if (a_from != b_from) 118 return a_from - b_from; 119 120 /* this should never be reached. according to the spec, all of the axis 121 * value map records for a given axis must have different fromCoord values 122 * */ 123 int a_to = a->coords[1].to_int (); 124 int b_to = b->coords[1].to_int (); 125 return a_to - b_to; 126 } 127 128 bool serialize (hb_serialize_context_t *c) const 129 { 130 TRACE_SERIALIZE (this); 131 return_trace (c->embed (this)); 132 } 133 134 public: 135 F2DOT14 coords[2]; 136 // F2DOT14 fromCoord; /* A normalized coordinate value obtained using 137 // * default normalization. */ 138 // F2DOT14 toCoord; /* The modified, normalized coordinate value. */ 139 140 public: 141 DEFINE_SIZE_STATIC (4); 142 }; 143 144 struct SegmentMaps : Array16Of<AxisValueMap> 145 { 146 float map_float (float value, unsigned int from_offset = 0, unsigned int to_offset = 1) const 147 { 148 #define fromCoord coords[from_offset].to_float () 149 #define toCoord coords[to_offset].to_float () 150 151 const auto *map = arrayZ; 152 153 /* The following special-cases are not part of OpenType, which requires 154 * that at least -1, 0, and +1 must be mapped. But we include these as 155 * part of a better error recovery scheme. */ 156 if (len < 2) 157 { 158 if (!len) 159 return value; 160 else /* len == 1*/ 161 return value - map[0].fromCoord + map[0].toCoord; 162 } 163 164 // At least two mappings now. 165 166 /* CoreText is wild... 167 * PingFangUI avar needs all this special-casing... 168 * So we implement an extended version of the spec here, 169 * which is more robust and more likely to be compatible with 170 * the wild. */ 171 172 unsigned start = 0; 173 unsigned end = len; 174 if (map[start].fromCoord == -1 && map[start].toCoord == -1 && map[start+1].fromCoord == -1) 175 start++; 176 if (map[end-1].fromCoord == +1 && map[end-1].toCoord == +1 && map[end-2].fromCoord == +1) 177 end--; 178 179 /* Look for exact match first, and do lots of special-casing. */ 180 unsigned i; 181 for (i = start; i < end; i++) 182 if (value == map[i].fromCoord) 183 break; 184 if (i < end) 185 { 186 // There's at least one exact match. See if there are more. 187 unsigned j = i; 188 for (; j + 1 < end; j++) 189 if (value != map[j + 1].fromCoord) 190 break; 191 192 // [i,j] inclusive are all exact matches: 193 194 // If there's only one, return it. This is the only spec-compliant case. 195 if (i == j) 196 return map[i].toCoord; 197 // If there's exactly three, return the middle one. 198 if (i + 2 == j) 199 return map[i + 1].toCoord; 200 201 // Ignore the middle ones. Return the one mapping closer to 0. 202 if (value < 0) return map[j].toCoord; 203 if (value > 0) return map[i].toCoord; 204 205 // Mapping 0? CoreText seems confused. It seems to prefer 0 here... 206 // So we'll just return the smallest one. lol 207 return fabsf (map[i].toCoord) < fabsf (map[j].toCoord) ? map[i].toCoord : map[j].toCoord; 208 209 // Mapping 0? Return one not mapping to 0. 210 if (map[i].toCoord == 0) 211 return map[j].toCoord; 212 else 213 return map[i].toCoord; 214 } 215 216 /* There's at least two and we're not an exact match. Prepare to lerp. */ 217 218 // Find the segment we're in. 219 for (i = start; i < end; i++) 220 if (value < map[i].fromCoord) 221 break; 222 223 if (i == 0) 224 { 225 // Value before all segments; Shift. 226 return value - map[0].fromCoord + map[0].toCoord; 227 } 228 if (i == end) 229 { 230 // Value after all segments; Shift. 231 return value - map[end - 1].fromCoord + map[end - 1].toCoord; 232 } 233 234 // Actually interpolate. 235 auto &before = map[i-1]; 236 auto &after = map[i]; 237 float denom = after.fromCoord - before.fromCoord; // Can't be zero by now. 238 return before.toCoord + ((after.toCoord - before.toCoord) * (value - before.fromCoord)) / denom; 239 240 #undef toCoord 241 #undef fromCoord 242 } 243 244 float unmap_float (float value) const { return map_float (value, 1, 0); } 245 246 247 // TODO Kill this. 248 Triple unmap_axis_range (const Triple& axis_range) const 249 { 250 float unmapped_min = unmap_float (axis_range.minimum); 251 float unmapped_middle = unmap_float (axis_range.middle); 252 float unmapped_max = unmap_float (axis_range.maximum); 253 254 return Triple{(double) unmapped_min, (double) unmapped_middle, (double) unmapped_max}; 255 } 256 257 bool subset (hb_subset_context_t *c, hb_tag_t axis_tag) const 258 { 259 TRACE_SUBSET (this); 260 261 /* This function cannot work on avar2 table (and currently doesn't). 262 * We should instead keep the design coords in the shape plan and use 263 * those. unmap_axis_range needs to be killed. */ 264 265 /* avar mapped normalized axis range*/ 266 Triple *axis_range; 267 if (!c->plan->axes_location.has (axis_tag, &axis_range)) 268 return c->serializer->embed (*this); 269 270 TripleDistances *axis_triple_distances; 271 if (!c->plan->axes_triple_distances.has (axis_tag, &axis_triple_distances)) 272 return_trace (false); 273 274 auto *out = c->serializer->start_embed (this); 275 if (unlikely (!c->serializer->extend_min (out))) return_trace (false); 276 277 Triple unmapped_range = unmap_axis_range (*axis_range); 278 279 /* create a vector of retained mappings and sort */ 280 hb_vector_t<AxisValueMap> value_mappings; 281 for (const auto& _ : as_array ()) 282 { 283 if (_.is_outside_axis_range (unmapped_range)) 284 continue; 285 AxisValueMap mapping; 286 mapping = _; 287 mapping.instantiate (*axis_range, unmapped_range, *axis_triple_distances); 288 /* (-1, -1), (0, 0), (1, 1) mappings will be added later, so avoid 289 * duplicates here */ 290 if (mapping.must_include ()) 291 continue; 292 value_mappings.push (mapping); 293 } 294 295 AxisValueMap m; 296 m.set_mapping (-1.f, -1.f); 297 value_mappings.push (m); 298 299 m.set_mapping (0.f, 0.f); 300 value_mappings.push (m); 301 302 m.set_mapping (1.f, 1.f); 303 value_mappings.push (m); 304 305 value_mappings.qsort (); 306 307 for (const auto& _ : value_mappings) 308 { 309 if (!_.serialize (c->serializer)) 310 return_trace (false); 311 } 312 return_trace (c->serializer->check_assign (out->len, value_mappings.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)); 313 } 314 315 public: 316 DEFINE_SIZE_ARRAY (2, *this); 317 }; 318 319 struct avar 320 { 321 static constexpr hb_tag_t tableTag = HB_OT_TAG_avar; 322 323 bool has_data () const { return version.to_int (); } 324 325 const SegmentMaps* get_segment_maps () const 326 { return &firstAxisSegmentMaps; } 327 328 unsigned get_axis_count () const 329 { return axisCount; } 330 331 bool sanitize (hb_sanitize_context_t *c) const 332 { 333 TRACE_SANITIZE (this); 334 if (!(version.sanitize (c) && 335 hb_barrier () && 336 (version.major == 1 337 #ifndef HB_NO_AVAR2 338 || version.major == 2 339 #endif 340 ) && 341 c->check_struct (this))) 342 return_trace (false); 343 344 const SegmentMaps *map = &firstAxisSegmentMaps; 345 unsigned int count = axisCount; 346 for (unsigned int i = 0; i < count; i++) 347 { 348 if (unlikely (!map->sanitize (c))) 349 return_trace (false); 350 map = &StructAfter<SegmentMaps> (*map); 351 } 352 353 #ifndef HB_NO_AVAR2 354 if (version.major < 2) 355 return_trace (true); 356 hb_barrier (); 357 358 const auto &v2 = * (const avarV2Tail *) map; 359 if (unlikely (!v2.sanitize (c, this))) 360 return_trace (false); 361 #endif 362 363 return_trace (true); 364 } 365 366 void map_coords_16_16 (int *coords, unsigned int coords_length) const 367 { 368 unsigned int count = hb_min (coords_length, axisCount); 369 370 const SegmentMaps *map = &firstAxisSegmentMaps; 371 for (unsigned int i = 0; i < count; i++) 372 { 373 coords[i] = roundf (map->map_float (coords[i] / 65536.f) * 65536.f); 374 map = &StructAfter<SegmentMaps> (*map); 375 } 376 377 #ifndef HB_NO_AVAR2 378 if (version.major < 2) 379 return; 380 hb_barrier (); 381 382 for (; count < axisCount; count++) 383 map = &StructAfter<SegmentMaps> (*map); 384 385 const auto &v2 = * (const avarV2Tail *) map; 386 387 const auto &varidx_map = this+v2.varIdxMap; 388 const auto &var_store = this+v2.varStore; 389 auto *var_store_cache = var_store.create_cache (); 390 391 hb_vector_t<int> coords_2_14; 392 coords_2_14.resize (coords_length); 393 for (unsigned i = 0; i < coords_length; i++) 394 coords_2_14[i] = roundf (coords[i] / 4.f); // 16.16 -> 2.14 395 396 hb_vector_t<int> out; 397 out.alloc (coords_length); 398 for (unsigned i = 0; i < coords_length; i++) 399 { 400 int v = coords[i]; 401 uint32_t varidx = varidx_map.map (i); 402 float delta = var_store.get_delta (varidx, coords_2_14.arrayZ, coords_2_14.length, var_store_cache); 403 v += roundf (delta * 4); // 2.14 -> 16.16 404 v = hb_clamp (v, -(1<<16), +(1<<16)); 405 out.push (v); 406 } 407 for (unsigned i = 0; i < coords_length; i++) 408 coords[i] = out[i]; 409 410 OT::ItemVariationStore::destroy_cache (var_store_cache); 411 #endif 412 } 413 414 bool has_v2_data () const { return version.major > 1; } 415 416 // axis normalization is done in 2.14 here 417 // TODO: deprecate this API once fonttools is updated to use 16.16 normalization 418 bool map_coords_2_14 (float *coords, unsigned int coords_length) const 419 { 420 hb_vector_t<int> coords_2_14; 421 if (!coords_2_14.resize (coords_length)) return false; 422 unsigned int count = hb_min (coords_length, axisCount); 423 424 const SegmentMaps *map = &firstAxisSegmentMaps; 425 for (unsigned int i = 0; i < count; i++) 426 { 427 int v = roundf (map->map_float (coords[i]) * 16384.f); 428 coords_2_14[i] = v; 429 coords[i] = v / 16384.f; 430 map = &StructAfter<SegmentMaps> (*map); 431 } 432 433 #ifndef HB_NO_AVAR2 434 if (version.major < 2) 435 return true; 436 hb_barrier (); 437 438 for (; count < axisCount; count++) 439 map = &StructAfter<SegmentMaps> (*map); 440 441 const auto &v2 = * (const avarV2Tail *) map; 442 443 const auto &varidx_map = this+v2.varIdxMap; 444 const auto &var_store = this+v2.varStore; 445 auto *var_store_cache = var_store.create_cache (); 446 447 for (unsigned i = 0; i < coords_length; i++) 448 { 449 int v = coords_2_14[i]; 450 uint32_t varidx = varidx_map.map (i); 451 float delta = var_store.get_delta (varidx, coords_2_14.arrayZ, coords_2_14.length, var_store_cache); 452 v += roundf (delta); 453 v = hb_clamp (v, -(1<<16), +(1<<16)); 454 coords[i] = v / 16384.f; 455 } 456 457 OT::ItemVariationStore::destroy_cache (var_store_cache); 458 return true; 459 #endif 460 } 461 462 bool subset (hb_subset_context_t *c) const 463 { 464 TRACE_SUBSET (this); 465 unsigned retained_axis_count = c->plan->axes_index_map.get_population (); 466 if (!retained_axis_count) //all axes are pinned/dropped 467 return_trace (false); 468 469 avar *out = c->serializer->allocate_min<avar> (); 470 if (unlikely (!out)) return_trace (false); 471 472 out->version.major = 1; 473 out->version.minor = 0; 474 if (!c->serializer->check_assign (out->axisCount, retained_axis_count, HB_SERIALIZE_ERROR_INT_OVERFLOW)) 475 return_trace (false); 476 477 const hb_map_t& axes_index_map = c->plan->axes_index_map; 478 const SegmentMaps *map = &firstAxisSegmentMaps; 479 unsigned count = axisCount; 480 for (unsigned int i = 0; i < count; i++) 481 { 482 if (axes_index_map.has (i)) 483 { 484 hb_tag_t *axis_tag; 485 if (!c->plan->axes_old_index_tag_map.has (i, &axis_tag)) 486 return_trace (false); 487 if (!map->subset (c, *axis_tag)) 488 return_trace (false); 489 } 490 map = &StructAfter<SegmentMaps> (*map); 491 } 492 return_trace (true); 493 } 494 495 protected: 496 FixedVersion<>version; /* Version of the avar table 497 * initially set to 0x00010000u */ 498 HBUINT16 reserved; /* This field is permanently reserved. Set to 0. */ 499 HBUINT16 axisCount; /* The number of variation axes in the font. This 500 * must be the same number as axisCount in the 501 * 'fvar' table. */ 502 SegmentMaps firstAxisSegmentMaps; 503 504 public: 505 DEFINE_SIZE_MIN (8); 506 }; 507 508 } /* namespace OT */ 509 510 511 #endif /* HB_OT_VAR_AVAR_TABLE_HH */