/*
 * plotRastUtils.c --
 *
 * This file contains several procedures for manipulating rasters.
 * The procedures are used to create bitmaps to directly drive
 * printers such as the black-and-white Versatec.  For example,
 * the procedures draw stippled areas and raster-ize text strings
 * using fonts.
 *
 *     ********************************************************************* 
 *     * Copyright (C) 1985, 1990 Regents of the University of California. * 
 *     * Permission to use, copy, modify, and distribute this              * 
 *     * software and its documentation for any purpose and without        * 
 *     * fee is hereby granted, provided that the above copyright          * 
 *     * notice appear in all copies.  The University of California        * 
 *     * makes no representations about the suitability of this            * 
 *     * software for any purpose.  It is provided "as is" without         * 
 *     * express or implied warranty.  Export of this software outside     * 
 *     * of the United States of America may require an export license.    * 
 *     *********************************************************************
 */

#ifndef lint
static char rcsid[]="$Header: /ufs/repository/magic/plot/plotRutils.c,v 1.3 2001/01/12 22:13:10 jsolomon Exp $";
#endif  not lint

#include <errno.h>
#include <stdio.h>
#include "misc/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "utils/malloc.h"
#include "plot/plotInt.h"
#include "textio/textio.h"
#include "utils/utils.h"

extern double sqrt();

int rasFileByteCount = 0;
/* A solid black stipple: */

Stipple PlotBlackStipple =
{
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
};

/* The following two arrays are used to select a range of bits within
 * a word.  The first array selects all the bits at or to the left of
 * the given bit-position. "To the left" means in a lower-numbered
 * byte (according to byte order) or in a more significant bit of the
 * same byte.  The second array selects the bits at or to the right of
 * the given bit-position.  Bit position 0 is considered to be the
 * leftmost bit of the word;  it's the highest-order bit in the 0th
 * byte.
 */

static unsigned int leftBits[32] =
{
    0x00000080, 0x000000c0, 0x000000e0, 0x000000f0,
    0x000000f8, 0x000000fc, 0x000000fe, 0x000000ff,
    0x000080ff, 0x0000c0ff, 0x0000e0ff, 0x0000f0ff,
    0x0000f8ff, 0x0000fcff, 0x0000feff, 0x0000ffff,
    0x0080ffff, 0x00c0ffff, 0x00e0ffff, 0x00f0ffff,
    0x00f8ffff, 0x00fcffff, 0x00feffff, 0x00ffffff,
    0x80ffffff, 0xc0ffffff, 0xe0ffffff, 0xf0ffffff,
    0xf8ffffff, 0xfcffffff, 0xfeffffff, 0xffffffff
};
static unsigned int rightBits[32] =
{
    0xffffffff,	0xffffff7f, 0xffffff3f, 0xffffff1f,
    0xffffff0f, 0xffffff07, 0xffffff03, 0xffffff01,
    0xffffff00,	0xffff7f00, 0xffff3f00, 0xffff1f00,
    0xffff0f00, 0xffff0700, 0xffff0300, 0xffff0100,
    0xffff0000,	0xff7f0000, 0xff3f0000, 0xff1f0000,
    0xff0f0000, 0xff070000, 0xff030000, 0xff010000,
    0xff000000,	0x7f000000, 0x3f000000, 0x1f000000,
    0x0f000000, 0x07000000, 0x03000000, 0x01000000
};

/* The following arrray selects a single bit within a word.  The
 * zeroth entry selects the bit that will be leftmost in the plot.
 */
static unsigned int singleBit[32] =
{
    0x00000080, 0x00000040, 0x00000020, 0x00000010,
    0x00000008, 0x00000004, 0x00000002, 0x00000001,
    0x00008000, 0x00004000, 0x00002000, 0x00001000,
    0x00000800, 0x00000400, 0x00000200, 0x00000100,
    0x00800000, 0x00400000, 0x00200000, 0x00100000,
    0x00080000, 0x00040000, 0x00020000, 0x00010000,
    0x80000000, 0x40000000, 0x20000000, 0x10000000,
    0x08000000, 0x04000000, 0x02000000, 0x01000000
};

Font *PlotFontList;		/* Linked list of all fonts that have
				 * been read in so far.
				 */

/*
 * ----------------------------------------------------------------------------
 *
 * PlotRastInit --
 *
 * 	Initialize data structures related to rasters.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	All this procedure does is to swap bytes in the stipple
 *	masks if we're running on a non-VAX.
 *
 * ----------------------------------------------------------------------------
 */

void
PlotRastInit()
{
#ifdef IS_BIG_ENDIAN
    int i;

    for (i = 0; i < 32; i++)
    {
	leftBits[i] = PlotSwapBytes(leftBits[i]);
	rightBits[i] = PlotSwapBytes(rightBits[i]);
	singleBit[i] = PlotSwapBytes(singleBit[i]);
    }
#endif IS_BIG_ENDIAN
}

/*
 * ----------------------------------------------------------------------------
 *
 * PlotNewRaster --
 *
 * 	Allocate and initialize a new raster structure.
 *
 * Results:
 *	The return value is a pointer to the new raster object.
 *
 * Side effects:
 *	Memory is allocated.
 *
 * ----------------------------------------------------------------------------
 */

Raster *
PlotNewRaster(height, width)
    int height;			/* Raster's height in pixels.  Should generally
				 * be a multiple of 16.
				 */
    int width;			/* Raster's width in pixels.  Should generally
				 * be a multiple of 32 bits.
				 */
{
    Raster *new;

    new = (Raster *) mallocMagic(sizeof(Raster));
    new->ras_width = width;
    new->ras_intsPerLine = (width+31)/32;
    new->ras_bytesPerLine = new->ras_intsPerLine*4;
    new->ras_height = height;
    new->ras_bits = (int *) mallocMagic(
	    (unsigned) (height * new->ras_intsPerLine * 4));

    return new;
}

/*
 * ----------------------------------------------------------------------------
 *
 * PlotFreeRaster --
 *
 * 	Frees up the memory associated with an existing raster structure.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The storage associated with raster is returned to the allocator.
 *
 * ----------------------------------------------------------------------------
 */

void
PlotFreeRaster(raster)
    Raster *raster;		/* Raster whose memory is to be freed.  Should
				 * have been created with PlotNewRaster.
				 */
{
    freeMagic((char *) raster->ras_bits);
    freeMagic((char *) raster);
}

/*
 * ----------------------------------------------------------------------------
 *
 * PlotClearRaster --
 *
 * 	This procedure clears out an area of the raster.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The area of the raster indicated by "area" is set to all zeroes.
 *
 * ----------------------------------------------------------------------------
 */

void
PlotClearRaster(raster, area)
    Raster *raster;			/* Raster that's to be cleared. */
    Rect *area;				/* Area to be cleared, in raster
					 * coords.  NULL means clear the
					 * whole raster.
					 */
{
    int *left, *right, *cur;
    int leftMask, rightMask, line;

    if (area == NULL)
    {
	bzero((char *) raster->ras_bits,
		raster->ras_bytesPerLine * raster->ras_height);
	return;
    }

    /* Compute the address of the leftmost word in the topmost line
     * to be cleared, and the rightmost word in the topmost line to
     * be cleared.
     */
    
    left = raster->ras_bits +
	((raster->ras_height-1) - area->r_ytop)*raster->ras_intsPerLine;
    right = left + area->r_xtop/32;
    left += area->r_xbot/32;

    /* Divide the x-span of the area into three parts:  the leftmost
     * word, of which only the rightmost bits are cleared, the
     * rightmost word, of which only the leftmost bits are cleared,
     * and the middle section, in which all bits of each word are
     * cleared.  Compute masks that determine which bits of the end
     * words will be cleared.  There's a special case when the left
     * and right ends are in the same word.
     */
    
    leftMask = rightBits[area->r_xbot&037];
    rightMask = leftBits[area->r_xtop&037];
    if (left == right)
	leftMask &= rightMask;
    
    /* Clear the area one raster line at a time, top to bottom. */

    for (line = area->r_ytop; line >= area->r_ybot; line -= 1)
    {
	/* Clear the leftmost word on this line. */

	*left &= ~leftMask;

	if (left != right)
	{
	    /* Clear the center of the line. */

	    for (cur = left+1; cur < right; cur += 1)
		*cur = 0;
	    
	    /* Clear the rightmost word on this line. */

	    *cur &= ~rightMask;
	}

	left += raster->ras_intsPerLine;
	right += raster->ras_intsPerLine;
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * PlotFillRaster --
 *
 * 	Given a raster and an area, this procedure fills the given area
 *	of the raster with a particular stipple pattern.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

void
PlotFillRaster(raster, area, stipple)
    register Raster *raster;		/* Pointer to raster whose bits are
					 * to be filled in.
					 */
    register Rect *area;		/* Area to be filled in pixel coords.
					 * This is an inclusive area:  it
					 * includes the boundary pixels.  The
					 * caller must ensure that it is
					 * clipped to the raster area.
					 */
    Stipple stipple;			/* Stipple pattern to be used to fill
					 * in the raster area.
					 */
{
    register int *left, *cur, line;
    int *right;
    int leftMask, rightMask, curStipple;

    /* Compute the address of the leftmost word in the topmost line
     * to be filled, and the rightmost word in the topmost line to
     * be filled.
     */
    
    left = raster->ras_bits +
	((raster->ras_height-1) - area->r_ytop)*raster->ras_intsPerLine;
    right = left + area->r_xtop/32;
    left += area->r_xbot/32;

    /* Divide the x-span of the area into three parts:  the leftmost
     * word, of which only the rightmost bits are modified, the
     * rightmost word, of which only the leftmost bits are modified,
     * and the middle section, in which all bits of each word are
     * modified.  Compute masks that determine which bits of the end
     * words will be modified.  There's a special case when the left
     * and right ends are in the same word.
     */
    
    leftMask = rightBits[area->r_xbot&037];
    rightMask = leftBits[area->r_xtop&037];
    if (left == right)
	leftMask &= rightMask;
    
    /* Process the stippled area one raster line at a time, top to bottom. */

    for (line = area->r_ytop; line >= area->r_ybot; line -= 1)
    {
	curStipple = stipple[(-line)&017];

	/* Fill the leftmost word on this line. */

	*left |= curStipple & leftMask;

	if (left != right)
	{
	    /* Fill the center of the line. */

	    for (cur = left+1; cur < right; cur += 1)
		*cur |= curStipple;
	    
	    /* Fill the rightmost word on this line. */

	    *cur |= curStipple & rightMask;
	}

	left += raster->ras_intsPerLine;
	right += raster->ras_intsPerLine;
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * PlotDumpRaster  --
 *
 * 	Writes out the contents of the given raster to the given file,
 *	in binary format.
 *
 * Results:
 *	Returns 0 if all was well.  Returns non-zero if there was
 *	an I/O error.  In this event, this procedure prints an
 *	error message before returning.
 *
 * Side effects:
 *	Information is added to file.
 *
 * ----------------------------------------------------------------------------
 */

int
PlotDumpRaster(raster, file)
    Raster *raster;		/* Raster to be dumped. */
    FILE *file;			/* File in which to dump it. */
{
    int count;

    count = write(fileno(file), (char *) raster->ras_bits,
	    raster->ras_bytesPerLine*raster->ras_height);
    if (count < 0)
    {
	TxError("I/O error in writing raster file:  %s.\n",
		STRERROR(errno));
	return 1;
    }
    rasFileByteCount += count;
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * PlotLoadFont --
 *
 * 	Loads a font into memory, if it isn't already there.
 *
 ** Patched to accomodate both VAX format and Sun format vfont(5) files,
 ** regardless of the native byte order of the processor.  J. Gealow 3/30/94
 *
 * Results:
 *	The return value is a pointer to the font.  This must be used
 *	when calling procedures like PlotTextSize.  If the font file
 *	couldn't be found, an error message is output and NULL is
 *	returned.
 *
 * Side effects:
 *	New memory gets allocated.
 *
 * ----------------------------------------------------------------------------
 */

Font *
PlotLoadFont(name)
    char *name;			/* Name of font file. */
{
    FILE *f;
    Font *new;
    struct dispatch *d;

    /* See if we've already got the font. */

    for (new = PlotFontList; new != NULL; new = new->fo_next)
    {
	if (strcmp(new->fo_name, name) == 0)
	    return new;
    }

    f = PaOpen(name, "r", (char *) NULL, ".", SysLibPath, (char **) NULL);
    if (f == NULL)
    {
	TxError("Couldn't read font file \"%s\".\n", name);
	return NULL;
    }

    new = (Font *) mallocMagic(sizeof(Font));
    new->fo_name = NULL;
    StrDup(&new->fo_name, name);

    /* Read in the font's header and check the magic number. */

    if (read(fileno(f), (char *) &new->fo_hdr, sizeof(new->fo_hdr))
	    != sizeof(new->fo_hdr))
    {
	fontError:
	TxError("Error in reading font file \"%s\".\n", name);
	fclose(f);
	return NULL;
    }
    if (PlotSwapShort(new->fo_hdr.magic) == 0436)
    {
	new->fo_hdr.size = (PlotSwapShort(new->fo_hdr.size));
	new->fo_hdr.maxx = (PlotSwapShort(new->fo_hdr.maxx));
	new->fo_hdr.maxy = (PlotSwapShort(new->fo_hdr.maxy));
	new->fo_hdr.xtend = (PlotSwapShort(new->fo_hdr.xtend));
    } 
    else if (new->fo_hdr.magic != 0436)
    {
	TxError("Bad magic number in font file \"%s\".\n", name);
	fclose(f);
	return NULL;
    }

    /* Read the character descriptors and the bit map. */

    if (read(fileno(f), (char *) new->fo_chars, sizeof(new->fo_chars))
	    != sizeof(new->fo_chars))
	goto fontError;
    new->fo_bits = mallocMagic(new->fo_hdr.size);
    if (read(fileno(f), new->fo_bits, (unsigned) new->fo_hdr.size)
	    != new->fo_hdr.size)
	goto fontError;
    fclose(f);

    /* Compute the bounding box of all characters in the font. */

    new->fo_bbox.r_xbot = new->fo_bbox.r_xtop = 0;
    new->fo_bbox.r_ybot = new->fo_bbox.r_ytop = 0;
    for (d = &new->fo_chars[0]; d < &new->fo_chars[256]; d++)
    {
	if (PlotSwapShort(new->fo_hdr.magic) == 0436)
	{
	    d->addr = PlotSwapShort(d->addr);
	    d->nbytes = PlotSwapShort(d->nbytes);
	    d->width = PlotSwapShort(d->width);
	}
	if (d->nbytes == 0) continue;
	if (d->up > new->fo_bbox.r_ytop)
	    new->fo_bbox.r_ytop = d->up;
	if (d->down > new->fo_bbox.r_ybot)
	    new->fo_bbox.r_ybot = d->down;
	if (d->right > new->fo_bbox.r_xtop)
	    new->fo_bbox.r_xtop = d->right;
	if (d->left > new->fo_bbox.r_xbot)
	    new->fo_bbox.r_xbot = d->left;
#ifdef	DEBUG_FONT
	if (d == &new->fo_chars['d'])
	{
	    char *fontcp;
	    int count;

	    TxError("Character 'd' in font '%s' is at addr 0x%x, bytes %d, width %d\n",
		 new->fo_name, d->addr, d->nbytes, d->width);
	    count = 0;
	    for (fontcp = new->fo_bits + d->addr; 
	      fontcp < new->fo_bits + d->addr + d->nbytes; fontcp++)
	    {
		int i;
		char ch;
		ch = *fontcp;
		for (i = 0; i < 8; i++)
		{
		    TxError("%c", ((ch & 0x80) ? 'X' : '.'));
		    ch = (ch << 1);
		}
		count++;
		if (count >= (d->left + d->right + 7) >> 3)
		{
		    TxError("\n");
		    count = 0;
		}
	    }
	    TxError("\n");
	}
#endif	DEBUG_FONT
    }
    new->fo_bbox.r_xbot = - new->fo_bbox.r_xbot;
    new->fo_bbox.r_ybot = - new->fo_bbox.r_ybot;

    new->fo_next = PlotFontList;
    PlotFontList = new;
    return new;
}

/*
 * ----------------------------------------------------------------------------
 *
 * PlotTextSize --
 *
 * 	Compute the area that a string will occupy.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The rectangle "area" is filled in with the bounding
 *	box of the bits in "string", assuming that the origin
 *	for the text is (0,0) and the text is rendered in font
 *	"font".
 *
 * ----------------------------------------------------------------------------
 */

void
PlotTextSize(font, string, area)
    Font *font;			/* Font to use in computing text size. */
    char *string;		/* String to compute size of. */
    register Rect *area;	/* Place to store bounding box. */
{
    register int x;
    register struct dispatch *d;

    area->r_xbot = area->r_xtop = 0;
    area->r_ybot = area->r_ytop = 0;
    x = 0;
    
    for ( ; *string != 0; string ++)
    {
	if ((*string == ' ') || (*string == '\t'))
	    d = &font->fo_chars['t'];
	else d = &font->fo_chars[*string];
	if (d->nbytes == 0) continue;
	if (d->up > area->r_ytop)
	    area->r_ytop = d->up;
	if (d->down > area->r_ybot)
	    area->r_ybot = d->down;
	if ((x+d->right) > area->r_xtop)
	    area->r_xtop = x + d->right;
	if ((x-d->left) < area->r_ybot)
	    area->r_ybot = x - d->left;
	x += d->width;
    }
    area->r_ybot = -area->r_ybot;
}

/*
 * ----------------------------------------------------------------------------
 *
 * PlotRasterText --
 *
 * 	Given a text string and a font, this procedure scan-converts
 *	the string and sets bits in the current raster that correspond
 *	to on-bits in the text.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Bits are modified in the raster.
 *
 * ----------------------------------------------------------------------------
 */

void
PlotRasterText(raster, clip, font, string, point)
    register Raster *raster;	/* Raster whose bits are to be filled in. */
    Rect *clip;			/* Area to which to clip the text.  Must be
				 * entirely within the area of the raster.
				 */
    Font *font;			/* Font to use for rasterizing string.  Must
				 * have been obtained by calling PlotLoadFont.
				 */
    char *string;		/* String of text to rasterize. */
    Point *point;		/* X-Y coordinates of origin of text.  The
				 * origin need not be inside the area of
				 * the raster, but only raster points inside
				 * the area will be modified.
				 */
{
    int xOrig;			/* X-origin for current character. */

    
    /* Outer loop:  process each character. */

    xOrig = point->p_x;
    for ( ; *string != 0; string++)
    {
	int cBytesPerLine, i;
	register struct dispatch *d;	/* Descriptor for current character. */

	/* Handle spaces and tabs specially by just spacing over. */

	if ((*string == ' ') || (*string == '\t'))
	{
	    xOrig += font->fo_chars['t'].width;
	    continue;
	}

	/* Middle loop:  render each character one raster line at a
	 * time, from top to bottom.  Skip rows that are outside the
	 * area of the raster.
	 */
	
	d = &font->fo_chars[*string];
	cBytesPerLine = (d->left + d->right + 7) >> 3;
	for (i = 0; i < d->up + d->down; i++)
	{
	    int y, j;
	    char *charBitPtr;

	    y = point->p_y + d->up - 1 - i;
	    if (y < clip->r_ybot) break;
	    if (y > clip->r_ytop) continue;

	    /* Inner loop: process a series of bytes in a row to
	     * render one raster line of one character.  Be sure
	     * to skip areas that fall outside the raster to the
	     * left or right.
	     */
	    
	    for (j = -d->left,
		     charBitPtr = font->fo_bits + d->addr + i*cBytesPerLine;
	         j < d->right;
		 j += 8, charBitPtr++)
	    {
		char *rPtr;
		int charBits, x;

		x = xOrig + j;
		if (x > clip->r_xtop) break;
		if (x < clip->r_xbot - 7) continue;

		rPtr = (char *) raster->ras_bits;
		rPtr += (raster->ras_height - 1 - y)*raster->ras_bytesPerLine
			+ (x>>3);
		charBits = *charBitPtr & 0xff;

		/* One byte of the character's bit map may span two
		 * bytes of the raster, so process each of the two
		 * raster bytes separately.  Either of the two bytes
		 * may be off the edge of the raster, in which case
		 * it must be skipped.
		 */
		
		if (x >= 0)
		    *rPtr |= charBits >> (x & 0x7);
		rPtr += 1;
		if (x+8 <= clip->r_xtop)
		{
		    charBits = charBits << (8 - (x & 0x7));
		    *rPtr |= charBits;
		}
	    }
	}

	xOrig += d->width;
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * PlotRastPoint --
 *
 * 	Sets a particular pixel of a raster.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	If x and y lie inside the raster then the pixel that they select
 *	is set to 1.  If x or y is outside the raster area then nothing
 *	happens.
 *
 * ----------------------------------------------------------------------------
 */

void
PlotRastPoint(raster, x, y)
    register Raster *raster;		/* Raster containing pixel to be
					 * filled.
					 */
    int x, y;				/* Coordinates of pixel. */
{
    if ((x < 0) || (x >= raster->ras_width)) return;
    y = (raster->ras_height - 1) - y;
    if ((y < 0) || (y >= raster->ras_height)) return;

    raster->ras_bits[((y*raster->ras_intsPerLine) + (x>>5))]
	    |= singleBit[x&037];
}

/*
 * ----------------------------------------------------------------------------
 *
 * PlotRastLine --
 *
 * 	Draws a one-pixel-wide line between two points.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	A line is drawn between pixels src and dst.  Only the portion
 *	of the line that lies inside the raster is drawn;  the endpoints
 *	may lie outside the raster (this feature is necessary to draw
 *	straight lines that cross multiple swaths).
 *
 * ----------------------------------------------------------------------------
 */

void
PlotRastLine(raster, src, dst)
    Raster *raster;		/* Where to render the line. */
    Point *src;			/* One endpoint of line, in raster coords. */
    Point *dst;			/* The other endpoint, in raster coords. */
{
    int x, y, dx, dy, xinc, incr1, incr2, d, done;

    /* Compute the total x- and y-motions, and arrange for the line to be
     * drawn in increasing order of y-coordinate.
     */

    dx = dst->p_x - src->p_x;
    dy = dst->p_y - src->p_y;
    if (dy < 0)
    {
	dy = -dy;
	dx = -dx;
	x = dst->p_x;
	y = dst->p_y;
	dst = src;
    }
    else
    {
	x = src->p_x;
	y = src->p_y;
    }

    /* The code below is just the Bresenham algorithm from Foley and
     * Van Dam (pp. 435), modified slightly so that it can work in
     * all directions.
     */

    if (dx < 0)
    {
	xinc = -1;
	dx = -dx;
    }
    else xinc = 1;
    if (dx >= dy)
    {
	d = 2*dy - dx;
	incr1 = 2*dy;
	incr2 = 2*(dy - dx);
	done = dst->p_x;
	for ( ; x != done ; x += xinc)
	{
	    PlotRastPoint(raster, x, y);
	    if (d < 0)
		d += incr1;
	    else
	    {
		d += incr2;
		y += 1;
	    }
	}
    }
    else
    {
	d = 2*dx - dy;
	incr1 = 2*dx;
	incr2 = 2*(dx - dy);
	done = dst->p_y;
	for ( ; y != done ; y += 1)
	{
	    PlotRastPoint(raster, x, y);
	    if (d < 0)
		d += incr1;
	    else
	    {
		d += incr2;
		x += xinc;
	    }
	}
    }
    PlotRastPoint(raster, x, y);
}

/*
 * ----------------------------------------------------------------------------
 *
 * PlotRastFatLine --
 *
 * 	Draws a line many pixels wide between two points.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	A line is drawn between pixels src and dst.  Only the portion
 *	of the line that lies inside the raster is drawn;  the endpoints
 *	may lie outside the raster (this feature is necessary to draw
 *	straight lines that cross multiple swaths).  The line is drawn
 *	several pixels wide, as determined by the "widen" parameter.
 *	The ends of the line are square, not rounded, which may cause
 *	upleasant effects for some uses.  If the line is Manhattan,
 *	this procedure is very inefficient:  it's better to use the
 *	PlotFillRaster procedure.
 *
 * ----------------------------------------------------------------------------
 */

void
PlotRastFatLine(raster, src, dst, widen)
    Raster *raster;		/* Where to render the line. */
    Point *src;			/* One endpoint of line, in raster coords. */
    Point *dst;			/* The other endpoint, in raster coords. */
    int widen;			/* How much to widen the line.  0 means the
				 * line is one pixel wide, 1 means it's 3
				 * pixels wide, and so on.
				 */
{
    double dx, dy, x, y;
    int nLines;

    /* Just draw (2*widen) + 1 lines spaced about one pixel apart.
     * The first lines here compute how far apart to space the lines.
     */

    nLines = (2*widen) + 1;
    x = dst->p_x - src->p_x;
    y = dst->p_y - src->p_y;
    dy = sqrt(x*x + y*y);
    dx = y/dy;
    dy = -x/dy;
    x = -dy*(widen);
    y = dx*(widen);

    for (x = -dx*widen,  y = -dy*widen;
	 nLines > 0;
	 nLines -= 1, x += dx, y += dy)
    {
	Point newSrc, newDst;

	if (x > 0)
	    newSrc.p_x = (x + .5);
	else newSrc.p_x = (x - .5);
	if (y > 0)
	    newSrc.p_y = (y + .5);
	else newSrc.p_y = (y - .5);
	newDst.p_x = dst->p_x + newSrc.p_x;
	newDst.p_y = dst->p_y + newSrc.p_y;
	newSrc.p_x += src->p_x;
	newSrc.p_y += src->p_y;

	PlotRastLine(raster, &newSrc, &newDst);
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * PlotSwapBytes --
 * PlotSwapShort --
 *
 * 	These two procedures do byte swapping in the way that's
 *	required when moving binary files between VAXes and Suns.
 *
 * Results:
 *	Each procedure returns a value in which the order of bytes
 *	has been reversed.  PlotSwapBytes takes an integer and returns
 *	an integer with the four bytes in reverse order;  PlotSwapShort
 *	takes a short and swaps the two bytes.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

int
PlotSwapBytes(value)
    int value;				/* 4-byte Value whose bytes are to
					 * be reversed.
					 */
{
    int result;

#define src ((char *) &value)
#define dst ((char *) &result)

    dst[0] = src[3];
    dst[1] = src[2];
    dst[2] = src[1];
    dst[3] = src[0];

    return result;
}

short
PlotSwapShort(value)
    short value;		/* Value whose bytes are to be swapped. */
{
    short result;

#define src ((char *) &value)
#define dst ((char *) &result)

    dst[0] = src[1];
    dst[1] = src[0];

    return result;
}



/*
 * ----------------------------------------------------------------------------
 *
 * PlotDumpColorPreamble --
 *
 * 	Dump a color preamble in vdmpc format for the color Versatec.  See
 *	the vdmpc(5) man page for details on the format.
 *
 *	Format:  
 *	    preamble is a 1K block
 *	    first word is 0xA5CF4DFB (a magic number)
 *	    second word gives the number of scan lines
 *	    third word gives width of the plot in pixels (must be multiple of 8)
 *	    rest of the words are zero
 *	
 * ----------------------------------------------------------------------------
 */

#define VERSATEC_BLOCK          1024
#define VERSATEC_MAGIC_WORD     0xA5CF4DFB
unsigned int VersHeader[VERSATEC_BLOCK/sizeof(unsigned int)] = {VERSATEC_MAGIC_WORD};

int
PlotDumpColorPreamble(color, file, lines, columns)
    VersatecColor color;	/* The color that the following raster will 
				 * be printed in.
				 */
    FILE *file;			/* file in which to place header */
    int lines;			/* number of scan lines */
    int columns;		/* Width in pixels. */
{

    int count;

    if (color == BLACK)
    {
	VersHeader[1] = lines;
	VersHeader[2] = columns;
	count = write(fileno(file), &VersHeader[0], sizeof(VersHeader));
	TxPrintf("Wrote %d bytes of control.\n", count);
    }
    return 0;
}
