1 /*
2  * plotRastUtils.c --
3  *
4  * This file contains several procedures for manipulating rasters.
5  * The procedures are used to create bitmaps to directly drive
6  * printers such as the black-and-white Versatec.  For example,
7  * the procedures draw stippled areas and raster-ize text strings
8  * using fonts.
9  *
10  *     *********************************************************************
11  *     * Copyright (C) 1985, 1990 Regents of the University of California. *
12  *     * Permission to use, copy, modify, and distribute this              *
13  *     * software and its documentation for any purpose and without        *
14  *     * fee is hereby granted, provided that the above copyright          *
15  *     * notice appear in all copies.  The University of California        *
16  *     * makes no representations about the suitability of this            *
17  *     * software for any purpose.  It is provided "as is" without         *
18  *     * express or implied warranty.  Export of this software outside     *
19  *     * of the United States of America may require an export license.    *
20  *     *********************************************************************
21  */
22 
23 #ifndef lint
24 static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/plot/plotRutils.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
25 #endif  /* not lint */
26 
27 #include <stdio.h>
28 #include <string.h>
29 #include <errno.h>
30 
31 #include "utils/magic.h"
32 #include "utils/geometry.h"
33 #include "utils/geofast.h"
34 #include "tiles/tile.h"
35 #include "utils/hash.h"
36 #include "database/database.h"
37 #include "utils/malloc.h"
38 #include "plot/plotInt.h"
39 #include "textio/textio.h"
40 #include "utils/utils.h"
41 
42 extern double sqrt();
43 
44 int rasFileByteCount = 0;
45 /* A solid black stipple: */
46 
47 Stipple PlotBlackStipple =
48 {
49     0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
50     0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
51     0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
52     0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
53 };
54 
55 /* The following two arrays are used to select a range of bits within
56  * a word.  The first array selects all the bits at or to the left of
57  * the given bit-position. "To the left" means in a lower-numbered
58  * byte (according to byte order) or in a more significant bit of the
59  * same byte.  The second array selects the bits at or to the right of
60  * the given bit-position.  Bit position 0 is considered to be the
61  * leftmost bit of the word;  it's the highest-order bit in the 0th
62  * byte.
63  */
64 
65 static unsigned int leftBits[32] =
66 {
67     0x00000080, 0x000000c0, 0x000000e0, 0x000000f0,
68     0x000000f8, 0x000000fc, 0x000000fe, 0x000000ff,
69     0x000080ff, 0x0000c0ff, 0x0000e0ff, 0x0000f0ff,
70     0x0000f8ff, 0x0000fcff, 0x0000feff, 0x0000ffff,
71     0x0080ffff, 0x00c0ffff, 0x00e0ffff, 0x00f0ffff,
72     0x00f8ffff, 0x00fcffff, 0x00feffff, 0x00ffffff,
73     0x80ffffff, 0xc0ffffff, 0xe0ffffff, 0xf0ffffff,
74     0xf8ffffff, 0xfcffffff, 0xfeffffff, 0xffffffff
75 };
76 static unsigned int rightBits[32] =
77 {
78     0xffffffff,	0xffffff7f, 0xffffff3f, 0xffffff1f,
79     0xffffff0f, 0xffffff07, 0xffffff03, 0xffffff01,
80     0xffffff00,	0xffff7f00, 0xffff3f00, 0xffff1f00,
81     0xffff0f00, 0xffff0700, 0xffff0300, 0xffff0100,
82     0xffff0000,	0xff7f0000, 0xff3f0000, 0xff1f0000,
83     0xff0f0000, 0xff070000, 0xff030000, 0xff010000,
84     0xff000000,	0x7f000000, 0x3f000000, 0x1f000000,
85     0x0f000000, 0x07000000, 0x03000000, 0x01000000
86 };
87 
88 /* The following arrray selects a single bit within a word.  The
89  * zeroth entry selects the bit that will be leftmost in the plot.
90  */
91 static unsigned int singleBit[32] =
92 {
93     0x00000080, 0x00000040, 0x00000020, 0x00000010,
94     0x00000008, 0x00000004, 0x00000002, 0x00000001,
95     0x00008000, 0x00004000, 0x00002000, 0x00001000,
96     0x00000800, 0x00000400, 0x00000200, 0x00000100,
97     0x00800000, 0x00400000, 0x00200000, 0x00100000,
98     0x00080000, 0x00040000, 0x00020000, 0x00010000,
99     0x80000000, 0x40000000, 0x20000000, 0x10000000,
100     0x08000000, 0x04000000, 0x02000000, 0x01000000
101 };
102 
103 RasterFont *PlotFontList;	/* Linked list of all fonts that have
104 				 * been read in so far.
105 				 */
106 
107 /*
108  * ----------------------------------------------------------------------------
109  *
110  * PlotRastInit --
111  *
112  * 	Initialize data structures related to rasters.
113  *
114  * Results:
115  *	None.
116  *
117  * Side effects:
118  *	All this procedure does is to swap bytes in the stipple
119  *	masks if we're running on a non-VAX.
120  *
121  * ----------------------------------------------------------------------------
122  */
123 
124 void
PlotRastInit()125 PlotRastInit()
126 {
127 #ifdef WORDS_BIGENDIAN
128     int i;
129 
130     for (i = 0; i < 32; i++)
131     {
132 	leftBits[i] = PlotSwapBytes(leftBits[i]);
133 	rightBits[i] = PlotSwapBytes(rightBits[i]);
134 	singleBit[i] = PlotSwapBytes(singleBit[i]);
135     }
136 #endif /* WORDS_BIGENDIAN */
137 }
138 
139 /*
140  * ----------------------------------------------------------------------------
141  *
142  * PlotNewRaster --
143  *
144  * 	Allocate and initialize a new raster structure.
145  *
146  * Results:
147  *	The return value is a pointer to the new raster object.
148  *
149  * Side effects:
150  *	Memory is allocated.
151  *
152  * ----------------------------------------------------------------------------
153  */
154 
155 Raster *
PlotNewRaster(height,width)156 PlotNewRaster(height, width)
157     int height;			/* Raster's height in pixels.  Should generally
158 				 * be a multiple of 16.
159 				 */
160     int width;			/* Raster's width in pixels.  Should generally
161 				 * be a multiple of 32 bits.
162 				 */
163 {
164     Raster *new;
165 
166     new = (Raster *) mallocMagic(sizeof(Raster));
167     new->ras_width = width;
168     new->ras_intsPerLine = (width+31)/32;
169     new->ras_bytesPerLine = new->ras_intsPerLine*4;
170     new->ras_height = height;
171     new->ras_bits = (int *) mallocMagic(
172 	    (unsigned) (height * new->ras_intsPerLine * 4));
173 
174     return new;
175 }
176 
177 /*
178  * ----------------------------------------------------------------------------
179  *
180  * PlotFreeRaster --
181  *
182  * 	Frees up the memory associated with an existing raster structure.
183  *
184  * Results:
185  *	None.
186  *
187  * Side effects:
188  *	The storage associated with raster is returned to the allocator.
189  *
190  * ----------------------------------------------------------------------------
191  */
192 
193 void
PlotFreeRaster(raster)194 PlotFreeRaster(raster)
195     Raster *raster;		/* Raster whose memory is to be freed.  Should
196 				 * have been created with PlotNewRaster.
197 				 */
198 {
199     if (raster == NULL) return;
200     freeMagic((char *) raster->ras_bits);
201     freeMagic((char *) raster);
202 }
203 
204 /*
205  * ----------------------------------------------------------------------------
206  *
207  * PlotClearRaster --
208  *
209  * 	This procedure clears out an area of the raster.
210  *
211  * Results:
212  *	None.
213  *
214  * Side effects:
215  *	The area of the raster indicated by "area" is set to all zeroes.
216  *
217  * ----------------------------------------------------------------------------
218  */
219 
220 void
PlotClearRaster(raster,area)221 PlotClearRaster(raster, area)
222     Raster *raster;			/* Raster that's to be cleared. */
223     Rect *area;				/* Area to be cleared, in raster
224 					 * coords.  NULL means clear the
225 					 * whole raster.
226 					 */
227 {
228     int *left, *right, *cur;
229     int leftMask, rightMask, line;
230 
231     if (area == NULL)
232     {
233 	bzero((char *) raster->ras_bits,
234 		raster->ras_bytesPerLine * raster->ras_height);
235 	return;
236     }
237 
238     /* Compute the address of the leftmost word in the topmost line
239      * to be cleared, and the rightmost word in the topmost line to
240      * be cleared.
241      */
242 
243     left = raster->ras_bits +
244 	((raster->ras_height-1) - area->r_ytop)*raster->ras_intsPerLine;
245     right = left + area->r_xtop/32;
246     left += area->r_xbot/32;
247 
248     /* Divide the x-span of the area into three parts:  the leftmost
249      * word, of which only the rightmost bits are cleared, the
250      * rightmost word, of which only the leftmost bits are cleared,
251      * and the middle section, in which all bits of each word are
252      * cleared.  Compute masks that determine which bits of the end
253      * words will be cleared.  There's a special case when the left
254      * and right ends are in the same word.
255      */
256 
257     leftMask = rightBits[area->r_xbot&037];
258     rightMask = leftBits[area->r_xtop&037];
259     if (left == right)
260 	leftMask &= rightMask;
261 
262     /* Clear the area one raster line at a time, top to bottom. */
263 
264     for (line = area->r_ytop; line >= area->r_ybot; line -= 1)
265     {
266 	/* Clear the leftmost word on this line. */
267 
268 	*left &= ~leftMask;
269 
270 	if (left != right)
271 	{
272 	    /* Clear the center of the line. */
273 
274 	    for (cur = left+1; cur < right; cur += 1)
275 		*cur = 0;
276 
277 	    /* Clear the rightmost word on this line. */
278 
279 	    *cur &= ~rightMask;
280 	}
281 
282 	left += raster->ras_intsPerLine;
283 	right += raster->ras_intsPerLine;
284     }
285 }
286 
287 /*
288  * ----------------------------------------------------------------------------
289  *
290  *  PlotPolyRaster --
291  *
292  *	Fill a polygonal raster area, given the area of a triangular
293  *	tile (in pixel coordinates), information about what side and
294  *	direction are filled, and the area to clip to (also in raster
295  *	coordinates).  This is *not* a general-purpose polygon-filling
296  *	routine.  It can only handle clipped right triangles.
297  *
298  *  Results:
299  *  	None.
300  *
301  * Side effects:
302  *	None.
303  *
304  * ----------------------------------------------------------------------------
305  */
306 
307 void
PlotPolyRaster(raster,tileArea,clipArea,dinfo,stipple)308 PlotPolyRaster(raster, tileArea, clipArea, dinfo, stipple)
309     Raster *raster;	/* Pointer to raster whose bits are
310 			 * to be filled in.
311 			 */
312     Rect *tileArea;	/* Area of split tile, in pixel coordinates */
313     Rect *clipArea;	/* Area to clip, in pixel coordinates */
314     TileType dinfo;	/* Split tile side and direction information */
315     Stipple stipple;	/* Stipple pattern to be used to fill
316 			 * in the raster area.
317 			 */
318 {
319     int *cur, *rasleft, *tbase, *right, *left;
320     int leftMask, rightMask, curStipple;
321     int line, width, height, locleft, locright;
322     Rect area;
323 
324     /* Compute the address of the leftmost word in the topmost line
325      * to be filled, and the rightmost word in the topmost line to
326      * be filled.
327      */
328 
329     area = *tileArea;
330     GEOCLIP(&area, clipArea);
331 
332     /* Ensure that we have not been clipped out of existence */
333     if (area.r_xbot > area.r_xtop) return;
334     if (area.r_ybot >= area.r_ytop) return;
335 
336     rasleft = raster->ras_bits +
337 	((raster->ras_height-1) - area.r_ytop)*raster->ras_intsPerLine;
338     width = tileArea->r_xtop - tileArea->r_xbot;
339     height = tileArea->r_ytop - tileArea->r_ybot;
340 
341     /* Process the stippled area one raster line at a time, top to bottom. */
342 
343     if (dinfo & TT_SIDE)
344     {
345 	locright = area.r_xtop;
346 	tbase = rasleft + area.r_xtop / 32;	/* base is on right */
347     }
348     else
349     {
350 	locleft = area.r_xbot;
351 	tbase = rasleft + area.r_xbot / 32;	/* base is on left */
352     }
353 
354     for (line = area.r_ytop; line >= area.r_ybot; line -= 1)
355     {
356 	if (dinfo & TT_SIDE)
357 	{
358 	    if (dinfo & TT_DIRECTION)
359 		locleft = tileArea->r_xbot + (((tileArea->r_ytop - line) * width)
360 			/ height);
361 	    else
362 		locleft = tileArea->r_xbot + (((line - tileArea->r_ybot) * width)
363 			/ height);
364 	    right = tbase;
365 	    left = rasleft + locleft / 32;
366 	}
367 	else
368 	{
369 	    if (dinfo & TT_DIRECTION)
370 		locright = tileArea->r_xbot + (((tileArea->r_ytop - line) * width)
371 			/ height);
372 	    else
373 		locright = tileArea->r_xbot + (((line - tileArea->r_ybot) * width)
374 			/ height);
375 	    right = rasleft + locright / 32;
376 	    left = tbase;
377 	}
378 	if (left > right) continue;
379 
380 	leftMask = rightBits[locleft & 037];
381 	rightMask = leftBits[locright & 037];
382 	if (left == right)
383 	    leftMask &= rightMask;
384 
385 	curStipple = stipple[(-line)&017];
386 
387 	/* Fill the leftmost word on this line. */
388 
389 	*left |= curStipple & leftMask;
390 
391 	if (left != right)
392 	{
393 	    /* Fill the center of the line. */
394 
395 	    for (cur = left+1; cur < right; cur += 1)
396 		*cur |= curStipple;
397 
398 	    /* Fill the rightmost word on this line. */
399 
400 	    *cur |= curStipple & rightMask;
401 	}
402 
403 	rasleft += raster->ras_intsPerLine;
404 	tbase += raster->ras_intsPerLine;
405     }
406 }
407 
408 /*
409  * ----------------------------------------------------------------------------
410  *
411  * PlotFillRaster --
412  *
413  * 	Given a raster and an area, this procedure fills the given area
414  *	of the raster with a particular stipple pattern.
415  *
416  * Results:
417  *	None.
418  *
419  * Side effects:
420  *	None.
421  *
422  * ----------------------------------------------------------------------------
423  */
424 
425 void
PlotFillRaster(raster,area,stipple)426 PlotFillRaster(raster, area, stipple)
427     Raster *raster;		/* Pointer to raster whose bits are
428 					 * to be filled in.
429 					 */
430     Rect *area;		/* Area to be filled in pixel coords.
431 					 * This is an inclusive area:  it
432 					 * includes the boundary pixels.  The
433 					 * caller must ensure that it is
434 					 * clipped to the raster area.
435 					 */
436     Stipple stipple;			/* Stipple pattern to be used to fill
437 					 * in the raster area.
438 					 */
439 {
440     int *left, *cur, line;
441     int *right;
442     int leftMask, rightMask, curStipple;
443 
444     /* Compute the address of the leftmost word in the topmost line
445      * to be filled, and the rightmost word in the topmost line to
446      * be filled.
447      */
448 
449     left = raster->ras_bits +
450 	((raster->ras_height-1) - area->r_ytop)*raster->ras_intsPerLine;
451     right = left + area->r_xtop/32;
452     left += area->r_xbot/32;
453 
454     /* Divide the x-span of the area into three parts:  the leftmost
455      * word, of which only the rightmost bits are modified, the
456      * rightmost word, of which only the leftmost bits are modified,
457      * and the middle section, in which all bits of each word are
458      * modified.  Compute masks that determine which bits of the end
459      * words will be modified.  There's a special case when the left
460      * and right ends are in the same word.
461      */
462 
463     leftMask = rightBits[area->r_xbot&037];
464     rightMask = leftBits[area->r_xtop&037];
465     if (left == right)
466 	leftMask &= rightMask;
467 
468     /* Process the stippled area one raster line at a time, top to bottom. */
469 
470     for (line = area->r_ytop; line >= area->r_ybot; line -= 1)
471     {
472 	curStipple = stipple[(-line)&017];
473 
474 	/* Fill the leftmost word on this line. */
475 
476 	*left |= curStipple & leftMask;
477 
478 	if (left != right)
479 	{
480 	    /* Fill the center of the line. */
481 
482 	    for (cur = left+1; cur < right; cur += 1)
483 		*cur |= curStipple;
484 
485 	    /* Fill the rightmost word on this line. */
486 
487 	    *cur |= curStipple & rightMask;
488 	}
489 
490 	left += raster->ras_intsPerLine;
491 	right += raster->ras_intsPerLine;
492     }
493 }
494 
495 /*
496  * ----------------------------------------------------------------------------
497  *
498  * PlotDumpRaster  --
499  *
500  * 	Writes out the contents of the given raster to the given file,
501  *	in binary format.
502  *
503  * Results:
504  *	Returns 0 if all was well.  Returns non-zero if there was
505  *	an I/O error.  In this event, this procedure prints an
506  *	error message before returning.
507  *
508  * Side effects:
509  *	Information is added to file.
510  *
511  * ----------------------------------------------------------------------------
512  */
513 
514 int
PlotDumpRaster(raster,file)515 PlotDumpRaster(raster, file)
516     Raster *raster;		/* Raster to be dumped. */
517     FILE *file;			/* File in which to dump it. */
518 {
519     int count;
520 
521     count = write(fileno(file), (char *) raster->ras_bits,
522 	    raster->ras_bytesPerLine*raster->ras_height);
523     if (count < 0)
524     {
525 	TxError("I/O error in writing raster file:  %s.\n",
526 		strerror(errno));
527 	return 1;
528     }
529     rasFileByteCount += count;
530     return 0;
531 }
532 
533 /*
534  * ----------------------------------------------------------------------------
535  *
536  * PlotLoadFont --
537  *
538  * 	Loads a font into memory, if it isn't already there.
539  *
540  ** Patched to accommodate both VAX format and Sun format vfont(5) files,
541  ** regardless of the native byte order of the processor.  J. Gealow 3/30/94
542  *
543  * Results:
544  *	The return value is a pointer to the font.  This must be used
545  *	when calling procedures like PlotTextSize.  If the font file
546  *	couldn't be found, an error message is output and NULL is
547  *	returned.
548  *
549  * Side effects:
550  *	New memory gets allocated.
551  *
552  * ----------------------------------------------------------------------------
553  */
554 
555 RasterFont *
PlotLoadFont(name)556 PlotLoadFont(name)
557     char *name;			/* Name of font file. */
558 {
559     FILE *f;
560     RasterFont *new;
561     struct dispatch *d;
562 
563     /* See if we've already got the font. */
564 
565     for (new = PlotFontList; new != NULL; new = new->fo_next)
566     {
567 	if (strcmp(new->fo_name, name) == 0)
568 	    return new;
569     }
570 
571     f = PaOpen(name, "r", (char *) NULL, ".", SysLibPath, (char **) NULL);
572     if (f == NULL)
573     {
574 	TxError("Couldn't read font file \"%s\".\n", name);
575 	return NULL;
576     }
577 
578     new = (RasterFont *) mallocMagic(sizeof(RasterFont));
579     new->fo_name = NULL;
580     StrDup(&new->fo_name, name);
581 
582     /* Read in the font's header and check the magic number. */
583 
584     if (read(fileno(f), (char *) &new->fo_hdr, sizeof(new->fo_hdr))
585 	    != sizeof(new->fo_hdr))
586     {
587 	fontError:
588 	TxError("Error in reading font file \"%s\".\n", name);
589 	fclose(f);
590 	return NULL;
591     }
592     if (PlotSwapShort(new->fo_hdr.magic) == 0436)
593     {
594 	new->fo_hdr.size = (PlotSwapShort(new->fo_hdr.size));
595 	new->fo_hdr.maxx = (PlotSwapShort(new->fo_hdr.maxx));
596 	new->fo_hdr.maxy = (PlotSwapShort(new->fo_hdr.maxy));
597 	new->fo_hdr.xtend = (PlotSwapShort(new->fo_hdr.xtend));
598     }
599     else if (new->fo_hdr.magic != 0436)
600     {
601 	TxError("Bad magic number in font file \"%s\".\n", name);
602 	fclose(f);
603 	return NULL;
604     }
605 
606     /* Read the character descriptors and the bit map. */
607 
608     if (read(fileno(f), (char *) new->fo_chars, sizeof(new->fo_chars))
609 	    != sizeof(new->fo_chars))
610 	goto fontError;
611     new->fo_bits = mallocMagic(new->fo_hdr.size);
612     if (read(fileno(f), new->fo_bits, (unsigned) new->fo_hdr.size)
613 	    != new->fo_hdr.size)
614 	goto fontError;
615     fclose(f);
616 
617     /* Compute the bounding box of all characters in the font. */
618 
619     new->fo_bbox.r_xbot = new->fo_bbox.r_xtop = 0;
620     new->fo_bbox.r_ybot = new->fo_bbox.r_ytop = 0;
621     for (d = &new->fo_chars[0]; d < &new->fo_chars[256]; d++)
622     {
623 	if (PlotSwapShort(new->fo_hdr.magic) == 0436)
624 	{
625 	    d->addr = PlotSwapShort(d->addr);
626 	    d->nbytes = PlotSwapShort(d->nbytes);
627 	    d->width = PlotSwapShort(d->width);
628 	}
629 	if (d->nbytes == 0) continue;
630 	if (d->up > new->fo_bbox.r_ytop)
631 	    new->fo_bbox.r_ytop = d->up;
632 	if (d->down > new->fo_bbox.r_ybot)
633 	    new->fo_bbox.r_ybot = d->down;
634 	if (d->right > new->fo_bbox.r_xtop)
635 	    new->fo_bbox.r_xtop = d->right;
636 	if (d->left > new->fo_bbox.r_xbot)
637 	    new->fo_bbox.r_xbot = d->left;
638 #ifdef	DEBUG_FONT
639 	if (d == &new->fo_chars['d'])
640 	{
641 	    char *fontcp;
642 	    int count;
643 
644 	    TxError("Character 'd' in font '%s' is at addr 0x%x, bytes %d, width %d\n",
645 		 new->fo_name, d->addr, d->nbytes, d->width);
646 	    count = 0;
647 	    for (fontcp = new->fo_bits + d->addr;
648 	      fontcp < new->fo_bits + d->addr + d->nbytes; fontcp++)
649 	    {
650 		int i;
651 		char ch;
652 		ch = *fontcp;
653 		for (i = 0; i < 8; i++)
654 		{
655 		    TxError("%c", ((ch & 0x80) ? 'X' : '.'));
656 		    ch = (ch << 1);
657 		}
658 		count++;
659 		if (count >= (d->left + d->right + 7) >> 3)
660 		{
661 		    TxError("\n");
662 		    count = 0;
663 		}
664 	    }
665 	    TxError("\n");
666 	}
667 #endif	/* DEBUG_FONT */
668     }
669     new->fo_bbox.r_xbot = - new->fo_bbox.r_xbot;
670     new->fo_bbox.r_ybot = - new->fo_bbox.r_ybot;
671 
672     new->fo_next = PlotFontList;
673     PlotFontList = new;
674     return new;
675 }
676 
677 /*
678  * ----------------------------------------------------------------------------
679  *
680  * PlotTextSize --
681  *
682  * 	Compute the area that a string will occupy.
683  *
684  * Results:
685  *	None.
686  *
687  * Side effects:
688  *	The rectangle "area" is filled in with the bounding
689  *	box of the bits in "string", assuming that the origin
690  *	for the text is (0,0) and the text is rendered in font
691  *	"font".
692  *
693  * ----------------------------------------------------------------------------
694  */
695 
696 void
PlotTextSize(font,string,area)697 PlotTextSize(font, string, area)
698     RasterFont *font;		/* Font to use in computing text size. */
699     char *string;		/* String to compute size of. */
700     Rect *area;	/* Place to store bounding box. */
701 {
702     int x;
703     struct dispatch *d;
704 
705     area->r_xbot = area->r_xtop = 0;
706     area->r_ybot = area->r_ytop = 0;
707     x = 0;
708 
709     for ( ; *string != 0; string ++)
710     {
711 	if ((*string == ' ') || (*string == '\t'))
712 	    d = &font->fo_chars['t'];
713 	else d = &font->fo_chars[*string];
714 	if (d->nbytes == 0) continue;
715 	if (d->up > area->r_ytop)
716 	    area->r_ytop = d->up;
717 	if (d->down > area->r_ybot)
718 	    area->r_ybot = d->down;
719 	if ((x+d->right) > area->r_xtop)
720 	    area->r_xtop = x + d->right;
721 	if ((x-d->left) < area->r_ybot)
722 	    area->r_ybot = x - d->left;
723 	x += d->width;
724     }
725     area->r_ybot = -area->r_ybot;
726 }
727 
728 /*
729  * ----------------------------------------------------------------------------
730  *
731  * PlotRasterText --
732  *
733  * 	Given a text string and a font, this procedure scan-converts
734  *	the string and sets bits in the current raster that correspond
735  *	to on-bits in the text.
736  *
737  * Results:
738  *	None.
739  *
740  * Side effects:
741  *	Bits are modified in the raster.
742  *
743  * ----------------------------------------------------------------------------
744  */
745 
746 void
PlotRasterText(raster,clip,font,string,point)747 PlotRasterText(raster, clip, font, string, point)
748     Raster *raster;	/* Raster whose bits are to be filled in. */
749     Rect *clip;			/* Area to which to clip the text.  Must be
750 				 * entirely within the area of the raster.
751 				 */
752     RasterFont *font;		/* Font to use for rasterizing string.  Must
753 				 * have been obtained by calling PlotLoadFont.
754 				 */
755     char *string;		/* String of text to rasterize. */
756     Point *point;		/* X-Y coordinates of origin of text.  The
757 				 * origin need not be inside the area of
758 				 * the raster, but only raster points inside
759 				 * the area will be modified.
760 				 */
761 {
762     int xOrig;			/* X-origin for current character. */
763 
764 
765     /* Outer loop:  process each character. */
766 
767     xOrig = point->p_x;
768     for ( ; *string != 0; string++)
769     {
770 	int cBytesPerLine, i;
771 	struct dispatch *d;	/* Descriptor for current character. */
772 
773 	/* Handle spaces and tabs specially by just spacing over. */
774 
775 	if ((*string == ' ') || (*string == '\t'))
776 	{
777 	    xOrig += font->fo_chars['t'].width;
778 	    continue;
779 	}
780 
781 	/* Middle loop:  render each character one raster line at a
782 	 * time, from top to bottom.  Skip rows that are outside the
783 	 * area of the raster.
784 	 */
785 
786 	d = &font->fo_chars[*string];
787 	cBytesPerLine = (d->left + d->right + 7) >> 3;
788 	for (i = 0; i < d->up + d->down; i++)
789 	{
790 	    int y, j;
791 	    char *charBitPtr;
792 
793 	    y = point->p_y + d->up - 1 - i;
794 	    if (y < clip->r_ybot) break;
795 	    if (y > clip->r_ytop) continue;
796 
797 	    /* Inner loop: process a series of bytes in a row to
798 	     * render one raster line of one character.  Be sure
799 	     * to skip areas that fall outside the raster to the
800 	     * left or right.
801 	     */
802 
803 	    for (j = -d->left,
804 		     charBitPtr = font->fo_bits + d->addr + i*cBytesPerLine;
805 	         j < d->right;
806 		 j += 8, charBitPtr++)
807 	    {
808 		char *rPtr;
809 		int charBits, x;
810 
811 		x = xOrig + j;
812 		if (x > clip->r_xtop) break;
813 		if (x < clip->r_xbot - 7) continue;
814 
815 		rPtr = (char *) raster->ras_bits;
816 		rPtr += (raster->ras_height - 1 - y)*raster->ras_bytesPerLine
817 			+ (x>>3);
818 		charBits = *charBitPtr & 0xff;
819 
820 		/* One byte of the character's bit map may span two
821 		 * bytes of the raster, so process each of the two
822 		 * raster bytes separately.  Either of the two bytes
823 		 * may be off the edge of the raster, in which case
824 		 * it must be skipped.
825 		 */
826 
827 		if (x >= 0)
828 		    *rPtr |= charBits >> (x & 0x7);
829 		rPtr += 1;
830 		if (x+8 <= clip->r_xtop)
831 		{
832 		    charBits = charBits << (8 - (x & 0x7));
833 		    *rPtr |= charBits;
834 		}
835 	    }
836 	}
837 
838 	xOrig += d->width;
839     }
840 }
841 
842 /*
843  * ----------------------------------------------------------------------------
844  *
845  * PlotRastPoint --
846  *
847  * 	Sets a particular pixel of a raster.
848  *
849  * Results:
850  *	None.
851  *
852  * Side effects:
853  *	If x and y lie inside the raster then the pixel that they select
854  *	is set to 1.  If x or y is outside the raster area then nothing
855  *	happens.
856  *
857  * ----------------------------------------------------------------------------
858  */
859 
860 void
PlotRastPoint(raster,x,y)861 PlotRastPoint(raster, x, y)
862     Raster *raster;		/* Raster containing pixel to be
863 					 * filled.
864 					 */
865     int x, y;				/* Coordinates of pixel. */
866 {
867     if ((x < 0) || (x >= raster->ras_width)) return;
868     y = (raster->ras_height - 1) - y;
869     if ((y < 0) || (y >= raster->ras_height)) return;
870 
871     raster->ras_bits[((y*raster->ras_intsPerLine) + (x>>5))]
872 	    |= singleBit[x&037];
873 }
874 
875 /*
876  * ----------------------------------------------------------------------------
877  *
878  * PlotRastLine --
879  *
880  * 	Draws a one-pixel-wide line between two points.
881  *
882  * Results:
883  *	None.
884  *
885  * Side effects:
886  *	A line is drawn between pixels src and dst.  Only the portion
887  *	of the line that lies inside the raster is drawn;  the endpoints
888  *	may lie outside the raster (this feature is necessary to draw
889  *	straight lines that cross multiple swaths).
890  *
891  * ----------------------------------------------------------------------------
892  */
893 
894 void
PlotRastLine(raster,src,dst)895 PlotRastLine(raster, src, dst)
896     Raster *raster;		/* Where to render the line. */
897     Point *src;			/* One endpoint of line, in raster coords. */
898     Point *dst;			/* The other endpoint, in raster coords. */
899 {
900     int x, y, dx, dy, xinc, incr1, incr2, d, done;
901 
902     /* Compute the total x- and y-motions, and arrange for the line to be
903      * drawn in increasing order of y-coordinate.
904      */
905 
906     dx = dst->p_x - src->p_x;
907     dy = dst->p_y - src->p_y;
908     if (dy < 0)
909     {
910 	dy = -dy;
911 	dx = -dx;
912 	x = dst->p_x;
913 	y = dst->p_y;
914 	dst = src;
915     }
916     else
917     {
918 	x = src->p_x;
919 	y = src->p_y;
920     }
921 
922     /* The code below is just the Bresenham algorithm from Foley and
923      * Van Dam (pp. 435), modified slightly so that it can work in
924      * all directions.
925      */
926 
927     if (dx < 0)
928     {
929 	xinc = -1;
930 	dx = -dx;
931     }
932     else xinc = 1;
933     if (dx >= dy)
934     {
935 	d = 2*dy - dx;
936 	incr1 = 2*dy;
937 	incr2 = 2*(dy - dx);
938 	done = dst->p_x;
939 	for ( ; x != done ; x += xinc)
940 	{
941 	    PlotRastPoint(raster, x, y);
942 	    if (d < 0)
943 		d += incr1;
944 	    else
945 	    {
946 		d += incr2;
947 		y += 1;
948 	    }
949 	}
950     }
951     else
952     {
953 	d = 2*dx - dy;
954 	incr1 = 2*dx;
955 	incr2 = 2*(dx - dy);
956 	done = dst->p_y;
957 	for ( ; y != done ; y += 1)
958 	{
959 	    PlotRastPoint(raster, x, y);
960 	    if (d < 0)
961 		d += incr1;
962 	    else
963 	    {
964 		d += incr2;
965 		x += xinc;
966 	    }
967 	}
968     }
969     PlotRastPoint(raster, x, y);
970 }
971 
972 /*
973  * ----------------------------------------------------------------------------
974  *
975  * PlotRastFatLine --
976  *
977  * 	Draws a line many pixels wide between two points.
978  *
979  * Results:
980  *	None.
981  *
982  * Side effects:
983  *	A line is drawn between pixels src and dst.  Only the portion
984  *	of the line that lies inside the raster is drawn;  the endpoints
985  *	may lie outside the raster (this feature is necessary to draw
986  *	straight lines that cross multiple swaths).  The line is drawn
987  *	several pixels wide, as determined by the "widen" parameter.
988  *	The ends of the line are square, not rounded, which may cause
989  *	upleasant effects for some uses.  If the line is Manhattan,
990  *	this procedure is very inefficient:  it's better to use the
991  *	PlotFillRaster procedure.
992  *
993  * ----------------------------------------------------------------------------
994  */
995 
996 void
PlotRastFatLine(raster,src,dst,widen)997 PlotRastFatLine(raster, src, dst, widen)
998     Raster *raster;		/* Where to render the line. */
999     Point *src;			/* One endpoint of line, in raster coords. */
1000     Point *dst;			/* The other endpoint, in raster coords. */
1001     int widen;			/* How much to widen the line.  0 means the
1002 				 * line is one pixel wide, 1 means it's 3
1003 				 * pixels wide, and so on.
1004 				 */
1005 {
1006     double dx, dy, x, y;
1007     int nLines;
1008 
1009     /* Just draw (2*widen) + 1 lines spaced about one pixel apart.
1010      * The first lines here compute how far apart to space the lines.
1011      */
1012 
1013     nLines = (2*widen) + 1;
1014     x = dst->p_x - src->p_x;
1015     y = dst->p_y - src->p_y;
1016     dy = sqrt(x*x + y*y);
1017     dx = y/dy;
1018     dy = -x/dy;
1019     x = -dy*(widen);
1020     y = dx*(widen);
1021 
1022     for (x = -dx*widen,  y = -dy*widen;
1023 	 nLines > 0;
1024 	 nLines -= 1, x += dx, y += dy)
1025     {
1026 	Point newSrc, newDst;
1027 
1028 	if (x > 0)
1029 	    newSrc.p_x = (x + .5);
1030 	else newSrc.p_x = (x - .5);
1031 	if (y > 0)
1032 	    newSrc.p_y = (y + .5);
1033 	else newSrc.p_y = (y - .5);
1034 	newDst.p_x = dst->p_x + newSrc.p_x;
1035 	newDst.p_y = dst->p_y + newSrc.p_y;
1036 	newSrc.p_x += src->p_x;
1037 	newSrc.p_y += src->p_y;
1038 
1039 	PlotRastLine(raster, &newSrc, &newDst);
1040     }
1041 }
1042 
1043 /*
1044  * ----------------------------------------------------------------------------
1045  *
1046  * PlotSwapBytes --
1047  * PlotSwapShort --
1048  *
1049  * 	These two procedures do byte swapping in the way that's
1050  *	required when moving binary files between VAXes and Suns.
1051  *
1052  * Results:
1053  *	Each procedure returns a value in which the order of bytes
1054  *	has been reversed.  PlotSwapBytes takes an integer and returns
1055  *	an integer with the four bytes in reverse order;  PlotSwapShort
1056  *	takes a short and swaps the two bytes.
1057  *
1058  * Side effects:
1059  *	None.
1060  *
1061  * ----------------------------------------------------------------------------
1062  */
1063 
1064 int
PlotSwapBytes(value)1065 PlotSwapBytes(value)
1066     int value;				/* 4-byte Value whose bytes are to
1067 					 * be reversed.
1068 					 */
1069 {
1070     int result;
1071 
1072 #define src ((char *) &value)
1073 #define dst ((char *) &result)
1074 
1075     dst[0] = src[3];
1076     dst[1] = src[2];
1077     dst[2] = src[1];
1078     dst[3] = src[0];
1079 
1080     return result;
1081 }
1082 
1083 short
PlotSwapShort(value)1084 PlotSwapShort(value)
1085     short value;		/* Value whose bytes are to be swapped. */
1086 {
1087     short result;
1088 
1089 #define src ((char *) &value)
1090 #define dst ((char *) &result)
1091 
1092     dst[0] = src[1];
1093     dst[1] = src[0];
1094 
1095     return result;
1096 }
1097 
1098 
1099 
1100 /*
1101  * ----------------------------------------------------------------------------
1102  *
1103  * PlotDumpColorPreamble --
1104  *
1105  * 	Dump a color preamble in vdmpc format for the color Versatec.  See
1106  *	the vdmpc(5) man page for details on the format.
1107  *
1108  *	Format:
1109  *	    preamble is a 1K block
1110  *	    first word is 0xA5CF4DFB (a magic number)
1111  *	    second word gives the number of scan lines
1112  *	    third word gives width of the plot in pixels (must be multiple of 8)
1113  *	    rest of the words are zero
1114  *
1115  * ----------------------------------------------------------------------------
1116  */
1117 
1118 #define VERSATEC_BLOCK          1024
1119 #define VERSATEC_MAGIC_WORD     0xA5CF4DFB
1120 unsigned int VersHeader[VERSATEC_BLOCK/sizeof(unsigned int)] = {VERSATEC_MAGIC_WORD};
1121 
1122 int
PlotDumpColorPreamble(color,file,lines,columns)1123 PlotDumpColorPreamble(color, file, lines, columns)
1124     VersatecColor color;	/* The color that the following raster will
1125 				 * be printed in.
1126 				 */
1127     FILE *file;			/* file in which to place header */
1128     int lines;			/* number of scan lines */
1129     int columns;		/* Width in pixels. */
1130 {
1131 
1132     int count;
1133 
1134     if (color == BLACK)
1135     {
1136 	VersHeader[1] = lines;
1137 	VersHeader[2] = columns;
1138 	count = write(fileno(file), &VersHeader[0], sizeof(VersHeader));
1139 	TxPrintf("Wrote %d bytes of control.\n", count);
1140     }
1141     return 0;
1142 }
1143