--- ray/src/rt/text.c 1991/01/03 11:54:54 1.5 +++ ray/src/rt/text.c 1992/06/25 12:05:10 2.6 @@ -1,4 +1,4 @@ -/* Copyright (c) 1990 Regents of the University of California */ +/* Copyright (c) 1991 Regents of the University of California */ #ifndef lint static char SCCSid[] = "$SunId$ LBL"; @@ -14,6 +14,8 @@ static char SCCSid[] = "$SunId$ LBL"; #include "otypes.h" +#include "font.h" + /* * A text pattern is specified as the text (a file or line), * the upper left anchor point, the right motion vector, the down @@ -23,22 +25,24 @@ static char SCCSid[] = "$SunId$ LBL"; * modifier brighttext id * 2 fontfile textfile * 0 - * 11 + * 11+ * Ax Ay Az * Rx Ry Rz * Dx Dy Dz * foreground background + * [spacing] * * For a single line, we use: * * modifier brighttext id * N+2 fontfile . This is a line with N words... * 0 - * 11 + * 11+ * Ax Ay Az * Rx Ry Rz * Dx Dy Dz * foreground background + * [spacing] * * Colortext is identical, except colors are given rather than * brightnesses. Mixtext has foreground and background modifiers: @@ -46,65 +50,55 @@ static char SCCSid[] = "$SunId$ LBL"; * modifier mixtext id * 4+ foremod backmod fontfile text.. * 0 - * 9 + * 9+ * Ax Ay Az * Rx Ry Rz * Dx Dy Dz + * [spacing] */ #define fndx(m) ((m)->otype==MIX_TEXT ? 2 : 0) #define tndx(m) ((m)->otype==MIX_TEXT ? 3 : 1) +#define sndx(m) ((m)->otype==PAT_BTEXT ? 11 : \ + (m)->otype==PAT_CTEXT ? 15 : 9) -extern char *libpath; /* library search path */ +typedef struct tline { + struct tline *next; /* pointer to next line */ + short *spc; /* character spacing */ + int width; /* total line width */ + /* followed by the string */ +} TLINE; -typedef unsigned char GLYPH; +#define TLSTR(l) ((char *)((l)+1)) -typedef struct font { - GLYPH *fg[256]; /* font glyphs */ - char *name; /* font file name */ - struct font *next; /* next font in list */ -} FONT; +typedef struct { + FVECT right, down; /* right and down unit vectors */ + FONT *f; /* our font */ + TLINE tl; /* line list */ +} TEXT; -extern GLYPH *getglyph(); +extern char *libpath; -extern FONT *getfont(); +extern char *fgetword(); -static FONT *fontlist = NULL; /* our font list */ +TEXT *gettext(); +TLINE *tlalloc(); + text(m, r) register OBJREC *m; RAY *r; { - double v[3], y, x; - int col, lno; + FVECT v; int foreground; - GLYPH *g; - register double *ap; - - if (m->oargs.nsargs - tndx(m) < 1 || - m->oargs.nfargs != (m->otype == PAT_BTEXT ? 11 : - m->otype == PAT_CTEXT ? 15 : 9)) - objerror(m, USER, "bad # arguments"); - - /* first, discover position in text */ - ap = m->oargs.farg; + /* get transformed position */ if (r->rox != NULL) multp3(v, r->rop, r->rox->b.xfm); else VCOPY(v, r->rop); - v[0] -= ap[0]; - v[1] -= ap[1]; - v[2] -= ap[2]; - col = x = DOT(v, ap+3) / DOT(ap+3, ap+3); - lno = y = DOT(v, ap+6) / DOT(ap+6, ap+6); - x -= col; - y = (lno+1) - y; - /* get the font character, check it */ - if ((g = getglyph(m, lno, col)) == NULL) - foreground = 0; - else - foreground = inglyph(x, y, g); + /* check if we are within a text glyph */ + foreground = intext(v, m); /* modify */ if (m->otype == MIX_TEXT) { OBJECT omod; @@ -115,186 +109,234 @@ RAY *r; sprintf(errmsg, "undefined modifier \"%s\"", modname); objerror(m, USER, errmsg); } - raymixture(r, omod, OVOID, 1.0); + raytexture(r, omod); } else if (m->otype == PAT_BTEXT) { if (foreground) - scalecolor(r->pcol, ap[9]); + scalecolor(r->pcol, m->oargs.farg[9]); else - scalecolor(r->pcol, ap[10]); + scalecolor(r->pcol, m->oargs.farg[10]); } else { /* PAT_CTEXT */ COLOR cval; if (foreground) - setcolor(cval, ap[9], ap[10], ap[11]); + setcolor(cval, m->oargs.farg[9], + m->oargs.farg[10], + m->oargs.farg[11]); else - setcolor(cval, ap[12], ap[13], ap[14]); + setcolor(cval, m->oargs.farg[12], + m->oargs.farg[13], + m->oargs.farg[14]); multcolor(r->pcol, cval); } } -GLYPH * -getglyph(tm, lno, col) /* get a glyph from a text description */ +TLINE * +tlalloc(s) /* allocate and assign text line */ +char *s; +{ + extern char *strcpy(); + register int siz; + register TLINE *tl; + + siz = strlen(s) + 1; + if ((tl=(TLINE *)malloc(sizeof(TLINE)+siz)) == NULL || + (tl->spc=(short *)malloc(siz*sizeof(short))) == NULL) + error(SYSTEM, "out of memory in tlalloc"); + tl->spc = NULL; + tl->next = NULL; + strcpy(TLSTR(tl), s); + return(tl); +} + + +TEXT * +gettext(tm) /* get text structure for material */ register OBJREC *tm; -int lno; -int col; { +#define R (tm->oargs.farg+3) +#define D (tm->oargs.farg+6) extern char *strcpy(), *fgets(); + FVECT DxR; + double d; FILE *fp; char linbuf[512]; + TEXT *t; register int i; - register char **txt; + register TLINE *tlp; register char *s; - if (lno < 0 || col < 0) - return(NULL); - if (tm->os == NULL) { - txt = (char **)malloc(2*sizeof(char **)); - if (txt == NULL) - goto memerr; - if (tm->oargs.nsargs - tndx(tm) > 1) { /* single line */ - s = linbuf; - for (i = tndx(tm)+1; i < tm->oargs.nsargs; i++) { - strcpy(s, tm->oargs.sarg[i]); - s += strlen(s); - *s++ = ' '; - } - *--s = '\0'; - txt[0] = savqstr(linbuf); - txt[1] = NULL; - } else { /* text file */ - if ((s = getpath(tm->oargs.sarg[tndx(tm)], - libpath, R_OK)) == NULL) { - sprintf(errmsg, "cannot find text file \"%s\"", - tm->oargs.sarg[tndx(tm)]); - error(USER, errmsg); - } - if ((fp = fopen(s, "r")) == NULL) { - sprintf(errmsg, "cannot open text file \"%s\"", - s); - error(SYSTEM, errmsg); - } - for (i=0; fgets(linbuf,sizeof(linbuf),fp)!=NULL; i++) { - s = linbuf + strlen(linbuf) - 1; - if (*s == '\n') - *s = '\0'; - txt=(char **)realloc(txt,(i+2)*sizeof(char **)); - if (txt == NULL) - goto memerr; - txt[i] = savqstr(linbuf); - } - txt[i] = NULL; - fclose(fp); + if ((t = (TEXT *)tm->os) != NULL) + return(t); + /* check arguments */ + if (tm->oargs.nsargs - tndx(tm) < 1 || tm->oargs.nfargs < sndx(tm)) + objerror(tm, USER, "bad # arguments"); + if ((t = (TEXT *)malloc(sizeof(TEXT))) == NULL) + goto memerr; + /* compute vectors */ + fcross(DxR, D, R); + fcross(t->right, DxR, D); + d = DOT(D,D)/DOT(t->right,t->right); + for (i = 0; i < 3; i++) + t->right[i] *= d; + fcross(t->down, R, DxR); + d = DOT(R,R)/DOT(t->down,t->down); + for (i = 0; i < 3; i++) + t->down[i] *= d; + /* get text */ + tlp = &t->tl; + if (tm->oargs.nsargs - tndx(tm) > 1) { /* single line */ + s = linbuf; + for (i = tndx(tm)+1; i < tm->oargs.nsargs; i++) { + strcpy(s, tm->oargs.sarg[i]); + s += strlen(s); + *s++ = ' '; } - tm->os = (char *)txt; + *--s = '\0'; + tlp->next = tlalloc(linbuf); + tlp = tlp->next; + } else { /* text file */ + if ((s = getpath(tm->oargs.sarg[tndx(tm)], + libpath, R_OK)) == NULL) { + sprintf(errmsg, "cannot find text file \"%s\"", + tm->oargs.sarg[tndx(tm)]); + error(USER, errmsg); + } + if ((fp = fopen(s, "r")) == NULL) { + sprintf(errmsg, "cannot open text file \"%s\"", s); + error(SYSTEM, errmsg); + } + while (fgets(linbuf, sizeof(linbuf), fp) != NULL) { + s = linbuf + strlen(linbuf) - 1; + if (*s == '\n') + *s = '\0'; + tlp->next = tlalloc(linbuf); + tlp = tlp->next; + } + fclose(fp); } - txt = (char **)tm->os; - for (i = 0; i < lno; i++) - if (txt[i] == NULL) - break; - if ((s = txt[i]) == NULL || col >= strlen(s)) - return(NULL); - else - return(getfont(tm->oargs.sarg[fndx(tm)])->fg[s[col]]); + tlp->next = NULL; + /* get the font */ + t->f = getfont(tm->oargs.sarg[fndx(tm)]); + /* compute character spacing */ + i = sndx(tm); + d = i < tm->oargs.nfargs ? tm->oargs.farg[i] : 0.0; + i = d * 256.0; + t->tl.width = 0; + for (tlp = t->tl.next; tlp != NULL; tlp = tlp->next) { + if ((tlp->spc = (short *)malloc( + (strlen(TLSTR(tlp))+1)*sizeof(short))) == NULL) + goto memerr; + if (i < 0) + tlp->width = squeeztext(tlp->spc, TLSTR(tlp), t->f, -i); + else if (i > 0) + tlp->width = proptext(tlp->spc, TLSTR(tlp), t->f, i, 3); + else + tlp->width = uniftext(tlp->spc, TLSTR(tlp), t->f); + if (tlp->width > t->tl.width) + t->tl.width = tlp->width; + } + /* we're done */ + tm->os = (char *)t; + return(t); memerr: - error(SYSTEM, "out of memory in getglyph"); + error(SYSTEM, "out of memory in gettext"); +#undef R +#undef D } -FONT * -getfont(fname) /* return font fname */ -char *fname; +freetext(m) /* free text structures associated with m */ +OBJREC *m; { - FILE *fp; - char *pathname, *err; - int gn, ngv, gv; - register GLYPH *g; - register FONT *f; + register TEXT *tp; + register TLINE *tlp; - for (f = fontlist; f != NULL; f = f->next) - if (!strcmp(f->name, fname)) - return(f); - /* load the font file */ - if ((pathname = getpath(fname, libpath, R_OK)) == NULL) { - sprintf(errmsg, "cannot find font file \"%s\"", fname); - error(USER, errmsg); + tp = (TEXT *)m->os; + if (tp == NULL) + return; + for (tlp = tp->tl.next; tlp != NULL; tlp = tlp->next) { + free((char *)tlp->spc); + free((char *)tlp); } - f = (FONT *)calloc(1, sizeof(FONT)); - if (f == NULL) - goto memerr; - f->name = savestr(fname); - if ((fp = fopen(pathname, "r")) == NULL) { - sprintf(errmsg, "cannot open font file \"%s\"", - pathname); - error(SYSTEM, errmsg); - } - while (fscanf(fp, "%d", &gn) == 1) { /* get each glyph */ - if (gn < 0 || gn > 255) { - err = "illegal"; - goto fonterr; - } - if (f->fg[gn] != NULL) { - err = "duplicate"; - goto fonterr; - } - if (fscanf(fp, "%d", &ngv) != 1 || - ngv < 0 || ngv > 255) { - err = "bad # vertices for"; - goto fonterr; - } - g = (GLYPH *)malloc((2*ngv+1)*sizeof(GLYPH)); - if (g == NULL) - goto memerr; - f->fg[gn] = g; - *g++ = ngv; - ngv *= 2; - while (ngv--) { - if (fscanf(fp, "%d", &gv) != 1 || - gv < 0 || gv > 255) { - err = "bad vertex for"; - goto fonterr; - } - *g++ = gv; - } - } - fclose(fp); - f->next = fontlist; - return(fontlist = f); -fonterr: - sprintf(errmsg, "%s character (%d) in font file \"%s\"", - err, gn, pathname); - error(USER, errmsg); -memerr: - error(SYSTEM, "out of memory in fontglyph"); + free((char *)tp); + m->os = NULL; } +intext(p, m) /* check to see if p is in text glyph */ +FVECT p; +OBJREC *m; +{ + extern double fabs(); + register TEXT *tp; + register TLINE *tlp; + FVECT v; + double y, x; + register int i, h; + /* first, compute position in text */ + tp = gettext(m); + v[0] = p[0] - m->oargs.farg[0]; + v[1] = p[1] - m->oargs.farg[1]; + v[2] = p[2] - m->oargs.farg[2]; + x = DOT(v, tp->right); + i = sndx(m); + if (i < m->oargs.nfargs) + x *= tp->f->mwidth + 256.*fabs(m->oargs.farg[i]); + else + x *= 256.; + h = x; + i = y = DOT(v, tp->down); + if (x < 0.0 || y < 0.0) + return(0); + x -= (double)h; + y = ((i+1) - y)*256.; + /* find the line position */ + for (tlp = tp->tl.next; tlp != NULL; tlp = tlp->next) + if (--i < 0) + break; + if (tlp == NULL || h >= tlp->width) + return(0); + for (i = 0; (h -= tlp->spc[i]) >= 0; i++) + if (h < 256 && inglyph(h+x, y, + tp->f->fg[TLSTR(tlp)[i]&0xff])) + return(1); + return(0); +} + + inglyph(x, y, gl) /* (x,y) within font glyph gl? */ -double x, y; -GLYPH *gl; +double x, y; /* real coordinates in range [0,256) */ +register GLYPH *gl; { int n, ncross; int xlb, ylb; - register GLYPH *p0, *p1; + int tv; + register GORD *p0, *p1; - if (x < 0.0 || y < 0.0) + if (gl == NULL) return(0); - x *= 256.0; /* get glyph coordinates */ - y *= 256.0; - xlb = x + 0.5; - ylb = y + 0.5; - n = *gl++; /* get # of vertices */ - p0 = gl + 2*(n-1); /* connect last to first */ - p1 = gl; + xlb = x; + ylb = y; + if (gl->left > xlb || gl->right <= xlb || /* check extent */ + gl->bottom > ylb || gl->top <= ylb) + return(0); + xlb = xlb<<1 | 1; /* add 1/2 to test points... */ + ylb = ylb<<1 | 1; /* ...so no equal comparisons */ + n = gl->nverts; /* get # of vertices */ + p0 = gvlist(gl) + 2*(n-1); /* connect last to first */ + p1 = gvlist(gl); ncross = 0; /* positive x axis cross test */ while (n--) { - if ((p0[1] > ylb) ^ (p1[1] > ylb)) - if (p0[0] > xlb && p1[0] > xlb) + if ((p0[1]<<1 > ylb) ^ (p1[1]<<1 > ylb)) { + tv = p0[0]<<1 > xlb | (p1[0]<<1 > xlb) << 1; + if (tv == 03) ncross++; - else if (p0[0] > xlb || p1[0] > xlb) + else if (tv) ncross += (p1[1] > p0[1]) ^ ((p0[1]-y)*(p1[0]-x) > (p0[0]-x)*(p1[1]-y)); + } p0 = p1; p1 += 2; }