Coverage Report

Created: 2026-02-26 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/OT/Var/VARC/VARC.cc
Line
Count
Source
1
#include "VARC.hh"
2
3
#ifndef HB_NO_VAR_COMPOSITES
4
5
#include "../../../hb-draw.hh"
6
#include "../../../hb-ot-layout-common.hh"
7
#include "../../../hb-ot-layout-gdef-table.hh"
8
9
namespace OT {
10
11
//namespace Var {
12
13
14
#ifndef HB_NO_DRAW
15
16
struct hb_transforming_pen_context_t
17
{
18
  hb_transform_t<> transform;
19
  hb_draw_funcs_t *dfuncs;
20
  void *data;
21
  hb_draw_state_t *st;
22
};
23
24
static void
25
hb_transforming_pen_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
26
           void *data,
27
           hb_draw_state_t *st,
28
           float to_x, float to_y,
29
           void *user_data HB_UNUSED)
30
0
{
31
0
  hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
32
33
0
  c->transform.transform_point (to_x, to_y);
34
35
0
  c->dfuncs->move_to (c->data, *c->st, to_x, to_y);
36
0
}
37
38
static void
39
hb_transforming_pen_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
40
           void *data,
41
           hb_draw_state_t *st,
42
           float to_x, float to_y,
43
           void *user_data HB_UNUSED)
44
0
{
45
0
  hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
46
47
0
  c->transform.transform_point (to_x, to_y);
48
49
0
  c->dfuncs->line_to (c->data, *c->st, to_x, to_y);
50
0
}
51
52
static void
53
hb_transforming_pen_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
54
          void *data,
55
          hb_draw_state_t *st,
56
          float control_x, float control_y,
57
          float to_x, float to_y,
58
          void *user_data HB_UNUSED)
59
0
{
60
0
  hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
61
62
0
  c->transform.transform_point (control_x, control_y);
63
0
  c->transform.transform_point (to_x, to_y);
64
65
0
  c->dfuncs->quadratic_to (c->data, *c->st, control_x, control_y, to_x, to_y);
66
0
}
67
68
static void
69
hb_transforming_pen_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
70
            void *data,
71
            hb_draw_state_t *st,
72
            float control1_x, float control1_y,
73
            float control2_x, float control2_y,
74
            float to_x, float to_y,
75
            void *user_data HB_UNUSED)
76
0
{
77
0
  hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
78
79
0
  c->transform.transform_point (control1_x, control1_y);
80
0
  c->transform.transform_point (control2_x, control2_y);
81
0
  c->transform.transform_point (to_x, to_y);
82
83
0
  c->dfuncs->cubic_to (c->data, *c->st, control1_x, control1_y, control2_x, control2_y, to_x, to_y);
84
0
}
85
86
static void
87
hb_transforming_pen_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED,
88
        void *data,
89
        hb_draw_state_t *st,
90
        void *user_data HB_UNUSED)
91
0
{
92
0
  hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
93
94
0
  c->dfuncs->close_path (c->data, *c->st);
95
0
}
96
97
static inline void free_static_transforming_pen_funcs ();
98
99
static struct hb_transforming_pen_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t<hb_transforming_pen_funcs_lazy_loader_t>
100
{
101
  static hb_draw_funcs_t *create ()
102
0
  {
103
0
    hb_draw_funcs_t *funcs = hb_draw_funcs_create ();
104
105
0
    hb_draw_funcs_set_move_to_func (funcs, hb_transforming_pen_move_to, nullptr, nullptr);
106
0
    hb_draw_funcs_set_line_to_func (funcs, hb_transforming_pen_line_to, nullptr, nullptr);
107
0
    hb_draw_funcs_set_quadratic_to_func (funcs, hb_transforming_pen_quadratic_to, nullptr, nullptr);
108
0
    hb_draw_funcs_set_cubic_to_func (funcs, hb_transforming_pen_cubic_to, nullptr, nullptr);
109
0
    hb_draw_funcs_set_close_path_func (funcs, hb_transforming_pen_close_path, nullptr, nullptr);
110
111
0
    hb_draw_funcs_make_immutable (funcs);
112
113
0
    hb_atexit (free_static_transforming_pen_funcs);
114
115
0
    return funcs;
116
0
  }
117
} static_transforming_pen_funcs;
118
119
static inline
120
void free_static_transforming_pen_funcs ()
121
0
{
122
0
  static_transforming_pen_funcs.free_instance ();
123
0
}
124
125
static hb_draw_funcs_t *
126
hb_transforming_pen_get_funcs ()
127
0
{
128
0
  return static_transforming_pen_funcs.get_unconst ();
129
0
}
130
131
hb_ubytes_t
132
VarComponent::get_path_at (const hb_varc_context_t &c,
133
         hb_codepoint_t parent_gid,
134
         hb_array_t<const int> coords,
135
         hb_transform_t<> total_transform,
136
         hb_ubytes_t total_record,
137
         hb_scalar_cache_t *cache) const
138
0
{
139
0
  const unsigned char *end = total_record.arrayZ + total_record.length;
140
0
  const unsigned char *record = total_record.arrayZ;
141
142
0
  auto &VARC = *c.font->face->table.VARC->table;
143
0
  auto &varStore = &VARC+VARC.varStore;
144
145
0
#define READ_UINT32VAR(name) \
146
0
  HB_STMT_START { \
147
0
    if (unlikely (unsigned (end - record) < HBUINT32VAR::min_size)) return hb_ubytes_t (); \
148
0
    hb_barrier (); \
149
0
    auto &varint = * (const HBUINT32VAR *) record; \
150
0
    unsigned size = varint.get_size (); \
151
0
    if (unlikely (unsigned (end - record) < size)) return hb_ubytes_t (); \
152
0
    name = (uint32_t) varint; \
153
0
    record += size; \
154
0
  } HB_STMT_END
155
156
0
  uint32_t flags;
157
0
  READ_UINT32VAR (flags);
158
159
  // gid
160
161
0
  hb_codepoint_t gid = 0;
162
0
  if (flags & (unsigned) flags_t::GID_IS_24BIT)
163
0
  {
164
0
    if (unlikely (unsigned (end - record) < HBGlyphID24::static_size))
165
0
      return hb_ubytes_t ();
166
0
    hb_barrier ();
167
0
    gid = * (const HBGlyphID24 *) record;
168
0
    record += HBGlyphID24::static_size;
169
0
  }
170
0
  else
171
0
  {
172
0
    if (unlikely (unsigned (end - record) < HBGlyphID16::static_size))
173
0
      return hb_ubytes_t ();
174
0
    hb_barrier ();
175
0
    gid = * (const HBGlyphID16 *) record;
176
0
    record += HBGlyphID16::static_size;
177
0
  }
178
179
  // Condition
180
0
  bool show = true;
181
0
  if (flags & (unsigned) flags_t::HAVE_CONDITION)
182
0
  {
183
0
    unsigned conditionIndex;
184
0
    READ_UINT32VAR (conditionIndex);
185
0
    const auto &condition = (&VARC+VARC.conditionList)[conditionIndex];
186
0
    auto instancer = MultiItemVarStoreInstancer(&varStore, nullptr, coords, cache);
187
0
    show = condition.evaluate (coords.arrayZ, coords.length, &instancer);
188
0
  }
189
190
  // Axis values
191
192
0
  auto &axisIndices = c.scratch.axisIndices;
193
0
  axisIndices.clear ();
194
0
  auto &axisValues = c.scratch.axisValues;
195
0
  axisValues.clear ();
196
0
  if (flags & (unsigned) flags_t::HAVE_AXES)
197
0
  {
198
0
    unsigned axisIndicesIndex;
199
0
    READ_UINT32VAR (axisIndicesIndex);
200
0
    axisIndices.extend ((&VARC+VARC.axisIndicesList)[axisIndicesIndex]);
201
0
    axisValues.resize (axisIndices.length);
202
0
    const HBUINT8 *p = (const HBUINT8 *) record;
203
0
    TupleValues::decompile (p, axisValues, (const HBUINT8 *) end);
204
0
    record = (const unsigned char *) p;
205
0
  }
206
207
  // Apply variations if any
208
0
  if (flags & (unsigned) flags_t::AXIS_VALUES_HAVE_VARIATION)
209
0
  {
210
0
    uint32_t axisValuesVarIdx;
211
0
    READ_UINT32VAR (axisValuesVarIdx);
212
0
    if (show && coords && !axisValues.in_error ())
213
0
      varStore.get_delta (axisValuesVarIdx, coords, axisValues.as_array (), cache);
214
0
  }
215
216
0
  auto component_coords = coords;
217
  /* Copying coords is expensive; so we have put an arbitrary
218
   * limit on the max number of coords for now. */
219
0
  if ((flags & (unsigned) flags_t::RESET_UNSPECIFIED_AXES) ||
220
0
      coords.length > HB_VAR_COMPOSITE_MAX_AXES)
221
0
    component_coords = hb_array (c.font->coords, c.font->num_coords);
222
223
  // Transform
224
225
0
  uint32_t transformVarIdx = VarIdx::NO_VARIATION;
226
0
  if (flags & (unsigned) flags_t::TRANSFORM_HAS_VARIATION)
227
0
    READ_UINT32VAR (transformVarIdx);
228
229
0
#define PROCESS_TRANSFORM_COMPONENTS \
230
0
  HB_STMT_START { \
231
0
  PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TRANSLATE_X, translateX); \
232
0
  PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TRANSLATE_Y, translateY); \
233
0
  PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_ROTATION, rotation); \
234
0
  PROCESS_TRANSFORM_COMPONENT (10, F6DOT10, HAVE_SCALE_X, scaleX); \
235
0
  PROCESS_TRANSFORM_COMPONENT (10, F6DOT10, HAVE_SCALE_Y, scaleY); \
236
0
  PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_SKEW_X, skewX); \
237
0
  PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_SKEW_Y, skewY); \
238
0
  PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TCENTER_X, tCenterX); \
239
0
  PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TCENTER_Y, tCenterY); \
240
0
  } HB_STMT_END
241
242
0
  hb_transform_decomposed_t<> transform;
243
244
  // Read transform components
245
0
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
246
0
  if (flags & (unsigned) flags_t::flag) \
247
0
  { \
248
0
    static_assert (type::static_size == HBINT16::static_size, ""); \
249
0
    if (unlikely (unsigned (end - record) < HBINT16::static_size)) \
250
0
      return hb_ubytes_t (); \
251
0
    hb_barrier (); \
252
0
    transform.name = * (const HBINT16 *) record; \
253
0
    record += HBINT16::static_size; \
254
0
  }
255
0
  PROCESS_TRANSFORM_COMPONENTS;
256
0
#undef PROCESS_TRANSFORM_COMPONENT
257
258
  // Read reserved records
259
0
  unsigned i = flags & (unsigned) flags_t::RESERVED_MASK;
260
0
  while (i)
261
0
  {
262
0
    HB_UNUSED uint32_t discard;
263
0
    READ_UINT32VAR (discard);
264
0
    i &= i - 1;
265
0
  }
266
267
  /* Parsing is over now. */
268
269
0
  if (show)
270
0
  {
271
    // Only use coord_setter if there's actually any axis overrides.
272
0
    coord_setter_t coord_setter (axisIndices ? component_coords : hb_array<int> ());
273
0
    for (unsigned i = 0; i < axisIndices.length; i++)
274
0
      coord_setter[axisIndices[i]] = roundf (axisValues[i]);
275
0
    if (axisIndices)
276
0
      component_coords = coord_setter.get_coords ();
277
278
    // Apply transform variations if any
279
0
    if (transformVarIdx != VarIdx::NO_VARIATION && coords)
280
0
    {
281
0
      float transformValues[9];
282
0
      unsigned numTransformValues = 0;
283
0
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
284
0
    if (flags & (unsigned) flags_t::flag) \
285
0
      transformValues[numTransformValues++] = transform.name;
286
0
      PROCESS_TRANSFORM_COMPONENTS;
287
0
#undef PROCESS_TRANSFORM_COMPONENT
288
0
      varStore.get_delta (transformVarIdx, coords, hb_array (transformValues, numTransformValues), cache);
289
0
      numTransformValues = 0;
290
0
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
291
0
    if (flags & (unsigned) flags_t::flag) \
292
0
      transform.name = transformValues[numTransformValues++];
293
0
      PROCESS_TRANSFORM_COMPONENTS;
294
0
#undef PROCESS_TRANSFORM_COMPONENT
295
0
    }
296
297
    // Divide them by their divisors
298
0
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
299
0
    if (shift && (flags & (unsigned) flags_t::flag)) \
300
0
       transform.name *= 1.f / (1 << shift);
301
0
    PROCESS_TRANSFORM_COMPONENTS;
302
0
#undef PROCESS_TRANSFORM_COMPONENT
303
304
0
    if (!(flags & (unsigned) flags_t::HAVE_SCALE_Y))
305
0
      transform.scaleY = transform.scaleX;
306
307
0
    transform.rotation *= HB_PI;
308
0
    transform.skewX *= HB_PI;
309
0
    transform.skewY *= HB_PI;
310
311
0
    total_transform.transform (transform.to_transform ());
312
313
0
    bool same_coords = component_coords.length == coords.length &&
314
0
           component_coords.arrayZ == coords.arrayZ;
315
316
0
    c.depth_left--;
317
0
    VARC.get_path_at (c, gid,
318
0
          component_coords, total_transform,
319
0
          parent_gid,
320
0
          same_coords ? cache : nullptr);
321
0
    c.depth_left++;
322
0
  }
323
324
0
#undef PROCESS_TRANSFORM_COMPONENTS
325
0
#undef READ_UINT32VAR
326
327
0
  return hb_ubytes_t (record, end - record);
328
0
}
329
330
bool
331
VARC::get_path_at (const hb_varc_context_t &c,
332
       hb_codepoint_t glyph,
333
       hb_array_t<const int> coords,
334
       hb_transform_t<> transform,
335
       hb_codepoint_t parent_glyph,
336
       hb_scalar_cache_t *parent_cache) const
337
0
{
338
  // Don't recurse on the same glyph.
339
0
  unsigned idx = glyph == parent_glyph ?
340
0
     NOT_COVERED :
341
0
     (this+coverage).get_coverage (glyph);
342
0
  if (idx == NOT_COVERED)
343
0
  {
344
0
    if (c.draw_session)
345
0
    {
346
0
      hb_transform_t<> leaf_transform = transform;
347
0
      leaf_transform.x0 *= c.font->x_multf;
348
0
      leaf_transform.y0 *= c.font->y_multf;
349
350
      // Build a transforming pen to apply the transform.
351
0
      hb_draw_funcs_t *transformer_funcs = hb_transforming_pen_get_funcs ();
352
0
      hb_transforming_pen_context_t context {leaf_transform,
353
0
               c.draw_session->funcs,
354
0
               c.draw_session->draw_data,
355
0
               &c.draw_session->st};
356
0
      hb_draw_session_t transformer_session {transformer_funcs, &context};
357
0
      hb_draw_session_t &shape_draw_session = leaf_transform.is_identity () ? *c.draw_session : transformer_session;
358
359
0
      if (c.font->face->table.glyf->get_path_at (c.font, glyph, shape_draw_session, coords, c.scratch.glyf_scratch)) return true;
360
0
#ifndef HB_NO_CFF
361
0
      if (c.font->face->table.cff2->get_path_at (c.font, glyph, shape_draw_session, coords)) return true;
362
0
      if (c.font->face->table.cff1->get_path (c.font, glyph, shape_draw_session)) return true; // Doesn't have variations
363
0
#endif
364
0
      return false;
365
0
    }
366
0
    else if (c.extents)
367
0
    {
368
0
      hb_glyph_extents_t glyph_extents;
369
0
      if (!c.font->face->table.glyf->get_extents_at (c.font, glyph, &glyph_extents, coords))
370
0
#ifndef HB_NO_CFF
371
0
      if (!c.font->face->table.cff2->get_extents_at (c.font, glyph, &glyph_extents, coords))
372
0
      if (!c.font->face->table.cff1->get_extents (c.font, glyph, &glyph_extents)) // Doesn't have variations
373
0
#endif
374
0
  return false;
375
376
0
      hb_extents_t<> comp_extents (glyph_extents);
377
0
      hb_transform_t<> leaf_transform = transform;
378
0
      leaf_transform.x0 *= c.font->x_multf;
379
0
      leaf_transform.y0 *= c.font->y_multf;
380
0
      leaf_transform.transform_extents (comp_extents);
381
0
      c.extents->union_ (comp_extents);
382
0
    }
383
0
    return true;
384
0
  }
385
386
0
  if (c.depth_left <= 0)
387
0
    return true;
388
389
0
  if (c.edges_left <= 0)
390
0
    return true;
391
0
  (c.edges_left)--;
392
393
0
  hb_decycler_node_t node (c.decycler);
394
0
  if (unlikely (!node.visit (glyph)))
395
0
    return true;
396
397
0
  hb_ubytes_t record = (this+glyphRecords)[idx];
398
399
0
  hb_scalar_cache_t static_cache;
400
0
  hb_scalar_cache_t *cache = parent_cache ?
401
0
          parent_cache :
402
0
          (this+varStore).create_cache (&static_cache);
403
404
0
  VarCompositeGlyph::get_path_at (c,
405
0
          glyph,
406
0
          coords, transform,
407
0
          record,
408
0
          cache);
409
410
0
  if (cache != parent_cache)
411
0
    (this+varStore).destroy_cache (cache, &static_cache);
412
413
0
  return true;
414
0
}
415
416
#endif
417
418
//} // namespace Var
419
} // namespace OT
420
421
#endif