[cairo-commit] 7 commits - src/cairo-ft-font.c src/cairoint.h src/cairo-scaled-font.c src/cairo-scaled-font-private.h src/cairo-surface.c

Behdad Esfahbod behdad at kemper.freedesktop.org
Sat Jul 29 15:22:45 UTC 2017


 src/cairo-ft-font.c             |   87 ++++++++++++----
 src/cairo-scaled-font-private.h |    3 
 src/cairo-scaled-font.c         |   30 +++++
 src/cairo-surface.c             |  209 +++++++++++++++++++++++++++++++++++++++-
 src/cairoint.h                  |   11 +-
 5 files changed, 317 insertions(+), 23 deletions(-)

New commits:
commit 7a1e378466971d26a3f6d3c9437744bc5ffef1eb
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sat Jul 29 16:20:21 2017 +0100

    Fix color font support infinite-loop with empty glyphs
    
    Ouch! But it all works now! Only took five years to merge this feature.
    Thanks Matthias Clasen for working out a real patch from my initial
    sketch.

diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index 3bf5381a..e1070f4c 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -1438,6 +1438,7 @@ _render_glyph_outline (FT_Face                    face,
 
 	(*surface) = (cairo_image_surface_t *)
 	    cairo_image_surface_create_for_data (NULL, format, 0, 0, 0);
+	pixman_image_set_component_alpha ((*surface)->pixman_image, TRUE);
 	if ((*surface)->base.status)
 	    return (*surface)->base.status;
     } else {
@@ -1662,6 +1663,10 @@ _transform_glyph_bitmap (cairo_matrix_t         * shape,
 
     old_image = (*surface);
     (*surface) = (cairo_image_surface_t *)image;
+
+    /* Note: we converted subpixel-rendered RGBA images to grayscale,
+     * so, no need to copy component alpha to new image. */
+
     cairo_surface_destroy (&old_image->base);
 
     cairo_surface_set_device_offset (&(*surface)->base,
@@ -2309,16 +2314,6 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
     }
 
 #ifdef FT_LOAD_COLOR
-    /* Color-glyph support:
-     *
-     * This flags needs plumbing through fontconfig (does it?), and
-     * maybe we should cache color and grayscale bitmaps separately
-     * such that users of the font (ie. the surface) can choose which
-     * version to use based on target content type.
-     *
-     * Moreover, none of our backends and compositors currently support
-     * color glyphs.  As such, this is currently disabled.
-     */
     load_flags |= FT_LOAD_COLOR;
 #endif
 
@@ -2469,7 +2464,7 @@ LOAD:
 	if (unlikely (status))
 	    goto FAIL;
 
-        if ((pixman_image_get_format (surface->pixman_image) == PIXMAN_a8r8g8b8) &&
+        if (pixman_image_get_format (surface->pixman_image) == PIXMAN_a8r8g8b8 &&
             !pixman_image_get_component_alpha (surface->pixman_image)) {
             _cairo_scaled_glyph_set_color_surface (scaled_glyph,
                                                    &scaled_font->base,
commit 5e3350e4d1df6aeb4333cb1e4134feb22280735c
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Fri Jun 30 21:17:43 2017 -0400

    Simplify things a bit
    
    Do away with the separate check for color glyphs - we can just
    do the filtering, and if there are no color glyphs, it is a
    no-op. As long as we only do this for fonts with color glyphs,
    it should be fine.
    
    Reduce repetition in composite_color_glyphs by breaking out
    some helper functions.

diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index d0d1cad6..c524fe4b 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -2523,46 +2523,62 @@ 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)
+static inline cairo_int_status_t
+ensure_scaled_glyph (cairo_scaled_font_t   *scaled_font,
+                     cairo_scaled_glyph_t **glyph_cache,
+                     cairo_glyph_t         *glyph,
+                     cairo_scaled_glyph_t **scaled_glyph)
 {
-    cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
-    int i;
-    cairo_bool_t has_color = FALSE;
+    int cache_index;
+    cairo_int_status_t status;
 
-    _cairo_scaled_font_freeze_cache (scaled_font);
+    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);
+
+        glyph_cache[cache_index] = *scaled_glyph;
+    }
 
-    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;
-            }
+    return status;
+}
 
-            glyph_cache[cache_index] = scaled_glyph;
-        }
+static inline cairo_int_status_t
+composite_one_color_glyph (cairo_surface_t       *surface,
+                           cairo_operator_t       op,
+                           const cairo_pattern_t *source,
+                           const cairo_clip_t    *clip,
+                           cairo_glyph_t         *glyph,
+                           cairo_scaled_glyph_t  *scaled_glyph)
+{
+    cairo_int_status_t status;
+    cairo_image_surface_t *glyph_surface;
+    cairo_pattern_t *pattern;
+    cairo_matrix_t matrix;
 
-        if ((scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) != 0) {
-            has_color = TRUE;
-            break;
-        }
+    status = CAIRO_STATUS_SUCCESS;
+
+    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 (glyph->x - glyph_surface->base.device_transform.x0);
+	y = _cairo_lround (glyph->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);
     }
 
-    _cairo_scaled_font_thaw_cache (scaled_font);
-    return has_color;
+    return status;
 }
 
 static cairo_int_status_t
@@ -2577,22 +2593,20 @@ composite_color_glyphs (cairo_surface_t             *surface,
 	                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)
+                        const cairo_clip_t          *clip)
 {
     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;
+    int gp;
+    cairo_scaled_glyph_t *glyph_cache[GLYPH_CACHE_SIZE];
+
+    memset (glyph_cache, 0, sizeof (glyph_cache));
 
     status = CAIRO_STATUS_SUCCESS;
 
@@ -2607,28 +2621,16 @@ composite_color_glyphs (cairo_surface_t             *surface,
             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;
-                }
+                status = ensure_scaled_glyph (scaled_font, glyph_cache,
+                                              &glyphs[gp], &scaled_glyph);
+                if (unlikely (status))
+                    goto UNLOCK;
+
                 if ((scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) == 0) {
                     skip_cluster = TRUE;
                     break;
@@ -2650,43 +2652,20 @@ composite_color_glyphs (cairo_surface_t             *surface,
             }
 
             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;
-                }
+                status = ensure_scaled_glyph (scaled_font, glyph_cache,
+                                              &glyphs[gp], &scaled_glyph);
+                if (unlikely (status))
+                    goto UNLOCK;
+
+                status = composite_one_color_glyph (surface, op, source, clip,
+                                                    &glyphs[gp], scaled_glyph);
+                if (unlikely (status))
+                    goto UNLOCK;
             }
 
             if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
@@ -2707,43 +2686,20 @@ composite_color_glyphs (cairo_surface_t             *surface,
     } 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;
-           }
+           status = ensure_scaled_glyph (scaled_font, glyph_cache,
+                                         &glyphs[glyph_pos], &scaled_glyph);
+           if (unlikely (status))
+               goto UNLOCK;
 
            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;
-            }
+           status = composite_one_color_glyph (surface, op, source, clip,
+                                               &glyphs[glyph_pos], scaled_glyph);
+           if (unlikely (status))
+               goto UNLOCK;
         }
 
         *num_glyphs = remaining_glyphs;
@@ -2812,24 +2768,19 @@ _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;
-        }
+        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);
+
+        if (unlikely (status))
+            goto DONE;
+
+        if (num_glyphs == 0)
+            goto DONE;
     }
 
     /* The logic here is duplicated in _cairo_analysis_surface show_glyphs and
commit db14d6d70759df221d6aaee9a1dc0354c39f1aa3
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Thu Jun 29 22:57:02 2017 -0400

    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.

diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index fc5f876d..d0d1cad6 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -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++;
commit 6a8a25cc4792eca774fa6d0857229659adbe063b
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Fri Apr 8 13:20:56 2016 -0400

    Implement has_color_glyphs for freetype
    
    This information is available from the FT_Face using the
    FT_HAS_COLOR macro. We cache the value in the unscaled_font
    object as soon as we have an FT_Face.

diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index 4b9af2f8..3bf5381a 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -169,6 +169,9 @@ struct _cairo_ft_unscaled_font {
     cairo_matrix_t current_shape;
     FT_Matrix Current_Shape;
 
+    unsigned int have_color_set : 1;
+    unsigned int have_color     : 1;  /* true if the font contains color glyphs */
+
     cairo_mutex_t mutex;
     int lock_count;
 
@@ -426,6 +429,9 @@ _cairo_ft_unscaled_font_init (cairo_ft_unscaled_font_t *unscaled,
     if (from_face) {
 	unscaled->from_face = TRUE;
 	_cairo_ft_unscaled_font_init_key (unscaled, TRUE, NULL, 0, face);
+
+        unscaled->have_color = FT_HAS_COLOR (face) != 0;
+        unscaled->have_color_set = TRUE;
     } else {
 	char *filename_copy;
 
@@ -437,6 +443,8 @@ _cairo_ft_unscaled_font_init (cairo_ft_unscaled_font_t *unscaled,
 	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
 	_cairo_ft_unscaled_font_init_key (unscaled, FALSE, filename_copy, id, NULL);
+
+        unscaled->have_color_set = FALSE;
     }
 
     unscaled->have_scale = FALSE;
@@ -704,6 +712,9 @@ _cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled)
 
     unscaled->face = face;
 
+    unscaled->have_color = FT_HAS_COLOR (face) != 0;
+    unscaled->have_color_set = TRUE;
+
     font_map->num_open_faces++;
 
     return face;
@@ -2826,6 +2837,20 @@ _cairo_ft_load_type1_data (void	            *abstract_font,
     return status;
 }
 
+static cairo_bool_t
+_cairo_ft_has_color_glyphs (void *scaled)
+{
+    cairo_ft_unscaled_font_t *unscaled = ((cairo_ft_scaled_font_t *)scaled)->unscaled;
+
+    if (!unscaled->have_color_set) {
+        FT_Face face;
+        face = _cairo_ft_unscaled_font_lock_face (unscaled);
+        _cairo_ft_unscaled_font_unlock_face (unscaled);
+    }
+
+    return unscaled->have_color;
+}
+
 static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend = {
     CAIRO_FONT_TYPE_FT,
     _cairo_ft_scaled_font_fini,
@@ -2836,7 +2861,8 @@ static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend = {
     _cairo_ft_index_to_ucs4,
     _cairo_ft_is_synthetic,
     _cairo_index_to_glyph_name,
-    _cairo_ft_load_type1_data
+    _cairo_ft_load_type1_data,
+    _cairo_ft_has_color_glyphs
 };
 
 /* #cairo_ft_font_face_t */
commit 66228456531c8a80f3985160bf1013248e433681
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Fri Apr 8 13:21:47 2016 -0400

    Expose 'has color glyphs' as a scaled font property
    
    This information will be used in subsequent commits to quickly
    decide that we won't try to handle glpyhs as masks. Implementing
    the new has_color_glyphs vfunc is optional - only backends that
    support color glyphs need to implement it.

diff --git a/src/cairo-scaled-font-private.h b/src/cairo-scaled-font-private.h
index 6ce6bb6d..317d2119 100644
--- a/src/cairo-scaled-font-private.h
+++ b/src/cairo-scaled-font-private.h
@@ -178,6 +178,8 @@ _cairo_scaled_glyph_attach_private (cairo_scaled_glyph_t *scaled_glyph,
 				   void (*destroy) (cairo_scaled_glyph_private_t *,
 						    cairo_scaled_glyph_t *,
 						    cairo_scaled_font_t *));
+cairo_private cairo_bool_t
+_cairo_scaled_font_has_color_glyphs (cairo_scaled_font_t *scaled_font);
 
 CAIRO_END_DECLS
 
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index 665395b5..b8804903 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -3190,3 +3190,12 @@ cairo_scaled_font_get_font_options (cairo_scaled_font_t		*scaled_font,
     _cairo_font_options_init_copy (options, &scaled_font->options);
 }
 slim_hidden_def (cairo_scaled_font_get_font_options);
+
+cairo_bool_t
+_cairo_scaled_font_has_color_glyphs (cairo_scaled_font_t *scaled_font)
+{
+    if (scaled_font->backend != NULL && scaled_font->backend->has_color_glyphs != NULL)
+        return scaled_font->backend->has_color_glyphs (scaled_font);
+    else
+       return FALSE;
+}
diff --git a/src/cairoint.h b/src/cairoint.h
index a04a92e9..f6435592 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -608,6 +608,9 @@ struct _cairo_scaled_font_backend {
                            long                  offset,
                            unsigned char        *buffer,
                            unsigned long        *length);
+
+    cairo_bool_t
+    (*has_color_glyphs)   (void                 *scaled_font);
 };
 
 struct _cairo_font_face_backend {
commit 52b17c724260512873ab0731e2e2314bb0a15e4b
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Thu Jun 29 20:19:56 2017 -0400

    Support loading color glyphs with freetype
    
    Use the FT_LOAD_COLOR flag to instruct freetype to load embedded
    PNGs without converting them to grayscale. We always load both
    the color and regular surface when we are loading surfaces.

diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index 5b367e7b..4b9af2f8 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -2283,8 +2283,10 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
     load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
 
     if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0 &&
-	(info & CAIRO_SCALED_GLYPH_INFO_SURFACE) == 0)
+	(info & (CAIRO_SCALED_GLYPH_INFO_SURFACE |
+                 CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE)) == 0) {
 	load_flags |= FT_LOAD_NO_BITMAP;
+    }
 
     /*
      * Don't pass FT_LOAD_VERTICAL_LAYOUT to FT_Load_Glyph here as
@@ -2306,7 +2308,7 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
      * Moreover, none of our backends and compositors currently support
      * color glyphs.  As such, this is currently disabled.
      */
-    /* load_flags |= FT_LOAD_COLOR; */
+    load_flags |= FT_LOAD_COLOR;
 #endif
 
 
@@ -2420,7 +2422,8 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
 					 &fs_metrics);
     }
 
-    if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) {
+LOAD:
+    if (info & (CAIRO_SCALED_GLYPH_INFO_SURFACE | CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE)) {
 	cairo_image_surface_t	*surface;
 
 	if (!scaled_glyph_loaded) {
@@ -2448,18 +2451,39 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
 	    {
 		status = _transform_glyph_bitmap (&unscaled->current_shape,
 						  &surface);
-		if (unlikely (status))
-		    cairo_surface_destroy (&surface->base);
-	    }
+                if (unlikely (status))
+                    cairo_surface_destroy (&surface->base);
+            }
 	}
 	if (unlikely (status))
 	    goto FAIL;
 
-	_cairo_scaled_glyph_set_surface (scaled_glyph,
-					 &scaled_font->base,
-					 surface);
+        if ((pixman_image_get_format (surface->pixman_image) == PIXMAN_a8r8g8b8) &&
+            !pixman_image_get_component_alpha (surface->pixman_image)) {
+            _cairo_scaled_glyph_set_color_surface (scaled_glyph,
+                                                   &scaled_font->base,
+                                                   surface);
+        } else {
+            _cairo_scaled_glyph_set_surface (scaled_glyph,
+                                             &scaled_font->base,
+                                             surface);
+        }
     }
 
+#ifdef FT_LOAD_COLOR
+    if (((info & (CAIRO_SCALED_GLYPH_INFO_SURFACE | CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE)) != 0) &&
+        ((scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_SURFACE) == 0)) {
+        /*
+         * A kludge -- load again, without color.
+         * No need to load the metrics again, though
+         */
+	scaled_glyph_loaded = FALSE;
+        info &= ~CAIRO_SCALED_GLYPH_INFO_METRICS;
+        load_flags &= ~FT_LOAD_COLOR;
+        goto LOAD;
+    }
+#endif
+
     if (info & CAIRO_SCALED_GLYPH_INFO_PATH) {
 	cairo_path_fixed_t *path = NULL; /* hide compiler warning */
 
@@ -2467,7 +2491,7 @@ _cairo_ft_scaled_glyph_init (void			*abstract_font,
 	 * A kludge -- the above code will trash the outline,
 	 * so reload it. This will probably never occur though
 	 */
-	if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) {
+	if ((info & (CAIRO_SCALED_GLYPH_INFO_SURFACE | CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE)) != 0) {
 	    scaled_glyph_loaded = FALSE;
 	    load_flags |= FT_LOAD_NO_BITMAP;
 	}
commit 0d8859c66fb82a00147ab77d1971111306ca2272
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Thu Apr 7 20:38:09 2016 -0400

    Add support for color glyphs to cairo_scaled_glyph_t
    
    With this, glyphs can have either a surface that is expected
    to be used as mask, or a color_surface that should be used
    as source, or both.
    
    This will be used to support colored emoji glyphs that are
    stored as PNG images in OpenType fonts.

diff --git a/src/cairo-scaled-font-private.h b/src/cairo-scaled-font-private.h
index da7b3469..6ce6bb6d 100644
--- a/src/cairo-scaled-font-private.h
+++ b/src/cairo-scaled-font-private.h
@@ -141,6 +141,7 @@ struct _cairo_scaled_glyph {
     cairo_image_surface_t   *surface;		/* device-space image */
     cairo_path_fixed_t	    *path;		/* device-space outline */
     cairo_surface_t         *recording_surface;	/* device-space recording-surface */
+    cairo_image_surface_t   *color_surface;	/* device-space color image */
 
     const void		   *dev_private_key;
     void		   *dev_private;
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index dff30538..665395b5 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -224,6 +224,9 @@ _cairo_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
 	cairo_surface_finish (scaled_glyph->recording_surface);
 	cairo_surface_destroy (scaled_glyph->recording_surface);
     }
+
+    if (scaled_glyph->color_surface != NULL)
+	cairo_surface_destroy (&scaled_glyph->color_surface->base);
 }
 
 #define ZOMBIE 0
@@ -2830,6 +2833,24 @@ _cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph,
 	scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE;
 }
 
+void
+_cairo_scaled_glyph_set_color_surface (cairo_scaled_glyph_t *scaled_glyph,
+	                               cairo_scaled_font_t *scaled_font,
+	                               cairo_image_surface_t *surface)
+{
+    if (scaled_glyph->color_surface != NULL)
+	cairo_surface_destroy (&scaled_glyph->color_surface->base);
+
+    /* sanity check the backend glyph contents */
+    _cairo_debug_check_image_surface_is_defined (&surface->base);
+    scaled_glyph->color_surface = surface;
+
+    if (surface != NULL)
+	scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE;
+    else
+	scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE;
+}
+
 static cairo_bool_t
 _cairo_scaled_glyph_page_can_remove (const void *closure)
 {
diff --git a/src/cairoint.h b/src/cairoint.h
index 4fedf861..a04a92e9 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -477,7 +477,8 @@ typedef enum _cairo_scaled_glyph_info {
     CAIRO_SCALED_GLYPH_INFO_METRICS	 = (1 << 0),
     CAIRO_SCALED_GLYPH_INFO_SURFACE	 = (1 << 1),
     CAIRO_SCALED_GLYPH_INFO_PATH	 = (1 << 2),
-    CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE = (1 << 3)
+    CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE = (1 << 3),
+    CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE = (1 << 4)
 } cairo_scaled_glyph_info_t;
 
 typedef struct _cairo_scaled_font_subset {
@@ -1255,6 +1256,11 @@ _cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph,
                                            cairo_scaled_font_t *scaled_font,
                                            cairo_surface_t *recording_surface);
 
+cairo_private void
+_cairo_scaled_glyph_set_color_surface (cairo_scaled_glyph_t *scaled_glyph,
+		                       cairo_scaled_font_t *scaled_font,
+		                       cairo_image_surface_t *surface);
+
 cairo_private cairo_int_status_t
 _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font,
 			    unsigned long index,


More information about the cairo-commit mailing list