--- cairo-xlib-surface.c.ori 2006-09-05 08:59:24.949330000 +0200 +++ cairo-xlib-surface.c 2006-09-05 10:33:22.711144400 +0200 @@ -81,6 +81,8 @@ #define CAIRO_ASSUME_PIXMAP 20 +struct clut_r3g3b2; + struct _cairo_xlib_surface { cairo_surface_t base; @@ -126,6 +128,8 @@ int num_clip_rects; XRenderPictFormat *xrender_format; + + struct clut_r3g3b2 *clut; }; #define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \ @@ -504,6 +508,82 @@ } } +struct clut_r3g3b2 { + struct clut_r3g3b2 *next; + Display *dpy; + Colormap cmap; + uint32_t clut[256]; + unsigned char ilut[256]; +}; + +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, @@ -657,6 +737,35 @@ } else { + + if(surface->clut != NULL) { + + /* + * 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) { + printf("Cannot allocate RGB buffer\n"); + 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 { /* * XXX This can't work. We must convert the data to one of the * supported pixman formats. Pixman needs another function @@ -669,6 +778,8 @@ ximage->width, ximage->height, ximage->bytes_per_line); + } + if (image->base.status) goto FAIL; } @@ -743,6 +854,32 @@ _cairo_xlib_surface_set_gc_clip_rects (surface); } + +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, @@ -751,21 +888,54 @@ { 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; pixman_format_get_masks (pixman_image_get_format (image->pixman_image), &bpp, &alpha, &red, &green, &blue); + + if(surface->clut != NULL) { + 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 { + 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; @@ -1890,7 +2060,13 @@ surface->have_clip_rects = FALSE; surface->clip_rects = NULL; surface->num_clip_rects = 0; + surface->clut = NULL; + if (xrender_format == NULL && + (visual->class == PseudoColor || visual->class == StaticColor)) { + surface->clut = _get_clut_r3g3b2(dpy, + DefaultColormapOfScreen(surface->screen)); + } return (cairo_surface_t *) surface; }