diff options
Diffstat (limited to 'x11.c')
-rw-r--r-- | x11.c | 456 |
1 files changed, 456 insertions, 0 deletions
@@ -0,0 +1,456 @@ +/* + * some X11 ximage / pixmaps rotines + * + * (c) 1996 Gerd Knorr <kraxel@goldbach.in-berlin.de> + * + * basic usage: + * 1) call x11_color_init() + * this does all the visual checking/colormap handling stuff and returns + * TRUECOLOR or PSEUDOCOLOR + * 2) create/load the image + * 3) call x11_create_pixmaps() + * For TRUECOLOR: It expects the data in one long (4 byte) per pixel. + * To create the long, run the rgb-values throuth the + * x11_lut_[red|green|blue] tables and or the results + * For PSEUDOCOLOR: The data is expected to be one byte per pixel, + * containing the results from dither_line (see dither.c) + * Not required to call init_dither, this is done by + * x11_color_init + * returns a pixmap. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ipc.h> +#include <sys/shm.h> + +#include <X11/Xlib.h> +#include <X11/Intrinsic.h> +#include <X11/extensions/XShm.h> + +#include "x11.h" +#include "dither.h" + +extern Display *dpy; + +#define PERROR(str) fprintf(stderr,"%s:%d: %s: %s\n",__FILE__,__LINE__,str,strerror(errno)) + +/* ------------------------------------------------------------------------ */ + +int display_type = 0; +int display_depth = 0; +XVisualInfo *info; + +/* PseudoColor: ditherresult => colormap-entry */ +int x11_colors; +int x11_grays; +unsigned long *x11_map; +unsigned long x11_map_color[256]; +unsigned long x11_map_gray[64]; + +unsigned long x11_red; +unsigned long x11_green; +unsigned long x11_blue; + +int have_shmem = 0; + +/* + * - xv uses 4:8:4 for truecolor images. + * - The GIMP 0.99.9 uses 6:6:4, but the 6 intervals for red+green are + * choosen somehow wired :-( + * - ImageMagick tries to optimize the palette for each image individual + */ +static int try_red[] = {4, 6, 6, 5, 4}; +static int try_green[] = {8, 6, 6, 5, 4}; +static int try_blue[] = {4, 6, 4, 5, 4}; + +/* TrueColor: r,g,b => X11-color */ +unsigned long x11_lut_red[256]; +unsigned long x11_lut_green[256]; +unsigned long x11_lut_blue[256]; +unsigned long x11_lut_gray[256]; + +static int +x11_alloc_grays(Display * dpy, Colormap cmap, unsigned long *colors, int gray) +{ + XColor akt_color; + int i; + + for (i = 0; i < gray; i++) { + akt_color.red = i * 65535 / (gray - 1); + akt_color.green = i * 65535 / (gray - 1); + akt_color.blue = i * 65535 / (gray - 1); + + if (!XAllocColor(dpy, cmap, &akt_color)) { + /* failed, free them */ + XFreeColors(dpy, cmap, colors, i, 0); + return 1; + } + colors[i] = akt_color.pixel; +#if 0 + fprintf(stderr, "%2lx: %04x %04x %04x\n", + akt_color.pixel,akt_color.red,akt_color.green,akt_color.red); +#endif + } + return 0; +} + +static int +x11_alloc_colorcube(Display * dpy, Colormap cmap, unsigned long *colors, + int red, int green, int blue) +{ + XColor akt_color; + int i; + + for (i = 0; i < red * green * blue; i++) { + akt_color.red = ((i / (green * blue)) % red) * 65535 / (red - 1); + akt_color.green = ((i / blue) % green) * 65535 / (green - 1); + akt_color.blue = (i % blue) * 65535 / (blue - 1); +#if 0 + fprintf(stderr, "%04x %04x %04x\n", + akt_color.red, akt_color.green, akt_color.red); +#endif + + if (!XAllocColor(dpy, cmap, &akt_color)) { + /* failed, free them */ + XFreeColors(dpy, cmap, colors, i, 0); + return 1; + } + colors[i] = akt_color.pixel; + } + return 0; +} + +static unsigned long +x11_alloc_color(Display * dpy, Colormap cmap, int red, int green, int blue) +{ + XColor akt_color; + + akt_color.red = red; + akt_color.green = green; + akt_color.blue = blue; + + XAllocColor(dpy, cmap, &akt_color); + return akt_color.pixel; +} + +static void +x11_create_lut(unsigned long red_mask, + unsigned long green_mask, + unsigned long blue_mask) +{ + int rgb_red_bits = 0; + int rgb_red_shift = 0; + int rgb_green_bits = 0; + int rgb_green_shift = 0; + int rgb_blue_bits = 0; + int rgb_blue_shift = 0; + int i; + unsigned long mask; + + for (i = 0; i < 24; i++) { + mask = (1 << i); + if (red_mask & mask) + rgb_red_bits++; + else if (!rgb_red_bits) + rgb_red_shift++; + if (green_mask & mask) + rgb_green_bits++; + else if (!rgb_green_bits) + rgb_green_shift++; + if (blue_mask & mask) + rgb_blue_bits++; + else if (!rgb_blue_bits) + rgb_blue_shift++; + } +#if 0 + printf("color: bits shift\n"); + printf("red : %04i %05i\n", rgb_red_bits, rgb_red_shift); + printf("green: %04i %05i\n", rgb_green_bits, rgb_green_shift); + printf("blue : %04i %05i\n", rgb_blue_bits, rgb_blue_shift); +#endif + + for (i = 0; i < 256; i++) { + x11_lut_red[i] = (i >> (8 - rgb_red_bits)) << rgb_red_shift; + x11_lut_green[i] = (i >> (8 - rgb_green_bits)) << rgb_green_shift; + x11_lut_blue[i] = (i >> (8 - rgb_blue_bits)) << rgb_blue_shift; + x11_lut_gray[i] = + x11_lut_red[i] | x11_lut_green[i] | x11_lut_blue[i]; + } +} + +int +x11_color_init(Widget shell, int *gray) +{ + Screen *scr; + Colormap cmap; + XVisualInfo template; + unsigned int found, i; + + scr = XtScreen(shell); + cmap = DefaultColormapOfScreen(scr); + if (0 == x11_grays) + x11_grays = 8; + + /* Ask for visual type */ + template.screen = XDefaultScreen(dpy); + template.visualid = + XVisualIDFromVisual(DefaultVisualOfScreen(scr)); + info = XGetVisualInfo(dpy, VisualIDMask | VisualScreenMask, &template, + &found); + if (XShmQueryExtension(dpy)) { + have_shmem = 1; + } + + /* display_depth = (info->depth+7)/8; */ + if (info->class == TrueColor) { + /* TrueColor */ + *gray = 0; /* XXX testing... */ + display_depth = 4; + display_type = TRUECOLOR; + x11_create_lut(info->red_mask, info->green_mask, info->blue_mask); + x11_black = x11_alloc_color(dpy, cmap, 0, 0, 0); + x11_gray = x11_alloc_color(dpy, cmap, 0xc400, 0xc400, 0xc400); + x11_lightgray = x11_alloc_color(dpy, cmap, 0xe000, 0xe000, 0xe000); + x11_white = x11_alloc_color(dpy, cmap, 0xffff, 0xffff, 0xffff); + } else if (info->class == PseudoColor && info->depth == 8) { + /* 8bit PseudoColor */ + display_depth = 1; + display_type = PSEUDOCOLOR; + if (0 != x11_alloc_grays(dpy, cmap, x11_map_gray, x11_grays)) { + fprintf(stderr, "sorry, can't allocate %d grays\n", x11_grays); + exit(1); + } + if (!*gray) { + for (i = 0; i < sizeof(try_red) / sizeof(int); i++) { + if (0 == x11_alloc_colorcube + (dpy, cmap, x11_map_color, + try_red[i], try_green[i], try_blue[i])) { + x11_colors = try_red[i] * try_green[i] * try_blue[i]; + init_dither(try_red[i], try_green[i], try_blue[i], x11_grays); + break; + } + } + if (i == sizeof(try_red) / sizeof(int)) { + *gray = 1; + fprintf(stderr, "failed to allocate enouth colors, " + "using grayscaled\n"); + } + } + if (*gray) + init_dither(2, 2, 2, x11_grays); + } else if (info->class == StaticGray || info->class == GrayScale) { + /* Grayscale */ + display_depth = 1; + display_type = PSEUDOCOLOR; + x11_grays = 64; + *gray = 1; + init_dither(2, 2, 2, x11_grays); + if (0 != x11_alloc_grays(dpy, cmap, x11_map_gray, x11_grays)) { + fprintf(stderr, "sorry, can't allocate %d grays\n", x11_grays); + exit(1); + } + } else { + fprintf(stderr, "sorry, can't handle visual\n"); + exit(1); + } + + /* some common colors */ + x11_red = x11_alloc_color(dpy, cmap, 65535, 0, 0); + x11_green = x11_alloc_color(dpy, cmap, 0, 65535, 0); + x11_blue = x11_alloc_color(dpy, cmap, 0, 0, 65535); + + if (*gray) { + x11_map = x11_map_gray; + dither_line = dither_line_gray; + } else { + x11_map = x11_map_color; + dither_line = dither_line_color; + } + + return display_type; +} + +/* ------------------------------------------------------------------------ */ + +static int mitshm_bang = 0; + +static int +x11_error_dev_null(Display * dpy, XErrorEvent * event) +{ + mitshm_bang = 1; + return 0; +} + +XImage* +x11_create_ximage(Widget shell, int width, int height, void **shm) +{ + XImage *ximage = NULL; + unsigned char *ximage_data; + XShmSegmentInfo *shminfo = NULL; + XtPointer old_handler; + Screen *scr = XtScreen(shell); + + if (have_shmem) { + old_handler = XSetErrorHandler(x11_error_dev_null); + (*shm) = shminfo = malloc(sizeof(XShmSegmentInfo)); + memset(shminfo, 0, sizeof(XShmSegmentInfo)); + ximage = XShmCreateImage(dpy, + DefaultVisualOfScreen(scr), + DefaultDepthOfScreen(scr), + ZPixmap, NULL, + shminfo, width, height); + if (ximage) { + shminfo->shmid = shmget(IPC_PRIVATE, + ximage->bytes_per_line * ximage->height, + IPC_CREAT | 0777); + if (-1 == shminfo->shmid) { + fprintf(stderr,"shmget(%dMB): %s\n", + ximage->bytes_per_line * ximage->height / 1024 / 1024, + strerror(errno)); + goto oom; + } + shminfo->shmaddr = (char *) shmat(shminfo->shmid, 0, 0); + if ((void *) -1 == shminfo->shmaddr) { + perror("shmat"); + goto oom; + } + ximage->data = shminfo->shmaddr; + shminfo->readOnly = False; + + XShmAttach(dpy, shminfo); + XSync(dpy, False); + shmctl(shminfo->shmid, IPC_RMID, 0); + if (mitshm_bang) { + have_shmem = 0; + shmdt(shminfo->shmaddr); + free(shminfo); + shminfo = *shm = NULL; + XDestroyImage(ximage); + ximage = NULL; + } + } else { + have_shmem = 0; + free(shminfo); + shminfo = *shm = NULL; + } + XSetErrorHandler(old_handler); + } + + if (ximage == NULL) { + (*shm) = NULL; + if (NULL == (ximage_data = malloc(width * height * display_depth))) { + fprintf(stderr,"Oops: out of memory\n"); + goto oom; + } + ximage = XCreateImage(dpy, + DefaultVisualOfScreen(scr), + DefaultDepthOfScreen(scr), + ZPixmap, 0, ximage_data, + width, height, + 8, 0); + } + memset(ximage->data, 0, ximage->bytes_per_line * ximage->height); + + return ximage; + + oom: + if (shminfo) { + if (shminfo->shmid && shminfo->shmid != -1) + shmctl(shminfo->shmid, IPC_RMID, 0); + free(shminfo); + } + if (ximage) + XDestroyImage(ximage); + return NULL; +} + +void +x11_destroy_ximage(Widget shell, XImage * ximage, void *shm) +{ + XShmSegmentInfo *shminfo = shm; + + if (shminfo) { + XShmDetach(dpy, shminfo); + XDestroyImage(ximage); + shmdt(shminfo->shmaddr); + free(shminfo); + } else + XDestroyImage(ximage); +} + +Pixmap +x11_create_pixmap(Widget shell, unsigned char *byte_data, + int width, int height, int gray) +{ + Pixmap pixmap; + XImage *ximage; + XGCValues values; + GC gc; + unsigned long *long_data = (unsigned long *) byte_data; + int x, y; + void *shm; + unsigned long *map = gray ? x11_map_gray : x11_map; + + Screen *scr = XtScreen(shell); + + pixmap = XCreatePixmap(dpy, + RootWindowOfScreen(scr), + width, height, + DefaultDepthOfScreen(scr)); + gc = XCreateGC(dpy, pixmap, 0, &values); + + if (NULL == (ximage = x11_create_ximage(shell, width, height, &shm))) { + XFreePixmap(dpy, pixmap); + XFreeGC(dpy, gc); + return 0; + } + for (y = 0; y < height; y++) + if (display_type == TRUECOLOR) + for (x = 0; x < width; x++) + XPutPixel(ximage, x, y, *(long_data++)); + else + for (x = 0; x < width; x++) + XPutPixel(ximage, x, y, map[(int) (*(byte_data++))]); + + XPUTIMAGE(dpy, pixmap, gc, ximage, 0, 0, 0, 0, width, height); + + x11_destroy_ximage(shell, ximage, shm); + XFreeGC(dpy, gc); + return pixmap; +} + +void +x11_data_to_ximage(unsigned char *data, unsigned char *ximage, + int x, int y, int sy, int gray) +{ + unsigned long *d; + int i, n; + + if (display_type == PSEUDOCOLOR) { + if (gray) { + for (i = 0; i < y; i++) + dither_line_gray(data + x * i, ximage + x * i, i + sy, x); + } else { + for (i = 0; i < y; i++) + dither_line(data + 3 * x * i, ximage + x * i, i + sy, x); + } + } else { + d = (unsigned long *) ximage; + if (gray) { + n = x * y; + for (i = 0; i < n; i++) + *(d++) = x11_lut_gray[data[i]]; + } else { + n = 3 * x * y; + for (i = 0; i < n; i += 3) + *(d++) = x11_lut_red[data[i]] | + x11_lut_green[data[i + 1]] | + x11_lut_blue[data[i + 2]]; + } + } +} |