home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 350_01 / pcx_disp.c < prev    next >
C/C++ Source or Header  |  1991-12-01  |  40KB  |  1,141 lines

  1. /*
  2.  *************************************************************************
  3.  *
  4.  *  PCX_DISP.C - PCX_LIB Library Image Display Functions
  5.  *
  6.  *  Version:    1.00C
  7.  *
  8.  *  History:    91/02/14 - Created
  9.  *              91/04/01 - Release 1.00A
  10.  *              91/04/03 - Fixed "segread" call.
  11.  *              91/04/07 - Release 1.00B
  12.  *              91/11/18 - Fixed "pcx_set_palette" for large memory model.
  13.  *              91/12/01 - Release 1.00C
  14.  *
  15.  *  Compiler:   Microsoft C V6.0
  16.  *
  17.  *  Author:     Ian Ashdown, P.Eng.
  18.  *              byHeart Software
  19.  *              620 Ballantree Road
  20.  *              West Vancouver, B.C.
  21.  *              Canada V7S 1W3
  22.  *              Tel. (604) 922-6148
  23.  *              Fax. (604) 987-7621
  24.  *
  25.  *  Copyright:  Public Domain
  26.  *
  27.  *************************************************************************
  28.  */
  29.  
  30. /*
  31.  *************************************************************************
  32.  *
  33.  *  PORTABILITY NOTES
  34.  *
  35.  *  1.  While this program is written in ANSI C, it uses a number of 
  36.  *      function calls that are specific to the Microsoft C V6.0 library.
  37.  *      These are documented as follows for the purposes of porting this
  38.  *      program to other compilers and/or processors: 
  39.  *
  40.  *          _ffree              - "free" for small model / far data
  41.  *          _fmalloc            - "malloc" for small model / far data
  42.  *          _fmemcpy            - "memcpy" for small model / far data
  43.  *          int86               - execute 80x86 interrupt routine
  44.  *          int86x              - execute 80x86 interrupt routine (far
  45.  *                                data)
  46.  *          _remapallpalette    - remap entire video display color palette
  47.  *          _selectpalette      - select CGA color palette
  48.  *          outpw               - output word to 80x86 I/O port
  49.  *
  50.  *  2.  When porting this program to other processors, remember that words
  51.  *      are stored by 80x86-based machines in the big-endian format.  That
  52.  *      is, the eight least significant bits (lower byte) are stored
  53.  *      first, followed by the eight most significant bits (upper byte).
  54.  *      If PCX-format files are transferred to little-endian machines
  55.  *      (such as those based on 680x0 and Z8000 processors), the order of
  56.  *      bytes within each word will have to be reversed before they can 
  57.  *      be interpreted.  (This applies to the file header only, since the
  58.  *      encoded image data and optional 256-color palette are stored as
  59.  *      bytes.)
  60.  *
  61.  * 3.   MS-DOS does not recognize the 720 x 348 graphics mode of the
  62.  *      Hercules monochrome display adapter.  Therefore, the constant
  63.  *      PCX_HERC should never be passed as a video mode parameter to any
  64.  *      BIOS service routine.
  65.  *
  66.  *      The Microsoft C compiler includes a "video mode" parameter
  67.  *      definition (_HERCMONO) that is defined as 0x08.  This is a
  68.  *      reserved MS-DOS video mode that is apparently used internally by
  69.  *      the ROM BIOS.  It can, however, be passed to the Microsoft C
  70.  *      library function "_setvideomode" to force the Hercules display
  71.  *      adapter into graphics mode.
  72.  *
  73.  *      Most other MS-DOS C compilers offer similar library functions to
  74.  *      force the Hercules monochrome display adapter into its 720 x 348
  75.  *      graphics mode.
  76.  *
  77.  ************************************************************************* 
  78.  */
  79.  
  80. /*      INCLUDE FILES                                                   */
  81.  
  82. #include <stdio.h>
  83. #include <stdlib.h>
  84. #include <string.h>
  85. #include <conio.h>
  86. #include <dos.h>
  87. #include <malloc.h>
  88. #include <graph.h>
  89. #include "pcx_int.h"
  90.  
  91. /*      FORWARD REFERENCES                                              */
  92.  
  93. static BOOL pcx_read_init(PCX_WORKBLK *, int, int);
  94. static BOOL pcx_read_extpal(PCX_WORKBLK *);
  95. static BOOL pcx_read_header(PCX_WORKBLK *);
  96. static BOOL pcx_read_line(PCX_WORKBLK *, unsigned char *, int);
  97. static BOOL pcx_set_palette(PCX_PAL *, int);
  98.  
  99. static void pcx_cga_palette(PCX_PAL *, int);
  100. static void pcx_put_cga(PCX_WORKBLK *, unsigned char _far *, int);
  101. static void pcx_put_ega(PCX_WORKBLK *, unsigned char _far *, int);
  102. static void pcx_put_herc(PCX_WORKBLK *, unsigned char _far *, int);
  103. static void pcx_put_vga(PCX_WORKBLK *, unsigned char _far *, int);
  104.  
  105. /*      GLOBALS                                                         */
  106.  
  107. /*      PUBLIC FUNCTIONS                                                */
  108.  
  109. /*
  110.  *************************************************************************
  111.  *
  112.  *  PCX_READ - Read PCX Image File
  113.  *
  114.  *  Purpose:    To read and display a PCX-format image file.
  115.  *
  116.  *  Setup:      BOOL pcx_read
  117.  *              (
  118.  *                char *fname,
  119.  *                int vmode,
  120.  *                int page
  121.  *              )
  122.  *
  123.  *  Where:      fname is a PCX image file name.
  124.  *              vmode is the MS-DOS video mode.  Valid values are:
  125.  *
  126.  *                PCX_HERC -    720 x 348 Hercules monochrome
  127.  *                0x04 -        320 x 200 4-color CGA
  128.  *                0x05 -        320 x 200 4-color CGA (color burst off)
  129.  *                0x06 -        640 x 200 2-color CGA
  130.  *                0x0d -        320 x 200 16-color EGA/VGA
  131.  *                0x0e -        640 x 200 16-color EGA/VGA
  132.  *                0x0f -        640 x 350 2-color EGA/VGA
  133.  *                0x10 -        640 x 350 16-color EGA/VGA
  134.  *                0x11 -        640 x 480 2-color VGA
  135.  *                0x12 -        640 x 480 16-color VGA
  136.  *                0x13 -        320 x 200 256-color VGA
  137.  *
  138.  *              page is the video display page number.  Valid values are:
  139.  *
  140.  *                Mode PCX_HERC - 0 or 1
  141.  *                Mode 0x0d     - 0 to 7
  142.  *                Mode 0x0e     - 0 to 3
  143.  *                Mode 0x0f     - 0 or 1
  144.  *                Mode 0x10     - 0 or 1
  145.  *                All Other     - 0
  146.  *
  147.  *  Return:     TRUE if successful; otherwise FALSE.
  148.  *
  149.  *  Note:       The video display adapter must be in the appropriate mode
  150.  *              and active page for the image to be displayed.
  151.  *
  152.  *************************************************************************
  153.  */
  154.  
  155. BOOL pcx_read
  156. (
  157.   char *fname,
  158.   int vmode,
  159.   int page
  160. )
  161. {
  162.   int bpline;                   /* Number of bytes per scan line        */
  163.   int line_num;                 /* Scan line number                     */
  164.   int max_lines;                /* Maximum number of scan lines         */
  165.   unsigned char *linep;         /* PCX scan line buffer pointer         */
  166.   BOOL status = TRUE;           /* Return status                        */
  167.   PCX_WORKBLK *wbp;             /* PCX image file workblock pointer     */
  168.  
  169.   /* Open a PCX image file workblock                                    */
  170.  
  171.   if ((wbp = pcx_open(fname, FALSE)) == (PCX_WORKBLK *) NULL)
  172.     return (FALSE);
  173.  
  174.   /* Initialize the workblock for reading                               */
  175.  
  176.   if (pcx_read_init(wbp, vmode, page) == FALSE)
  177.   {
  178.     (void) pcx_close(wbp);      /* Close the PCX workblock              */
  179.     return (FALSE);
  180.   }
  181.  
  182.   /* Calculate the image height                                         */
  183.  
  184.   max_lines = wbp->header.yul + wbp->header.ylr + 1;
  185.  
  186.   /* Calculate number of bytes per line (for all color planes)          */
  187.  
  188.   bpline = wbp->header.bppscan * wbp->header.nplanes;
  189.  
  190.   /* Allocate the PCX scan line buffer                                  */
  191.  
  192.   if ((linep = (unsigned char *) malloc(bpline)) != (unsigned char *)
  193.       NULL)
  194.   {
  195.     /* Set the file pointer to the beginning of the encoded image data  */
  196.  
  197.     if (status == TRUE)
  198.       if (fseek(wbp->fp, (long) (sizeof(PCX_HDR)), SEEK_SET) != 0)
  199.         status = FALSE;
  200.  
  201.     /* Set the video display adapter color palette unless the PCX file  */
  202.     /* is Version 3.0 (i.e. - PC Paintbrush Version 2.8 w/o palette)    */
  203.  
  204.     if (status == TRUE)
  205.       if (wbp->header.version != 3)
  206.         if (pcx_set_palette(wbp->palettep, vmode) == FALSE)
  207.           status = FALSE;
  208.  
  209.     /* Read the image line by line                                      */
  210.  
  211.     if (status == TRUE)
  212.     {
  213.       for (line_num = 0; line_num < max_lines; line_num++)
  214.       {
  215.         /* Read the current scan line                                   */
  216.  
  217.         if ((status = pcx_read_line(wbp, linep, bpline)) == FALSE)
  218.         {
  219.           status = FALSE;
  220.           break;
  221.         }
  222.  
  223.         /* Display the current scan line                                */
  224.  
  225.         wbp->pcx_funcp(wbp, (unsigned char _far *) linep, line_num);
  226.       }
  227.     }
  228.  
  229.     free(linep);        /* Free the PCX scan line buffer                */
  230.   }
  231.   else
  232.     status = FALSE;
  233.                          
  234.   if (pcx_close(wbp) == FALSE)  /* Close the PCX workblock              */
  235.     status = FALSE;
  236.  
  237.   return (status);
  238. }
  239.  
  240. /*      PRIVATE FUNCTIONS                                               */
  241.  
  242. /*
  243.  *************************************************************************
  244.  *
  245.  *  PCX_READ_INIT - Initialize PCX Workblock For Reading
  246.  *
  247.  *  Purpose:    To initialize a PCX image file workblock for reading.
  248.  *
  249.  *  Setup:      static BOOL pcx_read_init
  250.  *              (
  251.  *                PCX_WORKBLK *wbp,
  252.  *                int vmode,
  253.  *                int page
  254.  *              )
  255.  *
  256.  *  Where:      wbp is a PCX workblock pointer.
  257.  *              vmode is the MS-DOS video mode.  Valid values are:
  258.  *
  259.  *                PCX_HERC -    720 x 348 Hercules monochrome
  260.  *                0x04 -        320 x 200 4-color CGA
  261.  *                0x05 -        320 x 200 4-color CGA (color burst off)
  262.  *                0x06 -        640 x 200 2-color CGA
  263.  *                0x0d -        320 x 200 16-color EGA/VGA
  264.  *                0x0e -        640 x 200 16-color EGA/VGA
  265.  *                0x0f -        640 x 350 2-color EGA/VGA
  266.  *                0x10 -        640 x 350 16-color EGA/VGA
  267.  *                0x11 -        640 x 480 2-color VGA
  268.  *                0x12 -        640 x 480 16-color VGA
  269.  *                0x13 -        320 x 200 256-color VGA
  270.  *
  271.  *              page is the video display page number.  Valid values are:
  272.  *
  273.  *                Mode PCX_HERC - 0 or 1
  274.  *                Mode 0x0d     - 0 to 7
  275.  *                Mode 0x0e     - 0 to 3
  276.  *                Mode 0x0f     - 0 or 1
  277.  *                Mode 0x10     - 0 or 1
  278.  *                All Other      - 0
  279.  *
  280.  *  Return:     TRUE if successful; otherwise FALSE.
  281.  *
  282.  *************************************************************************
  283.  */
  284.  
  285. static BOOL pcx_read_init
  286. (
  287.   PCX_WORKBLK *wbp,
  288.   int vmode,
  289.   int page
  290. )
  291. {
  292.   int width;                    /* Display width                        */
  293.   int leftover;                 /* Number of unseen bits                */
  294.   BOOL status = TRUE;           /* Return status                        */
  295.  
  296.   /* Read the file header                                               */
  297.  
  298.   if ((pcx_read_header(wbp)) == FALSE)
  299.     return (FALSE);
  300.  
  301.   /* Initialize the workblock color palette pointer                     */
  302.  
  303.   wbp->palettep = wbp->header.palette;
  304.   wbp->epal_flag = FALSE;
  305.  
  306.   /* Read the extended palette (if any)                                 */
  307.  
  308.   if (vmode == 0x13 && wbp->header.version == 5)
  309.     if (pcx_read_extpal(wbp) == FALSE)
  310.       return (FALSE);
  311.  
  312.   /* Initialize the display page address offset                         */
  313.  
  314.   wbp->page_offset = (unsigned long) 0L;
  315.  
  316.   switch (vmode)        /* Select PCX line display function             */
  317.   {
  318.     case PCX_HERC:      /* 720 x 348 Hercules monochrome                */
  319.  
  320.       /* Hercules monochrome display adapter supports 2 pages           */
  321.  
  322.       wbp->page_offset = 0x08000000L * (unsigned long) page;
  323.  
  324.       /* Calculate display width in pixels                              */
  325.  
  326.       width = min((wbp->header.xlr - wbp->header.xul + 1), 720);
  327.  
  328.       /* Calculate number of bytes to display                           */
  329.  
  330.       wbp->num_bytes = (width + 7) >> 3;
  331.  
  332.       /* Calculate mask for leftover bits                               */
  333.  
  334.       if ((leftover = width & 7) != 0)
  335.         wbp->mask = (0xff << (8 - leftover)) & 0xff;
  336.       else
  337.         wbp->mask = 0xff;
  338.  
  339.       wbp->pcx_funcp = pcx_put_herc;    /* Set the display function ptr */
  340.  
  341.       break;
  342.  
  343.     case 0x04:          /* 320 x 200 4-color CGA                        */
  344.     case 0x05:          /* 320 x 200 4-color CGA (color burst off)      */
  345.   
  346.       /* Calculate display width in pixels                              */
  347.  
  348.       width = min((wbp->header.xlr - wbp->header.xul + 1), 320);
  349.  
  350.       /* Calculate number of bytes to display                           */
  351.  
  352.       wbp->num_bytes = (width + 3) >> 2;
  353.  
  354.       /* Calculate mask for leftover bits                               */
  355.  
  356.       if ((leftover = (width & 3) << 1) != 0)
  357.         wbp->mask = (0xff << (8 - leftover)) & 0xff;
  358.       else
  359.         wbp->mask = 0xff;
  360.  
  361.       wbp->pcx_funcp = pcx_put_cga;     /* Set the display function ptr */
  362.  
  363.       break;
  364.  
  365.     case 0x06:          /* 640 x 200 2-color CGA                        */
  366.  
  367.       /* Calculate display width in pixels                              */
  368.  
  369.       width = min((wbp->header.xlr - wbp->header.xul + 1), 640);
  370.  
  371.       /* Calculate number of bytes to display                           */
  372.  
  373.       wbp->num_bytes = (width + 7) >> 3;
  374.  
  375.       /* Calculate mask for leftover bits                               */
  376.  
  377.       if ((leftover = width & 7) != 0)
  378.         wbp->mask = (0xff << (8 - leftover)) & 0xff;
  379.       else
  380.         wbp->mask = 0xff;
  381.  
  382.       wbp->pcx_funcp = pcx_put_cga;     /* Set the display function ptr */
  383.  
  384.       break;
  385.  
  386.     case 0x0d:          /* 320 x 200 16-color EGA/VGA                   */
  387.     case 0x0e:          /* 640 x 200 16-color EGA/VGA                   */
  388.     case 0x0f:          /* 640 x 350 2-color EGA/VGA                    */
  389.     case 0x10:          /* 640 x 350 16-color EGA/VGA                   */
  390.     case 0x11:          /* 640 x 480 2-color VGA                        */
  391.     case 0x12:          /* 640 x 480 16-color VGA                       */
  392.  
  393.       switch (vmode)    /* Initialize the display adapter page offset   */
  394.       {
  395.         case 0x0d:      /* 320 x 200 16-color EGA/VGA (8 pages maximum) */
  396.  
  397.           wbp->page_offset = 0x02000000L * (unsigned long) page;
  398.  
  399.           break;
  400.  
  401.         case 0x0e:      /* 640 x 200 16-color EGA/VGA (4 pages maximum) */
  402.  
  403.           wbp->page_offset = 0x04000000L * (unsigned long) page;
  404.  
  405.           break;
  406.  
  407.         case 0x0f:      /* 640 x 350 2-color EGA/VGA (2 pages maximum)  */
  408.         case 0x10:      /* 640 x 350 16-color EGA/VGA (2 pages maximum) */
  409.  
  410.           wbp->page_offset = 0x08000000L * (unsigned long) page;
  411.  
  412.           break;
  413.  
  414.         default:        /* All other modes support only one page        */
  415.  
  416.           break;
  417.       }
  418.  
  419.       /* Calculate display width in pixels                              */
  420.  
  421.       width = min((wbp->header.xlr - wbp->header.xul + 1), 640);
  422.  
  423.       /* Calculate number of bytes to display                           */
  424.  
  425.       wbp->num_bytes = (width + 7) >> 3;
  426.  
  427.       /* Calculate mask for leftover bits                               */
  428.  
  429.       if ((leftover = width & 7) != 0)
  430.         wbp->mask = (0xff << (8 - leftover)) & 0xff;
  431.       else
  432.         wbp->mask = 0xff;
  433.  
  434.       wbp->pcx_funcp = pcx_put_ega;     /* Set the display function ptr */
  435.  
  436.       break;
  437.  
  438.     case 0x13:          /* 320 x 200 256-color VGA                      */
  439.  
  440.       /* Calculate number of bytes to display                           */
  441.  
  442.       wbp->num_bytes = min((wbp->header.xlr - wbp->header.xul + 1), 320);
  443.  
  444.       wbp->mask = 0;  /* Dummy parameter                                */
  445.  
  446.       wbp->pcx_funcp = pcx_put_vga;     /* Set the display function ptr */
  447.  
  448.       break;
  449.  
  450.     default:            /* Other display adapters not supported         */
  451.  
  452.       status = FALSE;
  453.  
  454.       break;
  455.   }
  456.  
  457.   return (status);
  458. }
  459.  
  460. /*
  461.  *************************************************************************
  462.  *
  463.  *  PCX_READ_HEADER - Read PCX File Header
  464.  *
  465.  *  Purpose:    To read and validate a PCX file header.
  466.  *
  467.  *  Setup:      static BOOL pcx_read_header
  468.  *              (
  469.  *                PCX_WORKBLK *wbp
  470.  *              )
  471.  *
  472.  *  Where:      wbp is a PCX image file workblock pointer.
  473.  *
  474.  *  Return:     TRUE if successful; otherwise FALSE.
  475.  *
  476.  *  Result:     The file header is read into the "header" member of the
  477.  *              PCX workblock.
  478.  *
  479.  *************************************************************************
  480.  */
  481.  
  482. static BOOL pcx_read_header
  483. (
  484.   PCX_WORKBLK *wbp
  485. )
  486. {
  487.   BOOL status = TRUE;   /* Status flag                                  */
  488.   PCX_HDR *hdrp;        /* PCX file header buffer pointer               */
  489.  
  490.   hdrp = &(wbp->header);        /* Initialize the file header pointer   */
  491.  
  492.   /* Read the file header                                               */
  493.  
  494.   if (fseek(wbp->fp, 0L, SEEK_SET) != 0)
  495.     status = FALSE;
  496.  
  497.   if (status == TRUE)
  498.     if (fread(hdrp, sizeof(PCX_HDR), 1, wbp->fp) != 1)
  499.       status = FALSE;
  500.  
  501.   /* Validate the PCX file format                                       */
  502.  
  503.   if (status == TRUE)
  504.     if ((hdrp->pcx_id != 0x0a) || (hdrp->encoding != 1))
  505.       status = FALSE;
  506.  
  507.   return (status);
  508. }
  509.  
  510. /*
  511.  *************************************************************************
  512.  *
  513.  *  PCX_READ_EXTPAL - Read Extended Palette
  514.  *
  515.  *  Purpose:    To read an extended (256-color) palette (if it exists).
  516.  *
  517.  *  Setup:      static BOOL pcx_read_extpal
  518.  *              (
  519.  *                PCX_WORKBLK *wbp
  520.  *              )
  521.  *
  522.  *  Where:      wbp is a PCX image file workblock pointer.
  523.  *
  524.  *  Return:     TRUE if successful; otherwise FALSE.
  525.  *
  526.  *  Note:       It is possible for a PCX image file without an appended
  527.  *              256-color palette to have the value 0x0c as the 769th byte
  528.  *              (the location of the extended palette indicator byte) from 
  529.  *              the end of the file (i.e. - in the encoded image data 
  530.  *              section).  This function will misinterpret the following
  531.  *              768 bytes of encoded image data as an extended palette.
  532.  *
  533.  *              This problem will only occur if an attempt is made to
  534.  *              display a PCX image using the wrong MS-DOS video mode.  It
  535.  *              can be detected by decoding the image data and using 
  536.  *              "ftell" to note the file position of the end of the 
  537.  *              encoded image data section, then comparing it to the file
  538.  *              position of the indicator byte.  If the supposed indicator
  539.  *              byte is located within the encoded image data section, the
  540.  *              indicator byte is invalid and so the file header palette
  541.  *              should be used instead.
  542.  *
  543.  *************************************************************************
  544.  */
  545.  
  546. static BOOL pcx_read_extpal
  547. (
  548.   PCX_WORKBLK *wbp
  549. )
  550. {
  551.   int indicator;        /* PCX extended palette indicator               */
  552.  
  553.   /* Position the file pointer to the extended palette indicator byte   */
  554.  
  555.   if (fseek(wbp->fp, -769L, SEEK_END) != 0)
  556.     return (FALSE);
  557.  
  558.   /* Read the (assumed) extended palette indicator byte                 */
  559.  
  560.   if ((indicator = getc(wbp->fp)) == EOF)
  561.     return (FALSE);
  562.  
  563.   if (indicator == PCX_EPAL_FLAG)       /* Check for indicator byte     */
  564.   {
  565.     /* Allocate an extended palette buffer                              */
  566.  
  567.     if ((wbp->palettep = (PCX_PAL *) calloc(sizeof(PCX_PAL),
  568.         PCX_EPAL_SIZE)) == (PCX_PAL *) NULL)
  569.       return (FALSE);
  570.  
  571.     /* Read the extended palette                                        */
  572.  
  573.     if (fread(wbp->palettep, sizeof(PCX_PAL), PCX_EPAL_SIZE, wbp->fp) !=
  574.         PCX_EPAL_SIZE)
  575.     {
  576.       free(wbp->palettep);      /* Free the extended palette buffer     */
  577.       return (FALSE);
  578.     }
  579.  
  580.     wbp->epal_flag = TRUE;      /* Indicate extended palette present    */
  581.   }
  582.  
  583.   return (TRUE);
  584. }
  585.  
  586. /*
  587.  *************************************************************************
  588.  *
  589.  *  PCX_READ_LINE - Read PCX Line
  590.  *
  591.  *  Purpose:    To read an encoded line (all color planes) from a PCX-
  592.  *              format image file and write the decoded data to a line
  593.  *              buffer.
  594.  *
  595.  *  Setup:      static BOOL pcx_read_line
  596.  *              (
  597.  *                PCX_WORKBLK *wbp,
  598.  *                unsigned char *linep,
  599.  *                int bpline
  600.  *              )
  601.  *
  602.  *  Where:      wbp is a PCX image file workblock pointer.
  603.  *              linep is a PCX scan line buffer pointer.
  604.  *              bpline is the number of bytes per scan line (all color
  605.  *                planes).
  606.  *
  607.  *  Return:     TRUE if successful; otherwise FALSE.
  608.  *
  609.  *************************************************************************
  610.  */
  611.  
  612. static BOOL pcx_read_line
  613. (
  614.   PCX_WORKBLK *wbp,
  615.   unsigned char *linep,
  616.   int bpline
  617. )
  618. {
  619.   int data;             /* Image data byte                              */
  620.   int count;            /* Image data byte repeat count                 */
  621.   int offset = 0;       /* Scan line buffer offset                      */
  622.  
  623.   while (offset < bpline)       /* Decode current scan line             */
  624.   {
  625.     if ((data = getc(wbp->fp)) == EOF)  /* Get next byte                */
  626.       return (FALSE);
  627.  
  628.     /* If top two bits of byte are set, lower six bits show how         */
  629.     /* many times to duplicate next byte                                */
  630.  
  631.     if ((data & PCX_COMP_FLAG) == PCX_COMP_FLAG)
  632.     {
  633.       count = data & PCX_COMP_MASK;     /* Mask off repeat count        */
  634.  
  635.       if ((data = getc(wbp->fp)) == EOF)        /* Get next byte        */
  636.         return (FALSE);
  637.  
  638.       memset(linep, data, count);       /* Duplicate byte               */
  639.       linep += count;
  640.       offset += count;
  641.     }
  642.     else
  643.     {
  644.       *linep++ = (unsigned char) data;  /* Copy byte                    */
  645.       offset++;
  646.     }
  647.   }
  648.  
  649.   return (TRUE);
  650. }
  651.  
  652. /*
  653.  *************************************************************************
  654.  *
  655.  *  PCX_SET_PALETTE - Set Palette
  656.  *
  657.  *  Purpose:    To set the display palette according to a PCX file
  658.  *              palette.
  659.  *
  660.  *  Setup:      static BOOL pcx_set_palette
  661.  *              (
  662.  *                PCX_PAL *palettep,
  663.  *                int vmode
  664.  *              )
  665.  *
  666.  *  Where:      palettep is a pointer to a PCX file palette.
  667.  *              vmode is the MS-DOS video mode.  Valid values are:
  668.  *
  669.  *                PCX_HERC -    720 x 348 Hercules monochrome
  670.  *                0x04 -        320 x 200 4-color CGA
  671.  *                0x05 -        320 x 200 4-color CGA (color burst off)
  672.  *                0x06 -        640 x 200 2-color CGA
  673.  *                0x0d -        320 x 200 16-color EGA/VGA
  674.  *                0x0e -        640 x 200 16-color EGA/VGA
  675.  *                0x0f -        640 x 350 2-color EGA/VGA
  676.  *                0x10 -        640 x 350 16-color EGA/VGA
  677.  *                0x11 -        640 x 480 2-color VGA
  678.  *                0x12 -        640 x 480 16-color VGA
  679.  *                0x13 -        320 x 200 256-color VGA
  680.  *
  681.  *  Return:     TRUE if successful; otherwise FALSE.
  682.  *
  683.  *************************************************************************
  684.  */
  685.  
  686. static BOOL pcx_set_palette
  687. (
  688.   PCX_PAL *palettep,
  689.   int vmode
  690. )
  691. {
  692.   int i;                        /* Scratch counter                      */
  693.   int red_lo;                   /* Low red intensity                    */
  694.   int red_hi;                   /* High red intensity                   */
  695.   int green_lo;                 /* Green low intensity                  */
  696.   int green_hi;                 /* Green high intensity                 */
  697.   int blue_lo;                  /* Blue low intensity                   */
  698.   int blue_hi;                  /* Blue high intensity                  */
  699.   unsigned char _far *ega_palp; /* EGA 16-color palette buffer pointer  */
  700.   unsigned long *vga_palp;      /* VGA 256-color palette buffer pointer */
  701.   BOOL status = TRUE;           /* Return status                        */
  702.   union REGS regs;              /* 80x86 register values                */
  703.   struct SREGS sregs;           /* 80x86 segment register values        */
  704.  
  705.   switch (vmode)
  706.   {
  707.     case PCX_HERC:      /* 720 x 348 Hercules monochrome                */
  708.  
  709.       break;
  710.  
  711.     case 0x04:          /* 320 x 200 4-color CGA display                */
  712.     case 0x05:          /* 320 x 200 monochrome CGA display (burst off) */
  713.     case 0x06:          /* 640 x 200 2-color CGA display                */
  714.  
  715.       /* Set the CGA color palette                                      */
  716.  
  717.       pcx_cga_palette(palettep, vmode);
  718.  
  719.       break;
  720.  
  721.     case 0x0d:          /* 320 x 200 16-color EGA/VGA                   */
  722.     case 0x0e:          /* 640 x 200 16-color EGA/VGA                   */
  723.     case 0x0f:          /* 640 x 350 2-color EGA/VGA                    */
  724.     case 0x10:          /* 640 x 350 16-color EGA/VGA                   */
  725.     case 0x11:          /* 640 x 480 2-color VGA                        */
  726.     case 0x12:          /* 640 x 480 16-color VGA                       */
  727.  
  728.       if (pcx_isvga() == TRUE)  /* Check for VGA display adapter        */
  729.       {
  730.         /* Allocate a 16-color VGA display adapter palette buffer       */
  731.  
  732.         if ((vga_palp = (unsigned long *) calloc(sizeof(unsigned long),
  733.             PCX_PAL_SIZE)) == (unsigned long *) NULL)
  734.         {
  735.           status = FALSE;
  736.           break;
  737.         }
  738.  
  739.         /* Map PCX hardware palette to 16-color VGA palette (each color */
  740.         /* value is a "long" with the form:                             */
  741.         /*                                                              */
  742.         /*      00000000-00BBBBBB-00GGGGGG-00RRRRRR                     */
  743.         /*                                                              */
  744.         /* where each color is a 6-bit value.                           */
  745.  
  746.         for (i = 0; i < PCX_PAL_SIZE; i++)
  747.           vga_palp[i] = (long) (palettep[i].red >> 2) |
  748.               ((long) (palettep[i].green >> 2)) << 8 |
  749.               ((long) (palettep[i].blue >> 2)) << 16;
  750.  
  751.         (void) _remapallpalette(vga_palp);      /* Remap entire palette */
  752.  
  753.         free(vga_palp);         /* Free the VGA palette buffer          */
  754.       }
  755.       else      /* EGA display adapter                                  */
  756.       {
  757.         /* Allocate an EGA display adapter palette buffer               */
  758.  
  759.         if ((ega_palp = (unsigned char _far *)
  760.             _fmalloc(sizeof(unsigned char) * (PCX_PAL_SIZE + 1))) ==
  761.             (unsigned char *) NULL)
  762.         {
  763.           status = FALSE;
  764.           break;
  765.         }
  766.  
  767.         /* Map PCX hardware palette to 16-color EGA palette (each EGA   */
  768.         /* color value is an "unsigned char" with the form:             */
  769.         /*                                                              */
  770.         /*        0  0  R' G' B' R  G  B                                */
  771.         /*                                                              */
  772.         /* where X' is the low-intensity value and X is the high        */
  773.         /* intensity value for the color X.)                            */
  774.         /*                                                              */
  775.         /* NOTE: the "_remapallpalette" function could be used to set   */
  776.         /*       the palette for EGA display adapters.  However, this   */
  777.         /*       function does not appear to update the palette         */
  778.         /*       register values in the Dynamic Save Area (see the      */
  779.         /*       function header for "pcx_init_palette" in PCX_FILE.C)  */
  780.         /*       for a detailed explanation).                           */
  781.  
  782.         for (i = 0; i < PCX_PAL_SIZE; i++)
  783.         {
  784.           /* Extract low and high intensity bits for each color         */
  785.  
  786.           red_lo = (palettep[i].red >> 6) & 0x01;
  787.           red_hi = (palettep[i].red >> 6) & 0x02;
  788.           green_lo = (palettep[i].green >> 6) & 0x01;
  789.           green_hi = (palettep[i].green >> 6) & 0x02;
  790.           blue_lo = (palettep[i].blue >> 6) & 0x01;
  791.           blue_hi = (palettep[i].blue >> 6) & 0x02;
  792.  
  793.           /* Combine color intensity bits for EGA palette value         */
  794.  
  795.           ega_palp[i] = (unsigned char) ((red_lo << 5) | (green_lo << 4) |
  796.               (blue_lo << 3) | (red_hi << 1) | green_hi | (blue_hi >>
  797.               1));
  798.         }
  799.  
  800.         /* Set the border (overscan) color to black (BIOS default)      */
  801.  
  802.         ega_palp[16] = (unsigned char) 0;
  803.  
  804.         regs.h.ah = 0x10;       /* Select "Set All Palette Registers"   */
  805.         regs.h.al = 0x02;
  806.  
  807.         /* Get the EGA palette buffer segment and offset values         */
  808.  
  809.         sregs.es =  *((unsigned int _far *) &ega_palp + 1);
  810.         regs.x.dx = *((unsigned int _far *) &ega_palp);
  811.  
  812.         int86x(0x10, ®s, ®s, &sregs);  /* Call BIOS video service */
  813.  
  814.         _ffree(ega_palp);       /* Free the EGA palette buffer          */
  815.       }
  816.  
  817.       break;
  818.  
  819.     case 0x13:          /* 320 x 200 256-color VGA display              */
  820.  
  821.       /* Allocate a 256-color VGA display adapter palette buffer        */
  822.  
  823.       if ((vga_palp = (unsigned long *) calloc(sizeof(unsigned long),
  824.           PCX_EPAL_SIZE)) == (unsigned long *) NULL)
  825.       {
  826.         status = FALSE;
  827.         break;
  828.       }
  829.  
  830.       /* Map PCX extended palette to 256-color VGA palette              */
  831.  
  832.       for (i = 0; i < PCX_EPAL_SIZE; i++)
  833.         vga_palp[i] = (long) (palettep[i].red >> 2) |
  834.             ((long) (palettep[i].green >> 2)) << 8 |
  835.             ((long) (palettep[i].blue >> 2)) << 16;
  836.  
  837.       (void) _remapallpalette(vga_palp);        /* Remap entire palette */
  838.  
  839.       free(vga_palp);   /* Free the VGA palette buffer                  */
  840.  
  841.       break;
  842.  
  843.     default:            /* Other modes not supported                    */
  844.  
  845.       status = FALSE;
  846.  
  847.       break;
  848.   }
  849.  
  850.   return (status);
  851. }
  852.  
  853. /*
  854.  *************************************************************************
  855.  *
  856.  *  PCX_CGA_PALETTE - Select CGA Palette
  857.  *
  858.  *  Purpose:    To set the Color Graphics Adapter (CGA) display palette
  859.  *              according to a PCX file palette.
  860.  *
  861.  *  Setup:      static void pcx_cga_palette
  862.  *              (
  863.  *                PCX_PAL *palettep,
  864.  *                int vmode
  865.  *              )
  866.  *
  867.  *  Where:      palettep is a pointer to a PCX file palette.
  868.  *              vmode is the MS-DOS video mode.  Valid values are:
  869.  *
  870.  *                0x04 -        320 x 200 4-color CGA
  871.  *                0x05 -        320 x 200 4-color CGA (color burst off)
  872.  *                0x06 -        640 x 200 2-color CGA
  873.  *
  874.  *  Note:       ZSoft's PC Paintbrush products no longer support the CGA
  875.  *              color palette.  When a CGA color palette is encountered,
  876.  *              PC Paintbrush maps it to a monochrome (black and white)
  877.  *              palette.
  878.  *
  879.  *              MS-DOS video mode 0x05 (320 x 200 monochrome CGA display,
  880.  *              color burst off) will only display a monochrome image on
  881.  *              a composite video monitor (typically a television set with
  882.  *              an RF adapter).  All other monitors will display a color
  883.  *              palette in this mode (which is different for CGA and EGA
  884.  *              or VGA display adapters).
  885.  *
  886.  *              The "background" color is actually the foreground color
  887.  *              (i.e. - the color of activated pixels) for MS-DOS video
  888.  *              mode 0x06.  The background color is black for CGA display
  889.  *              adapters.
  890.  *
  891.  *************************************************************************
  892.  */
  893.  
  894. static void pcx_cga_palette
  895. (
  896.   PCX_PAL *palettep,
  897.   int vmode
  898. )
  899. {
  900.   short pal_num;        /* Palette number                               */
  901.   BOOL sel_flag;        /* Palette selector bit flag                    */
  902.   BOOL int_flag;        /* Intensity bit flag                           */
  903.   union REGS regs;      /* 80x86 register values                        */
  904.  
  905.   /* Set the background color                                           */
  906.  
  907.   regs.h.ah = 0x0b;     /* Select "Set Color Palette" BIOS routine      */
  908.   regs.h.bh = 0;
  909.  
  910.   regs.h.bl = (unsigned char) PCX_CGA_BKGND(palettep);
  911.  
  912.   int86(0x10, ®s, ®s);    /* Call BIOS video service              */
  913.  
  914.   if (vmode != 0x06)    /* Select the CGA color palette                 */
  915.   {
  916.     /* Check the palette selector bit flag                              */
  917.  
  918.     sel_flag = PCX_CGA_SELECT(palettep) ? TRUE : FALSE;
  919.  
  920.     /* Check the intensity bit flag                                     */
  921.  
  922.     int_flag = PCX_CGA_INTENSITY(palettep) ? TRUE : FALSE;
  923.  
  924.     /* Determine the CGA palette number                                 */
  925.  
  926.     if (int_flag == TRUE)       /* Intensity = bright                   */
  927.     {
  928.       if (sel_flag == TRUE)
  929.         pal_num = 3;    /* Light cyan - light magenta - white           */
  930.       else
  931.         pal_num = 1;    /* Cyan - magenta - light grey                  */
  932.     }
  933.     else                        /* Intensity = dim                      */
  934.     {
  935.       if (sel_flag == TRUE)
  936.         pal_num = 2;    /* Light green - light red - yellow             */
  937.       else
  938.         pal_num = 0;    /* Green - red - brown                          */
  939.     }
  940.  
  941.     /* Select the foreground color palette                              */
  942.  
  943.     (void) _selectpalette(pal_num);
  944.   }
  945. }
  946.  
  947. /*
  948.  *************************************************************************
  949.  *
  950.  *  PCX_PUT_HERC - Display Hercules PCX Line
  951.  *
  952.  *  Purpose:    To copy a decoded PCX image scan line to a Hercules
  953.  *              monochrome graphics display adapter buffer.
  954.  *
  955.  *  Setup:      static void pcx_put_herc
  956.  *              (
  957.  *                PCX_WORKBLK *wbp,
  958.  *                unsigned char _far *linep,
  959.  *                int line_num
  960.  *              )
  961.  *
  962.  *  Where:      wbp is a PCX image file workblock pointer.
  963.  *              linep is a PCX scan line buffer pointer.
  964.  *              line_num is the scan line number.
  965.  *
  966.  *************************************************************************
  967.  */
  968.  
  969. void pcx_put_herc
  970. (
  971.   PCX_WORKBLK *wbp,
  972.   unsigned char _far *linep,
  973.   int line_num
  974. )
  975. {
  976.   unsigned char _far *displayp;         /* Display buffer pointer       */
  977.  
  978.   /* Mask off unseen pixels                                             */
  979.  
  980.   linep[wbp->num_bytes - 1] &= wbp->mask;
  981.  
  982.   /* Calculate Hercules display buffer pointer                          */
  983.  
  984.   displayp = (unsigned char _far *) (0xb0000000L + wbp->page_offset) +
  985.       ((line_num >> 2) * 90) + 0x2000 * (line_num & 3);
  986.  
  987.   /* Copy data from the scan line buffer to the Hercules display buffer */
  988.  
  989.   (void) _fmemcpy(displayp, linep, wbp->num_bytes);
  990. }
  991.  
  992. /*
  993.  *************************************************************************
  994.  *
  995.  *  PCX_PUT_CGA - Display CGA PCX Line
  996.  *
  997.  *  Purpose:    To copy a decoded PCX image scan line to a CGA display
  998.  *              adapter buffer.
  999.  *
  1000.  *  Setup:      static void pcx_put_cga
  1001.  *              (
  1002.  *                PCX_WORKBLK *wbp,
  1003.  *                unsigned char _far *linep,
  1004.  *                int line_num
  1005.  *              )
  1006.  *
  1007.  *  Where:      wbp is a PCX image file workblock pointer.
  1008.  *              linep is a PCX scan line buffer pointer.
  1009.  *              line_num is the scan line number.
  1010.  *
  1011.  *************************************************************************
  1012.  */
  1013.  
  1014. static void pcx_put_cga
  1015. (
  1016.   PCX_WORKBLK *wbp,
  1017.   unsigned char _far *linep,
  1018.   int line_num
  1019. )
  1020. {
  1021.   unsigned char _far *displayp;         /* Display buffer pointer       */
  1022.  
  1023.   /* Mask off unseen pixels                                             */
  1024.  
  1025.   linep[wbp->num_bytes - 1] &= wbp->mask;
  1026.  
  1027.   /* Calculate CGA display buffer pointer                               */
  1028.  
  1029.   displayp = (unsigned char _far *) 0xb8000000L + ((line_num >> 1) * 80)
  1030.       + 0x2000 * (line_num & 1);
  1031.  
  1032.   /* Copy data from the scan line buffer to the CGA display buffer      */
  1033.  
  1034.   (void) _fmemcpy(displayp, linep, wbp->num_bytes);
  1035. }
  1036.  
  1037. /*
  1038.  *************************************************************************
  1039.  *
  1040.  *  PCX_PUT_EGA - Display EGA/VGA PCX Line
  1041.  *
  1042.  *  Purpose:    To copy a decoded PCX image scan line to an EGA/VGA
  1043.  *              display adapter buffer.
  1044.  *
  1045.  *  Setup:      static void pcx_put_ega
  1046.  *              (
  1047.  *                PCX_WORKBLK *wbp,
  1048.  *                unsigned char _far *linep,
  1049.  *                int line_num
  1050.  *              )
  1051.  *
  1052.  *  Where:      wbp is a PCX image file workblock pointer.
  1053.  *              linep is a PCX scan line buffer pointer.
  1054.  *              line_num is the scan line number.
  1055.  *
  1056.  *************************************************************************
  1057.  */
  1058.  
  1059. static void pcx_put_ega
  1060. (
  1061.   PCX_WORKBLK *wbp,
  1062.   unsigned char _far *linep,
  1063.   int line_num
  1064. )
  1065. {
  1066.   int plane_num;                /* EGA/VGA color plane number           */
  1067.   int plane_mask;               /* EGA/VGA color plane mask             */
  1068.   unsigned char _far *displayp; /* Display buffer pointer               */
  1069.  
  1070.   /* Calculate buffer pointer                                           */
  1071.  
  1072.   displayp = (unsigned char _far *) (0xa0000000L + wbp->page_offset) +
  1073.       line_num * 80;
  1074.  
  1075.   outpw(0x03ce, 0x0005);        /* Select EGA/VGA write mode 0          */
  1076.  
  1077.   /* Copy PCX scan line data to each color plane                        */
  1078.  
  1079.   plane_mask = 0x0100;          /* Start with color plane 0             */
  1080.  
  1081.   for (plane_num = 0; plane_num < (int) wbp->header.nplanes; plane_num++)
  1082.   {
  1083.     /* Mask off unseen pixels                                           */
  1084.  
  1085.     linep[wbp->num_bytes - 1] &= wbp->mask;
  1086.  
  1087.     outpw(0x03c4, plane_mask + 2);      /* Select current color plane   */
  1088.  
  1089.     /* Copy data from the scan line buffer to the EGA/VGA display       */
  1090.  
  1091.     (void) _fmemcpy(displayp, linep, wbp->num_bytes);
  1092.  
  1093.     linep += wbp->header.bppscan;       /* Increment plane offset       */
  1094.  
  1095.     plane_mask <<= 1;   /* Sequence plane mask                          */
  1096.   }
  1097.  
  1098.   outpw(0x03c4, 0x0f02);        /* Select all color planes              */
  1099. }
  1100.  
  1101. /*
  1102.  *************************************************************************
  1103.  *
  1104.  *  PCX_PUT_VGA - Display VGA PCX Line
  1105.  *
  1106.  *  Purpose:    To copy a decoded PCX image scan line to a VGA display
  1107.  *              adapter buffer.
  1108.  *
  1109.  *  Setup:      static void pcx_put_vga
  1110.  *              (
  1111.  *                PCX_WORKBLK *wbp,
  1112.  *                unsigned char _far *linep,
  1113.  *                int line_num
  1114.  *              )
  1115.  *
  1116.  *  Where:      wbp is a PCX image file workblock pointer.
  1117.  *              linep is a PCX scan line buffer pointer.
  1118.  *              line_num is the scan line number.
  1119.  *
  1120.  *************************************************************************
  1121.  */
  1122.  
  1123. static void pcx_put_vga
  1124. (
  1125.   PCX_WORKBLK *wbp,
  1126.   unsigned char _far *linep,
  1127.   int line_num
  1128. )
  1129. {
  1130.   unsigned char _far *displayp;         /* Display buffer pointer       */
  1131.  
  1132.   /* Calculate buffer pointer                                           */
  1133.  
  1134.   displayp = (unsigned char _far *) 0xa0000000L + line_num * 320;
  1135.  
  1136.   /* Copy data from the scan line buffer to the VGA display buffer      */
  1137.  
  1138.   (void) _fmemcpy(displayp, linep, wbp->num_bytes);
  1139. }
  1140.  
  1141.