Coverage Report

Created: 2026-09-28 10:59

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