/* #ident	"@(#)x11:contrib/clients/xloadimage/xbitmap.c 6.8 91/12/18 Labtam" */
/* xbitmap.c:
 *
 * at one time this was XRdBitF.c.  it bears very little resemblence to it
 * now.  that was ugly code.  this is cleaner, faster, and more reliable
 * in most cases.
 *
 * jim frost 10.06.89
 *
 * Copyright, 1987, Massachusetts Institute of Technology
 *
 * Copyright 1989 Jim Frost.  See included file "copyright.h" for complete
 * copyright information.
 */

#include "mit.cpyrght"
#include "copyright.h"
#include "xloadimage.h"
#include <ctype.h>

/* SUPPRESS 560 */

#if defined(SYSV) || defined(VMS)
#include <string.h>
#define rindex strrchr
#else
char *rindex();
#endif

#define MAX_SIZE 255

static short        HexTable[256];  /* conversion value */
static unsigned int Initialized= 0; /* easier to fill in at run time */

#define b0000 0 /* things make more sense if you see them by bit */
#define b0001 1
#define b0010 2
#define b0011 3
#define b0100 4
#define b0101 5
#define b0110 6
#define b0111 7
#define b1000 8
#define b1001 9
#define b1010 10
#define b1011 11
#define b1100 12
#define b1101 13
#define b1110 14
#define b1111 15

#define HEXSTART -1
#define HEXDELIM -2
#define HEXBAD   -3

/* build a hex digit value table with the bits inverted
 */

static void initHexTable()
{ int a;

  for (a= 0; a < 256; a++)
    HexTable[a]= HEXBAD;

  HexTable['0']= b0000;
  HexTable['1']= b1000;
  HexTable['2']= b0100;
  HexTable['3']= b1100;
  HexTable['4']= b0010;
  HexTable['5']= b1010;
  HexTable['6']= b0110;
  HexTable['7']= b1110;
  HexTable['8']= b0001;
  HexTable['9']= b1001;
  HexTable['A']= b0101; HexTable['a']= HexTable['A'];
  HexTable['B']= b1101; HexTable['b']= HexTable['B'];
  HexTable['C']= b0011; HexTable['c']= HexTable['C'];
  HexTable['D']= b1011; HexTable['d']= HexTable['D'];
  HexTable['E']= b0111; HexTable['e']= HexTable['E'];
  HexTable['F']= b1111; HexTable['f']= HexTable['F'];
  HexTable['x']= HEXSTART;
  HexTable['\r']= HEXDELIM;
  HexTable['\n']= HEXDELIM;
  HexTable['\t']= HEXDELIM;
  HexTable[' ']= HEXDELIM;
  HexTable[',']= HEXDELIM;
  HexTable['}']= HEXDELIM;

  Initialized = 1;
}

/* read a hex value and return its value
 */

static int nextInt(zf)
     ZFILE *zf;
{ int c;
  int value= 0;
  int shift= 0;
    
  for (;;) {
    c= zgetc(zf);
    if (c == EOF)
      return(-1);
    else {
      c= HexTable[c & 0xff];
      switch(c) {
      case HEXSTART:
	shift= 0; /* reset shift counter */
	break;
      case HEXDELIM:
	if (shift)
	  return(value);
	break;
      case HEXBAD:
	return(-1);
      default:
	value += (c << shift);
	shift += 4;
      }
    }
  }
}

/*
 * Return TRUE if the given file is an xbitmap file
 */

boolean isXbitmap(zf, w, h, v10p, title)
ZFILE         *zf;
unsigned int  *w, *h;
int           *v10p;
char         **title;
{
  char          line[MAX_SIZE];
  char          name_and_type[MAX_SIZE];
  char         *type;
  int           value;
  int           maxlines=8;	/* 8 lines to find #define etc. */

  *title = NULL;
  *w = 0;
  *h = 0;

  /* get width/height values */

  while (zgets((byte *)line, MAX_SIZE, zf) && maxlines-- > 0) {
    if (strlen(line) == MAX_SIZE-1) {
      return FALSE;
    }

    /* width/height/hot_x/hot_y scanning
     */

    if (sscanf(line,"#define %s %d", name_and_type, &value) == 2) {
      if (!(type = rindex(name_and_type, '_')))
	type = name_and_type;
      else
	type++;

      if (!strcmp("width", type))
	*w = (unsigned int)value;
      if (!strcmp("height", type))
	*h = (unsigned int)value;
    }

    /* if start of data, determine if it's X10 or X11 data and break
     */

    if (sscanf(line, "static short %s = {", name_and_type) == 1) {
      *v10p = 1;
      break;
    }
    if ((sscanf(line,"static unsigned char %s = {", name_and_type) == 1) ||
	(sscanf(line, "static char %s = {", name_and_type) == 1)) {
      *v10p = 0;
      break;
    }
  }

  if (!*w || !*h) {
    return FALSE;
  }

  /* get title of bitmap if any
   */

  if ((type = rindex(name_and_type, '_')) && !strcmp("bits[]", type + 1)) {
    *type= '\0';
    *title= dupString(name_and_type);
  }
  return TRUE;
}

Image *xbitmapLoad(fullname, image_ops, verbose)
     ImageOptions *image_ops;
     char         *fullname;
     boolean       verbose;
{ ZFILE        *zf;
  char         *name = image_ops->name;
  Image        *image;
  char         *title;
  int           value;
  int           v10p;
  unsigned int  linelen, dlinelen;
  unsigned int  x, y;
  unsigned int  w, h;
  byte         *dataptr;

  if (! (zf= zopen(fullname))) {
    perror("xbitmapLoad");
    return(NULL);
  }

  /* See if it's an x bitmap */
  {
    unsigned int  tw, th;
    int           tv10p;
    if (!isXbitmap(zf, &tw, &th, &tv10p, &title))
    {
      zclose(zf);
      return NULL;
    }
    w = tw;
    h = th;
    v10p = tv10p;
  }

  image= newBitImage(w, h);

  if (title == NULL)
    image->title= title;
  else
    image->title= dupString(name);

  if (verbose) {
    printf("%s is a %dx%d X%s bitmap file titled '%s'\n",
           name, image->width, image->height, v10p ? "10" : "11", image->title);
  }
    
  /* read bitmap data
   */

  if (!Initialized)
    initHexTable();

  znocache(zf);
  linelen= (w / 8) + (w % 8 ? 1 : 0); /* internal line length */
  if (v10p) {
    dlinelen= (w / 8) + (w % 16 ? 2 : 0);
    dataptr= image->data;
    for (y= 0; y < h; y++) {
      for (x= 0; x < dlinelen; x++) {
	if ((value= nextInt(zf)) < 0) {
          fprintf(stderr,"xbitmapLoad: %s - short read on X bitmap file\n", name);
	  zclose(zf);
	  return(image);
	}
	*(dataptr++)= value >> 8;
	if (++x < linelen)
	  *(dataptr++)= value & 0xff;
      }
    }
  }
  else {
    dataptr= image->data;
    for (y= 0; y < h; y++)
      for (x= 0; x < linelen; x++) {
	if ((value= nextInt(zf)) < 0)
        {
          fprintf(stderr,"xbitmapLoad: %s - short read on X bitmap file\n", name);
          zclose(zf);
          return(image);
        }
	*(dataptr++)= value;
      }
  }

  read_trail_opt(image_ops,zf,image,verbose);
  zclose(zf);
  return(image);
}

int xbitmapIdent(fullname, name)
     char         *fullname, *name;
{ ZFILE        *zf;
  char         *title;
  unsigned int  w, h;
  int           v10p;

  if (! (zf= zopen(fullname))) {
    perror("xbitmapIdent");
    return(NULL);
  }

  /* See if it's an x bitmap */
  if (!isXbitmap(zf, &w, &h, &v10p, &title))
  {
    zclose(zf);
    return 0;
  }

  if (title == NULL)
    title= dupString(name);
  
  printf("%s is a %dx%d X%s bitmap file titled '%s'\n",
         name, w, h, v10p ? "10" : "11",title);

  lfree(title);
  zclose(zf);
  return 1;
}

