[cairo] [PATCH 5/6] Add color glyph support to the image compositor

matthias.clasen at gmail.com matthias.clasen at gmail.com
Sat Apr 9 03:22:32 UTC 2016


From: Matthias Clasen <mclasen at redhat.com>

Since pixman doesn't know how to render color glyphs and insists
on treating ARGB32 glyphs as having component alpha, we keep track
of whether we've seen any color glyphs while populating the pixman
glyph cache. If we do, use a slow-path function that knows how to
handle color glyphs.

The non-pixman-glyph-cache code path is using the same approach,
and the same slow-path function.
---
 src/cairo-image-compositor.c | 220 +++++++++++++++++++++++++++++--------------
 1 file changed, 149 insertions(+), 71 deletions(-)

diff --git a/src/cairo-image-compositor.c b/src/cairo-image-compositor.c
index 48072f8..f480667 100644
--- a/src/cairo-image-compositor.c
+++ b/src/cairo-image-compositor.c
@@ -808,6 +808,82 @@ check_composite_glyphs (const cairo_composite_rectangles_t *extents,
     return CAIRO_STATUS_SUCCESS;
 }
 
+#define GLYPH_CACHE_SIZE 64
+
+static cairo_int_status_t
+composite_glyphs_slowly (void				*_dst,
+			 cairo_operator_t		 op,
+			 cairo_surface_t		*_src,
+			 int				 src_x,
+			 int				 src_y,
+			 int				 dst_x,
+			 int				 dst_y,
+			 cairo_composite_glyphs_info_t *info,
+                         cairo_scaled_glyph_t          **glyph_cache)
+{
+    cairo_int_status_t status;
+    int i;
+    pixman_image_t *dst, *src;
+
+    status = CAIRO_STATUS_SUCCESS;
+
+    op = _pixman_operator (op);
+    dst = to_pixman_image (_dst);
+    src = ((cairo_image_source_t *)_src)->pixman_image;
+
+    for (i = 0; i < info->num_glyphs; i++) {
+	int x, y;
+	cairo_image_surface_t *glyph_surface;
+	cairo_scaled_glyph_t *scaled_glyph;
+	unsigned long glyph_index = info->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 (info->font, glyph_index,
+						 CAIRO_SCALED_GLYPH_INFO_SURFACE,
+						 &scaled_glyph);
+
+	    if (unlikely (status))
+		break;
+
+	    glyph_cache[cache_index] = scaled_glyph;
+	}
+
+        if ((scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) != 0)
+            glyph_surface = scaled_glyph->color_surface;
+        else
+            glyph_surface = scaled_glyph->surface;
+	if (glyph_surface->width && glyph_surface->height) {
+	    /* round glyph locations to the nearest pixel */
+	    /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+	    x = _cairo_lround (info->glyphs[i].x -
+			       glyph_surface->base.device_transform.x0);
+	    y = _cairo_lround (info->glyphs[i].y -
+			       glyph_surface->base.device_transform.y0);
+
+            if ((scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) == 0)
+                pixman_image_composite32 (op, src, glyph_surface->pixman_image, dst,
+					  x + src_x,  y + src_y,
+					  0, 0,
+					  x - dst_x, y - dst_y,
+					  glyph_surface->width,
+					  glyph_surface->height);
+	    else /* Color glyph. */
+                pixman_image_composite32 (op, glyph_surface->pixman_image, NULL, dst,
+					  0, 0,
+					  x + src_x,  y + src_y,
+					  x - dst_x, y - dst_y,
+					  glyph_surface->width,
+					  glyph_surface->height);
+	}
+    }
+
+    return status;
+}
+
 #if HAS_PIXMAN_GLYPHS
 static pixman_glyph_cache_t *global_glyph_cache;
 
@@ -851,6 +927,7 @@ composite_glyphs (void				*_dst,
     pixman_glyph_t *pglyphs = pglyphs_stack;
     pixman_glyph_t *pg;
     int i;
+    cairo_bool_t has_color_glyphs;
 
     TRACE ((stderr, "%s\n", __FUNCTION__));
 
@@ -872,6 +949,8 @@ composite_glyphs (void				*_dst,
 	}
     }
 
+    has_color_glyphs = FALSE;
+
     pg = pglyphs;
     for (i = 0; i < info->num_glyphs; i++) {
 	unsigned long index = info->glyphs[i].index;
@@ -894,11 +973,17 @@ composite_glyphs (void				*_dst,
 	    if (unlikely (status))
 		goto out_thaw;
 
-	    glyph_surface = scaled_glyph->surface;
+            if ((scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) != 0) {
+                 has_color_glyphs = TRUE;
+                 break;
+            }
+
+            glyph_surface = scaled_glyph->surface;
 	    glyph = pixman_glyph_cache_insert (glyph_cache, info->font, (void *)index,
 					       glyph_surface->base.device_transform.x0,
 					       glyph_surface->base.device_transform.y0,
 					       glyph_surface->pixman_image);
+
 	    if (unlikely (!glyph)) {
 		status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 		goto out_thaw;
@@ -911,13 +996,18 @@ composite_glyphs (void				*_dst,
 	pg++;
     }
 
-    if (info->use_mask) {
+
+    if (has_color_glyphs) {
+        cairo_scaled_glyph_t *glyph_cache[GLYPH_CACHE_SIZE];
+        memset (glyph_cache, 0, sizeof (glyph_cache));
+        composite_glyphs_slowly (_dst, op, _src, src_x, src_y, dst_x, dst_y, info, glyph_cache);
+    } else if (info->use_mask) {
 	pixman_format_code_t mask_format;
 
 	mask_format = pixman_glyph_get_mask_format (glyph_cache, pg - pglyphs, pglyphs);
 
 	pixman_composite_glyphs (_pixman_operator (op),
-				 ((cairo_image_source_t *)_src)->pixman_image,
+				 to_pixman_image (_src),
 				 to_pixman_image (_dst),
 				 mask_format,
 				 info->extents.x + src_x, info->extents.y + src_y,
@@ -927,7 +1017,7 @@ composite_glyphs (void				*_dst,
 				 glyph_cache, pg - pglyphs, pglyphs);
     } else {
 	pixman_composite_glyphs_no_mask (_pixman_operator (op),
-					 ((cairo_image_source_t *)_src)->pixman_image,
+					 to_pixman_image (_src),
 					 to_pixman_image (_dst),
 					 src_x, src_y,
 					 - dst_x, - dst_y,
@@ -976,7 +1066,11 @@ composite_one_glyph (void				*_dst,
     if (unlikely (status))
 	return status;
 
-    glyph_surface = scaled_glyph->surface;
+    if ((scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) != 0)
+        glyph_surface = scaled_glyph->color_surface;
+    else
+        glyph_surface = scaled_glyph->surface;
+
     if (glyph_surface->width == 0 || glyph_surface->height == 0)
 	return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
@@ -987,15 +1081,26 @@ composite_one_glyph (void				*_dst,
     y = _cairo_lround (info->glyphs[0].y -
 		       glyph_surface->base.device_transform.y0);
 
-    pixman_image_composite32 (_pixman_operator (op),
-			      ((cairo_image_source_t *)_src)->pixman_image,
-			      glyph_surface->pixman_image,
-			      to_pixman_image (_dst),
-			      x + src_x,  y + src_y,
-			      0, 0,
-			      x - dst_x, y - dst_y,
-			      glyph_surface->width,
-			      glyph_surface->height);
+    if ((scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) == 0)
+        pixman_image_composite32 (_pixman_operator (op),
+			          ((cairo_image_source_t *)_src)->pixman_image,
+	        		  glyph_surface->pixman_image,
+		        	  to_pixman_image (_dst),
+        			  x + src_x,  y + src_y,
+	        		  0, 0,
+		        	  x - dst_x, y - dst_y,
+			          glyph_surface->width,
+        			  glyph_surface->height);
+    else
+        pixman_image_composite32 (_pixman_operator (op),
+                                  glyph_surface->pixman_image,
+                                  NULL,
+                                  to_pixman_image (_dst),
+        			  x + src_x,  y + src_y,
+	        		  0, 0,
+		        	  x - dst_x, y - dst_y,
+			          glyph_surface->width,
+        			  glyph_surface->height);
 
     return CAIRO_INT_STATUS_SUCCESS;
 }
@@ -1008,9 +1113,9 @@ composite_glyphs_via_mask (void				*_dst,
 			   int				 src_y,
 			   int				 dst_x,
 			   int				 dst_y,
-			   cairo_composite_glyphs_info_t *info)
+			   cairo_composite_glyphs_info_t *info,
+                           cairo_scaled_glyph_t          **glyph_cache)
 {
-    cairo_scaled_glyph_t *glyph_cache[64];
     pixman_image_t *white = _pixman_image_for_color (CAIRO_COLOR_WHITE);
     cairo_scaled_glyph_t *scaled_glyph;
     uint8_t buf[2048];
@@ -1030,17 +1135,7 @@ composite_glyphs_via_mask (void				*_dst,
      * mask formats.
      */
 
-    status = _cairo_scaled_glyph_lookup (info->font,
-					 info->glyphs[0].index,
-					 CAIRO_SCALED_GLYPH_INFO_SURFACE,
-					 &scaled_glyph);
-    if (unlikely (status)) {
-	pixman_image_unref (white);
-	return status;
-    }
-
-    memset (glyph_cache, 0, sizeof (glyph_cache));
-    glyph_cache[info->glyphs[0].index % ARRAY_LENGTH (glyph_cache)] = scaled_glyph;
+    scaled_glyph = glyph_cache[info->glyphs[0].index % GLYPH_CACHE_SIZE];
 
     format = PIXMAN_a8;
     i = (info->extents.width + 3) & ~3;
@@ -1172,9 +1267,9 @@ composite_glyphs (void				*_dst,
 		  int				 dst_y,
 		  cairo_composite_glyphs_info_t *info)
 {
-    cairo_scaled_glyph_t *glyph_cache[64];
-    pixman_image_t *dst, *src;
+    cairo_scaled_glyph_t *glyph_cache[GLYPH_CACHE_SIZE];
     cairo_status_t status;
+    cairo_bool_t use_mask;
     int i;
 
     TRACE ((stderr, "%s\n", __FUNCTION__));
@@ -1182,56 +1277,39 @@ composite_glyphs (void				*_dst,
     if (info->num_glyphs == 1)
 	return composite_one_glyph(_dst, op, _src, src_x, src_y, dst_x, dst_y, info);
 
-    if (info->use_mask)
-	return composite_glyphs_via_mask(_dst, op, _src, src_x, src_y, dst_x, dst_y, info);
-
-    op = _pixman_operator (op);
-    dst = to_pixman_image (_dst);
-    src = ((cairo_image_source_t *)_src)->pixman_image;
-
     memset (glyph_cache, 0, sizeof (glyph_cache));
-    status = CAIRO_STATUS_SUCCESS;
 
-    for (i = 0; i < info->num_glyphs; i++) {
-	int x, y;
-	cairo_image_surface_t *glyph_surface;
-	cairo_scaled_glyph_t *scaled_glyph;
-	unsigned long glyph_index = info->glyphs[i].index;
-	int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
+    use_mask = info->use_mask;
+    if (use_mask) {
+        for (i = 0; i < info->num_glyphs; i++) {
+            cairo_scaled_glyph_t *scaled_glyph;
+            unsigned long glyph_index = info->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 (info->font, glyph_index,
-						 CAIRO_SCALED_GLYPH_INFO_SURFACE,
-						 &scaled_glyph);
+       	    scaled_glyph = glyph_cache[cache_index];
+	    if (scaled_glyph == NULL ||
+	        _cairo_scaled_glyph_index (scaled_glyph) != glyph_index) {
+	        status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
+						     CAIRO_SCALED_GLYPH_INFO_SURFACE,
+						     &scaled_glyph);
 
-	    if (unlikely (status))
-		break;
+                if (unlikely (status))
+		    return status;
 
-	    glyph_cache[cache_index] = scaled_glyph;
-	}
-
-	glyph_surface = scaled_glyph->surface;
-	if (glyph_surface->width && glyph_surface->height) {
-	    /* round glyph locations to the nearest pixel */
-	    /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
-	    x = _cairo_lround (info->glyphs[i].x -
-			       glyph_surface->base.device_transform.x0);
-	    y = _cairo_lround (info->glyphs[i].y -
-			       glyph_surface->base.device_transform.y0);
+	        glyph_cache[cache_index] = scaled_glyph;
+	    }
 
-	    pixman_image_composite32 (op, src, glyph_surface->pixman_image, dst,
-                                      x + src_x,  y + src_y,
-                                      0, 0,
-                                      x - dst_x, y - dst_y,
-				      glyph_surface->width,
-				      glyph_surface->height);
-	}
+            if ((scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) != 0) {
+                use_mask = FALSE;
+                break;
+            }
+        }
     }
 
-    return status;
+    if (use_mask)
+	return composite_glyphs_via_mask(_dst, op, _src, src_x, src_y, dst_x, dst_y, info, glyph_cache);
+    else
+        return composite_glyphs_slowly (_dst, op, _src, src_x, src_y, dst_x, dst_y, info, glyph_cache);
 }
 #endif
 
-- 
2.7.3



More information about the cairo mailing list