tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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, &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