1 /* Copyright (C) 1991, 1995, 1996, 1997, 1998, 1999 artofcode LLC.  All rights reserved.
2 
3   This program is free software; you can redistribute it and/or modify it
4   under the terms of the GNU General Public License as published by the
5   Free Software Foundation; either version 2 of the License, or (at your
6   option) any later version.
7 
8   This program is distributed in the hope that it will be useful, but
9   WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   General Public License for more details.
12 
13   You should have received a copy of the GNU General Public License along
14   with this program; if not, write to the Free Software Foundation, Inc.,
15   59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16 
17 */
18 
19 /*$Id: gdevbit.c,v 1.2.6.1.2.1 2003/01/17 00:49:00 giles 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
bit_mono_map_rgb_color(gx_device * dev,gx_color_value red,gx_color_value green,gx_color_value blue)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
bit_forcemono_map_rgb_color(gx_device * dev,gx_color_value red,gx_color_value green,gx_color_value blue)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
bit_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value rgb[3])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
bit_map_cmyk_color(gx_device * dev,gx_color_value cyan,gx_color_value magenta,gx_color_value yellow,gx_color_value black)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
bit_get_params(gx_device * pdev,gs_param_list * plist)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
bit_put_params(gx_device * pdev,gs_param_list * plist)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
bit_print_page(gx_device_printer * pdev,FILE * prn_stream)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