C*PPDRIV -- PGPLOT PPM drivers
C+
      SUBROUTINE PPDRIV (IFUNC, RBUF, NBUF, CHR, LCHR, MODE)
      INTEGER IFUNC, NBUF, LCHR, MODE
      REAL    RBUF(*)
      CHARACTER*(*) CHR
*
* PGPLOT driver for Portable Pixel Map (PPM) files with 'true color'
* capability.
*
* Supported device: PPM (P6) file format
*
* Device type codes: /PPM or /VPPM
*
* Default device name: pgplot.ppm.
*
* If you have more than one image to plot (i.e. use PGPAGE) with this
* device, subsequent pages will be named: pgplot2.ppm, pgplot3.ppm,
* etc, disrespective of the device name you specified.
* You can however bypass this by specifying a device name including a
* number sign (#), which will henceforth be replaced by the pagenumber.
* Example: page#.ppm will produce files page1.ppm, page2.ppm, etc.
*
* Default view surface dimensions are:
* - PPM  : 850 x 680 pixels 
* - VPPM : 680 x 850 pixels 
* Default width and height can be overriden by specifying environment
* variables
* PGPLOT_PPM_WIDTH  (default 850)
* PGPLOT_PPM_HEIGHT (default 680)
* The nominal scale is 85 pixels per inch.
*
* Color capability:
*   Indices 0 to 255 are supported. 
*   Default colors for indices 0 to 15 are implemented.
*   Color representation can be changed with PGSCR; color changes
*   affect subsequently drawn pixels only, not previously drawn
*   pixels. Thus the image is not limited to 256 different colors.
*
* Obtaining hardcopy: Use a PPM viewer or converter.
*=
*  9-Aug-1993 - Created by Remko Scharroo
*  6-Jul-1994 - Adapted to new PGPLOT version 4.9h
*  4-Aug-1994 - Use FASTIO.
*  9-Aug-1994 - New scheme for line plotting
* 16-Aug-1994 - Provide multi-image plotting.
* 16-Nov-1994 - Revised (T. Pearson).
* 28-Dec-1995 - Prevent concurrent access [TJP].
* 29-Apr-1996 - Use GRCTOI to decode environment variables [TJP].
*-----------------------------------------------------------------------
      CHARACTER*(*) LTYPE, PTYPE, DEFNAM
      INTEGER DWD, DHT
      PARAMETER (
     1 LTYPE= 'PPM   (Portable Pixel Map file, landscape orientation)',
     2 PTYPE= 'VPPM  (Portable Pixel Map file, portrait orientation)')
      PARAMETER (DEFNAM='pgplot.ppm')
      PARAMETER (DWD=850, DHT=680)

      REAL XRES, YRES
      PARAMETER (XRES=85., YRES=XRES)
C
      INTEGER UNIT, IC, CVAL, CTABLE(3,0:255), IER, I, L, LL, NPICT
      INTEGER BX, BY, IX0, IY0, IX1, IY1, R, G, B, STATE, USERH, USERW
      INTEGER CDEFLT(3,0:15), JUNK
      INTEGER GRGMEM, GRFMEM, GROFIL, GRCFIL, GRCTOI
      CHARACTER*80 MSG, INSTR, FILENM
C
C Note: for 64-bit operating systems, change the following 
C declaration to INTEGER*8:
C
      INTEGER PIXMAP
C
      SAVE    UNIT, IC, CVAL, CTABLE, BX, BY, PIXMAP, NPICT, CDEFLT
      SAVE    STATE
      DATA CDEFLT /000,000,000, 255,255,255, 255,000,000, 000,255,000,
     1             000,000,255, 000,255,255, 255,000,255, 255,255,000,
     2             255,128,000, 128,255,000, 000,255,128, 000,128,255,
     3             128,000,255, 255,000,128, 085,085,085, 170,170,170/
      DATA    STATE /0/
C-----------------------------------------------------------------------
C
      GOTO( 10, 20, 30, 40, 50, 60, 70, 80, 90,100,
     1     110,120,130,140,150,160,170,180,190,200,
     2     210,220,230,240,250,260,270,280,290), IFUNC
  900 WRITE (MSG,'(I10)') IFUNC
      CALL GRWARN('Unimplemented function in PPM device driver:'
     1    //MSG)
      NBUF = -1
      RETURN
C
C--- IFUNC = 1, Return device name -------------------------------------
C
   10 IF (MODE.EQ.1) THEN
         CHR = LTYPE
         LCHR = LEN(LTYPE)
      ELSE IF (MODE.EQ.2) THEN
         CHR = PTYPE
         LCHR = LEN(PTYPE)
      ELSE
         CALL GRWARN('Requested MODE not implemented in PPM driver')
      ENDIF
      RETURN
C
C--- IFUNC = 2, Return physical min and max for plot device, and range
C               of color indices ---------------------------------------
C
   20 RBUF(1) = 0
      RBUF(2) = -1 
      RBUF(3) = 0
      RBUF(4) = -1 
      RBUF(5) = 0
      RBUF(6) = 255
      NBUF = 6
      RETURN
C
C--- IFUNC = 3, Return device resolution -------------------------------
C
   30 RBUF(1) = XRES
      RBUF(2) = YRES 
      RBUF(3) = 1
      NBUF = 3
      RETURN
C
C--- IFUNC = 4, Return misc device info --------------------------------
C    (This device is Hardcopy, supports rectangle fill, pixel 
C     primitives, and query color rep.)
C
   40 CHR = 'HNNNNRPNYN'
      LCHR = 10
      RETURN
C
C--- IFUNC = 5, Return default file name -------------------------------
C
   50 CHR = DEFNAM
      LCHR = LEN(DEFNAM)
      RETURN
C
C--- IFUNC = 6, Return default physical size of plot -------------------
C
   60 RBUF(1) = 0
      RBUF(2) = BX-1 
      RBUF(3) = 0
      RBUF(4) = BY-1 
      NBUF = 4
      RETURN
C
C--- IFUNC = 7, Return misc defaults -----------------------------------
C
   70 RBUF(1) = 1
      NBUF=1
      RETURN
C
C--- IFUNC = 8, Select plot --------------------------------------------
C
   80 CONTINUE
      RETURN
C
C--- IFUNC = 9, Open workstation ---------------------------------------
C
   90 CONTINUE
C     -- check for concurrent access
      IF (STATE.EQ.1) THEN
         CALL GRWARN('a PGPLOT PPM file is already open')
         RBUF(1) = 0
         RBUF(2) = 0
         RETURN
      END IF
C     -- dimensions of plot buffer
      USERW = 0
      USERH = 0
      CALL GRGENV('PPM_WIDTH', INSTR, L)
      LL = 1
      IF (L.GT.0) USERW = GRCTOI(INSTR(:L),LL)
      CALL GRGENV('PPM_HEIGHT', INSTR, L)
      LL = 1
      IF (L.GT.0) USERH = GRCTOI(INSTR(:L),LL)
      IF (MODE.EQ.1) THEN
*     -- Landscape
         BX = DWD
         IF (USERW.GE.8) BX = USERW
         BY = DHT
         IF (USERH.GE.8) BY = USERH
      ELSE
*     -- Portrait
         BX = DHT
         IF (USERH.GE.8) BX = USERH
         BY = DWD
         IF (USERW.GE.8) BY = USERW
      END IF
      NPICT=1
*     -- Initialize color table
      DO 95 I=0,15
         CTABLE(1,I) = CDEFLT(1,I)
         CTABLE(2,I) = CDEFLT(2,I)
         CTABLE(3,I) = CDEFLT(3,I)
 95   CONTINUE
      DO 96 I=16,255
         CTABLE(1,I) = 128
         CTABLE(2,I) = 128
         CTABLE(3,I) = 128
 96   CONTINUE       
*
      FILENM = CHR(:LCHR)
      CALL GRPP10 (FILENM, NPICT, MSG)
      UNIT = GROFIL (MSG)
      RBUF(1) = UNIT
      IF (UNIT.LT.0) THEN
          CALL GRWARN('Cannot open output file for PPM plot')
          RBUF(2) = 0
      ELSE
          RBUF(2) = 1
          STATE = 1
      END IF
      RETURN
C
C--- IFUNC=10, Close workstation ---------------------------------------
C
  100 CONTINUE
      STATE = 0
      RETURN
C
C--- IFUNC=11, Begin picture -------------------------------------------
C
  110 CONTINUE
      BX = NINT(RBUF(1))+1
      BY = NINT(RBUF(2))+1
C     -- allocate buffer with 4 bytes per pixel
      IER = GRGMEM(4*BX*BY, PIXMAP)
      IF (IER.NE.1) THEN
         CALL GRGMSG(IER)
         CALL GRWARN('Failed to allocate plot buffer.')
         BX = 0
         BY = 0
         PIXMAP = 0
      END IF
C     -- initialize to zero (background color)
      IF (PIXMAP.NE.0) 
     :     CALL GRPP03(1, 1, BX, BY, 0, BX, BY, %VAL(PIXMAP))
C     -- open new file if necessary
      IF (NPICT.GT.1) THEN
          CALL GRPP10 (FILENM, NPICT, MSG)
          UNIT = GROFIL (MSG)
          IF (UNIT.LT.0) 
     :        CALL GRWARN('Cannot open output file for PPM plot')
C         -- no way to return error status!
      END IF
      RETURN
C
C--- IFUNC=12, Draw line -----------------------------------------------
C
  120 CONTINUE
      IX0=NINT(RBUF(1))+1
      IX1=NINT(RBUF(3))+1
      IY0=BY-NINT(RBUF(2))
      IY1=BY-NINT(RBUF(4))
      IF (PIXMAP.NE.0) 
     :     CALL GRPP01(IX0, IY0, IX1, IY1, CVAL, BX, BY, %VAL(PIXMAP))
      RETURN
C
C--- IFUNC=13, Draw dot ------------------------------------------------
C
  130 CONTINUE
      IX0=NINT(RBUF(1))+1
      IY0=BY-NINT(RBUF(2))
      IF (PIXMAP.NE.0) 
     :     CALL GRPP01(IX0, IY0, IX0, IY0, CVAL, BX, BY, %VAL(PIXMAP))
      RETURN
C
C--- IFUNC=14, End picture ---------------------------------------------
C
  140 CONTINUE
      IF (PIXMAP.NE.0) THEN
         CALL GRPP02(UNIT, BX, BY, %VAL(PIXMAP))
         IF (UNIT.GE.0) JUNK = GRCFIL(UNIT)
         NPICT = NPICT+1
         IER = GRFMEM(4*BX*BY, PIXMAP)
         IF (IER.NE.1) THEN
            CALL GRGMSG(IER)
            CALL GRWARN('Failed to deallocate plot buffer.')
         END IF
      END IF
      RETURN
C
C--- IFUNC=15, Select color index --------------------------------------
C
  150 CONTINUE
      IC = NINT(RBUF(1))
      IF (IC.LT.0 .OR. IC.GT.255) IC = 1
      R = CTABLE(1,IC)
      G = CTABLE(2,IC)
      B = CTABLE(3,IC)
      CVAL = R + 256*(G + 256*B)
      RETURN
C
C--- IFUNC=16, Flush buffer. -------------------------------------------
C    (Not used.)
C
  160 CONTINUE
      RETURN
C
C--- IFUNC=17, Read cursor. --------------------------------------------
C    (Not implemented: should not be called)
C
  170 CONTINUE
      GOTO 900
C
C--- IFUNC=18, Erase alpha screen. -------------------------------------
C    (Not implemented: no alpha screen)
C
  180 CONTINUE
      RETURN
C
C--- IFUNC=19, Set line style. -----------------------------------------
C    (Not implemented: should not be called)
C
  190 CONTINUE
      GOTO 900
C
C--- IFUNC=20, Polygon fill. -------------------------------------------
C    (Not implemented: should not be called)
C
  200 CONTINUE
      GOTO 900
C
C--- IFUNC=21, Set color representation. -------------------------------
C
  210 CONTINUE
      I = NINT(RBUF(1))
      IF (I.GE.0 .AND. I.LE.255) THEN
         CTABLE(1, I) = NINT(RBUF(2)*255)
         CTABLE(2, I) = NINT(RBUF(3)*255)
         CTABLE(3, I) = NINT(RBUF(4)*255)
      END IF
      RETURN
C
C--- IFUNC=22, Set line width. -----------------------------------------
C    (Not implemented: should not be called)
C
  220 CONTINUE
      GOTO 900
C
C--- IFUNC=23, Escape --------------------------------------------------
C    (Not implemented: ignored)
C
  230 CONTINUE
      RETURN
C
C--- IFUNC=24, Rectangle fill ------------------------------------------
C
  240 CONTINUE
      IX0=NINT(RBUF(1))+1
      IX1=NINT(RBUF(3))+1
      IY1=BY-NINT(RBUF(2))
      IY0=BY-NINT(RBUF(4))
      IF (PIXMAP.NE.0) 
     :     CALL GRPP03(IX0, IY0, IX1, IY1, CVAL, BX, BY, %VAL(PIXMAP))
      RETURN
C
C--- IFUNC=25, Not implemented -----------------------------------------
C
  250 CONTINUE
      RETURN
C
C--- IFUNC=26, Line of pixels ------------------------------------------
C
  260 CONTINUE
      IF (PIXMAP.NE.0)
     :     CALL GRPP04(NBUF, RBUF, BX, BY, %VAL(PIXMAP), CTABLE)
      RETURN
C
C--- IFUNC=27, Not implemented -----------------------------------------
C
  270 CONTINUE
      RETURN
C
C--- IFUNC=28, Not implemented -----------------------------------------
C
  280 CONTINUE
      RETURN
C
C--- IFUNC=29, Query color representation. -----------------------------
C
  290 CONTINUE
      I = RBUF(1)
      RBUF(2) = CTABLE(1,I)/255.0
      RBUF(3) = CTABLE(2,I)/255.0
      RBUF(4) = CTABLE(3,I)/255.0
      NBUF = 4
      RETURN
C-----------------------------------------------------------------------
      END

**GRPP01 -- PGPLOT PPM driver, draw line
*+
      SUBROUTINE GRPP01 (IX0, IY0, IX1, IY1, ICOL, BX, BY, PIXMAP)
      INTEGER IX0, IY0, IX1, IY1
      INTEGER ICOL, BX, BY, PIXMAP(BX,BY)
*
* Draw a straight-line segment from absolute pixel coordinates
* (IX0, IY0) to (IX1, IY1).
*
* Arguments:
*  IX0, IY0        (input): Starting point of line.
*  IX1, IY1        (input): End point of line.
*  ICOL            (input): Color value.
*  BX, BY          (input): dimensions of PIXMAP.
*  PIXMAP   (input/output): The image data buffer.
*-
*  9-Aug-1994 - Recreated by Remko Scharroo from GRGI01 by Ge van Geldorp
*               Improved algorithm.
*-----------------------------------------------------------------------
      INTEGER IX, IY, IS
      REAL    D
C
      IF (IX0.EQ.IX1 .AND. IY0.EQ.IY1) THEN
         PIXMAP(IX0,IY0)=ICOL
      ELSE IF (ABS(IY1-IY0).GT.ABS(IX1-IX0)) THEN
         D=(IX1-IX0)/REAL(IY1-IY0)
         IS=1
         IF (IY1.LT.IY0) IS=-1
         DO 10 IY=IY0,IY1,IS
            IX=NINT(IX0+(IY-IY0)*D)
            PIXMAP(IX,IY)=ICOL
 10      CONTINUE
      ELSE
         D=(IY1-IY0)/REAL(IX1-IX0)
         IS=1
         IF (IX1.LT.IX0) IS=-1
         DO 20 IX=IX0,IX1,IS
            IY=NINT(IY0+(IX-IX0)*D)
            PIXMAP(IX,IY)=ICOL
 20      CONTINUE
      ENDIF
      END

**GRPP02 -- PGPLOT PPM driver, write PPM image
*+
      SUBROUTINE GRPP02 (UNIT, BX, BY, PIXMAP)
      INTEGER UNIT, BX, BY, PIXMAP(*)
*
* This routine copies the image buffer to an output buffer. A PPM image
* header and the output buffer are written to UNIT.
*
* Arguments:
*  UNIT    (input): Output unit
*  BX, BY  (input): Image size
*  PIXMAP  (input): Image data
*-
* 10-Aug-1993 - Created by Remko Scharroo
* 16-Nov-1994 - Rewritten by T. Pearson
*-----------------------------------------------------------------------
*       BUFSIZ must be a multiple of 3 (and not larger than 500 for
*       portability to AIX)
      INTEGER BUFSIZ
      PARAMETER (BUFSIZ=498)
      INTEGER I, N, IER, IBUF, L1, L2
      INTEGER GRTRIM, GRWFCH
      CHARACTER*128 HEAD
      CHARACTER*20 USER
      CHARACTER*20 TODAY
      CHARACTER*(BUFSIZ) BUF
      LOGICAL BAD
*
*     Write the header (magic number = P6)
*
      CALL GRUSER(USER,L1)
      CALL GRDATE(TODAY,L2)
      WRITE (HEAD, 1000) USER(:L1), TODAY(:L2),CHAR(10),
     :                   BX, BY, CHAR(10), 255, CHAR(10)
 1000 FORMAT ('P6 # PGPLOT PPM image ',A,1X,A,A1, I5,1X,I5,A1, I3,A1)
      N = GRTRIM(HEAD)
      IER = GRWFCH(UNIT, HEAD(1:N))
      IF (IER.NE.N) CALL GRWARN('Failed writing PPM header')
*
*     Write the pixel data as R,G,B components
*
      N=BX*BY
      IBUF=0
      BAD = .FALSE.
      DO 10 I=1,N
         BUF(IBUF+1:IBUF+1) = CHAR(MOD(PIXMAP(I),256))
         BUF(IBUF+2:IBUF+2) = CHAR(MOD(PIXMAP(I)/256,256))
         BUF(IBUF+3:IBUF+3) = CHAR(PIXMAP(I)/(256*256))
         IBUF = IBUF+3
         IF (IBUF.GE.BUFSIZ) THEN
            IER = GRWFCH(UNIT, BUF)
            IF (IER.NE.IBUF) BAD = .TRUE.
            IBUF = 0
         END IF
 10   CONTINUE
      IF (IBUF.GT.0) THEN
         IER = GRWFCH(UNIT, BUF(:IBUF))
         IF (IER.NE.IBUF) BAD = .TRUE.
         IBUF = 0
      END IF
      IF (BAD) CALL GRWARN('Failed writing PPM data')
      END

**GRPP03 -- PGPLOT PPM driver, fill rectangle
*+
      SUBROUTINE GRPP03 (IX0, IY0, IX1, IY1, ICOL, BX, BY, PIXMAP)
      INTEGER IX0, IY0, IX1, IY1
      INTEGER ICOL, BX, BY, PIXMAP(BX,BY)
*
* Arguments:
*  IX0, IY0        (input): Lower left corner.
*  IX1, IY1        (input): Upper right corner.
*  ICOL            (input): Color value.
*  BX, BY          (input): dimensions of PIXMAP.
*  PIXMAP   (input/output): The image data buffer.
*-----------------------------------------------------------------------
      INTEGER IX, IY
C
      DO 20 IY=IY0,IY1
         DO 10 IX=IX0,IX1
            PIXMAP(IX,IY) = ICOL
 10      CONTINUE
 20   CONTINUE
      END

**GRPP04 -- PGPLOT PPM driver, fill image line
*+
      SUBROUTINE GRPP04(NBUF,RBUF,BX,BY,PIXMAP,CTABLE)
      INTEGER I,J,NBUF,BX,BY,N,IC
      REAL RBUF(NBUF)
      INTEGER PIXMAP(BX,BY)
      INTEGER CTABLE(3,0:255)
      INTEGER R, G, B
*-
      I = NINT(RBUF(1))+1
      J = BY-NINT(RBUF(2))
      DO 10 N=3,NBUF
         IC=RBUF(N)
         R = CTABLE(1,IC)
         G = CTABLE(2,IC)
         B = CTABLE(3,IC)
         PIXMAP(I+N-3,J) = R + 256*(G + 256*B)
 10   CONTINUE
      END

**GRPP10 -- Replace # in filename by picture number
*
      SUBROUTINE GRPP10 (NAME1, NP, NAME2)
      CHARACTER*(*) NAME1
      CHARACTER*(*) NAME2
      CHARACTER*80  TMP
      INTEGER GRTRIM
      INTEGER NP, IDX, L, LN

      LN = GRTRIM(NAME1)
      IDX = INDEX(NAME1,'#')
      IF (IDX.GT.0) THEN
C        -- if the supplied name contains a #-character, replace
C           it with the page number
         CALL GRFAO(NAME1, L, TMP, NP, 0, 0, 0)
      ELSE IF (NP.EQ.1) THEN
C        -- if this is the first page, use the supplied name
         NAME2 = NAME1
         RETURN
      ELSE IF (LN+2.LE.LEN(NAME1)) THEN
C        -- append an underscore and the page number to the supplied
C           name
         NAME1(LN+1:LN+2) = '_#'
         CALL GRFAO(NAME1, L, TMP, NP, 0, 0, 0)
      ELSE
C        -- last resort: invent a new name
         CALL GRFAO('pgplot#.ppm', L, TMP, NP, 0, 0, 0)
      END IF
      CALL GRWARN ('Writing new PPM image as: '//TMP(:L))
      NAME2 = TMP(:L)
      END
