Coverage Report

Created: 2026-05-16 09:25

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