1
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2
/* cairo - a vector graphics library with display and print output
3
 *
4
 * Copyright © 2022 Matthias Clasen
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it either under the terms of the GNU Lesser General Public
8
 * License version 2.1 as published by the Free Software Foundation
9
 * (the "LGPL") or, at your option, under the terms of the Mozilla
10
 * Public License Version 1.1 (the "MPL"). If you do not alter this
11
 * notice, a recipient may use your version of this file under either
12
 * the MPL or the LGPL.
13
 *
14
 * You should have received a copy of the LGPL along with this library
15
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17
 * You should have received a copy of the MPL along with this library
18
 * in the file COPYING-MPL-1.1
19
 *
20
 * The contents of this file are subject to the Mozilla Public License
21
 * Version 1.1 (the "License"); you may not use this file except in
22
 * compliance with the License. You may obtain a copy of the License at
23
 * http://www.mozilla.org/MPL/
24
 *
25
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27
 * the specific language governing rights and limitations.
28
 *
29
 * The Original Code is the cairo graphics library.
30
 *
31
 * Contributor(s):
32
 *      Matthias Clasen <mclasen@redhat.com>
33
 */
34

            
35
#include "cairoint.h"
36
#include "cairo-array-private.h"
37
#include "cairo-ft-private.h"
38
#include "cairo-path-private.h"
39
#include "cairo-pattern-private.h"
40

            
41
#include <assert.h>
42
#include <math.h>
43
#include <stdio.h>
44
#include <string.h>
45

            
46
#ifdef _MSC_VER
47
#include <malloc.h>
48
#endif
49

            
50
#ifdef HAVE_ALLOCA_H
51
#include <alloca.h>
52
#endif
53

            
54
#if HAVE_FT_COLR_V1
55

            
56
#include <ft2build.h>
57
#include FT_CONFIG_OPTIONS_H
58
#include FT_COLOR_H
59
#include FT_GLYPH_H
60
#include FT_OUTLINE_H
61
#include FT_SIZES_H
62

            
63
/* #define DEBUG_COLR 1 */
64

            
65
typedef struct _cairo_colr_glyph_render {
66
    FT_Face face;
67
    FT_Color *palette;
68
    unsigned int num_palette_entries;
69
    cairo_pattern_t *foreground_marker;
70
    cairo_pattern_t *foreground_source;
71
    cairo_bool_t foreground_source_used;
72
    int level;
73
} cairo_colr_glyph_render_t;
74

            
75
static cairo_status_t
76
draw_paint (cairo_colr_glyph_render_t *render,
77
	    FT_OpaquePaint            *paint,
78
	    cairo_t                   *cr);
79

            
80

            
81
static inline double
82
double_from_16_16 (FT_Fixed f)
83
{
84
    return f / (double) (1 << 16);
85
}
86

            
87
static inline double
88
double_from_26_6 (FT_F26Dot6 f)
89
{
90
    return f / (double) (1 << 6);
91
}
92

            
93
static inline double
94
double_from_2_14 (FT_F2Dot14 f)
95
{
96
    return f / (double) (1 << 14);
97
}
98

            
99
static inline double
100
interpolate (double f0, double f1, double f)
101
{
102
    return f0 + f * (f1 - f0);
103
}
104

            
105
static inline void
106
interpolate_points (cairo_point_double_t *p0,
107
		    cairo_point_double_t *p1,
108
		    double                f,
109
		    cairo_point_double_t *out)
110
{
111
  out->x = interpolate (p0->x, p1->x, f);
112
  out->y = interpolate (p0->y, p1->y, f);
113
}
114

            
115
static inline void
116
interpolate_colors (cairo_color_t *c0,
117
		    cairo_color_t *c1,
118
		    double         f,
119
		    cairo_color_t *out)
120
{
121
    out->red = interpolate (c0->red, c1->red, f);
122
    out->green = interpolate (c0->green, c1->green, f);
123
    out->blue = interpolate (c0->blue, c1->blue, f);
124
    out->alpha = interpolate (c0->alpha, c1->alpha, f);
125
}
126

            
127
static inline double
128
dot (cairo_point_double_t p, cairo_point_double_t q)
129
{
130
    return p.x * q.x + p.y * q.y;
131
}
132

            
133
static inline cairo_point_double_t
134
normalize (cairo_point_double_t p)
135
{
136
    double len = sqrt (dot (p, p));
137

            
138
    return (cairo_point_double_t) { p.x / len, p.y / len };
139
}
140

            
141
static inline cairo_point_double_t
142
sum (cairo_point_double_t p, cairo_point_double_t q)
143
{
144
    return (cairo_point_double_t) { p.x + q.x, p.y + q.y };
145
}
146

            
147
static inline cairo_point_double_t
148
difference (cairo_point_double_t p, cairo_point_double_t q)
149
{
150
    return (cairo_point_double_t) { p.x - q.x, p.y - q.y };
151
}
152

            
153
static inline cairo_point_double_t
154
scale (cairo_point_double_t p, double f)
155
{
156
    return (cairo_point_double_t) { p.x * f, p.y * f };
157
}
158

            
159
static cairo_operator_t
160
cairo_operator_from_ft_composite_mode (FT_Composite_Mode mode)
161
{
162
    switch (mode)
163
    {
164
	case FT_COLR_COMPOSITE_CLEAR: return CAIRO_OPERATOR_CLEAR;
165
	case FT_COLR_COMPOSITE_SRC: return CAIRO_OPERATOR_SOURCE;
166
	case FT_COLR_COMPOSITE_DEST: return CAIRO_OPERATOR_DEST;
167
	case FT_COLR_COMPOSITE_SRC_OVER: return CAIRO_OPERATOR_OVER;
168
	case FT_COLR_COMPOSITE_DEST_OVER: return CAIRO_OPERATOR_DEST_OVER;
169
	case FT_COLR_COMPOSITE_SRC_IN: return CAIRO_OPERATOR_IN;
170
	case FT_COLR_COMPOSITE_DEST_IN: return CAIRO_OPERATOR_DEST_IN;
171
	case FT_COLR_COMPOSITE_SRC_OUT: return CAIRO_OPERATOR_OUT;
172
	case FT_COLR_COMPOSITE_DEST_OUT: return CAIRO_OPERATOR_DEST_OUT;
173
	case FT_COLR_COMPOSITE_SRC_ATOP: return CAIRO_OPERATOR_ATOP;
174
	case FT_COLR_COMPOSITE_DEST_ATOP: return CAIRO_OPERATOR_DEST_ATOP;
175
	case FT_COLR_COMPOSITE_XOR: return CAIRO_OPERATOR_XOR;
176
	case FT_COLR_COMPOSITE_PLUS: return CAIRO_OPERATOR_ADD;
177
	case FT_COLR_COMPOSITE_SCREEN: return CAIRO_OPERATOR_SCREEN;
178
	case FT_COLR_COMPOSITE_OVERLAY: return CAIRO_OPERATOR_OVERLAY;
179
	case FT_COLR_COMPOSITE_DARKEN: return CAIRO_OPERATOR_DARKEN;
180
	case FT_COLR_COMPOSITE_LIGHTEN: return CAIRO_OPERATOR_LIGHTEN;
181
	case FT_COLR_COMPOSITE_COLOR_DODGE: return CAIRO_OPERATOR_COLOR_DODGE;
182
	case FT_COLR_COMPOSITE_COLOR_BURN: return CAIRO_OPERATOR_COLOR_BURN;
183
	case FT_COLR_COMPOSITE_HARD_LIGHT: return CAIRO_OPERATOR_HARD_LIGHT;
184
	case FT_COLR_COMPOSITE_SOFT_LIGHT: return CAIRO_OPERATOR_SOFT_LIGHT;
185
	case FT_COLR_COMPOSITE_DIFFERENCE: return CAIRO_OPERATOR_DIFFERENCE;
186
	case FT_COLR_COMPOSITE_EXCLUSION: return CAIRO_OPERATOR_EXCLUSION;
187
	case FT_COLR_COMPOSITE_MULTIPLY: return CAIRO_OPERATOR_MULTIPLY;
188
	case FT_COLR_COMPOSITE_HSL_HUE: return CAIRO_OPERATOR_HSL_HUE;
189
	case FT_COLR_COMPOSITE_HSL_SATURATION: return CAIRO_OPERATOR_HSL_SATURATION;
190
	case FT_COLR_COMPOSITE_HSL_COLOR: return CAIRO_OPERATOR_HSL_COLOR;
191
	case FT_COLR_COMPOSITE_HSL_LUMINOSITY: return CAIRO_OPERATOR_HSL_LUMINOSITY;
192
	case FT_COLR_COMPOSITE_MAX:
193
	default:
194
	    ASSERT_NOT_REACHED;
195
    }
196
}
197

            
198
static cairo_extend_t
199
cairo_extend_from_ft_paint_extend (FT_PaintExtend extend)
200
{
201
    switch (extend)
202
    {
203
	case FT_COLR_PAINT_EXTEND_PAD: return CAIRO_EXTEND_PAD;
204
	case FT_COLR_PAINT_EXTEND_REPEAT: return CAIRO_EXTEND_REPEAT;
205
	case FT_COLR_PAINT_EXTEND_REFLECT: return CAIRO_EXTEND_REFLECT;
206
	default:
207
	    ASSERT_NOT_REACHED;
208
    }
209
}
210

            
211
static cairo_status_t
212
draw_paint_colr_layers (cairo_colr_glyph_render_t *render,
213
                        FT_PaintColrLayers        *colr_layers,
214
                        cairo_t                   *cr)
215
{
216
    FT_OpaquePaint paint;
217
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
218

            
219
#if DEBUG_COLR
220
    printf ("%*sDraw PaintColrLayers\n", 2 * render->level, "");
221
#endif
222

            
223
    while (FT_Get_Paint_Layers (render->face, &colr_layers->layer_iterator, &paint)) {
224
	cairo_push_group (cr);
225
	status = draw_paint (render, &paint, cr);
226
	cairo_pop_group_to_source (cr);
227
	cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
228
	cairo_paint (cr);
229

            
230
	if (unlikely (status))
231
	    break;
232
    }
233

            
234
    return status;
235
}
236

            
237
static void
238
get_palette_color (cairo_colr_glyph_render_t *render,
239
		   FT_ColorIndex             *ci,
240
		   cairo_color_t             *color,
241
		   double                    *colr_alpha,
242
		   cairo_bool_t              *is_foreground_color)
243
{
244
    cairo_bool_t foreground = FALSE;
245

            
246
    if (ci->palette_index == 0xffff || ci->palette_index >= render->num_palette_entries) {
247
	color->red = 0;
248
	color->green = 0;
249
	color->blue = 0;
250
	color->alpha = 1;
251
	foreground = TRUE;
252
    } else {
253
	FT_Color c = render->palette[ci->palette_index];
254
	color->red = c.red / 255.0;
255
	color->green = c.green / 255.0;
256
	color->blue = c.blue / 255.0;
257
	color->alpha = c.alpha / 255.0;
258
    }
259

            
260
    *colr_alpha = double_from_2_14 (ci->alpha);
261
    *is_foreground_color = foreground;
262
}
263

            
264
static cairo_status_t
265
draw_paint_solid (cairo_colr_glyph_render_t *render,
266
                  FT_PaintSolid             *solid,
267
                  cairo_t                   *cr)
268
{
269
    cairo_color_t color;
270
    double colr_alpha;
271
    cairo_bool_t is_foreground_color;
272

            
273
#if DEBUG_COLR
274
    printf ("%*sDraw PaintSolid\n", 2 * render->level, "");
275
#endif
276

            
277
    get_palette_color (render, &solid->color, &color, &colr_alpha, &is_foreground_color);
278
    if (is_foreground_color) {
279
	cairo_set_source (cr, render->foreground_marker);
280
	cairo_paint_with_alpha (cr, colr_alpha);
281
    } else {
282
	cairo_set_source_rgba (cr, color.red, color.green, color.blue, color.alpha * colr_alpha);
283
	cairo_paint (cr);
284
    }
285

            
286
    return CAIRO_STATUS_SUCCESS;
287
}
288

            
289
typedef struct _cairo_colr_color_stop {
290
    cairo_color_t color;
291
    double position;
292
} cairo_colr_color_stop_t;
293

            
294
typedef struct _cairo_colr_color_line {
295
    int n_stops;
296
    cairo_colr_color_stop_t *stops;
297
} cairo_colr_color_line_t;
298

            
299
static void
300
free_colorline (cairo_colr_color_line_t *cl)
301
{
302
    free (cl->stops);
303
    free (cl);
304
}
305

            
306
static int
307
_compare_stops (const void *p1, const void *p2)
308
{
309
    const cairo_colr_color_stop_t *c1 = p1;
310
    const cairo_colr_color_stop_t *c2 = p2;
311

            
312
    if (c1->position < c2->position)
313
	return -1;
314
    else if (c1->position > c2->position)
315
	return 1;
316
    else
317
	return 0;
318
}
319

            
320
static cairo_colr_color_line_t *
321
read_colorline (cairo_colr_glyph_render_t *render,
322
                FT_ColorLine              *colorline)
323
{
324
    cairo_colr_color_line_t *cl;
325
    FT_ColorStop stop;
326
    int i;
327
    double colr_alpha;
328
    cairo_bool_t is_foreground_color;
329

            
330
    cl = _cairo_calloc (sizeof (cairo_colr_color_line_t));
331
    if (unlikely (cl == NULL))
332
	return NULL;
333

            
334
    cl->n_stops = colorline->color_stop_iterator.num_color_stops;
335
    cl->stops = _cairo_calloc_ab (cl->n_stops, sizeof (cairo_colr_color_stop_t));
336
    if (unlikely (cl->stops == NULL)) {
337
	free (cl);
338
	return NULL;
339
    }
340

            
341
    i = 0;
342
    while (FT_Get_Colorline_Stops (render->face, &stop, &colorline->color_stop_iterator)) {
343
	cl->stops[i].position = double_from_16_16 (stop.stop_offset);
344
	get_palette_color (render, &stop.color, &cl->stops[i].color, &colr_alpha, &is_foreground_color);
345
	if (is_foreground_color) {
346
	    double red, green, blue, alpha;
347
	    if (cairo_pattern_get_rgba (render->foreground_source,
348
					&red, &green, &blue, &alpha) == CAIRO_STATUS_SUCCESS)
349
	    {
350
		cl->stops[i].color.red = red;
351
		cl->stops[i].color.green = green;
352
		cl->stops[i].color.blue = blue;
353
		cl->stops[i].color.alpha = alpha * colr_alpha;
354
		render->foreground_source_used = TRUE;
355
	    }
356
	    else
357
	    {
358
		cl->stops[i].color.red = 0;
359
		cl->stops[i].color.green = 0;
360
		cl->stops[i].color.blue = 0;
361
		cl->stops[i].color.alpha = colr_alpha;
362
	    }
363
	} else {
364
	    cl->stops[i].color.alpha *= colr_alpha;
365
	}
366
	i++;
367
    }
368

            
369
    qsort (cl->stops, cl->n_stops, sizeof (cairo_colr_color_stop_t), _compare_stops);
370

            
371
    return cl;
372
}
373

            
374
static void
375
reduce_anchors (FT_PaintLinearGradient *gradient,
376
                cairo_point_double_t   *pp0,
377
                cairo_point_double_t   *pp1)
378
{
379
    cairo_point_double_t p0, p1, p2;
380
    cairo_point_double_t q1, q2;
381
    double s;
382
    double k;
383

            
384
    p0.x = double_from_16_16 (gradient->p0.x);
385
    p0.y = double_from_16_16 (gradient->p0.y);
386
    p1.x = double_from_16_16 (gradient->p1.x);
387
    p1.y = double_from_16_16 (gradient->p1.y);
388
    p2.x = double_from_16_16 (gradient->p2.x);
389
    p2.y = double_from_16_16 (gradient->p2.y);
390

            
391
    q2.x = p2.x - p0.x;
392
    q2.y = p2.y - p0.y;
393
    q1.x = p1.x - p0.x;
394
    q1.y = p1.y - p0.y;
395

            
396
    s = q2.x * q2.x + q2.y * q2.y;
397
    if (s < 0.000001)
398
    {
399
	pp0->x = p0.x; pp0->y = p0.y;
400
	pp1->x = p1.x; pp1->y = p1.y;
401
	return;
402
    }
403

            
404
    k = (q2.x * q1.x + q2.y * q1.y) / s;
405
    pp0->x = p0.x;
406
    pp0->y = p0.y;
407
    pp1->x = p1.x - k * q2.x;
408
    pp1->y = p1.y - k * q2.y;
409
}
410

            
411
static void
412
normalize_colorline (cairo_colr_color_line_t *cl,
413
                     double                  *out_min,
414
                     double                  *out_max)
415
{
416
    double min, max;
417

            
418
    *out_min = 0.;
419
    *out_max = 1.;
420

            
421
    min = max = cl->stops[0].position;
422
    for (int i = 0; i < cl->n_stops; i++) {
423
	cairo_colr_color_stop_t *stop = &cl->stops[i];
424
	min = MIN (min, stop->position);
425
	max = MAX (max, stop->position);
426
    }
427

            
428
    if (min != max) {
429
	for (int i = 0; i < cl->n_stops; i++) {
430
	    cairo_colr_color_stop_t *stop = &cl->stops[i];
431
	    stop->position = (stop->position - min) / (max - min);
432
        }
433
	*out_min = min;
434
	*out_max = max;
435
    }
436
}
437

            
438
static cairo_status_t
439
draw_paint_linear_gradient (cairo_colr_glyph_render_t *render,
440
                            FT_PaintLinearGradient    *gradient,
441
                            cairo_t                   *cr)
442
{
443
    cairo_colr_color_line_t *cl;
444
    cairo_point_double_t p0, p1;
445
    cairo_point_double_t pp0, pp1;
446
    cairo_pattern_t *pattern;
447
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
448
    double min, max;
449

            
450
#if DEBUG_COLR
451
    printf ("%*sDraw PaintLinearGradient\n", 2 * render->level, "");
452
#endif
453

            
454
    cl = read_colorline (render, &gradient->colorline);
455
    if (unlikely (cl == NULL))
456
	return CAIRO_STATUS_NO_MEMORY;
457

            
458
    /* cairo only allows stop positions between 0 and 1 */
459
    normalize_colorline (cl, &min, &max);
460
    reduce_anchors (gradient, &p0, &p1);
461
    interpolate_points (&p0, &p1, min, &pp0);
462
    interpolate_points (&p0, &p1, max, &pp1);
463

            
464
    pattern = cairo_pattern_create_linear (pp0.x, pp0.y, pp1.x, pp1.y);
465

            
466
    cairo_pattern_set_extend (pattern, cairo_extend_from_ft_paint_extend (gradient->colorline.extend));
467

            
468
    for (int i = 0; i < cl->n_stops; i++) {
469
	cairo_colr_color_stop_t *stop = &cl->stops[i];
470
	cairo_pattern_add_color_stop_rgba (pattern, stop->position,
471
					   stop->color.red, stop->color.green, stop->color.blue, stop->color.alpha);
472
    }
473

            
474
    cairo_set_source (cr, pattern);
475
    cairo_paint (cr);
476

            
477
    cairo_pattern_destroy (pattern);
478

            
479
    free_colorline (cl);
480

            
481
    return status;
482
}
483

            
484
static cairo_status_t
485
draw_paint_radial_gradient (cairo_colr_glyph_render_t *render,
486
                            FT_PaintRadialGradient *gradient,
487
                            cairo_t *cr)
488
{
489
    cairo_colr_color_line_t *cl;
490
    cairo_point_double_t start, end;
491
    cairo_point_double_t start1, end1;
492
    double start_radius, end_radius;
493
    double start_radius1, end_radius1;
494
    double min, max;
495
    cairo_pattern_t *pattern;
496
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
497

            
498
#if DEBUG_COLR
499
    printf ("%*sDraw PaintRadialGradient\n", 2 * render->level, "");
500
#endif
501

            
502
    cl = read_colorline (render, &gradient->colorline);
503
    if (unlikely (cl == NULL))
504
	return CAIRO_STATUS_NO_MEMORY;
505

            
506
    start.x = double_from_16_16 (gradient->c0.x);
507
    start.y = double_from_16_16 (gradient->c0.y);
508
    end.x = double_from_16_16 (gradient->c1.x);
509
    end.y = double_from_16_16 (gradient->c1.y);
510

            
511
    start_radius = double_from_16_16 (gradient->r0);
512
    end_radius = double_from_16_16 (gradient->r1);
513

            
514
    /* cairo only allows stop positions between 0 and 1 */
515
    normalize_colorline (cl, &min, &max);
516
    interpolate_points (&start, &end, min, &start1);
517
    interpolate_points (&start, &end, max, &end1);
518
    start_radius1 = interpolate (start_radius, end_radius, min);
519
    end_radius1 = interpolate (start_radius, end_radius, max);
520

            
521
    pattern = cairo_pattern_create_radial (start1.x, start1.y, start_radius1,
522
					   end1.x, end1.y, end_radius1);
523

            
524
    cairo_pattern_set_extend (pattern, cairo_extend_from_ft_paint_extend (gradient->colorline.extend));
525

            
526
    for (int i = 0; i < cl->n_stops; i++) {
527
	cairo_colr_color_stop_t *stop = &cl->stops[i];
528
	cairo_pattern_add_color_stop_rgba (pattern, stop->position,
529
					   stop->color.red, stop->color.green, stop->color.blue, stop->color.alpha);
530
    }
531

            
532
    cairo_set_source (cr, pattern);
533
    cairo_paint (cr);
534

            
535
    cairo_pattern_destroy (pattern);
536

            
537
    free_colorline (cl);
538

            
539
    return status;
540
}
541

            
542
typedef struct {
543
    cairo_point_double_t center, p0, c0, c1, p1;
544
    cairo_color_t color0, color1;
545
} cairo_colr_gradient_patch_t;
546

            
547
static void
548
add_patch (cairo_pattern_t             *pattern,
549
	   cairo_point_double_t        *center,
550
	   cairo_colr_gradient_patch_t *p)
551
{
552
    cairo_mesh_pattern_begin_patch (pattern);
553
    cairo_mesh_pattern_move_to (pattern, center->x, center->y);
554
    cairo_mesh_pattern_line_to (pattern, p->p0.x, p->p0.y);
555
    cairo_mesh_pattern_curve_to (pattern,
556
				 p->c0.x, p->c0.y,
557
				 p->c1.x, p->c1.y,
558
				 p->p1.x, p->p1.y);
559
    cairo_mesh_pattern_line_to (pattern, center->x, center->y);
560
    cairo_mesh_pattern_set_corner_color_rgba (pattern, 0,
561
					      p->color0.red,
562
					      p->color0.green,
563
					      p->color0.blue,
564
					      p->color0.alpha);
565
    cairo_mesh_pattern_set_corner_color_rgba (pattern, 1,
566
					      p->color0.red,
567
					      p->color0.green,
568
					      p->color0.blue,
569
					      p->color0.alpha);
570
    cairo_mesh_pattern_set_corner_color_rgba (pattern, 2,
571
					      p->color1.red,
572
					      p->color1.green,
573
					      p->color1.blue,
574
					      p->color1.alpha);
575
    cairo_mesh_pattern_set_corner_color_rgba (pattern, 3,
576
					      p->color1.red,
577
					      p->color1.green,
578
					      p->color1.blue,
579
					      p->color1.alpha);
580
    cairo_mesh_pattern_end_patch (pattern);
581
}
582

            
583
#define MAX_ANGLE (M_PI / 8.)
584

            
585
static void
586
add_sweep_gradient_patches1 (cairo_point_double_t *center,
587
			     double                radius,
588
                             double                a0,
589
			     cairo_color_t        *c0,
590
                             double                a1,
591
			     cairo_color_t        *c1,
592
                             cairo_pattern_t      *pattern)
593
{
594

            
595
    int num_splits;
596
    cairo_point_double_t p0;
597
    cairo_color_t color0, color1;
598

            
599
    num_splits = ceilf (fabs (a1 - a0) / MAX_ANGLE);
600
    p0 = (cairo_point_double_t) { cosf (a0), sinf (a0) };
601
    color0 = *c0;
602

            
603
    for (int a = 0; a < num_splits; a++) {
604
	double k = (a + 1.) / num_splits;
605
	double angle1;
606
	cairo_point_double_t p1;
607
	cairo_point_double_t A, U;
608
	cairo_point_double_t C0, C1;
609
	cairo_colr_gradient_patch_t patch;
610

            
611
	angle1 = interpolate (a0, a1, k);
612
	interpolate_colors (c0, c1, k, &color1);
613

            
614
	patch.color0 = color0;
615
	patch.color1 = color1;
616

            
617
	p1 = (cairo_point_double_t) { cosf (angle1), sinf (angle1) };
618
	patch.p0 = sum (*center, scale (p0, radius));
619
	patch.p1 = sum (*center, scale (p1, radius));
620

            
621
	A = normalize (sum (p0, p1));
622
	U = (cairo_point_double_t) { -A.y, A.x };
623
	C0 = sum (A, scale (U, dot (difference (p0, A), p0) / dot (U, p0)));
624
	C1 = sum (A, scale (U, dot (difference (p1, A), p1) / dot (U, p1)));
625
	patch.c0 = sum (*center, scale (sum (C0, scale (difference (C0, p0), 0.33333)), radius));
626
	patch.c1 = sum (*center, scale (sum (C1, scale (difference (C1, p1), 0.33333)), radius));
627

            
628
	add_patch (pattern, center, &patch);
629

            
630
	p0 = p1;
631
	color0 = color1;
632
    }
633
}
634

            
635
static void
636
add_sweep_gradient_patches (cairo_colr_color_line_t *cl,
637
                            cairo_extend_t           extend,
638
                            cairo_point_double_t    *center,
639
                            double                   radius,
640
                            double                   start_angle,
641
                            double                   end_angle,
642
                            cairo_pattern_t         *pattern)
643
{
644
    double *angles;
645
    cairo_color_t color0, color1;
646

            
647
    if (start_angle == end_angle) {
648
	if (extend == CAIRO_EXTEND_PAD) {
649
	    if (start_angle > 0)
650
		add_sweep_gradient_patches1 (center, radius,
651
					     0.,          &cl->stops[0].color,
652
					     start_angle, &cl->stops[0].color,
653
					     pattern);
654
	    if (end_angle < 2 * M_PI)
655
		add_sweep_gradient_patches1 (center, radius,
656
					     end_angle, &cl->stops[cl->n_stops - 1].color,
657
					     2 * M_PI,  &cl->stops[cl->n_stops - 1].color,
658
					     pattern);
659
        }
660
	return;
661
    }
662

            
663
    assert (start_angle != end_angle);
664

            
665
    angles = alloca (sizeof (double) * cl->n_stops);
666

            
667
    for (int i = 0; i < cl->n_stops; i++)
668
	angles[i] = start_angle + cl->stops[i].position * (end_angle - start_angle);
669

            
670
    /* handle directions */
671
    if (end_angle < start_angle) {
672
	for (int i = 0; i < cl->n_stops - 1 - i; i++) {
673
	    cairo_colr_color_stop_t stop = cl->stops[i];
674
	    double a = angles[i];
675
	    cl->stops[i] = cl->stops[cl->n_stops - 1 - i];
676
	    cl->stops[cl->n_stops - 1 - i] = stop;
677
	    angles[i] = angles[cl->n_stops - 1 - i];
678
	    angles[cl->n_stops - 1 - i] = a;
679
        }
680
    }
681

            
682
    if (extend == CAIRO_EXTEND_PAD)
683
    {
684
	int pos;
685

            
686
	color0 = cl->stops[0].color;
687
	for (pos = 0; pos < cl->n_stops; pos++) {
688
	    if (angles[pos] >= 0) {
689
		if (pos > 0) {
690
		    double k = (0 - angles[pos - 1]) / (angles[pos] - angles[pos - 1]);
691
		    interpolate_colors (&cl->stops[pos - 1].color, &cl->stops[pos].color, k, &color0);
692
                }
693
		break;
694
            }
695
        }
696
	if (pos == cl->n_stops) {
697
	    /* everything is below 0 */
698
	    color0 = cl->stops[cl->n_stops - 1].color;
699
	    add_sweep_gradient_patches1 (center, radius,
700
					 0.,       &color0,
701
					 2 * M_PI, &color0,
702
					 pattern);
703
	    return;
704
        }
705

            
706
	add_sweep_gradient_patches1 (center, radius,
707
				     0.,          &color0,
708
				     angles[pos], &cl->stops[pos].color,
709
				     pattern);
710

            
711
	for (pos++; pos < cl->n_stops; pos++) {
712
	    if (angles[pos] <= 2 * M_PI) {
713
		add_sweep_gradient_patches1 (center, radius,
714
					     angles[pos - 1], &cl->stops[pos - 1].color,
715
					     angles[pos],     &cl->stops[pos].color,
716
					     pattern);
717
            } else {
718
		double k = (2 * M_PI - angles[pos - 1]) / (angles[pos] - angles[pos - 1]);
719
		interpolate_colors (&cl->stops[pos - 1].color, &cl->stops[pos].color, k, &color1);
720
		add_sweep_gradient_patches1 (center, radius,
721
					     angles[pos - 1], &cl->stops[pos - 1].color,
722
					     2 * M_PI,        &color1,
723
					     pattern);
724
		break;
725
            }
726
        }
727

            
728
	if (pos == cl->n_stops) {
729
	    /* everything is below 2*M_PI */
730
	    color0 = cl->stops[cl->n_stops - 1].color;
731
	    add_sweep_gradient_patches1 (center, radius,
732
					 angles[cl->n_stops - 1], &color0,
733
					 2 * M_PI,                &color0,
734
					 pattern);
735
	    return;
736
        }
737
    } else {
738
	int k;
739
	double span;
740

            
741
	span = angles[cl->n_stops - 1] - angles[0];
742
	k = 0;
743
	if (angles[0] >= 0) {
744
	    double ss = angles[0];
745
	    while (ss > 0) {
746
		if (span > 0) {
747
		    ss -= span;
748
		    k--;
749
                } else {
750
		    ss += span;
751
		    k++;
752
                }
753
            }
754
        }
755
	else if (angles[0] < 0)
756
        {
757
	    double ee = angles[cl->n_stops - 1];
758
	    while (ee < 0) {
759
		if (span > 0) {
760
		    ee += span;
761
		    k++;
762
                } else {
763
		    ee -= span;
764
		    k--;
765
                }
766
            }
767
        }
768

            
769
	//assert (angles[0] + k * span <= 0 && 0 < angles[cl->n_stops - 1] + k * span);
770

            
771
	for (int l = k; TRUE; l++) {
772
	    for (int i = 1; i < cl->n_stops; i++) {
773
		double a0, a1;
774
		cairo_color_t *c0, *c1;
775

            
776
		if ((l % 2 != 0) && (extend == CAIRO_EXTEND_REFLECT)) {
777
		    a0 = angles[0] + angles[cl->n_stops - 1] - angles[cl->n_stops - 1 - (i-1)] + l * span;
778
		    a1 = angles[0] + angles[cl->n_stops - 1] - angles[cl->n_stops - 1 - i] + l * span;
779
		    c0 = &cl->stops[cl->n_stops - 1 - (i-1)].color;
780
		    c1 = &cl->stops[cl->n_stops - 1 - i].color;
781
                } else {
782
		    a0 = angles[i-1] + l * span;
783
		    a1 = angles[i] + l * span;
784
		    c0 = &cl->stops[i-1].color;
785
		    c1 = &cl->stops[i].color;
786
                }
787

            
788
		if (a1 < 0)
789
		    continue;
790

            
791
		if (a0 < 0) {
792
		    cairo_color_t color;
793
		    double f = (0 - a0)/(a1 - a0);
794
		    interpolate_colors (c0, c1, f, &color);
795
		    add_sweep_gradient_patches1 (center, radius,
796
						 0,  &color,
797
						 a1, c1,
798
						 pattern);
799
                } else if (a1 >= 2 * M_PI) {
800
		    cairo_color_t color;
801
		    double f = (2 * M_PI - a0)/(a1 - a0);
802
		    interpolate_colors (c0, c1, f, &color);
803
		    add_sweep_gradient_patches1 (center, radius,
804
						 a0,       c0,
805
						 2 * M_PI, &color,
806
						 pattern);
807
		    return;
808
                } else {
809
		    add_sweep_gradient_patches1 (center, radius,
810
						 a0, c0,
811
						 a1, c1,
812
						 pattern);
813
                }
814
            }
815
        }
816
    }
817
}
818

            
819
static cairo_status_t
820
draw_paint_sweep_gradient (cairo_colr_glyph_render_t *render,
821
                           FT_PaintSweepGradient     *gradient,
822
                           cairo_t                   *cr)
823
{
824
    cairo_colr_color_line_t *cl;
825
    cairo_point_double_t center;
826
    double start_angle, end_angle;
827
    double x1, y1, x2, y2;
828
    double max_x, max_y, R;
829
    cairo_pattern_t *pattern;
830
    cairo_extend_t extend;
831

            
832
#if DEBUG_COLR
833
    printf ("%*sDraw PaintSweepGradient\n", 2 * render->level, "");
834
#endif
835

            
836
    cl = read_colorline (render, &gradient->colorline);
837
    if (unlikely (cl == NULL))
838
	return CAIRO_STATUS_NO_MEMORY;
839

            
840
    center.x = double_from_16_16 (gradient->center.x);
841
    center.y = double_from_16_16 (gradient->center.y);
842
    start_angle = (double_from_16_16 (gradient->start_angle) + 1) * M_PI;
843
    end_angle = (double_from_16_16 (gradient->end_angle) + 1) * M_PI;
844

            
845
    pattern = cairo_pattern_create_mesh ();
846

            
847
    cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
848
    max_x = MAX ((x1 - center.x) * (x1 - center.x), (x2 - center.x) * (x2 - center.x));
849
    max_y = MAX ((y1 - center.y) * (y1 - center.y), (y2 - center.y) * (y2 - center.y));
850
    R = sqrt (max_x + max_y);
851

            
852
    extend = cairo_extend_from_ft_paint_extend (gradient->colorline.extend);
853

            
854
    add_sweep_gradient_patches (cl, extend, &center, R, start_angle, end_angle, pattern);
855

            
856
    cairo_set_source (cr, pattern);
857
    cairo_paint (cr);
858

            
859
    cairo_pattern_destroy (pattern);
860

            
861
    free_colorline (cl);
862

            
863
    return CAIRO_STATUS_SUCCESS;
864
}
865

            
866
static cairo_status_t
867
draw_paint_glyph (cairo_colr_glyph_render_t *render,
868
                  FT_PaintGlyph             *glyph,
869
                  cairo_t                   *cr)
870
{
871
    cairo_path_fixed_t *path_fixed;
872
    cairo_path_t *path;
873
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
874
    FT_Error error;
875

            
876
#if DEBUG_COLR
877
    printf ("%*sDraw PaintGlyph\n", 2 * render->level, "");
878
#endif
879

            
880
    error = FT_Load_Glyph (render->face, glyph->glyphID, FT_LOAD_DEFAULT);
881
    status = _cairo_ft_to_cairo_error (error);
882
    if (unlikely (status))
883
        return status;
884

            
885
    status = _cairo_ft_face_decompose_glyph_outline (render->face, &path_fixed);
886
    if (unlikely (status))
887
        return status;
888

            
889
    cairo_save (cr);
890
    cairo_identity_matrix (cr);
891
    path = _cairo_path_create (path_fixed, cr);
892
    _cairo_path_fixed_destroy (path_fixed);
893
    cairo_restore (cr);
894

            
895
    cairo_save (cr);
896

            
897
    cairo_new_path (cr);
898
    cairo_append_path (cr, path);
899
    cairo_path_destroy (path);
900
    cairo_clip (cr);
901

            
902
    status = draw_paint (render, &glyph->paint, cr);
903

            
904
    cairo_restore (cr);
905

            
906
    return status;
907
}
908

            
909
static cairo_status_t draw_colr_glyph (cairo_colr_glyph_render_t *render,
910
				       unsigned long              glyph,
911
                                       FT_Color_Root_Transform    root,
912
                                       cairo_t                   *cr);
913

            
914
static cairo_status_t
915
draw_paint_colr_glyph (cairo_colr_glyph_render_t *render,
916
                       FT_PaintColrGlyph *colr_glyph,
917
                       cairo_t *cr)
918
{
919
#if DEBUG_COLR
920
    printf ("%*sDraw PaintColrGlyph\n", 2 * render->level, "");
921
#endif
922

            
923
    return draw_colr_glyph (render, colr_glyph->glyphID, FT_COLOR_NO_ROOT_TRANSFORM, cr);
924
}
925

            
926
static cairo_status_t
927
draw_paint_transform (cairo_colr_glyph_render_t *render,
928
                      FT_PaintTransform *transform,
929
                      cairo_t *cr)
930
{
931
    cairo_matrix_t t;
932
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
933

            
934
#if DEBUG_COLR
935
    printf ("%*sDraw PaintTransform\n", 2 * render->level, "");
936
#endif
937

            
938
    cairo_matrix_init (&t,
939
		       double_from_16_16 (transform->affine.xx),
940
		       double_from_16_16 (transform->affine.yx),
941
		       double_from_16_16 (transform->affine.xy),
942
		       double_from_16_16 (transform->affine.yy),
943
		       double_from_16_16 (transform->affine.dx),
944
		       double_from_16_16 (transform->affine.dy));
945

            
946
    cairo_save (cr);
947

            
948
    cairo_transform (cr, &t);
949
    status = draw_paint (render, &transform->paint, cr);
950

            
951
    cairo_restore (cr);
952

            
953
    return status;
954
}
955

            
956
static cairo_status_t
957
draw_paint_translate (cairo_colr_glyph_render_t *render,
958
                      FT_PaintTranslate *translate,
959
                      cairo_t *cr)
960
{
961
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
962

            
963
#if DEBUG_COLR
964
    printf ("%*sDraw PaintTranslate\n", 2 * render->level, "");
965
#endif
966

            
967
    cairo_save (cr);
968

            
969
    cairo_translate (cr, double_from_16_16 (translate->dx), double_from_16_16 (translate->dy));
970
    status = draw_paint (render, &translate->paint, cr);
971

            
972
    cairo_restore (cr);
973

            
974
    return status;
975
}
976

            
977
static cairo_status_t
978
draw_paint_rotate (cairo_colr_glyph_render_t *render,
979
                   FT_PaintRotate *rotate,
980
                   cairo_t *cr)
981
{
982
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
983

            
984
#if DEBUG_COLR
985
    printf ("%*sDraw PaintRotate\n", 2 * render->level, "");
986
#endif
987

            
988
    cairo_save (cr);
989

            
990
    cairo_translate (cr, double_from_16_16 (rotate->center_x), double_from_16_16 (rotate->center_y));
991
    cairo_rotate (cr, double_from_16_16 (rotate->angle) * M_PI);
992
    cairo_translate (cr, - double_from_16_16 (rotate->center_x), - double_from_16_16 (rotate->center_y));
993
    status = draw_paint (render, &rotate->paint, cr);
994

            
995
    cairo_restore (cr);
996

            
997
    return status;
998
}
999

            
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 */