Handle displays which don't match the local endianness by byteswapping on GetImage/PutImage. (#4321, reported by Sjoerd Simons)

This commit is contained in:
Owen Taylor 2005-08-31 15:09:43 +00:00
parent 568ce86026
commit d521fa3a75
2 changed files with 144 additions and 32 deletions

View file

@ -1,7 +1,9 @@
2005-08-31 Carl Worth <cworth@cworth.org>
2005-08-31 Owen Taylor <otaylor@redhat.com>
* configure.in: Increment CAIRO_VERSION to 1.1.1 after making
branch tag BRANCH_1_0.
* src/cairo-xlib-surface.c (_get_image_surface)
(_draw_image_surface): Handle displays which don't match
the local endianness by byteswapping on GetImage/PutImage.
(#4321, reported by Sjoerd Simons)
2005-08-31 Carl Worth <cworth@cworth.org>

View file

@ -60,6 +60,9 @@ _cairo_xlib_surface_ensure_dst_picture (cairo_xlib_surface_t *surface);
static cairo_bool_t
_cairo_surface_is_xlib (cairo_surface_t *surface);
static cairo_bool_t
_native_byte_order_lsb (void);
/*
* Instead of taking two round trips for each blending request,
* assume that if a particular drawable fails GetImage that it will
@ -302,6 +305,116 @@ _CAIRO_MASK_FORMAT (cairo_format_masks_t *masks, cairo_format_t *format)
return False;
}
static void
_swap_ximage_2bytes (XImage *ximage)
{
int i, j;
char *line = ximage->data;
for (j = ximage->height; j; j--) {
uint16_t *p = (uint16_t *)line;
for (i = ximage->width; i; i--) {
*p = (((*p & 0x00ff) << 8) |
((*p) >> 8));
p++;
}
line += ximage->bytes_per_line;
}
}
static void
_swap_ximage_4bytes (XImage *ximage)
{
int i, j;
char *line = ximage->data;
for (j = ximage->height; j; j--) {
uint32_t *p = (uint32_t *)line;
for (i = ximage->width; i; i--) {
*p = (((*p & 0x000000ff) << 24) |
((*p & 0x0000ff00) << 8) |
((*p & 0x00ff0000) >> 8) |
((*p) >> 24));
p++;
}
line += ximage->bytes_per_line;
}
}
static void
_swap_ximage_bits (XImage *ximage)
{
int i, j;
char *line = ximage->data;
int unit = ximage->bitmap_unit;
int line_bytes = ((ximage->width + unit - 1) & ~(unit - 1)) / 8;
for (j = ximage->height; j; j--) {
char *p = line;
for (i = line_bytes; i; i--) {
char b = *p;
b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55);
b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33);
b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f);
*p = b;
p++;
}
line += ximage->bytes_per_line;
}
}
static void
_swap_ximage_to_native (XImage *ximage)
{
int unit_bytes = 0;
int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst;
if (ximage->bits_per_pixel == 1 &&
ximage->bitmap_bit_order != native_byte_order) {
_swap_ximage_bits (ximage);
if (ximage->bitmap_bit_order == ximage->byte_order)
return;
}
if (ximage->byte_order == native_byte_order)
return;
switch (ximage->bits_per_pixel) {
case 1:
unit_bytes = ximage->bitmap_unit / 8;
break;
case 8:
case 16:
case 32:
unit_bytes = ximage->bits_per_pixel / 8;
break;
default:
/* This could be hit on some uncommon but possible cases,
* such as bpp=4. These are cases that libpixman can't deal
* with in any case.
*/
ASSERT_NOT_REACHED;
}
switch (unit_bytes) {
case 1:
return;
case 2:
_swap_ximage_2bytes (ximage);
break;
case 4:
_swap_ximage_4bytes (ximage);
break;
default:
ASSERT_NOT_REACHED;
}
}
static cairo_status_t
_get_image_surface (cairo_xlib_surface_t *surface,
cairo_rectangle_t *interest_rect,
@ -405,6 +518,8 @@ _get_image_surface (cairo_xlib_surface_t *surface,
}
if (!ximage)
return CAIRO_STATUS_NO_MEMORY;
_swap_ximage_to_native (ximage);
/*
* Compute the pixel format masks from either a visual or a
@ -545,40 +660,35 @@ _draw_image_surface (cairo_xlib_surface_t *surface,
int dst_x,
int dst_y)
{
XImage *ximage;
unsigned bitmap_pad;
/* XXX this is wrong */
if (image->depth > 16)
bitmap_pad = 32;
else if (image->depth > 8)
bitmap_pad = 16;
else
bitmap_pad = 8;
ximage = XCreateImage (surface->dpy,
DefaultVisual(surface->dpy, DefaultScreen(surface->dpy)),
image->depth,
ZPixmap,
0,
(char *) image->data,
image->width,
image->height,
bitmap_pad,
image->stride);
if (ximage == NULL)
return CAIRO_STATUS_NO_MEMORY;
XImage ximage;
int bpp, alpha, red, green, blue;
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);
ximage.width = image->width;
ximage.height = image->height;
ximage.format = ZPixmap;
ximage.data = (char *)image->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.bits_per_pixel = bpp;
ximage.red_mask = red;
ximage.green_mask = green;
ximage.blue_mask = blue;
XInitImage (&ximage);
_cairo_xlib_surface_ensure_gc (surface);
XPutImage(surface->dpy, surface->drawable, surface->gc,
ximage, 0, 0, dst_x, dst_y,
&ximage, 0, 0, dst_x, dst_y,
image->width, image->height);
/* Foolish XDestroyImage thinks it can free my data, but I won't
stand for it. */
ximage->data = NULL;
XDestroyImage (ximage);
return CAIRO_STATUS_SUCCESS;
}