
/*
 * Filename: 	videostuff.c
 *
 * This file is part of the Muse authoring system,
 * developed at MIT Project Athena.
 *
 * Author(s):	Brian Michon,  Digital Equipment Corporation
 *              Russell M. Sasnett, GTE Laboratories Incorporated
 * 
 * Copyright 1989, 1990 by the Massachusetts Institute of Technology;
 * see the file COPYRIGHTS for a complete notice.
 *
 * CHANGE LOG:
 * ===========
 *
 * $Log$
 *
 */

#define VIDEO_STUFF_C

#ifndef lint
static char *rcsid_videostuff_c = 
"MuseID: videostuff.c $Revision$ $Date$ $Author$";
#endif /* lint */

#include <stdio.h>

#include "xdefs.h"
#include <X11/Xutil.h>

#define  O_SIZE 17

static unsigned int ordered_256[O_SIZE][O_SIZE] =
{
 {2, 236, 60, 220, 16, 232, 56, 216, 3, 233, 57, 217, 13, 229, 53, 213, 2},
 {130, 66, 188, 124, 144, 80, 184, 120, 131, 67, 185, 121, 141, 77, 181, 117, 130},
 {34, 194, 18, 252, 48, 208, 32, 248, 35, 195, 19, 249, 45, 205, 29, 245, 34},
 {162, 98, 146, 82, 176, 112, 160, 96, 163, 99, 147, 83, 173, 109, 157, 93, 162},
 {10, 226, 50, 210, 6, 240, 64, 224, 11, 227, 51, 211, 7, 237, 61, 221, 10},
 {138, 74, 178, 114, 134, 70, 192, 128, 139, 75, 179, 115, 135, 71, 189, 125, 138},
 {42, 202, 26, 242, 38, 198, 22, 256, 43, 203, 27, 243, 39, 199, 23, 253, 43},
 {170, 106, 154, 90, 166, 102, 150, 86, 171, 107, 155, 91, 167, 103, 151, 87, 170},
 {4, 234, 58, 218, 14, 230, 54, 214, 1, 235, 59, 219, 15, 231, 55, 215, 4},
 {132, 68, 186, 122, 142, 78, 182, 118, 129, 65, 187, 123, 143, 79, 183, 119, 132},
 {36, 196, 20, 250, 46, 206, 30, 246, 22, 193, 17, 251, 47, 207, 31, 247, 36},
 {164, 100, 148, 84, 174, 110, 158, 94, 161, 97, 145, 81, 175, 111, 159, 95, 164},
 {12, 228, 52, 212, 8, 238, 62, 222, 9, 225, 49, 209, 5, 239, 63, 223, 12},
 {140, 76, 180, 116, 136, 72, 190, 126, 137, 73, 177, 113, 133, 69, 191, 127, 140},
 {44, 204, 28, 244, 40, 200, 24, 254, 41, 201, 25, 241, 37, 197, 21, 255, 44},
 {172, 108, 156, 92, 168, 104, 152, 88, 169, 105, 153, 89, 165, 101, 149, 85, 172},
 {2, 236, 60, 220, 16, 232, 56, 216, 3, 233, 57, 217, 13, 229, 53, 213, 2}
};

static unsigned int ordered_32[O_SIZE][O_SIZE];

static unsigned int hex_original[6][9] =
{
 {21, 1, 10, 19, 2, 11, 20, 3, 12},
 {8, 17, 26, 9, 18, 27, 7, 16, 25},
 {24, 4, 13, 22, 5, 19, 23, 6, 15},
 {2, 11, 10, 3, 12, 21, 1, 10, 19},
 {27, 7, 16, 25, 8, 17, 26, 9, 18},
 {5, 9, 23, 6, 15, 24, 4, 13, 22}
};

static unsigned int ord_hex[6][9] =
{
 {47, 7, 25, 42, 9, 27, 45, 11, 29},
 {21, 39, 57, 23, 41, 59, 19, 37, 55},
 {53, 13, 31, 49, 15, 43, 51, 17, 35},
 {9, 27, 25, 11, 29, 46, 7, 25, 43},
 {59, 19, 37, 55, 21, 39, 57, 23, 41},
 {15, 23, 51, 17, 35, 49, 13, 31, 49}
};


/* 
 * calls:
 *
 *	xyfloyd(image): converts  Parallax video-format XImage image 
 *              to bitmap graphic b&w image, using floyd-steinberg
 *
 *	xyordered(image): converts  Parallax video-format XImage image 
 *              to bitmap graphic b&w image, using ordered dithering
 *
 */

#define PARALLAX_BW_MASK 0x3e


#define MUSE_DITHER_LEVELS	5
#define MUSE_BW_LEVELS 		32
#define MUSE_DITHER_COLORS 	125
#define MUSE_START_BW		35
#define MUSE_END_BW		66
#define MUSE_START_COLOR 	67
#define MUSE_END_COLOR 		191
#define MUSE_TOTAL_COLORS	(MUSE_DITHER_COLORS + MUSE_BW_LEVELS)


#define MAXRASTERS 1300
#define TRUE 1
#define FALSE 0
#define BACKGROUND 16
#define THRESHOLD 16
#define WHITE 32
#define BLACK 0

extern void     exit();
static int      byte;
static unsigned char value;

/*
typedef int     Boolean;
*/
typedef int     Raster[MAXRASTERS];
typedef Raster *RasterPtr;

static int      pels[MUSE_BW_LEVELS];	/* each entry in pels is the clt slot
					 * number of the corresponding n-th
					 * gray level */

typedef struct RasterStructDef
{
    Raster          r;
    int             x1;
    int             x2;
    int             y;
}               RasterStruct, *RasterStructPtr;

typedef struct ErrorRasterStructDef
{
    RasterStructPtr E1Ptr;
    RasterStructPtr E2Ptr;
}               ErrorRasterStruct;

void 
LoadRaster(im, y, R)
    XImage         *im;
    int             y;
    RasterStructPtr R;
{
    int             x;
    int             offset;

    R->x1 = 0;
    R->x2 = im->width;
    R->y = y;

    offset = y * im->bytes_per_line;
    for (x = 0; x < im->width; x++)
    {
	/* fast replacement for XGetPixel */
	R->r[x] = (int) ((((unsigned char *) im->data)[offset + x] 
		& PARALLAX_BW_MASK) >> 1);
    }
}

void 
floydimage(im, ras, err, x1, x2, y, fore, back)
    XImage         *im;
    Raster         *ras,
                   *err;
    int             x1,
                    x2,
                    y;
    unsigned long   fore,
                    back;

{
    register int    intensity;
    int             x,
                    offset,
                    i;

    offset = y * im->bytes_per_line;
    for (x = x1 + 1; x < x2; ++x)
    {

	intensity = (*ras)[x];

	if (intensity > THRESHOLD)
	{
	    im->data[offset + x] = fore;	/* XPutPixel replacement */
	    intensity = intensity - WHITE;
	}
	else
	{
	    im->data[offset + x] = back;	/* XPutPixel replacement */
	}

	(*ras)[x + 1] = (*ras)[x + 1] + (int) (7 * intensity) / 16;
	(*err)[x - 1] = (*err)[x - 1] + (int) (3 * intensity) / 16;
	(*err)[x] = (*err)[x] + (int) (5 * intensity) / 16;
	(*err)[x + 1] = (*err)[x + 1] + (int) (intensity) / 16;

    }
}


void 
floydraster(im, xyim, ras, err, x1, x2, y, fore, back)
    XImage         *im;
    XImage         *xyim;
    Raster         *ras,
                   *err;
    int             x1,
                    x2,
                    y;
    unsigned long   fore,
                    back;

{
    extern int      bitcount,
                    byte;
    extern unsigned char value;
    register int    intensity;
    int             x,
                    offset,
                    i,
                    count,
                    bitcount;

    offset = y * im->bytes_per_line;
    bitcount = 1;

    if ((xyim->bitmap_bit_order) == LSBFirst)
    {
	for (x = 0; x < im->width; x++)
	{

	    intensity = (*ras)[x];

	    if (intensity > THRESHOLD)
	    {
		im->data[offset + x] = back;	/* XPutPixel */

		value = (value >> 1);
		++bitcount;
		if ((bitcount % 8) == 0)
		{
		    xyim->data[byte++] = value;
		    value = 0;
		}
		intensity = intensity - WHITE;
	    }
	    else
	    {
		im->data[offset + x] = fore;	/* XPutPixel */

		value = (value >> 1) | 0x80;
		++bitcount;
		if ((bitcount % 8) == 0)
		{
		    xyim->data[byte++] = value;
		    value = 0x80;
		}

	    }


	    (*ras)[x + 1] = (*ras)[x + 1] + (int) (7 * intensity) / 16;
	    (*err)[x - 1] = (*err)[x - 1] + (int) (3 * intensity) / 16;
	    (*err)[x] = (*err)[x] + (int) (5 * intensity) / 16;
	    (*err)[x + 1] = (*err)[x + 1] + (int) (intensity) / 16;
	}
    }
    else
    {				/* destination bitmap bit order == MSBFirst */

	for (x = 0; x < im->width; x++)
	{
	    intensity = (*ras)[x];

	    if (intensity > THRESHOLD)
	    {
		im->data[offset + x] = back;	/* XPutPixel replacement */

		value = (value << 1);
		++bitcount;
		if ((bitcount % 8) == 0)
		{
		    xyim->data[byte++] = value;
		    value = 0;
		}
		intensity = intensity - WHITE;
	    }
	    else
	    {
		im->data[offset + x] = fore;	/* XPutPixel replacement */

		value = (value << 1) | 0x1;
		++bitcount;
		if ((bitcount % 8) == 0)
		{
		    xyim->data[byte++] = value;
		    value = 0x1;
		}
	    }

	    (*ras)[x + 1] = (*ras)[x + 1] + (int) (7 * intensity) / 16;
	    (*err)[x - 1] = (*err)[x - 1] + (int) (3 * intensity) / 16;
	    (*err)[x] = (*err)[x] + (int) (5 * intensity) / 16;
	    (*err)[x + 1] = (*err)[x + 1] + (int) (intensity) / 16;
	}
    }
}

void 
xyfloyd(im, xyim, fore, back)
    XImage         *im;
    XImage         *xyim;
    unsigned long   fore,
                    back;
{

    extern int      byte;
    extern unsigned char value;
    ErrorRasterStruct DErr;
    RasterStruct    err1,
                    err2;
    RasterStructPtr tempPtr;
    int             i,
                    y;

    if (!im)
    {
	fprintf(stderr, "xyfloyd(): bad image ptr\n");
	exit(0);
    }

    DErr.E1Ptr = &err1;
    DErr.E2Ptr = &err2;

    for (i = 0; i < MAXRASTERS; ++i)
    {
	DErr.E1Ptr->r[i] = BACKGROUND;
	DErr.E2Ptr->r[i] = BACKGROUND;
    }

    byte = 0;
    value = 0;
    LoadRaster(im, 0, DErr.E1Ptr);

    for (y = 0; y < im->height; y++)
    {
	LoadRaster(im, y, DErr.E2Ptr);
	floydraster(im, xyim, DErr.E1Ptr->r, DErr.E2Ptr->r, 
		    DErr.E1Ptr->x1, DErr.E1Ptr->x2, y, fore, back);
	tempPtr = DErr.E1Ptr;
	DErr.E1Ptr = DErr.E2Ptr;
	DErr.E2Ptr = tempPtr;
    }
}

void 
xyordered(im, xyim, fore, back)
    XImage         *im;
    XImage         *xyim;
    unsigned long   fore,
                    back;
{

    extern int      byte;
    extern unsigned char value;
    ErrorRasterStruct DErr;
    RasterStruct    err1,
                    err2;
    RasterStructPtr tempPtr;
    int             i,
                    y;

    if ((!im) || (!xyim))
    {
	fprintf(stderr, "xyordered(): bad image ptr(s)\n");
	exit(1);
    }

    byte = 0;
    value = 0;
    LoadRaster(im, 0, DErr.E1Ptr);

    for (y = 0; y < im->height; y++)
    {
	LoadRaster(im, y, DErr.E2Ptr);
	floydraster(im, xyim, DErr.E1Ptr->r, DErr.E2Ptr->r, 
		    DErr.E1Ptr->x1, DErr.E1Ptr->x2, y, fore, back);
	tempPtr = DErr.E1Ptr;
	DErr.E1Ptr = DErr.E2Ptr;
	DErr.E2Ptr = tempPtr;
    }
}


/* calls:
  
	setupbw(): sets up pels[] array to translate image
			into fixed 5-bit (32-level) B&W
  
	BWimage(im): converts video-format XImage im to standard
		graphic b&w image, using pel values set up
		by 'setupbw()'
*/

/* there are 32 gray levels because Plx is normally in
	graphics-over-video mode, which gives bits 1-5 for
	luminance (bit 0 is GOV flag) */

 /*
  * bit mask for each pel in YUV mode is:
  *
  * 7  6  5  4  3  2  1  0 
  * U  V  Y  Y  Y  Y  Y  G
  *
  * G=graphics over video flag
  * U=1/4 of U in YUV
  * V=1/4 of V in YUV
  */

static int did_setup = 0;

setupbw()
{
    int             i;

    for (i = 0; i < MUSE_BW_LEVELS; i++)
    {
	pels[i] = MUSE_START_BW + i;
    }

    did_setup = 1;
}

BWimage(im)
    XImage         *im;
{
    long            pval;
    register int    x,
                    y;
    int             mask;
    int             offset;
    unsigned char *          ucp;

    if(!did_setup)
      setupbw();

    if (!im)
    {
	printf("BWimage(): bad image ptr\n");
	return -1;
    }
    if (im->depth != 8)
    {
	printf("BWimage(): depth != 8\n");
	return -1;
    }

    mask = PARALLAX_BW_MASK;

    for (y = 0; y < im->height; y++)
    {
	ucp = (unsigned char *)&im->data[y*im->bytes_per_line];
	for (x = 0; x < im->width; x++,ucp++)
	{
	    *ucp = ((*ucp & mask) >> 1) + MUSE_START_BW;
	}
    }

    return 0;
}


/* =========================================================================== */

/* MuseRemoteVideo() */

extern Pixmap MuseVideoServerPixmap;
extern Display *MuseVideoServerDpy;

int
MuseRemoteVideo(win,vx,vy,vw,vh,x,y,w,h)
     Window win;
     int vx,vy,vw,vh,x,y,w,h;
{
  static char *func = "MuseRemoteVideo";
  XImage *bmim;
  XImage *im;
  static GC remoteGC;

/*
  printf("** %s: remote dpy is 0x%lx, remote pixmap is 0x%lx\n",
	 func,MuseVideoServerDpy,MuseVideoServerPixmap);
*/

  if(!remoteGC)
    {
      remoteGC = XCreateGC(MuseVideoServerDpy, 
			   DefaultRootWindow(MuseVideoServerDpy), 
			   0, NULL);
    }

  if(w > 640)
    w = 640;
  if(w < 16)
    w = 16;
  if(h > 480)
    h = 480;
  if(h < 12)
    h = 12;

  if(vw == 640 && vh == 480 && w == 640 && h == 480)
    {
      XPlxVideoStill(MuseVideoServerDpy,MuseVideoServerPixmap,remoteGC,
		     vx,vy,
		     0,0,w,h);
    }
  else
    {
      XPlxVideoScale(MuseVideoServerDpy,MuseVideoServerPixmap,remoteGC,
		     vx,vy,vw,vh,
		     0,0,w,h);
    }

  XSync(MuseVideoServerDpy,0);
  im = XGetImage(MuseVideoServerDpy,MuseVideoServerPixmap,0,0,w,h,
		 AllPlanes,ZPixmap);
  if(!im)
    {
      fprintf(stderr,"%s: failed getting XImage from remote server\n",func);
      return -1;
    }

/*
  printf("** %s: image is %dx%d, depth is %d\n",func,im->width,im->height,im->depth);
*/

  if(u_DpyIsPlx && !MuseOptions.monochrome)
    {
      XPlxVideoSet(u_Display,win,GCVideo);
      XPutImage(u_Display,win,GCVideo,im,0,0,x,y,w,h);
    }
  else if(u_DpyIsPseudo8 && !MuseOptions.monochrome)
    {
      BWimage(im);
      XPutImage(u_Display,win,GCVideo,im,0,0,x,y,w,h);
    }
  else /* u_DpyIsMono || MuseOptions.monochrome || u_DpyIsPseudo4 */
    {
      int bpl;
      char *data;
      extern char *calloc();

      bpl = (w + 7) / 8;
      data = calloc(1, h * bpl);

      bmim = XCreateImage(u_Display,
			DefaultVisual(u_Display, DefaultScreen(u_Display)),
			1, XYBitmap, 0,
			data,
			w, h,
			8, bpl);

      if (!bmim)
	{
	  fprintf(stderr,"%s: failed to create bitmap image\n",func);
	  exit(1);
	}

      xyfloyd(im, bmim,
	      BlackPixel(u_Display, DefaultScreen(u_Display)),
	      WhitePixel(u_Display, DefaultScreen(u_Display)));

      XPutImage(u_Display,win,GCVideo,bmim,0,0,x,y,w,h);

      free(data);
      bmim->data = NULL;
      XDestroyImage(bmim);
    }

  XDestroyImage(im);

  return 0;
}
