| 1 | /* Copyright (C) 1992 Aladdin Enterprises. All rights reserved. |
| 2 | Distributed by Free Software Foundation, Inc. |
| 3 | |
| 4 | This file is part of Ghostscript. |
| 5 | |
| 6 | Ghostscript is distributed in the hope that it will be useful, but |
| 7 | WITHOUT ANY WARRANTY. No author or distributor accepts responsibility |
| 8 | to anyone for the consequences of using it or for whether it serves any |
| 9 | particular purpose or works at all, unless he says so in writing. Refer |
| 10 | to the Ghostscript General Public License for full details. |
| 11 | |
| 12 | Everyone is granted permission to copy, modify and redistribute |
| 13 | Ghostscript, but only under the conditions described in the Ghostscript |
| 14 | General Public License. A copy of this license is supposed to have been |
| 15 | given to you along with Ghostscript so you can know your rights and |
| 16 | responsibilities. It should be in a file named COPYING. Among other |
| 17 | things, the copyright notice and this notice must be preserved on all |
| 18 | copies. */ |
| 19 | |
| 20 | /* gdevpcx.c */ |
| 21 | /* PCX file format devices for Ghostscript */ |
| 22 | #include "gdevprn.h" |
| 23 | #include "gserrors.h" /* REALLY? */ |
| 24 | #include "gdevpccm.h" |
| 25 | |
| 26 | /* Thanks to Phil Conrad for donating the original version */ |
| 27 | /* of these drivers to Aladdin Enterprises. */ |
| 28 | |
| 29 | /* ------ The device descriptors ------ */ |
| 30 | |
| 31 | /* |
| 32 | * Standard U.S. page width and height. A4 paper is 8.4" x 11.7". |
| 33 | */ |
| 34 | #define WIDTH_10THS 85 |
| 35 | #define HEIGHT_10THS 110 |
| 36 | |
| 37 | /* |
| 38 | * Default X and Y resolution. |
| 39 | */ |
| 40 | #define X_DPI 72 |
| 41 | #define Y_DPI 72 |
| 42 | |
| 43 | /* Monochrome. */ |
| 44 | |
| 45 | private dev_proc_print_page(pcxmono_print_page); |
| 46 | |
| 47 | gx_device_printer gs_pcxmono_device = |
| 48 | prn_device(prn_std_procs, "pcxmono", |
| 49 | WIDTH_10THS, HEIGHT_10THS, |
| 50 | X_DPI, Y_DPI, |
| 51 | 0,0,0,0, /* margins */ |
| 52 | 1, pcxmono_print_page); |
| 53 | |
| 54 | /* 4-bit planar (EGA/VGA-style) color. */ |
| 55 | |
| 56 | private dev_proc_print_page(pcx16_print_page); |
| 57 | |
| 58 | private gx_device_procs pcx16_procs = |
| 59 | prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close, |
| 60 | pc_4bit_map_rgb_color, pc_4bit_map_color_rgb); |
| 61 | gx_device_printer gs_pcx16_device = |
| 62 | prn_device(pcx16_procs, "pcx16", |
| 63 | WIDTH_10THS, HEIGHT_10THS, |
| 64 | X_DPI, Y_DPI, |
| 65 | 0,0,0,0, /* margins */ |
| 66 | 4, pcx16_print_page); |
| 67 | |
| 68 | /* Chunky 8-bit (SuperVGA-style) color. */ |
| 69 | /* (Uses a fixed palette of 3,3,2 bits.) */ |
| 70 | |
| 71 | private dev_proc_print_page(pcx256_print_page); |
| 72 | |
| 73 | private gx_device_procs pcx256_procs = |
| 74 | prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close, |
| 75 | pc_8bit_map_rgb_color, pc_8bit_map_color_rgb); |
| 76 | gx_device_printer gs_pcx256_device = |
| 77 | prn_device(pcx256_procs, "pcx256", |
| 78 | WIDTH_10THS, HEIGHT_10THS, |
| 79 | X_DPI, Y_DPI, |
| 80 | 0,0,0,0, /* margins */ |
| 81 | 8, pcx256_print_page); |
| 82 | |
| 83 | /* ------ Private definitions ------ */ |
| 84 | |
| 85 | typedef struct pcx_header_s { |
| 86 | byte manuf; /* always 0x0a */ |
| 87 | byte version; /* version info = 0,2,3,5 */ |
| 88 | byte encoding; /* 1=RLE */ |
| 89 | byte bpp; /* bits per pixel */ |
| 90 | ushort x1; /* picture dimensions */ |
| 91 | ushort y1; |
| 92 | ushort x2; |
| 93 | ushort y2; |
| 94 | ushort hres; /* horz. resolution */ |
| 95 | ushort vres; /* vert. resolution */ |
| 96 | byte palette[16*3]; /* color palette */ |
| 97 | byte vmode; /* video mode for graphics board */ |
| 98 | byte nplanes; /* number of color planes */ |
| 99 | ushort bpl; /* number of bytes per line (uncompresses) */ |
| 100 | ushort palinfo; /* palette info 1=color, 2=grey */ |
| 101 | ushort shres; /* scanner horz. resolution */ |
| 102 | ushort svres; /* scanner vert. resolution */ |
| 103 | byte xtra[58]; /* fill out header to 128 bytes */ |
| 104 | } pcx_header; |
| 105 | |
| 106 | /* |
| 107 | ** version info for PCX is as follows |
| 108 | ** |
| 109 | ** 0 == 2.5 |
| 110 | ** 2 == 2.8 w/palette info |
| 111 | ** 3 == 2.8 without palette info |
| 112 | ** 5 == 3.0 |
| 113 | ** |
| 114 | */ |
| 115 | |
| 116 | /* Forward declarations */ |
| 117 | private void pcx_write_rle(P3(byte *, byte *, FILE *)); |
| 118 | private int pcx_write_page(P4(gx_device_printer *, FILE *, pcx_header _ss *, int)); |
| 119 | |
| 120 | /* Write an "old" PCX page. */ |
| 121 | static byte ega_palette[16*3] = { |
| 122 | 0x00,0x00,0x00, 0x00,0x00,0xaa, 0x00,0xaa,0x00, 0x00,0xaa,0xaa, |
| 123 | 0xaa,0x00,0x00, 0xaa,0x00,0xaa, 0xaa,0xaa,0x00, 0xaa,0xaa,0xaa, |
| 124 | 0x55,0x55,0x55, 0x55,0x55,0xff, 0x55,0xff,0x55, 0x55,0xff,0xff, |
| 125 | 0xff,0x55,0x55, 0xff,0x55,0xff, 0xff,0xff,0x55, 0xff,0xff,0xff |
| 126 | }; |
| 127 | private int |
| 128 | pcx16_print_page(gx_device_printer *pdev, FILE *file) |
| 129 | { pcx_header header; |
| 130 | header.version = 2; |
| 131 | header.bpp = 1; |
| 132 | header.nplanes = 4; |
| 133 | /* Fill the EGA palette appropriately. */ |
| 134 | memcpy((byte *)header.palette, ega_palette, sizeof(ega_palette)); |
| 135 | return pcx_write_page(pdev, file, &header, 1); |
| 136 | } |
| 137 | |
| 138 | /* Write a "new" PCX page. */ |
| 139 | private int |
| 140 | pcx256_print_page(gx_device_printer *pdev, FILE *file) |
| 141 | { pcx_header header; |
| 142 | int code; |
| 143 | header.version = 5; |
| 144 | header.bpp = 8; |
| 145 | header.nplanes = 1; |
| 146 | /* Clear the EGA palette */ |
| 147 | memset((byte *)header.palette, 0, sizeof(header.palette)); |
| 148 | code = pcx_write_page(pdev, file, &header, 0); |
| 149 | if ( code >= 0 ) |
| 150 | { /* Write out the palette. */ |
| 151 | fputc(0x0c, file); |
| 152 | code = pc_write_palette((gx_device *)pdev, 256, file); |
| 153 | } |
| 154 | return code; |
| 155 | } |
| 156 | |
| 157 | /* Write a monochrome PCX page. */ |
| 158 | private int |
| 159 | pcxmono_print_page(gx_device_printer *pdev, FILE *file) |
| 160 | { pcx_header header; |
| 161 | header.version = 2; |
| 162 | header.bpp = 1; |
| 163 | header.nplanes = 1; |
| 164 | /* Clear the EGA palette */ |
| 165 | memset((byte *)header.palette, 0, sizeof(header.palette)); |
| 166 | return pcx_write_page(pdev, file, &header, 0); |
| 167 | } |
| 168 | |
| 169 | /* Write out a page in PCX format. */ |
| 170 | /* This routine is used for all three formats (monochrome, planar */ |
| 171 | /* "8-bit" actually 4-bit color, and chunky 8-bit color.) */ |
| 172 | private int |
| 173 | pcx_write_page(gx_device_printer *pdev, FILE *file, pcx_header _ss *phdr, |
| 174 | int planar) |
| 175 | { int raster = gdev_prn_bytes_per_scan_line(pdev); |
| 176 | int height = pdev->height; |
| 177 | int depth = pdev->color_info.depth; |
| 178 | uint rsize = (pdev->width + 7) >> 3; |
| 179 | byte *row = (byte *)gs_malloc(raster + rsize, 1, "pcx file buffer"); |
| 180 | byte *end = row + raster; |
| 181 | byte *plane = end; |
| 182 | int y; |
| 183 | int code = 0; /* return code */ |
| 184 | if ( row == 0 ) /* can't allocate row buffer */ |
| 185 | return gs_error_VMerror; |
| 186 | |
| 187 | /* setup the header struct */ |
| 188 | |
| 189 | phdr->manuf = 10; |
| 190 | /* version and bpp were set by the caller */ |
| 191 | phdr->encoding = 1; /* 1 for rle 8-bit encoding */ |
| 192 | phdr->x1 = 0; |
| 193 | phdr->y1 = 0; |
| 194 | phdr->x2 = pdev->width-1; |
| 195 | phdr->y2 = height-1; |
| 196 | phdr->hres = (int)pdev->x_pixels_per_inch; |
| 197 | phdr->vres = (int)pdev->y_pixels_per_inch; |
| 198 | phdr->vmode = 0; |
| 199 | /* nplanes was set by the caller */ |
| 200 | phdr->bpl = (planar && depth > 1 ? rsize : raster); |
| 201 | phdr->palinfo = (gx_device_has_color(pdev) ? 1 : 2); |
| 202 | |
| 203 | /* Write the header. */ |
| 204 | |
| 205 | if ( fwrite((const char *)phdr, 1, 128, file) < 128 ) |
| 206 | { code = gs_error_ioerror; |
| 207 | goto pcx_done; |
| 208 | } |
| 209 | |
| 210 | /* Dump the contents of the image. */ |
| 211 | for ( y = 0; y < height; y++ ) |
| 212 | { gdev_prn_copy_scan_lines(pdev, y, row, raster); |
| 213 | switch ( depth ) |
| 214 | { |
| 215 | case 1: |
| 216 | { /* PCX assumes 0=black, 1=white. */ |
| 217 | register byte *bp; |
| 218 | for ( bp = row; bp < end; bp++ ) |
| 219 | *bp = ~*bp; |
| 220 | pcx_write_rle(row, end, file); |
| 221 | } |
| 222 | break; |
| 223 | case 8: |
| 224 | { register int shift; |
| 225 | |
| 226 | if ( !gx_device_has_color(pdev) ) |
| 227 | { /* Can't map gray scale */ |
| 228 | code = gs_error_undefinedresult; |
| 229 | goto pcx_done; |
| 230 | } |
| 231 | |
| 232 | if ( !planar ) |
| 233 | { /* Just write the bits */ |
| 234 | pcx_write_rle(row, end, file); |
| 235 | break; |
| 236 | } |
| 237 | |
| 238 | for ( shift = 0; shift < 4; shift++ ) |
| 239 | { register byte *from, *to; |
| 240 | register int bmask = 1 << shift; |
| 241 | for ( from = row, to = plane; |
| 242 | from < end; from += 8 |
| 243 | ) |
| 244 | { *to++ = |
| 245 | ((((uint)from[0] & bmask) << 7) + |
| 246 | (((uint)from[1] & bmask) << 6) + |
| 247 | (((uint)from[2] & bmask) << 5) + |
| 248 | (((uint)from[3] & bmask) << 4) + |
| 249 | (((uint)from[4] & bmask) << 3) + |
| 250 | (((uint)from[5] & bmask) << 2) + |
| 251 | (((uint)from[6] & bmask) << 1) + |
| 252 | (((uint)from[7] & bmask))) |
| 253 | >> shift; |
| 254 | } |
| 255 | pcx_write_rle(plane, to, file); |
| 256 | } |
| 257 | } |
| 258 | break; |
| 259 | |
| 260 | default: |
| 261 | code = gs_error_undefinedresult; |
| 262 | goto pcx_done; |
| 263 | |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | pcx_done: |
| 268 | gs_free((char *)row, raster + rsize, 1, "pcx file buffer"); |
| 269 | |
| 270 | return code; |
| 271 | } |
| 272 | |
| 273 | /* ------ Internal routines ------ */ |
| 274 | |
| 275 | /* Write one line in PCX run-length-encoded format. */ |
| 276 | private void |
| 277 | pcx_write_rle(byte *from, byte *end, FILE *file) |
| 278 | { while ( from < end ) |
| 279 | { byte data = *from++; |
| 280 | int count = 1; |
| 281 | while ( (from < end) && (*from == data) ) |
| 282 | count++, from++; |
| 283 | while ( count > 63 ) |
| 284 | { putc(0xff, file); |
| 285 | putc(data, file); |
| 286 | count -= 63; |
| 287 | } |
| 288 | if ( count != 1 || data >= 0xc0 ) |
| 289 | { putc(count | 0xc0, file); |
| 290 | } |
| 291 | putc(data, file); |
| 292 | } |
| 293 | } |