[PATCH] spans

Chris Wilson <[email protected]>
Newsgroups gmane.comp.graphics.pixman,gmane.comp.lib.cairo
Message-ID <[email protected]>
Taekjun Kim made a good suggestion on how to eliminate the majority of
the noise in the previous patch: simply handle opacity by first combining
(mask IN opacity) in the general_composite_rect. The rest of the patch is
then updating the internal API to propagate an opacity value from the new
public interface.

After a couple of weeks of work on the consumer of that public API, I've
concluded that the minimal interface of adding a single opacity value that
is equivalent to a mask is the simplest and yet complete enough for use by
cairo to implement clipped shapes with an unbounded operator with no less
efficiency than for bounded operations. (For the unbounded case, we create
a second compositor using OUT_REVERSE with WHITE and select the compositor
based on a bit in the span that indicates whether that span is
inside/outside the shape.)

Performing a pre-combine pass for opacity does measurably degrade
performance over implementing new opacity combiners (2% total time in
replaying a HTML5 canvas benchmark) - but the goal is not to hit the
general path at all! :)

I suspect that spans in their current form may prove hard to optimise
since the typical length of each non-opaque run will only be a pixel, with
the interior hitting the current opaque fast paths. (The exception to the
rule here is the prevalence of misaligned horizontal lines which includes
many benchmarks.) In theory, the true benefit then is this gives Cairo the
ability to implement single pass polygon fill, and speed up diagonal lines!
However, with only a fast C-path it is often faster to build an a8 mask and
then do a single SSE2 path than to operate with spans:

nearly-horizontal (dy of 1 pixel), 1 pixel wide lines:
          32      64    128    256    512
2-pass:  5206   5618   5971   6097   6967
1-pass:  8382   9115   9619   9528  10538

nearly-vertical (dx of 1 pixel), 1 pixel wide lines:
          32      64    128    256    512
2-pass:  8551   9164  14394  25218  50863
1-pass:  9283  13919  23135  42923  90104

diagonal (dx == dy), 1 pixel wide lines:
           32      64    128    256     512
2-pass:   8437  13077  24981  55254  153142
1-pass:  11898  18897  33314  61741  118610

(us, all measured on an Arrandale i7-640).

From which I can see that there is potential here, but it is dwarfed by the
overheads in the current implementation. My next step is to change the
rasterisers to spit out a RLE scanline in the form of (count, opacity[],
runs[]) and pass the arrays of spans to pixman. I felt this would be a
more invasive change and so avoided it... Suggestions very welcome.

I've pushed this patch along with conversion of fast-paths and the
lowlevel-bench to

  http://cgit.freedesktop.org/~ickle/pixman/log/?h=wip/spans

-Chris

---
 pixman/pixman-fast-path.c |    6 +-
 pixman/pixman-general.c   |   78 +++++++++-
 pixman/pixman-inlines.h   |   48 +++---
 pixman/pixman-noop.c      |    2 +-
 pixman/pixman-private.h   |   38 +++++-
 pixman/pixman.c           |  354 +++++++++++++++++++++++++++++++++++++++++---
 pixman/pixman.h           |   26 ++++
 test/lowlevel-blt-bench.c |    1 +
 8 files changed, 488 insertions(+), 65 deletions(-)

diff --git a/pixman/pixman-fast-path.c b/pixman/pixman-fast-path.c
index 7fd4478..a8cd9ba 100644
--- a/pixman/pixman-fast-path.c
+++ b/pixman/pixman-fast-path.c
@@ -1829,7 +1829,7 @@ static const pixman_fast_path_t c_fast_paths[] =
     SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, r5g6b5, 8888_565),
 
 #define NEAREST_FAST_PATH(op,s,d)		\
-    {   PIXMAN_OP_ ## op,			\
+    {   PIXMAN_OP_ ## op, 0,\
 	PIXMAN_ ## s, SCALED_NEAREST_FLAGS,	\
 	PIXMAN_null, 0,				\
 	PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS,	\
@@ -1863,13 +1863,13 @@ static const pixman_fast_path_t c_fast_paths[] =
      FAST_PATH_STANDARD_FLAGS)
 
 #define SIMPLE_ROTATE_FAST_PATH(op,s,d,suffix)				  \
-    {   PIXMAN_OP_ ## op,						  \
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			  \
 	PIXMAN_ ## s, SIMPLE_ROTATE_FLAGS (90),				  \
 	PIXMAN_null, 0,							  \
 	PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS,				  \
 	fast_composite_rotate_90_##suffix,				  \
     },									  \
-    {   PIXMAN_OP_ ## op,						  \
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			  \
 	PIXMAN_ ## s, SIMPLE_ROTATE_FLAGS (270),			  \
 	PIXMAN_null, 0,							  \
 	PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS,				  \
diff --git a/pixman/pixman-general.c b/pixman/pixman-general.c
index c24a3f3..ede6a09 100644
--- a/pixman/pixman-general.c
+++ b/pixman/pixman-general.c
@@ -104,10 +104,11 @@ general_composite_rect  (pixman_implementation_t *imp,
                          pixman_composite_info_t *info)
 {
     PIXMAN_COMPOSITE_ARGS (info);
-    uint64_t stack_scanline_buffer[(SCANLINE_BUFFER_LENGTH * 3 + 7) / 8];
+    uint64_t stack_scanline_buffer[(SCANLINE_BUFFER_LENGTH * 4 + 7) / 8];
     uint8_t *scanline_buffer = (uint8_t *) stack_scanline_buffer;
-    uint8_t *src_buffer, *mask_buffer, *dest_buffer;
-    pixman_iter_t src_iter, mask_iter, dest_iter;
+    uint8_t *src_buffer, *mask_buffer, *dest_buffer, *opacity_buffer, *ptr;
+    pixman_iter_t src_iter, mask_iter, dest_iter, opacity_iter;
+    pixman_image_t *opacity_image = NULL;
     pixman_combine_32_func_t compose;
     pixman_bool_t component_alpha;
     iter_flags_t narrow, src_flags;
@@ -129,15 +130,26 @@ general_composite_rect  (pixman_implementation_t *imp,
 
     if (width * Bpp > SCANLINE_BUFFER_LENGTH)
     {
-	scanline_buffer = pixman_malloc_abc (width, 3, Bpp);
+	scanline_buffer = pixman_malloc_abc (width, 4, Bpp);
 
 	if (!scanline_buffer)
 	    return;
     }
 
-    src_buffer = scanline_buffer;
-    mask_buffer = src_buffer + width * Bpp;
-    dest_buffer = mask_buffer + width * Bpp;
+    ptr = scanline_buffer;
+
+    src_buffer = ptr;
+    ptr += width * Bpp;
+
+    mask_buffer = ptr;
+    if (mask_image)
+	ptr += width * Bpp;
+
+    opacity_buffer = ptr;
+    if (opacity != 0xff)
+	ptr += width * Bpp;
+
+    dest_buffer = ptr;
 
     /* src iter */
     src_flags = narrow | op_flags[op].src;
@@ -166,6 +178,47 @@ general_composite_rect  (pixman_implementation_t *imp,
 	imp->toplevel, &mask_iter, mask_image, mask_x, mask_y, width, height,
 	mask_buffer, narrow | (component_alpha? 0 : ITER_IGNORE_RGB));
 
+    if (opacity != 0xff)
+    {
+	pixman_color_t color;
+
+	/* In order to implement LERP correctly we premultiply the mask
+	 * channel by opacity. This means that we may have to create
+	 * the mask channel...
+	 */
+
+	color.red = color.green = color.blue = 0;
+	color.alpha = opacity << 8 | opacity;
+	opacity_image = pixman_image_create_solid_fill (&color);
+	if (opacity_image != NULL)
+	{
+	    pixman_iter_t *iter;
+
+	    _pixman_image_validate (opacity_image);
+	    if (mask_image)
+	    {
+		/* Fill the opacity buffer and pre-combine the opacity onto
+		 * mask, i.e. mask' = mask IN dest
+		 */
+		iter = &opacity_iter;
+	    }
+	    else
+	    {
+		/* Otherwise we simply use the mask channel for opacity */
+		iter = &mask_iter;
+		opacity = 0xff; /* no need for a separate stage now */
+	    }
+	    _pixman_implementation_src_iter_init (
+		imp->toplevel, iter, opacity_image, 0, 0, width, 1,
+		opacity_buffer, narrow | ITER_IGNORE_RGB);
+	}
+	else
+	{
+	    /* Hmm, just pretend nothing happened and ignore opacity... */
+	    opacity = 0xff;
+	}
+    }
+
     /* dest iter */
     _pixman_implementation_dest_iter_init (
 	imp->toplevel, &dest_iter, dest_image, dest_x, dest_y, width, height,
@@ -195,6 +248,12 @@ general_composite_rect  (pixman_implementation_t *imp,
 
 	m = mask_iter.get_scanline (&mask_iter, NULL);
 	s = src_iter.get_scanline (&src_iter, m);
+
+	if (opacity != 0xff) {
+	    uint32_t *a = opacity_iter.get_scanline (&opacity_iter, NULL);
+	    compose (imp->toplevel, PIXMAN_OP_IN, m, m, a, width);
+	}
+
 	d = dest_iter.get_scanline (&dest_iter, NULL);
 
 	compose (imp->toplevel, op, d, s, m, width);
@@ -202,13 +261,16 @@ general_composite_rect  (pixman_implementation_t *imp,
 	dest_iter.write_back (&dest_iter);
     }
 
+    if (opacity_image)
+	pixman_image_unref (opacity_image);
+
     if (scanline_buffer != (uint8_t *) stack_scanline_buffer)
 	free (scanline_buffer);
 }
 
 static const pixman_fast_path_t general_fast_path[] =
 {
-    { PIXMAN_OP_any, PIXMAN_any, 0, PIXMAN_any,	0, PIXMAN_any, 0, general_composite_rect },
+    { PIXMAN_OP_any, 0, PIXMAN_any, 0, PIXMAN_any,	0, PIXMAN_any, 0, general_composite_rect },
     { PIXMAN_OP_NONE }
 };
 
diff --git a/pixman/pixman-inlines.h b/pixman/pixman-inlines.h
index f1e0cbd..e2839cc 100644
--- a/pixman/pixman-inlines.h
+++ b/pixman/pixman-inlines.h
@@ -550,7 +550,7 @@ fast_composite_scaled_nearest  ## scale_func_name (pixman_implementation_t *imp,
      FAST_PATH_NARROW_FORMAT)
 
 #define SIMPLE_NEAREST_FAST_PATH_NORMAL(op,s,d,func)			\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_NEAREST_FLAGS		|				\
 	 FAST_PATH_NORMAL_REPEAT	|				\
@@ -561,7 +561,7 @@ fast_composite_scaled_nearest  ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_NEAREST_FAST_PATH_PAD(op,s,d,func)			\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_NEAREST_FLAGS		|				\
 	 FAST_PATH_PAD_REPEAT		|				\
@@ -572,7 +572,7 @@ fast_composite_scaled_nearest  ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_NEAREST_FAST_PATH_NONE(op,s,d,func)			\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_NEAREST_FLAGS		|				\
 	 FAST_PATH_NONE_REPEAT		|				\
@@ -583,7 +583,7 @@ fast_composite_scaled_nearest  ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_NEAREST_FAST_PATH_COVER(op,s,d,func)			\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	SCALED_NEAREST_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP,		\
 	PIXMAN_null, 0,							\
@@ -592,7 +592,7 @@ fast_composite_scaled_nearest  ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_NEAREST_A8_MASK_FAST_PATH_NORMAL(op,s,d,func)		\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_NEAREST_FLAGS		|				\
 	 FAST_PATH_NORMAL_REPEAT	|				\
@@ -603,7 +603,7 @@ fast_composite_scaled_nearest  ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_NEAREST_A8_MASK_FAST_PATH_PAD(op,s,d,func)		\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_NEAREST_FLAGS		|				\
 	 FAST_PATH_PAD_REPEAT		|				\
@@ -614,7 +614,7 @@ fast_composite_scaled_nearest  ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_NEAREST_A8_MASK_FAST_PATH_NONE(op,s,d,func)		\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_NEAREST_FLAGS		|				\
 	 FAST_PATH_NONE_REPEAT		|				\
@@ -625,7 +625,7 @@ fast_composite_scaled_nearest  ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_NEAREST_A8_MASK_FAST_PATH_COVER(op,s,d,func)		\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	SCALED_NEAREST_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP,		\
 	PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA),		\
@@ -634,7 +634,7 @@ fast_composite_scaled_nearest  ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_NORMAL(op,s,d,func)		\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_NEAREST_FLAGS		|				\
 	 FAST_PATH_NORMAL_REPEAT	|				\
@@ -645,7 +645,7 @@ fast_composite_scaled_nearest  ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_PAD(op,s,d,func)		\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_NEAREST_FLAGS		|				\
 	 FAST_PATH_PAD_REPEAT		|				\
@@ -656,7 +656,7 @@ fast_composite_scaled_nearest  ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_NONE(op,s,d,func)		\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_NEAREST_FLAGS		|				\
 	 FAST_PATH_NONE_REPEAT		|				\
@@ -667,7 +667,7 @@ fast_composite_scaled_nearest  ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_COVER(op,s,d,func)		\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	SCALED_NEAREST_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP,		\
 	PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA),	\
@@ -1133,7 +1133,7 @@ fast_composite_scaled_bilinear ## scale_func_name (pixman_implementation_t *imp,
      FAST_PATH_NARROW_FORMAT)
 
 #define SIMPLE_BILINEAR_FAST_PATH_PAD(op,s,d,func)			\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_BILINEAR_FLAGS		|				\
 	 FAST_PATH_PAD_REPEAT		|				\
@@ -1144,7 +1144,7 @@ fast_composite_scaled_bilinear ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_BILINEAR_FAST_PATH_NONE(op,s,d,func)			\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_BILINEAR_FLAGS		|				\
 	 FAST_PATH_NONE_REPEAT		|				\
@@ -1155,7 +1155,7 @@ fast_composite_scaled_bilinear ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_BILINEAR_FAST_PATH_COVER(op,s,d,func)			\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	SCALED_BILINEAR_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP,		\
 	PIXMAN_null, 0,							\
@@ -1164,7 +1164,7 @@ fast_composite_scaled_bilinear ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_BILINEAR_FAST_PATH_NORMAL(op,s,d,func)			\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_BILINEAR_FLAGS		|				\
 	 FAST_PATH_NORMAL_REPEAT	|				\
@@ -1175,7 +1175,7 @@ fast_composite_scaled_bilinear ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_BILINEAR_A8_MASK_FAST_PATH_PAD(op,s,d,func)		\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_BILINEAR_FLAGS		|				\
 	 FAST_PATH_PAD_REPEAT		|				\
@@ -1186,7 +1186,7 @@ fast_composite_scaled_bilinear ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_BILINEAR_A8_MASK_FAST_PATH_NONE(op,s,d,func)		\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_BILINEAR_FLAGS		|				\
 	 FAST_PATH_NONE_REPEAT		|				\
@@ -1197,7 +1197,7 @@ fast_composite_scaled_bilinear ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_BILINEAR_A8_MASK_FAST_PATH_COVER(op,s,d,func)		\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	SCALED_BILINEAR_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP,		\
 	PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA),		\
@@ -1206,7 +1206,7 @@ fast_composite_scaled_bilinear ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_BILINEAR_A8_MASK_FAST_PATH_NORMAL(op,s,d,func)		\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_BILINEAR_FLAGS		|				\
 	 FAST_PATH_NORMAL_REPEAT	|				\
@@ -1217,7 +1217,7 @@ fast_composite_scaled_bilinear ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_PAD(op,s,d,func)		\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_BILINEAR_FLAGS		|				\
 	 FAST_PATH_PAD_REPEAT		|				\
@@ -1228,7 +1228,7 @@ fast_composite_scaled_bilinear ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_NONE(op,s,d,func)		\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_BILINEAR_FLAGS		|				\
 	 FAST_PATH_NONE_REPEAT		|				\
@@ -1239,7 +1239,7 @@ fast_composite_scaled_bilinear ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_COVER(op,s,d,func)		\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	SCALED_BILINEAR_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP,		\
 	PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA),	\
@@ -1248,7 +1248,7 @@ fast_composite_scaled_bilinear ## scale_func_name (pixman_implementation_t *imp,
     }
 
 #define SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_NORMAL(op,s,d,func)	\
-    {   PIXMAN_OP_ ## op,						\
+    {   PIXMAN_OP_ ## op, FAST_PATH_STD_OP_FLAGS,			\
 	PIXMAN_ ## s,							\
 	(SCALED_BILINEAR_FLAGS		|				\
 	 FAST_PATH_NORMAL_REPEAT	|				\
diff --git a/pixman/pixman-noop.c b/pixman/pixman-noop.c
index 906a491..2c486b9 100644
--- a/pixman/pixman-noop.c
+++ b/pixman/pixman-noop.c
@@ -120,7 +120,7 @@ noop_dest_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter)
 
 static const pixman_fast_path_t noop_fast_paths[] =
 {
-    { PIXMAN_OP_DST, PIXMAN_any, 0, PIXMAN_any, 0, PIXMAN_any, 0, noop_composite },
+    { PIXMAN_OP_DST, 0, PIXMAN_any, 0, PIXMAN_any, 0, PIXMAN_any, 0, noop_composite },
     { PIXMAN_OP_NONE },
 };
 
diff --git a/pixman/pixman-private.h b/pixman/pixman-private.h
index a25897d..f7d215b 100644
--- a/pixman/pixman-private.h
+++ b/pixman/pixman-private.h
@@ -360,6 +360,7 @@ typedef struct
     int32_t                  dest_y;
     int32_t                  width;
     int32_t                  height;
+    uint8_t                  opacity;
 } pixman_composite_info_t;
 
 #define PIXMAN_COMPOSITE_ARGS(info)					\
@@ -374,7 +375,8 @@ typedef struct
     MAYBE_UNUSED int32_t            dest_x = info->dest_x;		\
     MAYBE_UNUSED int32_t            dest_y = info->dest_y;		\
     MAYBE_UNUSED int32_t            width = info->width;		\
-    MAYBE_UNUSED int32_t            height = info->height
+    MAYBE_UNUSED int32_t            height = info->height;		\
+    MAYBE_UNUSED uint8_t            opacity = info->opacity
 
 typedef void (*pixman_combine_32_func_t) (pixman_implementation_t *imp,
 					  pixman_op_t              op,
@@ -420,9 +422,25 @@ typedef void (*pixman_iter_init_func_t) (pixman_implementation_t *imp,
 void _pixman_setup_combiner_functions_32 (pixman_implementation_t *imp);
 void _pixman_setup_combiner_functions_64 (pixman_implementation_t *imp);
 
+enum {
+    BLT_ZERO,
+    BLT_ALPHA,
+    BLT_OPAQUE,
+    BLT_N_FUNC
+};
+struct pixman_image_compositor
+{
+    pixman_composite_info_t info;
+    struct {
+	pixman_implementation_t *imp;
+	pixman_composite_func_t func;
+    } blt[BLT_N_FUNC];
+};
+
 typedef struct
 {
     pixman_op_t             op;
+    uint32_t		    op_flags;
     pixman_format_code_t    src_format;
     uint32_t		    src_flags;
     pixman_format_code_t    mask_format;
@@ -638,6 +656,9 @@ _pixman_iter_get_scanline_noop (pixman_iter_t *iter, const uint32_t *mask);
      FAST_PATH_NO_NORMAL_REPEAT		|				\
      FAST_PATH_NO_PAD_REPEAT)
 
+#define FAST_PATH_STD_OP_FLAGS \
+    (FAST_PATH_IS_OPAQUE)
+
 #define FAST_PATH_STANDARD_FLAGS					\
     (FAST_PATH_NO_CONVOLUTION_FILTER	|				\
      FAST_PATH_NO_ACCESSORS		|				\
@@ -657,8 +678,9 @@ _pixman_iter_get_scanline_noop (pixman_iter_t *iter, const uint32_t *mask);
 #define MASK_FLAGS(format, extra)					\
     ((PIXMAN_ ## format == PIXMAN_null) ? 0 : (SOURCE_FLAGS (format) | extra))
 
-#define FAST_PATH(op, src, src_flags, mask, mask_flags, dest, dest_flags, func) \
+#define FAST_PATH(op, op_flags, src, src_flags, mask, mask_flags, dest, dest_flags, func) \
     PIXMAN_OP_ ## op,							\
+    op_flags,								\
     PIXMAN_ ## src,							\
     src_flags,							        \
     PIXMAN_ ## mask,						        \
@@ -669,15 +691,23 @@ _pixman_iter_get_scanline_noop (pixman_iter_t *iter, const uint32_t *mask);
 
 #define PIXMAN_STD_FAST_PATH(op, src, mask, dest, func)			\
     { FAST_PATH (							\
-	    op,								\
+	    op,	  FAST_PATH_STD_OP_FLAGS,				\
 	    src,  SOURCE_FLAGS (src),					\
 	    mask, MASK_FLAGS (mask, FAST_PATH_UNIFIED_ALPHA),		\
 	    dest, FAST_PATH_STD_DEST_FLAGS,				\
 	    func) }
 
+#define PIXMAN_OPACITY_FAST_PATH(op, src, dest, func)			\
+    { FAST_PATH (							\
+	    op,	  FAST_PATH_STD_OP_FLAGS & ~FAST_PATH_IS_OPAQUE,	\
+	    src,  SOURCE_FLAGS (src),					\
+	    null, MASK_FLAGS (null, FAST_PATH_UNIFIED_ALPHA),		\
+	    dest, FAST_PATH_STD_DEST_FLAGS,				\
+	    func) }
+
 #define PIXMAN_STD_FAST_PATH_CA(op, src, mask, dest, func)		\
     { FAST_PATH (							\
-	    op,								\
+	    op,	  FAST_PATH_STD_OP_FLAGS,				\
 	    src,  SOURCE_FLAGS (src),					\
 	    mask, MASK_FLAGS (mask, FAST_PATH_COMPONENT_ALPHA),		\
 	    dest, FAST_PATH_STD_DEST_FLAGS,				\
diff --git a/pixman/pixman.c b/pixman/pixman.c
index 1c1d674..409330b 100644
--- a/pixman/pixman.c
+++ b/pixman/pixman.c
@@ -136,6 +136,79 @@ static const operator_info_t operator_table[] =
     PACK (HSL_COLOR,             HSL_COLOR,             HSL_COLOR,             HSL_COLOR),
     PACK (HSL_LUMINOSITY,        HSL_LUMINOSITY,        HSL_LUMINOSITY,        HSL_LUMINOSITY),
 };
+#undef PACK
+
+#define PACK(op) PIXMAN_OP_##op
+static const uint8_t zero_opacity_operator_table[] =
+{
+    /* CLEAR */		PACK(CLEAR),
+    /* SRC */		PACK(CLEAR),
+    /* DST */		PACK(DST),
+    /* OVER */		PACK(DST),
+    /* OVER_REVERSE */	PACK(CLEAR),
+    /* IN */		PACK(CLEAR),
+    /* IN_REVERSE */	PACK(CLEAR),
+    /* OUT */		PACK(DST),
+    /* OUT_REVERSE */	PACK(DST),
+    /* ATOP */		PACK(DST),
+    /* ATOP_REVERSE */	PACK(DST),
+    /* XOR */		PACK(DST),
+    /* ADD */		PACK(DST),
+    /* SATURATE */	PACK(DST),
+
+    /* LERP */		PACK(DST),
+    /* LERP_REVERSE */	PACK(SRC),
+
+    /* XXX Review disjoint/conjoint equations */
+    PACK(CLEAR),
+    PACK(CLEAR),
+    PACK(DST),
+    PACK(CLEAR),
+    PACK(CLEAR),
+    PACK(CLEAR),
+    PACK(CLEAR),
+    PACK(CLEAR),
+    PACK(CLEAR),
+    PACK(CLEAR),
+    PACK(CLEAR),
+    PACK(CLEAR),
+
+    0 /* 0x1c */,
+    0 /* 0x1d */,
+    0 /* 0x1e */,
+    0 /* 0x1f */,
+
+    PACK(CLEAR),
+    PACK(CLEAR),
+    PACK(DST),
+    PACK(CLEAR),
+    PACK(CLEAR),
+    PACK(CLEAR),
+    PACK(CLEAR),
+    PACK(CLEAR),
+    PACK(CLEAR),
+    PACK(CLEAR),
+    PACK(CLEAR),
+    PACK(CLEAR),
+
+    0 /* 0x2c */,
+    0 /* 0x2d */,
+    0 /* 0x2e */,
+    0 /* 0x2f */,
+
+    PACK(DST),
+    PACK(DST),
+    PACK(DST),
+    PACK(DST),
+    PACK(DST),
+    PACK(DST),
+    PACK(DST),
+    PACK(DST),
+    PACK(DST),
+    PACK(DST),
+    PACK(DST),
+    PACK(DST),
+};
 
 /*
  * Optimize the current operator based on opacity of source or destination
@@ -143,6 +216,7 @@ static const operator_info_t operator_table[] =
  */
 static pixman_op_t
 optimize_operator (pixman_op_t     op,
+		   uint32_t        op_flags,
 		   uint32_t        src_flags,
 		   uint32_t        mask_flags,
 		   uint32_t        dst_flags)
@@ -150,11 +224,12 @@ optimize_operator (pixman_op_t     op,
     pixman_bool_t is_source_opaque, is_dest_opaque;
 
 #define OPAQUE_SHIFT 13
-    
+
     COMPILE_TIME_ASSERT (FAST_PATH_IS_OPAQUE == (1 << OPAQUE_SHIFT));
-    
-    is_dest_opaque = (dst_flags & FAST_PATH_IS_OPAQUE);
-    is_source_opaque = ((src_flags & mask_flags) & FAST_PATH_IS_OPAQUE);
+
+    is_dest_opaque = dst_flags & FAST_PATH_IS_OPAQUE;
+    is_source_opaque =
+	(op_flags & src_flags & mask_flags) & FAST_PATH_IS_OPAQUE;
 
     is_dest_opaque >>= OPAQUE_SHIFT - 1;
     is_source_opaque >>= OPAQUE_SHIFT;
@@ -350,6 +425,7 @@ PIXMAN_DEFINE_THREAD_LOCAL (cache_t, fast_path_cache);
 
 static force_inline pixman_bool_t
 lookup_composite_function (pixman_op_t			op,
+			   uint32_t			op_flags,
 			   pixman_format_code_t		src_format,
 			   uint32_t			src_flags,
 			   pixman_format_code_t		mask_format,
@@ -379,6 +455,7 @@ lookup_composite_function (pixman_op_t			op,
 	    info->src_format == src_format	&&
 	    info->mask_format == mask_format	&&
 	    info->dest_format == dest_format	&&
+	    info->op_flags == op_flags		&&
 	    info->src_flags == src_flags	&&
 	    info->mask_flags == mask_flags	&&
 	    info->dest_flags == dest_flags	&&
@@ -406,6 +483,7 @@ lookup_composite_function (pixman_op_t			op,
 		((info->dest_format == dest_format) ||
 		 (info->dest_format == PIXMAN_any))			&&
 		/* Flags */
+		(info->op_flags & op_flags) == info->op_flags		&&
 		(info->src_flags & src_flags) == info->src_flags	&&
 		(info->mask_flags & mask_flags) == info->mask_flags	&&
 		(info->dest_flags & dest_flags) == info->dest_flags)
@@ -427,13 +505,14 @@ lookup_composite_function (pixman_op_t			op,
     return FALSE;
 
 update_cache:
-    if (i)
+    if (i > N_CACHED_FAST_PATHS / 2)
     {
 	while (i--)
 	    cache->cache[i + 1] = cache->cache[i];
 
 	cache->cache[0].imp = *out_imp;
 	cache->cache[0].fast_path.op = op;
+	cache->cache[0].fast_path.op_flags = op_flags;
 	cache->cache[0].fast_path.src_format = src_format;
 	cache->cache[0].fast_path.src_flags = src_flags;
 	cache->cache[0].fast_path.mask_format = mask_format;
@@ -681,22 +760,22 @@ analyze_extent (pixman_image_t *image, int x, int y,
 #if defined (USE_SSE2) && defined(__GNUC__) && !defined(__x86_64__) && !defined(__amd64__)
 __attribute__((__force_align_arg_pointer__))
 #endif
-PIXMAN_EXPORT void
-pixman_image_composite32 (pixman_op_t      op,
-                          pixman_image_t * src,
-                          pixman_image_t * mask,
-                          pixman_image_t * dest,
-                          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)
+static void
+_pixman_image_composite (pixman_op_t      op,
+			 pixman_image_t * src,
+			 pixman_image_t * mask,
+			 pixman_image_t * dest,
+			 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_format_code_t src_format, mask_format, dest_format;
-    uint32_t src_flags, mask_flags, dest_flags;
+    uint32_t op_flags, src_flags, mask_flags, dest_flags;
     pixman_region32_t region;
     pixman_box32_t *extents;
     pixman_implementation_t *imp;
@@ -707,6 +786,8 @@ pixman_image_composite32 (pixman_op_t      op,
 	_pixman_image_validate (mask);
     _pixman_image_validate (dest);
 
+    op_flags = FAST_PATH_IS_OPAQUE;
+
     src_format = src->common.extended_format_code;
     src_flags = src->common.flags;
 
@@ -760,18 +841,18 @@ pixman_image_composite32 (pixman_op_t      op,
 
     if ((src_flags & BOTH) == BOTH)
 	src_flags |= FAST_PATH_IS_OPAQUE;
-    
+
     if ((mask_flags & BOTH) == BOTH)
 	mask_flags |= FAST_PATH_IS_OPAQUE;
-    
+
     /*
      * Check if we can replace our operator by a simpler one
      * if the src or dest are opaque. The output operator should be
      * mathematically equivalent to the source.
      */
-    op = optimize_operator (op, src_flags, mask_flags, dest_flags);
+    op = optimize_operator (op, op_flags,src_flags, mask_flags, dest_flags);
 
-    if (lookup_composite_function (op,
+    if (lookup_composite_function (op, op_flags,
 				   src_format, src_flags,
 				   mask_format, mask_flags,
 				   dest_format, dest_flags,
@@ -785,6 +866,7 @@ pixman_image_composite32 (pixman_op_t      op,
 	info.src_image = src;
 	info.mask_image = mask;
 	info.dest_image = dest;
+	info.opacity = ~0;
 
 	pbox = pixman_region32_rectangles (&region, &n);
 
@@ -810,6 +892,27 @@ out:
 }
 
 PIXMAN_EXPORT void
+pixman_image_composite32 (pixman_op_t      op,
+			  pixman_image_t * src,
+			  pixman_image_t * mask,
+			  pixman_image_t * dest,
+			  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_image_composite (op, src, mask, dest,
+			     src_x, src_y,
+			     mask_x, mask_y,
+			     dest_x, dest_y,
+			     width, height);
+}
+
+PIXMAN_EXPORT void
 pixman_image_composite (pixman_op_t      op,
                         pixman_image_t * src,
                         pixman_image_t * mask,
@@ -823,8 +926,209 @@ pixman_image_composite (pixman_op_t      op,
                         uint16_t         width,
                         uint16_t         height)
 {
-    pixman_image_composite32 (op, src, mask, dest, src_x, src_y, 
-                              mask_x, mask_y, dest_x, dest_y, width, height);
+    _pixman_image_composite (op, src, mask, dest,
+			     src_x, src_y,
+			     mask_x, mask_y,
+			     dest_x, dest_y,
+			     width, height);
+}
+
+PIXMAN_EXPORT pixman_image_compositor_t *
+pixman_image_create_compositor (pixman_op_t      op,
+				pixman_image_t * src,
+				pixman_image_t * mask,
+				pixman_image_t * dest,
+				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_format_code_t src_format, mask_format, dest_format;
+    uint32_t op_flags, src_flags, mask_flags, dest_flags;
+    pixman_region32_t region;
+    pixman_box32_t *extents;
+    pixman_implementation_t *imp;
+    pixman_composite_func_t func;
+    pixman_image_compositor_t *compositor;
+
+    _pixman_image_validate (src);
+    if (mask)
+	_pixman_image_validate (mask);
+    _pixman_image_validate (dest);
+
+    op_flags = 0;
+
+    src_format = src->common.extended_format_code;
+    src_flags = src->common.flags;
+
+    if (mask)
+    {
+	mask_format = mask->common.extended_format_code;
+	mask_flags = mask->common.flags;
+    }
+    else
+    {
+	mask_format = PIXMAN_null;
+	mask_flags = FAST_PATH_IS_OPAQUE;
+    }
+
+    dest_format = dest->common.extended_format_code;
+    dest_flags = dest->common.flags;
+
+    /* Check for pixbufs */
+    if ((mask_format == PIXMAN_a8r8g8b8 || mask_format == PIXMAN_a8b8g8r8) &&
+	(src->type == BITS && src->bits.bits == mask->bits.bits)	   &&
+	(src->common.repeat == mask->common.repeat)			   &&
+	(src_x == mask_x && src_y == mask_y))
+    {
+	if (src_format == PIXMAN_x8b8g8r8)
+	    src_format = mask_format = PIXMAN_pixbuf;
+	else if (src_format == PIXMAN_x8r8g8b8)
+	    src_format = mask_format = PIXMAN_rpixbuf;
+    }
+
+    pixman_region32_init (&region);
+
+    if (!pixman_compute_composite_region32 (
+	    &region, src, mask, dest,
+	    src_x, src_y, mask_x, mask_y, dest_x, dest_y, width, height))
+    {
+	return NULL;
+    }
+
+    extents = pixman_region32_extents (&region);
+
+    if (!analyze_extent (src, dest_x - src_x, dest_y - src_y, extents, &src_flags))
+	return NULL;
+
+    if (!analyze_extent (mask, dest_x - mask_x, dest_y - mask_y, extents, &mask_flags))
+	return NULL;
+
+    /* If the clip is within the source samples, and the samples are opaque,
+     * then the source is effectively opaque.
+     */
+#define BOTH (FAST_PATH_SAMPLES_OPAQUE | FAST_PATH_SAMPLES_COVER_CLIP)
+
+    if ((src_flags & BOTH) == BOTH)
+	src_flags |= FAST_PATH_IS_OPAQUE;
+
+    if ((mask_flags & BOTH) == BOTH)
+	mask_flags |= FAST_PATH_IS_OPAQUE;
+
+    /*
+     * Check if we can replace our operator by a simpler one
+     * if the src or dest are opaque. The output operator should be
+     * mathematically equivalent to the source.
+     */
+    op = optimize_operator (op, op_flags, src_flags, mask_flags, dest_flags);
+    if (op == PIXMAN_OP_DST)
+	return NULL;
+
+    compositor = malloc (sizeof (*compositor));
+    if (compositor == NULL)
+	return NULL;
+
+    compositor->info.op = op;
+    compositor->info.src_image = src;
+    compositor->info.mask_image = mask;
+    compositor->info.dest_image = dest;
+
+    compositor->info.src_x = src_x - dest_x;
+    compositor->info.src_y = src_y - dest_y;
+    compositor->info.mask_x = mask_x - dest_x;
+    compositor->info.mask_y = mask_y - dest_y;
+
+    if (! lookup_composite_function (zero_opacity_operator_table[op],
+				     op_flags | FAST_PATH_IS_OPAQUE,
+				     PIXMAN_null, FAST_PATH_IS_OPAQUE,
+				     PIXMAN_null, FAST_PATH_IS_OPAQUE,
+				     dest_format, dest_flags,
+				     &imp, &func))
+    {
+	return FALSE;
+    }
+    compositor->blt[BLT_ZERO].imp = imp;
+    compositor->blt[BLT_ZERO].func = func;
+
+    if (! lookup_composite_function (op, op_flags,
+				     src_format, src_flags,
+				     mask_format, mask_flags,
+				     dest_format, dest_flags,
+				     &imp, &func))
+    {
+	free (compositor);
+	return NULL;
+    }
+    compositor->blt[BLT_ALPHA].imp = imp;
+    compositor->blt[BLT_ALPHA].func = func;
+
+    op = optimize_operator (op, op_flags | FAST_PATH_IS_OPAQUE,
+			    src_flags, mask_flags, dest_flags);
+    if (! lookup_composite_function (op, op_flags | FAST_PATH_IS_OPAQUE,
+				     src_format, src_flags,
+				     mask_format, mask_flags,
+				     dest_format, dest_flags,
+				     &imp, &func))
+    {
+	free (compositor);
+	return NULL;
+    }
+    compositor->blt[BLT_OPAQUE].imp = imp;
+    compositor->blt[BLT_OPAQUE].func = func;
+
+    return compositor;
+}
+
+PIXMAN_EXPORT void
+pixman_image_compositor_blt (pixman_image_compositor_t *compositor,
+			     int32_t          x,
+			     int32_t          y,
+			     int32_t          width,
+			     int32_t          height,
+			     uint8_t          opacity)
+{
+    compositor->info.src_x += x;
+    compositor->info.src_y += y;
+
+    compositor->info.mask_x += x;
+    compositor->info.mask_y += y;
+
+    compositor->info.dest_x = x;
+    compositor->info.dest_y = y;
+    compositor->info.width = width;
+    compositor->info.height = height;
+
+    compositor->info.opacity = opacity;
+
+    {
+	pixman_implementation_t *imp;
+	pixman_composite_func_t func;
+
+	if (opacity == 0)
+	    func = compositor->blt[BLT_ZERO].func, imp = compositor->blt[BLT_ZERO].imp;
+	else if (opacity == 0xff)
+	    func = compositor->blt[BLT_OPAQUE].func, imp = compositor->blt[BLT_OPAQUE].imp;
+	else
+	    func = compositor->blt[BLT_ALPHA].func, imp = compositor->blt[BLT_ALPHA].imp;
+
+	func(imp, &compositor->info);
+    }
+
+    compositor->info.src_x -= x;
+    compositor->info.src_y -= y;
+
+    compositor->info.mask_x -= x;
+    compositor->info.mask_y -= y;
+}
+
+PIXMAN_EXPORT void
+pixman_image_compositor_destroy (pixman_image_compositor_t *compositor)
+{
+    free (compositor);
 }
 
 PIXMAN_EXPORT pixman_bool_t
diff --git a/pixman/pixman.h b/pixman/pixman.h
index a4fcc3b..322de8a 100644
--- a/pixman/pixman.h
+++ b/pixman/pixman.h
@@ -852,6 +852,32 @@ void          pixman_image_composite32        (pixman_op_t        op,
 					       int32_t            width,
 					       int32_t            height);
 
+typedef struct pixman_image_compositor pixman_image_compositor_t;
+
+pixman_image_compositor_t *
+pixman_image_create_compositor (pixman_op_t      op,
+				pixman_image_t * src,
+				pixman_image_t * mask,
+				pixman_image_t * dest,
+				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);
+
+void pixman_image_compositor_blt (pixman_image_compositor_t *compositor,
+				  int32_t          x,
+				  int32_t          y,
+				  int32_t          width,
+				  int32_t          height,
+				  uint8_t          opacity);
+
+void pixman_image_compositor_destroy (pixman_image_compositor_t *compositor);
+
+
 /* Executive Summary: This function is a no-op that only exists
  * for historical reasons.
  *
diff --git a/test/lowlevel-blt-bench.c b/test/lowlevel-blt-bench.c
index b00e487..d0d416b 100644
--- a/test/lowlevel-blt-bench.c
+++ b/test/lowlevel-blt-bench.c
@@ -136,6 +136,7 @@ call_func (pixman_composite_func_t func,
     info.dest_y = dest_y;
     info.width = width;
     info.height = height;
+    info.opacity = 0xff;
 
     func (0, &info);
 }
-- 
1.7.5.4
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.