[cairo-commit] 3 commits - src/cairo-xlib-private.h src/cairo-xlib-screen.c src/cairo-xlib-surface.c src/cairo-xlib-surface-private.h src/cairo-xlib-visual.c src/Makefile.am test/rotate-image-surface-paint-ref.png

Carl Worth cworth at kemper.freedesktop.org
Thu Mar 20 11:52:20 PDT 2008


 src/Makefile.am                         |    1 
 src/cairo-xlib-private.h                |   15 +
 src/cairo-xlib-screen.c                 |   49 ++++
 src/cairo-xlib-surface-private.h        |    5 
 src/cairo-xlib-surface.c                |  372 +++++++++++++++++++++++++-------
 src/cairo-xlib-visual.c                 |  137 +++++++++++
 test/rotate-image-surface-paint-ref.png |binary
 7 files changed, 508 insertions(+), 71 deletions(-)

New commits:
commit c91a2a2e80f2172a4174cfc3b5e821cb49633da5
Author: Carl Worth <cworth at cworth.org>
Date:   Thu Mar 20 11:51:31 2008 -0700

    Track minor change in pixman filtering sample location
    
    This change in reference image was triggered by the following commit
    in the pixman library:
    
    	commit f2d8a5caa63596739b4dc47c00daaaafd9f522e2
    	Author: Søren Sandmann <sandmann at redhat.com>
    	Date:   Mon Mar 10 23:41:52 2008 -0400
    
    	Add rounding epsilon for NEAREST filter after transformation, not before.
    
    This hasn't appeared in a pixman release yet. So for now, getting
    correct results is a matter of "use latest pixman". Soon we'll
    have a new pixman release and we'll make cairo depend on that.

diff --git a/test/rotate-image-surface-paint-ref.png b/test/rotate-image-surface-paint-ref.png
index a63e9cb..bd20481 100644
Binary files a/test/rotate-image-surface-paint-ref.png and b/test/rotate-image-surface-paint-ref.png differ
commit e96f382549eb3411d461162fdd8989f4ade8b448
Author: Carl Worth <cworth at cworth.org>
Date:   Wed Mar 19 13:00:47 2008 -0700

    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.

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;
+}
commit d413c5ab21efb2ccc6a4847ff387f8e39ba2f3e1
Author: Carl Worth <cworth at cworth.org>
Date:   Wed Mar 19 14:23:35 2008 -0700

    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

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;
 }
 


More information about the cairo-commit mailing list