home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
gondwana.ecr.mu.oz.au/pub/
/
Graphics.tar
/
Graphics
/
VOGLE.ZIP
/
DRIVERS
/
PPM.C
< prev
next >
Wrap
C/C++ Source or Header
|
2000-02-11
|
28KB
|
720 lines
/*
ppm driver for vogle; Version 1.0, John S. Urban, Feb 1997
This driver makes pbmplus-readable color (P3 and P6 format) pixmap files.
I usually use the popular netplus/pbmplus(1) packages to convert the P3
and P6 files to other formats to use in HTML documents or to import
into many other products.
The xv(1) package can display, rescale and convert P3 and P6 files too.
Unfortunately, ppm only supports single-frame files. For P3 files you
need to use voutput(3[cf]} to keep changing the output file, or use
csplit(1) on the P3 files to split them apart.
A very different but much more productive tack is taken with the binary P6
format. Writing to device p6 writes to the standard input of a command
called 'p6to' that MUST BE IN YOUR SEARCH PATH. So something like:
#!/bin/sh
# this is the p6to script used by the VOGLE p6 driver
exec cat >$$_p6.ppm
would do, but this is intended primarily for you to use with scripts that call
pbmplus commands; giving VOGLE the appearance of being able to write in dozens
of popular bitmap formats. This way, you can write GIF or PCX or JPEG or
whatever kind of bitmap you can generate with P6 input files.
In fact, I only have the pbmplus package handy on a SunOS machine, but I use
vogle the most on a Cray. No problem. I use a remote shell to get to
the command from another machine and I generate GIF images this way
without even noticing all the forks and network connections being
made -- really!
One of the easiest ways is to make a script with a case statement in it that
triggers from an environmental variable. The details vary with the scripting
language (Bourne, ksh, csh, perl, tcl/tk) but the basic idea is the same.
Here's a basic Bourne shell script:
#!/bin/sh
# this is the p6to script used by the VOGLE p6 driver
case "$P6TO" in
GIF)remsh sirius ppmtogif >$$.gif # gif file
# remsh starts a remote shell. Use rsh on some machines
# sirius seems like a good fake name for a remote Sun. But siriusly folks ...
;;
TIFF)remsh sirius pnmtotiff >$$.tiff # tiff file
;;
XBM)remsh sirius 'ppmtopgm|pgmtopbm|pbmtoxbm' > $$.xbm # greyscale X11 bitmap
;;
*)
cat > $$_p6.ppm
# if you need more control of the filename, consider doing a putenv in your
# program of a variable name that is then used to build the file name.
;;
esac
exit
The popen(); remote shell and IO redirect all work so nicely together you just
figure that anyone not using Unix just never heard of it.
Please pass any upgrades or comments back to me ( urban@cray.com) if you get a
chance.
*-----------------------------------------------------------------*
| Author: John S. Urban |
*-----------------------------------*-----------------------------*
| Cray Research | urban@cray.com | CURRENTLY PREFERRED
| Silicon Graphics, Incorporated | urban@sgi.com |
*-----------------------------------*-----------------------------*
================================================================================
USAGE NOTES ON THE PPM DRIVER:
See the PBM driver, from which I derived this.
I have used this driver on UNICOS(Cray) and NeXT but it should be reasonably
portable.
In this version of the driver, only the color number (from 0 to 255) is stored
in the pixmap. This color number is then used to generate RGB values when the
page is WRITTEN. This reduces the amount of storage needed, but means that if
you draw with pen N and then change the color of pen N and draw with it again
that everything in the printed image that used pen N will all be the same
color (the last one defined for pen N before printing). To get a "true color"
behavior would require saving RGB values for each point, which would triple
the storage requirements but would otherwise be easy to do.
Line thickness is supported with filled rectangular polygons when the line
thickness is greater than 1. Square ends are used that go only to the
endpoints unless line thickness is greater than 5, in which case complete
circles are added to the endpoints. If very short polylines are drawn with
the circles on the ends slight errors can occur.
If you have a pre-ANSI C compiler you will have to remove the PROTOTYPE
statements and change a few procedure headers back to the old K&R style.
================================================================================
References: 1) Fundamentals of Interactive Computer Graphics, Foley & Van Dam, Addison Wesley Publishing Company, 1982
2) ppm - portable bitmap file format, 27 September 1991, Copyright (C) 1989, 1991 by Jef Poskanzer.
Copyright (C) 1996, 1997 John S. Urban
This software is public domain and may be used for any purpose
commercial or otherwise. It is offered without any guarantee as to its
suitability for any purpose or as to the sanity of its writers. We do
ask that the source is passed on to anyone that requests a copy, and
that people who get copies don't go round claiming they wrote it.
PS:
If you feel guilty about using this for commercial purposes and want to
send me money go ahead. If you can't find me give something to charity
and forget about it.
================================================================================
The macro SET_PIXEL will set a given pixel in the graphics arrays :
#define SET_BYTE_R(x,y,intensity) ( *(graphics_r + (x) * Y_SIZE + (y) ) = (intensity) )
#define SET_BYTE_G(x,y,intensity) ( *(graphics_g + (x) * Y_SIZE + (y) ) = (intensity) )
#define SET_BYTE_B(x,y,intensity) ( *(graphics_b + (x) * Y_SIZE + (y) ) = (intensity) )
#define SET_PIXEL(x,y) (SET_BYTE_R((x),(y),(cur_r)),SET_BYTE_G((x),(y),(cur_g)),SET_BYTE_B((x),(y),(cur_b)))
*/
#define SET_PIXEL(x,y) ( *(graphics_rgb + (x) * Y_SIZE + (y) ) = (char)(GLOBAL_color) )
/******************************************************************************/
#define PROTOTYPE
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include "vogle.h"
extern FILE *_voutfile();
static FILE *fp;
static FILE *fpP6;
#define byte unsigned char
#define MAX(x,y) ((x) > (y) ? (x) : (y))
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define ABS(x) ((x) < 0 ? -(x) : (x))
#define PROTOTYPE
static int X_SIZE, Y_SIZE; /* size of graphics array */
byte *graphics_rgb; /* the graphics data */
#define P3 2
#define P6 3
static int GLOBAL_driver = 0;
static int GLOBAL_color = 0;
static int BITVALUE=0;
#define UNDRAWN 0
#define DRAWN 1
static int GLOBAL_drawn = UNDRAWN; /* flag whether page is blank or not */
static int GLOBAL_rasters = 1; /* line thickness */
static int GLOBAL_lastx, GLOBAL_lasty; /* position of last draw */
typedef struct {
int r, g, b;
} ColorTable;
ColorTable coltab[256];
/******************************************************************************/
static int noop() { return(-1); } /* do nothing but return-1 */
/******************************************************************************/
static PPM_MEMSET() /* set graphics array to all zero */
{
int i;
/*--- IF YOU HAVE IT, MEMSET IS PROBABLY FASTER
memset(graphics_rgb, (char)GLOBAL_color, sizeof(byte) * Y_SIZE * X_SIZE);
---*/
for ( i=0; i< (X_SIZE * Y_SIZE); i++) {
*(graphics_rgb + i) = (char)GLOBAL_color;
}
}
/******************************************************************************/
static int PPM_color(col) /* change the current color */
int col;
{
GLOBAL_color = ABS(col % 256) ;
return(0);
}
/******************************************************************************/
static int PPM_mapcolor(indx, r, g, b) /* set values in pseudo color map. */
int indx, r, g, b;
{
if (indx < 256 && indx >= 0) {
coltab[indx].r = ABS(r % 256) ;
coltab[indx].g = ABS(g % 256) ;
coltab[indx].b = ABS(b % 256) ;
}
return(0);
}
/******************************************************************************/
static int PPM_init()
{
int prefx, prefy, prefxs, prefys;
int i;
PROTOTYPE static PPM_MEMSET(); /* set graphics array to all zero */
/* ---DETERMINE SIZE OF GRAPHICS PIXMAP */
/* see if a size was user-specified using the prefsize procedure */
getprefposandsize(&prefx, &prefy, &prefxs, &prefys);
if (prefxs != -1 ) {
if (prefys <= 0 ){
fprintf(stderr,"*PPM_init* y size of %d set to 400\n",prefys);
prefys = 400;
}
else{
vdevice.sizeSy = prefys;
}
if (prefxs <= 0 ){
fprintf(stderr,"*PPM_init* y size of %d set to 600\n",prefys);
prefxs = 600;
}
else{
vdevice.sizeSx = prefxs;
}
}
else{
/* nice default value */
prefx = 0;
prefy = 0;
vdevice.sizeSy = 400;
vdevice.sizeSx = 600;
}
vdevice.sizeX = vdevice.sizeY = MIN(vdevice.sizeSy,vdevice.sizeSx);
X_SIZE=vdevice.sizeSx;
Y_SIZE=vdevice.sizeSy;
graphics_rgb = (byte *) malloc( X_SIZE * Y_SIZE * sizeof(byte) ); /* the graphics array */
PPM_MEMSET(); /* set the graphics array to 0 */
vdevice.depth = 1;
fp = _voutfile();
/* Cause scaling to be 0 to maxX maxY: prefx, vdevice.sizeSx+prefx, prefy, vdevice.sizeSy+prefy */
GLOBAL_lastx = -1111111;
GLOBAL_lasty = -1111111;
GLOBAL_drawn = UNDRAWN;
PPM_mapcolor(0, 0, 0, 0);
PPM_mapcolor(1, 255, 0, 0);
PPM_mapcolor(2, 0, 255, 0);
PPM_mapcolor(3, 255, 255, 0);
PPM_mapcolor(4, 0, 0, 255);
PPM_mapcolor(5, 255, 0, 255);
PPM_mapcolor(6, 0, 255, 255);
PPM_mapcolor(7, 255, 255, 255);
for(i=8; i<256; i++){
PPM_mapcolor(i, 255, 255, 255);
}
return(1);
}
/******************************************************************************/
static PPM_DRAW_LINE(x,y) /* draws a line across a graphics array */
int x, y;
{
int runcount;
int dx,dy;
int xinc,yinc;
int xplot,yplot;
SET_PIXEL(GLOBAL_lastx,GLOBAL_lasty); /* move to initial spot */
runcount=0;
dx = abs(GLOBAL_lastx-x);
if (x > GLOBAL_lastx) xinc= 1;
if (x == GLOBAL_lastx) xinc= 0;
if (x < GLOBAL_lastx) xinc= -1;
dy = abs(GLOBAL_lasty-y);
if (y > GLOBAL_lasty) yinc= 1;
if (y == GLOBAL_lasty) yinc= 0;
if (y < GLOBAL_lasty) yinc= -1;
xplot = GLOBAL_lastx;
yplot = GLOBAL_lasty;
if (dx>dy) {
/* iterate x */
while (xplot != x) {
xplot += xinc;
runcount += dy;
if (runcount >= (dx-runcount)) {
yplot += yinc;
runcount -= dx;
}
SET_PIXEL(xplot,yplot);
}
} else {
/* iterate y */
while (yplot != y) {
yplot += yinc;
runcount += dx;
if (runcount >= (dy-runcount)) {
xplot += xinc;
runcount -= dy;
}
SET_PIXEL(xplot,yplot);
}
}
GLOBAL_lastx = xplot;
GLOBAL_lasty = yplot;
return(0);
}
/******************************************************************************/
static PPM_ENDCAP_CIRCLE(x, y) /* Draw a circle on thick line segment end point */
int x, y;
{
/* there are more efficient ways to do this */
/* circle precision */
#define NSEGS 15
static int nsegs= NSEGS;
float cx, cy, dx, dy, angle, cosine, sine ;
/* array to place circle points on */
int cxras[NSEGS], cyras[NSEGS];
int i;
PROTOTYPE static void PPM_SOLID_FILL(int n, int x[], int y[]); /* fill polygon of n points drawn by polyline <x,y>. */
angle = 2.0 * PI / nsegs;
cosine = cos((double)angle);
sine = sin((double)angle);
/* first point on circle */
cxras[0] = cx = x + GLOBAL_rasters/2.0;
cyras[0] = cy = y;
for (i = 1; i < nsegs; i++) {
dx = cx - x;
dy = cy - y;
cxras[i] = ( cx = x + dx * cosine - dy * sine) ;
cyras[i] = ( cy = y + dx * sine + dy * cosine) ;
}
PPM_SOLID_FILL(nsegs,cxras,cyras);
}
/******************************************************************************/
static PPM_fill(n, x, y) /* "fill" a polygon */
int n, x[], y[];
{
int i;
PROTOTYPE static void PPM_SOLID_FILL( int n, int x[], int y[]);
/* update current position if needed */
GLOBAL_lastx=x[0];
GLOBAL_lasty=y[0];
for (i = 1; i < n; i++){
PPM_DRAW_LINE(x[i],y[i]); /* draw outline across graphics array */
}
if ( x[n-1] != x[0] || y[n-1] != y[0] ) /* close the polygon if it is not closed */
PPM_DRAW_LINE(x[0],y[0]);
PPM_SOLID_FILL(n, x, y);
/* update current position */
GLOBAL_lastx = vdevice.cpVx = x[n - 1];
GLOBAL_lasty = vdevice.cpVy = y[n - 1];
GLOBAL_drawn = DRAWN;
}
/******************************************************************************/
static PPM_draw(x, y) /* print the commands to draw a line from the current graphics position to (x, y). */
int x, y;
{
int holdx, holdy;
int xwide[4], ywide[4];
float cosa, sina;
double angle;
PROTOTYPE static void PPM_SOLID_FILL(int n, int x[], int y[]); /* fill polygon of n points drawn by polyline <x,y>. */
if (GLOBAL_lastx != vdevice.cpVx || GLOBAL_lasty != vdevice.cpVy){
GLOBAL_lastx=vdevice.cpVx;
GLOBAL_lasty=vdevice.cpVy;
}
if ( GLOBAL_rasters <= 1){
PPM_DRAW_LINE(x,y);
}else{
/* thick lines are made from filled polygon(s) */
/* add a circle to ends of really thick lines */
if( GLOBAL_rasters >= 6){
holdx=GLOBAL_lastx;
holdy=GLOBAL_lasty;
PPM_ENDCAP_CIRCLE(GLOBAL_lastx,GLOBAL_lasty);
PPM_ENDCAP_CIRCLE(x,y);
GLOBAL_lastx=holdx;
GLOBAL_lasty=holdy;
}
angle=atan2((double)(y-GLOBAL_lasty),(double)(x-GLOBAL_lastx)) + PI/2.0;
cosa=(GLOBAL_rasters/2.0)*cos(angle);
sina=(GLOBAL_rasters/2.0)*sin(angle);
xwide[0]=x+cosa;
xwide[1]=GLOBAL_lastx+cosa;
xwide[2]=GLOBAL_lastx-cosa;
xwide[3]=x-cosa;
ywide[0]=y+sina;
ywide[1]=GLOBAL_lasty+sina;
ywide[2]=GLOBAL_lasty-sina;
ywide[3]=y-sina;
PPM_SOLID_FILL(4,xwide,ywide);
}
GLOBAL_drawn = DRAWN;
}
/*******************************************************************************/
static P3_print_graphics() /* print_graphics -- print the graphics bit array as a ppm P3 file*/
{
int x; /* current x BYTE */
int y; /* current y location */
int bit; /* bit we are testing in the current byte */
int index, pix;
(void) fprintf(fp,"P3\n"); /* magic number of a clear text PPM file */
(void) fprintf(fp,"# CREATOR: VOGLE ppm driver; version 1.0 1997/02/02\n"); /* ppm P3 file can contain comment lines*/
(void) fprintf(fp,"# Uncopyright (C) 19970202, John S. Urban\n");
(void) fprintf(fp,"# csplit multiframe files: csplit -f P3 -k $.p3 '%%^P3%%' '/^P3/' '{999}'\n");
(void) fprintf(fp,"%d %d\n",X_SIZE,Y_SIZE); /* size of bitmap */
(void) fprintf(fp,"255\n"); /* maximum value of a color intensity*/
/* notice going from bottom to top because putting out in a right handed coordinate system, was assuming left-handed */
for (y = (Y_SIZE-1); y >= 0; y--) { /* Loop for each byte in the array */
for ( x = 0; x < X_SIZE ; x++){
index = Y_SIZE * x + y;
pix = (int)*(graphics_rgb + index);
/* The manual says a P3 ppm file should not be wider than 70 characters */
(void) fprintf(fp,"%d %d %d\n",coltab[pix].r, coltab[pix].g, coltab[pix].b);
}
} /* end of writing a column */
(void) fprintf(fp,"\n");
GLOBAL_drawn = UNDRAWN;
}
/*******************************************************************************/
static P6_print_graphics() /* print_graphics -- print the graphics bit array as a ppm P6 file*/
{
int x; /* current x BYTE */
int y; /* current y location */
int index;
fpP6 = popen("p6to", "w");
if (!fpP6) {
fprintf(stderr, "Couldn't open pipe to lpr command.\n");
exit(1);
}
(void) fprintf(fpP6,"P6\n"); /* magic number of a ppm file */
(void) fprintf(fpP6,"# CREATOR: VOGLE ppm driver; version 1.0 1997/02/02\n"); /*ppm P6 file can contain comment lines*/
(void) fprintf(fpP6,"# Uncopyright (C) 19970202, John S. Urban\n");
(void) fprintf(fpP6,"%d %d\n",X_SIZE,Y_SIZE); /* size of bitmap */
(void) fprintf(fpP6,"255\n"); /* maximum value of a color intensity*/
/* notice going from bottom to top because putting out in a right handed coordinate system, was assuming left-handed */
for (y = (Y_SIZE-1); y >= 0; y--) {
for ( x = 0; x < X_SIZE ; x++){
index = Y_SIZE * x + y;
index = *(graphics_rgb + index);
putc((char)coltab[index].r,fpP6);
putc((char)coltab[index].g,fpP6);
putc((char)coltab[index].b,fpP6);
}
}
fflush(fpP6);
if (fpP6 != stdout)
pclose(fpP6);
GLOBAL_drawn = UNDRAWN;
}
/******************************************************************************/
static PPM_PRINT() /* exit from vogle printing the command to flush the buffer. */
{
if( GLOBAL_drawn ){
switch(GLOBAL_driver) {
case P3:
P3_print_graphics();
break;
case P6:
P6_print_graphics();
break;
default:
fprintf(stderr, "ppm driver: UNKNOWN DRIVER NAME\n");
P3_print_graphics();
}
}
fflush(fp); /* flush the output file */
}
/******************************************************************************/
static PPM_exit() /* exit from vogle printing the command to flush the buffer. */
{
PPM_PRINT();
if (fp != stdout){
fclose(fp);
}
}
/*******************************************************************************/
static PPM_setlw(w) /* Set the line width */
int w;
{
if (w == 0)
w = 1;
else if (w == 1)
w = 2;
GLOBAL_rasters = MAX(1,w);
}
/******************************************************************************/
static PPM_clear() /* flush current page and clear graphics array */
{
PROTOTYPE static PPM_MEMSET(); /* set graphics array to all zero */
PPM_PRINT();
PPM_MEMSET();
}
/******************************************************************************/
static int PPM_font(font) /* load in large or small */
char *font;
{
fprintf(stderr, "E-R-R-O-R: NO HARDWARE FONT\n");
if (strcmp(font, "small") == 0) {
vdevice.hwidth = 97.01; /* Size in plotter resolution units */
vdevice.hheight = vdevice.hwidth * 2.0;
} else if (strcmp(font, "large") == 0) {
vdevice.hwidth = 145.5;
vdevice.hheight = vdevice.hwidth * 2.0;
} else
return(0);
return(1);
}
/******************************************************************************/
static PPM_string(s) /* output a string. */
char *s;
{
int dy, dx;
if (GLOBAL_lastx != vdevice.cpVx || GLOBAL_lasty != vdevice.cpVy){
GLOBAL_lastx=vdevice.cpVx;
GLOBAL_lasty=vdevice.cpVy;
}
fputs(s, fp);
GLOBAL_lastx = GLOBAL_lasty = -1111111; /* undefine current position because used hardware text ?*/
GLOBAL_drawn = DRAWN;
}
/******************************************************************************/
static PPM_char(c) /* output a character */
char c;
{
char s[2];
s[0] = c; s[1]='\0';
PPM_string(s);
}
/******************************************************************************/
static DevEntry PPMdev = {
"ppm", /* name of device */
"large", /* name of large font */
"small", /* name of small font */
noop, /* Set drawing in back buffer */
PPM_char, /* Draw a hardware character */
noop, /* Check if a key was hit */
PPM_clear, /* Clear the screen to current color */
PPM_color, /* Set current color */
PPM_draw, /* Draw a line */
PPM_exit, /* Exit graphics */
PPM_fill, /* Fill a polygon */
PPM_font, /* Set hardware font */
noop, /* Set drawing in front buffer */
noop, /* Wait for and get the next key hit */
PPM_init, /* Initialize the device */
noop, /* Get mouse/cross hair position */
PPM_mapcolor,/* Set color indices */
PPM_setlw, /* Set line width */
PPM_string, /* Draw a hardware string */
noop, /* Swap front and back buffers */
noop /* Syncronize the display */
};
/******************************************************************************/
_P3_devcpy()
{
vdevice.dev = PPMdev;
vdevice.dev.Vinit = PPM_init;
GLOBAL_driver = P3;
}
/******************************************************************************/
_P6_devcpy()
{
vdevice.dev = PPMdev;
vdevice.dev.Vinit = PPM_init;
GLOBAL_driver = P6;
}
/*******************************************************************************/
static PPM_YINTERCEPT(yscan, x1, y1, x2, y2, xintercept,yprev)
/*
Determine if scan line intercepts the line segment. If it does, return the x intercept.
*/
int yscan, x1, y1, x2, y2, *xintercept, *yprev;
{
int deltay, yprevious;
float t;
yprevious = *yprev; /* the value we need to use in this pass */
*yprev = y1; /* store the value for the next call to (probably) use */
deltay = y2 - y1;
if ( deltay == 0 ){
/* horizontal lines do not contribute to scan line intercepts */
*yprev=yprevious;
return(0);
}
t = (float)(yscan - y1) / deltay;
if (t > 0.0 && t <= 1.0) {
/* scan line and line segment intersect but not at leading vertex */
*xintercept = x1 + t*(x2 - x1) + 0.5;
return (1);
} else if ( t == 0.0 ){
/* scan line and line segment intersect at leading vertex */
*xintercept = x1 + t*(x2 - x1) + 0.5;
if(yprevious <= y1 && y2 <= y1 ){
/* local maximum */
return (1);
} else if(yprevious >= y1 && y2 >= y1 ){
/* local minimum */
return (1);
} else{
/* ignore duplicate at vertex that is not a local maximum or minimum */
return (0);
}
}
/* scan line and line segment did not intersect */
return (0);
}
/*******************************************************************************/
static void PPM_SOLID_FILL(n, x, y) /* fill polygon of n points drawn by polyline <x,y>. */
int n, x[], y[];
{
int i, j, sorted, yhorizontal, xint, tmp, xmin, xmax, ymax, ymin, xi[MAXVERTS], yprev;
if ( n > MAXVERTS) {
fprintf(stderr,"*PPM_SOLID_FILL* more than %d vertices in a polygon\n",MAXVERTS);
return;
}
/* find clip range */
ymin = ymax = y[0];
xmin = xmax = y[0];
for (i = 0; i < n; i++) {
ymax = MAX(ymax, y[i]);
ymin = MIN(ymin, y[i]);
xmax = MAX(xmax, x[i]);
xmin = MIN(xmin, x[i]);
}
/* ensure scan lines are generated that do not cause out-of-bound problems in the y direction */
ymin=MAX(ymin,0);
ymax=MIN(ymax,Y_SIZE);
/* For each y value, get a list of X intersections... */
yhorizontal = ymax ;
while (yhorizontal >= ymin) {
j = 0;
yprev = y[n-1];
for (i = 0; i < n-1; i++)
if (PPM_YINTERCEPT(yhorizontal, x[i], y[i], x[i+1], y[i+1], &xint, &yprev))
xi[j++] = xint;
/* Last one. */
if (PPM_YINTERCEPT(yhorizontal, x[n-1], y[n-1], x[0], y[0], &xint, &yprev))
xi[j++] = xint;
/* odd pairs means something went wrong in figuring out whether to count vertices or not */
if( 2 * (j/2) != j){
fprintf(stderr,"*PPM_SOLID_FILL* Internal error: odd number of intersection points (%d) \n",j);
}
/* Sort the X intersections... */
sorted = 0;
while (!sorted) {
sorted = 1;
for (i = 0; i < j-1; i++)
if (xi[i] > xi[i+1]) {
tmp = xi[i];
xi[i] = xi[i+1];
xi[i+1] = tmp;
sorted = 0;
}
}
/* Draw the horizontal lines */
/* should make sure within X clipping range */
for (i = 0; i < j-1; i += 2) {
GLOBAL_lastx=MAX(0,MIN(xi[i],X_SIZE));
GLOBAL_lasty=yhorizontal;
PPM_DRAW_LINE(MAX(0,MIN(xi[i+1],X_SIZE)), yhorizontal);
}
yhorizontal -= 1;
}
}
/*******************************************************************************/