hb-ot-var-common.hh (72816B)
1 /* 2 * Copyright © 2021 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 */ 25 26 #ifndef HB_OT_VAR_COMMON_HH 27 #define HB_OT_VAR_COMMON_HH 28 29 #include "hb-ot-layout-common.hh" 30 #include "hb-alloc-pool.hh" 31 #include "hb-priority-queue.hh" 32 #include "hb-subset-instancer-iup.hh" 33 34 35 namespace OT { 36 37 using rebase_tent_result_scratch_t = hb_pair_t<rebase_tent_result_t, rebase_tent_result_t>; 38 39 /* https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuplevariationheader */ 40 struct TupleVariationHeader 41 { 42 friend struct tuple_delta_t; 43 unsigned get_size (unsigned axis_count_times_2) const 44 { 45 // This function is super hot in mega-var-fonts with hundreds of masters. 46 unsigned ti = tupleIndex; 47 if (unlikely ((ti & (TupleIndex::EmbeddedPeakTuple | TupleIndex::IntermediateRegion)))) 48 { 49 unsigned count = ((ti & TupleIndex::EmbeddedPeakTuple) != 0) + ((ti & TupleIndex::IntermediateRegion) != 0) * 2; 50 return min_size + count * axis_count_times_2; 51 } 52 return min_size; 53 } 54 55 unsigned get_data_size () const { return varDataSize; } 56 57 const TupleVariationHeader &get_next (unsigned axis_count_times_2) const 58 { return StructAtOffset<TupleVariationHeader> (this, get_size (axis_count_times_2)); } 59 60 bool unpack_axis_tuples (unsigned axis_count, 61 const hb_array_t<const F2DOT14> shared_tuples, 62 const hb_map_t *axes_old_index_tag_map, 63 hb_hashmap_t<hb_tag_t, Triple>& axis_tuples /* OUT */) const 64 { 65 const F2DOT14 *peak_tuple = nullptr; 66 if (has_peak ()) 67 peak_tuple = get_peak_tuple (axis_count); 68 else 69 { 70 unsigned int index = get_index (); 71 if (unlikely ((index + 1) * axis_count > shared_tuples.length)) 72 return false; 73 peak_tuple = shared_tuples.sub_array (axis_count * index, axis_count).arrayZ; 74 } 75 76 const F2DOT14 *start_tuple = nullptr; 77 const F2DOT14 *end_tuple = nullptr; 78 bool has_interm = has_intermediate (); 79 80 if (has_interm) 81 { 82 start_tuple = get_start_tuple (axis_count); 83 end_tuple = get_end_tuple (axis_count); 84 } 85 86 for (unsigned i = 0; i < axis_count; i++) 87 { 88 float peak = peak_tuple[i].to_float (); 89 if (peak == 0.f) continue; 90 91 hb_tag_t *axis_tag; 92 if (!axes_old_index_tag_map->has (i, &axis_tag)) 93 return false; 94 95 float start, end; 96 if (has_interm) 97 { 98 start = start_tuple[i].to_float (); 99 end = end_tuple[i].to_float (); 100 } 101 else 102 { 103 start = hb_min (peak, 0.f); 104 end = hb_max (peak, 0.f); 105 } 106 axis_tuples.set (*axis_tag, Triple ((double) start, (double) peak, (double) end)); 107 } 108 109 return true; 110 } 111 112 HB_ALWAYS_INLINE 113 double calculate_scalar (hb_array_t<const int> coords, unsigned int coord_count, 114 const hb_array_t<const F2DOT14> shared_tuples, 115 hb_scalar_cache_t *shared_tuple_scalar_cache = nullptr) const 116 { 117 unsigned tuple_index = tupleIndex; 118 119 const F2DOT14 *peak_tuple; 120 121 bool has_interm = tuple_index & TupleIndex::IntermediateRegion; // Inlined for performance 122 123 if (unlikely (tuple_index & TupleIndex::EmbeddedPeakTuple)) // Inlined for performance 124 { 125 peak_tuple = get_peak_tuple (coord_count); 126 shared_tuple_scalar_cache = nullptr; 127 } 128 else 129 { 130 unsigned int index = tuple_index & TupleIndex::TupleIndexMask; // Inlined for performance 131 132 float scalar; 133 if (shared_tuple_scalar_cache && 134 shared_tuple_scalar_cache->get (index, &scalar)) 135 { 136 if (has_interm && (scalar != 0 && scalar != 1.f)) 137 shared_tuple_scalar_cache = nullptr; 138 else 139 return (double) scalar; 140 } 141 142 if (unlikely ((index + 1) * coord_count > shared_tuples.length)) 143 return 0.0; 144 peak_tuple = shared_tuples.arrayZ + (coord_count * index); 145 146 } 147 148 const F2DOT14 *start_tuple = nullptr; 149 const F2DOT14 *end_tuple = nullptr; 150 151 if (has_interm) 152 { 153 start_tuple = get_start_tuple (coord_count); 154 end_tuple = get_end_tuple (coord_count); 155 } 156 157 double scalar = 1.0; 158 #ifndef HB_OPTIMIZE_SIZE 159 #if HB_FAST_NUM_ACCESS 160 bool skip = coord_count >= 16; 161 #endif 162 #endif 163 for (unsigned int i = 0; i < coord_count; i++) 164 { 165 #ifndef HB_OPTIMIZE_SIZE 166 #if HB_FAST_NUM_ACCESS 167 if (skip) 168 { 169 while (i + 4 <= coord_count && * (HBUINT64LE *) &peak_tuple[i] == 0) 170 i += 4; 171 while (i < coord_count && peak_tuple[i].to_int () == 0) 172 i += 1; 173 if (i >= coord_count) 174 break; 175 } 176 #endif 177 #endif 178 179 int peak = peak_tuple[i].to_int (); 180 if (!peak) continue; 181 182 int v = coords[i]; 183 if (!v) { scalar = 0.0; break; } 184 if (v == peak) continue; 185 186 if (has_interm) 187 { 188 shared_tuple_scalar_cache = nullptr; 189 int start = start_tuple[i].to_int (); 190 int end = end_tuple[i].to_int (); 191 if (unlikely (start > peak || peak > end || 192 (start < 0 && end > 0 && peak))) continue; 193 if (v < start || v > end) { scalar = 0.0; break; } 194 if (v < peak) 195 { if (peak != start) scalar *= (double) (v - start) / (peak - start); } 196 else 197 { if (peak != end) scalar *= (double) (end - v) / (end - peak); } 198 } 199 else if (v < hb_min (0, peak) || v > hb_max (0, peak)) { scalar = 0.0; break; } 200 else 201 scalar *= (double) v / peak; 202 } 203 if (shared_tuple_scalar_cache) 204 shared_tuple_scalar_cache->set (get_index (), scalar); 205 return scalar; 206 } 207 208 bool has_peak () const { return tupleIndex & TupleIndex::EmbeddedPeakTuple; } 209 bool has_intermediate () const { return tupleIndex & TupleIndex::IntermediateRegion; } 210 bool has_private_points () const { return tupleIndex & TupleIndex::PrivatePointNumbers; } 211 unsigned get_index () const { return tupleIndex & TupleIndex::TupleIndexMask; } 212 213 protected: 214 struct TupleIndex : HBUINT16 215 { 216 enum Flags { 217 EmbeddedPeakTuple = 0x8000u, 218 IntermediateRegion = 0x4000u, 219 PrivatePointNumbers = 0x2000u, 220 TupleIndexMask = 0x0FFFu 221 }; 222 223 TupleIndex& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } 224 DEFINE_SIZE_STATIC (2); 225 }; 226 227 hb_array_t<const F2DOT14> get_all_tuples (unsigned axis_count) const 228 { return StructAfter<UnsizedArrayOf<F2DOT14>> (tupleIndex).as_array ((has_peak () + has_intermediate () * 2) * axis_count); } 229 const F2DOT14* get_all_tuples_base (unsigned axis_count) const 230 { return StructAfter<UnsizedArrayOf<F2DOT14>> (tupleIndex).arrayZ; } 231 const F2DOT14* get_peak_tuple (unsigned axis_count) const 232 { return get_all_tuples_base (axis_count); } 233 const F2DOT14* get_start_tuple (unsigned axis_count) const 234 { return get_all_tuples_base (axis_count) + has_peak () * axis_count; } 235 const F2DOT14* get_end_tuple (unsigned axis_count) const 236 { return get_all_tuples_base (axis_count) + has_peak () * axis_count + axis_count; } 237 238 HBUINT16 varDataSize; /* The size in bytes of the serialized 239 * data for this tuple variation table. */ 240 TupleIndex tupleIndex; /* A packed field. The high 4 bits are flags (see below). 241 The low 12 bits are an index into a shared tuple 242 records array. */ 243 /* UnsizedArrayOf<F2DOT14> peakTuple - optional */ 244 /* Peak tuple record for this tuple variation table — optional, 245 * determined by flags in the tupleIndex value. 246 * 247 * Note that this must always be included in the 'cvar' table. */ 248 /* UnsizedArrayOf<F2DOT14> intermediateStartTuple - optional */ 249 /* Intermediate start tuple record for this tuple variation table — optional, 250 determined by flags in the tupleIndex value. */ 251 /* UnsizedArrayOf<F2DOT14> intermediateEndTuple - optional */ 252 /* Intermediate end tuple record for this tuple variation table — optional, 253 * determined by flags in the tupleIndex value. */ 254 public: 255 DEFINE_SIZE_MIN (4); 256 }; 257 258 struct optimize_scratch_t 259 { 260 iup_scratch_t iup; 261 hb_vector_t<bool> opt_indices; 262 hb_vector_t<int> rounded_x_deltas; 263 hb_vector_t<int> rounded_y_deltas; 264 hb_vector_t<float> opt_deltas_x; 265 hb_vector_t<float> opt_deltas_y; 266 hb_vector_t<unsigned char> opt_point_data; 267 hb_vector_t<unsigned char> opt_deltas_data; 268 hb_vector_t<unsigned char> point_data; 269 hb_vector_t<unsigned char> deltas_data; 270 hb_vector_t<int> rounded_deltas; 271 }; 272 273 struct tuple_delta_t 274 { 275 static constexpr bool realloc_move = true; // Watch out when adding new members! 276 277 public: 278 hb_hashmap_t<hb_tag_t, Triple> axis_tuples; 279 280 /* indices_length = point_count, indice[i] = 1 means point i is referenced */ 281 hb_vector_t<bool> indices; 282 283 hb_vector_t<float> deltas_x; 284 /* empty for cvar tuples */ 285 hb_vector_t<float> deltas_y; 286 287 /* compiled data: header and deltas 288 * compiled point data is saved in a hashmap within tuple_variations_t cause 289 * some point sets might be reused by different tuple variations */ 290 hb_vector_t<unsigned char> compiled_tuple_header; 291 hb_vector_t<unsigned char> compiled_deltas; 292 293 hb_vector_t<F2DOT14> compiled_peak_coords; 294 hb_vector_t<F2DOT14> compiled_interm_coords; 295 296 tuple_delta_t (hb_alloc_pool_t *pool = nullptr) {} 297 tuple_delta_t (const tuple_delta_t& o) = default; 298 tuple_delta_t& operator = (const tuple_delta_t& o) = default; 299 300 friend void swap (tuple_delta_t& a, tuple_delta_t& b) noexcept 301 { 302 hb_swap (a.axis_tuples, b.axis_tuples); 303 hb_swap (a.indices, b.indices); 304 hb_swap (a.deltas_x, b.deltas_x); 305 hb_swap (a.deltas_y, b.deltas_y); 306 hb_swap (a.compiled_tuple_header, b.compiled_tuple_header); 307 hb_swap (a.compiled_deltas, b.compiled_deltas); 308 hb_swap (a.compiled_peak_coords, b.compiled_peak_coords); 309 } 310 311 tuple_delta_t (tuple_delta_t&& o) noexcept : tuple_delta_t () 312 { hb_swap (*this, o); } 313 314 tuple_delta_t& operator = (tuple_delta_t&& o) noexcept 315 { 316 hb_swap (*this, o); 317 return *this; 318 } 319 320 void copy_from (const tuple_delta_t& o, hb_alloc_pool_t *pool = nullptr) 321 { 322 axis_tuples = o.axis_tuples; 323 indices.allocate_from_pool (pool, o.indices); 324 deltas_x.allocate_from_pool (pool, o.deltas_x); 325 deltas_y.allocate_from_pool (pool, o.deltas_y); 326 compiled_tuple_header.allocate_from_pool (pool, o.compiled_tuple_header); 327 compiled_deltas.allocate_from_pool (pool, o.compiled_deltas); 328 compiled_peak_coords.allocate_from_pool (pool, o.compiled_peak_coords); 329 compiled_interm_coords.allocate_from_pool (pool, o.compiled_interm_coords); 330 } 331 332 void remove_axis (hb_tag_t axis_tag) 333 { axis_tuples.del (axis_tag); } 334 335 bool set_tent (hb_tag_t axis_tag, Triple tent) 336 { return axis_tuples.set (axis_tag, tent); } 337 338 tuple_delta_t& operator += (const tuple_delta_t& o) 339 { 340 unsigned num = indices.length; 341 for (unsigned i = 0; i < num; i++) 342 { 343 if (indices.arrayZ[i]) 344 { 345 if (o.indices.arrayZ[i]) 346 { 347 deltas_x[i] += o.deltas_x[i]; 348 if (deltas_y && o.deltas_y) 349 deltas_y[i] += o.deltas_y[i]; 350 } 351 } 352 else 353 { 354 if (!o.indices.arrayZ[i]) continue; 355 indices.arrayZ[i] = true; 356 deltas_x[i] = o.deltas_x[i]; 357 if (deltas_y && o.deltas_y) 358 deltas_y[i] = o.deltas_y[i]; 359 } 360 } 361 return *this; 362 } 363 364 tuple_delta_t& operator *= (float scalar) 365 { 366 if (scalar == 1.0f) 367 return *this; 368 369 unsigned num = indices.length; 370 if (deltas_y) 371 for (unsigned i = 0; i < num; i++) 372 { 373 if (!indices.arrayZ[i]) continue; 374 deltas_x[i] *= scalar; 375 deltas_y[i] *= scalar; 376 } 377 else 378 for (unsigned i = 0; i < num; i++) 379 { 380 if (!indices.arrayZ[i]) continue; 381 deltas_x[i] *= scalar; 382 } 383 return *this; 384 } 385 386 void change_tuple_var_axis_limit (hb_tag_t axis_tag, Triple axis_limit, 387 TripleDistances axis_triple_distances, 388 hb_vector_t<tuple_delta_t>& out, 389 rebase_tent_result_scratch_t &scratch, 390 hb_alloc_pool_t *pool = nullptr) 391 { 392 // May move *this out. 393 394 out.reset (); 395 Triple *tent; 396 if (!axis_tuples.has (axis_tag, &tent)) 397 { 398 out.push (std::move (*this)); 399 return; 400 } 401 402 if ((tent->minimum < 0.0 && tent->maximum > 0.0) || 403 !(tent->minimum <= tent->middle && tent->middle <= tent->maximum)) 404 return; 405 406 if (tent->middle == 0.0) 407 { 408 out.push (std::move (*this)); 409 return; 410 } 411 412 rebase_tent_result_t &solutions = scratch.first; 413 rebase_tent (*tent, axis_limit, axis_triple_distances, solutions, scratch.second); 414 for (unsigned i = 0; i < solutions.length; i++) 415 { 416 auto &t = solutions.arrayZ[i]; 417 418 tuple_delta_t new_var; 419 if (i < solutions.length - 1) 420 new_var.copy_from (*this, pool); 421 else 422 new_var = std::move (*this); 423 424 if (t.second == Triple ()) 425 new_var.remove_axis (axis_tag); 426 else 427 new_var.set_tent (axis_tag, t.second); 428 429 new_var *= t.first; 430 out.push (std::move (new_var)); 431 } 432 } 433 434 bool compile_coords (const hb_map_t& axes_index_map, 435 const hb_map_t& axes_old_index_tag_map, 436 hb_alloc_pool_t *pool= nullptr) 437 { 438 unsigned cur_axis_count = axes_index_map.get_population (); 439 if (pool) 440 { 441 if (unlikely (!compiled_peak_coords.allocate_from_pool (pool, cur_axis_count))) 442 return false; 443 } 444 else if (unlikely (!compiled_peak_coords.resize (cur_axis_count))) 445 return false; 446 447 hb_array_t<F2DOT14> start_coords, end_coords; 448 449 unsigned orig_axis_count = axes_old_index_tag_map.get_population (); 450 unsigned j = 0; 451 for (unsigned i = 0; i < orig_axis_count; i++) 452 { 453 if (!axes_index_map.has (i)) 454 continue; 455 456 hb_tag_t axis_tag = axes_old_index_tag_map.get (i); 457 Triple *coords = nullptr; 458 if (axis_tuples.has (axis_tag, &coords)) 459 { 460 float min_val = coords->minimum; 461 float val = coords->middle; 462 float max_val = coords->maximum; 463 464 compiled_peak_coords.arrayZ[j].set_float (val); 465 466 if (min_val != hb_min (val, 0.f) || max_val != hb_max (val, 0.f)) 467 { 468 if (!compiled_interm_coords) 469 { 470 if (pool) 471 { 472 if (unlikely (!compiled_interm_coords.allocate_from_pool (pool, 2 * cur_axis_count))) 473 return false; 474 } 475 else if (unlikely (!compiled_interm_coords.resize (2 * cur_axis_count))) 476 return false; 477 start_coords = compiled_interm_coords.as_array ().sub_array (0, cur_axis_count); 478 end_coords = compiled_interm_coords.as_array ().sub_array (cur_axis_count); 479 480 for (unsigned k = 0; k < j; k++) 481 { 482 signed peak = compiled_peak_coords.arrayZ[k].to_int (); 483 if (!peak) continue; 484 start_coords.arrayZ[k].set_int (hb_min (peak, 0)); 485 end_coords.arrayZ[k].set_int (hb_max (peak, 0)); 486 } 487 } 488 489 } 490 491 if (compiled_interm_coords) 492 { 493 start_coords.arrayZ[j].set_float (min_val); 494 end_coords.arrayZ[j].set_float (max_val); 495 } 496 } 497 498 j++; 499 } 500 501 return !compiled_peak_coords.in_error () && !compiled_interm_coords.in_error (); 502 } 503 504 /* deltas should be compiled already before we compile tuple 505 * variation header cause we need to fill in the size of the 506 * serialized data for this tuple variation */ 507 bool compile_tuple_var_header (const hb_map_t& axes_index_map, 508 unsigned points_data_length, 509 const hb_map_t& axes_old_index_tag_map, 510 const hb_hashmap_t<const hb_vector_t<F2DOT14>*, unsigned>* shared_tuples_idx_map, 511 hb_alloc_pool_t *pool = nullptr) 512 { 513 /* compiled_deltas could be empty after iup delta optimization, we can skip 514 * compiling this tuple and return true */ 515 if (!compiled_deltas) return true; 516 517 unsigned cur_axis_count = axes_index_map.get_population (); 518 /* allocate enough memory: 1 peak + 2 intermediate coords + fixed header size */ 519 unsigned alloc_len = 3 * cur_axis_count * (F2DOT14::static_size) + 4; 520 if (unlikely (!compiled_tuple_header.allocate_from_pool (pool, alloc_len, false))) return false; 521 522 unsigned flag = 0; 523 /* skip the first 4 header bytes: variationDataSize+tupleIndex */ 524 F2DOT14* p = reinterpret_cast<F2DOT14 *> (compiled_tuple_header.begin () + 4); 525 F2DOT14* end = reinterpret_cast<F2DOT14 *> (compiled_tuple_header.end ()); 526 hb_array_t<F2DOT14> coords (p, end - p); 527 528 if (!shared_tuples_idx_map) 529 compile_coords (axes_index_map, axes_old_index_tag_map); // non-gvar tuples do not have compiled coords yet 530 531 /* encode peak coords */ 532 unsigned peak_count = 0; 533 unsigned *shared_tuple_idx; 534 if (shared_tuples_idx_map && 535 shared_tuples_idx_map->has (&compiled_peak_coords, &shared_tuple_idx)) 536 { 537 flag = *shared_tuple_idx; 538 } 539 else 540 { 541 peak_count = encode_peak_coords(coords, flag); 542 if (!peak_count) return false; 543 } 544 545 /* encode interim coords, it's optional so returned num could be 0 */ 546 unsigned interim_count = encode_interm_coords (coords.sub_array (peak_count), flag); 547 548 /* pointdata length = 0 implies "use shared points" */ 549 if (points_data_length) 550 flag |= TupleVariationHeader::TupleIndex::PrivatePointNumbers; 551 552 unsigned serialized_data_size = points_data_length + compiled_deltas.length; 553 TupleVariationHeader *o = reinterpret_cast<TupleVariationHeader *> (compiled_tuple_header.begin ()); 554 o->varDataSize = serialized_data_size; 555 o->tupleIndex = flag; 556 557 unsigned total_header_len = 4 + (peak_count + interim_count) * (F2DOT14::static_size); 558 compiled_tuple_header.shrink_back_to_pool (pool, total_header_len); 559 return true; 560 } 561 562 unsigned encode_peak_coords (hb_array_t<F2DOT14> peak_coords, 563 unsigned& flag) const 564 { 565 hb_memcpy (&peak_coords[0], &compiled_peak_coords[0], compiled_peak_coords.length * sizeof (compiled_peak_coords[0])); 566 flag |= TupleVariationHeader::TupleIndex::EmbeddedPeakTuple; 567 return compiled_peak_coords.length; 568 } 569 570 /* if no need to encode intermediate coords, then just return p */ 571 unsigned encode_interm_coords (hb_array_t<F2DOT14> coords, 572 unsigned& flag) const 573 { 574 if (compiled_interm_coords) 575 { 576 hb_memcpy (&coords[0], &compiled_interm_coords[0], compiled_interm_coords.length * sizeof (compiled_interm_coords[0])); 577 flag |= TupleVariationHeader::TupleIndex::IntermediateRegion; 578 } 579 return compiled_interm_coords.length; 580 } 581 582 bool compile_deltas (hb_vector_t<int> &rounded_deltas_scratch, 583 hb_alloc_pool_t *pool = nullptr) 584 { return compile_deltas (indices, deltas_x, deltas_y, compiled_deltas, rounded_deltas_scratch, pool); } 585 586 static bool compile_deltas (hb_array_t<const bool> point_indices, 587 hb_array_t<const float> x_deltas, 588 hb_array_t<const float> y_deltas, 589 hb_vector_t<unsigned char> &compiled_deltas, /* OUT */ 590 hb_vector_t<int> &rounded_deltas, /* scratch */ 591 hb_alloc_pool_t *pool = nullptr) 592 { 593 if (unlikely (!rounded_deltas.resize_dirty (point_indices.length))) 594 return false; 595 596 unsigned j = 0; 597 for (unsigned i = 0; i < point_indices.length; i++) 598 { 599 if (!point_indices[i]) continue; 600 rounded_deltas.arrayZ[j++] = (int) roundf (x_deltas.arrayZ[i]); 601 } 602 rounded_deltas.resize (j); 603 604 if (!rounded_deltas) return true; 605 /* Allocate enough memory: this is the correct bound: 606 * Worst case scenario is that each delta has to be encoded in 4 bytes, and there 607 * are runs of 64 items each. Any delta encoded in less than 4 bytes (2, 1, or 0) 608 * is still smaller than the 4-byte encoding even with their control byte. 609 * The initial 2 is to handle length==0, for both x and y deltas. */ 610 unsigned alloc_len = 2 + 4 * rounded_deltas.length + (rounded_deltas.length + 63) / 64; 611 if (y_deltas) 612 alloc_len *= 2; 613 614 if (unlikely (!compiled_deltas.allocate_from_pool (pool, alloc_len, false))) return false; 615 616 unsigned encoded_len = compile_deltas (compiled_deltas, rounded_deltas); 617 618 if (y_deltas) 619 { 620 /* reuse the rounded_deltas vector, check that y_deltas have the same num of deltas as x_deltas */ 621 unsigned j = 0; 622 for (unsigned idx = 0; idx < point_indices.length; idx++) 623 { 624 if (!point_indices[idx]) continue; 625 int rounded_delta = (int) roundf (y_deltas.arrayZ[idx]); 626 627 if (j >= rounded_deltas.length) return false; 628 629 rounded_deltas[j++] = rounded_delta; 630 } 631 632 if (j != rounded_deltas.length) return false; 633 encoded_len += compile_deltas (compiled_deltas.as_array ().sub_array (encoded_len), rounded_deltas); 634 } 635 compiled_deltas.shrink_back_to_pool (pool, encoded_len); 636 return true; 637 } 638 639 static unsigned compile_deltas (hb_array_t<unsigned char> encoded_bytes, 640 hb_array_t<const int> deltas) 641 { 642 return TupleValues::compile_unsafe (deltas, encoded_bytes); 643 } 644 645 bool calc_inferred_deltas (const contour_point_vector_t& orig_points, 646 hb_vector_t<unsigned> &scratch) 647 { 648 unsigned point_count = orig_points.length; 649 if (point_count != indices.length) 650 return false; 651 652 unsigned ref_count = 0; 653 654 hb_vector_t<unsigned> &end_points = scratch.reset (); 655 656 for (unsigned i = 0; i < point_count; i++) 657 { 658 ref_count += indices.arrayZ[i]; 659 if (orig_points.arrayZ[i].is_end_point) 660 end_points.push (i); 661 } 662 /* all points are referenced, nothing to do */ 663 if (ref_count == point_count) 664 return true; 665 if (unlikely (end_points.in_error ())) return false; 666 667 hb_bit_set_t inferred_idxes; 668 unsigned start_point = 0; 669 for (unsigned end_point : end_points) 670 { 671 /* Check the number of unreferenced points in a contour. If no unref points or no ref points, nothing to do. */ 672 unsigned unref_count = 0; 673 for (unsigned i = start_point; i < end_point + 1; i++) 674 unref_count += indices.arrayZ[i]; 675 unref_count = (end_point - start_point + 1) - unref_count; 676 677 unsigned j = start_point; 678 if (unref_count == 0 || unref_count > end_point - start_point) 679 goto no_more_gaps; 680 for (;;) 681 { 682 /* Locate the next gap of unreferenced points between two referenced points prev and next. 683 * Note that a gap may wrap around at left (start_point) and/or at right (end_point). 684 */ 685 unsigned int prev, next, i; 686 for (;;) 687 { 688 i = j; 689 j = next_index (i, start_point, end_point); 690 if (indices.arrayZ[i] && !indices.arrayZ[j]) break; 691 } 692 prev = j = i; 693 for (;;) 694 { 695 i = j; 696 j = next_index (i, start_point, end_point); 697 if (!indices.arrayZ[i] && indices.arrayZ[j]) break; 698 } 699 next = j; 700 /* Infer deltas for all unref points in the gap between prev and next */ 701 i = prev; 702 for (;;) 703 { 704 i = next_index (i, start_point, end_point); 705 if (i == next) break; 706 deltas_x.arrayZ[i] = infer_delta ((double) orig_points.arrayZ[i].x, 707 (double) orig_points.arrayZ[prev].x, 708 (double) orig_points.arrayZ[next].x, 709 (double) deltas_x.arrayZ[prev], (double) deltas_x.arrayZ[next]); 710 deltas_y.arrayZ[i] = infer_delta ((double) orig_points.arrayZ[i].y, 711 (double) orig_points.arrayZ[prev].y, 712 (double) orig_points.arrayZ[next].y, 713 (double) deltas_y.arrayZ[prev], (double) deltas_y.arrayZ[next]); 714 inferred_idxes.add (i); 715 if (--unref_count == 0) goto no_more_gaps; 716 } 717 } 718 no_more_gaps: 719 start_point = end_point + 1; 720 } 721 722 for (unsigned i = 0; i < point_count; i++) 723 { 724 /* if points are not referenced and deltas are not inferred, set to 0. 725 * reference all points for gvar */ 726 if ( !indices[i]) 727 { 728 if (!inferred_idxes.has (i)) 729 { 730 deltas_x.arrayZ[i] = 0.0; 731 deltas_y.arrayZ[i] = 0.0; 732 } 733 indices[i] = true; 734 } 735 } 736 return true; 737 } 738 739 bool optimize (const contour_point_vector_t& contour_points, 740 bool is_composite, 741 optimize_scratch_t &scratch, 742 double tolerance = 0.5 + 1e-10) 743 { 744 unsigned count = contour_points.length; 745 if (deltas_x.length != count || 746 deltas_y.length != count) 747 return false; 748 749 hb_vector_t<bool> &opt_indices = scratch.opt_indices.reset (); 750 hb_vector_t<int> &rounded_x_deltas = scratch.rounded_x_deltas; 751 hb_vector_t<int> &rounded_y_deltas = scratch.rounded_y_deltas; 752 753 if (unlikely (!rounded_x_deltas.resize_dirty (count) || 754 !rounded_y_deltas.resize_dirty (count))) 755 return false; 756 757 for (unsigned i = 0; i < count; i++) 758 { 759 rounded_x_deltas.arrayZ[i] = (int) roundf (deltas_x.arrayZ[i]); 760 rounded_y_deltas.arrayZ[i] = (int) roundf (deltas_y.arrayZ[i]); 761 } 762 763 if (!iup_delta_optimize (contour_points, rounded_x_deltas, rounded_y_deltas, opt_indices, scratch.iup, tolerance)) 764 return false; 765 766 unsigned ref_count = 0; 767 for (bool ref_flag : opt_indices) 768 ref_count += ref_flag; 769 770 if (ref_count == count) return true; 771 772 hb_vector_t<float> &opt_deltas_x = scratch.opt_deltas_x.reset (); 773 hb_vector_t<float> &opt_deltas_y = scratch.opt_deltas_y.reset (); 774 bool is_comp_glyph_wo_deltas = (is_composite && ref_count == 0); 775 if (is_comp_glyph_wo_deltas) 776 { 777 if (unlikely (!opt_deltas_x.resize (count) || 778 !opt_deltas_y.resize (count))) 779 return false; 780 781 opt_indices.arrayZ[0] = true; 782 for (unsigned i = 1; i < count; i++) 783 opt_indices.arrayZ[i] = false; 784 } 785 786 hb_vector_t<unsigned char> &opt_point_data = scratch.opt_point_data.reset (); 787 if (!compile_point_set (opt_indices, opt_point_data)) 788 return false; 789 hb_vector_t<unsigned char> &opt_deltas_data = scratch.opt_deltas_data.reset (); 790 if (!compile_deltas (opt_indices, 791 is_comp_glyph_wo_deltas ? opt_deltas_x : deltas_x, 792 is_comp_glyph_wo_deltas ? opt_deltas_y : deltas_y, 793 opt_deltas_data, 794 scratch.rounded_deltas)) 795 return false; 796 797 hb_vector_t<unsigned char> &point_data = scratch.point_data.reset (); 798 if (!compile_point_set (indices, point_data)) 799 return false; 800 hb_vector_t<unsigned char> &deltas_data = scratch.deltas_data.reset (); 801 if (!compile_deltas (indices, deltas_x, deltas_y, deltas_data, scratch.rounded_deltas)) 802 return false; 803 804 if (opt_point_data.length + opt_deltas_data.length < point_data.length + deltas_data.length) 805 { 806 indices = std::move (opt_indices); 807 808 if (is_comp_glyph_wo_deltas) 809 { 810 deltas_x = std::move (opt_deltas_x); 811 deltas_y = std::move (opt_deltas_y); 812 } 813 } 814 return !indices.in_error () && !deltas_x.in_error () && !deltas_y.in_error (); 815 } 816 817 static bool compile_point_set (const hb_vector_t<bool> &point_indices, 818 hb_vector_t<unsigned char>& compiled_points /* OUT */) 819 { 820 unsigned num_points = 0; 821 for (bool i : point_indices) 822 if (i) num_points++; 823 824 /* when iup optimization is enabled, num of referenced points could be 0 */ 825 if (!num_points) return true; 826 827 unsigned indices_length = point_indices.length; 828 /* If the points set consists of all points in the glyph, it's encoded with a 829 * single zero byte */ 830 if (num_points == indices_length) 831 return compiled_points.resize (1); 832 833 /* allocate enough memories: 2 bytes for count + 3 bytes for each point */ 834 unsigned num_bytes = 2 + 3 *num_points; 835 if (unlikely (!compiled_points.resize_dirty (num_bytes))) 836 return false; 837 838 unsigned pos = 0; 839 /* binary data starts with the total number of reference points */ 840 if (num_points < 0x80) 841 compiled_points.arrayZ[pos++] = num_points; 842 else 843 { 844 compiled_points.arrayZ[pos++] = ((num_points >> 8) | 0x80); 845 compiled_points.arrayZ[pos++] = num_points & 0xFF; 846 } 847 848 const unsigned max_run_length = 0x7F; 849 unsigned i = 0; 850 unsigned last_value = 0; 851 unsigned num_encoded = 0; 852 while (i < indices_length && num_encoded < num_points) 853 { 854 unsigned run_length = 0; 855 unsigned header_pos = pos; 856 compiled_points.arrayZ[pos++] = 0; 857 858 bool use_byte_encoding = false; 859 bool new_run = true; 860 while (i < indices_length && num_encoded < num_points && 861 run_length <= max_run_length) 862 { 863 // find out next referenced point index 864 while (i < indices_length && !point_indices[i]) 865 i++; 866 867 if (i >= indices_length) break; 868 869 unsigned cur_value = i; 870 unsigned delta = cur_value - last_value; 871 872 if (new_run) 873 { 874 use_byte_encoding = (delta <= 0xFF); 875 new_run = false; 876 } 877 878 if (use_byte_encoding && delta > 0xFF) 879 break; 880 881 if (use_byte_encoding) 882 compiled_points.arrayZ[pos++] = delta; 883 else 884 { 885 compiled_points.arrayZ[pos++] = delta >> 8; 886 compiled_points.arrayZ[pos++] = delta & 0xFF; 887 } 888 i++; 889 last_value = cur_value; 890 run_length++; 891 num_encoded++; 892 } 893 894 if (use_byte_encoding) 895 compiled_points.arrayZ[header_pos] = run_length - 1; 896 else 897 compiled_points.arrayZ[header_pos] = (run_length - 1) | 0x80; 898 } 899 return compiled_points.resize_dirty (pos); 900 } 901 902 static double infer_delta (double target_val, double prev_val, double next_val, double prev_delta, double next_delta) 903 { 904 if (prev_val == next_val) 905 return (prev_delta == next_delta) ? prev_delta : 0.0; 906 else if (target_val <= hb_min (prev_val, next_val)) 907 return (prev_val < next_val) ? prev_delta : next_delta; 908 else if (target_val >= hb_max (prev_val, next_val)) 909 return (prev_val > next_val) ? prev_delta : next_delta; 910 911 double r = (target_val - prev_val) / (next_val - prev_val); 912 return prev_delta + r * (next_delta - prev_delta); 913 } 914 915 static unsigned int next_index (unsigned int i, unsigned int start, unsigned int end) 916 { return (i >= end) ? start : (i + 1); } 917 }; 918 919 template <typename OffType = HBUINT16> 920 struct TupleVariationData 921 { 922 bool sanitize (hb_sanitize_context_t *c) const 923 { 924 TRACE_SANITIZE (this); 925 // here check on min_size only, TupleVariationHeader and var data will be 926 // checked while accessing through iterator. 927 return_trace (c->check_struct (this)); 928 } 929 930 unsigned get_size (unsigned axis_count_times_2) const 931 { 932 unsigned total_size = min_size; 933 unsigned count = tupleVarCount.get_count (); 934 const TupleVariationHeader *tuple_var_header = &(get_tuple_var_header()); 935 for (unsigned i = 0; i < count; i++) 936 { 937 total_size += tuple_var_header->get_size (axis_count_times_2) + tuple_var_header->get_data_size (); 938 tuple_var_header = &tuple_var_header->get_next (axis_count_times_2); 939 } 940 941 return total_size; 942 } 943 944 const TupleVariationHeader &get_tuple_var_header (void) const 945 { return StructAfter<TupleVariationHeader> (data); } 946 947 struct tuple_iterator_t; 948 struct tuple_variations_t 949 { 950 hb_vector_t<tuple_delta_t> tuple_vars; 951 952 private: 953 /* referenced point set->compiled point data map */ 954 hb_hashmap_t<const hb_vector_t<bool>*, hb_vector_t<unsigned char>> point_data_map; 955 /* referenced point set-> count map, used in finding shared points */ 956 hb_hashmap_t<const hb_vector_t<bool>*, unsigned> point_set_count_map; 957 958 /* empty for non-gvar tuples. 959 * shared_points_bytes is a pointer to some value in the point_data_map, 960 * which will be freed during map destruction. Save it for serialization, so 961 * no need to do find_shared_points () again */ 962 hb_vector_t<unsigned char> *shared_points_bytes = nullptr; 963 964 /* total compiled byte size as TupleVariationData format, initialized to 0 */ 965 unsigned compiled_byte_size = 0; 966 bool needs_padding = false; 967 968 /* for gvar iup delta optimization: whether this is a composite glyph */ 969 bool is_composite = false; 970 971 public: 972 tuple_variations_t () = default; 973 tuple_variations_t (const tuple_variations_t&) = delete; 974 tuple_variations_t& operator=(const tuple_variations_t&) = delete; 975 tuple_variations_t (tuple_variations_t&&) = default; 976 tuple_variations_t& operator=(tuple_variations_t&&) = default; 977 ~tuple_variations_t () = default; 978 979 explicit operator bool () const { return bool (tuple_vars); } 980 unsigned get_var_count () const 981 { 982 unsigned count = 0; 983 /* when iup delta opt is enabled, compiled_deltas could be empty and we 984 * should skip this tuple */ 985 for (auto& tuple: tuple_vars) 986 if (tuple.compiled_deltas) count++; 987 988 if (shared_points_bytes && shared_points_bytes->length) 989 count |= TupleVarCount::SharedPointNumbers; 990 return count; 991 } 992 993 unsigned get_compiled_byte_size () const 994 { return compiled_byte_size; } 995 996 bool create_from_tuple_var_data (tuple_iterator_t iterator, 997 unsigned tuple_var_count, 998 unsigned point_count, 999 bool is_gvar, 1000 const hb_map_t *axes_old_index_tag_map, 1001 const hb_vector_t<unsigned> &shared_indices, 1002 const hb_array_t<const F2DOT14> shared_tuples, 1003 hb_alloc_pool_t *pool = nullptr, 1004 bool is_composite_glyph = false) 1005 { 1006 hb_vector_t<unsigned> private_indices; 1007 hb_vector_t<int> deltas_x; 1008 hb_vector_t<int> deltas_y; 1009 do 1010 { 1011 const HBUINT8 *p = iterator.get_serialized_data (); 1012 unsigned int length = iterator.current_tuple->get_data_size (); 1013 if (unlikely (!iterator.var_data_bytes.check_range (p, length))) 1014 return false; 1015 1016 hb_hashmap_t<hb_tag_t, Triple> axis_tuples; 1017 if (!iterator.current_tuple->unpack_axis_tuples (iterator.get_axis_count (), shared_tuples, axes_old_index_tag_map, axis_tuples) 1018 || axis_tuples.is_empty ()) 1019 return false; 1020 1021 private_indices.reset (); 1022 bool has_private_points = iterator.current_tuple->has_private_points (); 1023 const HBUINT8 *end = p + length; 1024 if (has_private_points && 1025 !TupleVariationData::decompile_points (p, private_indices, end)) 1026 return false; 1027 1028 const hb_vector_t<unsigned> &indices = has_private_points ? private_indices : shared_indices; 1029 bool apply_to_all = (indices.length == 0); 1030 unsigned num_deltas = apply_to_all ? point_count : indices.length; 1031 1032 if (unlikely (!deltas_x.resize_dirty (num_deltas) || 1033 !TupleVariationData::decompile_deltas (p, deltas_x, end))) 1034 return false; 1035 1036 if (is_gvar) 1037 { 1038 if (unlikely (!deltas_y.resize_dirty (num_deltas) || 1039 !TupleVariationData::decompile_deltas (p, deltas_y, end))) 1040 return false; 1041 } 1042 1043 tuple_delta_t var; 1044 var.axis_tuples = std::move (axis_tuples); 1045 if (unlikely (!var.indices.allocate_from_pool (pool, point_count) || 1046 !var.deltas_x.allocate_from_pool (pool, point_count, false))) 1047 return false; 1048 1049 if (is_gvar && unlikely (!var.deltas_y.allocate_from_pool (pool, point_count, false))) 1050 return false; 1051 1052 for (unsigned i = 0; i < num_deltas; i++) 1053 { 1054 unsigned idx = apply_to_all ? i : indices[i]; 1055 if (idx >= point_count) continue; 1056 var.indices[idx] = true; 1057 var.deltas_x[idx] = deltas_x[i]; 1058 if (is_gvar) 1059 var.deltas_y[idx] = deltas_y[i]; 1060 } 1061 tuple_vars.push (std::move (var)); 1062 } while (iterator.move_to_next ()); 1063 1064 is_composite = is_composite_glyph; 1065 return true; 1066 } 1067 1068 bool create_from_item_var_data (const VarData &var_data, 1069 const hb_vector_t<hb_hashmap_t<hb_tag_t, Triple>>& regions, 1070 const hb_map_t& axes_old_index_tag_map, 1071 unsigned& item_count, 1072 const hb_inc_bimap_t* inner_map = nullptr) 1073 { 1074 /* NULL offset, to keep original varidx valid, just return */ 1075 if (&var_data == &Null (VarData)) 1076 return true; 1077 1078 unsigned num_regions = var_data.get_region_index_count (); 1079 if (!tuple_vars.alloc (num_regions)) return false; 1080 1081 item_count = inner_map ? inner_map->get_population () : var_data.get_item_count (); 1082 if (!item_count) return true; 1083 unsigned row_size = var_data.get_row_size (); 1084 const HBUINT8 *delta_bytes = var_data.get_delta_bytes (); 1085 1086 for (unsigned r = 0; r < num_regions; r++) 1087 { 1088 /* In VarData, deltas are organized in rows, convert them into 1089 * column(region) based tuples, resize deltas_x first */ 1090 tuple_delta_t tuple; 1091 if (!tuple.deltas_x.resize_dirty (item_count) || 1092 !tuple.indices.resize_dirty (item_count)) 1093 return false; 1094 1095 for (unsigned i = 0; i < item_count; i++) 1096 { 1097 tuple.indices.arrayZ[i] = true; 1098 tuple.deltas_x.arrayZ[i] = var_data.get_item_delta_fast (inner_map ? inner_map->backward (i) : i, 1099 r, delta_bytes, row_size); 1100 } 1101 1102 unsigned region_index = var_data.get_region_index (r); 1103 if (region_index >= regions.length) return false; 1104 tuple.axis_tuples = regions.arrayZ[region_index]; 1105 1106 tuple_vars.push (std::move (tuple)); 1107 } 1108 return !tuple_vars.in_error (); 1109 } 1110 1111 private: 1112 static int _cmp_axis_tag (const void *pa, const void *pb) 1113 { 1114 const hb_tag_t *a = (const hb_tag_t*) pa; 1115 const hb_tag_t *b = (const hb_tag_t*) pb; 1116 return (int)(*a) - (int)(*b); 1117 } 1118 1119 bool change_tuple_variations_axis_limits (const hb_hashmap_t<hb_tag_t, Triple>& normalized_axes_location, 1120 const hb_hashmap_t<hb_tag_t, TripleDistances>& axes_triple_distances, 1121 hb_alloc_pool_t *pool = nullptr) 1122 { 1123 /* sort axis_tag/axis_limits, make result deterministic */ 1124 hb_vector_t<hb_tag_t> axis_tags; 1125 if (!axis_tags.alloc (normalized_axes_location.get_population ())) 1126 return false; 1127 for (auto t : normalized_axes_location.keys ()) 1128 axis_tags.push (t); 1129 1130 // Reused vectors for reduced malloc pressure. 1131 rebase_tent_result_scratch_t scratch; 1132 hb_vector_t<tuple_delta_t> out; 1133 1134 axis_tags.qsort (_cmp_axis_tag); 1135 for (auto axis_tag : axis_tags) 1136 { 1137 Triple *axis_limit; 1138 if (!normalized_axes_location.has (axis_tag, &axis_limit)) 1139 return false; 1140 TripleDistances axis_triple_distances{1.0, 1.0}; 1141 if (axes_triple_distances.has (axis_tag)) 1142 axis_triple_distances = axes_triple_distances.get (axis_tag); 1143 1144 hb_vector_t<tuple_delta_t> new_vars; 1145 for (tuple_delta_t& var : tuple_vars) 1146 { 1147 // This may move var out. 1148 var.change_tuple_var_axis_limit (axis_tag, *axis_limit, axis_triple_distances, out, scratch, pool); 1149 if (!out) continue; 1150 1151 unsigned new_len = new_vars.length + out.length; 1152 1153 if (unlikely (!new_vars.alloc (new_len, false))) 1154 return false; 1155 1156 for (unsigned i = 0; i < out.length; i++) 1157 new_vars.push (std::move (out[i])); 1158 } 1159 tuple_vars = std::move (new_vars); 1160 } 1161 return true; 1162 } 1163 1164 /* merge tuple variations with overlapping tents, if iup delta optimization 1165 * is enabled, add default deltas to contour_points */ 1166 bool merge_tuple_variations (contour_point_vector_t* contour_points = nullptr) 1167 { 1168 hb_vector_t<tuple_delta_t> new_vars; 1169 // The pre-allocation is essential for address stability of pointers 1170 // we store in the hashmap. 1171 if (unlikely (!new_vars.alloc (tuple_vars.length))) 1172 return false; 1173 hb_hashmap_t<const hb_hashmap_t<hb_tag_t, Triple>*, unsigned> m; 1174 for (tuple_delta_t& var : tuple_vars) 1175 { 1176 /* if all axes are pinned, drop the tuple variation */ 1177 if (var.axis_tuples.is_empty ()) 1178 { 1179 /* if iup_delta_optimize is enabled, add deltas to contour coords */ 1180 if (contour_points && !contour_points->add_deltas (var.deltas_x, 1181 var.deltas_y, 1182 var.indices)) 1183 return false; 1184 continue; 1185 } 1186 1187 unsigned *idx; 1188 if (m.has (&(var.axis_tuples), &idx)) 1189 { 1190 new_vars[*idx] += var; 1191 } 1192 else 1193 { 1194 auto *new_var = new_vars.push (); 1195 if (unlikely (new_vars.in_error ())) 1196 return false; 1197 hb_swap (*new_var, var); 1198 if (unlikely (!m.set (&(new_var->axis_tuples), new_vars.length - 1))) 1199 return false; 1200 } 1201 } 1202 m.fini (); // Just in case, since it points into new_vars data. 1203 // Shouldn't be necessary though, since we only move new_vars, not its 1204 // contents. 1205 tuple_vars = std::move (new_vars); 1206 return true; 1207 } 1208 1209 /* compile all point set and store byte data in a point_set->hb_bytes_t hashmap, 1210 * also update point_set->count map, which will be used in finding shared 1211 * point set*/ 1212 bool compile_all_point_sets () 1213 { 1214 for (const auto& tuple: tuple_vars) 1215 { 1216 const hb_vector_t<bool>* points_set = &(tuple.indices); 1217 if (point_data_map.has (points_set)) 1218 { 1219 unsigned *count; 1220 if (unlikely (!point_set_count_map.has (points_set, &count) || 1221 !point_set_count_map.set (points_set, (*count) + 1))) 1222 return false; 1223 continue; 1224 } 1225 1226 hb_vector_t<unsigned char> compiled_point_data; 1227 if (!tuple_delta_t::compile_point_set (*points_set, compiled_point_data)) 1228 return false; 1229 1230 if (!point_data_map.set (points_set, std::move (compiled_point_data)) || 1231 !point_set_count_map.set (points_set, 1)) 1232 return false; 1233 } 1234 return true; 1235 } 1236 1237 /* find shared points set which saves most bytes */ 1238 void find_shared_points () 1239 { 1240 unsigned max_saved_bytes = 0; 1241 1242 for (const auto& _ : point_data_map.iter_ref ()) 1243 { 1244 const hb_vector_t<bool>* points_set = _.first; 1245 unsigned data_length = _.second.length; 1246 if (!data_length) continue; 1247 unsigned *count; 1248 if (unlikely (!point_set_count_map.has (points_set, &count) || 1249 *count <= 1)) 1250 { 1251 shared_points_bytes = nullptr; 1252 return; 1253 } 1254 1255 unsigned saved_bytes = data_length * ((*count) -1); 1256 if (saved_bytes > max_saved_bytes) 1257 { 1258 max_saved_bytes = saved_bytes; 1259 shared_points_bytes = &(_.second); 1260 } 1261 } 1262 } 1263 1264 bool calc_inferred_deltas (const contour_point_vector_t& contour_points, 1265 hb_vector_t<unsigned> &scratch) 1266 { 1267 for (tuple_delta_t& var : tuple_vars) 1268 if (!var.calc_inferred_deltas (contour_points, scratch)) 1269 return false; 1270 1271 return true; 1272 } 1273 1274 bool iup_optimize (const contour_point_vector_t& contour_points, 1275 optimize_scratch_t &scratch) 1276 { 1277 for (tuple_delta_t& var : tuple_vars) 1278 { 1279 if (!var.optimize (contour_points, is_composite, scratch)) 1280 return false; 1281 } 1282 return true; 1283 } 1284 1285 public: 1286 bool instantiate (const hb_hashmap_t<hb_tag_t, Triple>& normalized_axes_location, 1287 const hb_hashmap_t<hb_tag_t, TripleDistances>& axes_triple_distances, 1288 optimize_scratch_t &scratch, 1289 hb_alloc_pool_t *pool = nullptr, 1290 contour_point_vector_t* contour_points = nullptr, 1291 bool optimize = false) 1292 { 1293 if (!tuple_vars) return true; 1294 if (!change_tuple_variations_axis_limits (normalized_axes_location, axes_triple_distances, pool)) 1295 return false; 1296 /* compute inferred deltas only for gvar */ 1297 if (contour_points) 1298 { 1299 hb_vector_t<unsigned> scratch; 1300 if (!calc_inferred_deltas (*contour_points, scratch)) 1301 return false; 1302 } 1303 1304 /* if iup delta opt is on, contour_points can't be null */ 1305 if (optimize && !contour_points) 1306 return false; 1307 1308 if (!merge_tuple_variations (optimize ? contour_points : nullptr)) 1309 return false; 1310 1311 if (optimize && !iup_optimize (*contour_points, scratch)) return false; 1312 return !tuple_vars.in_error (); 1313 } 1314 1315 bool compile_bytes (const hb_map_t& axes_index_map, 1316 const hb_map_t& axes_old_index_tag_map, 1317 bool use_shared_points, 1318 bool is_gvar = false, 1319 const hb_hashmap_t<const hb_vector_t<F2DOT14>*, unsigned>* shared_tuples_idx_map = nullptr, 1320 hb_alloc_pool_t *pool = nullptr) 1321 { 1322 // return true for empty glyph 1323 if (!tuple_vars) 1324 return true; 1325 1326 // compile points set and store data in hashmap 1327 if (!compile_all_point_sets ()) 1328 return false; 1329 1330 /* total compiled byte size as TupleVariationData format, initialized to its 1331 * min_size: 4 */ 1332 compiled_byte_size += 4; 1333 1334 if (use_shared_points) 1335 { 1336 find_shared_points (); 1337 if (shared_points_bytes) 1338 compiled_byte_size += shared_points_bytes->length; 1339 } 1340 hb_vector_t<int> rounded_deltas_scratch; 1341 // compile delta and tuple var header for each tuple variation 1342 for (auto& tuple: tuple_vars) 1343 { 1344 const hb_vector_t<bool>* points_set = &(tuple.indices); 1345 hb_vector_t<unsigned char> *points_data; 1346 if (unlikely (!point_data_map.has (points_set, &points_data))) 1347 return false; 1348 1349 /* when iup optimization is enabled, num of referenced points could be 0 1350 * and thus the compiled points bytes is empty, we should skip compiling 1351 * this tuple */ 1352 if (!points_data->length) 1353 continue; 1354 if (!tuple.compile_deltas (rounded_deltas_scratch, pool)) 1355 return false; 1356 1357 unsigned points_data_length = (points_data != shared_points_bytes) ? points_data->length : 0; 1358 if (!tuple.compile_tuple_var_header (axes_index_map, points_data_length, axes_old_index_tag_map, 1359 shared_tuples_idx_map, 1360 pool)) 1361 return false; 1362 compiled_byte_size += tuple.compiled_tuple_header.length + points_data_length + tuple.compiled_deltas.length; 1363 } 1364 1365 if (is_gvar && (compiled_byte_size % 2)) 1366 { 1367 needs_padding = true; 1368 compiled_byte_size += 1; 1369 } 1370 1371 return true; 1372 } 1373 1374 bool serialize_var_headers (hb_serialize_context_t *c, unsigned& total_header_len) const 1375 { 1376 TRACE_SERIALIZE (this); 1377 for (const auto& tuple: tuple_vars) 1378 { 1379 tuple.compiled_tuple_header.as_array ().copy (c); 1380 if (c->in_error ()) return_trace (false); 1381 total_header_len += tuple.compiled_tuple_header.length; 1382 } 1383 return_trace (true); 1384 } 1385 1386 bool serialize_var_data (hb_serialize_context_t *c, bool is_gvar) const 1387 { 1388 TRACE_SERIALIZE (this); 1389 if (is_gvar && shared_points_bytes) 1390 { 1391 hb_ubytes_t s (shared_points_bytes->arrayZ, shared_points_bytes->length); 1392 s.copy (c); 1393 } 1394 1395 for (const auto& tuple: tuple_vars) 1396 { 1397 const hb_vector_t<bool>* points_set = &(tuple.indices); 1398 hb_vector_t<unsigned char> *point_data; 1399 if (!point_data_map.has (points_set, &point_data)) 1400 return_trace (false); 1401 1402 if (!is_gvar || point_data != shared_points_bytes) 1403 { 1404 hb_ubytes_t s (point_data->arrayZ, point_data->length); 1405 s.copy (c); 1406 } 1407 1408 tuple.compiled_deltas.as_array ().copy (c); 1409 if (c->in_error ()) return_trace (false); 1410 } 1411 1412 /* padding for gvar */ 1413 if (is_gvar && needs_padding) 1414 { 1415 HBUINT8 pad; 1416 pad = 0; 1417 if (!c->embed (pad)) return_trace (false); 1418 } 1419 return_trace (true); 1420 } 1421 }; 1422 1423 struct tuple_iterator_t 1424 { 1425 unsigned get_axis_count () const { return axis_count; } 1426 1427 void init (hb_bytes_t var_data_bytes_, unsigned int axis_count_, const void *table_base_) 1428 { 1429 var_data_bytes = var_data_bytes_; 1430 var_data = var_data_bytes_.as<TupleVariationData> (); 1431 tuples_left = var_data->tupleVarCount.get_count (); 1432 axis_count = axis_count_; 1433 axis_count_times_2 = axis_count_ * 2; 1434 current_tuple = &var_data->get_tuple_var_header (); 1435 data_offset = 0; 1436 table_base = table_base_; 1437 } 1438 1439 bool get_shared_indices (hb_vector_t<unsigned int> &shared_indices /* OUT */) 1440 { 1441 if (var_data->has_shared_point_numbers ()) 1442 { 1443 const HBUINT8 *base = &(table_base+var_data->data); 1444 const HBUINT8 *p = base; 1445 if (!decompile_points (p, shared_indices, (const HBUINT8 *) (var_data_bytes.arrayZ + var_data_bytes.length))) return false; 1446 data_offset = p - base; 1447 } 1448 return true; 1449 } 1450 1451 bool is_valid () 1452 { 1453 if (unlikely (tuples_left <= 0)) 1454 return false; 1455 1456 current_tuple_size = TupleVariationHeader::min_size; 1457 if (unlikely (!var_data_bytes.check_end ((const char *) current_tuple + current_tuple_size))) 1458 return false; 1459 1460 current_tuple_size = current_tuple->get_size (axis_count_times_2); 1461 if (unlikely (!var_data_bytes.check_end ((const char *) current_tuple + current_tuple_size))) 1462 return false; 1463 1464 return true; 1465 } 1466 1467 HB_ALWAYS_INLINE 1468 bool move_to_next () 1469 { 1470 data_offset += current_tuple->get_data_size (); 1471 current_tuple = &StructAtOffset<TupleVariationHeader> (current_tuple, current_tuple_size); 1472 tuples_left--; 1473 return is_valid (); 1474 } 1475 1476 // TODO: Make it return (sanitized) hb_bytes_t 1477 const HBUINT8 *get_serialized_data () const 1478 { return &(table_base+var_data->data) + data_offset; } 1479 1480 private: 1481 signed tuples_left; 1482 const TupleVariationData *var_data; 1483 unsigned int axis_count; 1484 unsigned int axis_count_times_2; 1485 unsigned int data_offset; 1486 unsigned int current_tuple_size; 1487 const void *table_base; 1488 1489 public: 1490 hb_bytes_t var_data_bytes; 1491 const TupleVariationHeader *current_tuple; 1492 }; 1493 1494 static bool get_tuple_iterator (hb_bytes_t var_data_bytes, unsigned axis_count, 1495 const void *table_base, 1496 hb_vector_t<unsigned int> &shared_indices /* OUT */, 1497 tuple_iterator_t *iterator /* OUT */) 1498 { 1499 iterator->init (var_data_bytes, axis_count, table_base); 1500 if (!iterator->get_shared_indices (shared_indices)) 1501 return false; 1502 return iterator->is_valid (); 1503 } 1504 1505 bool has_shared_point_numbers () const { return tupleVarCount.has_shared_point_numbers (); } 1506 1507 static bool decompile_points (const HBUINT8 *&p /* IN/OUT */, 1508 hb_vector_t<unsigned int> &points /* OUT */, 1509 const HBUINT8 *end) 1510 { 1511 enum packed_point_flag_t 1512 { 1513 POINTS_ARE_WORDS = 0x80, 1514 POINT_RUN_COUNT_MASK = 0x7F 1515 }; 1516 1517 if (unlikely (p + 1 > end)) return false; 1518 1519 unsigned count = *p++; 1520 if (count & POINTS_ARE_WORDS) 1521 { 1522 if (unlikely (p + 1 > end)) return false; 1523 count = ((count & POINT_RUN_COUNT_MASK) << 8) | *p++; 1524 } 1525 if (unlikely (!points.resize_dirty (count))) return false; 1526 1527 unsigned n = 0; 1528 unsigned i = 0; 1529 while (i < count) 1530 { 1531 if (unlikely (p + 1 > end)) return false; 1532 unsigned control = *p++; 1533 unsigned run_count = (control & POINT_RUN_COUNT_MASK) + 1; 1534 unsigned stop = i + run_count; 1535 if (unlikely (stop > count)) return false; 1536 if (control & POINTS_ARE_WORDS) 1537 { 1538 if (unlikely (p + run_count * HBUINT16::static_size > end)) return false; 1539 for (; i < stop; i++) 1540 { 1541 n += *(const HBUINT16 *)p; 1542 points.arrayZ[i] = n; 1543 p += HBUINT16::static_size; 1544 } 1545 } 1546 else 1547 { 1548 if (unlikely (p + run_count > end)) return false; 1549 for (; i < stop; i++) 1550 { 1551 n += *p++; 1552 points.arrayZ[i] = n; 1553 } 1554 } 1555 } 1556 return true; 1557 } 1558 1559 template <typename T> 1560 HB_ALWAYS_INLINE 1561 static bool decompile_deltas (const HBUINT8 *&p /* IN/OUT */, 1562 hb_vector_t<T> &deltas /* IN/OUT */, 1563 const HBUINT8 *end, 1564 bool consume_all = false, 1565 unsigned start = 0) 1566 { 1567 return TupleValues::decompile (p, deltas, end, consume_all, start); 1568 } 1569 1570 bool has_data () const { return tupleVarCount; } 1571 1572 bool decompile_tuple_variations (unsigned point_count, 1573 bool is_gvar, 1574 tuple_iterator_t iterator, 1575 const hb_map_t *axes_old_index_tag_map, 1576 const hb_vector_t<unsigned> &shared_indices, 1577 const hb_array_t<const F2DOT14> shared_tuples, 1578 tuple_variations_t& tuple_variations, /* OUT */ 1579 hb_alloc_pool_t *pool = nullptr, 1580 bool is_composite_glyph = false) const 1581 { 1582 return tuple_variations.create_from_tuple_var_data (iterator, tupleVarCount, 1583 point_count, is_gvar, 1584 axes_old_index_tag_map, 1585 shared_indices, 1586 shared_tuples, 1587 pool, 1588 is_composite_glyph); 1589 } 1590 1591 bool serialize (hb_serialize_context_t *c, 1592 bool is_gvar, 1593 const tuple_variations_t& tuple_variations) const 1594 { 1595 TRACE_SERIALIZE (this); 1596 /* empty tuple variations, just return and skip serialization. */ 1597 if (!tuple_variations) return_trace (true); 1598 1599 auto *out = c->start_embed (this); 1600 if (unlikely (!c->extend_min (out))) return_trace (false); 1601 1602 if (!c->check_assign (out->tupleVarCount, tuple_variations.get_var_count (), 1603 HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); 1604 1605 unsigned total_header_len = 0; 1606 1607 if (!tuple_variations.serialize_var_headers (c, total_header_len)) 1608 return_trace (false); 1609 1610 unsigned data_offset = min_size + total_header_len; 1611 if (!is_gvar) data_offset += 4; 1612 if (!c->check_assign (out->data, data_offset, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); 1613 1614 return tuple_variations.serialize_var_data (c, is_gvar); 1615 } 1616 1617 protected: 1618 struct TupleVarCount : HBUINT16 1619 { 1620 friend struct tuple_variations_t; 1621 bool has_shared_point_numbers () const { return ((*this) & SharedPointNumbers); } 1622 unsigned int get_count () const { return (*this) & CountMask; } 1623 TupleVarCount& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } 1624 explicit operator bool () const { return get_count (); } 1625 1626 protected: 1627 enum Flags 1628 { 1629 SharedPointNumbers= 0x8000u, 1630 CountMask = 0x0FFFu 1631 }; 1632 public: 1633 DEFINE_SIZE_STATIC (2); 1634 }; 1635 1636 TupleVarCount tupleVarCount; /* A packed field. The high 4 bits are flags, and the 1637 * low 12 bits are the number of tuple variation tables 1638 * for this glyph. The number of tuple variation tables 1639 * can be any number between 1 and 4095. */ 1640 OffsetTo<HBUINT8, OffType> 1641 data; /* Offset from the start of the base table 1642 * to the serialized data. */ 1643 /* TupleVariationHeader tupleVariationHeaders[] *//* Array of tuple variation headers. */ 1644 public: 1645 DEFINE_SIZE_MIN (2 + OffType::static_size); 1646 }; 1647 1648 // TODO: Move tuple_variations_t to outside of TupleVariationData 1649 using tuple_variations_t = TupleVariationData<HBUINT16>::tuple_variations_t; 1650 struct item_variations_t 1651 { 1652 using region_t = const hb_hashmap_t<hb_tag_t, Triple>*; 1653 private: 1654 /* each subtable is decompiled into a tuple_variations_t, in which all tuples 1655 * have the same num of deltas (rows) */ 1656 hb_vector_t<tuple_variations_t> vars; 1657 1658 /* num of retained rows for each subtable, there're 2 cases when var_data is empty: 1659 * 1. retained item_count is zero 1660 * 2. regions is empty and item_count is non-zero. 1661 * when converting to tuples, both will be dropped because the tuple is empty, 1662 * however, we need to retain 2. as all-zero rows to keep original varidx 1663 * valid, so we need a way to remember the num of rows for each subtable */ 1664 hb_vector_t<unsigned> var_data_num_rows; 1665 1666 /* original region list, decompiled from item varstore, used when rebuilding 1667 * region list after instantiation */ 1668 hb_vector_t<hb_hashmap_t<hb_tag_t, Triple>> orig_region_list; 1669 1670 /* region list: vector of Regions, maintain the original order for the regions 1671 * that existed before instantiate (), append the new regions at the end. 1672 * Regions are stored in each tuple already, save pointers only. 1673 * When converting back to item varstore, unused regions will be pruned */ 1674 hb_vector_t<region_t> region_list; 1675 1676 /* region -> idx map after instantiation and pruning unused regions */ 1677 hb_hashmap_t<region_t, unsigned> region_map; 1678 1679 /* all delta rows after instantiation */ 1680 hb_vector_t<hb_vector_t<int>> delta_rows; 1681 /* final optimized vector of encoding objects used to assemble the varstore */ 1682 hb_vector_t<delta_row_encoding_t> encodings; 1683 1684 /* old varidxes -> new var_idxes map */ 1685 hb_map_t varidx_map; 1686 1687 /* has long words */ 1688 bool has_long = false; 1689 1690 public: 1691 bool has_long_word () const 1692 { return has_long; } 1693 1694 const hb_vector_t<region_t>& get_region_list () const 1695 { return region_list; } 1696 1697 const hb_vector_t<delta_row_encoding_t>& get_vardata_encodings () const 1698 { return encodings; } 1699 1700 const hb_map_t& get_varidx_map () const 1701 { return varidx_map; } 1702 1703 bool instantiate (const ItemVariationStore& varStore, 1704 const hb_subset_plan_t *plan, 1705 bool optimize=true, 1706 bool use_no_variation_idx=true, 1707 const hb_array_t <const hb_inc_bimap_t> inner_maps = hb_array_t<const hb_inc_bimap_t> ()) 1708 { 1709 if (!create_from_item_varstore (varStore, plan->axes_old_index_tag_map, inner_maps)) 1710 return false; 1711 if (!instantiate_tuple_vars (plan->axes_location, plan->axes_triple_distances)) 1712 return false; 1713 return as_item_varstore (optimize, use_no_variation_idx); 1714 } 1715 1716 /* keep below APIs public only for unit test: test-item-varstore */ 1717 bool create_from_item_varstore (const ItemVariationStore& varStore, 1718 const hb_map_t& axes_old_index_tag_map, 1719 const hb_array_t <const hb_inc_bimap_t> inner_maps = hb_array_t<const hb_inc_bimap_t> ()) 1720 { 1721 const VarRegionList& regionList = varStore.get_region_list (); 1722 if (!regionList.get_var_regions (axes_old_index_tag_map, orig_region_list)) 1723 return false; 1724 1725 unsigned num_var_data = varStore.get_sub_table_count (); 1726 if (inner_maps && inner_maps.length != num_var_data) return false; 1727 if (!vars.alloc (num_var_data) || 1728 !var_data_num_rows.alloc (num_var_data)) return false; 1729 1730 for (unsigned i = 0; i < num_var_data; i++) 1731 { 1732 if (inner_maps && !inner_maps.arrayZ[i].get_population ()) 1733 continue; 1734 tuple_variations_t var_data_tuples; 1735 unsigned item_count = 0; 1736 if (!var_data_tuples.create_from_item_var_data (varStore.get_sub_table (i), 1737 orig_region_list, 1738 axes_old_index_tag_map, 1739 item_count, 1740 inner_maps ? &(inner_maps.arrayZ[i]) : nullptr)) 1741 return false; 1742 1743 var_data_num_rows.push (item_count); 1744 vars.push (std::move (var_data_tuples)); 1745 } 1746 return !vars.in_error () && !var_data_num_rows.in_error () && vars.length == var_data_num_rows.length; 1747 } 1748 1749 bool instantiate_tuple_vars (const hb_hashmap_t<hb_tag_t, Triple>& normalized_axes_location, 1750 const hb_hashmap_t<hb_tag_t, TripleDistances>& axes_triple_distances) 1751 { 1752 optimize_scratch_t scratch; 1753 for (tuple_variations_t& tuple_vars : vars) 1754 if (!tuple_vars.instantiate (normalized_axes_location, axes_triple_distances, scratch)) 1755 return false; 1756 1757 if (!build_region_list ()) return false; 1758 return true; 1759 } 1760 1761 bool build_region_list () 1762 { 1763 /* scan all tuples and collect all unique regions, prune unused regions */ 1764 hb_hashmap_t<region_t, unsigned> all_regions; 1765 hb_hashmap_t<region_t, unsigned> used_regions; 1766 1767 /* use a vector when inserting new regions, make result deterministic */ 1768 hb_vector_t<region_t> all_unique_regions; 1769 for (const tuple_variations_t& sub_table : vars) 1770 { 1771 for (const tuple_delta_t& tuple : sub_table.tuple_vars) 1772 { 1773 region_t r = &(tuple.axis_tuples); 1774 if (!used_regions.has (r)) 1775 { 1776 bool all_zeros = true; 1777 for (float d : tuple.deltas_x) 1778 { 1779 int delta = (int) roundf (d); 1780 if (delta != 0) 1781 { 1782 all_zeros = false; 1783 break; 1784 } 1785 } 1786 if (!all_zeros) 1787 { 1788 if (!used_regions.set (r, 1)) 1789 return false; 1790 } 1791 } 1792 if (all_regions.has (r)) 1793 continue; 1794 if (!all_regions.set (r, 1)) 1795 return false; 1796 all_unique_regions.push (r); 1797 } 1798 } 1799 1800 /* regions are empty means no variation data, return true */ 1801 if (!all_regions || !all_unique_regions) return true; 1802 1803 if (!region_list.alloc (all_regions.get_population ())) 1804 return false; 1805 1806 unsigned idx = 0; 1807 /* append the original regions that pre-existed */ 1808 for (const auto& r : orig_region_list) 1809 { 1810 if (!all_regions.has (&r) || !used_regions.has (&r)) 1811 continue; 1812 1813 region_list.push (&r); 1814 if (!region_map.set (&r, idx)) 1815 return false; 1816 all_regions.del (&r); 1817 idx++; 1818 } 1819 1820 /* append the new regions at the end */ 1821 for (const auto& r: all_unique_regions) 1822 { 1823 if (!all_regions.has (r) || !used_regions.has (r)) 1824 continue; 1825 region_list.push (r); 1826 if (!region_map.set (r, idx)) 1827 return false; 1828 all_regions.del (r); 1829 idx++; 1830 } 1831 return (!region_list.in_error ()) && (!region_map.in_error ()); 1832 } 1833 1834 /* main algorithm ported from fonttools VarStore_optimize() method, optimize 1835 * varstore by default */ 1836 1837 struct combined_gain_idx_tuple_t 1838 { 1839 uint64_t encoded; 1840 1841 combined_gain_idx_tuple_t () = default; 1842 combined_gain_idx_tuple_t (unsigned gain, unsigned i, unsigned j) 1843 : encoded ((uint64_t (0xFFFFFF - gain) << 40) | (uint64_t (i) << 20) | uint64_t (j)) 1844 { 1845 assert (gain < 0xFFFFFF); 1846 assert (i < 0xFFFFFFF && j < 0xFFFFFFF); 1847 } 1848 1849 bool operator < (const combined_gain_idx_tuple_t& o) 1850 { 1851 return encoded < o.encoded; 1852 } 1853 1854 bool operator <= (const combined_gain_idx_tuple_t& o) 1855 { 1856 return encoded <= o.encoded; 1857 } 1858 1859 unsigned idx_1 () const { return (encoded >> 20) & 0xFFFFF; }; 1860 unsigned idx_2 () const { return encoded & 0xFFFFF; }; 1861 }; 1862 1863 bool as_item_varstore (bool optimize=true, bool use_no_variation_idx=true) 1864 { 1865 /* return true if no variation data */ 1866 if (!region_list) return true; 1867 unsigned num_cols = region_list.length; 1868 /* pre-alloc a 2D vector for all sub_table's VarData rows */ 1869 unsigned total_rows = 0; 1870 for (unsigned major = 0; major < var_data_num_rows.length; major++) 1871 total_rows += var_data_num_rows[major]; 1872 1873 if (!delta_rows.resize (total_rows)) return false; 1874 /* init all rows to [0]*num_cols */ 1875 for (unsigned i = 0; i < total_rows; i++) 1876 if (!(delta_rows[i].resize (num_cols))) return false; 1877 1878 /* old VarIdxes -> full encoding_row mapping */ 1879 hb_hashmap_t<unsigned, const hb_vector_t<int>*> front_mapping; 1880 unsigned start_row = 0; 1881 hb_vector_t<delta_row_encoding_t> encoding_objs; 1882 1883 /* delta_rows map, used for filtering out duplicate rows */ 1884 hb_vector_t<const hb_vector_t<int> *> major_rows; 1885 hb_hashmap_t<const hb_vector_t<int>*, unsigned> delta_rows_map; 1886 for (unsigned major = 0; major < vars.length; major++) 1887 { 1888 /* deltas are stored in tuples(column based), convert them back into items 1889 * (row based) delta */ 1890 const tuple_variations_t& tuples = vars[major]; 1891 unsigned num_rows = var_data_num_rows[major]; 1892 1893 if (!num_rows) continue; 1894 1895 for (const tuple_delta_t& tuple: tuples.tuple_vars) 1896 { 1897 if (tuple.deltas_x.length != num_rows) 1898 return false; 1899 1900 /* skip unused regions */ 1901 unsigned *col_idx; 1902 if (!region_map.has (&(tuple.axis_tuples), &col_idx)) 1903 continue; 1904 1905 for (unsigned i = 0; i < num_rows; i++) 1906 { 1907 int rounded_delta = roundf (tuple.deltas_x[i]); 1908 delta_rows[start_row + i][*col_idx] += rounded_delta; 1909 has_long |= rounded_delta < -65536 || rounded_delta > 65535; 1910 } 1911 } 1912 1913 major_rows.reset (); 1914 for (unsigned minor = 0; minor < num_rows; minor++) 1915 { 1916 const hb_vector_t<int>& row = delta_rows[start_row + minor]; 1917 if (use_no_variation_idx) 1918 { 1919 bool all_zeros = true; 1920 for (int delta : row) 1921 { 1922 if (delta != 0) 1923 { 1924 all_zeros = false; 1925 break; 1926 } 1927 } 1928 if (all_zeros) 1929 continue; 1930 } 1931 1932 if (!front_mapping.set ((major<<16) + minor, &row)) 1933 return false; 1934 1935 if (delta_rows_map.has (&row)) 1936 continue; 1937 1938 delta_rows_map.set (&row, 1); 1939 1940 major_rows.push (&row); 1941 } 1942 1943 if (major_rows) 1944 encoding_objs.push (delta_row_encoding_t (std::move (major_rows), num_cols)); 1945 1946 start_row += num_rows; 1947 } 1948 1949 /* return directly if no optimization, maintain original VariationIndex so 1950 * varidx_map would be empty */ 1951 if (!optimize) 1952 { 1953 encodings = std::move (encoding_objs); 1954 return !encodings.in_error (); 1955 } 1956 1957 /* NOTE: Fonttools instancer always optimizes VarStore from scratch. This 1958 * is too costly for large fonts. So, instead, we retain the encodings of 1959 * the original VarStore, and just try to combine them if possible. This 1960 * is a compromise between optimization and performance and practically 1961 * works very well. */ 1962 1963 // This produces slightly smaller results in some cases. 1964 encoding_objs.qsort (); 1965 1966 /* main algorithm: repeatedly pick 2 best encodings to combine, and combine them */ 1967 using item_t = hb_priority_queue_t<combined_gain_idx_tuple_t>::item_t; 1968 hb_vector_t<item_t> queue_items; 1969 unsigned num_todos = encoding_objs.length; 1970 for (unsigned i = 0; i < num_todos; i++) 1971 { 1972 for (unsigned j = i + 1; j < num_todos; j++) 1973 { 1974 int combining_gain = encoding_objs.arrayZ[i].gain_from_merging (encoding_objs.arrayZ[j]); 1975 if (combining_gain > 0) 1976 queue_items.push (item_t (combined_gain_idx_tuple_t (combining_gain, i, j), 0)); 1977 } 1978 } 1979 1980 hb_priority_queue_t<combined_gain_idx_tuple_t> queue (std::move (queue_items)); 1981 1982 hb_bit_set_t removed_todo_idxes; 1983 while (queue) 1984 { 1985 auto t = queue.pop_minimum ().first; 1986 unsigned i = t.idx_1 (); 1987 unsigned j = t.idx_2 (); 1988 1989 if (removed_todo_idxes.has (i) || removed_todo_idxes.has (j)) 1990 continue; 1991 1992 delta_row_encoding_t& encoding = encoding_objs.arrayZ[i]; 1993 delta_row_encoding_t& other_encoding = encoding_objs.arrayZ[j]; 1994 1995 removed_todo_idxes.add (i); 1996 removed_todo_idxes.add (j); 1997 1998 encoding.merge (other_encoding); 1999 2000 for (unsigned idx = 0; idx < encoding_objs.length; idx++) 2001 { 2002 if (removed_todo_idxes.has (idx)) continue; 2003 2004 const delta_row_encoding_t& obj = encoding_objs.arrayZ[idx]; 2005 // In the unlikely event that the same encoding exists already, combine it. 2006 if (obj.width == encoding.width && obj.chars == encoding.chars) 2007 { 2008 // This is straight port from fonttools algorithm. I added this branch there 2009 // because I thought it can happen. But looks like we never get in here in 2010 // practice. I'm not confident enough to remove it though; in theory it can 2011 // happen. I think it's just that our tests are not extensive enough to hit 2012 // this path. 2013 2014 for (const auto& row : obj.items) 2015 encoding.add_row (row); 2016 2017 removed_todo_idxes.add (idx); 2018 continue; 2019 } 2020 2021 int combined_gain = encoding.gain_from_merging (obj); 2022 if (combined_gain > 0) 2023 queue.insert (combined_gain_idx_tuple_t (combined_gain, idx, encoding_objs.length), 0); 2024 } 2025 2026 auto moved_encoding = std::move (encoding); 2027 encoding_objs.push (moved_encoding); 2028 } 2029 2030 int num_final_encodings = (int) encoding_objs.length - (int) removed_todo_idxes.get_population (); 2031 if (num_final_encodings <= 0) return false; 2032 2033 if (!encodings.alloc (num_final_encodings)) return false; 2034 for (unsigned i = 0; i < encoding_objs.length; i++) 2035 { 2036 if (removed_todo_idxes.has (i)) continue; 2037 encodings.push (std::move (encoding_objs.arrayZ[i])); 2038 } 2039 2040 return compile_varidx_map (front_mapping); 2041 } 2042 2043 private: 2044 /* compile varidx_map for one VarData subtable (index specified by major) */ 2045 bool compile_varidx_map (const hb_hashmap_t<unsigned, const hb_vector_t<int>*>& front_mapping) 2046 { 2047 /* full encoding_row -> new VarIdxes mapping */ 2048 hb_hashmap_t<const hb_vector_t<int>*, unsigned> back_mapping; 2049 2050 for (unsigned major = 0; major < encodings.length; major++) 2051 { 2052 delta_row_encoding_t& encoding = encodings[major]; 2053 /* just sanity check, this shouldn't happen */ 2054 if (encoding.is_empty ()) 2055 return false; 2056 2057 unsigned num_rows = encoding.items.length; 2058 2059 /* sort rows, make result deterministic */ 2060 encoding.items.qsort (_cmp_row); 2061 2062 /* compile old to new var_idxes mapping */ 2063 for (unsigned minor = 0; minor < num_rows; minor++) 2064 { 2065 unsigned new_varidx = (major << 16) + minor; 2066 back_mapping.set (encoding.items.arrayZ[minor], new_varidx); 2067 } 2068 } 2069 2070 for (auto _ : front_mapping.iter ()) 2071 { 2072 unsigned old_varidx = _.first; 2073 unsigned *new_varidx; 2074 if (back_mapping.has (_.second, &new_varidx)) 2075 varidx_map.set (old_varidx, *new_varidx); 2076 else 2077 varidx_map.set (old_varidx, HB_OT_LAYOUT_NO_VARIATIONS_INDEX); 2078 } 2079 return !varidx_map.in_error (); 2080 } 2081 2082 static int _cmp_row (const void *pa, const void *pb) 2083 { 2084 /* compare pointers of vectors(const hb_vector_t<int>*) that represent a row */ 2085 const hb_vector_t<int>** a = (const hb_vector_t<int>**) pa; 2086 const hb_vector_t<int>** b = (const hb_vector_t<int>**) pb; 2087 2088 for (unsigned i = 0; i < (*b)->length; i++) 2089 { 2090 int va = (*a)->arrayZ[i]; 2091 int vb = (*b)->arrayZ[i]; 2092 if (va != vb) 2093 return va < vb ? -1 : 1; 2094 } 2095 return 0; 2096 } 2097 }; 2098 2099 2100 } /* namespace OT */ 2101 2102 2103 #endif /* HB_OT_VAR_COMMON_HH */