/* ************************************************************************* * * PCX_FILE.C - PCX_LIB Library Image File Functions * * Version: 1.00B * * History: 91/02/14 - Created * 91/04/01 - Release 1.00A * 91/04/03 - fixed "segread" call. * 91/04/07 - Release 1.00B * * Compiler: Microsoft C V6.0 * * Author: Ian Ashdown, P.Eng. * byHeart Software * 620 Ballantree Road * West Vancouver, B.C. * Canada V7S 1W3 * Tel. (604) 922-6148 * Fax. (604) 987-7621 * * Copyright: Public Domain * ************************************************************************* */ /* ************************************************************************* * * PORTABILITY NOTES * * 1. While this program is written in ANSI C, it uses a number of * function calls that are specific to the Microsoft C V6.0 library. * These are documented as follows for the purposes of porting this * program to other compilers and/or processors: * * _ffree - "free" for small model / far data * _fmalloc - "malloc" for small model / far data * _fmemcpy - "memcpy" for small model / far data * int86 - execute 80x86 interrupt routine * int86x - execute 80x86 interrupt routine (far data) * outpw - output word to 80x86 I/O port * segread - get current 80x86 segment register values * * 2. When porting this program to other processors, remember that words * are stored by 80x86-based machines in the big-endian format. That * is, the eight least significant bits (lower byte) are stored * first, followed by the eight most significant bits (upper byte). * If PCX-format files are transferred to little-endian machines * (such as those based on 680x0 and Z8000 processors), the order of * bytes within each word will have to be reversed before they can * be interpreted. (This applies to the file header only, since the * encoded image data and optional 256-color palette are stored as * bytes.) * * 3. MS-DOS does not recognize the 720 x 348 graphics mode of the * Hercules monochrome display adapter. Therefore, the constant * PCX_HERC should never be passed as a video mode parameter to any * BIOS service routine. * * The Microsoft C compiler includes a "video mode" parameter * definition (_HERCMONO) that is defined as 0x08. This is a * reserved MS-DOS video mode that is apparently used internally by * the ROM BIOS. It can, however, be passed to the Microsoft C * library function "_setvideomode" to force the Hercules display * adapter into graphics mode. * * Most other MS-DOS C compilers offer similar library functions to * force the Hercules monochrome display adapter into its 720 x 348 * graphics mode. * ************************************************************************* */ /* INCLUDE FILES */ #include #include #include #include #include #include #include #include "pcx_int.h" /* FORWARD REFERENCES */ static BOOL pcx_encode(int, int, FILE *); static BOOL pcx_init_palette(PCX_PAL *, int); static BOOL pcx_write_extpal(FILE *); static BOOL pcx_write_line(unsigned char *, int, FILE *); static BOOL pcx_write_init(PCX_WORKBLK *, int, int, int, int); static void pcx_get_cga(PCX_WORKBLK *, unsigned char _far *, int); static void pcx_get_ega(PCX_WORKBLK *, unsigned char _far *, int); static void pcx_get_herc(PCX_WORKBLK *, unsigned char _far *, int); static void pcx_get_vga(PCX_WORKBLK *, unsigned char _far *, int); /* GLOBALS */ /* Default EGA palette register values */ static BYTE pcx_EGA_DefPal_1[16] = /* Modes 0x0d and 0x0e */ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }; static BYTE pcx_EGA_DefPal_2[16] = /* Mode 0x0f */ { 0x00, 0x08, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00 }; static BYTE pcx_EGA_DefPal_3[16] = /* Mode 0x10 */ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f }; /* PUBLIC FUNCTIONS */ /* ************************************************************************* * * PCX_WRITE - Write PCX File * * Purpose: To write a PCX-format image file from an image stored in * a video buffer. The image is assumed to start in the * upper left corner of the screen. * * Setup: BOOL pcx_write * ( * char *fname, * int vmode, * int page, * int width, * int height, * ) * * Where: fname is a PCX image file name. * vmode is the MS-DOS video mode. Valid values are: * * PCX_HERC - 720 x 348 Hercules monochrome * 0x04 - 320 x 200 4-color CGA * 0x05 - 320 x 200 4-color CGA (color burst off) * 0x06 - 640 x 200 2-color CGA * 0x0d - 320 x 200 16-color EGA/VGA * 0x0e - 640 x 200 16-color EGA/VGA * 0x0f - 640 x 350 2-color EGA/VGA * 0x10 - 640 x 350 16-color EGA/VGA * 0x11 - 640 x 480 2-color VGA * 0x12 - 640 x 480 16-color VGA * 0x13 - 320 x 200 256-color VGA * * page is the video display page number. Valid values are: * * Mode PCX_HERC - 0 or 1 * Mode 0x0d - 0 to 7 * Mode 0x0e - 0 to 3 * Mode 0x0f - 0 or 1 * Mode 0x10 - 0 or 1 * All Other - 0 * * width is the image width in pixels. * height is the image height in pixels. * * Return: TRUE if successful; otherwise FALSE. * ************************************************************************* */ BOOL pcx_write ( char *fname, int vmode, int page, int width, int height ) { int bpline; /* Number of bytes per scan line */ int line_num; /* Scan line number */ unsigned char *linep; /* Image scan line buffer pointer */ BOOL status = TRUE; /* Return status */ PCX_WORKBLK *wbp; /* PCX image file workblock pointer */ /* Open a PCX image file workblock */ if ((wbp = pcx_open(fname, TRUE)) == (PCX_WORKBLK *) NULL) return (FALSE); /* Initialize the workblock for writing */ if (pcx_write_init(wbp, vmode, page, width, height) == FALSE) status = FALSE; /* Calculate number of bytes per line (for all color planes) */ bpline = wbp->header.bppscan * wbp->header.nplanes; /* Allocate a scan line buffer */ if (status == TRUE) if ((linep = (unsigned char *) malloc(bpline)) == (unsigned char *) NULL) status = FALSE; /* Write the file header to the file */ if (status == TRUE) if (fwrite(&(wbp->header), sizeof(PCX_HDR), 1, wbp->fp) != 1) status = FALSE; /* Write the encoded image data to the file */ if (status == TRUE) { for (line_num = 0; line_num <= (int) wbp->header.ylr; line_num++) { /* Get the current video buffer scan line */ wbp->pcx_funcp(wbp, (unsigned char _far *) linep, line_num); /* Encode the scan line and write it to the file */ if (pcx_write_line(linep, bpline, wbp->fp) == FALSE) { status = FALSE; break; } } } if (vmode == 0x13) /* Is a 256-color palette supported ? */ { /* Write the extended palette to the file */ if (status == TRUE) if (pcx_write_extpal(wbp->fp) == FALSE) status = FALSE; } if (pcx_close(wbp) == FALSE) /* Close the PCX workblock */ status = FALSE; free(linep); /* Free the scan line buffer */ /* Remove the PCX image file if an error occurred */ if (status == FALSE) (void) remove(fname); return (status); } /* ************************************************************************* * * PCX_INIT_DSA - Initialize Dynamic Save Area * * Purpose: To set up a Video Services Primary Pointer Table and an * associated Dynamic Save Area where BIOS service "Set All * Palette Registers" (function 0x02) can store the EGA color * palette registers settings after it updates them. * * Setup: BOOL pcx_init_dsa * ( * PCX_VSB **vsbpp * ) * * Where: vsbp is a pointer to where a pointer to an instantiated * PCX_VSB structure is to be returned. * * Return: TRUE if successful; otherwise FALSE. * * Note: The EGA display adapter color palette registers are * write-only. In order to save the current color palette * with a PCX-format image file by calling "pcx_write", you * must call this function BEFORE you display the image in * the following MS-DOS video modes: * * 0x0d - 320 x 200 16-color EGA * 0x0e - 640 x 200 16-color EGA * 0x0f - 640 x 350 2-color EGA * 0x10 - 640 x 350 16-color EGA * * You MUST call "pcx_free_dsa" upon completion of your * program. See the function header of "pcx_init_palette" * for more information. * ************************************************************************* */ BOOL pcx_init_dsa ( PCX_VSB **vsbpp ) { unsigned char _far *dsap; /* Dynamic Save Area pointer */ PCX_VSB *vsbp; /* Video services data save buffer ptr */ *vsbpp = (PCX_VSB *) NULL; /* Initialize returned pointer */ /* Allocate a Dynamic Save Area buffer */ if ((dsap = (unsigned char _far *) _fmalloc(sizeof(unsigned char) * 256)) == (unsigned char _far *) NULL) return (FALSE); /* Allocate a BIOS video services data save buffer */ if ((vsbp = (PCX_VSB *) malloc(sizeof(PCX_VSB))) == (PCX_VSB *) NULL) { _ffree(dsap); /* Free the Dynamic Save Area buffer */ return (FALSE); } /* Save the existing Primary Pointer Table pointer */ vsbp->prev_pptp = *((struct pcx_ppt _far * _far *) 0x004000a8L); /* Copy the existing Primary Pointer Table into the buffer */ (void) _fmemcpy((struct pcx_ppt _far *) &(vsbp->pcx_ppt), vsbp->prev_pptp, sizeof(struct pcx_ppt)); vsbp->pcx_ppt.dsap = dsap; /* Update the Dynamic Save Area ptr */ /* Update the Primary Pointer Table pointer in the Video Save Table */ *((struct pcx_ppt _far * _far *) 0x004000a8L) = &(vsbp->pcx_ppt); *vsbpp = vsbp; /* Return Video Services data save buffer ptr */ return (TRUE); } /* ************************************************************************* * * PCX_FREE_DSA - Release Dynamic Save Area * * Purpose: To release memory allocated to the Video Services Primary * Pointer Table and associated Dynamic Save Area and reset * the pointer in the Video Save Table. * * Setup: void pcx_free_dsa * ( * PCX_VSB *vsbp * ) * * Where: vsbp is a pointer to a PCX_VSB structure that was * previously allocated and initialized by "pcx_init_dsa". * * Note: You MUST call this function on completion of your program * if you previously called "pcx_init_dsa". Failure to do so * will leave the system in an unstable state. See the * function header of "pcx_init_palette" for more * information. * ************************************************************************* */ void pcx_free_dsa ( PCX_VSB *vsbp ) { /* Restore the previous primary pointer table pointer */ *((struct pcx_ppt _far * _far *) 0x004000a8L) = vsbp->prev_pptp; _ffree(vsbp->pcx_ppt.dsap); /* Free the Dynamic Save Area */ free(vsbp); /* Free the Video Services data save buffer */ } /* PRIVATE FUNCTIONS */ /* ************************************************************************* * * PCX_WRITE_INIT - Initialize PCX Workblock For Writing * * Purpose: To initialize a PCX image file workblock for writing. * * Setup: static BOOL pcx_write_init * ( * PCX_WORKBLK *wbp, * int vmode, * int page, * int width, * int height * ) * * Where: wbp is a PCX workblock pointer. * vmode is the MS-DOS video mode. Valid values are: * * 0x04 - 320 x 200 4-color CGA * 0x05 - 320 x 200 4-color CGA (color burst off) * 0x06 - 640 x 200 2-color CGA * Ox07 - 720 x 348 Hercules monochrome * 0x0d - 320 x 200 16-color EGA/VGA * 0x0e - 640 x 200 16-color EGA/VGA * 0x0f - 640 x 350 2-color EGA/VGA * 0x10 - 640 x 350 16-color EGA/VGA * 0x11 - 640 x 480 2-color VGA * 0x12 - 640 x 480 16-color VGA * 0x13 - 320 x 200 256-color VGA * * page is the video display page number. Valid values are: * * Mode PCX_HERC - 0 or 1 * Mode 0x0d - 0 to 7 * Mode 0x0e - 0 to 3 * Mode 0x0f - 0 or 1 * Mode 0x10 - 0 or 1 * All Other - 0 * * width is the image width in pixels. * height is the image height in pixels. * * Return: TRUE if successful; otherwise FALSE. * ************************************************************************* */ static BOOL pcx_write_init ( PCX_WORKBLK *wbp, int vmode, int page, int width, int height ) { int max_width; /* Maximum image width */ int max_height; /* Maximum image height */ int shift; /* Mask shift value */ BOOL status = TRUE; /* Return status */ PCX_HDR *hdrp; /* File header buffer pointer */ /* Initialize the display page address offset */ wbp->page_offset = (unsigned long) 0L; hdrp = &(wbp->header); /* Initialize the file header pointer */ /* Initialize the header constants */ hdrp->pcx_id = 0x0a; /* PCX format identifier */ hdrp->version = 5; /* Version number */ hdrp->encoding = 1; /* Encoding format (run-length) */ hdrp->xul = 0; /* Upper left x-position */ hdrp->yul = 0; /* Upper left y-position */ hdrp->reserved = 0; /* (Used to be video mode) */ hdrp->palette_type = 1; /* Color or b&w palette type */ memset(hdrp->filler, 0, sizeof(hdrp->filler)); /* Padding */ /* Initialize the video mode-dependent parameters */ switch (vmode) { case PCX_HERC: /* 720 x 348 Hercules monochrome */ max_width = min(width, 720); /* Maximum image width */ max_height = min(height, 348); /* Maximum image height */ hdrp->bppixel = 1; /* Bits per pixel */ hdrp->horz_res = 720; /* Horizontal resolution */ hdrp->vert_res = 348; /* Vertical resolution */ hdrp->nplanes = 1; /* Number of color planes */ /* Maximum two pages supported */ wbp->page_offset = 0x08000000L * (unsigned long) page; /* Calculate number of bytes to copy */ wbp->num_bytes = (max_width + 7) >> 3; shift = (max_width & 7); /* Calculate mask shift value */ wbp->pcx_funcp = pcx_get_herc; /* Set display capture fcn ptr */ break; case 0x04: /* 320 x 200 4-color CGA */ case 0x05: /* 320 x 200 4-color CGA (color burst off) */ max_width = min(width, 320); /* Maximum image width */ max_height = min(height, 200); /* Maximum image height */ hdrp->bppixel = 2; /* Bits per pixel */ hdrp->horz_res = 320; /* Horizontal resolution */ hdrp->vert_res = 200; /* Vertical resolution */ hdrp->nplanes = 1; /* Number of color planes */ /* Calculate number of bytes to copy */ wbp->num_bytes = (max_width + 3) >> 2; shift = (max_width & 3) << 1; /* Calculate mask shift value */ wbp->pcx_funcp = pcx_get_cga; /* Set display capture fcn ptr */ break; case 0x06: /* 640 x 200 2-color CGA */ max_width = min(width, 640); /* Maximum image width */ max_height = min(height, 200); /* Maximum image height */ hdrp->bppixel = 1; /* Bits per pixel */ hdrp->horz_res = 640; /* Horizontal resolution */ hdrp->vert_res = 200; /* Vertical resolution */ hdrp->nplanes = 1; /* Number of color planes */ /* Calculate number of bytes to copy */ wbp->num_bytes = (max_width + 7) >> 3; shift = (max_width & 7); /* Calculate mask shift value */ wbp->pcx_funcp = pcx_get_cga; /* Set display capture fcn ptr */ break; case 0x0d: /* 320 x 200 16-color EGA/VGA */ max_width = min(width, 320); /* Maximum image width */ max_height = min(height, 200); /* Maximum image height */ hdrp->bppixel = 1; /* Bits per pixel */ hdrp->horz_res = 320; /* Horizontal resolution */ hdrp->vert_res = 200; /* Vertical resolution */ hdrp->nplanes = 4; /* Number of color planes */ /* Maximum eight display pages supported */ wbp->page_offset = 0x02000000L * (unsigned long) page; /* Calculate number of bytes to copy */ wbp->num_bytes = (max_width + 7) >> 3; shift = (max_width & 7); /* Calculate mask shift value */ wbp->pcx_funcp = pcx_get_ega; /* Set display capture fcn ptr */ break; case 0x0e: /* 640 x 200 16-color EGA/VGA */ max_width = min(width, 640); /* Maximum image width */ max_height = min(height, 200); /* Maximum image height */ hdrp->bppixel = 1; /* Bits per pixel */ hdrp->horz_res = 640; /* Horizontal resolution */ hdrp->vert_res = 200; /* Vertical resolution */ hdrp->nplanes = 4; /* Number of color planes */ /* Maximum four display pages supported */ wbp->page_offset = 0x04000000L * (unsigned long) page; /* Calculate number of bytes to copy */ wbp->num_bytes = (max_width + 7) >> 3; shift = (max_width & 7); /* Calculate mask shift value */ wbp->pcx_funcp = pcx_get_ega; /* Set display capture fcn ptr */ break; case 0x0f: /* 640 x 350 2-color EGA/VGA */ max_width = min(width, 640); /* Maximum image width */ max_height = min(height, 350); /* Maximum image height */ hdrp->bppixel = 1; /* Bits per pixel */ hdrp->horz_res = 640; /* Horizontal resolution */ hdrp->vert_res = 350; /* Vertical resolution */ hdrp->nplanes = 2; /* Number of color planes */ /* Maximum two display pages supported */ wbp->page_offset = 0x08000000L * (unsigned long) page; /* Calculate number of bytes to copy */ wbp->num_bytes = (max_width + 7) >> 3; shift = (max_width & 7); /* Calculate mask shift value */ wbp->pcx_funcp = pcx_get_ega; /* Set display capture fcn ptr */ break; case 0x10: /* 640 x 350 16-color EGA/VGA */ max_width = min(width, 640); /* Maximum image width */ max_height = min(height, 350); /* Maximum image height */ hdrp->bppixel = 1; /* Bits per pixel */ hdrp->horz_res = 640; /* Horizontal resolution */ hdrp->vert_res = 350; /* Vertical resolution */ hdrp->nplanes = 4; /* Number of color planes */ /* Maximum two display pages supported */ wbp->page_offset = 0x08000000L * (unsigned long) page; /* Calculate number of bytes to copy */ wbp->num_bytes = (max_width + 7) >> 3; shift = (max_width & 7); /* Calculate mask shift value */ wbp->pcx_funcp = pcx_get_ega; /* Set display capture fcn ptr */ break; case 0x11: /* 640 x 480 2-color VGA */ max_width = min(width, 640); /* Maximum image width */ max_height = min(height, 480); /* Maximum image height */ hdrp->bppixel = 1; /* Bits per pixel */ hdrp->horz_res = 640; /* Horizontal resolution */ hdrp->vert_res = 480; /* Vertical resolution */ hdrp->nplanes = 1; /* Number of color planes */ /* Calculate number of bytes to copy */ wbp->num_bytes = (max_width + 7) >> 3; shift = (max_width & 7); /* Calculate mask shift value */ wbp->pcx_funcp = pcx_get_ega; /* Set display capture fcn ptr */ break; case 0x12: /* 640 x 480 16-color VGA */ max_width = min(width, 640); /* Maximum image width */ max_height = min(height, 480); /* Maximum image height */ hdrp->bppixel = 1; /* Bits per pixel */ hdrp->horz_res = 640; /* Horizontal resolution */ hdrp->vert_res = 480; /* Vertical resolution */ hdrp->nplanes = 4; /* Number of color planes */ /* Calculate number of bytes to copy */ wbp->num_bytes = (max_width + 7) >> 3; shift = (max_width & 7); /* Calculate mask shift value */ wbp->pcx_funcp = pcx_get_ega; /* Set display capture fcn ptr */ break; case 0x13: /* 320 x 200 256-color VGA */ max_width = min(width, 320); /* Maximum image width */ max_height = min(height, 200); /* Maximum image height */ hdrp->bppixel = 8; /* Bits per pixel */ hdrp->horz_res = 320; /* Horizontal resolution */ hdrp->vert_res = 200; /* Vertical resolution */ hdrp->nplanes = 1; /* Number of color planes */ /* Calculate number of bytes to copy */ wbp->num_bytes = max_width; shift = 0; /* Dummy parameter */ wbp->pcx_funcp = pcx_get_vga; /* Set display capture fcn ptr */ break; default: /* Other modes not supported */ status = FALSE; break; } /* Initialize common video mode-dependent parameters */ hdrp->xlr = max_width - 1; /* Lower right x-position */ hdrp->ylr = max_height - 1; /* Lower right y-position */ hdrp->scrn_width = hdrp->horz_res; /* Screen width */ hdrp->scrn_height = hdrp->vert_res; /* Screen height */ /* Calculate mask for "white" data */ if (shift != 0) wbp->mask = 0xff >> shift; else wbp->mask = 0x00; /* Initialize the file header palette */ status = pcx_init_palette(hdrp->palette, vmode); /* Calculate number of bytes per color plane scan line (must be an */ /* even number of bytes) */ hdrp->bppscan = 2 * (((((hdrp->xlr * hdrp->bppixel) + 7) / 8) + 1) / 2); return (status); } /* ************************************************************************* * * PCX_INIT_PALETTE - Initialize File Header Palette * * Purpose: To initialize the file header 16-color palette. * * Setup: static BOOL pcx_init_palette * ( * PCX_PAL *palettep, * int vmode * ) * * Where: palettep is a pointer to the PCX file header buffer * "palette" member. * vmode is the MS-DOS video mode. Valid values are: * * 0x04 - 320 x 200 4-color CGA * 0x05 - 320 x 200 4-color CGA (color burst off) * 0x06 - 640 x 200 2-color CGA * Ox07 - 720 x 348 Hercules monochrome * 0x0d - 320 x 200 16-color EGA/VGA * 0x0e - 640 x 200 16-color EGA/VGA * 0x0f - 640 x 350 2-color EGA/VGA * 0x10 - 640 x 350 16-color EGA/VGA * 0x11 - 640 x 480 2-color VGA * 0x12 - 640 x 480 16-color VGA * 0x13 - 320 x 200 256-color VGA * * Return: TRUE if successful; otherwise FALSE. * * Note: The CGA color palette is not supported. * * If a VGA display adapter is present, the color palette * registers can be read directly from the adapter using the * BIOS routine "Read All Palette Registers" (function 0x09). * * Unfortunately, the EGA display adapter color palette * registers are write-only. This means that the current * color palette for EGA displays cannot in general be read. * * The BIOS routine "Set All Palette Registers" (function * 0x02) will write the current palette register values to a * buffer called the Dynamic Save Area. The EGA color * palette can be read from the first 16 bytes of this 256- * byte RAM block. * * The Dynamic Save Area is not statically allocated; it must * be supplied by the user. The BIOS video services data in * segment 0x40 includes a pointer at address 0040:00a8 that * references the Video Services Primary Pointer Table in the * EGA/VGA BIOS. This table contains seven pointers, the * second of which is used by the "Set All Palette Registers" * routine to reference the Dynamic Save Area. Since the * Dynamic Save Area does not exist at system initialization, * the value of this pointer is 0000:0000 (in which case the * the updated palette register values are not saved to RAM * when they are updated). * * To utilize the EGA palette register save feature, the * user must perform the following: * * 1. Save a copy of the pointer at address 0040:00a8. * 2. Allocate a buffer for a new Primary Pointer Table. * 3. Copy the existing Primary Pointer Table to the * allocated buffer. * 4. Allocate a 256-byte buffer for a Dynamic Save Area. * 5. Initialize the second pointer of the Primary Pointer * Table to point to the Dynamic Save Area buffer. * * Before the program finishes, the user must also restore * the saved Primary Pointer Table pointer to address * 0040:00a8. Failure to do so will mean that subsequent * calls by other programs to the "Set All Palette * Registers" routine will result in the color palette * registers values being written to unallocated memory (or * memory that has been allocated for another purpose). * * The function "pcx_init_dsa" performs the five steps * outlined above, while the function "pcx_free_dsa" restores * the saved pointer on completion of your program. * * If the Dynamic Save Area pointer is 0000:0000 (the default * value at system initialization), the BIOS default color * palette settings for the appropriate mode are stored in * the file header color palette. * ************************************************************************* */ static BOOL pcx_init_palette ( PCX_PAL *palettep, int vmode ) { int i; /* Scratch counter */ int val; /* Current palette register value */ int red; /* Temporary value */ int green; /* Temporary value */ int blue; /* Temporary value */ unsigned char *ega_palp; /* EGA/VGA palette buffer pointer */ unsigned char _far *dsap; /* Dynamic Save Area pointer */ union REGS regs; /* 80x86 register values */ struct SREGS sregs; /* 80x86 segment register values */ if (vmode < 0x0d || vmode > 0x12) { /* Clear the file header palette */ memset(palettep, 0, sizeof(PCX_PAL) * PCX_PAL_SIZE); return (TRUE); } /* Allocate a 16-color (plus border color) EGA/VGA palette buffer */ if ((ega_palp = (unsigned char *) calloc(sizeof(unsigned char), (PCX_PAL_SIZE + 1))) == (unsigned char *) NULL) return (FALSE); if (pcx_isvga() == TRUE) /* Check for VGA display adapter */ { /* Read the EGA/VGA palette registers */ regs.h.ah = 0x10; /* Select "Read All Palette Registers" routine */ regs.h.al = 0x09; /* Get the EGA/VGA palette buffer offset value */ regs.x.dx = (unsigned int) ega_palp; segread(&sregs); /* Get the current DS segment register value */ sregs.es = sregs.ds; int86x(0x10, ®s, ®s, &sregs); /* Call BIOS video service */ } else { /* Check for a Dynamic Save Area buffer */ dsap = *(*((unsigned char _far * _far * _far *) 0x004000a8L) + 1); if (dsap != (unsigned char _far *) NULL) { /* Copy the current palette into the local EGA/VGA palette buffer */ (void) _fmemcpy((unsigned char _far *) ega_palp, dsap, PCX_PAL_SIZE); } else { /* Copy the appropriate default palette settings */ switch (vmode) { case 0x0d: /* 320 x 200 16-color EGA */ case 0x0e: /* 640 x 200 16-color EGA */ memcpy(ega_palp, pcx_EGA_DefPal_1, PCX_PAL_SIZE); break; case 0x0f: /* 640 x 350 2-color EGA */ memcpy(ega_palp, pcx_EGA_DefPal_2, PCX_PAL_SIZE); break; case 0x10: /* 640 x 350 16-color EGA */ memcpy(ega_palp, pcx_EGA_DefPal_3, PCX_PAL_SIZE); break; default: /* (Should never reach here) */ break; } } /* Map the EGA/VGA palette to the PCX file header palette */ for (i = 0; i < PCX_PAL_SIZE; i++) { val = (int) ega_palp[i]; /* Get current color palette value */ /* Extract color values */ red = ((val & 0x20) >> 5) | ((val & 0x04) >> 1); green = ((val & 0x10) >> 4) | (val & 0x02); blue = ((val & 0x08) >> 3) | ((val & 0x01) << 1); /* Map each color to a 6-bit value. Only the top two bits are */ /* significant for EGA displays. The lower four bits (which */ /* repeat the top two bits) are significant when the image is */ /* presented on a VGA display emulating an EGA display. */ palettep[i].red = (BYTE) ((red << 6) | (red << 4) | (red << 2)); palettep[i].green = (BYTE) ((green << 6) | (green << 4) | (green << 2)); palettep[i].blue = (BYTE) ((blue << 6) | (blue << 4) | (blue << 2)); } } free(ega_palp); /* Free the EGA/VGA palette buffer */ return (TRUE); } /* ************************************************************************* * * PCX_WRITE_LINE - Write PCX Line * * Purpose: To write an image scan line to a PCX-format image file. * * Setup: static BOOL pcx_write_line * ( * unsigned char *linep, * int buflen, * FILE *fp * ) * * Where: linep is a PCX scan line buffer pointer. * buflen is the length of the image scan line buffer in * bytes. * fp is a file pointer. * * Return: TRUE if successful; otherwise FALSE. * * Note: The PCX scan line buffer is assumed to contain the color * plane scan lines in sequence, with padding for an even * number of bytes and trailing "white" data for each line as * appropriate. * ************************************************************************* */ static BOOL pcx_write_line ( unsigned char *linep, int buflen, FILE *fp ) { int curr_data; /* Current data byte */ int prev_data; /* Previous data byte */ int data_count; /* Data repeat count */ int line_count; /* Scan line byte count */ prev_data = *linep++; /* Initialize the previous data byte */ data_count = 1; line_count = 1; while (line_count < buflen) /* Encode scan line */ { curr_data = *linep++; /* Get the current data byte */ line_count++; /* Increment the scan line counter */ if (curr_data == prev_data) /* Repeating data bytes ? */ { data_count++; /* Increment the data repeat count */ /* Check for maximum allowable repeat count */ if (data_count == PCX_COMP_MASK) { /* Encode the data */ if (pcx_encode(prev_data, data_count, fp) == FALSE) return (FALSE); data_count = 0; /* Reset the data repeat count */ } } else /* End of repeating data bytes */ { if (data_count > 0) { /* Encode the data */ if (pcx_encode(prev_data, data_count, fp) == FALSE) return (FALSE); } prev_data = curr_data; /* Current data byte now previous */ data_count = 1; } } if (data_count > 0) /* Any remaining data ? */ { /* Encode the data */ if (pcx_encode(prev_data, data_count, fp) == FALSE) return (FALSE); } return (TRUE); } /* ************************************************************************* * * PCX_ENCODE - Encode Byte Pair * * Purpose: To write an encoded byte pair (or a single unencoded * byte) to a PCX-format image file. * * Setup: static BOOL pcx_encode * ( * int data, * int count, * FILE *fp * ) * * Where: data is the data byte to be encoded (if necessary). * count is the number of times the data byte is repeated in * the image data. * fp is a pointer to the PCX-format file the encoded byte * pair or single byte is to be written to. * * Return: TRUE if successful; otherwise FALSE. * ************************************************************************* */ static BOOL pcx_encode ( int data, int count, FILE *fp ) { if (((data & PCX_COMP_FLAG) == PCX_COMP_FLAG) || count > 1) { /* Write the count byte */ if (putc(PCX_COMP_FLAG | count, fp) == EOF) return (FALSE); } /* Write the data byte */ if (putc(data, fp) == EOF) return (FALSE); return (TRUE); } /* ************************************************************************* * * PCX_WRITE_EXTPAL - Write Extended Palette * * Purpose: To read the current 256-color VGA palette and write an * equivalent extended PCX palette to a PCX-format image * file. * * Setup: static BOOL pcx_write_extpal * ( * FILE *fp * ) * * Where: fp is a file pointer. * * Return: TRUE if successful; otherwise FALSE. * ************************************************************************* */ static BOOL pcx_write_extpal ( FILE *fp ) { int i; /* Scratch counter */ BOOL status = TRUE; /* Return status */ PCX_PAL *palettep; /* Extended PCX palette buffer pointer */ unsigned char *vga_palp; /* 256-color VGA palette buffer pointer */ union REGS regs; /* 80x86 register values */ struct SREGS sregs; /* 80x86 segment register values */ /* Allocate an extended PCX palette buffer */ if ((palettep = (PCX_PAL *) calloc(sizeof(PCX_PAL), PCX_EPAL_SIZE)) == (PCX_PAL *) NULL) return (FALSE); /* Allocate a 256-color VGA palette buffer */ if ((vga_palp = (unsigned char *) calloc(sizeof(unsigned char), 768)) == (unsigned char *) NULL) { free(palettep); /* Free the extended PCX palette buffer */ return (FALSE); } /* Read the current VGA palette (DAC registers) */ regs.h.ah = 0x10; /* Select "Read DAC Registers" BIOS routine */ regs.h.al = 0x17; regs.x.bx = 0; /* Read all 256 DAC registers */ regs.x.cx = 256; /* Get the VGA palette buffer offset value */ regs.x.dx = (unsigned int) vga_palp; segread(&sregs); /* Get the current DS segment register value */ sregs.es = sregs.ds; int86x(0x10, ®s, ®s, &sregs); /* Call BIOS video service */ /* Map the VGA palette to an extended PCX palette */ for (i = 0; i < PCX_EPAL_SIZE; i++) { palettep[i].red = (BYTE) (vga_palp[i * 3] << 2); palettep[i].green = (BYTE) (vga_palp[i * 3 + 1] << 2); palettep[i].blue = (BYTE) (vga_palp[i * 3 + 2] << 2); } /* Write the extended PCX palette indicator byte to the file */ if (status == TRUE) if (fputc(PCX_EPAL_FLAG, fp) == EOF) status = FALSE; /* Write the extended PCX palette to the file */ if (status == TRUE) if (fwrite(palettep, sizeof(PCX_PAL), PCX_EPAL_SIZE, fp) != PCX_EPAL_SIZE) status = FALSE; free(palettep); /* Free the extended PCX palette buffer */ free(vga_palp); /* Free the VGA palette buffer */ return (status); } /* ************************************************************************* * * PCX_GET_HERC - Get Hercules Scan Line * * Purpose: To read a Hercules monochrome graphics display adapter * scan line into a buffer. * * Setup: static void pcx_get_herc * ( * PCX_WORKBLK *wbp, * unsigned char _far *linep, * int line_num * ) * * Where: wbp is a PCX workblock pointer. * linep is a pointer to where the scan line is to be * returned. * line_num is the scan line number. * ************************************************************************* */ static void pcx_get_herc ( PCX_WORKBLK *wbp, unsigned char _far *linep, int line_num ) { unsigned char _far *displayp; /* Display buffer pointer */ /* Calculate Hercules display buffer pointer */ displayp = (unsigned char _far *) (0xb0000000L + wbp->page_offset) + ((line_num >> 2) * 90) + 0x2000 * (line_num & 3); /* Copy data from the Hercules display buffer to the scan line buffer */ (void) _fmemcpy(linep, displayp, wbp->num_bytes); /* Mask off unseen pixels */ linep[wbp->num_bytes - 1] |= wbp->mask; /* Pad scan line with "white" data byte (if necessary) */ if (wbp->num_bytes & 1) linep[wbp->num_bytes] = 0xff; } /* ************************************************************************* * * PCX_GET_CGA - Get CGA Scan Line * * Purpose: To read a CGA display adapter scan line into a buffer. * * Setup: static void pcx_get_cga * ( * PCX_WORKBLK *wbp, * unsigned char _far *linep, * int line_num * ) * * Where: wbp is a PCX workblock pointer. * linep is a pointer to where the scan line is to be * returned. * line_num is the scan line number. * ************************************************************************* */ static void pcx_get_cga ( PCX_WORKBLK *wbp, unsigned char _far *linep, int line_num ) { unsigned char _far *displayp; /* Display buffer pointer */ /* Calculate CGA display buffer pointer */ displayp = (unsigned char _far *) 0xb8000000L + ((line_num >> 1) * 80) + 0x2000 * (line_num & 1); /* Copy data from the CGA display buffer to the scan line buffer */ (void) _fmemcpy(linep, displayp, wbp->num_bytes); /* Mask off unseen pixels */ linep[wbp->num_bytes - 1] |= wbp->mask; /* Pad scan line with "white" data byte (if necessary) */ if (wbp->num_bytes & 1) linep[wbp->num_bytes] = 0xff; } /* ************************************************************************* * * PCX_GET_EGA - Get EGA/VGA Scan Line * * Purpose: To read an EGA/VGA display adapter scan line into a * buffer. * * Setup: static void pcx_get_ega * ( * PCX_WORKBLK *wbp, * unsigned char _far *linep, * int line_num * ) * * Where: wbp is a PCX workblock pointer. * linep is a pointer to where the scan line is to be * returned. * line_num is the scan line number. * ************************************************************************* */ static void pcx_get_ega ( PCX_WORKBLK *wbp, unsigned char _far *linep, int line_num ) { int plane_num; /* EGA/VGA color plane number */ unsigned char _far *displayp; /* Display buffer pointer */ /* Calculate buffer pointer */ displayp = (unsigned char _far *) (0xa0000000L + wbp->page_offset) + line_num * 80; /* Copy PCX scan line data from each color plane */ for (plane_num = 0; plane_num < (int) wbp->header.nplanes; plane_num++) { /* Select the current color plane in EGA/VGA Read Mode 0 */ outpw(0x03ce, (plane_num << 8) | 0x04); /* Copy data from the EGA/VGA display to the scan line buffer */ (void) _fmemcpy(linep, displayp, wbp->num_bytes); /* Mask off unseen pixels */ linep[wbp->num_bytes - 1] |= wbp->mask; /* Pad plane scan line with "white" data byte (if necessary) */ if (wbp->num_bytes & 1) linep[wbp->num_bytes] = 0xff; linep += wbp->header.bppscan; /* Increment plane offset */ } /* Select EGA/VGA Write Mode 0 with all color planes enabled */ outpw(0x03c4, 0x0f02); } /* ************************************************************************* * * PCX_GET_VGA - Get VGA Scan Line * * Purpose: To read a VGA display adapter scan line into a buffer. * * Setup: static void pcx_get_vga * ( * PCX_WORKBLK *wbp, * unsigned char _far *linep, * int line_num * ) * * Where: wbp is a PCX workblock pointer. * linep is a pointer to where the scan line is to be * returned. * line_num is the scan line number. * ************************************************************************* */ static void pcx_get_vga ( PCX_WORKBLK *wbp, unsigned char _far *linep, int line_num ) { unsigned char _far *displayp; /* Display buffer pointer */ /* Calculate buffer pointer */ displayp = (unsigned char _far *) 0xa0000000L + line_num * 320; /* Copy data from the VGA display buffer to the scan line buffer */ (void) _fmemcpy(linep, displayp, wbp->num_bytes); /* Pad scan line with "white" data byte (if necessary) */ if (wbp->num_bytes & 1) linep[wbp->num_bytes] = 0xff; }