/************************************************************************/
/*									*/
/*	Copyright Pittsburgh Supercomputing Center 1987, 1988, 1990	*/
/*	All Rights Reserved						*/
/*	Author Joel Welling						*/
/*			    						*/
/*      CGMGEN.C version 3.2                                            */
/*                                                                      */
/************************************************************************/

/*  This module recognizes what type of machine it's on by the presence
of the symbol VMS, unix, CRAY, or ardent.  The following makes the machine
assignment less ambiguous.
*/
#ifdef CRAY
#undef unix
#endif
#ifdef ardent
#undef unix
#endif

#ifdef VMS
#include descrip
#include stdio                            
#endif
#ifdef CRAY
#include <stdio.h>
#include <string.h>
#include <fortran.h>
#endif
#ifdef unix
#include <stdio.h>
#include <string.h>
#endif
#ifdef ardent
#include <stdio.h>
#include <string.h>
#endif

/* Include defs needed to comply with the GPlot driver interface */
#include "defs.h"

/* Include defs files that allow linkage to Fortran on various systems */
#ifdef unix
#include "unix_defs.h"
#endif
#ifdef CRAY
#include "unicos_defs.h"
#endif
#ifdef ardent
#include "unicos_defs.h"  /* these are also appropriate on Ardent Titan */
#endif

/* Default GPlot device driver name, and associated storage. */
#define devnamlen 20
static char devnam[devnamlen]= "cgmb";

/* 
  CGM metafile states: 
	0=uninitialized, or after 'end metafile'
	1= after 'begin metafile' or 'end picture'
	2= after 'begin picture'
	3= after 'begin picture body'
*/
int cgmstate=0;

/* Page number */
static int pgnum= 0;

/* Storage for picture name */
#define maxpicnamelength 256
static char picture_name[maxpicnamelength];	
	/* will initially be nulls, which is correct */

/* Structures which maintain the CGM state */
static struct 	mf_d_struct 	glbl1;		/* the class 1 elements */
static struct 	pic_d_struct 	glbl2,dflt2;	/* the class 2 elements */
static struct 	control_struct	glbl3,dflt3;	/* the class 3 elements */
static struct 	attrib_struct	glbl5,dflt5; 	/* the class 5 elements */
       
/* 
The arrays of function pointers, filled in externally by the
device initialisation routine 
*/
static int (*delim[Delim_Size])();	/* delimiter functions */
static int (*mfdesc[MfDesc_Size])();	/* metafile descriptor functions */
static int (*pdesc[PDesc_Size])();	/* page descriptor functions */
static int (*mfctrl[Control_Size])();	/* mf control functions */
static int (*gprim[GPrim_Size])();	/* graphical primitives */
static int (*attr[Att_Size])();		/* the attribute functions */
static int (*escfun[Esc_Size])();	/* the escape functions */
static int (*extfun[Ext_Size])();	/* the external functions */
static int (*ctrl[Delim_Size])();	/* external controller functions */

/* Device driver attribute storage */
static struct info_struct dev_info;

/* 
Storage for simulated command line options (global so debugging
option will extend to driver)
*/
static struct one_opt cmd_opt[opt_size];

/* Abbreviation for debugging option */
#define debugmode cmd_opt[(int)debug].val.i

/* Set up interface to Fortran character string descriptors */
#define STRINGLENGTH /* nothing */
#define DEFSTRINGLENGTH /* nothing */
#ifdef VMS
typedef struct dsc$descriptor_s *string_descriptor;
#endif
#ifdef CRAY
typedef _fcd string_descriptor;
#endif                           
#ifdef unix
typedef char *string_descriptor;
#define STRINGLENGTH ,stringlength
#define DEFSTRINGLENGTH int stringlength;
#endif
#ifdef ardent
typedef struct { char *addr; int length; } *string_descriptor;
#endif

#define float_to_byte(fval) ((unsigned char)((int)((fval)*255.0)) % 256)

/* Definition of default data storage space, and associated vars */
#define defbufsize 1024
unsigned char data_default[defbufsize], *databuf= data_default;
static unsigned int datasize= defbufsize;

/* Coordinate buffers, enlarged as needed by getcmem */
static int *ixbuf, *iybuf;
static int ixbuf_sz=0, iybuf_sz=0;

/* Row buffer size to be used by WQCADC */
#define rowsize 1024

/* Define various constants */
#define maxcgmstr 257
#define mask_1_bit 1
#define mask_2_bits 3
#define mask_4_bits 15
#define mask_5_bits 31
#define mask_7_bits 127
#define mask_8_bits 255
#define mask_15_bits 32767
#define mask_16_bits 65535
#define byte_size 8

/* 
Storage for coord-to-pixel conversion factors, and macros to do conversion.
The scaling and shift factors are set by setscale.  The world coordinate
minima and maxima may be set by setwcd and are used by setscale.  The
_scale and _shift values are used in the coordinate-to-pixel conversion
expressions which follow immediately.  wcd_ values are the x and y limits
of the world coordinate system, in terms of which the user gives coordinates.
pxl_vdc is the number of pixels per VDC unit (in the x direction).
*/
static float xpxl_scale, ypxl_scale, xpxl_shift, ypxl_shift, pxl_vdc,
	wcd_xmin=0.0, wcd_xmax=1.0, wcd_ymin=0.0, wcd_ymax=1.0;
#define xpxl(val) ( (int) (xpxl_scale*((val) + xpxl_shift)) )
#define ypxl(val) ( (int) (ypxl_scale*((val) + ypxl_shift)) )

/* 
Macros to check the existence of a function pointer, and invoke it if
found.  gbl_ret holds the return code.
*/                               
static int gbl_ret;
#define check_do(group,op) if ( group[(int) op] ) \
  	gbl_ret= ( *group[(int) op] )

/* Macro to assign an error return value based on a driver return code */
#define err_chk( ierr ) switch (gbl_ret) { \
	case 0: error("Driver error, exiting."); break; \
	case 2: ierr= 1; break; \
	default:; }

/* Byte ordering information:  Note that this may be machine dependent! */
/* The packword macro puts a CGM word into a char array in CGM order */
#define packword(word,pbyte) { *pbyte++=(word)>>8; *pbyte++=(word); }

/* Macro and associated storage to pack a CGM 32-bit fixed-point value */
static unsigned int fxwrd1, fxwrd2;

#define packfix(fval,pbyte) \
if (fval>=0.0)                                                                \
 if (fval<32768.0) { fxwrd1= (int)fval; fxwrd2= (int)((fval-fxwrd1)*65536.0);}\
 else {fxwrd1= mask_15_bits; fxwrd2= mask_16_bits;}                           \
else if (fval>=-32768.0) { fxwrd1= (((int)(-fval+.99999)) ^ mask_16_bits)+1;  \
  fxwrd2= (int)((fval + ((int)(-fval+.99999)))*65536.0);}                     \
 else {fxwrd1= 1 << 15; fxwrd2= 0;};                                          \
packword(fxwrd1,pbyte); packword(fxwrd2,pbyte);

/* Macros to implement packing of data for arbitrary precision cell arrays */
        
/* Add an arbitrary precision color value, byte aligned or not */
/* in should be an unsigned integer at least precision bits long */
/* ptr points to unsigned chars, initially zero filled */
#define mcr_ptcv(ptr, precision, in, bit) switch (precision){		\
case 32: *ptr++ = ( in & (mask_8_bits << 24) ) >> 24;			\
case 24: *ptr++ = ( in & (mask_8_bits << 16) ) >> 16;			\
case 16: *ptr++ = ( in & (mask_8_bits << 8) ) >> 8;			\
case 8: *ptr++ = ( in & mask_8_bits ); break;				\
case 4: *ptr = *ptr | ((in & mask_4_bits) << (4-bit)); bit= bit+4;	\
if (bit == 8) { bit = 0; ++ptr; }; break;				\
case 2: *ptr = *ptr | ((in & mask_2_bits) << (6-bit)); bit= bit+2;	\
if (bit == 8) { bit = 0; ++ptr; }; break;				\
case 1: *ptr = *ptr | ((in & mask_1_bit) << (7-bit)); bit= bit+1;	\
if (bit == 8) { bit = 0; ++ptr; }; };

/* Add a 2-octet integer, byte aligned or not */
/* in should be an unsigned integer of at least 16 bits precision*/
/* ptr points to unsigned chars, initially zero filled */
#define mcr_ptei(ptr,in,bit)  switch (bit) {				\
case 0: *ptr++= (in >> 8) & mask_8_bits;				\
*ptr++= in & mask_8_bits; break;					\
case 1: *ptr= *ptr | ((in >> 9) & mask_8_bits); ptr++;			\
*ptr++= (in >> 1) & mask_8_bits; *ptr= (in << 7) & mask_8_bits; break;	\
case 2: *ptr= *ptr | ((in >> 10) & mask_8_bits); ptr++;    		\
*ptr++= (in >> 2) & mask_8_bits; *ptr= (in << 6) & mask_8_bits; break;	\
case 3: *ptr= *ptr | ((in >> 11) & mask_8_bits); ptr++;     		\
*ptr++= (in >> 3) & mask_8_bits; *ptr= (in << 5) & mask_8_bits; break;	\
case 4: *ptr= *ptr | ((in >> 12) & mask_8_bits); ptr++;			\
*ptr++= (in >> 4) & mask_8_bits; *ptr= (in << 4) & mask_8_bits; break;	\
case 5: *ptr= *ptr | ((in >> 13) & mask_8_bits); ptr++;			\
*ptr++= (in >> 5) & mask_8_bits; *ptr= (in << 3) & mask_8_bits; break;	\
case 6: *ptr= *ptr | ((in >> 14) & mask_8_bits); ptr++;     		\
*ptr++= (in >> 6) & mask_8_bits; *ptr= (in << 2) & mask_8_bits; break;	\
case 7: *ptr= *ptr | ((in >> 15) & mask_8_bits); ptr++;     		\
*ptr++= (in >> 7) & mask_8_bits; *ptr= (in << 1) & mask_8_bits; };

/* Get an arbitrary precision color value, byte aligned or not */
/* ptr should point to an unsigned char */
#define mcr_gtcv(ptr, precision, out, bit) out = 0; switch (precision){	\
case 32: out = *ptr++;							\
case 24: out = (out << byte_size) | (*ptr++ & 255);			\
case 16: out = (out << byte_size) | (*ptr++ & 255);			\
case 8: out = (out << byte_size) | (*ptr++ & 255); break;     		\
case 4: out= ( (*ptr >> (4-bit)) & 15 ); bit= bit + 4; 			\
if (bit == 8) { bit = 0; ++ptr; }; break;				\
case 2: out= ( (*ptr >> (6-bit)) & 3 ); bit= bit + 2; 			\
if (bit == 8) { bit = 0; ++ptr; }; break;				\
case 1: out= ( (*ptr >> (7-bit)) & 1 ); bit= bit + 1;			\
if (bit == 8) { bit = 0; ++ptr; }; }					\

igtmem(psz)
/*
	This routine returns a pointer to available memory;  it is included
	for the benefit of FORTRAN calling programs.
*/
int *psz;
{
	char *malloc();
	if (debugmode) fprintf(stderr," IGTMEM:  allocating %d bytes.\n",*psz);
	return((int) malloc( (unsigned int) *psz ));
}

ifrmem(pmem)
/*
	This routine frees previously allocated memory based on a pointer;
	it is included for the benefit of FORTRAN calling programs.
*/
char **pmem;
{
	if (debugmode) fprintf(stderr," IFRMEM:  freeing memory.\n");
	return(cgmfree(*pmem));
}

static int cgmfree(ptr)
char *ptr;
/* This is a portable version of the 'free' routine */
{
#ifdef VMS
	return( free(ptr) );
#else
	(void) free(ptr);
     	return(0);
#endif                         
}

static void error(errstr)
char *errstr;
/* This routine prints an error message in a portable way */
{
#ifdef VMS
	perror(errstr);
#else
   	fprintf(stderr,errstr); fprintf(stderr,"\n");
#endif

	(void) exit(2);
}

/*
This routine checks to see if sufficient data buffer memory is available,
allocating more as necessary.
*/       
static void checkbufsize(newsize)
unsigned int newsize;
{
	char *malloc(),*realloc(),msgbuf[256];

       	if ( newsize > datasize )
		{
		if (debugmode) fprintf(stderr,
		  " checkbufsize: expanding data buffer to %d chars.\n",
		  newsize);

		if (databuf == data_default)  
			{
			if ( ( databuf= (unsigned char *)
     				malloc( (unsigned int)newsize ) ) 
				== 0 )
				{
				(void) sprintf(msgbuf,
" CHECKBUFSIZE:  Error allocating %d chars of data buffer memory; exiting.",
				newsize);
				error(msgbuf);
				}
			}
		else
			{
			if ( ( databuf= (unsigned char *) realloc( databuf,
					(unsigned int)newsize ) ) 
				== 0 )
				{
				(void) sprintf(msgbuf,
" CHECKBUFSIZE:  Error increasing data buffer memory to %d chars; exiting.",
				newsize);
	      			error(msgbuf);
	    			}
			};
		datasize= newsize;
      		};
}             

/*
	This function checks that sufficient memory is available for
	coordinate storage, allocating more as needed.  Returns 1 if
	successful.
*/	
static int getcmem(nx,ny)
int nx,ny;
{
	char *malloc();

	if (nx>ixbuf_sz)
		{
		if (debugmode) fprintf(stderr,
		   " getcmem: increasing x coord mem to %d floats.\n",nx);
		if (ixbuf_sz>0) { cgmfree((char *)ixbuf); ixbuf_sz= 0; };
		if ( (ixbuf= (int *)malloc(nx*sizeof(float))) == 0 )
			{ 
			fprintf(stderr,"Unable to allocate x memory.\n");
		 	return(2);
			};
		ixbuf_sz= nx;
		};
	if (ny>iybuf_sz)
		{
		if (debugmode) fprintf(stderr,
		   " getcmem: increasing y coord mem to %d floats.\n",ny);
		if (iybuf_sz>0) { cgmfree((char *)iybuf); iybuf_sz= 0; };
		if ( (iybuf= (int *)malloc(ny*sizeof(float))) == 0 )
			{ 
			fprintf(stderr,"Unable to allocate y memory.\n");
			return(2);
			};
		iybuf_sz= ny;
		};
	return(1);
}

wrtimg(image,nxdim,nydim,fnamedesc)
int *image,*nxdim,*nydim;
char *fnamedesc;
/*
	This section of code is intended to write an integer array into
	a CGM metafile Cell Array, with all the appropriate trimmings. 
	The integers in image are to be between 0 and 255;  the dimensions
	of image are *nxdim and *nydim.
*/
{
	int tierr;
	float one= 1.0, zero= 0.0;
             
	if (debugmode) 
		{                
		fprintf(stderr," wrtimg: \n");
	    	fprintf(stderr,"    nxdim: %d, nydim: %d \n",*nxdim,*nydim);
		fprintf(stderr,"    image: %d %d %d %d ...\n",
			*image,*(image+1),*(image+2),*(image+3));
		};

	wrtopn(fnamedesc,&tierr);
	wrbegp(&tierr);
	wrbgpb(&tierr);
	wrtcla(image,nxdim,nydim,&zero,&one,&one,&zero,&one,&one,&tierr);
	wrendp(&tierr);
	wrtend(&tierr);
}                        
                       
static char *getstring(strdesc STRINGLENGTH)
string_descriptor strdesc;
DEFSTRINGLENGTH
/*  This routine finds a string in the supplied descriptor */
{
	static char stringcopy[maxcgmstr]="\0";
	int ichar;

#ifdef VMS
	if (strdesc->dsc$b_class != DSC$K_CLASS_S)
		{
		fprintf(stderr,
			" Wrong descriptor class in GETSTRING: %d\n",
	     		strdesc->dsc$b_class);
		stringcopy[0]= '\0';
		};
	if (strdesc->dsc$w_length >= maxcgmstr)
		{
		fprintf(stderr," Long string truncated in GETSTRING.\n");
		(void) strncpy(stringcopy,
			strdesc->dsc$a_pointer,maxcgmstr-1);
		stringcopy[maxcgmstr-1]='\0';
		}                           
      	else 
		{
		(void) strncpy(stringcopy,
			strdesc->dsc$a_pointer,strdesc->dsc$w_length);
		stringcopy[strdesc->dsc$w_length]='\0';
		};
#endif
#ifdef CRAY
/* Note that the Cray version of this routine truncates trailing blanks */
	if ( _fcdlen(strdesc) >= maxcgmstr )
		{
		fprintf(stderr," Long string truncated in GETSTRING.\n");
		(void) strncpy(stringcopy,_fcdtocp(strdesc),maxcgmstr-1);
		stringcopy[maxcgmstr-1]='\0';
		}
	else
		{
		strncpy(stringcopy,_fcdtocp(strdesc),_fcdlen(strdesc));
		stringcopy[_fcdlen(strdesc)]= '\0';
		};
	for (ichar= strlen(stringcopy)-1;(stringcopy[ichar]==' ')&&(ichar>=0);
		ichar--) stringcopy[ichar]= '\0';                     
#endif
#ifdef unix
/*   For this version of getstring, the macro STRINGLENGTH translates to
 *   ",stringlength", which should contain the string length.  Note that
 *   this version truncates trailing blanks.
 */
	if (stringlength >= maxcgmstr)
		{
		fprintf(stderr," Long string truncated in GETSTRING.\n");
		(void) strncpy(stringcopy,strdesc,maxcgmstr-1);
		stringcopy[maxcgmstr-1]='\0';
		}
	else
		{
		strncpy(stringcopy,strdesc,stringlength);
		stringcopy[stringlength]= '\0';
		};
	for (ichar= strlen(stringcopy)-1;(stringcopy[ichar]==' ')&&(ichar>=0);
		ichar--) stringcopy[ichar]= '\0';                     
#endif
#ifdef ardent
	if ( strdesc->length >= maxcgmstr )
		{
		fprintf(stderr," Long string truncated in GETSTRING.\n");
		(void) strncpy(stringcopy,strdesc->addr,maxcgmstr-1);
		stringcopy[maxcgmstr-1]= '\0';
		}
	else 
		{
		(void) strcpy(stringcopy,strdesc->addr);
		stringcopy[strdesc->length]= '\0';
		};
#endif

	if (debugmode) fprintf(stderr," getstring:  returning <%s>.\n",
	      	stringcopy);  

	return(stringcopy);	
}

static int devinit(fname)
char *fname;
/*
This routine initializes the device driver interface.  It does so by
simulating the environment provided by gplot.c in the GPlot software.
*/
{
	extern int parse_cline(), dev_setup(), consult_device(),
		open_output_file();
	int save_debug;

	/* Some booleans */
	int 	to_screen,	/* device is writing to screen */
		do_list;	/* listing is to be generated */

	/* String to hold device default filename */
	static char def_name[max_str]= ""; 

	/* String to hold simulated command line */
	char cmd_line[max_str];

	/* Simulated argv, argc */
	int argc;
	static char 	
		arg1[]= "gplot",
		arg2[max_str],
		arg3[]= "dummy",
		arg4[max_str]= "",
		*argv[]= { arg1, arg2, arg3, arg4 };

	if (debugmode) fprintf(stderr,"devinit: fname=<%s>.\n",fname);

	/* Fill out the command line, and argv */
#ifdef VMS
	sprintf(cmd_line,"gpt/dev=%s dummy",devnam);
	sprintf(arg2,"/dev=%s",devnam);
	argc= 3;
#else
	sprintf(cmd_line,"gplot -d%s dummy %s",devnam,fname);
	sprintf(arg2,"-d%s",devnam);
	sprintf(arg4,"%s",fname);
	argc= 4;
#endif

	/* 
	Parse the 'command line', initializing options.  This will
	turn debugging off if it is on, so save and reset it.
	*/
	save_debug= debugmode;
	if ( !parse_cline(cmd_line, argc, argv, &to_screen, &do_list,
       	   	&dev_info, cmd_opt) )
		error("devinit: simulated command line didn't parse.");
	debugmode= save_debug;

	/* Initialize the output device */
	if ( !dev_setup(cmd_opt, &dev_info, &glbl1, &glbl2, &glbl3, &glbl5,
 		delim, mfdesc, pdesc, mfctrl, gprim, attr, escfun, extfun,
		ctrl, &argc, argv) )
		error("devinit: device initialization failed.");
	consult_device(cmd_opt, &dev_info);

	/* Open the output file (default name may be set by driver). */
	if ( cmd_opt[(int) out_name].set )
		strncpy(def_name, cmd_opt[(int) out_name].val.str,max_str);
	else 
		{
#ifdef VMS
		strncpy(def_name, dev_info.out_name, max_str);
#endif
		};
	if ( !open_output_file(fname, def_name, do_list, &dev_info) )
		error("devinit: unable to open output file");
	
	/* All possible errors have already been processed. */
	return(1);
}

static void setscale()
/*
This routine sets the scaling factors used to convert floating point coords
to pxl integers.  It does so by finding a region of the same aspect ratio
as the VDC extent within the device output field, placing the region such
that the lower left corner corresponds with the lower left corner of
the device output field, and taking appropriate ratios.  This is done
separately for the four possible relative aspect ratios of the device
and VDC extent.  The goal is to come up with a factor that will map
a coordinate value of 1.0 into the last pixel of the device in the
direction that the VDC extent fits most snugly into the device display
area.
*/
{
	float vdcxsiz, vdcysiz;
	int devxpxl, devypxl;

	if (debugmode) fprintf(stderr," setscale: setting scale.\n");

	devxpxl= dev_info.pxl_in * dev_info.x_size;
	devypxl= dev_info.ypxl_in * dev_info.y_size;

	if (glbl1.vdc_type == vdc_int)
		{
		vdcxsiz= glbl2.vdc_extent.i[2] - glbl2.vdc_extent.i[0];
		vdcysiz= glbl2.vdc_extent.i[3] - glbl2.vdc_extent.i[1];
		}
	else
		{
		vdcxsiz= glbl2.vdc_extent.r[2] - glbl2.vdc_extent.r[0];
		vdcysiz= glbl2.vdc_extent.r[3] - glbl2.vdc_extent.r[1];
		};

	if ( (dev_info.x_size/dev_info.y_size) >= (vdcxsiz/vdcysiz) )
		{
	      	/* It touches both edges in the x direction */
		xpxl_scale= devxpxl / (wcd_xmax-wcd_xmin);
		xpxl_shift= -wcd_xmin;
		ypxl_scale= (vdcysiz/vdcxsiz)*devypxl / (wcd_ymax-wcd_ymin);
		ypxl_shift= -wcd_ymin;
		}
	else     
		{
		/* It touches both edges in the y direction */
		xpxl_scale= (vdcysiz/vdcxsiz)*devxpxl / (wcd_xmax-wcd_xmin);
		xpxl_shift= -wcd_xmin;
		ypxl_scale= devypxl / (wcd_ymax-wcd_ymin);
		ypxl_shift= -wcd_ymin;
		};

	pxl_vdc= (float)xpxl_scale * (wcd_xmax-wcd_xmin)/vdcxsiz;
}

setwcd(xmin,ymin,xmax,ymax,ierr)
float *xmin, *ymin, *xmax, *ymax;
int *ierr;
/*
This routine sets the minimum and maximum boundaries of the world coordinate
system.
*/
{
	if (debugmode) fprintf(stderr,
		" setwcd: x range %f to %f\n         y range %f to %f\n",
		*xmin, *xmax, *ymin, *ymax);

	if (*xmin == *xmax)
		{
		fprintf(stderr,
	     		" xmin = xmax in SETWCD; call ignored.\n");
		*ierr= 1;
		return;
		}

	if (*ymin == *ymax)
		{
		fprintf(stderr,
	     		" ymin = ymax in SETWCD; call ignored.\n");
		*ierr= 1;
		return;
		}

	wcd_xmin= *xmin;
	wcd_ymin= *ymin;
	wcd_xmax= *xmax;
	wcd_ymax= *ymax;

	/*
	If the system has been initialized, rescale now.  If not, the
	new values will be used when the system is initialized.
	*/
	if ( cgmstate != 0 ) setscale();
}

tgldbg(ierr)
int *ierr;
/*  
This routine toggles debugging, turning it on if it is off and vice
versa.
*/
{
	/* This message printed if debugging is being turned off */
	if (debugmode) fprintf(stderr," tgldbg: debugging off\n");

	debugmode= !debugmode;

	/* This message printed if debugging is being turned on */
	if (debugmode) fprintf(stderr," tgldbg: debugging on\n");
}

setdev(dev,ierr STRINGLENGTH)
string_descriptor dev;
int *ierr;
DEFSTRINGLENGTH
/*
This routine sets the output device.
*/
{
	char *name;

	name= getstring(dev STRINGLENGTH);

	if (debugmode) fprintf(stderr," setdev: setting device to %s\n",name);

	if (cgmstate!=0) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for SETDEV; call ignored.\n");
		*ierr= 1;
		return;
		}

	csetdev(name,ierr);
}

csetdev(name,ierr)
char *name;
int *ierr;
/*
This routine sets the output device.
*/
{
	if (debugmode) fprintf(stderr," csetdev: device set to %s\n",name);

	strncpy(devnam,name,devnamlen);
}

wrtopn(fnamedesc,ierr STRINGLENGTH)
string_descriptor fnamedesc;
int *ierr;
DEFSTRINGLENGTH
/*
	This section of code opens a CGM metafile for writing, and writes
	a begin metafile command followed by a metafile version command.
	The 'state' variable is set appropriately.
*/
{
	char *getstring(),*fname; 

	fname= getstring(fnamedesc STRINGLENGTH);
	if (fname == 0 || strlen(fname) == 0)
		{
		fprintf(stderr,
		   " Bad fname in call to WRTOPN; using default\n");
		fname= "image";
		};
	if (debugmode) fprintf(stderr," wrtopn: fname: %s\n",fname);
	if (cgmstate!=0) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRTOPN; call ignored.\n");
		*ierr= 1;
		return;
		}

	wrcopn(fname,ierr);
}                        
                       
wrcopn(fname,ierr)
char *fname;
int *ierr;
/*
	This section of code opens a CGM metafile for writing, and writes
   	a begin metafile command followed by a metafile version command.
	The 'state' variable is set appropriately.
	This version differs from wrtopn in that the file name is taken
	as a C character string rather than a fortran descriptor.  If
	the file name given is "-", output will be directed to stdout.
*/
{
	extern s_defaults(), rs_defaults();
	static int element_list[2]= {-1, 1};
#ifdef VMS
	static char stdout_equiv_string[]= "SYS$OUTPUT:";
#else
	static char stdout_equiv_string[]= "";
#endif

	if (fname == 0 || strlen(fname) == 0)
		{
		fprintf(stderr,
	      	   " Bad fname in call to WRCOPN; using default\n");
		fname= "image";
		};
	if (debugmode) fprintf(stderr," wrcopn: fname: %s\n",fname);
	if (cgmstate!=0) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRCOPN; call ignored.\n");
		*ierr= 1;
		return;
		}

	/* 
	The following implements the convention that an fname of '-'
	means the output should go to stdout.  Unfortunately the signal
	that must be sent to the underlieing GPlot software to accomplish
	this is operating system dependent.  The dependency is accomodated
	by the ifdef at the top of this routine.
	*/
	if ( !strncmp( fname, "-", 1 ) ) fname= stdout_equiv_string;

	/* 
	We must initialize the CGM attribute structs and the device.
	Unfortunately they are interdependent, so we make a partial set
	of definitions, then call the routines again to get everything
	straightened out.
	*/
	/* Set up the default attributes (and the global color table) */
	s_defaults(&glbl1,&dflt2,&dflt3,&dflt5,&glbl5,1.0);

	/* Set global attributes equal to default attributes */
	rs_defaults(&glbl1, &glbl2, &glbl3, &glbl5,
		&dflt2, &dflt3, &dflt5, 1.0 );

	/* Initialize the device */
	if ( !devinit(fname) ) error("Unable to initialize device; exiting.");

	/* Set the coordinate scaling factors */
	setscale();

	/* Reinitialize the default attributes (and the global color table) */
	s_defaults(&glbl1,&dflt2,&dflt3,&dflt5,&glbl5,pxl_vdc);

	/* Reset global attributes equal to default attributes */
	rs_defaults(&glbl1, &glbl2, &glbl3, &glbl5,
		&dflt2, &dflt3, &dflt5, pxl_vdc );

	/* 
	Write begin metafile, metafile version, metafile descriptor,
	and metafile element list elements. 
	*/
	check_do( delim, B_Mf )(fname,"CGMGEN","CGMGEN");
	err_chk( *ierr );
	check_do( mfdesc, MfVersion )(1);
	err_chk( *ierr );
	check_do( mfdesc, MfDescrip)("PSC CGM 3.0");
	err_chk( *ierr );
	check_do( mfdesc, MfElList)(1, element_list);
	err_chk( *ierr );
        
	cgmstate= 1;
}                        

wrmxci(maxclrindx,ierr)                    
int *maxclrindx,*ierr;
/*
	This routine writes a 'max color index' command to the default
	metafile, if the cgm state is appropriate.
*/
{
	float *clrptr, *tblend;
	char *realloc(),msgbuf[256];

	if (debugmode) fprintf(stderr," wrmxci: max color index= %d\n",
		*maxclrindx);
	if (cgmstate!=1) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRMXCI; call ignored.\n");
		*ierr= 1;
		return;
		}

	/* 
	If the global and default color tables are not this big,
	reallocate them so that they will be.  The new memory locations
	above the previously-set color indices are initialized to
	zeros.
	*/
	if (*maxclrindx > glbl1.max_c_index)
		{
		glbl5.ctab= (float *) realloc( (char *)glbl5.ctab,
			3 * sizeof(float) * (*maxclrindx+1) );
		if (!glbl5.ctab)
			{
			sprintf(msgbuf,
"Error increasing color table size to 3 * %d floats; exiting\n",
				*maxclrindx+1);
			error(msgbuf);
			}
		dflt5.ctab= (float *) realloc( (char *)dflt5.ctab,
			3 * sizeof(float) * (*maxclrindx+1) );
		if (!glbl5.ctab)
			{
			sprintf(msgbuf,
"Error increasing default color table size to 3 * %d floats; exiting\n",
				*maxclrindx+1);
			error(msgbuf);
			}
		tblend= glbl5.ctab + 3 * (*maxclrindx+1);
		for ( clrptr= glbl5.ctab + 3 * (glbl1.max_c_index+1);
			clrptr < tblend; clrptr++ ) *clrptr= 0.0;
		tblend= dflt5.ctab + 3 * (*maxclrindx+1);
		for ( clrptr= dflt5.ctab + 3 * (glbl1.max_c_index+1);
			clrptr < tblend; clrptr++ ) *clrptr= 0.0;
		}

	/* Set the new max index, and (possibly) make the driver call. */
	glbl1.max_c_index= *maxclrindx;

	check_do( mfdesc, MaxCInd )(*maxclrindx);
	err_chk( *ierr );
}

wrbegp(ierr)                   
int *ierr;
/*
	This routine writes a 'begin picture' command to the default
	metafile, setting the CGM state appropriately.
*/
{
	extern rs_defaults();

	if (debugmode) fprintf(stderr," wrbegp\n");
	if (cgmstate!=1) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRBEGP; call ignored.\n");
		*ierr= 1;
		return;
		}

	/* Reset the coordinate scaling factors */
	setscale();

	/* Reset all defaults */
	rs_defaults(&glbl1, &glbl2, &glbl3, &glbl5,
		&dflt2, &dflt3, &dflt5, pxl_vdc );

	/* Write begin picture element */
	check_do( delim, B_Pic )(picture_name);
	err_chk( *ierr );

	cgmstate= 2;
}

stpcnm(picnamedesc,ierr STRINGLENGTH)
string_descriptor picnamedesc;
int *ierr;
DEFSTRINGLENGTH
/* This routine allows the user to set the picture name from Fortran */
{
	char *getstring(),*picname; 

	picname= getstring(picnamedesc STRINGLENGTH);
	if (picname == 0 || strlen(picname) == 0)
		{
		fprintf(stderr,
		   " Bad picname in call to STPCNM; name not changed\n");
		*ierr= 1;
		};
	if (debugmode) fprintf(stderr," stpcnm: picname: %s\n",picname);

	stcpnm(picname,ierr);
}

stcpnm(picname,ierr)
char *picname;
int *ierr;
/* This routine allows the user to set the picture name from C */
{
	if (picname == 0 || strlen(picname) == 0)
		{
		fprintf(stderr,
		   " Bad picname in call to STCPNM; name not changed\n");
		*ierr= 1;
		};
	if (debugmode) fprintf(stderr," stcpnm: picname: %s\n",picname);

	strncpy( picture_name, picname, maxpicnamelength - 1 );
}

wrbgpb(ierr)
int *ierr;
/*
	This routine writes a 'begin picture body' command to the default
	metafile, setting the CGM state appropriately.
*/
{
	if (debugmode) fprintf(stderr," wrbgpb\n");
	if (cgmstate!=2) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRBGPB; call ignored.\n");
		*ierr= 1;
		return;
		}

	/* Increment the page number */
	pgnum++;

	check_do( delim, B_Pic_Body )("", 0, 0, 0.0,
		glbl2.back_col.red, glbl2.back_col.green, glbl2.back_col.blue,
		pgnum, (int)(dev_info.pxl_in * dev_info.x_size), 
		(int)(dev_info.ypxl_in * dev_info.y_size));
	err_chk( *ierr );

	cgmstate= 3;
}

wrendp(ierr)
int *ierr;
/*
	This routine writes an 'end picture' command to the default
	metafile, setting the CGM state appropriately.
*/
{
	if (debugmode) fprintf(stderr," wrendp\n");
	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRENDP; call ignored.\n");
		*ierr= 1;
		return;
		}

	check_do( delim, E_Pic )(1);
	err_chk( *ierr );

	cgmstate= 1;
}

wrtend(ierr)
int *ierr;
/*
	This routine writes an 'end metafile' command to the default
	metafile, flushing buffers and setting the CGM state appropriately.
*/
{
	extern close_up();

	if (debugmode) fprintf(stderr," wrtend\n");
	if (cgmstate!=1) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRTEND; call ignored.\n");
		*ierr= 1;          
		return;
		}

	check_do( delim, E_Mf )(pgnum);
	err_chk( *ierr );

	close_up(0);

	cgmstate= 0;
}

wrtcla(image,nxdim,nydim,xp,yp,xq,yq,xr,yr,ierr)
int *nxdim,*nydim,*image,*ierr;                                           
float *xp,*yp,*xq,*yq,*xr,*yr;
/*
	This routine writes a CGM cell array, if the CGM state is
	appropriate.  The color index values are assumed to be
	integers between 0 and 255 inclusive.  The dimensions are
	*nxdim and *nydim respectively, and (*xp,*yp) and (*xq,*yq)
	are the *real number* coordinates (between 0.0 and 1.0 
	inclusive) of the starting and ending corners of the
	area into which the cell array is mapped.  The first scan
	line is to be written along the vector from (*xp,*yp) to 
	(*xr,*yr).
*/                                       
{
        unsigned char *bufcpy;
	int *imagecopy, odd_row_flag, p[2], q[2], r[2];
	unsigned int ibyte, jbyte, tot_buf_sz;

	if (debugmode) 
		fprintf(stderr," wrtcla: nxdim= %d; nydim= %d\n",
			*nxdim,*nydim);
	if (cgmstate!=3)          
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRTCLA; call ignored.\n");
		*ierr= 1;
		return;
		}
	if (glbl2.c_s_mode != i_c_mode) 
		{
		fprintf(stderr,
	     		" Color mode not indexed in WRTCLA; call ignored.\n");
		*ierr= 1;
		return;
		}
	p[0]= xpxl(*xp); p[1]= ypxl(*yp);
	q[0]= xpxl(*xq); q[1]= ypxl(*yq);
	r[0]= xpxl(*xr); r[1]= ypxl(*yr);

	odd_row_flag= ( *nxdim % 2 != 0 );
	if (odd_row_flag) tot_buf_sz= (1+*nxdim)*(*nydim);
	else tot_buf_sz= (*nxdim)*(*nydim);
	checkbufsize(tot_buf_sz);

	bufcpy= databuf;
	imagecopy= image;
	for (jbyte=0; jbyte<*nydim; jbyte++)
		{
	      	for (ibyte=0; ibyte<*nxdim; ibyte++) *bufcpy++= *imagecopy++;
		if (odd_row_flag) *bufcpy++= 0;
		};

	check_do( gprim, Cell_Array )
 		(p,q,r,*nxdim,*nydim,8,databuf,1,tot_buf_sz);
	err_chk( *ierr );

}

wrgcla(image,nxdim,nydim,xp,yp,xq,yq,xr,yr,prec,mode,ierr)
int *nxdim,*nydim,*image,*prec,*mode,*ierr;
float *xp,*yp,*xq,*yq,*xr,*yr;
/*
	This routine writes a general CGM indexed color cell array, 
	if the CGM state is appropriate.  The color index values are 
	assumed to be integers between 0 and 2**(*prec) - 1 inclusive.  
	The dimensions are *nxdim and *nydim respectively, and (*xp,*yp) 
	and (*xq,*yq) are the *real number* coordinates (between 0.0 and 
	1.0 inclusive) of the starting and ending corners of the
	area into which the cell array is mapped.  The first scan line
	is to be written along the vector from (*xp,*yp) to (*xr,*yr).
*/
{                                             
        unsigned char *bufcpy, *obufcpy;
	unsigned int tot_buf_sz, *imagecopy, inow, ilast, lrun, ibufsz();
	int ibyte, jbyte, bit, p[2], q[2], r[2];
	extern int em_cell_array();

	if (debugmode) 
		fprintf(stderr," wrgcla: nxdim= %d; nydim= %d\n",
			*nxdim,*nydim);
	if (cgmstate!=3)          
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRGCLA; call ignored.\n");
		*ierr= 1;
		return;
		}
	if (glbl2.c_s_mode != i_c_mode) 
		{
		fprintf(stderr,
	     		" Color mode not indexed in WRGCLA; call ignored.\n");
		*ierr= 1;
		return;
		}
	if ((*mode<0) || (*mode>1))
		{
		fprintf(stderr,
	      		" Invalid cell rep. mode in WRGCLA;  call ignored.\n");
		*ierr= 1;
		return;
		}
	if ((*prec!=8) && (*prec!=1) && (*prec!=2) && (*prec!=4) 
		&& (*prec!=16) && (*prec!=24) && (*prec!=32))
		{
		fprintf(stderr,
			" Invalid precision in WRGCLA;  call ignored.\n");
		*ierr= 1;
		return;
		}

	p[0]= xpxl(*xp); p[1]= ypxl(*yp);
	q[0]= xpxl(*xq); q[1]= ypxl(*yq);
	r[0]= xpxl(*xr); r[1]= ypxl(*yr);

	/* Get enough memory to hold the cell array plus header */
	tot_buf_sz= ibufsz(image,nxdim,nydim,prec,mode);
	checkbufsize(tot_buf_sz);
	bufcpy= databuf;
	for(ibyte=0; ibyte<tot_buf_sz; ibyte++) *bufcpy++= 0;

	/* Pack the cell color index data (two possible rep. modes) */
	bufcpy= databuf;
	imagecopy= (unsigned int *)image;
	if ( *mode ) /* packed list mode */
   		{
		for (jbyte=0; jbyte<*nydim; jbyte++)
			{
			obufcpy= bufcpy;
			bit= 0;
			for (ibyte=0; ibyte<*nxdim; ibyte++)
				{ 
			       	mcr_ptcv(bufcpy,*prec,*imagecopy,bit);
				imagecopy++;
				};
			if (bit != 0) ++bufcpy;
			if ((int) (bufcpy - obufcpy) % 2) ++bufcpy;
			};
		}
	else /* run length list mode */
		{
		ilast= *imagecopy++;
		for (jbyte=0; jbyte<*nydim; jbyte++)
			{
			obufcpy= bufcpy;
			bit= 0;
			lrun= 1;
		      	for (ibyte=0; ibyte++<*nxdim;)
				{
				if ((ibyte<*nxdim)||(jbyte<*nydim))
					inow= *imagecopy++;
				if ((inow == ilast) && (ibyte<*nxdim))
					lrun++;
				else
					{
					mcr_ptei(bufcpy,lrun,bit);
					mcr_ptcv(bufcpy,*prec,ilast,bit);
					lrun= 1;
					ilast= inow;
				 	};
				};
			if (bit != 0) ++bufcpy;
			if ((int) (bufcpy - obufcpy) % 2) ++bufcpy;
			};
		};

	/* Write the cell array element */
	check_do( gprim, Cell_Array )
 		(p,q,r,*nxdim,*nydim,*prec,databuf,*mode,tot_buf_sz);
	else gbl_ret= em_cell_array
	        (p,q,r,*nxdim,*nydim,*prec,databuf,*mode,tot_buf_sz);
	err_chk( *ierr );

}                                              

wrpcla(image,nxdim,nydim,xp,yp,xq,yq,xr,yr,prec,mode,ierr)
int *nxdim,*nydim,*prec,*mode,*ierr;
float *xp,*yp,*xq,*yq,*xr,*yr;
unsigned char *image;
/*
	This routine writes a general CGM indexed color cell array, 
	if the CGM state is appropriate.  The color index values are 
	assumed to be integers between 0 and 2**(*prec) - 1 inclusive.  
	The dimensions are *nxdim and *nydim respectively, and (*xp,*yp) 
	and (*xq,*yq) are the *real number* coordinates (between 0.0 and 
	1.0 inclusive) of the starting and ending corners of the
	area into which the cell array is mapped.  The first scan line
	is to be written along the vector from (*xp,*yp) to (*xr,*yr).
	This routine differs from WRGCLA in that it expects the array
	pointed to by the parameter 'image' to be a packed array of the
	stated precision;  i.e. if the precision is 1 the input image
	is packed at 8 cells per unsigned char.  The input image is
	not to be run length encoded.  New rows of the input image
	are to start on byte boundaries.
*/
{                                             
        unsigned char *bufcpy, *obufcpy, *imagecopy;
	unsigned int tot_buf_sz, inow, ilast, lrun, pbufsz();
	int ibyte, jbyte, bit, inbit, p[2], q[2], r[2];
	extern int em_cell_array();

	if (debugmode)               
		fprintf(stderr," wrpcla: nxdim= %d; nydim= %d\n",
			*nxdim,*nydim);
	if (cgmstate!=3)          
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRPCLA; call ignored.\n");
		*ierr= 1;
		return;
		}
	if (glbl2.c_s_mode != i_c_mode) 
		{
		fprintf(stderr,
	     		" Color mode not indexed in WRPCLA; call ignored.\n");
		*ierr= 1;
		return;
		}
	if ((*mode<0) || (*mode>1))
		{
		fprintf(stderr,
	      		" Invalid cell rep. mode in WRPCLA;  call ignored.\n");
		*ierr= 1;
		return;
		}
	if ((*prec!=8) && (*prec!=1) && (*prec!=2) && (*prec!=4) 
		&& (*prec!=16) && (*prec!=24) && (*prec!=32))
		{
		fprintf(stderr,
			" Invalid precision in WRPCLA;  call ignored.\n");
		*ierr= 1;
		return;
		}

	p[0]= xpxl(*xp); p[1]= ypxl(*yp);
	q[0]= xpxl(*xq); q[1]= ypxl(*yq);
	r[0]= xpxl(*xr); r[1]= ypxl(*yr);

	/* Get enough memory to hold the cell array plus header */
	tot_buf_sz= pbufsz(image,nxdim,nydim,prec,mode);
	checkbufsize(tot_buf_sz);
	bufcpy= databuf;
	for(ibyte=0; ibyte<tot_buf_sz; ibyte++) *bufcpy++= 0;

	/* Pack the cell color index data (two possible rep. modes) */
	bufcpy= databuf;                                      
	imagecopy= image;
	inbit= 0;
	if ( *mode ) /* packed list mode */
		{
		for (jbyte=0; jbyte<*nydim; jbyte++)
			{
			obufcpy= bufcpy;
		 	bit= 0;
			if (inbit!=0) { inbit= 0; imagecopy++;};
			for (ibyte=0; ibyte<*nxdim; ibyte++)
				{ 
				mcr_gtcv(imagecopy,*prec,inow,inbit);
			       	mcr_ptcv(bufcpy,*prec,inow,bit);
				};
			if (bit != 0) ++bufcpy;
			if ((int) (bufcpy - obufcpy) % 2) ++bufcpy;
			};
		}              
	else /* run length list mode */
		{
		mcr_gtcv(imagecopy,*prec,ilast,inbit);
		for (jbyte=0; jbyte<*nydim; jbyte++)
			{
			obufcpy= bufcpy;
			bit= 0;
			lrun= 1;
		      	for (ibyte=0; ibyte++<*nxdim;)
				{
				if ((ibyte<*nxdim)||(jbyte<*nydim))
					mcr_gtcv(imagecopy,*prec,inow,inbit);
				if ((inow == ilast) && (ibyte<*nxdim))
					lrun++;
				else
					{
					mcr_ptei(bufcpy,lrun,bit);
      					mcr_ptcv(bufcpy,*prec,ilast,bit);
					lrun= 1;
					ilast= inow;
				 	};
				};
	      		if (bit != 0) ++bufcpy;
			if ((int) (bufcpy - obufcpy) % 2) ++bufcpy;
			if (inbit!=(*prec % 8))
				{
				if (inbit != 0) {inbit= 0; imagecopy++;};
				mcr_gtcv(imagecopy,*prec,ilast,inbit);
	     			};
			};
		};

	/* Write the cell array element */
	check_do( gprim, Cell_Array )
 		(p,q,r,*nxdim,*nydim,*prec,databuf,*mode,tot_buf_sz);
	else gbl_ret= em_cell_array
 		(p,q,r,*nxdim,*nydim,*prec,databuf,*mode,tot_buf_sz);
	err_chk( *ierr );

}                                              

static unsigned int ibufsz(image,nxdim,nydim,prec,mode)
int *nxdim,*nydim,*image,*prec,*mode;
/*
	This routine returns the total number of bytes necessary to store
	the color data for an indexed color cell array.  *image is the
	first int of the image data, *nxdim and *nydim are the dimensions
	of the image, *prec is the number of bits precision to use for
	color values, and *mode is the cell representation mode.
*/
{
	unsigned int totbits; /* total number of bits to encode image */
	int rowbits,ilast,inow,i,j; 

	if (debugmode) fprintf(stderr,
		" ibufsz: nxdim= %d, nydim= %d, prec= %d, mode= %d\n",
		*nxdim, *nydim, *prec, *mode);

	if (*mode) /* packed list mode */
		{
		rowbits= *nxdim * *prec;
		if (rowbits % 8) rowbits= rowbits + 8 - (rowbits % 8);
		if (rowbits % 16) rowbits= rowbits + 8;
		totbits= *nydim * rowbits;
		}
	else /* run length list mode */
		{
		totbits= 0;
		ilast= *image++;
		for (j=0; j<*nydim; j++)
			{
			rowbits= 0;
			for (i=0; i++<*nxdim;)
				{
				if ((i < *nxdim)||(j < *nydim))
					inow= *image++;
				if ((inow != ilast) || (i == *nxdim))
					{
					rowbits= rowbits + 16 + *prec;
					ilast= inow;
					};
				};
			if (rowbits % 8) rowbits= rowbits + 8 - (rowbits % 8);
			if (rowbits % 16) rowbits= rowbits + 8;
			totbits= totbits + rowbits;
			};
		};

	/* Return number of bytes needed to hold the bits, rounding up */
	if (totbits % 8) totbits= totbits+8; 
	return(totbits/8);
}

static unsigned int pbufsz(image,nxdim,nydim,prec,mode)
int *nxdim,*nydim,*prec,*mode;
unsigned char *image;
/*
	This routine returns the total number of bytes necessary to store
	the color data for an indexed color cell array.  *image is the
	first byte of the packed image data, *nxdim and *nydim are the 
	dimensions of the image, *prec is the number of bits precision 
	to use for color values, and *mode is the cell representation mode.
	The input image is assumed to be packed with 'prec' bits per cell,
	but not run length encoded.  Each new row is assumed to start on
	a byte boundary.
*/                         
{
	unsigned int totbits; /* total number of bits to encode image */
	int rowbits,ilast,inow,i,j,inbit; 

	if (debugmode) fprintf(stderr,
	     	" pbufsz: nxdim= %d, nydim= %d, prec= %d, mode= %d\n",
		*nxdim, *nydim, *prec, *mode);

	if (*mode) /* packed list mode */
		{
		rowbits= *nxdim * *prec;
		if (rowbits % 8) rowbits= rowbits + 8 - (rowbits % 8);
		if (rowbits % 16) rowbits= rowbits + 8;
		totbits= *nydim * rowbits;
	     	}
	else /* run length list mode */
		{
		totbits= 0;
		inbit= 0;
		mcr_gtcv(image,*prec,ilast,inbit);
		for (j=0; j<*nydim; j++)
			{
		   	rowbits= 0;
			for (i=0; i++<*nxdim;)
				{
				if ((i < *nxdim)||(j < *nydim))
					mcr_gtcv(image,*prec,inow,inbit);
				if ((inow != ilast) || (i == *nxdim))
					{
					rowbits= rowbits + 16 + *prec;
					ilast= inow;
	     				};
				};
			if (rowbits % 8) rowbits= rowbits + 8 - (rowbits % 8);
			if (rowbits % 16) rowbits= rowbits + 8;
			totbits= totbits + rowbits;
			if (inbit!=(*prec % 8))
				{
				if (inbit != 0) {inbit= 0; image++;};
				mcr_gtcv(image,*prec,ilast,inbit);
	     			};
			};
		};

	/* Return number of bytes needed to hold the bits, rounding up */
	if (totbits % 8) totbits= totbits+8; 
	return(totbits/8);
}

wcladc(rimage,gimage,bimage,nxdim,nydim,xp,yp,xq,yq,xr,yr,ierr)
int *nxdim,*nydim,*ierr;                                           
float *rimage,*gimage,*bimage,*xp,*yp,*xq,*yq,*xr,*yr;
/*
	This routine writes a CGM cell array, in direct color form,
	if the CGM state is appropriate.  The color values are assumed 
	to lie between 0.0 and 1.0.  The dimensions are *nxdim and 
	*nydim respectively, and (*xp,*yp) and (*xq,*yq)
	are the *real number* coordinates (between 0.0 and 1.0 
	inclusive) of the starting and ending corners of the
	area into which the cell array is mapped.  The first scan
	line is to be written along the vector from (*xp,*yp)
	to (*xr,*yr).
*/
{
        unsigned char *bufcpy,*oldbuf,rnow,gnow,bnow,rlast,glast,blast;
	float *rcpy, *gcpy, *bcpy;
	int lrun, lrow, max_row_size, size_increment;
	unsigned int tot_buf_sz, trigger_size, ldata;
	unsigned int ibyte, jbyte, p[2], q[2], r[2];
	extern int em_cell_array();

	if (debugmode) 
		fprintf(stderr," wcladc: nxdim= %d; nydim= %d\n",
			*nxdim,*nydim);
	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WCLADC; call ignored.\n");
		*ierr= 1;
		return;
		}
	if (glbl2.c_s_mode != d_c_mode) 
		{
		fprintf(stderr,
	     		" Color mode not direct in WCLADC; call ignored.\n");
		*ierr= 1;
		return;
		}

	/* Set cell array boundary coordinates */
	p[0]= xpxl(*xp); p[1]= ypxl(*yp);
	q[0]= xpxl(*xq); q[1]= ypxl(*yq);
	r[0]= xpxl(*xr); r[1]= ypxl(*yr);

	/* 
	Assume a 50% compression;  we'll add more space later if needed.
	We'll ask for more when there isn't enough space for a full row
	left.  An exception is made for very small cell arrays.  Each
	increment is to be 1/2 the original size.
	*/
	max_row_size= ( *nxdim % 2 != 0 ) ?
		5 * (*nxdim) * 1 : 5 * (*nxdim);
	tot_buf_sz= 0.5* ( 3*(*nxdim)*(*nydim) );
	if (tot_buf_sz < (2 * max_row_size ))
		{
		tot_buf_sz= (*nydim) * max_row_size ;
		trigger_size= tot_buf_sz;  /* never expand buffer */
		}
	else trigger_size= tot_buf_sz - max_row_size;
	size_increment= tot_buf_sz / 2 ;
	checkbufsize(tot_buf_sz);

	bufcpy= databuf;

	ldata= 0;
	rcpy= rimage; gcpy= gimage; bcpy= bimage;
	rlast= float_to_byte(*rcpy);
	glast= float_to_byte(*gcpy);
	blast= float_to_byte(*bcpy);
	for (jbyte=0; jbyte++<*nydim;)
		{
		lrun= 1; lrow= 0;
	      	for (ibyte=0; ibyte++<*nxdim;)
			{
			if ((ibyte<*nxdim)||(jbyte<*nydim))
				{
				rnow= float_to_byte(*++rcpy);
				gnow= float_to_byte(*++gcpy);
				bnow= float_to_byte(*++bcpy);
				};
			if ((rnow==rlast)&&(gnow==glast)&&(bnow==blast)
				&&(ibyte<*nxdim)) lrun++;
			else
				{
				packword(lrun,bufcpy);
				*bufcpy++= rlast;
				*bufcpy++= glast;
				*bufcpy++= blast;
				lrow= lrow+5;
				lrun= 1;
				rlast= rnow; glast= gnow; blast= bnow;
				};
			};
  		if ( lrow % 2 ) { *bufcpy++= 0; lrow++; };
		ldata= ldata +lrow;
		if (ldata >= trigger_size)
			{
			tot_buf_sz= tot_buf_sz + size_increment;
			oldbuf= databuf;
			checkbufsize(tot_buf_sz);
			trigger_size= tot_buf_sz - max_row_size;
			bufcpy= bufcpy - oldbuf + databuf;
			};
		};

	/* Write the cell array element */
	check_do( gprim, Cell_Array )
 		(p,q,r,*nxdim,*nydim,8,databuf,0,ldata);
	else gbl_ret= em_cell_array
 		(p,q,r,*nxdim,*nydim,8,databuf,0,ldata);
	err_chk( *ierr );

}                       

wqcadc(nxdim,nydim,xp,yp,xq,yq,xr,yr,ierr)     
int *nxdim,*nydim,*ierr;                                           
float *xp,*yp,*xq,*yq,*xr,*yr;
/*
	This routine writes a CGM cell array, in direct color form,
	if the CGM state is appropriate.  The color values are assumed 
	to lie between 0.0 and 1.0.  The dimensions are *nxdim and 
	*nydim respectively, and (*xmn,*ymn) and (*xmx,*ymx)
	are the *real number* coordinates (between 0.0 and 1.0 
	inclusive) of the starting and ending corners of the
   	area into which the cell array is mapped.  To save buffer space,
	the actual data is obtained by calling qclarw nydim times.
	The description of qclarw should be as follows:

	qclarw(rrow,grow,brow,nxdim,nydim,iy)
	int *nxdim,*nydim,*iy;
	float *rrow,*grow,*brow;
                                           
	On return each pointer should point to a pointer to a buffer
	nxdim floats long, containing the iy'th of nydim rows.  iy
	runs from 1 to nydim, and calls will be made in ascending
	order.  qclarw should return 0 if successful, as per the 
	CGMGEN return code convention.
	                                    
*/
{
        unsigned char *bufcpy,*oldbuf,rnow,gnow,bnow,rlast,glast,blast;
	float rrow[rowsize],grow[rowsize],brow[rowsize],*rcpy,*gcpy,*bcpy;
	int lrun, lrow, iy, max_row_size, size_increment, p[2], q[2], r[2];
	unsigned int tot_buf_sz, trigger_size, ldata;
	unsigned int ibyte, jbyte;

	if (debugmode) 
		fprintf(stderr," wqcadc: nxdim= %d; nydim= %d\n",
			*nxdim,*nydim);
	if (cgmstate!=3) 
  		{
		fprintf(stderr,
	     		" CGM in wrong state for WQCADC; call ignored.\n");
		*ierr= 1;
		return;               
		}
	if (glbl2.c_s_mode != d_c_mode) 
		{
		fprintf(stderr,
	     		" Color mode not direct in WQCADC; call ignored.\n");
		*ierr= 1;
		return;
		}
	if (*nxdim > rowsize)                 
		{
		fprintf(stderr,
	     		" Row length too long in WQCADC; call ignored.\n");
		*ierr= 1;
		return;
		}

	/* Set cell array boundary coordinates */
	p[0]= xpxl(*xp); p[1]= ypxl(*yp);
	q[0]= xpxl(*xq); q[1]= ypxl(*yq);
	r[0]= xpxl(*xr); r[1]= ypxl(*yr);

	/* 
	Assume a 50% compression;  we'll add more space later if needed.
	We'll ask for more when there isn't enough space for a full row
	left.  An exception is made for very small cell arrays.  Each
	increment is to be 1/2 the original size.
	*/
	max_row_size= ( *nxdim % 2 != 0 ) ?
		5 * (*nxdim) * 1 : 5 * (*nxdim);
	tot_buf_sz= 0.5* ( 3*(*nxdim)*(*nydim) );
	if (tot_buf_sz < (2 * max_row_size))
		{
		tot_buf_sz= (*nydim) * max_row_size;
		trigger_size= tot_buf_sz;  /* never expand buffer */
		}
	else trigger_size= tot_buf_sz - max_row_size;
	size_increment= tot_buf_sz / 2 ;
	checkbufsize(tot_buf_sz);

	bufcpy= databuf;

	ldata= 0;
	for (jbyte=0; jbyte++<*nydim;)
		{
		iy= (int) jbyte;
/*
This section is commented out to avoid an undefined symbol error
on loading.  To use qclarw, you must uncomment this section.
-----------------------------------------------------------------
*/
/*
		if ( (*ierr= qclarw(rrow,grow,brow,nxdim,nydim,&iy))
			 != 0 )
			{
			fprintf(stderr,
				"QCLARW reported error code %d on row %d",
			   	*ierr,jbyte);
  			fprintf(stderr," command not executed.\n");
			return;
			};
*/
/*
------------------------------------------------------------------
Here ends the section that must be uncommented to use qclarw.
*/
		rcpy= rrow; gcpy= grow; bcpy= brow;
		rlast= float_to_byte(*rcpy);
		glast= float_to_byte(*gcpy);
		blast= float_to_byte(*bcpy);
		lrun= 1; lrow= 0;
	      	for (ibyte=0; ibyte++<*nxdim;)
			{
			if ((ibyte<*nxdim)||(jbyte<*nydim))
				{
				rnow= float_to_byte(*++rcpy);
		     		gnow= float_to_byte(*++gcpy);
				bnow= float_to_byte(*++bcpy);
				};
			if ((rnow==rlast)&&(gnow==glast)&&(bnow==blast)
				&&(ibyte<*nxdim)) lrun++;
			else
				{
				packword(lrun,bufcpy);
				*bufcpy++= rlast;
				*bufcpy++= glast;
				*bufcpy++= blast;
				lrow= lrow+5;
				lrun= 1;
				rlast= rnow; glast= gnow; blast= bnow;
				};
			};
		if ( lrow % 2 ) { *bufcpy++= 0; lrow++; };
		ldata= ldata +lrow;
		if (ldata >= trigger_size)
			{
			tot_buf_sz= tot_buf_sz + size_increment;
			oldbuf= databuf;
			checkbufsize(tot_buf_sz);
			trigger_size= tot_buf_sz - max_row_size;
			bufcpy= bufcpy - oldbuf + databuf;
			};
		};
                             
	/* Write the cell array element */
	check_do( gprim, Cell_Array )
 		(p,q,r,*nxdim,*nydim,8,databuf,0,ldata);
	err_chk( *ierr );

}                       

wrctbl(rarray,garray,barray,iclr,nclrs,ierr)
int *iclr,*nclrs,*ierr;
float *rarray,*garray,*barray;
/*
	This routine writes a CGM color table command, if the CGM state
	is appropriate.  The color table entries are assumed to have
	values between 0.0 and 1.0 .
*/
{       
	int i;
	float *rcopy, *gcopy, *bcopy, *target;
                          
	if (debugmode) 
		fprintf(stderr," wrctbl: nclrs= %d\n",*nclrs);
	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRCTBL; call ignored.\n");
		*ierr= 1;
		return;
		}

	/* Set the global color table to the given values */
	rcopy= rarray; gcopy= garray; bcopy= barray;
	target= glbl5.ctab + 3*(*iclr);
	for (i=0; i<*nclrs; i++)
		{
		*target++= *rcopy++;
		*target++= *gcopy++;
		*target++= *bcopy++;
		};

	/* Send the device the color table info */
	check_do( attr, ColTab )(*iclr,*nclrs,glbl5.ctab);
	err_chk( *ierr );

}
                       
wrftxt(textdesc,x,y,ierr STRINGLENGTH)
string_descriptor textdesc;
int *ierr;
float *x,*y;
DEFSTRINGLENGTH
/*
	This routine writes a CGM text command from a Fortran text string,
	by parsing the string and calling WRTTXT with the result.
*/
{
	char *getstring(),*txtstr;
	txtstr= getstring(textdesc STRINGLENGTH);
	if (debugmode)
	   	fprintf(stderr,
			" wrftxt: x= %e, y= %e\n   text= <%s>\n",*x,*y,txtstr);
	wrttxt(txtstr,x,y,ierr);
}

wrttxt(txtstr,x,y,ierr)
int *ierr;
float *x,*y;
char *txtstr;
/*
	This routine writes a CGM text command, if the CGM state is
	appropriate.  This is a 'final' text command.
*/
{
	int ix,iy;
                          
	if (debugmode) 
		fprintf(stderr,
			" wrttxt: x= %e, y= %e\n   text= <%s>\n",*x,*y,txtstr);
	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRTTXT; call ignored.\n");
		*ierr= 1;
		return;
		}

	ix= xpxl( *x );
	iy= ypxl( *y );
                                   
	check_do( gprim, Text )(ix,iy,on,txtstr);
	err_chk( *ierr );
}

wtxtpr(prec,ierr)
int *ierr;
int *prec;
/*
	This routine writes a CGM text precision command, if the CGM state is
	appropriate.
*/
{                                                
	if (debugmode) fprintf(stderr," wtxtpr: precision= %d.\n",*prec);
	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WTXTPR; call ignored.\n");
		*ierr= 1;
		return;
		}
	if ((*prec<0)||(*prec>2))
		{
		fprintf(stderr,
	     	   " Invalid text precision (%d) in WTXTPR; call ignored.\n",
			*prec);
		*ierr= 1;
		return;
		}

	glbl5.t_prec= (enum txt_enum) *prec;
	check_do( attr, TPrec )(glbl5.t_prec);
	err_chk( *ierr );

}

wrtxts(height,ierr)
int *ierr;
float *height;
/*
	This routine writes a CGM character height command, if the CGM state is
	appropriate.
*/
{                                                
	if (debugmode) fprintf(stderr," wrtxts: height= %e.\n",*height);
	if (cgmstate!=3) 
		{
		fprintf(stderr,   
	     		" CGM in wrong state for WRTXTS; call ignored.\n");
		*ierr= 1;
		return;
		}
                  
	glbl5.c_height= (int)( xpxl_scale*( *height ) );
	check_do( attr, CHeight )(glbl5.c_height);
	err_chk( *ierr );
                                                
}

wrtxte(factor,ierr)
int *ierr;
float *factor;
/*
	This routine writes a CGM character expansion factor command, 
	if the CGM state is appropriate.
*/
{                                                
	if (debugmode) fprintf(stderr," wrtxte: factor= %f.\n",*factor);
	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRTXTE; call ignored.\n");
		*ierr= 1;
		return;
		}
	if (*factor<0.0)
		{
		fprintf(stderr,
    " Invalid character expansion factor (%f) in WRTXTE; call ignored.\n",
			*factor);
		*ierr= 1;
		return;
		}

	glbl5.c_exp_fac= *factor;
	check_do( attr, CExpFac )(glbl5.c_exp_fac);
	err_chk( *ierr );
}
           
wtxtsp(spacing,ierr)
int *ierr;
float *spacing;
/*
	This routine writes a CGM character expansion factor command, 
	if the CGM state is appropriate.
*/
{                                                
	if (debugmode) fprintf(stderr," wtxtsp: spacing= %f.\n",*spacing);
	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WTXTSP; call ignored.\n");
		*ierr= 1;
		return;
		}

	glbl5.c_space= *spacing;
	check_do( attr, CSpace )(glbl5.c_space);
	err_chk( *ierr );
                                                
}

wrtxtf(font,ierr)
int *font;
int *ierr;
/*
	This routine writes a CGM text font command, if the CGM state is
	appropriate.
*/
{                                                
	if (debugmode) fprintf(stderr," wrtxtf: font= %d.\n",*font);
	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRTXTF; call ignored.\n");
		*ierr= 1;
		return;
		}
	if (*font<1)
		{
		fprintf(stderr,
	     		" Invalid text font (%d) in WRTXTF; call ignored.\n",
			*font);
		*ierr= 1;
		return;
		}

	glbl5.t_f_index= *font;
	check_do( attr, TFIndex )(glbl5.t_f_index);
	err_chk( *ierr );
}

wrtxtp(path,ierr)
int *ierr;
int *path;
/*
	This routine writes a CGM text path command, if the CGM state is
	appropriate.
*/
{                                                
	if (debugmode) fprintf(stderr," wrtxtp: path= %d.\n",*path);
	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRTXTP; call ignored.\n");
		*ierr= 1;
		return;
		}
	if ((*path<0)||(*path>3))
		{
		fprintf(stderr,
	     		" Invalid text path (%d) in WRTXTP; call ignored.\n",
			*path);
		*ierr= 1;
		return;
		}

	glbl5.text_path= (enum path_enum) *path;
	check_do( attr, TPath )(glbl5.text_path);
	err_chk( *ierr );
}                                      

wrtxto(xu,yu,xb,yb,ierr)
int *ierr;
float *xu, *yu, *xb, *yb;
/*
	This routine writes a CGM character orientation command, if the 
	CGM state is appropriate.  *xu and *yu are the floating-point
	magnitudes of the x and y components of the character up vector;
	*xb and *yb are for the character base vector.  Only the ratios
	of the components matter;  absolute magnitudes are irrelevant.
*/
{                                                
	if (debugmode) 
		fprintf(stderr," wrtxto: up= (%f,%f); base=(%f,%f).\n",
		*xu,*yu,*xb,*yb);
	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRTXTO; call ignored.\n");
		*ierr= 1;
		return;
		}

	/* 0.01 is chosen as an arbitrary scale factor */
	glbl5.c_orient.x_up= 
		(int)( 0.01 * *xu * dev_info.x_size * dev_info.pxl_in );
	glbl5.c_orient.y_up= 
		(int)( 0.01 * *yu * dev_info.y_size * dev_info.ypxl_in );
	glbl5.c_orient.x_base= 
		(int)( 0.01 * *xb * dev_info.x_size * dev_info.pxl_in );
	glbl5.c_orient.y_base= 
		(int)( 0.01 * *yb * dev_info.y_size * dev_info.ypxl_in );

	check_do( attr, COrient )
		( glbl5.c_orient.x_up, glbl5.c_orient.y_up,
		  glbl5.c_orient.x_base, glbl5.c_orient.y_base );
	err_chk( *ierr );

}

wrtxta(hor,ver,cont_hor,cont_ver,ierr)
int *ierr;
int *hor,*ver;
float *cont_hor,*cont_ver;
/*
	This routine writes a CGM text alignment command, if the CGM state is
	appropriate.
*/
{                                                
	if (debugmode) fprintf(stderr,
		" wrtxta: hor= %d, ver= %d, cont_hor= %f, cont_ver= %f.\n",
		*hor,*ver,*cont_hor,*cont_ver);
	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRTXTA; call ignored.\n");
		*ierr= 1;
		return;
		}
	if ((*hor<0)||(*hor>4))
		{
		fprintf(stderr,
	    " Invalid horizontal alignment (%d) in WRTXTA; call ignored.\n",
			*hor);
		*ierr= 1;
		return;
		}
	if ((*ver<0)||(*ver>6))
		{
		fprintf(stderr,
	    " Invalid vertical alignment (%d) in WRTXTA; call ignored.\n",
			*ver);
		*ierr= 1;
		return;
		}

	glbl5.text_align.hor= (enum hor_align) *hor;
	glbl5.text_align.ver= (enum ver_align) *ver;
	glbl5.text_align.cont_hor= *cont_hor;
	glbl5.text_align.cont_ver= *cont_ver;

	check_do( attr, TAlign )
		( glbl5.text_align.hor, glbl5.text_align.ver,
		  glbl5.text_align.cont_hor, glbl5.text_align.cont_ver );
	err_chk( *ierr );
                                                
}

wrtxtc(icolor,ierr)
int *icolor,*ierr;
/*
	This routine writes a CGM text color command, if the CGM state is
	appropriate.
*/
{
	float *clrptr;

	if (debugmode) fprintf(stderr," wrtxtc: icolor= %d.\n",*icolor);
	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRTXTC; call ignored.\n");
		*ierr= 1;
		return;
		}
	if (glbl2.c_s_mode != i_c_mode) 
		{
		fprintf(stderr,
	     		" Color mode not indexed in WRTXTC; call ignored.\n");
		*ierr= 1;
		return;
		}

	clrptr= glbl5.ctab + 3*(*icolor);
	glbl5.text_colour.red= *clrptr++;
	glbl5.text_colour.green= *clrptr++;
	glbl5.text_colour.blue= *clrptr;
	glbl5.text_colour.ind= *icolor;

	check_do( attr, TColour )
		( glbl5.text_colour.red, glbl5.text_colour.green,
		  glbl5.text_colour.blue, glbl5.text_colour.ind );
	err_chk( *ierr );
}

wtxtdc(red,green,blue,ierr)
int *ierr;
float *red,*green,*blue;
/*                                                               
	This routine writes a CGM text color command in direct color
	form, if the CGM state is appropriate.  The color values are
	assumed to lie between 0.0 and 1.0
*/
{
	if (debugmode) fprintf(stderr," wtxtdc: red=%e; green=%e; blue=%e.\n",
		*red,*green,*blue);
	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WTXTDC; call ignored.\n");
		*ierr= 1;
		return;
		}
	if (glbl2.c_s_mode != d_c_mode) 
		{
		fprintf(stderr,
	     		" Color mode not direct in WTXTDC; call ignored.\n");
		*ierr= 1;
		return;
		}

 	/* 
	The index is set to -1 below to make it demonstrably invalid,
	a GPlot convention.
	*/
	glbl5.text_colour.red= *red;
	glbl5.text_colour.green= *green;
	glbl5.text_colour.blue= *blue;
	glbl5.text_colour.ind= -1;

	check_do( attr, TColour )
		( glbl5.text_colour.red, glbl5.text_colour.green,
		  glbl5.text_colour.blue, glbl5.text_colour.ind );
	err_chk( *ierr );

}

wrplin(xarray,yarray,npts,ierr)
int *npts,*ierr;
float *xarray,*yarray;
/*
	This routine writes a CGM polyline command, if the CGM state is
	appropriate.
*/
{
	int ipt, *ixcpy, *iycpy;

	if (debugmode) fprintf(stderr," wrplin:  npts= %d\n",*npts);
	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRPLIN; call ignored.\n");
		*ierr= 1;
		return;
		}
	if ( getcmem(*npts,*npts) != 1 )
		{ 
		fprintf(stderr,
			" Error allocating memory in WRPLIN; call ignored.\n");
		*ierr= 2;
		};

	ixcpy= ixbuf; 
	iycpy= iybuf;
	for ( ipt= 0; ipt<*npts; ipt++ )
		{
		*ixcpy++= xpxl( *xarray++ );
		*iycpy++= ypxl( *yarray++ );
		};
	check_do( gprim, PolyLine )( *npts, ixbuf, iybuf );
	err_chk( *ierr );
}

wrtpmk(xarray,yarray,npts,ierr)
int *npts,*ierr;
float *xarray,*yarray;
/*
	This routine writes a CGM polymarker command, if the CGM state is
	appropriate.
*/
{
	int ipt, *ixcpy, *iycpy;
	extern int em_pmarker();
                          
	if (debugmode) fprintf(stderr," wrtpmk:  npts= %d\n",*npts);
	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRTPMK; call ignored.\n");
		*ierr= 1;
		return;
		}
	if ( getcmem(*npts,*npts) != 1 )
		{
		fprintf(stderr,
			" Error allocating memory in WRTPMK; call ignored.\n");
		*ierr= 2;
		};

	ixcpy= ixbuf; 
	iycpy= iybuf;
	for ( ipt= 0; ipt<*npts; ipt++ )
		{
		*ixcpy++= xpxl( *xarray++ );
		*iycpy++= ypxl( *yarray++ );
		};
	check_do( gprim, PolyMarker )( *npts, ixbuf, iybuf );
	else gbl_ret= em_pmarker( *npts, ixbuf, iybuf );
	err_chk( *ierr );

}

wrplnc(icolor,ierr)
int *icolor,*ierr;
/*
	This routine writes a CGM line color command, if the CGM state is
	appropriate.
*/
{
	float *clrptr;

	if (debugmode) fprintf(stderr," wrplnc: icolor= %d\n",*icolor);

	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRPLNC; call ignored.\n");
		*ierr= 1;
		return;
		}
	if (glbl2.c_s_mode != i_c_mode) 
		{
		fprintf(stderr,
	     		" Color mode not indexed in WRPLNC; call ignored.\n");
		*ierr= 1;
		return;
		}

	clrptr= glbl5.ctab + 3*(*icolor);
	glbl5.line_colour.red= *clrptr++;
	glbl5.line_colour.green= *clrptr++;
	glbl5.line_colour.blue= *clrptr;
	glbl5.line_colour.ind= *icolor;

	check_do( attr, LColour )
		( glbl5.line_colour.red, glbl5.line_colour.green,
		  glbl5.line_colour.blue, glbl5.line_colour.ind );
	err_chk( *ierr );
}

wrpmkc(icolor,ierr)
int *icolor,*ierr;
/*
	This routine writes a CGM marker color command, if the CGM state is
	appropriate.
*/
{
	float *clrptr;

	if (debugmode) fprintf(stderr," wrpmkc: icolor= %d\n",*icolor);

	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRPMKC; call ignored.\n");
		*ierr= 1;
		return;
		}
	if (glbl2.c_s_mode != i_c_mode) 
		{
		fprintf(stderr,
	     		" Color mode not indexed in WRPMKC; call ignored.\n");
		*ierr= 1;
		return;
		}

	clrptr= glbl5.ctab + 3*(*icolor);
	glbl5.mk_colour.red= *clrptr++;
	glbl5.mk_colour.green= *clrptr++;
	glbl5.mk_colour.blue= *clrptr;
	glbl5.mk_colour.ind= *icolor;

	check_do( attr, MColour )
		( glbl5.mk_colour.red, glbl5.mk_colour.green,
		  glbl5.mk_colour.blue, glbl5.mk_colour.ind );
	err_chk( *ierr );

}

wrpmkt(mark,ierr)
int *mark,*ierr;
/*
	This routine writes a CGM marker type command, if the CGM state is
	appropriate.
*/
{
	if (debugmode) fprintf(stderr," wrpmkt: mark= %d\n",*mark);

	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRPMKT; call ignored.\n");
		*ierr= 1;
		return;
		}

	glbl5.mk_type= *mark;

	check_do( attr, MType )( glbl5.mk_type );
	err_chk( *ierr );
}

wrpmks(size,ierr)
int *ierr;
float *size;
/*
	This routine writes a CGM marker size command, in 'scaled' format,
	if the CGM state is appropriate.
*/
{
	if (debugmode) fprintf(stderr,
		" wrpmks: size scale factor= %f\n",*size);

	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRPMKS; call ignored.\n");
		*ierr= 1;
		return;
		}

	glbl5.mk_size.r= *size;
	glbl5.mk_size.i= *size * dev_info.d_m_size;

	check_do( attr, MSize )( glbl5.mk_size.i, glbl5.mk_size.r );
	err_chk( *ierr );
}

wplndc(red,green,blue,ierr)
int *ierr;
float *red,*green,*blue;
/*                                                               
	This routine writes a CGM line color command in direct color
	form, if the CGM state is appropriate.  The color values are
	assumed to lie between 0.0 and 1.0
*/
{
	if (debugmode) fprintf(stderr," wplndc: red=%e; green=%e; blue=%e.\n",
		*red,*green,*blue);

	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WPLNDC; call ignored.\n");
		*ierr= 1;
		return;
		}
	if (glbl2.c_s_mode != d_c_mode) 
		{
		fprintf(stderr,
	     		" Color mode not direct in WPLNDC; call ignored.\n");
		*ierr= 1;
		return;
		}

 	/* 
	The index is set to -1 below to make it demonstrably invalid,
	a GPlot convention.
	*/                  
	glbl5.line_colour.red= *red;
	glbl5.line_colour.green= *green;
	glbl5.line_colour.blue= *blue;
	glbl5.line_colour.ind= -1;

	check_do( attr, LColour )
		( glbl5.line_colour.red, glbl5.line_colour.green,
		  glbl5.line_colour.blue, glbl5.line_colour.ind );
	err_chk( *ierr );
}

wpmkdc(red,green,blue,ierr)
int *ierr;
float *red,*green,*blue;
/*                                                               
	This routine writes a CGM marker color command in direct color
	form, if the CGM state is appropriate.  The color values are
	assumed to lie between 0.0 and 1.0
*/
{
	if (debugmode) fprintf(stderr," wpmkdc: red=%e; green=%e; blue=%e.\n",
		*red,*green,*blue);

	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WPMKDC; call ignored.\n");
		*ierr= 1;
		return;
		}
	if (glbl2.c_s_mode != d_c_mode) 
		{
		fprintf(stderr,
	     		" Color mode not direct in WPMKDC; call ignored.\n");
		*ierr= 1;
		return;
		}

 	/* 
	The index is set to -1 below to make it demonstrably invalid,
	a GPlot convention.
	*/                  
	glbl5.mk_colour.red= *red;
	glbl5.mk_colour.green= *green;
	glbl5.mk_colour.blue= *blue;
	glbl5.mk_colour.ind= -1;

	check_do( attr, MColour )
		( glbl5.mk_colour.red, glbl5.mk_colour.green,
		  glbl5.mk_colour.blue, glbl5.mk_colour.ind );
	err_chk( *ierr );
}

wrtpgn(xarray,yarray,npts,ierr)
int *npts,*ierr;
float *xarray,*yarray;
/*
	This routine writes a CGM polygon command, if the CGM state is
	appropriate.
*/
{
	int ipt, *ixcpy, *iycpy;
	extern int em_pgon();
                          
	if (debugmode) fprintf(stderr," wrtpgn:  npts= %d\n",*npts);
	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRTPGN; call ignored.\n");
		*ierr= 1;
		return;
		}
	if ( getcmem(*npts,*npts) != 1 )
		{ 
		fprintf(stderr,
			" Error allocating memory in WRPLIN; call ignored.\n");
		*ierr= 2;
		};

	ixcpy= ixbuf; 
	iycpy= iybuf;
	for ( ipt= 0; ipt<*npts; ipt++ )
		{
		*ixcpy++= xpxl( *xarray++ );
		*iycpy++= ypxl( *yarray++ );
		};
	check_do( gprim, Polygon )( *npts, ixbuf, iybuf );
	else gbl_ret= em_pgon( *npts, ixbuf, iybuf );
	err_chk( *ierr );
}

wrpgnc(icolor,ierr)
int *icolor,*ierr;
/*
	This routine writes a CGM fill color command, if the CGM state is
	appropriate.
*/
{
	float *clrptr;

	if (debugmode) fprintf(stderr," wrpgnc: icolor= %d\n",*icolor);

	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRPGNC; call ignored.\n");
		*ierr= 1;    
		return;
		}
	if (glbl2.c_s_mode != i_c_mode) 
		{
		fprintf(stderr,
	     		" Color mode not indexed in WRPGNC; call ignored.\n");
		*ierr= 1;
		return;
		}

	clrptr= glbl5.ctab + 3*(*icolor);
	glbl5.fill_colour.red= *clrptr++;
	glbl5.fill_colour.green= *clrptr++;
	glbl5.fill_colour.blue= *clrptr;
	glbl5.fill_colour.ind= *icolor;

	check_do( attr, FillColour )
		( glbl5.fill_colour.red, glbl5.fill_colour.green,
		  glbl5.fill_colour.blue, glbl5.fill_colour.ind );
	err_chk( *ierr );
}

wpgndc(red,green,blue,ierr)
int *ierr;
float *red,*green,*blue;
/*                                                               
	This routine writes a CGM polygon color command in direct color
	form, if the CGM state is appropriate.  The color values are
	assumed to lie between 0.0 and 1.0
*/
{
	if (debugmode) fprintf(stderr," wpgndc: red=%e; green=%e; blue=%e.\n",
		*red,*green,*blue);

	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WPGNDC; call ignored.\n");
		*ierr= 1;
		return;
		}
	if (glbl2.c_s_mode != d_c_mode) 
		{
		fprintf(stderr,
	     		" Color mode not direct in WPGNDC; call ignored.\n");
		*ierr= 1;
		return;
		}

 	/* 
	The index is set to -1 below to make it demonstrably invalid,
	a GPlot convention.
	*/                  
	glbl5.fill_colour.red= *red;
	glbl5.fill_colour.green= *green;
	glbl5.fill_colour.blue= *blue;
	glbl5.fill_colour.ind= -1;

	check_do( attr, FillColour )
		( glbl5.fill_colour.red, glbl5.fill_colour.green,
		  glbl5.fill_colour.blue, glbl5.fill_colour.ind );
	err_chk( *ierr );
}

wristl(style,ierr)
int *style, *ierr;
/*
	This routine writes a CGM interior style element, if the CGM state is
	appropriate.
*/
{                                                
	if (debugmode) fprintf(stderr," wristl: style= %d.\n",*style);

	if (cgmstate!=3) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRISTL; call ignored.\n");
		*ierr= 1;
		return;
		}

	glbl5.int_style= (enum is_enum) *style;

	check_do( attr, IntStyle )( glbl5.int_style );
	err_chk( *ierr );
}

wrtcsm(imode,ierr)
int *imode,*ierr;
/*
	This routine writes a CGM color selection mode command, if the 
	CGM state is appropriate.  The color_sel_mode flag is set 
	appropriately.
*/
{
	if (debugmode) fprintf(stderr," wrtcsm: imode= %d.\n",*imode);

	if (cgmstate!=2) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRTCSM; call ignored.\n");
		*ierr= 1;
		return;
		}
	if ((*imode!=0) && (*imode!=1))
		{
		fprintf(stderr,
	     	 " Color_sel_mode must be 0 or 1 in WRTCSM; call ignored.\n");
		*ierr= 1;
		return;
		}

	glbl2.c_s_mode= (enum cs_enum) *imode;

	check_do( pdesc, ColSelMode )( glbl2.c_s_mode );
	err_chk( *ierr );
}

wrbgdc(red,green,blue,ierr)
int *ierr;
float *red,*green,*blue;
/*
	This routine writes a CGM background color command, if the 
	CGM state is appropriate.  The color_sel_mode flag is set 
	appropriately.
*/
{
	int ir, ig, ib;

	if (debugmode) fprintf(stderr," wrbgdc: red=%e; green=%e; blue=%e.\n",
		*red,*green,*blue);

	if (cgmstate!=2) 
		{
		fprintf(stderr,
	     		" CGM in wrong state for WRBGDC; call ignored.\n");
		*ierr= 1;
		return;
		}

	ir= (int) ((*red) * glbl1.c_v_extent.max[0] 
	     	+ (1.0-(*red)) * glbl1.c_v_extent.min[0]);
	ig= (int) ((*green) * glbl1.c_v_extent.max[1] 
	     	+ (1.0-(*green)) * glbl1.c_v_extent.min[1]);
	ib= (int) ((*blue) * glbl1.c_v_extent.max[2] 
	     	+ (1.0-(*blue)) * glbl1.c_v_extent.min[2]);

	glbl2.back_col.red= *red;
	glbl2.back_col.green= *green;
	glbl2.back_col.blue= *blue;

  	/* Set the index zero entry of the color table to the given color */
	*(glbl5.ctab)= *red;
	*(glbl5.ctab+1)= *green;
	*(glbl5.ctab+2)= *blue;

	check_do( pdesc, BackCol )( ir, ig, ib );
	err_chk( *ierr );
}             
