home *** CD-ROM | disk | FTP | other *** search
/ gondwana.ecr.mu.oz.au/pub/ / Graphics.tar / Graphics / VOGLE.ZIP / DRIVERS / PPM.C < prev    next >
C/C++ Source or Header  |  2000-02-11  |  28KB  |  720 lines

  1. /*
  2. ppm driver for vogle; Version 1.0, John S. Urban, Feb 1997
  3.  
  4. This driver makes  pbmplus-readable color (P3 and P6  format)  pixmap   files.
  5.  
  6. I usually use the popular netplus/pbmplus(1) packages to    convert  the    P3
  7. and   P6    files    to    other formats to use in HTML documents or to import
  8. into many other products.
  9.  
  10. The xv(1) package can display, rescale and convert P3 and P6 files too.
  11.  
  12. Unfortunately,  ppm  only  supports  single-frame  files.  For  P3  files  you
  13. need   to   use  voutput(3[cf]}   to   keep   changing the output file, or use
  14. csplit(1) on the P3 files to split them apart.
  15.  
  16. A very different but much more productive tack is taken  with  the  binary  P6
  17. format.   Writing  to device p6 writes to the  standard  input  of  a  command
  18. called   'p6to'   that  MUST BE IN YOUR SEARCH PATH. So something like:
  19.  
  20. #!/bin/sh
  21. # this is the p6to script used by the VOGLE p6 driver
  22. exec cat >$$_p6.ppm
  23.  
  24. would do, but this is intended primarily for you to use with scripts that call
  25. pbmplus commands; giving VOGLE the appearance of being able to write in dozens
  26. of popular bitmap formats. This way, you can write  GIF  or  PCX  or  JPEG  or
  27. whatever kind of bitmap you can generate with P6 input files.
  28.  
  29. In fact, I only have the pbmplus package handy on a  SunOS machine, but I  use
  30. vogle  the  most on a Cray. No problem. I   use   a remote   shell   to get to
  31. the command from another machine and I  generate   GIF   images    this    way
  32. without    even noticing   all   the   forks   and   network connections being
  33. made -- really!
  34.  
  35. One of the easiest ways is to make a script with a case statement in  it  that
  36. triggers  from an environmental variable.  The details vary with the scripting
  37. language (Bourne, ksh, csh, perl, tcl/tk) but the  basic  idea  is  the  same.
  38. Here's a basic Bourne shell script:
  39.  
  40. #!/bin/sh
  41. # this is the p6to script used by the VOGLE p6 driver
  42. case "$P6TO" in
  43. GIF)remsh sirius ppmtogif >$$.gif # gif file
  44. # remsh starts a remote shell. Use rsh on some machines
  45. # sirius seems like a good fake name for a remote Sun. But siriusly folks ...
  46. ;;
  47. TIFF)remsh sirius pnmtotiff >$$.tiff # tiff file
  48. ;;
  49. XBM)remsh sirius 'ppmtopgm|pgmtopbm|pbmtoxbm' > $$.xbm # greyscale X11 bitmap
  50. ;;
  51. *)
  52. cat > $$_p6.ppm
  53. # if you need more control of the filename, consider doing a putenv in your
  54. # program of a variable name that is then used to build the file name.
  55. ;;
  56. esac
  57. exit
  58.  
  59. The popen(); remote shell and IO redirect all work so nicely together you just
  60. figure that anyone not using Unix just never heard of it.
  61.  
  62. Please pass any upgrades or comments back to me ( urban@cray.com) if you get a
  63. chance.
  64.  
  65. *-----------------------------------------------------------------*
  66. | Author: John S. Urban                                           |
  67. *-----------------------------------*-----------------------------*
  68. | Cray Research                     | urban@cray.com              | CURRENTLY PREFERRED
  69. | Silicon Graphics, Incorporated    | urban@sgi.com               |
  70. *-----------------------------------*-----------------------------*
  71. ================================================================================
  72.    USAGE NOTES ON THE PPM DRIVER:
  73.  
  74. See the PBM driver, from which I derived this.
  75.  
  76. I have used this driver on UNICOS(Cray) and NeXT but it should  be  reasonably
  77. portable.
  78.  
  79. In this version of the driver, only the color number (from 0 to 255) is stored
  80. in  the pixmap. This color number is then used to generate RGB values when the
  81. page is WRITTEN. This reduces the amount of storage needed, but means that  if
  82. you  draw with pen N and then change the color of pen N and draw with it again
  83. that everything in the printed image that used pen N  will  all  be  the  same
  84. color  (the last one defined for pen N before printing). To get a "true color"
  85. behavior would require saving RGB values for each point,  which  would  triple
  86. the storage requirements but would otherwise be easy to do.
  87.  
  88. Line thickness is supported with filled rectangular  polygons  when  the  line
  89. thickness  is  greater  than  1.  Square  ends  are  used  that go only to the
  90. endpoints unless line thickness is greater than  5,  in  which  case  complete
  91. circles  are  added  to the endpoints.  If very short polylines are drawn with
  92. the circles on the ends slight errors can occur.
  93.  
  94. If you have a pre-ANSI C compiler  you  will  have  to  remove  the  PROTOTYPE
  95. statements and change a few procedure headers back to the old K&R style.
  96. ================================================================================
  97.  
  98. References: 1) Fundamentals of Interactive Computer Graphics, Foley & Van Dam, Addison Wesley Publishing Company, 1982
  99.             2) ppm - portable bitmap file format, 27 September 1991, Copyright (C) 1989, 1991 by Jef Poskanzer.
  100.  
  101.  Copyright (C) 1996, 1997 John S. Urban
  102.  
  103.  This  software  is  public  domain  and  may be  used  for  any  purpose
  104.  commercial or otherwise.  It is offered  without any guarantee as to its
  105.  suitability  for any purpose or as to the sanity of its  writers.  We do
  106.  ask that the  source is passed on to anyone  that  requests  a copy, and
  107.  that people who get copies don't go round claiming they wrote it.
  108.  
  109.  PS:
  110.  If you feel guilty about using this for commercial purposes and want to
  111.  send me money go ahead. If you can't find me give something to charity
  112.  and forget about it.
  113.  
  114. ================================================================================
  115.  
  116. The  macro  SET_PIXEL will set a given pixel in the graphics arrays :
  117.  
  118. #define SET_BYTE_R(x,y,intensity) ( *(graphics_r + (x) * Y_SIZE + (y) ) = (intensity) )
  119. #define SET_BYTE_G(x,y,intensity) ( *(graphics_g + (x) * Y_SIZE + (y) ) = (intensity) )
  120. #define SET_BYTE_B(x,y,intensity) ( *(graphics_b + (x) * Y_SIZE + (y) ) = (intensity) )
  121.  
  122. #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)))
  123.  
  124. */
  125. #define SET_PIXEL(x,y) ( *(graphics_rgb + (x) * Y_SIZE + (y) ) = (char)(GLOBAL_color) )
  126.  
  127. /******************************************************************************/
  128. #define PROTOTYPE
  129. #include <stdio.h>
  130. #include <math.h>
  131. #include <stdlib.h>
  132. #include "vogle.h"
  133.  
  134. extern FILE     *_voutfile();
  135. static FILE     *fp;
  136. static FILE     *fpP6;
  137.  
  138. #define byte unsigned char
  139.  
  140. #define MAX(x,y)  ((x) > (y) ? (x) : (y))
  141. #define MIN(x,y)  ((x) < (y) ? (x) : (y))
  142. #define ABS(x)    ((x) < 0 ? -(x) : (x))
  143.  
  144. #define PROTOTYPE
  145.  
  146. static int X_SIZE, Y_SIZE; /* size of graphics array */
  147.  
  148. byte *graphics_rgb; /* the graphics data */
  149.  
  150. #define P3  2
  151. #define P6  3
  152. static int GLOBAL_driver = 0;
  153.  
  154. static int GLOBAL_color = 0;
  155. static int BITVALUE=0;
  156.  
  157. #define UNDRAWN 0
  158. #define DRAWN   1
  159. static int GLOBAL_drawn = UNDRAWN; /* flag whether page is blank or not */
  160.  
  161. static int GLOBAL_rasters = 1; /* line thickness */
  162.  
  163. static int GLOBAL_lastx, GLOBAL_lasty;     /* position of last draw */
  164.  
  165. typedef struct {
  166.         int     r, g, b;
  167. } ColorTable;
  168. ColorTable coltab[256];
  169. /******************************************************************************/
  170. static int noop() { return(-1); } /* do nothing but return-1 */
  171. /******************************************************************************/
  172. static PPM_MEMSET() /* set graphics array to all zero */
  173. {
  174.         int i;
  175.  
  176.         /*--- IF YOU HAVE IT, MEMSET IS PROBABLY FASTER
  177.         memset(graphics_rgb, (char)GLOBAL_color, sizeof(byte) * Y_SIZE * X_SIZE);
  178.         ---*/
  179.  
  180.         for ( i=0; i< (X_SIZE * Y_SIZE); i++) {
  181.            *(graphics_rgb + i) = (char)GLOBAL_color;
  182.         }
  183. }
  184. /******************************************************************************/
  185. static int PPM_color(col) /* change the current color */
  186.         int     col;
  187. {
  188.         GLOBAL_color = ABS(col % 256) ;
  189.         return(0);
  190. }
  191. /******************************************************************************/
  192. static int PPM_mapcolor(indx, r, g, b) /* set values in pseudo color map.  */
  193.         int     indx, r, g, b;
  194. {
  195.         if (indx < 256 && indx >= 0) {
  196.                 coltab[indx].r = ABS(r % 256) ;
  197.                 coltab[indx].g = ABS(g % 256) ;
  198.                 coltab[indx].b = ABS(b % 256) ;
  199.         }
  200.         return(0);
  201. }
  202. /******************************************************************************/
  203. static int PPM_init()
  204. {
  205.         int prefx, prefy, prefxs, prefys;
  206.         int i;
  207.  
  208.         PROTOTYPE static PPM_MEMSET(); /* set graphics array to all zero */
  209.  
  210.         /* ---DETERMINE SIZE OF GRAPHICS PIXMAP */
  211.         /* see if a size was user-specified using the prefsize procedure */
  212.         getprefposandsize(&prefx, &prefy, &prefxs, &prefys);
  213.         if (prefxs != -1 ) {
  214.                 if (prefys <= 0 ){
  215.                         fprintf(stderr,"*PPM_init* y size of %d set to 400\n",prefys);
  216.                         prefys = 400;
  217.                 }
  218.                 else{
  219.                         vdevice.sizeSy = prefys;
  220.                 }
  221.                 if (prefxs <= 0 ){
  222.                         fprintf(stderr,"*PPM_init* y size of %d set to 600\n",prefys);
  223.                         prefxs = 600;
  224.                 }
  225.                 else{
  226.                         vdevice.sizeSx = prefxs;
  227.                 }
  228.         }
  229.         else{
  230.                 /* nice default value */
  231.                 prefx = 0;
  232.                 prefy = 0;
  233.                 vdevice.sizeSy = 400;
  234.                 vdevice.sizeSx = 600;
  235.         }
  236.         vdevice.sizeX = vdevice.sizeY = MIN(vdevice.sizeSy,vdevice.sizeSx);
  237.         X_SIZE=vdevice.sizeSx;
  238.         Y_SIZE=vdevice.sizeSy;
  239.         graphics_rgb = (byte *) malloc( X_SIZE * Y_SIZE * sizeof(byte) ); /* the graphics array */
  240.         PPM_MEMSET(); /* set the graphics array to 0 */
  241.  
  242.         vdevice.depth = 1;
  243.  
  244.         fp = _voutfile();
  245.  
  246.         /* Cause scaling to be 0 to maxX maxY: prefx, vdevice.sizeSx+prefx, prefy, vdevice.sizeSy+prefy */
  247.  
  248.         GLOBAL_lastx = -1111111;
  249.         GLOBAL_lasty = -1111111;
  250.  
  251.         GLOBAL_drawn = UNDRAWN;
  252.  
  253.         PPM_mapcolor(0, 0, 0, 0);
  254.         PPM_mapcolor(1, 255, 0, 0);
  255.         PPM_mapcolor(2, 0, 255, 0);
  256.         PPM_mapcolor(3, 255, 255, 0);
  257.         PPM_mapcolor(4, 0, 0, 255);
  258.         PPM_mapcolor(5, 255, 0, 255);
  259.         PPM_mapcolor(6, 0, 255, 255);
  260.         PPM_mapcolor(7, 255, 255, 255);
  261.  
  262.         for(i=8; i<256; i++){
  263.            PPM_mapcolor(i, 255, 255, 255);
  264.         }
  265.  
  266.         return(1);
  267. }
  268. /******************************************************************************/
  269. static PPM_DRAW_LINE(x,y) /* draws a line across a graphics array */
  270. int     x, y;
  271. {
  272.         int runcount;
  273.         int dx,dy;
  274.         int xinc,yinc;
  275.         int xplot,yplot;
  276.  
  277.         SET_PIXEL(GLOBAL_lastx,GLOBAL_lasty); /* move to initial spot */
  278.  
  279.         runcount=0;
  280.  
  281.         dx = abs(GLOBAL_lastx-x);
  282.  
  283.         if (x > GLOBAL_lastx)  xinc=  1;
  284.         if (x == GLOBAL_lastx) xinc=  0;
  285.         if (x < GLOBAL_lastx)  xinc= -1;
  286.  
  287.         dy = abs(GLOBAL_lasty-y);
  288.  
  289.         if (y > GLOBAL_lasty)  yinc=  1;
  290.         if (y == GLOBAL_lasty) yinc=  0;
  291.         if (y < GLOBAL_lasty)  yinc= -1;
  292.  
  293.         xplot = GLOBAL_lastx;
  294.         yplot = GLOBAL_lasty;
  295.  
  296.         if (dx>dy) {
  297.                 /* iterate x */
  298.                 while (xplot != x) {
  299.                         xplot += xinc;
  300.                         runcount += dy;
  301.                         if (runcount >= (dx-runcount)) {
  302.                                 yplot += yinc;
  303.                                 runcount -= dx;
  304.                         }
  305.                         SET_PIXEL(xplot,yplot);
  306.                 }
  307.         } else {
  308.                 /* iterate y */
  309.                 while (yplot != y) {
  310.                         yplot += yinc;
  311.                         runcount += dx;
  312.                         if (runcount >= (dy-runcount)) {
  313.                                 xplot += xinc;
  314.                                 runcount -= dy;
  315.                         }
  316.                         SET_PIXEL(xplot,yplot);
  317.                 }
  318.         }
  319.        
  320.         GLOBAL_lastx = xplot;
  321.         GLOBAL_lasty = yplot;
  322.         
  323.         return(0);
  324. }
  325. /******************************************************************************/
  326. static PPM_ENDCAP_CIRCLE(x, y) /* Draw a circle on thick line segment end point */
  327. int x, y;
  328. {
  329.         /* there are more efficient ways to do this */
  330.         /* circle precision */
  331. #define NSEGS    15
  332.         static int nsegs= NSEGS;
  333.         float cx, cy, dx, dy, angle, cosine, sine ;
  334.         /* array to place circle points on */
  335.         int cxras[NSEGS], cyras[NSEGS];
  336.         int i;
  337.  
  338.         PROTOTYPE static void PPM_SOLID_FILL(int n, int x[], int y[]); /* fill polygon of n points drawn by polyline <x,y>.  */
  339.  
  340.         angle = 2.0 * PI / nsegs;
  341.         cosine = cos((double)angle);
  342.         sine = sin((double)angle);
  343.  
  344.         /* first point on circle */
  345.         cxras[0] = cx =  x + GLOBAL_rasters/2.0;
  346.         cyras[0] = cy = y;
  347.         for (i = 1; i < nsegs; i++) {
  348.                 dx = cx - x;
  349.                 dy = cy - y;
  350.                 cxras[i] = ( cx = x + dx * cosine - dy * sine) ;
  351.                 cyras[i] = ( cy = y + dx * sine   + dy * cosine) ;
  352.         }
  353.         PPM_SOLID_FILL(nsegs,cxras,cyras);
  354. }
  355. /******************************************************************************/
  356. static PPM_fill(n, x, y) /* "fill" a polygon */
  357. int     n, x[], y[];
  358. {
  359.         int     i;
  360.  
  361.         PROTOTYPE static void PPM_SOLID_FILL( int n, int x[], int y[]);
  362.  
  363.         /* update current position if needed */
  364.         GLOBAL_lastx=x[0];
  365.         GLOBAL_lasty=y[0];
  366.  
  367.         for (i = 1; i < n; i++){
  368.                 PPM_DRAW_LINE(x[i],y[i]); /* draw outline across graphics array */
  369.         }
  370.         if ( x[n-1] != x[0] || y[n-1] != y[0] ) /* close the polygon if it is not closed */
  371.                 PPM_DRAW_LINE(x[0],y[0]);
  372.  
  373.         PPM_SOLID_FILL(n, x, y);
  374.  
  375.         /* update current position */
  376.         GLOBAL_lastx = vdevice.cpVx = x[n - 1];
  377.         GLOBAL_lasty = vdevice.cpVy = y[n - 1];
  378.  
  379.         GLOBAL_drawn = DRAWN;
  380. }
  381. /******************************************************************************/
  382. static PPM_draw(x, y) /* print the commands to draw a line from the current graphics position to (x, y).  */
  383.         int     x, y;
  384. {
  385.         int     holdx, holdy;
  386.         int xwide[4], ywide[4];
  387.         float cosa, sina;
  388.         double angle;
  389.  
  390.         PROTOTYPE static void PPM_SOLID_FILL(int n, int x[], int y[]); /* fill polygon of n points drawn by polyline <x,y>.  */
  391.  
  392.         if (GLOBAL_lastx != vdevice.cpVx || GLOBAL_lasty != vdevice.cpVy){
  393.              GLOBAL_lastx=vdevice.cpVx;
  394.              GLOBAL_lasty=vdevice.cpVy;
  395.         }
  396.  
  397.         if ( GLOBAL_rasters <= 1){
  398.            PPM_DRAW_LINE(x,y);
  399.         }else{
  400.            /* thick lines are made from filled polygon(s) */
  401.            /* add a circle to ends of really thick lines */
  402.            if( GLOBAL_rasters >= 6){
  403.               holdx=GLOBAL_lastx;
  404.               holdy=GLOBAL_lasty;
  405.               PPM_ENDCAP_CIRCLE(GLOBAL_lastx,GLOBAL_lasty);
  406.               PPM_ENDCAP_CIRCLE(x,y);
  407.               GLOBAL_lastx=holdx;
  408.               GLOBAL_lasty=holdy;
  409.            }
  410.            angle=atan2((double)(y-GLOBAL_lasty),(double)(x-GLOBAL_lastx)) + PI/2.0;
  411.            cosa=(GLOBAL_rasters/2.0)*cos(angle);
  412.            sina=(GLOBAL_rasters/2.0)*sin(angle);
  413.            xwide[0]=x+cosa;
  414.            xwide[1]=GLOBAL_lastx+cosa;
  415.            xwide[2]=GLOBAL_lastx-cosa;
  416.            xwide[3]=x-cosa;
  417.  
  418.            ywide[0]=y+sina;
  419.            ywide[1]=GLOBAL_lasty+sina;
  420.            ywide[2]=GLOBAL_lasty-sina;
  421.            ywide[3]=y-sina;
  422.  
  423.            PPM_SOLID_FILL(4,xwide,ywide);
  424.         }
  425.         GLOBAL_drawn = DRAWN;
  426. }
  427. /*******************************************************************************/
  428. static P3_print_graphics() /* print_graphics -- print the graphics bit array as a ppm P3 file*/
  429. {
  430.         int x; /* current x BYTE */
  431.         int y; /* current y location */
  432.         int bit; /* bit we are testing in the current byte */
  433.         int index, pix;
  434.  
  435.         (void) fprintf(fp,"P3\n"); /* magic number of a clear text PPM file */
  436.         (void) fprintf(fp,"# CREATOR: VOGLE ppm driver; version 1.0 1997/02/02\n"); /* ppm P3 file can contain comment lines*/
  437.         (void) fprintf(fp,"# Uncopyright (C) 19970202, John S. Urban\n");
  438.         (void) fprintf(fp,"# csplit multiframe files: csplit -f P3 -k $.p3 '%%^P3%%' '/^P3/' '{999}'\n");
  439.         (void) fprintf(fp,"%d %d\n",X_SIZE,Y_SIZE); /* size of bitmap */
  440.         (void) fprintf(fp,"255\n"); /* maximum value of a color intensity*/
  441.  
  442.         /* notice going from bottom to top because putting out in a right handed coordinate system, was assuming left-handed */
  443.         for (y = (Y_SIZE-1); y >= 0; y--) { /* Loop for each byte in the array */
  444.                 for ( x = 0; x < X_SIZE ; x++){
  445.                         index = Y_SIZE * x + y;
  446.                         pix = (int)*(graphics_rgb + index);
  447.                         /* The manual says a P3 ppm file should not be wider than 70 characters */
  448.                         (void) fprintf(fp,"%d %d %d\n",coltab[pix].r, coltab[pix].g, coltab[pix].b);
  449.                 }
  450.         } /* end of writing a column */
  451.         (void) fprintf(fp,"\n");
  452.         GLOBAL_drawn = UNDRAWN;
  453. }
  454. /*******************************************************************************/
  455. static P6_print_graphics() /* print_graphics -- print the graphics bit array as a ppm P6 file*/
  456. {
  457.         int x; /* current x BYTE */
  458.         int y; /* current y location */
  459.         int index;
  460.     fpP6 = popen("p6to", "w");
  461.     if (!fpP6) {
  462.         fprintf(stderr, "Couldn't open pipe to lpr command.\n");
  463.         exit(1);
  464.     }
  465.  
  466.  
  467.         (void) fprintf(fpP6,"P6\n"); /* magic number of a ppm file */
  468.         (void) fprintf(fpP6,"# CREATOR: VOGLE ppm driver; version 1.0 1997/02/02\n"); /*ppm P6 file can contain comment lines*/
  469.         (void) fprintf(fpP6,"# Uncopyright (C) 19970202, John S. Urban\n");
  470.         (void) fprintf(fpP6,"%d %d\n",X_SIZE,Y_SIZE); /* size of bitmap */
  471.         (void) fprintf(fpP6,"255\n"); /* maximum value of a color intensity*/
  472.  
  473.         /* notice going from bottom to top because putting out in a right handed coordinate system, was assuming left-handed */
  474.         for (y = (Y_SIZE-1); y >= 0; y--) {
  475.                 for ( x = 0; x < X_SIZE ; x++){
  476.                         index = Y_SIZE * x + y;
  477.                         index = *(graphics_rgb + index);
  478.                         putc((char)coltab[index].r,fpP6);
  479.                         putc((char)coltab[index].g,fpP6);
  480.                         putc((char)coltab[index].b,fpP6);
  481.                 }
  482.         }
  483.  
  484.     fflush(fpP6);
  485.     if (fpP6 != stdout)
  486.         pclose(fpP6);
  487.  
  488.         GLOBAL_drawn = UNDRAWN;
  489. }
  490. /******************************************************************************/
  491. static PPM_PRINT() /* exit from vogle printing the command to flush the buffer.  */
  492. {
  493.         if( GLOBAL_drawn ){
  494.                 switch(GLOBAL_driver) {
  495.                 case P3:
  496.                         P3_print_graphics();
  497.                         break;
  498.                 case P6:
  499.                         P6_print_graphics();
  500.                         break;
  501.                 default:
  502.                         fprintf(stderr, "ppm driver: UNKNOWN DRIVER NAME\n");
  503.                         P3_print_graphics();
  504.                 }
  505.         }
  506.         fflush(fp); /* flush the output file */
  507. }
  508. /******************************************************************************/
  509. static PPM_exit() /* exit from vogle printing the command to flush the buffer.  */
  510. {
  511.         PPM_PRINT();
  512.         if (fp != stdout){
  513.                 fclose(fp);
  514.         }
  515. }
  516. /*******************************************************************************/
  517. static PPM_setlw(w) /* Set the line width */
  518.         int     w;
  519. {
  520.  
  521.         if (w == 0)
  522.                 w = 1;
  523.         else if (w == 1)
  524.                 w = 2;
  525.  
  526.         GLOBAL_rasters = MAX(1,w);
  527. }
  528. /******************************************************************************/
  529. static PPM_clear() /* flush current page and clear graphics array */
  530. {
  531.  
  532.         PROTOTYPE static PPM_MEMSET(); /* set graphics array to all zero */
  533.  
  534.         PPM_PRINT();
  535.         PPM_MEMSET();
  536. }
  537. /******************************************************************************/
  538. static int PPM_font(font) /* load in large or small */
  539.         char    *font;
  540. {
  541.         fprintf(stderr, "E-R-R-O-R: NO HARDWARE FONT\n");
  542.         if (strcmp(font, "small") == 0) {
  543.                 vdevice.hwidth = 97.01; /* Size in plotter resolution units */
  544.                 vdevice.hheight = vdevice.hwidth * 2.0;
  545.         } else if (strcmp(font, "large") == 0) {
  546.                 vdevice.hwidth = 145.5;
  547.                 vdevice.hheight = vdevice.hwidth * 2.0;
  548.         } else
  549.                 return(0);
  550.  
  551.         return(1);
  552. }
  553. /******************************************************************************/
  554. static PPM_string(s) /* output a string.  */
  555.         char    *s;
  556. {
  557.         int             dy, dx;
  558.  
  559.         if (GLOBAL_lastx != vdevice.cpVx || GLOBAL_lasty != vdevice.cpVy){
  560.                 GLOBAL_lastx=vdevice.cpVx;
  561.                 GLOBAL_lasty=vdevice.cpVy;
  562.         }
  563.  
  564.         fputs(s, fp);
  565.  
  566.         GLOBAL_lastx = GLOBAL_lasty = -1111111; /* undefine current position because used hardware text ?*/
  567.         GLOBAL_drawn = DRAWN;
  568. }
  569. /******************************************************************************/
  570. static PPM_char(c) /* output a character */
  571. char    c;
  572. {
  573.   char  s[2];
  574.   s[0] = c; s[1]='\0';
  575.   PPM_string(s);
  576. }
  577. /******************************************************************************/
  578. static DevEntry PPMdev = {
  579.                 "ppm",       /* name of device */
  580.                 "large",     /* name of large font */
  581.                 "small",     /* name of small font */
  582.                 noop,        /* Set drawing in back buffer */
  583.                 PPM_char,    /* Draw a hardware character */
  584.                 noop,        /* Check if a key was hit */
  585.                 PPM_clear,   /* Clear the screen to current color */
  586.                 PPM_color,   /* Set current color */
  587.                 PPM_draw,    /* Draw a line */
  588.                 PPM_exit,    /* Exit graphics */
  589.                 PPM_fill,    /* Fill a polygon */
  590.                 PPM_font,    /* Set hardware font */
  591.                 noop,        /* Set drawing in front buffer */
  592.                 noop,        /* Wait for and get the next key hit */
  593.                 PPM_init,    /* Initialize the device */
  594.                 noop,        /* Get mouse/cross hair position */
  595.                 PPM_mapcolor,/* Set color indices */
  596.                 PPM_setlw,   /* Set line width */
  597.                 PPM_string,  /* Draw a hardware string */
  598.                 noop,        /* Swap front and back buffers */
  599.                 noop         /* Syncronize the display */
  600. };
  601. /******************************************************************************/
  602. _P3_devcpy()
  603. {
  604.         vdevice.dev = PPMdev;
  605.         vdevice.dev.Vinit = PPM_init;
  606.         GLOBAL_driver = P3;
  607. }
  608. /******************************************************************************/
  609. _P6_devcpy()
  610. {
  611.         vdevice.dev = PPMdev;
  612.         vdevice.dev.Vinit = PPM_init;
  613.         GLOBAL_driver = P6;
  614. }
  615. /*******************************************************************************/
  616. static PPM_YINTERCEPT(yscan, x1, y1, x2, y2, xintercept,yprev)
  617. /*
  618. Determine if scan line intercepts the line segment. If it does, return the x intercept.
  619. */
  620. int     yscan, x1, y1, x2, y2, *xintercept, *yprev;
  621. {
  622.         int deltay, yprevious;
  623.         float   t;
  624.         yprevious = *yprev; /* the value we need to use in this pass */
  625.         *yprev = y1;        /* store the value for the next call to (probably) use */
  626.         deltay = y2 - y1;
  627.         if ( deltay == 0 ){
  628.                 /* horizontal lines do not contribute to scan line intercepts */
  629.                 *yprev=yprevious;
  630.                 return(0);
  631.         }
  632.         t = (float)(yscan - y1) / deltay;
  633.         if (t > 0.0 && t <= 1.0) {
  634.                 /* scan line and line segment intersect but not at leading vertex */
  635.                 *xintercept = x1 + t*(x2 - x1) + 0.5;
  636.                 return (1);
  637.         } else if ( t == 0.0 ){
  638.                 /* scan line and line segment intersect at leading vertex */
  639.                 *xintercept = x1 + t*(x2 - x1) + 0.5;
  640.                 if(yprevious <= y1 && y2 <= y1 ){
  641.                    /* local maximum */
  642.                    return (1);
  643.                 } else if(yprevious >= y1 && y2 >= y1 ){
  644.                    /* local minimum */
  645.                    return (1);
  646.                 } else{
  647.                    /* ignore duplicate at vertex that is not a local maximum or minimum */
  648.                    return (0);
  649.                 }
  650.         }
  651.         /* scan line and line segment did not intersect */
  652.         return (0);
  653. }
  654. /*******************************************************************************/
  655. static void PPM_SOLID_FILL(n, x, y) /* fill polygon of n points drawn by polyline <x,y>.  */
  656. int     n, x[], y[];
  657. {
  658.         int i, j, sorted, yhorizontal, xint, tmp, xmin, xmax, ymax, ymin, xi[MAXVERTS], yprev;
  659.  
  660.         if ( n > MAXVERTS) {
  661.            fprintf(stderr,"*PPM_SOLID_FILL* more than %d vertices in a polygon\n",MAXVERTS);
  662.            return;
  663.         }
  664.  
  665.         /* find clip range */
  666.         ymin = ymax = y[0];
  667.         xmin = xmax = y[0];
  668.         for (i = 0; i < n; i++) {
  669.                 ymax = MAX(ymax, y[i]);
  670.                 ymin = MIN(ymin, y[i]);
  671.                 xmax = MAX(xmax, x[i]);
  672.                 xmin = MIN(xmin, x[i]);
  673.         }
  674.         /* ensure scan lines are generated that do not cause out-of-bound problems in the y direction */
  675.         ymin=MAX(ymin,0);
  676.         ymax=MIN(ymax,Y_SIZE);
  677.  
  678.         /* For each y value, get a list of X intersections... */
  679.         yhorizontal = ymax ;
  680.         while (yhorizontal >= ymin) {
  681.                 j = 0;
  682.                 yprev = y[n-1];
  683.                 for (i = 0; i < n-1; i++)
  684.                         if (PPM_YINTERCEPT(yhorizontal, x[i], y[i], x[i+1], y[i+1], &xint, &yprev))
  685.                                         xi[j++] = xint;
  686.                 /* Last one. */
  687.                 if (PPM_YINTERCEPT(yhorizontal, x[n-1], y[n-1], x[0], y[0], &xint, &yprev))
  688.                                 xi[j++] = xint;
  689.  
  690.                 /* odd pairs means something went wrong in figuring out whether to count vertices or not */
  691.                 if( 2 * (j/2) != j){
  692.                    fprintf(stderr,"*PPM_SOLID_FILL* Internal error: odd number of intersection points (%d) \n",j);
  693.                 }
  694.  
  695.                 /* Sort the X intersections... */
  696.                 sorted = 0;
  697.                 while (!sorted) {
  698.                         sorted = 1;
  699.                         for (i = 0; i < j-1; i++)
  700.                                 if (xi[i] > xi[i+1]) {
  701.                                         tmp = xi[i];
  702.                                         xi[i] = xi[i+1];
  703.                                         xi[i+1] = tmp;
  704.                                         sorted = 0;
  705.                                 }
  706.                 }
  707.  
  708.                 /* Draw the horizontal lines */
  709.                 /* should make sure within X clipping range */
  710.                 for (i = 0; i < j-1; i += 2) {
  711.                         GLOBAL_lastx=MAX(0,MIN(xi[i],X_SIZE));
  712.                         GLOBAL_lasty=yhorizontal;
  713.                         PPM_DRAW_LINE(MAX(0,MIN(xi[i+1],X_SIZE)), yhorizontal);
  714.                 }
  715.                 yhorizontal -= 1;
  716.         }
  717. }
  718. /*******************************************************************************/
  719.  
  720.