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