--- cairo-1.4.10/src/cairo-xlib-surface.c.orig 2007-08-02 16:14:33.613796000 +0800 +++ cairo-1.4.10/src/cairo-xlib-surface.c 2007-08-02 15:57:35.439213000 +0800 @@ -108,6 +108,10 @@ #define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) #define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) +#define WORKAROUND_NONE 0 +#define WORKAROUND_8BIT_PALETTE 1 +#define WORKAROUND_8BIT_DIRECT 2 + static int _CAIRO_FORMAT_DEPTH (cairo_format_t format) { @@ -494,6 +498,74 @@ } } +static struct clut_r3g3b2 * _get_clut_r3g3b2(Display *dpy, Colormap cmap) { + static struct clut_r3g3b2 *first = NULL; + int i,j, min, d; + struct clut_r3g3b2 *clut; + unsigned char r,g,b, r2,g2,b2; + + clut = first; + while(clut) { + if ( clut->dpy == dpy && clut->cmap == cmap ) + return clut; + clut = clut->next; + } + + clut = calloc(1, sizeof(*clut)); + if(clut == NULL) + return NULL; + + clut->next = first; + clut->dpy = dpy; + clut->cmap = cmap; + first = clut; + + /* Construct the clut from Colormap */ + for (i = 0; i < 256; i++) { + XColor xcol; + xcol.pixel = i; + XQueryColor(dpy, cmap, &xcol); + clut->clut[i] = ( ( ((uint32_t)xcol.red & 0xff00 ) << 8) | + ( ((uint32_t)xcol.green & 0xff00 ) ) | + ( ((uint32_t)xcol.blue & 0xff00 ) >> 8) ); + } + /* + + Find the best matching color in the colormap for all r3g3b2 + values. The distance is maybe not perceptively valid, but it + should not be too bad. + + */ + for (i = 0; i < 256; i++) { + r = i >> 5; + g = (i >> 2) & 0x7; + b = (i << 1) & 0x7; + min = 255; + for(j = 0; j < 256; j++) { + r2 = (clut->clut[j] & 0xff0000) >> 21; + g2 = (clut->clut[j] & 0x00ff00) >> 13; + b2 = (clut->clut[j] & 0x0000ff) >> 5; + if ( r2 == r && g2 == g && (b2 & 0x6) == b ) { + clut->ilut[i] = j; + break; + } + /* + Squares make higher bits much more important than lower + ones. + */ + d = (r2 ^ r) * (r2 ^ r); + d += (g2 ^ g) * (g2 ^ g); + d += (b2 ^ b) * (b2 ^ b); + if(d < min) { + clut->ilut[i] = j; + min = d; + } + } + } + + return clut; +} + static cairo_status_t _get_image_surface (cairo_xlib_surface_t *surface, cairo_rectangle_int16_t *interest_rect, @@ -655,18 +727,77 @@ } else { + if ((surface->clut != NULL) && (surface->workaround == WORKAROUND_8BIT_PALETTE)) { + + /* + * Otherwise, we construct a buffer containing RGB24 data + * using the specified workaround. + */ + uint32_t *data, *dst, *clut; + uint8_t *src8; + int i,j; + + data = (uint32_t*)malloc(ximage->height * ximage->width * 4); + if (data == NULL) { + _cairo_error(CAIRO_STATUS_NO_MEMORY); + goto FAIL; + } + + clut = surface->clut->clut; + src8 = (uint8_t*) ximage->data; + dst = data; + for (j = 0; j < ximage->height; j++) { + for (i = 0; i < ximage->width; i++) + *dst++ = clut[src8[i]]; + src8 += ximage->bytes_per_line; + } + free(ximage->data); + image = (cairo_image_surface_t*) + cairo_image_surface_create_for_data ((unsigned char *)data, + CAIRO_FORMAT_RGB24, ximage->width, ximage->height, + ximage->width*4); + } else if (surface->workaround == WORKAROUND_8BIT_DIRECT) { + + uint32_t *data, *dst; + uint8_t *src8; + int i,j; + + data = (uint32_t*)malloc(ximage->height * ximage->width * 4); + if (data == NULL) { + _cairo_error(CAIRO_STATUS_NO_MEMORY); + goto FAIL; + } + + src8 = (uint8_t*) ximage->data; + dst = data; + for (j = 0; j < ximage->height; j++) { + for (i = 0; i < ximage->width; i++) + *dst++ = (src8[i] & masks.red_mask << 21) | + (src8[i] & masks.green_mask << 10 ) | + (src8[i] & masks.blue_mask ); + + src8 += ximage->bytes_per_line; + } + free(ximage->data); + image = (cairo_image_surface_t*) + cairo_image_surface_create_for_data ((unsigned char *)data, + CAIRO_FORMAT_RGB24, ximage->width, ximage->height, + ximage->width*4); + }else if (surface->workaround == WORKAROUND_NONE){ /* * XXX This can't work. We must convert the data to one of the * supported pixman formats. Pixman needs another function * which takes data in an arbitrary format and converts it * to something supported by that library. */ - image = (cairo_image_surface_t*) - _cairo_image_surface_create_with_masks ((unsigned char *) ximage->data, + image = (cairo_image_surface_t*) + _cairo_image_surface_create_with_masks ((unsigned char *) ximage->data, &masks, ximage->width, ximage->height, ximage->bytes_per_line); + } + if (image->base.status) goto FAIL; } @@ -770,6 +901,31 @@ return CAIRO_STATUS_SUCCESS; } +static int +_make_space_for(unsigned char ** buf, int *size, int *stride, int width, int height, int Bpp) +{ + unsigned char * data; + int l; + + *stride = width * Bpp; + if(*stride%4) + *stride += 4 - *stride % 4; + l = (*stride * height); + if (*size < l) { + if(*buf) + data = realloc(*buf, l); + else + data = malloc(l); + if(data) { + *buf = data; + *size = l; + } else { + return -1; + } + } + return 0; +} + static cairo_status_t _draw_image_surface (cairo_xlib_surface_t *surface, cairo_image_surface_t *image, @@ -782,22 +938,78 @@ { XImage ximage; unsigned int bpp, alpha, red, green, blue; + unsigned int depth = image->depth; + unsigned int stride = image->stride; int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst; cairo_status_t status; pixman_format_get_masks (pixman_image_get_format (image->pixman_image), &bpp, &alpha, &red, &green, &blue); + if ((surface->clut != NULL) && (surface->workaround == WORKAROUND_8BIT_PALETTE)) { + static unsigned char *buf = NULL; + static int size = 0; + int i, j; + unsigned char *data, *ilut; + uint32_t *src; + uint8_t *dst8; + + if (_make_space_for(&buf, &size, &stride, image->width, image->height, 1)) + return CAIRO_STATUS_NO_MEMORY; + data = buf; + src = (uint32_t*)image->data; + ilut = surface->clut->ilut; + for (j=0;jheight;j++) { + dst8 = data + j * stride; + for (i=0;iwidth;i++) { + dst8[i] = ilut[ ((*src >> 16) & 0xe0) | + ((*src >> 11) & 0x1c) | + ((*src >> 6) & 0x03) ]; + src++; + } + } + alpha = red = green = blue = 0; + depth = bpp = 8; + ximage.data = data; + + }else if (surface->workaround == WORKAROUND_8BIT_DIRECT){ + static unsigned char *buf = NULL; + static int size = 0; + int i, j; + unsigned char *data; + uint32_t *src; + uint8_t *dst8; + + if (_make_space_for(&buf, &size, &stride, image->width, image->height, 1)) + return CAIRO_STATUS_NO_MEMORY; + data = buf; + src = (uint32_t*)image->data; + for (j=0;jheight;j++) { + dst8 = data + j * stride; + for (i=0;iwidth;i++) { + dst8[i] = ((*src >> 16) & 0xe0) | + ((*src >> 11) & 0x1c) | + ((*src >> 6) & 0x03) ; + src++; + } + } + alpha = red = green = blue = 0; + depth = bpp = 8; + ximage.data = data; + } else if( surface->workaround == WORKAROUND_NONE){ + ximage.data = (char *)image->data; + } + ximage.width = image->width; ximage.height = image->height; ximage.format = ZPixmap; - ximage.data = (char *)image->data; + // ximage.data is assigned above 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.depth = depth; + ximage.bytes_per_line = stride; ximage.bits_per_pixel = bpp; ximage.red_mask = red; ximage.green_mask = green; @@ -2042,6 +2254,20 @@ surface->have_clip_rects = FALSE; surface->clip_rects = surface->embedded_clip_rects; surface->num_clip_rects = 0; + surface->clut = NULL; + + if (xrender_format == NULL && + (visual==NULL?FALSE:(visual->class == PseudoColor || visual->class == StaticColor))) { + surface->clut = _get_clut_r3g3b2(dpy, + DefaultColormapOfScreen(surface->screen)); + }else if (xrender_format == NULL && + (visual==NULL?FALSE:((visual->class == TrueColor) + && (surface->visual->red_mask == 0x07) + && (surface->visual->green_mask == 0x38) + && (surface->visual->blue_mask == 0xc0)))) + surface->workaround = WORKAROUND_8BIT_DIRECT; + else + surface->workaround = WORKAROUND_NONE; return (cairo_surface_t *) surface; } --- cairo-1.4.10/src/cairo-xlib-surface-private.h.orig 2007-08-02 16:14:53.798170000 +0800 +++ cairo-1.4.10/src/cairo-xlib-surface-private.h 2007-08-02 15:32:17.912314000 +0800 @@ -39,6 +39,14 @@ typedef struct _cairo_xlib_surface cairo_xlib_surface_t; +struct clut_r3g3b2 { + struct clut_r3g3b2 *next; + Display *dpy; + Colormap cmap; + uint32_t clut[256]; + unsigned char ilut[256]; +}; + struct _cairo_xlib_surface { cairo_surface_t base; @@ -89,6 +97,9 @@ cairo_filter_t filter; int repeat; XTransform xtransform; + + struct clut_r3g3b2 *clut; + int workaround; }; enum {