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-paint.cc
Line
Count
Source
1
/*
2
 * Copyright © 2022 Matthias Clasen
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_PAINT
28
29
#include "hb-paint.hh"
30
31
/**
32
 * SECTION: hb-paint
33
 * @title: hb-paint
34
 * @short_description: Glyph painting
35
 * @include: hb.h
36
 *
37
 * Functions for painting glyphs.
38
 *
39
 * The main purpose of these functions is to paint (extract) color glyph layers
40
 * from the COLRv1 table, but the API works for drawing ordinary outlines and
41
 * images as well.
42
 *
43
 * The #hb_paint_funcs_t struct can be used with hb_font_paint_glyph().
44
 **/
45
46
static void
47
hb_paint_push_transform_nil (hb_paint_funcs_t *funcs, void *paint_data,
48
                             float xx, float yx,
49
                             float xy, float yy,
50
                             float dx, float dy,
51
0
                             void *user_data) {}
52
53
static void
54
hb_paint_pop_transform_nil (hb_paint_funcs_t *funcs, void *paint_data,
55
0
                            void *user_data) {}
56
57
static hb_bool_t
58
hb_paint_color_glyph_nil (hb_paint_funcs_t *funcs, void *paint_data,
59
                          hb_codepoint_t glyph,
60
                          hb_font_t *font,
61
0
                          void *user_data) { return false; }
62
63
static void
64
hb_paint_fill_glyph_nil (hb_paint_funcs_t *funcs, void *paint_data,
65
                         hb_codepoint_t glyph,
66
                         hb_font_t *font,
67
                         hb_bool_t is_foreground,
68
                         hb_color_t color,
69
                         void *user_data)
70
0
{
71
  /* Decompose into the equivalent general sequence. */
72
0
  funcs->push_clip_glyph (paint_data, glyph, font);
73
0
  funcs->color (paint_data, is_foreground, color);
74
0
  funcs->pop_clip (paint_data);
75
0
}
76
77
static void
78
hb_paint_push_clip_glyph_nil (hb_paint_funcs_t *funcs, void *paint_data,
79
                              hb_codepoint_t glyph,
80
                              hb_font_t *font,
81
0
                              void *user_data) {}
82
83
static void
84
hb_paint_push_clip_rectangle_nil (hb_paint_funcs_t *funcs, void *paint_data,
85
                                  float xmin, float ymin, float xmax, float ymax,
86
0
                                  void *user_data) {}
87
88
static hb_draw_funcs_t *
89
hb_paint_push_clip_path_start_nil (hb_paint_funcs_t *funcs, void *paint_data,
90
                                   void **draw_data,
91
0
                                   void *user_data) { if (draw_data) *draw_data = nullptr; return nullptr; }
92
93
static void
94
hb_paint_push_clip_path_end_nil (hb_paint_funcs_t *funcs, void *paint_data,
95
0
                                 void *user_data) {}
96
97
static void
98
hb_paint_pop_clip_nil (hb_paint_funcs_t *funcs, void *paint_data,
99
0
                       void *user_data) {}
100
101
static void
102
hb_paint_color_nil (hb_paint_funcs_t *funcs, void *paint_data,
103
                    hb_bool_t is_foreground,
104
                    hb_color_t color,
105
0
                    void *user_data) {}
106
107
static hb_bool_t
108
hb_paint_image_nil (hb_paint_funcs_t *funcs, void *paint_data,
109
                    hb_blob_t *image,
110
                    unsigned int width,
111
                    unsigned int height,
112
                    hb_tag_t format,
113
                    float slant_xy_deprecated,
114
                    hb_glyph_extents_t *extents,
115
0
                    void *user_data) { return false; }
116
117
static void
118
hb_paint_linear_gradient_nil (hb_paint_funcs_t *funcs, void *paint_data,
119
                              hb_color_line_t *color_line,
120
                              float x0, float y0,
121
                              float x1, float y1,
122
                              float x2, float y2,
123
0
                              void *user_data) {}
124
125
static void
126
hb_paint_radial_gradient_nil (hb_paint_funcs_t *funcs, void *paint_data,
127
                              hb_color_line_t *color_line,
128
                              float x0, float y0, float r0,
129
                              float x1, float y1, float r1,
130
0
                              void *user_data) {}
131
132
static void
133
hb_paint_sweep_gradient_nil (hb_paint_funcs_t *funcs, void *paint_data,
134
                             hb_color_line_t *color_line,
135
                             float x0, float y0,
136
                             float start_angle,
137
                             float end_angle,
138
0
                             void *user_data) {}
139
140
static void
141
hb_paint_push_group_nil (hb_paint_funcs_t *funcs, void *paint_data,
142
0
                         void *user_data) {}
143
144
static void
145
hb_paint_push_group_for_nil (hb_paint_funcs_t *funcs, void *paint_data,
146
                             hb_paint_composite_mode_t mode,
147
                             void *user_data)
148
0
{
149
0
  hb_paint_push_group (funcs, paint_data);
150
0
}
151
152
static void
153
hb_paint_pop_group_nil (hb_paint_funcs_t *funcs, void *paint_data,
154
                        hb_paint_composite_mode_t mode,
155
0
                        void *user_data) {}
156
157
static hb_bool_t
158
hb_paint_custom_palette_color_nil (hb_paint_funcs_t *funcs, void *paint_data,
159
                                   unsigned int color_index,
160
                                   hb_color_t *color,
161
0
                                   void *user_data) { return false; }
162
163
static hb_bool_t
164
hb_paint_set_budget_nil (hb_paint_funcs_t *funcs HB_UNUSED, void *paint_data HB_UNUSED,
165
       int64_t budget HB_UNUSED,
166
0
       void *user_data HB_UNUSED) { return false; }
167
168
static int64_t
169
hb_paint_get_budget_nil (hb_paint_funcs_t *funcs HB_UNUSED, void *paint_data HB_UNUSED,
170
0
       void *user_data HB_UNUSED) { return HB_BUDGET_DEFAULT; }
171
172
static int64_t *
173
hb_paint_get_budget_remaining_nil (hb_paint_funcs_t *funcs HB_UNUSED, void *paint_data HB_UNUSED,
174
0
           void *user_data HB_UNUSED) { return nullptr; }
175
176
static bool
177
_hb_paint_funcs_set_preamble (hb_paint_funcs_t  *funcs,
178
                             bool                func_is_null,
179
                             void              **user_data,
180
                             hb_destroy_func_t  *destroy)
181
0
{
182
0
  if (hb_object_is_immutable (funcs))
183
0
  {
184
0
    if (*destroy)
185
0
      (*destroy) (*user_data);
186
0
    return false;
187
0
  }
188
189
0
  if (func_is_null)
190
0
  {
191
0
    if (*destroy)
192
0
      (*destroy) (*user_data);
193
0
    *destroy = nullptr;
194
0
    *user_data = nullptr;
195
0
  }
196
197
0
  return true;
198
0
}
199
200
static bool
201
_hb_paint_funcs_set_middle (hb_paint_funcs_t  *funcs,
202
                            void              *user_data,
203
                            hb_destroy_func_t  destroy)
204
0
{
205
0
  auto destroy_guard = hb_make_scope_guard ([&]() {
206
0
    if (destroy) destroy (user_data);
207
0
  });
208
209
0
  if (user_data && !funcs->user_data)
210
0
  {
211
0
    funcs->user_data = (decltype (funcs->user_data)) hb_calloc (1, sizeof (*funcs->user_data));
212
0
    if (unlikely (!funcs->user_data))
213
0
      return false;
214
0
  }
215
0
  if (destroy && !funcs->destroy)
216
0
  {
217
0
    funcs->destroy = (decltype (funcs->destroy)) hb_calloc (1, sizeof (*funcs->destroy));
218
0
    if (unlikely (!funcs->destroy))
219
0
      return false;
220
0
  }
221
222
0
  destroy_guard.release ();
223
0
  return true;
224
0
}
225
226
#define HB_PAINT_FUNC_IMPLEMENT(name)                                           \
227
                                                                                \
228
void                                                                            \
229
hb_paint_funcs_set_##name##_func (hb_paint_funcs_t         *funcs,              \
230
                                  hb_paint_##name##_func_t  func,               \
231
                                  void                     *user_data,          \
232
0
                                  hb_destroy_func_t         destroy)            \
233
0
{                                                                               \
234
0
  if (!_hb_paint_funcs_set_preamble (funcs, !func, &user_data, &destroy))       \
235
0
      return;                                                                   \
236
0
                                                                                \
237
0
  if (funcs->destroy && funcs->destroy->name)                                   \
238
0
    funcs->destroy->name (!funcs->user_data ? nullptr : funcs->user_data->name);\
239
0
                                                                                \
240
0
  if (!_hb_paint_funcs_set_middle (funcs, user_data, destroy))                  \
241
0
      return;                                                                   \
242
0
                                                                                \
243
0
  if (func)                                                                     \
244
0
    funcs->func.name = func;                                                    \
245
0
  else                                                                          \
246
0
    funcs->func.name = hb_paint_##name##_nil;                                   \
247
0
                                                                                \
248
0
  if (funcs->user_data)                                                         \
249
0
    funcs->user_data->name = user_data;                                         \
250
0
  if (funcs->destroy)                                                           \
251
0
    funcs->destroy->name = destroy;                                             \
252
0
}
Unexecuted instantiation: hb_paint_funcs_set_push_transform_func
Unexecuted instantiation: hb_paint_funcs_set_pop_transform_func
Unexecuted instantiation: hb_paint_funcs_set_color_glyph_func
Unexecuted instantiation: hb_paint_funcs_set_push_clip_glyph_func
Unexecuted instantiation: hb_paint_funcs_set_push_clip_rectangle_func
Unexecuted instantiation: hb_paint_funcs_set_push_clip_path_start_func
Unexecuted instantiation: hb_paint_funcs_set_push_clip_path_end_func
Unexecuted instantiation: hb_paint_funcs_set_pop_clip_func
Unexecuted instantiation: hb_paint_funcs_set_color_func
Unexecuted instantiation: hb_paint_funcs_set_image_func
Unexecuted instantiation: hb_paint_funcs_set_linear_gradient_func
Unexecuted instantiation: hb_paint_funcs_set_radial_gradient_func
Unexecuted instantiation: hb_paint_funcs_set_sweep_gradient_func
Unexecuted instantiation: hb_paint_funcs_set_push_group_func
Unexecuted instantiation: hb_paint_funcs_set_push_group_for_func
Unexecuted instantiation: hb_paint_funcs_set_pop_group_func
Unexecuted instantiation: hb_paint_funcs_set_custom_palette_color_func
Unexecuted instantiation: hb_paint_funcs_set_fill_glyph_func
Unexecuted instantiation: hb_paint_funcs_set_set_budget_func
Unexecuted instantiation: hb_paint_funcs_set_get_budget_func
Unexecuted instantiation: hb_paint_funcs_set_get_budget_remaining_func
253
254
HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS
255
#undef HB_PAINT_FUNC_IMPLEMENT
256
257
/**
258
 * hb_paint_funcs_create:
259
 *
260
 * Creates a new #hb_paint_funcs_t structure of paint functions.
261
 *
262
 * The initial reference count of 1 should be released with hb_paint_funcs_destroy()
263
 * when you are done using the #hb_paint_funcs_t. This function never returns
264
 * `NULL`. If memory cannot be allocated, a special singleton #hb_paint_funcs_t
265
 * object will be returned.
266
 *
267
 * Returns value: (transfer full): the paint-functions structure
268
 *
269
 * Since: 7.0.0
270
 */
271
hb_paint_funcs_t *
272
hb_paint_funcs_create ()
273
0
{
274
0
  hb_paint_funcs_t *funcs;
275
0
  if (unlikely (!(funcs = hb_object_create<hb_paint_funcs_t> ())))
276
0
    return const_cast<hb_paint_funcs_t *> (&Null (hb_paint_funcs_t));
277
278
0
  funcs->func =  Null (hb_paint_funcs_t).func;
279
280
0
  return funcs;
281
0
}
282
283
DEFINE_NULL_INSTANCE (hb_paint_funcs_t) =
284
{
285
  HB_OBJECT_HEADER_STATIC,
286
287
  {
288
#define HB_PAINT_FUNC_IMPLEMENT(name) hb_paint_##name##_nil,
289
    HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS
290
#undef HB_PAINT_FUNC_IMPLEMENT
291
  }
292
};
293
294
/**
295
 * hb_paint_funcs_get_empty:
296
 *
297
 * Fetches the singleton empty paint-functions structure.
298
 *
299
 * Return value: (transfer full): The empty paint-functions structure
300
 *
301
 * Since: 7.0.0
302
 **/
303
hb_paint_funcs_t *
304
hb_paint_funcs_get_empty ()
305
0
{
306
0
  return const_cast<hb_paint_funcs_t *> (&Null (hb_paint_funcs_t));
307
0
}
308
309
/**
310
 * hb_paint_funcs_reference: (skip)
311
 * @funcs: The paint-functions structure
312
 *
313
 * Increases the reference count on a paint-functions structure.
314
 *
315
 * This prevents @funcs from being destroyed until a matching
316
 * call to hb_paint_funcs_destroy() is made.
317
 *
318
 * Return value: The paint-functions structure
319
 *
320
 * Since: 7.0.0
321
 */
322
hb_paint_funcs_t *
323
hb_paint_funcs_reference (hb_paint_funcs_t *funcs)
324
0
{
325
0
  return hb_object_reference (funcs);
326
0
}
327
328
/**
329
 * hb_paint_funcs_destroy: (skip)
330
 * @funcs: The paint-functions structure
331
 *
332
 * Decreases the reference count on a paint-functions structure.
333
 *
334
 * When the reference count reaches zero, the structure
335
 * is destroyed, freeing all memory.
336
 *
337
 * Since: 7.0.0
338
 */
339
void
340
hb_paint_funcs_destroy (hb_paint_funcs_t *funcs)
341
0
{
342
0
  if (!hb_object_destroy (funcs)) return;
343
344
0
  if (funcs->destroy)
345
0
  {
346
0
#define HB_PAINT_FUNC_IMPLEMENT(name) \
347
0
    if (funcs->destroy->name) funcs->destroy->name (!funcs->user_data ? nullptr : funcs->user_data->name);
348
0
      HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS
349
0
#undef HB_PAINT_FUNC_IMPLEMENT
350
0
  }
351
352
0
  hb_free (funcs->destroy);
353
0
  hb_free (funcs->user_data);
354
0
  hb_free (funcs);
355
0
}
356
357
/**
358
 * hb_paint_funcs_set_user_data: (skip)
359
 * @funcs: The paint-functions structure
360
 * @key: The user-data key
361
 * @data: A pointer to the user data
362
 * @destroy: (nullable): A callback to call when @data is not needed anymore
363
 * @replace: Whether to replace an existing data with the same key
364
 *
365
 * Attaches a user-data key/data pair to the specified paint-functions structure.
366
 *
367
 * Return value: `true` if success, `false` otherwise
368
 *
369
 * Since: 7.0.0
370
 **/
371
hb_bool_t
372
hb_paint_funcs_set_user_data (hb_paint_funcs_t *funcs,
373
           hb_user_data_key_t *key,
374
           void *              data,
375
           hb_destroy_func_t   destroy,
376
           hb_bool_t           replace)
377
0
{
378
0
  return hb_object_set_user_data (funcs, key, data, destroy, replace);
379
0
}
380
381
/**
382
 * hb_paint_funcs_get_user_data: (skip)
383
 * @funcs: The paint-functions structure
384
 * @key: The user-data key to query
385
 *
386
 * Fetches the user-data associated with the specified key,
387
 * attached to the specified paint-functions structure.
388
 *
389
 * Return value: (transfer none): A pointer to the user data
390
 *
391
 * Since: 7.0.0
392
 **/
393
void *
394
hb_paint_funcs_get_user_data (const hb_paint_funcs_t *funcs,
395
           hb_user_data_key_t       *key)
396
0
{
397
0
  return hb_object_get_user_data (funcs, key);
398
0
}
399
400
/**
401
 * hb_paint_funcs_make_immutable:
402
 * @funcs: The paint-functions structure
403
 *
404
 * Makes a paint-functions structure immutable.
405
 *
406
 * After this call, all attempts to set one of the callbacks
407
 * on @funcs will fail.
408
 *
409
 * Since: 7.0.0
410
 */
411
void
412
hb_paint_funcs_make_immutable (hb_paint_funcs_t *funcs)
413
0
{
414
0
  if (hb_object_is_immutable (funcs))
415
0
    return;
416
417
0
  hb_object_make_immutable (funcs);
418
0
}
419
420
/**
421
 * hb_paint_funcs_is_immutable:
422
 * @funcs: The paint-functions structure
423
 *
424
 * Tests whether a paint-functions structure is immutable.
425
 *
426
 * Return value: `true` if @funcs is immutable, `false` otherwise
427
 *
428
 * Since: 7.0.0
429
 */
430
hb_bool_t
431
hb_paint_funcs_is_immutable (hb_paint_funcs_t *funcs)
432
0
{
433
0
  return hb_object_is_immutable (funcs);
434
0
}
435
436
/**
437
 * hb_paint_set_budget:
438
 * @funcs: paint functions object
439
 * @paint_data: associated paint data
440
 * @budget: the new work-budget policy
441
 *
442
 * Sets the work-budget policy and recharges the live work budget. Negative
443
 * values other than #HB_BUDGET_DEFAULT are clamped to zero.
444
 *
445
 * Return value: `true` if the paint functions support work budgets
446
 *
447
 * Since: 14.5.0
448
 **/
449
hb_bool_t
450
hb_paint_set_budget (hb_paint_funcs_t *funcs, void *paint_data, int64_t budget)
451
0
{
452
0
  return funcs->set_budget (paint_data, budget);
453
0
}
454
455
/**
456
 * hb_paint_get_budget:
457
 * @funcs: paint functions object
458
 * @paint_data: associated paint data
459
 *
460
 * Fetches the configured work-budget policy.
461
 *
462
 * Return value: the configured policy, or #HB_BUDGET_DEFAULT if unsupported
463
 *
464
 * Since: 14.5.0
465
 **/
466
int64_t
467
hb_paint_get_budget (hb_paint_funcs_t *funcs, void *paint_data)
468
0
{
469
0
  return funcs->get_budget (paint_data);
470
0
}
471
472
/**
473
 * hb_paint_get_budget_remaining:
474
 * @funcs: paint functions object
475
 * @paint_data: associated paint data
476
 *
477
 * Fetches the live work budget. A negative value means the budget was
478
 * exhausted. Zero means the preceding work fit exactly, but no positive-cost
479
 * work can start.
480
 *
481
 * Return value: the live work budget; if it is not tracked, the concrete
482
 *   configured policy or #HB_BUDGET_UNLIMITED
483
 *
484
 * Since: 14.5.0
485
 **/
486
int64_t
487
hb_paint_get_budget_remaining (hb_paint_funcs_t *funcs, void *paint_data)
488
0
{
489
0
  int64_t *remaining = funcs->get_budget_remaining_ptr (paint_data);
490
0
  if (remaining)
491
0
    return *remaining;
492
493
0
  int64_t budget = funcs->get_budget (paint_data);
494
0
  return budget == HB_BUDGET_DEFAULT ? HB_BUDGET_UNLIMITED : budget;
495
0
}
496
497
498
/**
499
 * hb_color_line_get_color_stops:
500
 * @color_line: a #hb_color_line_t object
501
 * @start: the index of the first color stop to return
502
 * @count: (inout) (optional): Input = the maximum number of feature tags to return;
503
 *     Output = the actual number of feature tags returned (may be zero)
504
 * @color_stops: (out) (array length=count) (optional): Array of #hb_color_stop_t to populate
505
 *
506
 * Fetches a list of color stops from the given color line object.
507
 *
508
 * Note that due to variations being applied, the returned color stops
509
 * may be out of order. It is the callers responsibility to ensure that
510
 * color stops are sorted by their offset before they are used.
511
 *
512
 * Return value: the total number of color stops in @color_line
513
 *
514
 * Since: 7.0.0
515
 */
516
unsigned int
517
hb_color_line_get_color_stops (hb_color_line_t *color_line,
518
                               unsigned int start,
519
                               unsigned int *count,
520
                               hb_color_stop_t *color_stops)
521
0
{
522
0
  return color_line->get_color_stops (color_line,
523
0
              color_line->data,
524
0
              start, count,
525
0
              color_stops,
526
0
              color_line->get_color_stops_user_data);
527
0
}
528
529
/**
530
 * hb_color_line_get_extend:
531
 * @color_line: a #hb_color_line_t object
532
 *
533
 * Fetches the extend mode of the color line object.
534
 *
535
 * Return value: the extend mode of @color_line
536
 *
537
 * Since: 7.0.0
538
 */
539
hb_paint_extend_t
540
hb_color_line_get_extend (hb_color_line_t *color_line)
541
0
{
542
0
  return color_line->get_extend (color_line,
543
0
         color_line->data,
544
0
         color_line->get_extend_user_data);
545
0
}
546
547
548
/**
549
 * hb_paint_push_transform:
550
 * @funcs: paint functions
551
 * @paint_data: associated data passed by the caller
552
 * @xx: xx component of the transform matrix
553
 * @yx: yx component of the transform matrix
554
 * @xy: xy component of the transform matrix
555
 * @yy: yy component of the transform matrix
556
 * @dx: dx component of the transform matrix
557
 * @dy: dy component of the transform matrix
558
 *
559
 * Perform a "push-transform" paint operation.
560
 *
561
 * Since: 7.0.0
562
 */
563
void
564
hb_paint_push_transform (hb_paint_funcs_t *funcs, void *paint_data,
565
                         float xx, float yx,
566
                         float xy, float yy,
567
                         float dx, float dy)
568
0
{
569
0
  funcs->push_transform (paint_data, xx, yx, xy, yy, dx, dy);
570
0
}
571
572
/**
573
 * hb_paint_push_font_transform:
574
 * @funcs: paint functions
575
 * @paint_data: associated data passed by the caller
576
 * @font: a font
577
 *
578
 * Push the transform reflecting the font's scale and slant
579
 * settings onto the paint functions.
580
 *
581
 * Since: 11.0.0
582
 */
583
void
584
hb_paint_push_font_transform (hb_paint_funcs_t *funcs, void *paint_data,
585
                              const hb_font_t *font)
586
0
{
587
0
  funcs->push_font_transform (paint_data, font);
588
0
}
589
590
/**
591
 * hb_paint_push_inverse_font_transform:
592
 * @funcs: paint functions
593
 * @paint_data: associated data passed by the caller
594
 * @font: a font
595
 *
596
 * Push the inverse of the transform reflecting the font's
597
 * scale and slant settings onto the paint functions.
598
 *
599
 * Since: 11.0.0
600
 */
601
void
602
hb_paint_push_inverse_font_transform (hb_paint_funcs_t *funcs, void *paint_data,
603
                                      const hb_font_t *font)
604
0
{
605
0
  funcs->push_inverse_font_transform (paint_data, font);
606
0
}
607
608
/**
609
 * hb_paint_pop_transform:
610
 * @funcs: paint functions
611
 * @paint_data: associated data passed by the caller
612
 *
613
 * Perform a "pop-transform" paint operation.
614
 *
615
 * Since: 7.0.0
616
 */
617
void
618
hb_paint_pop_transform (hb_paint_funcs_t *funcs, void *paint_data)
619
0
{
620
0
  funcs->pop_transform (paint_data);
621
0
}
622
623
/**
624
 * hb_paint_color_glyph:
625
 * @funcs: paint functions
626
 * @paint_data: associated data passed by the caller
627
 * @glyph: the glyph ID
628
 * @font: the font
629
 *
630
 * Perform a "color-glyph" paint operation.
631
 *
632
 * Since: 8.2.0
633
 */
634
hb_bool_t
635
hb_paint_color_glyph (hb_paint_funcs_t *funcs, void *paint_data,
636
                      hb_codepoint_t glyph,
637
                      hb_font_t *font)
638
0
{
639
0
  return funcs->color_glyph (paint_data, glyph, font);
640
0
}
641
642
/**
643
 * hb_paint_fill_glyph:
644
 * @funcs: paint functions
645
 * @paint_data: associated data passed by the caller
646
 * @glyph: the glyph ID
647
 * @font: the font
648
 * @is_foreground: whether the color is the foreground
649
 * @color: The color to use
650
 *
651
 * Perform a "fill-glyph" paint operation: fill the glyph's shape
652
 * with a solid color.
653
 *
654
 * Since: 14.3.0
655
 */
656
void
657
hb_paint_fill_glyph (hb_paint_funcs_t *funcs, void *paint_data,
658
                     hb_codepoint_t glyph,
659
                     hb_font_t *font,
660
                     hb_bool_t is_foreground,
661
                     hb_color_t color)
662
0
{
663
0
  funcs->fill_glyph (paint_data, glyph, font, is_foreground, color);
664
0
}
665
666
/**
667
 * hb_paint_push_clip_glyph:
668
 * @funcs: paint functions
669
 * @paint_data: associated data passed by the caller
670
 * @glyph: the glyph ID
671
 * @font: the font
672
 *
673
 * Perform a "push-clip-glyph" paint operation.
674
 *
675
 * Since: 7.0.0
676
 */
677
void
678
hb_paint_push_clip_glyph (hb_paint_funcs_t *funcs, void *paint_data,
679
                          hb_codepoint_t glyph,
680
                          hb_font_t *font)
681
0
{
682
0
  funcs->push_clip_glyph (paint_data, glyph, font);
683
0
}
684
685
/**
686
 * hb_paint_push_clip_rectangle:
687
 * @funcs: paint functions
688
 * @paint_data: associated data passed by the caller
689
 * @xmin: min X for the rectangle
690
 * @ymin: min Y for the rectangle
691
 * @xmax: max X for the rectangle
692
 * @ymax: max Y for the rectangle
693
 *
694
 * Perform a "push-clip-rect" paint operation.
695
 *
696
 * Since: 7.0.0
697
 */
698
void
699
hb_paint_push_clip_rectangle (hb_paint_funcs_t *funcs, void *paint_data,
700
                              float xmin, float ymin, float xmax, float ymax)
701
0
{
702
0
  funcs->push_clip_rectangle (paint_data, xmin, ymin, xmax, ymax);
703
0
}
704
705
/**
706
 * hb_paint_push_clip_path_start:
707
 * @funcs: paint functions
708
 * @paint_data: associated data passed by the caller
709
 * @draw_data: (out) (nullable): location to receive the draw data
710
 *   the caller should pass alongside the returned draw funcs.
711
 *
712
 * Begin clipping to an arbitrary path.  Returns an
713
 * #hb_draw_funcs_t owned by the backend (the caller must not
714
 * free it) that the caller uses to emit the clip outline via
715
 * hb_draw_*() calls, using the returned @draw_data as the
716
 * draw data.  The returned draw funcs and draw data are only
717
 * valid until the matching hb_paint_push_clip_path_end() call;
718
 * no other paint calls should be made between start and end
719
 * except hb_draw_*() on the returned funcs.  Finish the path
720
 * with hb_paint_push_clip_path_end(); pop the clip later
721
 * with hb_paint_pop_clip().
722
 *
723
 * Usage:
724
 *
725
 * |[<!-- language="plain" -->
726
 * hb_draw_funcs_t *df = hb_paint_push_clip_path_start (pf, pd, &dd);
727
 * hb_draw_move_to (df, dd, NULL, ...);
728
 * hb_draw_line_to (df, dd, NULL, ...);
729
 * ...
730
 * hb_draw_close_path (df, dd, NULL);
731
 * hb_paint_push_clip_path_end (pf, pd);
732
 * /&ast; paint ops here are clipped to the emitted path &ast;/
733
 * hb_paint_pop_clip (pf, pd);
734
 * ]|
735
 *
736
 * Return value: (transfer none): draw funcs that accumulate
737
 *   the clip path, or `NULL` if the backend does not implement
738
 *   arbitrary-path clipping.
739
 *
740
 * Since: 14.2.0
741
 */
742
hb_draw_funcs_t *
743
hb_paint_push_clip_path_start (hb_paint_funcs_t  *funcs,
744
                               void              *paint_data,
745
                               void             **draw_data)
746
0
{
747
0
  void *scratch = nullptr;
748
0
  if (!draw_data) draw_data = &scratch;
749
0
  return funcs->push_clip_path_start (paint_data, draw_data);
750
0
}
751
752
/**
753
 * hb_paint_push_clip_path_end:
754
 * @funcs: paint functions
755
 * @paint_data: associated data passed by the caller
756
 *
757
 * Signal that the arbitrary-clip path started by
758
 * hb_paint_push_clip_path_start() is fully drawn.  The
759
 * accumulated path now acts as a clip on the paint context
760
 * until a matching hb_paint_pop_clip() call.
761
 *
762
 * Since: 14.2.0
763
 */
764
void
765
hb_paint_push_clip_path_end (hb_paint_funcs_t *funcs, void *paint_data)
766
0
{
767
0
  funcs->push_clip_path_end (paint_data);
768
0
}
769
770
/**
771
 * hb_paint_pop_clip:
772
 * @funcs: paint functions
773
 * @paint_data: associated data passed by the caller
774
 *
775
 * Perform a "pop-clip" paint operation.
776
 *
777
 * Since: 7.0.0
778
 */
779
void
780
hb_paint_pop_clip (hb_paint_funcs_t *funcs, void *paint_data)
781
0
{
782
0
  funcs->pop_clip (paint_data);
783
0
}
784
785
/**
786
 * hb_paint_color:
787
 * @funcs: paint functions
788
 * @paint_data: associated data passed by the caller
789
 * @is_foreground: whether the color is the foreground
790
 * @color: The color to use
791
 *
792
 * Perform a "color" paint operation.
793
 *
794
 * Since: 7.0.0
795
 */
796
void
797
hb_paint_color (hb_paint_funcs_t *funcs, void *paint_data,
798
                hb_bool_t is_foreground,
799
                hb_color_t color)
800
0
{
801
0
  funcs->color (paint_data, is_foreground, color);
802
0
}
803
804
/**
805
 * hb_paint_image:
806
 * @funcs: paint functions
807
 * @paint_data: associated data passed by the caller
808
 * @image: image data
809
 * @width: width of the raster image in pixels, or 0
810
 * @height: height of the raster image in pixels, or 0
811
 * @format: the image format as a tag
812
 * @slant: Deprecated. set to 0.0
813
 * @extents: (nullable): the extents of the glyph
814
 *
815
 * Perform a "image" paint operation.
816
 *
817
 * Since: 7.0.0
818
 */
819
void
820
hb_paint_image (hb_paint_funcs_t *funcs, void *paint_data,
821
                hb_blob_t *image,
822
                unsigned int width,
823
                unsigned int height,
824
                hb_tag_t format,
825
                HB_UNUSED float slant,
826
                hb_glyph_extents_t *extents)
827
0
{
828
0
  funcs->image (paint_data, image, width, height, format, 0.f, extents);
829
0
}
830
831
/**
832
 * hb_paint_linear_gradient:
833
 * @funcs: paint functions
834
 * @paint_data: associated data passed by the caller
835
 * @color_line: Color information for the gradient
836
 * @x0: X coordinate of the first point
837
 * @y0: Y coordinate of the first point
838
 * @x1: X coordinate of the second point
839
 * @y1: Y coordinate of the second point
840
 * @x2: X coordinate of the third point
841
 * @y2: Y coordinate of the third point
842
 *
843
 * Perform a "linear-gradient" paint operation.
844
 *
845
 * Since: 7.0.0
846
 */
847
void
848
hb_paint_linear_gradient (hb_paint_funcs_t *funcs, void *paint_data,
849
                          hb_color_line_t *color_line,
850
                          float x0, float y0,
851
                          float x1, float y1,
852
                          float x2, float y2)
853
0
{
854
0
  funcs->linear_gradient (paint_data, color_line, x0, y0, x1, y1, x2, y2);
855
0
}
856
857
/**
858
 * hb_paint_radial_gradient:
859
 * @funcs: paint functions
860
 * @paint_data: associated data passed by the caller
861
 * @color_line: Color information for the gradient
862
 * @x0: X coordinate of the first circle's center
863
 * @y0: Y coordinate of the first circle's center
864
 * @r0: radius of the first circle
865
 * @x1: X coordinate of the second circle's center
866
 * @y1: Y coordinate of the second circle's center
867
 * @r1: radius of the second circle
868
 *
869
 * Perform a "radial-gradient" paint operation.
870
 *
871
 * Since: 7.0.0
872
 */
873
void
874
hb_paint_radial_gradient (hb_paint_funcs_t *funcs, void *paint_data,
875
                          hb_color_line_t *color_line,
876
                          float x0, float y0, float r0,
877
                          float x1, float y1, float r1)
878
0
{
879
0
  funcs->radial_gradient (paint_data, color_line, x0, y0, r0, x1, y1, r1);
880
0
}
881
882
/**
883
 * hb_paint_sweep_gradient:
884
 * @funcs: paint functions
885
 * @paint_data: associated data passed by the caller
886
 * @color_line: Color information for the gradient
887
 * @x0: X coordinate of the circle's center
888
 * @y0: Y coordinate of the circle's center
889
 * @start_angle: the start angle
890
 * @end_angle: the end angle
891
 *
892
 * Perform a "sweep-gradient" paint operation.
893
 *
894
 * Since: 7.0.0
895
 */
896
void
897
hb_paint_sweep_gradient (hb_paint_funcs_t *funcs, void *paint_data,
898
                         hb_color_line_t *color_line,
899
                         float x0, float y0,
900
                         float start_angle, float end_angle)
901
0
{
902
0
  funcs->sweep_gradient (paint_data, color_line, x0, y0, start_angle, end_angle);
903
0
}
904
905
/**
906
 * hb_paint_push_group:
907
 * @funcs: paint functions
908
 * @paint_data: associated data passed by the caller
909
 *
910
 * Perform a "push-group" paint operation.
911
 *
912
 * Since: 7.0.0
913
 */
914
void
915
hb_paint_push_group (hb_paint_funcs_t *funcs, void *paint_data)
916
0
{
917
0
  funcs->push_group (paint_data);
918
0
}
919
920
/**
921
 * hb_paint_push_group_for:
922
 * @funcs: paint functions
923
 * @paint_data: associated data passed by the caller
924
 * @mode: the compositing mode that will be used when the group is popped
925
 *
926
 * Perform a "push-group" paint operation, with the compositing
927
 * mode known in advance.  By default, this calls
928
 * hb_paint_push_group().
929
 *
930
 * Since: 14.2.0
931
 */
932
void
933
hb_paint_push_group_for (hb_paint_funcs_t *funcs, void *paint_data,
934
                         hb_paint_composite_mode_t mode)
935
0
{
936
0
  funcs->push_group_for (paint_data, mode);
937
0
}
938
939
/**
940
 * hb_paint_pop_group:
941
 * @funcs: paint functions
942
 * @paint_data: associated data passed by the caller
943
 * @mode: the compositing mode to use
944
 *
945
 * Perform a "pop-group" paint operation.
946
 *
947
 * Since: 7.0.0
948
 */
949
void
950
hb_paint_pop_group (hb_paint_funcs_t *funcs, void *paint_data,
951
                    hb_paint_composite_mode_t mode)
952
0
{
953
0
  funcs->pop_group (paint_data, mode);
954
0
}
955
956
/**
957
 * hb_paint_custom_palette_color:
958
 * @funcs: paint functions.
959
 * @paint_data: associated data passed by the caller.
960
 * @color_index: color index to fetch.
961
 * @color: (out): fetched color.
962
 *
963
 * Gets the custom palette override color for @color_index.
964
 *
965
 * Return value: `true` if a custom color is provided, `false` otherwise.
966
 *
967
 * Since: 7.0.0
968
 */
969
hb_bool_t
970
hb_paint_custom_palette_color (hb_paint_funcs_t *funcs, void *paint_data,
971
                               unsigned int color_index,
972
                               hb_color_t *color)
973
0
{
974
0
  return funcs->custom_palette_color (paint_data, color_index, color);
975
0
}
976
977
978
/**
979
 * hb_paint_reduce_linear_anchors:
980
 * @x0: x coordinate of P0 (color stop 0).
981
 * @y0: y coordinate of P0 (color stop 0).
982
 * @x1: x coordinate of P1 (color stop 1).
983
 * @y1: y coordinate of P1 (color stop 1).
984
 * @x2: x coordinate of P2 (rotation reference).
985
 * @y2: y coordinate of P2 (rotation reference).
986
 * @xx0: (out): x coordinate of the resulting axis start.
987
 * @yy0: (out): y coordinate of the resulting axis start.
988
 * @xx1: (out): x coordinate of the resulting axis end.
989
 * @yy1: (out): y coordinate of the resulting axis end.
990
 *
991
 * Reduces a COLRv1 linear gradient's 3-anchor spec (P0=color
992
 * stop 0, P1=color stop 1, P2=rotation reference) to the
993
 * 2-point axis (P0, P1') used by SVG / cairo / most software
994
 * renderers.  P1' is the foot of P1 on the line through P0
995
 * perpendicular to (P2 - P0); the resulting axis is the
996
 * gradient's actual direction (perpendicular to the rotation
997
 * line).  Degenerate (P0 == P2) passes through unchanged.
998
 *
999
 * Since: 14.2.0
1000
 **/
1001
void
1002
hb_paint_reduce_linear_anchors (float x0, float y0,
1003
        float x1, float y1,
1004
        float x2, float y2,
1005
        float *xx0, float *yy0,
1006
        float *xx1, float *yy1)
1007
0
{
1008
0
  float q2x = x2 - x0, q2y = y2 - y0;
1009
0
  float s = q2x * q2x + q2y * q2y;
1010
0
  if (s < 1e-6f)
1011
0
  {
1012
0
    *xx0 = x0; *yy0 = y0;
1013
0
    *xx1 = x1; *yy1 = y1;
1014
0
    return;
1015
0
  }
1016
0
  float q1x = x1 - x0, q1y = y1 - y0;
1017
0
  float k = (q2x * q1x + q2y * q1y) / s;
1018
0
  *xx0 = x0;
1019
0
  *yy0 = y0;
1020
0
  *xx1 = x1 - k * q2x;
1021
0
  *yy1 = y1 - k * q2y;
1022
0
}
1023
1024
/**
1025
 * hb_paint_normalize_color_line:
1026
 * @stops: (array length=len) (inout): color stops.
1027
 * @len: number of stops.
1028
 * @min: (out): original minimum offset.
1029
 * @max: (out): original maximum offset.
1030
 *
1031
 * Sorts @stops by offset and rescales offsets into [0, 1] in
1032
 * place.  Writes the original (min, max) to @min / @max so the
1033
 * caller can shift the gradient geometry (axis endpoints for
1034
 * linear, centers+radii for radial, start+end angles for sweep)
1035
 * to keep the rendered gradient visually unchanged after the
1036
 * rescale.  Empty input is safe: both out-parameters set to 0.
1037
 *
1038
 * Since: 14.2.0
1039
 **/
1040
void
1041
hb_paint_normalize_color_line (hb_color_stop_t *stops,
1042
             unsigned int     len,
1043
             float           *min,
1044
             float           *max)
1045
0
{
1046
0
  if (unlikely (!len))
1047
0
  {
1048
0
    *min = *max = 0.f;
1049
0
    return;
1050
0
  }
1051
1052
0
  hb_array_t<hb_color_stop_t> (stops, len)
1053
0
    .qsort ([] (const hb_color_stop_t &a, const hb_color_stop_t &b) {
1054
0
      return (a.offset > b.offset) - (a.offset < b.offset);
1055
0
    });
1056
1057
0
  float mn = stops[0].offset, mx = stops[0].offset;
1058
0
  for (unsigned i = 1; i < len; i++)
1059
0
  {
1060
0
    mn = hb_min (mn, stops[i].offset);
1061
0
    mx = hb_max (mx, stops[i].offset);
1062
0
  }
1063
0
  if (mn != mx)
1064
0
    for (unsigned i = 0; i < len; i++)
1065
0
      stops[i].offset = (stops[i].offset - mn) / (mx - mn);
1066
1067
0
  *min = mn;
1068
0
  *max = mx;
1069
0
}
1070
1071
/**
1072
 * hb_paint_sweep_gradient_tiles:
1073
 * @stops: (array length=n_stops) (inout): color stops (sorted, offsets in [0,1]).
1074
 * @n_stops: number of stops.
1075
 * @extend: extend mode.
1076
 * @start_angle: sweep start angle, in radians.
1077
 * @end_angle: sweep end angle, in radians.
1078
 * @emit_patch: (scope call): callback invoked once per tile.
1079
 * @user_data: data passed to @emit_patch.
1080
 *
1081
 * Iterates the full 0..2π sweep produced by a color-stop list,
1082
 * invoking @emit_patch once per (start, end) angular segment.
1083
 * Handles #HB_PAINT_EXTEND_PAD, #HB_PAINT_EXTEND_REPEAT, and
1084
 * #HB_PAINT_EXTEND_REFLECT.  Stops must be pre-sorted by
1085
 * offset; use hb_paint_normalize_color_line() first if they
1086
 * aren't.
1087
 *
1088
 * Since: 14.2.0
1089
 **/
1090
void
1091
hb_paint_sweep_gradient_tiles (hb_color_stop_t                     *stops,
1092
             unsigned int                         n_stops,
1093
             hb_paint_extend_t                    extend,
1094
             float                                start_angle,
1095
             float                                end_angle,
1096
             hb_paint_sweep_gradient_tile_func_t  emit_patch,
1097
             void                                *user_data)
1098
0
{
1099
0
  if (!n_stops) return;
1100
1101
0
  if (start_angle == end_angle)
1102
0
  {
1103
0
    if (extend == HB_PAINT_EXTEND_PAD)
1104
0
    {
1105
0
      if (start_angle > 0.f)
1106
0
  emit_patch (0.f, stops[0].color, start_angle, stops[0].color, user_data);
1107
0
      if (end_angle < HB_2_PI)
1108
0
  emit_patch (end_angle, stops[n_stops - 1].color, HB_2_PI, stops[n_stops - 1].color, user_data);
1109
0
    }
1110
0
    return;
1111
0
  }
1112
1113
0
  if (end_angle < start_angle)
1114
0
  {
1115
0
    float tmp = start_angle; start_angle = end_angle; end_angle = tmp;
1116
0
    for (unsigned i = 0; i < n_stops - 1 - i; i++)
1117
0
    {
1118
0
      hb_color_stop_t t = stops[i];
1119
0
      stops[i] = stops[n_stops - 1 - i];
1120
0
      stops[n_stops - 1 - i] = t;
1121
0
    }
1122
0
    for (unsigned i = 0; i < n_stops; i++)
1123
0
      stops[i].offset = 1.f - stops[i].offset;
1124
0
  }
1125
1126
  /* Map stop offsets to angles. */
1127
0
  float angles_buf[16];
1128
0
  hb_color_t colors_buf[16];
1129
0
  float *angles = angles_buf;
1130
0
  hb_color_t *colors = colors_buf;
1131
0
  bool dynamic = false;
1132
1133
0
  if (n_stops > 16)
1134
0
  {
1135
0
    angles = (float *) hb_malloc (sizeof (float) * n_stops);
1136
0
    colors = (hb_color_t *) hb_malloc (sizeof (hb_color_t) * n_stops);
1137
0
    if (!angles || !colors)
1138
0
    {
1139
0
      hb_free (angles);
1140
0
      hb_free (colors);
1141
0
      return;
1142
0
    }
1143
0
    dynamic = true;
1144
0
  }
1145
1146
0
  for (unsigned i = 0; i < n_stops; i++)
1147
0
  {
1148
0
    angles[i] = start_angle + stops[i].offset * (end_angle - start_angle);
1149
0
    colors[i] = stops[i].color;
1150
0
  }
1151
1152
0
  if (extend == HB_PAINT_EXTEND_PAD)
1153
0
  {
1154
0
    unsigned pos;
1155
0
    hb_color_t color0 = colors[0];
1156
0
    for (pos = 0; pos < n_stops; pos++)
1157
0
    {
1158
0
      if (angles[pos] >= 0)
1159
0
      {
1160
0
  if (pos > 0)
1161
0
  {
1162
0
    float f = (0.f - angles[pos - 1]) / (angles[pos] - angles[pos - 1]);
1163
0
    color0 = hb_color_lerp (colors[pos - 1], colors[pos], f);
1164
0
  }
1165
0
  break;
1166
0
      }
1167
0
    }
1168
0
    if (pos == n_stops)
1169
0
    {
1170
0
      color0 = colors[n_stops - 1];
1171
0
      emit_patch (0.f, color0, HB_2_PI, color0, user_data);
1172
0
      goto done;
1173
0
    }
1174
0
    emit_patch (0.f, color0, angles[pos], colors[pos], user_data);
1175
0
    for (pos++; pos < n_stops; pos++)
1176
0
    {
1177
0
      if (angles[pos] <= HB_2_PI)
1178
0
  emit_patch (angles[pos - 1], colors[pos - 1], angles[pos], colors[pos], user_data);
1179
0
      else
1180
0
      {
1181
0
  float f = (HB_2_PI - angles[pos - 1]) / (angles[pos] - angles[pos - 1]);
1182
0
  hb_color_t color1 = hb_color_lerp (colors[pos - 1], colors[pos], f);
1183
0
  emit_patch (angles[pos - 1], colors[pos - 1], HB_2_PI, color1, user_data);
1184
0
  break;
1185
0
      }
1186
0
    }
1187
0
    if (pos == n_stops)
1188
0
    {
1189
0
      color0 = colors[n_stops - 1];
1190
0
      emit_patch (angles[n_stops - 1], color0, HB_2_PI, color0, user_data);
1191
0
      goto done;
1192
0
    }
1193
0
  }
1194
0
  else
1195
0
  {
1196
0
    float span = angles[n_stops - 1] - angles[0];
1197
0
    if (!(fabsf (span) >= 1e-6f)) /* Reversed to catch NaN. */
1198
0
      goto done;
1199
1200
0
    span = fabsf (span);
1201
1202
0
    if (span < HB_2_PI / HB_PAINT_MAX_SWEEP_TILES)
1203
0
    {
1204
      /* The pattern repeats faster than the tile resolution; tiling
1205
       * it would cost unbounded work for sub-visual detail.  Fill
1206
       * the whole circle with the period-average color instead. */
1207
0
      float r = 0.f, g = 0.f, b = 0.f, a = 0.f;
1208
0
      for (unsigned i = 1; i < n_stops; i++)
1209
0
      {
1210
0
  float w = (angles[i] - angles[i - 1]) / span * 0.5f;
1211
0
  r += w * (hb_color_get_red (colors[i - 1]) + hb_color_get_red (colors[i]));
1212
0
  g += w * (hb_color_get_green (colors[i - 1]) + hb_color_get_green (colors[i]));
1213
0
  b += w * (hb_color_get_blue (colors[i - 1]) + hb_color_get_blue (colors[i]));
1214
0
  a += w * (hb_color_get_alpha (colors[i - 1]) + hb_color_get_alpha (colors[i]));
1215
0
      }
1216
0
      hb_color_t avg = HB_COLOR (hb_clamp_to<uint8_t> ((double) b + 0.5),
1217
0
         hb_clamp_to<uint8_t> ((double) g + 0.5),
1218
0
         hb_clamp_to<uint8_t> ((double) r + 0.5),
1219
0
         hb_clamp_to<uint8_t> ((double) a + 0.5));
1220
0
      emit_patch (0.f, avg, HB_2_PI, avg, user_data);
1221
0
      goto done;
1222
0
    }
1223
1224
    /* First repeat that can reach angle 0, computed by division;
1225
     * counting there one span at a time can take effectively forever,
1226
     * or fail to terminate outright once the span drops below the
1227
     * float ulp of the angles.  One repeat of slack absorbs rounding
1228
     * differences; earlier repeats are fully clipped anyway. */
1229
0
    double dk = floor (-(double) hb_max (angles[0], angles[n_stops - 1]) / (double) span) - 1.;
1230
0
    if (!(dk >= -1.e18 && dk <= 1.e18)) /* Non-finite angles. */
1231
0
      goto done;
1232
1233
0
    unsigned iterations = 0, tiles = 0;
1234
0
    for (int64_t l = (int64_t) dk; ; l++)
1235
0
    {
1236
0
      for (unsigned i = 1; i < n_stops; i++)
1237
0
      {
1238
  /* Bounds clipped-away segments too; those stay negligible for
1239
   * sorted stops but are unbounded if the sort contract is
1240
   * violated. */
1241
0
  if (unlikely (iterations++ >= 256 * HB_PAINT_MAX_SWEEP_TILES))
1242
0
    goto done;
1243
1244
0
  float a0_l, a1_l;
1245
0
  hb_color_t col0, col1;
1246
0
  if ((l % 2 != 0) && (extend == HB_PAINT_EXTEND_REFLECT))
1247
0
  {
1248
0
    a0_l = angles[0] + angles[n_stops - 1] - angles[n_stops - i] + l * span;
1249
0
    a1_l = angles[0] + angles[n_stops - 1] - angles[n_stops - 1 - i] + l * span;
1250
0
    col0 = colors[n_stops - i];
1251
0
    col1 = colors[n_stops - 1 - i];
1252
0
  }
1253
0
  else
1254
0
  {
1255
0
    a0_l = angles[i - 1] + l * span;
1256
0
    a1_l = angles[i] + l * span;
1257
0
    col0 = colors[i - 1];
1258
0
    col1 = colors[i];
1259
0
  }
1260
1261
0
  if (a1_l < 0.f) continue;
1262
0
  if (unlikely (tiles++ >= HB_PAINT_MAX_SWEEP_TILES))
1263
0
    goto done;
1264
0
  if (a0_l < 0.f)
1265
0
  {
1266
0
    float f = (0.f - a0_l) / (a1_l - a0_l);
1267
0
    hb_color_t c = hb_color_lerp (col0, col1, f);
1268
0
    emit_patch (0.f, c, a1_l, col1, user_data);
1269
0
  }
1270
0
  else if (a1_l >= HB_2_PI)
1271
0
  {
1272
0
    float f = (HB_2_PI - a0_l) / (a1_l - a0_l);
1273
0
    hb_color_t c = hb_color_lerp (col0, col1, f);
1274
0
    emit_patch (a0_l, col0, HB_2_PI, c, user_data);
1275
0
    goto done;
1276
0
  }
1277
0
  else
1278
0
    emit_patch (a0_l, col0, a1_l, col1, user_data);
1279
0
      }
1280
0
    }
1281
0
  }
1282
1283
0
done:
1284
0
  if (dynamic)
1285
0
  {
1286
0
    hb_free (angles);
1287
0
    hb_free (colors);
1288
0
  }
1289
0
}
1290
1291
#endif