pixman: Branch 'master' - 8 commits

Søren Sandmann Pedersen sandmann at kemper.freedesktop.org
Sat Jun 9 12:48:31 PDT 2012


 pixman/Makefile.sources  |    1 
 pixman/pixman-compiler.h |    4 
 pixman/pixman-glyph.c    |  672 +++++++++++++++++++++++++++++++++++++++++++++++
 pixman/pixman-image.c    |   33 +-
 pixman/pixman-private.h  |   70 ++++
 pixman/pixman.c          |   42 +-
 pixman/pixman.h          |   59 ++++
 test/Makefile.sources    |    1 
 test/blitters-test.c     |   31 --
 test/glyph-test.c        |  331 +++++++++++++++++++++++
 test/utils.c             |  101 +++++++
 test/utils.h             |    7 
 12 files changed, 1289 insertions(+), 63 deletions(-)

New commits:
commit 706bf8264cb48aac36e36ff5e23f0ad8a47ff73c
Author: Søren Sandmann Pedersen <ssp at redhat.com>
Date:   Tue May 3 07:25:50 2011 -0400

    Speed up _pixman_image_get_solid() in common cases
    
    Make _pixman_image_get_solid() faster by special-casing the common
    cases where the image is SOLID or a repeating a8r8g8b8 image.
    
    This optimization together with the previous one results in a small
    but reproducable performance improvement on the xfce4-terminal-a1
    cairo trace:
    
    [ # ]  backend                         test   min(s) median(s) stddev. count
    Before:
    [  0]    image            xfce4-terminal-a1    1.221    1.239   1.21%  100/100
    After:
    [  0]    image            xfce4-terminal-a1    1.170    1.199   1.26%  100/100
    
    Either optimization by itself is difficult to separate from noise.

diff --git a/pixman/pixman-image.c b/pixman/pixman-image.c
index 6a965e4..8b634a7 100644
--- a/pixman/pixman-image.c
+++ b/pixman/pixman-image.c
@@ -879,13 +879,34 @@ _pixman_image_get_solid (pixman_implementation_t *imp,
                          pixman_format_code_t     format)
 {
     uint32_t result;
-    pixman_iter_t iter;
 
-    _pixman_implementation_src_iter_init (
-	imp, &iter, image, 0, 0, 1, 1,
-	(uint8_t *)&result, ITER_NARROW, image->common.flags);
-
-    result = *iter.get_scanline (&iter, NULL);
+    if (image->type == SOLID)
+    {
+	result = image->solid.color_32;
+    }
+    else if (image->type == BITS)
+    {
+	if (image->bits.format == PIXMAN_a8r8g8b8)
+	    result = image->bits.bits[0];
+	else if (image->bits.format == PIXMAN_x8r8g8b8)
+	    result = image->bits.bits[0] | 0xff000000;
+	else if (image->bits.format == PIXMAN_a8)
+	    result = (*(uint8_t *)image->bits.bits) << 24;
+	else
+	    goto otherwise;
+    }
+    else
+    {
+	pixman_iter_t iter;
+
+    otherwise:
+	_pixman_implementation_src_iter_init (
+	    imp, &iter, image, 0, 0, 1, 1,
+	    (uint8_t *)&result,
+	    ITER_NARROW, image->common.flags);
+	
+	result = *iter.get_scanline (&iter, NULL);
+    }
 
     /* If necessary, convert RGB <--> BGR. */
     if (PIXMAN_FORMAT_TYPE (format) != PIXMAN_TYPE_ARGB)
commit 934c9d8546b71ddea91ac16b0928101903e2608e
Author: Søren Sandmann Pedersen <ssp at redhat.com>
Date:   Mon May 28 02:36:22 2012 -0400

    Speed up _pixman_composite_glyphs_no_mask()
    
    Bypass much of the overhead of pixman_image_composite32() by only
    computing the composite region once instead of once per glyph, and by
    only looking up the composite function whenever the glyph format or
    flags change.
    
    As part of this, the pixman_compute_composite_region32() was renamed
    to _pixman_compute_composite_region32() and exported in
    pixman-private.h.
    
    I couldn't find a trace that would reliably demonstrate that this is
    actually an improvement by itself (since _pixman_composite_glyphs_no_mask()
    is called so rarely), but together with the following optimization for
    solid sources, there is a small but reliable improvement to the
    xfce4-a1-terminal cairo trace.

diff --git a/pixman/pixman-glyph.c b/pixman/pixman-glyph.c
index 186ef47..921eff1 100644
--- a/pixman/pixman-glyph.c
+++ b/pixman/pixman-glyph.c
@@ -399,24 +399,99 @@ pixman_composite_glyphs_no_mask (pixman_op_t            op,
 				 int                    n_glyphs,
 				 pixman_glyph_t        *glyphs)
 {
+    pixman_region32_t region;
+    pixman_format_code_t glyph_format = PIXMAN_null;
+    uint32_t glyph_flags = 0;
+    pixman_format_code_t dest_format;
+    uint32_t dest_flags;
+    pixman_composite_func_t func = NULL;
+    pixman_implementation_t *implementation = NULL;
+    pixman_composite_info_t info;
     int i;
 
+    _pixman_image_validate (src);
+    _pixman_image_validate (dest);
+    
+    dest_format = dest->common.extended_format_code;
+    dest_flags = dest->common.flags;
+    
+    pixman_region32_init (&region);
+    if (!_pixman_compute_composite_region32 (
+	    &region,
+	    src, NULL, dest,
+	    src_x - dest_x, src_y - dest_y, 0, 0, 0, 0,
+	    dest->bits.width, dest->bits.height))
+    {
+	goto out;
+    }
+
+    info.op = op;
+    info.src_image = src;
+    info.dest_image = dest;
+    info.src_flags = src->common.flags;
+    info.dest_flags = dest->common.flags;
+
     for (i = 0; i < n_glyphs; ++i)
     {
 	glyph_t *glyph = (glyph_t *)glyphs[i].glyph;
 	pixman_image_t *glyph_img = glyph->image;
+	pixman_box32_t glyph_box;
+	pixman_box32_t *pbox;
+	uint32_t extra = FAST_PATH_SAMPLES_COVER_CLIP_NEAREST;
+	pixman_box32_t composite_box;
+	int n;
 
-	pixman_image_composite32 (op, src, glyph_img, dest,
-				  src_x + glyphs[i].x - glyph->origin_x,
-				  src_y + glyphs[i].y - glyph->origin_y,
-				  0, 0,
-				  dest_x + glyphs[i].x - glyph->origin_x,
-				  dest_y + glyphs[i].y - glyph->origin_y,
-				  glyph_img->bits.width,
-				  glyph_img->bits.height);
+	glyph_box.x1 = dest_x + glyphs[i].x - glyph->origin_x;
+	glyph_box.y1 = dest_y + glyphs[i].y - glyph->origin_y;
+	glyph_box.x2 = glyph_box.x1 + glyph->image->bits.width;
+	glyph_box.y2 = glyph_box.y1 + glyph->image->bits.height;
+	
+	pbox = pixman_region32_rectangles (&region, &n);
+	
+	info.mask_image = glyph_img;
+
+	while (n--)
+	{
+	    if (box32_intersect (&composite_box, pbox, &glyph_box))
+	    {
+		if (glyph_img->common.extended_format_code != glyph_format	||
+		    glyph_img->common.flags != glyph_flags)
+		{
+		    glyph_format = glyph_img->common.extended_format_code;
+		    glyph_flags = glyph_img->common.flags;
+		    
+		    _pixman_lookup_composite_function (
+			get_implementation(), op,
+			src->common.extended_format_code, src->common.flags,
+			glyph_format, glyph_flags | extra,
+			dest_format, dest_flags,
+			&implementation, &func);
+
+		    if (!func)
+			goto out;
+		}
+
+		info.src_x = src_x + composite_box.x1 - dest_x;
+		info.src_y = src_y + composite_box.y1 - dest_y;
+		info.mask_x = composite_box.x1 - (dest_x + glyphs[i].x - glyph->origin_x);
+		info.mask_y = composite_box.y1 - (dest_y + glyphs[i].y - glyph->origin_y);
+		info.dest_x = composite_box.x1;
+		info.dest_y = composite_box.y1;
+		info.width = composite_box.x2 - composite_box.x1;
+		info.height = composite_box.y2 - composite_box.y1;
+
+		info.mask_flags = glyph_flags;
+
+		func (implementation, &info);
+	    }
 
+	    pbox++;
+	}
 	pixman_list_move_to_front (&cache->mru, &glyph->mru_link);
     }
+
+out:
+    pixman_region32_fini (&region);
 }
 
 static void
diff --git a/pixman/pixman-private.h b/pixman/pixman-private.h
index 8323426..873f321 100644
--- a/pixman/pixman-private.h
+++ b/pixman/pixman-private.h
@@ -581,6 +581,19 @@ _pixman_choose_implementation (void);
 /*
  * Utilities
  */
+pixman_bool_t
+_pixman_compute_composite_region32 (pixman_region32_t * region,
+				    pixman_image_t *    src_image,
+				    pixman_image_t *    mask_image,
+				    pixman_image_t *    dest_image,
+				    int32_t             src_x,
+				    int32_t             src_y,
+				    int32_t             mask_x,
+				    int32_t             mask_y,
+				    int32_t             dest_x,
+				    int32_t             dest_y,
+				    int32_t             width,
+				    int32_t             height);
 uint32_t *
 _pixman_iter_get_scanline_noop (pixman_iter_t *iter, const uint32_t *mask);
 
diff --git a/pixman/pixman.c b/pixman/pixman.c
index 7d841d3..0137c3c 100644
--- a/pixman/pixman.c
+++ b/pixman/pixman.c
@@ -224,19 +224,19 @@ clip_source_image (pixman_region32_t * region,
  * returns FALSE if the final region is empty.  Indistinguishable from
  * an allocation failure, but rendering ignores those anyways.
  */
-static pixman_bool_t
-pixman_compute_composite_region32 (pixman_region32_t * region,
-                                   pixman_image_t *    src_image,
-                                   pixman_image_t *    mask_image,
-                                   pixman_image_t *    dest_image,
-                                   int32_t             src_x,
-                                   int32_t             src_y,
-                                   int32_t             mask_x,
-                                   int32_t             mask_y,
-                                   int32_t             dest_x,
-                                   int32_t             dest_y,
-                                   int32_t             width,
-                                   int32_t             height)
+pixman_bool_t
+_pixman_compute_composite_region32 (pixman_region32_t * region,
+				    pixman_image_t *    src_image,
+				    pixman_image_t *    mask_image,
+				    pixman_image_t *    dest_image,
+				    int32_t             src_x,
+				    int32_t             src_y,
+				    int32_t             mask_x,
+				    int32_t             mask_y,
+				    int32_t             dest_x,
+				    int32_t             dest_y,
+				    int32_t             width,
+				    int32_t             height)
 {
     region->extents.x1 = dest_x;
     region->extents.x2 = dest_x + width;
@@ -615,7 +615,7 @@ pixman_image_composite32 (pixman_op_t      op,
 
     pixman_region32_init (&region);
 
-    if (!pixman_compute_composite_region32 (
+    if (!_pixman_compute_composite_region32 (
 	    &region, src, mask, dest,
 	    src_x, src_y, mask_x, mask_y, dest_x, dest_y, width, height))
     {
@@ -1114,7 +1114,7 @@ pixman_compute_composite_region (pixman_region16_t * region,
 
     pixman_region32_init (&r32);
 
-    retval = pixman_compute_composite_region32 (
+    retval = _pixman_compute_composite_region32 (
 	&r32, src_image, mask_image, dest_image,
 	src_x, src_y, mask_x, mask_y, dest_x, dest_y,
 	width, height);
commit a162189dc0fa9978a3b5b6dd24f9bc12995805ed
Author: Søren Sandmann Pedersen <ssp at redhat.com>
Date:   Mon May 28 01:22:26 2012 -0400

    Speed up pixman_composite_glyphs()
    
    When adding glyphs to the mask, bypass most of the overhead of
    pixman_image_composite32() by:
    
    - Only looking up the composite function when the glyph changes either
      format or flags.
    
    - Only using a white source when the glyph format is different from
      the mask format.
    
    - Simply intersecting the glyph rectangle with the destination
      rectangle instead of doing the full _pixman_composite_region32().
    
    Performance results:
    
    [ # ]  backend                         test   min(s) median(s) stddev. count
    Before:
    [  0]    image            firefox-talos-gfx    6.570    6.577   0.13%    8/10
    After:
    [  0]    image            firefox-talos-gfx    4.272    4.289   0.28%   10/10
    
    V2: Changes to deal with white sources

diff --git a/pixman/pixman-glyph.c b/pixman/pixman-glyph.c
index 30acc36..186ef47 100644
--- a/pixman/pixman-glyph.c
+++ b/pixman/pixman-glyph.c
@@ -374,6 +374,19 @@ pixman_glyph_get_mask_format (pixman_glyph_cache_t *cache,
     return format;
 }
 
+static pixman_bool_t
+box32_intersect (pixman_box32_t *dest,
+		 const pixman_box32_t *box1,
+		 const pixman_box32_t *box2)
+{
+    dest->x1 = MAX (box1->x1, box2->x1);
+    dest->y1 = MAX (box1->y1, box2->y1);
+    dest->x2 = MIN (box1->x2, box2->x2);
+    dest->y2 = MIN (box1->y2, box2->y2);
+
+    return dest->x2 > dest->x1 && dest->y2 > dest->y1;
+}
+
 PIXMAN_EXPORT void
 pixman_composite_glyphs_no_mask (pixman_op_t            op,
 				 pixman_image_t        *src,
@@ -406,6 +419,128 @@ pixman_composite_glyphs_no_mask (pixman_op_t            op,
     }
 }
 
+static void
+add_glyphs (pixman_glyph_cache_t *cache,
+	    pixman_image_t *dest,
+	    int off_x, int off_y,
+	    int n_glyphs, pixman_glyph_t *glyphs)
+{
+    pixman_format_code_t glyph_format = PIXMAN_null;
+    uint32_t glyph_flags = 0;
+    pixman_composite_func_t func = NULL;
+    pixman_implementation_t *implementation = NULL;
+    uint32_t dest_format;
+    uint32_t dest_flags;
+    pixman_box32_t dest_box;
+    pixman_composite_info_t info;
+    pixman_image_t *white_img = NULL;
+    pixman_bool_t white_src = FALSE;
+    int i;
+
+    _pixman_image_validate (dest);
+
+    dest_format = dest->common.extended_format_code;
+    dest_flags = dest->common.flags;
+
+    info.op = PIXMAN_OP_ADD;
+    info.dest_image = dest;
+    info.src_x = 0;
+    info.src_y = 0;
+    info.dest_flags = dest_flags;
+
+    dest_box.x1 = 0;
+    dest_box.y1 = 0;
+    dest_box.x2 = dest->bits.width;
+    dest_box.y2 = dest->bits.height;
+
+    for (i = 0; i < n_glyphs; ++i)
+    {
+	glyph_t *glyph = (glyph_t *)glyphs[i].glyph;
+	pixman_image_t *glyph_img = glyph->image;
+	pixman_box32_t glyph_box;
+	pixman_box32_t composite_box;
+
+	if (glyph_img->common.extended_format_code != glyph_format	||
+	    glyph_img->common.flags != glyph_flags)
+	{
+	    pixman_format_code_t src_format, mask_format;
+
+	    glyph_format = glyph_img->common.extended_format_code;
+	    glyph_flags = glyph_img->common.flags;
+
+	    if (glyph_format == dest->bits.format)
+	    {
+		src_format = glyph_format;
+		mask_format = PIXMAN_null;
+		info.src_flags = glyph_flags | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST;
+		info.mask_flags = FAST_PATH_IS_OPAQUE;
+		info.mask_image = NULL;
+		white_src = FALSE;
+	    }
+	    else
+	    {
+		if (!white_img)
+		{
+		    pixman_color_t white = { 0xffff, 0xffff, 0xffff, 0xffff };
+
+		    if (!(white_img = pixman_image_create_solid_fill (&white)))
+			goto out;
+
+		    _pixman_image_validate (white_img);
+		}
+
+		src_format = PIXMAN_solid;
+		mask_format = glyph_format;
+		info.src_flags = white_img->common.flags;
+		info.mask_flags = glyph_flags | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST;
+		info.src_image = white_img;
+		white_src = TRUE;
+	    }
+
+	    _pixman_lookup_composite_function (
+		get_implementation(), PIXMAN_OP_ADD,
+		src_format, info.src_flags,
+		mask_format, info.mask_flags,
+		dest_format, dest_flags,
+		&implementation, &func);
+
+	    if (!func)
+		goto out;
+	}
+
+	glyph_box.x1 = glyphs[i].x - glyph->origin_x + off_x;
+	glyph_box.y1 = glyphs[i].y - glyph->origin_y + off_y;
+	glyph_box.x2 = glyph_box.x1 + glyph->image->bits.width;
+	glyph_box.y2 = glyph_box.y1 + glyph->image->bits.height;
+	
+	if (box32_intersect (&composite_box, &glyph_box, &dest_box))
+	{
+	    int src_x = composite_box.x1 - glyph_box.x1;
+	    int src_y = composite_box.y1 - glyph_box.y1;
+
+	    if (white_src)
+		info.mask_image = glyph_img;
+	    else
+		info.src_image = glyph_img;
+
+	    info.mask_x = info.src_x = src_x;
+	    info.mask_y = info.src_y = src_y;
+	    info.dest_x = composite_box.x1;
+	    info.dest_y = composite_box.y1;
+	    info.width = composite_box.x2 - composite_box.x1;
+	    info.height = composite_box.y2 - composite_box.y1;
+
+	    func (implementation, &info);
+
+	    pixman_list_move_to_front (&cache->mru, &glyph->mru_link);
+	}
+    }
+
+out:
+    if (white_img)
+	pixman_image_unref (white_img);
+}
+
 /* Conceptually, for each glyph, (white IN glyph) is PIXMAN_OP_ADDed to an
  * infinitely big mask image at the position such that the glyph origin point
  * is positioned at the (glyphs[i].x, glyphs[i].y) point.
@@ -439,37 +574,18 @@ pixman_composite_glyphs (pixman_op_t            op,
 			 int			n_glyphs,
 			 pixman_glyph_t        *glyphs)
 {
-    pixman_color_t white_color = { 0xffff, 0xffff, 0xffff, 0xffff };
-    pixman_image_t *white;
     pixman_image_t *mask;
-    int i;
 
     if (!(mask = pixman_image_create_bits (mask_format, width, height, NULL, -1)))
 	return;
 
-    if (!(white = pixman_image_create_solid_fill (&white_color)))
-	goto out;
-
     if (PIXMAN_FORMAT_A   (mask_format) != 0 &&
 	PIXMAN_FORMAT_RGB (mask_format) != 0)
     {
 	pixman_image_set_component_alpha (mask, TRUE);
     }
 
-    for (i = 0; i < n_glyphs; ++i)
-    {
-	glyph_t *glyph = (glyph_t *)glyphs[i].glyph;
-	pixman_image_t *glyph_img = glyph->image;
-
-	pixman_image_composite32 (PIXMAN_OP_ADD, white, glyph_img, mask,
-				  0, 0, 0, 0,
-				  glyphs[i].x - glyph->origin_x - mask_x,
-				  glyphs[i].y - glyph->origin_y - mask_y,
-				  glyph->image->bits.width,
-				  glyph->image->bits.height);
-
-	pixman_list_move_to_front (&cache->mru, &glyph->mru_link);
-    }
+    add_glyphs (cache, mask, - mask_x, - mask_y, n_glyphs, glyphs);
 
     pixman_image_composite32 (op, src, mask, dest,
 			      src_x, src_y,
@@ -477,6 +593,5 @@ pixman_composite_glyphs (pixman_op_t            op,
 			      dest_x, dest_y,
 			      width, height);
 
-out:
     pixman_image_unref (mask);
 }
diff --git a/pixman/pixman-private.h b/pixman/pixman-private.h
index 76d65e2..8323426 100644
--- a/pixman/pixman-private.h
+++ b/pixman/pixman-private.h
@@ -688,6 +688,18 @@ _pixman_iter_get_scanline_noop (pixman_iter_t *iter, const uint32_t *mask);
 	    dest, FAST_PATH_STD_DEST_FLAGS,				\
 	    func) }
 
+extern pixman_implementation_t *global_implementation;
+
+static force_inline pixman_implementation_t *
+get_implementation (void)
+{
+#ifndef TOOLCHAIN_SUPPORTS_ATTRIBUTE_CONSTRUCTOR
+    if (!global_implementation)
+	global_implementation = _pixman_choose_implementation ();
+#endif
+    return global_implementation;
+}
+
 /* Memory allocation helpers */
 void *
 pixman_malloc_ab (unsigned int n, unsigned int b);
diff --git a/pixman/pixman.c b/pixman/pixman.c
index 8fb5356..7d841d3 100644
--- a/pixman/pixman.c
+++ b/pixman/pixman.c
@@ -30,7 +30,7 @@
 
 #include <stdlib.h>
 
-static pixman_implementation_t *global_implementation;
+pixman_implementation_t *global_implementation;
 
 #ifdef TOOLCHAIN_SUPPORTS_ATTRIBUTE_CONSTRUCTOR
 static void __attribute__((constructor))
@@ -40,16 +40,6 @@ pixman_constructor (void)
 }
 #endif
 
-static force_inline pixman_implementation_t *
-get_implementation (void)
-{
-#ifndef TOOLCHAIN_SUPPORTS_ATTRIBUTE_CONSTRUCTOR
-    if (!global_implementation)
-	global_implementation = _pixman_choose_implementation ();
-#endif
-    return global_implementation;
-}
-
 typedef struct operator_info_t operator_info_t;
 
 struct operator_info_t
commit d9710442b4b2294e1ccd1594c54ca8a4feda2ac5
Author: Søren Sandmann Pedersen <ssp at redhat.com>
Date:   Sun May 27 18:23:20 2012 -0400

    test: Add glyph-test
    
    This test tests the new glyph cache and compositing API. Much of this
    test is intending to making sure that clipping and alpha map handling
    survive any optimizations that may be added to the glyph compositing.
    
    V2: Evaluating lcg_rand_n() multiple times in an argument list lead
        to undefined behavior.

diff --git a/test/Makefile.sources b/test/Makefile.sources
index 99eb705..fad8c6f 100644
--- a/test/Makefile.sources
+++ b/test/Makefile.sources
@@ -16,6 +16,7 @@ TESTPROGRAMS =			\
 	stress-test		\
 	composite-traps-test	\
 	blitters-test		\
+	glyph-test		\
 	scaling-test		\
 	affine-test		\
 	composite		\
diff --git a/test/glyph-test.c b/test/glyph-test.c
new file mode 100644
index 0000000..9ff2a2b
--- /dev/null
+++ b/test/glyph-test.c
@@ -0,0 +1,331 @@
+#include <stdlib.h>
+#include "utils.h"
+
+static const pixman_format_code_t glyph_formats[] =
+{
+    PIXMAN_a8r8g8b8,
+    PIXMAN_a8,
+    PIXMAN_a4,
+    PIXMAN_a1,
+    PIXMAN_x8r8g8b8,
+    PIXMAN_r3g3b2,
+    PIXMAN_null,
+};
+
+static const pixman_format_code_t formats[] =
+{
+    PIXMAN_a8r8g8b8,
+    PIXMAN_a8b8g8r8,
+    PIXMAN_x8r8g8b8,
+    PIXMAN_x8b8g8r8,
+    PIXMAN_r5g6b5,
+    PIXMAN_b5g6r5,
+    PIXMAN_a8,
+    PIXMAN_a1,
+    PIXMAN_r3g3b2,
+    PIXMAN_b8g8r8a8,
+    PIXMAN_b8g8r8x8,
+    PIXMAN_r8g8b8a8,
+    PIXMAN_r8g8b8x8,
+    PIXMAN_x14r6g6b6,
+    PIXMAN_r8g8b8,
+    PIXMAN_b8g8r8,
+    PIXMAN_x2r10g10b10,
+    PIXMAN_a2r10g10b10,
+    PIXMAN_x2b10g10r10,
+    PIXMAN_a2b10g10r10,
+    PIXMAN_a1r5g5b5,
+    PIXMAN_x1r5g5b5,
+    PIXMAN_a1b5g5r5,
+    PIXMAN_x1b5g5r5,
+    PIXMAN_a4r4g4b4,
+    PIXMAN_x4r4g4b4,
+    PIXMAN_a4b4g4r4,
+    PIXMAN_x4b4g4r4,
+    PIXMAN_r3g3b2,
+    PIXMAN_b2g3r3,
+    PIXMAN_a2r2g2b2,
+    PIXMAN_a2b2g2r2,
+    PIXMAN_x4a4,
+    PIXMAN_a4,
+    PIXMAN_r1g2b1,
+    PIXMAN_b1g2r1,
+    PIXMAN_a1r1g1b1,
+    PIXMAN_a1b1g1r1,
+    PIXMAN_null,
+};
+
+static const pixman_op_t operators[] =
+{
+    PIXMAN_OP_SRC,
+    PIXMAN_OP_OVER,
+    PIXMAN_OP_ADD,
+    PIXMAN_OP_CLEAR,
+    PIXMAN_OP_SRC,
+    PIXMAN_OP_DST,
+    PIXMAN_OP_OVER,
+    PIXMAN_OP_OVER_REVERSE,
+    PIXMAN_OP_IN,
+    PIXMAN_OP_IN_REVERSE,
+    PIXMAN_OP_OUT,
+    PIXMAN_OP_OUT_REVERSE,
+    PIXMAN_OP_ATOP,
+    PIXMAN_OP_ATOP_REVERSE,
+    PIXMAN_OP_XOR,
+    PIXMAN_OP_ADD
+};
+
+enum
+{
+    ALLOW_CLIPPED		= (1 << 0),
+    ALLOW_ALPHA_MAP		= (1 << 1),
+    ALLOW_SOURCE_CLIPPING	= (1 << 2),
+    ALLOW_REPEAT		= (1 << 3),
+    ALLOW_SOLID			= (1 << 4),
+    ALLOW_FENCED_MEMORY		= (1 << 5),
+};
+
+static void
+destroy_fenced (pixman_image_t *image, void *data)
+{
+    fence_free (data);
+}
+
+static void
+destroy_malloced (pixman_image_t *image, void *data)
+{
+    free (data);
+}
+
+static pixman_format_code_t
+random_format (const pixman_format_code_t *formats)
+{
+    int i;
+    i = 0;
+    while (formats[i] != PIXMAN_null)
+	++i;
+    return formats[lcg_rand_n (i)];
+}
+
+static pixman_image_t *
+create_image (int max_size, const pixman_format_code_t *formats, uint32_t flags)
+{
+    int width, height;
+    pixman_image_t *image;
+    pixman_format_code_t format;
+    uint32_t *data;
+    int bpp;
+    int stride;
+    int i;
+    pixman_image_destroy_func_t destroy;
+
+    if ((flags & ALLOW_SOLID) && lcg_rand_n (4) == 0)
+    {
+	pixman_color_t color;
+
+	color.alpha = lcg_rand_u32();
+	color.red = lcg_rand_u32();
+	color.green = lcg_rand_u32();
+	color.blue = lcg_rand_u32();
+
+	return pixman_image_create_solid_fill (&color);
+    }
+
+    width = lcg_rand_n (max_size) + 1;
+    height = lcg_rand_n (max_size) + 1;
+    format = random_format (formats);
+
+    bpp = PIXMAN_FORMAT_BPP (format);
+    stride = (width * bpp + 7) / 8 + lcg_rand_n (17);
+    stride = (stride + 3) & ~3;
+
+    if (lcg_rand_n (64) == 0)
+    {
+	if (!(data = (uint32_t *)make_random_bytes (stride * height)))
+	{
+	    fprintf (stderr, "Out of memory\n");
+	    abort ();
+	}
+	destroy = destroy_fenced;
+    }
+    else
+    {
+	data = malloc (stride * height);
+	for (i = 0; i < height * stride / 4; ++i)
+	    data[i] = lcg_rand_u32();
+
+	destroy = destroy_malloced;
+    }
+
+    image = pixman_image_create_bits (format, width, height, data, stride);
+    pixman_image_set_destroy_function (image, destroy, data);
+
+    if ((flags & ALLOW_CLIPPED) && lcg_rand_n (8) == 0)
+    {
+	pixman_box16_t clip_boxes[8];
+	pixman_region16_t clip;
+	int n = lcg_rand_n (8) + 1;
+
+	for (i = 0; i < n; i++)
+	{
+	    clip_boxes[i].x1 = lcg_rand_n (width);
+	    clip_boxes[i].y1 = lcg_rand_n (height);
+	    clip_boxes[i].x2 =
+		clip_boxes[i].x1 + lcg_rand_n (width - clip_boxes[i].x1);
+	    clip_boxes[i].y2 =
+		clip_boxes[i].y1 + lcg_rand_n (height - clip_boxes[i].y1);
+	}
+
+	pixman_region_init_rects (&clip, clip_boxes, n);
+	pixman_image_set_clip_region (image, &clip);
+	pixman_region_fini (&clip);
+    }
+
+    if ((flags & ALLOW_SOURCE_CLIPPING) && lcg_rand_n (4) == 0)
+    {
+	pixman_image_set_source_clipping (image, TRUE);
+	pixman_image_set_has_client_clip (image, TRUE);
+    }
+
+    if ((flags & ALLOW_ALPHA_MAP) && lcg_rand_n (16) == 0)
+    {
+	pixman_image_t *alpha_map;
+	int alpha_x, alpha_y;
+
+	alpha_x = lcg_rand_n (width);
+	alpha_y = lcg_rand_n (height);
+	alpha_map =
+	    create_image (max_size, formats, (flags & ~ALLOW_ALPHA_MAP));
+	pixman_image_set_alpha_map (image, alpha_map, alpha_x, alpha_y);
+	pixman_image_unref (alpha_map);
+    }
+
+    if ((flags & ALLOW_REPEAT) && lcg_rand_n (2) == 0)
+	pixman_image_set_repeat (image, lcg_rand_n (4));
+
+    image_endian_swap (image);
+
+    return image;
+}
+
+#define KEY1(p) ((void *)(((unsigned long)p) ^ (0xa7e23dfaUL)))
+#define KEY2(p) ((void *)(((unsigned long)p) ^ (0xabcd9876UL)))
+
+#define MAX_GLYPHS 32
+
+uint32_t
+test_glyphs (int testnum, int verbose)
+{
+    pixman_image_t *glyph_images[MAX_GLYPHS];
+    pixman_glyph_t glyphs[4 * MAX_GLYPHS];
+    uint32_t crc32 = 0;
+    pixman_image_t *source, *dest;
+    int n_glyphs, i;
+    pixman_glyph_cache_t *cache;
+
+    lcg_srand (testnum);
+
+    cache = pixman_glyph_cache_create ();
+
+    source = create_image (300, formats,
+			   ALLOW_CLIPPED | ALLOW_ALPHA_MAP |
+			   ALLOW_SOURCE_CLIPPING |
+			   ALLOW_REPEAT | ALLOW_SOLID);
+
+    dest = create_image (128, formats,
+			 ALLOW_CLIPPED | ALLOW_ALPHA_MAP |
+			 ALLOW_SOURCE_CLIPPING);
+
+    pixman_glyph_cache_freeze (cache);
+
+    n_glyphs = lcg_rand_n (MAX_GLYPHS);
+    for (i = 0; i < n_glyphs; ++i)
+	glyph_images[i] = create_image (32, glyph_formats, 0);
+
+    for (i = 0; i < 4 * n_glyphs; ++i)
+    {
+	int g = lcg_rand_n (n_glyphs);
+	pixman_image_t *glyph_img = glyph_images[g];
+	void *key1 = KEY1 (glyph_img);
+	void *key2 = KEY2 (glyph_img);
+	const void *glyph;
+
+	if (!(glyph = pixman_glyph_cache_lookup (cache, key1, key2)))
+	{
+	    glyph =
+		pixman_glyph_cache_insert (cache, key1, key2, 5, 8, glyph_img);
+	}
+
+	glyphs[i].glyph = glyph;
+	glyphs[i].x = lcg_rand_n (128);
+	glyphs[i].y = lcg_rand_n (128);
+    }
+
+    if (lcg_rand_n (2) == 0)
+    {
+	int src_x = lcg_rand_n (300) - 150;
+	int src_y = lcg_rand_n (300) - 150;
+	int mask_x = lcg_rand_n (64) - 32;
+	int mask_y = lcg_rand_n (64) - 32;
+	int dest_x = lcg_rand_n (64) - 32;
+	int dest_y = lcg_rand_n (64) - 32;
+	int width = lcg_rand_n (64);
+	int height = lcg_rand_n (64);
+	pixman_op_t op = operators[lcg_rand_n (ARRAY_LENGTH (operators))];
+	pixman_format_code_t format = random_format (glyph_formats);
+
+	pixman_composite_glyphs (
+	    op,
+	    source, dest, format,
+	    src_x, src_y,
+	    mask_x, mask_y,
+	    dest_x, dest_y,
+	    width, height,
+	    cache, 4 * n_glyphs, glyphs);
+    }
+    else
+    {
+	pixman_op_t op = operators[lcg_rand_n (ARRAY_LENGTH (operators))];
+	int src_x = lcg_rand_n (300) - 150;
+	int src_y = lcg_rand_n (300) - 150;
+	int dest_x = lcg_rand_n (64) - 32;
+	int dest_y = lcg_rand_n (64) - 32;
+
+	pixman_composite_glyphs_no_mask (
+	    op, source, dest,
+	    src_x, src_y,
+	    dest_x, dest_y,
+	    cache, 4 * n_glyphs, glyphs);
+    }
+
+    pixman_glyph_cache_thaw (cache);
+
+    for (i = 0; i < n_glyphs; ++i)
+    {
+	pixman_image_t *img = glyph_images[i];
+	void *key1, *key2;
+
+	key1 = KEY1 (img);
+	key2 = KEY2 (img);
+
+	pixman_glyph_cache_remove (cache, key1, key2);
+	pixman_image_unref (glyph_images[i]);
+    }
+
+    crc32 = compute_crc32_for_image (0, dest);
+
+    pixman_image_unref (source);
+    pixman_image_unref (dest);
+
+    pixman_glyph_cache_destroy (cache);
+
+    return crc32;
+}
+
+int
+main (int argc, const char *argv[])
+{
+    return fuzzer_test_main ("glyph", 30000,
+			     0xA2B67F99,
+			     test_glyphs, argc, argv);
+}
commit dc9237472789b0b45393f6f7eeafa057a86280c4
Author: Søren Sandmann Pedersen <ssp at redhat.com>
Date:   Mon May 28 16:14:12 2012 -0400

    Add support for alpha maps to compute_crc32_for_image().
    
    When a destination image I has an alpha map A, the following rules apply:
    
       - If I has an alpha channel itself, the content of that channel is
         undefined
    
       - If A has RGB channels, the content of those channels is
         undefined.
    
    Hence in order to compute the CRC32 for such an image, we have to mask
    off the alpha channel of the image, and the RGB channels of the alpha
    map.
    
    V2: Shifting by 32 is undefined in C

diff --git a/test/utils.c b/test/utils.c
index fcf9a36..0abc32c 100644
--- a/test/utils.c
+++ b/test/utils.c
@@ -135,41 +135,103 @@ compute_crc32 (uint32_t    in_crc32,
     return (crc32 ^ 0xFFFFFFFF);
 }
 
-uint32_t
-compute_crc32_for_image (uint32_t        in_crc32,
-			 pixman_image_t *img)
+static uint32_t
+compute_crc32_for_image_internal (uint32_t        crc32,
+				  pixman_image_t *img,
+				  pixman_bool_t	  remove_alpha,
+				  pixman_bool_t	  remove_rgb)
 {
     pixman_format_code_t fmt = pixman_image_get_format (img);
     uint32_t *data = pixman_image_get_data (img);
     int stride = pixman_image_get_stride (img);
     int height = pixman_image_get_height (img);
-    uint32_t crc32;
-    
+    uint32_t mask = 0xffffffff;
+    int i;
+
     /* mask unused 'x' part */
     if (PIXMAN_FORMAT_BPP (fmt) - PIXMAN_FORMAT_DEPTH (fmt) &&
 	PIXMAN_FORMAT_DEPTH (fmt) != 0)
     {
-	uint32_t mask = (1 << PIXMAN_FORMAT_DEPTH (fmt)) - 1;
-	int i;
+	uint32_t m = (1 << PIXMAN_FORMAT_DEPTH (fmt)) - 1;
 
 	if (PIXMAN_FORMAT_TYPE (fmt) == PIXMAN_TYPE_BGRA ||
 	    PIXMAN_FORMAT_TYPE (fmt) == PIXMAN_TYPE_RGBA)
 	{
-	    mask <<= (PIXMAN_FORMAT_BPP (fmt) - PIXMAN_FORMAT_DEPTH (fmt));
+	    m <<= (PIXMAN_FORMAT_BPP (fmt) - PIXMAN_FORMAT_DEPTH (fmt));
 	}
 
-	for (i = 0; i * PIXMAN_FORMAT_BPP (fmt) < 32; i++)
-	    mask |= mask << (i * PIXMAN_FORMAT_BPP (fmt));
+	mask &= m;
+    }
+
+    /* mask alpha channel */
+    if (remove_alpha && PIXMAN_FORMAT_A (fmt))
+    {
+	uint32_t m;
+
+	if (PIXMAN_FORMAT_BPP (fmt) == 32)
+	    m = 0xffffffff;
+	else
+	    m = (1 << PIXMAN_FORMAT_BPP (fmt)) - 1;
+
+	m >>= PIXMAN_FORMAT_A (fmt);
+
+	if (PIXMAN_FORMAT_TYPE (fmt) == PIXMAN_TYPE_BGRA ||
+	    PIXMAN_FORMAT_TYPE (fmt) == PIXMAN_TYPE_RGBA ||
+	    PIXMAN_FORMAT_TYPE (fmt) == PIXMAN_TYPE_A)
+	{
+	    /* Alpha is at the bottom of the pixel */
+	    m <<= PIXMAN_FORMAT_A (fmt);
+	}
+
+	mask &= m;
+    }
+
+    /* mask rgb channels */
+    if (remove_rgb && PIXMAN_FORMAT_RGB (fmt))
+    {
+	uint32_t m = ((uint32_t)~0) >> (32 - PIXMAN_FORMAT_BPP (fmt));
+	uint32_t size = PIXMAN_FORMAT_R (fmt) + PIXMAN_FORMAT_G (fmt) + PIXMAN_FORMAT_B (fmt);
+
+	m &= ~((1 << size) - 1);
+
+	if (PIXMAN_FORMAT_TYPE (fmt) == PIXMAN_TYPE_BGRA ||
+	    PIXMAN_FORMAT_TYPE (fmt) == PIXMAN_TYPE_RGBA)
+	{
+	    /* RGB channels are at the top of the pixel */
+	    m >>= size;
+	}
 
-	for (i = 0; i < stride * height / 4; i++)
-	    data[i] &= mask;
+	mask &= m;
     }
 
+    for (i = 0; i * PIXMAN_FORMAT_BPP (fmt) < 32; i++)
+	mask |= mask << (i * PIXMAN_FORMAT_BPP (fmt));
+
+    for (i = 0; i < stride * height / 4; i++)
+	data[i] &= mask;
+
     /* swap endiannes in order to provide identical results on both big
      * and litte endian systems
      */
     image_endian_swap (img);
-    crc32 = compute_crc32 (in_crc32, data, stride * height);
+
+    return compute_crc32 (crc32, data, stride * height);
+}
+
+uint32_t
+compute_crc32_for_image (uint32_t        crc32,
+			 pixman_image_t *img)
+{
+    if (img->common.alpha_map)
+    {
+	crc32 = compute_crc32_for_image_internal (crc32, img, TRUE, FALSE);
+	crc32 = compute_crc32_for_image_internal (
+	    crc32, (pixman_image_t *)img->common.alpha_map, FALSE, TRUE);
+    }
+    else
+    {
+	crc32 = compute_crc32_for_image_internal (crc32, img, FALSE, FALSE);
+    }
 
     return crc32;
 }
commit 43e029d525c191a771e5b964fccff09b6d341bb2
Author: Søren Sandmann Pedersen <ssp at redhat.com>
Date:   Sun May 27 13:38:14 2012 -0400

    Move CRC32 computation from blitters-test.c into utils.c
    
    This way it can be used in other tests.

diff --git a/test/blitters-test.c b/test/blitters-test.c
index feea308..6a3cc86 100644
--- a/test/blitters-test.c
+++ b/test/blitters-test.c
@@ -83,39 +83,10 @@ free_random_image (uint32_t initcrc,
 		   pixman_format_code_t fmt)
 {
     uint32_t crc32 = 0;
-    int stride = pixman_image_get_stride (img);
     uint32_t *data = pixman_image_get_data (img);
-    int height = pixman_image_get_height (img);
 
     if (fmt != PIXMAN_null)
-    {
-	/* mask unused 'x' part */
-	if (PIXMAN_FORMAT_BPP (fmt) - PIXMAN_FORMAT_DEPTH (fmt) &&
-	    PIXMAN_FORMAT_DEPTH (fmt) != 0)
-	{
-	    int i;
-	    uint32_t *data = pixman_image_get_data (img);
-	    uint32_t mask = (1 << PIXMAN_FORMAT_DEPTH (fmt)) - 1;
-
-	    if (PIXMAN_FORMAT_TYPE (fmt) == PIXMAN_TYPE_BGRA ||
-		PIXMAN_FORMAT_TYPE (fmt) == PIXMAN_TYPE_RGBA)
-	    {
-		mask <<= (PIXMAN_FORMAT_BPP (fmt) - PIXMAN_FORMAT_DEPTH (fmt));
-	    }
-
-	    for (i = 0; i * PIXMAN_FORMAT_BPP (fmt) < 32; i++)
-		mask |= mask << (i * PIXMAN_FORMAT_BPP (fmt));
-
-	    for (i = 0; i < stride * height / 4; i++)
-		data[i] &= mask;
-	}
-
-	/* swap endiannes in order to provide identical results on both big
-	 * and litte endian systems
-	 */
-	image_endian_swap (img);
-	crc32 = compute_crc32 (initcrc, data, stride * height);
-    }
+	crc32 = compute_crc32_for_image (initcrc, img);
 
     pixman_image_unref (img);
     free (data);
diff --git a/test/utils.c b/test/utils.c
index c1bf6dc..fcf9a36 100644
--- a/test/utils.c
+++ b/test/utils.c
@@ -135,6 +135,45 @@ compute_crc32 (uint32_t    in_crc32,
     return (crc32 ^ 0xFFFFFFFF);
 }
 
+uint32_t
+compute_crc32_for_image (uint32_t        in_crc32,
+			 pixman_image_t *img)
+{
+    pixman_format_code_t fmt = pixman_image_get_format (img);
+    uint32_t *data = pixman_image_get_data (img);
+    int stride = pixman_image_get_stride (img);
+    int height = pixman_image_get_height (img);
+    uint32_t crc32;
+    
+    /* mask unused 'x' part */
+    if (PIXMAN_FORMAT_BPP (fmt) - PIXMAN_FORMAT_DEPTH (fmt) &&
+	PIXMAN_FORMAT_DEPTH (fmt) != 0)
+    {
+	uint32_t mask = (1 << PIXMAN_FORMAT_DEPTH (fmt)) - 1;
+	int i;
+
+	if (PIXMAN_FORMAT_TYPE (fmt) == PIXMAN_TYPE_BGRA ||
+	    PIXMAN_FORMAT_TYPE (fmt) == PIXMAN_TYPE_RGBA)
+	{
+	    mask <<= (PIXMAN_FORMAT_BPP (fmt) - PIXMAN_FORMAT_DEPTH (fmt));
+	}
+
+	for (i = 0; i * PIXMAN_FORMAT_BPP (fmt) < 32; i++)
+	    mask |= mask << (i * PIXMAN_FORMAT_BPP (fmt));
+
+	for (i = 0; i < stride * height / 4; i++)
+	    data[i] &= mask;
+    }
+
+    /* swap endiannes in order to provide identical results on both big
+     * and litte endian systems
+     */
+    image_endian_swap (img);
+    crc32 = compute_crc32 (in_crc32, data, stride * height);
+
+    return crc32;
+}
+
 pixman_bool_t
 is_little_endian (void)
 {
diff --git a/test/utils.h b/test/utils.h
index 01af316..ac2decd 100644
--- a/test/utils.h
+++ b/test/utils.h
@@ -63,7 +63,12 @@ compute_crc32 (uint32_t    in_crc32,
 	       const void *buf,
 	       size_t      buf_len);
 
-/* Returns TRUE if running on a little endian system */
+uint32_t
+compute_crc32_for_image (uint32_t        in_crc32,
+			 pixman_image_t *image);
+
+/* Returns TRUE if running on a little endian system
+ */
 pixman_bool_t
 is_little_endian (void);
 
commit fce31a5ef8c915ee6b7aee4c6b57bee245185c1f
Author: Søren Sandmann Pedersen <ssp at redhat.com>
Date:   Tue May 29 04:14:38 2012 -0400

    Add pixman_glyph_cache_t API
    
    This new API allows entire glyph strings to be composited in one go
    which reduces overhead compared to multiple calls to
    pixman_image_composite32().
    
    The pixman_glyph_cache_t is a hash table that maps two keys (a "font"
    and a "glyph" key, but they are just keys; there is no distinction
    between them as far as pixman is concerned) to a glyph. Glyphs in the
    cache can be composited through two new entry points
    pixman_glyph_cache_composite_glyphs() and
    pixman_glyph_cache_composite_glyphs_no_mask().
    
    A glyph cache may only be inserted into when it is "frozen", which is
    achieved by calling pixman_glyph_cache_freeze(). When
    pixman_glyph_cache_thaw() is later called, if the cache has become too
    crowded, some glyphs (currently the least-recently-used) will
    automatically be evicted. This means that a user must ensure that all
    the required glyphs are present in the cache before compositing a
    string. The intended way to use the cache is like this:
    
            pixman_glyph_t glyphs[MAX_GLYPHS];
    
            pixman_glyph_cache_freeze (cache);
    
            for (i = 0; i < n_glyphs; ++i)
            {
                const void *g;
    
                if (!(g = pixman_glyph_cache_lookup (cache, font_key, glyph_key)))
                {
                    img = <rasterize glyph as a pixman_image_t>;
    
                    g = pixman_glyph_cache_insert (cache, font_key, glyph_key,
                                                   glyph_origin_x, glyph_origin_y,
                                                   img);
    
                    if (!g)
                    {
                        /* Clean up out-of-memory condition */
                        goto oom;
                    }
    
                    glyphs[i].pos_x = glyph_x_pos;
                    glyphs[i].pos_y = glyph_y_pos;
                    glyphs[i].glyph = g;
                }
            }
    
            pixman_composite_glyphs (op, src, dest, ..., cache, n_glyphs, glyphs);
    
            pixman_glyph_cache_thaw (cache);
    
    V2:
    - Move glyphs to front of the MRU list when they are used. Pointed
      out by Behdad Esfahbod.
    - Composite glyphs with (white IN glyph) ADD mask in order to support
      mixed a8 and a8r8g8b8 glyphs. Also pointed out by Behdad.
    - Add pixman_glyph_get_mask_format

diff --git a/pixman/Makefile.sources b/pixman/Makefile.sources
index ca3f001..11f959d 100644
--- a/pixman/Makefile.sources
+++ b/pixman/Makefile.sources
@@ -10,6 +10,7 @@ libpixman_sources =			\
 	pixman-edge.c			\
 	pixman-edge-accessors.c		\
 	pixman-fast-path.c		\
+	pixman-glyph.c			\
 	pixman-general.c		\
 	pixman-gradient-walker.c	\
 	pixman-image.c			\
diff --git a/pixman/pixman-glyph.c b/pixman/pixman-glyph.c
new file mode 100644
index 0000000..30acc36
--- /dev/null
+++ b/pixman/pixman-glyph.c
@@ -0,0 +1,482 @@
+/*
+ * Copyright 2010, 2012, Soren Sandmann <sandmann at cs.au.dk>
+ * Copyright 2010, 2011, 2012, Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Soren Sandmann <sandmann at cs.au.dk>
+ */
+#include <config.h>
+#include <stdlib.h>
+#include "pixman-private.h"
+
+typedef struct glyph_metrics_t glyph_metrics_t;
+typedef struct glyph_t glyph_t;
+
+#define TOMBSTONE ((glyph_t *)0x1)
+
+/* XXX: These numbers are arbitrary---we've never done any measurements.
+ */
+#define N_GLYPHS_HIGH_WATER  (16384)
+#define N_GLYPHS_LOW_WATER   (8192)
+#define HASH_SIZE (2 * N_GLYPHS_HIGH_WATER)
+#define HASH_MASK (HASH_SIZE - 1)
+
+struct glyph_t
+{
+    void *		font_key;
+    void *		glyph_key;
+    int			origin_x;
+    int			origin_y;
+    pixman_image_t *	image;
+    pixman_link_t	mru_link;
+};
+
+struct pixman_glyph_cache_t
+{
+    int			n_glyphs;
+    int			n_tombstones;
+    int			freeze_count;
+    pixman_list_t	mru;
+    glyph_t *		glyphs[HASH_SIZE];
+};
+
+static void
+free_glyph (glyph_t *glyph)
+{
+    pixman_list_unlink (&glyph->mru_link);
+    pixman_image_unref (glyph->image);
+    free (glyph);
+}
+
+static unsigned int
+hash (const void *font_key, const void *glyph_key)
+{
+    size_t key = (size_t)font_key + (size_t)glyph_key;
+
+    /* This hash function is based on one found on Thomas Wang's
+     * web page at
+     *
+     *    http://www.concentric.net/~Ttwang/tech/inthash.htm
+     *
+     */
+    key = (key << 15) - key - 1;
+    key = key ^ (key >> 12);
+    key = key + (key << 2);
+    key = key ^ (key >> 4);
+    key = key + (key << 3) + (key << 11);
+    key = key ^ (key >> 16);
+
+    return key;
+}
+
+static glyph_t *
+lookup_glyph (pixman_glyph_cache_t *cache,
+	      void                 *font_key,
+	      void                 *glyph_key)
+{
+    unsigned idx;
+    glyph_t *g;
+
+    idx = hash (font_key, glyph_key);
+    while ((g = cache->glyphs[idx++ & HASH_MASK]))
+    {
+	if (g != TOMBSTONE			&&
+	    g->font_key == font_key		&&
+	    g->glyph_key == glyph_key)
+	{
+	    return g;
+	}
+    }
+
+    return NULL;
+}
+
+static void
+insert_glyph (pixman_glyph_cache_t *cache,
+	      glyph_t              *glyph)
+{
+    unsigned idx;
+    glyph_t **loc;
+
+    idx = hash (glyph->font_key, glyph->glyph_key);
+
+    /* Note: we assume that there is room in the table. If there isn't,
+     * this will be an infinite loop.
+     */
+    do
+    {
+	loc = &cache->glyphs[idx++ & HASH_MASK];
+    } while (*loc && *loc != TOMBSTONE);
+
+    if (*loc == TOMBSTONE)
+	cache->n_tombstones--;
+    cache->n_glyphs++;
+
+    *loc = glyph;
+}
+
+static void
+remove_glyph (pixman_glyph_cache_t *cache,
+	      glyph_t              *glyph)
+{
+    unsigned idx;
+
+    idx = hash (glyph->font_key, glyph->glyph_key);
+    while (cache->glyphs[idx & HASH_MASK] != glyph)
+	idx++;
+
+    cache->glyphs[idx & HASH_MASK] = TOMBSTONE;
+    cache->n_tombstones++;
+    cache->n_glyphs--;
+
+    /* Eliminate tombstones if possible */
+    if (cache->glyphs[(idx + 1) & HASH_MASK] == NULL)
+    {
+	while (cache->glyphs[idx & HASH_MASK] == TOMBSTONE)
+	{
+	    cache->glyphs[idx & HASH_MASK] = NULL;
+	    cache->n_tombstones--;
+	    idx--;
+	}
+    }
+}
+
+static void
+clear_table (pixman_glyph_cache_t *cache)
+{
+    int i;
+
+    for (i = 0; i < HASH_SIZE; ++i)
+    {
+	glyph_t *glyph = cache->glyphs[i];
+
+	if (glyph && glyph != TOMBSTONE)
+	    free_glyph (glyph);
+
+	cache->glyphs[i] = NULL;
+    }
+
+    cache->n_glyphs = 0;
+    cache->n_tombstones = 0;
+}
+
+PIXMAN_EXPORT pixman_glyph_cache_t *
+pixman_glyph_cache_create (void)
+{
+    pixman_glyph_cache_t *cache;
+
+    if (!(cache = malloc (sizeof *cache)))
+	return NULL;
+
+    memset (cache->glyphs, 0, sizeof (cache->glyphs));
+    cache->n_glyphs = 0;
+    cache->n_tombstones = 0;
+    cache->freeze_count = 0;
+
+    pixman_list_init (&cache->mru);
+
+    return cache;
+}
+
+PIXMAN_EXPORT void
+pixman_glyph_cache_destroy (pixman_glyph_cache_t *cache)
+{
+    return_if_fail (cache->freeze_count == 0);
+
+    clear_table (cache);
+
+    free (cache);
+}
+
+PIXMAN_EXPORT void
+pixman_glyph_cache_freeze (pixman_glyph_cache_t  *cache)
+{
+    cache->freeze_count++;
+}
+
+PIXMAN_EXPORT void
+pixman_glyph_cache_thaw (pixman_glyph_cache_t  *cache)
+{
+    if (--cache->freeze_count == 0					&&
+	cache->n_glyphs + cache->n_tombstones > N_GLYPHS_HIGH_WATER)
+    {
+	if (cache->n_tombstones > N_GLYPHS_HIGH_WATER)
+	{
+	    /* More than half the entries are
+	     * tombstones. Just dump the whole table.
+	     */
+	    clear_table (cache);
+	}
+
+	while (cache->n_glyphs > N_GLYPHS_LOW_WATER)
+	{
+	    glyph_t *glyph = CONTAINER_OF (glyph_t, mru_link, cache->mru.tail);
+
+	    remove_glyph (cache, glyph);
+	    free_glyph (glyph);
+	}
+    }
+}
+
+PIXMAN_EXPORT const void *
+pixman_glyph_cache_lookup (pixman_glyph_cache_t  *cache,
+			   void                  *font_key,
+			   void                  *glyph_key)
+{
+    return lookup_glyph (cache, font_key, glyph_key);
+}
+
+PIXMAN_EXPORT const void *
+pixman_glyph_cache_insert (pixman_glyph_cache_t  *cache,
+			   void                  *font_key,
+			   void                  *glyph_key,
+			   int			  origin_x,
+			   int                    origin_y,
+			   pixman_image_t        *image)
+{
+    glyph_t *glyph;
+    int32_t width, height;
+
+    return_val_if_fail (cache->freeze_count > 0, NULL);
+    return_val_if_fail (image->type == BITS, NULL);
+
+    width = image->bits.width;
+    height = image->bits.height;
+
+    if (cache->n_glyphs >= HASH_SIZE)
+	return NULL;
+
+    if (!(glyph = malloc (sizeof *glyph)))
+	return NULL;
+
+    glyph->font_key = font_key;
+    glyph->glyph_key = glyph_key;
+    glyph->origin_x = origin_x;
+    glyph->origin_y = origin_y;
+
+    if (!(glyph->image = pixman_image_create_bits (
+	      image->bits.format, width, height, NULL, -1)))
+    {
+	free (glyph);
+	return NULL;
+    }
+
+    pixman_image_composite32 (PIXMAN_OP_SRC,
+			      image, NULL, glyph->image, 0, 0, 0, 0, 0, 0,
+			      width, height);
+
+    if (PIXMAN_FORMAT_A   (glyph->image->bits.format) != 0	&&
+	PIXMAN_FORMAT_RGB (glyph->image->bits.format) != 0)
+    {
+	pixman_image_set_component_alpha (glyph->image, TRUE);
+    }
+
+    pixman_list_prepend (&cache->mru, &glyph->mru_link);
+
+    _pixman_image_validate (glyph->image);
+    insert_glyph (cache, glyph);
+
+    return glyph;
+}
+
+PIXMAN_EXPORT void
+pixman_glyph_cache_remove (pixman_glyph_cache_t  *cache,
+			   void                  *font_key,
+			   void                  *glyph_key)
+{
+    glyph_t *glyph;
+
+    if ((glyph = lookup_glyph (cache, font_key, glyph_key)))
+    {
+	remove_glyph (cache, glyph);
+
+	free_glyph (glyph);
+    }
+}
+
+PIXMAN_EXPORT void
+pixman_glyph_get_extents (pixman_glyph_cache_t *cache,
+			  int                   n_glyphs,
+			  pixman_glyph_t       *glyphs,
+			  pixman_box32_t       *extents)
+{
+    int i;
+
+    extents->x1 = extents->y1 = INT32_MAX;
+    extents->x2 = extents->y2 = INT32_MIN;
+
+    for (i = 0; i < n_glyphs; ++i)
+    {
+	glyph_t *glyph = (glyph_t *)glyphs[i].glyph;
+	int x1, y1, x2, y2;
+
+	x1 = glyphs[i].x - glyph->origin_x;
+	y1 = glyphs[i].y - glyph->origin_y;
+	x2 = glyphs[i].x - glyph->origin_x + glyph->image->bits.width;
+	y2 = glyphs[i].y - glyph->origin_y + glyph->image->bits.height;
+
+	if (x1 < extents->x1)
+	    extents->x1 = x1;
+	if (y1 < extents->y1)
+	    extents->y1 = y1;
+	if (x2 > extents->x2)
+	    extents->x2 = x2;
+	if (y2 > extents->y2)
+	    extents->y2 = y2;
+    }
+}
+
+/* This function returns a format that is suitable for use as a mask for the
+ * set of glyphs in question.
+ */
+PIXMAN_EXPORT pixman_format_code_t
+pixman_glyph_get_mask_format (pixman_glyph_cache_t *cache,
+			      int		    n_glyphs,
+			      pixman_glyph_t *      glyphs)
+{
+    pixman_format_code_t format = PIXMAN_a1;
+    int i;
+
+    for (i = 0; i < n_glyphs; ++i)
+    {
+	const glyph_t *glyph = glyphs[i].glyph;
+	pixman_format_code_t glyph_format = glyph->image->bits.format;
+
+	if (PIXMAN_FORMAT_TYPE (glyph_format) == PIXMAN_TYPE_A)
+	{
+	    if (PIXMAN_FORMAT_A (glyph_format) > PIXMAN_FORMAT_A (format))
+		format = glyph_format;
+	}
+	else
+	{
+	    return PIXMAN_a8r8g8b8;
+	}
+    }
+
+    return format;
+}
+
+PIXMAN_EXPORT void
+pixman_composite_glyphs_no_mask (pixman_op_t            op,
+				 pixman_image_t        *src,
+				 pixman_image_t        *dest,
+				 int32_t                src_x,
+				 int32_t                src_y,
+				 int32_t                dest_x,
+				 int32_t                dest_y,
+				 pixman_glyph_cache_t  *cache,
+				 int                    n_glyphs,
+				 pixman_glyph_t        *glyphs)
+{
+    int i;
+
+    for (i = 0; i < n_glyphs; ++i)
+    {
+	glyph_t *glyph = (glyph_t *)glyphs[i].glyph;
+	pixman_image_t *glyph_img = glyph->image;
+
+	pixman_image_composite32 (op, src, glyph_img, dest,
+				  src_x + glyphs[i].x - glyph->origin_x,
+				  src_y + glyphs[i].y - glyph->origin_y,
+				  0, 0,
+				  dest_x + glyphs[i].x - glyph->origin_x,
+				  dest_y + glyphs[i].y - glyph->origin_y,
+				  glyph_img->bits.width,
+				  glyph_img->bits.height);
+
+	pixman_list_move_to_front (&cache->mru, &glyph->mru_link);
+    }
+}
+
+/* Conceptually, for each glyph, (white IN glyph) is PIXMAN_OP_ADDed to an
+ * infinitely big mask image at the position such that the glyph origin point
+ * is positioned at the (glyphs[i].x, glyphs[i].y) point.
+ *
+ * Then (mask_x, mask_y) in the infinite mask and (src_x, src_y) in the source
+ * image are both aligned with (dest_x, dest_y) in the destination image. Then
+ * these three images are composited within the 
+ *
+ *       (dest_x, dest_y, dst_x + width, dst_y + height)
+ *
+ * rectangle.
+ *
+ * TODO:
+ *   - Trim the mask to the destination clip/image?
+ *   - Trim composite region based on sources, when the op ignores 0s.
+ */
+PIXMAN_EXPORT void
+pixman_composite_glyphs (pixman_op_t            op,
+			 pixman_image_t        *src,
+			 pixman_image_t        *dest,
+			 pixman_format_code_t   mask_format,
+			 int32_t                src_x,
+			 int32_t                src_y,
+			 int32_t		mask_x,
+			 int32_t		mask_y,
+			 int32_t                dest_x,
+			 int32_t                dest_y,
+			 int32_t                width,
+			 int32_t                height,
+			 pixman_glyph_cache_t  *cache,
+			 int			n_glyphs,
+			 pixman_glyph_t        *glyphs)
+{
+    pixman_color_t white_color = { 0xffff, 0xffff, 0xffff, 0xffff };
+    pixman_image_t *white;
+    pixman_image_t *mask;
+    int i;
+
+    if (!(mask = pixman_image_create_bits (mask_format, width, height, NULL, -1)))
+	return;
+
+    if (!(white = pixman_image_create_solid_fill (&white_color)))
+	goto out;
+
+    if (PIXMAN_FORMAT_A   (mask_format) != 0 &&
+	PIXMAN_FORMAT_RGB (mask_format) != 0)
+    {
+	pixman_image_set_component_alpha (mask, TRUE);
+    }
+
+    for (i = 0; i < n_glyphs; ++i)
+    {
+	glyph_t *glyph = (glyph_t *)glyphs[i].glyph;
+	pixman_image_t *glyph_img = glyph->image;
+
+	pixman_image_composite32 (PIXMAN_OP_ADD, white, glyph_img, mask,
+				  0, 0, 0, 0,
+				  glyphs[i].x - glyph->origin_x - mask_x,
+				  glyphs[i].y - glyph->origin_y - mask_y,
+				  glyph->image->bits.width,
+				  glyph->image->bits.height);
+
+	pixman_list_move_to_front (&cache->mru, &glyph->mru_link);
+    }
+
+    pixman_image_composite32 (op, src, mask, dest,
+			      src_x, src_y,
+			      0, 0,
+			      dest_x, dest_y,
+			      width, height);
+
+out:
+    pixman_image_unref (mask);
+}
diff --git a/pixman/pixman.h b/pixman/pixman.h
index 18d9513..7233ceb 100644
--- a/pixman/pixman.h
+++ b/pixman/pixman.h
@@ -868,6 +868,65 @@ void          pixman_image_composite32        (pixman_op_t        op,
 void pixman_disable_out_of_bounds_workaround (void);
 
 /*
+ * Glyphs
+ */
+typedef struct pixman_glyph_cache_t pixman_glyph_cache_t;
+typedef struct
+{
+    int		x, y;
+    const void *glyph;
+} pixman_glyph_t;
+
+pixman_glyph_cache_t *pixman_glyph_cache_create       (void);
+void                  pixman_glyph_cache_destroy      (pixman_glyph_cache_t *cache);
+void                  pixman_glyph_cache_freeze       (pixman_glyph_cache_t *cache);
+void                  pixman_glyph_cache_thaw         (pixman_glyph_cache_t *cache);
+const void *          pixman_glyph_cache_lookup       (pixman_glyph_cache_t *cache,
+						       void                 *font_key,
+						       void                 *glyph_key);
+const void *          pixman_glyph_cache_insert       (pixman_glyph_cache_t *cache,
+						       void                 *font_key,
+						       void                 *glyph_key,
+						       int		     origin_x,
+						       int                   origin_y,
+						       pixman_image_t       *glyph_image);
+void                  pixman_glyph_cache_remove       (pixman_glyph_cache_t *cache,
+						       void                 *font_key,
+						       void                 *glyph_key);
+void                  pixman_glyph_get_extents        (pixman_glyph_cache_t *cache,
+						       int                   n_glyphs,
+						       pixman_glyph_t       *glyphs,
+						       pixman_box32_t       *extents);
+pixman_format_code_t  pixman_glyph_get_mask_format    (pixman_glyph_cache_t *cache,
+						       int		     n_glyphs,
+						       pixman_glyph_t *      glyphs);
+void                  pixman_composite_glyphs         (pixman_op_t           op,
+						       pixman_image_t       *src,
+						       pixman_image_t       *dest,
+						       pixman_format_code_t  mask_format,
+						       int32_t               src_x,
+						       int32_t               src_y,
+						       int32_t		     mask_x,
+						       int32_t		     mask_y,
+						       int32_t               dest_x,
+						       int32_t               dest_y,
+						       int32_t		     width,
+						       int32_t		     height,
+						       pixman_glyph_cache_t *cache,
+						       int		     n_glyphs,
+						       pixman_glyph_t       *glyphs);
+void                  pixman_composite_glyphs_no_mask (pixman_op_t           op,
+						       pixman_image_t       *src,
+						       pixman_image_t       *dest,
+						       int32_t               src_x,
+						       int32_t               src_y,
+						       int32_t               dest_x,
+						       int32_t               dest_y,
+						       pixman_glyph_cache_t *cache,
+						       int		     n_glyphs,
+						       pixman_glyph_t       *glyphs);
+
+/*
  * Trapezoids
  */
 typedef struct pixman_edge pixman_edge_t;
commit a3ae88b71b9d2dfc53303963157ecce4b29f0486
Author: Søren Sandmann Pedersen <ssp at redhat.com>
Date:   Wed Apr 27 12:07:16 2011 -0400

    Add doubly linked lists
    
    This commit adds some new inline functions to maintain a doubly linked
    list.
    
    The way to use them is to embed a pixman_link_t into the structures
    that should be linked, and use a pixman_list_t as the head of the
    list.
    
    The new functions are
    
        pixman_list_init (pixman_list_t *list);
        pixman_list_prepend (pixman_list_t *list, pixman_link_t *link);
        pixman_list_move_to_front (pixman_list_t *list, pixman_link_t *link);
    
    There are also a new macro:
    
        CONTAINER_OF(type, member, data);
    
    that can be used to get from a pointer to a member to the containing
    structure.
    
    V2: Use the C89 macro offsetof() instead of rolling our own -
    suggested by Alan Coopersmith.

diff --git a/pixman/pixman-compiler.h b/pixman/pixman-compiler.h
index ffd5172..a978acc 100644
--- a/pixman/pixman-compiler.h
+++ b/pixman/pixman-compiler.h
@@ -89,6 +89,10 @@
 #   define PIXMAN_EXPORT
 #endif
 
+/* member offsets */
+#define CONTAINER_OF(type, member, data)				\
+    ((type *)(((uint8_t *)data) - offsetof (type, member)))
+
 /* TLS */
 #if defined(PIXMAN_NO_TLS)
 
diff --git a/pixman/pixman-private.h b/pixman/pixman-private.h
index f4ca632..76d65e2 100644
--- a/pixman/pixman-private.h
+++ b/pixman/pixman-private.h
@@ -13,6 +13,7 @@
 #include <assert.h>
 #include <stdio.h>
 #include <string.h>
+#include <stddef.h>
 
 #include "pixman-compiler.h"
 
@@ -736,6 +737,50 @@ pixman_bool_t
 pixman_region16_copy_from_region32 (pixman_region16_t *dst,
                                     pixman_region32_t *src);
 
+/* Doubly linked lists */
+typedef struct pixman_link_t pixman_link_t;
+struct pixman_link_t
+{
+    pixman_link_t *next;
+    pixman_link_t *prev;
+};
+
+typedef struct pixman_list_t pixman_list_t;
+struct pixman_list_t
+{
+    pixman_link_t *head;
+    pixman_link_t *tail;
+};
+
+static force_inline void
+pixman_list_init (pixman_list_t *list)
+{
+    list->head = (pixman_link_t *)list;
+    list->tail = (pixman_link_t *)list;
+}
+
+static force_inline void
+pixman_list_prepend (pixman_list_t *list, pixman_link_t *link)
+{
+    link->next = list->head;
+    link->prev = (pixman_link_t *)list;
+    list->head->prev = link;
+    list->head = link;
+}
+
+static force_inline void
+pixman_list_unlink (pixman_link_t *link)
+{
+    link->prev->next = link->next;
+    link->next->prev = link->prev;
+}
+
+static force_inline void
+pixman_list_move_to_front (pixman_list_t *list, pixman_link_t *link)
+{
+    pixman_list_unlink (link);
+    pixman_list_prepend (list, link);
+}
 
 /* Misc macros */
 


More information about the xorg-commit mailing list