grady@fxgrp (Steven Grady) (10/04/88)
I grabbed Paul's displayer, but I was offended by the fact that it wrote all over the colormap, so I decided to modify it. Then I realized that would be a bit of work, so I decided to rewrite it from scratch. My version still isn't all that great, but it does not use a new colormap (this would be bad if the colormap installation semantics were defined for x11, but they ain't, so it ain't), use an arbitrary number of bits of red, green, and blue (for experimentation purposes), and it does basic error propagation. I didn't implement a real floyd-steinberg algorithm (you know, split the error into parts and propagate in different directions) because that would have been harder. The main problem with this program is that it still can't cope with anything other than an 8-bit pseudo-color display. But, as the man said, it does work (running on a sun 3/280, using a color microvax for the display). Compile with something like: cc -DERROR_PROP -o drawpic{,.c} -lX11 usage: drawpic [red_bits green_bits blue_bits] < pic_file Oh, of course it doesn't follow any ICC conventions, like providing window manager hints and such. Sorry.. Steven ...!ucbvax!grady grady@postgres.berkeley.edu ------------------------------ #include <stdio.h> #include <X11/Xlib.h> /* Globally useful values (maybe) */ Display *dpy; unsigned long scr; unsigned long black, white; Window root, w; Colormap cmap; unsigned char *data; XImage *image; int width, height; #define NUM_RED 2 #define NUM_GREEN 3 #define NUM_BLUE 2 #define PIXELS (1 << (num_red + num_green + num_blue)) #define MAX_PIXELS (1 << 8) /* Arbitrary.. */ int num_red = NUM_RED, num_green = NUM_GREEN, num_blue = NUM_BLUE; unsigned long pixels[MAX_PIXELS]; /* Can't do things the real way because colormaps are hosed on the microvax */ do_colormap() { int r, g, b; XColor colors[MAX_PIXELS]; unsigned long pixel; unsigned long plane_mask; GC gc; if (XAllocColorCells(dpy, cmap, False, &plane_mask, 0, pixels, PIXELS) == 0) { fprintf(stderr, "Had problems allocating %d colors\n", PIXELS); exit(1); } for (r = 0; r < 1<<num_red; r++) { for (g = 0; g < 1<<num_green; g++) { for (b = 0; b < 1<<num_blue; b++) { pixel = (r << (num_green + num_blue)) + (g << num_blue) + b; colors[pixel].flags = DoRed | DoGreen | DoBlue; colors[pixel].pixel = pixels[pixel]; colors[pixel].red = (unsigned short) (r ? (65535.0 / ((1<< num_red) - 1.0) * r): 0); colors[pixel].green = (unsigned short) (g ? (65535.0 / ((1<< num_green) - 1.0) * g): 0); colors[pixel].blue = (unsigned short) (b ? (65535.0 / ((1<< num_blue) - 1.0) * b): 0); } } } XStoreColors(dpy, cmap, colors, PIXELS); } /* On the server I am using (VAXstation II/GPX), putting an image at an arbirtrary position doesn't seem to work. So this function redraws the entire image. On a working server, I assume it would work to use the area information to only redraw the exposed region. */ draw_window(x, y, wid, h) int x, y, wid, h; { XPutImage(dpy, w, DefaultGC(dpy, scr), image, 0, 0, 0, 0, width, height); } main(argc, argv) int argc; char *argv[]; { XEvent ev; unsigned char *picture, *d, r, g, b; int i, j; int ret; unsigned char er, eg, eb; short pr, pg, pb; /* For experimentation's sake, you can specify the number of red, green, and blue bits to be used to draw. I find that you only need a few bits if you use error propagation. For instance, the teapot looks better at 1 red, 2 green, 1 blue with e.p. than 2, 2, 2 without it. */ if (argc == 4) { num_red = atoi(argv[1]); num_green = atoi(argv[2]); num_blue = atoi(argv[3]); } srandom(time()); dpy = XOpenDisplay(NULL); scr = DefaultScreen(dpy); root = RootWindow(dpy, scr); black = BlackPixel(dpy, scr); white = WhitePixel(dpy, scr); cmap = DefaultColormap(dpy, scr); if (scanf("%d %d\n", &width, &height) != 2) { fprintf(stderr, "bad format for input file\n"); exit(1); } do_colormap(); data = (unsigned char *) malloc(3*width*height); picture = (unsigned char *) malloc(width*height); /* Read in width x height RGB triples */ /* Doing one read speeds up processing.. */ if ((ret = fread(data, 3, width*height, stdin)) != width*height) { printf("Got %d triples out of %d\n", ret, width*height); } for (i = 0; i < height; i++) { er = random() % (1 << (8 - num_red)); eg = random() % (1 << (8 - num_green)); eb = random() % (1 << (8 - num_blue)); for (j = 0; j < width; j++) { d = &(data[(i * width + j) * 3]); /* Error propagation is done badly here - it only propagates to the right. Perhaps I'll do Floyd-Steinberg sometime.. */ #ifdef ERROR_PROP /* Add in the previous error values */ pr = d[0] + er; if (pr > 255) pr = 255; pg = d[1] + eg; if (pg > 255) pg = 255; pb = d[2] + eb; if (pb > 255) pb = 255; /* Compute the values that will actually be used to determine the pixel (ie with 2 bits of red, R=11111011 becomes R=11000000 [only the top two bits are significant]) */ r = pr & (((1 << num_red) - 1) << (8 - num_red)); g = pg & (((1 << num_green) - 1) << (8 - num_green)); b = pb & (((1 << num_blue) - 1) << (8 - num_blue)); /* Now that we know the values that will be used, we can figure out the error due to rounding off, and add that error back in the next pixel */ er = pr - r; eg = pg - g; eb = pb - b; #else ERROR_PROP r = d[0]; g = d[1]; b = d[2]; #endif *(picture + i * width + j) = pixels[ ((r >> (8-num_red)) << (num_green + num_blue)) + ((g >> (8 - num_green)) << num_blue) + (b >> (8 - num_blue))]; } } image = XCreateImage(dpy, DefaultVisual(dpy, scr), 8, ZPixmap, 0, picture, width, height, 8, 0); w = XCreateSimpleWindow(dpy, root, 0, 0, width, height, 1, black, white); XSelectInput(dpy, w, ExposureMask); XMapWindow(dpy, w); while (1) { XExposeEvent *xev = (XExposeEvent *) &ev; XNextEvent(dpy, &ev); switch (ev.type) { case Expose: draw_window(xev->x, xev->y, xev->width, xev->height); break; default: fprintf(stderr, "unexpected event %d from window %X\n", ev.type, ev.xany.window); break; } } }