2 *************************************************************************
\r
4 * PCX_FILE.C - PCX_LIB Library Image File Functions
\r
8 * History: 91/02/14 - Created
\r
9 * 91/04/01 - Release 1.00A
\r
10 * 91/04/03 - fixed "segread" call.
\r
11 * 91/04/07 - Release 1.00B
\r
13 * Compiler: Microsoft C V6.0
\r
15 * Author: Ian Ashdown, P.Eng.
\r
17 * 620 Ballantree Road
\r
18 * West Vancouver, B.C.
\r
20 * Tel. (604) 922-6148
\r
21 * Fax. (604) 987-7621
\r
23 * Copyright: Public Domain
\r
25 *************************************************************************
\r
29 *************************************************************************
\r
33 * 1. While this program is written in ANSI C, it uses a number of
\r
34 * function calls that are specific to the Microsoft C V6.0 library.
\r
35 * These are documented as follows for the purposes of porting this
\r
36 * program to other compilers and/or processors:
\r
38 * _ffree - "free" for small model / far data
\r
39 * _fmalloc - "malloc" for small model / far data
\r
40 * _fmemcpy - "memcpy" for small model / far data
\r
41 * int86 - execute 80x86 interrupt routine
\r
42 * int86x - execute 80x86 interrupt routine (far data)
\r
43 * outpw - output word to 80x86 I/O port
\r
44 * segread - get current 80x86 segment register values
\r
46 * 2. When porting this program to other processors, remember that words
\r
47 * are stored by 80x86-based machines in the big-endian format. That
\r
48 * is, the eight least significant bits (lower byte) are stored
\r
49 * first, followed by the eight most significant bits (upper byte).
\r
50 * If PCX-format files are transferred to little-endian machines
\r
51 * (such as those based on 680x0 and Z8000 processors), the order of
\r
52 * bytes within each word will have to be reversed before they can
\r
53 * be interpreted. (This applies to the file header only, since the
\r
54 * encoded image data and optional 256-color palette are stored as
\r
57 * 3. MS-DOS does not recognize the 720 x 348 graphics mode of the
\r
58 * Hercules monochrome display adapter. Therefore, the constant
\r
59 * PCX_HERC should never be passed as a video mode parameter to any
\r
60 * BIOS service routine.
\r
62 * The Microsoft C compiler includes a "video mode" parameter
\r
63 * definition (_HERCMONO) that is defined as 0x08. This is a
\r
64 * reserved MS-DOS video mode that is apparently used internally by
\r
65 * the ROM BIOS. It can, however, be passed to the Microsoft C
\r
66 * library function "_setvideomode" to force the Hercules display
\r
67 * adapter into graphics mode.
\r
69 * Most other MS-DOS C compilers offer similar library functions to
\r
70 * force the Hercules monochrome display adapter into its 720 x 348
\r
73 *************************************************************************
\r
85 #include "pcx_int.h"
\r
87 /* FORWARD REFERENCES */
\r
89 static BOOL pcx_encode(int, int, FILE *);
\r
90 static BOOL pcx_init_palette(PCX_PAL *, int);
\r
91 static BOOL pcx_write_extpal(FILE *);
\r
92 static BOOL pcx_write_line(unsigned char *, int, FILE *);
\r
93 static BOOL pcx_write_init(PCX_WORKBLK *, int, int, int, int);
\r
95 static void pcx_get_cga(PCX_WORKBLK *, unsigned char _far *, int);
\r
96 static void pcx_get_ega(PCX_WORKBLK *, unsigned char _far *, int);
\r
97 static void pcx_get_herc(PCX_WORKBLK *, unsigned char _far *, int);
\r
98 static void pcx_get_vga(PCX_WORKBLK *, unsigned char _far *, int);
\r
102 /* Default EGA palette register values */
\r
104 static BYTE pcx_EGA_DefPal_1[16] = /* Modes 0x0d and 0x0e */
\r
106 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
\r
107 0x14, 0x15, 0x16, 0x17
\r
110 static BYTE pcx_EGA_DefPal_2[16] = /* Mode 0x0f */
\r
112 0x00, 0x08, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
\r
113 0x00, 0x18, 0x00, 0x00
\r
116 static BYTE pcx_EGA_DefPal_3[16] = /* Mode 0x10 */
\r
118 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3a, 0x3b,
\r
119 0x3c, 0x3d, 0x3e, 0x3f
\r
122 /* PUBLIC FUNCTIONS */
\r
125 *************************************************************************
\r
127 * PCX_WRITE - Write PCX File
\r
129 * Purpose: To write a PCX-format image file from an image stored in
\r
130 * a video buffer. The image is assumed to start in the
\r
131 * upper left corner of the screen.
\r
133 * Setup: BOOL pcx_write
\r
142 * Where: fname is a PCX image file name.
\r
143 * vmode is the MS-DOS video mode. Valid values are:
\r
145 * PCX_HERC - 720 x 348 Hercules monochrome
\r
146 * 0x04 - 320 x 200 4-color CGA
\r
147 * 0x05 - 320 x 200 4-color CGA (color burst off)
\r
148 * 0x06 - 640 x 200 2-color CGA
\r
149 * 0x0d - 320 x 200 16-color EGA/VGA
\r
150 * 0x0e - 640 x 200 16-color EGA/VGA
\r
151 * 0x0f - 640 x 350 2-color EGA/VGA
\r
152 * 0x10 - 640 x 350 16-color EGA/VGA
\r
153 * 0x11 - 640 x 480 2-color VGA
\r
154 * 0x12 - 640 x 480 16-color VGA
\r
155 * 0x13 - 320 x 200 256-color VGA
\r
157 * page is the video display page number. Valid values are:
\r
159 * Mode PCX_HERC - 0 or 1
\r
160 * Mode 0x0d - 0 to 7
\r
161 * Mode 0x0e - 0 to 3
\r
162 * Mode 0x0f - 0 or 1
\r
163 * Mode 0x10 - 0 or 1
\r
166 * width is the image width in pixels.
\r
167 * height is the image height in pixels.
\r
169 * Return: TRUE if successful; otherwise FALSE.
\r
171 *************************************************************************
\r
183 int bpline; /* Number of bytes per scan line */
\r
184 int line_num; /* Scan line number */
\r
185 unsigned char *linep; /* Image scan line buffer pointer */
\r
186 BOOL status = TRUE; /* Return status */
\r
187 PCX_WORKBLK *wbp; /* PCX image file workblock pointer */
\r
189 /* Open a PCX image file workblock */
\r
191 if ((wbp = pcx_open(fname, TRUE)) == (PCX_WORKBLK *) NULL)
\r
194 /* Initialize the workblock for writing */
\r
196 if (pcx_write_init(wbp, vmode, page, width, height) == FALSE)
\r
199 /* Calculate number of bytes per line (for all color planes) */
\r
201 bpline = wbp->header.bppscan * wbp->header.nplanes;
\r
203 /* Allocate a scan line buffer */
\r
205 if (status == TRUE)
\r
206 if ((linep = (unsigned char *) malloc(bpline)) == (unsigned char *)
\r
210 /* Write the file header to the file */
\r
212 if (status == TRUE)
\r
213 if (fwrite(&(wbp->header), sizeof(PCX_HDR), 1, wbp->fp) != 1)
\r
216 /* Write the encoded image data to the file */
\r
218 if (status == TRUE)
\r
220 for (line_num = 0; line_num <= (int) wbp->header.ylr; line_num++)
\r
222 /* Get the current video buffer scan line */
\r
224 wbp->pcx_funcp(wbp, (unsigned char _far *) linep, line_num);
\r
226 /* Encode the scan line and write it to the file */
\r
228 if (pcx_write_line(linep, bpline, wbp->fp) == FALSE)
\r
236 if (vmode == 0x13) /* Is a 256-color palette supported ? */
\r
238 /* Write the extended palette to the file */
\r
240 if (status == TRUE)
\r
241 if (pcx_write_extpal(wbp->fp) == FALSE)
\r
245 if (pcx_close(wbp) == FALSE) /* Close the PCX workblock */
\r
248 free(linep); /* Free the scan line buffer */
\r
250 /* Remove the PCX image file if an error occurred */
\r
252 if (status == FALSE)
\r
253 (void) remove(fname);
\r
259 *************************************************************************
\r
261 * PCX_INIT_DSA - Initialize Dynamic Save Area
\r
263 * Purpose: To set up a Video Services Primary Pointer Table and an
\r
264 * associated Dynamic Save Area where BIOS service "Set All
\r
265 * Palette Registers" (function 0x02) can store the EGA color
\r
266 * palette registers settings after it updates them.
\r
268 * Setup: BOOL pcx_init_dsa
\r
273 * Where: vsbp is a pointer to where a pointer to an instantiated
\r
274 * PCX_VSB structure is to be returned.
\r
276 * Return: TRUE if successful; otherwise FALSE.
\r
278 * Note: The EGA display adapter color palette registers are
\r
279 * write-only. In order to save the current color palette
\r
280 * with a PCX-format image file by calling "pcx_write", you
\r
281 * must call this function BEFORE you display the image in
\r
282 * the following MS-DOS video modes:
\r
284 * 0x0d - 320 x 200 16-color EGA
\r
285 * 0x0e - 640 x 200 16-color EGA
\r
286 * 0x0f - 640 x 350 2-color EGA
\r
287 * 0x10 - 640 x 350 16-color EGA
\r
289 * You MUST call "pcx_free_dsa" upon completion of your
\r
290 * program. See the function header of "pcx_init_palette"
\r
291 * for more information.
\r
293 *************************************************************************
\r
301 unsigned char _far *dsap; /* Dynamic Save Area pointer */
\r
302 PCX_VSB *vsbp; /* Video services data save buffer ptr */
\r
304 *vsbpp = (PCX_VSB *) NULL; /* Initialize returned pointer */
\r
306 /* Allocate a Dynamic Save Area buffer */
\r
308 if ((dsap = (unsigned char _far *) _fmalloc(sizeof(unsigned char) *
\r
309 256)) == (unsigned char _far *) NULL)
\r
312 /* Allocate a BIOS video services data save buffer */
\r
314 if ((vsbp = (PCX_VSB *) malloc(sizeof(PCX_VSB))) == (PCX_VSB *) NULL)
\r
316 _ffree(dsap); /* Free the Dynamic Save Area buffer */
\r
320 /* Save the existing Primary Pointer Table pointer */
\r
322 vsbp->prev_pptp = *((struct pcx_ppt _far * _far *) 0x004000a8L);
\r
324 /* Copy the existing Primary Pointer Table into the buffer */
\r
326 (void) _fmemcpy((struct pcx_ppt _far *) &(vsbp->pcx_ppt),
\r
327 vsbp->prev_pptp, sizeof(struct pcx_ppt));
\r
329 vsbp->pcx_ppt.dsap = dsap; /* Update the Dynamic Save Area ptr */
\r
331 /* Update the Primary Pointer Table pointer in the Video Save Table */
\r
333 *((struct pcx_ppt _far * _far *) 0x004000a8L) = &(vsbp->pcx_ppt);
\r
335 *vsbpp = vsbp; /* Return Video Services data save buffer ptr */
\r
341 *************************************************************************
\r
343 * PCX_FREE_DSA - Release Dynamic Save Area
\r
345 * Purpose: To release memory allocated to the Video Services Primary
\r
346 * Pointer Table and associated Dynamic Save Area and reset
\r
347 * the pointer in the Video Save Table.
\r
349 * Setup: void pcx_free_dsa
\r
354 * Where: vsbp is a pointer to a PCX_VSB structure that was
\r
355 * previously allocated and initialized by "pcx_init_dsa".
\r
357 * Note: You MUST call this function on completion of your program
\r
358 * if you previously called "pcx_init_dsa". Failure to do so
\r
359 * will leave the system in an unstable state. See the
\r
360 * function header of "pcx_init_palette" for more
\r
363 *************************************************************************
\r
371 /* Restore the previous primary pointer table pointer */
\r
373 *((struct pcx_ppt _far * _far *) 0x004000a8L) = vsbp->prev_pptp;
\r
375 _ffree(vsbp->pcx_ppt.dsap); /* Free the Dynamic Save Area */
\r
377 free(vsbp); /* Free the Video Services data save buffer */
\r
380 /* PRIVATE FUNCTIONS */
\r
383 *************************************************************************
\r
385 * PCX_WRITE_INIT - Initialize PCX Workblock For Writing
\r
387 * Purpose: To initialize a PCX image file workblock for writing.
\r
389 * Setup: static BOOL pcx_write_init
\r
391 * PCX_WORKBLK *wbp,
\r
398 * Where: wbp is a PCX workblock pointer.
\r
399 * vmode is the MS-DOS video mode. Valid values are:
\r
401 * 0x04 - 320 x 200 4-color CGA
\r
402 * 0x05 - 320 x 200 4-color CGA (color burst off)
\r
403 * 0x06 - 640 x 200 2-color CGA
\r
404 * Ox07 - 720 x 348 Hercules monochrome
\r
405 * 0x0d - 320 x 200 16-color EGA/VGA
\r
406 * 0x0e - 640 x 200 16-color EGA/VGA
\r
407 * 0x0f - 640 x 350 2-color EGA/VGA
\r
408 * 0x10 - 640 x 350 16-color EGA/VGA
\r
409 * 0x11 - 640 x 480 2-color VGA
\r
410 * 0x12 - 640 x 480 16-color VGA
\r
411 * 0x13 - 320 x 200 256-color VGA
\r
413 * page is the video display page number. Valid values are:
\r
415 * Mode PCX_HERC - 0 or 1
\r
416 * Mode 0x0d - 0 to 7
\r
417 * Mode 0x0e - 0 to 3
\r
418 * Mode 0x0f - 0 or 1
\r
419 * Mode 0x10 - 0 or 1
\r
422 * width is the image width in pixels.
\r
423 * height is the image height in pixels.
\r
425 * Return: TRUE if successful; otherwise FALSE.
\r
427 *************************************************************************
\r
430 static BOOL pcx_write_init
\r
439 int max_width; /* Maximum image width */
\r
440 int max_height; /* Maximum image height */
\r
441 int shift; /* Mask shift value */
\r
442 BOOL status = TRUE; /* Return status */
\r
443 PCX_HDR *hdrp; /* File header buffer pointer */
\r
445 /* Initialize the display page address offset */
\r
447 wbp->page_offset = (unsigned long) 0L;
\r
449 hdrp = &(wbp->header); /* Initialize the file header pointer */
\r
451 /* Initialize the header constants */
\r
453 hdrp->pcx_id = 0x0a; /* PCX format identifier */
\r
454 hdrp->version = 5; /* Version number */
\r
455 hdrp->encoding = 1; /* Encoding format (run-length) */
\r
456 hdrp->xul = 0; /* Upper left x-position */
\r
457 hdrp->yul = 0; /* Upper left y-position */
\r
458 hdrp->reserved = 0; /* (Used to be video mode) */
\r
459 hdrp->palette_type = 1; /* Color or b&w palette type */
\r
461 memset(hdrp->filler, 0, sizeof(hdrp->filler)); /* Padding */
\r
463 /* Initialize the video mode-dependent parameters */
\r
467 case PCX_HERC: /* 720 x 348 Hercules monochrome */
\r
469 max_width = min(width, 720); /* Maximum image width */
\r
470 max_height = min(height, 348); /* Maximum image height */
\r
472 hdrp->bppixel = 1; /* Bits per pixel */
\r
473 hdrp->horz_res = 720; /* Horizontal resolution */
\r
474 hdrp->vert_res = 348; /* Vertical resolution */
\r
475 hdrp->nplanes = 1; /* Number of color planes */
\r
477 /* Maximum two pages supported */
\r
479 wbp->page_offset = 0x08000000L * (unsigned long) page;
\r
481 /* Calculate number of bytes to copy */
\r
483 wbp->num_bytes = (max_width + 7) >> 3;
\r
485 shift = (max_width & 7); /* Calculate mask shift value */
\r
487 wbp->pcx_funcp = pcx_get_herc; /* Set display capture fcn ptr */
\r
491 case 0x04: /* 320 x 200 4-color CGA */
\r
492 case 0x05: /* 320 x 200 4-color CGA (color burst off) */
\r
494 max_width = min(width, 320); /* Maximum image width */
\r
495 max_height = min(height, 200); /* Maximum image height */
\r
497 hdrp->bppixel = 2; /* Bits per pixel */
\r
498 hdrp->horz_res = 320; /* Horizontal resolution */
\r
499 hdrp->vert_res = 200; /* Vertical resolution */
\r
500 hdrp->nplanes = 1; /* Number of color planes */
\r
502 /* Calculate number of bytes to copy */
\r
504 wbp->num_bytes = (max_width + 3) >> 2;
\r
506 shift = (max_width & 3) << 1; /* Calculate mask shift value */
\r
508 wbp->pcx_funcp = pcx_get_cga; /* Set display capture fcn ptr */
\r
512 case 0x06: /* 640 x 200 2-color CGA */
\r
514 max_width = min(width, 640); /* Maximum image width */
\r
515 max_height = min(height, 200); /* Maximum image height */
\r
517 hdrp->bppixel = 1; /* Bits per pixel */
\r
518 hdrp->horz_res = 640; /* Horizontal resolution */
\r
519 hdrp->vert_res = 200; /* Vertical resolution */
\r
520 hdrp->nplanes = 1; /* Number of color planes */
\r
522 /* Calculate number of bytes to copy */
\r
524 wbp->num_bytes = (max_width + 7) >> 3;
\r
526 shift = (max_width & 7); /* Calculate mask shift value */
\r
528 wbp->pcx_funcp = pcx_get_cga; /* Set display capture fcn ptr */
\r
532 case 0x0d: /* 320 x 200 16-color EGA/VGA */
\r
534 max_width = min(width, 320); /* Maximum image width */
\r
535 max_height = min(height, 200); /* Maximum image height */
\r
537 hdrp->bppixel = 1; /* Bits per pixel */
\r
538 hdrp->horz_res = 320; /* Horizontal resolution */
\r
539 hdrp->vert_res = 200; /* Vertical resolution */
\r
540 hdrp->nplanes = 4; /* Number of color planes */
\r
542 /* Maximum eight display pages supported */
\r
544 wbp->page_offset = 0x02000000L * (unsigned long) page;
\r
546 /* Calculate number of bytes to copy */
\r
548 wbp->num_bytes = (max_width + 7) >> 3;
\r
550 shift = (max_width & 7); /* Calculate mask shift value */
\r
552 wbp->pcx_funcp = pcx_get_ega; /* Set display capture fcn ptr */
\r
556 case 0x0e: /* 640 x 200 16-color EGA/VGA */
\r
558 max_width = min(width, 640); /* Maximum image width */
\r
559 max_height = min(height, 200); /* Maximum image height */
\r
561 hdrp->bppixel = 1; /* Bits per pixel */
\r
562 hdrp->horz_res = 640; /* Horizontal resolution */
\r
563 hdrp->vert_res = 200; /* Vertical resolution */
\r
564 hdrp->nplanes = 4; /* Number of color planes */
\r
566 /* Maximum four display pages supported */
\r
568 wbp->page_offset = 0x04000000L * (unsigned long) page;
\r
570 /* Calculate number of bytes to copy */
\r
572 wbp->num_bytes = (max_width + 7) >> 3;
\r
574 shift = (max_width & 7); /* Calculate mask shift value */
\r
576 wbp->pcx_funcp = pcx_get_ega; /* Set display capture fcn ptr */
\r
580 case 0x0f: /* 640 x 350 2-color EGA/VGA */
\r
582 max_width = min(width, 640); /* Maximum image width */
\r
583 max_height = min(height, 350); /* Maximum image height */
\r
585 hdrp->bppixel = 1; /* Bits per pixel */
\r
586 hdrp->horz_res = 640; /* Horizontal resolution */
\r
587 hdrp->vert_res = 350; /* Vertical resolution */
\r
588 hdrp->nplanes = 2; /* Number of color planes */
\r
590 /* Maximum two display pages supported */
\r
592 wbp->page_offset = 0x08000000L * (unsigned long) page;
\r
594 /* Calculate number of bytes to copy */
\r
596 wbp->num_bytes = (max_width + 7) >> 3;
\r
598 shift = (max_width & 7); /* Calculate mask shift value */
\r
600 wbp->pcx_funcp = pcx_get_ega; /* Set display capture fcn ptr */
\r
604 case 0x10: /* 640 x 350 16-color EGA/VGA */
\r
606 max_width = min(width, 640); /* Maximum image width */
\r
607 max_height = min(height, 350); /* Maximum image height */
\r
609 hdrp->bppixel = 1; /* Bits per pixel */
\r
610 hdrp->horz_res = 640; /* Horizontal resolution */
\r
611 hdrp->vert_res = 350; /* Vertical resolution */
\r
612 hdrp->nplanes = 4; /* Number of color planes */
\r
614 /* Maximum two display pages supported */
\r
616 wbp->page_offset = 0x08000000L * (unsigned long) page;
\r
618 /* Calculate number of bytes to copy */
\r
620 wbp->num_bytes = (max_width + 7) >> 3;
\r
622 shift = (max_width & 7); /* Calculate mask shift value */
\r
624 wbp->pcx_funcp = pcx_get_ega; /* Set display capture fcn ptr */
\r
628 case 0x11: /* 640 x 480 2-color VGA */
\r
630 max_width = min(width, 640); /* Maximum image width */
\r
631 max_height = min(height, 480); /* Maximum image height */
\r
633 hdrp->bppixel = 1; /* Bits per pixel */
\r
634 hdrp->horz_res = 640; /* Horizontal resolution */
\r
635 hdrp->vert_res = 480; /* Vertical resolution */
\r
636 hdrp->nplanes = 1; /* Number of color planes */
\r
638 /* Calculate number of bytes to copy */
\r
640 wbp->num_bytes = (max_width + 7) >> 3;
\r
642 shift = (max_width & 7); /* Calculate mask shift value */
\r
644 wbp->pcx_funcp = pcx_get_ega; /* Set display capture fcn ptr */
\r
648 case 0x12: /* 640 x 480 16-color VGA */
\r
650 max_width = min(width, 640); /* Maximum image width */
\r
651 max_height = min(height, 480); /* Maximum image height */
\r
653 hdrp->bppixel = 1; /* Bits per pixel */
\r
654 hdrp->horz_res = 640; /* Horizontal resolution */
\r
655 hdrp->vert_res = 480; /* Vertical resolution */
\r
656 hdrp->nplanes = 4; /* Number of color planes */
\r
658 /* Calculate number of bytes to copy */
\r
660 wbp->num_bytes = (max_width + 7) >> 3;
\r
662 shift = (max_width & 7); /* Calculate mask shift value */
\r
664 wbp->pcx_funcp = pcx_get_ega; /* Set display capture fcn ptr */
\r
668 case 0x13: /* 320 x 200 256-color VGA */
\r
670 max_width = min(width, 320); /* Maximum image width */
\r
671 max_height = min(height, 200); /* Maximum image height */
\r
673 hdrp->bppixel = 8; /* Bits per pixel */
\r
674 hdrp->horz_res = 320; /* Horizontal resolution */
\r
675 hdrp->vert_res = 200; /* Vertical resolution */
\r
676 hdrp->nplanes = 1; /* Number of color planes */
\r
678 /* Calculate number of bytes to copy */
\r
680 wbp->num_bytes = max_width;
\r
682 shift = 0; /* Dummy parameter */
\r
684 wbp->pcx_funcp = pcx_get_vga; /* Set display capture fcn ptr */
\r
688 default: /* Other modes not supported */
\r
695 /* Initialize common video mode-dependent parameters */
\r
697 hdrp->xlr = max_width - 1; /* Lower right x-position */
\r
698 hdrp->ylr = max_height - 1; /* Lower right y-position */
\r
699 hdrp->scrn_width = hdrp->horz_res; /* Screen width */
\r
700 hdrp->scrn_height = hdrp->vert_res; /* Screen height */
\r
702 /* Calculate mask for "white" data */
\r
705 wbp->mask = 0xff >> shift;
\r
709 /* Initialize the file header palette */
\r
711 status = pcx_init_palette(hdrp->palette, vmode);
\r
713 /* Calculate number of bytes per color plane scan line (must be an */
\r
714 /* even number of bytes) */
\r
716 hdrp->bppscan = 2 * (((((hdrp->xlr * hdrp->bppixel) + 7) / 8) + 1) / 2);
\r
722 *************************************************************************
\r
724 * PCX_INIT_PALETTE - Initialize File Header Palette
\r
726 * Purpose: To initialize the file header 16-color palette.
\r
728 * Setup: static BOOL pcx_init_palette
\r
730 * PCX_PAL *palettep,
\r
734 * Where: palettep is a pointer to the PCX file header buffer
\r
735 * "palette" member.
\r
736 * vmode is the MS-DOS video mode. Valid values are:
\r
738 * 0x04 - 320 x 200 4-color CGA
\r
739 * 0x05 - 320 x 200 4-color CGA (color burst off)
\r
740 * 0x06 - 640 x 200 2-color CGA
\r
741 * Ox07 - 720 x 348 Hercules monochrome
\r
742 * 0x0d - 320 x 200 16-color EGA/VGA
\r
743 * 0x0e - 640 x 200 16-color EGA/VGA
\r
744 * 0x0f - 640 x 350 2-color EGA/VGA
\r
745 * 0x10 - 640 x 350 16-color EGA/VGA
\r
746 * 0x11 - 640 x 480 2-color VGA
\r
747 * 0x12 - 640 x 480 16-color VGA
\r
748 * 0x13 - 320 x 200 256-color VGA
\r
750 * Return: TRUE if successful; otherwise FALSE.
\r
752 * Note: The CGA color palette is not supported.
\r
754 * If a VGA display adapter is present, the color palette
\r
755 * registers can be read directly from the adapter using the
\r
756 * BIOS routine "Read All Palette Registers" (function 0x09).
\r
758 * Unfortunately, the EGA display adapter color palette
\r
759 * registers are write-only. This means that the current
\r
760 * color palette for EGA displays cannot in general be read.
\r
762 * The BIOS routine "Set All Palette Registers" (function
\r
763 * 0x02) will write the current palette register values to a
\r
764 * buffer called the Dynamic Save Area. The EGA color
\r
765 * palette can be read from the first 16 bytes of this 256-
\r
768 * The Dynamic Save Area is not statically allocated; it must
\r
769 * be supplied by the user. The BIOS video services data in
\r
770 * segment 0x40 includes a pointer at address 0040:00a8 that
\r
771 * references the Video Services Primary Pointer Table in the
\r
772 * EGA/VGA BIOS. This table contains seven pointers, the
\r
773 * second of which is used by the "Set All Palette Registers"
\r
774 * routine to reference the Dynamic Save Area. Since the
\r
775 * Dynamic Save Area does not exist at system initialization,
\r
776 * the value of this pointer is 0000:0000 (in which case the
\r
777 * the updated palette register values are not saved to RAM
\r
778 * when they are updated).
\r
780 * To utilize the EGA palette register save feature, the
\r
781 * user must perform the following:
\r
783 * 1. Save a copy of the pointer at address 0040:00a8.
\r
784 * 2. Allocate a buffer for a new Primary Pointer Table.
\r
785 * 3. Copy the existing Primary Pointer Table to the
\r
786 * allocated buffer.
\r
787 * 4. Allocate a 256-byte buffer for a Dynamic Save Area.
\r
788 * 5. Initialize the second pointer of the Primary Pointer
\r
789 * Table to point to the Dynamic Save Area buffer.
\r
791 * Before the program finishes, the user must also restore
\r
792 * the saved Primary Pointer Table pointer to address
\r
793 * 0040:00a8. Failure to do so will mean that subsequent
\r
794 * calls by other programs to the "Set All Palette
\r
795 * Registers" routine will result in the color palette
\r
796 * registers values being written to unallocated memory (or
\r
797 * memory that has been allocated for another purpose).
\r
799 * The function "pcx_init_dsa" performs the five steps
\r
800 * outlined above, while the function "pcx_free_dsa" restores
\r
801 * the saved pointer on completion of your program.
\r
803 * If the Dynamic Save Area pointer is 0000:0000 (the default
\r
804 * value at system initialization), the BIOS default color
\r
805 * palette settings for the appropriate mode are stored in
\r
806 * the file header color palette.
\r
808 *************************************************************************
\r
811 static BOOL pcx_init_palette
\r
817 int i; /* Scratch counter */
\r
818 int val; /* Current palette register value */
\r
819 int red; /* Temporary value */
\r
820 int green; /* Temporary value */
\r
821 int blue; /* Temporary value */
\r
822 unsigned char *ega_palp; /* EGA/VGA palette buffer pointer */
\r
823 unsigned char _far *dsap; /* Dynamic Save Area pointer */
\r
824 union REGS regs; /* 80x86 register values */
\r
825 struct SREGS sregs; /* 80x86 segment register values */
\r
827 if (vmode < 0x0d || vmode > 0x12)
\r
829 /* Clear the file header palette */
\r
831 memset(palettep, 0, sizeof(PCX_PAL) * PCX_PAL_SIZE);
\r
836 /* Allocate a 16-color (plus border color) EGA/VGA palette buffer */
\r
838 if ((ega_palp = (unsigned char *) calloc(sizeof(unsigned char),
\r
839 (PCX_PAL_SIZE + 1))) == (unsigned char *) NULL)
\r
842 if (pcx_isvga() == TRUE) /* Check for VGA display adapter */
\r
844 /* Read the EGA/VGA palette registers */
\r
846 regs.h.ah = 0x10; /* Select "Read All Palette Registers" routine */
\r
849 /* Get the EGA/VGA palette buffer offset value */
\r
851 regs.x.dx = (unsigned int) ega_palp;
\r
853 segread(&sregs); /* Get the current DS segment register value */
\r
855 sregs.es = sregs.ds;
\r
857 int86x(0x10, ®s, ®s, &sregs); /* Call BIOS video service */
\r
861 /* Check for a Dynamic Save Area buffer */
\r
863 dsap = *(*((unsigned char _far * _far * _far *) 0x004000a8L) + 1);
\r
865 if (dsap != (unsigned char _far *) NULL)
\r
867 /* Copy the current palette into the local EGA/VGA palette buffer */
\r
869 (void) _fmemcpy((unsigned char _far *) ega_palp, dsap,
\r
874 /* Copy the appropriate default palette settings */
\r
878 case 0x0d: /* 320 x 200 16-color EGA */
\r
879 case 0x0e: /* 640 x 200 16-color EGA */
\r
881 memcpy(ega_palp, pcx_EGA_DefPal_1, PCX_PAL_SIZE);
\r
885 case 0x0f: /* 640 x 350 2-color EGA */
\r
887 memcpy(ega_palp, pcx_EGA_DefPal_2, PCX_PAL_SIZE);
\r
891 case 0x10: /* 640 x 350 16-color EGA */
\r
893 memcpy(ega_palp, pcx_EGA_DefPal_3, PCX_PAL_SIZE);
\r
897 default: /* (Should never reach here) */
\r
903 /* Map the EGA/VGA palette to the PCX file header palette */
\r
905 for (i = 0; i < PCX_PAL_SIZE; i++)
\r
907 val = (int) ega_palp[i]; /* Get current color palette value */
\r
909 /* Extract color values */
\r
911 red = ((val & 0x20) >> 5) | ((val & 0x04) >> 1);
\r
912 green = ((val & 0x10) >> 4) | (val & 0x02);
\r
913 blue = ((val & 0x08) >> 3) | ((val & 0x01) << 1);
\r
915 /* Map each color to a 6-bit value. Only the top two bits are */
\r
916 /* significant for EGA displays. The lower four bits (which */
\r
917 /* repeat the top two bits) are significant when the image is */
\r
918 /* presented on a VGA display emulating an EGA display. */
\r
920 palettep[i].red = (BYTE) ((red << 6) | (red << 4) | (red << 2));
\r
921 palettep[i].green = (BYTE) ((green << 6) | (green << 4) | (green <<
\r
923 palettep[i].blue = (BYTE) ((blue << 6) | (blue << 4) | (blue << 2));
\r
927 free(ega_palp); /* Free the EGA/VGA palette buffer */
\r
933 *************************************************************************
\r
935 * PCX_WRITE_LINE - Write PCX Line
\r
937 * Purpose: To write an image scan line to a PCX-format image file.
\r
939 * Setup: static BOOL pcx_write_line
\r
941 * unsigned char *linep,
\r
946 * Where: linep is a PCX scan line buffer pointer.
\r
947 * buflen is the length of the image scan line buffer in
\r
949 * fp is a file pointer.
\r
951 * Return: TRUE if successful; otherwise FALSE.
\r
953 * Note: The PCX scan line buffer is assumed to contain the color
\r
954 * plane scan lines in sequence, with padding for an even
\r
955 * number of bytes and trailing "white" data for each line as
\r
958 *************************************************************************
\r
961 static BOOL pcx_write_line
\r
963 unsigned char *linep,
\r
968 int curr_data; /* Current data byte */
\r
969 int prev_data; /* Previous data byte */
\r
970 int data_count; /* Data repeat count */
\r
971 int line_count; /* Scan line byte count */
\r
973 prev_data = *linep++; /* Initialize the previous data byte */
\r
977 while (line_count < buflen) /* Encode scan line */
\r
979 curr_data = *linep++; /* Get the current data byte */
\r
980 line_count++; /* Increment the scan line counter */
\r
982 if (curr_data == prev_data) /* Repeating data bytes ? */
\r
984 data_count++; /* Increment the data repeat count */
\r
986 /* Check for maximum allowable repeat count */
\r
988 if (data_count == PCX_COMP_MASK)
\r
990 /* Encode the data */
\r
992 if (pcx_encode(prev_data, data_count, fp) == FALSE)
\r
995 data_count = 0; /* Reset the data repeat count */
\r
998 else /* End of repeating data bytes */
\r
1000 if (data_count > 0)
\r
1002 /* Encode the data */
\r
1004 if (pcx_encode(prev_data, data_count, fp) == FALSE)
\r
1008 prev_data = curr_data; /* Current data byte now previous */
\r
1013 if (data_count > 0) /* Any remaining data ? */
\r
1015 /* Encode the data */
\r
1017 if (pcx_encode(prev_data, data_count, fp) == FALSE)
\r
1025 *************************************************************************
\r
1027 * PCX_ENCODE - Encode Byte Pair
\r
1029 * Purpose: To write an encoded byte pair (or a single unencoded
\r
1030 * byte) to a PCX-format image file.
\r
1032 * Setup: static BOOL pcx_encode
\r
1039 * Where: data is the data byte to be encoded (if necessary).
\r
1040 * count is the number of times the data byte is repeated in
\r
1042 * fp is a pointer to the PCX-format file the encoded byte
\r
1043 * pair or single byte is to be written to.
\r
1045 * Return: TRUE if successful; otherwise FALSE.
\r
1047 *************************************************************************
\r
1050 static BOOL pcx_encode
\r
1057 if (((data & PCX_COMP_FLAG) == PCX_COMP_FLAG) || count > 1)
\r
1059 /* Write the count byte */
\r
1061 if (putc(PCX_COMP_FLAG | count, fp) == EOF)
\r
1065 /* Write the data byte */
\r
1067 if (putc(data, fp) == EOF)
\r
1074 *************************************************************************
\r
1076 * PCX_WRITE_EXTPAL - Write Extended Palette
\r
1078 * Purpose: To read the current 256-color VGA palette and write an
\r
1079 * equivalent extended PCX palette to a PCX-format image
\r
1082 * Setup: static BOOL pcx_write_extpal
\r
1087 * Where: fp is a file pointer.
\r
1089 * Return: TRUE if successful; otherwise FALSE.
\r
1091 *************************************************************************
\r
1094 static BOOL pcx_write_extpal
\r
1099 int i; /* Scratch counter */
\r
1100 BOOL status = TRUE; /* Return status */
\r
1101 PCX_PAL *palettep; /* Extended PCX palette buffer pointer */
\r
1102 unsigned char *vga_palp; /* 256-color VGA palette buffer pointer */
\r
1103 union REGS regs; /* 80x86 register values */
\r
1104 struct SREGS sregs; /* 80x86 segment register values */
\r
1106 /* Allocate an extended PCX palette buffer */
\r
1108 if ((palettep = (PCX_PAL *) calloc(sizeof(PCX_PAL), PCX_EPAL_SIZE)) ==
\r
1112 /* Allocate a 256-color VGA palette buffer */
\r
1114 if ((vga_palp = (unsigned char *) calloc(sizeof(unsigned char), 768))
\r
1115 == (unsigned char *) NULL)
\r
1117 free(palettep); /* Free the extended PCX palette buffer */
\r
1121 /* Read the current VGA palette (DAC registers) */
\r
1123 regs.h.ah = 0x10; /* Select "Read DAC Registers" BIOS routine */
\r
1125 regs.x.bx = 0; /* Read all 256 DAC registers */
\r
1128 /* Get the VGA palette buffer offset value */
\r
1130 regs.x.dx = (unsigned int) vga_palp;
\r
1132 segread(&sregs); /* Get the current DS segment register value */
\r
1134 sregs.es = sregs.ds;
\r
1136 int86x(0x10, ®s, ®s, &sregs); /* Call BIOS video service */
\r
1138 /* Map the VGA palette to an extended PCX palette */
\r
1140 for (i = 0; i < PCX_EPAL_SIZE; i++)
\r
1142 palettep[i].red = (BYTE) (vga_palp[i * 3] << 2);
\r
1143 palettep[i].green = (BYTE) (vga_palp[i * 3 + 1] << 2);
\r
1144 palettep[i].blue = (BYTE) (vga_palp[i * 3 + 2] << 2);
\r
1147 /* Write the extended PCX palette indicator byte to the file */
\r
1149 if (status == TRUE)
\r
1150 if (fputc(PCX_EPAL_FLAG, fp) == EOF)
\r
1153 /* Write the extended PCX palette to the file */
\r
1155 if (status == TRUE)
\r
1156 if (fwrite(palettep, sizeof(PCX_PAL), PCX_EPAL_SIZE, fp) !=
\r
1160 free(palettep); /* Free the extended PCX palette buffer */
\r
1162 free(vga_palp); /* Free the VGA palette buffer */
\r
1168 *************************************************************************
\r
1170 * PCX_GET_HERC - Get Hercules Scan Line
\r
1172 * Purpose: To read a Hercules monochrome graphics display adapter
\r
1173 * scan line into a buffer.
\r
1175 * Setup: static void pcx_get_herc
\r
1177 * PCX_WORKBLK *wbp,
\r
1178 * unsigned char _far *linep,
\r
1182 * Where: wbp is a PCX workblock pointer.
\r
1183 * linep is a pointer to where the scan line is to be
\r
1185 * line_num is the scan line number.
\r
1187 *************************************************************************
\r
1190 static void pcx_get_herc
\r
1193 unsigned char _far *linep,
\r
1197 unsigned char _far *displayp; /* Display buffer pointer */
\r
1199 /* Calculate Hercules display buffer pointer */
\r
1201 displayp = (unsigned char _far *) (0xb0000000L + wbp->page_offset) +
\r
1202 ((line_num >> 2) * 90) + 0x2000 * (line_num & 3);
\r
1204 /* Copy data from the Hercules display buffer to the scan line buffer */
\r
1206 (void) _fmemcpy(linep, displayp, wbp->num_bytes);
\r
1208 /* Mask off unseen pixels */
\r
1210 linep[wbp->num_bytes - 1] |= wbp->mask;
\r
1212 /* Pad scan line with "white" data byte (if necessary) */
\r
1214 if (wbp->num_bytes & 1)
\r
1215 linep[wbp->num_bytes] = 0xff;
\r
1219 *************************************************************************
\r
1221 * PCX_GET_CGA - Get CGA Scan Line
\r
1223 * Purpose: To read a CGA display adapter scan line into a buffer.
\r
1225 * Setup: static void pcx_get_cga
\r
1227 * PCX_WORKBLK *wbp,
\r
1228 * unsigned char _far *linep,
\r
1232 * Where: wbp is a PCX workblock pointer.
\r
1233 * linep is a pointer to where the scan line is to be
\r
1235 * line_num is the scan line number.
\r
1237 *************************************************************************
\r
1240 static void pcx_get_cga
\r
1243 unsigned char _far *linep,
\r
1247 unsigned char _far *displayp; /* Display buffer pointer */
\r
1249 /* Calculate CGA display buffer pointer */
\r
1251 displayp = (unsigned char _far *) 0xb8000000L + ((line_num >> 1) * 80)
\r
1252 + 0x2000 * (line_num & 1);
\r
1254 /* Copy data from the CGA display buffer to the scan line buffer */
\r
1256 (void) _fmemcpy(linep, displayp, wbp->num_bytes);
\r
1258 /* Mask off unseen pixels */
\r
1260 linep[wbp->num_bytes - 1] |= wbp->mask;
\r
1262 /* Pad scan line with "white" data byte (if necessary) */
\r
1264 if (wbp->num_bytes & 1)
\r
1265 linep[wbp->num_bytes] = 0xff;
\r
1269 *************************************************************************
\r
1271 * PCX_GET_EGA - Get EGA/VGA Scan Line
\r
1273 * Purpose: To read an EGA/VGA display adapter scan line into a
\r
1276 * Setup: static void pcx_get_ega
\r
1278 * PCX_WORKBLK *wbp,
\r
1279 * unsigned char _far *linep,
\r
1283 * Where: wbp is a PCX workblock pointer.
\r
1284 * linep is a pointer to where the scan line is to be
\r
1286 * line_num is the scan line number.
\r
1288 *************************************************************************
\r
1291 static void pcx_get_ega
\r
1294 unsigned char _far *linep,
\r
1298 int plane_num; /* EGA/VGA color plane number */
\r
1299 unsigned char _far *displayp; /* Display buffer pointer */
\r
1301 /* Calculate buffer pointer */
\r
1303 displayp = (unsigned char _far *) (0xa0000000L + wbp->page_offset) +
\r
1306 /* Copy PCX scan line data from each color plane */
\r
1308 for (plane_num = 0; plane_num < (int) wbp->header.nplanes; plane_num++)
\r
1310 /* Select the current color plane in EGA/VGA Read Mode 0 */
\r
1312 outpw(0x03ce, (plane_num << 8) | 0x04);
\r
1314 /* Copy data from the EGA/VGA display to the scan line buffer */
\r
1316 (void) _fmemcpy(linep, displayp, wbp->num_bytes);
\r
1318 /* Mask off unseen pixels */
\r
1320 linep[wbp->num_bytes - 1] |= wbp->mask;
\r
1322 /* Pad plane scan line with "white" data byte (if necessary) */
\r
1324 if (wbp->num_bytes & 1)
\r
1325 linep[wbp->num_bytes] = 0xff;
\r
1327 linep += wbp->header.bppscan; /* Increment plane offset */
\r
1330 /* Select EGA/VGA Write Mode 0 with all color planes enabled */
\r
1332 outpw(0x03c4, 0x0f02);
\r
1336 *************************************************************************
\r
1338 * PCX_GET_VGA - Get VGA Scan Line
\r
1340 * Purpose: To read a VGA display adapter scan line into a buffer.
\r
1342 * Setup: static void pcx_get_vga
\r
1344 * PCX_WORKBLK *wbp,
\r
1345 * unsigned char _far *linep,
\r
1349 * Where: wbp is a PCX workblock pointer.
\r
1350 * linep is a pointer to where the scan line is to be
\r
1352 * line_num is the scan line number.
\r
1354 *************************************************************************
\r
1357 static void pcx_get_vga
\r
1360 unsigned char _far *linep,
\r
1364 unsigned char _far *displayp; /* Display buffer pointer */
\r
1366 /* Calculate buffer pointer */
\r
1368 displayp = (unsigned char _far *) 0xa0000000L + line_num * 320;
\r
1370 /* Copy data from the VGA display buffer to the scan line buffer */
\r
1372 (void) _fmemcpy(linep, displayp, wbp->num_bytes);
\r
1374 /* Pad scan line with "white" data byte (if necessary) */
\r
1376 if (wbp->num_bytes & 1)
\r
1377 linep[wbp->num_bytes] = 0xff;
\r