xref: /plan9/sys/src/cmd/gs/src/gdevpcx.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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