1 /*
2  * @(#)graphics.c	1.2	01/03/85
3  *
4  * Graphics routines for the SUN Gremlin picture editor.
5  *
6  * Mark Opperman (opcode@monet.BERKELEY)
7  *
8  */
9 
10 #include <suntool/tool_hs.h>
11 #include <vfont.h>
12 #include "icondata.h"
13 #include "gremlin.h"
14 
15 /* imports from main.c */
16 
17 extern error();
18 extern struct pixwin *pix_pw;
19 extern struct rect pix_size;
20 extern struct pixrect *cset_pr;
21 extern struct pixrect *scratch_pr;
22 extern ELT *cset;
23 extern Artmode;
24 extern CSIZE;
25 extern CFONT;
26 extern CsetOn;
27 extern SUN_XORIGIN;
28 extern SUN_YORIGIN;
29 
30 /* imports from display.c */
31 
32 extern minsunx, maxsunx, minsuny, maxsuny;
33 
34 /* imports from C */
35 
36 extern char *malloc();
37 
38 /* forward references */
39 
40 extern char *GRReadFontFile();
41 
42 /* symbolic font from text.c */
43 
44 extern struct pixfont *text_pf;
45 
46 /* locally defined variables */
47 
48 int charysizes[NFONTS][NSIZES];	/* Character y dimensions for each size */
49 int curve_set;			/* TRUE if spline points pre-computed */
50 
51 int linestyle;			/* Current line style */
52 int linemod;			/* Type of line (SOLID, DOTTED, ...) */
53 int linethickness;		/* 1, 2, 3 */
54 
55 char fontdir[128] = "/usr/lib/font/devsun/";
56 char stippledir[128] = "/usr/lib/font/devsun/";
57 char stippletype[32] = "cf";
58 
59 char *font_types[NFONTS] = { "R", "I", "B", "S" };
60 int font_sizes[NSIZES] = { 7, 10, 14, 24 };
61 int stipple_index[NSTIPPLES] = { 1, 3, 12, 14, 16, 19, 21, 23 };
62 
63 /* NOTE: all stipple fonts are expected to be 32 x 32 bit rasters */
64 
65 /* pointers to the stipple pixrects (16 x 16 bits) in the menu ... */
66 struct pixrect *stipple_prs[NSTIPPLES] = {
67     &stipple1_pr, &stipple2_pr, &stipple3_pr, &stipple4_pr,
68     &stipple5_pr, &stipple6_pr, &stipple7_pr, &stipple8_pr
69 };
70 
71 /* ... and the corresponding images (32 x 32 bits) from the vfont file */
72 char stipple_patterns[NSTIPPLES][128];
73 
74 /* data used in graphics2.c for drawing polygons */
75 int rasterlength;		/* real # horizontal bits in scratch_pr */
76 int bytesperline;		/* rasterlength / 8 */
77 int nlines;			/* # horizontal bits defined by scratch_pr */
78 char *fill;			/* pointer to scratch_pr image */
79 
80 /*
81  *  This matrix points to the DISPATCH data for each font/size pair
82  *  if an unsuccesful attempt is made to open a particular font/size pair,
83  *  its entry in this table is marked as -1.
84  */
85 char *font_info[NFONTS][NSIZES] = {
86     { NULL, NULL, NULL, NULL },
87     { NULL, NULL, NULL, NULL },
88     { NULL, NULL, NULL, NULL },
89     { NULL, NULL, NULL, NULL },
90 };
91 struct pixrect *char_pr;
92 
93 /* Splines use these global arrays */
94 
95 static float h[MAXPOINTS];
96 static float x[MAXPOINTS], dx[MAXPOINTS], d2x[MAXPOINTS], d3x[MAXPOINTS];
97 static float y[MAXPOINTS], dy[MAXPOINTS], d2y[MAXPOINTS], d3y[MAXPOINTS];
98 static numpoints;
99 
100 /* These are used as bit masks to create the right style lines. */
101 #define SOLID -1
102 #define DOTTED 002
103 #define DASHED 004
104 #define DOTDASHED 012
105 
106 
107 /*
108  * This routine sets the current line style.
109  */
110 GRSetLineStyle(style)
111 int style;			/* new stipple pattern for lines */
112 {
113     switch (linestyle = style) {
114 	case 1:			/* dotted */
115 	    linemod = DOTTED;
116 	    linethickness = 1;
117 	    break;
118 	case 2:			/* broken */
119 	    linemod = DOTDASHED;
120 	    linethickness = 1;
121 	    break;
122 	case 3:			/* thick */
123 	    linemod = SOLID;
124 	    linethickness = 3;
125 	    break;
126 	case 4:			/* dashed */
127 	    linemod = DASHED;
128 	    linethickness = 1;
129 	    break;
130 	case 5:			/* narrow */
131 	    linemod = SOLID;
132 	    linethickness = 1;
133 	    break;
134 	case 6:			/* medium */
135 	    linemod = SOLID;
136 	    linethickness = 2;
137 	    break;
138     }
139 }
140 
141 
142 /*
143  * This routine returns the maximum vertical size (in bits) of a character
144  * of the specified font/size.
145  */
146 GRGetCharYSize(font, size)
147 register font;			/* character font (1 - 4) */
148 register size;			/* character size (1 - 4) */
149 {
150     return(charysizes[--font][--size]);
151 }
152 
153 
154 #define pi 3.14159265359
155 #define twopi 6.28318530718
156 #define log2_10 3.321915
157 
158 
159 /*
160  * Draw arc - always to scratch_pr.
161  * Note: must check for zero radius before calling.
162  */
163 GRArc(center, cpoint, angle, style)
164 register POINT *center, *cpoint;
165 float angle;
166 register style;
167 {
168     double radius, resolution, t1, fullcircle;
169     double degreesperpoint;
170     float xs, ys, epsilon;
171     float x1, y1, x2, y2;
172     register i, extent;
173 
174     xs = cpoint->x - center->x;
175     ys = cpoint->y - center->y;
176 
177     /* calculate drawing parameters */
178 
179     radius = sqrt((double) (xs * xs + ys * ys));
180     t1 = floor(log10(radius) * log2_10);
181     resolution = pow(2.0, t1);
182     epsilon = (float) 1.0 / resolution;
183     fullcircle = ceil(twopi * resolution);
184     degreesperpoint = 360.0 / fullcircle;
185 
186     extent = (angle == 0) ? fullcircle : angle/degreesperpoint;
187 
188     GRSetLineStyle(style);
189 
190     x1 = cpoint->x;
191     y1 = cpoint->y;
192 
193     for (i=0; i<extent; ++i) {
194 	xs -= epsilon * ys;
195 	x2 = xs + center->x;
196 	ys += epsilon * xs;
197 	y2 = ys + center->y;
198 
199 	GRVector(x1, y1, x2, y2);
200 
201 	x1 = x2;
202 	y1 = y2;
203     }
204 }  /* end GRArc */;
205 
206 
207 /* This routine calculates parameteric values for use in calculating
208  * curves.  The values are an approximation of cumulative arc lengths
209  * of the curve (uses cord * length).  For additional information,
210  * see paper cited below.
211  */
212 static
213 Paramaterize(x, y, h, n)
214 float x[MAXPOINTS];
215 float y[MAXPOINTS];
216 float h[MAXPOINTS];
217 register n;
218 {
219     register i, j;
220     float t1, t2;
221     float u[MAXPOINTS];
222 
223     n = numpoints;
224 
225     for (i=1; i<=n; ++i) {
226 	u[i] = 0.0;
227 	for (j=1; j<i; ++j) {
228 	    t1 = x[j+1] - x[j];
229 	    t2 = y[j+1] - y[j];
230 	    u[i] += (float) sqrt((double) ((t1 * t1) + (t2 * t2)));
231 	}
232     }
233 
234     for (i=1; i<n; ++i)
235 	h[i] = u[i+1] - u[i];
236 }  /* end Paramaterize */
237 
238 
239 /*
240  * This routine solves for the cubic polynomial to fit a spline
241  * curve to the the points  specified by the list of values.
242  * The curve generated is periodic.  The alogrithms for this
243  * curve are from the "Spline Curve Techniques" paper cited below.
244  */
245 static
246 PeriodicSpline(h, z, dz, d2z, d3z, npoints)
247 float h[MAXPOINTS];		/* paramaterization */
248 float z[MAXPOINTS];		/* point list */
249 float dz[MAXPOINTS];		/* to return the 1st derivative */
250 float d2z[MAXPOINTS];		/* 2nd derivative */
251 float d3z[MAXPOINTS];		/* and 3rd derivative */
252 register npoints;		/* number of valid points */
253 {
254     float a[MAXPOINTS];
255     float b[MAXPOINTS];
256     float c[MAXPOINTS];
257     float d[MAXPOINTS];
258     float deltaz[MAXPOINTS];
259     float r[MAXPOINTS];
260     float s[MAXPOINTS];
261     float ftmp;
262     register i;
263 
264     /* step 1 */
265     for (i=1; i<npoints; ++i) {
266 	if (h[i] != 0)
267 	    deltaz[i] = (z[i+1] - z[i]) / h[i];
268 	else
269 	    deltaz[i] = 0;
270     }
271     h[0] = h[npoints-1];
272     deltaz[0] = deltaz[npoints-1];
273 
274     /* step 2 */
275     for (i=1; i<npoints-1; ++i) {
276 	d[i] = deltaz[i+1] - deltaz[i];
277     }
278     d[0] = deltaz[1] - deltaz[0];
279 
280     /* step 3a */
281     a[1] = 2 * (h[0] + h[1]);
282     if (a[1] == 0)
283 	return(-1);  /* 3 consecutive knots at same point */
284     b[1] = d[0];
285     c[1] = h[0];
286 
287     for (i=2; i<npoints-1; ++i) {
288 	ftmp = h[i-1];
289 	a[i] = ftmp + ftmp + h[i] + h[i] - (ftmp * ftmp)/a[i-1];
290 	    if (a[i] == 0)
291 		return(-1);  /* 3 consec knots at same point */
292 	b[i] = d[i-1] - ftmp * b[i-1]/a[i-1];
293 	c[i] = -ftmp * c[i-1]/a[i-1];
294     }
295 
296     /* step 3b */
297     r[npoints-1] = 1;
298     s[npoints-1] = 0;
299     for (i=npoints-2; i>0; --i) {
300 	r[i] = -(h[i] * r[i+1] + c[i])/a[i];
301 	s[i] = (6 * b[i] - h[i] * s[i+1])/a[i];
302     }
303 
304     /* step 4 */
305     d2z[npoints-1] = (6 * d[npoints-2] - h[0] * s[1]
306 		       - h[npoints-1] * s[npoints-2])
307 		     / (h[0] * r[1] + h[npoints-1] * r[npoints-2]
308 			+ 2 * (h[npoints-2] + h[0]));
309     for (i=1; i<npoints-1; ++i) {
310 	d2z[i] = r[i] * d2z[npoints-1] + s[i];
311     }
312     d2z[npoints] = d2z[1];
313 
314     /* step 5 */
315     for (i=1; i<npoints; ++i) {
316 	dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i+1])/6;
317 	if (h[i] != 0)
318 	    d3z[i] = (d2z[i+1] - d2z[i])/h[i];
319 	else
320 	    d3z[i] = 0;
321     }
322 
323     return(0);
324 }  /* end PeriodicSpline */
325 
326 
327 /*
328  * This routine solves for the cubic polynomial to fit a spline
329  * curve from the points specified by the list of values.  The alogrithms for
330  * this curve are from the "Spline Curve Techniques" paper cited below.
331  */
332 static
333 NaturalEndSpline(h, z, dz, d2z, d3z, npoints)
334 float h[MAXPOINTS];		/* paramaterization */
335 float z[MAXPOINTS];		/* point list */
336 float dz[MAXPOINTS];		/* to return the 1st derivative */
337 float d2z[MAXPOINTS];		/* 2nd derivative */
338 float d3z[MAXPOINTS];		/* and 3rd derivative */
339 register npoints;		/* number of valid points */
340 {
341     float a[MAXPOINTS];
342     float b[MAXPOINTS];
343     float d[MAXPOINTS];
344     float deltaz[MAXPOINTS];
345     float ftmp;
346     register i;
347 
348     /* step 1 */
349     for (i=1; i<npoints; ++i) {
350 	if (h[i] != 0)
351 	    deltaz[i] = (z[i+1] - z[i]) / h[i];
352 	else
353 	    deltaz[i] = 0;
354     }
355     deltaz[0] = deltaz[npoints-1];
356 
357     /* step 2 */
358     for (i=1; i<npoints-1; ++i) {
359 	d[i] = deltaz[i+1] - deltaz[i];
360     }
361     d[0] = deltaz[1] - deltaz[0];
362 
363     /* step 3 */
364     a[0] = 2 * (h[2] + h[1]);
365     if (a[0] == 0)		/* 3 consec knots at same point */
366 	return(-1);
367     b[0] = d[1];
368 
369     for (i=1; i<npoints-2; ++i) {
370 	ftmp = h[i+1];
371 	a[i] = ftmp + ftmp + h[i+2] + h[i+2] - (ftmp * ftmp) / a[i-1];
372 	if (a[i] == 0)		/* 3 consec knots at same point */
373 	    return(-1);
374 	b[i] = d[i+1] - ftmp * b[i-1]/a[i-1];
375     }
376 
377     /* step 4 */
378     d2z[npoints] = d2z[1] = 0;
379     for (i=npoints-1; i>1; --i) {
380 	d2z[i] = (6 * b[i-2] - h[i] *d2z[i+1])/a[i-2];
381     }
382 
383     /* step 5 */
384     for (i=1; i<npoints; ++i) {
385 	dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i+1])/6;
386 	if (h[i] != 0)
387 	    d3z[i] = (d2z[i+1] - d2z[i])/h[i];
388 	else
389 	    d3z[i] = 0;
390     }
391 
392     return(0);
393 }  /* end NaturalEndSpline */
394 
395 
396 #define PointsPerInterval 16
397 
398 
399 /*
400  * This routine computes a smooth curve through a set of points.
401  * Returns -1 if there are too many knots to draw the curve.
402  * Use GRCurve AFTER this routine to actually draw the curve.
403  * [Formerly the first half of GRCurve()]
404  *
405  * The method used is the parametric spline curve on unit knot mesh described
406  * in "Spline Curve Techniques" by Patrick Baudelaire, Robert Flegal, and
407  * Robert Sproull -- Xerox Parc.
408  */
409 GRSetCurve(pointlist)
410 POINT *pointlist;
411 {
412     register POINT *ptr;
413     register i, stat;
414 
415     /* Copy point list to array for easier access */
416     ptr = pointlist;
417     for (i=1; (!Nullpoint(ptr)); ++i) {
418 	x[i] = ptr->x;
419 	y[i] = ptr->y;
420 	ptr = PTNextPoint(ptr);
421     }
422 
423     /* Solve for derivatives of the curve at each point
424        separately for x and y (parametric). */
425 
426     numpoints = i - 1;		/* set global numpoints */
427 
428     Paramaterize(x, y, h, numpoints);
429 
430     stat = 0;
431     if ((x[1] == x[numpoints]) && (y[1] == y[numpoints])) { /* closed curve */
432 	stat |= PeriodicSpline(h, x, dx, d2x, d3x, numpoints);
433 	stat |= PeriodicSpline(h, y, dy, d2y, d3y, numpoints);
434     }
435     else {
436 	stat |= NaturalEndSpline(h, x, dx, d2x, d3x, numpoints);
437 	stat |= NaturalEndSpline(h, y, dy, d2y, d3y, numpoints);
438     }
439 
440     curve_set = 1;		/* indicates that paramterization is done */
441     return(stat);
442 }
443 
444 
445 /*
446  * This routine displays a smooth curve through a set of points.  The
447  * method used is the parametric spline curve on unit knot mesh described
448  * in "Spline Curve Techniques" by Patrick Baudelaire, Robert Flegal, and
449  * Robert Sproull -- Xerox Parc.
450  * [formerly the second half of GRCurve()]
451  *
452  * Uses the data computed first by GRSetCurve().
453  * GRSetCurve() MUST be called before this routine and have returned a ZERO.
454  */
455 GRCurve(style)
456 int style;
457 {
458     float t, t2, t3, xinter, yinter;
459     float x1, y1, x2, y2;
460     register j, k;
461 
462     GRSetLineStyle(style);
463 
464     x1 = x[1];
465     y1 = y[1];
466 
467     /* generate the curve using the information from GRSetCurve() and
468        PointsPerInterval vectors between each specified knot. */
469 
470     for (j=1; j<numpoints; ++j) {
471 	for (k=0; k<=PointsPerInterval; ++k) {
472 	    t = (float) k * h[j] / (float) PointsPerInterval;
473 	    t2 = t * t;
474 	    t3 = t2 * t;
475 	    x2 = x[j] + t * dx[j] + t2 * d2x[j]/2.0 + t3 * d3x[j]/6.0;
476 	    y2 = y[j] + t * dy[j] + t2 * d2y[j]/2.0 + t3 * d3y[j]/6.0;
477 
478 	    GRVector(x1, y1, x2, y2);
479 
480 	    x1 = x2;
481 	    y1 = y2;
482 	}
483     }
484 }  /* end GRCurve */
485 
486 
487 /*
488  * This routine clears the Gremlin pix subwindow or current set
489  * pixrect image as specified in the mask.
490  */
491 GRClear(mask)
492 register mask;
493 {
494     if (mask & pixmask)
495 	pw_writebackground(pix_pw, 0, 0, 2000, 2000, PIX_SRC);
496 
497     if (mask & csetmask)
498 	pr_rop(cset_pr, 0, 0, 2000, 2000, PIX_SRC, NULL, 0, 0);
499 }  /* end GRClear */
500 
501 
502 /*
503  *  Display justification of TEXT element.
504  */
505 GRDisplayJustify(elt)
506 register ELT *elt;
507 {
508     register POINT *point;
509     register x, y, length, ysize;
510 
511     ysize = GRGetCharYSize(elt->brushf, elt->size);
512     length = GRFontStrlen(elt->textpt, elt->brushf, elt->size);
513     point = PTNextPoint(elt->ptlist);	/* lower left corner of text */
514     x = dbx_to_win(point->x);
515     y = dby_to_win(point->y);
516 
517     switch (elt->type) {
518 	case TOPLEFT:
519 	    y -= ysize;
520 	    break;
521 	case TOPCENT:
522 	    y -= ysize;
523 	    x += (length >> 1);
524 	    break;
525 	case TOPRIGHT:
526 	    y -= ysize;
527 	    x += length;
528 	    break;
529 	case CENTLEFT:
530 	    y -= (ysize >> 1);
531 	    break;
532 	case CENTCENT:
533 	    y -= (ysize >> 1);
534 	    x += (length >> 1);
535 	    break;
536 	case CENTRIGHT:
537 	    y -= (ysize >> 1);
538 	    x += length;
539 	    break;
540 	case BOTLEFT:
541 	    break;
542 	case BOTCENT:
543 	    x += (length >> 1);
544 	    break;
545 	case BOTRIGHT:
546 	    x += length;
547 	    break;
548     }
549 
550     pw_write(pix_pw, x - 2, y - 2, 5, 5, PIX_SRC ^ PIX_DST, &dot_pr, 0, 0);
551     pr_rop(cset_pr, x - 2, y - 2, 5, 5, PIX_SRC ^ PIX_DST, &dot_pr, 0, 0);
552 }
553 
554 
555 /*
556  * This routine displays a point (layed down by the user) in the
557  * pix subwindow.
558  */
559 GRDisplayPoint(dbx, dby, number)
560 float dbx, dby;			/* data base coordinates */
561 register number;		/* point number */
562 {
563     register x, y;
564     char numbuf[5];
565 
566     x = dbx_to_win(dbx);
567     y = dby_to_win(dby);
568 
569     if (Artmode)
570 	pw_write(pix_pw, x-1, y-1, 3, 3, PIX_SRC ^ PIX_DST,
571 				    &littlepoint_pr, 3, 2);
572     else {
573 	pw_write(pix_pw, x-3, y-3, 7, 7, PIX_SRC ^ PIX_DST,
574 				    &littlepoint_pr, 1, 7);
575 	(void) sprintf(numbuf, "%d", number+1);
576 	pw_text(pix_pw, x+5, y+3, PIX_SRC^PIX_DST, text_pf, numbuf);
577     }
578 }  /* end GRDisplayPoint */
579 
580 
581 /*
582  * This routine erases the specified point.
583  */
584 GRErasePoint(dbx, dby, number)
585 float dbx, dby;
586 register number;
587 {
588     GRDisplayPoint(dbx, dby, number);
589 }  /* end GRErasePoint */
590 
591 
592 /*
593  * This routine clears all points in plist.
594  */
595 GRBlankPoints(plist)
596 register POINT *plist;
597 {
598     register i = 0;
599 
600     while (!Nullpoint(plist)) {
601 	GRErasePoint(plist->x, plist->y, i++);
602 	plist = PTNextPoint(plist);
603     }
604 }  /* end GRBlankPoints */
605 
606 
607 /*
608  * This routine displays the grid.
609  */
610 GRDisplayGrid()
611 {
612     pw_replrop(pix_pw, 0, 0, 2000, 2000, PIX_SRC ^ PIX_DST,
613 						&replgrid32_pr, 0, 0);
614 }  /* end GRDisplayGrid */
615 
616 
617 /*
618  * This routine erases the grid.
619  */
620 GRBlankGrid()
621 {
622     GRDisplayGrid();
623 }  /* end GRBlankGrid */
624 
625 
626 /*
627  * Flash current set display.
628  */
629 GRCurrentSet()
630 {
631     if (DBNullelt(cset))
632 	return;
633 
634     pw_write(pix_pw, 0, 0, pix_size.r_width, pix_size.r_height,
635 	PIX_SRC ^ PIX_DST, cset_pr, 0, 0);
636 
637     CsetOn = !CsetOn;
638 }
639 
640 
641 /*
642  * Make current set on.
643  */
644 GRCurrentSetOn()
645 {
646     if (!CsetOn)
647 	GRCurrentSet();
648 }
649 
650 
651 /*
652  * Make current set off.
653  */
654 GRCurrentSetOff()
655 {
656     if (CsetOn)
657 	GRCurrentSet();
658 }
659 
660 
661 /*
662  * Return TRUE if font file exists and is readable.
663  */
664 GRfontfound(font, size)
665 register font, size;
666 {
667     return(font_info[font-1][size-1] != (char *) -1);
668 }
669 
670 
671 /*
672  * Open the default font file on startup.
673  */
674 GRFontInit()
675 {
676     /* create memory pixrect template for displaying text with GRPutText() */
677     if ((char_pr = mem_create(1, 1, 1)) == NULL) {
678 	printf("GRFontInit: can't create char_pr\n");
679 	exit(1);
680     }
681 
682     GROpenFont(CFONT, CSIZE);
683     GRStippleInit();
684 }  /* end GRFontInit */
685 
686 
687 /*
688  * Initialize stipple patterns from font file.
689  * Big assumption: all stipples are defined by 32 x 32 bit patterns.
690  * All fonts do not contain exactly 32 rows of 4 bytes - this is ok -
691  * Fonts wider than 32 bits will be clipped.
692  */
693 GRStippleInit()
694 {
695     register struct mpr_data *mpr_data;
696     register char *from, *to;
697     register char *fbase;
698     register i, j, k;
699     struct dispatch *dispatch, *dstart;
700     int width, height, bytewidth;
701     char *stipple_info;
702     char name[128];
703 
704     (void) sprintf(name, "%s%s.0", stippledir, stippletype);
705 
706     if ((stipple_info = GRReadFontFile(name)) == (char *) -1) {
707 	/*
708 	 * use default stipple pixrects since we can't read the
709 	 * user specified stipple font file.
710 	 * copy stipple pixrects to stipple_patterns for display
711 	 */
712 	for (i=0; i<NSTIPPLES; i++)
713 	    GRCopyStipple(i);
714 
715 	return;
716     }
717 
718     dstart = (struct dispatch *) (stipple_info + sizeof(struct header));
719     fbase = (char *) ((char *) dstart + NUM_DISPATCH * sizeof(struct dispatch));
720 
721     for (i=0; i<NSTIPPLES; i++) {
722 	mpr_data = (struct mpr_data *) stipple_prs[i]->pr_data;
723 	dispatch = dstart + stipple_index[i];
724 	if (dispatch->nbytes != 0) {
725 	    width = dispatch->left + dispatch->right;
726 	    height = dispatch->up + dispatch->down;
727 	    bytewidth = (width + 7) >> 3;
728 	    if (bytewidth > 4)		/* force size constraint */
729 		bytewidth = 4;		/* pattern screwed up if ever > 4 */
730 
731 	    from = (char *) ((char *) fbase + dispatch->addr);
732 	    to = stipple_patterns[i];
733 
734 	    for (j=1; j<=height; j++) {	/* copy font entry to known location */
735 		for (k=1; k<=bytewidth; k++)
736 		    *to++ = *from++;
737 		for ( ;k<=4; k++)
738 		    *to++ = '\0';
739 	    }
740 
741 	    for ( ; j<=32; j++)		/* fix up any non- 32 x 32 font */
742 		for (k=1; k<=4; k++)
743 		    *to++ = '\0';
744 
745 	    /* copy vfont stipple to stipple pixrect for menu display */
746 	    /* can only display a 16 x 16 menu icon */
747 	    from = stipple_patterns[i];
748 	    to = (char *) mpr_data->md_image;
749 	    for (j=0; j<16; j++) {
750 		*to++ = *from++;
751 		*to++ = *from++;
752 		from += 2;
753 	    }
754 	}
755 	else {
756 	    (void) sprintf(name, "stipple index=%d not defined",
757 							    stipple_index[i]);
758 	    error(name);
759 
760 	    /* copy stipple pixrect to stipple_patterns for display */
761 	    GRCopyStipple(i);
762 	}
763     }
764 
765     /* bit maps are all in core now */
766     free(stipple_info);
767 
768     /* set up parameters for drawing polygons */
769     mpr_data = (struct mpr_data *) scratch_pr->pr_data;
770     bytesperline = mpr_data->md_linebytes;
771     fill = (char *) mpr_data->md_image;
772     rasterlength = bytesperline << 3;
773     nlines = scratch_pr->pr_size.x;
774 } /* end GRStippleInit */;
775 
776 
777 /*
778  * Copy the stipple bit map image as defined in the menu pixrect
779  * to the bit maps used for drawing polygons.  The pixrect bit map
780  * is 16 x 16 bits and the target bit map is 32 x 32 bits.  The
781  * image is expanded appropriately.
782  */
783 GRCopyStipple(index)
784 int index;
785 {
786     register struct mpr_data *mpr_data;
787     register char *from, *to;
788     register i, j;
789 
790     mpr_data = (struct mpr_data *) stipple_prs[index]->pr_data;
791     from = (char *) mpr_data->md_image;
792     to = stipple_patterns[index];
793 
794     for (i=0; i<16; i++) {
795 	j = i << 2;
796 	to[j]   = to[j+2] = to[j+64] = to[j+66] = *from++;
797 	to[j+1] = to[j+3] = to[j+65] = to[j+67] = *from++;
798     }
799 }
800 
801 
802 /*
803  * Open a font file for use by first reading it into memory.
804  * If the file is read successfully, the appropriate entry in
805  * font_info[] is set to point to its memory image.  If the
806  * file cannot be opened or there is a error in reading the
807  * file, set the font_info[] entry to -1.
808  */
809 GROpenFont(font, size)
810 register font;		/* font is 1 to 4 */
811 register size;		/* size is 1 to 4 */
812 {
813     char name[128];
814 
815     if (font_info[--font][--size] != NULL)	/* already tried to open */
816 	return;
817 
818     sprintf(name, "%s%s.%d", fontdir, font_types[font], font_sizes[size]);
819 
820     if ((font_info[font][size] = GRReadFontFile(name)) == (char *) -1)
821 	return;
822 
823     /* height of this font/size combination */
824     charysizes[font][size] = ((struct header *) font_info[font][size])->maxy;
825 }  /* end GROpenFont */
826 
827 
828 /*
829  * Read a font file into memory and return a pointer to its
830  * memory image, or -1 if any error occurs.
831  */
832 char *
833 GRReadFontFile(file)
834 char *file;
835 {
836     char *image;			/* pointer to font memory image */
837     char msg[128];
838     struct header header;
839     int fd, filesize;
840 
841     if ((fd = open(file, 0)) < 0) {
842 	sprintf(msg, "can't open font file: %s", file);
843 	error(msg);
844 	return((char *) -1);
845     }
846 
847     if (read(fd, &header, sizeof(struct header)) != sizeof(struct header)) {
848 	sprintf(msg, "can't read font header: %s\n", file);
849 	error(msg);
850 	return((char *) -1);
851     }
852 
853     if (header.magic != VFONT_MAGIC) {
854 	sprintf(msg, "bad magic number %o in font file\n", header.magic);
855 	error(msg);
856 	return((char *) -1);
857     }
858 
859     filesize = (sizeof(struct header) +
860 		sizeof(struct dispatch) * NUM_DISPATCH + header.size);
861 
862     if ((image = malloc(filesize)) == NULL) {
863 	error("not enough memory for font file");
864 	return((char *) -1);
865     }
866 
867     lseek(fd, (long) 0, 0);
868 
869     if (read(fd, image, filesize) != filesize) {
870 	error("can't read font file");
871 	return((char *) -1);
872     }
873 
874     close(fd);
875     return(image);
876 }  /* end GRReadFontFile */
877 
878 
879 /*
880  * Determine pixel length of string s in font and size.
881  */
882 GRFontStrlen(text, font, size)
883 char *text;
884 int font;		/* 1 - 4 */
885 int size;		/* 1 - 4 */
886 {
887     register struct dispatch *dispatch, *start;
888     register length, spacewidth;
889 
890     if (*text == '\0')
891 	return(0);
892 
893     if (font_info[font-1][size-1] == NULL)	/* not open yet */
894 	GROpenFont(font, size);
895 
896     if (!GRfontfound(font, size))		/* unreadable font */
897 	return(0);
898 
899     start = (struct dispatch *) (font_info[font-1][size-1] +
900 						sizeof(struct header));
901     spacewidth = font_sizes[size-1] * (120.0 / 216.0) + 0.5;
902     length = 0;
903     while (*text != '\0') {
904 	dispatch = start + (*text);
905 
906 	if (*text == ' ')
907 	    length += spacewidth;
908 	else if (dispatch->nbytes != 0)
909 	    length += dispatch->width;
910 
911 	text++;
912     }
913 
914     return(length);
915 }
916 
917 /*
918  * Display text string of font/size at position pos.
919  */
920 GRPutText(text, font, size, pos)
921 char *text;
922 int font, size;
923 POINT *pos;
924 {
925     register struct dispatch *dispatch, *dstart;
926     register struct mpr_data *mpr_data;
927     register char *fbase;
928     register width, height, spacewidth;
929     int x, y;
930 
931     if (font_info[font-1][size-1] == NULL)
932 	GROpenFont(font, size);
933 
934     if (!GRfontfound(font, size))
935 	return;
936 
937     dstart = (struct dispatch *) (font_info[font-1][size-1] +
938 						    sizeof(struct header));
939     fbase = (char *) ((char *) dstart + NUM_DISPATCH * sizeof(struct dispatch));
940 
941     x = dbx_to_win(pos->x);
942     y = dby_to_win(pos->y);
943 
944     /* define region of screen to be drawn with text */
945     minsunx = x;
946     maxsuny = y + 8;	/* catch descenders */
947     minsuny = y - GRGetCharYSize(font, size);
948 
949     spacewidth = font_sizes[size-1] * (120.0 / 216.0) + 0.5;
950     mpr_data = (struct mpr_data *) char_pr->pr_data;
951     while (*text != '\0') {
952 	dispatch = dstart + (*text);
953 
954 	if (*text == ' ')
955 	    x += spacewidth;
956 	else if (dispatch->nbytes != 0) {
957 	    mpr_data->md_image = (short *) ((char *) fbase + dispatch->addr);
958 	    char_pr->pr_size.x = width = dispatch->left + dispatch->right;
959 	    char_pr->pr_size.y = height = dispatch->up + dispatch->down;
960 	    mpr_data->md_linebytes = ((width + 15) >> 3) & ~1;
961 
962 	    if (*text != ' ')
963 		pr_rop(scratch_pr, x - dispatch->left, y - dispatch->up,
964 			    width, height, PIX_SRC ^ PIX_DST, char_pr, 0, 0);
965 	    x += dispatch->width;
966 	}
967 
968 	text++;
969     }
970 
971     maxsunx = x;
972 } /* end GRPutText */;
973 
974 
975 /*
976  * Set the actual positioning point for text with the justify, font, and
977  * size attributes.  Point is a pointer to the POINT layed down by the user.
978  * Pos is a pointer to the returned positioning POINT used in a subsequent
979  * call to GRPutText().
980  */
981 GRSetTextPos(text, justify, font, size, point, pos)
982 char *text;
983 int justify, font, size;
984 POINT *point, *pos;
985 {
986     register length;
987     register charysize;
988 
989     charysize = GRGetCharYSize(font, size);
990     length = GRFontStrlen(text, font, size);
991 
992     switch (justify) {
993 	case BOTLEFT:
994 	    pos->x = point->x;
995 	    pos->y = point->y;
996 	    break;
997 	case BOTCENT:
998 	    pos->x = point->x - (length / 2);
999 	    pos->y = point->y;
1000 	    break;
1001 	case BOTRIGHT:
1002 	    pos->x = point->x - length;
1003 	    pos->y = point->y;
1004 	    break;
1005 	case CENTLEFT:
1006 	    pos->x = point->x;
1007 	    pos->y = point->y - (charysize / 2);
1008 	    break;
1009 	case CENTCENT:
1010 	    pos->x = point->x - (length / 2);
1011 	    pos->y = point->y - (charysize / 2);
1012 	    break;
1013 	case CENTRIGHT:
1014 	    pos->x = point->x - length;
1015 	    pos->y = point->y - (charysize / 2);
1016 	    break;
1017 	case TOPLEFT:
1018 	    pos->x = point->x;
1019 	    pos->y = point->y - charysize;
1020 	    break;
1021 	case TOPCENT:
1022 	    pos->x = point->x - (length / 2);
1023 	    pos->y = point->y - charysize;
1024 	    break;
1025 	case TOPRIGHT:
1026 	    pos->x = point->x - length;
1027 	    pos->y = point->y - charysize;
1028 	    break;
1029     }
1030 }
1031