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-draw.cc
Line
Count
Source
1
/*
2
 * Copyright © 2019-2020  Ebrahim Byagowi
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
#include "hb.hh"
26
27
#ifndef HB_NO_DRAW
28
29
#include "hb-draw.hh"
30
31
#include "hb-geometry.hh"
32
33
#include "hb-machinery.hh"
34
35
#include <cmath>
36
37
38
/**
39
 * SECTION:hb-draw
40
 * @title: hb-draw
41
 * @short_description: Glyph drawing
42
 * @include: hb.h
43
 *
44
 * Functions for drawing (extracting) glyph shapes.
45
 *
46
 * The #hb_draw_funcs_t struct can be used with hb_font_draw_glyph().
47
 **/
48
49
static void
50
hb_draw_move_to_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED,
51
         hb_draw_state_t *st HB_UNUSED,
52
         float to_x HB_UNUSED, float to_y HB_UNUSED,
53
0
         void *user_data HB_UNUSED) {}
54
55
static void
56
hb_draw_line_to_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED,
57
         hb_draw_state_t *st HB_UNUSED,
58
         float to_x HB_UNUSED, float to_y HB_UNUSED,
59
0
         void *user_data HB_UNUSED) {}
60
61
static void
62
hb_draw_quadratic_to_nil (hb_draw_funcs_t *dfuncs, void *draw_data,
63
        hb_draw_state_t *st,
64
        float control_x, float control_y,
65
        float to_x, float to_y,
66
        void *user_data HB_UNUSED)
67
317
{
68
  /* Compute in double precision, so that the result does not depend
69
   * on how the compiler evaluates float expressions. */
70
1.26k
#define HB_TWO_THIRD 0.66666666666666666666666667
71
317
  double current_x = (double) st->current_x;
72
317
  double current_y = (double) st->current_y;
73
317
  double cx = (double) control_x;
74
317
  double cy = (double) control_y;
75
317
  double tx = (double) to_x;
76
317
  double ty = (double) to_y;
77
317
  dfuncs->emit_cubic_to (draw_data, *st,
78
317
       (float) (current_x + (cx - current_x) * HB_TWO_THIRD),
79
317
       (float) (current_y + (cy - current_y) * HB_TWO_THIRD),
80
317
       (float) (tx + (cx - tx) * HB_TWO_THIRD),
81
317
       (float) (ty + (cy - ty) * HB_TWO_THIRD),
82
317
       to_x, to_y);
83
317
#undef HB_TWO_THIRD
84
317
}
85
86
static void
87
hb_draw_cubic_to_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED,
88
          hb_draw_state_t *st HB_UNUSED,
89
          float control1_x HB_UNUSED, float control1_y HB_UNUSED,
90
          float control2_x HB_UNUSED, float control2_y HB_UNUSED,
91
          float to_x HB_UNUSED, float to_y HB_UNUSED,
92
0
          void *user_data HB_UNUSED) {}
93
94
static void
95
hb_draw_close_path_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED,
96
      hb_draw_state_t *st HB_UNUSED,
97
0
      void *user_data HB_UNUSED) {}
98
99
static hb_bool_t
100
hb_draw_set_budget_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED,
101
      int64_t budget HB_UNUSED,
102
0
      void *user_data HB_UNUSED) { return false; }
103
104
static int64_t
105
hb_draw_get_budget_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED,
106
0
      void *user_data HB_UNUSED) { return HB_BUDGET_DEFAULT; }
107
108
static int64_t *
109
hb_draw_get_budget_remaining_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED,
110
332
          void *user_data HB_UNUSED) { return nullptr; }
111
112
113
static bool
114
_hb_draw_funcs_set_preamble (hb_draw_funcs_t    *dfuncs,
115
           bool                func_is_null,
116
           void              **user_data,
117
           hb_destroy_func_t  *destroy)
118
32
{
119
32
  if (hb_object_is_immutable (dfuncs))
120
0
  {
121
0
    if (*destroy)
122
0
      (*destroy) (*user_data);
123
0
    return false;
124
0
  }
125
126
32
  if (func_is_null)
127
0
  {
128
0
    if (*destroy)
129
0
      (*destroy) (*user_data);
130
0
    *destroy = nullptr;
131
0
    *user_data = nullptr;
132
0
  }
133
134
32
  return true;
135
32
}
136
137
static bool
138
_hb_draw_funcs_set_middle (hb_draw_funcs_t   *dfuncs,
139
         void              *user_data,
140
         hb_destroy_func_t  destroy)
141
32
{
142
32
  auto destroy_guard = hb_make_scope_guard ([&]() {
143
0
    if (destroy) destroy (user_data);
144
0
  });
145
146
32
  if (user_data && !dfuncs->user_data)
147
8
  {
148
8
    dfuncs->user_data = (decltype (dfuncs->user_data)) hb_calloc (1, sizeof (*dfuncs->user_data));
149
8
    if (unlikely (!dfuncs->user_data))
150
0
      return false;
151
8
  }
152
32
  if (destroy && !dfuncs->destroy)
153
0
  {
154
0
    dfuncs->destroy = (decltype (dfuncs->destroy)) hb_calloc (1, sizeof (*dfuncs->destroy));
155
0
    if (unlikely (!dfuncs->destroy))
156
0
      return false;
157
0
  }
158
159
32
  destroy_guard.release ();
160
32
  return true;
161
32
}
162
163
#define HB_DRAW_FUNC_IMPLEMENT(name)            \
164
                    \
165
void                    \
166
hb_draw_funcs_set_##name##_func (hb_draw_funcs_t   *dfuncs,   \
167
         hb_draw_##name##_func_t  func,     \
168
         void      *user_data,    \
169
32
         hb_destroy_func_t    destroy)    \
170
32
{                   \
171
32
  if (!_hb_draw_funcs_set_preamble (dfuncs, !func, &user_data, &destroy))\
172
32
      return;                                                            \
173
32
                    \
174
32
  if (dfuncs->destroy && dfuncs->destroy->name)         \
175
32
    dfuncs->destroy->name (!dfuncs->user_data ? nullptr : dfuncs->user_data->name); \
176
32
                   \
177
32
  if (!_hb_draw_funcs_set_middle (dfuncs, user_data, destroy))           \
178
32
      return;                                                            \
179
32
                  \
180
32
  if (func)               \
181
32
    dfuncs->func.name = func;           \
182
32
  else                  \
183
32
    dfuncs->func.name = hb_draw_##name##_nil;       \
184
32
                  \
185
32
  if (dfuncs->user_data)           \
186
32
    dfuncs->user_data->name = user_data;       \
187
32
  if (dfuncs->destroy)             \
188
32
    dfuncs->destroy->name = destroy;         \
189
32
}
hb_draw_funcs_set_move_to_func
Line
Count
Source
169
8
         hb_destroy_func_t    destroy)    \
170
8
{                   \
171
8
  if (!_hb_draw_funcs_set_preamble (dfuncs, !func, &user_data, &destroy))\
172
8
      return;                                                            \
173
8
                    \
174
8
  if (dfuncs->destroy && dfuncs->destroy->name)         \
175
8
    dfuncs->destroy->name (!dfuncs->user_data ? nullptr : dfuncs->user_data->name); \
176
8
                   \
177
8
  if (!_hb_draw_funcs_set_middle (dfuncs, user_data, destroy))           \
178
8
      return;                                                            \
179
8
                  \
180
8
  if (func)               \
181
8
    dfuncs->func.name = func;           \
182
8
  else                  \
183
8
    dfuncs->func.name = hb_draw_##name##_nil;       \
184
8
                  \
185
8
  if (dfuncs->user_data)           \
186
8
    dfuncs->user_data->name = user_data;       \
187
8
  if (dfuncs->destroy)             \
188
8
    dfuncs->destroy->name = destroy;         \
189
8
}
hb_draw_funcs_set_line_to_func
Line
Count
Source
169
8
         hb_destroy_func_t    destroy)    \
170
8
{                   \
171
8
  if (!_hb_draw_funcs_set_preamble (dfuncs, !func, &user_data, &destroy))\
172
8
      return;                                                            \
173
8
                    \
174
8
  if (dfuncs->destroy && dfuncs->destroy->name)         \
175
8
    dfuncs->destroy->name (!dfuncs->user_data ? nullptr : dfuncs->user_data->name); \
176
8
                   \
177
8
  if (!_hb_draw_funcs_set_middle (dfuncs, user_data, destroy))           \
178
8
      return;                                                            \
179
8
                  \
180
8
  if (func)               \
181
8
    dfuncs->func.name = func;           \
182
8
  else                  \
183
8
    dfuncs->func.name = hb_draw_##name##_nil;       \
184
8
                  \
185
8
  if (dfuncs->user_data)           \
186
8
    dfuncs->user_data->name = user_data;       \
187
8
  if (dfuncs->destroy)             \
188
8
    dfuncs->destroy->name = destroy;         \
189
8
}
Unexecuted instantiation: hb_draw_funcs_set_quadratic_to_func
hb_draw_funcs_set_cubic_to_func
Line
Count
Source
169
8
         hb_destroy_func_t    destroy)    \
170
8
{                   \
171
8
  if (!_hb_draw_funcs_set_preamble (dfuncs, !func, &user_data, &destroy))\
172
8
      return;                                                            \
173
8
                    \
174
8
  if (dfuncs->destroy && dfuncs->destroy->name)         \
175
8
    dfuncs->destroy->name (!dfuncs->user_data ? nullptr : dfuncs->user_data->name); \
176
8
                   \
177
8
  if (!_hb_draw_funcs_set_middle (dfuncs, user_data, destroy))           \
178
8
      return;                                                            \
179
8
                  \
180
8
  if (func)               \
181
8
    dfuncs->func.name = func;           \
182
8
  else                  \
183
8
    dfuncs->func.name = hb_draw_##name##_nil;       \
184
8
                  \
185
8
  if (dfuncs->user_data)           \
186
8
    dfuncs->user_data->name = user_data;       \
187
8
  if (dfuncs->destroy)             \
188
8
    dfuncs->destroy->name = destroy;         \
189
8
}
hb_draw_funcs_set_close_path_func
Line
Count
Source
169
8
         hb_destroy_func_t    destroy)    \
170
8
{                   \
171
8
  if (!_hb_draw_funcs_set_preamble (dfuncs, !func, &user_data, &destroy))\
172
8
      return;                                                            \
173
8
                    \
174
8
  if (dfuncs->destroy && dfuncs->destroy->name)         \
175
8
    dfuncs->destroy->name (!dfuncs->user_data ? nullptr : dfuncs->user_data->name); \
176
8
                   \
177
8
  if (!_hb_draw_funcs_set_middle (dfuncs, user_data, destroy))           \
178
8
      return;                                                            \
179
8
                  \
180
8
  if (func)               \
181
8
    dfuncs->func.name = func;           \
182
8
  else                  \
183
8
    dfuncs->func.name = hb_draw_##name##_nil;       \
184
8
                  \
185
8
  if (dfuncs->user_data)           \
186
8
    dfuncs->user_data->name = user_data;       \
187
8
  if (dfuncs->destroy)             \
188
8
    dfuncs->destroy->name = destroy;         \
189
8
}
Unexecuted instantiation: hb_draw_funcs_set_set_budget_func
Unexecuted instantiation: hb_draw_funcs_set_get_budget_func
Unexecuted instantiation: hb_draw_funcs_set_get_budget_remaining_func
190
191
HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
192
#undef HB_DRAW_FUNC_IMPLEMENT
193
194
/**
195
 * hb_draw_funcs_create:
196
 *
197
 * Creates a new draw callbacks object.
198
 *
199
 * Return value: (transfer full):
200
 * A newly allocated #hb_draw_funcs_t with a reference count of 1. The initial
201
 * reference count should be released with hb_draw_funcs_destroy when you are
202
 * done using the #hb_draw_funcs_t. This function never returns `NULL`. If
203
 * memory cannot be allocated, a special singleton #hb_draw_funcs_t object will
204
 * be returned.
205
 *
206
 * Since: 4.0.0
207
 **/
208
hb_draw_funcs_t *
209
hb_draw_funcs_create ()
210
8
{
211
8
  hb_draw_funcs_t *dfuncs;
212
8
  if (unlikely (!(dfuncs = hb_object_create<hb_draw_funcs_t> ())))
213
0
    return const_cast<hb_draw_funcs_t *> (&Null (hb_draw_funcs_t));
214
215
8
  dfuncs->func =  Null (hb_draw_funcs_t).func;
216
217
8
  return dfuncs;
218
8
}
219
220
DEFINE_NULL_INSTANCE (hb_draw_funcs_t) =
221
{
222
  HB_OBJECT_HEADER_STATIC,
223
224
  {
225
#define HB_DRAW_FUNC_IMPLEMENT(name) hb_draw_##name##_nil,
226
    HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
227
#undef HB_DRAW_FUNC_IMPLEMENT
228
  }
229
};
230
231
/**
232
 * hb_draw_funcs_get_empty:
233
 *
234
 * Fetches the singleton empty draw-functions structure.
235
 *
236
 * Return value: (transfer full): The empty draw-functions structure
237
 *
238
 * Since: 7.0.0
239
 **/
240
hb_draw_funcs_t *
241
hb_draw_funcs_get_empty ()
242
0
{
243
0
  return const_cast<hb_draw_funcs_t *> (&Null (hb_draw_funcs_t));
244
0
}
245
246
/**
247
 * hb_draw_funcs_reference: (skip)
248
 * @dfuncs: draw functions
249
 *
250
 * Increases the reference count on @dfuncs by one.
251
 *
252
 * This prevents @dfuncs from being destroyed until a matching
253
 * call to hb_draw_funcs_destroy() is made.
254
 *
255
 * Return value: (transfer full):
256
 * The referenced #hb_draw_funcs_t.
257
 *
258
 * Since: 4.0.0
259
 **/
260
hb_draw_funcs_t *
261
hb_draw_funcs_reference (hb_draw_funcs_t *dfuncs)
262
0
{
263
0
  return hb_object_reference (dfuncs);
264
0
}
265
266
/**
267
 * hb_draw_funcs_destroy: (skip)
268
 * @dfuncs: draw functions
269
 *
270
 * Deallocate the @dfuncs.
271
 * Decreases the reference count on @dfuncs by one. If the result is zero, then
272
 * @dfuncs and all associated resources are freed. See hb_draw_funcs_reference().
273
 *
274
 * Since: 4.0.0
275
 **/
276
void
277
hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs)
278
7
{
279
7
  if (!hb_object_destroy (dfuncs)) return;
280
281
7
  if (dfuncs->destroy)
282
0
  {
283
0
#define HB_DRAW_FUNC_IMPLEMENT(name) \
284
0
    if (dfuncs->destroy->name) dfuncs->destroy->name (!dfuncs->user_data ? nullptr : dfuncs->user_data->name);
285
0
      HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
286
0
#undef HB_DRAW_FUNC_IMPLEMENT
287
0
  }
288
289
7
  hb_free (dfuncs->destroy);
290
7
  hb_free (dfuncs->user_data);
291
292
7
  hb_free (dfuncs);
293
7
}
294
295
/**
296
 * hb_draw_funcs_set_user_data: (skip)
297
 * @dfuncs: The draw-functions structure
298
 * @key: The user-data key
299
 * @data: A pointer to the user data
300
 * @destroy: (nullable): A callback to call when @data is not needed anymore
301
 * @replace: Whether to replace an existing data with the same key
302
 *
303
 * Attaches a user-data key/data pair to the specified draw-functions structure.
304
 *
305
 * Return value: `true` if success, `false` otherwise
306
 *
307
 * Since: 7.0.0
308
 **/
309
hb_bool_t
310
hb_draw_funcs_set_user_data (hb_draw_funcs_t *dfuncs,
311
           hb_user_data_key_t *key,
312
           void *              data,
313
           hb_destroy_func_t   destroy,
314
           hb_bool_t           replace)
315
0
{
316
0
  return hb_object_set_user_data (dfuncs, key, data, destroy, replace);
317
0
}
318
319
/**
320
 * hb_draw_funcs_get_user_data: (skip)
321
 * @dfuncs: The draw-functions structure
322
 * @key: The user-data key to query
323
 *
324
 * Fetches the user-data associated with the specified key,
325
 * attached to the specified draw-functions structure.
326
 *
327
 * Return value: (transfer none): A pointer to the user data
328
 *
329
 * Since: 7.0.0
330
 **/
331
void *
332
hb_draw_funcs_get_user_data (const hb_draw_funcs_t *dfuncs,
333
           hb_user_data_key_t       *key)
334
0
{
335
0
  return hb_object_get_user_data (dfuncs, key);
336
0
}
337
338
/**
339
 * hb_draw_funcs_make_immutable:
340
 * @dfuncs: draw functions
341
 *
342
 * Makes @dfuncs object immutable.
343
 *
344
 * Since: 4.0.0
345
 **/
346
void
347
hb_draw_funcs_make_immutable (hb_draw_funcs_t *dfuncs)
348
0
{
349
0
  if (hb_object_is_immutable (dfuncs))
350
0
    return;
351
352
0
  hb_object_make_immutable (dfuncs);
353
0
}
354
355
/**
356
 * hb_draw_funcs_is_immutable:
357
 * @dfuncs: draw functions
358
 *
359
 * Checks whether @dfuncs is immutable.
360
 *
361
 * Return value: `true` if @dfuncs is immutable, `false` otherwise
362
 *
363
 * Since: 4.0.0
364
 **/
365
hb_bool_t
366
hb_draw_funcs_is_immutable (hb_draw_funcs_t *dfuncs)
367
0
{
368
0
  return hb_object_is_immutable (dfuncs);
369
0
}
370
371
/**
372
 * hb_draw_set_budget:
373
 * @dfuncs: draw functions object
374
 * @draw_data: associated draw data
375
 * @budget: the new work-budget policy
376
 *
377
 * Sets the work-budget policy and recharges the live work budget. Negative
378
 * values other than #HB_BUDGET_DEFAULT are clamped to zero.
379
 *
380
 * Return value: `true` if the draw functions support work budgets
381
 *
382
 * Since: 14.5.0
383
 **/
384
hb_bool_t
385
hb_draw_set_budget (hb_draw_funcs_t *dfuncs, void *draw_data, int64_t budget)
386
0
{
387
0
  return dfuncs->set_budget (draw_data, budget);
388
0
}
389
390
/**
391
 * hb_draw_get_budget:
392
 * @dfuncs: draw functions object
393
 * @draw_data: associated draw data
394
 *
395
 * Fetches the configured work-budget policy.
396
 *
397
 * Return value: the configured policy, or #HB_BUDGET_DEFAULT if unsupported
398
 *
399
 * Since: 14.5.0
400
 **/
401
int64_t
402
hb_draw_get_budget (hb_draw_funcs_t *dfuncs, void *draw_data)
403
0
{
404
0
  return dfuncs->get_budget (draw_data);
405
0
}
406
407
/**
408
 * hb_draw_get_budget_remaining:
409
 * @dfuncs: draw functions object
410
 * @draw_data: associated draw data
411
 *
412
 * Fetches the live work budget. A negative value means the budget was
413
 * exhausted. Zero means the preceding work fit exactly, but no positive-cost
414
 * work can start.
415
 *
416
 * Return value: the live work budget; if it is not tracked, the concrete
417
 *   configured policy or #HB_BUDGET_UNLIMITED
418
 *
419
 * Since: 14.5.0
420
 **/
421
int64_t
422
hb_draw_get_budget_remaining (hb_draw_funcs_t *dfuncs, void *draw_data)
423
0
{
424
0
  int64_t *remaining = dfuncs->get_budget_remaining_ptr (draw_data);
425
0
  if (remaining)
426
0
    return *remaining;
427
428
0
  int64_t budget = dfuncs->get_budget (draw_data);
429
0
  return budget == HB_BUDGET_DEFAULT ? HB_BUDGET_UNLIMITED : budget;
430
0
}
431
432
433
/**
434
 * hb_draw_move_to:
435
 * @dfuncs: draw functions
436
 * @draw_data: associated draw data passed by the caller
437
 * @st: current draw state
438
 * @to_x: X component of target point
439
 * @to_y: Y component of target point
440
 *
441
 * Perform a "move-to" draw operation.
442
 *
443
 * Since: 4.0.0
444
 **/
445
void
446
hb_draw_move_to (hb_draw_funcs_t *dfuncs, void *draw_data,
447
     hb_draw_state_t *st,
448
     float to_x, float to_y)
449
0
{
450
0
  dfuncs->move_to (draw_data, *st,
451
0
       to_x, to_y);
452
0
}
453
454
/**
455
 * hb_draw_line_to:
456
 * @dfuncs: draw functions
457
 * @draw_data: associated draw data passed by the caller
458
 * @st: current draw state
459
 * @to_x: X component of target point
460
 * @to_y: Y component of target point
461
 *
462
 * Perform a "line-to" draw operation.
463
 *
464
 * Since: 4.0.0
465
 **/
466
void
467
hb_draw_line_to (hb_draw_funcs_t *dfuncs, void *draw_data,
468
     hb_draw_state_t *st,
469
     float to_x, float to_y)
470
0
{
471
0
  dfuncs->line_to (draw_data, *st,
472
0
       to_x, to_y);
473
0
}
474
475
/**
476
 * hb_draw_quadratic_to:
477
 * @dfuncs: draw functions
478
 * @draw_data: associated draw data passed by the caller
479
 * @st: current draw state
480
 * @control_x: X component of control point
481
 * @control_y: Y component of control point
482
 * @to_x: X component of target point
483
 * @to_y: Y component of target point
484
 *
485
 * Perform a "quadratic-to" draw operation.
486
 *
487
 * Since: 4.0.0
488
 **/
489
void
490
hb_draw_quadratic_to (hb_draw_funcs_t *dfuncs, void *draw_data,
491
          hb_draw_state_t *st,
492
          float control_x, float control_y,
493
          float to_x, float to_y)
494
0
{
495
0
  dfuncs->quadratic_to (draw_data, *st,
496
0
      control_x, control_y,
497
0
      to_x, to_y);
498
0
}
499
500
/**
501
 * hb_draw_cubic_to:
502
 * @dfuncs: draw functions
503
 * @draw_data: associated draw data passed by the caller
504
 * @st: current draw state
505
 * @control1_x: X component of first control point
506
 * @control1_y: Y component of first control point
507
 * @control2_x: X component of second control point
508
 * @control2_y: Y component of second control point
509
 * @to_x: X component of target point
510
 * @to_y: Y component of target point
511
 *
512
 * Perform a "cubic-to" draw operation.
513
 *
514
 * Since: 4.0.0
515
 **/
516
void
517
hb_draw_cubic_to (hb_draw_funcs_t *dfuncs, void *draw_data,
518
      hb_draw_state_t *st,
519
      float control1_x, float control1_y,
520
      float control2_x, float control2_y,
521
      float to_x, float to_y)
522
0
{
523
0
  dfuncs->cubic_to (draw_data, *st,
524
0
        control1_x, control1_y,
525
0
        control2_x, control2_y,
526
0
        to_x, to_y);
527
0
}
528
529
/**
530
 * hb_draw_close_path:
531
 * @dfuncs: draw functions
532
 * @draw_data: associated draw data passed by the caller
533
 * @st: current draw state
534
 *
535
 * Perform a "close-path" draw operation.
536
 *
537
 * Since: 4.0.0
538
 **/
539
void
540
hb_draw_close_path (hb_draw_funcs_t *dfuncs, void *draw_data,
541
        hb_draw_state_t *st)
542
0
{
543
0
  dfuncs->close_path (draw_data, *st);
544
0
}
545
546
547
/**
548
 * hb_draw_line:
549
 * @dfuncs: draw functions
550
 * @draw_data: associated draw data passed by the caller
551
 * @st: current draw state
552
 * @x0: start X coordinate
553
 * @y0: start Y coordinate
554
 * @w0: stroke width at the start
555
 * @x1: end X coordinate
556
 * @y1: end Y coordinate
557
 * @w1: stroke width at the end
558
 * @cap: end-cap shape (butt or square)
559
 *
560
 * Emits a tapered line segment as a filled trapezoid.  @w0 and
561
 * @w1 are the full stroke widths at the start and end points
562
 * respectively; they may differ for a tapered stroke or match
563
 * for a uniform one.  Pass `NaN` for @w1 to use @w0 (uniform
564
 * stroke) without repeating the value.
565
 *
566
 * With #HB_DRAW_LINE_CAP_SQUARE each endpoint is extended along
567
 * the line direction by half its local stroke width, so four
568
 * `hb_draw_line()` calls form a closed rectangle without gaps
569
 * at the corners.
570
 *
571
 * Since: 14.2.0
572
 **/
573
void
574
hb_draw_line (hb_draw_funcs_t *dfuncs, void *draw_data,
575
        hb_draw_state_t *st,
576
        float x0, float y0, float w0,
577
        float x1, float y1, float w1,
578
        hb_draw_line_cap_t cap)
579
0
{
580
0
  if (std::isnan (w1)) w1 = w0;
581
0
  float dx = x1 - x0, dy = y1 - y0;
582
0
  float len = sqrtf (dx * dx + dy * dy);
583
0
  if (len <= 0.f)
584
0
    return;
585
  /* Unit tangent and normal to the line direction. */
586
0
  float tx = dx / len;
587
0
  float ty = dy / len;
588
0
  float nx = -ty;
589
0
  float ny =  tx;
590
0
  float h0 = 0.5f * w0;
591
0
  float h1 = 0.5f * w1;
592
  /* Square caps: extend each endpoint outward along the line
593
   * tangent by half its local stroke width. */
594
0
  if (cap == HB_DRAW_LINE_CAP_SQUARE)
595
0
  {
596
0
    x0 -= tx * h0; y0 -= ty * h0;
597
0
    x1 += tx * h1; y1 += ty * h1;
598
0
  }
599
  /* Trapezoid corners (counter-clockwise). */
600
0
  float ax = x0 + nx * h0, ay = y0 + ny * h0;
601
0
  float bx = x1 + nx * h1, by = y1 + ny * h1;
602
0
  float cx = x1 - nx * h1, cy = y1 - ny * h1;
603
0
  float dx_ = x0 - nx * h0, dy_ = y0 - ny * h0;
604
605
0
  hb_draw_move_to   (dfuncs, draw_data, st, ax, ay);
606
0
  hb_draw_line_to   (dfuncs, draw_data, st, bx, by);
607
0
  hb_draw_line_to   (dfuncs, draw_data, st, cx, cy);
608
0
  hb_draw_line_to   (dfuncs, draw_data, st, dx_, dy_);
609
0
  hb_draw_close_path (dfuncs, draw_data, st);
610
0
}
611
612
/* Emit an axis-aligned rectangle as a single closed contour.
613
 * @ccw picks the winding direction (useful for cutting a hole
614
 * out of another rectangle in a stroked rect). */
615
static void
616
_hb_draw_rect_contour (hb_draw_funcs_t *dfuncs, void *draw_data,
617
           hb_draw_state_t *st,
618
           float x, float y, float w, float h,
619
           bool ccw)
620
0
{
621
0
  hb_draw_move_to (dfuncs, draw_data, st, x, y);
622
0
  if (ccw)
623
0
  {
624
0
    hb_draw_line_to (dfuncs, draw_data, st, x + w, y);
625
0
    hb_draw_line_to (dfuncs, draw_data, st, x + w, y + h);
626
0
    hb_draw_line_to (dfuncs, draw_data, st, x,     y + h);
627
0
  }
628
0
  else
629
0
  {
630
0
    hb_draw_line_to (dfuncs, draw_data, st, x,     y + h);
631
0
    hb_draw_line_to (dfuncs, draw_data, st, x + w, y + h);
632
0
    hb_draw_line_to (dfuncs, draw_data, st, x + w, y);
633
0
  }
634
0
  hb_draw_close_path (dfuncs, draw_data, st);
635
0
}
636
637
/**
638
 * hb_draw_rectangle:
639
 * @dfuncs: draw functions
640
 * @draw_data: associated draw data passed by the caller
641
 * @st: current draw state
642
 * @x: top-left X coordinate
643
 * @y: top-left Y coordinate
644
 * @w: width (may be negative)
645
 * @h: height (may be negative)
646
 * @stroke_width: stroke width, or `NaN` for a filled rectangle
647
 *
648
 * Emits an axis-aligned rectangle.  If @stroke_width is a finite
649
 * positive value, the rectangle is rendered as an outlined ring
650
 * of that thickness centered on the edges; if @stroke_width is
651
 * `NaN`, the rectangle is rendered filled.
652
 *
653
 * Note: stroked rectangles produce a bounding box covering the
654
 * full outer rectangle, so if the pen is a GPU fragment-shader
655
 * backend, the shader runs for every interior pixel even though
656
 * only the outline contributes coverage.  For very thin
657
 * outlines where the interior is much larger than the stroke,
658
 * emitting four hb_draw_line() segments (one per edge) is
659
 * considerably cheaper per frame.
660
 *
661
 * Since: 14.2.0
662
 **/
663
void
664
hb_draw_rectangle (hb_draw_funcs_t *dfuncs, void *draw_data,
665
       hb_draw_state_t *st,
666
       float x, float y,
667
       float w, float h,
668
       float stroke_width)
669
0
{
670
0
  if (std::isnan (stroke_width))
671
0
  {
672
    /* Filled rectangle with zero area is nothing to draw. */
673
0
    if (w == 0.f || h == 0.f)
674
0
      return;
675
0
    _hb_draw_rect_contour (dfuncs, draw_data, st, x, y, w, h, /*ccw*/ true);
676
0
    return;
677
0
  }
678
679
0
  if (stroke_width <= 0.f || !std::isfinite (stroke_width))
680
0
    return;
681
682
  /* Normalize to non-negative width/height so the stroke math
683
   * below (outer grows by sw, inner shrinks by sw) produces the
684
   * expected outer-contains-inner ring regardless of w/h signs. */
685
0
  if (w < 0.f) { x += w; w = -w; }
686
0
  if (h < 0.f) { y += h; h = -h; }
687
  /* w or h == 0 is still meaningful when stroking: a stroked
688
   * zero-height rect is a horizontal line of length w; zero
689
   * width is a vertical line.  Both degenerate to a single
690
   * outer contour because the inner hole collapses. */
691
692
  /* Stroke is centered on the edge: outer contour grows by
693
   * stroke_width/2, inner contour shrinks by the same. */
694
0
  float s = 0.5f * stroke_width;
695
  /* Outer rectangle (CCW = adds coverage). */
696
0
  _hb_draw_rect_contour (dfuncs, draw_data, st,
697
0
       x - s, y - s,
698
0
       w + stroke_width, h + stroke_width,
699
0
       /*ccw*/ true);
700
  /* Inner rectangle (CW = removes coverage for the hole). */
701
0
  float iw = w - stroke_width;
702
0
  float ih = h - stroke_width;
703
0
  if (iw > 0.f && ih > 0.f)
704
0
    _hb_draw_rect_contour (dfuncs, draw_data, st,
705
0
         x + s, y + s, iw, ih,
706
0
         /*ccw*/ false);
707
0
}
708
709
/* Circle approximated by 4 cubic Beziers, one per quadrant.
710
 * The magic constant 0.5522847498307936 is
711
 *   (4/3) * (sqrt(2) - 1)
712
 * and minimizes the max radial error to ~2.7e-4 of r. */
713
static void
714
_hb_draw_circle_contour (hb_draw_funcs_t *dfuncs, void *draw_data,
715
       hb_draw_state_t *st,
716
       float cx, float cy, float r,
717
       bool ccw)
718
0
{
719
0
  static const float k = 0.5522847498307936f;
720
0
  float ck = r * k;
721
722
0
  hb_draw_move_to (dfuncs, draw_data, st, cx + r, cy);
723
0
  if (ccw)
724
0
  {
725
0
    hb_draw_cubic_to (dfuncs, draw_data, st,
726
0
          cx + r, cy + ck,
727
0
          cx + ck, cy + r,
728
0
          cx,      cy + r);
729
0
    hb_draw_cubic_to (dfuncs, draw_data, st,
730
0
          cx - ck, cy + r,
731
0
          cx - r,  cy + ck,
732
0
          cx - r,  cy);
733
0
    hb_draw_cubic_to (dfuncs, draw_data, st,
734
0
          cx - r,  cy - ck,
735
0
          cx - ck, cy - r,
736
0
          cx,      cy - r);
737
0
    hb_draw_cubic_to (dfuncs, draw_data, st,
738
0
          cx + ck, cy - r,
739
0
          cx + r,  cy - ck,
740
0
          cx + r,  cy);
741
0
  }
742
0
  else
743
0
  {
744
0
    hb_draw_cubic_to (dfuncs, draw_data, st,
745
0
          cx + r, cy - ck,
746
0
          cx + ck, cy - r,
747
0
          cx,      cy - r);
748
0
    hb_draw_cubic_to (dfuncs, draw_data, st,
749
0
          cx - ck, cy - r,
750
0
          cx - r,  cy - ck,
751
0
          cx - r,  cy);
752
0
    hb_draw_cubic_to (dfuncs, draw_data, st,
753
0
          cx - r,  cy + ck,
754
0
          cx - ck, cy + r,
755
0
          cx,      cy + r);
756
0
    hb_draw_cubic_to (dfuncs, draw_data, st,
757
0
          cx + ck, cy + r,
758
0
          cx + r,  cy + ck,
759
0
          cx + r,  cy);
760
0
  }
761
0
  hb_draw_close_path (dfuncs, draw_data, st);
762
0
}
763
764
/**
765
 * hb_draw_circle:
766
 * @dfuncs: draw functions
767
 * @draw_data: associated draw data passed by the caller
768
 * @st: current draw state
769
 * @cx: center X coordinate
770
 * @cy: center Y coordinate
771
 * @r: radius
772
 * @stroke_width: stroke width, or `NaN` for a filled disc
773
 *
774
 * Emits a circle approximated by four cubic Bezier curves.  If
775
 * @stroke_width is a finite positive value, the circle is
776
 * rendered as an outlined ring of that thickness centered on
777
 * the nominal radius; if @stroke_width is `NaN`, the circle is
778
 * rendered as a filled disc.
779
 *
780
 * Since: 14.2.0
781
 **/
782
void
783
hb_draw_circle (hb_draw_funcs_t *dfuncs, void *draw_data,
784
    hb_draw_state_t *st,
785
    float cx, float cy,
786
    float r,
787
    float stroke_width)
788
0
{
789
0
  if (r <= 0.f)
790
0
    return;
791
792
0
  if (std::isnan (stroke_width))
793
0
  {
794
0
    _hb_draw_circle_contour (dfuncs, draw_data, st, cx, cy, r, /*ccw*/ true);
795
0
    return;
796
0
  }
797
798
0
  if (stroke_width <= 0.f || !std::isfinite (stroke_width))
799
0
    return;
800
801
0
  float s = 0.5f * stroke_width;
802
0
  _hb_draw_circle_contour (dfuncs, draw_data, st, cx, cy, r + s, /*ccw*/ true);
803
0
  float ir = r - s;
804
0
  if (ir > 0.f)
805
0
    _hb_draw_circle_contour (dfuncs, draw_data, st, cx, cy, ir, /*ccw*/ false);
806
0
}
807
808
809
static void
810
hb_draw_extents_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
811
       void *data,
812
       hb_draw_state_t *st,
813
       float to_x, float to_y,
814
       void *user_data HB_UNUSED)
815
0
{
816
0
  hb_extents_t<> *extents = (hb_extents_t<> *) data;
817
818
0
  extents->add_point (to_x, to_y);
819
0
}
820
821
static void
822
hb_draw_extents_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
823
       void *data,
824
       hb_draw_state_t *st,
825
       float to_x, float to_y,
826
       void *user_data HB_UNUSED)
827
0
{
828
0
  hb_extents_t<> *extents = (hb_extents_t<> *) data;
829
830
0
  extents->add_point (to_x, to_y);
831
0
}
832
833
static void
834
hb_draw_extents_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
835
            void *data,
836
            hb_draw_state_t *st,
837
            float control_x, float control_y,
838
            float to_x, float to_y,
839
            void *user_data HB_UNUSED)
840
0
{
841
0
  hb_extents_t<> *extents = (hb_extents_t<> *) data;
842
843
0
  extents->add_point (control_x, control_y);
844
0
  extents->add_point (to_x, to_y);
845
0
}
846
847
static void
848
hb_draw_extents_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
849
        void *data,
850
        hb_draw_state_t *st,
851
        float control1_x, float control1_y,
852
        float control2_x, float control2_y,
853
        float to_x, float to_y,
854
        void *user_data HB_UNUSED)
855
0
{
856
0
  hb_extents_t<> *extents = (hb_extents_t<> *) data;
857
858
0
  extents->add_point (control1_x, control1_y);
859
0
  extents->add_point (control2_x, control2_y);
860
0
  extents->add_point (to_x, to_y);
861
0
}
862
863
static inline void free_static_draw_extents_funcs ();
864
865
static struct hb_draw_extents_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t<hb_draw_extents_funcs_lazy_loader_t>
866
{
867
  static hb_draw_funcs_t *create ()
868
0
  {
869
0
    hb_draw_funcs_t *funcs = hb_draw_funcs_create ();
870
871
0
    hb_draw_funcs_set_move_to_func (funcs, hb_draw_extents_move_to, nullptr, nullptr);
872
0
    hb_draw_funcs_set_line_to_func (funcs, hb_draw_extents_line_to, nullptr, nullptr);
873
0
    hb_draw_funcs_set_quadratic_to_func (funcs, hb_draw_extents_quadratic_to, nullptr, nullptr);
874
0
    hb_draw_funcs_set_cubic_to_func (funcs, hb_draw_extents_cubic_to, nullptr, nullptr);
875
876
0
    hb_draw_funcs_make_immutable (funcs);
877
878
0
    hb_atexit (free_static_draw_extents_funcs);
879
880
0
    return funcs;
881
0
  }
882
} static_draw_extents_funcs;
883
884
static inline
885
void free_static_draw_extents_funcs ()
886
0
{
887
0
  static_draw_extents_funcs.free_instance ();
888
0
}
889
890
hb_draw_funcs_t *
891
hb_draw_extents_get_funcs ()
892
0
{
893
0
  return static_draw_extents_funcs.get_unconst ();
894
0
}
895
896
897
#endif