Coverage Report

Created: 2025-12-04 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh
Line
Count
Source
1
#ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH
2
#define OT_LAYOUT_GPOS_VALUEFORMAT_HH
3
4
#include "../../../hb-ot-layout-gsubgpos.hh"
5
6
namespace OT {
7
namespace Layout {
8
namespace GPOS_impl {
9
10
typedef HBUINT16 Value;
11
12
struct ValueBase {}; // Dummy base class tag for OffsetTo<Value> bases.
13
14
typedef UnsizedArrayOf<Value> ValueRecord;
15
16
struct ValueFormat : HBUINT16
17
{
18
  enum Flags {
19
    xPlacement  = 0x0001u,      /* Includes horizontal adjustment for placement */
20
    yPlacement  = 0x0002u,      /* Includes vertical adjustment for placement */
21
    xAdvance    = 0x0004u,      /* Includes horizontal adjustment for advance */
22
    yAdvance    = 0x0008u,      /* Includes vertical adjustment for advance */
23
    xPlaDevice  = 0x0010u,      /* Includes horizontal Device table for placement */
24
    yPlaDevice  = 0x0020u,      /* Includes vertical Device table for placement */
25
    xAdvDevice  = 0x0040u,      /* Includes horizontal Device table for advance */
26
    yAdvDevice  = 0x0080u,      /* Includes vertical Device table for advance */
27
    ignored     = 0x0F00u,      /* Was used in TrueType Open for MM fonts */
28
    reserved    = 0xF000u,      /* For future use */
29
30
    devices     = 0x00F0u       /* Mask for having any Device table */
31
  };
32
33
/* All fields are options.  Only those available advance the value pointer. */
34
#if 0
35
  HBINT16               xPlacement;     /* Horizontal adjustment for
36
                                         * placement--in design units */
37
  HBINT16               yPlacement;     /* Vertical adjustment for
38
                                         * placement--in design units */
39
  HBINT16               xAdvance;       /* Horizontal adjustment for
40
                                         * advance--in design units (only used
41
                                         * for horizontal writing) */
42
  HBINT16               yAdvance;       /* Vertical adjustment for advance--in
43
                                         * design units (only used for vertical
44
                                         * writing) */
45
  Offset16To<Device>    xPlaDevice;     /* Offset to Device table for
46
                                         * horizontal placement--measured from
47
                                         * beginning of PosTable (may be NULL) */
48
  Offset16To<Device>    yPlaDevice;     /* Offset to Device table for vertical
49
                                         * placement--measured from beginning
50
                                         * of PosTable (may be NULL) */
51
  Offset16To<Device>    xAdvDevice;     /* Offset to Device table for
52
                                         * horizontal advance--measured from
53
                                         * beginning of PosTable (may be NULL) */
54
  Offset16To<Device>    yAdvDevice;     /* Offset to Device table for vertical
55
                                         * advance--measured from beginning of
56
                                         * PosTable (may be NULL) */
57
#endif
58
59
0
  NumType& operator = (uint16_t i) { v = i; return *this; }
60
61
  // Note: spec says skip 2 bytes per bit in the valueformat. But reports
62
  // from Microsoft developers indicate that only the fields that are
63
  // currently defined are counted. We don't expect any new fields to
64
  // be added to ValueFormat. As such, we use the faster hb_popcount8
65
  // that only processes the lowest 8 bits.
66
54
  unsigned int get_len () const  { return hb_popcount8 ((uint8_t) *this); }
67
0
  unsigned int get_size () const { return get_len () * Value::static_size; }
68
69
0
  hb_vector_t<unsigned> get_device_table_indices () const {
70
0
    unsigned i = 0;
71
0
    hb_vector_t<unsigned> result;
72
0
    unsigned format = *this;
73
0
74
0
    if (format & xPlacement) i++;
75
0
    if (format & yPlacement) i++;
76
0
    if (format & xAdvance)   i++;
77
0
    if (format & yAdvance)   i++;
78
0
79
0
    if (format & xPlaDevice) result.push (i++);
80
0
    if (format & yPlaDevice) result.push (i++);
81
0
    if (format & xAdvDevice) result.push (i++);
82
0
    if (format & yAdvDevice) result.push (i++);
83
0
84
0
    return result;
85
0
  }
86
87
  bool apply_value (hb_ot_apply_context_t *c,
88
                    const ValueBase       *base,
89
                    const Value           *values,
90
                    hb_glyph_position_t   &glyph_pos) const
91
0
  {
92
0
    bool ret = false;
93
0
    unsigned int format = *this;
94
0
    if (!format) return ret;
95
96
0
    hb_font_t *font = c->font;
97
0
    bool horizontal =
98
0
#ifndef HB_NO_VERTICAL
99
0
      HB_DIRECTION_IS_HORIZONTAL (c->direction)
100
#else
101
      true
102
#endif
103
0
      ;
104
105
0
    if (format & xPlacement) glyph_pos.x_offset  += font->em_scale_x (get_short (values++, &ret));
106
0
    if (format & yPlacement) glyph_pos.y_offset  += font->em_scale_y (get_short (values++, &ret));
107
0
    if (format & xAdvance) {
108
0
      if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values, &ret));
109
0
      values++;
110
0
    }
111
    /* y_advance values grow downward but font-space grows upward, hence negation */
112
0
    if (format & yAdvance) {
113
0
      if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values, &ret));
114
0
      values++;
115
0
    }
116
117
0
    if (!has_device ()) return ret;
118
119
0
    bool use_x_device = font->x_ppem || font->has_nonzero_coords;
120
0
    bool use_y_device = font->y_ppem || font->has_nonzero_coords;
121
122
0
    if (!use_x_device && !use_y_device) return ret;
123
124
0
    const ItemVariationStore &store = c->var_store;
125
0
    auto *cache = c->var_store_cache;
126
127
    /* pixel -> fractional pixel */
128
0
    if (format & xPlaDevice)
129
0
    {
130
0
      if (use_x_device) glyph_pos.x_offset  += get_device (values, &ret, base, c->sanitizer).get_x_delta (font, store, cache);
131
0
      values++;
132
0
    }
133
0
    if (format & yPlaDevice)
134
0
    {
135
0
      if (use_y_device) glyph_pos.y_offset  += get_device (values, &ret, base, c->sanitizer).get_y_delta (font, store, cache);
136
0
      values++;
137
0
    }
138
0
    if (format & xAdvDevice)
139
0
    {
140
0
      if (horizontal && use_x_device) glyph_pos.x_advance += get_device (values, &ret, base, c->sanitizer).get_x_delta (font, store, cache);
141
0
      values++;
142
0
    }
143
0
    if (format & yAdvDevice)
144
0
    {
145
      /* y_advance values grow downward but font-space grows upward, hence negation */
146
0
      if (!horizontal && use_y_device) glyph_pos.y_advance -= get_device (values, &ret, base, c->sanitizer).get_y_delta (font, store, cache);
147
0
      values++;
148
0
    }
149
0
    return ret;
150
0
  }
151
152
  unsigned int get_effective_format (const Value *values, bool strip_hints, bool strip_empty, const ValueBase *base,
153
                                     const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const
154
0
  {
155
0
    unsigned int format = *this;
156
0
    for (unsigned flag = xPlacement; flag <= yAdvDevice; flag = flag << 1) {
157
0
      if (format & flag)
158
0
      {
159
0
        if (strip_hints && flag >= xPlaDevice)
160
0
        {
161
0
          format = format & ~flag;
162
0
          values++;
163
0
          continue;
164
0
        }
165
0
        if (varidx_delta_map && flag >= xPlaDevice)
166
0
        {
167
0
          update_var_flag (values++, (Flags) flag, &format, base, varidx_delta_map);
168
0
          continue;
169
0
        }
170
0
        /* do not strip empty when instancing, cause we don't know whether the new
171
0
         * default value is 0 or not */
172
0
        if (strip_empty) should_drop (*values, (Flags) flag, &format);
173
0
        values++;
174
0
      }
175
0
    }
176
0
177
0
    return format;
178
0
  }
179
180
  template<typename Iterator,
181
      hb_requires (hb_is_iterator (Iterator))>
182
  unsigned int get_effective_format (Iterator it, bool strip_hints, bool strip_empty, const ValueBase *base,
183
0
                                     const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const {
184
0
    unsigned int new_format = 0;
185
0
186
0
    for (const hb_array_t<const Value>& values : it)
187
0
      new_format = new_format | get_effective_format (&values, strip_hints, strip_empty, base, varidx_delta_map);
188
0
189
0
    return new_format;
190
0
  }
Unexecuted instantiation: hb-ot-face.cc:_ZNK2OT6Layout9GPOS_impl11ValueFormat20get_effective_formatI13hb_map_iter_tIS4_I16hb_filter_iter_tI13hb_zip_iter_tINS0_6Common8Coverage6iter_tE15hb_range_iter_tIjjEERK8hb_set_tRK3$_6LPv0EEZNKS1_16SinglePosFormat26subsetEP19hb_subset_context_tEUlRK9hb_pair_tIjjEE_L24hb_function_sortedness_t1ELSJ_0EERK3$_7LST_0ELSJ_0EETnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT_NS10_6item_tEEE5valueEvE4typeELSJ_0EEEjS10_bbPKNS1_9ValueBaseEPK12hb_hashmap_tIjSO_IjiELb0EE
Unexecuted instantiation: hb-aat-layout.cc:_ZNK2OT6Layout9GPOS_impl11ValueFormat20get_effective_formatI13hb_map_iter_tIS4_I16hb_filter_iter_tI13hb_zip_iter_tINS0_6Common8Coverage6iter_tE15hb_range_iter_tIjjEERK8hb_set_tRK3$_6LPv0EEZNKS1_16SinglePosFormat26subsetEP19hb_subset_context_tEUlRK9hb_pair_tIjjEE_L24hb_function_sortedness_t1ELSJ_0EERK3$_7LST_0ELSJ_0EETnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT_NS10_6item_tEEE5valueEvE4typeELSJ_0EEEjS10_bbPKNS1_9ValueBaseEPK12hb_hashmap_tIjSO_IjiELb0EE
Unexecuted instantiation: hb-ot-layout.cc:_ZNK2OT6Layout9GPOS_impl11ValueFormat20get_effective_formatI13hb_map_iter_tIS4_I16hb_filter_iter_tI13hb_zip_iter_tINS0_6Common8Coverage6iter_tE15hb_range_iter_tIjjEERK8hb_set_tRK3$_6LPv0EEZNKS1_16SinglePosFormat26subsetEP19hb_subset_context_tEUlRK9hb_pair_tIjjEE_L24hb_function_sortedness_t1ELSJ_0EERK3$_7LST_0ELSJ_0EETnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT_NS10_6item_tEEE5valueEvE4typeELSJ_0EEEjS10_bbPKNS1_9ValueBaseEPK12hb_hashmap_tIjSO_IjiELb0EE
Unexecuted instantiation: hb-ot-shape-fallback.cc:_ZNK2OT6Layout9GPOS_impl11ValueFormat20get_effective_formatI13hb_map_iter_tIS4_I16hb_filter_iter_tI13hb_zip_iter_tINS0_6Common8Coverage6iter_tE15hb_range_iter_tIjjEERK8hb_set_tRK3$_6LPv0EEZNKS1_16SinglePosFormat26subsetEP19hb_subset_context_tEUlRK9hb_pair_tIjjEE_L24hb_function_sortedness_t1ELSJ_0EERK3$_7LST_0ELSJ_0EETnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT_NS10_6item_tEEE5valueEvE4typeELSJ_0EEEjS10_bbPKNS1_9ValueBaseEPK12hb_hashmap_tIjSO_IjiELb0EE
191
192
  void copy_values (hb_serialize_context_t *c,
193
                    unsigned int new_format,
194
                    const ValueBase *base,
195
                    const Value *values,
196
                    const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const
197
0
  {
198
0
    unsigned int format = *this;
199
0
    if (!format) return;
200
0
201
0
    HBINT16 *x_placement = nullptr, *y_placement = nullptr, *x_adv = nullptr, *y_adv = nullptr;
202
0
    if (format & xPlacement) x_placement = copy_value (c, new_format, xPlacement, *values++);
203
0
    if (format & yPlacement) y_placement = copy_value (c, new_format, yPlacement, *values++);
204
0
    if (format & xAdvance)   x_adv = copy_value (c, new_format, xAdvance, *values++);
205
0
    if (format & yAdvance)   y_adv = copy_value (c, new_format, yAdvance, *values++);
206
0
207
0
    if (!has_device ())
208
0
      return;
209
0
210
0
    if (format & xPlaDevice)
211
0
    {
212
0
      add_delta_to_value (x_placement, base, values, layout_variation_idx_delta_map);
213
0
      copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xPlaDevice);
214
0
    }
215
0
216
0
    if (format & yPlaDevice)
217
0
    {
218
0
      add_delta_to_value (y_placement, base, values, layout_variation_idx_delta_map);
219
0
      copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yPlaDevice);
220
0
    }
221
0
222
0
    if (format & xAdvDevice)
223
0
    {
224
0
      add_delta_to_value (x_adv, base, values, layout_variation_idx_delta_map);
225
0
      copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xAdvDevice);
226
0
    }
227
0
228
0
    if (format & yAdvDevice)
229
0
    {
230
0
      add_delta_to_value (y_adv, base, values, layout_variation_idx_delta_map);
231
0
      copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yAdvDevice);
232
0
    }
233
0
  }
234
235
  HBINT16* copy_value (hb_serialize_context_t *c,
236
                       unsigned int new_format,
237
                       Flags flag,
238
                       Value value) const
239
0
  {
240
0
    // Filter by new format.
241
0
    if (!(new_format & flag)) return nullptr;
242
0
    return reinterpret_cast<HBINT16 *> (c->copy (value));
243
0
  }
244
245
  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
246
                                  const ValueBase *base,
247
                                  const hb_array_t<const Value>& values) const
248
0
  {
249
0
    unsigned format = *this;
250
0
    unsigned i = 0;
251
0
    if (format & xPlacement) i++;
252
0
    if (format & yPlacement) i++;
253
0
    if (format & xAdvance) i++;
254
0
    if (format & yAdvance) i++;
255
0
    if (format & xPlaDevice)
256
0
    {
257
0
      (base + get_device (&(values[i]))).collect_variation_indices (c);
258
0
      i++;
259
0
    }
260
0
261
0
    if (format & ValueFormat::yPlaDevice)
262
0
    {
263
0
      (base + get_device (&(values[i]))).collect_variation_indices (c);
264
0
      i++;
265
0
    }
266
0
267
0
    if (format & ValueFormat::xAdvDevice)
268
0
    {
269
0
      (base + get_device (&(values[i]))).collect_variation_indices (c);
270
0
      i++;
271
0
    }
272
0
273
0
    if (format & ValueFormat::yAdvDevice)
274
0
    {
275
0
      (base + get_device (&(values[i]))).collect_variation_indices (c);
276
0
      i++;
277
0
    }
278
0
  }
279
280
  private:
281
  bool sanitize_value_devices (hb_sanitize_context_t *c, const ValueBase *base, const Value *values) const
282
0
  {
283
0
    unsigned int format = *this;
284
285
0
    if (format & xPlacement) values++;
286
0
    if (format & yPlacement) values++;
287
0
    if (format & xAdvance)   values++;
288
0
    if (format & yAdvance)   values++;
289
290
0
    if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
291
0
    if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
292
0
    if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
293
0
    if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
294
295
0
    return true;
296
0
  }
297
298
  static inline Offset16To<Device, ValueBase>& get_device (Value* value)
299
0
  {
300
0
    return *static_cast<Offset16To<Device, ValueBase> *> (value);
301
0
  }
302
  static inline const Offset16To<Device, ValueBase>& get_device (const Value* value)
303
0
  {
304
0
    return *static_cast<const Offset16To<Device, ValueBase> *> (value);
305
0
  }
306
  static inline const Device& get_device (const Value* value,
307
            bool *worked,
308
            const ValueBase *base,
309
            hb_sanitize_context_t &c)
310
0
  {
311
0
    if (worked) *worked |= bool (*value);
312
0
    auto &offset = *static_cast<const Offset16To<Device> *> (value);
313
314
0
    if (unlikely (!offset.sanitize (&c, base)))
315
0
      return Null(Device);
316
0
    hb_barrier ();
317
318
0
    return base + offset;
319
0
  }
320
321
  void add_delta_to_value (HBINT16 *value,
322
                           const ValueBase *base,
323
                           const Value *src_value,
324
                           const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const
325
0
  {
326
0
    if (!value) return;
327
0
    unsigned varidx = (base + get_device (src_value)).get_variation_index ();
328
0
    hb_pair_t<unsigned, int> *varidx_delta;
329
0
    if (!layout_variation_idx_delta_map->has (varidx, &varidx_delta)) return;
330
0
331
0
    *value += hb_second (*varidx_delta);
332
0
  }
333
334
  bool copy_device (hb_serialize_context_t *c,
335
                    const ValueBase *base,
336
                    const Value *src_value,
337
                    const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map,
338
                    unsigned int new_format, Flags flag) const
339
0
  {
340
0
    // Filter by new format.
341
0
    if (!(new_format & flag)) return true;
342
0
343
0
    Value       *dst_value = c->copy (*src_value);
344
0
345
0
    if (!dst_value) return false;
346
0
    if (*dst_value == 0) return true;
347
0
348
0
    *dst_value = 0;
349
0
    c->push ();
350
0
    if ((base + get_device (src_value)).copy (c, layout_variation_idx_delta_map))
351
0
    {
352
0
      c->add_link (*dst_value, c->pop_pack ());
353
0
      return true;
354
0
    }
355
0
    else
356
0
    {
357
0
      c->pop_discard ();
358
0
      return false;
359
0
    }
360
0
  }
361
362
  static inline const HBINT16& get_short (const Value* value, bool *worked=nullptr)
363
0
  {
364
0
    if (worked) *worked |= bool (*value);
365
0
    return *reinterpret_cast<const HBINT16 *> (value);
366
0
  }
367
368
  public:
369
370
  bool has_device () const
371
0
  {
372
0
    unsigned int format = *this;
373
0
    return (format & devices) != 0;
374
0
  }
375
376
  bool sanitize_value (hb_sanitize_context_t *c, const ValueBase *base, const Value *values) const
377
0
  {
378
0
    TRACE_SANITIZE (this);
379
380
0
    if (unlikely (!c->check_range (values, get_size ()))) return_trace (false);
381
382
0
    if (c->lazy_some_gpos)
383
0
      return_trace (true);
384
385
0
    return_trace (!has_device () || sanitize_value_devices (c, base, values));
386
0
  }
387
388
  bool sanitize_values (hb_sanitize_context_t *c, const ValueBase *base, const Value *values, unsigned int count) const
389
0
  {
390
0
    TRACE_SANITIZE (this);
391
0
    unsigned size = get_size ();
392
393
0
    if (!c->check_range (values, count, size)) return_trace (false);
394
395
0
    if (c->lazy_some_gpos)
396
0
      return_trace (true);
397
398
0
    hb_barrier ();
399
0
    return_trace (sanitize_values_stride_unsafe (c, base, values, count, size));
400
0
  }
401
402
  /* Just sanitize referenced Device tables.  Doesn't check the values themselves. */
403
  bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const ValueBase *base, const Value *values, unsigned int count, unsigned int stride) const
404
0
  {
405
0
    TRACE_SANITIZE (this);
406
407
0
    if (!has_device ()) return_trace (true);
408
409
0
    for (unsigned int i = 0; i < count; i++) {
410
0
      if (!sanitize_value_devices (c, base, values))
411
0
        return_trace (false);
412
0
      values = &StructAtOffset<const Value> (values, stride);
413
0
    }
414
415
0
    return_trace (true);
416
0
  }
417
418
 private:
419
420
  void should_drop (Value value, Flags flag, unsigned int* format) const
421
0
  {
422
0
    if (value) return;
423
0
    *format = *format & ~flag;
424
0
  }
425
426
  void update_var_flag (const Value* value, Flags flag,
427
                        unsigned int* format, const ValueBase *base,
428
                        const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const
429
0
  {
430
0
    if (*value)
431
0
    {
432
0
      unsigned varidx = (base + get_device (value)).get_variation_index ();
433
0
      hb_pair_t<unsigned, int> *varidx_delta;
434
0
      if (varidx_delta_map->has (varidx, &varidx_delta) &&
435
0
          varidx_delta->first != HB_OT_LAYOUT_NO_VARIATIONS_INDEX)
436
0
        return;
437
0
    }
438
0
    *format = *format & ~flag;
439
0
  }
440
};
441
442
}
443
}
444
}
445
446
#endif  // #ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH