This is a quick hack to prove that the hb-cairo apis workpull/3976/head
parent
767bdd43a6
commit
0d6ee4621e
5 changed files with 25 additions and 1506 deletions
@ -1,847 +0,0 @@ |
||||
/*
|
||||
* Copyright © 2022 Red Hat, Inc |
||||
* |
||||
* This is part of HarfBuzz, a text shaping library. |
||||
* |
||||
* Permission is hereby granted, without written agreement and without |
||||
* license or royalty fees, to use, copy, modify, and distribute this |
||||
* software and its documentation for any purpose, provided that the |
||||
* above copyright notice and the following two paragraphs appear in |
||||
* all copies of this software. |
||||
* |
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
||||
* DAMAGE. |
||||
* |
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
||||
* |
||||
* Google Author(s): Matthias Clasen |
||||
*/ |
||||
|
||||
#include "config.h" |
||||
|
||||
#include "hb-cairo-utils.h" |
||||
|
||||
#include <cairo.h> |
||||
#include <hb-ot.h> |
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
#include <stdio.h> |
||||
#define _USE_MATH_DEFINES |
||||
#include <math.h> |
||||
#include <assert.h> |
||||
|
||||
#ifndef MIN |
||||
#define MIN(x,y) ((x) < (y) ? (x) : (y)) |
||||
#endif |
||||
|
||||
#ifndef MAX |
||||
#define MAX(x,y) ((x) > (y) ? (x) : (y)) |
||||
#endif |
||||
|
||||
#ifndef FALSE |
||||
#define TRUE 1 |
||||
#define FALSE 0 |
||||
#endif |
||||
|
||||
#define PREALLOCATED_COLOR_STOPS 16 |
||||
|
||||
typedef struct { |
||||
float r, g, b, a; |
||||
} color_t; |
||||
|
||||
static inline cairo_extend_t |
||||
cairo_extend (hb_paint_extend_t extend) |
||||
{ |
||||
switch (extend) |
||||
{ |
||||
case HB_PAINT_EXTEND_PAD: return CAIRO_EXTEND_PAD; |
||||
case HB_PAINT_EXTEND_REPEAT: return CAIRO_EXTEND_REPEAT; |
||||
case HB_PAINT_EXTEND_REFLECT: return CAIRO_EXTEND_REFLECT; |
||||
default:; |
||||
} |
||||
|
||||
return CAIRO_EXTEND_PAD; |
||||
} |
||||
|
||||
#ifdef CAIRO_HAS_PNG_FUNCTIONS |
||||
typedef struct |
||||
{ |
||||
hb_blob_t *blob; |
||||
unsigned int offset; |
||||
} read_blob_data_t; |
||||
|
||||
static cairo_status_t |
||||
read_blob (void *closure, |
||||
unsigned char *data, |
||||
unsigned int length) |
||||
{ |
||||
read_blob_data_t *r = (read_blob_data_t *) closure; |
||||
const char *d; |
||||
unsigned int size; |
||||
|
||||
d = hb_blob_get_data (r->blob, &size); |
||||
|
||||
if (r->offset + length > size) |
||||
return CAIRO_STATUS_READ_ERROR; |
||||
|
||||
memcpy (data, d + r->offset, length); |
||||
r->offset += length; |
||||
|
||||
return CAIRO_STATUS_SUCCESS; |
||||
} |
||||
#endif |
||||
|
||||
static const cairo_user_data_key_t *_hb_cairo_surface_blob_user_data_key = {0}; |
||||
|
||||
static void |
||||
_hb_cairo_destroy_blob (void *p) |
||||
{ |
||||
hb_blob_destroy ((hb_blob_t *) p); |
||||
} |
||||
|
||||
hb_bool_t |
||||
hb_cairo_paint_glyph_image (cairo_t *cr, |
||||
hb_blob_t *blob, |
||||
unsigned width, |
||||
unsigned height, |
||||
hb_tag_t format, |
||||
float slant, |
||||
hb_glyph_extents_t *extents) |
||||
{ |
||||
if (!extents) /* SVG currently. */ |
||||
return FALSE; |
||||
|
||||
cairo_surface_t *surface = NULL; |
||||
|
||||
#ifdef CAIRO_HAS_PNG_FUNCTIONS |
||||
if (format == HB_PAINT_IMAGE_FORMAT_PNG) |
||||
{ |
||||
read_blob_data_t r; |
||||
r.blob = blob; |
||||
r.offset = 0; |
||||
surface = cairo_image_surface_create_from_png_stream (read_blob, &r); |
||||
|
||||
/* For PNG, width,height can be unreliable, as is the case for NotoColorEmoji :(.
|
||||
* Just pull them out of the surface. */ |
||||
width = cairo_image_surface_get_width (surface); |
||||
height = cairo_image_surface_get_width (surface); |
||||
} |
||||
else |
||||
#endif |
||||
if (format == HB_PAINT_IMAGE_FORMAT_BGRA) |
||||
{ |
||||
/* Byte-endian conversion. */ |
||||
unsigned data_size = hb_blob_get_length (blob); |
||||
if (data_size < width * height * 4) |
||||
return FALSE; |
||||
|
||||
unsigned char *data; |
||||
#ifdef __BYTE_ORDER |
||||
if (__BYTE_ORDER == __BIG_ENDIAN) |
||||
{ |
||||
data = (unsigned char *) hb_blob_get_data_writable (blob, NULL); |
||||
if (!data) |
||||
return FALSE; |
||||
|
||||
unsigned count = width * height * 4; |
||||
for (unsigned i = 0; i < count; i += 4) |
||||
{ |
||||
unsigned char b; |
||||
b = data[i]; |
||||
data[i] = data[i+3]; |
||||
data[i+3] = b; |
||||
b = data[i+1]; |
||||
data[i+1] = data[i+2]; |
||||
data[i+2] = b; |
||||
} |
||||
} |
||||
else |
||||
#endif |
||||
data = (unsigned char *) hb_blob_get_data (blob, NULL); |
||||
|
||||
surface = cairo_image_surface_create_for_data (data, |
||||
CAIRO_FORMAT_ARGB32, |
||||
width, height, |
||||
width * 4); |
||||
|
||||
cairo_surface_set_user_data (surface, |
||||
_hb_cairo_surface_blob_user_data_key, |
||||
hb_blob_reference (blob), |
||||
_hb_cairo_destroy_blob); |
||||
} |
||||
|
||||
if (!surface) |
||||
return FALSE; |
||||
|
||||
cairo_save (cr); |
||||
/* this clip is here to work around recording surface limitations */ |
||||
cairo_rectangle (cr, |
||||
extents->x_bearing, |
||||
extents->y_bearing, |
||||
extents->width, |
||||
extents->height); |
||||
cairo_clip (cr); |
||||
|
||||
cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface); |
||||
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); |
||||
|
||||
cairo_matrix_t matrix = {(double) width, 0, 0, (double) height, 0, 0}; |
||||
cairo_pattern_set_matrix (pattern, &matrix); |
||||
|
||||
/* Undo slant in the extents and apply it in the context. */ |
||||
extents->width -= extents->height * slant; |
||||
extents->x_bearing -= extents->y_bearing * slant; |
||||
cairo_matrix_t cairo_matrix = {1., 0., slant, 1., 0., 0.}; |
||||
cairo_transform (cr, &cairo_matrix); |
||||
|
||||
cairo_translate (cr, extents->x_bearing, extents->y_bearing); |
||||
cairo_scale (cr, extents->width, extents->height); |
||||
cairo_set_source (cr, pattern); |
||||
|
||||
cairo_paint (cr); |
||||
|
||||
cairo_pattern_destroy (pattern); |
||||
cairo_surface_destroy (surface); |
||||
|
||||
cairo_restore (cr); |
||||
|
||||
return TRUE; |
||||
} |
||||
|
||||
static void |
||||
reduce_anchors (float x0, float y0, |
||||
float x1, float y1, |
||||
float x2, float y2, |
||||
float *xx0, float *yy0, |
||||
float *xx1, float *yy1) |
||||
{ |
||||
float q1x, q1y, q2x, q2y; |
||||
float s; |
||||
float k; |
||||
|
||||
q2x = x2 - x0; |
||||
q2y = y2 - y0; |
||||
q1x = y1 - x0; |
||||
q1y = y1 - y0; |
||||
|
||||
s = q2x * q2x + q2y * q2y; |
||||
if (s < 0.000001f) |
||||
{ |
||||
*xx0 = x0; *yy0 = y0; |
||||
*xx1 = x1; *yy1 = y1; |
||||
return; |
||||
} |
||||
|
||||
k = (q2x * q1x + q2y * q1y) / s; |
||||
*xx0 = x0; |
||||
*yy0 = y0; |
||||
*xx1 = x1 - k * q2x; |
||||
*yy1 = y1 - k * q2y; |
||||
} |
||||
|
||||
static int |
||||
cmp_color_stop (const void *p1, |
||||
const void *p2) |
||||
{ |
||||
const hb_color_stop_t *c1 = (const hb_color_stop_t *) p1; |
||||
const hb_color_stop_t *c2 = (const hb_color_stop_t *) p2; |
||||
|
||||
if (c1->offset < c2->offset) |
||||
return -1; |
||||
else if (c1->offset > c2->offset) |
||||
return 1; |
||||
else |
||||
return 0; |
||||
} |
||||
|
||||
static void |
||||
normalize_color_line (hb_color_stop_t *stops, |
||||
unsigned int len, |
||||
float *omin, |
||||
float *omax) |
||||
{ |
||||
float min, max; |
||||
|
||||
qsort (stops, len, sizeof (hb_color_stop_t), cmp_color_stop); |
||||
|
||||
min = max = stops[0].offset; |
||||
for (unsigned int i = 0; i < len; i++) |
||||
{ |
||||
min = MIN (min, stops[i].offset); |
||||
max = MAX (max, stops[i].offset); |
||||
} |
||||
|
||||
if (min != max) |
||||
{ |
||||
for (unsigned int i = 0; i < len; i++) |
||||
stops[i].offset = (stops[i].offset - min) / (max - min); |
||||
} |
||||
|
||||
*omin = min; |
||||
*omax = max; |
||||
} |
||||
|
||||
void |
||||
hb_cairo_paint_linear_gradient (cairo_t *cr, |
||||
hb_color_line_t *color_line, |
||||
float x0, float y0, |
||||
float x1, float y1, |
||||
float x2, float y2) |
||||
{ |
||||
hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS]; |
||||
hb_color_stop_t *stops = stops_; |
||||
unsigned int len; |
||||
float xx0, yy0, xx1, yy1; |
||||
float xxx0, yyy0, xxx1, yyy1; |
||||
float min, max; |
||||
cairo_pattern_t *pattern; |
||||
|
||||
len = hb_color_line_get_color_stops (color_line, 0, NULL, NULL); |
||||
if (len > PREALLOCATED_COLOR_STOPS) |
||||
stops = (hb_color_stop_t *) malloc (len * sizeof (hb_color_stop_t)); |
||||
hb_color_line_get_color_stops (color_line, 0, &len, stops); |
||||
|
||||
reduce_anchors (x0, y0, x1, y1, x2, y2, &xx0, &yy0, &xx1, &yy1); |
||||
normalize_color_line (stops, len, &min, &max); |
||||
|
||||
xxx0 = xx0 + min * (xx1 - xx0); |
||||
yyy0 = yy0 + min * (yy1 - yy0); |
||||
xxx1 = xx0 + max * (xx1 - xx0); |
||||
yyy1 = yy0 + max * (yy1 - yy0); |
||||
|
||||
pattern = cairo_pattern_create_linear (xxx0, yyy0, xxx1, yyy1); |
||||
cairo_pattern_set_extend (pattern, cairo_extend (hb_color_line_get_extend (color_line))); |
||||
for (unsigned int i = 0; i < len; i++) |
||||
{ |
||||
double r, g, b, a; |
||||
r = hb_color_get_red (stops[i].color) / 255.; |
||||
g = hb_color_get_green (stops[i].color) / 255.; |
||||
b = hb_color_get_blue (stops[i].color) / 255.; |
||||
a = hb_color_get_alpha (stops[i].color) / 255.; |
||||
cairo_pattern_add_color_stop_rgba (pattern, stops[i].offset, r, g, b, a); |
||||
} |
||||
|
||||
cairo_set_source (cr, pattern); |
||||
cairo_paint (cr); |
||||
|
||||
cairo_pattern_destroy (pattern); |
||||
|
||||
if (stops != stops_) |
||||
free (stops); |
||||
} |
||||
|
||||
void |
||||
hb_cairo_paint_radial_gradient (cairo_t *cr, |
||||
hb_color_line_t *color_line, |
||||
float x0, float y0, float r0, |
||||
float x1, float y1, float r1) |
||||
{ |
||||
hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS]; |
||||
hb_color_stop_t *stops = stops_; |
||||
unsigned int len; |
||||
float min, max; |
||||
float xx0, yy0, xx1, yy1; |
||||
float rr0, rr1; |
||||
cairo_pattern_t *pattern; |
||||
|
||||
len = hb_color_line_get_color_stops (color_line, 0, NULL, NULL); |
||||
if (len > PREALLOCATED_COLOR_STOPS) |
||||
stops = (hb_color_stop_t *) malloc (len * sizeof (hb_color_stop_t)); |
||||
hb_color_line_get_color_stops (color_line, 0, &len, stops); |
||||
|
||||
normalize_color_line (stops, len, &min, &max); |
||||
|
||||
xx0 = x0 + min * (x1 - x0); |
||||
yy0 = y0 + min * (y1 - y0); |
||||
xx1 = x0 + max * (x1 - x0); |
||||
yy1 = y0 + max * (y1 - y0); |
||||
rr0 = r0 + min * (r1 - r0); |
||||
rr1 = r0 + max * (r1 - r0); |
||||
|
||||
pattern = cairo_pattern_create_radial (xx0, yy0, rr0, xx1, yy1, rr1); |
||||
cairo_pattern_set_extend (pattern, cairo_extend (hb_color_line_get_extend (color_line))); |
||||
|
||||
for (unsigned int i = 0; i < len; i++) |
||||
{ |
||||
double r, g, b, a; |
||||
r = hb_color_get_red (stops[i].color) / 255.; |
||||
g = hb_color_get_green (stops[i].color) / 255.; |
||||
b = hb_color_get_blue (stops[i].color) / 255.; |
||||
a = hb_color_get_alpha (stops[i].color) / 255.; |
||||
cairo_pattern_add_color_stop_rgba (pattern, stops[i].offset, r, g, b, a); |
||||
} |
||||
|
||||
cairo_set_source (cr, pattern); |
||||
cairo_paint (cr); |
||||
|
||||
cairo_pattern_destroy (pattern); |
||||
|
||||
if (stops != stops_) |
||||
free (stops); |
||||
} |
||||
|
||||
typedef struct { |
||||
float x, y; |
||||
} Point; |
||||
|
||||
static inline float |
||||
interpolate (float f0, float f1, float f) |
||||
{ |
||||
return f0 + f * (f1 - f0); |
||||
} |
||||
|
||||
static inline void |
||||
premultiply (color_t *c) |
||||
{ |
||||
c->r *= c->a; |
||||
c->g *= c->a; |
||||
c->b *= c->a; |
||||
} |
||||
|
||||
static inline void |
||||
unpremultiply (color_t *c) |
||||
{ |
||||
if (c->a != 0.) |
||||
{ |
||||
c->r /= c->a; |
||||
c->g /= c->a; |
||||
c->b /= c->a; |
||||
} |
||||
} |
||||
|
||||
static void |
||||
interpolate_colors (color_t *c0, color_t *c1, float k, color_t *c) |
||||
{ |
||||
// According to the COLR specification, gradients
|
||||
// should be interpolated in premultiplied form
|
||||
premultiply (c0); |
||||
premultiply (c1); |
||||
c->r = c0->r + k * (c1->r - c0->r); |
||||
c->g = c0->g + k * (c1->g - c0->g); |
||||
c->b = c0->b + k * (c1->b - c0->b); |
||||
c->a = c0->a + k * (c1->a - c0->a); |
||||
unpremultiply (c); |
||||
} |
||||
|
||||
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 }; |
||||
} |
||||
|
||||
typedef struct { |
||||
Point center, p0, c0, c1, p1; |
||||
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.r, |
||||
p->color0.g, |
||||
p->color0.b, |
||||
p->color0.a); |
||||
cairo_mesh_pattern_set_corner_color_rgba (pattern, 1, |
||||
p->color0.r, |
||||
p->color0.g, |
||||
p->color0.b, |
||||
p->color0.a); |
||||
cairo_mesh_pattern_set_corner_color_rgba (pattern, 2, |
||||
p->color1.r, |
||||
p->color1.g, |
||||
p->color1.b, |
||||
p->color1.a); |
||||
cairo_mesh_pattern_set_corner_color_rgba (pattern, 3, |
||||
p->color1.r, |
||||
p->color1.g, |
||||
p->color1.b, |
||||
p->color1.a); |
||||
cairo_mesh_pattern_end_patch (pattern); |
||||
} |
||||
|
||||
#define MAX_ANGLE (M_PI / 8.) |
||||
|
||||
static void |
||||
add_sweep_gradient_patches1 (float cx, float cy, float radius, |
||||
float a0, color_t *c0, |
||||
float a1, color_t *c1, |
||||
cairo_pattern_t *pattern) |
||||
{ |
||||
Point center = (Point) { cx, cy }; |
||||
int num_splits; |
||||
Point p0; |
||||
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, ¢er, &patch); |
||||
|
||||
p0 = p1; |
||||
color0 = color1; |
||||
} |
||||
} |
||||
|
||||
static void |
||||
add_sweep_gradient_patches (hb_color_stop_t *stops, |
||||
unsigned int n_stops, |
||||
cairo_extend_t extend, |
||||
float cx, float cy, |
||||
float radius, |
||||
float start_angle, |
||||
float end_angle, |
||||
cairo_pattern_t *pattern) |
||||
{ |
||||
float angles_[PREALLOCATED_COLOR_STOPS]; |
||||
float *angles = angles_; |
||||
color_t colors_[PREALLOCATED_COLOR_STOPS]; |
||||
color_t *colors = colors_; |
||||
color_t color0, color1; |
||||
|
||||
if (start_angle == end_angle) |
||||
{ |
||||
if (extend == CAIRO_EXTEND_PAD) |
||||
{ |
||||
color_t c; |
||||
if (start_angle > 0) |
||||
{ |
||||
c.r = hb_color_get_red (stops[0].color) / 255.; |
||||
c.g = hb_color_get_green (stops[0].color) / 255.; |
||||
c.b = hb_color_get_blue (stops[0].color) / 255.; |
||||
c.a = hb_color_get_alpha (stops[0].color) / 255.; |
||||
add_sweep_gradient_patches1 (cx, cy, radius, |
||||
0., &c, |
||||
start_angle, &c, |
||||
pattern); |
||||
} |
||||
if (end_angle < 2 * M_PI) |
||||
{ |
||||
c.r = hb_color_get_red (stops[n_stops - 1].color) / 255.; |
||||
c.g = hb_color_get_green (stops[n_stops - 1].color) / 255.; |
||||
c.b = hb_color_get_blue (stops[n_stops - 1].color) / 255.; |
||||
c.a = hb_color_get_alpha (stops[n_stops - 1].color) / 255.; |
||||
add_sweep_gradient_patches1 (cx, cy, radius, |
||||
end_angle, &c, |
||||
2 * M_PI, &c, |
||||
pattern); |
||||
} |
||||
} |
||||
return; |
||||
} |
||||
|
||||
assert (start_angle != end_angle); |
||||
|
||||
/* handle directions */ |
||||
if (end_angle < start_angle) |
||||
{ |
||||
float angle = end_angle; |
||||
end_angle = start_angle; |
||||
start_angle = angle; |
||||
|
||||
for (unsigned i = 0; i < n_stops - 1 - i; i++) |
||||
{ |
||||
hb_color_stop_t stop = stops[i]; |
||||
stops[i] = stops[n_stops - 1 - i]; |
||||
stops[n_stops - 1 - i] = stop; |
||||
} |
||||
} |
||||
|
||||
if (n_stops > PREALLOCATED_COLOR_STOPS) |
||||
{ |
||||
angles = (float *) malloc (sizeof (float) * n_stops); |
||||
colors = (color_t *) malloc (sizeof (color_t) * n_stops); |
||||
} |
||||
|
||||
for (unsigned i = 0; i < n_stops; i++) |
||||
{ |
||||
angles[i] = start_angle + stops[i].offset * (end_angle - start_angle); |
||||
colors[i].r = hb_color_get_red (stops[i].color) / 255.; |
||||
colors[i].g = hb_color_get_green (stops[i].color) / 255.; |
||||
colors[i].b = hb_color_get_blue (stops[i].color) / 255.; |
||||
colors[i].a = hb_color_get_alpha (stops[i].color) / 255.; |
||||
} |
||||
|
||||
if (extend == CAIRO_EXTEND_PAD) |
||||
{ |
||||
unsigned pos; |
||||
|
||||
color0 = colors[0]; |
||||
for (pos = 0; pos < n_stops; pos++) |
||||
{ |
||||
if (angles[pos] >= 0) |
||||
{ |
||||
if (pos > 0) |
||||
{ |
||||
float k = (0 - angles[pos - 1]) / (angles[pos] - angles[pos - 1]); |
||||
interpolate_colors (&colors[pos-1], &colors[pos], k, &color0); |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
if (pos == n_stops) |
||||
{ |
||||
/* everything is below 0 */ |
||||
color0 = colors[n_stops-1]; |
||||
add_sweep_gradient_patches1 (cx, cy, radius, |
||||
0., &color0, |
||||
2 * M_PI, &color0, |
||||
pattern); |
||||
goto done; |
||||
} |
||||
|
||||
add_sweep_gradient_patches1 (cx, cy, radius, |
||||
0., &color0, |
||||
angles[pos], &colors[pos], |
||||
pattern); |
||||
|
||||
for (pos++; pos < n_stops; pos++) |
||||
{ |
||||
if (angles[pos] <= 2 * M_PI) |
||||
{ |
||||
add_sweep_gradient_patches1 (cx, cy, radius, |
||||
angles[pos - 1], &colors[pos-1], |
||||
angles[pos], &colors[pos], |
||||
pattern); |
||||
} |
||||
else |
||||
{ |
||||
float k = (2 * M_PI - angles[pos - 1]) / (angles[pos] - angles[pos - 1]); |
||||
interpolate_colors (&colors[pos - 1], &colors[pos], k, &color1); |
||||
add_sweep_gradient_patches1 (cx, cy, radius, |
||||
angles[pos - 1], &colors[pos - 1], |
||||
2 * M_PI, &color1, |
||||
pattern); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (pos == n_stops) |
||||
{ |
||||
/* everything is below 2*M_PI */ |
||||
color0 = colors[n_stops - 1]; |
||||
add_sweep_gradient_patches1 (cx, cy, radius, |
||||
angles[n_stops - 1], &color0, |
||||
2 * M_PI, &color0, |
||||
pattern); |
||||
goto done; |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
int k; |
||||
float span; |
||||
|
||||
span = angles[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[n_stops - 1]; |
||||
while (ee < 0) |
||||
{ |
||||
if (span > 0) |
||||
{ |
||||
ee += span; |
||||
k++; |
||||
} |
||||
else |
||||
{ |
||||
ee -= span; |
||||
k--; |
||||
} |
||||
} |
||||
} |
||||
|
||||
//assert (angles[0] + k * span <= 0 && 0 < angles[n_stops - 1] + k * span);
|
||||
|
||||
for (unsigned l = k; 1; l++) |
||||
{ |
||||
for (unsigned i = 1; i < n_stops; i++) |
||||
{ |
||||
float a0, a1; |
||||
color_t *c0, *c1; |
||||
|
||||
if ((l % 2 != 0) && (extend == CAIRO_EXTEND_REFLECT)) |
||||
{ |
||||
a0 = angles[0] + angles[n_stops - 1] - angles[n_stops - 1 - (i-1)] + l * span; |
||||
a1 = angles[0] + angles[n_stops - 1] - angles[n_stops - 1 - i] + l * span; |
||||
c0 = &colors[n_stops - 1 - (i - 1)]; |
||||
c1 = &colors[n_stops - 1 - i]; |
||||
} |
||||
else |
||||
{ |
||||
a0 = angles[i-1] + l * span; |
||||
a1 = angles[i] + l * span; |
||||
c0 = &colors[i-1]; |
||||
c1 = &colors[i]; |
||||
} |
||||
|
||||
if (a1 < 0) |
||||
continue; |
||||
if (a0 < 0) |
||||
{ |
||||
color_t color; |
||||
float f = (0 - a0)/(a1 - a0); |
||||
interpolate_colors (c0, c1, f, &color); |
||||
add_sweep_gradient_patches1 (cx, cy, radius, |
||||
0, &color, |
||||
a1, c1, |
||||
pattern); |
||||
} |
||||
else if (a1 >= 2 * M_PI) |
||||
{ |
||||
color_t color; |
||||
float f = (2 * M_PI - a0)/(a1 - a0); |
||||
interpolate_colors (c0, c1, f, &color); |
||||
add_sweep_gradient_patches1 (cx, cy, radius, |
||||
a0, c0, |
||||
2 * M_PI, &color, |
||||
pattern); |
||||
goto done; |
||||
} |
||||
else |
||||
{ |
||||
add_sweep_gradient_patches1 (cx, cy, radius, |
||||
a0, c0, |
||||
a1, c1, |
||||
pattern); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
done: |
||||
|
||||
if (angles != angles_) |
||||
free (angles); |
||||
if (colors != colors_) |
||||
free (colors); |
||||
} |
||||
|
||||
void |
||||
hb_cairo_paint_sweep_gradient (cairo_t *cr, |
||||
hb_color_line_t *color_line, |
||||
float cx, float cy, |
||||
float start_angle, |
||||
float end_angle) |
||||
{ |
||||
unsigned int len; |
||||
hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS]; |
||||
hb_color_stop_t *stops = stops_; |
||||
cairo_extend_t extend; |
||||
double x1, y1, x2, y2; |
||||
float max_x, max_y, radius; |
||||
cairo_pattern_t *pattern; |
||||
|
||||
len = hb_color_line_get_color_stops (color_line, 0, NULL, NULL); |
||||
if (len > PREALLOCATED_COLOR_STOPS) |
||||
stops = (hb_color_stop_t *) malloc (len * sizeof (hb_color_stop_t)); |
||||
hb_color_line_get_color_stops (color_line, 0, &len, stops); |
||||
|
||||
qsort (stops, len, sizeof (hb_color_stop_t), cmp_color_stop); |
||||
|
||||
cairo_clip_extents (cr, &x1, &y1, &x2, &y2); |
||||
max_x = MAX ((x1 - cx) * (x1 - cx), (x2 - cx) * (x2 - cx)); |
||||
max_y = MAX ((y1 - cy) * (y1 - cy), (y2 - cy) * (y2 - cy)); |
||||
radius = sqrt (max_x + max_y); |
||||
|
||||
extend = cairo_extend (hb_color_line_get_extend (color_line)); |
||||
pattern = cairo_pattern_create_mesh (); |
||||
|
||||
add_sweep_gradient_patches (stops, len, extend, cx, cy, |
||||
radius, start_angle, end_angle, pattern); |
||||
|
||||
cairo_set_source (cr, pattern); |
||||
cairo_paint (cr); |
||||
|
||||
cairo_pattern_destroy (pattern); |
||||
|
||||
if (stops != stops_) |
||||
free (stops); |
||||
} |
@ -1,97 +0,0 @@ |
||||
/*
|
||||
* Copyright © 2022 Red Hat, Inc |
||||
* |
||||
* This is part of HarfBuzz, a text shaping library. |
||||
* |
||||
* Permission is hereby granted, without written agreement and without |
||||
* license or royalty fees, to use, copy, modify, and distribute this |
||||
* software and its documentation for any purpose, provided that the |
||||
* above copyright notice and the following two paragraphs appear in |
||||
* all copies of this software. |
||||
* |
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
||||
* DAMAGE. |
||||
* |
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
||||
* |
||||
* Google Author(s): Matthias Clasen |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <hb.h> |
||||
#include <cairo.h> |
||||
|
||||
HB_BEGIN_DECLS |
||||
|
||||
static inline cairo_operator_t |
||||
hb_paint_composite_mode_to_cairo (hb_paint_composite_mode_t mode) |
||||
{ |
||||
switch (mode) |
||||
{ |
||||
case HB_PAINT_COMPOSITE_MODE_CLEAR: return CAIRO_OPERATOR_CLEAR; |
||||
case HB_PAINT_COMPOSITE_MODE_SRC: return CAIRO_OPERATOR_SOURCE; |
||||
case HB_PAINT_COMPOSITE_MODE_DEST: return CAIRO_OPERATOR_DEST; |
||||
case HB_PAINT_COMPOSITE_MODE_SRC_OVER: return CAIRO_OPERATOR_OVER; |
||||
case HB_PAINT_COMPOSITE_MODE_DEST_OVER: return CAIRO_OPERATOR_DEST_OVER; |
||||
case HB_PAINT_COMPOSITE_MODE_SRC_IN: return CAIRO_OPERATOR_IN; |
||||
case HB_PAINT_COMPOSITE_MODE_DEST_IN: return CAIRO_OPERATOR_DEST_IN; |
||||
case HB_PAINT_COMPOSITE_MODE_SRC_OUT: return CAIRO_OPERATOR_OUT; |
||||
case HB_PAINT_COMPOSITE_MODE_DEST_OUT: return CAIRO_OPERATOR_DEST_OUT; |
||||
case HB_PAINT_COMPOSITE_MODE_SRC_ATOP: return CAIRO_OPERATOR_ATOP; |
||||
case HB_PAINT_COMPOSITE_MODE_DEST_ATOP: return CAIRO_OPERATOR_DEST_ATOP; |
||||
case HB_PAINT_COMPOSITE_MODE_XOR: return CAIRO_OPERATOR_XOR; |
||||
case HB_PAINT_COMPOSITE_MODE_PLUS: return CAIRO_OPERATOR_ADD; |
||||
case HB_PAINT_COMPOSITE_MODE_SCREEN: return CAIRO_OPERATOR_SCREEN; |
||||
case HB_PAINT_COMPOSITE_MODE_OVERLAY: return CAIRO_OPERATOR_OVERLAY; |
||||
case HB_PAINT_COMPOSITE_MODE_DARKEN: return CAIRO_OPERATOR_DARKEN; |
||||
case HB_PAINT_COMPOSITE_MODE_LIGHTEN: return CAIRO_OPERATOR_LIGHTEN; |
||||
case HB_PAINT_COMPOSITE_MODE_COLOR_DODGE: return CAIRO_OPERATOR_COLOR_DODGE; |
||||
case HB_PAINT_COMPOSITE_MODE_COLOR_BURN: return CAIRO_OPERATOR_COLOR_BURN; |
||||
case HB_PAINT_COMPOSITE_MODE_HARD_LIGHT: return CAIRO_OPERATOR_HARD_LIGHT; |
||||
case HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT: return CAIRO_OPERATOR_SOFT_LIGHT; |
||||
case HB_PAINT_COMPOSITE_MODE_DIFFERENCE: return CAIRO_OPERATOR_DIFFERENCE; |
||||
case HB_PAINT_COMPOSITE_MODE_EXCLUSION: return CAIRO_OPERATOR_EXCLUSION; |
||||
case HB_PAINT_COMPOSITE_MODE_MULTIPLY: return CAIRO_OPERATOR_MULTIPLY; |
||||
case HB_PAINT_COMPOSITE_MODE_HSL_HUE: return CAIRO_OPERATOR_HSL_HUE; |
||||
case HB_PAINT_COMPOSITE_MODE_HSL_SATURATION: return CAIRO_OPERATOR_HSL_SATURATION; |
||||
case HB_PAINT_COMPOSITE_MODE_HSL_COLOR: return CAIRO_OPERATOR_HSL_COLOR; |
||||
case HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY: return CAIRO_OPERATOR_HSL_LUMINOSITY; |
||||
default:; |
||||
} |
||||
|
||||
return CAIRO_OPERATOR_SOURCE; |
||||
} |
||||
|
||||
hb_bool_t hb_cairo_paint_glyph_image (cairo_t *cr, |
||||
hb_blob_t *blob, |
||||
unsigned width, |
||||
unsigned height, |
||||
hb_tag_t format, |
||||
float slant, |
||||
hb_glyph_extents_t *extents); |
||||
|
||||
void hb_cairo_paint_linear_gradient (cairo_t *cr, |
||||
hb_color_line_t *color_line, |
||||
float x0, float y0, |
||||
float x1, float y1, |
||||
float x2, float y2); |
||||
|
||||
void hb_cairo_paint_radial_gradient (cairo_t *cr, |
||||
hb_color_line_t *color_line, |
||||
float x0, float y0, float r0, |
||||
float x1, float y1, float r1); |
||||
|
||||
void hb_cairo_paint_sweep_gradient (cairo_t *cr, |
||||
hb_color_line_t *color_line, |
||||
float x0, float y0, |
||||
float start_angle, float end_angle); |
||||
|
||||
HB_END_DECLS |
@ -1,541 +0,0 @@ |
||||
/*
|
||||
* Copyright © 2022 Google, Inc. |
||||
* |
||||
* This is part of HarfBuzz, a text shaping library. |
||||
* |
||||
* Permission is hereby granted, without written agreement and without |
||||
* license or royalty fees, to use, copy, modify, and distribute this |
||||
* software and its documentation for any purpose, provided that the |
||||
* above copyright notice and the following two paragraphs appear in |
||||
* all copies of this software. |
||||
* |
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
||||
* DAMAGE. |
||||
* |
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
||||
* |
||||
* Google Author(s): Behdad Esfahbod |
||||
*/ |
||||
|
||||
#ifndef HELPER_CAIRO_USER_HH |
||||
#define HELPER_CAIRO_USER_HH |
||||
|
||||
#include "config.h" |
||||
|
||||
#include "font-options.hh" |
||||
|
||||
#include <cairo.h> |
||||
#include <hb.h> |
||||
|
||||
#include "hb-blob.hh" |
||||
#include "hb-cairo-utils.h" |
||||
|
||||
static bool debug = false; |
||||
static int level = 0; |
||||
|
||||
static void print (const char *format, ...) __attribute__((format (printf, 1, 2))); |
||||
|
||||
static void |
||||
print (const char *format, |
||||
...) |
||||
{ |
||||
va_list args; |
||||
|
||||
if (!debug) |
||||
return; |
||||
|
||||
printf ("%*s", 2 * level, ""); |
||||
|
||||
va_start (args, format); |
||||
vprintf (format, args); |
||||
va_end (args); |
||||
|
||||
printf ("\n"); |
||||
} |
||||
|
||||
static const cairo_user_data_key_t _hb_font_cairo_user_data_key = {0}; |
||||
|
||||
static void |
||||
move_to (hb_draw_funcs_t *dfuncs, |
||||
cairo_t *cr, |
||||
hb_draw_state_t *st, |
||||
float to_x, float to_y, |
||||
void *) |
||||
{ |
||||
print ("move to %f %f", |
||||
(double) to_x, (double) to_y); |
||||
cairo_move_to (cr, |
||||
(double) to_x, (double) to_y); |
||||
} |
||||
|
||||
static void |
||||
line_to (hb_draw_funcs_t *dfuncs, |
||||
cairo_t *cr, |
||||
hb_draw_state_t *st, |
||||
float to_x, float to_y, |
||||
void *) |
||||
{ |
||||
print ("line to %f %f", |
||||
(double) to_x, (double) to_y); |
||||
cairo_line_to (cr, |
||||
(double) to_x, (double) to_y); |
||||
} |
||||
|
||||
static void |
||||
cubic_to (hb_draw_funcs_t *dfuncs, |
||||
cairo_t *cr, |
||||
hb_draw_state_t *st, |
||||
float control1_x, float control1_y, |
||||
float control2_x, float control2_y, |
||||
float to_x, float to_y, |
||||
void *) |
||||
{ |
||||
print ("cubic to %f %f %f %f %f %f", |
||||
(double) control1_x, (double) control1_y, |
||||
(double) control2_x, (double) control2_y, |
||||
(double) to_x, (double) to_y); |
||||
cairo_curve_to (cr, |
||||
(double) control1_x, (double) control1_y, |
||||
(double) control2_x, (double) control2_y, |
||||
(double) to_x, (double) to_y); |
||||
} |
||||
|
||||
static void |
||||
close_path (hb_draw_funcs_t *dfuncs, |
||||
cairo_t *cr, |
||||
hb_draw_state_t *st, |
||||
void *) |
||||
{ |
||||
print ("close path"); |
||||
cairo_close_path (cr); |
||||
} |
||||
|
||||
|
||||
static hb_draw_funcs_t * |
||||
get_cairo_draw_funcs (void) |
||||
{ |
||||
static hb_draw_funcs_t *funcs; |
||||
|
||||
if (!funcs) |
||||
{ |
||||
funcs = hb_draw_funcs_create (); |
||||
hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) move_to, nullptr, nullptr); |
||||
hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) line_to, nullptr, nullptr); |
||||
hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) cubic_to, nullptr, nullptr); |
||||
hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) close_path, nullptr, nullptr); |
||||
} |
||||
|
||||
return funcs; |
||||
} |
||||
|
||||
#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC |
||||
|
||||
static void |
||||
push_transform (hb_paint_funcs_t *funcs, |
||||
void *paint_data, |
||||
float xx, float yx, |
||||
float xy, float yy, |
||||
float dx, float dy, |
||||
void *user_data) |
||||
{ |
||||
cairo_t *cr = (cairo_t *)paint_data; |
||||
cairo_matrix_t m; |
||||
|
||||
print ("start transform %f %f %f %f %f %f", |
||||
(double) xx, (double) yx, |
||||
(double) xy, (double) yy, |
||||
(double) dx, (double) dy); |
||||
level++; |
||||
|
||||
cairo_save (cr); |
||||
cairo_matrix_init (&m, (double) xx, (double) yx, |
||||
(double) xy, (double) yy, |
||||
(double) dx, (double) dy); |
||||
cairo_transform (cr, &m); |
||||
} |
||||
|
||||
static void |
||||
pop_transform (hb_paint_funcs_t *funcs, |
||||
void *paint_data, |
||||
void *user_data) |
||||
{ |
||||
cairo_t *cr = (cairo_t *)paint_data; |
||||
|
||||
cairo_restore (cr); |
||||
|
||||
level--; |
||||
print ("end transform"); |
||||
} |
||||
|
||||
static void |
||||
push_clip_glyph (hb_paint_funcs_t *funcs, |
||||
void *paint_data, |
||||
hb_codepoint_t glyph, |
||||
hb_font_t *font, |
||||
void *user_data) |
||||
{ |
||||
cairo_t *cr = (cairo_t *)paint_data; |
||||
|
||||
print ("start clip glyph %u", glyph); |
||||
level++; |
||||
|
||||
cairo_save (cr); |
||||
cairo_new_path (cr); |
||||
hb_font_draw_glyph (font, glyph, get_cairo_draw_funcs (), cr); |
||||
cairo_close_path (cr); |
||||
cairo_clip (cr); |
||||
} |
||||
|
||||
static void |
||||
push_clip_rectangle (hb_paint_funcs_t *funcs, |
||||
void *paint_data, |
||||
float xmin, float ymin, float xmax, float ymax, |
||||
void *user_data) |
||||
{ |
||||
cairo_t *cr = (cairo_t *)paint_data; |
||||
|
||||
print ("start clip rectangle %f %f %f %f", |
||||
(double) xmin, (double) ymin, |
||||
(double) xmax, (double) ymax); |
||||
level++; |
||||
|
||||
cairo_save (cr); |
||||
cairo_rectangle (cr, |
||||
(double) xmin, (double) ymin, |
||||
(double) (xmax - xmin), (double) (ymax - ymin)); |
||||
cairo_clip (cr); |
||||
} |
||||
|
||||
static void |
||||
pop_clip (hb_paint_funcs_t *funcs, |
||||
void *paint_data, |
||||
void *user_data) |
||||
{ |
||||
cairo_t *cr = (cairo_t *)paint_data; |
||||
|
||||
cairo_restore (cr); |
||||
|
||||
level--; |
||||
print ("end clip"); |
||||
} |
||||
|
||||
static void |
||||
push_group (hb_paint_funcs_t *funcs, |
||||
void *paint_data, |
||||
void *user_data) |
||||
{ |
||||
cairo_t *cr = (cairo_t *)paint_data; |
||||
|
||||
print ("push group"); |
||||
level++; |
||||
|
||||
cairo_save (cr); |
||||
cairo_push_group (cr); |
||||
} |
||||
|
||||
static void |
||||
pop_group (hb_paint_funcs_t *funcs, |
||||
void *paint_data, |
||||
hb_paint_composite_mode_t mode, |
||||
void *user_data) |
||||
{ |
||||
cairo_t *cr = (cairo_t *)paint_data; |
||||
|
||||
cairo_pop_group_to_source (cr); |
||||
cairo_set_operator (cr, hb_paint_composite_mode_to_cairo (mode)); |
||||
cairo_paint (cr); |
||||
|
||||
cairo_restore (cr); |
||||
|
||||
level--; |
||||
print ("pop group mode %d", mode); |
||||
} |
||||
|
||||
static void |
||||
paint_color (hb_paint_funcs_t *funcs, |
||||
void *paint_data, |
||||
hb_bool_t use_foreground, |
||||
hb_color_t color, |
||||
void *user_data) |
||||
{ |
||||
cairo_t *cr = (cairo_t *)paint_data; |
||||
|
||||
print ("solid (fg %d) %d %d %d %d", |
||||
use_foreground, |
||||
hb_color_get_red (color), |
||||
hb_color_get_green (color), |
||||
hb_color_get_blue (color), |
||||
hb_color_get_alpha (color)); |
||||
|
||||
cairo_set_source_rgba (cr, |
||||
hb_color_get_red (color) / 255., |
||||
hb_color_get_green (color) / 255., |
||||
hb_color_get_blue (color) / 255., |
||||
hb_color_get_alpha (color) / 255.); |
||||
cairo_paint (cr); |
||||
} |
||||
|
||||
static hb_bool_t |
||||
paint_image (hb_paint_funcs_t *funcs, |
||||
void *paint_data, |
||||
hb_blob_t *blob, |
||||
unsigned width, |
||||
unsigned height, |
||||
hb_tag_t format, |
||||
float slant, |
||||
hb_glyph_extents_t *extents, |
||||
void *user_data) |
||||
{ |
||||
cairo_t *cr = (cairo_t *)paint_data; |
||||
|
||||
char buf[5] = { 0, }; |
||||
|
||||
hb_tag_to_string (format, buf); |
||||
print ("image type '%s' size %u %u slant %f extents %d %d %d %d", |
||||
buf, width, height, (double) slant, |
||||
extents->x_bearing, extents->y_bearing, extents->width, extents->height); |
||||
|
||||
return hb_cairo_paint_glyph_image (cr, blob, width, height, format, slant, extents); |
||||
} |
||||
|
||||
static void |
||||
print_color_line (hb_color_line_t *color_line) |
||||
{ |
||||
hb_color_stop_t *stops; |
||||
unsigned int len; |
||||
|
||||
len = hb_color_line_get_color_stops (color_line, 0, NULL, NULL); |
||||
stops = (hb_color_stop_t *)alloca (len * sizeof (hb_color_stop_t)); |
||||
hb_color_line_get_color_stops (color_line, 0, &len, stops); |
||||
|
||||
print ("colors extend %d", hb_color_line_get_extend (color_line)); |
||||
level += 1; |
||||
for (unsigned int i = 0; i < len; i++) |
||||
print ("%f (fg %d) %d %d %d %d", |
||||
(double) stops[i].offset, |
||||
stops[i].is_foreground, |
||||
hb_color_get_red (stops[i].color), |
||||
hb_color_get_green (stops[i].color), |
||||
hb_color_get_blue (stops[i].color), |
||||
hb_color_get_alpha (stops[i].color)); |
||||
level -= 1; |
||||
} |
||||
|
||||
static void |
||||
paint_linear_gradient (hb_paint_funcs_t *funcs, |
||||
void *paint_data, |
||||
hb_color_line_t *color_line, |
||||
float x0, float y0, |
||||
float x1, float y1, |
||||
float x2, float y2, |
||||
void *user_data) |
||||
{ |
||||
cairo_t *cr = (cairo_t *)paint_data; |
||||
|
||||
print ("linear gradient"); |
||||
level += 1; |
||||
print ("p0 %f %f", (double) x0, (double) y0); |
||||
print ("p1 %f %f", (double) x1, (double) y1); |
||||
print ("p2 %f %f", (double) x2, (double) y2); |
||||
|
||||
print_color_line (color_line); |
||||
level -= 1; |
||||
|
||||
hb_cairo_paint_linear_gradient (cr, color_line, x0, y0, x1, y1, x2, y2); |
||||
} |
||||
|
||||
static void |
||||
paint_radial_gradient (hb_paint_funcs_t *funcs, |
||||
void *paint_data, |
||||
hb_color_line_t *color_line, |
||||
float x0, float y0, float r0, |
||||
float x1, float y1, float r1, |
||||
void *user_data) |
||||
{ |
||||
cairo_t *cr = (cairo_t *)paint_data; |
||||
|
||||
print ("radial gradient"); |
||||
level += 1; |
||||
print ("p0 %f %f radius %f", (double) x0, (double) y0, (double) r0); |
||||
print ("p1 %f %f radius %f", (double) x1, (double) y1, (double) r1); |
||||
|
||||
print_color_line (color_line); |
||||
level -= 1; |
||||
|
||||
hb_cairo_paint_radial_gradient (cr, color_line, x0, y0, r0, x1, y1, r1); |
||||
} |
||||
|
||||
static void |
||||
paint_sweep_gradient (hb_paint_funcs_t *funcs, |
||||
void *paint_data, |
||||
hb_color_line_t *color_line, |
||||
float x0, float y0, |
||||
float start_angle, float end_angle, |
||||
void *user_data) |
||||
{ |
||||
cairo_t *cr = (cairo_t *)paint_data; |
||||
|
||||
print ("sweep gradient"); |
||||
level++; |
||||
print ("center %f %f", (double) x0, (double) y0); |
||||
print ("angles %f %f", (double) start_angle, (double) end_angle); |
||||
|
||||
print_color_line (color_line); |
||||
level--; |
||||
|
||||
hb_cairo_paint_sweep_gradient (cr, color_line, x0, y0, start_angle, end_angle); |
||||
} |
||||
|
||||
static hb_paint_funcs_t * |
||||
get_cairo_paint_funcs () |
||||
{ |
||||
static hb_paint_funcs_t *funcs; |
||||
|
||||
if (!funcs) |
||||
{ |
||||
funcs = hb_paint_funcs_create (); |
||||
|
||||
hb_paint_funcs_set_push_transform_func (funcs, push_transform, nullptr, nullptr); |
||||
hb_paint_funcs_set_pop_transform_func (funcs, pop_transform, nullptr, nullptr); |
||||
hb_paint_funcs_set_push_clip_glyph_func (funcs, push_clip_glyph, nullptr, nullptr); |
||||
hb_paint_funcs_set_push_clip_rectangle_func (funcs, push_clip_rectangle, nullptr, nullptr); |
||||
hb_paint_funcs_set_pop_clip_func (funcs, pop_clip, nullptr, nullptr); |
||||
hb_paint_funcs_set_push_group_func (funcs, push_group, nullptr, nullptr); |
||||
hb_paint_funcs_set_pop_group_func (funcs, pop_group, nullptr, nullptr); |
||||
hb_paint_funcs_set_color_func (funcs, paint_color, nullptr, nullptr); |
||||
hb_paint_funcs_set_image_func (funcs, paint_image, nullptr, nullptr); |
||||
hb_paint_funcs_set_linear_gradient_func (funcs, paint_linear_gradient, nullptr, nullptr); |
||||
hb_paint_funcs_set_radial_gradient_func (funcs, paint_radial_gradient, nullptr, nullptr); |
||||
hb_paint_funcs_set_sweep_gradient_func (funcs, paint_sweep_gradient, nullptr, nullptr); |
||||
} |
||||
|
||||
return funcs; |
||||
} |
||||
|
||||
#endif |
||||
|
||||
#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC |
||||
static cairo_status_t |
||||
render_color_glyph (cairo_scaled_font_t *scaled_font, |
||||
unsigned long glyph, |
||||
cairo_t *cr, |
||||
cairo_text_extents_t *extents); |
||||
#endif |
||||
|
||||
static cairo_status_t |
||||
render_glyph (cairo_scaled_font_t *scaled_font, |
||||
unsigned long glyph, |
||||
cairo_t *cr, |
||||
cairo_text_extents_t *extents) |
||||
{ |
||||
#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC |
||||
static char *p = getenv ("HB_DRAW"); |
||||
if (p && atoi (p) >= 2) |
||||
return render_color_glyph (scaled_font, glyph, cr, extents); |
||||
#endif |
||||
|
||||
hb_font_t *font = (hb_font_t *) (cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), |
||||
&_hb_font_cairo_user_data_key)); |
||||
|
||||
hb_position_t x_scale, y_scale; |
||||
hb_font_get_scale (font, &x_scale, &y_scale); |
||||
cairo_scale (cr, +1./x_scale, -1./y_scale); |
||||
|
||||
hb_font_draw_glyph (font, glyph, get_cairo_draw_funcs (), cr); |
||||
cairo_fill (cr); |
||||
|
||||
return CAIRO_STATUS_SUCCESS; |
||||
} |
||||
|
||||
#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC |
||||
|
||||
static cairo_status_t |
||||
render_color_glyph (cairo_scaled_font_t *scaled_font, |
||||
unsigned long glyph, |
||||
cairo_t *cr, |
||||
cairo_text_extents_t *extents) |
||||
{ |
||||
hb_font_t *font = (hb_font_t *) (cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), |
||||
&_hb_font_cairo_user_data_key)); |
||||
unsigned int palette = 0; |
||||
#ifdef CAIRO_COLOR_PALETTE_DEFAULT |
||||
cairo_font_options_t *options = cairo_font_options_create (); |
||||
cairo_scaled_font_get_font_options (scaled_font, options); |
||||
palette = cairo_font_options_get_color_palette (options); |
||||
cairo_font_options_destroy (options); |
||||
#endif |
||||
|
||||
hb_color_t color = HB_COLOR (0, 0, 0, 255); |
||||
cairo_pattern_t *pattern = cairo_get_source (cr); |
||||
|
||||
if (cairo_pattern_get_type (pattern) == CAIRO_PATTERN_TYPE_SOLID) |
||||
{ |
||||
double r, g, b, a; |
||||
cairo_pattern_get_rgba (pattern, &r, &g, &b, &a); |
||||
color = HB_COLOR ((int)(b * 255.), (int)(g * 255.), (int) (r * 255.), (int)(a * 255.)); |
||||
} |
||||
|
||||
hb_position_t x_scale, y_scale; |
||||
hb_font_get_scale (font, &x_scale, &y_scale); |
||||
cairo_scale (cr, +1./x_scale, -1./y_scale); |
||||
|
||||
if (getenv ("HB_PAINT_DEBUG")) |
||||
{ |
||||
debug = atoi (getenv ("HB_PAINT_DEBUG")); |
||||
level = 0; |
||||
} |
||||
|
||||
hb_font_paint_glyph (font, glyph, get_cairo_paint_funcs (), cr, palette, color); |
||||
|
||||
return CAIRO_STATUS_SUCCESS; |
||||
} |
||||
|
||||
#endif |
||||
|
||||
static inline cairo_font_face_t * |
||||
helper_cairo_create_user_font_face (const font_options_t *font_opts) |
||||
{ |
||||
cairo_font_face_t *cairo_face = cairo_user_font_face_create (); |
||||
|
||||
cairo_font_face_set_user_data (cairo_face, |
||||
&_hb_font_cairo_user_data_key, |
||||
hb_font_reference (font_opts->font), |
||||
(cairo_destroy_func_t) hb_font_destroy); |
||||
|
||||
cairo_user_font_face_set_render_glyph_func (cairo_face, render_glyph); |
||||
|
||||
#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC |
||||
hb_face_t *face = hb_font_get_face (font_opts->font); |
||||
if (hb_ot_color_has_png (face) || hb_ot_color_has_layers (face) || hb_ot_color_has_paint (face)) |
||||
{ |
||||
cairo_user_font_face_set_render_color_glyph_func (cairo_face, render_color_glyph); |
||||
} |
||||
#endif |
||||
|
||||
return cairo_face; |
||||
} |
||||
|
||||
static inline bool |
||||
helper_cairo_user_font_face_has_data (cairo_font_face_t *font_face) |
||||
{ |
||||
return cairo_font_face_get_user_data (font_face, &_hb_font_cairo_user_data_key); |
||||
} |
||||
|
||||
static inline bool |
||||
helper_cairo_user_scaled_font_has_color (cairo_scaled_font_t *scaled_font) |
||||
{ |
||||
/* Ignoring SVG for now, since we cannot render it. */ |
||||
hb_font_t *font = (hb_font_t *) (cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), |
||||
&_hb_font_cairo_user_data_key)); |
||||
hb_face_t *face = hb_font_get_face (font); |
||||
return hb_ot_color_has_png (face) || hb_ot_color_has_layers (face) || hb_ot_color_has_paint (face); |
||||
} |
||||
|
||||
#endif |
Loading…
Reference in new issue