mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-16 14:18:07 +02:00
1256 lines
36 KiB
C
1256 lines
36 KiB
C
/* -*- 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
|
|
* 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@redhat.com>
|
|
*/
|
|
|
|
#include "cairoint.h"
|
|
#include "cairo-array-private.h"
|
|
#include "cairo-ft-private.h"
|
|
#include "cairo-path-private.h"
|
|
#include "cairo-pattern-private.h"
|
|
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#ifdef _MSC_VER
|
|
#include <malloc.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_ALLOCA_H
|
|
#include <alloca.h>
|
|
#endif
|
|
|
|
#if HAVE_FT_COLR_V1
|
|
|
|
#include <ft2build.h>
|
|
#include FT_CONFIG_OPTIONS_H
|
|
#include FT_COLOR_H
|
|
#include FT_GLYPH_H
|
|
#include FT_OUTLINE_H
|
|
#include FT_SIZES_H
|
|
|
|
/* #define DEBUG_COLR 1 */
|
|
|
|
typedef struct _cairo_colr_glyph_render {
|
|
FT_Face face;
|
|
FT_Color *palette;
|
|
unsigned int num_palette_entries;
|
|
cairo_pattern_t *foreground_marker;
|
|
cairo_pattern_t *foreground_source;
|
|
cairo_bool_t foreground_source_used;
|
|
int level;
|
|
} cairo_colr_glyph_render_t;
|
|
|
|
static cairo_status_t
|
|
draw_paint (cairo_colr_glyph_render_t *render,
|
|
FT_OpaquePaint *paint,
|
|
cairo_t *cr);
|
|
|
|
|
|
static inline double
|
|
double_from_16_16 (FT_Fixed f)
|
|
{
|
|
return f / (double) (1 << 16);
|
|
}
|
|
|
|
static inline double
|
|
double_from_26_6 (FT_F26Dot6 f)
|
|
{
|
|
return f / (double) (1 << 6);
|
|
}
|
|
|
|
static inline double
|
|
double_from_2_14 (FT_F2Dot14 f)
|
|
{
|
|
return f / (double) (1 << 14);
|
|
}
|
|
|
|
static inline double
|
|
interpolate (double f0, double f1, double f)
|
|
{
|
|
return f0 + f * (f1 - f0);
|
|
}
|
|
|
|
static inline void
|
|
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,
|
|
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);
|
|
}
|
|
|
|
static inline double
|
|
dot (cairo_point_double_t p, cairo_point_double_t q)
|
|
{
|
|
return p.x * q.x + p.y * q.y;
|
|
}
|
|
|
|
static inline cairo_point_double_t
|
|
normalize (cairo_point_double_t p)
|
|
{
|
|
double len = sqrt (dot (p, p));
|
|
|
|
return (cairo_point_double_t) { p.x / len, p.y / len };
|
|
}
|
|
|
|
static inline cairo_point_double_t
|
|
sum (cairo_point_double_t p, cairo_point_double_t q)
|
|
{
|
|
return (cairo_point_double_t) { p.x + q.x, p.y + q.y };
|
|
}
|
|
|
|
static inline cairo_point_double_t
|
|
difference (cairo_point_double_t p, cairo_point_double_t q)
|
|
{
|
|
return (cairo_point_double_t) { p.x - q.x, p.y - q.y };
|
|
}
|
|
|
|
static inline cairo_point_double_t
|
|
scale (cairo_point_double_t p, double f)
|
|
{
|
|
return (cairo_point_double_t) { p.x * f, p.y * f };
|
|
}
|
|
|
|
static cairo_operator_t
|
|
cairo_operator_from_ft_composite_mode (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_NOT_REACHED;
|
|
}
|
|
}
|
|
|
|
static cairo_extend_t
|
|
cairo_extend_from_ft_paint_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_NOT_REACHED;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
#if DEBUG_COLR
|
|
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;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
get_palette_color (cairo_colr_glyph_render_t *render,
|
|
FT_ColorIndex *ci,
|
|
cairo_color_t *color,
|
|
double *colr_alpha,
|
|
cairo_bool_t *is_foreground_color)
|
|
{
|
|
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;
|
|
color->alpha = 1;
|
|
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 = c.alpha / 255.0;
|
|
}
|
|
|
|
*colr_alpha = double_from_2_14 (ci->alpha);
|
|
*is_foreground_color = foreground;
|
|
}
|
|
|
|
static cairo_status_t
|
|
draw_paint_solid (cairo_colr_glyph_render_t *render,
|
|
FT_PaintSolid *solid,
|
|
cairo_t *cr)
|
|
{
|
|
cairo_color_t color;
|
|
double colr_alpha;
|
|
cairo_bool_t is_foreground_color;
|
|
|
|
#if DEBUG_COLR
|
|
printf ("%*sDraw PaintSolid\n", 2 * render->level, "");
|
|
#endif
|
|
|
|
get_palette_color (render, &solid->color, &color, &colr_alpha, &is_foreground_color);
|
|
if (is_foreground_color) {
|
|
cairo_set_source (cr, render->foreground_marker);
|
|
cairo_paint_with_alpha (cr, colr_alpha);
|
|
} else {
|
|
cairo_set_source_rgba (cr, color.red, color.green, color.blue, color.alpha * colr_alpha);
|
|
cairo_paint (cr);
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
typedef struct _cairo_colr_color_stop {
|
|
cairo_color_t color;
|
|
double position;
|
|
} cairo_colr_color_stop_t;
|
|
|
|
typedef struct _cairo_colr_color_line {
|
|
int n_stops;
|
|
cairo_colr_color_stop_t *stops;
|
|
} cairo_colr_color_line_t;
|
|
|
|
static void
|
|
free_colorline (cairo_colr_color_line_t *cl)
|
|
{
|
|
free (cl->stops);
|
|
free (cl);
|
|
}
|
|
|
|
static int
|
|
_compare_stops (const void *p1, const void *p2)
|
|
{
|
|
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 cairo_colr_color_line_t *
|
|
read_colorline (cairo_colr_glyph_render_t *render,
|
|
FT_ColorLine *colorline)
|
|
{
|
|
cairo_colr_color_line_t *cl;
|
|
FT_ColorStop stop;
|
|
int i;
|
|
double colr_alpha;
|
|
cairo_bool_t is_foreground_color;
|
|
|
|
cl = _cairo_calloc (sizeof (cairo_colr_color_line_t));
|
|
if (unlikely (cl == NULL))
|
|
return NULL;
|
|
|
|
cl->n_stops = colorline->color_stop_iterator.num_color_stops;
|
|
cl->stops = _cairo_calloc_ab (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 = double_from_16_16 (stop.stop_offset);
|
|
get_palette_color (render, &stop.color, &cl->stops[i].color, &colr_alpha, &is_foreground_color);
|
|
if (is_foreground_color) {
|
|
double red, green, blue, alpha;
|
|
if (cairo_pattern_get_rgba (render->foreground_source,
|
|
&red, &green, &blue, &alpha) == CAIRO_STATUS_SUCCESS)
|
|
{
|
|
cl->stops[i].color.red = red;
|
|
cl->stops[i].color.green = green;
|
|
cl->stops[i].color.blue = blue;
|
|
cl->stops[i].color.alpha = alpha * colr_alpha;
|
|
render->foreground_source_used = TRUE;
|
|
}
|
|
else
|
|
{
|
|
cl->stops[i].color.red = 0;
|
|
cl->stops[i].color.green = 0;
|
|
cl->stops[i].color.blue = 0;
|
|
cl->stops[i].color.alpha = colr_alpha;
|
|
}
|
|
} else {
|
|
cl->stops[i].color.alpha *= colr_alpha;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
qsort (cl->stops, cl->n_stops, sizeof (cairo_colr_color_stop_t), _compare_stops);
|
|
|
|
return cl;
|
|
}
|
|
|
|
static void
|
|
reduce_anchors (FT_PaintLinearGradient *gradient,
|
|
cairo_point_double_t *pp0,
|
|
cairo_point_double_t *pp1)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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 (cairo_colr_color_line_t *cl,
|
|
double *out_min,
|
|
double *out_max)
|
|
{
|
|
double min, max;
|
|
|
|
*out_min = 0.;
|
|
*out_max = 1.;
|
|
|
|
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++) {
|
|
cairo_colr_color_stop_t *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)
|
|
{
|
|
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, "");
|
|
#endif
|
|
|
|
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_from_ft_paint_extend (gradient->colorline.extend));
|
|
|
|
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_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)
|
|
{
|
|
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, "");
|
|
#endif
|
|
|
|
cl = read_colorline (render, &gradient->colorline);
|
|
if (unlikely (cl == NULL))
|
|
return CAIRO_STATUS_NO_MEMORY;
|
|
|
|
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 = 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);
|
|
|
|
pattern = cairo_pattern_create_radial (start1.x, start1.y, start_radius1,
|
|
end1.x, end1.y, end_radius1);
|
|
|
|
cairo_pattern_set_extend (pattern, cairo_extend_from_ft_paint_extend (gradient->colorline.extend));
|
|
|
|
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_pattern_destroy (pattern);
|
|
|
|
free_colorline (cl);
|
|
|
|
return status;
|
|
}
|
|
|
|
typedef struct {
|
|
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,
|
|
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);
|
|
}
|
|
|
|
#define MAX_ANGLE (M_PI / 8.)
|
|
|
|
static void
|
|
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;
|
|
cairo_point_double_t p0;
|
|
cairo_color_t color0, color1;
|
|
|
|
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++) {
|
|
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 (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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
assert (start_angle != end_angle);
|
|
|
|
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);
|
|
|
|
/* 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)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
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 {
|
|
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;
|
|
}
|
|
} 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)
|
|
{
|
|
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++) {
|
|
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;
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static cairo_status_t
|
|
draw_paint_sweep_gradient (cairo_colr_glyph_render_t *render,
|
|
FT_PaintSweepGradient *gradient,
|
|
cairo_t *cr)
|
|
{
|
|
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, "");
|
|
#endif
|
|
|
|
cl = read_colorline (render, &gradient->colorline);
|
|
if (unlikely (cl == NULL))
|
|
return CAIRO_STATUS_NO_MEMORY;
|
|
|
|
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 ();
|
|
|
|
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_from_ft_paint_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_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, "");
|
|
#endif
|
|
|
|
error = FT_Load_Glyph (render->face, glyph->glyphID, FT_LOAD_DEFAULT);
|
|
status = _cairo_ft_to_cairo_error (error);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = _cairo_ft_face_decompose_glyph_outline (render->face, &path_fixed);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
cairo_save (cr);
|
|
cairo_identity_matrix (cr);
|
|
path = _cairo_path_create (path_fixed, cr);
|
|
_cairo_path_fixed_destroy (path_fixed);
|
|
cairo_restore (cr);
|
|
|
|
cairo_save (cr);
|
|
|
|
cairo_new_path (cr);
|
|
cairo_append_path (cr, path);
|
|
cairo_path_destroy (path);
|
|
cairo_clip (cr);
|
|
|
|
status = draw_paint (render, &glyph->paint, cr);
|
|
|
|
cairo_restore (cr);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_status_t draw_colr_glyph (cairo_colr_glyph_render_t *render,
|
|
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)
|
|
{
|
|
#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);
|
|
}
|
|
|
|
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;
|
|
|
|
#if DEBUG_COLR
|
|
printf ("%*sDraw PaintTransform\n", 2 * render->level, "");
|
|
#endif
|
|
|
|
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_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;
|
|
|
|
#if DEBUG_COLR
|
|
printf ("%*sDraw PaintTranslate\n", 2 * render->level, "");
|
|
#endif
|
|
|
|
cairo_save (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);
|
|
|
|
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;
|
|
|
|
#if DEBUG_COLR
|
|
printf ("%*sDraw PaintRotate\n", 2 * render->level, "");
|
|
#endif
|
|
|
|
cairo_save (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);
|
|
|
|
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;
|
|
|
|
#if DEBUG_COLR
|
|
printf ("%*sDraw PaintScale\n", 2 * render->level, "");
|
|
#endif
|
|
|
|
cairo_save (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);
|
|
|
|
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;
|
|
|
|
#if DEBUG_COLR
|
|
printf ("%*sDraw PaintSkew\n", 2 * render->level, "");
|
|
#endif
|
|
|
|
cairo_save (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);
|
|
|
|
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;
|
|
|
|
|
|
#if DEBUG_COLR
|
|
printf ("%*sDraw PaintComposite\n", 2 * render->level, "");
|
|
#endif
|
|
|
|
cairo_save (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);
|
|
|
|
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_NOT_REACHED;
|
|
}
|
|
|
|
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 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;
|
|
|
|
cairo_save (cr);
|
|
|
|
if (FT_Get_Color_Glyph_ClipBox (render->face, glyph, &box)) {
|
|
double xmin, ymin, xmax, ymax;
|
|
|
|
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);
|
|
}
|
|
|
|
if (FT_Get_Color_Glyph_Paint (render->face, glyph, root, &paint))
|
|
status = draw_paint (render, &paint, cr);
|
|
|
|
cairo_restore (cr);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* 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_Color *palette,
|
|
int num_palette_entries,
|
|
cairo_t *cr,
|
|
cairo_pattern_t *foreground_source,
|
|
cairo_bool_t *foreground_source_used)
|
|
{
|
|
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
|
cairo_colr_glyph_render_t colr_render;
|
|
|
|
#if DEBUG_COLR
|
|
printf ("_cairo_render_colr_glyph glyph index: %ld\n", glyph);
|
|
#endif
|
|
|
|
colr_render.face = face;
|
|
colr_render.palette = palette;
|
|
colr_render.num_palette_entries = num_palette_entries;
|
|
colr_render.foreground_marker = _cairo_pattern_create_foreground_marker ();
|
|
colr_render.foreground_source = cairo_pattern_reference (foreground_source);;
|
|
colr_render.foreground_source_used = FALSE;
|
|
colr_render.level = 0;
|
|
|
|
status = draw_colr_glyph (&colr_render,
|
|
glyph,
|
|
FT_COLOR_INCLUDE_ROOT_TRANSFORM,
|
|
cr);
|
|
|
|
cairo_pattern_destroy (colr_render.foreground_marker);
|
|
cairo_pattern_destroy (colr_render.foreground_source);
|
|
*foreground_source_used = colr_render.foreground_source_used;
|
|
|
|
return status;
|
|
}
|
|
|
|
#endif /* HAVE_FT_COLR_V1 */
|