/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 | | * /* paint ops here are clipped to the emitted path */ |
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 |