[cairo-commit] 9 commits - .gitlab-ci.yml meson.build src/cairo.c src/cairo-colr-glyph-render.c src/cairo-ft-font.c src/cairo-ft-private.h src/cairoint.h src/cairo-pattern.c src/meson.build

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Tue Jan 10 11:09:16 UTC 2023


 .gitlab-ci.yml                |    2 
 meson.build                   |    5 
 src/cairo-colr-glyph-render.c | 1235 ++++++++++++++++++++++++++++++++++++++++++
 src/cairo-ft-font.c           |  251 +++++++-
 src/cairo-ft-private.h        |   15 
 src/cairo-pattern.c           |    4 
 src/cairo.c                   |    2 
 src/cairoint.h                |    5 
 src/meson.build               |    1 
 9 files changed, 1479 insertions(+), 41 deletions(-)

New commits:
commit 1cfea7d979c1f2f95480bac8fbc10d30d28bf05e
Merge: c76a699f6 7471a323a
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Tue Jan 10 11:09:11 2023 +0000

    Merge branch 'colr-v1' into 'master'
    
    COLRv1 glyph renderer
    
    See merge request cairo/cairo!397

commit 7471a323a70203e983b88e7561a4c95d653f875f
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sun Jan 8 14:56:21 2023 +1030

    Enable error log output of meson test --no-suite=slow in CI
    
    So the cause of the failure can be seen.

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f1c4ecab7..aa20af746 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -254,7 +254,7 @@ fedora meson build:
     - touch builddir/src/.libs/libfoo.so
     # Run all the tests, except for the big test executable which
     # gets run separately
-    - meson test -C builddir --no-suite=slow
+    - meson test -C builddir --no-suite=slow --print-errorlogs
 
     # TODO: These aren't set up as Meson tests yet
     - (cd doc/public && bash "check-doc-syntax.sh")
commit dd8f55100a260d34732dbddff94c3d20071228a7
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sun Jan 8 13:33:14 2023 +1030

    Require -DDEBUG_ENABLE_COLR_V1 to enable the COLR v1 renderer
    
    Once there is a FreeType release where the COLR v1 API is no longer
    marked experimental, we can change this to a version check.

diff --git a/meson.build b/meson.build
index 75bbf37b1..4dedeadee 100644
--- a/meson.build
+++ b/meson.build
@@ -320,7 +320,6 @@ if freetype_dep.found()
   }]
 
   ft_check_funcs = [
-    'FT_Get_Color_Glyph_Paint',
     'FT_Get_X11_Font_Format',
     'FT_GlyphSlot_Embolden',
     'FT_GlyphSlot_Oblique',
@@ -346,6 +345,11 @@ if freetype_dep.found()
         conf.set('CAIRO_CAN_TEST_TTX_FONT', 1)
       endif
     endif
+    # When FT COLRv1 API is no longer experimental we can change this to a
+    # version check for the stable COLRv1 API.
+    if cc.get_define('DEBUG_ENABLE_COLR_V1') != ''
+      conf.set('HAVE_FT_COLR_V1', 1)
+    endif
     check_funcs += ft_check_funcs
     deps += [freetype_dep]
   endif
diff --git a/src/cairo-colr-glyph-render.c b/src/cairo-colr-glyph-render.c
index 149d2f8ba..2388d9e02 100644
--- a/src/cairo-colr-glyph-render.c
+++ b/src/cairo-colr-glyph-render.c
@@ -42,7 +42,7 @@
 #include <stdio.h>
 #include <string.h>
 
-#if HAVE_FT_GET_COLOR_GLYPH_PAINT
+#if HAVE_FT_COLR_V1
 
 #include <ft2build.h>
 #include FT_CONFIG_OPTIONS_H
@@ -1232,4 +1232,4 @@ _cairo_render_colr_v1_glyph (FT_Face               face,
     return status;
 }
 
-#endif /* HAVE_FT_GET_COLOR_GLYPH_PAINT */
+#endif /* HAVE_FT_COLR_V1 */
diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index 752fd0473..6db833c83 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -2792,7 +2792,7 @@ _cairo_ft_scaled_glyph_init_record_colr_v0_glyph (cairo_ft_scaled_font_t *scaled
 }
 #endif
 
-#if HAVE_FT_GET_COLOR_GLYPH_PAINT
+#if HAVE_FT_COLR_V1
 static cairo_int_status_t
 _cairo_ft_scaled_glyph_init_record_colr_v1_glyph (cairo_ft_scaled_font_t *scaled_font,
 						  cairo_scaled_glyph_t   *scaled_glyph,
@@ -3250,7 +3250,7 @@ _cairo_ft_scaled_glyph_is_colr_v1 (cairo_ft_scaled_font_t *scaled_font,
 				   cairo_scaled_glyph_t   *scaled_glyph,
 				   FT_Face                 face)
 {
-#if HAVE_FT_GET_COLOR_GLYPH_PAINT
+#if HAVE_FT_COLR_V1
     FT_OpaquePaint paint = { NULL, 0 };
 
     if (FT_Get_Color_Glyph_Paint (face,
@@ -3351,7 +3351,7 @@ _cairo_ft_scaled_glyph_init_metrics (cairo_ft_scaled_font_t     *scaled_font,
     }
 #endif
 
-#if HAVE_FT_GET_COLOR_GLYPH_PAINT
+#if HAVE_FT_COLR_V1
     if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_COLR_V1) {
 	status = (cairo_int_status_t)_cairo_ft_scaled_glyph_init_record_colr_v1_glyph (scaled_font,
 										       scaled_glyph,
diff --git a/src/cairo-ft-private.h b/src/cairo-ft-private.h
index e02235fa0..4775d8135 100644
--- a/src/cairo-ft-private.h
+++ b/src/cairo-ft-private.h
@@ -77,7 +77,7 @@ _cairo_render_svg_glyph (const char           *svg_document,
                          cairo_t              *cr);
 #endif
 
-#if HAVE_FT_GET_COLOR_GLYPH_PAINT
+#if HAVE_FT_COLR_V1
 cairo_private cairo_status_t
 _cairo_render_colr_v1_glyph (FT_Face                 face,
                              unsigned long           glyph,
commit 89d004432b218d2641439c01fd93deda390a25b1
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sun Jan 8 11:07:37 2023 +1030

    COLRv1: Use cairo style and types

diff --git a/src/cairo-colr-glyph-render.c b/src/cairo-colr-glyph-render.c
index 4a25f54af..149d2f8ba 100644
--- a/src/cairo-colr-glyph-render.c
+++ b/src/cairo-colr-glyph-render.c
@@ -1,4 +1,7 @@
-/* Copyright © 2022 Matthias Clasen
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2022 Matthias Clasen
  *
  * This library is free software; you can redistribute it and/or
  * modify it either under the terms of the GNU Lesser General Public
@@ -50,189 +53,181 @@
 
 /* #define DEBUG_COLR 1 */
 
+typedef struct _cairo_colr_glyph_render {
+    FT_Face face;
+    cairo_pattern_t *foreground_color;
+    FT_Color *palette;
+    unsigned int num_palette_entries;
+    int level;
+} cairo_colr_glyph_render_t;
 
-/* {{{ Utilities */
+static cairo_status_t
+draw_paint (cairo_colr_glyph_render_t *render,
+	    FT_OpaquePaint            *paint,
+	    cairo_t                   *cr);
 
-typedef struct {
-  float x, y;
-} Point;
 
-static inline float
-f16_16 (FT_Fixed f)
+static inline double
+double_from_16_16 (FT_Fixed f)
 {
-  return f / (float) (1 << 16);
+    return f / (double) (1 << 16);
 }
 
-static inline float
-f26_6 (FT_F26Dot6 f)
+static inline double
+double_from_26_6 (FT_F26Dot6 f)
 {
-  return f / (float) (1 << 6);
+    return f / (double) (1 << 6);
 }
 
-static inline float
-f2_14 (FT_F2Dot14 f)
+static inline double
+double_from_2_14 (FT_F2Dot14 f)
 {
-  return f / (float) (1 << 14);
+    return f / (double) (1 << 14);
 }
 
-static inline float
-interpolate (float f0, float f1, float f)
+static inline double
+interpolate (double f0, double f1, double f)
 {
-  return f0 + f * (f1 - f0);
+    return f0 + f * (f1 - f0);
 }
 
 static inline void
-interpolate_points (Point *p0, Point *p1, float f, Point *out)
+interpolate_points (cairo_point_double_t *p0,
+		    cairo_point_double_t *p1,
+		    double                f,
+		    cairo_point_double_t *out)
 {
   out->x = interpolate (p0->x, p1->x, f);
   out->y = interpolate (p0->y, p1->y, f);
 }
 
 static inline void
-interpolate_colors (cairo_color_t *c0, cairo_color_t *c1, float f, cairo_color_t *out)
+interpolate_colors (cairo_color_t *c0,
+		    cairo_color_t *c1,
+		    double         f,
+		    cairo_color_t *out)
 {
-  out->red = interpolate (c0->red, c1->red, f);
-  out->green = interpolate (c0->green, c1->green, f);
-  out->blue = interpolate (c0->blue, c1->blue, f);
-  out->alpha = interpolate (c0->alpha, c1->alpha, f);
+    out->red = interpolate (c0->red, c1->red, f);
+    out->green = interpolate (c0->green, c1->green, f);
+    out->blue = interpolate (c0->blue, c1->blue, f);
+    out->alpha = interpolate (c0->alpha, c1->alpha, f);
 }
 
-static inline float
-dot (Point p, Point q)
+static inline double
+dot (cairo_point_double_t p, cairo_point_double_t q)
 {
-  return p.x * q.x + p.y * q.y;
+    return p.x * q.x + p.y * q.y;
 }
 
-static inline Point
-normalize (Point p)
+static inline cairo_point_double_t
+normalize (cairo_point_double_t p)
 {
-  float len = sqrt (dot (p, p));
+    double len = sqrt (dot (p, p));
 
-  return (Point) { p.x / len, p.y / len };
+    return (cairo_point_double_t) { p.x / len, p.y / len };
 }
 
-static inline Point
-sum (Point p, Point q)
+static inline cairo_point_double_t
+sum (cairo_point_double_t p, cairo_point_double_t q)
 {
-  return (Point) { p.x + q.x, p.y + q.y };
+    return (cairo_point_double_t) { p.x + q.x, p.y + q.y };
 }
 
-static inline Point
-difference (Point p, Point q)
+static inline cairo_point_double_t
+difference (cairo_point_double_t p, cairo_point_double_t q)
 {
-  return (Point) { p.x - q.x, p.y - q.y };
+    return (cairo_point_double_t) { p.x - q.x, p.y - q.y };
 }
 
-static inline Point
-scale (Point p, float f)
+static inline cairo_point_double_t
+scale (cairo_point_double_t p, double f)
 {
-  return (Point) { p.x * f, p.y * f };
+    return (cairo_point_double_t) { p.x * f, p.y * f };
 }
 
 static cairo_operator_t
-cairo_operator (FT_Composite_Mode mode)
+cairo_operator_from_ft_composite_mode (FT_Composite_Mode mode)
 {
-  switch (mode)
+    switch (mode)
     {
-    case FT_COLR_COMPOSITE_CLEAR: return CAIRO_OPERATOR_CLEAR;
-    case FT_COLR_COMPOSITE_SRC: return CAIRO_OPERATOR_SOURCE;
-    case FT_COLR_COMPOSITE_DEST: return CAIRO_OPERATOR_DEST;
-    case FT_COLR_COMPOSITE_SRC_OVER: return CAIRO_OPERATOR_OVER;
-    case FT_COLR_COMPOSITE_DEST_OVER: return CAIRO_OPERATOR_DEST_OVER;
-    case FT_COLR_COMPOSITE_SRC_IN: return CAIRO_OPERATOR_IN;
-    case FT_COLR_COMPOSITE_DEST_IN: return CAIRO_OPERATOR_DEST_IN;
-    case FT_COLR_COMPOSITE_SRC_OUT: return CAIRO_OPERATOR_OUT;
-    case FT_COLR_COMPOSITE_DEST_OUT: return CAIRO_OPERATOR_DEST_OUT;
-    case FT_COLR_COMPOSITE_SRC_ATOP: return CAIRO_OPERATOR_ATOP;
-    case FT_COLR_COMPOSITE_DEST_ATOP: return CAIRO_OPERATOR_DEST_ATOP;
-    case FT_COLR_COMPOSITE_XOR: return CAIRO_OPERATOR_XOR;
-    case FT_COLR_COMPOSITE_PLUS: return CAIRO_OPERATOR_ADD;
-    case FT_COLR_COMPOSITE_SCREEN: return CAIRO_OPERATOR_SCREEN;
-    case FT_COLR_COMPOSITE_OVERLAY: return CAIRO_OPERATOR_OVERLAY;
-    case FT_COLR_COMPOSITE_DARKEN: return CAIRO_OPERATOR_DARKEN;
-    case FT_COLR_COMPOSITE_LIGHTEN: return CAIRO_OPERATOR_LIGHTEN;
-    case FT_COLR_COMPOSITE_COLOR_DODGE: return CAIRO_OPERATOR_COLOR_DODGE;
-    case FT_COLR_COMPOSITE_COLOR_BURN: return CAIRO_OPERATOR_COLOR_BURN;
-    case FT_COLR_COMPOSITE_HARD_LIGHT: return CAIRO_OPERATOR_HARD_LIGHT;
-    case FT_COLR_COMPOSITE_SOFT_LIGHT: return CAIRO_OPERATOR_SOFT_LIGHT;
-    case FT_COLR_COMPOSITE_DIFFERENCE: return CAIRO_OPERATOR_DIFFERENCE;
-    case FT_COLR_COMPOSITE_EXCLUSION: return CAIRO_OPERATOR_EXCLUSION;
-    case FT_COLR_COMPOSITE_MULTIPLY: return CAIRO_OPERATOR_MULTIPLY;
-    case FT_COLR_COMPOSITE_HSL_HUE: return CAIRO_OPERATOR_HSL_HUE;
-    case FT_COLR_COMPOSITE_HSL_SATURATION: return CAIRO_OPERATOR_HSL_SATURATION;
-    case FT_COLR_COMPOSITE_HSL_COLOR: return CAIRO_OPERATOR_HSL_COLOR;
-    case FT_COLR_COMPOSITE_HSL_LUMINOSITY: return CAIRO_OPERATOR_HSL_LUMINOSITY;
-    case FT_COLR_COMPOSITE_MAX:
-    default:
-      assert (0);
+	case FT_COLR_COMPOSITE_CLEAR: return CAIRO_OPERATOR_CLEAR;
+	case FT_COLR_COMPOSITE_SRC: return CAIRO_OPERATOR_SOURCE;
+	case FT_COLR_COMPOSITE_DEST: return CAIRO_OPERATOR_DEST;
+	case FT_COLR_COMPOSITE_SRC_OVER: return CAIRO_OPERATOR_OVER;
+	case FT_COLR_COMPOSITE_DEST_OVER: return CAIRO_OPERATOR_DEST_OVER;
+	case FT_COLR_COMPOSITE_SRC_IN: return CAIRO_OPERATOR_IN;
+	case FT_COLR_COMPOSITE_DEST_IN: return CAIRO_OPERATOR_DEST_IN;
+	case FT_COLR_COMPOSITE_SRC_OUT: return CAIRO_OPERATOR_OUT;
+	case FT_COLR_COMPOSITE_DEST_OUT: return CAIRO_OPERATOR_DEST_OUT;
+	case FT_COLR_COMPOSITE_SRC_ATOP: return CAIRO_OPERATOR_ATOP;
+	case FT_COLR_COMPOSITE_DEST_ATOP: return CAIRO_OPERATOR_DEST_ATOP;
+	case FT_COLR_COMPOSITE_XOR: return CAIRO_OPERATOR_XOR;
+	case FT_COLR_COMPOSITE_PLUS: return CAIRO_OPERATOR_ADD;
+	case FT_COLR_COMPOSITE_SCREEN: return CAIRO_OPERATOR_SCREEN;
+	case FT_COLR_COMPOSITE_OVERLAY: return CAIRO_OPERATOR_OVERLAY;
+	case FT_COLR_COMPOSITE_DARKEN: return CAIRO_OPERATOR_DARKEN;
+	case FT_COLR_COMPOSITE_LIGHTEN: return CAIRO_OPERATOR_LIGHTEN;
+	case FT_COLR_COMPOSITE_COLOR_DODGE: return CAIRO_OPERATOR_COLOR_DODGE;
+	case FT_COLR_COMPOSITE_COLOR_BURN: return CAIRO_OPERATOR_COLOR_BURN;
+	case FT_COLR_COMPOSITE_HARD_LIGHT: return CAIRO_OPERATOR_HARD_LIGHT;
+	case FT_COLR_COMPOSITE_SOFT_LIGHT: return CAIRO_OPERATOR_SOFT_LIGHT;
+	case FT_COLR_COMPOSITE_DIFFERENCE: return CAIRO_OPERATOR_DIFFERENCE;
+	case FT_COLR_COMPOSITE_EXCLUSION: return CAIRO_OPERATOR_EXCLUSION;
+	case FT_COLR_COMPOSITE_MULTIPLY: return CAIRO_OPERATOR_MULTIPLY;
+	case FT_COLR_COMPOSITE_HSL_HUE: return CAIRO_OPERATOR_HSL_HUE;
+	case FT_COLR_COMPOSITE_HSL_SATURATION: return CAIRO_OPERATOR_HSL_SATURATION;
+	case FT_COLR_COMPOSITE_HSL_COLOR: return CAIRO_OPERATOR_HSL_COLOR;
+	case FT_COLR_COMPOSITE_HSL_LUMINOSITY: return CAIRO_OPERATOR_HSL_LUMINOSITY;
+	case FT_COLR_COMPOSITE_MAX:
+	default:
+	    ASSERT_NOT_REACHED;
     }
 }
 
 static cairo_extend_t
-cairo_extend (FT_PaintExtend extend)
+cairo_extend_from_ft_paint_extend (FT_PaintExtend extend)
 {
-  switch (extend)
+    switch (extend)
     {
-    case FT_COLR_PAINT_EXTEND_PAD: return CAIRO_EXTEND_PAD;
-    case FT_COLR_PAINT_EXTEND_REPEAT: return CAIRO_EXTEND_REPEAT;
-    case FT_COLR_PAINT_EXTEND_REFLECT: return CAIRO_EXTEND_REFLECT;
-    default:
-      assert (0);
+	case FT_COLR_PAINT_EXTEND_PAD: return CAIRO_EXTEND_PAD;
+	case FT_COLR_PAINT_EXTEND_REPEAT: return CAIRO_EXTEND_REPEAT;
+	case FT_COLR_PAINT_EXTEND_REFLECT: return CAIRO_EXTEND_REFLECT;
+	default:
+	    ASSERT_NOT_REACHED;
     }
 }
 
-/* }}} */
-
-
-/* }}} */
-/* {{{ cairo_colr_glyph_render_t implementation */
-
-typedef struct cairo_colr_glyph_render_t cairo_colr_glyph_render_t;
-
-struct cairo_colr_glyph_render_t {
-  FT_Face face;
-  cairo_pattern_t *foreground_color;
-  FT_Color *palette;
-  unsigned int num_palette_entries;
-  int level;
-};
-
-static cairo_status_t draw_paint (cairo_colr_glyph_render_t *render,
-                                  FT_OpaquePaint *paint,
-                                  cairo_t *cr);
-
 static cairo_status_t
 draw_paint_colr_layers (cairo_colr_glyph_render_t *render,
-                        FT_PaintColrLayers *colr_layers,
-                        cairo_t *cr)
+                        FT_PaintColrLayers        *colr_layers,
+                        cairo_t                   *cr)
 {
-  FT_OpaquePaint paint;
-  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    FT_OpaquePaint paint;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
 #if DEBUG_COLR
-  printf ("%*sDraw PaintColrLayers\n", 2 * render->level, "");
+    printf ("%*sDraw PaintColrLayers\n", 2 * render->level, "");
 #endif
 
-  while (FT_Get_Paint_Layers (render->face, &colr_layers->layer_iterator, &paint))
-    {
-      cairo_push_group (cr);
-      status = draw_paint (render, &paint, cr);
-      cairo_pop_group_to_source (cr);
-      cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
-      cairo_paint (cr);
-
-      if (unlikely (status))
-        break;
+    while (FT_Get_Paint_Layers (render->face, &colr_layers->layer_iterator, &paint)) {
+	cairo_push_group (cr);
+	status = draw_paint (render, &paint, cr);
+	cairo_pop_group_to_source (cr);
+	cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+	cairo_paint (cr);
+
+	if (unlikely (status))
+	    break;
     }
 
-  return status;
+    return status;
 }
 
 static void
 get_palette_color (cairo_colr_glyph_render_t *render,
-                   FT_ColorIndex *ci,
-                   cairo_color_t *color,
-                   cairo_bool_t *is_foreground_color)
+                   FT_ColorIndex             *ci,
+                   cairo_color_t             *color,
+                   cairo_bool_t              *is_foreground_color)
 {
     cairo_bool_t foreground = FALSE;
 
@@ -247,209 +242,205 @@ get_palette_color (cairo_colr_glyph_render_t *render,
         color->green = c.green / 255.0;
         color->blue = c.blue / 255.0;
     }
-    color->alpha = f2_14 (ci->alpha);
+
+    color->alpha = double_from_2_14 (ci->alpha);
     if (foreground)
         *is_foreground_color = TRUE;
 }
 
 static cairo_status_t
 draw_paint_solid (cairo_colr_glyph_render_t *render,
-                  FT_PaintSolid *solid,
-                  cairo_t *cr)
+                  FT_PaintSolid             *solid,
+                  cairo_t                   *cr)
 {
-  cairo_color_t color;
-  cairo_bool_t is_foreground_color;
+    cairo_color_t color;
+    cairo_bool_t is_foreground_color;
 
 #if DEBUG_COLR
-  printf ("%*sDraw PaintSolid\n", 2 * render->level, "");
+    printf ("%*sDraw PaintSolid\n", 2 * render->level, "");
 #endif
 
-  get_palette_color (render, &solid->color, &color, &is_foreground_color);
-  if (is_foreground_color) {
-      cairo_set_source (cr, render->foreground_color);
-  } else
-      cairo_set_source_rgba (cr, color.red, color.green, color.blue, color.alpha);
+    get_palette_color (render, &solid->color, &color, &is_foreground_color);
+    if (is_foreground_color)
+	cairo_set_source (cr, render->foreground_color);
+    else
+	cairo_set_source_rgba (cr, color.red, color.green, color.blue, color.alpha);
 
-  cairo_paint (cr);
+    cairo_paint (cr);
 
-  return CAIRO_STATUS_SUCCESS;
+    return CAIRO_STATUS_SUCCESS;
 }
 
-typedef struct {
-  cairo_color_t color;
-  float position;
-} ColorStop;
+typedef struct _cairo_colr_color_stop {
+    cairo_color_t color;
+    double position;
+} cairo_colr_color_stop_t;
 
-typedef struct {
-  int n_stops;
-  ColorStop *stops;
-} ColorLine;
+typedef struct _cairo_colr_color_line {
+    int n_stops;
+    cairo_colr_color_stop_t *stops;
+} cairo_colr_color_line_t;
 
 static void
-free_colorline (ColorLine *cl)
+free_colorline (cairo_colr_color_line_t *cl)
 {
-  free (cl->stops);
-  free (cl);
+    free (cl->stops);
+    free (cl);
 }
 
 static int
-compare_stops (const void *p1, const void *p2)
+_compare_stops (const void *p1, const void *p2)
 {
-  const ColorStop *c1 = p1;
-  const ColorStop *c2 = p2;
-
-  if (c1->position < c2->position)
-    return -1;
-  else if (c1->position > c2->position)
-    return 1;
-  else
-    return 0;
+    const cairo_colr_color_stop_t *c1 = p1;
+    const cairo_colr_color_stop_t *c2 = p2;
+
+    if (c1->position < c2->position)
+	return -1;
+    else if (c1->position > c2->position)
+	return 1;
+    else
+	return 0;
 }
 
-static ColorLine *
+static cairo_colr_color_line_t *
 read_colorline (cairo_colr_glyph_render_t *render,
-                FT_ColorLine *colorline)
+                FT_ColorLine              *colorline)
 {
-  ColorLine *cl;
-  FT_ColorStop stop;
-  int i;
-
-  cl = calloc (1, sizeof (ColorLine));
-  if (unlikely (cl == NULL))
-    return NULL;
-
-  cl->n_stops = colorline->color_stop_iterator.num_color_stops;
-  cl->stops = calloc (cl->n_stops, sizeof (ColorStop));
-  if (unlikely (cl->stops == NULL)) {
-    free (cl);
-    return NULL;
-  }
+    cairo_colr_color_line_t *cl;
+    FT_ColorStop stop;
+    int i;
+
+    cl = calloc (1, sizeof (cairo_colr_color_line_t));
+    if (unlikely (cl == NULL))
+	return NULL;
+
+    cl->n_stops = colorline->color_stop_iterator.num_color_stops;
+    cl->stops = calloc (cl->n_stops, sizeof (cairo_colr_color_stop_t));
+    if (unlikely (cl->stops == NULL)) {
+	free (cl);
+	return NULL;
+    }
 
-  i = 0;
-  while (FT_Get_Colorline_Stops (render->face, &stop, &colorline->color_stop_iterator))
-    {
-      cl->stops[i].position = f16_16 (stop.stop_offset);
-      get_palette_color (render, &stop.color, &cl->stops[i].color, NULL);
-      i++;
+    i = 0;
+    while (FT_Get_Colorline_Stops (render->face, &stop, &colorline->color_stop_iterator)) {
+	cl->stops[i].position = double_from_16_16 (stop.stop_offset);
+	get_palette_color (render, &stop.color, &cl->stops[i].color, NULL);
+	i++;
     }
 
-  qsort (cl->stops, cl->n_stops, sizeof (ColorStop), compare_stops);
+    qsort (cl->stops, cl->n_stops, sizeof (cairo_colr_color_stop_t), _compare_stops);
 
-  return cl;
+    return cl;
 }
 
 static void
 reduce_anchors (FT_PaintLinearGradient *gradient,
-                Point *pp0,
-                Point *pp1)
+                cairo_point_double_t   *pp0,
+                cairo_point_double_t   *pp1)
 {
-  Point p0, p1, p2;
-  Point q1, q2;
-  float s;
-  float k;
-
-  p0.x = f16_16 (gradient->p0.x);
-  p0.y = f16_16 (gradient->p0.y);
-  p1.x = f16_16 (gradient->p1.x);
-  p1.y = f16_16 (gradient->p1.y);
-  p2.x = f16_16 (gradient->p2.x);
-  p2.y = f16_16 (gradient->p2.y);
-
-  q2.x = p2.x - p0.x;
-  q2.y = p2.y - p0.y;
-  q1.x = p1.x - p0.x;
-  q1.y = p1.y - p0.y;
-
-  s = q2.x * q2.x + q2.y * q2.y;
-  if (s < 0.000001)
+    cairo_point_double_t p0, p1, p2;
+    cairo_point_double_t q1, q2;
+    double s;
+    double k;
+
+    p0.x = double_from_16_16 (gradient->p0.x);
+    p0.y = double_from_16_16 (gradient->p0.y);
+    p1.x = double_from_16_16 (gradient->p1.x);
+    p1.y = double_from_16_16 (gradient->p1.y);
+    p2.x = double_from_16_16 (gradient->p2.x);
+    p2.y = double_from_16_16 (gradient->p2.y);
+
+    q2.x = p2.x - p0.x;
+    q2.y = p2.y - p0.y;
+    q1.x = p1.x - p0.x;
+    q1.y = p1.y - p0.y;
+
+    s = q2.x * q2.x + q2.y * q2.y;
+    if (s < 0.000001)
     {
-      pp0->x = p0.x; pp0->y = p0.y;
-      pp1->x = p1.x; pp1->y = p1.y;
-      return;
+	pp0->x = p0.x; pp0->y = p0.y;
+	pp1->x = p1.x; pp1->y = p1.y;
+	return;
     }
 
-  k = (q2.x * q1.x + q2.y * q1.y) / s;
-  pp0->x = p0.x;
-  pp0->y = p0.y;
-  pp1->x = p1.x - k * q2.x;
-  pp1->y = p1.y - k * q2.y;
+    k = (q2.x * q1.x + q2.y * q1.y) / s;
+    pp0->x = p0.x;
+    pp0->y = p0.y;
+    pp1->x = p1.x - k * q2.x;
+    pp1->y = p1.y - k * q2.y;
 }
 
 static void
-normalize_colorline (ColorLine *cl,
-                     float *out_min,
-                     float *out_max)
+normalize_colorline (cairo_colr_color_line_t *cl,
+                     double                  *out_min,
+                     double                  *out_max)
 {
-  float min, max;
+    double min, max;
 
-  *out_min = 0.;
-  *out_max = 1.;
+    *out_min = 0.;
+    *out_max = 1.;
 
-  min = max = cl->stops[0].position;
-  for (int i = 0; i < cl->n_stops; i++)
-    {
-      ColorStop *stop = &cl->stops[i];
-      min = MIN (min, stop->position);
-      max = MAX (max, stop->position);
+    min = max = cl->stops[0].position;
+    for (int i = 0; i < cl->n_stops; i++) {
+	cairo_colr_color_stop_t *stop = &cl->stops[i];
+	min = MIN (min, stop->position);
+	max = MAX (max, stop->position);
     }
 
-  if (min != max)
-    {
-      for (int i = 0; i < cl->n_stops; i++)
-        {
-          ColorStop *stop = &cl->stops[i];
-          stop->position = (stop->position - min) / (max - min);
+    if (min != max) {
+	for (int i = 0; i < cl->n_stops; i++) {
+	    cairo_colr_color_stop_t *stop = &cl->stops[i];
+	    stop->position = (stop->position - min) / (max - min);
         }
-      *out_min = min;
-      *out_max = max;
+	*out_min = min;
+	*out_max = max;
     }
 }
 
 static cairo_status_t
 draw_paint_linear_gradient (cairo_colr_glyph_render_t *render,
-                            FT_PaintLinearGradient *gradient,
-                            cairo_t *cr)
+                            FT_PaintLinearGradient    *gradient,
+                            cairo_t                   *cr)
 {
-  ColorLine *cl;
-  Point p0, p1;
-  Point pp0, pp1;
-  cairo_pattern_t *pattern;
-  cairo_status_t status = CAIRO_STATUS_SUCCESS;
-  float min, max;
+    cairo_colr_color_line_t *cl;
+    cairo_point_double_t p0, p1;
+    cairo_point_double_t pp0, pp1;
+    cairo_pattern_t *pattern;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    double min, max;
 
 #if DEBUG_COLR
-  printf ("%*sDraw PaintLinearGradient\n", 2 * render->level, "");
+    printf ("%*sDraw PaintLinearGradient\n", 2 * render->level, "");
 #endif
 
-  cl = read_colorline (render, &gradient->colorline);
-  if (unlikely (cl == NULL))
-    return CAIRO_STATUS_NO_MEMORY;
+    cl = read_colorline (render, &gradient->colorline);
+    if (unlikely (cl == NULL))
+	return CAIRO_STATUS_NO_MEMORY;
 
-  /* cairo only allows stop positions between 0 and 1 */
-  normalize_colorline (cl, &min, &max);
-  reduce_anchors (gradient, &p0, &p1);
-  interpolate_points (&p0, &p1, min, &pp0);
-  interpolate_points (&p0, &p1, max, &pp1);
+    /* cairo only allows stop positions between 0 and 1 */
+    normalize_colorline (cl, &min, &max);
+    reduce_anchors (gradient, &p0, &p1);
+    interpolate_points (&p0, &p1, min, &pp0);
+    interpolate_points (&p0, &p1, max, &pp1);
 
-  pattern = cairo_pattern_create_linear (pp0.x, pp0.y, pp1.x, pp1.y);
+    pattern = cairo_pattern_create_linear (pp0.x, pp0.y, pp1.x, pp1.y);
 
-  cairo_pattern_set_extend (pattern, cairo_extend (gradient->colorline.extend));
+    cairo_pattern_set_extend (pattern, cairo_extend_from_ft_paint_extend (gradient->colorline.extend));
 
-  for (int i = 0; i < cl->n_stops; i++)
-    {
-      ColorStop *stop = &cl->stops[i];
-      cairo_pattern_add_color_stop_rgba (pattern, stop->position,
-                                         stop->color.red, stop->color.green, stop->color.blue, stop->color.alpha);
+    for (int i = 0; i < cl->n_stops; i++) {
+	cairo_colr_color_stop_t *stop = &cl->stops[i];
+	cairo_pattern_add_color_stop_rgba (pattern, stop->position,
+					   stop->color.red, stop->color.green, stop->color.blue, stop->color.alpha);
     }
 
-  cairo_set_source (cr, pattern);
-  cairo_paint (cr);
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
 
-  cairo_pattern_destroy (pattern);
+    cairo_pattern_destroy (pattern);
 
-  free_colorline (cl);
+    free_colorline (cl);
 
-  return status;
+    return status;
 }
 
 static cairo_status_t
@@ -457,360 +448,330 @@ draw_paint_radial_gradient (cairo_colr_glyph_render_t *render,
                             FT_PaintRadialGradient *gradient,
                             cairo_t *cr)
 {
-  ColorLine *cl;
-  Point start, end;
-  Point start1, end1;
-  float start_radius, end_radius;
-  float start_radius1, end_radius1;
-  float min, max;
-  cairo_pattern_t *pattern;
-  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_colr_color_line_t *cl;
+    cairo_point_double_t start, end;
+    cairo_point_double_t start1, end1;
+    double start_radius, end_radius;
+    double start_radius1, end_radius1;
+    double min, max;
+    cairo_pattern_t *pattern;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
 #if DEBUG_COLR
-  printf ("%*sDraw PaintRadialGradient\n", 2 * render->level, "");
+    printf ("%*sDraw PaintRadialGradient\n", 2 * render->level, "");
 #endif
 
-  cl = read_colorline (render, &gradient->colorline);
-  if (unlikely (cl == NULL))
-    return CAIRO_STATUS_NO_MEMORY;
+    cl = read_colorline (render, &gradient->colorline);
+    if (unlikely (cl == NULL))
+	return CAIRO_STATUS_NO_MEMORY;
 
-  start.x = f16_16 (gradient->c0.x);
-  start.y = f16_16 (gradient->c0.y);
-  end.x = f16_16 (gradient->c1.x);
-  end.y = f16_16 (gradient->c1.y);
+    start.x = double_from_16_16 (gradient->c0.x);
+    start.y = double_from_16_16 (gradient->c0.y);
+    end.x = double_from_16_16 (gradient->c1.x);
+    end.y = double_from_16_16 (gradient->c1.y);
 
-  start_radius = f16_16 (gradient->r0);
-  end_radius = f16_16 (gradient->r1);
+    start_radius = double_from_16_16 (gradient->r0);
+    end_radius = double_from_16_16 (gradient->r1);
 
-  /* cairo only allows stop positions between 0 and 1 */
-  normalize_colorline (cl, &min, &max);
-  interpolate_points (&start, &end, min, &start1);
-  interpolate_points (&start, &end, max, &end1);
-  start_radius1 = interpolate (start_radius, end_radius, min);
-  end_radius1 = interpolate (start_radius, end_radius, max);
+    /* cairo only allows stop positions between 0 and 1 */
+    normalize_colorline (cl, &min, &max);
+    interpolate_points (&start, &end, min, &start1);
+    interpolate_points (&start, &end, max, &end1);
+    start_radius1 = interpolate (start_radius, end_radius, min);
+    end_radius1 = interpolate (start_radius, end_radius, max);
 
-  pattern = cairo_pattern_create_radial (start1.x, start1.y, start_radius1,
-                                         end1.x, end1.y, end_radius1);
+    pattern = cairo_pattern_create_radial (start1.x, start1.y, start_radius1,
+					   end1.x, end1.y, end_radius1);
 
-  cairo_pattern_set_extend (pattern, cairo_extend (gradient->colorline.extend));
+    cairo_pattern_set_extend (pattern, cairo_extend_from_ft_paint_extend (gradient->colorline.extend));
 
-  for (int i = 0; i < cl->n_stops; i++)
-    {
-      ColorStop *stop = &cl->stops[i];
-      cairo_pattern_add_color_stop_rgba (pattern, stop->position,
-                                         stop->color.red, stop->color.green, stop->color.blue, stop->color.alpha);
+    for (int i = 0; i < cl->n_stops; i++) {
+	cairo_colr_color_stop_t *stop = &cl->stops[i];
+	cairo_pattern_add_color_stop_rgba (pattern, stop->position,
+					   stop->color.red, stop->color.green, stop->color.blue, stop->color.alpha);
     }
 
-  cairo_set_source (cr, pattern);
-  cairo_paint (cr);
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
 
-  cairo_pattern_destroy (pattern);
+    cairo_pattern_destroy (pattern);
 
-  free_colorline (cl);
+    free_colorline (cl);
 
-  return status;
+    return status;
 }
 
 typedef struct {
-  Point center, p0, c0, c1, p1;
-  cairo_color_t color0, color1;
-} Patch;
+    cairo_point_double_t center, p0, c0, c1, p1;
+    cairo_color_t color0, color1;
+} cairo_colr_gradient_patch_t;
 
 static void
-add_patch (cairo_pattern_t *pattern, Point *center, Patch *p)
+add_patch (cairo_pattern_t             *pattern,
+	   cairo_point_double_t        *center,
+	   cairo_colr_gradient_patch_t *p)
 {
-  cairo_mesh_pattern_begin_patch (pattern);
-  cairo_mesh_pattern_move_to (pattern, center->x, center->y);
-  cairo_mesh_pattern_line_to (pattern, p->p0.x, p->p0.y);
-  cairo_mesh_pattern_curve_to (pattern,
-                               p->c0.x, p->c0.y,
-                               p->c1.x, p->c1.y,
-                               p->p1.x, p->p1.y);
-  cairo_mesh_pattern_line_to (pattern, center->x, center->y);
-  cairo_mesh_pattern_set_corner_color_rgba (pattern, 0,
-                                            p->color0.red,
-                                            p->color0.green,
-                                            p->color0.blue,
-                                            p->color0.alpha);
-  cairo_mesh_pattern_set_corner_color_rgba (pattern, 1,
-                                            p->color0.red,
-                                            p->color0.green,
-                                            p->color0.blue,
-                                            p->color0.alpha);
-  cairo_mesh_pattern_set_corner_color_rgba (pattern, 2,
-                                            p->color1.red,
-                                            p->color1.green,
-                                            p->color1.blue,
-                                            p->color1.alpha);
-  cairo_mesh_pattern_set_corner_color_rgba (pattern, 3,
-                                            p->color1.red,
-                                            p->color1.green,
-                                            p->color1.blue,
-                                            p->color1.alpha);
-  cairo_mesh_pattern_end_patch (pattern);
+    cairo_mesh_pattern_begin_patch (pattern);
+    cairo_mesh_pattern_move_to (pattern, center->x, center->y);
+    cairo_mesh_pattern_line_to (pattern, p->p0.x, p->p0.y);
+    cairo_mesh_pattern_curve_to (pattern,
+				 p->c0.x, p->c0.y,
+				 p->c1.x, p->c1.y,
+				 p->p1.x, p->p1.y);
+    cairo_mesh_pattern_line_to (pattern, center->x, center->y);
+    cairo_mesh_pattern_set_corner_color_rgba (pattern, 0,
+					      p->color0.red,
+					      p->color0.green,
+					      p->color0.blue,
+					      p->color0.alpha);
+    cairo_mesh_pattern_set_corner_color_rgba (pattern, 1,
+					      p->color0.red,
+					      p->color0.green,
+					      p->color0.blue,
+					      p->color0.alpha);
+    cairo_mesh_pattern_set_corner_color_rgba (pattern, 2,
+					      p->color1.red,
+					      p->color1.green,
+					      p->color1.blue,
+					      p->color1.alpha);
+    cairo_mesh_pattern_set_corner_color_rgba (pattern, 3,
+					      p->color1.red,
+					      p->color1.green,
+					      p->color1.blue,
+					      p->color1.alpha);
+    cairo_mesh_pattern_end_patch (pattern);
 }
 
 #define MAX_ANGLE (M_PI / 8.)
 
 static void
-add_sweep_gradient_patches1 (Point *center, float radius,
-                             float a0, cairo_color_t *c0,
-                             float a1, cairo_color_t *c1,
-                             cairo_pattern_t *pattern)
+add_sweep_gradient_patches1 (cairo_point_double_t *center,
+			     double                radius,
+                             double                a0,
+			     cairo_color_t        *c0,
+                             double                a1,
+			     cairo_color_t        *c1,
+                             cairo_pattern_t      *pattern)
 {
 
-  int num_splits;
-  Point p0;
-  cairo_color_t color0, color1;
+    int num_splits;
+    cairo_point_double_t p0;
+    cairo_color_t color0, color1;
 
-  num_splits = ceilf (fabs (a1 - a0) / MAX_ANGLE);
-  p0 = (Point) { cosf (a0), sinf (a0) };
-  color0 = *c0;
+    num_splits = ceilf (fabs (a1 - a0) / MAX_ANGLE);
+    p0 = (cairo_point_double_t) { cosf (a0), sinf (a0) };
+    color0 = *c0;
 
-  for (int a = 0; a < num_splits; a++)
-    {
-      float k = (a + 1.) / num_splits;
-      float angle1;
-      Point p1;
-      Point A, U;
-      Point C0, C1;
-      Patch patch;
-
-      angle1 = interpolate (a0, a1, k);
-      interpolate_colors (c0, c1, k, &color1);
-
-      patch.color0 = color0;
-      patch.color1 = color1;
-
-      p1 = (Point) { cosf (angle1), sinf (angle1) };
-      patch.p0 = sum (*center, scale (p0, radius));
-      patch.p1 = sum (*center, scale (p1, radius));
-
-      A = normalize (sum (p0, p1));
-      U = (Point) { -A.y, A.x };
-      C0 = sum (A, scale (U, dot (difference (p0, A), p0) / dot (U, p0)));
-      C1 = sum (A, scale (U, dot (difference (p1, A), p1) / dot (U, p1)));
-      patch.c0 = sum (*center, scale (sum (C0, scale (difference (C0, p0), 0.33333)), radius));
-      patch.c1 = sum (*center, scale (sum (C1, scale (difference (C1, p1), 0.33333)), radius));
-
-      add_patch (pattern, center, &patch);
-
-      p0 = p1;
-      color0 = color1;
+    for (int a = 0; a < num_splits; a++) {
+	double k = (a + 1.) / num_splits;
+	double angle1;
+	cairo_point_double_t p1;
+	cairo_point_double_t A, U;
+	cairo_point_double_t C0, C1;
+	cairo_colr_gradient_patch_t patch;
+
+	angle1 = interpolate (a0, a1, k);
+	interpolate_colors (c0, c1, k, &color1);
+
+	patch.color0 = color0;
+	patch.color1 = color1;
+
+	p1 = (cairo_point_double_t) { cosf (angle1), sinf (angle1) };
+	patch.p0 = sum (*center, scale (p0, radius));
+	patch.p1 = sum (*center, scale (p1, radius));
+
+	A = normalize (sum (p0, p1));
+	U = (cairo_point_double_t) { -A.y, A.x };
+	C0 = sum (A, scale (U, dot (difference (p0, A), p0) / dot (U, p0)));
+	C1 = sum (A, scale (U, dot (difference (p1, A), p1) / dot (U, p1)));
+	patch.c0 = sum (*center, scale (sum (C0, scale (difference (C0, p0), 0.33333)), radius));
+	patch.c1 = sum (*center, scale (sum (C1, scale (difference (C1, p1), 0.33333)), radius));
+
+	add_patch (pattern, center, &patch);
+
+	p0 = p1;
+	color0 = color1;
     }
 }
 
 static void
-add_sweep_gradient_patches (ColorLine *cl,
-                            cairo_extend_t extend,
-                            Point *center,
-                            float radius,
-                            float start_angle,
-                            float end_angle,
-                            cairo_pattern_t *pattern)
+add_sweep_gradient_patches (cairo_colr_color_line_t *cl,
+                            cairo_extend_t           extend,
+                            cairo_point_double_t    *center,
+                            double                   radius,
+                            double                   start_angle,
+                            double                   end_angle,
+                            cairo_pattern_t         *pattern)
 {
-  float *angles;
-  cairo_color_t color0, color1;
-
-  if (start_angle == end_angle)
-    {
-      if (extend == CAIRO_EXTEND_PAD)
-        {
-          if (start_angle > 0)
-            add_sweep_gradient_patches1 (center, radius,
-                                         0.,          &cl->stops[0].color,
-                                         start_angle, &cl->stops[0].color,
-                                         pattern);
-          if (end_angle < 2 * M_PI)
-            add_sweep_gradient_patches1 (center, radius,
-                                         end_angle, &cl->stops[cl->n_stops - 1].color,
-                                         2 * M_PI,  &cl->stops[cl->n_stops - 1].color,
-                                         pattern);
+    double *angles;
+    cairo_color_t color0, color1;
+
+    if (start_angle == end_angle) {
+	if (extend == CAIRO_EXTEND_PAD) {
+	    if (start_angle > 0)
+		add_sweep_gradient_patches1 (center, radius,
+					     0.,          &cl->stops[0].color,
+					     start_angle, &cl->stops[0].color,
+					     pattern);
+	    if (end_angle < 2 * M_PI)
+		add_sweep_gradient_patches1 (center, radius,
+					     end_angle, &cl->stops[cl->n_stops - 1].color,
+					     2 * M_PI,  &cl->stops[cl->n_stops - 1].color,
+					     pattern);
         }
-      return;
+	return;
     }
 
-  assert (start_angle != end_angle);
+    assert (start_angle != end_angle);
 
-  angles = alloca (sizeof (float) * cl->n_stops);
+    angles = alloca (sizeof (double) * cl->n_stops);
 
-  for (int i = 0; i < cl->n_stops; i++)
-    angles[i] = start_angle + cl->stops[i].position * (end_angle - start_angle);
+    for (int i = 0; i < cl->n_stops; i++)
+	angles[i] = start_angle + cl->stops[i].position * (end_angle - start_angle);
 
-  /* handle directions */
-  if (end_angle < start_angle)
-    {
-      for (int i = 0; i < cl->n_stops - 1 - i; i++)
-        {
-          ColorStop stop = cl->stops[i];
-          float a = angles[i];
-          cl->stops[i] = cl->stops[cl->n_stops - 1 - i];
-          cl->stops[cl->n_stops - 1 - i] = stop;
-          angles[i] = angles[cl->n_stops - 1 - i];
-          angles[cl->n_stops - 1 - i] = a;
+    /* handle directions */
+    if (end_angle < start_angle) {
+	for (int i = 0; i < cl->n_stops - 1 - i; i++) {
+	    cairo_colr_color_stop_t stop = cl->stops[i];
+	    double a = angles[i];
+	    cl->stops[i] = cl->stops[cl->n_stops - 1 - i];
+	    cl->stops[cl->n_stops - 1 - i] = stop;
+	    angles[i] = angles[cl->n_stops - 1 - i];
+	    angles[cl->n_stops - 1 - i] = a;
         }
     }
 
-  if (extend == CAIRO_EXTEND_PAD)
+    if (extend == CAIRO_EXTEND_PAD)
     {
-      int pos;
-
-      color0 = cl->stops[0].color;
-      for (pos = 0; pos < cl->n_stops; pos++)
-        {
-          if (angles[pos] >= 0)
-            {
-              if (pos > 0)
-                {
-                  float k = (0 - angles[pos - 1]) / (angles[pos] - angles[pos - 1]);
-                  interpolate_colors (&cl->stops[pos - 1].color, &cl->stops[pos].color, k, &color0);
+	int pos;
+
+	color0 = cl->stops[0].color;
+	for (pos = 0; pos < cl->n_stops; pos++) {
+	    if (angles[pos] >= 0) {
+		if (pos > 0) {
+		    double k = (0 - angles[pos - 1]) / (angles[pos] - angles[pos - 1]);
+		    interpolate_colors (&cl->stops[pos - 1].color, &cl->stops[pos].color, k, &color0);
                 }
-              break;
+		break;
             }
         }
-      if (pos == cl->n_stops)
-        {
-          /* everything is below 0 */
-          color0 = cl->stops[cl->n_stops - 1].color;
-          add_sweep_gradient_patches1 (center, radius,
-                                       0.,       &color0,
-                                       2 * M_PI, &color0,
-                                       pattern);
-          return;
+	if (pos == cl->n_stops) {
+	    /* everything is below 0 */
+	    color0 = cl->stops[cl->n_stops - 1].color;
+	    add_sweep_gradient_patches1 (center, radius,
+					 0.,       &color0,
+					 2 * M_PI, &color0,
+					 pattern);
+	    return;
         }
 
-      add_sweep_gradient_patches1 (center, radius,
-                                   0.,          &color0,
-                                   angles[pos], &cl->stops[pos].color,
-                                   pattern);
-
-      for (pos++; pos < cl->n_stops; pos++)
-        {
-          if (angles[pos] <= 2 * M_PI)
-            {
-              add_sweep_gradient_patches1 (center, radius,
-                                           angles[pos - 1], &cl->stops[pos - 1].color,
-                                           angles[pos],     &cl->stops[pos].color,
-                                           pattern);
-            }
-          else
-            {
-              float k = (2 * M_PI - angles[pos - 1]) / (angles[pos] - angles[pos - 1]);
-              interpolate_colors (&cl->stops[pos - 1].color, &cl->stops[pos].color, k, &color1);
-              add_sweep_gradient_patches1 (center, radius,
-                                           angles[pos - 1], &cl->stops[pos - 1].color,
-                                           2 * M_PI,        &color1,
-                                           pattern);
-              break;
+	add_sweep_gradient_patches1 (center, radius,
+				     0.,          &color0,
+				     angles[pos], &cl->stops[pos].color,
+				     pattern);
+
+	for (pos++; pos < cl->n_stops; pos++) {
+	    if (angles[pos] <= 2 * M_PI) {
+		add_sweep_gradient_patches1 (center, radius,
+					     angles[pos - 1], &cl->stops[pos - 1].color,
+					     angles[pos],     &cl->stops[pos].color,
+					     pattern);
+            } else {
+		double k = (2 * M_PI - angles[pos - 1]) / (angles[pos] - angles[pos - 1]);
+		interpolate_colors (&cl->stops[pos - 1].color, &cl->stops[pos].color, k, &color1);
+		add_sweep_gradient_patches1 (center, radius,
+					     angles[pos - 1], &cl->stops[pos - 1].color,
+					     2 * M_PI,        &color1,
+					     pattern);
+		break;
             }
         }
 
-      if (pos == cl->n_stops)
-        {
-          /* everything is below 2*M_PI */
-          color0 = cl->stops[cl->n_stops - 1].color;
-          add_sweep_gradient_patches1 (center, radius,
-                                       angles[cl->n_stops - 1], &color0,
-                                       2 * M_PI,                &color0,
-                                       pattern);
-          return;
+	if (pos == cl->n_stops) {
+	    /* everything is below 2*M_PI */
+	    color0 = cl->stops[cl->n_stops - 1].color;
+	    add_sweep_gradient_patches1 (center, radius,
+					 angles[cl->n_stops - 1], &color0,
+					 2 * M_PI,                &color0,
+					 pattern);
+	    return;
         }
-    }
-  else
-    {
-      int k;
-      float span;
-
-      span = angles[cl->n_stops - 1] - angles[0];
-      k = 0;
-      if (angles[0] >= 0)
-        {
-          float ss = angles[0];
-          while (ss > 0)
-            {
-              if (span > 0)
-                {
-                  ss -= span;
-                  k--;
-                }
-              else
-                {
-                  ss += span;
-                  k++;
+    } else {
+	int k;
+	double span;
+
+	span = angles[cl->n_stops - 1] - angles[0];
+	k = 0;
+	if (angles[0] >= 0) {
+	    double ss = angles[0];
+	    while (ss > 0) {
+		if (span > 0) {
+		    ss -= span;
+		    k--;
+                } else {
+		    ss += span;
+		    k++;
                 }
             }
         }
-      else if (angles[0] < 0)
+	else if (angles[0] < 0)
         {
-          float ee = angles[cl->n_stops - 1];
-          while (ee < 0)
-            {
-              if (span > 0)
-                {
-                  ee += span;
-                  k++;
-                }
-              else
-                {
-                  ee -= span;
-                  k--;
+	    double ee = angles[cl->n_stops - 1];
+	    while (ee < 0) {
+		if (span > 0) {
+		    ee += span;
+		    k++;
+                } else {
+		    ee -= span;
+		    k--;
                 }
             }
         }
 
-      //assert (angles[0] + k * span <= 0 && 0 < angles[cl->n_stops - 1] + k * span);
-
-      for (int l = k; TRUE; l++)
-        {
-          for (int i = 1; i < cl->n_stops; i++)
-            {
-              float a0, a1;
-              cairo_color_t *c0, *c1;
-
-              if ((l % 2 != 0) && (extend == CAIRO_EXTEND_REFLECT))
-                {
-                  a0 = angles[0] + angles[cl->n_stops - 1] - angles[cl->n_stops - 1 - (i-1)] + l * span;
-                  a1 = angles[0] + angles[cl->n_stops - 1] - angles[cl->n_stops - 1 - i] + l * span;
-                  c0 = &cl->stops[cl->n_stops - 1 - (i-1)].color;
-                  c1 = &cl->stops[cl->n_stops - 1 - i].color;
-                }
-              else
-                {
-                  a0 = angles[i-1] + l * span;
-                  a1 = angles[i] + l * span;
-                  c0 = &cl->stops[i-1].color;
-                  c1 = &cl->stops[i].color;
+	//assert (angles[0] + k * span <= 0 && 0 < angles[cl->n_stops - 1] + k * span);
+
+	for (int l = k; TRUE; l++) {
+	    for (int i = 1; i < cl->n_stops; i++) {
+		double a0, a1;
+		cairo_color_t *c0, *c1;
+
+		if ((l % 2 != 0) && (extend == CAIRO_EXTEND_REFLECT)) {
+		    a0 = angles[0] + angles[cl->n_stops - 1] - angles[cl->n_stops - 1 - (i-1)] + l * span;
+		    a1 = angles[0] + angles[cl->n_stops - 1] - angles[cl->n_stops - 1 - i] + l * span;
+		    c0 = &cl->stops[cl->n_stops - 1 - (i-1)].color;
+		    c1 = &cl->stops[cl->n_stops - 1 - i].color;
+                } else {
+		    a0 = angles[i-1] + l * span;
+		    a1 = angles[i] + l * span;
+		    c0 = &cl->stops[i-1].color;
+		    c1 = &cl->stops[i].color;
                 }
 
-              if (a1 < 0)
-                continue;
-              if (a0 < 0)
-                {
-                  cairo_color_t color;
-                  float f = (0 - a0)/(a1 - a0);
-                  interpolate_colors (c0, c1, f, &color);
-                  add_sweep_gradient_patches1 (center, radius,
-                                               0,  &color,
-                                               a1, c1,
-                                               pattern);
-                }
-              else if (a1 >= 2 * M_PI)
-                {
-                  cairo_color_t color;
-                  float f = (2 * M_PI - a0)/(a1 - a0);
-                  interpolate_colors (c0, c1, f, &color);
-                  add_sweep_gradient_patches1 (center, radius,
-                                               a0,       c0,
-                                               2 * M_PI, &color,
-                                               pattern);
-                  return;
-                }
-              else
-                {
-                  add_sweep_gradient_patches1 (center, radius,
-                                               a0, c0,
-                                               a1, c1,
-                                               pattern);
+		if (a1 < 0)
+		    continue;
+
+		if (a0 < 0) {
+		    cairo_color_t color;
+		    double f = (0 - a0)/(a1 - a0);
+		    interpolate_colors (c0, c1, f, &color);
+		    add_sweep_gradient_patches1 (center, radius,
+						 0,  &color,
+						 a1, c1,
+						 pattern);
+                } else if (a1 >= 2 * M_PI) {
+		    cairo_color_t color;
+		    double f = (2 * M_PI - a0)/(a1 - a0);
+		    interpolate_colors (c0, c1, f, &color);
+		    add_sweep_gradient_patches1 (center, radius,
+						 a0,       c0,
+						 2 * M_PI, &color,
+						 pattern);
+		    return;
+                } else {
+		    add_sweep_gradient_patches1 (center, radius,
+						 a0, c0,
+						 a1, c1,
+						 pattern);
                 }
             }
         }
@@ -819,55 +780,55 @@ add_sweep_gradient_patches (ColorLine *cl,
 
 static cairo_status_t
 draw_paint_sweep_gradient (cairo_colr_glyph_render_t *render,
-                           FT_PaintSweepGradient *gradient,
-                           cairo_t *cr)
+                           FT_PaintSweepGradient     *gradient,
+                           cairo_t                   *cr)
 {
-  ColorLine *cl;
-  Point center;
-  float start_angle, end_angle;
-  double x1, y1, x2, y2;
-  double max_x, max_y, R;
-  cairo_pattern_t *pattern;
-  cairo_extend_t extend;
+    cairo_colr_color_line_t *cl;
+    cairo_point_double_t center;
+    double start_angle, end_angle;
+    double x1, y1, x2, y2;
+    double max_x, max_y, R;
+    cairo_pattern_t *pattern;
+    cairo_extend_t extend;
 
 #if DEBUG_COLR
-  printf ("%*sDraw PaintSweepGradient\n", 2 * render->level, "");
+    printf ("%*sDraw PaintSweepGradient\n", 2 * render->level, "");
 #endif
 
-  cl = read_colorline (render, &gradient->colorline);
-  if (unlikely (cl == NULL))
-    return CAIRO_STATUS_NO_MEMORY;
+    cl = read_colorline (render, &gradient->colorline);
+    if (unlikely (cl == NULL))
+	return CAIRO_STATUS_NO_MEMORY;
 
-  center.x = f16_16 (gradient->center.x);
-  center.y = f16_16 (gradient->center.y);
-  start_angle = (f16_16 (gradient->start_angle) + 1) * M_PI;
-  end_angle = (f16_16 (gradient->end_angle) + 1) * M_PI;
+    center.x = double_from_16_16 (gradient->center.x);
+    center.y = double_from_16_16 (gradient->center.y);
+    start_angle = (double_from_16_16 (gradient->start_angle) + 1) * M_PI;
+    end_angle = (double_from_16_16 (gradient->end_angle) + 1) * M_PI;
 
-  pattern = cairo_pattern_create_mesh ();
+    pattern = cairo_pattern_create_mesh ();
 
-  cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
-  max_x = MAX ((x1 - center.x) * (x1 - center.x), (x2 - center.x) * (x2 - center.x));
-  max_y = MAX ((y1 - center.y) * (y1 - center.y), (y2 - center.y) * (y2 - center.y));
-  R = sqrt (max_x + max_y);
+    cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
+    max_x = MAX ((x1 - center.x) * (x1 - center.x), (x2 - center.x) * (x2 - center.x));
+    max_y = MAX ((y1 - center.y) * (y1 - center.y), (y2 - center.y) * (y2 - center.y));
+    R = sqrt (max_x + max_y);
 
-  extend = cairo_extend (gradient->colorline.extend);
+    extend = cairo_extend_from_ft_paint_extend (gradient->colorline.extend);
 
-  add_sweep_gradient_patches (cl, extend, &center, R, start_angle, end_angle, pattern);
+    add_sweep_gradient_patches (cl, extend, &center, R, start_angle, end_angle, pattern);
 
-  cairo_set_source (cr, pattern);
-  cairo_paint (cr);
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
 
-  cairo_pattern_destroy (pattern);
+    cairo_pattern_destroy (pattern);
 
-  free_colorline (cl);
+    free_colorline (cl);
 
-  return CAIRO_STATUS_SUCCESS;
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_status_t
 draw_paint_glyph (cairo_colr_glyph_render_t *render,
-                  FT_PaintGlyph *glyph,
-                  cairo_t *cr)
+                  FT_PaintGlyph             *glyph,
+                  cairo_t                   *cr)
 {
     cairo_path_fixed_t *path_fixed;
     cairo_path_t *path;
@@ -908,18 +869,20 @@ draw_paint_glyph (cairo_colr_glyph_render_t *render,
 }
 
 static cairo_status_t draw_colr_glyph (cairo_colr_glyph_render_t *render,
-                                       unsigned int glyph,
-                                       FT_Color_Root_Transform  root,
-                                       cairo_t *cr);
+				       unsigned long              glyph,
+                                       FT_Color_Root_Transform    root,
+                                       cairo_t                   *cr);
 
 static cairo_status_t
 draw_paint_colr_glyph (cairo_colr_glyph_render_t *render,
                        FT_PaintColrGlyph *colr_glyph,
                        cairo_t *cr)
 {
-  //printf ("%*sDraw PaintColrGlyph\n", 2 * render->level, "");
+#if DEBUG_COLR
+    printf ("%*sDraw PaintColrGlyph\n", 2 * render->level, "");
+#endif
 
-  return draw_colr_glyph (render, colr_glyph->glyphID, FT_COLOR_NO_ROOT_TRANSFORM, cr);
+    return draw_colr_glyph (render, colr_glyph->glyphID, FT_COLOR_NO_ROOT_TRANSFORM, cr);
 }
 
 static cairo_status_t
@@ -927,29 +890,29 @@ draw_paint_transform (cairo_colr_glyph_render_t *render,
                       FT_PaintTransform *transform,
                       cairo_t *cr)
 {
-  cairo_matrix_t t;
-  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_matrix_t t;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
 #if DEBUG_COLR
-  printf ("%*sDraw PaintTransform\n", 2 * render->level, "");
+    printf ("%*sDraw PaintTransform\n", 2 * render->level, "");
 #endif
 
-  cairo_matrix_init (&t,
-                     f16_16 (transform->affine.xx),
-                     f16_16 (transform->affine.yx),
-                     f16_16 (transform->affine.xy),
-                     f16_16 (transform->affine.yy),
-                     f16_16 (transform->affine.dx),
-                     f16_16 (transform->affine.dy));
+    cairo_matrix_init (&t,
+		       double_from_16_16 (transform->affine.xx),
+		       double_from_16_16 (transform->affine.yx),
+		       double_from_16_16 (transform->affine.xy),
+		       double_from_16_16 (transform->affine.yy),
+		       double_from_16_16 (transform->affine.dx),
+		       double_from_16_16 (transform->affine.dy));
 
-  cairo_save (cr);
+    cairo_save (cr);
 
-  cairo_transform (cr, &t);
-  status = draw_paint (render, &transform->paint, cr);
+    cairo_transform (cr, &t);
+    status = draw_paint (render, &transform->paint, cr);
 
-  cairo_restore (cr);
+    cairo_restore (cr);
 
-  return status;
+    return status;
 }
 
 static cairo_status_t
@@ -957,20 +920,20 @@ draw_paint_translate (cairo_colr_glyph_render_t *render,
                       FT_PaintTranslate *translate,
                       cairo_t *cr)
 {
-  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
 #if DEBUG_COLR
-  printf ("%*sDraw PaintTranslate\n", 2 * render->level, "");
+    printf ("%*sDraw PaintTranslate\n", 2 * render->level, "");
 #endif
 
-  cairo_save (cr);
+    cairo_save (cr);
 
-  cairo_translate (cr, f16_16 (translate->dx), f16_16 (translate->dy));
-  status = draw_paint (render, &translate->paint, cr);
+    cairo_translate (cr, double_from_16_16 (translate->dx), double_from_16_16 (translate->dy));
+    status = draw_paint (render, &translate->paint, cr);
 
-  cairo_restore (cr);
+    cairo_restore (cr);
 
-  return status;
+    return status;
 }
 
 static cairo_status_t
@@ -978,22 +941,22 @@ draw_paint_rotate (cairo_colr_glyph_render_t *render,
                    FT_PaintRotate *rotate,
                    cairo_t *cr)
 {
-  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
 #if DEBUG_COLR
-  printf ("%*sDraw PaintRotate\n", 2 * render->level, "");
+    printf ("%*sDraw PaintRotate\n", 2 * render->level, "");
 #endif
 
-  cairo_save (cr);
+    cairo_save (cr);
 
-  cairo_translate (cr, f16_16 (rotate->center_x), f16_16 (rotate->center_y));
-  cairo_rotate (cr, f16_16 (rotate->angle) * M_PI);
-  cairo_translate (cr, - f16_16 (rotate->center_x), - f16_16 (rotate->center_y));
-  status = draw_paint (render, &rotate->paint, cr);
+    cairo_translate (cr, double_from_16_16 (rotate->center_x), double_from_16_16 (rotate->center_y));
+    cairo_rotate (cr, double_from_16_16 (rotate->angle) * M_PI);
+    cairo_translate (cr, - double_from_16_16 (rotate->center_x), - double_from_16_16 (rotate->center_y));
+    status = draw_paint (render, &rotate->paint, cr);
 
-  cairo_restore (cr);
+    cairo_restore (cr);
 
-  return status;
+    return status;
 }
 
 static cairo_status_t
@@ -1001,89 +964,89 @@ draw_paint_scale (cairo_colr_glyph_render_t *render,
                   FT_PaintScale *scale,
                   cairo_t *cr)
 {
-  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
 #if DEBUG_COLR
-  printf ("%*sDraw PaintScale\n", 2 * render->level, "");
+    printf ("%*sDraw PaintScale\n", 2 * render->level, "");
 #endif
 
-  cairo_save (cr);
+    cairo_save (cr);
 
-  cairo_translate (cr, f16_16 (scale->center_x), f16_16 (scale->center_y));
-  cairo_scale (cr, f16_16 (scale->scale_x), f16_16 (scale->scale_y));
-  cairo_translate (cr, - f16_16 (scale->center_x), - f16_16 (scale->center_y));
-  status = draw_paint (render, &scale->paint, cr);
+    cairo_translate (cr, double_from_16_16 (scale->center_x), double_from_16_16 (scale->center_y));
+    cairo_scale (cr, double_from_16_16 (scale->scale_x), double_from_16_16 (scale->scale_y));
+    cairo_translate (cr, - double_from_16_16 (scale->center_x), - double_from_16_16 (scale->center_y));
+    status = draw_paint (render, &scale->paint, cr);
 
-  cairo_restore (cr);
+    cairo_restore (cr);
 
-  return status;
+    return status;
 }
 
 static cairo_status_t
 draw_paint_skew (cairo_colr_glyph_render_t *render,
-                 FT_PaintSkew *skew,
-                 cairo_t *cr)
+                 FT_PaintSkew              *skew,
+                 cairo_t                   *cr)
 {
-  cairo_matrix_t s;
-  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_matrix_t s;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
 #if DEBUG_COLR
-  printf ("%*sDraw PaintSkew\n", 2 * render->level, "");
+    printf ("%*sDraw PaintSkew\n", 2 * render->level, "");
 #endif
 
-  cairo_save (cr);
+    cairo_save (cr);
 
-  cairo_translate (cr, f16_16 (skew->center_x), f16_16 (skew->center_y));
-  cairo_matrix_init (&s, 1., tan (f16_16 (skew->y_skew_angle) * M_PI), - tan (f16_16 (skew->x_skew_angle) * M_PI), 1., 0., 0.);
-  cairo_transform (cr, &s);
-  cairo_translate (cr, - f16_16 (skew->center_x), - f16_16 (skew->center_y));
-  status = draw_paint (render, &skew->paint, cr);
+    cairo_translate (cr, double_from_16_16 (skew->center_x), double_from_16_16 (skew->center_y));
+    cairo_matrix_init (&s, 1., tan (double_from_16_16 (skew->y_skew_angle) * M_PI), - tan (double_from_16_16 (skew->x_skew_angle) * M_PI), 1., 0., 0.);
+    cairo_transform (cr, &s);
+    cairo_translate (cr, - double_from_16_16 (skew->center_x), - double_from_16_16 (skew->center_y));
+    status = draw_paint (render, &skew->paint, cr);
 
-  cairo_restore (cr);
+    cairo_restore (cr);
 
-  return status;
+    return status;
 }
 
 static cairo_status_t
 draw_paint_composite (cairo_colr_glyph_render_t *render,
-                      FT_PaintComposite *composite,
-                      cairo_t *cr)
+                      FT_PaintComposite         *composite,
+                      cairo_t                   *cr)
 {
-  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
 
 #if DEBUG_COLR
-  printf ("%*sDraw PaintComposite\n", 2 * render->level, "");
+    printf ("%*sDraw PaintComposite\n", 2 * render->level, "");
 #endif
 
-  cairo_save (cr);
-
-  cairo_push_group (cr);
-  status = draw_paint (render, &composite->backdrop_paint, cr);
-  if (unlikely (status)) {
-    cairo_pattern_destroy (cairo_pop_group (cr));
-    goto cleanup;
-  }
-
-  cairo_push_group (cr);
-  status = draw_paint (render, &composite->source_paint, cr);
-  if (unlikely (status)) {
-    cairo_pattern_destroy (cairo_pop_group (cr));
-    cairo_pattern_destroy (cairo_pop_group (cr));
-    goto cleanup;
-  }
-
-  cairo_pop_group_to_source (cr);
-  cairo_set_operator (cr, cairo_operator (composite->composite_mode));
-  cairo_paint (cr);
-  cairo_pop_group_to_source (cr);
-  cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
-  cairo_paint (cr);
-
-cleanup:
-  cairo_restore (cr);
-
-  return status;
+    cairo_save (cr);
+
+    cairo_push_group (cr);
+    status = draw_paint (render, &composite->backdrop_paint, cr);
+    if (unlikely (status)) {
+	cairo_pattern_destroy (cairo_pop_group (cr));
+	goto cleanup;
+    }
+
+    cairo_push_group (cr);
+    status = draw_paint (render, &composite->source_paint, cr);
+    if (unlikely (status)) {
+	cairo_pattern_destroy (cairo_pop_group (cr));
+	cairo_pattern_destroy (cairo_pop_group (cr));
+	goto cleanup;
+    }
+
+    cairo_pop_group_to_source (cr);
+    cairo_set_operator (cr, cairo_operator_from_ft_composite_mode (composite->composite_mode));
+    cairo_paint (cr);
+    cairo_pop_group_to_source (cr);
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_paint (cr);
+
+  cleanup:
+    cairo_restore (cr);
+
+    return status;
 }
 
 static cairo_status_t
@@ -1091,196 +1054,182 @@ draw_paint (cairo_colr_glyph_render_t *render,
             FT_OpaquePaint *paint,
             cairo_t *cr)
 {
-  FT_COLR_Paint p;
-  FT_Size orig_size;
-  FT_Size unscaled_size;
-  FT_Matrix orig_transform;
-  FT_Vector orig_delta;
-  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    FT_COLR_Paint p;
+    FT_Size orig_size;
+    FT_Size unscaled_size;
+    FT_Matrix orig_transform;
+    FT_Vector orig_delta;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
-  assert (cairo_status (cr) == CAIRO_STATUS_SUCCESS);
+    assert (cairo_status (cr) == CAIRO_STATUS_SUCCESS);
 
-  if (!FT_Get_Paint (render->face, *paint, &p))
-    return CAIRO_STATUS_NO_MEMORY;
+    if (!FT_Get_Paint (render->face, *paint, &p))
+	return CAIRO_STATUS_NO_MEMORY;
 
-  if (render->level == 0)
-    {
-      /* Now that the FT_Get_Paint call has applied the root transform,
-       * make the face unscaled and untransformed, so we can load glyph
-       * contours.
-       */
+    if (render->level == 0) {
+	/* Now that the FT_Get_Paint call has applied the root transform,
+	 * make the face unscaled and untransformed, so we can load glyph
+	 * contours.
+	 */
 
-      FT_Matrix transform;
-      FT_Vector delta;
+	FT_Matrix transform;
+	FT_Vector delta;
 
-      orig_size = render->face->size;
-      FT_New_Size (render->face, &unscaled_size);
-      FT_Activate_Size (unscaled_size);
-      FT_Set_Char_Size (render->face, render->face->units_per_EM << 6, 0, 0, 0);
+	orig_size = render->face->size;
+	FT_New_Size (render->face, &unscaled_size);
+	FT_Activate_Size (unscaled_size);
+	FT_Set_Char_Size (render->face, render->face->units_per_EM << 6, 0, 0, 0);
 
-      transform.xx = transform.yy = 1 << 16;
-      transform.xy = transform.yx = 0;
-      delta.x = delta.y = 0;
+	transform.xx = transform.yy = 1 << 16;
+	transform.xy = transform.yx = 0;
+	delta.x = delta.y = 0;
 
-      FT_Get_Transform (render->face, &orig_transform, &orig_delta);
-      FT_Set_Transform (render->face, &transform, &delta);
+	FT_Get_Transform (render->face, &orig_transform, &orig_delta);
+	FT_Set_Transform (render->face, &transform, &delta);
     }
 
-  render->level++;
-
-  switch (p.format)
-    {
-    case FT_COLR_PAINTFORMAT_COLR_LAYERS:
-      status = draw_paint_colr_layers (render, &p.u.colr_layers, cr);
-      break;
-    case FT_COLR_PAINTFORMAT_SOLID:
-      status = draw_paint_solid (render, &p.u.solid, cr);
-      break;
-    case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT:
-      status = draw_paint_linear_gradient (render, &p.u.linear_gradient, cr);
-      break;
-    case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT:
-      status = draw_paint_radial_gradient (render, &p.u.radial_gradient, cr);
-      break;
-    case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT:
-      status = draw_paint_sweep_gradient (render, &p.u.sweep_gradient, cr);
-      break;
-    case FT_COLR_PAINTFORMAT_GLYPH:
-      status = draw_paint_glyph (render, &p.u.glyph, cr);
-      break;
-    case FT_COLR_PAINTFORMAT_COLR_GLYPH:
-      status = draw_paint_colr_glyph (render, &p.u.colr_glyph, cr);
-      break;
-    case FT_COLR_PAINTFORMAT_TRANSFORM:
-      status = draw_paint_transform (render, &p.u.transform, cr);
-      break;
-    case FT_COLR_PAINTFORMAT_TRANSLATE:
-      status = draw_paint_translate (render, &p.u.translate, cr);
-      break;
-    case FT_COLR_PAINTFORMAT_ROTATE:
-      status = draw_paint_rotate (render, &p.u.rotate, cr);
-      break;
-    case FT_COLR_PAINTFORMAT_SCALE:
-      status = draw_paint_scale (render, &p.u.scale, cr);
-      break;
-    case FT_COLR_PAINTFORMAT_SKEW:
-      status = draw_paint_skew (render, &p.u.skew, cr);
-      break;
-    case FT_COLR_PAINTFORMAT_COMPOSITE:
-      status = draw_paint_composite (render, &p.u.composite, cr);
-      break;
-    case FT_COLR_PAINT_FORMAT_MAX:
-    case FT_COLR_PAINTFORMAT_UNSUPPORTED:
-    default:
-      assert (0);
+    render->level++;
+
+    switch (p.format) {
+	case FT_COLR_PAINTFORMAT_COLR_LAYERS:
+	    status = draw_paint_colr_layers (render, &p.u.colr_layers, cr);
+	    break;
+	case FT_COLR_PAINTFORMAT_SOLID:
+	    status = draw_paint_solid (render, &p.u.solid, cr);
+	    break;
+	case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT:
+	    status = draw_paint_linear_gradient (render, &p.u.linear_gradient, cr);
+	    break;
+	case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT:
+	    status = draw_paint_radial_gradient (render, &p.u.radial_gradient, cr);
+	    break;
+	case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT:
+	    status = draw_paint_sweep_gradient (render, &p.u.sweep_gradient, cr);
+	    break;
+	case FT_COLR_PAINTFORMAT_GLYPH:
+	    status = draw_paint_glyph (render, &p.u.glyph, cr);
+	    break;
+	case FT_COLR_PAINTFORMAT_COLR_GLYPH:
+	    status = draw_paint_colr_glyph (render, &p.u.colr_glyph, cr);
+	    break;
+	case FT_COLR_PAINTFORMAT_TRANSFORM:
+	    status = draw_paint_transform (render, &p.u.transform, cr);
+	    break;
+	case FT_COLR_PAINTFORMAT_TRANSLATE:
+	    status = draw_paint_translate (render, &p.u.translate, cr);
+	    break;
+	case FT_COLR_PAINTFORMAT_ROTATE:
+	    status = draw_paint_rotate (render, &p.u.rotate, cr);
+	    break;
+	case FT_COLR_PAINTFORMAT_SCALE:
+	    status = draw_paint_scale (render, &p.u.scale, cr);
+	    break;
+	case FT_COLR_PAINTFORMAT_SKEW:
+	    status = draw_paint_skew (render, &p.u.skew, cr);
+	    break;
+	case FT_COLR_PAINTFORMAT_COMPOSITE:
+	    status = draw_paint_composite (render, &p.u.composite, cr);
+	    break;
+	case FT_COLR_PAINT_FORMAT_MAX:
+	case FT_COLR_PAINTFORMAT_UNSUPPORTED:
+	default:
+	    ASSERT_NOT_REACHED;
     }
 
-  render->level--;
+    render->level--;
 
-  if (render->level == 0)
-    {
-      FT_Set_Transform (render->face, &orig_transform, &orig_delta);
-      FT_Activate_Size (orig_size);
-      FT_Done_Size (unscaled_size);
+    if (render->level == 0) {
+	FT_Set_Transform (render->face, &orig_transform, &orig_delta);
+	FT_Activate_Size (orig_size);
+	FT_Done_Size (unscaled_size);
     }
 
-  return status;
+    return status;
 }
 
 static cairo_status_t
 draw_colr_glyph (cairo_colr_glyph_render_t *render,
-                 unsigned int glyph,
-                 FT_Color_Root_Transform  root,
-                 cairo_t *cr)
+		 unsigned long              glyph,
+                 FT_Color_Root_Transform    root,
+                 cairo_t                   *cr)
 {
-  FT_OpaquePaint paint = { NULL, 0 };
-  FT_ClipBox box;
-  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    FT_OpaquePaint paint = { NULL, 0 };
+    FT_ClipBox box;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
-  cairo_save (cr);
+    cairo_save (cr);
 
-  if (FT_Get_Color_Glyph_ClipBox (render->face, glyph, &box))
-    {
-      float xmin, ymin, xmax, ymax;
+    if (FT_Get_Color_Glyph_ClipBox (render->face, glyph, &box)) {
+	double xmin, ymin, xmax, ymax;
 
-      xmin = f26_6 (box.bottom_left.x);
-      ymin = f26_6 (box.bottom_left.y);
-      xmax = f26_6 (box.top_right.x);
-      ymax = f26_6 (box.top_right.y);
+	xmin = double_from_26_6 (box.bottom_left.x);
+	ymin = double_from_26_6 (box.bottom_left.y);
+	xmax = double_from_26_6 (box.top_right.x);
+	ymax = double_from_26_6 (box.top_right.y);
 
-      cairo_new_path (cr);
-      cairo_rectangle (cr, xmin, ymin, xmax - xmin, ymax - ymin);
-      cairo_clip (cr);
-      printf("%f %f %f %f\n", xmin, ymin, xmax, ymax);
+	cairo_new_path (cr);
+	cairo_rectangle (cr, xmin, ymin, xmax - xmin, ymax - ymin);
+	cairo_clip (cr);
     }
 
-  if (FT_Get_Color_Glyph_Paint (render->face, glyph, root, &paint))
-    {
-      status = draw_paint (render, &paint, cr);
-    }
+    if (FT_Get_Color_Glyph_Paint (render->face, glyph, root, &paint))
+	status = draw_paint (render, &paint, cr);
 
-  cairo_restore (cr);
+    cairo_restore (cr);
 
-  return status;
+    return status;
 }
 
-/* }}} */
-/* {{{ cairo_colr_glyph_render_t API */
-
 /* Create an image surface and render the glyph onto it,
  * using the given colors.
  */
 cairo_status_t
-_cairo_render_colr_v1_glyph (FT_Face face,
-                             unsigned long glyph,
-                             FT_UShort palette_index,
+_cairo_render_colr_v1_glyph (FT_Face               face,
+                             unsigned long         glyph,
+                             FT_UShort             palette_index,
                              cairo_t              *cr)
 {
-  cairo_status_t status = CAIRO_STATUS_SUCCESS;
-  cairo_colr_glyph_render_t *colr_render = NULL;
-  FT_Color *palette = NULL;
-  FT_Palette_Data palette_data;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_colr_glyph_render_t *colr_render = NULL;
+    FT_Color *palette = NULL;
+    FT_Palette_Data palette_data;
 
 #if DEBUG_COLR
-  printf ("_cairo_render_colr_glyph  glyph index: %ld\n", glyph);
+    printf ("_cairo_render_colr_glyph  glyph index: %ld\n", glyph);
 #endif
 
-  colr_render = _cairo_malloc (sizeof (cairo_colr_glyph_render_t));
-  if (unlikely (colr_render == NULL)) {
-    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
-    goto cleanup;
-  }
-
-  if (FT_Palette_Data_Get (face, &palette_data) == 0 && palette_data.num_palettes > 0) {
-    if (palette_index >= palette_data.num_palettes)
-      palette_index = CAIRO_COLOR_PALETTE_DEFAULT;
-    if (FT_Palette_Select (face, palette_index, &palette) != 0)
-      palette = NULL;
-  }
-
-  colr_render->face = face;
-  colr_render->palette = palette;
-  colr_render->num_palette_entries = palette_data.num_palette_entries;
-  colr_render->foreground_color = cairo_pattern_reference (cairo_get_source (cr));
-  colr_render->level = 0;
-
-  status = draw_colr_glyph (colr_render,
-                            glyph,
-                            FT_COLOR_INCLUDE_ROOT_TRANSFORM,
-                            cr);
-  
-cleanup:
+    colr_render = _cairo_malloc (sizeof (cairo_colr_glyph_render_t));
+    if (unlikely (colr_render == NULL)) {
+	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	goto cleanup;
+    }
 
-  cairo_pattern_destroy (colr_render->foreground_color);
+    if (FT_Palette_Data_Get (face, &palette_data) == 0 && palette_data.num_palettes > 0) {
+	if (palette_index >= palette_data.num_palettes)
+	    palette_index = CAIRO_COLOR_PALETTE_DEFAULT;
 
-  if (colr_render)
-    free (colr_render);
+	if (FT_Palette_Select (face, palette_index, &palette) != 0)
+	    palette = NULL;
+    }
 
-  return status;
-}
+    colr_render->face = face;
+    colr_render->palette = palette;
+    colr_render->num_palette_entries = palette_data.num_palette_entries;
+    colr_render->foreground_color = cairo_pattern_reference (cairo_get_source (cr));
+    colr_render->level = 0;
 
-/* }}} */
+    status = draw_colr_glyph (colr_render,
+			      glyph,
+			      FT_COLOR_INCLUDE_ROOT_TRANSFORM,
+			      cr);
+  
+  cleanup:
+    cairo_pattern_destroy (colr_render->foreground_color);
 
-#endif /* HAVE_FT_GET_COLOR_GLYPH_PAINT */
+    if (colr_render)
+	free (colr_render);
 
-/* vim:set foldmethod=marker expandtab: */
+    return status;
+}
+
+#endif /* HAVE_FT_GET_COLOR_GLYPH_PAINT */
commit 2dfc52064ee9139333e741c998b0066aade9c538
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sun Jan 8 10:08:14 2023 +1030

    Use cairo-ft-font.c decompose function

diff --git a/src/cairo-colr-glyph-render.c b/src/cairo-colr-glyph-render.c
index d55bed583..4a25f54af 100644
--- a/src/cairo-colr-glyph-render.c
+++ b/src/cairo-colr-glyph-render.c
@@ -32,6 +32,7 @@
 #include "cairoint.h"
 #include "cairo-array-private.h"
 #include "cairo-ft-private.h"
+#include "cairo-path-private.h"
 
 #include <assert.h>
 #include <math.h>
@@ -181,242 +182,6 @@ cairo_extend (FT_PaintExtend extend)
 }
 
 /* }}} */
-/* {{{ Paths */
-
-typedef struct {
-  cairo_array_t points;
-  int needs_move;
-  int last_move_op;
-} path_data_t;
-
-static cairo_status_t
-close_path (path_data_t *pdata)
-{
-  cairo_path_data_t p;
-  int status;
-
-  if (pdata->last_move_op < 0)
-    return CAIRO_STATUS_SUCCESS;
-
-  p.header.type = CAIRO_PATH_LINE_TO;
-  p.header.length = 2;
-  status = _cairo_array_append (&pdata->points, &p);
-  if (unlikely (status))
-    return status;
-
-  p = *(cairo_path_data_t *) _cairo_array_index (&pdata->points, pdata->last_move_op + 1);
-  status = _cairo_array_append (&pdata->points, &p);
-  if (unlikely (status))
-    return status;
-
-  p.header.type = CAIRO_PATH_CLOSE_PATH;
-  p.header.length = 1;
-  status = _cairo_array_append (&pdata->points, &p);
-  if (unlikely (status))
-    return status;
-
-  pdata->needs_move = 1;
-  pdata->last_move_op = -1;
-
-  return CAIRO_STATUS_SUCCESS;
-}
-
-static int
-move_to (const FT_Vector *to,
-         void *data)
-{
-  path_data_t *pdata = data;
-  cairo_path_data_t p;
-  cairo_status_t status;
-
-  status = close_path (pdata);
-  if (unlikely (status))
-    return status;
-
-  pdata->needs_move = 0;
-  pdata->last_move_op = pdata->points.num_elements;
-
-  p.header.type = CAIRO_PATH_MOVE_TO;
-  p.header.length = 2;
-  status = _cairo_array_append (&pdata->points, &p);
-  if (unlikely (status))
-    return status;
-
-  p.point.x = to->x / (float) (1 << 6);
-  p.point.y = to->y / (float) (1 << 6);
-  status = _cairo_array_append (&pdata->points, &p);
-  if (unlikely (status))
-    return status;
-
-  return CAIRO_STATUS_SUCCESS;
-}
-
-static int
-line_to (const FT_Vector *to,
-         void *data)
-{
-  path_data_t *pdata = data;
-  cairo_path_data_t p;
-  cairo_status_t status;
-
-  if (pdata->needs_move)
-    return move_to (to, data);
-
-  p.header.type = CAIRO_PATH_LINE_TO;
-  p.header.length = 2;
-  status = _cairo_array_append (&pdata->points, &p);
-  if (unlikely (status))
-    return status;
-
-  p.point.x = to->x / (float) (1 << 6);
-  p.point.y = to->y / (float) (1 << 6);
-  status = _cairo_array_append (&pdata->points, &p);
-  if (unlikely (status))
-    return status;
-
-  return CAIRO_STATUS_SUCCESS;
-}
-
-static int
-conic_to (const FT_Vector *control,
-          const FT_Vector *to,
-          void *data)
-{
-  path_data_t *pdata = data;
-  cairo_path_data_t p;
-  double cx, cy;
-  double x0, y0;
-  double x1, y1;
-  double x2, y2;
-  double x3, y3;
-  cairo_status_t status;
-
-  assert (!pdata->needs_move);
-  assert (pdata->points.num_elements > 0);
-
-  p.header.type = CAIRO_PATH_CURVE_TO;
-  p.header.length = 4;
-  status = _cairo_array_append (&pdata->points, &p);
-  if (unlikely (status))
-    return status;
-
-  p = *(cairo_path_data_t *) _cairo_array_index (&pdata->points, pdata->points.num_elements - 2);
-
-  x0 = p.point.x;
-  y0 = p.point.y;
-
-  cx = control->x / (float) (1 << 6);
-  cy = control->y / (float) (1 << 6);
-
-  x3 = to->x / (float) (1 << 6);
-  y3 = to->y / (float) (1 << 6);
-
-  x1 = x0 + (2./3.) * (cx - x0);
-  y1 = y0 + (2./3.) * (cy - y0);
-
-  x2 = x3 + (2./3.) * (cx - x3);
-  y2 = y3 + (2./3.) * (cy - y3);
-
-  p.point.x = x1;
-  p.point.y = y1;
-  status = _cairo_array_append (&pdata->points, &p);
-  if (unlikely (status))
-    return status;
-
-  p.point.x = x2;
-  p.point.y = y2;
-  status = _cairo_array_append (&pdata->points, &p);
-  if (unlikely (status))
-    return status;
-
-  p.point.x = x3;
-  p.point.y = y3;
-  status = _cairo_array_append (&pdata->points, &p);
-  if (unlikely (status))
-    return status;
-
-  return CAIRO_STATUS_SUCCESS;
-}
-
-static int
-cubic_to (const FT_Vector *control1,
-          const FT_Vector *control2,
-          const FT_Vector *to,
-          void *data)
-{
-  path_data_t *pdata = data;
-  cairo_path_data_t p;
-  cairo_status_t status;
-
-  assert (!pdata->needs_move);
-  assert (pdata->points.num_elements > 0);
-
-  p.header.type = CAIRO_PATH_CURVE_TO;
-  p.header.length = 4;
-  status = _cairo_array_append (&pdata->points, &p);
-  if (unlikely (status))
-    return status;
-
-  p.point.x = control1->x / (float) (1 << 6);
-  p.point.y = control1->y / (float) (1 << 6);
-  status = _cairo_array_append (&pdata->points, &p);
-  if (unlikely (status))
-    return status;
-
-  p.point.x = control2->x / (float) (1 << 6);
-  p.point.y = control2->y / (float) (1 << 6);
-  status = _cairo_array_append (&pdata->points, &p);
-  if (unlikely (status))
-    return status;
-
-  p.point.x = to->x / (float) (1 << 6);
-  p.point.y = to->y / (float) (1 << 6);
-  status = _cairo_array_append (&pdata->points, &p);
-  if (unlikely (status))
-    return status;
-
-  return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-get_path_for_glyph (FT_Face face,
-                    unsigned int glyph,
-                    cairo_path_t **path)
-{
-  path_data_t pdata;
-  FT_Outline_Funcs callbacks = {
-    move_to, line_to, conic_to, cubic_to, 0, 0
-  };
-  FT_Error error;
-
-  *path = NULL;
-
-  error = FT_Load_Glyph (face, glyph, FT_LOAD_DEFAULT);
-  if (error != 0)
-    return CAIRO_STATUS_INVALID_PATH_DATA;
-
-  _cairo_array_init (&pdata.points, sizeof (cairo_path_data_t));
-  pdata.needs_move = 1;
-  pdata.last_move_op = -1;
-
-  if (FT_Outline_Decompose (&face->glyph->outline, &callbacks, &pdata) != 0)
-    {
-      _cairo_array_fini (&pdata.points);
-      return CAIRO_STATUS_INVALID_PATH_DATA;
-    }
-
-  close_path (&pdata);
-
-  *path = _cairo_malloc (sizeof (cairo_path_t));
-  if (unlikely (path == NULL))
-    return CAIRO_STATUS_NO_MEMORY;
-
-  (*path)->status = CAIRO_STATUS_SUCCESS;
-  (*path)->data = (cairo_path_data_t *) pdata.points.elements;
-  (*path)->num_data = pdata.points.num_elements;
-
-  return CAIRO_STATUS_SUCCESS;
-}
 
 
 /* }}} */
@@ -1104,33 +869,42 @@ draw_paint_glyph (cairo_colr_glyph_render_t *render,
                   FT_PaintGlyph *glyph,
                   cairo_t *cr)
 {
-  cairo_path_t *path;
-  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_path_fixed_t *path_fixed;
+    cairo_path_t *path;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    FT_Error error;
 
 #if DEBUG_COLR
-  printf ("%*sDraw PaintGlyph\n", 2 * render->level, "");
+    printf ("%*sDraw PaintGlyph\n", 2 * render->level, "");
 #endif
 
-  status = get_path_for_glyph (render->face, glyph->glyphID, &path);
-  if (unlikely (status))
-    goto cleanup;
+    error = FT_Load_Glyph (render->face, glyph->glyphID, FT_LOAD_DEFAULT);
+    status = _cairo_ft_to_cairo_error (error);
+    if (unlikely (status))
+        return status;
 
-  cairo_save (cr);
+    status = _cairo_ft_face_decompose_glyph_outline (render->face, &path_fixed);
+    if (unlikely (status))
+        return status;
 
-  cairo_new_path (cr);
-  cairo_append_path (cr, path);
-  cairo_clip (cr);
+    cairo_save (cr);
+    cairo_identity_matrix (cr);
+    path = _cairo_path_create (path_fixed, cr);
+    _cairo_path_fixed_destroy (path_fixed);
+    cairo_restore (cr);
 
-  status = draw_paint (render, &glyph->paint, cr);
+    cairo_save (cr);
 
-  cairo_restore (cr);
+    cairo_new_path (cr);
+    cairo_append_path (cr, path);
+    cairo_path_destroy (path);
+    cairo_clip (cr);
 
-cleanup:
+    status = draw_paint (render, &glyph->paint, cr);
 
-  if (path)
-    cairo_path_destroy (path);
+    cairo_restore (cr);
 
-  return status;
+    return status;
 }
 
 static cairo_status_t draw_colr_glyph (cairo_colr_glyph_render_t *render,
diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index 3ea15e862..752fd0473 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -248,17 +248,19 @@ _cairo_ft_resolve_pattern (FcPattern		      *pattern,
 
 #endif
 
-static cairo_status_t
-_ft_to_cairo_error (FT_Error error)
+cairo_status_t
+_cairo_ft_to_cairo_error (FT_Error error)
 {
   /* Currently we don't get many (any?) useful statuses here.
    * Populate as needed. */
   switch (error)
   {
-  case FT_Err_Out_Of_Memory:
-      return CAIRO_STATUS_NO_MEMORY;
-  default:
-      return CAIRO_STATUS_FREETYPE_ERROR;
+      case FT_Err_Ok:
+	  return CAIRO_STATUS_SUCCESS;
+      case FT_Err_Out_Of_Memory:
+	  return CAIRO_STATUS_NO_MEMORY;
+      default:
+	  return CAIRO_STATUS_FREETYPE_ERROR;
   }
 }
 
@@ -758,7 +760,7 @@ _cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled)
     {
 	unscaled->lock_count--;
 	CAIRO_MUTEX_UNLOCK (unscaled->mutex);
-	_cairo_error_throw (_ft_to_cairo_error (error));
+	_cairo_error_throw (_cairo_ft_to_cairo_error (error));
 	return NULL;
     }
 
@@ -915,7 +917,7 @@ _cairo_ft_unscaled_font_set_scale (cairo_ft_unscaled_font_t *unscaled,
 			      sf.y_scale * 64.0 + .5,
 			      0, 0);
     if (error)
-      return _cairo_error (_ft_to_cairo_error (error));
+      return _cairo_error (_cairo_ft_to_cairo_error (error));
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -1357,7 +1359,7 @@ _get_bitmap_surface (FT_Bitmap		     *bitmap,
 
 	    error = FT_Bitmap_Convert( library, bitmap, &tmp, align );
 	    if (error)
-		return _cairo_error (_ft_to_cairo_error (error));
+		return _cairo_error (_cairo_ft_to_cairo_error (error));
 
 	    FT_Bitmap_Done( library, bitmap );
 	    *bitmap = tmp;
@@ -1565,7 +1567,7 @@ _render_glyph_outline (FT_Face                    face,
 #endif
 
 	if (error)
-	    return _cairo_error (_ft_to_cairo_error (error));
+	    return _cairo_error (_cairo_ft_to_cairo_error (error));
 
 	bitmap_size = _compute_xrender_bitmap_size (&bitmap,
 						    face->glyph,
@@ -2268,10 +2270,9 @@ _cubic_to (FT_Vector *control1, FT_Vector *control2,
     return 0;
 }
 
-static cairo_status_t
-_decompose_glyph_outline (FT_Face		  face,
-			  cairo_font_options_t	 *options,
-			  cairo_path_fixed_t	**pathp)
+cairo_status_t
+_cairo_ft_face_decompose_glyph_outline (FT_Face		     face,
+					cairo_path_fixed_t **pathp)
 {
     static const FT_Outline_Funcs outline_funcs = {
 	(FT_Outline_MoveToFunc)_move_to,
@@ -2765,7 +2766,7 @@ _cairo_ft_scaled_glyph_init_record_colr_v0_glyph (cairo_ft_scaled_font_t *scaled
 	    goto cleanup;
 	}
 
-	status = _decompose_glyph_outline (face, &scaled_font->ft_options.base, &path_fixed);
+	status = _cairo_ft_face_decompose_glyph_outline (face, &path_fixed);
 	if (unlikely (status))
 	    return status;
 
@@ -3513,8 +3514,7 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
 	    }
 
 	    if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
-		status = _decompose_glyph_outline (face, &scaled_font->ft_options.base,
-						   &path);
+		status = _cairo_ft_face_decompose_glyph_outline (face, &path);
 	    } else {
 		status = CAIRO_INT_STATUS_UNSUPPORTED;
 	    }
@@ -3656,7 +3656,7 @@ _cairo_ft_is_synthetic (void	        *abstract_font,
 
 	error = FT_Get_MM_Var (face, &mm_var);
 	if (error) {
-	    status = _cairo_error (_ft_to_cairo_error (error));
+	    status = _cairo_error (_cairo_ft_to_cairo_error (error));
 	    goto cleanup;
 	}
 
diff --git a/src/cairo-ft-private.h b/src/cairo-ft-private.h
index ba5c81e29..e02235fa0 100644
--- a/src/cairo-ft-private.h
+++ b/src/cairo-ft-private.h
@@ -55,6 +55,13 @@ _cairo_scaled_font_is_ft (cairo_scaled_font_t *scaled_font);
 cairo_private unsigned int
 _cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font);
 
+cairo_private cairo_status_t
+_cairo_ft_to_cairo_error (FT_Error error);
+
+cairo_private cairo_status_t
+_cairo_ft_face_decompose_glyph_outline (FT_Face		     face,
+					cairo_path_fixed_t **pathp);
+
 #if HAVE_FT_SVG_DOCUMENT
 
 typedef struct FT_Color_ FT_Color;
commit 1129b194b5d65ee27df404106b1b5f51c3739e7e
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Jan 7 22:41:00 2023 +1030

    Render COLRv1 to a recording surface

diff --git a/src/cairo-colr-glyph-render.c b/src/cairo-colr-glyph-render.c
index 605b49713..d55bed583 100644
--- a/src/cairo-colr-glyph-render.c
+++ b/src/cairo-colr-glyph-render.c
@@ -52,18 +52,6 @@
 
 /* {{{ Utilities */
 
-#ifndef CLAMP
-#define CLAMP(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
-#endif
-
-#ifndef MIN
-#define MIN(x,y) ((x) < (y) ? (x) : (y))
-#endif
-
-#ifndef MAX
-#define MAX(x,y) ((x) > (y) ? (x) : (y))
-#endif
-
 typedef struct {
   float x, y;
 } Point;
@@ -430,501 +418,6 @@ get_path_for_glyph (FT_Face face,
   return CAIRO_STATUS_SUCCESS;
 }
 
-/* }}} */
-/* {{{ Bounds and miscellaneous info */
-
-typedef struct {
-  float xmin, ymin, xmax, ymax;
-} Bounds;
-
-static void
-union_bounds (Bounds *b1, Bounds *b2)
-{
-  b2->xmin = MIN (b1->xmin, b2->xmin);
-  b2->ymin = MIN (b1->ymin, b2->ymin);
-  b2->xmax = MAX (b1->xmax, b2->xmax);
-  b2->ymax = MAX (b1->ymax, b2->ymax);
-}
-
-static void
-intersect_bounds (Bounds *b1, Bounds *b2)
-{
-  b2->xmin = MAX (b1->xmin, b2->xmin);
-  b2->ymin = MAX (b1->ymin, b2->ymin);
-  b2->xmax = MIN (b1->xmax, b2->xmax);
-  b2->ymax = MIN (b1->ymax, b2->ymax);
-
-  if (b2->xmin > b2->ymax || b2->ymin > b2->ymax)
-    b2->xmin = b2->ymin = b2->xmax = b2->ymax = 0;
-}
-
-static void
-get_glyph_bounds (FT_Face face, unsigned int glyph_index, Bounds *bounds)
-{
-  FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT);
-
-  bounds->xmin = f26_6 (face->glyph->metrics.horiBearingX);
-  bounds->ymin = f26_6 (face->glyph->metrics.horiBearingY);
-  bounds->xmax = bounds->xmin + f26_6 (face->glyph->metrics.width);
-  bounds->ymax = - (bounds->ymin + f26_6 (face->glyph->metrics.height));
-}
-
-static void
-grow_bounds (Bounds *b, float x, float y)
-{
-  b->xmin = MIN (b->xmin, x);
-  b->ymin = MIN (b->ymin, y);
-  b->xmax = MAX (b->xmax, x);
-  b->ymax = MAX (b->ymax, y);
-}
-
-static void
-transform_bounds_f (Bounds *b, float xx, float yx, float xy, float yy, float dx, float dy)
-{
-  float x, y;
-  Bounds out;
-
-  out.xmin = out.xmax = b->xmin * xx + b->ymin * xy + dx;
-  out.ymin = out.ymax = b->xmin * yx + b->ymin * yy + dy;
-
-  x = b->xmax * xx + b->ymax * xy + dx;
-  y = b->xmax * yx + b->ymax * yy + dy;
-  grow_bounds (&out, x, y);
-
-  x = b->xmax * xx + b->ymin * xy + dx;
-  y = b->xmax * yx + b->ymin * yy + dy;
-  grow_bounds (&out, x, y);
-
-  x = b->xmin * xx + b->ymax * xy + dx;
-  y = b->xmin * yx + b->ymax * yy + dy;
-  grow_bounds (&out, x, y);
-
-  *b = out;
-}
-
-static void
-transform_bounds (Bounds *b, FT_Affine23 *affine)
-{
-  float xx, xy, yx, yy, dx, dy;
-
-  xx = f16_16 (affine->xx);
-  yx = f16_16 (affine->yx);
-  xy = f16_16 (affine->xy);
-  yy = f16_16 (affine->yy);
-  dx = f16_16 (affine->dx);
-  dy = f16_16 (affine->dy);
-
-  transform_bounds_f (b, xx, yx, xy, yy, dx, dy);
-}
-
-static void
-translate_bounds (Bounds *b, float dx, float dy)
-{
-  b->xmin = b->xmin + dx;
-  b->ymin = b->ymin + dy;
-  b->xmax = b->xmax + dx;
-  b->ymax = b->ymax + dy;
-}
-
-static void
-rotate_bounds (Bounds *b, float r)
-{
-  float c, s;
-
-  c = cosf (r);
-  s = sinf (r);
-  transform_bounds_f (b, c, s, -s, c, 0, 0);
-}
-
-static void
-scale_bounds (Bounds *b, float sx, float sy)
-{
-  transform_bounds_f (b, sx, 0, 0, sy, 0, 0);
-}
-
-static void
-skew_bounds (Bounds *b, float ax, float ay)
-{
-  transform_bounds_f (b, 1, tanf (ay), - tanf (ax), 1, 0, 0);
-}
-
-static int
-compute_bounds (FT_Face face, FT_OpaquePaint *paint, Bounds *bounds, int drop_transform)
-{
-  FT_COLR_Paint p;
-  FT_Size orig_size;
-  FT_Size unscaled_size;
-  FT_Matrix orig_transform;
-  FT_Vector orig_delta;
-  int ret = 1;
-
-  if (!FT_Get_Paint (face, *paint, &p))
-    return 0;
-
-  if (drop_transform)
-    {
-      FT_Matrix transform;
-      FT_Vector delta;
-
-      orig_size = face->size;
-      FT_New_Size (face, &unscaled_size);
-      FT_Activate_Size (unscaled_size);
-      FT_Set_Char_Size (face, face->units_per_EM << 6, 0, 0, 0);
-
-      transform.xx = transform.yy = 1 << 16;
-      transform.xy = transform.yx = 0;
-      delta.x = delta.y = 0;
-
-      FT_Get_Transform (face, &orig_transform, &orig_delta);
-      FT_Set_Transform (face, &transform, &delta);
-    }
-
-  switch (p.format)
-    {
-    case FT_COLR_PAINTFORMAT_COLR_LAYERS:
-      {
-        FT_OpaquePaint layer_paint = { NULL, 0 };
-        Bounds b;
-        int first = 1;
-
-        while (FT_Get_Paint_Layers (face, &p.u.colr_layers.layer_iterator, &layer_paint))
-          {
-            if (!compute_bounds (face, &layer_paint, &b, FALSE))
-              {
-                ret = 0;
-                break;
-              }
-
-            if (first)
-              {
-                *bounds = b;
-                first = 0;
-              }
-            else
-              union_bounds (&b, bounds);
-          }
-        //printf ("Layer bounds: %f %f %f %f\n", bounds->xmin, bounds->ymin, bounds->xmax, bounds->ymax);
-      }
-      break;
-    case FT_COLR_PAINTFORMAT_SOLID:
-      ret = 0; // Solid is unbounded
-      break;
-
-    case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT:
-    case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT:
-    case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT:
-      ret = 0; // Gradients are unbounded
-      break;
-
-    case FT_COLR_PAINTFORMAT_GLYPH:
-      get_glyph_bounds (face, p.u.glyph.glyphID, bounds);
-      //printf ("Glyph bounds: %f %f %f %f\n", bounds->xmin, bounds->ymin, bounds->xmax, bounds->ymax);
-      break;
-
-    case FT_COLR_PAINTFORMAT_COLR_GLYPH:
-      get_glyph_bounds (face, p.u.colr_glyph.glyphID, bounds);
-      //printf ("Glyph bounds: %f %f %f %f\n", bounds->xmin, bounds->ymin, bounds->xmax, bounds->ymax);
-      break;
-
-    case FT_COLR_PAINTFORMAT_TRANSFORM:
-      {
-        if (!compute_bounds (face, &p.u.transform.paint, bounds, FALSE))
-          ret = 0;
-        else
-          {
-            transform_bounds (bounds, &p.u.transform.affine);
-            //printf ("Transform bounds: %f %f %f %f\n", bounds->xmin, bounds->ymin, bounds->xmax, bounds->ymax);
-          }
-      }
-      break;
-    case FT_COLR_PAINTFORMAT_TRANSLATE:
-      {
-        if (!compute_bounds (face, &p.u.translate.paint, bounds, FALSE))
-          ret = 0;
-        else
-          translate_bounds (bounds,
-                            f16_16 (p.u.translate.dx),
-                            f16_16 (p.u.translate.dy));
-      }
-      break;
-    case FT_COLR_PAINTFORMAT_ROTATE:
-      {
-        if (!compute_bounds (face, &p.u.rotate.paint, bounds, FALSE))
-          ret = 0;
-        else
-          {
-            translate_bounds (bounds,
-                              - f16_16 (p.u.rotate.center_x),
-                              - f16_16 (p.u.rotate.center_y));
-            rotate_bounds (bounds,
-                           f16_16 (p.u.rotate.angle) * M_PI);
-            translate_bounds (bounds,
-                              f16_16 (p.u.rotate.center_x),
-                              f16_16 (p.u.rotate.center_y));
-          }
-      }
-      break;
-    case FT_COLR_PAINTFORMAT_SCALE:
-      {
-        if (!compute_bounds (face, &p.u.scale.paint, bounds, FALSE))
-          ret = 0;
-        else
-          {
-            translate_bounds (bounds,
-                              - f16_16 (p.u.scale.center_x),
-                              - f16_16 (p.u.scale.center_y));
-            scale_bounds (bounds,
-                          f16_16 (p.u.scale.scale_x),
-                          f16_16 (p.u.scale.scale_y));
-            translate_bounds (bounds,
-                              f16_16 (p.u.scale.center_x),
-                              f16_16 (p.u.scale.center_y));
-          }
-      }
-      break;
-    case FT_COLR_PAINTFORMAT_SKEW:
-      {
-        if (!compute_bounds (face, &p.u.skew.paint, bounds, FALSE))
-          ret = 0;
-        else
-          {
-            translate_bounds (bounds,
-                              - f16_16 (p.u.skew.center_x),
-                              - f16_16 (p.u.skew.center_y));
-            skew_bounds (bounds,
-                         f16_16 (p.u.skew.x_skew_angle) * M_PI,
-                         f16_16 (p.u.skew.y_skew_angle) * M_PI);
-            translate_bounds (bounds,
-                              f16_16 (p.u.skew.center_x),
-                              f16_16 (p.u.skew.center_y));
-          }
-      }
-      break;
-    case FT_COLR_PAINTFORMAT_COMPOSITE:
-      switch ((int)p.u.composite.composite_mode)
-        {
-        case FT_COLR_COMPOSITE_CLEAR:
-          bounds->xmin = bounds->xmax = bounds->ymin = bounds->ymax = 0;
-          break;
-        case FT_COLR_COMPOSITE_SRC:
-        case FT_COLR_COMPOSITE_SRC_OUT:
-          ret = compute_bounds (face, &p.u.composite.source_paint, bounds, FALSE);
-          break;
-        case FT_COLR_COMPOSITE_DEST:
-        case FT_COLR_COMPOSITE_DEST_OUT:
-          ret = compute_bounds (face, &p.u.composite.backdrop_paint, bounds, FALSE);
-          break;
-        case FT_COLR_COMPOSITE_SRC_IN:
-        case FT_COLR_COMPOSITE_DEST_IN:
-          {
-            if (compute_bounds (face, &p.u.composite.source_paint, bounds, FALSE))
-              {
-                Bounds b;
-                if (compute_bounds (face, &p.u.composite.backdrop_paint, &b, FALSE))
-                  intersect_bounds (&b, bounds);
-              }
-            else
-              ret = compute_bounds (face, &p.u.composite.backdrop_paint, bounds, FALSE);
-          }
-          break;
-        default:
-          {
-            if (compute_bounds (face, &p.u.composite.source_paint, bounds, FALSE))
-              {
-                Bounds b;
-
-                if (compute_bounds (face, &p.u.composite.backdrop_paint, &b, FALSE))
-                  union_bounds (&b, bounds);
-                else
-                  ret = 0;
-              }
-            else
-              ret = 0;
-          }
-          break;
-        }
-      break;
-    case FT_COLR_PAINT_FORMAT_MAX:
-    case FT_COLR_PAINTFORMAT_UNSUPPORTED:
-    default:
-      ret = 0;
-      break;
-    }
-
-  if (drop_transform)
-    {
-      FT_Set_Transform (face, &orig_transform, &orig_delta);
-      FT_Activate_Size (orig_size);
-      FT_Done_Size (unscaled_size);
-    }
-
-  return ret;
-}
-
-/* Compute bounds; we don't calculate tight bounds, since
- * we don't need to. So we use control boxes for outlines
- * and transform the bounding boxes.
- */
-static cairo_status_t
-_cairo_colr_glyph_bounds (FT_Face face,
-                          unsigned int glyph,
-                          float *xmin,
-                          float *ymin,
-                          float *xmax,
-                          float *ymax)
-{
-  FT_ClipBox box;
-  FT_OpaquePaint paint = { NULL, 0 };
-
-  if (FT_Get_Color_Glyph_ClipBox (face, glyph, &box))
-    {
-      *xmin = f26_6 (box.bottom_left.x);
-      *ymin = f26_6 (box.bottom_left.y);
-      *xmax = f26_6 (box.top_right.x);
-      *ymax = f26_6 (box.top_right.y);
-      //printf ("bounds from ClipBox\n");
-      return CAIRO_STATUS_SUCCESS;
-    }
-  if (FT_Get_Color_Glyph_Paint (face, glyph, FT_COLOR_INCLUDE_ROOT_TRANSFORM, &paint))
-    {
-      Bounds bounds;
-      compute_bounds (face, &paint, &bounds, TRUE);
-      *xmin = bounds.xmin;
-      *ymin = bounds.ymin;
-      *xmax = bounds.xmax;
-      *ymax = bounds.ymax;
-      //printf ("bounds from Paint\n");
-      return CAIRO_STATUS_SUCCESS;
-    }
-  if (1)
-    {
-      FT_UInt glyph_index, color_index;
-      FT_LayerIterator iter;
-      int count = 0;
-      Bounds bounds;
-
-      iter.p = NULL;
-      while (FT_Get_Color_Glyph_Layer (face, glyph, &glyph_index, &color_index, &iter))
-        {
-          Bounds b;
-
-          get_glyph_bounds (face, glyph_index, &b);
-
-          if (count > 0)
-            union_bounds (&b, &bounds);
-          else
-            bounds = b;
-
-          count++;
-        }
-
-      if (count > 0)
-        {
-          *xmin = bounds.xmin;
-          *ymin = bounds.ymin;
-          *xmax = bounds.xmax;
-          *ymax = bounds.ymax;
-          //printf ("bounds from Layers\n");
-          return CAIRO_STATUS_SUCCESS;
-        }
-    }
-
-  return CAIRO_STATUS_CLIP_NOT_REPRESENTABLE;
-}
-
-static int
-colorline_uses_foreground (FT_Face face,
-                           FT_ColorLine *colorline)
-{
-  FT_ColorStop stop;
-
-  while (FT_Get_Colorline_Stops (face, &stop, &colorline->color_stop_iterator))
-    {
-      if (stop.color.palette_index == 0xffff)
-        return TRUE;
-    }
-
-  return FALSE;
-}
-
-static int
-paint_uses_foreground (FT_Face face,
-                       FT_OpaquePaint *paint)
-{
-  FT_COLR_Paint p;
-  FT_OpaquePaint layer_paint;
-
-  if (!FT_Get_Paint (face, *paint, &p))
-    return FALSE;
-
-  switch (p.format)
-    {
-    case FT_COLR_PAINTFORMAT_COLR_LAYERS:
-      while (FT_Get_Paint_Layers (face, &p.u.colr_layers.layer_iterator, &layer_paint))
-        {
-          if (paint_uses_foreground (face, &layer_paint))
-            return TRUE;
-        }
-      return FALSE;
-    case FT_COLR_PAINTFORMAT_SOLID:
-      return p.u.solid.color.palette_index == 0xffff;
-    case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT:
-      return colorline_uses_foreground (face, &p.u.linear_gradient.colorline);
-    case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT:
-      return colorline_uses_foreground (face, &p.u.radial_gradient.colorline);
-    case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT:
-      return colorline_uses_foreground (face, &p.u.sweep_gradient.colorline);
-    case FT_COLR_PAINTFORMAT_GLYPH:
-      return paint_uses_foreground (face, &p.u.glyph.paint);
-    case FT_COLR_PAINTFORMAT_COLR_GLYPH:
-      return _cairo_colr_glyph_uses_foreground (face, p.u.glyph.glyphID);
-    case FT_COLR_PAINTFORMAT_TRANSFORM:
-      return paint_uses_foreground (face, &p.u.transform.paint);
-    case FT_COLR_PAINTFORMAT_TRANSLATE:
-      return paint_uses_foreground (face, &p.u.translate.paint);
-    case FT_COLR_PAINTFORMAT_ROTATE:
-      return paint_uses_foreground (face, &p.u.rotate.paint);
-    case FT_COLR_PAINTFORMAT_SCALE:
-      return paint_uses_foreground (face, &p.u.scale.paint);
-    case FT_COLR_PAINTFORMAT_SKEW:
-      return paint_uses_foreground (face, &p.u.skew.paint);
-    case FT_COLR_PAINTFORMAT_COMPOSITE:
-      return paint_uses_foreground (face, &p.u.composite.source_paint) ||
-             paint_uses_foreground (face, &p.u.composite.backdrop_paint);
-    case FT_COLR_PAINT_FORMAT_MAX:
-    case FT_COLR_PAINTFORMAT_UNSUPPORTED:
-    default:
-      assert (0);
-    }
-}
-
-/* Return TRUE if the paint graph for glyph refers to
- * the foreground color (i.e. uses the color index 0xffff.
- */
-int
-_cairo_colr_glyph_uses_foreground (FT_Face face,
-                                   unsigned long glyph)
-{
-  FT_OpaquePaint paint = { NULL, 0 };
-  FT_UInt glyph_index, color_index;
-  FT_LayerIterator iter;
-
-  if (FT_Get_Color_Glyph_Paint (face, glyph, FT_COLOR_INCLUDE_ROOT_TRANSFORM, &paint))
-    return paint_uses_foreground (face, &paint);
-
-  iter.p = NULL;
-  if (FT_Get_Color_Glyph_Layer (face, glyph, &glyph_index, &color_index, &iter))
-    {
-      do
-        {
-          if (color_index == 0xffff)
-            return TRUE;
-        }
-      while (FT_Get_Color_Glyph_Layer (face, glyph, &glyph_index, &color_index, &iter));
-    }
-
-  return FALSE;
-}
 
 /* }}} */
 /* {{{ cairo_colr_glyph_render_t implementation */
@@ -933,7 +426,7 @@ typedef struct cairo_colr_glyph_render_t cairo_colr_glyph_render_t;
 
 struct cairo_colr_glyph_render_t {
   FT_Face face;
-  const cairo_color_t *foreground_color;
+  cairo_pattern_t *foreground_color;
   FT_Color *palette;
   unsigned int num_palette_entries;
   int level;
@@ -973,51 +466,25 @@ draw_paint_colr_layers (cairo_colr_glyph_render_t *render,
 static void
 get_palette_color (cairo_colr_glyph_render_t *render,
                    FT_ColorIndex *ci,
-                   cairo_color_t *color)
-{
-  if (ci->palette_index == 0xffff)
-    *color = *render->foreground_color;
-  else
-    {
-      if (ci->palette_index >= render->num_palette_entries)
-        {
-          fprintf (stderr, "Ignoring out-of-range palette index");
-          *color = *render->foreground_color;
-        }
-      else
-        {
-          FT_Color c = render->palette[ci->palette_index];
-          color->red = c.red / 255.0;
-          color->green = c.green / 255.0;
-          color->blue = c.blue / 255.0;
-       }
-    }
-  color->alpha = f2_14 (ci->alpha);
-}
-
-static void
-get_palette_color_v0 (cairo_colr_glyph_render_t *render,
-                      FT_UInt color_index,
-                      cairo_color_t *color)
+                   cairo_color_t *color,
+                   cairo_bool_t *is_foreground_color)
 {
-  if (color_index == 0xffff)
-    *color = *render->foreground_color;
-  else
-    {
-      if (color_index >= render->num_palette_entries)
-        {
-          fprintf (stderr, "Ignoring out-of-range palette index");
-          *color = *render->foreground_color;
-        }
-      else
-        {
-          FT_Color c = render->palette[color_index];
-          color->red = c.red / 255.0;
-          color->green = c.green / 255.0;
-          color->blue = c.blue / 255.0;
-          color->alpha = c.alpha / 255.0;
-        }
+    cairo_bool_t foreground = FALSE;
+
+    if (ci->palette_index == 0xffff || ci->palette_index >= render->num_palette_entries) {
+        color->red = 0;
+        color->green = 0;
+        color->blue = 0;
+        foreground = TRUE;
+    } else {
+        FT_Color c = render->palette[ci->palette_index];
+        color->red = c.red / 255.0;
+        color->green = c.green / 255.0;
+        color->blue = c.blue / 255.0;
     }
+    color->alpha = f2_14 (ci->alpha);
+    if (foreground)
+        *is_foreground_color = TRUE;
 }
 
 static cairo_status_t
@@ -1026,13 +493,18 @@ draw_paint_solid (cairo_colr_glyph_render_t *render,
                   cairo_t *cr)
 {
   cairo_color_t color;
+  cairo_bool_t is_foreground_color;
 
 #if DEBUG_COLR
   printf ("%*sDraw PaintSolid\n", 2 * render->level, "");
 #endif
 
-  get_palette_color (render, &solid->color, &color);
-  cairo_set_source_rgba (cr, color.red, color.green, color.blue, color.alpha);
+  get_palette_color (render, &solid->color, &color, &is_foreground_color);
+  if (is_foreground_color) {
+      cairo_set_source (cr, render->foreground_color);
+  } else
+      cairo_set_source_rgba (cr, color.red, color.green, color.blue, color.alpha);
+
   cairo_paint (cr);
 
   return CAIRO_STATUS_SUCCESS;
@@ -1092,7 +564,7 @@ read_colorline (cairo_colr_glyph_render_t *render,
   while (FT_Get_Colorline_Stops (render->face, &stop, &colorline->color_stop_iterator))
     {
       cl->stops[i].position = f16_16 (stop.stop_offset);
-      get_palette_color (render, &stop.color, &cl->stops[i].color);
+      get_palette_color (render, &stop.color, &cl->stops[i].color, NULL);
       i++;
     }
 
@@ -1972,38 +1444,6 @@ draw_colr_glyph (cairo_colr_glyph_render_t *render,
     {
       status = draw_paint (render, &paint, cr);
     }
-  else
-    {
-      FT_UInt glyph_index, color_index;
-      FT_LayerIterator iter;
-
-      iter.p = NULL;
-      while (FT_Get_Color_Glyph_Layer (render->face, glyph, &glyph_index, &color_index, &iter))
-        {
-          cairo_color_t color;
-          cairo_path_t *path;
-
-          get_palette_color_v0 (render, color_index, &color);
-          status = get_path_for_glyph (render->face, glyph_index, &path);
-          if (unlikely (status)) {
-            if (path)
-              cairo_path_destroy (path);
-            break;
-          }
-
-          cairo_save (cr);
-
-          cairo_new_path (cr);
-          cairo_append_path (cr, path);
-          cairo_clip (cr);
-          cairo_set_source_rgba (cr, color.red, color.green, color.blue, color.alpha);
-          cairo_paint (cr);
-
-          cairo_restore (cr);
-
-          cairo_path_destroy (path);
-        }
-    }
 
   cairo_restore (cr);
 
@@ -2017,23 +1457,15 @@ draw_colr_glyph (cairo_colr_glyph_render_t *render,
  * using the given colors.
  */
 cairo_status_t
-_cairo_render_colr_glyph (FT_Face face,
-                          unsigned long glyph,
-                          FT_UShort palette_index,
-                          const cairo_color_t *foreground_color,
-                          cairo_image_surface_t **out_surface)
+_cairo_render_colr_v1_glyph (FT_Face face,
+                             unsigned long glyph,
+                             FT_UShort palette_index,
+                             cairo_t              *cr)
 {
   cairo_status_t status = CAIRO_STATUS_SUCCESS;
-  float xmin, ymin, xmax, ymax;
   cairo_colr_glyph_render_t *colr_render = NULL;
   FT_Color *palette = NULL;
   FT_Palette_Data palette_data;
-  cairo_surface_t *surface = NULL;
-  cairo_pattern_t *pattern = NULL;
-  cairo_t *cr = NULL;
-  cairo_matrix_t matrix;
-
-  *out_surface = NULL;
 
 #if DEBUG_COLR
   printf ("_cairo_render_colr_glyph  glyph index: %ld\n", glyph);
@@ -2055,61 +1487,18 @@ _cairo_render_colr_glyph (FT_Face face,
   colr_render->face = face;
   colr_render->palette = palette;
   colr_render->num_palette_entries = palette_data.num_palette_entries;
-  colr_render->foreground_color = foreground_color;
+  colr_render->foreground_color = cairo_pattern_reference (cairo_get_source (cr));
   colr_render->level = 0;
 
-  status = _cairo_colr_glyph_bounds (face, glyph, &xmin, &ymin, &xmax, &ymax);
-  if (unlikely (status))
-    goto cleanup;
-
-  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-                                        ceil (xmax - xmin),
-                                        ceil (ymax - ymin));
-  if (unlikely (surface == NULL)) {
-    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
-    goto cleanup;
-  }
-
-  cairo_surface_set_device_offset (surface, - xmin,  - ymin);
-
-  cr = cairo_create (surface);
-
-  cairo_push_group (cr);
-
   status = draw_colr_glyph (colr_render,
                             glyph,
                             FT_COLOR_INCLUDE_ROOT_TRANSFORM,
                             cr);
-
-  pattern = cairo_pop_group (cr);
-
-  if (unlikely (status))
-    goto cleanup;
-
-  /* Flip the result */
-  cairo_matrix_init_scale (&matrix, 1, -1);
-  cairo_matrix_translate (&matrix, 0, - (ymax - ymin) -  2 * ymin);
-  cairo_pattern_set_matrix (pattern, &matrix);
-  cairo_set_source (cr, pattern);
-
-  cairo_paint (cr);
-
-  /* Adjust the device offset to keep the glyphs reference
-   * point at the origin
-   */
-  cairo_surface_set_device_offset (surface, - xmin,  ymax);
-
-  *out_surface = (cairo_image_surface_t *) surface;
-  surface = NULL;
-
+  
 cleanup:
 
-  if (cr)
-    cairo_destroy (cr);
-  if (surface)
-    cairo_surface_destroy (surface);
-  if (pattern)
-    cairo_pattern_destroy (pattern);
+  cairo_pattern_destroy (colr_render->foreground_color);
+
   if (colr_render)
     free (colr_render);
 
diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index 2578692fd..3ea15e862 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -2645,16 +2645,8 @@ _cairo_ft_scaled_glyph_init_surface (cairo_ft_scaled_font_t     *scaled_font,
 
     glyph = face->glyph;
 
-    if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_COLR_V1) {
-	uses_foreground_color = _cairo_colr_glyph_uses_foreground (face,
-								   _cairo_scaled_glyph_index(scaled_glyph));
-	status = _cairo_render_colr_glyph (face,
-					   glyph->glyph_index,
-					   scaled_font->base.options.palette_index,
-					   foreground_color,
-					   &surface);
-    } else if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_COLR_V0 ||
-	       glyph_priv->format == CAIRO_FT_GLYPH_TYPE_OUTLINE) {
+    if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_COLR_V0 ||
+	glyph_priv->format == CAIRO_FT_GLYPH_TYPE_OUTLINE) {
 
 	status = _render_glyph_outline (face, &scaled_font->ft_options.base,
 					    &surface);
@@ -2799,6 +2791,141 @@ _cairo_ft_scaled_glyph_init_record_colr_v0_glyph (cairo_ft_scaled_font_t *scaled
 }
 #endif
 
+#if HAVE_FT_GET_COLOR_GLYPH_PAINT
+static cairo_int_status_t
+_cairo_ft_scaled_glyph_init_record_colr_v1_glyph (cairo_ft_scaled_font_t *scaled_font,
+						  cairo_scaled_glyph_t   *scaled_glyph,
+						  FT_Face                 face,
+						  cairo_text_extents_t   *extents)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_surface_t *recording_surface;
+    cairo_t *cr;
+    cairo_pattern_t *pattern;
+    FT_Color *palette;
+    unsigned int num_palette_entries;
+
+    recording_surface =
+	cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
+
+    cairo_surface_set_device_scale (recording_surface, 1, -1);
+
+    cr = cairo_create (recording_surface);
+
+    cairo_set_font_size (cr, 1.0);
+    cairo_set_font_options (cr, &scaled_font->base.options);
+
+    pattern = cairo_pattern_create_rgb (0, 0, 0);
+    pattern->is_userfont_foreground = TRUE;
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+
+    extents->x_bearing = DOUBLE_FROM_26_6(face->bbox.xMin);
+    extents->y_bearing = DOUBLE_FROM_26_6(face->bbox.yMin);
+    extents->width = DOUBLE_FROM_26_6(face->bbox.xMax) - extents->x_bearing;
+    extents->height = DOUBLE_FROM_26_6(face->bbox.yMax) - extents->y_bearing;
+
+    _cairo_ft_scaled_glyph_set_palette (scaled_font, face, &num_palette_entries, &palette);
+
+    if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) {
+	status = _cairo_render_colr_v1_glyph (face,
+					      _cairo_scaled_glyph_index (scaled_glyph),
+					      scaled_font->base.options.palette_index,
+					      cr);
+	if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED)
+	    status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+	if (status == CAIRO_STATUS_SUCCESS)
+	    status = cairo_status (cr);
+    }
+
+    cairo_destroy (cr);
+
+    if (status) {
+	cairo_surface_destroy (recording_surface);
+	scaled_glyph->color_glyph = FALSE;
+	scaled_glyph->color_glyph_set = TRUE;
+	return status;
+    }
+
+    _cairo_scaled_glyph_set_recording_surface (scaled_glyph,
+					       &scaled_font->base,
+					       recording_surface);
+
+    scaled_glyph->color_glyph = TRUE;
+    scaled_glyph->color_glyph_set = TRUE;
+
+    /* get metrics */
+
+    /* Copied from cairo-user-font.c */
+    cairo_matrix_t extent_scale;
+    double extent_x_scale;
+    double extent_y_scale;
+    double snap_x_scale;
+    double snap_y_scale;
+    double fixed_scale, x_scale, y_scale;
+
+    extent_scale = scaled_font->base.scale_inverse;
+    snap_x_scale = 1.0;
+    snap_y_scale = 1.0;
+    status = _cairo_matrix_compute_basis_scale_factors (&extent_scale,
+							&x_scale, &y_scale,
+							1);
+    if (status == CAIRO_STATUS_SUCCESS) {
+	if (x_scale == 0)
+	    x_scale = 1;
+	if (y_scale == 0)
+	    y_scale = 1;
+
+	snap_x_scale = x_scale;
+	snap_y_scale = y_scale;
+
+	/* since glyphs are pretty much 1.0x1.0, we can reduce error by
+	 * scaling to a larger square.  say, 1024.x1024. */
+	fixed_scale = 1024;
+	x_scale /= fixed_scale;
+	y_scale /= fixed_scale;
+
+	cairo_matrix_scale (&extent_scale, 1.0 / x_scale, 1.0 / y_scale);
+
+	extent_x_scale = x_scale;
+	extent_y_scale = y_scale;
+    }
+
+    {
+	/* compute width / height */
+	cairo_box_t bbox;
+	double x1, y1, x2, y2;
+	double x_scale, y_scale;
+
+	/* Compute extents.x/y/width/height from recording_surface,
+	 * in font space.
+	 */
+	status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
+						    &bbox,
+						    &extent_scale);
+	if (unlikely (status))
+	    return status;
+
+	_cairo_box_to_doubles (&bbox, &x1, &y1, &x2, &y2);
+
+	x_scale = extent_x_scale;
+	y_scale = extent_y_scale;
+	extents->x_bearing = x1 * x_scale;
+	extents->y_bearing = y1 * y_scale;
+	extents->width     = (x2 - x1) * x_scale;
+	extents->height    = (y2 - y1) * y_scale;
+    }
+
+    if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) {
+	extents->x_advance = _cairo_lround (extents->x_advance / snap_x_scale) * snap_x_scale;
+	extents->y_advance = _cairo_lround (extents->y_advance / snap_y_scale) * snap_y_scale;
+    }
+
+    return status;
+}
+#endif
+
 #if HAVE_FT_SVG_DOCUMENT
 static cairo_int_status_t
 _cairo_ft_scaled_glyph_init_record_svg_glyph (cairo_ft_scaled_font_t *scaled_font,
@@ -2950,9 +3077,9 @@ _cairo_ft_scaled_glyph_init_record_svg_glyph (cairo_ft_scaled_font_t *scaled_fon
 #endif
 
 static cairo_int_status_t
-_cairo_ft_scaled_glyph_init_surface_svg_glyph (cairo_ft_scaled_font_t *scaled_font,
-					       cairo_scaled_glyph_t   *scaled_glyph,
-					       const cairo_color_t    *foreground_color)
+_cairo_ft_scaled_glyph_init_surface_for_recording_surface (cairo_ft_scaled_font_t *scaled_font,
+							   cairo_scaled_glyph_t   *scaled_glyph,
+							   const cairo_color_t    *foreground_color)
 {
     cairo_surface_t *surface;
     int width, height;
@@ -3206,14 +3333,16 @@ _cairo_ft_scaled_glyph_init_metrics (cairo_ft_scaled_font_t     *scaled_font,
 					load_flags,
 					&fs_metrics);
 
+
+/* SVG and COLR v1 glyphs require the bounding box to be obtained from
+ * the ink extents of the rendering. We need to render glyph to a
+ * recording surface to obtain these extents. But we also need the
+ * advance from _cairo_ft_scaled_glyph_get_metrics() before calling
+ * this function.
+ */
+
 #if HAVE_FT_SVG_DOCUMENT
     if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_SVG) {
-	/* SVG glyphs require the bounding box to be obtained from the
-	 * ink extents of the SVG rendering. We need to render the SVG
-	 * to a recording surface to obtain these extents. But we also
-	 * need the advance from _cairo_ft_scaled_glyph_get_metrics()
-	 * before calling this function.
-	 */
 	status = (cairo_int_status_t)_cairo_ft_scaled_glyph_init_record_svg_glyph (scaled_font,
 										   scaled_glyph,
 										   face,
@@ -3221,6 +3350,15 @@ _cairo_ft_scaled_glyph_init_metrics (cairo_ft_scaled_font_t     *scaled_font,
     }
 #endif
 
+#if HAVE_FT_GET_COLOR_GLYPH_PAINT
+    if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_COLR_V1) {
+	status = (cairo_int_status_t)_cairo_ft_scaled_glyph_init_record_colr_v1_glyph (scaled_font,
+										       scaled_glyph,
+										       face,
+										       &fs_metrics);
+    }
+#endif
+
     _cairo_scaled_glyph_set_metrics (scaled_glyph,
 				     &scaled_font->base,
 				     &fs_metrics);
@@ -3283,11 +3421,12 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
 	switch (glyph_priv->format) {
 	    case CAIRO_FT_GLYPH_TYPE_BITMAP:
 	    case CAIRO_FT_GLYPH_TYPE_OUTLINE:
-	    case CAIRO_FT_GLYPH_TYPE_COLR_V1:
-		status = CAIRO_INT_STATUS_UNSUPPORTED;
 		break;
 	    case CAIRO_FT_GLYPH_TYPE_SVG:
-		/* The SVG recording surface is initialized in _cairo_ft_scaled_glyph_init_metrics() */
+	    case CAIRO_FT_GLYPH_TYPE_COLR_V1:
+		/* The SVG and COLR v1 recording surfaces are
+		 * initialized in _cairo_ft_scaled_glyph_init_metrics()
+		 */
 		status = CAIRO_STATUS_SUCCESS;
 		break;
 	    case CAIRO_FT_GLYPH_TYPE_COLR_V0:
@@ -3305,8 +3444,10 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
     }
 
     if (info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) {
-	if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_SVG) {
-	    status = _cairo_ft_scaled_glyph_init_surface_svg_glyph (scaled_font,
+	if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_SVG ||
+	    glyph_priv->format == CAIRO_FT_GLYPH_TYPE_COLR_V1)
+	{
+	    status = _cairo_ft_scaled_glyph_init_surface_for_recording_surface (scaled_font,
 								    scaled_glyph,
 								    foreground_color);
 	} else {
diff --git a/src/cairo-ft-private.h b/src/cairo-ft-private.h
index 8ce0e95cd..ba5c81e29 100644
--- a/src/cairo-ft-private.h
+++ b/src/cairo-ft-private.h
@@ -70,17 +70,13 @@ _cairo_render_svg_glyph (const char           *svg_document,
                          cairo_t              *cr);
 #endif
 
+#if HAVE_FT_GET_COLOR_GLYPH_PAINT
 cairo_private cairo_status_t
-_cairo_render_colr_glyph (FT_Face                 face,
-                          unsigned long           glyph,
-                          FT_UShort               palette_index,
-                          const cairo_color_t    *foreground_color,
-                          cairo_image_surface_t **surface);
-
-cairo_private int
-_cairo_colr_glyph_uses_foreground (FT_Face              face,
-                                   unsigned long        glyph);
-
+_cairo_render_colr_v1_glyph (FT_Face                 face,
+                             unsigned long           glyph,
+                             FT_UShort               palette_index,
+                             cairo_t                *cr);
+#endif
 
 CAIRO_END_DECLS
 
commit 9ed34574a6f15e9485a44e882da8e715105b6a5a
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Jan 7 20:59:33 2023 +1030

    Fix a couple of bugs and add a #define to enable debug

diff --git a/src/cairo-colr-glyph-render.c b/src/cairo-colr-glyph-render.c
index 83e8a6229..605b49713 100644
--- a/src/cairo-colr-glyph-render.c
+++ b/src/cairo-colr-glyph-render.c
@@ -47,6 +47,8 @@
 #include FT_OUTLINE_H
 #include FT_SIZES_H
 
+/* #define DEBUG_COLR 1 */
+
 
 /* {{{ Utilities */
 
@@ -462,9 +464,9 @@ get_glyph_bounds (FT_Face face, unsigned int glyph_index, Bounds *bounds)
   FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT);
 
   bounds->xmin = f26_6 (face->glyph->metrics.horiBearingX);
-  bounds->ymax = f26_6 (face->glyph->metrics.horiBearingY);
+  bounds->ymin = f26_6 (face->glyph->metrics.horiBearingY);
   bounds->xmax = bounds->xmin + f26_6 (face->glyph->metrics.width);
-  bounds->ymin = - (bounds->ymin + f26_6 (face->glyph->metrics.height));
+  bounds->ymax = - (bounds->ymin + f26_6 (face->glyph->metrics.height));
 }
 
 static void
@@ -949,7 +951,9 @@ draw_paint_colr_layers (cairo_colr_glyph_render_t *render,
   FT_OpaquePaint paint;
   cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
-  //printf ("%*sDraw PaintColrLayers\n", 2 * render->level, "");
+#if DEBUG_COLR
+  printf ("%*sDraw PaintColrLayers\n", 2 * render->level, "");
+#endif
 
   while (FT_Get_Paint_Layers (render->face, &colr_layers->layer_iterator, &paint))
     {
@@ -1023,7 +1027,9 @@ draw_paint_solid (cairo_colr_glyph_render_t *render,
 {
   cairo_color_t color;
 
-  //printf ("%*sDraw PaintSolid\n", 2 * render->level, "");
+#if DEBUG_COLR
+  printf ("%*sDraw PaintSolid\n", 2 * render->level, "");
+#endif
 
   get_palette_color (render, &solid->color, &color);
   cairo_set_source_rgba (cr, color.red, color.green, color.blue, color.alpha);
@@ -1085,7 +1091,7 @@ read_colorline (cairo_colr_glyph_render_t *render,
   i = 0;
   while (FT_Get_Colorline_Stops (render->face, &stop, &colorline->color_stop_iterator))
     {
-      cl->stops[i].position = f2_14 (stop.stop_offset);
+      cl->stops[i].position = f16_16 (stop.stop_offset);
       get_palette_color (render, &stop.color, &cl->stops[i].color);
       i++;
     }
@@ -1174,7 +1180,9 @@ draw_paint_linear_gradient (cairo_colr_glyph_render_t *render,
   cairo_status_t status = CAIRO_STATUS_SUCCESS;
   float min, max;
 
-  //printf ("%*sDraw PaintLinearGradient\n", 2 * render->level, "");
+#if DEBUG_COLR
+  printf ("%*sDraw PaintLinearGradient\n", 2 * render->level, "");
+#endif
 
   cl = read_colorline (render, &gradient->colorline);
   if (unlikely (cl == NULL))
@@ -1187,6 +1195,7 @@ draw_paint_linear_gradient (cairo_colr_glyph_render_t *render,
   interpolate_points (&p0, &p1, max, &pp1);
 
   pattern = cairo_pattern_create_linear (pp0.x, pp0.y, pp1.x, pp1.y);
+
   cairo_pattern_set_extend (pattern, cairo_extend (gradient->colorline.extend));
 
   for (int i = 0; i < cl->n_stops; i++)
@@ -1220,7 +1229,9 @@ draw_paint_radial_gradient (cairo_colr_glyph_render_t *render,
   cairo_pattern_t *pattern;
   cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
-  //printf ("%*sDraw PaintRadialGradient\n", 2 * render->level, "");
+#if DEBUG_COLR
+  printf ("%*sDraw PaintRadialGradient\n", 2 * render->level, "");
+#endif
 
   cl = read_colorline (render, &gradient->colorline);
   if (unlikely (cl == NULL))
@@ -1582,7 +1593,9 @@ draw_paint_sweep_gradient (cairo_colr_glyph_render_t *render,
   cairo_pattern_t *pattern;
   cairo_extend_t extend;
 
-  //printf ("%*sDraw PaintSweepGradient\n", 2 * render->level, "");
+#if DEBUG_COLR
+  printf ("%*sDraw PaintSweepGradient\n", 2 * render->level, "");
+#endif
 
   cl = read_colorline (render, &gradient->colorline);
   if (unlikely (cl == NULL))
@@ -1622,7 +1635,9 @@ draw_paint_glyph (cairo_colr_glyph_render_t *render,
   cairo_path_t *path;
   cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
-  //printf ("%*sDraw PaintGlyph\n", 2 * render->level, "");
+#if DEBUG_COLR
+  printf ("%*sDraw PaintGlyph\n", 2 * render->level, "");
+#endif
 
   status = get_path_for_glyph (render->face, glyph->glyphID, &path);
   if (unlikely (status))
@@ -1669,7 +1684,9 @@ draw_paint_transform (cairo_colr_glyph_render_t *render,
   cairo_matrix_t t;
   cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
-  //printf ("%*sDraw PaintTransform\n", 2 * render->level, "");
+#if DEBUG_COLR
+  printf ("%*sDraw PaintTransform\n", 2 * render->level, "");
+#endif
 
   cairo_matrix_init (&t,
                      f16_16 (transform->affine.xx),
@@ -1696,7 +1713,9 @@ draw_paint_translate (cairo_colr_glyph_render_t *render,
 {
   cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
-  //printf ("%*sDraw PaintTranslate\n", 2 * render->level, "");
+#if DEBUG_COLR
+  printf ("%*sDraw PaintTranslate\n", 2 * render->level, "");
+#endif
 
   cairo_save (cr);
 
@@ -1715,7 +1734,9 @@ draw_paint_rotate (cairo_colr_glyph_render_t *render,
 {
   cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
-  //printf ("%*sDraw PaintRotate\n", 2 * render->level, "");
+#if DEBUG_COLR
+  printf ("%*sDraw PaintRotate\n", 2 * render->level, "");
+#endif
 
   cairo_save (cr);
 
@@ -1736,7 +1757,9 @@ draw_paint_scale (cairo_colr_glyph_render_t *render,
 {
   cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
-  //printf ("%*sDraw PaintScale\n", 2 * render->level, "");
+#if DEBUG_COLR
+  printf ("%*sDraw PaintScale\n", 2 * render->level, "");
+#endif
 
   cairo_save (cr);
 
@@ -1758,7 +1781,9 @@ draw_paint_skew (cairo_colr_glyph_render_t *render,
   cairo_matrix_t s;
   cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
-  //printf ("%*sDraw PaintSkew\n", 2 * render->level, "");
+#if DEBUG_COLR
+  printf ("%*sDraw PaintSkew\n", 2 * render->level, "");
+#endif
 
   cairo_save (cr);
 
@@ -1780,7 +1805,10 @@ draw_paint_composite (cairo_colr_glyph_render_t *render,
 {
   cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
-  //printf ("%*sDraw PaintComposite\n", 2 * render->level, "");
+
+#if DEBUG_COLR
+  printf ("%*sDraw PaintComposite\n", 2 * render->level, "");
+#endif
 
   cairo_save (cr);
 
@@ -1937,6 +1965,7 @@ draw_colr_glyph (cairo_colr_glyph_render_t *render,
       cairo_new_path (cr);
       cairo_rectangle (cr, xmin, ymin, xmax - xmin, ymax - ymin);
       cairo_clip (cr);
+      printf("%f %f %f %f\n", xmin, ymin, xmax, ymax);
     }
 
   if (FT_Get_Color_Glyph_Paint (render->face, glyph, root, &paint))
@@ -2006,6 +2035,10 @@ _cairo_render_colr_glyph (FT_Face face,
 
   *out_surface = NULL;
 
+#if DEBUG_COLR
+  printf ("_cairo_render_colr_glyph  glyph index: %ld\n", glyph);
+#endif
+
   colr_render = _cairo_malloc (sizeof (cairo_colr_glyph_render_t));
   if (unlikely (colr_render == NULL)) {
     status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
commit 3c8dec60e5c9534ccfef25d1a916cf33eba911d4
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Fri Jan 6 22:21:16 2023 +1030

    Integrate COLR v1 renderer with cairo-ft-font.c

diff --git a/meson.build b/meson.build
index 02f74856c..75bbf37b1 100644
--- a/meson.build
+++ b/meson.build
@@ -320,6 +320,7 @@ if freetype_dep.found()
   }]
 
   ft_check_funcs = [
+    'FT_Get_Color_Glyph_Paint',
     'FT_Get_X11_Font_Format',
     'FT_GlyphSlot_Embolden',
     'FT_GlyphSlot_Oblique',
diff --git a/src/cairo-colr-glyph-render.c b/src/cairo-colr-glyph-render.c
index 68dd100c4..83e8a6229 100644
--- a/src/cairo-colr-glyph-render.c
+++ b/src/cairo-colr-glyph-render.c
@@ -29,23 +29,24 @@
  *      Matthias Clasen <mclasen at redhat.com>
  */
 
+#include "cairoint.h"
+#include "cairo-array-private.h"
+#include "cairo-ft-private.h"
+
+#include <assert.h>
 #include <math.h>
-#include <string.h>
 #include <stdio.h>
-#include <assert.h>
-
-#include <freetype/config/ftoption.h>
-#include <freetype/ftcolor.h>
-#include <freetype/ftglyph.h>
-#include <freetype/ftoutln.h>
-#include <freetype/ftsizes.h>
+#include <string.h>
 
-#include "cairoint.h"
-#include "cairo-ft-private.h"
+#if HAVE_FT_GET_COLOR_GLYPH_PAINT
 
-#include "cairo-array-private.h"
+#include <ft2build.h>
+#include FT_CONFIG_OPTIONS_H
+#include FT_COLOR_H
+#include FT_GLYPH_H
+#include FT_OUTLINE_H
+#include FT_SIZES_H
 
-#ifdef TT_SUPPORT_COLRV1
 
 /* {{{ Utilities */
 
@@ -829,27 +830,6 @@ _cairo_colr_glyph_bounds (FT_Face face,
   return CAIRO_STATUS_CLIP_NOT_REPRESENTABLE;
 }
 
-/* Return what COLR table version this glyph is using, 0 or 1.
- * Return -1 if the glyph is not in the COLR table.
- */
-int
-_cairo_colr_glyph_version (FT_Face face,
-                           unsigned long glyph)
-{
-  FT_OpaquePaint paint = { NULL, 0 };
-  FT_UInt glyph_index, color_index;
-  FT_LayerIterator iter;
-
-  if (FT_Get_Color_Glyph_Paint (face, glyph, FT_COLOR_INCLUDE_ROOT_TRANSFORM, &paint))
-    return 1;
-
-  iter.p = NULL;
-  if (FT_Get_Color_Glyph_Layer (face, glyph, &glyph_index, &color_index, &iter))
-    return 0;
-
-  return -1;
-}
-
 static int
 colorline_uses_foreground (FT_Face face,
                            FT_ColorLine *colorline)
@@ -1575,7 +1555,7 @@ add_sweep_gradient_patches (ColorLine *cl,
                                                a0,       c0,
                                                2 * M_PI, &color,
                                                pattern);
-                  goto done;
+                  return;
                 }
               else
                 {
@@ -1586,7 +1566,6 @@ add_sweep_gradient_patches (ColorLine *cl,
                 }
             }
         }
-done:
     }
 }
 
@@ -2106,6 +2085,6 @@ cleanup:
 
 /* }}} */
 
-#endif
+#endif /* HAVE_FT_GET_COLOR_GLYPH_PAINT */
 
 /* vim:set foldmethod=marker expandtab: */
diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index 823898c23..2578692fd 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -2498,6 +2498,7 @@ typedef enum {
     CAIRO_FT_GLYPH_TYPE_OUTLINE,
     CAIRO_FT_GLYPH_TYPE_SVG,
     CAIRO_FT_GLYPH_TYPE_COLR_V0,
+    CAIRO_FT_GLYPH_TYPE_COLR_V1,
 } cairo_ft_glyph_format_t;
 
 typedef struct {
@@ -2600,6 +2601,7 @@ _cairo_ft_scaled_glyph_init_surface (cairo_ft_scaled_font_t     *scaled_font,
     cairo_status_t status;
     cairo_image_surface_t	*surface;
     cairo_bool_t uses_foreground_color = FALSE;
+    cairo_ft_glyph_private_t *glyph_priv = scaled_glyph->dev_private;
 
     /* Only one info type at a time handled in this function */
     assert (info == CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE || info == CAIRO_SCALED_GLYPH_INFO_SURFACE);
@@ -2643,7 +2645,17 @@ _cairo_ft_scaled_glyph_init_surface (cairo_ft_scaled_font_t     *scaled_font,
 
     glyph = face->glyph;
 
-    if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
+    if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_COLR_V1) {
+	uses_foreground_color = _cairo_colr_glyph_uses_foreground (face,
+								   _cairo_scaled_glyph_index(scaled_glyph));
+	status = _cairo_render_colr_glyph (face,
+					   glyph->glyph_index,
+					   scaled_font->base.options.palette_index,
+					   foreground_color,
+					   &surface);
+    } else if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_COLR_V0 ||
+	       glyph_priv->format == CAIRO_FT_GLYPH_TYPE_OUTLINE) {
+
 	status = _render_glyph_outline (face, &scaled_font->ft_options.base,
 					    &surface);
     } else {
@@ -3084,9 +3096,8 @@ _cairo_ft_scaled_glyph_get_metrics (cairo_ft_scaled_font_t     *scaled_font,
 
 static cairo_bool_t
 _cairo_ft_scaled_glyph_is_colr_v0 (cairo_ft_scaled_font_t *scaled_font,
-				   cairo_scaled_glyph_t	  *scaled_glyph,
+				   cairo_scaled_glyph_t   *scaled_glyph,
 				   FT_Face                 face)
-
 {
 #ifdef HAVE_FT_PALETTE_SELECT
     FT_LayerIterator  iterator;
@@ -3095,15 +3106,33 @@ _cairo_ft_scaled_glyph_is_colr_v0 (cairo_ft_scaled_font_t *scaled_font,
 
     iterator.p  = NULL;
     if (FT_Get_Color_Glyph_Layer(face,
-				 _cairo_scaled_glyph_index (scaled_glyph),
-				 &layer_glyph_index,
-				 &layer_color_index,
-				 &iterator))
+                                 _cairo_scaled_glyph_index (scaled_glyph),
+                                 &layer_glyph_index,
+                                 &layer_color_index,
+				 &iterator) == 1)
     {
 	return TRUE;
     }
 #endif
+    return FALSE;
+}
+
+static cairo_bool_t
+_cairo_ft_scaled_glyph_is_colr_v1 (cairo_ft_scaled_font_t *scaled_font,
+				   cairo_scaled_glyph_t   *scaled_glyph,
+				   FT_Face                 face)
+{
+#if HAVE_FT_GET_COLOR_GLYPH_PAINT
+    FT_OpaquePaint paint = { NULL, 0 };
 
+    if (FT_Get_Color_Glyph_Paint (face,
+				  _cairo_scaled_glyph_index (scaled_glyph),
+				  FT_COLOR_INCLUDE_ROOT_TRANSFORM,
+				  &paint) == 1)
+    {
+	return TRUE;
+    }
+#endif
     return FALSE;
 }
 
@@ -3160,7 +3189,9 @@ _cairo_ft_scaled_glyph_init_metrics (cairo_ft_scaled_font_t     *scaled_font,
     if (is_svg_format) {
          glyph_priv->format = CAIRO_FT_GLYPH_TYPE_SVG;
     } else if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
-	if (_cairo_ft_scaled_glyph_is_colr_v0 (scaled_font, scaled_glyph, face))
+	if (_cairo_ft_scaled_glyph_is_colr_v1 (scaled_font, scaled_glyph, face))
+	    glyph_priv->format = CAIRO_FT_GLYPH_TYPE_COLR_V1;
+	else if (_cairo_ft_scaled_glyph_is_colr_v0 (scaled_font, scaled_glyph, face))
 	    glyph_priv->format = CAIRO_FT_GLYPH_TYPE_COLR_V0;
 	else
 	    glyph_priv->format = CAIRO_FT_GLYPH_TYPE_OUTLINE;
@@ -3252,6 +3283,7 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
 	switch (glyph_priv->format) {
 	    case CAIRO_FT_GLYPH_TYPE_BITMAP:
 	    case CAIRO_FT_GLYPH_TYPE_OUTLINE:
+	    case CAIRO_FT_GLYPH_TYPE_COLR_V1:
 		status = CAIRO_INT_STATUS_UNSUPPORTED;
 		break;
 	    case CAIRO_FT_GLYPH_TYPE_SVG:
diff --git a/src/cairo-ft-private.h b/src/cairo-ft-private.h
index 75d65b4db..8ce0e95cd 100644
--- a/src/cairo-ft-private.h
+++ b/src/cairo-ft-private.h
@@ -70,6 +70,18 @@ _cairo_render_svg_glyph (const char           *svg_document,
                          cairo_t              *cr);
 #endif
 
+cairo_private cairo_status_t
+_cairo_render_colr_glyph (FT_Face                 face,
+                          unsigned long           glyph,
+                          FT_UShort               palette_index,
+                          const cairo_color_t    *foreground_color,
+                          cairo_image_surface_t **surface);
+
+cairo_private int
+_cairo_colr_glyph_uses_foreground (FT_Face              face,
+                                   unsigned long        glyph);
+
+
 CAIRO_END_DECLS
 
 #endif /* CAIRO_HAS_FT_FONT */
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 2cbd52b2c..64db0a336 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -1048,6 +1048,7 @@ cairo_pattern_create_mesh (void)
 
     return &pattern->base;
 }
+slim_hidden_def (cairo_pattern_create_mesh);
 
 /**
  * cairo_pattern_reference:
@@ -1283,7 +1284,7 @@ cairo_mesh_pattern_begin_patch (cairo_pattern_t *pattern)
     for (i = 0; i < 4; i++)
 	mesh->has_color[i] = FALSE;
 }
-
+slim_hidden_def (cairo_mesh_pattern_begin_patch);
 
 static void
 _calc_control_point (cairo_mesh_patch_t *patch, int control_point)
@@ -1400,6 +1401,7 @@ cairo_mesh_pattern_end_patch (cairo_pattern_t *pattern)
 
     mesh->current_patch = NULL;
 }
+slim_hidden_def (cairo_mesh_pattern_end_patch);
 
 /**
  * cairo_mesh_pattern_curve_to:
diff --git a/src/cairo.c b/src/cairo.c
index 7bfc6a143..35696ab89 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -1595,6 +1595,7 @@ cairo_identity_matrix (cairo_t *cr)
     if (unlikely (status))
 	_cairo_set_error (cr, status);
 }
+slim_hidden_def (cairo_identity_matrix);
 
 /**
  * cairo_user_to_device:
@@ -2874,6 +2875,7 @@ cairo_clip_extents (cairo_t *cr,
     if (unlikely (status))
 	_cairo_set_error (cr, status);
 }
+slim_hidden_def (cairo_clip_extents);
 
 /**
  * cairo_in_clip:
diff --git a/src/cairoint.h b/src/cairoint.h
index 976162e6f..032d42145 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1950,6 +1950,7 @@ slim_hidden_proto (cairo_append_path);
 slim_hidden_proto (cairo_arc);
 slim_hidden_proto (cairo_arc_negative);
 slim_hidden_proto (cairo_clip);
+slim_hidden_proto (cairo_clip_extents);
 slim_hidden_proto (cairo_clip_preserve);
 slim_hidden_proto (cairo_close_path);
 slim_hidden_proto (cairo_copy_path);
@@ -1983,6 +1984,7 @@ slim_hidden_proto (cairo_get_tolerance);
 slim_hidden_proto (cairo_glyph_allocate);
 slim_hidden_proto (cairo_glyph_free);
 slim_hidden_proto (cairo_has_current_point);
+slim_hidden_proto (cairo_identity_matrix);
 slim_hidden_proto (cairo_image_surface_create);
 slim_hidden_proto (cairo_image_surface_create_for_data);
 slim_hidden_proto (cairo_image_surface_get_data);
@@ -2004,7 +2006,9 @@ slim_hidden_proto (cairo_matrix_scale);
 slim_hidden_proto (cairo_matrix_transform_distance);
 slim_hidden_proto (cairo_matrix_transform_point);
 slim_hidden_proto (cairo_matrix_translate);
+slim_hidden_proto (cairo_mesh_pattern_begin_patch);
 slim_hidden_proto (cairo_mesh_pattern_curve_to);
+slim_hidden_proto (cairo_mesh_pattern_end_patch);
 slim_hidden_proto (cairo_mesh_pattern_get_control_point);
 slim_hidden_proto (cairo_mesh_pattern_get_corner_color_rgba);
 slim_hidden_proto (cairo_mesh_pattern_get_patch_count);
@@ -2020,6 +2024,7 @@ slim_hidden_proto_no_warn (cairo_path_destroy);
 slim_hidden_proto (cairo_pattern_add_color_stop_rgba);
 slim_hidden_proto (cairo_pattern_create_for_surface);
 slim_hidden_proto (cairo_pattern_create_linear);
+slim_hidden_proto (cairo_pattern_create_mesh);
 slim_hidden_proto (cairo_pattern_create_radial);
 slim_hidden_proto (cairo_pattern_create_rgb);
 slim_hidden_proto (cairo_pattern_create_rgba);
diff --git a/src/meson.build b/src/meson.build
index abb04e1ff..30de39f35 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -130,6 +130,7 @@ cairo_feature_sources = {
   ],
   'cairo-ft': [
     'cairo-ft-font.c',
+    'cairo-colr-glyph-render.c',
     'cairo-svg-glyph-render.c'
   ],
 
commit e892d0e92f9a1fe9d19b9da74c3f9b618187cb53
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Fri Jan 6 22:04:03 2023 +1030

    COLRv1 glyph renderer
    
    Matthias Clasen's COLR v1 glyph renderer from !370

diff --git a/src/cairo-colr-glyph-render.c b/src/cairo-colr-glyph-render.c
new file mode 100644
index 000000000..68dd100c4
--- /dev/null
+++ b/src/cairo-colr-glyph-render.c
@@ -0,0 +1,2111 @@
+/* Copyright © 2022 Matthias Clasen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * Contributor(s):
+ *      Matthias Clasen <mclasen at redhat.com>
+ */
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <freetype/config/ftoption.h>
+#include <freetype/ftcolor.h>
+#include <freetype/ftglyph.h>
+#include <freetype/ftoutln.h>
+#include <freetype/ftsizes.h>
+
+#include "cairoint.h"
+#include "cairo-ft-private.h"
+
+#include "cairo-array-private.h"
+
+#ifdef TT_SUPPORT_COLRV1
+
+/* {{{ Utilities */
+
+#ifndef CLAMP
+#define CLAMP(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+#endif
+
+#ifndef MIN
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef MAX
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+#endif
+
+typedef struct {
+  float x, y;
+} Point;
+
+static inline float
+f16_16 (FT_Fixed f)
+{
+  return f / (float) (1 << 16);
+}
+
+static inline float
+f26_6 (FT_F26Dot6 f)
+{
+  return f / (float) (1 << 6);
+}
+
+static inline float
+f2_14 (FT_F2Dot14 f)
+{
+  return f / (float) (1 << 14);
+}
+
+static inline float
+interpolate (float f0, float f1, float f)
+{
+  return f0 + f * (f1 - f0);
+}
+
+static inline void
+interpolate_points (Point *p0, Point *p1, float f, Point *out)
+{
+  out->x = interpolate (p0->x, p1->x, f);
+  out->y = interpolate (p0->y, p1->y, f);
+}
+
+static inline void
+interpolate_colors (cairo_color_t *c0, cairo_color_t *c1, float f, cairo_color_t *out)
+{
+  out->red = interpolate (c0->red, c1->red, f);
+  out->green = interpolate (c0->green, c1->green, f);
+  out->blue = interpolate (c0->blue, c1->blue, f);
+  out->alpha = interpolate (c0->alpha, c1->alpha, f);
+}
+
+static inline float
+dot (Point p, Point q)
+{
+  return p.x * q.x + p.y * q.y;
+}
+
+static inline Point
+normalize (Point p)
+{
+  float len = sqrt (dot (p, p));
+
+  return (Point) { p.x / len, p.y / len };
+}
+
+static inline Point
+sum (Point p, Point q)
+{
+  return (Point) { p.x + q.x, p.y + q.y };
+}
+
+static inline Point
+difference (Point p, Point q)
+{
+  return (Point) { p.x - q.x, p.y - q.y };
+}
+
+static inline Point
+scale (Point p, float f)
+{
+  return (Point) { p.x * f, p.y * f };
+}
+
+static cairo_operator_t
+cairo_operator (FT_Composite_Mode mode)
+{
+  switch (mode)
+    {
+    case FT_COLR_COMPOSITE_CLEAR: return CAIRO_OPERATOR_CLEAR;
+    case FT_COLR_COMPOSITE_SRC: return CAIRO_OPERATOR_SOURCE;
+    case FT_COLR_COMPOSITE_DEST: return CAIRO_OPERATOR_DEST;
+    case FT_COLR_COMPOSITE_SRC_OVER: return CAIRO_OPERATOR_OVER;
+    case FT_COLR_COMPOSITE_DEST_OVER: return CAIRO_OPERATOR_DEST_OVER;
+    case FT_COLR_COMPOSITE_SRC_IN: return CAIRO_OPERATOR_IN;
+    case FT_COLR_COMPOSITE_DEST_IN: return CAIRO_OPERATOR_DEST_IN;
+    case FT_COLR_COMPOSITE_SRC_OUT: return CAIRO_OPERATOR_OUT;
+    case FT_COLR_COMPOSITE_DEST_OUT: return CAIRO_OPERATOR_DEST_OUT;
+    case FT_COLR_COMPOSITE_SRC_ATOP: return CAIRO_OPERATOR_ATOP;
+    case FT_COLR_COMPOSITE_DEST_ATOP: return CAIRO_OPERATOR_DEST_ATOP;
+    case FT_COLR_COMPOSITE_XOR: return CAIRO_OPERATOR_XOR;
+    case FT_COLR_COMPOSITE_PLUS: return CAIRO_OPERATOR_ADD;
+    case FT_COLR_COMPOSITE_SCREEN: return CAIRO_OPERATOR_SCREEN;
+    case FT_COLR_COMPOSITE_OVERLAY: return CAIRO_OPERATOR_OVERLAY;
+    case FT_COLR_COMPOSITE_DARKEN: return CAIRO_OPERATOR_DARKEN;
+    case FT_COLR_COMPOSITE_LIGHTEN: return CAIRO_OPERATOR_LIGHTEN;
+    case FT_COLR_COMPOSITE_COLOR_DODGE: return CAIRO_OPERATOR_COLOR_DODGE;
+    case FT_COLR_COMPOSITE_COLOR_BURN: return CAIRO_OPERATOR_COLOR_BURN;
+    case FT_COLR_COMPOSITE_HARD_LIGHT: return CAIRO_OPERATOR_HARD_LIGHT;
+    case FT_COLR_COMPOSITE_SOFT_LIGHT: return CAIRO_OPERATOR_SOFT_LIGHT;
+    case FT_COLR_COMPOSITE_DIFFERENCE: return CAIRO_OPERATOR_DIFFERENCE;
+    case FT_COLR_COMPOSITE_EXCLUSION: return CAIRO_OPERATOR_EXCLUSION;
+    case FT_COLR_COMPOSITE_MULTIPLY: return CAIRO_OPERATOR_MULTIPLY;
+    case FT_COLR_COMPOSITE_HSL_HUE: return CAIRO_OPERATOR_HSL_HUE;
+    case FT_COLR_COMPOSITE_HSL_SATURATION: return CAIRO_OPERATOR_HSL_SATURATION;
+    case FT_COLR_COMPOSITE_HSL_COLOR: return CAIRO_OPERATOR_HSL_COLOR;
+    case FT_COLR_COMPOSITE_HSL_LUMINOSITY: return CAIRO_OPERATOR_HSL_LUMINOSITY;
+    case FT_COLR_COMPOSITE_MAX:
+    default:
+      assert (0);
+    }
+}
+
+static cairo_extend_t
+cairo_extend (FT_PaintExtend extend)
+{
+  switch (extend)
+    {
+    case FT_COLR_PAINT_EXTEND_PAD: return CAIRO_EXTEND_PAD;
+    case FT_COLR_PAINT_EXTEND_REPEAT: return CAIRO_EXTEND_REPEAT;
+    case FT_COLR_PAINT_EXTEND_REFLECT: return CAIRO_EXTEND_REFLECT;
+    default:
+      assert (0);
+    }
+}
+
+/* }}} */
+/* {{{ Paths */
+
+typedef struct {
+  cairo_array_t points;
+  int needs_move;
+  int last_move_op;
+} path_data_t;
+
+static cairo_status_t
+close_path (path_data_t *pdata)
+{
+  cairo_path_data_t p;
+  int status;
+
+  if (pdata->last_move_op < 0)
+    return CAIRO_STATUS_SUCCESS;
+
+  p.header.type = CAIRO_PATH_LINE_TO;
+  p.header.length = 2;
+  status = _cairo_array_append (&pdata->points, &p);
+  if (unlikely (status))
+    return status;
+
+  p = *(cairo_path_data_t *) _cairo_array_index (&pdata->points, pdata->last_move_op + 1);
+  status = _cairo_array_append (&pdata->points, &p);
+  if (unlikely (status))
+    return status;
+
+  p.header.type = CAIRO_PATH_CLOSE_PATH;
+  p.header.length = 1;
+  status = _cairo_array_append (&pdata->points, &p);
+  if (unlikely (status))
+    return status;
+
+  pdata->needs_move = 1;
+  pdata->last_move_op = -1;
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+static int
+move_to (const FT_Vector *to,
+         void *data)
+{
+  path_data_t *pdata = data;
+  cairo_path_data_t p;
+  cairo_status_t status;
+
+  status = close_path (pdata);
+  if (unlikely (status))
+    return status;
+
+  pdata->needs_move = 0;
+  pdata->last_move_op = pdata->points.num_elements;
+
+  p.header.type = CAIRO_PATH_MOVE_TO;
+  p.header.length = 2;
+  status = _cairo_array_append (&pdata->points, &p);
+  if (unlikely (status))
+    return status;
+
+  p.point.x = to->x / (float) (1 << 6);
+  p.point.y = to->y / (float) (1 << 6);
+  status = _cairo_array_append (&pdata->points, &p);
+  if (unlikely (status))
+    return status;
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+static int
+line_to (const FT_Vector *to,
+         void *data)
+{
+  path_data_t *pdata = data;
+  cairo_path_data_t p;
+  cairo_status_t status;
+
+  if (pdata->needs_move)
+    return move_to (to, data);
+
+  p.header.type = CAIRO_PATH_LINE_TO;
+  p.header.length = 2;
+  status = _cairo_array_append (&pdata->points, &p);
+  if (unlikely (status))
+    return status;
+
+  p.point.x = to->x / (float) (1 << 6);
+  p.point.y = to->y / (float) (1 << 6);
+  status = _cairo_array_append (&pdata->points, &p);
+  if (unlikely (status))
+    return status;
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+static int
+conic_to (const FT_Vector *control,
+          const FT_Vector *to,
+          void *data)
+{
+  path_data_t *pdata = data;
+  cairo_path_data_t p;
+  double cx, cy;
+  double x0, y0;
+  double x1, y1;
+  double x2, y2;
+  double x3, y3;
+  cairo_status_t status;
+
+  assert (!pdata->needs_move);
+  assert (pdata->points.num_elements > 0);
+
+  p.header.type = CAIRO_PATH_CURVE_TO;
+  p.header.length = 4;
+  status = _cairo_array_append (&pdata->points, &p);
+  if (unlikely (status))
+    return status;
+
+  p = *(cairo_path_data_t *) _cairo_array_index (&pdata->points, pdata->points.num_elements - 2);
+
+  x0 = p.point.x;
+  y0 = p.point.y;
+
+  cx = control->x / (float) (1 << 6);
+  cy = control->y / (float) (1 << 6);
+
+  x3 = to->x / (float) (1 << 6);
+  y3 = to->y / (float) (1 << 6);
+
+  x1 = x0 + (2./3.) * (cx - x0);
+  y1 = y0 + (2./3.) * (cy - y0);
+
+  x2 = x3 + (2./3.) * (cx - x3);
+  y2 = y3 + (2./3.) * (cy - y3);
+
+  p.point.x = x1;
+  p.point.y = y1;
+  status = _cairo_array_append (&pdata->points, &p);
+  if (unlikely (status))
+    return status;
+
+  p.point.x = x2;
+  p.point.y = y2;
+  status = _cairo_array_append (&pdata->points, &p);
+  if (unlikely (status))
+    return status;
+
+  p.point.x = x3;
+  p.point.y = y3;
+  status = _cairo_array_append (&pdata->points, &p);
+  if (unlikely (status))
+    return status;
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+static int
+cubic_to (const FT_Vector *control1,
+          const FT_Vector *control2,
+          const FT_Vector *to,
+          void *data)
+{
+  path_data_t *pdata = data;
+  cairo_path_data_t p;
+  cairo_status_t status;
+
+  assert (!pdata->needs_move);
+  assert (pdata->points.num_elements > 0);
+
+  p.header.type = CAIRO_PATH_CURVE_TO;
+  p.header.length = 4;
+  status = _cairo_array_append (&pdata->points, &p);
+  if (unlikely (status))
+    return status;
+
+  p.point.x = control1->x / (float) (1 << 6);
+  p.point.y = control1->y / (float) (1 << 6);
+  status = _cairo_array_append (&pdata->points, &p);
+  if (unlikely (status))
+    return status;
+
+  p.point.x = control2->x / (float) (1 << 6);
+  p.point.y = control2->y / (float) (1 << 6);
+  status = _cairo_array_append (&pdata->points, &p);
+  if (unlikely (status))
+    return status;
+
+  p.point.x = to->x / (float) (1 << 6);
+  p.point.y = to->y / (float) (1 << 6);
+  status = _cairo_array_append (&pdata->points, &p);
+  if (unlikely (status))
+    return status;
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+get_path_for_glyph (FT_Face face,
+                    unsigned int glyph,
+                    cairo_path_t **path)
+{
+  path_data_t pdata;
+  FT_Outline_Funcs callbacks = {
+    move_to, line_to, conic_to, cubic_to, 0, 0
+  };
+  FT_Error error;
+
+  *path = NULL;
+
+  error = FT_Load_Glyph (face, glyph, FT_LOAD_DEFAULT);
+  if (error != 0)
+    return CAIRO_STATUS_INVALID_PATH_DATA;
+
+  _cairo_array_init (&pdata.points, sizeof (cairo_path_data_t));
+  pdata.needs_move = 1;
+  pdata.last_move_op = -1;
+
+  if (FT_Outline_Decompose (&face->glyph->outline, &callbacks, &pdata) != 0)
+    {
+      _cairo_array_fini (&pdata.points);
+      return CAIRO_STATUS_INVALID_PATH_DATA;
+    }
+
+  close_path (&pdata);
+
+  *path = _cairo_malloc (sizeof (cairo_path_t));
+  if (unlikely (path == NULL))
+    return CAIRO_STATUS_NO_MEMORY;
+
+  (*path)->status = CAIRO_STATUS_SUCCESS;
+  (*path)->data = (cairo_path_data_t *) pdata.points.elements;
+  (*path)->num_data = pdata.points.num_elements;
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+/* }}} */
+/* {{{ Bounds and miscellaneous info */
+
+typedef struct {
+  float xmin, ymin, xmax, ymax;
+} Bounds;
+
+static void
+union_bounds (Bounds *b1, Bounds *b2)
+{
+  b2->xmin = MIN (b1->xmin, b2->xmin);
+  b2->ymin = MIN (b1->ymin, b2->ymin);
+  b2->xmax = MAX (b1->xmax, b2->xmax);
+  b2->ymax = MAX (b1->ymax, b2->ymax);
+}
+
+static void
+intersect_bounds (Bounds *b1, Bounds *b2)
+{
+  b2->xmin = MAX (b1->xmin, b2->xmin);
+  b2->ymin = MAX (b1->ymin, b2->ymin);
+  b2->xmax = MIN (b1->xmax, b2->xmax);
+  b2->ymax = MIN (b1->ymax, b2->ymax);
+
+  if (b2->xmin > b2->ymax || b2->ymin > b2->ymax)
+    b2->xmin = b2->ymin = b2->xmax = b2->ymax = 0;
+}
+
+static void
+get_glyph_bounds (FT_Face face, unsigned int glyph_index, Bounds *bounds)
+{
+  FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT);
+
+  bounds->xmin = f26_6 (face->glyph->metrics.horiBearingX);
+  bounds->ymax = f26_6 (face->glyph->metrics.horiBearingY);
+  bounds->xmax = bounds->xmin + f26_6 (face->glyph->metrics.width);
+  bounds->ymin = - (bounds->ymin + f26_6 (face->glyph->metrics.height));
+}
+
+static void
+grow_bounds (Bounds *b, float x, float y)
+{
+  b->xmin = MIN (b->xmin, x);
+  b->ymin = MIN (b->ymin, y);
+  b->xmax = MAX (b->xmax, x);
+  b->ymax = MAX (b->ymax, y);
+}
+
+static void
+transform_bounds_f (Bounds *b, float xx, float yx, float xy, float yy, float dx, float dy)
+{
+  float x, y;
+  Bounds out;
+
+  out.xmin = out.xmax = b->xmin * xx + b->ymin * xy + dx;
+  out.ymin = out.ymax = b->xmin * yx + b->ymin * yy + dy;
+
+  x = b->xmax * xx + b->ymax * xy + dx;
+  y = b->xmax * yx + b->ymax * yy + dy;
+  grow_bounds (&out, x, y);
+
+  x = b->xmax * xx + b->ymin * xy + dx;
+  y = b->xmax * yx + b->ymin * yy + dy;
+  grow_bounds (&out, x, y);
+
+  x = b->xmin * xx + b->ymax * xy + dx;
+  y = b->xmin * yx + b->ymax * yy + dy;
+  grow_bounds (&out, x, y);
+
+  *b = out;
+}
+
+static void
+transform_bounds (Bounds *b, FT_Affine23 *affine)
+{
+  float xx, xy, yx, yy, dx, dy;
+
+  xx = f16_16 (affine->xx);
+  yx = f16_16 (affine->yx);
+  xy = f16_16 (affine->xy);
+  yy = f16_16 (affine->yy);
+  dx = f16_16 (affine->dx);
+  dy = f16_16 (affine->dy);
+
+  transform_bounds_f (b, xx, yx, xy, yy, dx, dy);
+}
+
+static void
+translate_bounds (Bounds *b, float dx, float dy)
+{
+  b->xmin = b->xmin + dx;
+  b->ymin = b->ymin + dy;
+  b->xmax = b->xmax + dx;
+  b->ymax = b->ymax + dy;
+}
+
+static void
+rotate_bounds (Bounds *b, float r)
+{
+  float c, s;
+
+  c = cosf (r);
+  s = sinf (r);
+  transform_bounds_f (b, c, s, -s, c, 0, 0);
+}
+
+static void
+scale_bounds (Bounds *b, float sx, float sy)
+{
+  transform_bounds_f (b, sx, 0, 0, sy, 0, 0);
+}
+
+static void
+skew_bounds (Bounds *b, float ax, float ay)
+{
+  transform_bounds_f (b, 1, tanf (ay), - tanf (ax), 1, 0, 0);
+}
+
+static int
+compute_bounds (FT_Face face, FT_OpaquePaint *paint, Bounds *bounds, int drop_transform)
+{
+  FT_COLR_Paint p;
+  FT_Size orig_size;
+  FT_Size unscaled_size;
+  FT_Matrix orig_transform;
+  FT_Vector orig_delta;
+  int ret = 1;
+
+  if (!FT_Get_Paint (face, *paint, &p))
+    return 0;
+
+  if (drop_transform)
+    {
+      FT_Matrix transform;
+      FT_Vector delta;
+
+      orig_size = face->size;
+      FT_New_Size (face, &unscaled_size);
+      FT_Activate_Size (unscaled_size);
+      FT_Set_Char_Size (face, face->units_per_EM << 6, 0, 0, 0);
+
+      transform.xx = transform.yy = 1 << 16;
+      transform.xy = transform.yx = 0;
+      delta.x = delta.y = 0;
+
+      FT_Get_Transform (face, &orig_transform, &orig_delta);
+      FT_Set_Transform (face, &transform, &delta);
+    }
+
+  switch (p.format)
+    {
+    case FT_COLR_PAINTFORMAT_COLR_LAYERS:
+      {
+        FT_OpaquePaint layer_paint = { NULL, 0 };
+        Bounds b;
+        int first = 1;
+
+        while (FT_Get_Paint_Layers (face, &p.u.colr_layers.layer_iterator, &layer_paint))
+          {
+            if (!compute_bounds (face, &layer_paint, &b, FALSE))
+              {
+                ret = 0;
+                break;
+              }
+
+            if (first)
+              {
+                *bounds = b;
+                first = 0;
+              }
+            else
+              union_bounds (&b, bounds);
+          }
+        //printf ("Layer bounds: %f %f %f %f\n", bounds->xmin, bounds->ymin, bounds->xmax, bounds->ymax);
+      }
+      break;
+    case FT_COLR_PAINTFORMAT_SOLID:
+      ret = 0; // Solid is unbounded
+      break;
+
+    case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT:
+    case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT:
+    case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT:
+      ret = 0; // Gradients are unbounded
+      break;
+
+    case FT_COLR_PAINTFORMAT_GLYPH:
+      get_glyph_bounds (face, p.u.glyph.glyphID, bounds);
+      //printf ("Glyph bounds: %f %f %f %f\n", bounds->xmin, bounds->ymin, bounds->xmax, bounds->ymax);
+      break;
+
+    case FT_COLR_PAINTFORMAT_COLR_GLYPH:
+      get_glyph_bounds (face, p.u.colr_glyph.glyphID, bounds);
+      //printf ("Glyph bounds: %f %f %f %f\n", bounds->xmin, bounds->ymin, bounds->xmax, bounds->ymax);
+      break;
+
+    case FT_COLR_PAINTFORMAT_TRANSFORM:
+      {
+        if (!compute_bounds (face, &p.u.transform.paint, bounds, FALSE))
+          ret = 0;
+        else
+          {
+            transform_bounds (bounds, &p.u.transform.affine);
+            //printf ("Transform bounds: %f %f %f %f\n", bounds->xmin, bounds->ymin, bounds->xmax, bounds->ymax);
+          }
+      }
+      break;
+    case FT_COLR_PAINTFORMAT_TRANSLATE:
+      {
+        if (!compute_bounds (face, &p.u.translate.paint, bounds, FALSE))
+          ret = 0;
+        else
+          translate_bounds (bounds,
+                            f16_16 (p.u.translate.dx),
+                            f16_16 (p.u.translate.dy));
+      }
+      break;
+    case FT_COLR_PAINTFORMAT_ROTATE:
+      {
+        if (!compute_bounds (face, &p.u.rotate.paint, bounds, FALSE))
+          ret = 0;
+        else
+          {
+            translate_bounds (bounds,
+                              - f16_16 (p.u.rotate.center_x),
+                              - f16_16 (p.u.rotate.center_y));
+            rotate_bounds (bounds,
+                           f16_16 (p.u.rotate.angle) * M_PI);
+            translate_bounds (bounds,
+                              f16_16 (p.u.rotate.center_x),
+                              f16_16 (p.u.rotate.center_y));
+          }
+      }
+      break;
+    case FT_COLR_PAINTFORMAT_SCALE:
+      {
+        if (!compute_bounds (face, &p.u.scale.paint, bounds, FALSE))
+          ret = 0;
+        else
+          {
+            translate_bounds (bounds,
+                              - f16_16 (p.u.scale.center_x),
+                              - f16_16 (p.u.scale.center_y));
+            scale_bounds (bounds,
+                          f16_16 (p.u.scale.scale_x),
+                          f16_16 (p.u.scale.scale_y));
+            translate_bounds (bounds,
+                              f16_16 (p.u.scale.center_x),
+                              f16_16 (p.u.scale.center_y));
+          }
+      }
+      break;
+    case FT_COLR_PAINTFORMAT_SKEW:
+      {
+        if (!compute_bounds (face, &p.u.skew.paint, bounds, FALSE))
+          ret = 0;
+        else
+          {
+            translate_bounds (bounds,
+                              - f16_16 (p.u.skew.center_x),
+                              - f16_16 (p.u.skew.center_y));
+            skew_bounds (bounds,
+                         f16_16 (p.u.skew.x_skew_angle) * M_PI,
+                         f16_16 (p.u.skew.y_skew_angle) * M_PI);
+            translate_bounds (bounds,
+                              f16_16 (p.u.skew.center_x),
+                              f16_16 (p.u.skew.center_y));
+          }
+      }
+      break;
+    case FT_COLR_PAINTFORMAT_COMPOSITE:
+      switch ((int)p.u.composite.composite_mode)
+        {
+        case FT_COLR_COMPOSITE_CLEAR:
+          bounds->xmin = bounds->xmax = bounds->ymin = bounds->ymax = 0;
+          break;
+        case FT_COLR_COMPOSITE_SRC:
+        case FT_COLR_COMPOSITE_SRC_OUT:
+          ret = compute_bounds (face, &p.u.composite.source_paint, bounds, FALSE);
+          break;
+        case FT_COLR_COMPOSITE_DEST:
+        case FT_COLR_COMPOSITE_DEST_OUT:
+          ret = compute_bounds (face, &p.u.composite.backdrop_paint, bounds, FALSE);
+          break;
+        case FT_COLR_COMPOSITE_SRC_IN:
+        case FT_COLR_COMPOSITE_DEST_IN:
+          {
+            if (compute_bounds (face, &p.u.composite.source_paint, bounds, FALSE))
+              {
+                Bounds b;
+                if (compute_bounds (face, &p.u.composite.backdrop_paint, &b, FALSE))
+                  intersect_bounds (&b, bounds);
+              }
+            else
+              ret = compute_bounds (face, &p.u.composite.backdrop_paint, bounds, FALSE);
+          }
+          break;
+        default:
+          {
+            if (compute_bounds (face, &p.u.composite.source_paint, bounds, FALSE))
+              {
+                Bounds b;
+
+                if (compute_bounds (face, &p.u.composite.backdrop_paint, &b, FALSE))
+                  union_bounds (&b, bounds);
+                else
+                  ret = 0;
+              }
+            else
+              ret = 0;
+          }
+          break;
+        }
+      break;
+    case FT_COLR_PAINT_FORMAT_MAX:
+    case FT_COLR_PAINTFORMAT_UNSUPPORTED:
+    default:
+      ret = 0;
+      break;
+    }
+
+  if (drop_transform)
+    {
+      FT_Set_Transform (face, &orig_transform, &orig_delta);
+      FT_Activate_Size (orig_size);
+      FT_Done_Size (unscaled_size);
+    }
+
+  return ret;
+}
+
+/* Compute bounds; we don't calculate tight bounds, since
+ * we don't need to. So we use control boxes for outlines
+ * and transform the bounding boxes.
+ */
+static cairo_status_t
+_cairo_colr_glyph_bounds (FT_Face face,
+                          unsigned int glyph,
+                          float *xmin,
+                          float *ymin,
+                          float *xmax,
+                          float *ymax)
+{
+  FT_ClipBox box;
+  FT_OpaquePaint paint = { NULL, 0 };
+
+  if (FT_Get_Color_Glyph_ClipBox (face, glyph, &box))
+    {
+      *xmin = f26_6 (box.bottom_left.x);
+      *ymin = f26_6 (box.bottom_left.y);
+      *xmax = f26_6 (box.top_right.x);
+      *ymax = f26_6 (box.top_right.y);
+      //printf ("bounds from ClipBox\n");
+      return CAIRO_STATUS_SUCCESS;
+    }
+  if (FT_Get_Color_Glyph_Paint (face, glyph, FT_COLOR_INCLUDE_ROOT_TRANSFORM, &paint))
+    {
+      Bounds bounds;
+      compute_bounds (face, &paint, &bounds, TRUE);
+      *xmin = bounds.xmin;
+      *ymin = bounds.ymin;
+      *xmax = bounds.xmax;
+      *ymax = bounds.ymax;
+      //printf ("bounds from Paint\n");
+      return CAIRO_STATUS_SUCCESS;
+    }
+  if (1)
+    {
+      FT_UInt glyph_index, color_index;
+      FT_LayerIterator iter;
+      int count = 0;
+      Bounds bounds;
+
+      iter.p = NULL;
+      while (FT_Get_Color_Glyph_Layer (face, glyph, &glyph_index, &color_index, &iter))
+        {
+          Bounds b;
+
+          get_glyph_bounds (face, glyph_index, &b);
+
+          if (count > 0)
+            union_bounds (&b, &bounds);
+          else
+            bounds = b;
+
+          count++;
+        }
+
+      if (count > 0)
+        {
+          *xmin = bounds.xmin;
+          *ymin = bounds.ymin;
+          *xmax = bounds.xmax;
+          *ymax = bounds.ymax;
+          //printf ("bounds from Layers\n");
+          return CAIRO_STATUS_SUCCESS;
+        }
+    }
+
+  return CAIRO_STATUS_CLIP_NOT_REPRESENTABLE;
+}
+
+/* Return what COLR table version this glyph is using, 0 or 1.
+ * Return -1 if the glyph is not in the COLR table.
+ */
+int
+_cairo_colr_glyph_version (FT_Face face,
+                           unsigned long glyph)
+{
+  FT_OpaquePaint paint = { NULL, 0 };
+  FT_UInt glyph_index, color_index;
+  FT_LayerIterator iter;
+
+  if (FT_Get_Color_Glyph_Paint (face, glyph, FT_COLOR_INCLUDE_ROOT_TRANSFORM, &paint))
+    return 1;
+
+  iter.p = NULL;
+  if (FT_Get_Color_Glyph_Layer (face, glyph, &glyph_index, &color_index, &iter))
+    return 0;
+
+  return -1;
+}
+
+static int
+colorline_uses_foreground (FT_Face face,
+                           FT_ColorLine *colorline)
+{
+  FT_ColorStop stop;
+
+  while (FT_Get_Colorline_Stops (face, &stop, &colorline->color_stop_iterator))
+    {
+      if (stop.color.palette_index == 0xffff)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static int
+paint_uses_foreground (FT_Face face,
+                       FT_OpaquePaint *paint)
+{
+  FT_COLR_Paint p;
+  FT_OpaquePaint layer_paint;
+
+  if (!FT_Get_Paint (face, *paint, &p))
+    return FALSE;
+
+  switch (p.format)
+    {
+    case FT_COLR_PAINTFORMAT_COLR_LAYERS:
+      while (FT_Get_Paint_Layers (face, &p.u.colr_layers.layer_iterator, &layer_paint))
+        {
+          if (paint_uses_foreground (face, &layer_paint))
+            return TRUE;
+        }
+      return FALSE;
+    case FT_COLR_PAINTFORMAT_SOLID:
+      return p.u.solid.color.palette_index == 0xffff;
+    case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT:
+      return colorline_uses_foreground (face, &p.u.linear_gradient.colorline);
+    case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT:
+      return colorline_uses_foreground (face, &p.u.radial_gradient.colorline);
+    case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT:
+      return colorline_uses_foreground (face, &p.u.sweep_gradient.colorline);
+    case FT_COLR_PAINTFORMAT_GLYPH:
+      return paint_uses_foreground (face, &p.u.glyph.paint);
+    case FT_COLR_PAINTFORMAT_COLR_GLYPH:
+      return _cairo_colr_glyph_uses_foreground (face, p.u.glyph.glyphID);
+    case FT_COLR_PAINTFORMAT_TRANSFORM:
+      return paint_uses_foreground (face, &p.u.transform.paint);
+    case FT_COLR_PAINTFORMAT_TRANSLATE:
+      return paint_uses_foreground (face, &p.u.translate.paint);
+    case FT_COLR_PAINTFORMAT_ROTATE:
+      return paint_uses_foreground (face, &p.u.rotate.paint);
+    case FT_COLR_PAINTFORMAT_SCALE:
+      return paint_uses_foreground (face, &p.u.scale.paint);
+    case FT_COLR_PAINTFORMAT_SKEW:
+      return paint_uses_foreground (face, &p.u.skew.paint);
+    case FT_COLR_PAINTFORMAT_COMPOSITE:
+      return paint_uses_foreground (face, &p.u.composite.source_paint) ||
+             paint_uses_foreground (face, &p.u.composite.backdrop_paint);
+    case FT_COLR_PAINT_FORMAT_MAX:
+    case FT_COLR_PAINTFORMAT_UNSUPPORTED:
+    default:
+      assert (0);
+    }
+}
+
+/* Return TRUE if the paint graph for glyph refers to
+ * the foreground color (i.e. uses the color index 0xffff.
+ */
+int
+_cairo_colr_glyph_uses_foreground (FT_Face face,
+                                   unsigned long glyph)
+{
+  FT_OpaquePaint paint = { NULL, 0 };
+  FT_UInt glyph_index, color_index;
+  FT_LayerIterator iter;
+
+  if (FT_Get_Color_Glyph_Paint (face, glyph, FT_COLOR_INCLUDE_ROOT_TRANSFORM, &paint))
+    return paint_uses_foreground (face, &paint);
+
+  iter.p = NULL;
+  if (FT_Get_Color_Glyph_Layer (face, glyph, &glyph_index, &color_index, &iter))
+    {
+      do
+        {
+          if (color_index == 0xffff)
+            return TRUE;
+        }
+      while (FT_Get_Color_Glyph_Layer (face, glyph, &glyph_index, &color_index, &iter));
+    }
+
+  return FALSE;
+}
+
+/* }}} */
+/* {{{ cairo_colr_glyph_render_t implementation */
+
+typedef struct cairo_colr_glyph_render_t cairo_colr_glyph_render_t;
+
+struct cairo_colr_glyph_render_t {
+  FT_Face face;
+  const cairo_color_t *foreground_color;
+  FT_Color *palette;
+  unsigned int num_palette_entries;
+  int level;
+};
+
+static cairo_status_t draw_paint (cairo_colr_glyph_render_t *render,
+                                  FT_OpaquePaint *paint,
+                                  cairo_t *cr);
+
+static cairo_status_t
+draw_paint_colr_layers (cairo_colr_glyph_render_t *render,
+                        FT_PaintColrLayers *colr_layers,
+                        cairo_t *cr)
+{
+  FT_OpaquePaint paint;
+  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+  //printf ("%*sDraw PaintColrLayers\n", 2 * render->level, "");
+
+  while (FT_Get_Paint_Layers (render->face, &colr_layers->layer_iterator, &paint))
+    {
+      cairo_push_group (cr);
+      status = draw_paint (render, &paint, cr);
+      cairo_pop_group_to_source (cr);
+      cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+      cairo_paint (cr);
+
+      if (unlikely (status))
+        break;
+    }
+
+  return status;
+}
+
+static void
+get_palette_color (cairo_colr_glyph_render_t *render,
+                   FT_ColorIndex *ci,
+                   cairo_color_t *color)
+{
+  if (ci->palette_index == 0xffff)
+    *color = *render->foreground_color;
+  else
+    {
+      if (ci->palette_index >= render->num_palette_entries)
+        {
+          fprintf (stderr, "Ignoring out-of-range palette index");
+          *color = *render->foreground_color;
+        }
+      else
+        {
+          FT_Color c = render->palette[ci->palette_index];
+          color->red = c.red / 255.0;
+          color->green = c.green / 255.0;
+          color->blue = c.blue / 255.0;
+       }
+    }
+  color->alpha = f2_14 (ci->alpha);
+}
+
+static void
+get_palette_color_v0 (cairo_colr_glyph_render_t *render,
+                      FT_UInt color_index,
+                      cairo_color_t *color)
+{
+  if (color_index == 0xffff)
+    *color = *render->foreground_color;
+  else
+    {
+      if (color_index >= render->num_palette_entries)
+        {
+          fprintf (stderr, "Ignoring out-of-range palette index");
+          *color = *render->foreground_color;
+        }
+      else
+        {
+          FT_Color c = render->palette[color_index];
+          color->red = c.red / 255.0;
+          color->green = c.green / 255.0;
+          color->blue = c.blue / 255.0;
+          color->alpha = c.alpha / 255.0;
+        }
+    }
+}
+
+static cairo_status_t
+draw_paint_solid (cairo_colr_glyph_render_t *render,
+                  FT_PaintSolid *solid,
+                  cairo_t *cr)
+{
+  cairo_color_t color;
+
+  //printf ("%*sDraw PaintSolid\n", 2 * render->level, "");
+
+  get_palette_color (render, &solid->color, &color);
+  cairo_set_source_rgba (cr, color.red, color.green, color.blue, color.alpha);
+  cairo_paint (cr);
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+typedef struct {
+  cairo_color_t color;
+  float position;
+} ColorStop;
+
+typedef struct {
+  int n_stops;
+  ColorStop *stops;
+} ColorLine;
+
+static void
+free_colorline (ColorLine *cl)
+{
+  free (cl->stops);
+  free (cl);
+}
+
+static int
+compare_stops (const void *p1, const void *p2)
+{
+  const ColorStop *c1 = p1;
+  const ColorStop *c2 = p2;
+
+  if (c1->position < c2->position)
+    return -1;
+  else if (c1->position > c2->position)
+    return 1;
+  else
+    return 0;
+}
+
+static ColorLine *
+read_colorline (cairo_colr_glyph_render_t *render,
+                FT_ColorLine *colorline)
+{
+  ColorLine *cl;
+  FT_ColorStop stop;
+  int i;
+
+  cl = calloc (1, sizeof (ColorLine));
+  if (unlikely (cl == NULL))
+    return NULL;
+
+  cl->n_stops = colorline->color_stop_iterator.num_color_stops;
+  cl->stops = calloc (cl->n_stops, sizeof (ColorStop));
+  if (unlikely (cl->stops == NULL)) {
+    free (cl);
+    return NULL;
+  }
+
+  i = 0;
+  while (FT_Get_Colorline_Stops (render->face, &stop, &colorline->color_stop_iterator))
+    {
+      cl->stops[i].position = f2_14 (stop.stop_offset);
+      get_palette_color (render, &stop.color, &cl->stops[i].color);
+      i++;
+    }
+
+  qsort (cl->stops, cl->n_stops, sizeof (ColorStop), compare_stops);
+
+  return cl;
+}
+
+static void
+reduce_anchors (FT_PaintLinearGradient *gradient,
+                Point *pp0,
+                Point *pp1)
+{
+  Point p0, p1, p2;
+  Point q1, q2;
+  float s;
+  float k;
+
+  p0.x = f16_16 (gradient->p0.x);
+  p0.y = f16_16 (gradient->p0.y);
+  p1.x = f16_16 (gradient->p1.x);
+  p1.y = f16_16 (gradient->p1.y);
+  p2.x = f16_16 (gradient->p2.x);
+  p2.y = f16_16 (gradient->p2.y);
+
+  q2.x = p2.x - p0.x;
+  q2.y = p2.y - p0.y;
+  q1.x = p1.x - p0.x;
+  q1.y = p1.y - p0.y;
+
+  s = q2.x * q2.x + q2.y * q2.y;
+  if (s < 0.000001)
+    {
+      pp0->x = p0.x; pp0->y = p0.y;
+      pp1->x = p1.x; pp1->y = p1.y;
+      return;
+    }
+
+  k = (q2.x * q1.x + q2.y * q1.y) / s;
+  pp0->x = p0.x;
+  pp0->y = p0.y;
+  pp1->x = p1.x - k * q2.x;
+  pp1->y = p1.y - k * q2.y;
+}
+
+static void
+normalize_colorline (ColorLine *cl,
+                     float *out_min,
+                     float *out_max)
+{
+  float min, max;
+
+  *out_min = 0.;
+  *out_max = 1.;
+
+  min = max = cl->stops[0].position;
+  for (int i = 0; i < cl->n_stops; i++)
+    {
+      ColorStop *stop = &cl->stops[i];
+      min = MIN (min, stop->position);
+      max = MAX (max, stop->position);
+    }
+
+  if (min != max)
+    {
+      for (int i = 0; i < cl->n_stops; i++)
+        {
+          ColorStop *stop = &cl->stops[i];
+          stop->position = (stop->position - min) / (max - min);
+        }
+      *out_min = min;
+      *out_max = max;
+    }
+}
+
+static cairo_status_t
+draw_paint_linear_gradient (cairo_colr_glyph_render_t *render,
+                            FT_PaintLinearGradient *gradient,
+                            cairo_t *cr)
+{
+  ColorLine *cl;
+  Point p0, p1;
+  Point pp0, pp1;
+  cairo_pattern_t *pattern;
+  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+  float min, max;
+
+  //printf ("%*sDraw PaintLinearGradient\n", 2 * render->level, "");
+
+  cl = read_colorline (render, &gradient->colorline);
+  if (unlikely (cl == NULL))
+    return CAIRO_STATUS_NO_MEMORY;
+
+  /* cairo only allows stop positions between 0 and 1 */
+  normalize_colorline (cl, &min, &max);
+  reduce_anchors (gradient, &p0, &p1);
+  interpolate_points (&p0, &p1, min, &pp0);
+  interpolate_points (&p0, &p1, max, &pp1);
+
+  pattern = cairo_pattern_create_linear (pp0.x, pp0.y, pp1.x, pp1.y);
+  cairo_pattern_set_extend (pattern, cairo_extend (gradient->colorline.extend));
+
+  for (int i = 0; i < cl->n_stops; i++)
+    {
+      ColorStop *stop = &cl->stops[i];
+      cairo_pattern_add_color_stop_rgba (pattern, stop->position,
+                                         stop->color.red, stop->color.green, stop->color.blue, stop->color.alpha);
+    }
+
+  cairo_set_source (cr, pattern);
+  cairo_paint (cr);
+
+  cairo_pattern_destroy (pattern);
+
+  free_colorline (cl);
+
+  return status;
+}
+
+static cairo_status_t
+draw_paint_radial_gradient (cairo_colr_glyph_render_t *render,
+                            FT_PaintRadialGradient *gradient,
+                            cairo_t *cr)
+{
+  ColorLine *cl;
+  Point start, end;
+  Point start1, end1;
+  float start_radius, end_radius;
+  float start_radius1, end_radius1;
+  float min, max;
+  cairo_pattern_t *pattern;
+  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+  //printf ("%*sDraw PaintRadialGradient\n", 2 * render->level, "");
+
+  cl = read_colorline (render, &gradient->colorline);
+  if (unlikely (cl == NULL))
+    return CAIRO_STATUS_NO_MEMORY;
+
+  start.x = f16_16 (gradient->c0.x);
+  start.y = f16_16 (gradient->c0.y);
+  end.x = f16_16 (gradient->c1.x);
+  end.y = f16_16 (gradient->c1.y);
+
+  start_radius = f16_16 (gradient->r0);
+  end_radius = f16_16 (gradient->r1);
+
+  /* cairo only allows stop positions between 0 and 1 */
+  normalize_colorline (cl, &min, &max);
+  interpolate_points (&start, &end, min, &start1);
+  interpolate_points (&start, &end, max, &end1);
+  start_radius1 = interpolate (start_radius, end_radius, min);
+  end_radius1 = interpolate (start_radius, end_radius, max);
+
+  pattern = cairo_pattern_create_radial (start1.x, start1.y, start_radius1,
+                                         end1.x, end1.y, end_radius1);
+
+  cairo_pattern_set_extend (pattern, cairo_extend (gradient->colorline.extend));
+
+  for (int i = 0; i < cl->n_stops; i++)
+    {
+      ColorStop *stop = &cl->stops[i];
+      cairo_pattern_add_color_stop_rgba (pattern, stop->position,
+                                         stop->color.red, stop->color.green, stop->color.blue, stop->color.alpha);
+    }
+
+  cairo_set_source (cr, pattern);
+  cairo_paint (cr);
+
+  cairo_pattern_destroy (pattern);
+
+  free_colorline (cl);
+
+  return status;
+}
+
+typedef struct {
+  Point center, p0, c0, c1, p1;
+  cairo_color_t color0, color1;
+} Patch;
+
+static void
+add_patch (cairo_pattern_t *pattern, Point *center, Patch *p)
+{
+  cairo_mesh_pattern_begin_patch (pattern);
+  cairo_mesh_pattern_move_to (pattern, center->x, center->y);
+  cairo_mesh_pattern_line_to (pattern, p->p0.x, p->p0.y);
+  cairo_mesh_pattern_curve_to (pattern,
+                               p->c0.x, p->c0.y,
+                               p->c1.x, p->c1.y,
+                               p->p1.x, p->p1.y);
+  cairo_mesh_pattern_line_to (pattern, center->x, center->y);
+  cairo_mesh_pattern_set_corner_color_rgba (pattern, 0,
+                                            p->color0.red,
+                                            p->color0.green,
+                                            p->color0.blue,
+                                            p->color0.alpha);
+  cairo_mesh_pattern_set_corner_color_rgba (pattern, 1,
+                                            p->color0.red,
+                                            p->color0.green,
+                                            p->color0.blue,
+                                            p->color0.alpha);
+  cairo_mesh_pattern_set_corner_color_rgba (pattern, 2,
+                                            p->color1.red,
+                                            p->color1.green,
+                                            p->color1.blue,
+                                            p->color1.alpha);
+  cairo_mesh_pattern_set_corner_color_rgba (pattern, 3,
+                                            p->color1.red,
+                                            p->color1.green,
+                                            p->color1.blue,
+                                            p->color1.alpha);
+  cairo_mesh_pattern_end_patch (pattern);
+}
+
+#define MAX_ANGLE (M_PI / 8.)
+
+static void
+add_sweep_gradient_patches1 (Point *center, float radius,
+                             float a0, cairo_color_t *c0,
+                             float a1, cairo_color_t *c1,
+                             cairo_pattern_t *pattern)
+{
+
+  int num_splits;
+  Point p0;
+  cairo_color_t color0, color1;
+
+  num_splits = ceilf (fabs (a1 - a0) / MAX_ANGLE);
+  p0 = (Point) { cosf (a0), sinf (a0) };
+  color0 = *c0;
+
+  for (int a = 0; a < num_splits; a++)
+    {
+      float k = (a + 1.) / num_splits;
+      float angle1;
+      Point p1;
+      Point A, U;
+      Point C0, C1;
+      Patch patch;
+
+      angle1 = interpolate (a0, a1, k);
+      interpolate_colors (c0, c1, k, &color1);
+
+      patch.color0 = color0;
+      patch.color1 = color1;
+
+      p1 = (Point) { cosf (angle1), sinf (angle1) };
+      patch.p0 = sum (*center, scale (p0, radius));
+      patch.p1 = sum (*center, scale (p1, radius));
+
+      A = normalize (sum (p0, p1));
+      U = (Point) { -A.y, A.x };
+      C0 = sum (A, scale (U, dot (difference (p0, A), p0) / dot (U, p0)));
+      C1 = sum (A, scale (U, dot (difference (p1, A), p1) / dot (U, p1)));
+      patch.c0 = sum (*center, scale (sum (C0, scale (difference (C0, p0), 0.33333)), radius));
+      patch.c1 = sum (*center, scale (sum (C1, scale (difference (C1, p1), 0.33333)), radius));
+
+      add_patch (pattern, center, &patch);
+
+      p0 = p1;
+      color0 = color1;
+    }
+}
+
+static void
+add_sweep_gradient_patches (ColorLine *cl,
+                            cairo_extend_t extend,
+                            Point *center,
+                            float radius,
+                            float start_angle,
+                            float end_angle,
+                            cairo_pattern_t *pattern)
+{
+  float *angles;
+  cairo_color_t color0, color1;
+
+  if (start_angle == end_angle)
+    {
+      if (extend == CAIRO_EXTEND_PAD)
+        {
+          if (start_angle > 0)
+            add_sweep_gradient_patches1 (center, radius,
+                                         0.,          &cl->stops[0].color,
+                                         start_angle, &cl->stops[0].color,
+                                         pattern);
+          if (end_angle < 2 * M_PI)
+            add_sweep_gradient_patches1 (center, radius,
+                                         end_angle, &cl->stops[cl->n_stops - 1].color,
+                                         2 * M_PI,  &cl->stops[cl->n_stops - 1].color,
+                                         pattern);
+        }
+      return;
+    }
+
+  assert (start_angle != end_angle);
+
+  angles = alloca (sizeof (float) * cl->n_stops);
+
+  for (int i = 0; i < cl->n_stops; i++)
+    angles[i] = start_angle + cl->stops[i].position * (end_angle - start_angle);
+
+  /* handle directions */
+  if (end_angle < start_angle)
+    {
+      for (int i = 0; i < cl->n_stops - 1 - i; i++)
+        {
+          ColorStop stop = cl->stops[i];
+          float a = angles[i];
+          cl->stops[i] = cl->stops[cl->n_stops - 1 - i];
+          cl->stops[cl->n_stops - 1 - i] = stop;
+          angles[i] = angles[cl->n_stops - 1 - i];
+          angles[cl->n_stops - 1 - i] = a;
+        }
+    }
+
+  if (extend == CAIRO_EXTEND_PAD)
+    {
+      int pos;
+
+      color0 = cl->stops[0].color;
+      for (pos = 0; pos < cl->n_stops; pos++)
+        {
+          if (angles[pos] >= 0)
+            {
+              if (pos > 0)
+                {
+                  float k = (0 - angles[pos - 1]) / (angles[pos] - angles[pos - 1]);
+                  interpolate_colors (&cl->stops[pos - 1].color, &cl->stops[pos].color, k, &color0);
+                }
+              break;
+            }
+        }
+      if (pos == cl->n_stops)
+        {
+          /* everything is below 0 */
+          color0 = cl->stops[cl->n_stops - 1].color;
+          add_sweep_gradient_patches1 (center, radius,
+                                       0.,       &color0,
+                                       2 * M_PI, &color0,
+                                       pattern);
+          return;
+        }
+
+      add_sweep_gradient_patches1 (center, radius,
+                                   0.,          &color0,
+                                   angles[pos], &cl->stops[pos].color,
+                                   pattern);
+
+      for (pos++; pos < cl->n_stops; pos++)
+        {
+          if (angles[pos] <= 2 * M_PI)
+            {
+              add_sweep_gradient_patches1 (center, radius,
+                                           angles[pos - 1], &cl->stops[pos - 1].color,
+                                           angles[pos],     &cl->stops[pos].color,
+                                           pattern);
+            }
+          else
+            {
+              float k = (2 * M_PI - angles[pos - 1]) / (angles[pos] - angles[pos - 1]);
+              interpolate_colors (&cl->stops[pos - 1].color, &cl->stops[pos].color, k, &color1);
+              add_sweep_gradient_patches1 (center, radius,
+                                           angles[pos - 1], &cl->stops[pos - 1].color,
+                                           2 * M_PI,        &color1,
+                                           pattern);
+              break;
+            }
+        }
+
+      if (pos == cl->n_stops)
+        {
+          /* everything is below 2*M_PI */
+          color0 = cl->stops[cl->n_stops - 1].color;
+          add_sweep_gradient_patches1 (center, radius,
+                                       angles[cl->n_stops - 1], &color0,
+                                       2 * M_PI,                &color0,
+                                       pattern);
+          return;
+        }
+    }
+  else
+    {
+      int k;
+      float span;
+
+      span = angles[cl->n_stops - 1] - angles[0];
+      k = 0;
+      if (angles[0] >= 0)
+        {
+          float ss = angles[0];
+          while (ss > 0)
+            {
+              if (span > 0)
+                {
+                  ss -= span;
+                  k--;
+                }
+              else
+                {
+                  ss += span;
+                  k++;
+                }
+            }
+        }
+      else if (angles[0] < 0)
+        {
+          float ee = angles[cl->n_stops - 1];
+          while (ee < 0)
+            {
+              if (span > 0)
+                {
+                  ee += span;
+                  k++;
+                }
+              else
+                {
+                  ee -= span;
+                  k--;
+                }
+            }
+        }
+
+      //assert (angles[0] + k * span <= 0 && 0 < angles[cl->n_stops - 1] + k * span);
+
+      for (int l = k; TRUE; l++)
+        {
+          for (int i = 1; i < cl->n_stops; i++)
+            {
+              float a0, a1;
+              cairo_color_t *c0, *c1;
+
+              if ((l % 2 != 0) && (extend == CAIRO_EXTEND_REFLECT))
+                {
+                  a0 = angles[0] + angles[cl->n_stops - 1] - angles[cl->n_stops - 1 - (i-1)] + l * span;
+                  a1 = angles[0] + angles[cl->n_stops - 1] - angles[cl->n_stops - 1 - i] + l * span;
+                  c0 = &cl->stops[cl->n_stops - 1 - (i-1)].color;
+                  c1 = &cl->stops[cl->n_stops - 1 - i].color;
+                }
+              else
+                {
+                  a0 = angles[i-1] + l * span;
+                  a1 = angles[i] + l * span;
+                  c0 = &cl->stops[i-1].color;
+                  c1 = &cl->stops[i].color;
+                }
+
+              if (a1 < 0)
+                continue;
+              if (a0 < 0)
+                {
+                  cairo_color_t color;
+                  float f = (0 - a0)/(a1 - a0);
+                  interpolate_colors (c0, c1, f, &color);
+                  add_sweep_gradient_patches1 (center, radius,
+                                               0,  &color,
+                                               a1, c1,
+                                               pattern);
+                }
+              else if (a1 >= 2 * M_PI)
+                {
+                  cairo_color_t color;
+                  float f = (2 * M_PI - a0)/(a1 - a0);
+                  interpolate_colors (c0, c1, f, &color);
+                  add_sweep_gradient_patches1 (center, radius,
+                                               a0,       c0,
+                                               2 * M_PI, &color,
+                                               pattern);
+                  goto done;
+                }
+              else
+                {
+                  add_sweep_gradient_patches1 (center, radius,
+                                               a0, c0,
+                                               a1, c1,
+                                               pattern);
+                }
+            }
+        }
+done:
+    }
+}
+
+static cairo_status_t
+draw_paint_sweep_gradient (cairo_colr_glyph_render_t *render,
+                           FT_PaintSweepGradient *gradient,
+                           cairo_t *cr)
+{
+  ColorLine *cl;
+  Point center;
+  float start_angle, end_angle;
+  double x1, y1, x2, y2;
+  double max_x, max_y, R;
+  cairo_pattern_t *pattern;
+  cairo_extend_t extend;
+
+  //printf ("%*sDraw PaintSweepGradient\n", 2 * render->level, "");
+
+  cl = read_colorline (render, &gradient->colorline);
+  if (unlikely (cl == NULL))
+    return CAIRO_STATUS_NO_MEMORY;
+
+  center.x = f16_16 (gradient->center.x);
+  center.y = f16_16 (gradient->center.y);
+  start_angle = (f16_16 (gradient->start_angle) + 1) * M_PI;
+  end_angle = (f16_16 (gradient->end_angle) + 1) * M_PI;
+
+  pattern = cairo_pattern_create_mesh ();
+
+  cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
+  max_x = MAX ((x1 - center.x) * (x1 - center.x), (x2 - center.x) * (x2 - center.x));
+  max_y = MAX ((y1 - center.y) * (y1 - center.y), (y2 - center.y) * (y2 - center.y));
+  R = sqrt (max_x + max_y);
+
+  extend = cairo_extend (gradient->colorline.extend);
+
+  add_sweep_gradient_patches (cl, extend, &center, R, start_angle, end_angle, pattern);
+
+  cairo_set_source (cr, pattern);
+  cairo_paint (cr);
+
+  cairo_pattern_destroy (pattern);
+
+  free_colorline (cl);
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+draw_paint_glyph (cairo_colr_glyph_render_t *render,
+                  FT_PaintGlyph *glyph,
+                  cairo_t *cr)
+{
+  cairo_path_t *path;
+  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+  //printf ("%*sDraw PaintGlyph\n", 2 * render->level, "");
+
+  status = get_path_for_glyph (render->face, glyph->glyphID, &path);
+  if (unlikely (status))
+    goto cleanup;
+
+  cairo_save (cr);
+
+  cairo_new_path (cr);
+  cairo_append_path (cr, path);
+  cairo_clip (cr);
+
+  status = draw_paint (render, &glyph->paint, cr);
+
+  cairo_restore (cr);
+
+cleanup:
+
+  if (path)
+    cairo_path_destroy (path);
+
+  return status;
+}
+
+static cairo_status_t draw_colr_glyph (cairo_colr_glyph_render_t *render,
+                                       unsigned int glyph,
+                                       FT_Color_Root_Transform  root,
+                                       cairo_t *cr);
+
+static cairo_status_t
+draw_paint_colr_glyph (cairo_colr_glyph_render_t *render,
+                       FT_PaintColrGlyph *colr_glyph,
+                       cairo_t *cr)
+{
+  //printf ("%*sDraw PaintColrGlyph\n", 2 * render->level, "");
+
+  return draw_colr_glyph (render, colr_glyph->glyphID, FT_COLOR_NO_ROOT_TRANSFORM, cr);
+}
+
+static cairo_status_t
+draw_paint_transform (cairo_colr_glyph_render_t *render,
+                      FT_PaintTransform *transform,
+                      cairo_t *cr)
+{
+  cairo_matrix_t t;
+  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+  //printf ("%*sDraw PaintTransform\n", 2 * render->level, "");
+
+  cairo_matrix_init (&t,
+                     f16_16 (transform->affine.xx),
+                     f16_16 (transform->affine.yx),
+                     f16_16 (transform->affine.xy),
+                     f16_16 (transform->affine.yy),
+                     f16_16 (transform->affine.dx),
+                     f16_16 (transform->affine.dy));
+
+  cairo_save (cr);
+
+  cairo_transform (cr, &t);
+  status = draw_paint (render, &transform->paint, cr);
+
+  cairo_restore (cr);
+
+  return status;
+}
+
+static cairo_status_t
+draw_paint_translate (cairo_colr_glyph_render_t *render,
+                      FT_PaintTranslate *translate,
+                      cairo_t *cr)
+{
+  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+  //printf ("%*sDraw PaintTranslate\n", 2 * render->level, "");
+
+  cairo_save (cr);
+
+  cairo_translate (cr, f16_16 (translate->dx), f16_16 (translate->dy));
+  status = draw_paint (render, &translate->paint, cr);
+
+  cairo_restore (cr);
+
+  return status;
+}
+
+static cairo_status_t
+draw_paint_rotate (cairo_colr_glyph_render_t *render,
+                   FT_PaintRotate *rotate,
+                   cairo_t *cr)
+{
+  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+  //printf ("%*sDraw PaintRotate\n", 2 * render->level, "");
+
+  cairo_save (cr);
+
+  cairo_translate (cr, f16_16 (rotate->center_x), f16_16 (rotate->center_y));
+  cairo_rotate (cr, f16_16 (rotate->angle) * M_PI);
+  cairo_translate (cr, - f16_16 (rotate->center_x), - f16_16 (rotate->center_y));
+  status = draw_paint (render, &rotate->paint, cr);
+
+  cairo_restore (cr);
+
+  return status;
+}
+
+static cairo_status_t
+draw_paint_scale (cairo_colr_glyph_render_t *render,
+                  FT_PaintScale *scale,
+                  cairo_t *cr)
+{
+  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+  //printf ("%*sDraw PaintScale\n", 2 * render->level, "");
+
+  cairo_save (cr);
+
+  cairo_translate (cr, f16_16 (scale->center_x), f16_16 (scale->center_y));
+  cairo_scale (cr, f16_16 (scale->scale_x), f16_16 (scale->scale_y));
+  cairo_translate (cr, - f16_16 (scale->center_x), - f16_16 (scale->center_y));
+  status = draw_paint (render, &scale->paint, cr);
+
+  cairo_restore (cr);
+
+  return status;
+}
+
+static cairo_status_t
+draw_paint_skew (cairo_colr_glyph_render_t *render,
+                 FT_PaintSkew *skew,
+                 cairo_t *cr)
+{
+  cairo_matrix_t s;
+  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+  //printf ("%*sDraw PaintSkew\n", 2 * render->level, "");
+
+  cairo_save (cr);
+
+  cairo_translate (cr, f16_16 (skew->center_x), f16_16 (skew->center_y));
+  cairo_matrix_init (&s, 1., tan (f16_16 (skew->y_skew_angle) * M_PI), - tan (f16_16 (skew->x_skew_angle) * M_PI), 1., 0., 0.);
+  cairo_transform (cr, &s);
+  cairo_translate (cr, - f16_16 (skew->center_x), - f16_16 (skew->center_y));
+  status = draw_paint (render, &skew->paint, cr);
+
+  cairo_restore (cr);
+
+  return status;
+}
+
+static cairo_status_t
+draw_paint_composite (cairo_colr_glyph_render_t *render,
+                      FT_PaintComposite *composite,
+                      cairo_t *cr)
+{
+  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+  //printf ("%*sDraw PaintComposite\n", 2 * render->level, "");
+
+  cairo_save (cr);
+
+  cairo_push_group (cr);
+  status = draw_paint (render, &composite->backdrop_paint, cr);
+  if (unlikely (status)) {
+    cairo_pattern_destroy (cairo_pop_group (cr));
+    goto cleanup;
+  }
+
+  cairo_push_group (cr);
+  status = draw_paint (render, &composite->source_paint, cr);
+  if (unlikely (status)) {
+    cairo_pattern_destroy (cairo_pop_group (cr));
+    cairo_pattern_destroy (cairo_pop_group (cr));
+    goto cleanup;
+  }
+
+  cairo_pop_group_to_source (cr);
+  cairo_set_operator (cr, cairo_operator (composite->composite_mode));
+  cairo_paint (cr);
+  cairo_pop_group_to_source (cr);
+  cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+  cairo_paint (cr);
+
+cleanup:
+  cairo_restore (cr);
+
+  return status;
+}
+
+static cairo_status_t
+draw_paint (cairo_colr_glyph_render_t *render,
+            FT_OpaquePaint *paint,
+            cairo_t *cr)
+{
+  FT_COLR_Paint p;
+  FT_Size orig_size;
+  FT_Size unscaled_size;
+  FT_Matrix orig_transform;
+  FT_Vector orig_delta;
+  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+  assert (cairo_status (cr) == CAIRO_STATUS_SUCCESS);
+
+  if (!FT_Get_Paint (render->face, *paint, &p))
+    return CAIRO_STATUS_NO_MEMORY;
+
+  if (render->level == 0)
+    {
+      /* Now that the FT_Get_Paint call has applied the root transform,
+       * make the face unscaled and untransformed, so we can load glyph
+       * contours.
+       */
+
+      FT_Matrix transform;
+      FT_Vector delta;
+
+      orig_size = render->face->size;
+      FT_New_Size (render->face, &unscaled_size);
+      FT_Activate_Size (unscaled_size);
+      FT_Set_Char_Size (render->face, render->face->units_per_EM << 6, 0, 0, 0);
+
+      transform.xx = transform.yy = 1 << 16;
+      transform.xy = transform.yx = 0;
+      delta.x = delta.y = 0;
+
+      FT_Get_Transform (render->face, &orig_transform, &orig_delta);
+      FT_Set_Transform (render->face, &transform, &delta);
+    }
+
+  render->level++;
+
+  switch (p.format)
+    {
+    case FT_COLR_PAINTFORMAT_COLR_LAYERS:
+      status = draw_paint_colr_layers (render, &p.u.colr_layers, cr);
+      break;
+    case FT_COLR_PAINTFORMAT_SOLID:
+      status = draw_paint_solid (render, &p.u.solid, cr);
+      break;
+    case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT:
+      status = draw_paint_linear_gradient (render, &p.u.linear_gradient, cr);
+      break;
+    case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT:
+      status = draw_paint_radial_gradient (render, &p.u.radial_gradient, cr);
+      break;
+    case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT:
+      status = draw_paint_sweep_gradient (render, &p.u.sweep_gradient, cr);
+      break;
+    case FT_COLR_PAINTFORMAT_GLYPH:
+      status = draw_paint_glyph (render, &p.u.glyph, cr);
+      break;
+    case FT_COLR_PAINTFORMAT_COLR_GLYPH:
+      status = draw_paint_colr_glyph (render, &p.u.colr_glyph, cr);
+      break;
+    case FT_COLR_PAINTFORMAT_TRANSFORM:
+      status = draw_paint_transform (render, &p.u.transform, cr);
+      break;
+    case FT_COLR_PAINTFORMAT_TRANSLATE:
+      status = draw_paint_translate (render, &p.u.translate, cr);
+      break;
+    case FT_COLR_PAINTFORMAT_ROTATE:
+      status = draw_paint_rotate (render, &p.u.rotate, cr);
+      break;
+    case FT_COLR_PAINTFORMAT_SCALE:
+      status = draw_paint_scale (render, &p.u.scale, cr);
+      break;
+    case FT_COLR_PAINTFORMAT_SKEW:
+      status = draw_paint_skew (render, &p.u.skew, cr);
+      break;
+    case FT_COLR_PAINTFORMAT_COMPOSITE:
+      status = draw_paint_composite (render, &p.u.composite, cr);
+      break;
+    case FT_COLR_PAINT_FORMAT_MAX:
+    case FT_COLR_PAINTFORMAT_UNSUPPORTED:
+    default:
+      assert (0);
+    }
+
+  render->level--;
+
+  if (render->level == 0)
+    {
+      FT_Set_Transform (render->face, &orig_transform, &orig_delta);
+      FT_Activate_Size (orig_size);
+      FT_Done_Size (unscaled_size);
+    }
+
+  return status;
+}
+
+static cairo_status_t
+draw_colr_glyph (cairo_colr_glyph_render_t *render,
+                 unsigned int glyph,
+                 FT_Color_Root_Transform  root,
+                 cairo_t *cr)
+{
+  FT_OpaquePaint paint = { NULL, 0 };
+  FT_ClipBox box;
+  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+  cairo_save (cr);
+
+  if (FT_Get_Color_Glyph_ClipBox (render->face, glyph, &box))
+    {
+      float xmin, ymin, xmax, ymax;
+
+      xmin = f26_6 (box.bottom_left.x);
+      ymin = f26_6 (box.bottom_left.y);
+      xmax = f26_6 (box.top_right.x);
+      ymax = f26_6 (box.top_right.y);
+
+      cairo_new_path (cr);
+      cairo_rectangle (cr, xmin, ymin, xmax - xmin, ymax - ymin);
+      cairo_clip (cr);
+    }
+
+  if (FT_Get_Color_Glyph_Paint (render->face, glyph, root, &paint))
+    {
+      status = draw_paint (render, &paint, cr);
+    }
+  else
+    {
+      FT_UInt glyph_index, color_index;
+      FT_LayerIterator iter;
+
+      iter.p = NULL;
+      while (FT_Get_Color_Glyph_Layer (render->face, glyph, &glyph_index, &color_index, &iter))
+        {
+          cairo_color_t color;
+          cairo_path_t *path;
+
+          get_palette_color_v0 (render, color_index, &color);
+          status = get_path_for_glyph (render->face, glyph_index, &path);
+          if (unlikely (status)) {
+            if (path)
+              cairo_path_destroy (path);
+            break;
+          }
+
+          cairo_save (cr);
+
+          cairo_new_path (cr);
+          cairo_append_path (cr, path);
+          cairo_clip (cr);
+          cairo_set_source_rgba (cr, color.red, color.green, color.blue, color.alpha);
+          cairo_paint (cr);
+
+          cairo_restore (cr);
+
+          cairo_path_destroy (path);
+        }
+    }
+
+  cairo_restore (cr);
+
+  return status;
+}
+
+/* }}} */
+/* {{{ cairo_colr_glyph_render_t API */
+
+/* Create an image surface and render the glyph onto it,
+ * using the given colors.
+ */
+cairo_status_t
+_cairo_render_colr_glyph (FT_Face face,
+                          unsigned long glyph,
+                          FT_UShort palette_index,
+                          const cairo_color_t *foreground_color,
+                          cairo_image_surface_t **out_surface)
+{
+  cairo_status_t status = CAIRO_STATUS_SUCCESS;
+  float xmin, ymin, xmax, ymax;
+  cairo_colr_glyph_render_t *colr_render = NULL;
+  FT_Color *palette = NULL;
+  FT_Palette_Data palette_data;
+  cairo_surface_t *surface = NULL;
+  cairo_pattern_t *pattern = NULL;
+  cairo_t *cr = NULL;
+  cairo_matrix_t matrix;
+
+  *out_surface = NULL;
+
+  colr_render = _cairo_malloc (sizeof (cairo_colr_glyph_render_t));
+  if (unlikely (colr_render == NULL)) {
+    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    goto cleanup;
+  }
+
+  if (FT_Palette_Data_Get (face, &palette_data) == 0 && palette_data.num_palettes > 0) {
+    if (palette_index >= palette_data.num_palettes)
+      palette_index = CAIRO_COLOR_PALETTE_DEFAULT;
+    if (FT_Palette_Select (face, palette_index, &palette) != 0)
+      palette = NULL;
+  }
+
+  colr_render->face = face;
+  colr_render->palette = palette;
+  colr_render->num_palette_entries = palette_data.num_palette_entries;
+  colr_render->foreground_color = foreground_color;
+  colr_render->level = 0;
+
+  status = _cairo_colr_glyph_bounds (face, glyph, &xmin, &ymin, &xmax, &ymax);
+  if (unlikely (status))
+    goto cleanup;
+
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                        ceil (xmax - xmin),
+                                        ceil (ymax - ymin));
+  if (unlikely (surface == NULL)) {
+    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    goto cleanup;
+  }
+
+  cairo_surface_set_device_offset (surface, - xmin,  - ymin);
+
+  cr = cairo_create (surface);
+
+  cairo_push_group (cr);
+
+  status = draw_colr_glyph (colr_render,
+                            glyph,
+                            FT_COLOR_INCLUDE_ROOT_TRANSFORM,
+                            cr);
+
+  pattern = cairo_pop_group (cr);
+
+  if (unlikely (status))
+    goto cleanup;
+
+  /* Flip the result */
+  cairo_matrix_init_scale (&matrix, 1, -1);
+  cairo_matrix_translate (&matrix, 0, - (ymax - ymin) -  2 * ymin);
+  cairo_pattern_set_matrix (pattern, &matrix);
+  cairo_set_source (cr, pattern);
+
+  cairo_paint (cr);
+
+  /* Adjust the device offset to keep the glyphs reference
+   * point at the origin
+   */
+  cairo_surface_set_device_offset (surface, - xmin,  ymax);
+
+  *out_surface = (cairo_image_surface_t *) surface;
+  surface = NULL;
+
+cleanup:
+
+  if (cr)
+    cairo_destroy (cr);
+  if (surface)
+    cairo_surface_destroy (surface);
+  if (pattern)
+    cairo_pattern_destroy (pattern);
+  if (colr_render)
+    free (colr_render);
+
+  return status;
+}
+
+/* }}} */
+
+#endif
+
+/* vim:set foldmethod=marker expandtab: */


More information about the cairo-commit mailing list