Coverage Report

Created: 2026-10-30 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/OT/Var/VARC/VARC.cc
Line
Count
Source
1
#ifndef HB_OT_VAR_VARC_CC
2
#define HB_OT_VAR_VARC_CC
3
#ifdef HB_OT_VAR_VARC_CC /* Pacify -Wunused-macros. */
4
5
#include "VARC.hh"
6
7
#ifndef HB_NO_VAR_COMPOSITES
8
9
#include "../../../hb-draw.hh"
10
#include "../../../hb-depend-data.hh"
11
#include "../../../hb-ot-layout-common.hh"
12
#include "../../../hb-ot-layout-gdef-table.hh"
13
14
namespace OT {
15
16
//namespace Var {
17
18
#define VARC_PROCESS_TRANSFORM_COMPONENTS \
19
0
  HB_STMT_START { \
20
0
  PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TRANSLATE_X, translateX); \
21
0
  PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TRANSLATE_Y, translateY); \
22
0
  PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_ROTATION, rotation); \
23
0
  PROCESS_TRANSFORM_COMPONENT (10, F6DOT10, HAVE_SCALE_X, scaleX); \
24
0
  PROCESS_TRANSFORM_COMPONENT (10, F6DOT10, HAVE_SCALE_Y, scaleY); \
25
0
  PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_SKEW_X, skewX); \
26
0
  PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_SKEW_Y, skewY); \
27
0
  PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TCENTER_X, tCenterX); \
28
0
  PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TCENTER_Y, tCenterY); \
29
0
  } HB_STMT_END
30
31
bool
32
VarComponent::decompile_record (const VARC &varc,
33
        hb_ubytes_t total_record,
34
        hb_vector_t<unsigned> *axis_indices,
35
        hb_vector_t<double> *axis_values,
36
        record_t *decoded,
37
        int64_t *budget)
38
0
{
39
0
  const unsigned char *start = total_record.arrayZ;
40
0
  const unsigned char *record = start;
41
0
  const unsigned char *end = start + total_record.length;
42
43
0
  if (axis_indices) axis_indices->clear ();
44
0
  if (axis_values) axis_values->clear ();
45
46
0
#define READ_UINT32VAR(name) \
47
0
  HB_STMT_START { \
48
0
    if (unlikely (unsigned (end - record) < HBUINT32VAR::min_size)) return false; \
49
0
    hb_barrier (); \
50
0
    auto &varint = * (const HBUINT32VAR *) record; \
51
0
    unsigned size = varint.get_size (); \
52
0
    if (unlikely (unsigned (end - record) < size)) return false; \
53
0
    name = (uint32_t) varint; \
54
0
    record += size; \
55
0
  } HB_STMT_END
56
0
#define READ_UINT32VAR_FIELD(name, field) \
57
0
  HB_STMT_START { \
58
0
    decoded->field##_offset = record - start; \
59
0
    READ_UINT32VAR (name); \
60
0
    decoded->field##_size = record - start - decoded->field##_offset; \
61
0
  } HB_STMT_END
62
63
0
  READ_UINT32VAR (decoded->flags);
64
65
0
  decoded->gid_offset = record - start;
66
0
  if (decoded->flags & (unsigned) flags_t::GID_IS_24BIT)
67
0
  {
68
0
    decoded->gid_size = HBGlyphID24::static_size;
69
0
    if (unlikely (unsigned (end - record) < HBGlyphID24::static_size)) return false;
70
0
    hb_barrier ();
71
0
    decoded->gid = * (const HBGlyphID24 *) record;
72
0
    record += HBGlyphID24::static_size;
73
0
  }
74
0
  else
75
0
  {
76
0
    decoded->gid_size = HBGlyphID16::static_size;
77
0
    if (unlikely (unsigned (end - record) < HBGlyphID16::static_size)) return false;
78
0
    hb_barrier ();
79
0
    decoded->gid = * (const HBGlyphID16 *) record;
80
0
    record += HBGlyphID16::static_size;
81
0
  }
82
83
0
  if (decoded->flags & (unsigned) flags_t::HAVE_CONDITION)
84
0
    READ_UINT32VAR_FIELD (decoded->condition_index, condition);
85
86
0
  if (decoded->flags & (unsigned) flags_t::HAVE_AXES)
87
0
  {
88
0
    READ_UINT32VAR_FIELD (decoded->axis_indices_index, axis_indices);
89
0
    unsigned axis_count;
90
0
    if (axis_indices)
91
0
    {
92
0
      axis_indices->extend ((&varc+varc.axisIndicesList)[decoded->axis_indices_index]);
93
0
      if (unlikely (axis_indices->in_error ())) return false;
94
0
      axis_count = axis_indices->length;
95
0
    }
96
0
    else
97
0
      axis_count = hb_len ((&varc+varc.axisIndicesList)[decoded->axis_indices_index]);
98
0
    if (unlikely (budget && axis_count &&
99
0
      !hb_budget_spend (*budget, HB_BUDGET_1, axis_count)))
100
0
      return false;
101
0
    if (axis_values)
102
0
    {
103
0
      if (unlikely (!axis_values->resize (axis_count))) return false;
104
0
      const HBUINT8 *p = (const HBUINT8 *) record;
105
0
      if (unlikely (!TupleValues::decompile (p, *axis_values,
106
0
              (const HBUINT8 *) end)))
107
0
  return false;
108
0
      record = (const unsigned char *) p;
109
0
    }
110
0
    else if (unlikely (!TupleValues::skip (record, end, axis_count)))
111
0
      return false;
112
0
  }
113
114
0
  decoded->axis_values_var_idx = VarIdx::NO_VARIATION;
115
0
  if (decoded->flags & (unsigned) flags_t::AXIS_VALUES_HAVE_VARIATION)
116
0
    READ_UINT32VAR_FIELD (decoded->axis_values_var_idx, axis_values_var);
117
118
0
  decoded->transform_var_idx = VarIdx::NO_VARIATION;
119
0
  if (decoded->flags & (unsigned) flags_t::TRANSFORM_HAS_VARIATION)
120
0
    READ_UINT32VAR_FIELD (decoded->transform_var_idx, transform_var);
121
122
0
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
123
0
  if (decoded->flags & (unsigned) flags_t::flag) \
124
0
  { \
125
0
    static_assert (type::static_size == HBINT16::static_size, ""); \
126
0
    if (unlikely (unsigned (end - record) < HBINT16::static_size)) return false; \
127
0
    hb_barrier (); \
128
0
    decoded->transform.name = * (const HBINT16 *) record; \
129
0
    record += HBINT16::static_size; \
130
0
  }
131
0
  VARC_PROCESS_TRANSFORM_COMPONENTS;
132
0
#undef PROCESS_TRANSFORM_COMPONENT
133
134
0
  unsigned reserved = decoded->flags & (unsigned) flags_t::RESERVED_MASK;
135
0
  while (reserved)
136
0
  {
137
0
    uint32_t discard HB_UNUSED;
138
0
    READ_UINT32VAR (discard);
139
0
    reserved &= reserved - 1;
140
0
  }
141
142
0
  decoded->size = record - start;
143
0
#undef READ_UINT32VAR_FIELD
144
0
#undef READ_UINT32VAR
145
0
  return true;
146
0
}
147
148
bool
149
VARC::closure_glyphs (hb_set_t *glyphset) const
150
0
{
151
0
  if (unlikely (glyphset->in_error ()))
152
0
    return false;
153
154
0
  hb_set_t pending = *glyphset;
155
0
  if (unlikely (pending.in_error ()))
156
0
    return false;
157
158
0
  while (pending)
159
0
  {
160
0
    hb_codepoint_t gid = pending.get_min ();
161
0
    pending.del (gid);
162
163
0
    unsigned index = (this+coverage).get_coverage (gid);
164
0
    if (index == NOT_COVERED) continue;
165
166
0
    hb_ubytes_t record = (this+glyphRecords)[index];
167
0
    while (record)
168
0
    {
169
0
      VarComponent::record_t component;
170
0
      if (unlikely (!VarComponent::decompile_record (*this, record,
171
0
                 nullptr, nullptr, &component)))
172
0
  return false;
173
0
      if (!glyphset->has (component.gid))
174
0
      {
175
0
  glyphset->add (component.gid);
176
0
  if (unlikely (glyphset->in_error ()))
177
0
    return false;
178
0
  pending.add (component.gid);
179
0
  if (unlikely (pending.in_error ()))
180
0
    return false;
181
0
      }
182
0
      record = record.sub_array (component.size);
183
0
    }
184
0
  }
185
0
  return true;
186
0
}
187
188
void
189
VARC::depend (hb_depend_data_builder_t *depend_data) const
190
0
{
191
0
  const CFF2Index &records = this+glyphRecords;
192
0
  for (auto _ : + hb_zip (this+coverage,
193
0
        hb_range ((unsigned) records.count)))
194
0
  {
195
0
    hb_codepoint_t gid = _.first;
196
0
    hb_ubytes_t record = records[_.second];
197
0
    while (record)
198
0
    {
199
0
  VarComponent::record_t component;
200
0
  if (unlikely (!VarComponent::decompile_record (*this, record,
201
0
                   nullptr, nullptr, &component)))
202
0
  {
203
0
    depend_data->fail ();
204
0
    return;
205
0
  }
206
0
      depend_data->add_depend (gid, tableTag, component.gid);
207
0
      record = record.sub_array (component.size);
208
0
    }
209
0
  }
210
0
}
211
212
213
#ifndef HB_NO_DRAW
214
215
struct hb_transforming_pen_context_t
216
{
217
  hb_transform_t<> transform;
218
  hb_draw_funcs_t *dfuncs;
219
  void *data;
220
  hb_draw_state_t *st;
221
  int64_t *budget_remaining;
222
};
223
224
static void
225
hb_transforming_pen_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
226
           void *data,
227
           hb_draw_state_t *st,
228
           float to_x, float to_y,
229
           void *user_data HB_UNUSED)
230
0
{
231
0
  hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
232
233
0
  c->transform.transform_point (to_x, to_y);
234
235
0
  c->dfuncs->move_to (c->data, *c->st, to_x, to_y);
236
0
}
237
238
static void
239
hb_transforming_pen_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
240
           void *data,
241
           hb_draw_state_t *st,
242
           float to_x, float to_y,
243
           void *user_data HB_UNUSED)
244
0
{
245
0
  hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
246
247
0
  c->transform.transform_point (to_x, to_y);
248
249
0
  c->dfuncs->line_to (c->data, *c->st, to_x, to_y);
250
0
}
251
252
static void
253
hb_transforming_pen_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
254
          void *data,
255
          hb_draw_state_t *st,
256
          float control_x, float control_y,
257
          float to_x, float to_y,
258
          void *user_data HB_UNUSED)
259
0
{
260
0
  hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
261
262
0
  c->transform.transform_point (control_x, control_y);
263
0
  c->transform.transform_point (to_x, to_y);
264
265
0
  c->dfuncs->quadratic_to (c->data, *c->st, control_x, control_y, to_x, to_y);
266
0
}
267
268
static void
269
hb_transforming_pen_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
270
            void *data,
271
            hb_draw_state_t *st,
272
            float control1_x, float control1_y,
273
            float control2_x, float control2_y,
274
            float to_x, float to_y,
275
            void *user_data HB_UNUSED)
276
0
{
277
0
  hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
278
279
0
  c->transform.transform_point (control1_x, control1_y);
280
0
  c->transform.transform_point (control2_x, control2_y);
281
0
  c->transform.transform_point (to_x, to_y);
282
283
0
  c->dfuncs->cubic_to (c->data, *c->st, control1_x, control1_y, control2_x, control2_y, to_x, to_y);
284
0
}
285
286
static void
287
hb_transforming_pen_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED,
288
        void *data,
289
        hb_draw_state_t *st,
290
        void *user_data HB_UNUSED)
291
0
{
292
0
  hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
293
294
0
  c->dfuncs->close_path (c->data, *c->st);
295
0
}
296
297
static int64_t *
298
hb_transforming_pen_get_budget_remaining (hb_draw_funcs_t *, void *draw_data, void *)
299
0
{
300
0
  return ((hb_transforming_pen_context_t *) draw_data)->budget_remaining;
301
0
}
302
303
static inline void free_static_transforming_pen_funcs ();
304
305
static struct hb_transforming_pen_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t<hb_transforming_pen_funcs_lazy_loader_t>
306
{
307
  static hb_draw_funcs_t *create ()
308
0
  {
309
0
    hb_draw_funcs_t *funcs = hb_draw_funcs_create ();
310
311
0
    hb_draw_funcs_set_move_to_func (funcs, hb_transforming_pen_move_to, nullptr, nullptr);
312
0
    hb_draw_funcs_set_line_to_func (funcs, hb_transforming_pen_line_to, nullptr, nullptr);
313
0
    hb_draw_funcs_set_quadratic_to_func (funcs, hb_transforming_pen_quadratic_to, nullptr, nullptr);
314
0
    hb_draw_funcs_set_cubic_to_func (funcs, hb_transforming_pen_cubic_to, nullptr, nullptr);
315
0
    hb_draw_funcs_set_close_path_func (funcs, hb_transforming_pen_close_path, nullptr, nullptr);
316
0
    hb_draw_funcs_set_get_budget_remaining_func (funcs, hb_transforming_pen_get_budget_remaining, nullptr, nullptr);
317
318
0
    hb_draw_funcs_make_immutable (funcs);
319
320
0
    hb_atexit (free_static_transforming_pen_funcs);
321
322
0
    return funcs;
323
0
  }
324
} static_transforming_pen_funcs;
325
326
static inline
327
void free_static_transforming_pen_funcs ()
328
0
{
329
0
  static_transforming_pen_funcs.free_instance ();
330
0
}
331
332
static hb_draw_funcs_t *
333
hb_transforming_pen_get_funcs ()
334
0
{
335
0
  return static_transforming_pen_funcs.get_unconst ();
336
0
}
337
338
hb_ubytes_t
339
VarComponent::get_path_at (const hb_varc_context_t &c,
340
         hb_codepoint_t parent_gid,
341
         hb_array_t<const int> coords,
342
         hb_transform_t<> total_transform,
343
         hb_ubytes_t total_record,
344
         hb_scalar_cache_t *cache) const
345
0
{
346
0
  if (unlikely (!hb_budget_spend (c.budget, HB_BUDGET_4)))
347
0
    return hb_ubytes_t ();
348
349
0
  const unsigned char *end = total_record.arrayZ + total_record.length;
350
0
  auto &VARC = *c.font->face->table.VARC->table;
351
0
  auto &varStore = &VARC+VARC.varStore;
352
353
0
  auto &axisIndices = c.scratch.axisIndices;
354
0
  auto &axisValues = c.scratch.axisValues;
355
0
  record_t component;
356
0
  if (unlikely (!decompile_record (VARC, total_record,
357
0
           &axisIndices, &axisValues, &component,
358
0
           &c.budget)))
359
0
    return hb_ubytes_t ();
360
361
0
  uint32_t flags = component.flags;
362
0
  hb_codepoint_t gid = component.gid;
363
0
  const unsigned char *record = total_record.arrayZ + component.size;
364
365
  // Condition
366
0
  bool show = true;
367
0
  if (flags & (unsigned) flags_t::HAVE_CONDITION)
368
0
  {
369
0
    const auto &condition = (&VARC+VARC.conditionList)[component.condition_index];
370
0
    auto instancer = MultiItemVarStoreInstancer(&varStore, nullptr, coords, cache);
371
0
    show = condition.evaluate (coords.arrayZ, coords.length, &instancer);
372
0
  }
373
374
  // Axis values
375
376
  // Apply variations if any
377
0
  if ((flags & (unsigned) flags_t::AXIS_VALUES_HAVE_VARIATION) &&
378
0
      show && !axisValues.in_error ())
379
0
  {
380
0
    if (unlikely (!hb_budget_spend (c.budget, axisValues.length,
381
0
            coords.length)))
382
0
      return hb_ubytes_t ();
383
0
    varStore.get_delta (component.axis_values_var_idx, coords,
384
0
      axisValues.as_array (), cache);
385
0
  }
386
387
0
  auto component_coords = coords;
388
  /* Copying coords is expensive; so we have put an arbitrary
389
   * limit on the max number of coords for now. */
390
0
  if ((flags & (unsigned) flags_t::RESET_UNSPECIFIED_AXES) ||
391
0
      coords.length > HB_VAR_COMPOSITE_MAX_AXES)
392
0
    component_coords = hb_array (c.font->coords, c.font->num_coords);
393
394
0
  uint32_t transformVarIdx = component.transform_var_idx;
395
0
  hb_transform_decomposed_t<> transform = component.transform;
396
397
0
  if (show)
398
0
  {
399
    // Only use coord_setter if there's actually any axis overrides.
400
0
    if (unlikely (axisIndices &&
401
0
      !hb_budget_spend (c.budget, HB_BUDGET_1,
402
0
            component_coords.length)))
403
0
      return hb_ubytes_t ();
404
0
    coord_setter_t coord_setter (axisIndices ? component_coords : hb_array<int> ());
405
0
    for (unsigned i = 0; i < axisIndices.length; i++)
406
0
      coord_setter[axisIndices[i]] = hb_clamp_to<int> (round (axisValues[i]));
407
0
    if (axisIndices)
408
0
      component_coords = coord_setter.get_coords ();
409
410
    // Apply transform variations if any
411
0
    if (transformVarIdx != VarIdx::NO_VARIATION)
412
0
    {
413
0
      double transformValues[9];
414
0
      unsigned numTransformValues = 0;
415
0
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
416
0
    if (flags & (unsigned) flags_t::flag) \
417
0
      transformValues[numTransformValues++] = (double) transform.name;
418
0
      VARC_PROCESS_TRANSFORM_COMPONENTS;
419
0
#undef PROCESS_TRANSFORM_COMPONENT
420
0
      if (unlikely (!hb_budget_spend (c.budget, numTransformValues,
421
0
              coords.length)))
422
0
  return hb_ubytes_t ();
423
0
      varStore.get_delta (transformVarIdx, coords, hb_array (transformValues, numTransformValues), cache);
424
0
      numTransformValues = 0;
425
0
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
426
0
    if (flags & (unsigned) flags_t::flag) \
427
0
      transform.name = (float) transformValues[numTransformValues++];
428
0
      VARC_PROCESS_TRANSFORM_COMPONENTS;
429
0
#undef PROCESS_TRANSFORM_COMPONENT
430
0
    }
431
432
    // Divide them by their divisors
433
0
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
434
0
    if (shift && (flags & (unsigned) flags_t::flag)) \
435
0
       transform.name *= 1.f / (1 << shift);
436
0
    VARC_PROCESS_TRANSFORM_COMPONENTS;
437
0
#undef PROCESS_TRANSFORM_COMPONENT
438
439
0
    if (!(flags & (unsigned) flags_t::HAVE_SCALE_Y))
440
0
      transform.scaleY = transform.scaleX;
441
442
0
    transform.rotation *= HB_PI;
443
0
    transform.skewX *= HB_PI;
444
0
    transform.skewY *= HB_PI;
445
446
0
    total_transform.transform (transform.to_transform ());
447
448
0
    bool same_coords = component_coords.length == coords.length &&
449
0
           component_coords.arrayZ == coords.arrayZ;
450
451
0
    c.depth_left--;
452
0
    VARC.get_path_at (c, gid,
453
0
          component_coords, total_transform,
454
0
          parent_gid,
455
0
          same_coords ? cache : nullptr);
456
0
    c.depth_left++;
457
0
  }
458
459
0
  return hb_ubytes_t (record, end - record);
460
0
}
461
462
bool
463
VARC::get_path_at (const hb_varc_context_t &c,
464
       hb_codepoint_t glyph,
465
       hb_array_t<const int> coords,
466
       hb_transform_t<> transform,
467
       hb_codepoint_t parent_glyph,
468
       hb_scalar_cache_t *parent_cache) const
469
0
{
470
  // Don't recurse on the same glyph.
471
0
  unsigned idx = glyph == parent_glyph ?
472
0
     NOT_COVERED :
473
0
     (this+coverage).get_coverage (glyph);
474
0
  if (idx == NOT_COVERED)
475
0
  {
476
0
    if (c.draw_session)
477
0
    {
478
      /* Out of budget: draw nothing, but signal success so remaining
479
       * leaves are skipped instead of falling back per-glyph. */
480
0
      if (unlikely (!hb_budget_spend (c.budget, HB_BUDGET_1))) return true;
481
482
0
      hb_transform_t<> leaf_transform = transform;
483
0
      leaf_transform.x0 *= c.font->x_multf;
484
0
      leaf_transform.y0 *= c.font->y_multf;
485
486
      // Build a transforming pen to apply the transform.
487
0
      hb_draw_funcs_t *transformer_funcs = hb_transforming_pen_get_funcs ();
488
0
      hb_transforming_pen_context_t context {leaf_transform,
489
0
               c.draw_session->funcs,
490
0
               c.draw_session->draw_data,
491
0
               &c.draw_session->st,
492
0
               &c.budget};
493
0
      hb_draw_session_t transformer_session {transformer_funcs, &context};
494
0
      hb_draw_session_t &shape_draw_session = leaf_transform.is_identity () ? *c.draw_session : transformer_session;
495
496
0
      if (c.font->face->table.glyf->get_path_at (c.font, glyph, shape_draw_session, coords, c.scratch.glyf_scratch, nullptr, &shape_draw_session.get_budget ())) return true;
497
0
#ifndef HB_NO_CFF
498
0
      if (c.font->face->table.cff2->get_path_at (c.font, glyph, shape_draw_session, coords, &shape_draw_session.get_budget ())) return true;
499
0
      if (c.font->face->table.cff1->get_path (c.font, glyph, shape_draw_session, &shape_draw_session.get_budget ())) return true; // Doesn't have variations
500
0
#endif
501
0
      return false;
502
0
    }
503
0
    else if (c.extents)
504
0
    {
505
0
      if (unlikely (!hb_budget_spend (c.budget, HB_BUDGET_1))) return true;
506
507
0
      hb_glyph_extents_t glyph_extents;
508
0
      if (!c.font->face->table.glyf->get_extents_at (c.font, glyph, &glyph_extents, coords, &c.budget))
509
0
#ifndef HB_NO_CFF
510
0
      if (!c.font->face->table.cff2->get_extents_at (c.font, glyph, &glyph_extents, coords, &c.budget))
511
0
      if (!c.font->face->table.cff1->get_extents (c.font, glyph, &glyph_extents, &c.budget)) // Doesn't have variations
512
0
#endif
513
0
  return false;
514
515
0
      hb_extents_t<> comp_extents (glyph_extents);
516
0
      hb_transform_t<> leaf_transform = transform;
517
0
      leaf_transform.x0 *= c.font->x_multf;
518
0
      leaf_transform.y0 *= c.font->y_multf;
519
0
      leaf_transform.transform_extents (comp_extents);
520
0
      c.extents->union_ (comp_extents);
521
0
    }
522
0
    return true;
523
0
  }
524
525
0
  if (c.depth_left <= 0)
526
0
    return true;
527
528
0
  if (c.edges_left <= 0)
529
0
    return true;
530
0
  (c.edges_left)--;
531
532
0
  hb_decycler_node_t node (c.decycler);
533
0
  if (unlikely (!node.visit (glyph)))
534
0
    return true;
535
536
0
  hb_ubytes_t record = (this+glyphRecords)[idx];
537
538
0
  hb_scalar_cache_t static_cache;
539
0
  hb_scalar_cache_t *cache = parent_cache ?
540
0
          parent_cache :
541
0
          (this+varStore).create_cache (&static_cache);
542
543
0
  VarCompositeGlyph::get_path_at (c,
544
0
          glyph,
545
0
          coords, transform,
546
0
          record,
547
0
          cache);
548
549
0
  if (cache != parent_cache)
550
0
    (this+varStore).destroy_cache (cache, &static_cache);
551
552
0
  return true;
553
0
}
554
555
#endif
556
557
#undef VARC_PROCESS_TRANSFORM_COMPONENTS
558
559
//} // namespace Var
560
} // namespace OT
561
562
#endif
563
564
#endif /* HB_OT_VAR_VARC_CC (pacify clang) */
565
#endif /* HB_OT_VAR_VARC_CC */