Commit | Line | Data |
---|---|---|
1eff4643 WJ |
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 | } |