home *** CD-ROM | disk | FTP | other *** search
/ C!T ROM 5 / ctrom5b.zip / ctrom5b / PROGRAM / DIVERSEN / PCXK53 / PCX.CPP < prev    next >
C/C++ Source or Header  |  1995-01-17  |  17KB  |  545 lines

  1. /* Developed with Turbo C++ for DOS, version 3.0, and Borland
  2.     and Borland C++, version 4.5.
  3.  
  4.     Will compile only as COMPACT, LARGE, or HUGE, and the model
  5.     must match the .MODEL directive in the ASM files. */
  6.  
  7. // #define registered_version
  8.  
  9. /* ----------------------------------------------------------------------
  10.  
  11.                   PCX.CPP
  12.  
  13.                  Copyright (c) 1994
  14.                  by Peter Donnelly
  15.                   Skookum Software
  16.                   1301 Ryan Street
  17.              Victoria BC Canada V8T 4Y8
  18.  
  19.    ╒══════════════════════════════════════════════════════════════════════╕
  20.    │  Permission is granted for the non-commercial distribution and       │
  21.    │  private use of this source code. This is shareware; if you use all  │
  22.    │  or portions of it in programs you distribute, or make any other     │
  23.    │  public use of it, you are expected to pay a modest registration     │
  24.    │  fee. Registered users will receive the latest version of the code,  │
  25.    │  including support for 256-color Super-VGA modes. Please see the     │
  26.    │  READ.ME file for details.                                           │
  27.    ╘══════════════════════════════════════════════════════════════════════╛
  28. */
  29. #include "pcx.h"
  30. #include <stdio.h>
  31. #include <conio.h>
  32. #include <io.h>
  33. #include <string.h>
  34. #include <dos.h>
  35. #include <alloc.h>
  36.  
  37. #define VIDEO 0x10
  38. #ifdef __cplusplus                     // don't mangle function names
  39.   extern "C" {
  40. #endif
  41. void Decode16(void);
  42. void Decode256(void);
  43. void DecodeSVGA256(void);
  44. #ifdef __cplusplus
  45. }
  46. #endif
  47.  
  48. // Assembler variables
  49.  
  50. int file_error;
  51. unsigned int ColumnCount;
  52. unsigned int Plane;
  53. unsigned int BytesPerLine;
  54. unsigned char RepeatCount;
  55. unsigned int DataLength;
  56. int LineEnd, ScreenWidth;
  57. int Margin;
  58. unsigned int WindowEnd, WindowStep, WindowPos;
  59. int BytesPerScanLine;
  60. int StartCol, XMax;
  61. int far *LineBuf;
  62. unsigned int LineBufSeg, LineBufOffs, LineBufIndex;
  63. int far *Scratch;              // needs to be normalized
  64. unsigned int VideoSeg, VideoOffs;
  65. char WriteWindow;
  66. unsigned int buffer_size;
  67. const num_modes = 11;
  68. union REGS inregs, outregs;
  69. struct SREGS segregs;
  70. FILE *pcx_file;
  71. pathstr pcx_filename;
  72. char *error_str;
  73. int pic_width;
  74. int our_modes[num_modes] = {0x0D, 0x0E, 0x10, 0x12, 0x13, 0x100, 0x101,
  75.                 0x102, 0x103, 0x105, 0x107};
  76.  
  77.  
  78. /* ------------------------ Structures ---------------------------- */
  79.  
  80. rgb_struct rgb_pal[15];
  81. pcx_header_struct pcx_header;
  82. VESA_info_struct VESA_info;
  83. mode_info_struct mode_info;
  84.  
  85. typedef rgb_struct cregisters[256];
  86. cregisters rgb256;
  87.  
  88. /* ---------------------- Video mode functions ------------------------- */
  89.  
  90. void video_off(int vid_status)
  91. // Hides the image by turning off video refresh. See Ferraro p. 468.
  92. // vid_status 0 = off, 1 = on
  93. {
  94. inregs.h.ah = 0x12;
  95. inregs.h.bl = 0x36;
  96. inregs.h.al = vid_status;
  97. int86(VIDEO, &inregs, &outregs);
  98. }
  99.  
  100.  
  101. int detect_VESA(VESA_info_struct *VESA_rec) // returns !0 if VESA BIOS present
  102. {
  103.   inregs.x.ax = 0x4F00;
  104.   segregs.es = FP_SEG(VESA_rec);
  105.   inregs.x.di = FP_OFF(VESA_rec);
  106.   int86x(VIDEO, &inregs, &outregs, &segregs);
  107.   if (outregs.h.ah) return 0;
  108.   return(!strncmp(VESA_rec->signature, "VESA", 4));
  109. }
  110.  
  111.  
  112. int detect_VGA(void)        // see Ferraro p. 887
  113. {
  114. inregs.h.ah = 0x1A;
  115. inregs.h.al = 0;
  116. int86(VIDEO, &inregs, &outregs);
  117. return(outregs.h.al != 0x1A);
  118. }
  119.  
  120.  
  121. int hardware_supports(unsigned int mode)    // returns 1 if mode supported
  122. {
  123.   unsigned int far *modeptr;
  124.  
  125.   if (mode >= 0x100) {
  126.     if (detect_VESA(&VESA_info)) {          // fills VESA_info structure
  127.       modeptr = VESA_info.mode_list_ptr;
  128.       do {
  129.     if (*modeptr++ == mode) return 1;
  130.       } while (*modeptr != 0xFFFF);
  131.     }
  132.     return 0;               // didn't find match, or no VESA
  133.   }
  134.   return 1;                 // not VESA mode; assume supported
  135. }
  136.  
  137. int we_support(unsigned int mode)           // returns 1 if mode supported
  138. {
  139.   int x;
  140.   int in_there = 0;
  141.  
  142.   for (x = 0; x < num_modes; x++) {
  143.     if (mode == our_modes[x]) in_there = 1; }
  144.   return in_there;
  145. }
  146.  
  147. int fits(pcx_header_struct header)
  148. {
  149.   return((header.xmax < header.hres) && (header.ymax < header.vres));
  150. }
  151.  
  152. void try_it(unsigned int mode, unsigned int *m)
  153. {
  154.   if ((hardware_supports(mode)) && (we_support(mode)))
  155.     *m = mode;
  156.   return;
  157. }
  158.  
  159. unsigned int best_mode(pcx_header_struct header)
  160. /* Finds the lowest resolution at which the picture will fit on screen
  161.    but not lower than the originating device. */
  162. {
  163.   unsigned int m;
  164.  
  165.   if (header.num_planes == 1) {
  166.     m = 0x13;
  167.     if ((header.hres > 320) || (!fits(header))) try_it(0x101, &m);
  168.     if ((header.hres > 640) || (!fits(header))) try_it(0x103, &m);
  169.     if ((header.hres > 800) || (!fits(header))) try_it(0x105, &m);
  170.     if ((header.hres > 1024) || (!fits(header))) try_it(0x107, &m);
  171.   } else if (header.num_planes == 4) {
  172.     m = (header.hres <= 320) ? 0x0D : 0x0E;
  173.     if ((header.vres > 200) || (!fits(header))) try_it(0x10, &m);
  174.     if ((header.vres > 350) || (!fits(header))) try_it(0x12, &m);
  175.     if ((header.vres > 480) || (!fits(header))) try_it(0x102, &m);
  176.   } else {
  177.     file_error = 5;
  178.     m = 0xFFFF;
  179.   }
  180.   return m;
  181.   }
  182.  
  183. void get_mode_info(unsigned int mode, mode_info_struct *m)
  184. // puts info on any VESA mode into the structure.
  185. {
  186.   inregs.x.ax = 0x4F01;           // VESA function
  187.   inregs.x.cx = mode;
  188.   segregs.es = FP_SEG(m);
  189.   inregs.x.di = FP_OFF(m);
  190.   int86x(VIDEO, &inregs, &outregs, &segregs);
  191.   /* Early versions of VESA BIOS extensions do not return values in the
  192.      xres and yres fields. We need to know yres for centering images. */
  193.   switch(mode) {
  194.     case 0x100: m->yres = 400; break;
  195.     case 0x101: m->yres = 480; break;
  196.     case 0x102: m->yres = 600; break;
  197.     case 0x103: m->yres = 600; break;
  198.     case 0x105: m->yres = 768; break;
  199.     case 0x107: m->yres = 1024;
  200.     }
  201.   return;
  202. }
  203.  
  204. int get_mode(void)
  205. {
  206.   int curr_mode;
  207.  
  208.   if (detect_VESA(&VESA_info)) {
  209.     inregs.x.ax = 0x4F03;
  210.     int86(VIDEO, &inregs, &outregs);
  211.     curr_mode = outregs.x.bx;          // may be inaccurate if not SVGA
  212.     curr_mode &= 0x3FFF;                //  - see Wilton p. 448
  213.     if (hardware_supports(curr_mode) && (curr_mode >= 0x100))
  214.       return curr_mode;
  215.   }
  216.     inregs.h.ah = 0x0F;                // return VGA mode
  217.     int86(VIDEO, &inregs, &outregs);
  218.     return outregs.h.al;
  219. }
  220.  
  221. void set_mode(int mode, int options)
  222. {
  223.   if (mode >= 0x100) {
  224.     if (options & save_mem) mode |= 0x8000;
  225.                   // set bit 15 to preserve video mem.
  226.     inregs.x.ax = 0x4F02;
  227.     inregs.x.bx = mode;
  228.     } else {
  229.     if (options & save_mem) mode |= 0x80;
  230.                   // set bit 7 to preserve video mem.
  231.     inregs.x.ax = mode;           // only use low byte
  232.     inregs.h.ah = 0;              // function no.
  233.     }
  234.     int86(VIDEO, &inregs, &outregs);
  235.   return;
  236. }
  237.  
  238. void put_window(int step, char window)
  239. {
  240.   inregs.x.ax = 0x4f05;           // VESA window function
  241.   inregs.h.bh = 0;                 // move-window subfunction
  242.   inregs.h.bl = window;                // window A    NEED TO CHECK WRITABILITY!!
  243.   inregs.x.dx = step;             // window position, in granules
  244.   int86(VIDEO, &inregs, &outregs);
  245. }
  246.  
  247. /* ------------------------ Palette functions ------------------------- */
  248.  
  249. void set_color_registers(cregisters rgb)
  250. {
  251. // Replaces the BGI setrgbpalette function
  252.  
  253.   inregs.h.ah = 0x10;             // function
  254.   inregs.h.al = 0x12;             // subfunction
  255.   segregs.es = FP_SEG(rgb);       // list of colors to put in
  256.   inregs.x.dx = FP_OFF(rgb);
  257.   inregs.x.bx = 0;                // first register to change
  258.   inregs.x.cx = 0x100;            // how many regs to change
  259.   int86x(VIDEO, &inregs, &outregs, &segregs);
  260.   return;
  261. }
  262.  
  263. void set_palette(unsigned char *palette)
  264. {
  265. // Replaces the BGI setallpalette function
  266.  
  267.   inregs.h.ah = 0x10;             // function
  268.   inregs.h.al = 0x02;             // subfunction
  269.   segregs.es = FP_SEG(palette);   // list of 17 values to put in
  270.   inregs.x.dx = FP_OFF(palette);
  271.   int86x(VIDEO, &inregs, &outregs, &segregs);
  272.   return;
  273. }
  274.  
  275. long get_256_palette(FILE *the_file, cregisters rgb256)
  276.  
  277. /* File must be open. Returns palette offset if present, 0 if not.
  278.  
  279.    The last 769 bytes of the file are palette information, starting with
  280.    a one-byte identifier. Each group of three bytes represents the RGB
  281.    values of one of the color registers. We take the 6 most significant
  282.    bits to bring the values within the range 0-63 expected by the registers. */
  283. {
  284.   unsigned char palette_flag;
  285.   long palette_start;
  286.   int x;
  287.  
  288.   palette_start = filelength(fileno(the_file)) - 769;
  289.   fseek(the_file, palette_start, SEEK_SET);
  290.   palette_flag = getc(the_file);
  291.   if ((palette_flag != 12) || (pcx_header.version < 5)
  292.      || (fread(rgb256, 3, 256, the_file) < 256)) {
  293.     file_error = 2;
  294.     return 0;
  295.   }
  296.   for (x = 0; x < 256; x++) {
  297.     rgb256[x].r >>= 2;
  298.     rgb256[x].g >>= 2;
  299.     rgb256[x].b >>= 2;
  300.   }
  301.   return palette_start;
  302. }
  303.  
  304. /* ---------------------- Miscellaneous functions ---------------------- */
  305.  
  306. FILE *open_file(pathstr pic_file_name, char *file_mode,
  307.         pcx_header_struct *header)
  308. {
  309.   FILE *f;
  310.  
  311.   f = fopen(pic_file_name, file_mode);
  312.   if (f != NULL) fread(header, 1, 128, f);
  313.   return f;
  314. }
  315.  
  316. char *report_error(int error)
  317. {
  318.   switch(error) {
  319.     case 1: return "Could not open file.\n";
  320.     case 2: return "No palette information in file.\n";
  321.     case 3: return "Picture is too wide for requested video mode.\n";
  322.     case 4: return "Number of colors in file does not match selected mode.\n";
  323.     case 5: return "Unsupported picture format.\n";
  324.     default: return "Undefined error.\n";
  325.     }
  326. }
  327.  
  328. void get_margin(int screenwd, int *margin, int *line_end,
  329.                 pcx_header_struct header)
  330. {
  331. // Calculates how many pixels have to be skipped when advancing to
  332. // the next line, so files of less than screen width can be displayed
  333. // and centered.
  334.  
  335.   *line_end = header.bytes_per_line;
  336.   *margin = screenwd - *line_end;
  337.   if (*margin < 0) file_error = 3;          // too wide
  338.   return;
  339. }
  340.  
  341. unsigned int set_buffer_size(void)
  342. {
  343.   unsigned int buf;
  344.  
  345.   buf = 64512;
  346.   if (buf > coreleft())
  347.     buf = coreleft() - (coreleft() % 1024);
  348.   // fread is faster if buffer_size is a round number?
  349.   return buf;
  350. }
  351.  
  352. long get_first_pix(pcx_header_struct header, int optns,
  353.                    int screenwid, int screenht)
  354.  
  355. /* The image is centered if the Options call for it. Otherwise it is offset
  356.   on the screen according to the values of XMin and YMin in the file header.
  357.   These are usually zero. This function returns the offset in bytes from
  358.   the start of the video buffer to where the first pixel will be written. */
  359. {
  360.  
  361.   long first_pix = 0;
  362.   int picwid, picht;
  363.  
  364.   picwid = header.xmax - header.xmin + 1;
  365.   if (header.bits_per_plane == 1) picwid /= 8;
  366.   picht = header.ymax - header.ymin;
  367.   if (picht < screenht) {
  368.     if (!(optns & vcenter))
  369.       first_pix += (long)header.ymin * screenwid;
  370.     else first_pix += (long)(screenht-1-picht) / 2 * screenwid;
  371.   };
  372.   if (picwid < screenwid) {
  373.     if (!(optns & hcenter)) first_pix += header.xmin;
  374.     else first_pix += (screenwid - picwid) / 2;
  375.   };
  376. return first_pix;
  377. }
  378.  
  379.  
  380. /* ---------------- Main procedure for 16-color modes ------------------ */
  381.  
  382. int read_16(FILE *pic_file, unsigned int mode, unsigned int options)
  383. {
  384.      // don't call directly; needs pcx_header initialized by read_it()
  385.   typedef unsigned char palette_bytes[3];
  386.  
  387.   unsigned char  entry, gun, pcx_code;
  388.   unsigned char pal_regs[17];
  389.   unsigned int screen_height;
  390.  
  391.   if (pcx_header.num_planes != 4) return 4;
  392.   if (mode >= 0x100) {
  393.     get_mode_info(mode, &mode_info);
  394.     ScreenWidth = mode_info.bytes_per_line;
  395.     screen_height = mode_info.yres;
  396.   } else
  397.   switch(mode) {
  398.     case 0x0D: ScreenWidth = 40; screen_height = 200; break;
  399.     case 0x0E: ScreenWidth = 80; screen_height = 200; break;
  400.     case 0x10: ScreenWidth = 80; screen_height = 350; break;
  401.     case 0x12: ScreenWidth = 80; screen_height = 480; break;
  402.   }
  403.   get_margin(ScreenWidth, &Margin, &LineEnd, pcx_header);
  404.   if (file_error) return file_error;
  405.   VideoOffs = get_first_pix(pcx_header, options, ScreenWidth, screen_height);
  406.   VideoSeg = 0xA000;              // Segment of video memory
  407.   outportb(0x3C4, 2);             // Index to map mask register }
  408.   Plane = 1;                      // Initialize plane }
  409.   outportb(0x3C5, Plane);         // Set sequencer to mask out other planes }
  410.  
  411.   // --- Decipher 16-color palette
  412.  
  413.   /*  The palette information is stored in bytes 16-63 of the header. Each of
  414.      the 16 palette slots is allotted 3 bytes - one for each primary color.
  415.      Any of these bytes can have a value of 0-255. However, the VGA is
  416.      capable only of 6-bit RGB values (making for 64x64x64 = 256K possible
  417.      colors), so we take only the 6 most significant bits from each PCX
  418.      color value.
  419.  
  420.      In 16-color modes, the VGA uses the 16 CGA/EGA palette registers.
  421.      However, the actual color values (18 bits per slot) won't fit here,
  422.      so the palette registers are used as pointers to 16 of the 256 color
  423.      registers, which hold the RGB values.
  424.  
  425.      What we have to do is extract the RGB values from the PCX header, put
  426.      them in the first 16 color registers, then set the palette to point to
  427.      those registers. */
  428.  
  429.   for (entry = 0; entry < 16; entry++) {
  430.     for (gun = 0; gun < 3; gun++) {
  431.       pcx_code = ((palette_bytes &)pcx_header.palette[entry])[gun];
  432.       switch (gun) {
  433.     case 0: rgb_pal[entry].r = pcx_code >> 2; break;
  434.     case 1: rgb_pal[entry].g = pcx_code >> 2; break;
  435.     case 2: rgb_pal[entry].b = pcx_code >> 2;
  436.       }
  437.     }
  438.     pal_regs[entry] = entry;
  439.   }
  440.   pal_regs[16] = 0;               // overscan color
  441.   set_color_registers(rgb_pal);   // RGB values into registers 0-15
  442.   set_palette(pal_regs);          // point to registers 0-15
  443.  
  444.   // --- Read and decode the image data ---
  445.  
  446.   BytesPerLine = pcx_header.bytes_per_line;
  447.   RepeatCount = 0;                // Initialize assembler vars.
  448.   ColumnCount = 0;
  449.   buffer_size = set_buffer_size();
  450.   Scratch = (int far*)malloc(buffer_size);
  451.   fseek(pic_file, 128, SEEK_SET);
  452.   do {
  453.     DataLength = fread(Scratch, 1, buffer_size, pic_file);
  454.     Decode16();
  455.   } while (!feof(pic_file));
  456.   free(Scratch);
  457.   outportb(0x3C5, 0xF);           // Reset mask map
  458.   return 0;
  459. }
  460.  
  461. /* -------------------------- VGA 256-color modes ---------------------- */
  462.  
  463. int read_256(FILE *pic_file, unsigned int mode, unsigned int options)
  464. {
  465.      // don't call directly; needs pcx_header initialized by read_it()
  466.   long palette_start, total_read;
  467.  
  468.   if (pcx_header.num_planes != 1) return 4;
  469.   palette_start = get_256_palette(pic_file, rgb256);
  470.   if (!palette_start) return 2;
  471.   ScreenWidth = 320;
  472.   get_margin(ScreenWidth, &Margin, &LineEnd, pcx_header);
  473.   if (file_error) return file_error;
  474.   fseek(pic_file, 128, SEEK_SET);
  475.   total_read = 128;
  476.   RepeatCount = 0;
  477.   set_color_registers(rgb256);
  478.   VideoOffs = get_first_pix(pcx_header, options, ScreenWidth, 200);
  479.   VideoSeg = 0xA000;
  480.   buffer_size = set_buffer_size();
  481.   Scratch = (int far*)malloc(buffer_size);
  482.   do {
  483.     DataLength = fread(Scratch, 1, buffer_size, pic_file);
  484.     total_read += DataLength;
  485.     if (total_read > palette_start)
  486.       DataLength -= (total_read - palette_start);
  487.     Decode256();
  488.   } while ((!feof(pic_file)) && (total_read < palette_start));
  489.   free(Scratch);
  490.   if (mode);                 // to stop compiler warning
  491.   return 0;
  492. }
  493.  
  494. /* ---------------------- SVGA 256-color files ------------------------- */
  495.  
  496. #ifdef registered_version
  497.   #include "svga256.h"
  498. #else
  499.  
  500. int read_SVGA256(FILE *pic_file, unsigned int mode, unsigned int options)
  501. {
  502.   set_mode(3, no_options);
  503.   printf("Support for this video mode is available only to registered\n"
  504.      "users of PCX.CPP. Please see READ.ME for details.\n");
  505.   return 0;
  506. }
  507. #endif
  508.  
  509. /* ---------------------------- read_it() ------------------------------ */
  510.  
  511. int read_it(pathstr pic_file_name, int mode, int options)
  512. {           // returns file_error
  513.   FILE *pcx_file;
  514.  
  515.   file_error = 0;
  516.   pcx_file = open_file(pic_file_name, "rb", &pcx_header);
  517.   if (pcx_file == NULL) return 1;
  518.   if ((pcx_header.bits_per_plane < 8) && (pcx_header.num_planes == 1)) {
  519.     fclose(pcx_file);
  520.     return 5;
  521.   }
  522.   if (mode == auto_set) mode = best_mode(pcx_header);
  523.   set_mode(mode, options);
  524.   if (options & blackout) video_off(1);
  525.   switch(mode) {
  526.     case 0x0D:
  527.     case 0x0E:
  528.     case 0x10:
  529.     case 0x12:
  530.     case 0x102: file_error = read_16(pcx_file, mode, options);
  531.      break;
  532.     case 0x13: file_error = read_256(pcx_file, mode, options);
  533.      break;
  534.     case 0x100:
  535.     case 0x101:
  536.     case 0x103:
  537.     case 0x105:
  538.     case 0x107: file_error = read_SVGA256(pcx_file, mode, options);
  539.      break;
  540.   }
  541.   if (options & blackout) video_off(0);
  542.   fclose(pcx_file);
  543.   return file_error;
  544. }
  545.