Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/harfbuzz/src/hb-cairo-utils.cc
9903 views
1
/*
2
* Copyright © 2022 Red Hat, Inc
3
* Copyright © 2021, 2022 Black Foundry
4
*
5
* This is part of HarfBuzz, a text shaping library.
6
*
7
* Permission is hereby granted, without written agreement and without
8
* license or royalty fees, to use, copy, modify, and distribute this
9
* software and its documentation for any purpose, provided that the
10
* above copyright notice and the following two paragraphs appear in
11
* all copies of this software.
12
*
13
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17
* DAMAGE.
18
*
19
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24
*
25
* Google Author(s): Matthias Clasen
26
*/
27
28
#include "hb.hh"
29
30
#ifdef HAVE_CAIRO
31
32
#include "hb-cairo-utils.hh"
33
34
/* Some routines in this file were ported from BlackRenderer by Black Foundry.
35
* Used by permission to relicense to HarfBuzz license.
36
*
37
* https://github.com/BlackFoundryCom/black-renderer
38
*/
39
40
#define PREALLOCATED_COLOR_STOPS 16
41
42
typedef struct {
43
float r, g, b, a;
44
} hb_cairo_color_t;
45
46
static inline cairo_extend_t
47
hb_cairo_extend (hb_paint_extend_t extend)
48
{
49
switch (extend)
50
{
51
case HB_PAINT_EXTEND_PAD: return CAIRO_EXTEND_PAD;
52
case HB_PAINT_EXTEND_REPEAT: return CAIRO_EXTEND_REPEAT;
53
case HB_PAINT_EXTEND_REFLECT: return CAIRO_EXTEND_REFLECT;
54
default: break;
55
}
56
57
return CAIRO_EXTEND_PAD;
58
}
59
60
#ifdef CAIRO_HAS_PNG_FUNCTIONS
61
typedef struct
62
{
63
hb_blob_t *blob;
64
unsigned int offset;
65
} hb_cairo_read_blob_data_t;
66
67
static cairo_status_t
68
hb_cairo_read_blob (void *closure,
69
unsigned char *data,
70
unsigned int length)
71
{
72
hb_cairo_read_blob_data_t *r = (hb_cairo_read_blob_data_t *) closure;
73
const char *d;
74
unsigned int size;
75
76
d = hb_blob_get_data (r->blob, &size);
77
78
if (r->offset + length > size)
79
return CAIRO_STATUS_READ_ERROR;
80
81
hb_memcpy (data, d + r->offset, length);
82
r->offset += length;
83
84
return CAIRO_STATUS_SUCCESS;
85
}
86
#endif
87
88
static const cairo_user_data_key_t *_hb_cairo_surface_blob_user_data_key = {0};
89
90
static void
91
_hb_cairo_destroy_blob (void *p)
92
{
93
hb_blob_destroy ((hb_blob_t *) p);
94
}
95
96
hb_bool_t
97
_hb_cairo_paint_glyph_image (hb_cairo_context_t *c,
98
hb_blob_t *blob,
99
unsigned width,
100
unsigned height,
101
hb_tag_t format,
102
HB_UNUSED float slant_deprecated,
103
hb_glyph_extents_t *extents)
104
{
105
cairo_t *cr = c->cr;
106
107
if (!extents) /* SVG currently. */
108
return false;
109
110
cairo_surface_t *surface = nullptr;
111
112
#ifdef CAIRO_HAS_PNG_FUNCTIONS
113
if (format == HB_PAINT_IMAGE_FORMAT_PNG)
114
{
115
hb_cairo_read_blob_data_t r;
116
r.blob = blob;
117
r.offset = 0;
118
surface = cairo_image_surface_create_from_png_stream (hb_cairo_read_blob, &r);
119
120
/* For PNG, width,height can be unreliable, as is the case for NotoColorEmoji :(.
121
* Just pull them out of the surface. */
122
width = cairo_image_surface_get_width (surface);
123
height = cairo_image_surface_get_width (surface);
124
}
125
else
126
#endif
127
if (format == HB_PAINT_IMAGE_FORMAT_BGRA)
128
{
129
/* Byte-endian conversion. */
130
unsigned data_size = hb_blob_get_length (blob);
131
if (data_size < width * height * 4)
132
return false;
133
134
unsigned char *data;
135
#ifdef __BYTE_ORDER
136
if (__BYTE_ORDER == __BIG_ENDIAN)
137
{
138
data = (unsigned char *) hb_blob_get_data_writable (blob, nullptr);
139
if (!data)
140
return false;
141
142
unsigned count = width * height * 4;
143
for (unsigned i = 0; i < count; i += 4)
144
{
145
unsigned char b;
146
b = data[i];
147
data[i] = data[i+3];
148
data[i+3] = b;
149
b = data[i+1];
150
data[i+1] = data[i+2];
151
data[i+2] = b;
152
}
153
}
154
else
155
#endif
156
data = (unsigned char *) hb_blob_get_data (blob, nullptr);
157
158
surface = cairo_image_surface_create_for_data (data,
159
CAIRO_FORMAT_ARGB32,
160
width, height,
161
width * 4);
162
163
cairo_surface_set_user_data (surface,
164
_hb_cairo_surface_blob_user_data_key,
165
hb_blob_reference (blob),
166
_hb_cairo_destroy_blob);
167
}
168
169
if (!surface)
170
return false;
171
172
cairo_save (cr);
173
/* this clip is here to work around recording surface limitations */
174
cairo_rectangle (cr,
175
extents->x_bearing,
176
extents->y_bearing,
177
extents->width,
178
extents->height);
179
cairo_clip (cr);
180
181
cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface);
182
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
183
184
cairo_matrix_t matrix = {(double) width, 0, 0, (double) height, 0, 0};
185
cairo_pattern_set_matrix (pattern, &matrix);
186
187
cairo_translate (cr, extents->x_bearing, extents->y_bearing);
188
cairo_scale (cr, extents->width, extents->height);
189
cairo_set_source (cr, pattern);
190
191
cairo_paint (cr);
192
193
cairo_pattern_destroy (pattern);
194
cairo_surface_destroy (surface);
195
196
cairo_restore (cr);
197
198
return true;
199
}
200
201
static void
202
_hb_cairo_reduce_anchors (float x0, float y0,
203
float x1, float y1,
204
float x2, float y2,
205
float *xx0, float *yy0,
206
float *xx1, float *yy1)
207
{
208
float q1x, q1y, q2x, q2y;
209
float s;
210
float k;
211
212
q2x = x2 - x0;
213
q2y = y2 - y0;
214
q1x = x1 - x0;
215
q1y = y1 - y0;
216
217
s = q2x * q2x + q2y * q2y;
218
if (s < 0.000001f)
219
{
220
*xx0 = x0; *yy0 = y0;
221
*xx1 = x1; *yy1 = y1;
222
return;
223
}
224
225
k = (q2x * q1x + q2y * q1y) / s;
226
*xx0 = x0;
227
*yy0 = y0;
228
*xx1 = x1 - k * q2x;
229
*yy1 = y1 - k * q2y;
230
}
231
232
static int
233
_hb_cairo_cmp_color_stop (const void *p1,
234
const void *p2)
235
{
236
const hb_color_stop_t *c1 = (const hb_color_stop_t *) p1;
237
const hb_color_stop_t *c2 = (const hb_color_stop_t *) p2;
238
239
if (c1->offset < c2->offset)
240
return -1;
241
else if (c1->offset > c2->offset)
242
return 1;
243
else
244
return 0;
245
}
246
247
static void
248
_hb_cairo_normalize_color_line (hb_color_stop_t *stops,
249
unsigned int len,
250
float *omin,
251
float *omax)
252
{
253
float min, max;
254
255
hb_qsort (stops, len, sizeof (hb_color_stop_t), _hb_cairo_cmp_color_stop);
256
257
min = max = stops[0].offset;
258
for (unsigned int i = 0; i < len; i++)
259
{
260
min = hb_min (min, stops[i].offset);
261
max = hb_max (max, stops[i].offset);
262
}
263
264
if (min != max)
265
{
266
for (unsigned int i = 0; i < len; i++)
267
stops[i].offset = (stops[i].offset - min) / (max - min);
268
}
269
270
*omin = min;
271
*omax = max;
272
}
273
274
static bool
275
_hb_cairo_get_color_stops (hb_cairo_context_t *c,
276
hb_color_line_t *color_line,
277
unsigned *count,
278
hb_color_stop_t **stops)
279
{
280
unsigned len = hb_color_line_get_color_stops (color_line, 0, nullptr, nullptr);
281
if (len > *count)
282
{
283
*stops = (hb_color_stop_t *) hb_malloc (len * sizeof (hb_color_stop_t));
284
if (unlikely (!stops))
285
return false;
286
}
287
hb_color_line_get_color_stops (color_line, 0, &len, *stops);
288
for (unsigned i = 0; i < len; i++)
289
if ((*stops)[i].is_foreground)
290
{
291
#ifdef HAVE_CAIRO_USER_SCALED_FONT_GET_FOREGROUND_SOURCE
292
double r, g, b, a;
293
cairo_pattern_t *foreground = cairo_user_scaled_font_get_foreground_source (c->scaled_font);
294
if (cairo_pattern_get_rgba (foreground, &r, &g, &b, &a) == CAIRO_STATUS_SUCCESS)
295
(*stops)[i].color = HB_COLOR (round (b * 255.), round (g * 255.), round (r * 255.),
296
round (a * hb_color_get_alpha ((*stops)[i].color)));
297
else
298
#endif
299
(*stops)[i].color = HB_COLOR (0, 0, 0, hb_color_get_alpha ((*stops)[i].color));
300
}
301
302
*count = len;
303
return true;
304
}
305
306
void
307
_hb_cairo_paint_linear_gradient (hb_cairo_context_t *c,
308
hb_color_line_t *color_line,
309
float x0, float y0,
310
float x1, float y1,
311
float x2, float y2)
312
{
313
cairo_t *cr = c->cr;
314
315
unsigned int len = PREALLOCATED_COLOR_STOPS;
316
hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS];
317
hb_color_stop_t *stops = stops_;
318
float xx0, yy0, xx1, yy1;
319
float xxx0, yyy0, xxx1, yyy1;
320
float min, max;
321
cairo_pattern_t *pattern;
322
323
if (unlikely (!_hb_cairo_get_color_stops (c, color_line, &len, &stops)))
324
return;
325
_hb_cairo_normalize_color_line (stops, len, &min, &max);
326
327
_hb_cairo_reduce_anchors (x0, y0, x1, y1, x2, y2, &xx0, &yy0, &xx1, &yy1);
328
329
xxx0 = xx0 + min * (xx1 - xx0);
330
yyy0 = yy0 + min * (yy1 - yy0);
331
xxx1 = xx0 + max * (xx1 - xx0);
332
yyy1 = yy0 + max * (yy1 - yy0);
333
334
pattern = cairo_pattern_create_linear ((double) xxx0, (double) yyy0, (double) xxx1, (double) yyy1);
335
cairo_pattern_set_extend (pattern, hb_cairo_extend (hb_color_line_get_extend (color_line)));
336
for (unsigned int i = 0; i < len; i++)
337
{
338
double r, g, b, a;
339
r = hb_color_get_red (stops[i].color) / 255.;
340
g = hb_color_get_green (stops[i].color) / 255.;
341
b = hb_color_get_blue (stops[i].color) / 255.;
342
a = hb_color_get_alpha (stops[i].color) / 255.;
343
cairo_pattern_add_color_stop_rgba (pattern, (double) stops[i].offset, r, g, b, a);
344
}
345
346
cairo_set_source (cr, pattern);
347
cairo_paint (cr);
348
349
cairo_pattern_destroy (pattern);
350
351
if (stops != stops_)
352
hb_free (stops);
353
}
354
355
void
356
_hb_cairo_paint_radial_gradient (hb_cairo_context_t *c,
357
hb_color_line_t *color_line,
358
float x0, float y0, float r0,
359
float x1, float y1, float r1)
360
{
361
cairo_t *cr = c->cr;
362
363
unsigned int len = PREALLOCATED_COLOR_STOPS;
364
hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS];
365
hb_color_stop_t *stops = stops_;
366
float min, max;
367
float xx0, yy0, xx1, yy1;
368
float rr0, rr1;
369
cairo_pattern_t *pattern;
370
371
if (unlikely (!_hb_cairo_get_color_stops (c, color_line, &len, &stops)))
372
return;
373
_hb_cairo_normalize_color_line (stops, len, &min, &max);
374
375
xx0 = x0 + min * (x1 - x0);
376
yy0 = y0 + min * (y1 - y0);
377
xx1 = x0 + max * (x1 - x0);
378
yy1 = y0 + max * (y1 - y0);
379
rr0 = r0 + min * (r1 - r0);
380
rr1 = r0 + max * (r1 - r0);
381
382
pattern = cairo_pattern_create_radial ((double) xx0, (double) yy0, (double) rr0, (double) xx1, (double) yy1, (double) rr1);
383
cairo_pattern_set_extend (pattern, hb_cairo_extend (hb_color_line_get_extend (color_line)));
384
385
for (unsigned int i = 0; i < len; i++)
386
{
387
double r, g, b, a;
388
r = hb_color_get_red (stops[i].color) / 255.;
389
g = hb_color_get_green (stops[i].color) / 255.;
390
b = hb_color_get_blue (stops[i].color) / 255.;
391
a = hb_color_get_alpha (stops[i].color) / 255.;
392
cairo_pattern_add_color_stop_rgba (pattern, (double) stops[i].offset, r, g, b, a);
393
}
394
395
cairo_set_source (cr, pattern);
396
cairo_paint (cr);
397
398
cairo_pattern_destroy (pattern);
399
400
if (stops != stops_)
401
hb_free (stops);
402
}
403
404
typedef struct {
405
float x, y;
406
} hb_cairo_point_t;
407
408
static inline float
409
_hb_cairo_interpolate (float f0, float f1, float f)
410
{
411
return f0 + f * (f1 - f0);
412
}
413
414
static inline void
415
_hb_cairo_premultiply (hb_cairo_color_t *c)
416
{
417
c->r *= c->a;
418
c->g *= c->a;
419
c->b *= c->a;
420
}
421
422
static inline void
423
_hb_cairo_unpremultiply (hb_cairo_color_t *c)
424
{
425
if (c->a != 0.f)
426
{
427
c->r /= c->a;
428
c->g /= c->a;
429
c->b /= c->a;
430
}
431
}
432
433
static void
434
_hb_cairo_interpolate_colors (hb_cairo_color_t *c0, hb_cairo_color_t *c1, float k, hb_cairo_color_t *c)
435
{
436
// According to the COLR specification, gradients
437
// should be interpolated in premultiplied form
438
_hb_cairo_premultiply (c0);
439
_hb_cairo_premultiply (c1);
440
c->r = c0->r + k * (c1->r - c0->r);
441
c->g = c0->g + k * (c1->g - c0->g);
442
c->b = c0->b + k * (c1->b - c0->b);
443
c->a = c0->a + k * (c1->a - c0->a);
444
_hb_cairo_unpremultiply (c);
445
}
446
447
static inline float
448
_hb_cairo_dot (hb_cairo_point_t p, hb_cairo_point_t q)
449
{
450
return p.x * q.x + p.y * q.y;
451
}
452
453
static inline hb_cairo_point_t
454
_hb_cairo_normalize (hb_cairo_point_t p)
455
{
456
float len = sqrtf (_hb_cairo_dot (p, p));
457
458
return hb_cairo_point_t { p.x / len, p.y / len };
459
}
460
461
static inline hb_cairo_point_t
462
_hb_cairo_sum (hb_cairo_point_t p, hb_cairo_point_t q)
463
{
464
return hb_cairo_point_t { p.x + q.x, p.y + q.y };
465
}
466
467
static inline hb_cairo_point_t
468
_hb_cairo_difference (hb_cairo_point_t p, hb_cairo_point_t q)
469
{
470
return hb_cairo_point_t { p.x - q.x, p.y - q.y };
471
}
472
473
static inline hb_cairo_point_t
474
_hb_cairo_scale (hb_cairo_point_t p, float f)
475
{
476
return hb_cairo_point_t { p.x * f, p.y * f };
477
}
478
479
typedef struct {
480
hb_cairo_point_t center, p0, c0, c1, p1;
481
hb_cairo_color_t color0, color1;
482
} hb_cairo_patch_t;
483
484
static void
485
_hb_cairo_add_patch (cairo_pattern_t *pattern, hb_cairo_point_t *center, hb_cairo_patch_t *p)
486
{
487
cairo_mesh_pattern_begin_patch (pattern);
488
cairo_mesh_pattern_move_to (pattern, (double) center->x, (double) center->y);
489
cairo_mesh_pattern_line_to (pattern, (double) p->p0.x, (double) p->p0.y);
490
cairo_mesh_pattern_curve_to (pattern,
491
(double) p->c0.x, (double) p->c0.y,
492
(double) p->c1.x, (double) p->c1.y,
493
(double) p->p1.x, (double) p->p1.y);
494
cairo_mesh_pattern_line_to (pattern, (double) center->x, (double) center->y);
495
cairo_mesh_pattern_set_corner_color_rgba (pattern, 0,
496
(double) p->color0.r,
497
(double) p->color0.g,
498
(double) p->color0.b,
499
(double) p->color0.a);
500
cairo_mesh_pattern_set_corner_color_rgba (pattern, 1,
501
(double) p->color0.r,
502
(double) p->color0.g,
503
(double) p->color0.b,
504
(double) p->color0.a);
505
cairo_mesh_pattern_set_corner_color_rgba (pattern, 2,
506
(double) p->color1.r,
507
(double) p->color1.g,
508
(double) p->color1.b,
509
(double) p->color1.a);
510
cairo_mesh_pattern_set_corner_color_rgba (pattern, 3,
511
(double) p->color1.r,
512
(double) p->color1.g,
513
(double) p->color1.b,
514
(double) p->color1.a);
515
cairo_mesh_pattern_end_patch (pattern);
516
}
517
518
#define MAX_ANGLE (HB_PI / 8.f)
519
520
static void
521
_hb_cairo_add_sweep_gradient_patches1 (float cx, float cy, float radius,
522
float a0, hb_cairo_color_t *c0,
523
float a1, hb_cairo_color_t *c1,
524
cairo_pattern_t *pattern)
525
{
526
hb_cairo_point_t center = hb_cairo_point_t { cx, cy };
527
int num_splits;
528
hb_cairo_point_t p0;
529
hb_cairo_color_t color0, color1;
530
531
num_splits = ceilf (fabsf (a1 - a0) / MAX_ANGLE);
532
p0 = hb_cairo_point_t { cosf (a0), sinf (a0) };
533
color0 = *c0;
534
535
for (int a = 0; a < num_splits; a++)
536
{
537
float k = (a + 1.) / num_splits;
538
float angle1;
539
hb_cairo_point_t p1;
540
hb_cairo_point_t A, U;
541
hb_cairo_point_t C0, C1;
542
hb_cairo_patch_t patch;
543
544
angle1 = _hb_cairo_interpolate (a0, a1, k);
545
_hb_cairo_interpolate_colors (c0, c1, k, &color1);
546
547
patch.color0 = color0;
548
patch.color1 = color1;
549
550
p1 = hb_cairo_point_t { cosf (angle1), sinf (angle1) };
551
patch.p0 = _hb_cairo_sum (center, _hb_cairo_scale (p0, radius));
552
patch.p1 = _hb_cairo_sum (center, _hb_cairo_scale (p1, radius));
553
554
A = _hb_cairo_normalize (_hb_cairo_sum (p0, p1));
555
U = hb_cairo_point_t { -A.y, A.x };
556
C0 = _hb_cairo_sum (A, _hb_cairo_scale (U, _hb_cairo_dot (_hb_cairo_difference (p0, A), p0) / _hb_cairo_dot (U, p0)));
557
C1 = _hb_cairo_sum (A, _hb_cairo_scale (U, _hb_cairo_dot (_hb_cairo_difference (p1, A), p1) / _hb_cairo_dot (U, p1)));
558
559
patch.c0 = _hb_cairo_sum (center, _hb_cairo_scale (_hb_cairo_sum (C0, _hb_cairo_scale (_hb_cairo_difference (C0, p0), 0.33333f)), radius));
560
patch.c1 = _hb_cairo_sum (center, _hb_cairo_scale (_hb_cairo_sum (C1, _hb_cairo_scale (_hb_cairo_difference (C1, p1), 0.33333f)), radius));
561
562
_hb_cairo_add_patch (pattern, &center, &patch);
563
564
p0 = p1;
565
color0 = color1;
566
}
567
}
568
569
static void
570
_hb_cairo_add_sweep_gradient_patches (hb_color_stop_t *stops,
571
unsigned int n_stops,
572
cairo_extend_t extend,
573
float cx, float cy,
574
float radius,
575
float start_angle,
576
float end_angle,
577
cairo_pattern_t *pattern)
578
{
579
float angles_[PREALLOCATED_COLOR_STOPS];
580
float *angles = angles_;
581
hb_cairo_color_t colors_[PREALLOCATED_COLOR_STOPS];
582
hb_cairo_color_t *colors = colors_;
583
hb_cairo_color_t color0, color1;
584
585
if (start_angle == end_angle)
586
{
587
if (extend == CAIRO_EXTEND_PAD)
588
{
589
hb_cairo_color_t c;
590
if (start_angle > 0)
591
{
592
c.r = hb_color_get_red (stops[0].color) / 255.;
593
c.g = hb_color_get_green (stops[0].color) / 255.;
594
c.b = hb_color_get_blue (stops[0].color) / 255.;
595
c.a = hb_color_get_alpha (stops[0].color) / 255.;
596
_hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
597
0., &c,
598
start_angle, &c,
599
pattern);
600
}
601
if (end_angle < HB_2_PI)
602
{
603
c.r = hb_color_get_red (stops[n_stops - 1].color) / 255.;
604
c.g = hb_color_get_green (stops[n_stops - 1].color) / 255.;
605
c.b = hb_color_get_blue (stops[n_stops - 1].color) / 255.;
606
c.a = hb_color_get_alpha (stops[n_stops - 1].color) / 255.;
607
_hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
608
end_angle, &c,
609
HB_2_PI, &c,
610
pattern);
611
}
612
}
613
return;
614
}
615
616
assert (start_angle != end_angle);
617
618
/* handle directions */
619
if (end_angle < start_angle)
620
{
621
hb_swap (start_angle, end_angle);
622
623
for (unsigned i = 0; i < n_stops - 1 - i; i++)
624
hb_swap (stops[i], stops[n_stops - 1 - i]);
625
for (unsigned i = 0; i < n_stops; i++)
626
stops[i].offset = 1 - stops[i].offset;
627
}
628
629
if (n_stops > PREALLOCATED_COLOR_STOPS)
630
{
631
angles = (float *) hb_malloc (sizeof (float) * n_stops);
632
colors = (hb_cairo_color_t *) hb_malloc (sizeof (hb_cairo_color_t) * n_stops);
633
if (unlikely (!angles || !colors))
634
{
635
hb_free (angles);
636
hb_free (colors);
637
return;
638
}
639
}
640
641
for (unsigned i = 0; i < n_stops; i++)
642
{
643
angles[i] = start_angle + stops[i].offset * (end_angle - start_angle);
644
colors[i].r = hb_color_get_red (stops[i].color) / 255.;
645
colors[i].g = hb_color_get_green (stops[i].color) / 255.;
646
colors[i].b = hb_color_get_blue (stops[i].color) / 255.;
647
colors[i].a = hb_color_get_alpha (stops[i].color) / 255.;
648
}
649
650
if (extend == CAIRO_EXTEND_PAD)
651
{
652
unsigned pos;
653
654
color0 = colors[0];
655
for (pos = 0; pos < n_stops; pos++)
656
{
657
if (angles[pos] >= 0)
658
{
659
if (pos > 0)
660
{
661
float k = (0 - angles[pos - 1]) / (angles[pos] - angles[pos - 1]);
662
_hb_cairo_interpolate_colors (&colors[pos-1], &colors[pos], k, &color0);
663
}
664
break;
665
}
666
}
667
if (pos == n_stops)
668
{
669
/* everything is below 0 */
670
color0 = colors[n_stops-1];
671
_hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
672
0., &color0,
673
HB_2_PI, &color0,
674
pattern);
675
goto done;
676
}
677
678
_hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
679
0., &color0,
680
angles[pos], &colors[pos],
681
pattern);
682
683
for (pos++; pos < n_stops; pos++)
684
{
685
if (angles[pos] <= HB_2_PI)
686
{
687
_hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
688
angles[pos - 1], &colors[pos-1],
689
angles[pos], &colors[pos],
690
pattern);
691
}
692
else
693
{
694
float k = (HB_2_PI - angles[pos - 1]) / (angles[pos] - angles[pos - 1]);
695
_hb_cairo_interpolate_colors (&colors[pos - 1], &colors[pos], k, &color1);
696
_hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
697
angles[pos - 1], &colors[pos - 1],
698
HB_2_PI, &color1,
699
pattern);
700
break;
701
}
702
}
703
704
if (pos == n_stops)
705
{
706
/* everything is below 2*M_PI */
707
color0 = colors[n_stops - 1];
708
_hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
709
angles[n_stops - 1], &color0,
710
HB_2_PI, &color0,
711
pattern);
712
goto done;
713
}
714
}
715
else
716
{
717
int k;
718
float span;
719
720
span = angles[n_stops - 1] - angles[0];
721
if (!span)
722
goto done;
723
724
k = 0;
725
if (angles[0] >= 0)
726
{
727
float ss = angles[0];
728
while (ss > 0)
729
{
730
if (span > 0)
731
{
732
ss -= span;
733
k--;
734
}
735
else
736
{
737
ss += span;
738
k++;
739
}
740
}
741
}
742
else if (angles[0] < 0)
743
{
744
float ee = angles[n_stops - 1];
745
while (ee < 0)
746
{
747
if (span > 0)
748
{
749
ee += span;
750
k++;
751
}
752
else
753
{
754
ee -= span;
755
k--;
756
}
757
}
758
}
759
760
//assert (angles[0] + k * span <= 0 && 0 < angles[n_stops - 1] + k * span);
761
span = fabsf (span);
762
763
for (signed l = k; l < 1000; l++)
764
{
765
for (unsigned i = 1; i < n_stops; i++)
766
{
767
float a0, a1;
768
hb_cairo_color_t *c0, *c1;
769
770
if ((l % 2 != 0) && (extend == CAIRO_EXTEND_REFLECT))
771
{
772
a0 = angles[0] + angles[n_stops - 1] - angles[n_stops - 1 - (i-1)] + l * span;
773
a1 = angles[0] + angles[n_stops - 1] - angles[n_stops - 1 - i] + l * span;
774
c0 = &colors[n_stops - 1 - (i - 1)];
775
c1 = &colors[n_stops - 1 - i];
776
}
777
else
778
{
779
a0 = angles[i-1] + l * span;
780
a1 = angles[i] + l * span;
781
c0 = &colors[i-1];
782
c1 = &colors[i];
783
}
784
785
if (a1 < 0)
786
continue;
787
if (a0 < 0)
788
{
789
hb_cairo_color_t color;
790
float f = (0 - a0)/(a1 - a0);
791
_hb_cairo_interpolate_colors (c0, c1, f, &color);
792
_hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
793
0, &color,
794
a1, c1,
795
pattern);
796
}
797
else if (a1 >= HB_2_PI)
798
{
799
hb_cairo_color_t color;
800
float f = (HB_2_PI - a0)/(a1 - a0);
801
_hb_cairo_interpolate_colors (c0, c1, f, &color);
802
_hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
803
a0, c0,
804
HB_2_PI, &color,
805
pattern);
806
goto done;
807
}
808
else
809
{
810
_hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
811
a0, c0,
812
a1, c1,
813
pattern);
814
}
815
}
816
}
817
}
818
819
done:
820
821
if (angles != angles_)
822
hb_free (angles);
823
if (colors != colors_)
824
hb_free (colors);
825
}
826
827
void
828
_hb_cairo_paint_sweep_gradient (hb_cairo_context_t *c,
829
hb_color_line_t *color_line,
830
float cx, float cy,
831
float start_angle,
832
float end_angle)
833
{
834
cairo_t *cr = c->cr;
835
836
unsigned int len = PREALLOCATED_COLOR_STOPS;
837
hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS];
838
hb_color_stop_t *stops = stops_;
839
cairo_extend_t extend;
840
double x1, y1, x2, y2;
841
float max_x, max_y, radius;
842
cairo_pattern_t *pattern;
843
844
if (unlikely (!_hb_cairo_get_color_stops (c, color_line, &len, &stops)))
845
return;
846
847
hb_qsort (stops, len, sizeof (hb_color_stop_t), _hb_cairo_cmp_color_stop);
848
849
cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
850
max_x = (float) hb_max ((x1 - (double) cx) * (x1 - (double) cx), (x2 - (double) cx) * (x2 - (double) cx));
851
max_y = (float) hb_max ((y1 - (double) cy) * (y1 - (double) cy), (y2 - (double) cy) * (y2 - (double) cy));
852
radius = sqrtf (max_x + max_y);
853
854
extend = hb_cairo_extend (hb_color_line_get_extend (color_line));
855
pattern = cairo_pattern_create_mesh ();
856
857
_hb_cairo_add_sweep_gradient_patches (stops, len, extend, cx, cy,
858
radius, start_angle, end_angle, pattern);
859
860
cairo_set_source (cr, pattern);
861
cairo_paint (cr);
862
863
cairo_pattern_destroy (pattern);
864
865
if (stops != stops_)
866
hb_free (stops);
867
}
868
869
#endif
870
871