--- src/cairo-xlib-surface-private.h.orig Sat May 5 16:10:26 2007 +++ src/cairo-xlib-surface-private.h Sat May 5 16:10:51 2007 @@ -39,6 +39,8 @@ typedef struct _cairo_xlib_surface cairo_xlib_surface_t; +struct clut_r3g3b2; + struct _cairo_xlib_surface { cairo_surface_t base; @@ -88,6 +90,9 @@ struct _cairo_xlib_surface { cairo_filter_t filter; int repeat; XTransform xtransform; + + struct clut_r3g3b2 *clut; + int workaround; }; #endif /* CAIRO_XLIB_SURFACE_PRIVATE_H */ --- src/cairo-xlib-surface.c.orig Tue May 1 19:39:47 2007 +++ src/cairo-xlib-surface.c Sat May 5 18:47:44 2007 @@ -446,6 +446,255 @@ _swap_ximage_to_native (XImage *ximage) } } + +#if 0 +static void _set_optimal_cmap(Display *dpy, Colormap cmap) { + int i, r, g, b; + XColor cm[256]; + + for (i = 0; i < 256; i++) { + r = i >> 5; + g = (i >> 2) & 0x7; + b = (i << 1) & 0x7; + cm[i].pixel = i; + cm[i].flags = DoRed | DoGreen | DoBlue; + cm[i].red = r << 13 | r << 10 | r << 7 | r << 4 | r << 1 | r >> 2; + cm[i].green = g << 13 | g << 10 | g << 7 | g << 4 | g << 1 | g >> 2; + cm[i].blue = b << 13 | b << 10 | b << 7 | b << 4 | b << 1 | b >> 2; + } + XStoreColors(dpy, cmap, cm, 256); +} +#endif + +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) { + fprintf(stderr, "cairo-xlib: cannot allocate clut_r3g3b2\n"); + 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 const char * _visualClass[] = { + "StaticGray", + "GrayScale", + "StaticColor", + "PseudoColor", + "TrueColor", + "DirectColor" +}; + + +static void _print_Visual(Visual *v) { + if(v) + fprintf(stderr, + " Visual: %p\n" + " class: %s\n" + " bpRGB: %i\n" + " map_entries: %i\n" + " RGB masks: %08lx %08lx %08lx\n", + v, + _visualClass[v->class], + v->bits_per_rgb, + v->map_entries, + v->red_mask, v->green_mask, v->blue_mask); + else + fprintf(stderr, " Visual: NULL\n"); +} + + +#if 0 +static void _print_XImage(XImage *x) +{ + const char * format[] = { "XYBitmap", "XYPixmap", "ZPixmap" }; + if(XImage) + fprintf(stderr, + " XImage: %p\n" + " size: %ix%i\n" + " xoffset: %i\n" + " format: %s\n" + " depth: %i\n" + " bpp: %i\n" + " stride: %i\n" + " RGB masks: %08lx %08lx %08lx\n" + " bitmap_unit: %i\n" + " bitmap_pad: %i\n", + x, + x->width, + x->height, + x->xoffset, + format[x->format], + x->depth, + x->bits_per_pixel, + x->bytes_per_line, + x->red_mask, x->green_mask, x->blue_mask, + x->bitmap_unit, x->bitmap_pad); + else + fprintf(stderr, " XImage: NULL\n") +} + +const char * _cairoFormats[] = { "ARGB32", "RGB24", "A8", "A1" }; + +static void _print_cairo_image_surface(cairo_image_surface_t *i) +{ + if(i) + fprintf(stderr, + " CairoImage: %p\n" + " size: %ix%i\n" + " format: %s\n" + " depth: %i\n" + " stride: %i\n", + i, + i->width, + i->height, + _cairoFormats[i->format], + i->depth, + i->stride); + else + fprintf(stderr, " CairoImage: NULL\n") +} + +static void _print_cairo_format_masks(cairo_format_masks_t *m) +{ + if(m) + printf(stderr, + " CairoFormatMask: %p\n" + " bpp: %i\n" + " ARGB masks: %lx %lx %lx %lx\n", + m, + m->bpp, m->alpha_mask, + m->red_mask, m->green_mask, m->blue_mask); + else + fprintf(stderr, " CairoFormatMasks: NULL\n") +} +#endif + +#define WORKAROUND_NONE 0 +#define WORKAROUND_8BIT_GRAYLEVEL 1 +#define WORKAROUND_8BIT_PALETTE 2 +#define WORKAROUND_R5G6B5 3 + +static const char *workarounds[] = { + "NONE", + "8BIT_GRAYLEVEL", + "8BIT_PALETTE", + "R5G6B5" +}; + +static void +_print_XRenderPictFormat(XRenderPictFormat *f) +{ + if(f) + fprintf(stderr, + " XRenderPictFormat: %p\n" + " format: %lu\n" + " type: %i\n" + " depth: %i\n" + " red: %hx mask: %hx\n" + " green: %hx mask: %hx\n" + " blue: %hx mask: %hx\n" + " alpha: %hx mask: %hx\n", + f, + f->id, f->type, f->depth, + f->direct.red, f->direct.redMask, + f->direct.green, f->direct.greenMask, + f->direct.blue, f->direct.blueMask, + f->direct.alpha, f->direct.alphaMask + ); + else + fprintf(stderr, + " XRenderPictFormat: NULL\n"); +} + +static void +_print_surface(cairo_xlib_surface_t *s) +{ + if(!s) { + fprintf(stderr, "CairoXlibSurface: NULL\n"); + return; + } + fprintf(stderr, + "CairoXlibSurface: %p\n" + " render version: %i.%i\n" + " width: %i\n" + " height: %i\n" + " depth: %i\n", + s, + s->render_major, s->render_minor, + s->width, s->height, s->depth); + _print_Visual(s->visual); + _print_XRenderPictFormat(s->xrender_format); +} + + static cairo_status_t _get_image_surface (cairo_xlib_surface_t *surface, cairo_rectangle_int16_t *interest_rect, @@ -604,9 +853,14 @@ _get_image_surface (cairo_xlib_surface_t *surface, ximage->bytes_per_line); if (image->base.status) goto FAIL; + if (surface->workaround != WORKAROUND_NONE) { + fprintf(stderr, "cancelling workaround %s\n", workarounds[surface->workaround]); + surface->workaround = WORKAROUND_NONE; + } } else { +#if 0 /* * XXX This can't work. We must convert the data to one of the * supported pixman formats. Pixman needs another function @@ -621,6 +875,98 @@ _get_image_surface (cairo_xlib_surface_t *surface, ximage->bytes_per_line); if (image->base.status) goto FAIL; +#else + /* + * Otherwise, we construct a buffer containing RGB24 data + * using the specified workaround. + */ + uint32_t *data, *dst, *clut; + uint8_t *src8; + uint16_t *src16; + int i,j; + + if(surface->visual == NULL) { + fprintf(stderr, "cairo-xlib: no visual for surface\n"); + goto FAIL; + } + + if (surface->workaround == WORKAROUND_NONE) { + fprintf(stderr, "cairo-xlib: no workaround for surface\n"); + _print_surface(surface); + goto FAIL; + } + + data = (uint32_t*)malloc(ximage->height * ximage->width * 4); + if(data == NULL) { + fprintf(stderr, "cairo-xlib: cannot allocate ARGB buffer\n"); + goto FAIL; + } + + switch (surface->workaround) { + + case WORKAROUND_8BIT_GRAYLEVEL: + + dst = data; + for(j = 0; j < ximage->height; j++) { + src8 = (uint8_t *) (ximage->data + ximage->bytes_per_line * j); + for(i = 0; i < ximage->width; i++) { + *dst++ = (*src8 << 16) | (*src8 << 8) | *src8; + src8++; + } + } + break; + + case WORKAROUND_8BIT_PALETTE: + + if(surface->clut == NULL) { + surface->clut = _get_clut_r3g3b2( + surface->dpy, + DefaultColormapOfScreen(surface->screen)); + } + + if(surface->clut == NULL) { + free(data); + 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; + } + break; + + case WORKAROUND_R5G6B5: + + src16 = (uint16_t*)ximage->data; + dst = data; + for(j = 0; j < ximage->height; j++) { + for(i = 0; i < ximage->width; i++) { + *dst++ = ( ( ((src16[i] & 0xf800) << 8) | ((src16[i] & 0xe000) << 3) ) | + ( ((src16[i] & 0x07e0) << 5) | ((src16[i] & 0x0600) >> 1) ) | + ( ((src16[i] & 0x001f) << 3) | ((src16[i] & 0x001f) >> 2) ) ); + } + src16 += ximage->bytes_per_line / sizeof(*src16); + } + break; + default: + fprintf(stderr, "cairo-xlib: dunno what to do with surface\n"); + _print_surface(surface); + goto FAIL; + } + 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); + + if (image->base.status) { + fprintf(stderr, "cairo-xlib: cairo_image_surface_create_for_data failed\n"); + free(data); + goto FAIL; + } +#endif } /* Let the surface take ownership of the data */ @@ -698,6 +1044,30 @@ _cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *s 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, @@ -713,19 +1083,98 @@ _draw_image_surface (cairo_xlib_surface_t *surface, int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst; cairo_status_t status; + static unsigned char *buf = NULL; + static int size = 0; + int i, j, depth, stride; + unsigned char *data, *ilut; + uint32_t *src; + uint8_t *dst8; + uint16_t *dst16; + pixman_format_get_masks (pixman_image_get_format (image->pixman_image), &bpp, &alpha, &red, &green, &blue); + + switch(surface->workaround) { + + case WORKAROUND_NONE: + /* Default behaviour is supposed to work */ + stride = image->stride; + depth = image->depth; + data = image->data; + break; + + case WORKAROUND_8BIT_GRAYLEVEL: + + if (make_space_for(&buf, &size, &stride, image->width, image->height, 1)) + return CAIRO_STATUS_NO_MEMORY; + data = buf; + + for(j=0;jheight;j++) { + src = (uint32_t*)(image->data); + dst8 = data + j * stride; + for(i=0;iwidth;i++) { + dst8[i] = ( 30 * ((*src >> 16) & 0xff) + + 59 * ((*src >> 8) & 0xff) + + 11 * (*src & 0xff) ) / 100; + src++; + } + } + + alpha = red = green = blue = 0; + depth = bpp = 8; + break; + + case WORKAROUND_8BIT_PALETTE: + + 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; + break; + + case WORKAROUND_R5G6B5: + + if (make_space_for(&buf, &size, &stride, image->width, image->height, 2)) + return CAIRO_STATUS_NO_MEMORY; + data = buf; + src = (uint32_t*)image->data; + for(j=0;jheight;j++) { + dst16 = (uint16_t*)(data + j * stride); + for(i=0;iwidth;i++) { + dst16[i] = ( ((*src >> 8) & 0xf800) | + ((*src >> 5) & 0x07e0) | + ((*src >> 3) & 0x001f) ); + src++; + } + } + alpha = 0; red = 0xf800; green = 0x07e0; blue = 0x001f; + depth = bpp = 16; + break; + } + ximage.width = image->width; ximage.height = image->height; ximage.format = ZPixmap; - ximage.data = (char *)image->data; + ximage.data = data; 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; @@ -1793,6 +2242,7 @@ _cairo_xlib_surface_create_internal (Display * int height, int depth) { + static int _trace = -1; cairo_xlib_surface_t *surface; cairo_xlib_screen_info_t *screen_info; @@ -1885,7 +2335,38 @@ _cairo_xlib_surface_create_internal (Display * surface->have_clip_rects = FALSE; surface->clip_rects = surface->embedded_clip_rects; surface->num_clip_rects = 0; + + surface->clut = NULL; + surface->workaround = WORKAROUND_NONE; + if (visual) { + /* Install the correct workaround */ + switch (visual->class) { + case StaticGray: + case GrayScale: + surface->workaround = WORKAROUND_8BIT_GRAYLEVEL; + break; + case PseudoColor: + case StaticColor: + surface->workaround = WORKAROUND_8BIT_PALETTE; + break; + case TrueColor: + if (visual->red_mask == 0xf800 && + visual->green_mask == 0x07e0 && + visual->blue_mask == 0x001f) { + surface->workaround = WORKAROUND_R5G6B5; + } + } + } + + if(_trace == -1) + _trace = getenv("DEBUG_CAIRO_XLIB") ? 1 : 0; + if(_trace) { + fprintf(stderr, "New surface with workaround %s\n", + workarounds[surface->workaround]); + _print_surface(surface); + } + return (cairo_surface_t *) surface; }