[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, ¢er, R, start_angle, end_angle, pattern);
+ add_sweep_gradient_patches (cl, extend, ¢er, 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, ¢er, 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