[cairo] Patch series for supporting arbitrary X server visuals

Carl Worth cworth at cworth.org
Wed Mar 19 14:59:20 PDT 2008


On Wed, 19 Mar 2008 14:43:26 -0700, Carl Worth wrote:
> 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

Argh.

The patch series I originally attached was missing the one new file
where all the PseudoColor-allocation magic was happening. I've
repushed a corrected branch to my personal repository, and I've
reattached a corrected, final patch.

Sorry for all the noise on this one.

-Carl

-------------- next part --------------
From 2efad349832ab495d0f2d412f3b6f2094ddf2296 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 +++++++++++++++++++++++++---------------
 src/cairo-xlib-visual.c  |  137 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 278 insertions(+), 46 deletions(-)
 create mode 100644 src/cairo-xlib-visual.c

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;
diff --git a/src/cairo-xlib-visual.c b/src/cairo-xlib-visual.c
new file mode 100644
index 0000000..016ab00
--- /dev/null
+++ b/src/cairo-xlib-visual.c
@@ -0,0 +1,137 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright ? 2008 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *	Carl D. Worth <cworth at cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-xlib-private.h"
+
+/* A perceptual distance metric between two colors. No sqrt needed
+ * since the square of the distance is still a valid metric. */
+
+/* XXX: This is currently using linear distance in RGB space which is
+ * decidedly not perceptually linear. If someone cared a lot about the
+ * quality, they might choose something else here. Then again, they
+ * might also choose not to use a PseudoColor visual... */
+static inline int
+_color_distance (unsigned short r1, unsigned short g1, unsigned short b1,
+		 unsigned short r2, unsigned short g2, unsigned short b2)
+{
+    r1 >>= 8; g1 >>= 8; b1 >>= 8;
+    r2 >>= 8; g2 >>= 8; b2 >>= 8;
+
+    return ((r2 - r1) * (r2 - r1) +
+	    (g2 - g1) * (g2 - g1) +
+	    (b2 - b1) * (b2 - b1));
+}
+
+cairo_xlib_visual_info_t *
+_cairo_xlib_visual_info_create (Display *dpy, int screen, VisualID visualid)
+{
+    cairo_xlib_visual_info_t *info;
+    Colormap colormap = DefaultColormap (dpy, screen);
+    XColor color;
+    int gray, red, green, blue;
+    int i, distance, min_distance, index;
+
+    const unsigned short index5_to_short[5] = {
+	0x0000, 0x4000, 0x8000, 0xc000, 0xffff
+    };
+    const unsigned short index8_to_short[8] = {
+	0x0000, 0x2492, 0x4924, 0x6db6,
+	0x9249, 0xb6db, 0xdb6d, 0xffff
+    };
+
+    info = malloc (sizeof (cairo_xlib_visual_info_t));
+    if (info == NULL)
+	return NULL;
+
+    info->visualid = visualid;
+
+    /* Allocate a 16-entry gray ramp and a 5x5x5 color cube. Give up
+     * as soon as failures start. */
+    for (gray = 0; gray < 16; gray++) {
+	color.red   = (gray << 12) | (gray << 8) | (gray << 4) | gray;
+	color.green = (gray << 12) | (gray << 8) | (gray << 4) | gray;
+	color.blue  = (gray << 12) | (gray << 8) | (gray << 4) | gray;
+	if (! XAllocColor (dpy, colormap, &color))
+	    goto DONE_ALLOCATE;
+    }
+
+    /* XXX: Could do this in a more clever order to have the best
+     * possible results from early failure. Could also choose a cube
+     * uniformly distributed in a better space than RGB. */
+    for (red = 0; red < 5; red++) {
+	for (green = 0; green < 5; green++) {
+	    for (blue = 0; blue < 5; blue++) {
+		color.red = index5_to_short[red];
+		color.green = index5_to_short[green];
+		color.blue = index5_to_short[blue];
+		color.pixel = 0;
+		color.flags = 0;
+		color.pad = 0;
+		if (! XAllocColor (dpy, colormap, &color))
+		    goto DONE_ALLOCATE;
+	    }
+	}
+    }
+  DONE_ALLOCATE:
+
+    for (i = 0; i < ARRAY_LENGTH (info->colors); i++)
+	info->colors[i].pixel = i;
+    XQueryColors (dpy, colormap, info->colors, ARRAY_LENGTH (info->colors));
+
+    /* Search for nearest colors within allocated colormap. */
+    for (red = 0; red < 8; red++) {
+	for (green = 0; green < 8; green++) {
+	    for (blue = 0; blue < 8; blue++) {
+		index = (red << 6) | (green << 3) | (blue);
+		for (i = 0; i < 256; i++) {
+		    distance = _color_distance (index8_to_short[red],
+						index8_to_short[green],
+						index8_to_short[blue],
+						info->colors[i].red,
+						info->colors[i].green,
+						info->colors[i].blue);
+		    if (i == 0 || distance < min_distance) {
+			info->rgb333_to_pseudocolor[index] = info->colors[i].pixel;
+			min_distance = distance;
+		    }
+		}
+	    }
+	}
+    }
+
+    return info;
+}
-- 
1.5.4.2.185.gc5e4



-------------- 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/35da8feb/attachment.pgp 


More information about the cairo mailing list