summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cairo-xlib-private.h12
-rw-r--r--src/cairo-xlib-surface.c189
-rw-r--r--src/cairo-xlib-visual.c103
3 files changed, 215 insertions, 89 deletions
diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h
index a88e6b532..b8ca027ee 100644
--- a/src/cairo-xlib-private.h
+++ b/src/cairo-xlib-private.h
@@ -73,10 +73,18 @@ struct _cairo_xlib_display {
unsigned int closed :1;
};
+/* size of color cube */
+#define CUBE_SIZE 6
+/* size of gray ramp */
+#define RAMP_SIZE 16
+
typedef struct _cairo_xlib_visual_info {
VisualID visualid;
- XColor colors[256];
- unsigned long rgb333_to_pseudocolor[512];
+ struct { uint8_t a, r, g, b; } colors[256];
+ uint8_t cube_to_pseudocolor[CUBE_SIZE][CUBE_SIZE][CUBE_SIZE];
+ uint8_t field8_to_cube[256];
+ int8_t dither8_to_cube[256];
+ uint8_t gray8_to_pseudocolor[256];
} cairo_xlib_visual_info_t;
struct _cairo_xlib_screen_info {
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 8dbf7b786..01158381b 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -430,10 +430,11 @@ _swap_ximage_to_native (XImage *ximage)
}
}
+
/* 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
+static void
_characterize_field (uint32_t mask, int *width, int *shift)
{
*width = _cairo_popcount (mask);
@@ -441,6 +442,7 @@ _characterize_field (uint32_t mask, int *width, int *shift)
*shift = _cairo_popcount ((mask - 1) & ~mask) & 31;
}
+
/* Convert a field of 'width' bits to 'new_width' bits with correct
* rounding. */
static inline uint32_t
@@ -462,6 +464,12 @@ _resize_field (uint32_t field, int width, int new_width)
}
}
+static inline uint32_t
+_adjust_field (uint32_t field, int adjustment)
+{
+ return MIN (255, MAX(0, (int)field + adjustment));
+}
+
/* Given a shifted field value, (described by 'width' and 'shift),
* resize it 8-bits and return that value.
*
@@ -474,6 +482,13 @@ _field_to_8 (uint32_t field, int width, int shift)
return _resize_field (field >> shift, width, 8);
}
+static inline uint32_t
+_field_to_8_undither (uint32_t field, int width, int shift,
+ int dither_adjustment)
+{
+ return _adjust_field (_field_to_8 (field, width, shift), - dither_adjustment>>width);
+}
+
/* Given an 8-bit value, convert it to a field of 'width', shift it up
* to 'shift, and return it. */
static inline uint32_t
@@ -482,6 +497,62 @@ _field_from_8 (uint32_t field, int width, int shift)
return _resize_field (field, 8, width) << shift;
}
+static inline uint32_t
+_field_from_8_dither (uint32_t field, int width, int shift,
+ int8_t dither_adjustment)
+{
+ return _field_from_8 (_adjust_field (field, dither_adjustment>>width), width, shift);
+}
+
+static inline uint32_t
+_pseudocolor_from_rgb888_dither (cairo_xlib_visual_info_t *visual_info,
+ uint32_t r, uint32_t g, uint32_t b,
+ int8_t dither_adjustment)
+{
+ if (r == g && g == b) {
+ dither_adjustment /= RAMP_SIZE;
+ return visual_info->gray8_to_pseudocolor[_adjust_field (r, dither_adjustment)];
+ } else {
+ dither_adjustment = visual_info->dither8_to_cube[dither_adjustment+128];
+ return visual_info->cube_to_pseudocolor[visual_info->field8_to_cube[_adjust_field (r, dither_adjustment)]]
+ [visual_info->field8_to_cube[_adjust_field (g, dither_adjustment)]]
+ [visual_info->field8_to_cube[_adjust_field (b, dither_adjustment)]];
+ }
+}
+
+static inline uint32_t
+_pseudocolor_to_rgb888_undither (cairo_xlib_visual_info_t *visual_info,
+ uint32_t pixel,
+ int8_t dither_adjustment)
+{
+ uint32_t r, g, b;
+ pixel &= 0xff;
+ r = visual_info->colors[pixel].r;
+ g = visual_info->colors[pixel].g;
+ b = visual_info->colors[pixel].b;
+ if (r == g && g == b) {
+ dither_adjustment /= RAMP_SIZE;
+ return _adjust_field (r, - dither_adjustment) * 0x010101;
+ } else {
+ dither_adjustment = - visual_info->dither8_to_cube[dither_adjustment+128];
+ return (_adjust_field (r, dither_adjustment) << 16) |
+ (_adjust_field (g, dither_adjustment) << 8) |
+ (_adjust_field (b, dither_adjustment) );
+ }
+}
+
+
+/* should range from -128 to 127 */
+#define X 16
+static const int8_t dither_pattern[4][4] = {
+ {-8*X, +0*X, -6*X, +2*X},
+ {+4*X, -4*X, +6*X, -2*X},
+ {-5*X, +4*X, -7*X, +1*X},
+ {+7*X, -1*X, +5*X, -3*X}
+};
+#undef X
+
+
static cairo_status_t
_get_image_surface (cairo_xlib_surface_t *surface,
cairo_rectangle_int_t *interest_rect,
@@ -489,7 +560,7 @@ _get_image_surface (cairo_xlib_surface_t *surface,
cairo_rectangle_int_t *image_rect)
{
cairo_int_status_t status;
- cairo_image_surface_t *image;
+ cairo_image_surface_t *image = NULL;
XImage *ximage;
unsigned short x1, y1, x2, y2;
pixman_format_code_t pixman_format;
@@ -609,15 +680,17 @@ _get_image_surface (cairo_xlib_surface_t *surface,
ximage->height,
ximage->bytes_per_line);
status = image->base.status;
- if (status) {
- XDestroyImage (ximage);
- return status;
- }
+ if (status)
+ goto BAIL;
/* Let the surface take ownership of the data */
_cairo_image_surface_assume_ownership_of_data (image);
ximage->data = NULL;
} else {
+ /* 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. */
+
cairo_format_t format;
unsigned char *data;
uint32_t *row;
@@ -626,20 +699,17 @@ _get_image_surface (cairo_xlib_surface_t *surface,
uint32_t a_mask=0, r_mask=0, g_mask=0, b_mask=0;
int a_width=0, r_width=0, g_width=0, b_width=0;
int a_shift=0, r_shift=0, g_shift=0, b_shift=0;
- int x, y;
- XColor *colors = NULL;
+ int x, y, x0, y0, x_off, y_off;
+ cairo_xlib_visual_info_t *visual_info;
- /* 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. */
if (surface->visual->class == TrueColor) {
- cairo_bool_t has_color;
cairo_bool_t has_alpha;
+ cairo_bool_t has_color;
+ has_alpha = surface->a_mask;
has_color = (surface->r_mask ||
surface->g_mask ||
surface->b_mask);
- has_alpha = surface->a_mask;
if (has_color) {
if (has_alpha) {
@@ -666,48 +736,44 @@ _get_image_surface (cairo_xlib_surface_t *surface,
_characterize_field (b_mask, &b_width, &b_shift);
} else {
- cairo_xlib_visual_info_t *visual_info;
-
format = CAIRO_FORMAT_RGB24;
status = _cairo_xlib_screen_get_visual_info (surface->screen_info,
surface->visual,
&visual_info);
- if (status) {
- XDestroyImage (ximage);
- return status;
- }
-
- colors = visual_info->colors;
+ if (status)
+ goto BAIL;
}
image = (cairo_image_surface_t *) cairo_image_surface_create
(format, ximage->width, ximage->height);
status = image->base.status;
- if (status) {
- XDestroyImage (ximage);
- return status;
- }
+ if (status)
+ goto BAIL;
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++) {
+ x0 = x1 + surface->base.device_transform.x0;
+ y0 = y1 + surface->base.device_transform.y0;
+ for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern);
+ y < ximage->height;
+ y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern)) {
+ const int8_t *dither_row = dither_pattern[y_off];
+ for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]);
+ x < ximage->width;
+ x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0])) {
+ int dither_adjustment = dither_row[x_off];
+
in_pixel = XGetPixel (ximage, x, y);
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));
+ _field_to_8_undither (in_pixel & r_mask, r_width, r_shift, dither_adjustment) << 16 |
+ _field_to_8_undither (in_pixel & g_mask, g_width, g_shift, dither_adjustment) << 8 |
+ _field_to_8_undither (in_pixel & b_mask, b_width, b_shift, dither_adjustment));
} 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));
+ out_pixel = _pseudocolor_to_rgb888_undither (visual_info, in_pixel, dither_adjustment);
}
row[x] = out_pixel;
}
@@ -715,10 +781,17 @@ _get_image_surface (cairo_xlib_surface_t *surface,
}
}
+ BAIL:
XDestroyImage (ximage);
+ if (status) {
+ if (image) {
+ cairo_surface_destroy (&image->base);
+ image = NULL;
+ }
+ }
*image_out = image;
- return CAIRO_STATUS_SUCCESS;
+ return status;
}
static void
@@ -822,7 +895,6 @@ _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 = NULL;
_pixman_format_to_masks (image->pixman_format, &image_masks);
@@ -850,10 +922,11 @@ _draw_image_surface (cairo_xlib_surface_t *surface,
XInitImage (&ximage);
} else {
unsigned int stride, rowstride;
- int x, y;
+ int x, y, x0, y0, x_off, y_off;
uint32_t in_pixel, out_pixel, *row;
int a_width=0, r_width=0, g_width=0, b_width=0;
int a_shift=0, r_shift=0, g_shift=0, b_shift=0;
+ cairo_xlib_visual_info_t *visual_info = NULL;
if (surface->depth > 16) {
ximage.bits_per_pixel = 32;
@@ -876,12 +949,13 @@ _draw_image_surface (cairo_xlib_surface_t *surface,
XInitImage (&ximage);
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;
status = _cairo_xlib_screen_get_visual_info (surface->screen_info,
surface->visual,
@@ -889,28 +963,36 @@ _draw_image_surface (cairo_xlib_surface_t *surface,
if (status)
goto BAIL;
- 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);
- for (y = 0; y < ximage.height; y++) {
- for (x = 0; x < ximage.width; x++) {
+ x0 = dst_x + surface->base.device_transform.x0;
+ y0 = dst_y + surface->base.device_transform.y0;
+ for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern);
+ y < ximage.height;
+ y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern)) {
+ const int8_t *dither_row = dither_pattern[y_off];
+ for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]);
+ x < ximage.width;
+ x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0])) {
+ int dither_adjustment = dither_row[x_off];
+
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;
- if (surface->visual->class == TrueColor)
+ 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)];
+ _field_from_8_dither (r, r_width, r_shift, dither_adjustment) |
+ _field_from_8_dither (g, g_width, g_shift, dither_adjustment) |
+ _field_from_8_dither (b, b_width, b_shift, dither_adjustment));
+ } else {
+ out_pixel = _pseudocolor_from_rgb888_dither (visual_info, r, g, b, dither_adjustment);
+ }
XPutPixel (&ximage, x, y, out_pixel);
}
row += rowstride;
@@ -1644,10 +1726,7 @@ _cairo_xlib_surface_solid_fill_rectangles (cairo_xlib_surface_t *surface,
if (status)
return CAIRO_INT_STATUS_UNSUPPORTED;
- gcv.foreground =
- visual_info->rgb333_to_pseudocolor[_field_from_8 (r, 3, 6) |
- _field_from_8 (g, 3, 3) |
- _field_from_8 (b, 3, 0)];
+ gcv.foreground = _pseudocolor_from_rgb888 (visual_info, r, g, b);
}
xgc = XCreateGC (surface->dpy, surface->drawable, GCForeground, &gcv);
diff --git a/src/cairo-xlib-visual.c b/src/cairo-xlib-visual.c
index 3a7b58657..f6eb1ee9b 100644
--- a/src/cairo-xlib-visual.c
+++ b/src/cairo-xlib-visual.c
@@ -66,15 +66,16 @@ _cairo_xlib_visual_info_create (Display *dpy,
Colormap colormap = DefaultColormap (dpy, screen);
XColor color;
int gray, red, green, blue;
- int i, index, distance, min_distance = 0;
+ int i, j, distance, min_distance = 0;
+ XColor colors[256];
+ unsigned short cube_index_to_short[CUBE_SIZE];
+ unsigned short ramp_index_to_short[RAMP_SIZE];
+ unsigned char gray_to_pseudocolor[RAMP_SIZE];
- const unsigned short index5_to_short[5] = {
- 0x0000, 0x36db, 0x8000, 0xc925, 0xffff
- };
- const unsigned short index8_to_short[8] = {
- 0x0000, 0x2492, 0x4924, 0x6db6,
- 0x9249, 0xb6db, 0xdb6d, 0xffff
- };
+ for (i = 0; i < CUBE_SIZE; i++)
+ cube_index_to_short[i] = (0xffff * i + ((CUBE_SIZE-1)>>1)) / (CUBE_SIZE-1);
+ for (i = 0; i < RAMP_SIZE; i++)
+ ramp_index_to_short[i] = (0xffff * i + ((RAMP_SIZE-1)>>1)) / (RAMP_SIZE-1);
info = malloc (sizeof (cairo_xlib_visual_info_t));
if (info == NULL)
@@ -82,10 +83,11 @@ _cairo_xlib_visual_info_create (Display *dpy,
info->visualid = visualid;
- /* Allocate a 8-entry gray ramp and a 5x5x5 color cube. Give up
- * as soon as failures start. */
- for (gray = 0; gray < 8; gray++) {
- color.red = color.green = color.blue = index8_to_short[gray];
+ /* Allocate a gray ramp and a color cube.
+ * Give up as soon as failures start. */
+
+ for (gray = 0; gray < RAMP_SIZE; gray++) {
+ color.red = color.green = color.blue = ramp_index_to_short[gray];
if (! XAllocColor (dpy, colormap, &color))
goto DONE_ALLOCATE;
}
@@ -93,12 +95,12 @@ _cairo_xlib_visual_info_create (Display *dpy,
/* 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];
+ for (red = 0; red < CUBE_SIZE; red++) {
+ for (green = 0; green < CUBE_SIZE; green++) {
+ for (blue = 0; blue < CUBE_SIZE; blue++) {
+ color.red = cube_index_to_short[red];
+ color.green = cube_index_to_short[green];
+ color.blue = cube_index_to_short[blue];
color.pixel = 0;
color.flags = 0;
color.pad = 0;
@@ -109,31 +111,68 @@ _cairo_xlib_visual_info_create (Display *dpy,
}
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));
+ for (i = 0; i < ARRAY_LENGTH (colors); i++)
+ colors[i].pixel = i;
+ XQueryColors (dpy, colormap, colors, ARRAY_LENGTH (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 (gray = 0; gray < RAMP_SIZE; gray++) {
+ for (i = 0; i < 256; i++) {
+ distance = _color_distance (ramp_index_to_short[gray],
+ ramp_index_to_short[gray],
+ ramp_index_to_short[gray],
+ colors[i].red,
+ colors[i].green,
+ colors[i].blue);
+ if (i == 0 || distance < min_distance) {
+ gray_to_pseudocolor[gray] = colors[i].pixel;
+ min_distance = distance;
+ if (!min_distance)
+ break;
+ }
+ }
+ }
+ for (red = 0; red < CUBE_SIZE; red++) {
+ for (green = 0; green < CUBE_SIZE; green++) {
+ for (blue = 0; blue < CUBE_SIZE; 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);
+ distance = _color_distance (cube_index_to_short[red],
+ cube_index_to_short[green],
+ cube_index_to_short[blue],
+ colors[i].red,
+ colors[i].green,
+ colors[i].blue);
if (i == 0 || distance < min_distance) {
- info->rgb333_to_pseudocolor[index] = info->colors[i].pixel;
+ info->cube_to_pseudocolor[red][green][blue] = colors[i].pixel;
min_distance = distance;
+ if (!min_distance)
+ break;
}
}
}
}
}
+ for (i = 0, j = 0; i < 256; i++) {
+ if (j < CUBE_SIZE - 1 && (((i<<8)+i) - (int)cube_index_to_short[j]) > ((int)cube_index_to_short[j+1] - ((i<<8)+i)))
+ j++;
+ info->field8_to_cube[i] = j;
+
+ info->dither8_to_cube[i] = ((int)i - 128) / (CUBE_SIZE - 1);
+ }
+ for (i = 0, j = 0; i < 256; i++) {
+ if (j < RAMP_SIZE - 1 && (((i<<8)+i) - (int)ramp_index_to_short[j]) > ((int)ramp_index_to_short[j+1] - ((i<<8)+i)))
+ j++;
+ info->gray8_to_pseudocolor[i] = gray_to_pseudocolor[j];
+ }
+
+ for (i = 0; i < 256; i++) {
+ info->colors[i].a = 0xff;
+ info->colors[i].r = colors[i].red >> 8;
+ info->colors[i].g = colors[i].green >> 8;
+ info->colors[i].b = colors[i].blue >> 8;
+ }
+
*out = info;
return CAIRO_STATUS_SUCCESS;
}