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 (®ion);
+ if (!_pixman_compute_composite_region32 (
+ ®ion,
+ 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 (®ion, &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 (®ion);
}
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 (®ion);
- if (!pixman_compute_composite_region32 (
+ if (!_pixman_compute_composite_region32 (
®ion, 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