home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
DP Tool Club 21
/
CD_ASCQ_21_040595.iso
/
dos
/
prg
/
pas
/
pcxkit53
/
pcx.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1995-01-17
|
17KB
|
545 lines
/* Developed with Turbo C++ for DOS, version 3.0, and Borland
and Borland C++, version 4.5.
Will compile only as COMPACT, LARGE, or HUGE, and the model
must match the .MODEL directive in the ASM files. */
// #define registered_version
/* ----------------------------------------------------------------------
PCX.CPP
Copyright (c) 1994
by Peter Donnelly
Skookum Software
1301 Ryan Street
Victoria BC Canada V8T 4Y8
╒══════════════════════════════════════════════════════════════════════╕
│ Permission is granted for the non-commercial distribution and │
│ private use of this source code. This is shareware; if you use all │
│ or portions of it in programs you distribute, or make any other │
│ public use of it, you are expected to pay a modest registration │
│ fee. Registered users will receive the latest version of the code, │
│ including support for 256-color Super-VGA modes. Please see the │
│ READ.ME file for details. │
╘══════════════════════════════════════════════════════════════════════╛
*/
#include "pcx.h"
#include <stdio.h>
#include <conio.h>
#include <io.h>
#include <string.h>
#include <dos.h>
#include <alloc.h>
#define VIDEO 0x10
#ifdef __cplusplus // don't mangle function names
extern "C" {
#endif
void Decode16(void);
void Decode256(void);
void DecodeSVGA256(void);
#ifdef __cplusplus
}
#endif
// Assembler variables
int file_error;
unsigned int ColumnCount;
unsigned int Plane;
unsigned int BytesPerLine;
unsigned char RepeatCount;
unsigned int DataLength;
int LineEnd, ScreenWidth;
int Margin;
unsigned int WindowEnd, WindowStep, WindowPos;
int BytesPerScanLine;
int StartCol, XMax;
int far *LineBuf;
unsigned int LineBufSeg, LineBufOffs, LineBufIndex;
int far *Scratch; // needs to be normalized
unsigned int VideoSeg, VideoOffs;
char WriteWindow;
unsigned int buffer_size;
const num_modes = 11;
union REGS inregs, outregs;
struct SREGS segregs;
FILE *pcx_file;
pathstr pcx_filename;
char *error_str;
int pic_width;
int our_modes[num_modes] = {0x0D, 0x0E, 0x10, 0x12, 0x13, 0x100, 0x101,
0x102, 0x103, 0x105, 0x107};
/* ------------------------ Structures ---------------------------- */
rgb_struct rgb_pal[15];
pcx_header_struct pcx_header;
VESA_info_struct VESA_info;
mode_info_struct mode_info;
typedef rgb_struct cregisters[256];
cregisters rgb256;
/* ---------------------- Video mode functions ------------------------- */
void video_off(int vid_status)
// Hides the image by turning off video refresh. See Ferraro p. 468.
// vid_status 0 = off, 1 = on
{
inregs.h.ah = 0x12;
inregs.h.bl = 0x36;
inregs.h.al = vid_status;
int86(VIDEO, &inregs, &outregs);
}
int detect_VESA(VESA_info_struct *VESA_rec) // returns !0 if VESA BIOS present
{
inregs.x.ax = 0x4F00;
segregs.es = FP_SEG(VESA_rec);
inregs.x.di = FP_OFF(VESA_rec);
int86x(VIDEO, &inregs, &outregs, &segregs);
if (outregs.h.ah) return 0;
return(!strncmp(VESA_rec->signature, "VESA", 4));
}
int detect_VGA(void) // see Ferraro p. 887
{
inregs.h.ah = 0x1A;
inregs.h.al = 0;
int86(VIDEO, &inregs, &outregs);
return(outregs.h.al != 0x1A);
}
int hardware_supports(unsigned int mode) // returns 1 if mode supported
{
unsigned int far *modeptr;
if (mode >= 0x100) {
if (detect_VESA(&VESA_info)) { // fills VESA_info structure
modeptr = VESA_info.mode_list_ptr;
do {
if (*modeptr++ == mode) return 1;
} while (*modeptr != 0xFFFF);
}
return 0; // didn't find match, or no VESA
}
return 1; // not VESA mode; assume supported
}
int we_support(unsigned int mode) // returns 1 if mode supported
{
int x;
int in_there = 0;
for (x = 0; x < num_modes; x++) {
if (mode == our_modes[x]) in_there = 1; }
return in_there;
}
int fits(pcx_header_struct header)
{
return((header.xmax < header.hres) && (header.ymax < header.vres));
}
void try_it(unsigned int mode, unsigned int *m)
{
if ((hardware_supports(mode)) && (we_support(mode)))
*m = mode;
return;
}
unsigned int best_mode(pcx_header_struct header)
/* Finds the lowest resolution at which the picture will fit on screen
but not lower than the originating device. */
{
unsigned int m;
if (header.num_planes == 1) {
m = 0x13;
if ((header.hres > 320) || (!fits(header))) try_it(0x101, &m);
if ((header.hres > 640) || (!fits(header))) try_it(0x103, &m);
if ((header.hres > 800) || (!fits(header))) try_it(0x105, &m);
if ((header.hres > 1024) || (!fits(header))) try_it(0x107, &m);
} else if (header.num_planes == 4) {
m = (header.hres <= 320) ? 0x0D : 0x0E;
if ((header.vres > 200) || (!fits(header))) try_it(0x10, &m);
if ((header.vres > 350) || (!fits(header))) try_it(0x12, &m);
if ((header.vres > 480) || (!fits(header))) try_it(0x102, &m);
} else {
file_error = 5;
m = 0xFFFF;
}
return m;
}
void get_mode_info(unsigned int mode, mode_info_struct *m)
// puts info on any VESA mode into the structure.
{
inregs.x.ax = 0x4F01; // VESA function
inregs.x.cx = mode;
segregs.es = FP_SEG(m);
inregs.x.di = FP_OFF(m);
int86x(VIDEO, &inregs, &outregs, &segregs);
/* Early versions of VESA BIOS extensions do not return values in the
xres and yres fields. We need to know yres for centering images. */
switch(mode) {
case 0x100: m->yres = 400; break;
case 0x101: m->yres = 480; break;
case 0x102: m->yres = 600; break;
case 0x103: m->yres = 600; break;
case 0x105: m->yres = 768; break;
case 0x107: m->yres = 1024;
}
return;
}
int get_mode(void)
{
int curr_mode;
if (detect_VESA(&VESA_info)) {
inregs.x.ax = 0x4F03;
int86(VIDEO, &inregs, &outregs);
curr_mode = outregs.x.bx; // may be inaccurate if not SVGA
curr_mode &= 0x3FFF; // - see Wilton p. 448
if (hardware_supports(curr_mode) && (curr_mode >= 0x100))
return curr_mode;
}
inregs.h.ah = 0x0F; // return VGA mode
int86(VIDEO, &inregs, &outregs);
return outregs.h.al;
}
void set_mode(int mode, int options)
{
if (mode >= 0x100) {
if (options & save_mem) mode |= 0x8000;
// set bit 15 to preserve video mem.
inregs.x.ax = 0x4F02;
inregs.x.bx = mode;
} else {
if (options & save_mem) mode |= 0x80;
// set bit 7 to preserve video mem.
inregs.x.ax = mode; // only use low byte
inregs.h.ah = 0; // function no.
}
int86(VIDEO, &inregs, &outregs);
return;
}
void put_window(int step, char window)
{
inregs.x.ax = 0x4f05; // VESA window function
inregs.h.bh = 0; // move-window subfunction
inregs.h.bl = window; // window A NEED TO CHECK WRITABILITY!!
inregs.x.dx = step; // window position, in granules
int86(VIDEO, &inregs, &outregs);
}
/* ------------------------ Palette functions ------------------------- */
void set_color_registers(cregisters rgb)
{
// Replaces the BGI setrgbpalette function
inregs.h.ah = 0x10; // function
inregs.h.al = 0x12; // subfunction
segregs.es = FP_SEG(rgb); // list of colors to put in
inregs.x.dx = FP_OFF(rgb);
inregs.x.bx = 0; // first register to change
inregs.x.cx = 0x100; // how many regs to change
int86x(VIDEO, &inregs, &outregs, &segregs);
return;
}
void set_palette(unsigned char *palette)
{
// Replaces the BGI setallpalette function
inregs.h.ah = 0x10; // function
inregs.h.al = 0x02; // subfunction
segregs.es = FP_SEG(palette); // list of 17 values to put in
inregs.x.dx = FP_OFF(palette);
int86x(VIDEO, &inregs, &outregs, &segregs);
return;
}
long get_256_palette(FILE *the_file, cregisters rgb256)
/* File must be open. Returns palette offset if present, 0 if not.
The last 769 bytes of the file are palette information, starting with
a one-byte identifier. Each group of three bytes represents the RGB
values of one of the color registers. We take the 6 most significant
bits to bring the values within the range 0-63 expected by the registers. */
{
unsigned char palette_flag;
long palette_start;
int x;
palette_start = filelength(fileno(the_file)) - 769;
fseek(the_file, palette_start, SEEK_SET);
palette_flag = getc(the_file);
if ((palette_flag != 12) || (pcx_header.version < 5)
|| (fread(rgb256, 3, 256, the_file) < 256)) {
file_error = 2;
return 0;
}
for (x = 0; x < 256; x++) {
rgb256[x].r >>= 2;
rgb256[x].g >>= 2;
rgb256[x].b >>= 2;
}
return palette_start;
}
/* ---------------------- Miscellaneous functions ---------------------- */
FILE *open_file(pathstr pic_file_name, char *file_mode,
pcx_header_struct *header)
{
FILE *f;
f = fopen(pic_file_name, file_mode);
if (f != NULL) fread(header, 1, 128, f);
return f;
}
char *report_error(int error)
{
switch(error) {
case 1: return "Could not open file.\n";
case 2: return "No palette information in file.\n";
case 3: return "Picture is too wide for requested video mode.\n";
case 4: return "Number of colors in file does not match selected mode.\n";
case 5: return "Unsupported picture format.\n";
default: return "Undefined error.\n";
}
}
void get_margin(int screenwd, int *margin, int *line_end,
pcx_header_struct header)
{
// Calculates how many pixels have to be skipped when advancing to
// the next line, so files of less than screen width can be displayed
// and centered.
*line_end = header.bytes_per_line;
*margin = screenwd - *line_end;
if (*margin < 0) file_error = 3; // too wide
return;
}
unsigned int set_buffer_size(void)
{
unsigned int buf;
buf = 64512;
if (buf > coreleft())
buf = coreleft() - (coreleft() % 1024);
// fread is faster if buffer_size is a round number?
return buf;
}
long get_first_pix(pcx_header_struct header, int optns,
int screenwid, int screenht)
/* The image is centered if the Options call for it. Otherwise it is offset
on the screen according to the values of XMin and YMin in the file header.
These are usually zero. This function returns the offset in bytes from
the start of the video buffer to where the first pixel will be written. */
{
long first_pix = 0;
int picwid, picht;
picwid = header.xmax - header.xmin + 1;
if (header.bits_per_plane == 1) picwid /= 8;
picht = header.ymax - header.ymin;
if (picht < screenht) {
if (!(optns & vcenter))
first_pix += (long)header.ymin * screenwid;
else first_pix += (long)(screenht-1-picht) / 2 * screenwid;
};
if (picwid < screenwid) {
if (!(optns & hcenter)) first_pix += header.xmin;
else first_pix += (screenwid - picwid) / 2;
};
return first_pix;
}
/* ---------------- Main procedure for 16-color modes ------------------ */
int read_16(FILE *pic_file, unsigned int mode, unsigned int options)
{
// don't call directly; needs pcx_header initialized by read_it()
typedef unsigned char palette_bytes[3];
unsigned char entry, gun, pcx_code;
unsigned char pal_regs[17];
unsigned int screen_height;
if (pcx_header.num_planes != 4) return 4;
if (mode >= 0x100) {
get_mode_info(mode, &mode_info);
ScreenWidth = mode_info.bytes_per_line;
screen_height = mode_info.yres;
} else
switch(mode) {
case 0x0D: ScreenWidth = 40; screen_height = 200; break;
case 0x0E: ScreenWidth = 80; screen_height = 200; break;
case 0x10: ScreenWidth = 80; screen_height = 350; break;
case 0x12: ScreenWidth = 80; screen_height = 480; break;
}
get_margin(ScreenWidth, &Margin, &LineEnd, pcx_header);
if (file_error) return file_error;
VideoOffs = get_first_pix(pcx_header, options, ScreenWidth, screen_height);
VideoSeg = 0xA000; // Segment of video memory
outportb(0x3C4, 2); // Index to map mask register }
Plane = 1; // Initialize plane }
outportb(0x3C5, Plane); // Set sequencer to mask out other planes }
// --- Decipher 16-color palette
/* The palette information is stored in bytes 16-63 of the header. Each of
the 16 palette slots is allotted 3 bytes - one for each primary color.
Any of these bytes can have a value of 0-255. However, the VGA is
capable only of 6-bit RGB values (making for 64x64x64 = 256K possible
colors), so we take only the 6 most significant bits from each PCX
color value.
In 16-color modes, the VGA uses the 16 CGA/EGA palette registers.
However, the actual color values (18 bits per slot) won't fit here,
so the palette registers are used as pointers to 16 of the 256 color
registers, which hold the RGB values.
What we have to do is extract the RGB values from the PCX header, put
them in the first 16 color registers, then set the palette to point to
those registers. */
for (entry = 0; entry < 16; entry++) {
for (gun = 0; gun < 3; gun++) {
pcx_code = ((palette_bytes &)pcx_header.palette[entry])[gun];
switch (gun) {
case 0: rgb_pal[entry].r = pcx_code >> 2; break;
case 1: rgb_pal[entry].g = pcx_code >> 2; break;
case 2: rgb_pal[entry].b = pcx_code >> 2;
}
}
pal_regs[entry] = entry;
}
pal_regs[16] = 0; // overscan color
set_color_registers(rgb_pal); // RGB values into registers 0-15
set_palette(pal_regs); // point to registers 0-15
// --- Read and decode the image data ---
BytesPerLine = pcx_header.bytes_per_line;
RepeatCount = 0; // Initialize assembler vars.
ColumnCount = 0;
buffer_size = set_buffer_size();
Scratch = (int far*)malloc(buffer_size);
fseek(pic_file, 128, SEEK_SET);
do {
DataLength = fread(Scratch, 1, buffer_size, pic_file);
Decode16();
} while (!feof(pic_file));
free(Scratch);
outportb(0x3C5, 0xF); // Reset mask map
return 0;
}
/* -------------------------- VGA 256-color modes ---------------------- */
int read_256(FILE *pic_file, unsigned int mode, unsigned int options)
{
// don't call directly; needs pcx_header initialized by read_it()
long palette_start, total_read;
if (pcx_header.num_planes != 1) return 4;
palette_start = get_256_palette(pic_file, rgb256);
if (!palette_start) return 2;
ScreenWidth = 320;
get_margin(ScreenWidth, &Margin, &LineEnd, pcx_header);
if (file_error) return file_error;
fseek(pic_file, 128, SEEK_SET);
total_read = 128;
RepeatCount = 0;
set_color_registers(rgb256);
VideoOffs = get_first_pix(pcx_header, options, ScreenWidth, 200);
VideoSeg = 0xA000;
buffer_size = set_buffer_size();
Scratch = (int far*)malloc(buffer_size);
do {
DataLength = fread(Scratch, 1, buffer_size, pic_file);
total_read += DataLength;
if (total_read > palette_start)
DataLength -= (total_read - palette_start);
Decode256();
} while ((!feof(pic_file)) && (total_read < palette_start));
free(Scratch);
if (mode); // to stop compiler warning
return 0;
}
/* ---------------------- SVGA 256-color files ------------------------- */
#ifdef registered_version
#include "svga256.h"
#else
int read_SVGA256(FILE *pic_file, unsigned int mode, unsigned int options)
{
set_mode(3, no_options);
printf("Support for this video mode is available only to registered\n"
"users of PCX.CPP. Please see READ.ME for details.\n");
return 0;
}
#endif
/* ---------------------------- read_it() ------------------------------ */
int read_it(pathstr pic_file_name, int mode, int options)
{ // returns file_error
FILE *pcx_file;
file_error = 0;
pcx_file = open_file(pic_file_name, "rb", &pcx_header);
if (pcx_file == NULL) return 1;
if ((pcx_header.bits_per_plane < 8) && (pcx_header.num_planes == 1)) {
fclose(pcx_file);
return 5;
}
if (mode == auto_set) mode = best_mode(pcx_header);
set_mode(mode, options);
if (options & blackout) video_off(1);
switch(mode) {
case 0x0D:
case 0x0E:
case 0x10:
case 0x12:
case 0x102: file_error = read_16(pcx_file, mode, options);
break;
case 0x13: file_error = read_256(pcx_file, mode, options);
break;
case 0x100:
case 0x101:
case 0x103:
case 0x105:
case 0x107: file_error = read_SVGA256(pcx_file, mode, options);
break;
}
if (options & blackout) video_off(0);
fclose(pcx_file);
return file_error;
}