Merge pull request #4065 from harfbuzz/cairo-fix-foreground-color

hb-cairo: Fix handling of foreground color
diff --git a/meson.build b/meson.build
index 07ed493..9819d80 100644
--- a/meson.build
+++ b/meson.build
@@ -211,6 +211,7 @@
   else
     check_funcs += [['cairo_user_font_face_set_render_color_glyph_func', {'deps': cairo_dep}]]
     check_funcs += [['cairo_font_options_get_custom_palette_color', {'deps': cairo_dep}]]
+    check_funcs += [['cairo_user_scaled_font_get_foreground_source', {'deps': cairo_dep}]]
   endif
 endif
 
diff --git a/src/hb-cairo-utils.cc b/src/hb-cairo-utils.cc
index 299fdbe..77b3cb0 100644
--- a/src/hb-cairo-utils.cc
+++ b/src/hb-cairo-utils.cc
@@ -274,6 +274,38 @@
   *omax = max;
 }
 
+static bool
+_hb_cairo_get_color_stops (hb_cairo_context_t *c,
+			   hb_color_line_t *color_line,
+			   unsigned *count,
+			   hb_color_stop_t **stops)
+{
+  unsigned len = hb_color_line_get_color_stops (color_line, 0, nullptr, nullptr);
+  if (len > *count)
+  {
+    *stops = (hb_color_stop_t *) hb_malloc (len * sizeof (hb_color_stop_t));
+    if (unlikely (!stops))
+      return false;
+  }
+  hb_color_line_get_color_stops (color_line, 0, &len, *stops);
+  for (unsigned i = 0; i < len; i++)
+    if ((*stops)[i].is_foreground)
+    {
+#ifdef HAVE_CAIRO_USER_SCALED_FONT_GET_FOREGROUND_SOURCE
+      double r, g, b, a;
+      cairo_pattern_t *foreground = cairo_user_scaled_font_get_foreground_source (c->scaled_font);
+      if (cairo_pattern_get_rgba (foreground, &r, &g, &b, &a) == CAIRO_STATUS_SUCCESS)
+        (*stops)[i].color = HB_COLOR (round (b * 255.), round (g * 255.), round (r * 255.),
+                                      round (a * hb_color_get_alpha ((*stops)[i].color)));
+      else
+#endif
+        (*stops)[i].color = HB_COLOR (0, 0, 0, hb_color_get_alpha ((*stops)[i].color));
+    }
+
+  *count = len;
+  return true;
+}
+
 void
 _hb_cairo_paint_linear_gradient (hb_cairo_context_t *c,
 				 hb_color_line_t *color_line,
@@ -285,19 +317,17 @@
 
   hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS];
   hb_color_stop_t *stops = stops_;
-  unsigned int len;
+  unsigned int len = PREALLOCATED_COLOR_STOPS;
   float xx0, yy0, xx1, yy1;
   float xxx0, yyy0, xxx1, yyy1;
   float min, max;
   cairo_pattern_t *pattern;
 
-  len = hb_color_line_get_color_stops (color_line, 0, nullptr, nullptr);
-  if (len > PREALLOCATED_COLOR_STOPS)
-    stops = (hb_color_stop_t *) hb_malloc (len * sizeof (hb_color_stop_t));
-  hb_color_line_get_color_stops (color_line, 0, &len, stops);
+  if (unlikely (!_hb_cairo_get_color_stops (c, color_line, &len, &stops)))
+    return;
+  _hb_cairo_normalize_color_line (stops, len, &min, &max);
 
   _hb_cairo_reduce_anchors (x0, y0, x1, y1, x2, y2, &xx0, &yy0, &xx1, &yy1);
-  _hb_cairo_normalize_color_line (stops, len, &min, &max);
 
   xxx0 = xx0 + min * (xx1 - xx0);
   yyy0 = yy0 + min * (yy1 - yy0);
@@ -341,11 +371,8 @@
   float rr0, rr1;
   cairo_pattern_t *pattern;
 
-  len = hb_color_line_get_color_stops (color_line, 0, nullptr, nullptr);
-  if (len > PREALLOCATED_COLOR_STOPS)
-    stops = (hb_color_stop_t *) hb_malloc (len * sizeof (hb_color_stop_t));
-  hb_color_line_get_color_stops (color_line, 0, &len, stops);
-
+  if (unlikely (!_hb_cairo_get_color_stops (c, color_line, &len, &stops)))
+    return;
   _hb_cairo_normalize_color_line (stops, len, &min, &max);
 
   xx0 = x0 + min * (x1 - x0);
@@ -606,6 +633,12 @@
   {
     angles = (float *) hb_malloc (sizeof (float) * n_stops);
     colors = (hb_cairo_color_t *) hb_malloc (sizeof (hb_cairo_color_t) * n_stops);
+    if (unlikely (!angles || !colors))
+    {
+      hb_free (angles);
+      hb_free (colors);
+      return;
+    }
   }
 
   for (unsigned i = 0; i < n_stops; i++)
@@ -808,10 +841,8 @@
   float max_x, max_y, radius;
   cairo_pattern_t *pattern;
 
-  len = hb_color_line_get_color_stops (color_line, 0, nullptr, nullptr);
-  if (len > PREALLOCATED_COLOR_STOPS)
-    stops = (hb_color_stop_t *) hb_malloc (len * sizeof (hb_color_stop_t));
-  hb_color_line_get_color_stops (color_line, 0, &len, stops);
+  if (unlikely (!_hb_cairo_get_color_stops (c, color_line, &len, &stops)))
+    return;
 
   hb_qsort (stops, len, sizeof (hb_color_stop_t), _hb_cairo_cmp_color_stop);
 
diff --git a/src/hb-cairo.cc b/src/hb-cairo.cc
index b0bcb4a..43e3f05 100644
--- a/src/hb-cairo.cc
+++ b/src/hb-cairo.cc
@@ -251,11 +251,23 @@
   hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
   cairo_t *cr = c->cr;
 
-  cairo_set_source_rgba (cr,
-                         hb_color_get_red (color) / 255.,
-                         hb_color_get_green (color) / 255.,
-                         hb_color_get_blue (color) / 255.,
-                         hb_color_get_alpha (color) / 255.);
+  if (use_foreground)
+  {
+#ifdef HAVE_CAIRO_USER_SCALED_FONT_GET_FOREGROUND_SOURCE
+    double r, g, b, a;
+    cairo_pattern_t *foreground = cairo_user_scaled_font_get_foreground_source (c->scaled_font);
+    if (cairo_pattern_get_rgba (foreground, &r, &g, &b, &a) == CAIRO_STATUS_SUCCESS)
+      cairo_set_source_rgba (cr, r, g, b, a * hb_color_get_alpha (color) / 255.);
+    else
+#endif
+      cairo_set_source_rgba (cr, 0, 0, 0, hb_color_get_alpha (color) / 255.);
+  }
+  else
+    cairo_set_source_rgba (cr,
+			   hb_color_get_red (color) / 255.,
+			   hb_color_get_green (color) / 255.,
+			   hb_color_get_blue (color) / 255.,
+			   hb_color_get_alpha (color) / 255.);
   cairo_paint (cr);
 }
 
@@ -586,11 +598,6 @@
 #endif
 
   hb_color_t color = HB_COLOR (0, 0, 0, 255);
-  cairo_pattern_t *pattern = cairo_get_source (cr);
-  double r, g, b, a;
-  if (cairo_pattern_get_rgba (pattern, &r, &g, &b, &a) == CAIRO_STATUS_SUCCESS)
-    color = HB_COLOR (round (b * 255.), round (g * 255.), round (r * 255.), round (a * 255.));
-
   hb_position_t x_scale, y_scale;
   hb_font_get_scale (font, &x_scale, &y_scale);
   cairo_scale (cr, +1./x_scale, -1./y_scale);