/******************************************************************************
 * draw.c - Chapter 4 sample code                                             *
 *                                                                            *
 * To be used with Linear Aperture mach64 sample code.                        *
 * This module contains draw functions supporting the Linear frame            *
 * buffer for all mach64 spatial and pixel depth resolutions.                 *
 * functions to clear the screen, draw points, draw lines, draw rects,        *
 * and draw filled polygons are included.                                     *
 *                                                                            *
 * Copyright (c) 1994-1998 ATI Technologies Inc.  All rights reserved.        *
 ******************************************************************************/

#include <stdlib.h>
#include "defines.h"
#include "main.h"

// Prototypes.

int draw_hline (int y0, int x0, int x1, int colour);
int draw_vline (int x0, int y0, int y1, int colour);
int read_host (char *host, unsigned long position);     // position = y*pitch+x
int toggle_host (char *host, unsigned long position);   // position = y*pitch+x
int draw_memline (char *host, int height, int width,
                  int x0, int y0, int x1, int y1);

/******************************************************************************
 * clear_screen                                                               *
 *  Function: clears graphics screen memory to black.                         *
 *            MODE_INFO must be already set.                                  *
 *    Inputs: NONE                                                            *
 *   Outputs: 0 - unsuccessful                                                *
 *            1 - successful                                                  *
 ******************************************************************************/

int clear_screen (void)
{
    int pixel_pitch;

    // Determine pixel size in bytes.
    switch (MODE_INFO.bpp)
    {
        case 8:
            pixel_pitch = 1;
            break;

        case 15:
        case 16:
            pixel_pitch = 2;
            break;

        case 24:
            pixel_pitch = 3;
            break;

        case 32:
            pixel_pitch = 4;
            break;

        default:
            return (0);
    } // switch

    // Clear screen by writing zeros into video memory.
    memset (MODE_INFO.virt_seg, 0x00,
            (size_t) (pixel_pitch * MODE_INFO.xres * MODE_INFO.yres));

    return (1);

} // clear_screen


/******************************************************************************
 * draw_point                                                                 *
 *  Function: draws a point on the screen by direct memory write to the       *
 *            linear aperture.                                                *
 *    Inputs: x coordinate in pixels (0 = left, xres = right)                 *
 *            y coordinate in pixels (0 = top, yres = bot)                    *
 *            colour values (0 - 16) representing first 16 VGA colours        *
 *   Outputs: 0 - unsuccessful                                                *
 *            1 - successful                                                  *
 ******************************************************************************/

int draw_point (int x, int y, int colour)
{
    char *pointer;

    // Validate parameters.
    if ((x < 0) || (x >= MODE_INFO.xres) || (y < 0) || (y >= MODE_INFO.yres))
    {
        return (0);
    } // if
    if ((colour < 0) || (colour >= NUM_COLOURS))
    {
        return (0);
    } // if

    // Draw point based on pixel depth.
    switch (MODE_INFO.bpp)
    {
        case 8:
            pointer = (char *) (MODE_INFO.virt_seg + y*MODE_INFO.xres + x);
            *pointer = get_colour_code (colour);
            break;

        case 15:
        case 16:
            pointer = (char *) (MODE_INFO.virt_seg + 2*(y*MODE_INFO.xres + x));
            *pointer = get_colour_code (colour);
            *(pointer + 1) = get_colour_code (colour) >> 8;
            break;

        case 24:
            pointer = (char *) (MODE_INFO.virt_seg + 3*(y*MODE_INFO.xres + x));
            *pointer = get_colour_code (colour);
            *(pointer + 1) = get_colour_code (colour) >> 8;
            *(pointer + 2) = get_colour_code (colour) >> 16;
            break;

        case 32:
            pointer = (char *) (MODE_INFO.virt_seg + 4*(y*MODE_INFO.xres + x));
            *pointer = get_colour_code (colour);
            *(pointer + 1) = get_colour_code (colour) >> 8;
            *(pointer + 2) = get_colour_code (colour) >> 16;
            *(pointer + 3) = get_colour_code (colour) >> 24;
            break;
    } // switch

    return (1);

} // draw_point


/******************************************************************************
 * draw_frect                                                                 *
 *  Function: draws a filled rectangle on the screen using draw_hline and     *
 *            linear aperture.                                                *
 *    Inputs: x0 1st coordinate in pixels (0 = left, xres = right)            *
 *            y0 1st coordinate in pixels (0 = top, yres = bot)               *
 *            x1 2nd coordinate in pixels (0 = left, xres = right)            *
 *            y1 2nd coordinate in pixels (0 = top, yres = bot)               *
 *            colour values (0 - 16) representing first 16 vga colours        *
 *   Outputs: 0 - unsuccessful                                                *
 *            1 - successful                                                  *
 ******************************************************************************/
int draw_frect (int x0, int y0, int x1, int y1, int colour)
{
    int row;
    int temp;

    // Ensure rectangle is on screen and colour is valid.
    if ((x0 < 0) || (x0 >= MODE_INFO.xres) ||
        (y0 < 0) || (y0 >= MODE_INFO.yres))
    {
        return (0);
    } // if
    if ((x1 < 0) || (x1 >= MODE_INFO.xres) ||
        (y1 < 0) || (y1 >= MODE_INFO.yres))
    {
        return (0);
    } // if
    if ((colour < 0) || (colour >= NUM_COLOURS))
    {
        return (0);
    } // if

    // Make (x0, y0) the top left point.

    if (x0 > x1)
    {
        temp = x0;
        x0 = x1;
        x1 = temp;
    } // if
    if (y0 > y1)
    {
        temp = y0;
        y0 = y1;
        y1 = temp;
    } // if

    // Draw rectangle.

    for (row = y0; row < y1; row++)
    {
        draw_hline (row, x0, x1, colour);
    } // for

    return (1);

} // draw_frect


/******************************************************************************
 * draw_line                                                                  *
 *  Function: draws a line using the Bresenham algorithm and the linear       *
 *            aperture.                                                       *
 *    Inputs: x0 1st coordinate in pixels (0 = left, xres = right)            *
 *            y0 1st coordinate in pixels (0 = top, yres = bot)               *
 *            x1 2nd coordinate in pixels (0 = left, xres = right)            *
 *            y1 2nd coordinate in pixels (0 = top, yres = bot)               *
 *            colour values (0 - 16) representing first 16 VGA colours        *
 *   Outputs: 0 - unsuccessful                                                *
 *            1 - successful                                                  *
 ******************************************************************************/

int draw_line (int x0, int y0, int x1, int y1, int colour)
{
    register int t, dist;
    int xerr = 0, yerr = 0, xdel = 0, ydel = 0;
    int xinc, yinc;

    // Ensure line is on screen and colour is valid.
    if ((x0 < 0) || (x0 >= MODE_INFO.xres) ||
        (y0 < 0) || (y0 >= MODE_INFO.yres))
    {
        return (0);
    } // if
    if ((x1 < 0) || (x1 >= MODE_INFO.xres) ||
        (y1 < 0) || (y1 >= MODE_INFO.yres))
    {
        return (0);
    } // if
    if ((colour < 0) || (colour >= NUM_COLOURS))
    {
        return (0);
    } // if

    // Draw line, handling special case lines first.
    if (y0 == y1)
    {
        // Special case - horizontal line.
        draw_hline (y0, x0, x1, colour);
    }
    else if (x0 == x1)
    {
        // Special case - vertical line.
        draw_vline (x0, y0, y1, colour);
    }
    else
    {
        // General line.
        // Compute distances.
        xdel = x1 - x0;
        ydel = y1 - y0;

        // Compute direction.
        xinc = (xdel > 0) ? 1 : -1;     // xdel == 0 already handled above
        yinc = (ydel > 0) ? 1 : -1;     // ydel == 0 already handled above

        // Determine greater distance.
        xdel = abs (xdel);
        ydel = abs (ydel);
        dist = (xdel > ydel) ? xdel : ydel;

        // Draw line.
        for (t = 0; t <= dist + 1; t++)
        {
            draw_point (x0, y0, colour);
            xerr += xdel;
            yerr += ydel;
            if (xerr > dist)
            {
                xerr -= dist;
                x0 += xinc;
            } // if
            if (yerr > dist)
            {
                yerr -= dist;
                y0 += yinc;
            } // if
        } // for
    } // if

    return (1);

} // draw_line


/******************************************************************************
 * draw_hline                                                                 *
 *  Function: draws a horizontal line on the screen using memset and the      *
 *            linear aperture.                                                *
 *    Inputs: x0 1st coordinate in pixels (0 = left, xres = right)            *
 *            y0 1st coordinate in pixels (0 = top, yres = bot)               *
 *            x1 2nd coordinate in pixels (0 = left, xres = right)            *
 *            Note y1 is the same as y0, so it is not passed                  *
 *            colour values (0 - 16) representing first 16 vga colours        *
 *   Outputs: 0 - unsuccessful                                                *
 *            1 - successful                                                  *
 ******************************************************************************/

int draw_hline (int y0, int x0, int x1, int colour)
{
    char *pointer;
    int xs, xdel;
    int count;
    unsigned long col;

    // Ensure line is on screen and colour is valid.
    if ((x0 < 0) || (x0 >= MODE_INFO.xres) ||
        (y0 < 0) || (y0 >= MODE_INFO.yres))
    {
        return (0);
    } // if
    if ((x1 < 0) || (x1 >= MODE_INFO.xres))
    {
        return (0);
    } // if
    if ((colour < 0) || (colour >= NUM_COLOURS))
    {
        return (0);
    } // if

    // Make x0 the lesser value.
    if (x0 < x1)
    {
        xdel = x1 - x0 + 1;
        xs = x0;
    }
    else
    {
        xdel = x0 - x1 + 1;
        xs = x1;
    } // if

    // Draw horizontal line.
    col = get_colour_code (colour);
    switch(MODE_INFO.bpp)
    {
        case 8:
            pointer = (char *) (MODE_INFO.virt_seg + y0*MODE_INFO.xres + xs);
            memset (pointer, col, xdel);
            break;

        case 15:
        case 16:
            pointer = (char *) (MODE_INFO.virt_seg + 2*(y0*MODE_INFO.xres + xs));
            for (count = 0; count < xdel; count++)
            {
                *pointer = col;
                *(pointer + 1) = col >> 8;
                pointer += 2;
            } // for
            break;

        case 24:
            pointer = (char *) (MODE_INFO.virt_seg + 3*(y0*MODE_INFO.xres + xs));
            for (count = 0; count < xdel; count++)
            {
                *pointer = col;
                *(pointer + 1) = col >> 8;
                *(pointer + 2) = col >> 16;
                pointer += 3;
            } // for
            break;

        case 32:
            pointer = (char *) (MODE_INFO.virt_seg + 4*(y0*MODE_INFO.xres + xs));
            for (count = 0; count < xdel; count++)
            {
                *pointer = col;
                *(pointer + 1) = col >> 8;
                *(pointer + 2) = col >> 16;
                *(pointer + 3) = col >> 24;
                pointer += 4;
            } // for
            break;

        default:
            return (0);
    } // switch

    return (1);

} // draw_hline


/******************************************************************************
 * draw_vline                                                                 *
 *  Function: draws a vertical line on the screen using draw_hline and        *
 *            linear aperture.                                                *
 *    Inputs: x0 1st coordinate in pixels (0 = left, xres = right)            *
 *            y0 1st coordinate in pixels (0 = top, yres = bot)               *
 *            Note x1 is the same as x0 and is not passed.                    *
 *            y1 2nd coordinate in pixels (0 = top, yres = bot)               *
 *            colour values (0 - 16) representing first 16 vga colours        *
 *   Outputs: 0 - unsuccessful                                                *
 *            1 - successful                                                  *
 ******************************************************************************/

int draw_vline (int x0, int y0, int y1, int colour)
{
    char *pointer;
    int ys, ydel, count;
    unsigned long col;

    // Ensure line is on screen and colour is valid.
    if ((x0 < 0) || (x0 >= MODE_INFO.xres) ||
        (y0 < 0) || (y0 >= MODE_INFO.yres))
    {
        return (0);
    } // if
    if ((y1 < 0) || (y1 >= MODE_INFO.yres))
    {
        return (0);
    } // if
    if ((colour < 0) || (colour >= NUM_COLOURS))
    {
        return (0);
    } // if

    // Make y0 the lesser value.
    if (y0 < y1)
    {
        ydel = y1 - y0;
        ys = y0;
    }
    else
    {
        ydel = y0 - y1;
        ys = y1;
    } // if

    // Draw vertical line.
    col = get_colour_code (colour);
    switch (MODE_INFO.bpp)
    {
        case 8:
            pointer = (char *) (MODE_INFO.virt_seg + ys*MODE_INFO.xres + x0);
            for (count = 0; count <= ydel; count++)
            {
                *pointer = col;
                pointer += MODE_INFO.xres;
            } // for
            break;

        case 15:
        case 16:
            pointer = (char *) (MODE_INFO.virt_seg + 2*(ys*MODE_INFO.xres + x0));
            for (count = 0; count <= ydel; count++)
            {
                *pointer = col;
                *(pointer + 1) = col >> 8;
                pointer += 2*MODE_INFO.xres;
            } // for
            break;

        case 24:
            pointer = (char *) (MODE_INFO.virt_seg + 3*(ys*MODE_INFO.xres + x0));
            for (count = 0; count <= ydel; count++)
            {
                *pointer = col;
                *(pointer + 1) = col >> 8;
                *(pointer + 2) = col >> 16;
                pointer += 3*MODE_INFO.xres;
            } // for
            break;

        case 32:
            pointer = (char *) (MODE_INFO.virt_seg + 4*(ys*MODE_INFO.xres + x0));
            for (count = 0; count < ydel; count++)
            {
                *pointer = col;
                *(pointer + 1) = col >> 8;
                *(pointer + 2) = col >> 16;
                *(pointer + 3) = col >> 24;
                pointer += 4*MODE_INFO.xres;
            } // for
            break;

        default:
            return (0);
    } // switch

    return (1);

} // draw_vline


/******************************************************************************
 * draw_rect                                                                  *
 *  Function: draws an open rectangle on the screen using draw_hline and      *
 *            draw_vline and the linear aperture.                             *
 *    Inputs: x0 1st coordinate in pixels (0 = left, xres = right)            *
 *            y0 1st coordinate in pixels (0 = top, yres = bot)               *
 *            x1 2nd coordinate in pixels (0 = left, xres = right)            *
 *            y1 2nd coordinate in pixels (0 = top, yres = bot)               *
 *            colour values (0 - 16) representing first 16 vga colours        *
 *   Outputs: 0 - unsuccessful                                                *
 *            1 - successful                                                  *
 ******************************************************************************/

int draw_rect (int x0, int y0, int x1, int y1, int colour)
{
    // Ensure rectangle is on screen and colour is valid.
    if ((x0 < 0) || (x0 >= MODE_INFO.xres) ||
        (y0 < 0) || (y0 >= MODE_INFO.yres))
    {
        return (0);
    } // if
    if ((x1 < 0) || (x1 >= MODE_INFO.xres) ||
        (y1 < 0) || (y1 >= MODE_INFO.yres))
    {
        return (0);
    } // if
    if ((colour < 0) || (colour >= NUM_COLOURS))
    {
        return (0);
    } // if

    // Draw rectangle.
    draw_vline (x0, y0, y1, colour);
    draw_hline (y1, x0, x1, colour);
    draw_vline (x1, y1, y0, colour);
    draw_hline (y0, x1, x0, colour);

    return (1);

} // draw_rect


/******************************************************************************
 * draw_polygon                                                               *
 *  Function: draws a polygon on the screen by direct memory write to the     *
 *            linear aperture.  Special off screen buffer is created to       *
 *            determine fill regions.  This ensures that objects currently    *
 *            on screen will not interfere with the filling.  The outline     *
 *            of the polygon is first draw using the polygon structure of     *
 *            points.  A duplicate drawing is created in the buffer with      *
 *            1 bit representing a pixel.  The bits are toggled to            *
 *            appropriately indicate start and stop regions (horizontally)    *
 *            to fill.  The algoritm uses this buffer to determine what to    *
 *            fill.  Each horizontal pixel line is scanned from the buffer    *
 *            and appropriate horizontal lines are drawn on screen until      *
 *            the bottom of the shape is reached.                             *
 *    Inputs: pgon - structure containing list of point, and # of points      *
 *            xoff - x direction offset from the left in pixels               *
 *            yoff - y direction offset from the left in pixels               *
 *            colour values (0 - 16) representing first 16 vga colours        *
 *   Outputs: 0 - unsuccessful                                                *
 *            1 - successful                                                  *
 ******************************************************************************/

int draw_polygon (polygon *pgon, int xoff, int yoff, int colour)
{
    int count, row, col;
    int top, bottom ,left, right;
    int height, width;
    int flag;
    char *host;
    int this;
    int xold, yold;
    point *vertex;

    vertex = pgon->point_ptr;

    // Check to see if all points are on screen.
    for (count = 0; count < pgon->length; count++)
    {
        if ((vertex[count].x + xoff) < 0 ||
            (vertex[count].x + xoff) > (MODE_INFO.xres - 1) ||
            (vertex[count].y + yoff) < 0 ||
            (vertex[count].y + yoff) > (MODE_INFO.yres - 1))
        {
            return (0);
        } // if
    } // for

    // Find top, bottom, left and right.
    top = bottom = (vertex[0].y + yoff);
    left = right = (vertex[0].x + xoff);
    for (count = 1; count < pgon->length; count++)
    {
        if ((vertex[count].y + yoff) < top)
        {
            top = vertex[count].y + yoff;
        }
        else if ((vertex[count].y + yoff) > bottom)
        {
            bottom = vertex[count].y + yoff;
        } // if

        if ((vertex[count].x + xoff) < left)
        {
            left = vertex[count].x + xoff;
        }
        else if ((vertex[count].x + xoff) > right)
        {
            right = vertex[count].x + xoff;
        } // if
    } // for

    // Allocate space to create shape in host memory.
    height = bottom - top + 1;
    width = right - left + 1;
    host = (char *) calloc ((int) (1 + (height * ((float) width/8))),
                            sizeof (char));
    if (host == NULL) return (0);       // Error: no free memory.

    // Draw outline of polygon on screen and in host memory
    // vertices that are max and min have a value set to two points
    // this is automatically done when drawing the lines, but points that
    // are not maxs or mins need one point removed.
    // Further the draw line in memory routine draws y-independent lines
    // ie - only one point is drawn for each horizontal section of line.
    // This allows the fill switch to work properly.
    // Pure horizontal lines are not drawn in memory.  The fill ignores them.

    // Take care of first vertex.
    draw_line ((vertex[0].x + xoff), (vertex[0].y + yoff),
               (vertex[1].x + xoff), (vertex[1].y + yoff), colour);
    draw_memline (host, height, width,
                  (vertex[0].x - left + xoff), (vertex[0].y - top + yoff),
                  (vertex[1].x - left + xoff), (vertex[1].y - top + yoff));
    if (((vertex[0].y < vertex[1].y) &&
         (vertex[0].y > vertex[pgon->length-1].y)) ||
        ((vertex[0].y > vertex[1].y) &&
         (vertex[0].y < vertex[pgon->length-1].y)))
    {
        toggle_host (host,
                     ((unsigned long)(vertex[0].y + yoff - top))*width +
                      vertex[0].x + xoff - left);
    } // if

    // Take care of middle vertices.
    for (count = 1; count < pgon->length-1; count++)
    {
        draw_line (vertex[count].x + xoff, vertex[count].y + yoff,
                   vertex[count+1].x + xoff, vertex[count+1].y + yoff, colour);
        draw_memline (host, height, width,
                      vertex[count].x - left + xoff,
                      vertex[count].y - top + yoff,
                      vertex[count+1].x - left + xoff,
                      vertex[count+1].y - top + yoff);
        if (((vertex[count].y < vertex[count-1].y) &&
             (vertex[count].y > vertex[count+1].y)) ||
            ((vertex[count].y > vertex[count-1].y) &&
             (vertex[count].y < vertex[count+1].y)))
        {
            toggle_host (host,
                         ((unsigned long)(vertex[count].y + yoff - top))*width +
                         vertex[count].x + xoff - left);
        } // if
    } // for

    // Take care of last vertex.
    draw_line (vertex[pgon->length - 1].x + xoff,
               vertex[pgon->length - 1].y + yoff,
               vertex[0].x + xoff, vertex[0].y + yoff, colour);
    draw_memline (host, height, width,
                  vertex[pgon->length - 1].x - left + xoff,
                  vertex[pgon->length - 1].y - top + yoff,
                  vertex[0].x - left + xoff, vertex[0].y - top + yoff);
    if (((vertex[pgon->length-1].y < vertex[0].y) &&
         (vertex[pgon->length-1].y > vertex[pgon->length-2].y)) ||
        ((vertex[pgon->length-1].y > vertex[0].y) &&
         (vertex[pgon->length-1].y < vertex[pgon->length-2].y)))
    {
        toggle_host (host,
                     ((unsigned long)(vertex[pgon->length-1].y + yoff - top)) * width +
                     vertex[pgon->length-1].x + xoff - left);
    } // if

    // height = 0. Avoid infinite loop.
    if (bottom == top)
    {
        free (host);
        return (1);
    }

    // Scan each line and fill where appropriate.  The decision is made by
    // using the Host memory but the actual filling is only done on screen.
    for (row = top; row <= bottom; row ++)
    {
        flag = OFF;

        // Take care of the case where we start filling at column 0.

        if (read_host (host, (((unsigned long) (row - top))*width)))
        {
            flag = ON;
            xold = left;
            yold = row;
        } // if

        // Traverse across each row.
        for (col = left + 1; col <= right; col++)
        {
            this = read_host (host, ((unsigned long) (row - top))*width +
                                    col - left);

            if (flag == OFF)
            {
                // Filling is off.
                if (this)
                {
                    flag = ON;          // Indicate we are in fill section.
                    xold = col;
                    yold = row;
                } // if
            }
            else if (flag == ON)
            {
                // Filling is on.
                if (this)
                {
                    flag = OFF;         // Turn fill off when end is reached.
                    draw_hline (yold, xold, col, colour);
                } // if
            } // if
        }
    } // for
    free (host);

    return (1);

} // draw_polygon


/******************************************************************************
 * read_host                                                                  *
 *  Function: Used with draw_polygon.  It reads the bit corresponding to      *
 *            the pixel on the screen at position (x,y).  value is 0 or 1     *
 *    Inputs: *host -  pointer to buffer of polygon                           *
 *            position - location of bit to read = y * pitch + x              *
 *   Outputs: value - bit value                                               *
 ******************************************************************************/

int read_host (char *host, unsigned long position)
{
    unsigned long byte_num;
    int bit_num;
    char value;
    char mask;

    byte_num = position / 8;
    bit_num = position % 8;
    value = host[byte_num];             // Read byte containing bit info.
    if (!value) 
    {
        return (0);
    }

    mask = 0;                           // Initialize mask.
    mask = (1 << bit_num);              // Set up mask.
    value = mask & value;               // Get bit in question.

    return (value);

} // read_host


/******************************************************************************
 * toggle_host                                                                *
 *  Function: read, modify, write of bit corresponding to on screen pixel     *
 *            The bit is toggled.                                             *
 *    Inputs: *host - pointer to buffer of polygon                            *
 *            position - location of bit to read = y * pitch + x              *
 *   Outputs: value of 1 alway returned                                       *
 ******************************************************************************/

int toggle_host (char *host, unsigned long position)
{
    unsigned long byte_num;
    int bit_num;
    char value;
    char mask;

    byte_num = position / 8;
    bit_num = position % 8;
    value = host[byte_num];             // Read byte containing bit info.
    mask = 0;                           // Initialize mask.
    mask = (1 << bit_num);              // Set up mask.
    if (mask & value)                   // Bit is on.
    {
        mask = ~mask;                   // 1's complement.
        value = value & mask;           // Toggle required bit to 0.
    }
    else                                // Bit is off.
    {
        value = value | mask;           // Toggle required bit to 1.
    } // if
    host[byte_num] = value;             // Write byte.

    return (1);

} // toggle_host


/******************************************************************************
 * draw_mem_line                                                              *
 *  Function: used with draw_polygon.  line is drawn in a host buffer         *
 *            corresponding to an on screen image.  Lines are drawn as        *
 *            y-independant lines for fill algoritm reasons.                  *
 *    Inputs: *host - pointer to buffer of polygon                            *
 *            height of buffer                                                *
 *            width of buffer                                                 *
 *            x0 1st coordinate in pixels (0 = left, xres = right)            *
 *            y0 1st coordinate in pixels (0 = top, yres = bot)               *
 *            x1 2nd coordinate in pixels (0 = left, xres = right)            *
 *            y1 2nd coordinate in pixels (0 = top, yres = bot)               *
 *   Outputs: 1 is returned upon completion                                   *
 ******************************************************************************/

int draw_memline (char *host, int height, int width,
                  int x0, int y0, int x1, int y1)
{
    int t, dist;
    int xerr = 0, yerr = 0, xdel = 0, ydel = 0;
    int xinc, yinc;

    // Check to make sure that everything fits on screen.
    if ((x0 < 0) || (x0 >= width) || (y0 < 0) || (y0 >= height))
    {
        return(0);
    } // if
    if ((x1 < 0) || (x1 >= width) || (y1 < 0) || (y1 >= height))
    {
        return(0);
    } // if

    // Compute distances.
    xdel = x1 - x0;
    ydel = y1 - y0;

    // Compute direction.
    if (xdel > 0)
    {
        xinc = 1;
    }
    else if (xdel == 0)
    {
        xinc = 0;
    }
    else
    {
        xinc = -1;
    } // if
    if (ydel > 0)
    {
        yinc = 1;
    }
    else if (ydel == 0)
    {
        yinc = 0;
    }
    else
    {
        yinc = -1;
    } // if

    // Determine greater distance.
    xdel = abs (xdel);
    ydel = abs (ydel);
    if (xdel > ydel)
    {
        dist = xdel;
    }
    else
    {
        dist = ydel;
    } // if

    // Draw line if not horizontal - Horizontal lines are ignored.
    if (y0 == y1)
    {
        toggle_host (host, ((unsigned long)y0) * width + x0);
    }
    else
    {
        // Draw first point.
        toggle_host (host, ((unsigned long)y0) * width + x0);
        for (t = 1; t <= dist + 1; t++)
        {
            xerr += xdel;
            yerr += ydel;
            if (xerr > dist)
            {
                xerr -= dist;
                x0 += xinc;
            } // if

            // Make sure lines are y-independent
            if (yerr > dist)
            {
                yerr -= dist;
                y0 += yinc;
                toggle_host (host, ((unsigned long) y0) * width + x0);
            } // if
        } // for
    } // if

    return (1);

} // draw_memline
