/* Copyright (c) 1992 Regents of the University of California */

#ifndef lint
static char SCCSid[] = "@(#)psign.c 2.12 9/8/92 LBL";
#endif

/*
 *  psign.c - produce picture from text.
 *
 *      7/1/87
 */

#include  "standard.h"

#include  "color.h"

#include  "font.h"

#include  "paths.h"

#ifndef  SSS
#define  SSS                    3       /* super-sample size */
#endif

#define  MAXLINE                512     /* longest allowable line */

char  *fontfile = "helvet.fnt";         /* our font file */

COLOR  bgcolor = WHTCOLOR;              /* background color */
COLOR  fgcolor = BLKCOLOR;              /* foreground color */

int  direct = 'r';                      /* direction (right, up, left, down) */

int  cheight = 32*SSS;                  /* character height */
double  aspect = 1.67;                  /* height/width character aspect */
double  spacing = 0.0;                  /* character spacing */
int  cwidth;                            /* computed character width */

unsigned char  *ourbitmap;              /* our output bitmap */
int  xsiz, ysiz;                        /* bitmap dimensions */
int  xdim;                              /* size of horizontal scan (bytes) */

#define  bitop(x,y,op)          (ourbitmap[(y)*xdim+((x)>>3)] op (1<<((x)&7)))
#define  tstbit(x,y)            bitop(x,y,&)
#define  setbit(x,y)            bitop(x,y,|=)
#define  clrbit(x,y)            bitop(x,y,&=~)
#define  tglbit(x,y)            bitop(x,y,^=)

FONT  *ourfont;                         /* our font */

char  *libpath;                         /* library search path */

typedef struct line {
	char  *s;               /* line w/o LF */
	short  *sp;             /* character spacing */
	struct line  *next;     /* next line up */
} LINE;

LINE  *ourtext;                         /* our text */
int  nlines, maxline;                   /* text dimensions */
int  maxwidth;                          /* maximum line width (dvi) */


main(argc, argv)
int  argc;
char  *argv[];
{
	int  an;
#ifdef MSDOS
	setmode(fileno(stdout), O_BINARY);
#endif
	for (an = 1; an < argc && argv[an][0] == '-'; an++)
		switch (argv[an][1]) {
		case 'c':                       /* color */
			switch (argv[an][2]) {
			case 'f':                       /* foreground */
				setcolor(fgcolor, atof(argv[an+1]),
						atof(argv[an+2]),
						atof(argv[an+3]));
				an += 3;
				break;
			case 'b':                       /* background */
				setcolor(bgcolor, atof(argv[an+1]),
						atof(argv[an+2]),
						atof(argv[an+3]));
				an += 3;
				break;
			default:
				goto unkopt;
			}
			break;
		case 'f':                       /* font */
			fontfile = argv[++an];
			break;
		case 'd':                       /* direction */
			switch (argv[an][2]) {
			case 'r':                       /* right */
			case 'u':                       /* up */
			case 'l':                       /* left */
			case 'd':                       /* down */
				direct = argv[an][2];
				break;
			default:
				goto unkopt;
			}
			break;
		case 'x':                       /* x resolution */
			xsiz = atoi(argv[++an])*SSS;
			break;
		case 'y':
			ysiz = atoi(argv[++an])*SSS;
			break;
		case 'h':                       /* height of characters */
			cheight = atoi(argv[++an])*SSS;
			break;
		case 'a':                       /* aspect ratio */
			aspect = atof(argv[++an]);
			break;
		case 's':                       /* spacing */
			spacing = atof(argv[++an]);
			break;
		default:;
unkopt:
			fprintf(stderr, "%s: unknown option: %s\n",
					argv[0], argv[an]);
			exit(1);
		}
					/* load font file */
	if ((libpath = getenv(ULIBVAR)) == NULL)
		libpath = DEFPATH;
	ourfont = getfont(fontfile);
					/* get text */
	if (an == argc)
		gettext(stdin);
	else
		arg_text(argc-an, argv+an);

					/* create bit map */
	makemap();
					/* convert text to bitmap */
	maptext();
					/* print header */
	printargs(argc, argv, stdout);
	fputformat(COLRFMT, stdout);
	putchar('\n');
					/* write out bitmap */
	writemap(stdout);

	exit(0);
}


makemap()                       /* create the bit map */
{
	double  pictaspect;
	
	if (direct == 'r' || direct == 'l') {
		if (xsiz <= 0) {
			cwidth = cheight/aspect + 0.5;
			xsiz = (long)maxwidth*cwidth >> 8;
			ysiz = nlines*cheight;
		} else if (aspect > FTINY) {
			if (ysiz <= 0)
				ysiz = cheight*nlines;
			pictaspect = 256*nlines*aspect/maxwidth;
			if (pictaspect*xsiz < ysiz)
				ysiz = pictaspect*xsiz + 0.5;
			else
				xsiz = ysiz/pictaspect + 0.5;
			cheight = ysiz/nlines;
			cwidth = cheight/aspect + 0.5;
		} else {
			if (ysiz <= 0)
				ysiz = cheight*nlines;
			pictaspect = (double)ysiz/xsiz;
			aspect = pictaspect*maxwidth/(256*nlines);
			cheight = ysiz/nlines;
			cwidth = cheight/aspect + 0.5;
		}
	} else {                        /* reverse orientation */
		if (ysiz <= 0) {
			cwidth = cheight/aspect + 0.5;
			xsiz = nlines*cheight;
			ysiz = (long)maxwidth*cwidth >> 8;
		} else if (aspect > FTINY) {
			if (xsiz <= 0)
				xsiz = cheight*nlines;
			pictaspect = maxwidth/(256*nlines*aspect);
			if (pictaspect*xsiz < ysiz)
				ysiz = pictaspect*xsiz + 0.5;
			else
				xsiz = ysiz/pictaspect + 0.5;
			cheight = xsiz/nlines;
			cwidth = cheight/aspect + 0.5;
		} else {
			if (xsiz <= 0)
				xsiz = cheight*nlines;
			pictaspect = (double)ysiz/xsiz;
			aspect = maxwidth/(256*nlines*pictaspect);
			cheight = xsiz/nlines;
			cwidth = cheight/aspect + 0.5;
		}
	}
	if (xsiz % SSS)
		xsiz += SSS - xsiz%SSS;
	if (ysiz % SSS)
		ysiz += SSS - ysiz%SSS;
	xdim = (xsiz+7)/8;
	ourbitmap = (BYTE *)bmalloc(ysiz*xdim);
	if (ourbitmap == NULL)
		error(SYSTEM, "Out of memory in makemap");
	bzero((char *)ourbitmap, ysiz*xdim);
}


gettext(fp)                     /* get text from a file */
FILE  *fp;
{
	char  *fgets();
	char  buf[MAXLINE];
	register LINE  *curl;
	int  len;

	maxline = 0;
	maxwidth = 0;
	nlines = 0;
	while (fgets(buf, MAXLINE, fp) != NULL) {
		curl = (LINE *)malloc(sizeof(LINE));
		if (curl == NULL)
			goto memerr;
		len = strlen(buf);
		curl->s = malloc(len);
		curl->sp = (short *)malloc(sizeof(short)*len--);
		if (curl->s == NULL | curl->sp == NULL)
			goto memerr;
		if (len > maxline)
			maxline = len;
		strncpy(curl->s, buf, len);
		curl->s[len] = '\0';
		if (spacing < -1./256.)
			len = squeeztext(curl->sp, curl->s, ourfont,
					(int)(spacing*-256.0));
		else if (spacing > 1./256.)
			len = proptext(curl->sp, curl->s, ourfont,
					(int)(spacing*256.0), 3);
		else
			len = uniftext(curl->sp, curl->s, ourfont);
		if (len > maxwidth)
			maxwidth = len;
		curl->next = ourtext;
		ourtext = curl;
		nlines++;
	}
	return;
memerr:
	error(SYSTEM, "Out of memory in gettext");
}


arg_text(ac, av)                        /* get text from arguments */
int  ac;
char  *av[];
{
	register char  *cp;

	ourtext = (LINE *)malloc(sizeof(LINE));
	if (ourtext == NULL)
		goto memerr;
	ourtext->s = malloc(MAXLINE);
	if (ourtext->s == NULL)
		goto memerr;
	for (cp = ourtext->s; ac-- > 0; av++) {
		strcpy(cp, *av);
		cp += strlen(*av);
		*cp++ = ' ';
	}
	*--cp = '\0';
	ourtext->next = NULL;
	maxline = strlen(ourtext->s);
	ourtext->sp = (short *)malloc(sizeof(short)*(maxline+1));
	if (ourtext->sp == NULL)
		goto memerr;
	if (spacing < 0.0)
		maxwidth = squeeztext(ourtext->sp, ourtext->s, ourfont,
				(int)(spacing*-256.0));
	else if (spacing > 0.0)
		maxwidth = proptext(ourtext->sp, ourtext->s, ourfont,
				(int)(spacing*256.0), 3);
	else
		maxwidth = uniftext(ourtext->sp, ourtext->s, ourfont);
	nlines = 1;
	return;
memerr:
	error(SYSTEM, "Out of memory in arg_text");
}


maptext()                       /* map our text */
{
	register LINE  *curl;
	int  l, len;
	register int  i, c;

	for (l = 0, curl = ourtext; curl != NULL; l += 256, curl = curl->next) {
		len = strlen(curl->s); c = 0;
		for (i = 0; i < len; i++) {
			c += curl->sp[i];
			mapglyph(ourfont->fg[curl->s[i]&0xff], c, l);
		}
	}
}


mapglyph(gl, tx0, ty0)          /* convert a glyph */
GLYPH  *gl;
int  tx0, ty0;
{
	int  n;
	register GORD  *gp;
	int  p0[2], p1[2];

	if (gl == NULL)
		return;

	n = gl->nverts;
	gp = gvlist(gl);
	mapcoord(p0, gp[2*n-2]+tx0, gp[2*n-1]+ty0);
	while (n--) {
		mapcoord(p1, gp[0]+tx0, gp[1]+ty0);
		mapedge(p0[0], p0[1], p1[0]-p0[0], p1[1]-p0[1]);
		p0[0] = p1[0]; p0[1] = p1[1];
		gp += 2;
	}
}


mapcoord(p, tx, ty)             /* map text to picture coordinates */
int  p[2], tx, ty;
{
	tx = (long)tx*cwidth >> 8;
	ty = (long)ty*cheight >> 8;

	switch (direct) {
	case 'r':                       /* right */
		p[0] = tx;
		p[1] = ty;
		return;
	case 'u':                       /* up */
		p[0] = xsiz-1-ty;
		p[1] = tx;
		return;
	case 'l':                       /* left */
		p[0] = xsiz-1-tx;
		p[1] = ysiz-1-ty;
		return;
	case 'd':                       /* down */
		p[0] = ty;
		p[1] = ysiz-1-tx;
		return;
	}
}


mapedge(x, y, run, rise)                /* map an edge */
register int  x, y;
int  run, rise;
{
	int  xstep;
	int  rise2, run2;
	int  n;

	if (rise == 0)
		return;
						/* always draw up */
	if (rise < 0) {
		x += run;
		y += rise;
		rise = -rise;
		run = -run;
	}
	if (run < 0) {
		xstep = -1;
		run = -run;
	} else
		xstep = 1;
	n = rise;
	run2 = rise2 = 0;
	while (n)
		if (rise2 >= run2) {
			tglbit(x, y);
			n--;
			y++;
			run2 += run;
		} else {
			x += xstep;
			rise2 += rise;
		}
}


writemap(fp)                    /* write out bitmap */
FILE  *fp;
{
	COLR  pixval[SSS*SSS+1];        /* possible pixel values */
	COLOR  ctmp0, ctmp1;
	double  d;
	COLR  *scanout;
	int  x, y;
	register int  i, j;
	int  cnt;
	register int  inglyph;

	fprintf(fp, "-Y %d +X %d\n", ysiz/SSS, xsiz/SSS);

	scanout = (COLR *)malloc(xsiz/SSS*sizeof(COLR));
	if (scanout == NULL)
		error(SYSTEM, "Out of memory in writemap");
	for (i = 0; i <= SSS*SSS; i++) {        /* compute possible values */
		copycolor(ctmp0, fgcolor);
		d = (double)i/(SSS*SSS);
		scalecolor(ctmp0, d);
		copycolor(ctmp1, bgcolor);
		d = 1.0 - d;
		scalecolor(ctmp1, d);
		addcolor(ctmp0, ctmp1);
		setcolr(pixval[i], colval(ctmp0,RED),
				colval(ctmp0,GRN), colval(ctmp0,BLU));
	}
	for (y = ysiz/SSS-1; y >= 0; y--) {
		inglyph = 0;
		for (x = 0; x < xsiz/SSS; x++) {
			cnt = 0;
			for (j = 0; j < SSS; j++)
				for (i = 0; i < SSS; i++) {
					if (tstbit(x*SSS+i, y*SSS+j))
						inglyph ^= 1<<j;
					if (inglyph & 1<<j)
						cnt++;
				}
			copycolr(scanout[x], pixval[cnt]);
		}
		if (fwritecolrs(scanout, xsiz/SSS, fp) < 0) {
			fprintf(stderr, "write error in writemap\n");
			exit(1);
		}
	}
	free((char *)scanout);
}
