/* * xdl 2.0 -- display a DL animation in an X-window. * * * Author: * Jonas Yngvesson * * Derived from dltogl.c by: * George Phillips * * Support for user defined animation speed: * Per Beremark */ #include #include #ifdef __convex__ #include #else #include #endif #include #include #include #include #include #define isneg16(x) ((x) & 0x8000) #define neg16(x) ((~(x) + 1) & 0x7fff) typedef struct { int version; int format; int images_per_screen; char title[21]; char author[21]; int num_screen; int num_command; } DL_info; Display *x_display; Window x_window; int x_depth; u_long pixels[256]; Pixmap *pixmap; XImage *x_image; GC gc_clear; /* * Initialize the colormap. I use a private one for PseudoColor, * I was too tired to fiddle with allocating shared colors. */ static void colormap_setup(fp, version) FILE *fp; int version; { Colormap cmap; XColor color; u_char pal[768]; int i; /* Is this the border colour? */ if (version == 2) for (i = 0; i < 3; i++) fgetc(fp); else fgetc(fp); /* * Here comes the colormap. */ fread(pal, 1, 768, fp); /* * Set up for grayscale conversion on a monochrome display. */ if (x_depth == 1) { for (i = 0; i < 256; i++) { pixels[i] = (u_long)((pal[3 * i] << 2) * 0.30 + (pal[3 * i + 1] << 2) * 0.59 + (pal[3 * i + 2] << 2) * 0.11); } return; } /* * Allocate colors on color displays. */ if (x_depth == 8) { cmap = XCreateColormap(x_display, x_window, DefaultVisual(x_display, DefaultScreen(x_display)), AllocNone); } else { cmap = DefaultColormap(x_display, DefaultScreen(x_display)); } for (i = 0; i < 256; i++) { /* * X wants 16 bit color specs and VGA uses 6 ==> shift 10 bits. */ color.red = pal[3 * i ] << 10; color.green = pal[3 * i + 1] << 10; color.blue = pal[3 * i + 2] << 10; XAllocColor(x_display, cmap, &color); pixels[i] = color.pixel; } if (x_depth == 8) { XSetWindowColormap(x_display, x_window, cmap); } } /* * Wait for a key to be pressed. In "dltogl" it was * printed as a "waitkey" command to GL with a numeric * argument. I don't know any GL commands so I don't know * if the argument was a request to wait for a specific key * or a timeout for how long to wait or anything else. * I just wait forever for any key to be pressed. */ static void wait_for_key() { XEvent event; Bool done; done = False; while (!done) { XNextEvent(x_display, &event); if (event.type == KeyPress) { done = True; } } } /* * Set up the X window. */ static void x_window_setup(title, author) char *title; char *author; { XSetWindowAttributes win_attr; /* storage for "window attributes" */ XSizeHints hints; /* storage for "window hints" */ XGCValues gcval; int screen; char label[256]; /* * Open the display. */ if (NULL == (x_display = XOpenDisplay(NULL))) { fputs("Can't open display.\n", stderr); exit(1); } /* * Create the window. */ screen = DefaultScreen(x_display); x_depth = DefaultDepth(x_display, DefaultScreen(x_display)); x_window = XCreateSimpleWindow(x_display, DefaultRootWindow(x_display), 100, 100, 320, 200, 0, BlackPixel(x_display, screen), BlackPixel(x_display, screen)); /* * set up "window hints" so that we won't be allowed to * resize the window while it's running */ sprintf(label, "%s %s %s", title, (author[0] ? "by" : ""), author); hints.flags = PSize | PMinSize | PMaxSize; hints.width = hints.min_width = hints.max_width = 320; hints.height = hints.min_height = hints.max_height = 200; XSetStandardProperties(x_display, x_window, label, title, None, NULL, 0, &hints); /* * Tell the server which events we want to recieve. * Enable backing store so we don't have to worry * about exposes (yes I know, backing store is not * guaranteed, but it works for me, hah). */ win_attr.event_mask = KeyPressMask; win_attr.backing_store = Always; XChangeWindowAttributes(x_display, x_window, CWEventMask | CWBackingStore, &win_attr); gcval.foreground = BlackPixel(x_display, screen); gc_clear = XCreateGC(x_display, x_window, GCForeground, &gcval); XMapWindow(x_display, x_window); XSync(x_display, False); } static void die(s)char*s;{fprintf(stderr,"%s\n",s);exit(1);} void main(argc, argv) int argc; char* argv[]; { DL_info dlinfo; struct itimerval timer; struct timeval timeout; char *filename; FILE *fp; u_char *image_data; char *err1, *err2, *tmp; short alpha[256], beta[256], gamma[256], delta[256]; short gray, err; u_long black, white; int dx, dy; int width, height; int *cmd; int labelpos, label; int cmdnum; int frame_freq; int fps = 25; int zoomflag = 0; int errflg = 0; int i, j; extern char *optarg; extern int optind; /* * Lets see what option we got from the user. */ while ((i = getopt(argc, argv, "zhr:")) != -1) { switch (i) { case 'z': zoomflag = 1; break; case 'h': errflg++; break; case 'r': fps = atoi(optarg); if (fps < 2) { printf("Minimum value is 2 frames per second.\n"); fps = 2; } break; case '?': default: errflg++; } } if (errflg) { fputs("usage: xdl [-z] [-h] [-r frames/second] [file.dl]\n", stderr); exit (2); } if (argv[optind] == NULL) { fp = stdin; filename = "stdin"; } else if (NULL == (fp = fopen(argv[optind], "r"))) { fprintf(stderr, "xdl: can't open %s\n", argv[1]); exit(1); } else { filename = argv[optind]; } /* * Check the version number... */ if (1 != (dlinfo.version = fgetc(fp)) && 2 != dlinfo.version) { fprintf(stderr, "xdl: This file is in an unknown format.\n"); fprintf(stderr, " I can only do .DL version 1 and 2.\n", dlinfo.version); exit(1); } /* * ...and the format. */ if (dlinfo.version == 1) dlinfo.format = 1; else dlinfo.format = fgetc(fp); switch (dlinfo.format) { case 0: /* large */ dx = dy = 0; width = 320; height = 200; dlinfo.images_per_screen = 1; zoomflag = 0; break; case 1: /* medium */ if (zoomflag) { dx = dy = 0; width = 320; height = 200; } else { dx = 80; dy = 50; width = 160; height = 100; } dlinfo.images_per_screen = 4; break; default: die("xdl: only large and medium formats are handled"); break; } /* * Get title and author (if any). */ dlinfo.title[20] = dlinfo.author[20] = 0; for (i = 0; i < 20; i++) { dlinfo.title[i] = fgetc(fp) ^ 255; if ((u_char)dlinfo.title[i] == 255) { dlinfo.title[i] = 0; } } for (i = 0; i < 20; i++) { if (dlinfo.version == 2) { dlinfo.author[i] = fgetc(fp) ^ 255; if ((u_char)dlinfo.author[i] == 255) { dlinfo.author[i] = 0; } } else { dlinfo.author[i] = 0; } } /* * Read number of screens and commands. */ dlinfo.num_screen = fgetc(fp); dlinfo.num_command = fgetc(fp); /* * Display what we know so far. */ printf("%s is a %s sized version %d .DL file.\n", filename, (dlinfo.format == 0 ? "large" : "medium"), dlinfo.version); printf("It containes %d images in a %d frame loop.\n", dlinfo.num_screen * dlinfo.images_per_screen, dlinfo.num_command); if (dlinfo.format == 1 && zoomflag) { puts("Zooming images to 320x200."); } printf("Displaying animation at %d frames per second.\n",fps); /* * Kick X into action. */ x_window_setup(dlinfo.title, dlinfo.author); colormap_setup(fp, dlinfo.version); /* * Allocate memory for the commands, the image data * and all the pixmaps. */ if (!(cmd = (int *)malloc(dlinfo.num_command * sizeof(int)))) die("xdl: out of memory"); if (NULL == (image_data = (u_char *)malloc(320 * 200))) { die("xdl: not enough memory."); } if (NULL == (pixmap = (Pixmap *)malloc(dlinfo.num_screen * dlinfo.images_per_screen * sizeof(Pixmap)))) { die("xdl: not enough memory."); } /* * Set up for error distribution * on monochrome displays. */ if (x_depth == 1) { for (i = 0; i < 256; i++) { alpha[i] = ((i - 128) * 7) / 16; beta[i] = ((i - 128) * 3) / 16; gamma[i] = ((i - 128) * 5) / 16; delta[i] = ((i - 128) * 1) / 16; } err1 = malloc(322); err2 = malloc(322); black = BlackPixel(x_display, DefaultScreen(x_display)); white = WhitePixel(x_display, DefaultScreen(x_display)); } /* * Build the pixmaps for the animation. */ printf("Building pixmaps, wait..."); fflush(stdout); for (j = 0; j < dlinfo.num_screen; j++) { u_char *src; int row; int col; /* * Read one screen of data. */ fread(image_data, 1, 320 * 200, fp); /* * Each screen can hold several images. */ for (i = 0; i < dlinfo.images_per_screen; i++) { if (x_depth == 1) { bzero(err1, 322); } /* * Get a pixmap for the image. */ pixmap[j * (dlinfo.format * 3 + 1) + i] = XCreatePixmap(x_display, x_window, 320, 200, x_depth); XFillRectangle(x_display, pixmap[j * (dlinfo.format * 3 + 1) + i], gc_clear, 0, 0, 320, 200); x_image = XGetImage(x_display, pixmap[j * (dlinfo.format * 3 + 1) + i], dx, dy, width, height, AllPlanes, ZPixmap); /* * Get a pointer to the beginning of this image. * Put the pixels in the x-image and perform * error distribution if needed. We do zooming * by reading the same data several times, together * with error distribution this gives us some smoothing * for free! :-) */ src = image_data + (i % 2) * 160 + (i / 2) * 100 * 320; for (row = 0; row < height; row++) { if (x_depth == 1) { bzero(err2, 322); } for (col = 0; col < width; col++) { if (x_depth == 1) { gray = pixels[*src] + err1[col + 1]; if (gray < 128) { err = gray; XPutPixel(x_image, col, row, black); } else { err = gray - 256; XPutPixel(x_image, col, row, white); } err1[col + 2] += alpha[err + 128]; err2[col ] += beta[err + 128]; err2[col + 1] += gamma[err + 128]; err2[col + 2] += delta[err + 128]; } else { XPutPixel(x_image, col, row, pixels[*src]); } src += (zoomflag) ? (col & 1) : 1; } if (x_depth == 1) { tmp = err1; err1 = err2; err2 = tmp; } if (dlinfo.format) { if (zoomflag) { src += (row & 1) ? 160 : -160; } else { src += 160; } } } /* * Put the image in the pixmap. */ XPutImage(x_display, pixmap[j * (dlinfo.format * 3 + 1) + i], DefaultGC(x_display, DefaultScreen(x_display)), x_image, 0, 0, dx, dy, width, height); } } printf("done.\n"); /* * Read the commands. */ for (i = 0; i < dlinfo.num_command; i++) { if (dlinfo.version == 2) { j = fgetc(fp); j += fgetc(fp) << 8; cmd[i] = j; } else { j = fgetc(fp); cmd[i] = (j % 10) - 1 + ((j / 10) - 1) * 4; } } labelpos = 0; if (isneg16(cmd[dlinfo.num_command - 1])) { labelpos = (neg16(cmd[dlinfo.num_command - 1]) + 1); /* Correct? Why add 1 ?? */ dlinfo.num_command--; /* ignore that last command */ } /* * Now for the animation. I use setitimer() and getitimer() * to try to keep 25 frames/sec. (Not that all DL-files are * adjusted to that but it seem natural) * However, as Per Beremark noted, most files seem to be tuned * for 10 - 12 frames/sec so it is possible to change the * speed with the -r switch. */ signal(SIGALRM, SIG_IGN); frame_freq = 1000000/fps; i = 0; cmdnum = 0; label = -1; while (1) { for (; i < dlinfo.num_command; i++, cmdnum++) { if (cmdnum == labelpos && label == -1) { label = i; } if (isneg16(cmd[i])) { i++; /* Skip argument to waitkey, see above */ wait_for_key(); } else { timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = frame_freq; timer.it_value.tv_sec = 0; timer.it_value.tv_usec = frame_freq; setitimer(ITIMER_REAL, &timer, NULL); XCopyArea(x_display, pixmap[cmd[i]], x_window, DefaultGC(x_display, DefaultScreen(x_display)), 0, 0, 320, 200, 0, 0); XSync(x_display, False); getitimer(ITIMER_REAL, &timer); timeout = timer.it_value; select(0, NULL, NULL, NULL, &timeout); } } i = cmdnum = label; } }