[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 (®ion, &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 (®ion);
+
+ if (!pixman_compute_composite_region32 (
+ ®ion, src, mask, dest,
+ src_x, src_y, mask_x, mask_y, dest_x, dest_y, width, height))
+ {
+ return NULL;
+ }
+
+ extents = pixman_region32_extents (®ion);
+
+ 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