xref: /plan9/sys/src/cmd/gs/src/gdevcmap.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 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: gdevcmap.c,v 1.6 2004/05/26 04:10:58 dan Exp $ */
18 /* Special color mapping device */
19 #include "gx.h"
20 #include "gserrors.h"
21 #include "gxdevice.h"
22 #include "gxlum.h"
23 #include "gxfrac.h"
24 #include "gxdcconv.h"
25 #include "gdevcmap.h"
26 
27 /*
28  * The devices in this file exist only to implement the PCL5 special
29  * color mapping algorithms.  They are not useful for PostScript.
30  */
31 
32 /* GC descriptor */
33 public_st_device_cmap();
34 
35 /* Device procedures */
36 private dev_proc_get_params(cmap_get_params);
37 private dev_proc_put_params(cmap_put_params);
38 private dev_proc_begin_typed_image(cmap_begin_typed_image);
39 private dev_proc_get_color_mapping_procs(cmap_get_color_mapping_procs);
40 
41 /*
42  * NB: all of the device color model information will be replaced by
43  * the target's color model information. Only the
44  * get_color_mapping_procs method is modified (aside from
45  * get_params/put_params).
46  *
47  * The begin_typed_image method is used only to force use of the default
48  * image rendering routines if a special mapping_method (anything other
49  * than device_cmap_identity) is requested.
50  */
51 
52 private const gx_device_cmap gs_cmap_device = {
53     std_device_dci_body(gx_device_cmap, 0, "special color mapper",
54                         0, 0, 1, 1,
55                         3, 24, 255, 255, 256, 256),
56     {
57         0, 0, 0, 0, 0, 0, 0,
58         gx_forward_fill_rectangle,
59         gx_forward_tile_rectangle,
60         gx_forward_copy_mono,
61         gx_forward_copy_color,
62         0, 0,
63         cmap_get_params,
64         cmap_put_params,
65         0, 0, 0, 0,
66         0, 0, 0, 0, 0,
67         0, 0, 0, 0, 0, 0, 0,
68         gx_default_begin_image,
69         0, 0, 0, 0, 0,
70         cmap_begin_typed_image,
71         0, 0, 0, 0, 0, 0,
72         0, 0, 0, 0, 0,
73         cmap_get_color_mapping_procs,
74         0, 0, 0
75     },
76     0,                          /* target */
77     device_cmap_identity
78 };
79 
80 /* Set the color mapping method. */
81 private int
gdev_cmap_set_method(gx_device_cmap * cmdev,gx_device_color_mapping_method_t method)82 gdev_cmap_set_method(gx_device_cmap * cmdev,
83 		     gx_device_color_mapping_method_t method)
84 {
85     gx_device *target = cmdev->target;
86 
87     /*
88      * If we're transforming the color, we may need to fool the graphics
89      * core into not halftoning.
90      */
91     set_dev_proc(cmdev, map_cmyk_color, gx_default_map_cmyk_color);
92     set_dev_proc(cmdev, map_color_rgb, gx_forward_map_color_rgb);
93 
94     switch (method) {
95 
96 	case device_cmap_identity:
97 	    /*
98 	     * In this case, and only this case, we can allow the target's
99 	     * color model to propagate here.
100 	     */
101 	    set_dev_proc(cmdev, map_cmyk_color, gx_forward_map_cmyk_color);
102 	    cmdev->color_info.max_gray = target->color_info.max_gray;
103 	    cmdev->color_info.max_color = target->color_info.max_color;
104 	    cmdev->color_info.max_components =
105 	        target->color_info.max_components;
106 	    cmdev->color_info.num_components =
107 		target->color_info.num_components;
108 	    cmdev->color_info.polarity = target->color_info.polarity;
109 	    cmdev->color_info.gray_index = target->color_info.gray_index;
110 	    cmdev->color_info.cm_name = target->color_info.cm_name;
111 	    gx_device_copy_color_procs((gx_device *)cmdev, target);
112 	    break;
113 
114 	case device_cmap_monochrome:
115 	    cmdev->color_info.max_gray = target->color_info.max_gray;
116 	    cmdev->color_info.max_color = target->color_info.max_color;
117 	    cmdev->color_info.max_components =
118 	        cmdev->color_info.num_components = 1;
119 	    cmdev->color_info.cm_name = "DeviceGray";
120 	    break;
121 
122 	case device_cmap_snap_to_primaries:
123 	case device_cmap_color_to_black_over_white:
124 	    cmdev->color_info.max_gray = cmdev->color_info.max_color = 4095;
125 	    /*
126 	     * We have to be an RGB device, otherwise "primaries" doesn't
127 	     * have the proper meaning.
128 	     */
129 	    cmdev->color_info.max_components =
130 	        cmdev->color_info.num_components = 3;
131 	    cmdev->color_info.cm_name = "DeviceRGB";
132 	    break;
133 
134 	default:
135 	    return_error(gs_error_rangecheck);
136     }
137     cmdev->mapping_method = method;
138     return 0;
139 }
140 
141 /* Initialize the device. */
142 int
gdev_cmap_init(gx_device_cmap * dev,gx_device * target,gx_device_color_mapping_method_t method)143 gdev_cmap_init(gx_device_cmap * dev, gx_device * target,
144 	       gx_device_color_mapping_method_t method)
145 {
146     int code;
147 
148     gx_device_init((gx_device *) dev, (const gx_device *)&gs_cmap_device,
149 		   target->memory, true);
150     gx_device_set_target((gx_device_forward *)dev, target);
151     gx_device_copy_params((gx_device *)dev, target);
152     check_device_separable((gx_device *)dev);
153     gx_device_forward_fill_in_procs((gx_device_forward *) dev);
154     code = gdev_cmap_set_method(dev, method);
155     if (code < 0)
156 	return code;
157     return 0;
158 }
159 
160 /* Get parameters. */
161 private int
cmap_get_params(gx_device * dev,gs_param_list * plist)162 cmap_get_params(gx_device * dev, gs_param_list * plist)
163 {
164     int code = gx_forward_get_params(dev, plist);
165     int ecode = code;
166     gx_device_cmap * const cmdev = (gx_device_cmap *)dev;
167     int cmm = cmdev->mapping_method;
168 
169     if ((code = param_write_int(plist, "ColorMappingMethod", &cmm)) < 0)
170 	ecode = code;
171     return ecode;
172 }
173 
174 /* Update parameters; copy the device information back afterwards. */
175 private int
cmap_put_params(gx_device * dev,gs_param_list * plist)176 cmap_put_params(gx_device * dev, gs_param_list * plist)
177 {
178     int code = gx_forward_put_params(dev, plist);
179     int ecode = code;
180     gx_device_cmap * const cmdev = (gx_device_cmap *)dev;
181     int cmm = cmdev->mapping_method;
182     const char *param_name;
183 
184     switch (code = param_read_int(plist, param_name = "ColorMappingMethod",
185 				  &cmm)) {
186     case 0:
187 	if (cmm < 0 || cmm > device_cmap_max_method) {
188 	    code = gs_note_error(gs_error_rangecheck);
189 	} else
190 	    break;
191     default:
192 	ecode = code;
193 	param_signal_error(plist, param_name, ecode);
194 	break;
195     case 1:
196 	break;
197     }
198     if (code >= 0) {
199 	gx_device_copy_params(dev, cmdev->target);
200 	gdev_cmap_set_method(cmdev, cmm);
201     }
202     return ecode;
203 }
204 
205 /*
206  * Handle high-level images.  The only reason we do this, rather than simply
207  * pass the operation to the target, is that the image still has to call the
208  * cmap device to do its color mapping.  As presently implemented, this
209  * disables any high-level implementation that the target may provide.
210  */
211 private int
cmap_begin_typed_image(gx_device * dev,const gs_imager_state * pis,const gs_matrix * pmat,const gs_image_common_t * pic,const gs_int_rect * prect,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath,gs_memory_t * memory,gx_image_enum_common_t ** pinfo)212 cmap_begin_typed_image(gx_device * dev,
213 		       const gs_imager_state * pis, const gs_matrix * pmat,
214 		   const gs_image_common_t * pic, const gs_int_rect * prect,
215 		       const gx_drawing_color * pdcolor,
216 		       const gx_clip_path * pcpath,
217 		       gs_memory_t * memory, gx_image_enum_common_t ** pinfo)
218 {
219     const gx_device_cmap *const cmdev = (const gx_device_cmap *)dev;
220     gx_device *target = cmdev->target;
221 
222     if (cmdev->mapping_method == device_cmap_identity)
223 	return (*dev_proc(target, begin_typed_image))
224 	    (target, pis, pmat, pic, prect, pdcolor, pcpath, memory, pinfo);
225     return gx_default_begin_typed_image(dev, pis, pmat, pic, prect,
226 					pdcolor, pcpath, memory, pinfo);
227 }
228 
229 private void
cmap_gray_cs_to_cm(gx_device * dev,frac gray,frac out[])230 cmap_gray_cs_to_cm(gx_device * dev, frac gray, frac out[])
231 {
232     gx_device_cmap *    cmdev = (gx_device_cmap *)dev;
233     frac gx_max_color_frac   = cv2frac(gx_max_color_value);
234     switch (cmdev->mapping_method) {
235     case device_cmap_snap_to_primaries:
236         gray = (gray <= gx_max_color_frac / 2 ? 0 : gx_max_color_frac);
237         break;
238 
239     case device_cmap_color_to_black_over_white:
240         gray = (gray == 0 ? gx_max_color_frac : 0);
241         break;
242     }
243     {
244         gx_device *target = cmdev->target ? cmdev->target : dev;
245         gx_cm_color_map_procs *cm_procs =  (dev_proc( target, get_color_mapping_procs)(target));
246         cm_procs->map_gray(target, gray, out );
247     }
248 }
249 
250 private void
cmap_rgb_cs_to_cm(gx_device * dev,const gs_imager_state * pis,frac r,frac g,frac b,frac out[])251 cmap_rgb_cs_to_cm(gx_device * dev, const gs_imager_state * pis, frac r, frac g, frac b, frac out[])
252 {
253 
254     gx_device_cmap *    cmdev = (gx_device_cmap *)dev;
255     frac gx_max_color_frac   = cv2frac(gx_max_color_value);
256     switch (cmdev->mapping_method) {
257 
258     case device_cmap_snap_to_primaries:
259         r = (r <= gx_max_color_frac / 2 ? 0 : gx_max_color_frac);
260         g = (g <= gx_max_color_frac / 2 ? 0 : gx_max_color_frac);
261         b = (b <= gx_max_color_frac / 2 ? 0 : gx_max_color_frac);
262         break;
263 
264     case device_cmap_color_to_black_over_white:
265         if (r == 0 && g == 0 && b == 0)
266             r = g = b = gx_max_color_frac;
267         else
268             r = g = b = 0;
269         break;
270 
271     case device_cmap_monochrome:
272         r = g = b = color_rgb_to_gray(r, g, b, NULL);
273         break;
274     }
275     {
276         gx_device *target = cmdev->target ? cmdev->target : dev;
277         gx_cm_color_map_procs *cm_procs =  (dev_proc( target, get_color_mapping_procs)(target));
278         cm_procs->map_rgb(target, pis, r, g, b, out );
279     }
280 }
281 
282 private void
cmap_cmyk_cs_to_cm(gx_device * dev,frac c,frac m,frac y,frac k,frac out[])283 cmap_cmyk_cs_to_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[])
284 {
285     gx_device_cmap *    cmdev = (gx_device_cmap *)dev;
286     frac gx_max_color_frac   = cv2frac(gx_max_color_value);
287     /* We aren't sure what to do with k so we leave it alone.  NB this
288        routine is untested and does not appear to be called.  More
289        testing needed. */
290     switch (cmdev->mapping_method) {
291     case device_cmap_snap_to_primaries:
292         c = (c > gx_max_color_frac / 2 ? 0 : gx_max_color_frac);
293         m = (m > gx_max_color_frac / 2 ? 0 : gx_max_color_frac);
294         y = (y > gx_max_color_frac / 2 ? 0 : gx_max_color_frac);
295         break;
296 
297     case device_cmap_color_to_black_over_white:
298         if (c == gx_max_color_frac && m == gx_max_color_frac && y == gx_max_color_frac)
299             c = m = y = 0;
300         else
301             c = m = y = gx_max_color_frac;
302         break;
303 
304     case device_cmap_monochrome:
305         c = m = y = color_cmyk_to_gray(c, m, y, k, NULL);
306         break;
307     }
308     {
309         gx_device *target = cmdev->target ? cmdev->target : dev;
310         gx_cm_color_map_procs *cm_procs =  (dev_proc( target, get_color_mapping_procs)(target));
311         cm_procs->map_cmyk(target, c, m, y, k, out );
312     }
313 }
314 
315 private const gx_cm_color_map_procs cmap_cm_procs = {
316     cmap_gray_cs_to_cm, cmap_rgb_cs_to_cm, cmap_cmyk_cs_to_cm
317 };
318 
319 
320 private const gx_cm_color_map_procs *
cmap_get_color_mapping_procs(const gx_device * dev)321 cmap_get_color_mapping_procs(const gx_device * dev)
322 {
323     return &cmap_cm_procs;
324 }
325 
326