Coverage Report

Created: 2026-09-01 06:20

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