xref: /plan9/sys/src/cmd/gs/src/gdevbit.c (revision 3ff48bf5ed603850fcd251ddf13025d23d693782)
1 /* Copyright (C) 1991, 1995, 1996, 1997, 1998, 1999 Aladdin Enterprises.  All rights reserved.
2 
3   This file is part of AFPL Ghostscript.
4 
5   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
6   distributor accepts any responsibility for the consequences of using it, or
7   for whether it serves any particular purpose or works at all, unless he or
8   she says so in writing.  Refer to the Aladdin Free Public License (the
9   "License") for full details.
10 
11   Every copy of AFPL Ghostscript must include a copy of the License, normally
12   in a plain ASCII text file named PUBLIC.  The License grants you the right
13   to copy, modify and redistribute AFPL Ghostscript, but only under certain
14   conditions described in the License.  Among other things, the License
15   requires that the copyright notice and this notice be preserved on all
16   copies.
17 */
18 
19 /*$Id: gdevbit.c,v 1.2 2000/09/19 19:00:11 lpd Exp $ */
20 /* "Plain bits" devices to measure rendering time. */
21 #include "math_.h"
22 #include "gdevprn.h"
23 #include "gsparam.h"
24 #include "gscrd.h"
25 #include "gscrdp.h"
26 #include "gxlum.h"
27 #include "gdevdcrd.h"
28 
29 /* Define the device parameters. */
30 #ifndef X_DPI
31 #  define X_DPI 72
32 #endif
33 #ifndef Y_DPI
34 #  define Y_DPI 72
35 #endif
36 
37 /* The device descriptor */
38 private dev_proc_map_rgb_color(bit_mono_map_rgb_color);
39 private dev_proc_map_rgb_color(bit_forcemono_map_rgb_color);
40 private dev_proc_map_color_rgb(bit_map_color_rgb);
41 private dev_proc_map_cmyk_color(bit_map_cmyk_color);
42 private dev_proc_get_params(bit_get_params);
43 private dev_proc_put_params(bit_put_params);
44 private dev_proc_print_page(bit_print_page);
45 
46 #define bit_procs(map_rgb_color, map_cmyk_color)\
47 {	gdev_prn_open,\
48 	gx_default_get_initial_matrix,\
49 	NULL,	/* sync_output */\
50 	gdev_prn_output_page,\
51 	gdev_prn_close,\
52 	map_rgb_color,\
53 	bit_map_color_rgb,\
54 	NULL,	/* fill_rectangle */\
55 	NULL,	/* tile_rectangle */\
56 	NULL,	/* copy_mono */\
57 	NULL,	/* copy_color */\
58 	NULL,	/* draw_line */\
59 	NULL,	/* get_bits */\
60 	bit_get_params,\
61 	bit_put_params,\
62 	map_cmyk_color,\
63 	NULL,	/* get_xfont_procs */\
64 	NULL,	/* get_xfont_device */\
65 	NULL,	/* map_rgb_alpha_color */\
66 	gx_page_device_get_page_device	/* get_page_device */\
67 }
68 
69 /*
70  * The following macro is used in get_params and put_params to determine the
71  * num_components for the current device. It works using the device name
72  * character after "bit" which is either '\0', 'r', or 'c'. Any new devices
73  * that are added to this module must modify this macro to return the
74  * correct num_components. This is needed to support the ForceMono
75  * parameter, which alters dev->num_components.
76  */
77 #define REAL_NUM_COMPONENTS(dev) (dev->dname[3] == 'c' ? 4 : \
78 				  dev->dname[3] == 'r' ? 3 : 1)
79 
80 private const gx_device_procs bitmono_procs =
81 bit_procs(bit_mono_map_rgb_color, NULL);
82 const gx_device_printer gs_bit_device =
83 {prn_device_body(gx_device_printer, bitmono_procs, "bit",
84 		 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
85 		 X_DPI, Y_DPI,
86 		 0, 0, 0, 0,    /* margins */
87 		 1, 1, 1, 0, 2, 1, bit_print_page)
88 };
89 
90 private const gx_device_procs bitrgb_procs =
91 bit_procs(gx_default_rgb_map_rgb_color, NULL);
92 const gx_device_printer gs_bitrgb_device =
93 {prn_device_body(gx_device_printer, bitrgb_procs, "bitrgb",
94 		 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
95 		 X_DPI, Y_DPI,
96 		 0, 0, 0, 0,	/* margins */
97 		 3, 4, 1, 1, 2, 2, bit_print_page)
98 };
99 
100 private const gx_device_procs bitcmyk_procs =
101 bit_procs(bit_forcemono_map_rgb_color, bit_map_cmyk_color);
102 const gx_device_printer gs_bitcmyk_device =
103 {prn_device_body(gx_device_printer, bitcmyk_procs, "bitcmyk",
104 		 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
105 		 X_DPI, Y_DPI,
106 		 0, 0, 0, 0,	/* margins */
107 		 4, 4, 1, 1, 2, 2, bit_print_page)
108 };
109 
110 /* Map gray to color. */
111 /* Note that 1-bit monochrome is a special case. */
112 private gx_color_index
113 bit_mono_map_rgb_color(gx_device * dev, gx_color_value red,
114 		       gx_color_value green, gx_color_value blue)
115 {
116     int bpc = dev->color_info.depth;
117     int drop = sizeof(gx_color_value) * 8 - bpc;
118     gx_color_value gray =
119     (red * (unsigned long)lum_red_weight +
120      green * (unsigned long)lum_green_weight +
121      blue * (unsigned long)lum_blue_weight +
122      (lum_all_weights / 2))
123     / lum_all_weights;
124 
125     return (bpc == 1 ? gx_max_color_value - gray : gray) >> drop;
126 }
127 
128 /* Map RGB to gray shade. */
129 /* Only used in CMYK mode when put_params has set ForceMono=1 */
130 private gx_color_index
131 bit_forcemono_map_rgb_color(gx_device * dev, gx_color_value red,
132 		  gx_color_value green, gx_color_value blue)
133 {
134     gx_color_value color;
135     int bpc = dev->color_info.depth / 4;	/* This function is used in CMYK mode */
136     int drop = sizeof(gx_color_value) * 8 - bpc;
137     gx_color_value gray = red;
138 
139     if ((red != green) || (green != blue))
140 	gray = (red * (unsigned long)lum_red_weight +
141 	     green * (unsigned long)lum_green_weight +
142 	     blue * (unsigned long)lum_blue_weight +
143 	     (lum_all_weights / 2))
144 		/ lum_all_weights;
145 
146     color = (gx_max_color_value - gray) >> drop;	/* color is in K channel */
147     return color;
148 }
149 
150 /* Map color to RGB.  This has 3 separate cases, but since it is rarely */
151 /* used, we do a case test rather than providing 3 separate routines. */
152 private int
153 bit_map_color_rgb(gx_device * dev, gx_color_index color, gx_color_value rgb[3])
154 {
155     int depth = dev->color_info.depth;
156     int ncomp = REAL_NUM_COMPONENTS(dev);
157     int bpc = depth / ncomp;
158     uint mask = (1 << bpc) - 1;
159 
160 #define cvalue(c) ((gx_color_value)((ulong)(c) * gx_max_color_value / mask))
161 
162     switch (ncomp) {
163 	case 1:		/* gray */
164 	    rgb[0] = rgb[1] = rgb[2] =
165 		(depth == 1 ? (color ? 0 : gx_max_color_value) :
166 		 cvalue(color));
167 	    break;
168 	case 3:		/* RGB */
169 	    {
170 		gx_color_index cshift = color;
171 
172 		rgb[2] = cvalue(cshift & mask);
173 		cshift >>= bpc;
174 		rgb[1] = cvalue(cshift & mask);
175 		rgb[0] = cvalue(cshift >> bpc);
176 	    }
177 	    break;
178 	case 4:		/* CMYK */
179 	    /* Map CMYK back to RGB. */
180 	    {
181 		gx_color_index cshift = color;
182 		uint c, m, y, k;
183 
184 		k = cshift & mask;
185 		cshift >>= bpc;
186 		y = cshift & mask;
187 		cshift >>= bpc;
188 		m = cshift & mask;
189 		c = cshift >> bpc;
190 		/* We use our improved conversion rule.... */
191 		rgb[0] = cvalue((mask - c) * (mask - k) / mask);
192 		rgb[1] = cvalue((mask - m) * (mask - k) / mask);
193 		rgb[2] = cvalue((mask - y) * (mask - k) / mask);
194 	    }
195 	    break;
196     }
197     return 0;
198 #undef cvalue
199 }
200 
201 /* Map CMYK to color. */
202 private gx_color_index
203 bit_map_cmyk_color(gx_device * dev, gx_color_value cyan,
204 	gx_color_value magenta, gx_color_value yellow, gx_color_value black)
205 {
206     int bpc = dev->color_info.depth / 4;
207     int drop = sizeof(gx_color_value) * 8 - bpc;
208     gx_color_index color =
209     ((((((cyan >> drop) << bpc) +
210 	(magenta >> drop)) << bpc) +
211       (yellow >> drop)) << bpc) +
212     (black >> drop);
213 
214     return (color == gx_no_color_index ? color ^ 1 : color);
215 }
216 
217 /* Get parameters.  We provide a default CRD. */
218 private int
219 bit_get_params(gx_device * pdev, gs_param_list * plist)
220 {
221     int code, ecode;
222     /*
223      * The following is a hack to get the original num_components.
224      * See comment above.
225      */
226     int real_ncomps = REAL_NUM_COMPONENTS(pdev);
227     int ncomps = pdev->color_info.num_components;
228     int forcemono = (ncomps == real_ncomps ? 0 : 1);
229 
230     /*
231      * Temporarily set num_components back to the "real" value to avoid
232      * confusing those that rely on it.
233      */
234     pdev->color_info.num_components = real_ncomps;
235 
236     ecode = gdev_prn_get_params(pdev, plist);
237     code = sample_device_crd_get_params(pdev, plist, "CRDDefault");
238 	if (code < 0)
239 	    ecode = code;
240     if ((code = param_write_int(plist, "ForceMono", &forcemono)) < 0) {
241 	ecode = code;
242     }
243 
244     /* Restore the working num_components */
245     pdev->color_info.num_components = ncomps;
246 
247     return ecode;
248 }
249 
250 /* Set parameters.  We allow setting the number of bits per component. */
251 /* Also, ForceMono=1 forces monochrome output from RGB/CMYK devices. */
252 private int
253 bit_put_params(gx_device * pdev, gs_param_list * plist)
254 {
255     gx_device_color_info save_info;
256     int ncomps = pdev->color_info.num_components;
257     int real_ncomps = REAL_NUM_COMPONENTS(pdev);
258     int bpc = pdev->color_info.depth / real_ncomps;
259     int v;
260     int ecode = 0;
261     int code;
262     static const byte depths[4][8] = {
263 	{1, 2, 0, 4, 8, 0, 0, 8},
264 	{0},
265 	{4, 8, 0, 16, 16, 0, 0, 24},
266 	{4, 8, 0, 16, 32, 0, 0, 32}
267     };
268     const char *vname;
269 
270     /*
271      * Temporarily set num_components back to the "real" value to avoid
272      * confusing those that rely on it.
273      */
274     pdev->color_info.num_components = real_ncomps;
275 
276     if ((code = param_read_int(plist, (vname = "GrayValues"), &v)) != 1 ||
277 	(code = param_read_int(plist, (vname = "RedValues"), &v)) != 1 ||
278 	(code = param_read_int(plist, (vname = "GreenValues"), &v)) != 1 ||
279 	(code = param_read_int(plist, (vname = "BlueValues"), &v)) != 1
280 	) {
281 	if (code < 0)
282 	    ecode = code;
283 	else
284 	    switch (v) {
285 		case   2: bpc = 1; break;
286 		case   4: bpc = 2; break;
287 		case  16: bpc = 4; break;
288 		case  32: bpc = 5; break;
289 		case 256: bpc = 8; break;
290 		default:
291 		    param_signal_error(plist, vname,
292 				       ecode = gs_error_rangecheck);
293 	    }
294     }
295 
296     switch (code = param_read_int(plist, (vname = "ForceMono"), &v)) {
297     case 0:
298 	if (v == 1) {
299 	    ncomps = 1;
300 	    break;
301 	}
302 	else if (v == 0) {
303 	    ncomps = real_ncomps;
304 	    break;
305 	}
306 	code = gs_error_rangecheck;
307     default:
308 	ecode = code;
309 	param_signal_error(plist, vname, ecode);
310     case 1:
311 	break;
312     }
313     if (ecode < 0)
314 	return ecode;
315 
316     /*
317      * Save the color_info in case gdev_prn_put_params fails, and for
318      * comparison.  Note that depth is computed from real_ncomps.
319      */
320     save_info = pdev->color_info;
321     pdev->color_info.depth = depths[real_ncomps - 1][bpc - 1];
322     pdev->color_info.max_gray = pdev->color_info.max_color =
323 	(pdev->color_info.dither_grays =
324 	 pdev->color_info.dither_colors =
325 	 (1 << bpc)) - 1;
326     ecode = gdev_prn_put_params(pdev, plist);
327     if (ecode < 0) {
328 	pdev->color_info = save_info;
329 	return ecode;
330     }
331     /* Now restore/change num_components. This is done after other	*/
332     /* processing since it is used in gx_default_put_params		*/
333     pdev->color_info.num_components = ncomps;
334     if (pdev->color_info.depth != save_info.depth ||
335 	pdev->color_info.num_components != save_info.num_components
336 	) {
337 	gs_closedevice(pdev);
338     }
339     /* Reset the map_cmyk_color procedure if appropriate. */
340     if (dev_proc(pdev, map_cmyk_color) == cmyk_1bit_map_cmyk_color ||
341 	dev_proc(pdev, map_cmyk_color) == cmyk_8bit_map_cmyk_color ||
342 	dev_proc(pdev, map_cmyk_color) == bit_map_cmyk_color) {
343 	set_dev_proc(pdev, map_cmyk_color,
344 		     pdev->color_info.depth == 4 ? cmyk_1bit_map_cmyk_color :
345 		     pdev->color_info.depth == 32 ? cmyk_8bit_map_cmyk_color :
346 		     bit_map_cmyk_color);
347     }
348     return 0;
349 }
350 
351 /* Send the page to the printer. */
352 private int
353 bit_print_page(gx_device_printer * pdev, FILE * prn_stream)
354 {				/* Just dump the bits on the file. */
355     /* If the file is 'nul', don't even do the writes. */
356     int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
357     byte *in = gs_alloc_bytes(pdev->memory, line_size, "bit_print_page(in)");
358     byte *data;
359     int nul = !strcmp(pdev->fname, "nul");
360     int lnum = 0, bottom = pdev->height;
361 
362     if (in == 0)
363 	return_error(gs_error_VMerror);
364     for (; lnum < bottom; ++lnum) {
365 	gdev_prn_get_bits(pdev, lnum, in, &data);
366 	if (!nul)
367 	    fwrite(data, 1, line_size, prn_stream);
368     }
369     gs_free_object(pdev->memory, in, "bit_print_page(in)");
370     return 0;
371 }
372