diff options
-rw-r--r-- | src/cairo-xlib-private.h | 12 | ||||
-rw-r--r-- | src/cairo-xlib-surface.c | 189 | ||||
-rw-r--r-- | src/cairo-xlib-visual.c | 103 |
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; } |