[cairo] Patch series for supporting arbitrary X server visuals

Carl Worth cworth at cworth.org
Wed Mar 19 14:43:26 PDT 2008


If you're like me, then you're sick of seeing bug reports from cairo
printing a message like the following:

Error: Cairo 1.5.9 does not yet support the requested image format:
        Depth: 32
        Alpha mask: 0x00000000
        Red   mask: 0x000007ff
        Green mask: 0x003ff800
        Blue  mask: 0xffc00000 [*]

Things like this, (including the similar all-zero masks case for a
PseudoColor visual), are among the most oft-duplicated bug reports in
cairo.

That's why we put items on the cairo 1.6 roadmap to fix these issues
once and for all, (and that's basically all that's left on the roadmap
at this point).

So I'm happy to present a patch series that completely eliminates this
error message from all supported backends. It's attached and also
available in my personal repository as the last two commits in the
"visuals" branch:

	http://cgit.freedesktop.org/~cworth/cairo/log/?h=visuals

This patch series is simply two patches, (I already pushed some
totally non-controversial preparatory steps). The first patch adds
image-conversion code to support arbitrary TrueColor visuals. I feel
quite good about the state of this patch and think it's definitely
ready to be pushed (and soon released in cairo 1.6). About the only
thing that should obviously be improved here is that the
image-conversion code should be factored out so that other backends,
(such as for glitz and xcb), could use it as well.

The second patch adds support for PseudoColor visuals. This patch has
several things that could still be further improved. For example, when
doing color-cube allocation, this code doesn't do read-write
allocation in advance in order to determine the number of read-only
colors that could be allocated. Keith described doing such here:

	[cairo] Color cube stuff for pseudo-color support
	http://lists.cairographics.org/archives/cairo/2008-February/013255.html

It also doesn't re-allocate colors after finding nearest matching
colors in the colormap, (to avoid using a color that's a good fit now,
but might be a read-write color entry that could be changed by another
client later). Bill described doing this second allocation here:

	[cairo] Color cube stuff for pseudo-color support
	http://lists.cairographics.org/archives/cairo/2008-March/013270.html

Heck, this patch isn't even careful to allocate its color cube in a
clever order, (such as corners first and then widely-spaced entries
until allocating the whole thing). Bill described doing that here:

	[cairo] Color cube stuff for pseudo-color support
	http://lists.cairographics.org/archives/cairo/2008-February/013257.html

So there's a lot that could be tweaked to improve things, and people
have described what needs to be done. I'm just not that motivated
about this particular use case to care that much. So I'm leaving the
quality-of-implementation issues to someone that really cares.

What I do care about is that people can at least run cairo
applications with PseudoColor visuals and that things run and
generally work. The testing that I've done is with "Xvnc -depth 8" and
several clients linking against a patched cairo:

	metacity
	gtk-demo
	epiphany
	a test program drawing a gray ramp and overlapping red, green,
	and blue linear gradients (attached as color-smear.c)

With these programs, things look pretty good, (though epiphany appears
to render text with Xft which seems to have some stride bug in the
testing I've done). What the patch allocates is a 16-entry gray ramp
and a 5x5x5 color cube. And if it succeeds at getting all of that,
(which is what happens if no clients are allocating other colors),
then things look pretty good. If it doesn't succeed, then things
should still look pretty close to correct, (unless applications change
the colormap out from under cairo). Meanwhile it's also leaving quite
a bit of the colormap unallocated for other clients to play with.

So I think this is a reasonable first attempt, anyway, and I'd be
interested in any review from people that know more about X colormaps
than I do, (like Keith and Bill, say). Testing from people who care
about cairo working with 8-bit PseudoColor visuals would also be
handy.

If nobody raises any serious objection, then I'll be inclined to push
this series out, and base the cairo 1.6 release on this as soon as
possible.

Thanks in advance, and have fun!

-Carl

[*] Please ignore the fact that the above masks show a BGR visual with
10, 11, and 11 bits per channel. That kind of visual isn't sane at
all, and probably only came about because an innocent-looking
command-line such as "Xvnc -depth 32" causes Xvnc to choose this
insane visual. We probably would have been better off printing a
message telling users to reconfigure the X server to something sane,
(such as what "Xvnc -depth 24" gives), but no matter now.

-------------- next part --------------
From d00282c8da53f593bbbec922704e32f5f13304cd Mon Sep 17 00:00:00 2001
From: Carl Worth <cworth at cworth.org>
Date: Wed, 19 Mar 2008 14:23:35 -0700
Subject: [PATCH] xlib: Add support for arbitrary TrueColor visuals

This fixes the following bugs:

	cairo doesn't support 8-bit truecolor visuals
	https://bugs.freedesktop.org/show_bug.cgi?id=7735

	cairo doesn't support 655 xlib format
	https://bugs.freedesktop.org/show_bug.cgi?id=9719
---
 src/cairo-xlib-surface-private.h |    5 +
 src/cairo-xlib-surface.c         |  342 ++++++++++++++++++++++++++++++--------
 2 files changed, 276 insertions(+), 71 deletions(-)

diff --git a/src/cairo-xlib-surface-private.h b/src/cairo-xlib-surface-private.h
index e5ccf90..d5df19c 100644
--- a/src/cairo-xlib-surface-private.h
+++ b/src/cairo-xlib-surface-private.h
@@ -91,6 +91,11 @@ struct _cairo_xlib_surface {
     cairo_filter_t filter;
     int repeat;
     XTransform xtransform;
+
+    uint32_t a_mask;
+    uint32_t r_mask;
+    uint32_t g_mask;
+    uint32_t b_mask;
 };
 
 enum {
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index b8bea7c..fd5b595 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -455,6 +455,79 @@ _swap_ximage_to_native (XImage *ximage)
     }
 }
 
+/* Return the number of 1 bits in mask.
+ *
+ * GCC 3.4 supports a "population count" builtin, which on many targets is
+ * implemented with a single instruction. There is a fallback definition
+ * in libgcc in case a target does not have one, which should be just as
+ * good as the open-coded solution below, (which is "HACKMEM 169").
+ */
+static inline int
+_popcount (uint32_t mask)
+{
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+    return __builtin_popcount (mask);
+#else
+    register int y;
+
+    y = (mask >> 1) &033333333333;
+    y = mask - y - ((y >>1) & 033333333333);
+    return (((y + (y >> 3)) & 030707070707) % 077);
+#endif
+}
+
+/* Given a mask, (with a single sequence of contiguous 1 bits), return
+ * the number of 1 bits in 'width' and the number of 0 bits to its
+ * right in 'shift'. */
+static inline void
+_characterize_field (uint32_t mask, int *width, int *shift)
+{
+    *width = _popcount (mask);
+    /* The final '& 31' is to force a 0 mask to result in 0 shift. */
+    *shift = _popcount ((mask - 1) & ~mask) & 31;
+}
+
+/* Convert a field of 'width' bits to 'new_width' bits with correct
+ * rounding. */
+static inline uint32_t
+_resize_field (uint32_t field, int width, int new_width)
+{
+    if (width == 0)
+	return 0;
+
+    if (width >= new_width) {
+	return field >> (width - new_width);
+    } else {
+	uint32_t result = field << (new_width - width);
+
+	while (width < new_width) {
+	    result |= result >> width;
+	    width <<= 1;
+	}
+	return result;
+    }
+}
+
+/* Given a shifted field value, (described by 'width' and 'shift),
+ * resize it 8-bits and return that value.
+ *
+ * Note that the original field value must not have any non-field bits
+ * set.
+ */
+static inline uint32_t
+_field_to_8 (uint32_t field, int width, int shift)
+{
+    return _resize_field (field >> shift, width, 8);
+}
+
+/* Given an 8-bit value, convert it to a field of 'width', shift it up
+ *  to 'shift, and return it. */
+static inline uint32_t
+_field_from_8 (uint32_t field, int width, int shift)
+{
+    return _resize_field (field, 8, width) << shift;
+}
+
 static cairo_status_t
 _get_image_surface (cairo_xlib_surface_t    *surface,
 		    cairo_rectangle_int_t   *interest_rect,
@@ -464,9 +537,9 @@ _get_image_surface (cairo_xlib_surface_t    *surface,
     cairo_int_status_t status;
     cairo_image_surface_t *image;
     XImage *ximage;
-    short x1, y1, x2, y2;
-    cairo_format_masks_t masks;
+    unsigned short x1, y1, x2, y2;
     pixman_format_code_t pixman_format;
+    cairo_format_masks_t xlib_masks;
 
     x1 = 0;
     y1 = 0;
@@ -567,71 +640,104 @@ _get_image_surface (cairo_xlib_surface_t    *surface,
 
     _swap_ximage_to_native (ximage);
 
-    /*
-     * Compute the pixel format masks from either a XrenderFormat or
-     * else from a visual; failing that we assume the drawable is an
-     * alpha-only pixmap as it could only have been created that way
-     * through the cairo_xlib_surface_create_for_bitmap function.
-     */
-    if (surface->xrender_format) {
-	masks.bpp = ximage->bits_per_pixel;
-	masks.red_mask =   (unsigned long) surface->xrender_format->direct.redMask
-	    << surface->xrender_format->direct.red;
-	masks.green_mask = (unsigned long) surface->xrender_format->direct.greenMask
-	    << surface->xrender_format->direct.green;
-	masks.blue_mask =  (unsigned long) surface->xrender_format->direct.blueMask
-	    << surface->xrender_format->direct.blue;
-	masks.alpha_mask = (unsigned long) surface->xrender_format->direct.alphaMask
-	    << surface->xrender_format->direct.alpha;
-    } else if (surface->visual) {
-	masks.bpp = ximage->bits_per_pixel;
-	masks.alpha_mask = 0;
-	masks.red_mask = surface->visual->red_mask;
-	masks.green_mask = surface->visual->green_mask;
-	masks.blue_mask = surface->visual->blue_mask;
+    xlib_masks.bpp = ximage->bits_per_pixel;
+    xlib_masks.alpha_mask = surface->a_mask;
+    xlib_masks.red_mask = surface->r_mask;
+    xlib_masks.green_mask = surface->g_mask;
+    xlib_masks.blue_mask = surface->b_mask;
+
+    status = _pixman_format_from_masks (&xlib_masks, &pixman_format);
+    if (status == CAIRO_STATUS_SUCCESS) {
+	image = (cairo_image_surface_t*)
+	    _cairo_image_surface_create_with_pixman_format ((unsigned char *) ximage->data,
+							    pixman_format,
+							    ximage->width,
+							    ximage->height,
+							    ximage->bytes_per_line);
+	status = image->base.status;
+	if (status) {
+	    XDestroyImage (ximage);
+	    return status;
+	}
+
+	/* Let the surface take ownership of the data */
+	_cairo_image_surface_assume_ownership_of_data (image);
+	ximage->data = NULL;
     } else {
-	masks.bpp = ximage->bits_per_pixel;
-	masks.red_mask = 0;
-	masks.green_mask = 0;
-	masks.blue_mask = 0;
-	if (surface->depth < 32)
-	    masks.alpha_mask = (1 << surface->depth) - 1;
-	else
-	    masks.alpha_mask = 0xffffffff;
-    }
-
-    status = _pixman_format_from_masks (&masks, &pixman_format);
-    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
-	fprintf (stderr,
-		 "Error: Cairo " PACKAGE_VERSION " does not yet support the requested image format:\n"
-		 "\tDepth: %d\n"
-		 "\tAlpha mask: 0x%08lx\n"
-		 "\tRed   mask: 0x%08lx\n"
-		 "\tGreen mask: 0x%08lx\n"
-		 "\tBlue  mask: 0x%08lx\n"
-		 "Please file an enhancement request (quoting the above) at:\n"
-		 PACKAGE_BUGREPORT "\n",
-		 masks.bpp, masks.alpha_mask,
-		 masks.red_mask, masks.green_mask, masks.blue_mask);
+	cairo_format_t format;
+	cairo_bool_t has_color;
+	cairo_bool_t has_alpha;
+	unsigned char *data;
+	uint32_t *row;
+	uint32_t in_pixel, out_pixel;
+	unsigned int rowstride;
+	uint32_t a_mask, r_mask, g_mask, b_mask;
+	int a_width, r_width, g_width, b_width;
+	int a_shift, r_shift, g_shift, b_shift;
+	int x, y;
+
+	/* The visual we are dealing with is not supported by the
+	 * standard pixman formats. So we must first convert the data
+	 * to a supported format. */
+	has_color = (surface->r_mask ||
+		     surface->g_mask ||
+		     surface->b_mask);
+	has_alpha = surface->a_mask;
+
+	if (has_color) {
+	    if (has_alpha) {
+		format = CAIRO_FORMAT_ARGB32;
+	    } else {
+		format = CAIRO_FORMAT_RGB24;
+	    }
+	} else {
+	    if (has_alpha) {
+		/* XXX: Using CAIRO_FORMAT_A8 here would be more
+		 * efficient, but would require slightly different code in
+		 * the image conversion to put the alpha channel values
+		 * into the right place. */
+		format = CAIRO_FORMAT_ARGB32;
+	    } else {
+		fprintf (stderr, "Cairo does not (yet) support pseudocolor visuals.\n");
+		exit (1);
+	    }
+	}
 
-	ASSERT_NOT_REACHED;
-    }
+	image = (cairo_image_surface_t *) cairo_image_surface_create
+	    (format, ximage->width, ximage->height);
+	status = image->base.status;
+	if (status) {
+	    XDestroyImage (ximage);
+	    return status;
+	}
 
-    image = (cairo_image_surface_t*)
-	_cairo_image_surface_create_with_pixman_format ((unsigned char *) ximage->data,
-							pixman_format,
-							ximage->width,
-							ximage->height,
-							ximage->bytes_per_line);
-    status = image->base.status;
-    if (status) {
-	XDestroyImage (ximage);
-	return status;
+	a_mask = surface->a_mask;
+	r_mask = surface->r_mask;
+	g_mask = surface->g_mask;
+	b_mask = surface->b_mask;
+
+	_characterize_field (a_mask, &a_width, &a_shift);
+	_characterize_field (r_mask, &r_width, &r_shift);
+	_characterize_field (g_mask, &g_width, &g_shift);
+	_characterize_field (b_mask, &b_width, &b_shift);
+
+	data = cairo_image_surface_get_data (&image->base);
+	rowstride = cairo_image_surface_get_stride (&image->base) >> 2;
+	row = (uint32_t *) data;
+	for (y = 0; y < ximage->height; y++) {
+	    for (x = 0; x < ximage->width; x++) {
+		in_pixel = XGetPixel (ximage, x, y);
+		out_pixel = (
+		    _field_to_8 (in_pixel & a_mask, a_width, a_shift) << 24 |
+		    _field_to_8 (in_pixel & r_mask, r_width, r_shift) << 16 |
+		    _field_to_8 (in_pixel & g_mask, g_width, g_shift) << 8 |
+		    _field_to_8 (in_pixel & b_mask, b_width, b_shift));
+		row[x] = out_pixel;
+	    }
+	    row += rowstride;
+	}
     }
 
-    /* Let the surface take ownership of the data */
-    _cairo_image_surface_assume_ownership_of_data (image);
-    ximage->data = NULL;
     XDestroyImage (ximage);
 
     *image_out = image;
@@ -738,36 +844,96 @@ _draw_image_surface (cairo_xlib_surface_t   *surface,
     cairo_format_masks_t image_masks;
     int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst;
     cairo_status_t status;
+    cairo_bool_t own_data;
 
     _pixman_format_to_masks (image->pixman_format, &image_masks);
     
     ximage.width = image->width;
     ximage.height = image->height;
     ximage.format = ZPixmap;
-    ximage.data = (char *)image->data;
     ximage.byte_order = native_byte_order;
     ximage.bitmap_unit = 32;	/* always for libpixman */
     ximage.bitmap_bit_order = native_byte_order;
     ximage.bitmap_pad = 32;	/* always for libpixman */
-    ximage.depth = image->depth;
-    ximage.bytes_per_line = image->stride;
-    ximage.bits_per_pixel = image_masks.bpp;
-    ximage.red_mask = image_masks.red_mask;
-    ximage.green_mask = image_masks.green_mask;
-    ximage.blue_mask = image_masks.blue_mask;
+    ximage.depth = surface->depth;
+    ximage.red_mask = surface->r_mask;
+    ximage.green_mask = surface->g_mask;
+    ximage.blue_mask = surface->b_mask;
     ximage.xoffset = 0;
 
-    XInitImage (&ximage);
+    if (image_masks.red_mask   == surface->r_mask   &&
+	image_masks.green_mask == surface->g_mask &&
+	image_masks.blue_mask  == surface->b_mask)
+    {
+	ximage.bits_per_pixel = image_masks.bpp;
+	ximage.bytes_per_line = image->stride;
+	ximage.data = (char *)image->data;
+	own_data = FALSE;
+	XInitImage (&ximage);
+    } else {
+	unsigned int stride, rowstride;
+	int x, y;
+	uint32_t in_pixel, out_pixel, *row;
+	int a_width, r_width, g_width, b_width;
+	int a_shift, r_shift, g_shift, b_shift;
+	uint32_t combined_mask;
+
+	combined_mask = (surface->a_mask | surface->r_mask |
+			 surface->g_mask | surface->b_mask);
+	if (combined_mask > 0xffff) {
+	    ximage.bits_per_pixel = 32;
+	} else if (combined_mask > 0xff) {
+	    ximage.bits_per_pixel = 16;
+	} else if (combined_mask > 0x1) {
+	    ximage.bits_per_pixel = 8;
+	} else {
+	    ximage.bits_per_pixel = 1;
+	}
+	stride = CAIRO_STRIDE_FOR_WIDTH_BPP (ximage.width,
+					     ximage.bits_per_pixel);
+	ximage.bytes_per_line = stride;
+	ximage.data = malloc (stride * ximage.height);
+	own_data = TRUE;
+
+	XInitImage (&ximage);
+
+	_characterize_field (surface->a_mask, &a_width, &a_shift);
+	_characterize_field (surface->r_mask, &r_width, &r_shift);
+	_characterize_field (surface->g_mask, &g_width, &g_shift);
+	_characterize_field (surface->b_mask, &b_width, &b_shift);
+
+	rowstride = cairo_image_surface_get_stride (&image->base) >> 2;
+	row = (uint32_t *) cairo_image_surface_get_data (&image->base);
+	for (y = 0; y < ximage.height; y++) {
+	    for (x = 0; x < ximage.width; x++) {
+		int a, r, g, b;
+		in_pixel = row[x];
+		a = (in_pixel >> 24) & 0xff;
+		r = (in_pixel >> 16) & 0xff;
+		g = (in_pixel >>  8) & 0xff;
+		b = (in_pixel      ) & 0xff;
+		out_pixel = (_field_from_8 (a, a_width, a_shift) |
+			     _field_from_8 (r, r_width, r_shift) |
+			     _field_from_8 (g, g_width, g_shift) |
+			     _field_from_8 (b, b_width, b_shift));
+		XPutPixel (&ximage, x, y, out_pixel);
+	    }
+	    row += rowstride;
+	}
+    }
 
     status = _cairo_xlib_surface_ensure_gc (surface);
     if (status)
 	return status;
+
     XPutImage(surface->dpy, surface->drawable, surface->gc,
 	      &ximage, src_x, src_y, dst_x, dst_y,
 	      width, height);
 
-    return CAIRO_STATUS_SUCCESS;
+    if (own_data)
+	free (ximage.data);
 
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_status_t
@@ -2048,6 +2214,40 @@ _cairo_xlib_surface_create_internal (Display		       *dpy,
     surface->clip_rects = surface->embedded_clip_rects;
     surface->num_clip_rects = 0;
 
+    /*
+     * Compute the pixel format masks from either a XrenderFormat or
+     * else from a visual; failing that we assume the drawable is an
+     * alpha-only pixmap as it could only have been created that way
+     * through the cairo_xlib_surface_create_for_bitmap function.
+     */
+    if (xrender_format) {
+	surface->a_mask = (unsigned long)
+	    surface->xrender_format->direct.alphaMask
+	    << surface->xrender_format->direct.alpha;
+	surface->r_mask = (unsigned long)
+	    surface->xrender_format->direct.redMask
+	    << surface->xrender_format->direct.red;
+	surface->g_mask = (unsigned long)
+	    surface->xrender_format->direct.greenMask
+	    << surface->xrender_format->direct.green;
+	surface->b_mask = (unsigned long)
+	    surface->xrender_format->direct.blueMask
+	    << surface->xrender_format->direct.blue;
+    } else if (visual) {
+	surface->a_mask = 0;
+	surface->r_mask = visual->red_mask;
+	surface->g_mask = visual->green_mask;
+	surface->b_mask = visual->blue_mask;
+    } else {
+	if (depth < 32)
+	    surface->a_mask = (1 << depth) - 1;
+	else
+	    surface->a_mask = 0xffffffff;
+	surface->r_mask = 0;
+	surface->g_mask = 0;
+	surface->b_mask = 0;
+    }
+
     return (cairo_surface_t *) surface;
 }
 
-- 
1.5.4.2.185.gc5e4


From 05397cd29d54b003b23a99df24ad4a4c3c506738 Mon Sep 17 00:00:00 2001
From: Carl Worth <cworth at cworth.org>
Date: Wed, 19 Mar 2008 13:00:47 -0700
Subject: [PATCH] Add support for 8-bit PseudoColor visuals

This support involves allocating a 16-bit grayscale ramp as well
as a 5x5x5 RGB color cube. Afterwards, the 256 colors are queried
and an array is generated mapping from r3g3b3 colors to the closest
available color. Both the queried colors and the reverse mapping
are stored in a new visual-specific cairo_xlib_visual_info_t
structure which hangs off of the cairo_xlib_screen_info_t.

Both the color-cube allocation and the distance metric could be
improved by someone sufficiently motivated, (for example, allocating
and matching in a perceptually linear color space rather than just
in RGB space). Also, making this work well in the face of a changing
color map, or in the case of GrayScale, StaticGray, or DirectColor
visuals are left entirely as exercises for the reader. StaticColor
support should be fine as is, but is entirely untested.
---
 src/Makefile.am          |    1 +
 src/cairo-xlib-private.h |   15 ++++++
 src/cairo-xlib-screen.c  |   49 ++++++++++++++++++
 src/cairo-xlib-surface.c |  122 ++++++++++++++++++++++++++++-----------------
 4 files changed, 141 insertions(+), 46 deletions(-)

diff --git a/src/Makefile.am b/src/Makefile.am
index f16fd87..32b2df2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -152,6 +152,7 @@ xlib_sources =  cairo-xlib-surface.c \
 		cairo-xlib-surface-private.h \
 		cairo-xlib-display.c \
 		cairo-xlib-screen.c \
+		cairo-xlib-visual.c \
 		cairo-xlib-private.h \
 		cairo-xlib-xrender-private.h
 cairo_all_sources += $(xlib_headers) $(xlib_sources)
diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h
index 3e04ecd..09f06aa 100644
--- a/src/cairo-xlib-private.h
+++ b/src/cairo-xlib-private.h
@@ -70,6 +70,12 @@ struct _cairo_xlib_display {
     unsigned int closed :1;
 };
 
+typedef struct _cairo_xlib_visual_info {
+    VisualID visualid;
+    XColor colors[256];
+    unsigned long rgb333_to_pseudocolor[512];
+} cairo_xlib_visual_info_t;
+
 struct _cairo_xlib_screen_info {
     cairo_xlib_screen_info_t *next;
     cairo_reference_count_t ref_count;
@@ -82,6 +88,8 @@ struct _cairo_xlib_screen_info {
 
     GC gc[9];
     unsigned int gc_needs_clip_reset;
+
+    cairo_array_t visuals;
 };
 
 cairo_private cairo_xlib_display_t *
@@ -125,4 +133,11 @@ _cairo_xlib_screen_get_gc (cairo_xlib_screen_info_t *info, int depth);
 cairo_private cairo_status_t
 _cairo_xlib_screen_put_gc (cairo_xlib_screen_info_t *info, int depth, GC gc, cairo_bool_t reset_clip);
 
+cairo_xlib_visual_info_t *
+_cairo_xlib_screen_get_visual_info (cairo_xlib_screen_info_t *info,
+				    Visual *visual);
+
+cairo_xlib_visual_info_t *
+_cairo_xlib_visual_info_create (Display *dpy, int screen, VisualID visualid);
+
 #endif /* CAIRO_XLIB_PRIVATE_H */
diff --git a/src/cairo-xlib-screen.c b/src/cairo-xlib-screen.c
index b759a85..cdba294 100644
--- a/src/cairo-xlib-screen.c
+++ b/src/cairo-xlib-screen.c
@@ -269,6 +269,8 @@ _cairo_xlib_screen_info_destroy (cairo_xlib_screen_info_t *info)
 {
     cairo_xlib_screen_info_t **prev;
     cairo_xlib_screen_info_t *list;
+    cairo_xlib_visual_info_t **visuals;
+    int i;
 
     assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&info->ref_count));
 
@@ -282,12 +284,17 @@ _cairo_xlib_screen_info_destroy (cairo_xlib_screen_info_t *info)
 	    break;
 	}
     }
+    visuals = _cairo_array_index (&info->visuals, 0);
+    for (i = 0; i < _cairo_array_num_elements (&info->visuals); i++)
+	free (visuals[i]);
     CAIRO_MUTEX_UNLOCK (info->display->mutex);
 
     _cairo_xlib_screen_info_close_display (info);
 
     _cairo_xlib_display_destroy (info->display);
 
+    _cairo_array_fini (&info->visuals);
+
     free (info);
 }
 
@@ -335,6 +342,9 @@ _cairo_xlib_screen_info_get (Display *dpy, Screen *screen)
 	    memset (info->gc, 0, sizeof (info->gc));
 	    info->gc_needs_clip_reset = 0;
 
+	    _cairo_array_init (&info->visuals,
+			       sizeof (cairo_xlib_visual_info_t*));
+
 	    if (screen) {
 		int event_base, error_base;
 		info->has_render = (XRenderQueryExtension (dpy, &event_base, &error_base) &&
@@ -411,3 +421,42 @@ _cairo_xlib_screen_put_gc (cairo_xlib_screen_info_t *info, int depth, GC gc, cai
 
     return status;
 }
+
+cairo_xlib_visual_info_t *
+_cairo_xlib_screen_get_visual_info (cairo_xlib_screen_info_t *info,
+				    Visual *visual)
+{
+    cairo_xlib_visual_info_t **visuals, *ret = NULL;
+    cairo_status_t status;
+    int i;
+
+    CAIRO_MUTEX_LOCK (info->display->mutex);
+    visuals = _cairo_array_index (&info->visuals, 0);
+    for (i = 0; i < _cairo_array_num_elements (&info->visuals); i++) {
+	if (visuals[i]->visualid == visual->visualid) {
+	    ret = visuals[i];
+	    break;
+	}
+    }
+    CAIRO_MUTEX_UNLOCK (info->display->mutex);
+
+    if (ret)
+	return ret;
+
+    ret = _cairo_xlib_visual_info_create (info->display->display,
+					  XScreenNumberOfScreen (info->screen),
+					  visual->visualid);
+    if (ret == NULL)
+	return ret;
+
+    CAIRO_MUTEX_LOCK (info->display->mutex);
+    status = _cairo_array_append (&info->visuals, &ret);
+    CAIRO_MUTEX_UNLOCK (info->display->mutex);
+
+    if (status) {
+	free (ret);
+	ret = NULL;
+    }
+
+    return ret;
+}
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index fd5b595..5df69f8 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -665,8 +665,6 @@ _get_image_surface (cairo_xlib_surface_t    *surface,
 	ximage->data = NULL;
     } else {
 	cairo_format_t format;
-	cairo_bool_t has_color;
-	cairo_bool_t has_alpha;
 	unsigned char *data;
 	uint32_t *row;
 	uint32_t in_pixel, out_pixel;
@@ -675,32 +673,53 @@ _get_image_surface (cairo_xlib_surface_t    *surface,
 	int a_width, r_width, g_width, b_width;
 	int a_shift, r_shift, g_shift, b_shift;
 	int x, y;
+	XColor *colors;
 
 	/* The visual we are dealing with is not supported by the
 	 * standard pixman formats. So we must first convert the data
 	 * to a supported format. */
-	has_color = (surface->r_mask ||
-		     surface->g_mask ||
-		     surface->b_mask);
-	has_alpha = surface->a_mask;
-
-	if (has_color) {
-	    if (has_alpha) {
-		format = CAIRO_FORMAT_ARGB32;
+	if (surface->visual->class == TrueColor) {
+	    cairo_bool_t has_color;
+	    cairo_bool_t has_alpha;
+
+	    has_color = (surface->r_mask ||
+			 surface->g_mask ||
+			 surface->b_mask);
+	    has_alpha = surface->a_mask;
+
+	    if (has_color) {
+		if (has_alpha) {
+		    format = CAIRO_FORMAT_ARGB32;
+		} else {
+		    format = CAIRO_FORMAT_RGB24;
+		}
 	    } else {
-		format = CAIRO_FORMAT_RGB24;
-	    }
-	} else {
-	    if (has_alpha) {
 		/* XXX: Using CAIRO_FORMAT_A8 here would be more
 		 * efficient, but would require slightly different code in
 		 * the image conversion to put the alpha channel values
 		 * into the right place. */
 		format = CAIRO_FORMAT_ARGB32;
-	    } else {
-		fprintf (stderr, "Cairo does not (yet) support pseudocolor visuals.\n");
-		exit (1);
 	    }
+
+	    a_mask = surface->a_mask;
+	    r_mask = surface->r_mask;
+	    g_mask = surface->g_mask;
+	    b_mask = surface->b_mask;
+
+	    _characterize_field (a_mask, &a_width, &a_shift);
+	    _characterize_field (r_mask, &r_width, &r_shift);
+	    _characterize_field (g_mask, &g_width, &g_shift);
+	    _characterize_field (b_mask, &b_width, &b_shift);
+
+	} else {
+	    cairo_xlib_visual_info_t *visual_info;
+
+	    format = CAIRO_FORMAT_RGB24;
+
+	    visual_info = _cairo_xlib_screen_get_visual_info (surface->screen_info,
+							      surface->visual);
+
+	    colors = visual_info->colors;
 	}
 
 	image = (cairo_image_surface_t *) cairo_image_surface_create
@@ -711,27 +730,26 @@ _get_image_surface (cairo_xlib_surface_t    *surface,
 	    return status;
 	}
 
-	a_mask = surface->a_mask;
-	r_mask = surface->r_mask;
-	g_mask = surface->g_mask;
-	b_mask = surface->b_mask;
-
-	_characterize_field (a_mask, &a_width, &a_shift);
-	_characterize_field (r_mask, &r_width, &r_shift);
-	_characterize_field (g_mask, &g_width, &g_shift);
-	_characterize_field (b_mask, &b_width, &b_shift);
-
 	data = cairo_image_surface_get_data (&image->base);
 	rowstride = cairo_image_surface_get_stride (&image->base) >> 2;
 	row = (uint32_t *) data;
 	for (y = 0; y < ximage->height; y++) {
 	    for (x = 0; x < ximage->width; x++) {
 		in_pixel = XGetPixel (ximage, x, y);
-		out_pixel = (
-		    _field_to_8 (in_pixel & a_mask, a_width, a_shift) << 24 |
-		    _field_to_8 (in_pixel & r_mask, r_width, r_shift) << 16 |
-		    _field_to_8 (in_pixel & g_mask, g_width, g_shift) << 8 |
-		    _field_to_8 (in_pixel & b_mask, b_width, b_shift));
+		if (surface->visual->class == TrueColor) {
+		    out_pixel = (
+			_field_to_8 (in_pixel & a_mask, a_width, a_shift) << 24 |
+			_field_to_8 (in_pixel & r_mask, r_width, r_shift) << 16 |
+			_field_to_8 (in_pixel & g_mask, g_width, g_shift) << 8 |
+			_field_to_8 (in_pixel & b_mask, b_width, b_shift));
+		} else {
+		    XColor *color;
+		    color = &colors[in_pixel & 0xff];
+		    out_pixel = (
+			_field_to_8 (color->red,   16, 0) << 16 |
+			_field_to_8 (color->green, 16, 0) << 8 |
+			_field_to_8 (color->blue,  16, 0));
+		}
 		row[x] = out_pixel;
 	    }
 	    row += rowstride;
@@ -845,6 +863,7 @@ _draw_image_surface (cairo_xlib_surface_t   *surface,
     int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst;
     cairo_status_t status;
     cairo_bool_t own_data;
+    unsigned long *rgb333_to_pseudocolor;
 
     _pixman_format_to_masks (image->pixman_format, &image_masks);
     
@@ -876,15 +895,12 @@ _draw_image_surface (cairo_xlib_surface_t   *surface,
 	uint32_t in_pixel, out_pixel, *row;
 	int a_width, r_width, g_width, b_width;
 	int a_shift, r_shift, g_shift, b_shift;
-	uint32_t combined_mask;
 
-	combined_mask = (surface->a_mask | surface->r_mask |
-			 surface->g_mask | surface->b_mask);
-	if (combined_mask > 0xffff) {
+	if (surface->depth > 16) {
 	    ximage.bits_per_pixel = 32;
-	} else if (combined_mask > 0xff) {
+	} else if (surface->depth > 8) {
 	    ximage.bits_per_pixel = 16;
-	} else if (combined_mask > 0x1) {
+	} else if (surface->depth > 1) {
 	    ximage.bits_per_pixel = 8;
 	} else {
 	    ximage.bits_per_pixel = 1;
@@ -897,10 +913,19 @@ _draw_image_surface (cairo_xlib_surface_t   *surface,
 
 	XInitImage (&ximage);
 
-	_characterize_field (surface->a_mask, &a_width, &a_shift);
-	_characterize_field (surface->r_mask, &r_width, &r_shift);
-	_characterize_field (surface->g_mask, &g_width, &g_shift);
-	_characterize_field (surface->b_mask, &b_width, &b_shift);
+	if (surface->visual->class == TrueColor) {
+	    _characterize_field (surface->a_mask, &a_width, &a_shift);
+	    _characterize_field (surface->r_mask, &r_width, &r_shift);
+	    _characterize_field (surface->g_mask, &g_width, &g_shift);
+	    _characterize_field (surface->b_mask, &b_width, &b_shift);
+	} else {
+	    cairo_xlib_visual_info_t *visual_info;
+
+	    visual_info = _cairo_xlib_screen_get_visual_info (surface->screen_info,
+							      surface->visual);
+
+	    rgb333_to_pseudocolor = visual_info->rgb333_to_pseudocolor;
+	}
 
 	rowstride = cairo_image_surface_get_stride (&image->base) >> 2;
 	row = (uint32_t *) cairo_image_surface_get_data (&image->base);
@@ -912,10 +937,15 @@ _draw_image_surface (cairo_xlib_surface_t   *surface,
 		r = (in_pixel >> 16) & 0xff;
 		g = (in_pixel >>  8) & 0xff;
 		b = (in_pixel      ) & 0xff;
-		out_pixel = (_field_from_8 (a, a_width, a_shift) |
-			     _field_from_8 (r, r_width, r_shift) |
-			     _field_from_8 (g, g_width, g_shift) |
-			     _field_from_8 (b, b_width, b_shift));
+		if (surface->visual->class == TrueColor)
+		    out_pixel = (_field_from_8 (a, a_width, a_shift) |
+				 _field_from_8 (r, r_width, r_shift) |
+				 _field_from_8 (g, g_width, g_shift) |
+				 _field_from_8 (b, b_width, b_shift));
+		else
+		    out_pixel = rgb333_to_pseudocolor[_field_from_8 (r, 3, 6) |
+						      _field_from_8 (g, 3, 3) |
+						      _field_from_8 (b, 3, 0)];
 		XPutPixel (&ximage, x, y, out_pixel);
 	    }
 	    row += rowstride;
-- 
1.5.4.2.185.gc5e4

-------------- next part --------------
/* gcc `pkg-config --cflags --libs cairo-xlib` color-smear.c -o color-smear */
#include <stdio.h>
#include <stdlib.h>

#include <cairo.h>
#include <cairo-xlib.h>

#define RAMP_WIDTH 32
#define SIZE 256

static void
draw (cairo_t *cr, int width, int height)
{
    cairo_pattern_t *red, *green, *blue, *gray;

    cairo_set_source_rgb (cr, 1, 1, 1); /* white */
    cairo_paint (cr);

    width -= RAMP_WIDTH;
    cairo_save (cr);
    {
	cairo_translate (cr, RAMP_WIDTH, 0);

	cairo_push_group (cr);
	{
	    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);

	    red = cairo_pattern_create_linear (0, 0, width, height);
	    cairo_pattern_add_color_stop_rgba (red, 0.0, 1.0, 0.0, 0.0, 1.0);
	    cairo_pattern_add_color_stop_rgba (red, 1.0, 1.0, 0.0, 0.0, 0.0);
	    cairo_set_source (cr, red);
	    cairo_paint (cr);

	    green = cairo_pattern_create_linear (0, 0, width, 0);
	    cairo_pattern_add_color_stop_rgba (green, 0.0, 0.0, 1.0, 0.0, 0.0);
	    cairo_pattern_add_color_stop_rgba (green, 1.0, 0.0, 1.0, 0.0, 1.0);
	    cairo_set_source (cr, green);
	    cairo_paint (cr);

	    blue = cairo_pattern_create_linear (0, 0, 0, height);
	    cairo_pattern_add_color_stop_rgba (blue, 0.0, 0.0, 0.0, 1.0, 0.0);
	    cairo_pattern_add_color_stop_rgba (blue, 1.0, 0.0, 0.0, 1.0, 1.0);
	    cairo_set_source (cr, blue);
	    cairo_paint (cr);
	}
	cairo_pop_group_to_source (cr);

	cairo_paint (cr);
    }
    cairo_restore (cr);

    gray = cairo_pattern_create_linear (0, 0, 0, height);
    cairo_pattern_add_color_stop_rgb (gray, 0.0, 0.0, 0.0, 0.0);
    cairo_pattern_add_color_stop_rgb (gray, 1.0, 1.0, 1.0, 1.0);
    cairo_set_source (cr, gray);
    cairo_rectangle (cr, 0, 0, RAMP_WIDTH, height);
    cairo_fill (cr);
}

static void
xlib_draw (Display *dpy, Window window, int width, int height)
{
    cairo_surface_t *surface;
    cairo_t *cr;

    surface = cairo_xlib_surface_create (dpy, window,
					 DefaultVisual (dpy, DefaultScreen (dpy)),
					 width, height);
    cr = cairo_create (surface);

    draw (cr, width, height);

    if (cairo_status (cr)) {
	printf("Cairo is unhappy: %s\n",
	       cairo_status_to_string (cairo_status (cr)));
	exit(0);
    }

    cairo_destroy (cr);
    cairo_surface_destroy (surface);
}

static void
handle_events(Display *dpy, Window window, int width, int height)
{
    XEvent xev;
    KeyCode quit_code = XKeysymToKeycode (dpy, XStringToKeysym("Q"));

    XNextEvent (dpy, &xev);

    while (1) {
	XNextEvent (dpy, &xev);
	switch (xev.type) {
	case KeyPress:
	    if (xev.xkey.keycode == quit_code)
		return;
	break;
	case ConfigureNotify:
	    width = xev.xconfigure.width;
	    height = xev.xconfigure.height;
	break;
	case Expose:
	    if (xev.xexpose.count == 0)
		xlib_draw (dpy, window, width, height);
	break;
	}
    }
}

int
main (int argc, char *argv[])
{
    Display *dpy;
    Window window;
    int width = SIZE + RAMP_WIDTH, height = SIZE;

    dpy = XOpenDisplay (NULL);

    if (dpy == NULL) {
	fprintf(stderr, "Failed to open display %s\n", XDisplayName(NULL));
	return 1;
    }

    window = XCreateSimpleWindow(dpy, DefaultRootWindow (dpy),
				 0, 0, width, height, 0,
				 WhitePixel(dpy, DefaultScreen (dpy)),
				 WhitePixel(dpy, DefaultScreen (dpy)));

    XSelectInput(dpy, window,
		 KeyPressMask | StructureNotifyMask | ExposureMask);

    XMapWindow (dpy, window);

    handle_events (dpy, window, width, height);

    XDestroyWindow (dpy, window);
    XCloseDisplay (dpy);

    return 0;
}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url : http://lists.cairographics.org/archives/cairo/attachments/20080319/4c9d55c2/attachment-0001.pgp 


More information about the cairo mailing list