Render color glyphs as source, not as mask

Use paint instead of show_glyphs for color glyphs. To avoid
overhead, we only check for color glyphs if the font is known
to contain any. Paint clusters containing only color glyphs
and rewrite the inputs to remove the handled clusters and
glyphs.
This commit is contained in:
Matthias Clasen 2017-06-29 22:57:02 -04:00 committed by Behdad Esfahbod
parent 6a8a25cc47
commit db14d6d707

View file

@ -508,7 +508,7 @@ cairo_surface_create_similar (cairo_surface_t *other,
if (unlikely (! CAIRO_CONTENT_VALID (content)))
return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_CONTENT);
if (unlikely (other->status))
if (unlikely (other->status))
return _cairo_surface_create_in_error (other->status);
/* We inherit the device scale, so create a larger surface */
@ -2521,6 +2521,240 @@ cairo_surface_has_show_text_glyphs (cairo_surface_t *surface)
}
slim_hidden_def (cairo_surface_has_show_text_glyphs);
#define GLYPH_CACHE_SIZE 64
static cairo_bool_t
has_color_glyphs (cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_scaled_glyph_t **glyph_cache)
{
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
int i;
cairo_bool_t has_color = FALSE;
_cairo_scaled_font_freeze_cache (scaled_font);
for (i = 0; i < num_glyphs; i++) {
cairo_scaled_glyph_t *scaled_glyph;
unsigned long glyph_index = glyphs[i].index;
int cache_index = glyph_index % GLYPH_CACHE_SIZE;
scaled_glyph = glyph_cache[cache_index];
if (scaled_glyph == NULL ||
_cairo_scaled_glyph_index (scaled_glyph) != glyph_index) {
status = _cairo_scaled_glyph_lookup (scaled_font,
glyphs[i].index,
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
if (unlikely (status)) {
status = _cairo_scaled_font_set_error (scaled_font, status);
break;
}
glyph_cache[cache_index] = scaled_glyph;
}
if ((scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) != 0) {
has_color = TRUE;
break;
}
}
_cairo_scaled_font_thaw_cache (scaled_font);
return has_color;
}
static cairo_int_status_t
composite_color_glyphs (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
char *utf8,
int *utf8_len,
cairo_glyph_t *glyphs,
int *num_glyphs,
cairo_text_cluster_t *clusters,
int *num_clusters,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip,
cairo_scaled_glyph_t **glyph_cache)
{
cairo_int_status_t status;
int i, j;
cairo_scaled_glyph_t *scaled_glyph;
cairo_image_surface_t *glyph_surface;
cairo_pattern_t *pattern;
cairo_matrix_t matrix;
unsigned long glyph_index;
int cache_index;
int remaining_clusters = 0;
int remaining_glyphs = 0;
int remaining_bytes = 0;
int glyph_pos = 0;
int byte_pos = 0;
status = CAIRO_STATUS_SUCCESS;
_cairo_scaled_font_freeze_cache (scaled_font);
if (clusters) {
if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
glyph_pos = *num_glyphs - 1;
for (i = 0; i < *num_clusters; i++) {
cairo_bool_t skip_cluster = FALSE;
for (j = 0; j < clusters[i].num_glyphs; j++) {
int gp;
if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
gp = glyph_pos - j;
else
gp = glyph_pos + j;
glyph_index = glyphs[gp].index;
cache_index = glyph_index % GLYPH_CACHE_SIZE;
scaled_glyph = glyph_cache[cache_index];
if (scaled_glyph == NULL ||
_cairo_scaled_glyph_index (scaled_glyph) != glyph_index) {
status = _cairo_scaled_glyph_lookup (scaled_font,
glyph_index,
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
if (unlikely (status)) {
status = _cairo_scaled_font_set_error (scaled_font, status);
goto UNLOCK;
}
glyph_cache[cache_index] = scaled_glyph;
}
if ((scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) == 0) {
skip_cluster = TRUE;
break;
}
}
if (skip_cluster) {
memmove (utf8 + remaining_bytes, utf8 + byte_pos, clusters[i].num_bytes);
remaining_bytes += clusters[i].num_bytes;
byte_pos += clusters[i].num_bytes;
for (j = 0; j < clusters[i].num_glyphs; j++, remaining_glyphs++) {
if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
glyphs[*num_glyphs - 1 - remaining_glyphs] = glyphs[glyph_pos--];
else
glyphs[remaining_glyphs] = glyphs[glyph_pos++];
}
clusters[remaining_clusters++] = clusters[i];
continue;
}
for (j = 0; j < clusters[i].num_glyphs; j++) {
int gp;
if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
gp = glyph_pos - j;
else
gp = glyph_pos + j;
glyph_index = glyphs[gp].index;
cache_index = glyph_index % GLYPH_CACHE_SIZE;
scaled_glyph = glyph_cache[cache_index];
if (scaled_glyph == NULL ||
_cairo_scaled_glyph_index (scaled_glyph) != glyph_index) {
status = _cairo_scaled_glyph_lookup (scaled_font,
glyph_index,
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
if (unlikely (status)) {
status = _cairo_scaled_font_set_error (scaled_font, status);
goto UNLOCK;
}
glyph_cache[cache_index] = scaled_glyph;
}
glyph_surface = scaled_glyph->color_surface;
if (glyph_surface->width && glyph_surface->height) {
int x, y;
/* round glyph locations to the nearest pixels */
/* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
x = _cairo_lround (glyphs[gp].x - glyph_surface->base.device_transform.x0);
y = _cairo_lround (glyphs[gp].y - glyph_surface->base.device_transform.y0);
pattern = cairo_pattern_create_for_surface ((cairo_surface_t *)glyph_surface);
cairo_matrix_init_translate (&matrix, - x, - y);
cairo_pattern_set_matrix (pattern, &matrix);
status = surface->backend->paint (surface, op, pattern, clip);
if (unlikely (status))
goto UNLOCK;
}
}
if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
glyph_pos -= clusters[i].num_glyphs;
else
glyph_pos += clusters[i].num_glyphs;
byte_pos += clusters[i].num_bytes;
}
if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
memmove (utf8, utf8 + *utf8_len - remaining_bytes, remaining_bytes);
*utf8_len = remaining_bytes;
*num_glyphs = remaining_glyphs;
*num_clusters = remaining_clusters;
} else {
for (glyph_pos = 0; glyph_pos < *num_glyphs; glyph_pos++) {
glyph_index = glyphs[glyph_pos].index;
cache_index = glyph_index % GLYPH_CACHE_SIZE;
scaled_glyph = glyph_cache[cache_index];
if (scaled_glyph == NULL ||
_cairo_scaled_glyph_index (scaled_glyph) != glyph_index) {
status = _cairo_scaled_glyph_lookup (scaled_font,
glyph_index,
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
if (unlikely (status)) {
status = _cairo_scaled_font_set_error (scaled_font, status);
goto UNLOCK;
}
glyph_cache[cache_index] = scaled_glyph;
}
if ((scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) == 0) {
glyphs[remaining_glyphs++] = glyphs[glyph_pos];
continue;
}
glyph_surface = scaled_glyph->color_surface;
if (glyph_surface->width && glyph_surface->height) {
int x, y;
/* round glyph locations to the nearest pixels */
/* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
x = _cairo_lround (glyphs[glyph_pos].x - glyph_surface->base.device_transform.x0);
y = _cairo_lround (glyphs[glyph_pos].y - glyph_surface->base.device_transform.y0);
pattern = cairo_pattern_create_for_surface ((cairo_surface_t *)glyph_surface);
cairo_matrix_init_translate (&matrix, - x, - y);
cairo_pattern_set_matrix (pattern, &matrix);
status = surface->backend->paint (surface, op, pattern, clip);
if (unlikely (status))
goto UNLOCK;
}
}
*num_glyphs = remaining_glyphs;
}
UNLOCK:
_cairo_scaled_font_thaw_cache (scaled_font);
return status;
}
/* Note: the backends may modify the contents of the glyph array as long as
* they do not return %CAIRO_INT_STATUS_UNSUPPORTED. This makes it possible to
* avoid copying the array again and again, and edit it in-place.
@ -2577,6 +2811,27 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface,
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (_cairo_scaled_font_has_color_glyphs (scaled_font)) {
cairo_scaled_glyph_t *glyph_cache[GLYPH_CACHE_SIZE];
memset (glyph_cache, 0, sizeof (glyph_cache));
if (has_color_glyphs (glyphs, num_glyphs, scaled_font, glyph_cache)) {
status = composite_color_glyphs (surface, op,
source,
(char *)utf8, &utf8_len,
glyphs, &num_glyphs,
(cairo_text_cluster_t *)clusters, &num_clusters, cluster_flags,
scaled_font,
clip,
glyph_cache);
if (unlikely (status))
goto DONE;
if (num_glyphs == 0)
goto DONE;
}
}
/* The logic here is duplicated in _cairo_analysis_surface show_glyphs and
* show_text_glyphs. Keep in synch. */
if (clusters) {
@ -2627,6 +2882,7 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface,
}
}
DONE:
if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
surface->is_clear = FALSE;
surface->serial++;