[comp.graphics] another X11 "markv" pic file displayer

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;
	}
    }
}