
#include <math.h>
#include "display.h"

extern int  hwidth;
extern int  hheight;
extern unsigned char *pdata;
extern unsigned char rgbdata[];
extern int shmem;
extern int dithertype;

int  gXErrorFlag = 0;

/* Range values for lum, cr, cb. */
int LUM_RANGE;
int CR_RANGE;
int CB_RANGE;

/* Array that remaps color numbers to actual pixel values used by X server. */

unsigned char pixel[256];

/* Arrays holding quantized value ranged for lum, cr, and cb. */

int *lum_values;
int *cr_values;
int *cb_values;

/* Structures used by the X server. */

Display *display;
int depth;

static XImage *ximage = NULL;
static Colormap cmap;
static Window window;
static GC gc;

#ifdef SH_MEM
static XShmSegmentInfo shminfo;
#endif

void InitColor()
{
  int i;

  LUM_RANGE=8;
  CR_RANGE =4;
  CB_RANGE =4;

  lum_values = (int *) malloc(LUM_RANGE*sizeof(int));
  cr_values  = (int *) malloc(CR_RANGE*sizeof(int));
  cb_values  = (int *) malloc(CB_RANGE*sizeof(int));
    
  for (i=0; i<LUM_RANGE; i++) 
    lum_values[i] = ((i * 256) / (LUM_RANGE)) + (256/(LUM_RANGE*2));
    
  for (i=0; i<CR_RANGE; i++) 
    cr_values[i] = ((i * 256) / (CR_RANGE)) + (256/(CR_RANGE*2));
    
  for (i=0; i<CB_RANGE; i++) 
    cb_values[i] = ((i * 256) / (CB_RANGE)) + (256/(CB_RANGE*2));
}

static void ConvertColor(l, cr, cb, r, g, b)
     unsigned char l, cr, cb;
     unsigned char *r, *g, *b;
{
  double fl, fcr, fcb, fr, fg, fb;
    
  fl  = (double) l;
  fcr = ((double) cr) - 128.0;
  fcb = ((double) cb) - 128.0;
    
  fr = fl + (1.40200 * fcb);
  fg = fl - (0.71414 * fcb) - (0.34414 * fcr);
  fb = fl + (1.77200 * fcr);
  
  if (fr < 0.0) fr = 0.0;
  else if (fr > 255.0) fr = 255.0;
    
  if (fg < 0.0) fg = 0.0;
  else if (fg > 255.0) fg = 255.0;
    
  if (fb < 0.0) fb = 0.0;
  else if (fb > 255.0) fb = 255.0;
    
  *r = (unsigned char) fr;
  *g = (unsigned char) fg;
  *b = (unsigned char) fb;
    
}

void MakeWindow(name) 
     char *name;
{
    
  XSizeHints hint;
  unsigned int fg, bg;
  char *hello = "Smart Capture Card Window Ver 0.21";
  int screen;
  XVisualInfo vinfo;    

  display = XOpenDisplay(name);
  if (display == NULL) {
    fprintf(stderr, "Can not open display\n");
    exit(-2);
  }
    
  screen = DefaultScreen (display);
  depth  = DefaultDepth(display,DefaultScreen(display));

  /* Fill in hint structure */
    
  hint.x      = 0;
  hint.y      = 0;
  hint.width  = hwidth;
  hint.height = hheight;

  hint.flags = PPosition | PSize;
  
  /* Get some colors */
    
  bg = WhitePixel (display, screen);
  fg = BlackPixel (display, screen);
    
  /* Make the window */

  switch(depth){

  case  8 :

    if (!XMatchVisualInfo (display, screen, 8, PseudoColor, &vinfo)) {
	    
      if (!XMatchVisualInfo(display, screen, 8, GrayScale, &vinfo)) {
	fprintf(stderr, "-requires 8 bit display\n");
	exit(-1);
      }
    }

  case 16 :

    window = XCreateSimpleWindow (display,
				  DefaultRootWindow (display),
				  hint.x, hint.y,
				  hint.width, hint.height,
				  4, fg, bg);
    break;

  case 24 :
  case 32 :

    window = CreateFullColorWindow (display, 
				    hint.x, hint.y, 
				    hint.width, hint.height);
  break;

  }

  XSelectInput(display, window, StructureNotifyMask);
  XSetStandardProperties (display, window, hello, hello, None, NULL, 0, &hint);
  XMapWindow(display, window);
    
  while(1) {
    XEvent	xev;
    XNextEvent(display, &xev);
    if(xev.type == MapNotify && xev.xmap.event == window)
      break;
  }

  XSelectInput(display, window, ButtonPressMask | KeyPressMask);

}

void InitDisplay()
{
    
  int ncolors;
  XColor xcolor;
  int i, lum_num, cr_num, cb_num;
  unsigned char r, g, b;
  Colormap dcmap;
  char dummy;
  Visual *fc_visual;

  gc = XCreateGC(display, window, 0, 0);

  switch(dithertype){

  case ORDERED_DITHER:
    ncolors = LUM_RANGE*CB_RANGE*CR_RANGE;

    dcmap = cmap = XDefaultColormap(display, DefaultScreen(display));
    xcolor.flags = DoRed | DoGreen | DoBlue;
    
    for (i=0; i<ncolors; i++) {
	
      lum_num = (i / (CR_RANGE*CB_RANGE))%LUM_RANGE;
      cr_num  = (i / (CB_RANGE         ))% CR_RANGE;
      cb_num  = (i                      )% CB_RANGE;
	
      ConvertColor(lum_values[lum_num], 
		   cr_values[cr_num], 
		   cb_values[cb_num],
		   &r, &g, &b);
	
      xcolor.red   = r * 256;
      xcolor.green = g * 256;
      xcolor.blue  = b * 256;
	
      if(XAllocColor(display, cmap, &xcolor) == 0 && cmap == dcmap) {

	int j;
	long tmp_pixel;
	XWindowAttributes xwa;

	for(j = 0; j < i; j ++) {
	  tmp_pixel = pixel[j];
	  XFreeColors(display, cmap, &tmp_pixel, 1, 0);
	}

	XGetWindowAttributes(display, window, &xwa);
	cmap = XCreateColormap(display, window, xwa.visual, AllocNone);
	XSetWindowColormap(display, window, cmap);

      }

      pixel[i] = xcolor.pixel;

    }

    break;

  case GRAY_DITHER :

    ncolors = NUM_COLORS ;
    dcmap = cmap = XDefaultColormap(display, DefaultScreen(display));
    xcolor.flags = DoRed | DoGreen | DoBlue;

  for (i=0; i<ncolors; i++) {

    xcolor.red   = i * 256;
    xcolor.green = i * 256;
    xcolor.blue  = i * 256;

    if(XAllocColor(display, cmap, &xcolor) == 0 && cmap == dcmap) {

	int j;
	long tmp_pixel;
	XWindowAttributes xwa;

	for(j = 0; j < i; j ++) {
	  tmp_pixel = pixel[j];
	  XFreeColors(display, cmap, &tmp_pixel, 1, 0);
	}

	XGetWindowAttributes(display, window, &xwa);
	cmap = XCreateColormap(display, window, xwa.visual, AllocNone);
	XSetWindowColormap(display, window, cmap);

    }

    pixel[i]   = xcolor.pixel;

  }

  break;

  case FULL_COLOR_DITHER:
  case DEPTH16:
  break;
    
  }

#ifdef SH_MEM

  if(shmem){
    int HandleXError();

    switch(dithertype){

    case ORDERED_DITHER:
    case GRAY_DITHER :

      ximage = XShmCreateImage (display, None, depth, ZPixmap,
				NULL, &shminfo, hwidth,hheight);
      shminfo.shmid = shmget (IPC_PRIVATE,hwidth*hheight,IPC_CREAT | 0777);

      break;

    case DEPTH16:

      fc_visual = FindFullColorVisual(display, &depth);
      ximage = XShmCreateImage(display, fc_visual, depth, ZPixmap,
			       NULL, &shminfo, hwidth, hheight);

      shminfo.shmid = shmget (IPC_PRIVATE,hwidth*hheight*2,IPC_CREAT | 0777);
      break;

    case FULL_COLOR_DITHER:

      fc_visual = FindFullColorVisual(display, &depth);
      ximage = XShmCreateImage(display, fc_visual, depth, ZPixmap,
			       NULL, &shminfo, hwidth, hheight);

      shminfo.shmid = shmget (IPC_PRIVATE,hwidth*hheight*4,IPC_CREAT | 0777);
      break;

    }

    shminfo.shmaddr = ximage->data = shmat (shminfo.shmid, 0, 0);
    shminfo.readOnly = True;

    XSetErrorHandler(HandleXError);
    XShmAttach (display, &shminfo);
    XSync(display, False);
    shmctl(shminfo.shmid, IPC_RMID, NULL);

    if(gXErrorFlag){
      XDestroyImage(ximage);
      ximage = NULL;
      shmem = 0;
    } else {
      pdata = ximage->data;
    }
  }

#endif

  if(!shmem){

    switch(dithertype){

    case ORDERED_DITHER:
    case GRAY_DITHER :
      ximage = XCreateImage(display, None, depth, ZPixmap, 0, &dummy,
			    hwidth,hheight, 8, 0);
      pdata = (char *)malloc(hwidth*hheight);
    break;

    case DEPTH16 :
      fc_visual = FindFullColorVisual(display, &depth);
      ximage = XCreateImage (display, fc_visual, depth, ZPixmap, 0, &dummy,
			     hwidth,hheight, 16, 0);
      pdata = (char *)malloc(hwidth*hheight*2);
    break;

    case FULL_COLOR_DITHER :
      fc_visual = FindFullColorVisual(display, &depth);
      ximage = XCreateImage (display, fc_visual, depth, ZPixmap, 0, &dummy,
			     hwidth,hheight, 32, 0);
      pdata = (char *)malloc(hwidth*hheight*4);
    break;

    }
    ximage->data=pdata;
  }

  if (ximage == NULL) {
    fprintf(stderr, "Unable to create ximage!\n");
    exit(1);
  }

  XSync(display, False);

}


void ExecuteDisplay()
{

#ifdef SH_MEM

  if(shmem){

    XShmPutImage(display, window, gc, ximage, 0, 0, 0, 0,
		 ximage->width, ximage->height, True);
  }

#endif

  if(!shmem){
    XPutImage(display, window, gc, ximage, 0, 0, 0, 0, 
	      ximage->width, ximage->height);
  }

  XFlush(display);

}

int HandleXError(dpy, event)
     Display *dpy;
     XErrorEvent *event;
{
  gXErrorFlag = 1;

  return 0;
}
