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