1 /* Copyright (C) 1992, 1995, 1996, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: gdevpcx.c,v 1.8 2004/09/20 22:14:59 dan Exp $ */
18 /* PCX file format drivers */
19 #include "gdevprn.h"
20 #include "gdevpccm.h"
21 #include "gxlum.h"
22
23 /* Thanks to Phil Conrad for donating the original version */
24 /* of these drivers to Aladdin Enterprises. */
25
26 /* ------ The device descriptors ------ */
27
28 /*
29 * Default X and Y resolution.
30 */
31 #define X_DPI 72
32 #define Y_DPI 72
33
34 /* Monochrome. */
35
36 private dev_proc_print_page(pcxmono_print_page);
37
38 /* Use the default RGB->color map, so we get black=0, white=1. */
39 private const gx_device_procs pcxmono_procs =
40 prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
41 gx_default_map_rgb_color, gx_default_map_color_rgb);
42 const gx_device_printer gs_pcxmono_device =
43 prn_device(pcxmono_procs, "pcxmono",
44 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
45 X_DPI, Y_DPI,
46 0, 0, 0, 0, /* margins */
47 1, pcxmono_print_page);
48
49 /* Chunky 8-bit gray scale. */
50
51 private dev_proc_print_page(pcx256_print_page);
52
53 private const gx_device_procs pcxgray_procs =
54 prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
55 gx_default_gray_map_rgb_color, gx_default_gray_map_color_rgb);
56 const gx_device_printer gs_pcxgray_device =
57 {prn_device_body(gx_device_printer, pcxgray_procs, "pcxgray",
58 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
59 X_DPI, Y_DPI,
60 0, 0, 0, 0, /* margins */
61 1, 8, 255, 255, 256, 256, pcx256_print_page)
62 };
63
64 /* 4-bit planar (EGA/VGA-style) color. */
65
66 private dev_proc_print_page(pcx16_print_page);
67
68 private const gx_device_procs pcx16_procs =
69 prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
70 pc_4bit_map_rgb_color, pc_4bit_map_color_rgb);
71 const gx_device_printer gs_pcx16_device =
72 {prn_device_body(gx_device_printer, pcx16_procs, "pcx16",
73 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
74 X_DPI, Y_DPI,
75 0, 0, 0, 0, /* margins */
76 3, 4, 1, 1, 2, 2, pcx16_print_page)
77 };
78
79 /* Chunky 8-bit (SuperVGA-style) color. */
80 /* (Uses a fixed palette of 3,3,2 bits.) */
81
82 private const gx_device_procs pcx256_procs =
83 prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
84 pc_8bit_map_rgb_color, pc_8bit_map_color_rgb);
85 const gx_device_printer gs_pcx256_device =
86 {prn_device_body(gx_device_printer, pcx256_procs, "pcx256",
87 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
88 X_DPI, Y_DPI,
89 0, 0, 0, 0, /* margins */
90 3, 8, 5, 5, 6, 6, pcx256_print_page)
91 };
92
93 /* 24-bit color, 3 8-bit planes. */
94
95 private dev_proc_print_page(pcx24b_print_page);
96
97 private const gx_device_procs pcx24b_procs =
98 prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
99 gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb);
100 const gx_device_printer gs_pcx24b_device =
101 prn_device(pcx24b_procs, "pcx24b",
102 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
103 X_DPI, Y_DPI,
104 0, 0, 0, 0, /* margins */
105 24, pcx24b_print_page);
106
107 /* 4-bit chunky CMYK color. */
108
109 private dev_proc_print_page(pcxcmyk_print_page);
110
111 private const gx_device_procs pcxcmyk_procs =
112 {
113 gdev_prn_open,
114 NULL, /* get_initial_matrix */
115 NULL, /* sync_output */
116 gdev_prn_output_page,
117 gdev_prn_close,
118 NULL, /* map_rgb_color */
119 cmyk_1bit_map_color_rgb,
120 NULL, /* fill_rectangle */
121 NULL, /* tile_rectangle */
122 NULL, /* copy_mono */
123 NULL, /* copy_color */
124 NULL, /* draw_line */
125 NULL, /* get_bits */
126 gdev_prn_get_params,
127 gdev_prn_put_params,
128 cmyk_1bit_map_cmyk_color,
129 NULL, /* get_xfont_procs */
130 NULL, /* get_xfont_device */
131 NULL, /* map_rgb_alpha_color */
132 gx_page_device_get_page_device
133 };
134 const gx_device_printer gs_pcxcmyk_device =
135 {prn_device_body(gx_device_printer, pcxcmyk_procs, "pcxcmyk",
136 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
137 X_DPI, Y_DPI,
138 0, 0, 0, 0, /* margins */
139 4, 4, 1, 1, 2, 2, pcxcmyk_print_page)
140 };
141
142 /* ------ Private definitions ------ */
143
144 /* All two-byte quantities are stored LSB-first! */
145 #if arch_is_big_endian
146 # define assign_ushort(a,v) a = ((v) >> 8) + ((v) << 8)
147 #else
148 # define assign_ushort(a,v) a = (v)
149 #endif
150
151 typedef struct pcx_header_s {
152 byte manuf; /* always 0x0a */
153 byte version;
154 #define version_2_5 0
155 #define version_2_8_with_palette 2
156 #define version_2_8_without_palette 3
157 #define version_3_0 /* with palette */ 5
158 byte encoding; /* 1=RLE */
159 byte bpp; /* bits per pixel per plane */
160 ushort x1; /* X of upper left corner */
161 ushort y1; /* Y of upper left corner */
162 ushort x2; /* x1 + width - 1 */
163 ushort y2; /* y1 + height - 1 */
164 ushort hres; /* horz. resolution (dots per inch) */
165 ushort vres; /* vert. resolution (dots per inch) */
166 byte palette[16 * 3]; /* color palette */
167 byte reserved;
168 byte nplanes; /* number of color planes */
169 ushort bpl; /* number of bytes per line (uncompressed) */
170 ushort palinfo;
171 #define palinfo_color 1
172 #define palinfo_gray 2
173 byte xtra[58]; /* fill out header to 128 bytes */
174 } pcx_header;
175
176 /* Define the prototype header. */
177 private const pcx_header pcx_header_prototype =
178 {
179 10, /* manuf */
180 0, /* version (variable) */
181 1, /* encoding */
182 0, /* bpp (variable) */
183 00, 00, /* x1, y1 */
184 00, 00, /* x2, y2 (variable) */
185 00, 00, /* hres, vres (variable) */
186 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* palette (variable) */
187 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
188 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
189 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
190 0, /* reserved */
191 0, /* nplanes (variable) */
192 00, /* bpl (variable) */
193 00, /* palinfo (variable) */
194 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* xtra */
195 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
196 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
197 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
198 };
199
200 /*
201 * Define the DCX header. We don't actually use this yet.
202 * All quantities are stored little-endian!
203 bytes 0-3: ID = 987654321
204 bytes 4-7: file offset of page 1
205 [... up to 1023 entries ...]
206 bytes N-N+3: 0 to mark end of page list
207 * This is followed by the pages in order, each of which is a PCX file.
208 */
209 #define dcx_magic 987654321
210 #define dcx_max_pages 1023
211
212 /* Forward declarations */
213 private void pcx_write_rle(const byte *, const byte *, int, FILE *);
214 private int pcx_write_page(gx_device_printer *, FILE *, pcx_header *, bool);
215
216 /* Write a monochrome PCX page. */
217 private int
pcxmono_print_page(gx_device_printer * pdev,FILE * file)218 pcxmono_print_page(gx_device_printer * pdev, FILE * file)
219 {
220 pcx_header header;
221
222 header = pcx_header_prototype;
223 header.version = version_2_8_with_palette;
224 header.bpp = 1;
225 header.nplanes = 1;
226 assign_ushort(header.palinfo, palinfo_gray);
227 /* Set the first two entries of the short palette. */
228 memcpy((byte *) header.palette, "\000\000\000\377\377\377", 6);
229 return pcx_write_page(pdev, file, &header, false);
230 }
231
232 /* Write an "old" PCX page. */
233 static const byte pcx_ega_palette[16 * 3] =
234 {
235 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0x00, 0xaa, 0xaa,
236 0xaa, 0x00, 0x00, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa,
237 0x55, 0x55, 0x55, 0x55, 0x55, 0xff, 0x55, 0xff, 0x55, 0x55, 0xff, 0xff,
238 0xff, 0x55, 0x55, 0xff, 0x55, 0xff, 0xff, 0xff, 0x55, 0xff, 0xff, 0xff
239 };
240 private int
pcx16_print_page(gx_device_printer * pdev,FILE * file)241 pcx16_print_page(gx_device_printer * pdev, FILE * file)
242 {
243 pcx_header header;
244
245 header = pcx_header_prototype;
246 header.version = version_2_8_with_palette;
247 header.bpp = 1;
248 header.nplanes = 4;
249 /* Fill the EGA palette appropriately. */
250 memcpy((byte *) header.palette, pcx_ega_palette,
251 sizeof(pcx_ega_palette));
252 return pcx_write_page(pdev, file, &header, true);
253 }
254
255 /* Write a "new" PCX page. */
256 private int
pcx256_print_page(gx_device_printer * pdev,FILE * file)257 pcx256_print_page(gx_device_printer * pdev, FILE * file)
258 {
259 pcx_header header;
260 int code;
261
262 header = pcx_header_prototype;
263 header.version = version_3_0;
264 header.bpp = 8;
265 header.nplanes = 1;
266 assign_ushort(header.palinfo,
267 (pdev->color_info.num_components > 1 ?
268 palinfo_color : palinfo_gray));
269 code = pcx_write_page(pdev, file, &header, false);
270 if (code >= 0) { /* Write out the palette. */
271 fputc(0x0c, file);
272 code = pc_write_palette((gx_device *) pdev, 256, file);
273 }
274 return code;
275 }
276
277 /* Write a 24-bit color PCX page. */
278 private int
pcx24b_print_page(gx_device_printer * pdev,FILE * file)279 pcx24b_print_page(gx_device_printer * pdev, FILE * file)
280 {
281 pcx_header header;
282
283 header = pcx_header_prototype;
284 header.version = version_3_0;
285 header.bpp = 8;
286 header.nplanes = 3;
287 assign_ushort(header.palinfo, palinfo_color);
288 return pcx_write_page(pdev, file, &header, true);
289 }
290
291 /* Write a 4-bit chunky CMYK color PCX page. */
292 static const byte pcx_cmyk_palette[16 * 3] =
293 {
294 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x0f, 0x0f, 0x00,
295 0xff, 0x00, 0xff, 0x0f, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x0f, 0x00, 0x00,
296 0x00, 0xff, 0xff, 0x00, 0x0f, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x00,
297 0x00, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x1f, 0x1f, 0x1f, 0x0f, 0x0f, 0x0f,
298 };
299 private int
pcxcmyk_print_page(gx_device_printer * pdev,FILE * file)300 pcxcmyk_print_page(gx_device_printer * pdev, FILE * file)
301 {
302 pcx_header header;
303
304 header = pcx_header_prototype;
305 header.version = 2;
306 header.bpp = 4;
307 header.nplanes = 1;
308 /* Fill the palette appropriately. */
309 memcpy((byte *) header.palette, pcx_cmyk_palette,
310 sizeof(pcx_cmyk_palette));
311 return pcx_write_page(pdev, file, &header, false);
312 }
313
314 /* Write out a page in PCX format. */
315 /* This routine is used for all formats. */
316 /* The caller has set header->bpp, nplanes, and palette. */
317 private int
pcx_write_page(gx_device_printer * pdev,FILE * file,pcx_header * phdr,bool planar)318 pcx_write_page(gx_device_printer * pdev, FILE * file, pcx_header * phdr,
319 bool planar)
320 {
321 int raster = gdev_prn_raster(pdev);
322 uint rsize = ROUND_UP((pdev->width * phdr->bpp + 7) >> 3, 2); /* PCX format requires even */
323 int height = pdev->height;
324 int depth = pdev->color_info.depth;
325 uint lsize = raster + rsize;
326 byte *line = gs_alloc_bytes(pdev->memory, lsize, "pcx file buffer");
327 byte *plane = line + raster;
328 int y;
329 int code = 0; /* return code */
330
331 if (line == 0) /* can't allocate line buffer */
332 return_error(gs_error_VMerror);
333
334 /* Fill in the other variable entries in the header struct. */
335
336 assign_ushort(phdr->x2, pdev->width - 1);
337 assign_ushort(phdr->y2, height - 1);
338 assign_ushort(phdr->hres, (int)pdev->x_pixels_per_inch);
339 assign_ushort(phdr->vres, (int)pdev->y_pixels_per_inch);
340 assign_ushort(phdr->bpl, (planar || depth == 1 ? rsize :
341 raster + (raster & 1)));
342
343 /* Write the header. */
344
345 if (fwrite((const char *)phdr, 1, 128, file) < 128) {
346 code = gs_error_ioerror;
347 goto pcx_done;
348 }
349 /* Write the contents of the image. */
350 for (y = 0; y < height; y++) {
351 byte *row;
352 byte *end;
353
354 code = gdev_prn_get_bits(pdev, y, line, &row);
355 if (code < 0)
356 break;
357 end = row + raster;
358 if (!planar) { /* Just write the bits. */
359 if (raster & 1) { /* Round to even, with predictable padding. */
360 *end = end[-1];
361 ++end;
362 }
363 pcx_write_rle(row, end, 1, file);
364 } else
365 switch (depth) {
366
367 case 4:
368 {
369 byte *pend = plane + rsize;
370 int shift;
371
372 for (shift = 0; shift < 4; shift++) {
373 register byte *from, *to;
374 register int bright = 1 << shift;
375 register int bleft = bright << 4;
376
377 for (from = row, to = plane;
378 from < end; from += 4
379 ) {
380 *to++ =
381 (from[0] & bleft ? 0x80 : 0) |
382 (from[0] & bright ? 0x40 : 0) |
383 (from[1] & bleft ? 0x20 : 0) |
384 (from[1] & bright ? 0x10 : 0) |
385 (from[2] & bleft ? 0x08 : 0) |
386 (from[2] & bright ? 0x04 : 0) |
387 (from[3] & bleft ? 0x02 : 0) |
388 (from[3] & bright ? 0x01 : 0);
389 }
390 /* We might be one byte short of rsize. */
391 if (to < pend)
392 *to = to[-1];
393 pcx_write_rle(plane, pend, 1, file);
394 }
395 }
396 break;
397
398 case 24:
399 {
400 int pnum;
401
402 for (pnum = 0; pnum < 3; ++pnum) {
403 pcx_write_rle(row + pnum, row + raster, 3, file);
404 if (pdev->width & 1)
405 fputc(0, file); /* pad to even */
406 }
407 }
408 break;
409
410 default:
411 code = gs_note_error(gs_error_rangecheck);
412 goto pcx_done;
413
414 }
415 }
416
417 pcx_done:
418 gs_free_object(pdev->memory, line, "pcx file buffer");
419
420 return code;
421 }
422
423 /* ------ Internal routines ------ */
424
425 /* Write one line in PCX run-length-encoded format. */
426 private void
pcx_write_rle(const byte * from,const byte * end,int step,FILE * file)427 pcx_write_rle(const byte * from, const byte * end, int step, FILE * file)
428 { /*
429 * The PCX format theoretically allows encoding runs of 63
430 * identical bytes, but some readers can't handle repetition
431 * counts greater than 15.
432 */
433 #define MAX_RUN_COUNT 15
434 int max_run = step * MAX_RUN_COUNT;
435
436 while (from < end) {
437 byte data = *from;
438
439 from += step;
440 if (data != *from || from == end) {
441 if (data >= 0xc0)
442 putc(0xc1, file);
443 } else {
444 const byte *start = from;
445
446 while ((from < end) && (*from == data))
447 from += step;
448 /* Now (from - start) / step + 1 is the run length. */
449 while (from - start >= max_run) {
450 putc(0xc0 + MAX_RUN_COUNT, file);
451 putc(data, file);
452 start += max_run;
453 }
454 if (from > start || data >= 0xc0)
455 putc((from - start) / step + 0xc1, file);
456 }
457 putc(data, file);
458 }
459 #undef MAX_RUN_COUNT
460 }
461