
/* fiddle with X11 dumps
 * Seth Finkelstein (sethf@athena.mit.edu) 
 * Copyright 1988,1989 All rights reserved
 * This code is stolen from everywhere. It's research. 
 * It's also a horror. I didn't want to spend much time on it.
 * YUV conversion from Parallax Graphics and Phil Thompson, phils@athena.mit.edu
 */

#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <X11/Xlib.h>

extern char *calloc();

typedef unsigned long xwdval;

typedef struct _xwd7_file_header {
	xwdval header_size;	  /* Size of the entire file header (bytes). */
	xwdval file_version;	  /* XWD_FILE_VERSION */
	xwdval pixmap_format;	  /* Pixmap format */
	xwdval pixmap_depth;	  /* Pixmap depth */
	xwdval pixmap_width;	  /* Pixmap width */
	xwdval pixmap_height;	  /* Pixmap height */
	xwdval xoffset;           /* Bitmap x offset */
	xwdval byte_order;        /* MSBFirst, LSBFirst */
	xwdval bitmap_unit;       /* Bitmap unit */
	xwdval bitmap_bit_order;  /* MSBFirst, LSBFirst */
	xwdval bitmap_pad;	  /* Bitmap scanline pad */
	xwdval bits_per_pixel;	  /* Bits per pixel */
	xwdval bytes_per_line;	  /* Bytes per scanline */
	xwdval visual_class;	  /* Class of colormap */
	xwdval red_mask;	  /* Z red mask */
	xwdval green_mask;	  /* Z green mask */
	xwdval blue_mask;	  /* Z blue mask */
	xwdval bits_per_rgb;	  /* Log base 2 of distinct color values */
	xwdval colormap_entries;  /* Number of entries in colormap */
	xwdval ncolors;		  /* Number of Color structures */
	xwdval window_width;	  /* Window width */
	xwdval window_height;	  /* Window height */
	long window_x;		  /* Window upper left X coordinate */
	long window_y;		  /* Window upper left Y coordinate */
	xwdval window_bdrwidth;	  /* Window border width */
} XWD7FileHeader;

#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define ABS(a) ((a) < 0 ? -(a) : (a))

#define FAILURE 0

#define FALSE 0
#define TRUE 1

extern int errno;
char *progname;
int debug = FALSE;

main(argc, argv)
    int argc;
    char **argv;
{
    u_char *buffer;
    u_char *old_buffer;
    XWD7FileHeader xf7;
    unsigned long swaptest = 1;
    unsigned status;
    unsigned buffer_size;
    int win_name_size;
    char *str_index;
    char *in_file_name;
    char *out_file_name;
    char display[256];
    char *win_name;
    int standard_in = TRUE;
    int standard_out = TRUE;
    int preview = FALSE;
    int mpel = FALSE;

    int ncolors;
    XColor *colors;
    register int i;

    FILE *in_file = stdin;
    FILE *out_file = stdout;

    progname = argv[0];

    for (i = 1; i < argc; i++) {
	str_index = (char *)index (argv[i], ':');
	if(str_index != NULL) {
	    (void) strncpy(display,argv[i],sizeof(display));
	    continue;
        }
	str_index = (char *) index (argv [i], '-');
	if (str_index == NULL) Syntax(argv[0]);
	if (strncmp(argv[i], "-help", 5) == 0) {
	    Syntax(argv[0]);
	}
	if (strncmp(argv[i], "-in", 3) == 0) {
	    if (++i >= argc) Syntax(argv[0]);
	    in_file_name = argv[i];
	    standard_in = FALSE;
	    continue;
	}
	if (strncmp(argv[i], "-out", 4) == 0) {
	    if (++i >= argc) Syntax(argv[0]);
	    out_file_name = argv[i];
	    standard_out = FALSE;
	    continue;
	}
	if(strcmp(argv[i], "-debug") == 0) {
	    debug = TRUE;
	    continue;
	}
	if (strcmp(argv[i], "-p") == 0) {
	    preview = !preview;
	    continue;
	}
	if (strcmp(argv[i], "-m") == 0) {
	    mpel = !mpel;
	    continue;
	}
	Syntax(argv[0]);
    }
    
    if (!standard_in) {
	/*
	 * Open the input file.
	 */
	in_file = fopen(in_file_name, "r");
	if (in_file == NULL) {
	    Error("Can't open input file as specified.");
	}
    }    

    /*
     * Read in header information.
     */
    if(fread((char *)&xf7, sizeof(xf7), 1, in_file) != 1)
      Error("Unable to read dump file header.");

     if (*(char *) &swaptest)
        _swaplong((char *) &xf7, sizeof(xf7));

    /*
     * check to see if the dump file is in the proper format.
     */
    if (xf7.file_version != 7) {
	    fprintf(stderr,"%s: XWD file format version missmatch. Got %d\n",progname,xf7.file_version);
    }

    if (xf7.header_size < sizeof(xf7)) {
      fprintf(stderr,"%s: XWD header size is too small.",progname);
      Error("exiting.");
    }

    ncolors = xf7.ncolors;

    /*
     * Calloc window name.
     */
    win_name_size = ABS(xf7.header_size - sizeof(xf7));
    if((win_name = calloc((unsigned) win_name_size, sizeof(char))) == NULL)
      Error("Can't calloc window name storage.");

    /*
     * Read in window name.
     */
    if(fread(win_name, sizeof(char), win_name_size, in_file) != win_name_size)
      Error("Unable to read window name from dump file.");
    if(debug) fprintf(stderr,"win_name =%s\n", win_name);


    /* Calloc the color map buffer.
     */
    if(ncolors = xf7.ncolors) {
      colors = (XColor *)calloc((unsigned)ncolors,sizeof(XColor));
      if(fread((char *) colors, sizeof(XColor), ncolors, in_file) != ncolors)
        Error("Unable to read color map from dump file.");
      if(debug)
        fprintf(stderr,"Read %d colors\n", ncolors);
      if (*(char *) &swaptest) {
          for (i = 0; i < ncolors; i++) {
              _swaplong((char *) &colors[i].pixel, sizeof(long));
              _swapshort((char *) &colors[i].red, 3 * sizeof(short));
          }
      }
    }

    buffer_size = xf7.bytes_per_line * xf7.pixmap_height;
    /*
     * Calloc the pixel buffer.
     */
    if((buffer = (u_char *) calloc(buffer_size, 1)) == NULL)
      Error("Can't calloc data buffer.");

    /*
     * Read in the pixmap buffer.
     */
    if((status = fread((char *)buffer, sizeof(char), (int)buffer_size, in_file))
       != buffer_size)
      Error("Unable to read pixmap from dump file.");
    /*
     * Close the input file.
     */
    (void) fclose(in_file);


        buffer += 44 * xf7.pixmap_width; /* hack for removing to top buttons */
        xf7.pixmap_height -= 44;    /* assuming height of 44 for box */
        buffer_size = (unsigned) xf7.pixmap_width * xf7.pixmap_height;
        /* ONLY on Athena's xvideo widget */
        /* Above from Phil Thompson, phils@athena.mit.edu */

        yuv_decode(buffer, NULL, NULL, buffer_size, 1);

	if (mpel) xf7.bits_per_rgb = 4;
        ncolors = 256;
	if (preview) ncolors = 192;	/* For parallax */

	xf7.colormap_entries = ncolors;
	xf7.ncolors = ncolors;

        colors = (XColor *)calloc((unsigned)ncolors,sizeof(XColor));

        for (i=0; i < ncolors; i++) {
	colors[i].pixel = i;
        colors[i].red = colors[i].green =  colors[i].blue = 
        (u_short) ((i/((double) (ncolors - 1))) * 65535);
        }

    /*
     * Calculate header size.
    if (debug) fprintf(stderr,"%s: Calculating header size.\n",progname);
    xf7_size = sizeof(xf7) + win_name_size;
     */

    if (!standard_out) {
	/*
	 * Open the output file.
	 */
	out_file = fopen(out_file_name, "w");
	if (out_file == NULL) {
	    Error("Can't open output file as specified.");
	}
    }    

    /*
     * Write out header information.
     */
    if (debug) fprintf(stderr,"%s: Constructing and dumping file header.\n",progname);


    if (*(char *) &swaptest) {
      _swaplong((char *) &xf7, sizeof(xf7));
      for (i = 0; i < ncolors; i++) {
          _swaplong((char *) &colors[i].pixel, sizeof(long));
          _swapshort((char *) &colors[i].red, 3 * sizeof(short));
      }
    }

    (void) fwrite((char *)&xf7, sizeof(xf7), 1, out_file);
    (void) fwrite(win_name, win_name_size, 1, out_file);

    if (debug) fprintf(stderr,"%s: Dumping %d colors.\n",progname,ncolors);
    (void) fwrite((char *) colors, sizeof(XColor), ncolors, out_file);

    /*
     * Write out the buffer.
     */
    if (debug) fprintf(stderr,"%s: Dumping pixmap.\n",progname);
    (void) fwrite((char *)buffer, (int) buffer_size, 1, out_file);

    /*
     * Close the output file.
     */
    if (debug) fprintf(stderr,"%s: Closing output file.\n",progname);
    (void) fclose(out_file);

    /*
     * Free the picture buffer.
     */
    if (debug) fprintf(stderr,"%s: Freeing picture buffer.\n",progname);
    free((char *)buffer);

    /*
     * Free window name string.
     */
    if (debug) fprintf(stderr,"%s: Freeing window name string.\n",progname);
    free(win_name);
    exit(0);
}



/*
 * Report the syntax for calling xpand.
 */
Syntax(call)
    char *call;
{
    fprintf( stderr,
	"%s: %s [-help][-debug][-in <file>][-out <file>][[host]:vs]\n",
    	progname,call);
    exit(1);
}


/*
 * Error - Fatal program error.
 */
Error(string)
	char *string;	/* Error description string. */
{
	fprintf(stderr, "%s: Error => %s\n",progname,string);

	if (errno != 0) {
		perror(progname);
		fprintf(stderr, "\n");
	}

	exit(1);
}

_swapshort (bp, n)
    register char *bp;
    register unsigned n;
{
    register char c;
    register char *ep = bp + n;

    while (bp < ep) {
      c = *bp;
      *bp = *(bp + 1);
      bp++;
      *bp++ = c;
    }
}

_swaplong (bp, n)
    register char *bp;
    register unsigned n;
{
    register char c;
    register char *ep = bp + n;
    register char *sp;

    while (bp < ep) {
        sp = bp + 3;
        c = *sp;
        *sp = *bp;
        *bp++ = c;
        sp = bp + 1;
        c = *sp;
        *sp = *bp;
        *bp++ = c;
        bp += 2;
    }
}

/* From Phil Thompson, phils@athena.mit.edu */
struct conv_table {
    long ry, gy, by;
};

yuv_decode(red_buf, grn_buf, blu_buf, bufsize, grey_flag)
u_char *red_buf, *grn_buf, *blu_buf;
unsigned  bufsize;
Bool grey_flag;
{
    register int  i, j, k;
    register int  red, green, blue, gray, idx;
    struct conv_table  *p, ctable[256];
    u_char minmax_table[768], *normalize;

    setup_tables(ctable, minmax_table);
    normalize = &minmax_table[256];

    for (j=0, i=0; i < bufsize; i+=4) {
        idx = ((red_buf[i] & 0xc0)>>6) | ((red_buf[i+1] & 0xc0)>>4) |
                ((red_buf[i+2] & 0xc0)>>2) | ((red_buf[i+3] & 0xc0));
        p = &ctable[idx];
        for (k=0; k < 4; k++, j++) {
            gray = (red_buf[j] & 0x3e) << 2;
            red = normalize[gray + p->ry];
            green = normalize[gray + p->gy];
            blue = normalize[gray + p->by];
                   red_buf[j] = (u_char)((int)((float)red*0.299 +
                    (float)green*0.587 + (float)blue*0.114 + 0.5));
        }
    }
}
            
/*  Copyright (c) 1987 by
*   PARALLAX GRAPHICS, INCORPORATED, Santa Clara, California.
*   All rights reserved
*   Author:   Martin J. Levy.
*             Parallax Graphics
*   Date:     October 1987
*   Modified: P. Thompson, Nov. 1988.  Simplified code.
*/
#define max(a,b)    ((a) < (b) ? (b) : (a))
#define min(a,b)    ((a) > (b) ? (b) : (a))

setup_tables(ctbl, minmax_table)
struct conv_table  ctbl[256];
u_char  minmax_table[768];
{
    register int i;
    register u_char *minmax = &minmax_table[256];

    for (i=0; i < 256; i++) {
        ctbl[i].ry = (((((i & 0x03)<<2) | ((i & 0x0c)>>2)) & 0xf) - 8)
            << 13;
        ctbl[i].ry *= 179;

        ctbl[i].by = (((((i & 0x30)>>2) | ((i & 0xc0)>>6)) & 0xf) - 8)
            << 13;
        ctbl[i].by *= 228;

        ctbl[i].gy = -((ctbl[i].ry>>8) * 131) - ((ctbl[i].by>>8) * 49);

        ctbl[i].ry >>= 16;
        ctbl[i].gy >>= 16;
        ctbl[i].by >>= 16;

        if (debug)
            fprintf(stderr,"pixel[%u]: r %4ld   g %4ld   b %4ld\n", i,
                ctbl[i].ry, ctbl[i].gy, ctbl[i].by);
    }
    /* pre computed min and max of all pixel values, normalizes 0-255 */
    for (i= -256; i < 512; i++)
        minmax[i] = min(255, max(0, i));
}
