hb-cairo-utils.cc (23650B)
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, ¢er, &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