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