/* readxwd.c */
/* This program is limited to X11 format XWD files */

#include <stdio.h>
#include "x11wd.h"
#define MAXCOLORS 256
char *malloc();

char *
readxwd(input_file,hdfpal,palsize,cols,rows)
     char *input_file;
     char *hdfpal;
     int *cols,*rows,*palsize;
{
  FILE *ifd;
  char *imageP;
  unsigned int nspace;
  int padright;

  /* open input file */
  if (strcmp(input_file,"-") == 0)
    ifd = stdin;
  else
    ifd = fopen(input_file,"r");
  /* get X info from file */
  getinit( ifd, cols, rows, &padright, palsize, hdfpal);
  
  /* alloc image array */
  nspace = *cols * *rows;
  imageP = malloc(nspace);

  /* fill image array */
  getimage(ifd, imageP, *rows, *cols, padright);

  return(imageP);
}

int bits_per_item, bits_used, bit_shift, bits_per_pixel, pixel_mask;
int bit_order, byte_swap;
char buf[4];
char *byteP;
short *shortP;
long *longP;

short bs_short();
int bs_int();
long bs_long();

getinit( file, colsP, rowsP, padrightP, palsizeP, hdfpal)
     FILE *file;
     int *colsP, *rowsP, *padrightP, *palsizeP;
     char *hdfpal;
{
  /* Assume X11 headers are larger than X10 ones. */
  unsigned char header[sizeof(X11WDFileHeader)];
  X11WDFileHeader *h11P;
  char junk[10000];
  int i, np, dummy1, dummy2, dummy3;
  unsigned  short minred, maxred;
  X11XColor x11col;
  
  h11P = (X11WDFileHeader *) header;
  
  if (fread(header,sizeof(*h11P),1,file) != 1 )
    {
      fprintf(stderr,"couldn't read X11 XWD file header");
      exit(-1);
    }
  if ( h11P->file_version != X11WD_FILE_VERSION )
    {
      byte_swap = 1;
      h11P->header_size = h11P->header_size;
      h11P->file_version = h11P->file_version;
      h11P->pixmap_format = h11P->pixmap_format;
      h11P->pixmap_depth = h11P->pixmap_depth;
      h11P->pixmap_width = h11P->pixmap_width;
      h11P->pixmap_height = h11P->pixmap_height;
      h11P->xoffset = h11P->xoffset;
      h11P->byte_order = h11P->byte_order;
      h11P->bitmap_unit = h11P->bitmap_unit;
      h11P->bitmap_bit_order = h11P->bitmap_bit_order;
      h11P->bitmap_pad = h11P->bitmap_pad;
      h11P->bits_per_pixel = h11P->bits_per_pixel;
      h11P->bytes_per_line = h11P->bytes_per_line;
      h11P->visual_class = h11P->visual_class;
      h11P->red_mask = h11P->red_mask;
      h11P->green_mask = h11P->green_mask;
      h11P->blue_mask = h11P->blue_mask;
      h11P->bits_per_rgb = h11P->bits_per_rgb;
      h11P->colormap_entries = h11P->colormap_entries;
      h11P->ncolors = h11P->ncolors;
      h11P->window_width = h11P->window_width;
      h11P->window_height = h11P->window_height;
      h11P->window_x = h11P->window_x;
      h11P->window_y = h11P->window_y;
      h11P->window_bdrwidth = h11P->window_bdrwidth;
    }

  *colsP = h11P->pixmap_width;
  *rowsP = h11P->pixmap_height;
  *padrightP = h11P->bytes_per_line * 8 / h11P->bits_per_pixel - *colsP;
  *palsizeP = h11P->colormap_entries;

  if ( fread( junk, 1, h11P->header_size - sizeof(*h11P), file ) != 
      h11P->header_size - sizeof(*h11P) )
    {
      fprintf(stderr,"couldn't read rest of X11 XWD file header");
      exit(-1);
    }
  
  /* Check whether we can handle this dump. */
  if ( h11P->pixmap_depth > 8 )
    {
      fprintf(stderr,"can't handle X11 pixmap_depth > 8");
      exit(-1);
    }
  if ( h11P->bits_per_rgb > 8 )
    {
      fprintf(stderr,"can't handle X11 bits_per_rgb > 8");
      exit(-1);
    }
  if ( h11P->ncolors > MAXCOLORS )
    {
      fprintf(stderr,"can't handle X11 ncolors > %d");
      exit(-1);
    }
  if ( h11P->pixmap_format != ZPixmap )
    {
      fprintf(stderr,"can't handle X11 pixmap_format %d", h11P->pixmap_format,
	      0,0,0,0 );
      exit(-1);
    }
  if ( h11P->bitmap_unit != 8 && h11P->bitmap_unit != 16 && 
      h11P->bitmap_unit != 32 )
    {
      fprintf(stderr,"X11 bitmap_unit (%d) is non-standard - can't handle");
      exit(-1);
    }
  
/*****************************************************************************/
  /* Read X11 colormap. */
minred = 65535;
maxred = 0;
  for ( i = 0; i < h11P->colormap_entries; i++ )
    {
      if ( fread( &x11col, sizeof(X11XColor), 1, file ) != 1 )
	{
	  fprintf(stderr,"couldn't read X11 XWD colormap");
	  exit(-1);
	}
      if (x11col.pixel < 256)
	{
	  if (minred > x11col.red) minred = x11col.red;
	  if (maxred < x11col.red) maxred = x11col.red;
	  np = x11col.pixel * 3;
	  dummy1 = (unsigned) x11col.red / 256 ;
	  dummy2 = (unsigned) x11col.green / 256 ;
	  dummy3 = (unsigned) x11col.blue / 256 ;
	  hdfpal[np]   = (unsigned char) dummy1;
	  hdfpal[np+1] = (unsigned char) dummy2;
	  hdfpal[np+2] = (unsigned char) dummy3;
	}
      else
	{
	  fprintf(stderr,"pixel value outside of valid HDF palette\n");
	  exit(-1);
	}
    }
  fprintf(stderr,"minred = %d, maxred = %d\n",minred,maxred);
  /* rest of stuff for getpixnum */
  bits_per_item = h11P->bitmap_unit;
  bits_used = bits_per_item;
  bits_per_pixel = h11P->bits_per_pixel;
  bit_order = h11P->bitmap_bit_order;
  pixel_mask = ( 1 << bits_per_pixel ) - 1;
  
  byteP = (char *) buf;
  shortP = (short *) buf;
  longP = (long *) buf;

}

getimage(file,image,y,x,pad)
     FILE *file;
     char *image;
     int x,y,pad;
{
  int i,j;

  for (i=0; i<y; i++)
    {
      for (j=0; j<x; j++)
	*(image++) = getpixnum(file);
      for ( j = 0; j < pad; j++ ) 
	(void) getpixnum( file );
    }
}


int
  getpixnum( file )
FILE *file;
{
  int p;
  
  if ( bits_used == bits_per_item )
    {
      if ( fread( buf, bits_per_item / 8, 1, file ) != 1 )
	fprintf(stderr, "couldn't read bits" );
      if ( byte_swap )
	switch ( bits_per_item )
	  {
	  case 8:
	    break;
	    
	  case 16:
	    *shortP = bs_short( *shortP );
	    break;
	    
	  case 32:
	    *longP = bs_long( *longP );
	    break;
	    
	  default:
	    fprintf(stderr, "can't happen" );
	  }
      bits_used = 0;
      
      if ( bit_order == MSBFirst )
	bit_shift = bits_per_item - bits_per_pixel;
      else
	bit_shift = 0;
    }
  
  switch ( bits_per_item )
    {
    case 8:
      p = ( *byteP >> bit_shift) & pixel_mask;
      break;
      
    case 16:
      p = ( *shortP >> bit_shift) & pixel_mask;
      break;
      
    case 32:
      p = ( *longP >> bit_shift) & pixel_mask;
      break;
      
    default:
      fprintf(stderr, "can't happen" );
    }
  
  if ( bit_order == MSBFirst )
    bit_shift -= bits_per_pixel;
  else
    bit_shift += bits_per_pixel;
  bits_used += bits_per_pixel;
  
  return p;
}


short
  bs_short( s )
short s;
{
  short ss;
  unsigned char *bp, t;
  
  ss = s;
  bp = (unsigned char *) &ss;
  t = bp[0];
  bp[0] = bp[1];
  bp[1] = t;
  return ss;
}

int
  bs_int( i )
int i;
{
  int ii;
  unsigned char *bp, t;
  
  ii = i;
  bp = (unsigned char *) &ii;
  t = bp[0];
  bp[0] = bp[3];
  bp[3] = t;
  t = bp[1];
  bp[1] = bp[2];
  bp[2] = t;
  return ii;
}

long bs_long( l )
     long l;
{
  return bs_int( l );
}
