home *** CD-ROM | disk | FTP | other *** search
/ Piper's Pit BBS/FTP: ibm 0040 - 0049 / ibm0040-0049 / ibm0040.tar / ibm0040 / IMGPROC.ZIP / C6PCX.ZIP / PCX.C < prev    next >
Encoding:
C/C++ Source or Header  |  1990-10-11  |  34.1 KB  |  1,021 lines

  1. /*  
  2. Copyright 1990 by John Wiley & Sons, Inc.
  3.           All Rights Reserved.
  4. */
  5. /****************************************/
  6. /*        PCX Function Library          */
  7. /*       written in Turbo C 2.0         */
  8. /*                by                    */
  9. /*         Craig A. Lindley             */
  10. /*                                      */
  11. /*   Vers: 1.0  Last Update: 11/21/89   */
  12. /****************************************/
  13.  
  14. #include <stdio.h>
  15. #include <string.h>
  16. #include <process.h>
  17. #include <conio.h>
  18. #include <dos.h>
  19. #include <alloc.h>
  20. #include <graphics.h>
  21. #include "misc.h"
  22. #include "pcx.h"
  23. #include "vga.h"
  24.  
  25. /* Externally Accessable Global Variables */
  26. struct   PCX_File PCXData;        /* PCX File Hdr Variable */
  27. unsigned ImageWidth, ImageHeight;
  28.  
  29. /* Variables global to this file only */
  30. static FILE    *PCXFile;       /* file handle */
  31. static BYTE     ScanLine[MAXBYTESPERSCAN];
  32. static BYTE     PixelColorNum[MAXSCREENWIDTH];
  33. static unsigned Is256ColorFile;
  34. static struct   ExtendedPalette Color256Palette;
  35.  
  36.  
  37. /* Start of Functions */
  38.  
  39. CompletionCode ReadPCXFileHdr (char *FileName, int Verbose)
  40. {
  41.    unsigned Index;
  42.    char     String[80];
  43.  
  44.  
  45.    Is256ColorFile = FALSE;             /* initialize mode variable */
  46.  
  47.    if (!strchr(FileName,'.'))          /* is there an ext ? */
  48.    {
  49.       strcpy(String,FileName);         /* copy filename to buffer */
  50.       FileName = String;               /* FileName now pts at buffer */
  51.       strcat(FileName,".pcx");         /* if not add .pcx ext */
  52.    }
  53.    /* try to open the PCX file */
  54.    if ((PCXFile = fopen(FileName,"rb")) == NULL)
  55.    {
  56.       printf("PCX file: %s not found\n",FileName);
  57.       return(EFileNotFound);
  58.    }
  59.    /* try to read the file header record */
  60.    if (fread(&PCXData,sizeof(struct PCX_File),1,PCXFile) != 1)
  61.    {
  62.       printf("Error reading PCX file header\n");
  63.       return(EReadFileHdr);
  64.    }
  65.    /* check to make sure its a PCX file */
  66.    if (PCXData.PCXHeader.Header != PCXHdrTag)
  67.    {
  68.       printf("Error not a PCX file\n");
  69.       return(ENotPCXFile);
  70.    }
  71.    /* Yep, we've got a PCX file OK. Display info if requested */
  72.    if (Verbose)
  73.    {
  74.       clrscr();
  75.       printf("PCX Image Information for file: %s\n\n",FileName);
  76.       printf("\tVersion: %d\n", PCXData.PCXHeader.Version);
  77.       printf("\tCompression: %s\n",
  78.               PCXData.PCXHeader.Encode == 0 ? "None":"RLL");
  79.       printf("\tBits Per Pixel: %d\n",PCXData.PCXHeader.BitPerPix);
  80.       printf("\tX1: %d\n",PCXData.PCXHeader.X1);
  81.       printf("\tY1: %d\n",PCXData.PCXHeader.Y1);
  82.       printf("\tX2: %d\n",PCXData.PCXHeader.X2);
  83.       printf("\tY2: %d\n",PCXData.PCXHeader.Y2);
  84.       printf("\tHoriz Resolution: %d\n",PCXData.PCXHeader.Hres);
  85.       printf("\tVert  Resolution: %d\n",PCXData.PCXHeader.Vres);
  86.       printf("\tVMode: %d\n",PCXData.Info.Vmode);
  87.       printf("\tNumber of Planes: %d\n",PCXData.Info.NumOfPlanes);
  88.       printf("\tBytes Per Scan Line One Plane: %d\n",PCXData.Info.BytesPerLine);
  89.       printf("\nHit any key to proceed\n");
  90.       getch();                       /* wait for operator input */
  91.  
  92.       clrscr();
  93.       printf("Color Register Values for PCX file: %s\n\n",FileName);
  94.       for (Index = 0; Index < MAXPALETTECOLORS; Index++)
  95.       {
  96.          printf("Palette Index: %2d  R = %2x G = %2x B = %2x\n",
  97.         Index,PCXData.Palette[Index].Red,
  98.               PCXData.Palette[Index].Green,
  99.               PCXData.Palette[Index].Blue);
  100.       }
  101.       printf("\nHit <Enter> to proceed - ^C to abort\n");
  102.       getchar();                       /* wait for operator input */
  103.    }
  104.    return(NoError);
  105. }
  106.  
  107. static CompletionCode ExpandScanLine (FILE *InFile)
  108. {
  109.    register short    BitNum;
  110.    register unsigned ByteNum;
  111.    register short    CharRead;
  112.    unsigned          InPtr,RepCount,PixelsData;
  113.    unsigned          BytesToRead,PlaneNum,ShiftCount;
  114.    unsigned          ByteOffset, BitOffset;
  115.  
  116.    BytesToRead = PCXData.Info.NumOfPlanes * PCXData.Info.BytesPerLine;
  117.  
  118.    InPtr = ShiftCount = 0;             /* initialize vars */
  119.    do
  120.    {
  121.       CharRead = getc(InFile);         /* read a byte from the file */
  122.       if (CharRead == EOF)             /* error should never read EOF */
  123.          return(FALSE);                /* abort picture */
  124.  
  125.       if ((CharRead & 0xC0) == 0xC0)  /* a repeat tag ? */
  126.       {
  127.          RepCount = CharRead & ~0xC0;  /* repeat 1..63 */
  128.          CharRead = getc(InFile);      /* read byte to repeat */
  129.          if (CharRead == EOF)          /* error should never read EOF */
  130.             return(FALSE);             /* abort picture */
  131.  
  132.          while (RepCount--)            /* expand byte */
  133.             ScanLine[InPtr++] =        /* RepCount times */
  134.               CharRead;
  135.       }
  136.       else                             /* just a byte of data */
  137.         ScanLine[InPtr++] = CharRead;  /* store in buffer */
  138.    } while (InPtr < BytesToRead);      /* expand a full scan line */
  139. /*
  140. When we get here, we have an array, ScanLine, which is composed of
  141. NumOfPlanes sections each BytesPerLine long. For a normal EGA/VGA image
  142. this works out to be 4 planes of 80 bytes each. For a 256 color VGA image
  143. it is 1 plane of 320 bytes. For the normal image we must merge each of these
  144. bit planes into the array PixelColorNum so that we can display the
  145. resultant image. Each entry into this array corresponds to a pixel on
  146. a single scan line of the monitor. For a 256 color image, the ScanLine is
  147. simply copied into the PixelColorNum array because there is no interleaving
  148. of bit planes.
  149. */
  150.  
  151.    if (PCXData.PCXHeader.X2 == 319)    /* if 256 color image */
  152.       memcpy(PixelColorNum,ScanLine,ImageWidth);
  153.    else                                /* normal image file */
  154.    {
  155.       /* clear PixelColorNum array to zeros */
  156.       memset(PixelColorNum,'\0',ImageWidth);
  157.  
  158.       for (PlaneNum=0; PlaneNum < PCXData.Info.NumOfPlanes; PlaneNum++)
  159.       {
  160.          ByteOffset = PlaneNum * PCXData.Info.BytesPerLine;
  161.          for (ByteNum=0; ByteNum < PCXData.Info.BytesPerLine; ByteNum++)
  162.          {
  163.             /* read 8 bits of pixel data for one plane */
  164.            PixelsData = ScanLine[ByteOffset+ByteNum];
  165.            BitOffset = ByteNum * BITSPERBYTE;
  166.  
  167.            for (BitNum=BITSPERBYTE-1; BitNum >= 0; BitNum--)
  168.            {
  169.           if (PixelsData & (1 << BitNum))
  170.           {
  171.          /* OR in each component of the color */
  172.          PixelColorNum[BitOffset + (7 - BitNum)] |=
  173.             (1 << ShiftCount);
  174.           }
  175.        }
  176.          }
  177.          ShiftCount++;
  178.       }
  179.    }
  180. /*
  181. When we get here, the PixelColorNum array has a byte color value for each
  182. pixel on the display. Return an indication that this operation went
  183. smoothly.
  184. */
  185.  
  186.    return(TRUE);
  187. }
  188.  
  189. unsigned InstallPCXFilePalette(void)
  190. {
  191.    struct   palettetype palette;
  192.    union    REGS regs;
  193.    unsigned Index;
  194.  
  195.    /*
  196.    Always load the VGA palette as long as the version is not 3 which
  197.    doesn't have palette information in the file. If version 3
  198.    use the default palette
  199.    */
  200.  
  201.    if (PCXData.PCXHeader.Version != 3)
  202.    {
  203.       if (Is256ColorFile)           /* if a mode 13h file */
  204.       {
  205.          /*
  206.          When we get here, we have a mode 13h file image. In this
  207.          VGA mode, the palette mechanism is bypassed. The color
  208.          registers are loaded from the extended palette in the
  209.      PCX file. The values in the file's palette are four
  210.      times their actual values. They must be scaled before
  211.      being used. All 256 of the color registers must be loaded.
  212.          */
  213.  
  214.      for (Index=0; Index < MAX256PALETTECOLORS; Index++)
  215.      {
  216.          Color256Palette.Palette[Index].Red   >>= 2;
  217.          Color256Palette.Palette[Index].Green >>= 2;
  218.          Color256Palette.Palette[Index].Blue  >>= 2;
  219.      }
  220.  
  221.      /* set a block of Color Registers */
  222.      regs.h.ah = 0x10;
  223.      regs.h.al = 0x12;
  224.      regs.x.bx = 0;
  225.      regs.x.cx = MAX256PALETTECOLORS;
  226.      _ES = FP_SEG(&Color256Palette.Palette);
  227.      regs.x.dx =FP_OFF(&Color256Palette.Palette);
  228.      int86(VIDEO,®s,®s);
  229.          return(TRUE);       /* indicate palette installed successfully */
  230.       }
  231.       else
  232.       {
  233.          /*
  234.          When we get here we have a 16 color VGA image. We must
  235.          build a palette data structure with data loaded from the PCX
  236.          file. The palette is set up in sequential order and the
  237.      color register are set from values in the file. The file
  238.      values are scaled before being installed.
  239.          */
  240.  
  241.      palette.size = MAXPALETTECOLORS;
  242.  
  243.      for (Index = 0; Index < MAXPALETTECOLORS; Index++)
  244.      {
  245.         palette.colors[Index] = Index;
  246.         PCXData.Palette[Index].Red   >>= 2;
  247.         PCXData.Palette[Index].Green >>= 2;
  248.         PCXData.Palette[Index].Blue  >>= 2;
  249.      }
  250.  
  251.      /* set a block of Color Registers */
  252.      regs.h.ah = 0x10;
  253.      regs.h.al = 0x12;
  254.      regs.x.bx = 0;
  255.      regs.x.cx = MAXPALETTECOLORS;
  256.      _ES = FP_SEG(&PCXData.Palette);
  257.      regs.x.dx =FP_OFF(&PCXData.Palette);
  258.      int86(VIDEO,®s,®s);
  259.  
  260.      /* enable the palette we just read from the file */
  261.          setallpalette(&palette);
  262.          return(TRUE);       /* indicate palette installed successfully */
  263.       }
  264.    }
  265.    else
  266.       return(FALSE);         /* no palette info to load */
  267. }
  268.  
  269.  
  270. /*
  271. This function reads a PCX file into a buffer in memory. It does not
  272. alter the palette currently used for the VGA display.
  273. */
  274.  
  275. CompletionCode ReadPCXFileToBuf (char *FileName, BYTE huge * *BufferPtr)
  276. {
  277.    register unsigned ScanNum;          /* scan line being expanded
  278.                                           and displayed */
  279.    register unsigned ColNum;           /* pixel being read */
  280.    int      PCXError;
  281.    BYTE huge *ImageMemory;             /* memory block where image */
  282.                                        /* will be stored */
  283.    unsigned long PixelBufOffset;
  284.  
  285.    if ((PCXError = ReadPCXFileHdr(FileName,FALSE)) != NoError)
  286.       return(PCXError);
  287.  
  288.    /* Header has been read, now we are ready to read the PCX image */
  289.    /* PCC files cannot be displayed */
  290.  
  291.    if ((PCXData.PCXHeader.X1 != 0) || (PCXData.PCXHeader.Y1 != 0))
  292.    {
  293.       printf("Error PCC file not PCX file\n");
  294.       return (EPCCFile);
  295.    }
  296.  
  297.    /*
  298.    From the header information determine the size of the buffer
  299.    required to store the image. Set the global vars ImageWidth
  300.    and ImageHeight are accordingly.
  301.    */
  302.  
  303.    if (PCXData.PCXHeader.X2 == 319)
  304.    {
  305.       ImageWidth  = 320;
  306.       ImageHeight = 200;
  307.    }
  308.    else
  309.    {
  310.       ImageWidth = 640;
  311.       switch(PCXData.PCXHeader.Y2)
  312.       {
  313.      case 479: ImageHeight = 480;
  314.                  break;
  315.      case 349: ImageHeight = 350;
  316.            break;
  317.      case 199: ImageHeight = 200;
  318.            break;
  319.       }
  320.    }
  321.  
  322.    /* allocate far memory for the image */
  323.    ImageMemory = (BYTE huge *) farcalloc((long) ImageWidth * ImageHeight,
  324.                                           sizeof(BYTE));
  325.    if (ImageMemory == NULL)
  326.    {
  327.       printf("Error Not enough memory for PCX buffer\n");
  328.       return (ENoMemory);
  329.    }
  330.    /*
  331.    Proceed to unpack and store the PCX data. A scan line at
  332.    a time.
  333.    */
  334.  
  335.    for (ScanNum=0; ScanNum < ImageHeight; ScanNum++)
  336.    {
  337.       if (ExpandScanLine(PCXFile) != TRUE)
  338.       {
  339.          printf("Error Scanline corrupt in PCX file\n");
  340.      return(ECorrupt);
  341.       }
  342.       PixelBufOffset = (long) ScanNum * ImageWidth;
  343.       for (ColNum=0; ColNum < ImageWidth; ColNum++)
  344.       {
  345.          ImageMemory[PixelBufOffset + ColNum] =
  346.                     PixelColorNum[ColNum];
  347.       }
  348.    }
  349.    /*
  350.    Determine if the PCX file is a mode 13h extended color file by
  351.    trying to read the extended palette record located after the
  352.    raster data. If EOF is read then no extended palette info
  353.    is included in the file.
  354.    */
  355.  
  356.    Is256ColorFile = FALSE;   /* set global flag to indicate file type */
  357.  
  358.    if (fread(&Color256Palette,sizeof(struct ExtendedPalette),1,PCXFile) == 1)
  359.       /* Extended palette read ok. Now check tag. */
  360.       if (Color256Palette.ExtendedPalette == PCX256ColorTag)
  361.          /*
  362.          Tag is ok, extended palette RGB values in Color256Palette
  363.          structure.
  364.          */
  365.          Is256ColorFile = TRUE;
  366.  
  367.    /* file has been read prepare to close up shop */
  368.  
  369.    fclose(PCXFile);
  370.    *BufferPtr = ImageMemory;           /* return the buffer address */
  371.    return(NoError);
  372. }
  373.  
  374.  
  375. /*
  376. This function displays an image in a buffer. If SetMode is TRUE,
  377. the graphics mode will be set and the palette will be loaded. If FALSE,
  378. neither will be performed, the image will be displayed with current
  379. settings. If Pause is requested, this function will wait for a key
  380. press before returning to the calling code.
  381. */
  382.  
  383. void DisplayImageInBuf(BYTE huge *Image, unsigned SetMode, unsigned Pause)
  384. {
  385.    register unsigned ScanNum, PixelNum;
  386.    unsigned long PixelBufOffset;
  387.  
  388.    if (SetMode)
  389.       InitGraphics();        /* initialize graphic system if required */
  390.    if (ImageWidth == 320)    /* a 256 color image ? */
  391.    {
  392.       if (SetMode)           /* if a mode set is required */
  393.       {                      /* set mode and load palette */
  394.      Set256ColorMode();
  395.      InstallPCXFilePalette();
  396.       }
  397.       for (ScanNum=0; ScanNum < ImageHeight; ScanNum++)
  398.     for (PixelNum=0; PixelNum < ImageWidth; PixelNum++)
  399.     {
  400.        PixelBufOffset  = ScanNum;  /* done to prevent overflow */
  401.        PixelBufOffset *= ImageWidth;
  402.        PixelBufOffset += PixelNum;
  403.        PutPixel256(PixelNum,ScanNum,Image[PixelBufOffset]);
  404.     }
  405.    }
  406.    else
  407.    {
  408.       if (SetMode)           /* if mode set required */
  409.       {
  410.      switch(ImageHeight) /* determine VGA mode */
  411.      {
  412.         case 480:  setgraphmode(VGAHI);
  413.                break;
  414.         case 350:  setgraphmode(VGAMED);
  415.                break;
  416.         case 200:  setgraphmode(VGALO);
  417.                break;
  418.      }
  419.      InstallPCXFilePalette();
  420.       }
  421.       for (ScanNum=0; ScanNum < ImageHeight; ScanNum++)
  422.     for (PixelNum=0; PixelNum < ImageWidth; PixelNum++)
  423.     {
  424.        PixelBufOffset  = ScanNum;  /* done to prevent overflow */
  425.        PixelBufOffset *= ImageWidth;
  426.        PixelBufOffset += PixelNum;
  427.        putpixel(PixelNum,ScanNum,Image[PixelBufOffset]);
  428.     }
  429.    }
  430.    if (Pause)                /* if pause requested wait for key */
  431.       getch();
  432. }
  433.  
  434. /*
  435. This function reads and displays a PCX file.
  436. */
  437.  
  438. void DisplayPCXFile (char *FileName, int Verbose)
  439. {
  440.    register unsigned ScanNum;          /* scan line being expanded
  441.                                           and displayed */
  442.    register unsigned ColNum;           /* pixel being read */
  443.    int      PCXError;
  444.  
  445.  
  446.    if ((PCXError = ReadPCXFileHdr(FileName,Verbose)) != NoError)
  447.       exit(PCXError);
  448.  
  449.    /* Header has been read, now we are ready to display the PCX image */
  450.    /* PCC files cannot be displayed */
  451.  
  452.    if ((PCXData.PCXHeader.X1 != 0) || (PCXData.PCXHeader.Y1 != 0))
  453.    {
  454.       printf("Error PCC file not PCX file\n");
  455.       exit(EPCCFile);
  456.    }
  457.  
  458.    InitGraphics();             /* initialize graphs subsystem */
  459.  
  460.    /* From the header information determine which mode the display should
  461.       be in. If width is 320 then its mode 13 hex 256 colors. Otherwise
  462.       its a VGA mode with a width of 640. */
  463.  
  464.    if (PCXData.PCXHeader.X2 == 319)
  465.    {
  466.       Set256ColorMode();
  467.       ImageWidth  = 320;
  468.       ImageHeight = 200;
  469.    }
  470.    else
  471.    {
  472.       ImageWidth = 640;
  473.       switch(PCXData.PCXHeader.Y2)
  474.       {
  475.          case 479: setgraphmode(VGAHI);
  476.            ImageHeight = 480;
  477.                  break;
  478.          case 349: setgraphmode(VGAMED);
  479.            ImageHeight = 350;
  480.            break;
  481.      case 199: setgraphmode(VGALO);
  482.            ImageHeight = 200;
  483.            break;
  484.       }
  485.    }
  486.  
  487.  
  488.    /* proceed to unpack and diplay the PCX file */
  489.    for (ScanNum=0; ScanNum < ImageHeight; ScanNum++)
  490.    {
  491.       if (ExpandScanLine(PCXFile) != TRUE)
  492.       {
  493.          printf("Scanline corrupt in PCX file\n");
  494.          exit(ECorrupt);
  495.       }
  496.       if (ImageWidth == 320) /* 256 color mode */
  497.       {
  498.          for (ColNum=0; ColNum < ImageWidth; ColNum++)
  499.             PutPixel256(ColNum,ScanNum,(int) PixelColorNum[ColNum]);
  500.       }
  501.       else                   /* normal 16 color modes */
  502.       {
  503.          for (ColNum=0; ColNum < ImageWidth; ColNum++)
  504.             putpixel(ColNum,ScanNum,(int) PixelColorNum[ColNum]);
  505.       }
  506.    }
  507.    /*
  508.    Determine if the PCX file is a mode 13h extended color file by
  509.    trying to read the extended palette record located after the
  510.    raster data. If EOF is read then no extended palette info
  511.    is included in the file.
  512.    */
  513.  
  514.    Is256ColorFile = FALSE;   /* set global flag to indicate file type */
  515.  
  516.    if (fread(&Color256Palette,sizeof(struct ExtendedPalette),1,PCXFile) == 1)
  517.       /* Extended palette read ok. Now check tag. */
  518.       if (Color256Palette.ExtendedPalette == PCX256ColorTag)
  519.          /*
  520.          Tag is ok, extended palette RGB values in Color256Palette
  521.          structure.
  522.          */
  523.          Is256ColorFile = TRUE;
  524.  
  525.    /* file has been read prepare to close up shop */
  526.  
  527.    /* Install palette read from file */
  528.    InstallPCXFilePalette();
  529.  
  530.    fclose(PCXFile);
  531. }
  532.  
  533.  
  534. /*
  535. The following routines create a PCX file from a raster image
  536. or a memory buffer and writes it to disk.
  537. */
  538.  
  539. CompletionCode WritePCXHdr(char *FileName, unsigned BitsPerPixel,
  540.                unsigned MaxX,  unsigned MaxY, unsigned Planes,
  541.                unsigned BytesPerLine)
  542. {
  543.    struct palettetype palette;
  544.    unsigned    Index;
  545.    union REGS  regs;
  546.    char        String[80];
  547.  
  548.    if (!strchr(FileName,'.'))          /* is there an ext ? */
  549.    {                                   /* if not ... */
  550.       strcpy(String,FileName);         /* copy filename to buffer */
  551.       FileName = String;               /* FileName now pts at buffer */
  552.       strcat(FileName,".pcx");         /* add .pcx ext */
  553.    }
  554.  
  555.    if ((PCXFile = fopen(FileName,"w+b")) == NULL)
  556.    {
  557.       restorecrtmode();
  558.       printf("Could not open output PCX file\n");
  559.       return (EWrtOutFile);
  560.    }
  561.  
  562.    /* initialize the PCX file header info */
  563.    PCXData.PCXHeader.Header    = PCXHdrTag;
  564.    PCXData.PCXHeader.Version   = 5;
  565.    PCXData.PCXHeader.Encode    = 1;
  566.    PCXData.PCXHeader.BitPerPix = BitsPerPixel;
  567.    PCXData.PCXHeader.X1        = 0;
  568.    PCXData.PCXHeader.Y1        = 0;
  569.    PCXData.PCXHeader.X2        = MaxX-1;
  570.    PCXData.PCXHeader.Y2        = MaxY-1;
  571.    PCXData.PCXHeader.Hres      = MaxX;
  572.    PCXData.PCXHeader.Vres      = MaxY;
  573.    ImageWidth                  = MaxX;
  574.    ImageHeight                 = MaxY;
  575.    PCXData.Info.Vmode          = 0;
  576.    PCXData.Info.NumOfPlanes    = Planes;
  577.    PCXData.Info.BytesPerLine   = BytesPerLine;
  578.  
  579.    /*
  580.    Initialize the palette structure in the PCX file data. The palette
  581.    will be written to the PCX file regardless of whether it is a 16
  582.    or 256 color image. If its a 256 color image an extended palette
  583.    structure will be written at the end of the PCX raster data.
  584.    The palette values must be scaled up by four before being
  585.    written to the file.
  586.    */
  587.  
  588.    getpalette(&palette);
  589.    for (Index = 0; Index < palette.size; Index++)
  590.    {
  591.       regs.h.ah = 0x10;
  592.       regs.h.al = 0x15;
  593.       regs.x.bx = palette.colors[Index];
  594.       int86(VIDEO,®s,®s);
  595.       PCXData.Palette[Index].Red   = regs.h.dh <<= 2;
  596.       PCXData.Palette[Index].Green = regs.h.ch <<= 2;
  597.       PCXData.Palette[Index].Blue  = regs.h.cl <<= 2;
  598.    }
  599.  
  600.    /* clear the unused area at the end of the PCX header */
  601.    memset(&PCXData.Info.unused,'\0',sizeof(PCXData.Info.unused));
  602.  
  603.    /* now write the file header to the physical file */
  604.    if (fwrite(&PCXData,sizeof(struct PCX_File),1,PCXFile) != 1)
  605.    {
  606.       restorecrtmode();
  607.       printf("Error writing PCX file header\n");
  608.       return(EWrtFileHdr);
  609.    }
  610.    return(NoError);
  611. }
  612.  
  613.  
  614. static CompletionCode CompressScanLine(FILE *OutFile)
  615. {
  616.    register unsigned OutPtr,RepCount,RepChar;
  617.    register unsigned BytesToWrite;
  618.  
  619.    BytesToWrite = PCXData.Info.NumOfPlanes * PCXData.Info.BytesPerLine;
  620.  
  621.    OutPtr = 0;                         /* ptr to data to compress */
  622.    do
  623.    {
  624.       RepChar = ScanLine[OutPtr++];    /* get byte to start compression */
  625.       RepCount = 1;                    /* byte seen once at this point */
  626.       while ((ScanLine[OutPtr]==RepChar) &&
  627.              (RepCount < MaxRepCount)    &&
  628.          (OutPtr < BytesToWrite))
  629.       {
  630.          RepCount++;                   /* count all repetitions of char */
  631.          OutPtr++;                     /* bump ptr and check again */
  632.       }
  633.  
  634.       /* repeat sequence found or if chars has either or both MSBs set
  635.          than must process as a repetition count and char sequence */
  636.  
  637.       if ((RepCount > 1) || (RepChar > 0xBF))
  638.       {
  639.          RepCount |= 0xC0;             /* set two MSBs */
  640.          if (putc(RepCount,OutFile) == EOF) /* write count to file */
  641.             return(FALSE);             /* if error return error */
  642.       }
  643.       if (putc(RepChar,OutFile) == EOF)/* write char to file */
  644.          return(FALSE);                /* if error return error */
  645.    } while (OutPtr < BytesToWrite);    /* until all bytes in scan
  646.                                           are compressed */
  647.    return(TRUE);                       /* indicate operation successful */
  648. }
  649.  
  650. /*
  651. This function writes a PCX file to disk contained in a buffer in
  652. memory. The current palette being used for display is written to
  653. the PCX file. All entries in the PCX header are from the image
  654. which was originally read into the buffer under the assumption
  655. that the content of the image might change but not its basic
  656. parameters.
  657. */
  658.  
  659. CompletionCode WritePCXFileFromBuf (char *FileName, BYTE huge *ImageMemory)
  660. {
  661.    register unsigned PlaneNum, BitNum, ByteNum, PData;
  662.    register unsigned ScanLineNum,PixelNum, Index;
  663.    int PCXError;
  664.    unsigned long PixelBufOffset;
  665.    union    REGS regs;
  666.  
  667.    /* write out PCX header and palette */
  668.    if ((PCXError = WritePCXHdr(FileName, PCXData.PCXHeader.BitPerPix,
  669.            PCXData.PCXHeader.X2+1,PCXData.PCXHeader.Y2+1,
  670.                    PCXData.Info.NumOfPlanes,
  671.                    PCXData.Info.BytesPerLine)) != NoError)
  672.       return(PCXError);
  673.  
  674. /*
  675. At this point we will read the image from the buffer a scanline
  676. at at time. For 320x200 256 color images there is only a single bit plane
  677. so the data read from the buffer is placed directly into the ScanLine array
  678. for compressing. For normal VGA images, the color value returned need to be
  679. separated into their component parts which will be compressed separately.
  680. In essense, the single array of 640 bytes corresponding to 640 separate
  681. pixels on the scan line is broken up into four separate arrays 80 bytes
  682. apiece (all contained in ScanLine[]). These arrays are the i,r,g,b components
  683. of the pixel values. The separates components or planes are then compressed.
  684. The components are written to disk in the following order: blue, green,
  685. red and then intensity.
  686. */
  687.  
  688.    for (ScanLineNum=0; ScanLineNum < ImageHeight; ScanLineNum++)
  689.    {
  690.       PixelBufOffset = (long) ScanLineNum * ImageWidth;
  691.       if (PCXData.PCXHeader.X2 == 319)    /* if 256 color image */
  692.       {
  693.      for (PixelNum=0; PixelNum < ImageWidth; PixelNum++)
  694.         ScanLine[PixelNum] = ImageMemory[PixelBufOffset + PixelNum];
  695.       }
  696.       else                                /* normal image file */
  697.       {
  698.      /* clear ScanLine array to zeros for each scan line */
  699.      /* this is an array of NumOfPlanes * BytesPerLine bytes */
  700.      memset(ScanLine,'\0',MAXBYTESPERSCAN);
  701.  
  702.      for (PixelNum=0; PixelNum < ImageWidth; PixelNum++)
  703.      {
  704.             /* get pixel value from buffer */
  705.         PData = ImageMemory[PixelBufOffset + PixelNum];
  706.         ByteNum = PixelNum/BITSPERBYTE;         /* calc byte offset */
  707.         BitNum  = 7 - (PixelNum % BITSPERBYTE); /* calc bit offset */
  708.         for (PlaneNum=0; PlaneNum < PCXData.Info.NumOfPlanes; PlaneNum++)
  709.           if (PData & (1<<PlaneNum))           /* if bit in plane is 1 */
  710.          ScanLine[(PlaneNum * PCXData.Info.BytesPerLine) + ByteNum] |=
  711.               (1<<BitNum);
  712.      }
  713.       }
  714.  
  715.       if (CompressScanLine(PCXFile) != TRUE)    /* compress a complete scan */
  716.       {                                         /* line */
  717.          restorecrtmode();
  718.          printf("Error writing a compressed scan line\n");
  719.          return(EWrtScanLine);
  720.       }
  721.    }
  722.  
  723.    /*
  724.    Determine if the PCX file is a mode 13h extended color image.
  725.    If so, write an extended palette record to the file after the
  726.    raster data.
  727.    */
  728.  
  729.    if (ImageWidth == 320)    /* 256 color mode 13h image ? */
  730.    {                         /* yes it is */
  731.       /*
  732.       Read the 256 color register RGB values and store them in
  733.       the Color256Palette structure before writing them to the
  734.       PCX file. This structure is tagged to assure validity.
  735.       */
  736.  
  737.       Color256Palette.ExtendedPalette = PCX256ColorTag;
  738.  
  739.       /* get a block of Color Registers */
  740.       regs.h.ah = 0x10;
  741.       regs.h.al = 0x17;
  742.       regs.x.bx = 0;
  743.       regs.x.cx = MAX256PALETTECOLORS;
  744.       _ES = FP_SEG(&Color256Palette.Palette);
  745.       regs.x.dx =FP_OFF(&Color256Palette.Palette);
  746.       int86(VIDEO,®s,®s);
  747.  
  748.       /*
  749.       The palette data must be scaled up by four before
  750.       being written to the file.
  751.       */
  752.       for (Index=0; Index < MAX256PALETTECOLORS; Index++)
  753.       {
  754.     Color256Palette.Palette[Index].Red   <<= 2;
  755.     Color256Palette.Palette[Index].Green <<= 2;
  756.     Color256Palette.Palette[Index].Blue  <<= 2;
  757.       }
  758.  
  759.       /*
  760.       With all of the color register values read, write the
  761.       extended palette structure to the PCX file.
  762.       */
  763.  
  764.       if (fwrite(&Color256Palette,
  765.                  sizeof(struct ExtendedPalette),1,PCXFile) != 1)
  766.       {
  767.          restorecrtmode();
  768.          printf("Error writing extended palette structure\n");
  769.          fclose(PCXFile);              /* close the PCX file */
  770.          farfree((BYTE far *) ImageMemory);  /* return buffer memory */
  771.          return(EWrtExtPal);
  772.       }
  773.    }
  774.    /* file has been written prepare to close up shop */
  775.    fclose(PCXFile);                    /* close the completed PCX file */
  776.    farfree((BYTE far *) ImageMemory);  /* return buffer memory */
  777.    return(NoError);
  778. }
  779.  
  780. /*
  781. This function writes a PCX file to disk from the image currently
  782. being displayed on the monitor.
  783. */
  784.  
  785. void WritePCXFile (char *FileName, unsigned BitsPerPixel,
  786.            unsigned MaxX,  unsigned MaxY, unsigned Planes,
  787.            unsigned BytesPerLine)
  788. {
  789.    register unsigned PlaneNum, BitNum, ByteNum, PData;
  790.    register unsigned ScanLineNum,PixelNum;
  791.    int PCXError;
  792.    unsigned Index;
  793.    union    REGS regs;
  794.  
  795.  
  796.    /* write out PCX header and palette */
  797.    if ((PCXError = WritePCXHdr(FileName, BitsPerPixel,
  798.            MaxX, MaxY, Planes, BytesPerLine)) != NoError)
  799.       exit(PCXError);
  800.  
  801. /*
  802. At this point we will read the displayed image from the screen a scanline
  803. at at time. For 320x200 256 color images there is only a single bit plane
  804. so the data read from the screen is placed directly into the ScanLine array
  805. for compressing. For normal VGA images, the color value returned need to be
  806. separated into their component parts which will be compressed separately.
  807. In essense, the single array of 640 bytes corresponding to 640 separate
  808. pixels on the scan line is broken up into four separate arrays 80 bytes
  809. apiece (all contained in ScanLine[]). These arrays are the i,r,g,b components
  810. of the pixel values. The separates components or planes are then compressed.
  811. The components are written to disk in the following order: blue, green,
  812. red and then intensity.
  813. */
  814.  
  815.    for (ScanLineNum=0; ScanLineNum < ImageHeight; ScanLineNum++)
  816.    {
  817.       if (PCXData.PCXHeader.X2 == 319)    /* if 256 color image */
  818.       {
  819.      for (PixelNum=0; PixelNum < ImageWidth; PixelNum++)
  820.         ScanLine[PixelNum] = GetPixel256(PixelNum,ScanLineNum);
  821.       }
  822.       else                                /* normal image file */
  823.       {
  824.      /* clear ScanLine array to zeros for each scan line */
  825.      /* this is an array of NumOfPlanes * BytesPerLine bytes */
  826.      memset(ScanLine,'\0',MAXBYTESPERSCAN);
  827.  
  828.      for (PixelNum=0; PixelNum < ImageWidth; PixelNum++)
  829.      {
  830.         PData = getpixel(PixelNum,ScanLineNum); /* get pixel value */
  831.         ByteNum = PixelNum/BITSPERBYTE;         /* calc byte offset */
  832.         BitNum  = 7 - (PixelNum % BITSPERBYTE); /* calc bit offset */
  833.         for (PlaneNum=0; PlaneNum < PCXData.Info.NumOfPlanes; PlaneNum++)
  834.           if (PData & (1<<PlaneNum))           /* if bit in plane is 1 */
  835.          ScanLine[(PlaneNum * PCXData.Info.BytesPerLine) + ByteNum] |=
  836.               (1<<BitNum);
  837.      }
  838.       }
  839.  
  840.       if (CompressScanLine(PCXFile) != TRUE)    /* compress a complete scan */
  841.       {                                         /* line */
  842.          restorecrtmode();
  843.          printf("Error writing a compressed scan line\n");
  844.          exit(EWrtScanLine);
  845.       }
  846.    }
  847.    /*
  848.    Determine if the PCX file is a mode 13h extended color image.
  849.    If so, write an extended palette record to the file after the
  850.    raster data.
  851.    */
  852.  
  853.    if (ImageWidth == 320)    /* 256 color mode 13h image ? */
  854.    {                         /* yes it is */
  855.       /*
  856.       Read the 256 color register RGB values and store them in
  857.       the Color256Palette structure before writing them to the
  858.       PCX file. This structure is tagged to assure validity.
  859.       */
  860.  
  861.       Color256Palette.ExtendedPalette = PCX256ColorTag;
  862.  
  863.       for (Index = 0; Index < MAX256PALETTECOLORS; Index++)
  864.       {
  865.          regs.h.ah = 0x10;
  866.          regs.h.al = 0x15;
  867.          regs.x.bx = Index;  /* get this color reg RGB values */
  868.          int86(VIDEO,®s,®s);
  869.      Color256Palette.Palette[Index].Red   = regs.h.dh <<= 2;
  870.      Color256Palette.Palette[Index].Green = regs.h.ch <<= 2;
  871.      Color256Palette.Palette[Index].Blue  = regs.h.cl <<= 2;
  872.       }
  873.       /*
  874.       With all of the color register values read, write the
  875.       extended palette structure to the PCX file.
  876.       */
  877.  
  878.       if (fwrite(&Color256Palette,
  879.                  sizeof(struct ExtendedPalette),1,PCXFile) != 1)
  880.       {
  881.          restorecrtmode();
  882.          printf("Error writing extended palette structure\n");
  883.       }
  884.    }
  885.    /* file has been written prepare to close up shop */
  886.  
  887.    fclose(PCXFile);                    /* close the completed PCX file */
  888. }
  889.  
  890. /*
  891. This function reads a raw image file into memory. The amount
  892. of data read from the file is determined from the specified image
  893. dimensions. Various error codes are returns in the advent
  894. of an error reading the image file. If the read is successful,
  895. a pointer to the image data and the NoError status will be
  896. returned to the calling code. No assumptions are made about the
  897. format of the data read other than that it should be stored
  898. in sequential image buffer memory locations.
  899. */
  900.  
  901. CompletionCode ReadRawImageFileToBuf(char *FileName,
  902.                      unsigned ImageWidth,
  903.                      unsigned ImageHeight,
  904.                      BYTE huge * *BufferPtr)
  905. {
  906.    unsigned long RasterSize, Index;
  907.    BYTE huge    *ImageBuffer;
  908.    FILE         *ImageDataFile;
  909.    int           DataRead;
  910.  
  911.    /* Assigned an error value until read is completed successfully */
  912.    *BufferPtr = NULL;
  913.  
  914.    /* Calculate size of buffer required to store image */
  915.    RasterSize = (long)ImageWidth * ImageHeight;
  916.  
  917.    /* Check for sufficient memory for buffer */
  918.    if (RasterSize >= farcoreleft())
  919.    {
  920.       printf("Not enough memory for image!\n");
  921.       return(ENoMemory);
  922.    }
  923.    /* Allocate buffer from FAR heap */
  924.    ImageBuffer = (BYTE huge *)farcalloc(RasterSize,
  925.                        (unsigned long) sizeof(BYTE));
  926.  
  927.    /* Attempt to open the raw image data file */
  928.    if ((ImageDataFile = fopen(FileName,"rb")) == NULL)
  929.    {
  930.      printf("Cannot open image data file: %s\n",FileName);
  931.      farfree((char far *) ImageBuffer);
  932.      return(EFileNotFound);
  933.    }
  934.    /* With file open, read RasterSize number of bytes from file */
  935.    for (Index=0; Index < RasterSize; Index++)
  936.    {
  937.       /* Check each read for an error */
  938.       if((DataRead = fgetc(ImageDataFile)) == EOF)
  939.       {
  940.      fclose(ImageDataFile);   /* close file */
  941.      farfree((char far *) ImageBuffer);
  942.      return(ECorrupt);        /* return error code */
  943.       }
  944.       /* If all is well, store the BYTE read into the buffer */
  945.       ImageBuffer[Index] = (BYTE) DataRead;
  946.    }
  947.    /* Everythings ok. Close file, store ptr and return NoError */
  948.  
  949.    fclose(ImageDataFile);
  950.    *BufferPtr = ImageBuffer;      /* return ptr to buffer */
  951.    return(NoError);
  952. }
  953.  
  954. /*
  955. This function writes to a file the raw image data produced
  956. by the digitizer. NOTE: This function will convert the column
  957. by column image data returned by the digitizer to a row
  958. by row raster data format if Transpose is TRUE. Otherwise,
  959. the output file is written in the same manner as the data
  960. returned by the digitizer; column by column.
  961. */
  962.  
  963. CompletionCode WriteRawImageFileFromBuf(char *FileName,
  964.                     unsigned ImageWidth,
  965.                     unsigned ImageHeight,
  966.                     unsigned Transpose,
  967.                     BYTE huge *ImageBuffer)
  968. {
  969.    unsigned long RasterSize, Index, Col, Row;
  970.    FILE         *ImageDataFile;
  971.    BYTE          WriteData;
  972.  
  973.    /* Attempt to open the raw image output data file */
  974.    if ((ImageDataFile = fopen(FileName,"wb")) == NULL)
  975.    {
  976.      printf("Cannot open image data output file: %s\n",FileName);
  977.      return(EWrtOutFile);
  978.    }
  979.  
  980.    if (!Transpose)                /* write data in normal format */
  981.    {
  982.       /* Calculate number of bytes to write */
  983.       RasterSize = (long)ImageWidth * ImageHeight;
  984.  
  985.       /* With file open, write RasterSize number of bytes to file */
  986.       for (Index=0; Index < RasterSize; Index++)
  987.       {
  988.      /* Get the byte to write */
  989.      WriteData = ImageBuffer[Index];
  990.  
  991.      /* Check each write for an error */
  992.      if (fputc(WriteData,ImageDataFile) != WriteData)
  993.      {
  994.         fclose(ImageDataFile);   /* close file */
  995.         return(EWrtOutFile);     /* return error code */
  996.      }
  997.       }
  998.    }
  999.    else                           /* data must be in raster format */
  1000.    {
  1001.       for(Row=0; Row < ImageHeight; Row++)
  1002.      for (Col=0; Col < ImageWidth; Col++)
  1003.      {
  1004.         /* Get the byte to write */
  1005.         WriteData = ImageBuffer[(Col * ImageHeight) + Row];
  1006.  
  1007.         /* Check each write for an error */
  1008.         if (fputc(WriteData,ImageDataFile) != WriteData)
  1009.         {
  1010.            fclose(ImageDataFile);   /* close file */
  1011.            return(EWrtOutFile);     /* return error code */
  1012.         }
  1013.      }
  1014.    }
  1015.    /* Everythings ok. Close file, free buffer memory and return NoError */
  1016.  
  1017.    fclose(ImageDataFile);
  1018.    farfree((char far*) ImageBuffer);   /* free memory */
  1019.    return(NoError);
  1020. }
  1021.