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