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.
This commit is contained in:
Carl Worth 2008-03-19 13:00:47 -07:00
parent d413c5ab21
commit e96f382549
5 changed files with 277 additions and 45 deletions

View file

@ -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)

View file

@ -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 */

View file

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

View file

@ -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 (surface->visual->class == TrueColor) {
cairo_bool_t has_color;
cairo_bool_t has_alpha;
if (has_color) {
if (has_alpha) {
format = CAIRO_FORMAT_ARGB32;
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;

137
src/cairo-xlib-visual.c Normal file
View file

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