Coverage Report

Created: 2026-08-31 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/OT/Var/VARC/VARC.hh
Line
Count
Source
1
#ifndef OT_VAR_VARC_VARC_HH
2
#define OT_VAR_VARC_VARC_HH
3
4
#include "../../../hb-decycler.hh"
5
#include "../../../hb-geometry.hh"
6
#include "../../../hb-ot-layout-common.hh"
7
#include "../../../hb-ot-glyf-table.hh"
8
#include "../../../hb-ot-cff2-table.hh"
9
#include "../../../hb-ot-cff1-table.hh"
10
11
#include "coord-setter.hh"
12
13
namespace OT {
14
15
//namespace Var {
16
17
/*
18
 * VARC -- Variable Composites
19
 * https://github.com/harfbuzz/boring-expansion-spec/blob/main/VARC.md
20
 */
21
22
#ifndef HB_NO_VAR_COMPOSITES
23
24
struct hb_varc_scratch_t
25
{
26
  hb_vector_t<unsigned> axisIndices;
27
  hb_vector_t<float> axisValues;
28
  hb_glyf_scratch_t glyf_scratch;
29
};
30
31
struct hb_varc_context_t
32
{
33
  hb_font_t *font;
34
  hb_draw_session_t *draw_session;
35
  hb_extents_t<> *extents;
36
  mutable hb_decycler_t decycler;
37
  mutable signed edges_left;
38
  mutable signed depth_left;
39
  /* Work budget shared by all leaf glyphs loaded from glyf/CFF/CFF2,
40
   * so their per-glyph work limits cannot multiply with our own
41
   * composite-graph limits. */
42
  mutable int64_t budget_left;
43
  hb_varc_scratch_t &scratch;
44
};
45
46
struct VarComponent
47
{
48
  enum class flags_t : uint32_t
49
  {
50
    RESET_UNSPECIFIED_AXES  = 1u << 0,
51
    HAVE_AXES     = 1u << 1,
52
    AXIS_VALUES_HAVE_VARIATION  = 1u << 2,
53
    TRANSFORM_HAS_VARIATION = 1u << 3,
54
    HAVE_TRANSLATE_X    = 1u << 4,
55
    HAVE_TRANSLATE_Y    = 1u << 5,
56
    HAVE_ROTATION   = 1u << 6,
57
    HAVE_CONDITION    = 1u << 7,
58
    HAVE_SCALE_X    = 1u << 8,
59
    HAVE_SCALE_Y    = 1u << 9,
60
    HAVE_TCENTER_X    = 1u << 10,
61
    HAVE_TCENTER_Y    = 1u << 11,
62
    GID_IS_24BIT    = 1u << 12,
63
    HAVE_SKEW_X     = 1u << 13,
64
    HAVE_SKEW_Y     = 1u << 14,
65
    RESERVED_MASK   = ~((1u << 15) - 1),
66
  };
67
68
  HB_INTERNAL hb_ubytes_t
69
  get_path_at (const hb_varc_context_t &c,
70
         hb_codepoint_t parent_gid,
71
         hb_array_t<const int> coords,
72
         hb_transform_t<> transform,
73
         hb_ubytes_t record,
74
         hb_scalar_cache_t *cache = nullptr) const;
75
};
76
77
struct VarCompositeGlyph
78
{
79
  static void
80
  get_path_at (const hb_varc_context_t &c,
81
         hb_codepoint_t gid,
82
         hb_array_t<const int> coords,
83
         hb_transform_t<> transform,
84
         hb_ubytes_t record,
85
         hb_scalar_cache_t *cache)
86
0
  {
87
0
    while (record)
88
0
    {
89
0
      const VarComponent &comp = * (const VarComponent *) (record.arrayZ);
90
0
      record = comp.get_path_at (c,
91
0
         gid,
92
0
         coords, transform,
93
0
         record,
94
0
         cache);
95
0
    }
96
0
  }
97
};
98
99
HB_MARK_AS_FLAG_T (VarComponent::flags_t);
100
101
struct VARC
102
{
103
  friend struct VarComponent;
104
105
  static constexpr hb_tag_t tableTag = HB_TAG ('V', 'A', 'R', 'C');
106
107
  HB_INTERNAL bool
108
  get_path_at (const hb_varc_context_t &c,
109
         hb_codepoint_t gid,
110
         hb_array_t<const int> coords,
111
         hb_transform_t<> transform = HB_TRANSFORM_IDENTITY,
112
         hb_codepoint_t parent_gid = HB_CODEPOINT_INVALID,
113
         hb_scalar_cache_t *parent_cache = nullptr) const;
114
115
  bool
116
  get_path (hb_font_t *font,
117
      hb_codepoint_t gid,
118
      hb_draw_session_t &draw_session,
119
      hb_varc_scratch_t &scratch) const
120
0
  {
121
0
    hb_varc_context_t c {font,
122
0
       &draw_session,
123
0
       nullptr,
124
0
       hb_decycler_t {},
125
0
       HB_MAX_GRAPH_EDGE_COUNT,
126
0
       HB_MAX_NESTING_LEVEL,
127
0
       HB_VARC_MAX_WORK,
128
0
       scratch};
129
130
0
    return get_path_at (c, gid,
131
0
      hb_array (font->coords, font->num_coords));
132
0
  }
133
134
  bool
135
  get_extents (hb_font_t *font,
136
         hb_codepoint_t gid,
137
         hb_extents_t<> *extents,
138
         hb_varc_scratch_t &scratch) const
139
0
  {
140
0
    hb_varc_context_t c {font,
141
0
       nullptr,
142
0
       extents,
143
0
       hb_decycler_t {},
144
0
       HB_MAX_GRAPH_EDGE_COUNT,
145
0
       HB_MAX_NESTING_LEVEL,
146
0
       HB_VARC_MAX_WORK,
147
0
       scratch};
148
149
0
    return get_path_at (c, gid,
150
0
      hb_array (font->coords, font->num_coords));
151
0
  }
152
153
  bool sanitize (hb_sanitize_context_t *c) const
154
0
  {
155
0
    TRACE_SANITIZE (this);
156
0
    return_trace (version.sanitize (c) &&
157
0
      hb_barrier () &&
158
0
      version.major == 1 &&
159
0
      coverage.sanitize (c, this) &&
160
0
      varStore.sanitize (c, this) &&
161
0
      conditionList.sanitize (c, this) &&
162
0
      axisIndicesList.sanitize (c, this) &&
163
0
      glyphRecords.sanitize (c, this));
164
0
  }
165
166
  struct accelerator_t
167
  {
168
    friend struct VarComponent;
169
170
    accelerator_t (hb_face_t *face)
171
0
    {
172
0
      table = hb_sanitize_context_t ().reference_table<VARC> (face);
173
0
    }
174
    ~accelerator_t ()
175
0
    {
176
0
      auto *scratch = cached_scratch.get_relaxed ();
177
0
      if (scratch)
178
0
      {
179
0
  scratch->~hb_varc_scratch_t ();
180
0
  hb_free (scratch);
181
0
      }
182
183
0
      table.destroy ();
184
0
    }
185
186
    bool
187
    get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
188
0
    {
189
0
      if (!table->has_data ()) return false;
190
191
0
      auto *scratch = acquire_scratch ();
192
0
      if (unlikely (!scratch)) return true;
193
0
      bool ret = table->get_path (font, gid, draw_session, *scratch);
194
0
      release_scratch (scratch);
195
0
      return ret;
196
0
    }
197
198
    bool
199
    get_extents (hb_font_t *font,
200
     hb_codepoint_t gid,
201
     hb_glyph_extents_t *extents) const
202
0
    {
203
0
#ifndef HB_NO_DRAW
204
0
      if (!table->has_data ()) return false;
205
206
0
      hb_extents_t<> f_extents;
207
208
0
      auto *scratch = acquire_scratch ();
209
0
      if (unlikely (!scratch)) return true;
210
0
      bool ret = table->get_extents (font, gid, &f_extents, *scratch);
211
0
      release_scratch (scratch);
212
213
0
      if (ret)
214
0
  *extents = f_extents.to_glyph_extents (font->x_scale < 0, font->y_scale < 0);
215
216
0
      return ret;
217
#else
218
      return false;
219
#endif
220
0
    }
221
222
    private:
223
224
    hb_varc_scratch_t *acquire_scratch () const
225
0
    {
226
0
      hb_varc_scratch_t *scratch = cached_scratch.get_acquire ();
227
228
0
      if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
229
0
      {
230
0
  scratch = (hb_varc_scratch_t *) hb_calloc (1, sizeof (hb_varc_scratch_t));
231
0
  if (unlikely (!scratch))
232
0
    return nullptr;
233
0
      }
234
235
0
      return scratch;
236
0
    }
237
    void release_scratch (hb_varc_scratch_t *scratch) const
238
0
    {
239
0
      if (!cached_scratch.cmpexch (nullptr, scratch))
240
0
      {
241
0
  scratch->~hb_varc_scratch_t ();
242
0
  hb_free (scratch);
243
0
      }
244
0
    }
245
246
    private:
247
    hb_blob_ptr_t<VARC> table;
248
    mutable hb_atomic_t<hb_varc_scratch_t *> cached_scratch;
249
  };
250
251
0
  bool has_data () const { return version.major != 0; }
252
253
  protected:
254
  FixedVersion<> version; /* Version identifier */
255
  Offset32To<Coverage> coverage;
256
  Offset32To<MultiItemVariationStore> varStore;
257
  Offset32To<ConditionList> conditionList;
258
  Offset32To<TupleList> axisIndicesList;
259
  Offset32To<CFF2Index/*Of<VarCompositeGlyph>*/> glyphRecords;
260
  public:
261
  DEFINE_SIZE_STATIC (24);
262
};
263
264
struct VARC_accelerator_t : VARC::accelerator_t {
265
0
  VARC_accelerator_t (hb_face_t *face) : VARC::accelerator_t (face) {}
266
};
267
268
#endif
269
270
//}
271
272
}
273
274
#endif  /* OT_VAR_VARC_VARC_HH */