/src/ghostpdl/base/gdevdevn.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2024 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | /* Example DeviceN process color model devices. */ |
17 | | |
18 | | #include "math_.h" |
19 | | #include "string_.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 | | #include "gstypes.h" |
27 | | #include "gxdcconv.h" |
28 | | #include "gdevdevn.h" |
29 | | #include "gsequivc.h" |
30 | | #include "gxblend.h" |
31 | | #include "gdevp14.h" |
32 | | #include "gdevdevnprn.h" |
33 | | #include "gxdevsop.h" |
34 | | |
35 | | /* |
36 | | * Utility routines for common DeviceN related parameters: |
37 | | * SeparationColorNames, SeparationOrder, and MaxSeparations |
38 | | */ |
39 | | |
40 | | /* Convert a gray color space to DeviceN colorants. */ |
41 | | void |
42 | | gray_cs_to_devn_cm(const gx_device * dev, int * map, frac gray, frac out[]) |
43 | 7.29k | { |
44 | 7.29k | int i = dev->color_info.num_components - 1; |
45 | | |
46 | 40.2k | for(; i >= 0; i--) /* Clear colors */ |
47 | 32.9k | out[i] = frac_0; |
48 | 7.29k | if ((i = map[3]) != GX_DEVICE_COLOR_MAX_COMPONENTS) |
49 | 7.29k | out[i] = frac_1 - gray; |
50 | 7.29k | } |
51 | | |
52 | | /* Convert an RGB color space to DeviceN colorants. */ |
53 | | void |
54 | | rgb_cs_to_devn_cm(const gx_device * dev, int * map, |
55 | | const gs_gstate *pgs, frac r, frac g, frac b, frac out[]) |
56 | 0 | { |
57 | 0 | int i = dev->color_info.num_components - 1; |
58 | 0 | frac cmyk[4]; |
59 | |
|
60 | 0 | for(; i >= 0; i--) /* Clear colors */ |
61 | 0 | out[i] = frac_0; |
62 | 0 | color_rgb_to_cmyk(r, g, b, pgs, cmyk, dev->memory); |
63 | 0 | if ((i = map[0]) != GX_DEVICE_COLOR_MAX_COMPONENTS) |
64 | 0 | out[i] = cmyk[0]; |
65 | 0 | if ((i = map[1]) != GX_DEVICE_COLOR_MAX_COMPONENTS) |
66 | 0 | out[i] = cmyk[1]; |
67 | 0 | if ((i = map[2]) != GX_DEVICE_COLOR_MAX_COMPONENTS) |
68 | 0 | out[i] = cmyk[2]; |
69 | 0 | if ((i = map[3]) != GX_DEVICE_COLOR_MAX_COMPONENTS) |
70 | 0 | out[i] = cmyk[3]; |
71 | 0 | } |
72 | | |
73 | | /* Convert a CMYK color space to DeviceN colorants. */ |
74 | | void |
75 | | cmyk_cs_to_devn_cm(const gx_device * dev, const int * map, |
76 | | frac c, frac m, frac y, frac k, frac out[]) |
77 | 50.2M | { |
78 | 50.2M | int i = dev->color_info.num_components - 1; |
79 | | |
80 | 254M | for(; i >= 0; i--) /* Clear colors */ |
81 | 203M | out[i] = frac_0; |
82 | 50.2M | if ((i = map[0]) != GX_DEVICE_COLOR_MAX_COMPONENTS) |
83 | 50.2M | out[i] = c; |
84 | 50.2M | if ((i = map[1]) != GX_DEVICE_COLOR_MAX_COMPONENTS) |
85 | 50.2M | out[i] = m; |
86 | 50.2M | if ((i = map[2]) != GX_DEVICE_COLOR_MAX_COMPONENTS) |
87 | 50.2M | out[i] = y; |
88 | 50.2M | if ((i = map[3]) != GX_DEVICE_COLOR_MAX_COMPONENTS) |
89 | 50.2M | out[i] = k; |
90 | 50.2M | } |
91 | | |
92 | | /* Some devices need to create composite mappings of the spot colorants. |
93 | | This code was originally in the tiffsep device but was moved here to be |
94 | | sharable across multiple separation devices that need this capability */ |
95 | | |
96 | | |
97 | | /* |
98 | | * Build the map to be used to create a CMYK equivalent to the current |
99 | | * device components. |
100 | | */ |
101 | | void build_cmyk_map(gx_device *pdev, int num_comp, |
102 | | equivalent_cmyk_color_params *equiv_cmyk_colors, |
103 | | cmyk_composite_map * cmyk_map) |
104 | 4.17k | { |
105 | 4.17k | int comp_num; |
106 | 4.17k | gs_devn_params *devn_params = dev_proc(pdev, ret_devn_params)(pdev); |
107 | | |
108 | 4.17k | if (devn_params == NULL) |
109 | 0 | return; |
110 | | |
111 | 20.9k | for (comp_num = 0; comp_num < num_comp; comp_num++) { |
112 | 16.7k | int sep_num = devn_params->separation_order_map[comp_num]; |
113 | | |
114 | 16.7k | cmyk_map[comp_num].c = cmyk_map[comp_num].m = |
115 | 16.7k | cmyk_map[comp_num].y = cmyk_map[comp_num].k = frac_0; |
116 | | /* The tiffsep device has 4 standard colors: CMYK */ |
117 | 16.7k | if (sep_num < devn_params->num_std_colorant_names) { |
118 | 16.7k | switch (sep_num) { |
119 | 4.17k | case 0: cmyk_map[comp_num].c = frac_1; break; |
120 | 4.17k | case 1: cmyk_map[comp_num].m = frac_1; break; |
121 | 4.17k | case 2: cmyk_map[comp_num].y = frac_1; break; |
122 | 4.17k | case 3: cmyk_map[comp_num].k = frac_1; break; |
123 | 16.7k | } |
124 | 16.7k | } else { |
125 | 83 | sep_num -= devn_params->num_std_colorant_names; |
126 | 83 | if (equiv_cmyk_colors->color[sep_num].color_info_valid) { |
127 | 71 | cmyk_map[comp_num].c = equiv_cmyk_colors->color[sep_num].c; |
128 | 71 | cmyk_map[comp_num].m = equiv_cmyk_colors->color[sep_num].m; |
129 | 71 | cmyk_map[comp_num].y = equiv_cmyk_colors->color[sep_num].y; |
130 | 71 | cmyk_map[comp_num].k = equiv_cmyk_colors->color[sep_num].k; |
131 | 71 | } |
132 | 83 | } |
133 | 16.7k | } |
134 | 4.17k | } |
135 | | |
136 | | /* |
137 | | * This utility routine calculates the number of bits required to store |
138 | | * color information. In general the values are rounded up to an even |
139 | | * byte boundary except those cases in which mulitple pixels can evenly |
140 | | * into a single byte. |
141 | | * |
142 | | * The parameter are: |
143 | | * ncomp - The number of components (colorants) for the device. Valid |
144 | | * values are 1 to GX_DEVICE_COLOR_MAX_COMPONENTS |
145 | | * bpc - The number of bits per component. Valid values are 1, 2, 4, 5, |
146 | | * and 8. |
147 | | * Input values are not tested for validity. |
148 | | */ |
149 | | int |
150 | | bpc_to_depth(uchar ncomp, int bpc) |
151 | 33.8k | { |
152 | 33.8k | static const byte depths[4][8] = { |
153 | 33.8k | {1, 2, 0, 4, 8, 0, 0, 8}, |
154 | 33.8k | {2, 4, 0, 8, 16, 0, 0, 16}, |
155 | 33.8k | {4, 8, 0, 16, 16, 0, 0, 24}, |
156 | 33.8k | {4, 8, 0, 16, 32, 0, 0, 32} |
157 | 33.8k | }; |
158 | | |
159 | 33.8k | if (ncomp <=4 && bpc <= 8) |
160 | 3.81k | return depths[ncomp -1][bpc-1]; |
161 | 30.0k | else |
162 | 30.0k | return (ncomp * bpc + 7) & ~7; |
163 | 33.8k | } |
164 | | |
165 | | #define compare_color_names(name, name_size, str, str_size) \ |
166 | 6.38M | (name_size == str_size && \ |
167 | 6.38M | (strncmp((const char *)name, (const char *)str, name_size) == 0)) |
168 | | |
169 | | /* |
170 | | * This routine will check if a name matches any item in a list of process |
171 | | * color model colorant names. |
172 | | */ |
173 | | static bool |
174 | | check_process_color_names(fixed_colorant_names_list plist, |
175 | | const gs_param_string * pstring) |
176 | 0 | { |
177 | 0 | if (plist) { |
178 | 0 | uint size = pstring->size; |
179 | |
|
180 | 0 | while( *plist) { |
181 | 0 | if (compare_color_names(*plist, strlen(*plist), pstring->data, size)) { |
182 | 0 | return true; |
183 | 0 | } |
184 | 0 | plist++; |
185 | 0 | } |
186 | 0 | } |
187 | 0 | return false; |
188 | 0 | } |
189 | | |
190 | | static int count_process_color_names(fixed_colorant_names_list plist) |
191 | 0 | { |
192 | 0 | int count = 0; |
193 | |
|
194 | 0 | if (plist) { |
195 | 0 | while( *plist){ |
196 | 0 | count++; |
197 | 0 | plist++; |
198 | 0 | } |
199 | 0 | } |
200 | 0 | return count; |
201 | 0 | } |
202 | | |
203 | | /* Check only the separation names */ |
204 | | int |
205 | | check_separation_names(const gx_device * dev, const gs_devn_params * pparams, |
206 | | const char * pname, int name_size, int component_type, int number) |
207 | 111k | { |
208 | 111k | const gs_separations * separations = &pparams->separations; |
209 | 111k | int num_spot = separations->num_separations; |
210 | 111k | int color_component_number = number; |
211 | 111k | int i; |
212 | | |
213 | 111k | for (i = 0; i<num_spot; i++) { |
214 | 4.08k | if (compare_color_names((const char *)separations->names[i].data, |
215 | 4.08k | separations->names[i].size, pname, name_size)) { |
216 | 3.75k | return color_component_number; |
217 | 3.75k | } |
218 | 326 | color_component_number++; |
219 | 326 | } |
220 | 107k | return -1; |
221 | 111k | } |
222 | | |
223 | | /* |
224 | | * This routine will check to see if the color component name match those |
225 | | * of either the process color model colorants or the names on the |
226 | | * SeparationColorNames list. |
227 | | * |
228 | | * Parameters: |
229 | | * dev - pointer to device data structure. |
230 | | * pname - pointer to name (zero termination not required) |
231 | | * nlength - length of the name |
232 | | * |
233 | | * This routine returns a positive value (0 to n) which is the device colorant |
234 | | * number if the name is found. It returns a negative value if not found. |
235 | | */ |
236 | | int |
237 | | check_pcm_and_separation_names(const gx_device * dev, |
238 | | const gs_devn_params * pparams, const char * pname, |
239 | | int name_size, int component_type) |
240 | 2.01M | { |
241 | 2.01M | fixed_colorant_name * pcolor = pparams->std_colorant_names; |
242 | 2.01M | int color_component_number = 0; |
243 | | |
244 | | /* Check if the component is in the process color model list. */ |
245 | 2.01M | if (pcolor) { |
246 | 6.48M | while( *pcolor) { |
247 | 6.37M | if (compare_color_names(pname, name_size, *pcolor, strlen(*pcolor))) |
248 | 1.89M | return color_component_number; |
249 | 4.47M | pcolor++; |
250 | 4.47M | color_component_number++; |
251 | 4.47M | } |
252 | 2.01M | } |
253 | | /* For some devices, Tags is part of the process color model list. If so, |
254 | | * that throws us off here since it is thrown at the end of the list. Adjust. */ |
255 | 111k | if (device_encodes_tags(dev)) { |
256 | 0 | color_component_number--; |
257 | 0 | } |
258 | | |
259 | 111k | return check_separation_names(dev, pparams, pname, name_size, |
260 | 111k | component_type, color_component_number); |
261 | 2.01M | } |
262 | | |
263 | | /* |
264 | | * This routine will check to see if the color component name match those |
265 | | * that are available amoung the current device's color components. |
266 | | * |
267 | | * Parameters: |
268 | | * dev - pointer to device data structure. |
269 | | * pname - pointer to name (zero termination not required) |
270 | | * nlength - length of the name |
271 | | * component_type - separation name or not |
272 | | * pdevn_params - pointer to device's DeviceN paramters |
273 | | * pequiv_colors - pointer to equivalent color structure (may be NULL) |
274 | | * |
275 | | * This routine returns a positive value (0 to n) which is the device colorant |
276 | | * number if the name is found. It returns GX_DEVICE_COLOR_MAX_COMPONENTS if |
277 | | * the color component is found but is not being used due to the |
278 | | * SeparationOrder device parameter. It returns a negative value if not found. |
279 | | * |
280 | | * This routine will also add separations to the device if space is |
281 | | * available. |
282 | | */ |
283 | | int |
284 | | devn_get_color_comp_index(gx_device * dev, gs_devn_params * pdevn_params, |
285 | | equivalent_cmyk_color_params * pequiv_colors, |
286 | | const char * pname, int name_size, int component_type, |
287 | | int auto_spot_colors) |
288 | 2.00M | { |
289 | 2.00M | int num_order = pdevn_params->num_separation_order_names; |
290 | 2.00M | int color_component_number = 0; |
291 | 2.00M | int num_res_comps = pdevn_params->num_reserved_components; |
292 | 2.00M | int max_spot_colors = GX_DEVICE_MAX_SEPARATIONS - pdevn_params->num_std_colorant_names - num_res_comps; |
293 | | |
294 | | /* |
295 | | * Check if the component is in either the process color model list |
296 | | * or in the SeparationNames list. |
297 | | */ |
298 | 2.00M | color_component_number = check_pcm_and_separation_names(dev, pdevn_params, |
299 | 2.00M | pname, name_size, component_type); |
300 | | |
301 | | /* If we have a valid component */ |
302 | 2.00M | if (color_component_number >= 0) { |
303 | | /* Check if the component is in the separation order map. */ |
304 | 1.89M | if (num_order) |
305 | 0 | color_component_number = |
306 | 0 | pdevn_params->separation_order_map[color_component_number]; |
307 | 1.89M | else |
308 | | /* |
309 | | * We can have more spot colors than we can image. We simply |
310 | | * ignore the component (i.e. treat it the same as we would |
311 | | * treat a component that is not in the separation order map). |
312 | | * Note: Most device do not allow more spot colors than we can |
313 | | * image. (See the options for auto_spot_color in gdevdevn.h.) |
314 | | */ |
315 | 1.89M | if (color_component_number >= dev->color_info.max_components) |
316 | 0 | color_component_number = GX_DEVICE_COLOR_MAX_COMPONENTS; |
317 | | |
318 | 1.89M | return color_component_number; |
319 | 1.89M | } |
320 | | /* |
321 | | * The given name does not match any of our current components or |
322 | | * separations. Check if we should add the spot color to our list. |
323 | | * If the SeparationOrder parameter has been specified then we should |
324 | | * already have our complete list of desired spot colorants. |
325 | | */ |
326 | 107k | if (component_type != SEPARATION_NAME || |
327 | 107k | auto_spot_colors == NO_AUTO_SPOT_COLORS || |
328 | 107k | pdevn_params->num_separation_order_names != 0) |
329 | 107k | return -1; /* Do not add --> indicate colorant unknown. */ |
330 | | |
331 | | /* Make sure the name is not "None" this is sometimes |
332 | | within a DeviceN list and should not be added as one of the |
333 | | separations. */ |
334 | 224 | if (strncmp(pname, "None", name_size) == 0) { |
335 | 0 | return -1; |
336 | 0 | } |
337 | | /* Additive devices should NOT have C/M/Y/K Colorants added to them. |
338 | | * This is a decision we take here to avoid problems with PDFI not |
339 | | * counting such colorants as spots. */ |
340 | 224 | if (dev->color_info.polarity == GX_CINFO_POLARITY_ADDITIVE) { |
341 | 0 | if (name_size == 5 && strncmp(pname, "Black", 7) == 0) |
342 | 0 | return -1; |
343 | 0 | if (name_size == 4 && strncmp(pname, "Cyan", 4) == 0) |
344 | 0 | return -1; |
345 | 0 | if (name_size == 7 && strncmp(pname, "Magenta", 7) == 0) |
346 | 0 | return -1; |
347 | 0 | if (name_size == 6 && strncmp(pname, "Yellow", 6) == 0) |
348 | 0 | return -1; |
349 | 0 | } |
350 | | |
351 | | /* |
352 | | * Check if we have room for another spot colorant. |
353 | | */ |
354 | 224 | if (auto_spot_colors == ENABLE_AUTO_SPOT_COLORS) |
355 | | /* limit max_spot_colors to what the device can handle given max_components */ |
356 | 224 | max_spot_colors = min(max_spot_colors, |
357 | 224 | dev->color_info.max_components - pdevn_params->num_std_colorant_names - num_res_comps); |
358 | 224 | if (pdevn_params->separations.num_separations < max_spot_colors) { |
359 | 224 | byte * sep_name; |
360 | 224 | gs_separations * separations = &pdevn_params->separations; |
361 | 224 | int sep_num = separations->num_separations++; |
362 | | /* We have a new spot colorant - put in stable memory to avoid "restore" */ |
363 | 224 | sep_name = gs_alloc_bytes(dev->memory->stable_memory, name_size, "devn_get_color_comp_index"); |
364 | 224 | if (sep_name == NULL) { |
365 | 0 | separations->num_separations--; /* we didn't add it */ |
366 | 0 | return -1; |
367 | 0 | } |
368 | 224 | memcpy(sep_name, pname, name_size); |
369 | 224 | separations->names[sep_num].size = name_size; |
370 | 224 | separations->names[sep_num].data = sep_name; |
371 | 224 | color_component_number = sep_num + pdevn_params->num_std_colorant_names; |
372 | 224 | if (color_component_number >= dev->color_info.max_components) |
373 | 0 | color_component_number = GX_DEVICE_COLOR_MAX_COMPONENTS; |
374 | 224 | else |
375 | 224 | pdevn_params->separation_order_map[color_component_number] = |
376 | 224 | color_component_number; |
377 | | |
378 | 224 | if (pequiv_colors != NULL) { |
379 | | /* Indicate that we need to find equivalent CMYK color. */ |
380 | 224 | pequiv_colors->color[sep_num].color_info_valid = false; |
381 | 224 | pequiv_colors->all_color_info_valid = false; |
382 | 224 | } |
383 | 224 | } |
384 | | |
385 | 224 | return color_component_number; |
386 | 224 | } |
387 | | |
388 | | #define set_param_array(a, d, s)\ |
389 | 939k | (a.data = d, a.size = s, a.persistent = false); |
390 | | |
391 | | static int |
392 | | gs_device_supports_spots(gx_device *pdev) |
393 | 606k | { |
394 | | /* Separations are only valid with a subtractive color model, |
395 | | * or additive ones that specifically want them. */ |
396 | 606k | if (pdev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE) |
397 | 606k | return 1; |
398 | 0 | else if (pdev->color_info.polarity == GX_CINFO_POLARITY_ADDITIVE) |
399 | 0 | return (dev_proc(pdev, dev_spec_op)(pdev, gxdso_is_sep_supporting_additive_device, NULL, 0) > 0); |
400 | 0 | return 0; |
401 | 606k | } |
402 | | |
403 | | /* Get parameters. We provide a default CRD. */ |
404 | | int |
405 | | devn_get_params(gx_device * pdev, gs_param_list * plist, |
406 | | gs_devn_params * pdevn_params, equivalent_cmyk_color_params * pequiv_colors) |
407 | 469k | { |
408 | 469k | int code, i = 0, spot_num; |
409 | 469k | bool seprs = false; |
410 | 469k | gs_param_string_array scna; |
411 | 469k | gs_param_string_array sona; |
412 | 469k | gs_param_int_array equiv_cmyk; |
413 | | /* there are 5 ints per colorant in equiv_elements: a valid flag and an int for C, M, Y and K */ |
414 | 469k | int equiv_elements[5 * GX_DEVICE_MAX_SEPARATIONS] = { 0 }; /* 5 * max_colors */ |
415 | | /* limit in case num_separations in pdevn_params exceeds what is expected. */ |
416 | 469k | int num_separations = min(pdevn_params->separations.num_separations, sizeof(equiv_elements)/(5*sizeof(int))); |
417 | | |
418 | 469k | set_param_array(scna, NULL, 0); |
419 | 469k | set_param_array(sona, NULL, 0); |
420 | | |
421 | 469k | if (pequiv_colors != NULL) { |
422 | 470k | for (spot_num = 0; spot_num < num_separations; spot_num++) { |
423 | 208 | equiv_elements[i++] = pequiv_colors->color[spot_num].color_info_valid ? 1 : 0; |
424 | 208 | equiv_elements[i++] = pequiv_colors->color[spot_num].c; |
425 | 208 | equiv_elements[i++] = pequiv_colors->color[spot_num].m; |
426 | 208 | equiv_elements[i++] = pequiv_colors->color[spot_num].y; |
427 | 208 | equiv_elements[i++] = pequiv_colors->color[spot_num].k; |
428 | 208 | } |
429 | 469k | } |
430 | | |
431 | 469k | equiv_cmyk.data = equiv_elements; |
432 | 469k | equiv_cmyk.size = i; |
433 | 469k | equiv_cmyk.persistent = false; |
434 | | |
435 | 469k | if ( (code = sample_device_crd_get_params(pdev, plist, "CRDDefault")) < 0 || |
436 | 469k | (code = param_write_name_array(plist, "SeparationColorNames", &scna)) < 0 || |
437 | 469k | (code = param_write_name_array(plist, "SeparationOrder", &sona)) < 0 || |
438 | 469k | (code = param_write_bool(plist, "Separations", &seprs)) < 0) |
439 | 0 | return code; |
440 | | |
441 | 469k | if (gs_device_supports_spots(pdev) && |
442 | 469k | (code = param_write_int(plist, "PageSpotColors", &(pdevn_params->page_spot_colors))) < 0) |
443 | 0 | return code; |
444 | | |
445 | 469k | if (pdevn_params->separations.num_separations > 0) |
446 | 141 | code = param_write_int_array(plist, ".EquivCMYKColors", &equiv_cmyk); |
447 | | |
448 | 469k | return code; |
449 | 469k | } |
450 | | #undef set_param_array |
451 | | |
452 | | #define BEGIN_ARRAY_PARAM(pread, pname, pa, psize, e)\ |
453 | 411k | BEGIN\ |
454 | 411k | switch (code = pread(plist, (param_name = pname), &(pa))) {\ |
455 | 101k | case 0:\ |
456 | 101k | if ((pa).size != psize) {\ |
457 | 0 | ecode = gs_note_error(gs_error_rangecheck);\ |
458 | 0 | (pa).data = 0; /* mark as not filled */\ |
459 | 0 | } else |
460 | | #define END_ARRAY_PARAM(pa, e)\ |
461 | 0 | goto e;\ |
462 | 101k | default:\ |
463 | 0 | ecode = code;\ |
464 | 0 | e: param_signal_error(plist, param_name, ecode);\ |
465 | 310k | case 1:\ |
466 | 310k | (pa).data = 0; /* mark as not filled */\ |
467 | 822k | }\ |
468 | 822k | END |
469 | | |
470 | | /* |
471 | | * Utility routine for handling DeviceN related parameters. This routine |
472 | | * may modify the color_info, devn_params, and the equiv_cmyk_colors fields. |
473 | | * |
474 | | * Note: This routine does not restore values in case of a problem. This |
475 | | * is left to the caller. |
476 | | */ |
477 | | int |
478 | | devn_put_params(gx_device * pdev, gs_param_list * plist, |
479 | | gs_devn_params * pdevn_params, equivalent_cmyk_color_params * pequiv_colors) |
480 | 137k | { |
481 | 137k | int code = 0, ecode, i; |
482 | 137k | gs_param_name param_name; |
483 | 137k | int npcmcolors = pdevn_params->num_std_colorant_names; |
484 | 137k | int num_spot = pdevn_params->separations.num_separations; |
485 | 137k | bool num_spot_changed = false; |
486 | 137k | int num_order = pdevn_params->num_separation_order_names; |
487 | 137k | int max_sep = pdevn_params->max_separations; |
488 | 137k | int page_spot_colors = pdevn_params->page_spot_colors; |
489 | 137k | gs_param_string_array scna; /* SeparationColorNames array */ |
490 | 137k | gs_param_string_array sona; /* SeparationOrder names array */ |
491 | 137k | gs_param_int_array equiv_cmyk; /* equivalent_cmyk_color_params */ |
492 | 137k | int num_res_comps = pdevn_params->num_reserved_components; |
493 | | |
494 | | /* Get the SeparationOrder names */ |
495 | 145k | BEGIN_ARRAY_PARAM(param_read_name_array, "SeparationOrder", |
496 | 145k | sona, sona.size, sone) |
497 | 8.68k | { |
498 | 8.68k | break; |
499 | 8.68k | } END_ARRAY_PARAM(sona, sone); |
500 | 137k | if (sona.data != 0 && sona.size > pdev->color_info.max_components) { |
501 | 0 | param_signal_error(plist, "SeparationOrder", gs_error_rangecheck); |
502 | 0 | return_error(gs_error_rangecheck); |
503 | 0 | } |
504 | | |
505 | | /* Get the SeparationColorNames */ |
506 | 229k | BEGIN_ARRAY_PARAM(param_read_name_array, "SeparationColorNames", |
507 | 229k | scna, scna.size, scne) |
508 | 92.4k | { |
509 | 92.4k | break; |
510 | 92.4k | } END_ARRAY_PARAM(scna, scne); |
511 | 137k | if (scna.data != 0 && scna.size > pdev->color_info.max_components) { |
512 | 0 | param_signal_error(plist, "SeparationColorNames", gs_error_rangecheck); |
513 | 0 | return_error(gs_error_rangecheck); |
514 | 0 | } |
515 | | /* Get the equivalent_cmyk_color_params -- array is N * 5 elements */ |
516 | 137k | BEGIN_ARRAY_PARAM(param_read_int_array, ".EquivCMYKColors", |
517 | 137k | equiv_cmyk, equiv_cmyk.size, equiv_cmyk_e) |
518 | 0 | { |
519 | 0 | break; |
520 | 0 | } END_ARRAY_PARAM(equiv_cmyk, equiv_cmyk_e); |
521 | 137k | if (equiv_cmyk.data != 0 && equiv_cmyk.size > 5 * pdev->color_info.max_components) { |
522 | 0 | param_signal_error(plist, ".EquivCMYKColors", gs_error_rangecheck); |
523 | 0 | return_error(gs_error_rangecheck); |
524 | 0 | } |
525 | | |
526 | 137k | if (gs_device_supports_spots(pdev)) { |
527 | | /* |
528 | | * Process the SeparationColorNames. Remove any names that already |
529 | | * match the process color model colorant names for the device. |
530 | | */ |
531 | 137k | if (scna.data != 0) { |
532 | 0 | int num_names = scna.size, num_std_names = 0; |
533 | 0 | fixed_colorant_names_list pcomp_names = pdevn_params->std_colorant_names; |
534 | | |
535 | | /* You would expect that pdevn_params->num_std_colorant_names would have this value but it does not. |
536 | | * That appears to be copied from the 'ncomps' of the device and that has to be the number of components |
537 | | * in the 'base' colour model, 1, 3 or 4 for Gray, RGB or CMYK. Other kinds of DeviceN devices can have |
538 | | * additional standard names, eg Tags, or Artifex Orange and Artifex Green, but these are not counted in |
539 | | * the num_std_colorant_names. They are, however, listed in pdevn_params->std_colorant_names (when is a |
540 | | * std_colorant_name not a std_colorant_name ?), which is checked to see if a SeparationOrder name is one |
541 | | * of the inks we are already dealing with. If it is, then we *don't* add it to num_spots. |
542 | | * So we need to actually count the number of colorants in std_colorant_names to make sure that we |
543 | | * don't exceed the maximum number of components. |
544 | | */ |
545 | 0 | num_std_names = count_process_color_names(pcomp_names); |
546 | 0 | num_spot = 0; |
547 | | /* And now we check each ink to see if it's already in the separations list. If not then we count |
548 | | * up the number of new inks |
549 | | */ |
550 | 0 | for (i = 0; i < num_names; i++) { |
551 | | /* Verify that the name is not one of our process colorants */ |
552 | 0 | if (!check_process_color_names(pcomp_names, &scna.data[i])) { |
553 | 0 | if (check_separation_names(pdev, pdevn_params, (const char *)scna.data[i].data, scna.data[i].size, 0, 0) < 0) |
554 | 0 | num_spot++; |
555 | 0 | } |
556 | 0 | } |
557 | | /* Now we can check the number of standard colourants (eg CMYKOG) + number of existing separations + number of new separations |
558 | | * and make sure we have enough components to handle all of them |
559 | | */ |
560 | 0 | if (num_std_names + pdevn_params->separations.num_separations + num_spot > pdev->color_info.max_components) { |
561 | 0 | param_signal_error(plist, "SeparationColorNames", gs_error_rangecheck); |
562 | 0 | return_error(gs_error_rangecheck); |
563 | 0 | } |
564 | | /* Save this value because we need it after the loop */ |
565 | 0 | num_spot = pdevn_params->separations.num_separations; |
566 | | /* Now go through the ink names again, this time if we get a new one we add it to the list of |
567 | | * separations. We can do that safely now because we know we can't overflow the array of names. |
568 | | */ |
569 | 0 | for (i = 0; i < num_names; i++) { |
570 | | /* Verify that the name is not one of our process colorants */ |
571 | 0 | if (!check_process_color_names(pcomp_names, &scna.data[i])) { |
572 | 0 | if (check_separation_names(pdev, pdevn_params, (const char *)scna.data[i].data, scna.data[i].size, 0, 0) < 0) { |
573 | | /* We have a new separation */ |
574 | 0 | byte * sep_name; |
575 | 0 | int name_size = scna.data[i].size; |
576 | |
|
577 | 0 | sep_name = (byte *)gs_alloc_bytes(pdev->memory, |
578 | 0 | name_size, "devicen_put_params_no_sep_order"); |
579 | 0 | if (sep_name == NULL) { |
580 | 0 | param_signal_error(plist, "SeparationColorNames", gs_error_VMerror); |
581 | 0 | return_error(gs_error_VMerror); |
582 | 0 | } |
583 | 0 | memcpy(sep_name, scna.data[i].data, name_size); |
584 | 0 | pdevn_params->separations.names[pdevn_params->separations.num_separations].size = name_size; |
585 | 0 | pdevn_params->separations.names[pdevn_params->separations.num_separations].data = sep_name; |
586 | 0 | if (pequiv_colors != NULL) { |
587 | | /* Indicate that we need to find equivalent CMYK color. */ |
588 | 0 | pequiv_colors->color[num_spot].color_info_valid = false; |
589 | 0 | pequiv_colors->all_color_info_valid = false; |
590 | 0 | } |
591 | 0 | pdevn_params->separations.num_separations++; |
592 | 0 | num_spot_changed = true; |
593 | 0 | } |
594 | 0 | } |
595 | 0 | } |
596 | | |
597 | 0 | for (i = num_spot; i < pdevn_params->separations.num_separations; i++) |
598 | 0 | pdevn_params->separation_order_map[i + pdevn_params->num_std_colorant_names] = |
599 | 0 | i + pdevn_params->num_std_colorant_names; |
600 | | /* We use num_spot below, to reset pdevn_params->separations.num_separations, set it |
601 | | * here in case it gets used elsewhere |
602 | | */ |
603 | 0 | num_spot = pdevn_params->separations.num_separations; |
604 | 0 | } |
605 | | /* Process any .EquivCMYKColors info */ |
606 | 137k | if (equiv_cmyk.data != 0 && pequiv_colors != 0) { |
607 | 0 | int spot_num = 0; |
608 | |
|
609 | 0 | for (i=0; i < equiv_cmyk.size; i += 5) { /* valid, C, M, Y, K for each equiv_color */ |
610 | 0 | if (equiv_cmyk.data[i] == 0) { |
611 | | /* This occurs if we've added a spot, but not yet set it's equiv color */ |
612 | 0 | pequiv_colors->color[spot_num].color_info_valid = false; |
613 | 0 | pequiv_colors->all_color_info_valid = false; |
614 | 0 | } else { |
615 | 0 | pequiv_colors->color[spot_num].color_info_valid = true; |
616 | 0 | pequiv_colors->color[spot_num].c = (frac)(equiv_cmyk.data[i+1]); |
617 | 0 | pequiv_colors->color[spot_num].m = (frac)(equiv_cmyk.data[i+2]); |
618 | 0 | pequiv_colors->color[spot_num].y = (frac)(equiv_cmyk.data[i+3]); |
619 | 0 | pequiv_colors->color[spot_num].k = (frac)(equiv_cmyk.data[i+4]); |
620 | 0 | } |
621 | 0 | spot_num++; |
622 | 0 | } |
623 | 0 | } |
624 | | /* |
625 | | * Process the SeparationOrder names. |
626 | | */ |
627 | 137k | if (sona.data != 0) { |
628 | 0 | int comp_num; |
629 | |
|
630 | 0 | num_order = sona.size; |
631 | 0 | for (i = 0; i < num_order; i++) { |
632 | | /* |
633 | | * Check if names match either the process color model or |
634 | | * SeparationColorNames. If not then error. |
635 | | */ |
636 | 0 | if ((comp_num = (*dev_proc(pdev, get_color_comp_index)) |
637 | 0 | (pdev, (const char *)sona.data[i].data, |
638 | 0 | sona.data[i].size, SEPARATION_NAME)) < 0) { |
639 | 0 | param_signal_error(plist, "SeparationOrder", gs_error_rangecheck); |
640 | 0 | return_error(gs_error_rangecheck); |
641 | 0 | } |
642 | 0 | pdevn_params->separation_order_map[i] = comp_num; |
643 | | /* If the device enabled AUTO_SPOT_COLORS some separations may */ |
644 | | /* have been added. Adjust num_spots if so. */ |
645 | 0 | if (num_spot != pdevn_params->separations.num_separations) { |
646 | 0 | num_spot = pdevn_params->separations.num_separations; |
647 | 0 | num_spot_changed = true; |
648 | 0 | } |
649 | 0 | } |
650 | 0 | } |
651 | | /* |
652 | | * Adobe says that MaxSeparations is supposed to be 'read only' |
653 | | * however we use this to allow the specification of the maximum |
654 | | * number of separations. Memory is allocated for the specified |
655 | | * number of separations. This allows us to then accept separation |
656 | | * colors in color spaces even if they we not specified at the start |
657 | | * of the image file. |
658 | | */ |
659 | 137k | code = param_read_int(plist, param_name = "MaxSeparations", &max_sep); |
660 | 137k | switch (code) { |
661 | 0 | default: |
662 | 0 | param_signal_error(plist, param_name, code); |
663 | 128k | case 1: |
664 | 128k | break; |
665 | 8.68k | case 0: |
666 | 8.68k | if (max_sep < 1 || max_sep > GX_DEVICE_COLOR_MAX_COMPONENTS) { |
667 | 0 | param_signal_error(plist, "MaxSeparations", gs_error_rangecheck); |
668 | 0 | return_error(gs_error_rangecheck); |
669 | 0 | } |
670 | 137k | } |
671 | | /* |
672 | | * The PDF interpreter scans the resources for pages to try to |
673 | | * determine the number of spot colors. (Unfortuneately there is |
674 | | * no way to determine the number of spot colors for a PS page |
675 | | * except to interpret the entire page.) The spot color count for |
676 | | * a PDF page may be high since there may be spot colors in a PDF |
677 | | * page's resources that are not used. However this does give us |
678 | | * an upper limit on the number of spot colors. A value of -1 |
679 | | * indicates that the number of spot colors in unknown (a PS file). |
680 | | */ |
681 | 137k | code = param_read_int(plist, param_name = "PageSpotColors", |
682 | 137k | &page_spot_colors); |
683 | 137k | switch (code) { |
684 | 0 | default: |
685 | 0 | param_signal_error(plist, param_name, code); |
686 | 115k | case 1: |
687 | 115k | break; |
688 | 21.8k | case 0: |
689 | 21.8k | if (page_spot_colors < -1) { |
690 | 0 | param_signal_error(plist, "PageSpotColors", gs_error_rangecheck); |
691 | 0 | return_error(gs_error_rangecheck); |
692 | 0 | } |
693 | 21.8k | if (page_spot_colors > pdev->color_info.max_components - pdevn_params->num_std_colorant_names - num_res_comps) |
694 | 0 | page_spot_colors = pdev->color_info.max_components - pdevn_params->num_std_colorant_names - num_res_comps; |
695 | | /* Need to leave room for the process colors (and tags!) in GX_DEVICE_COLOR_MAX_COMPONENTS */ |
696 | 137k | } |
697 | | /* |
698 | | * The DeviceN device can have zero components if nothing has been |
699 | | * specified. This causes some problems so force at least one |
700 | | * component until something is specified. |
701 | | */ |
702 | 137k | if (!pdev->color_info.num_components) |
703 | 0 | pdev->color_info.num_components = 1; |
704 | | /* |
705 | | * Update the number of device components if we have changes in |
706 | | * SeparationColorNames, SeparationOrder, or MaxSeparations. |
707 | | */ |
708 | 137k | if (num_spot_changed || pdevn_params->max_separations != max_sep || |
709 | 137k | pdevn_params->num_separation_order_names != num_order || |
710 | 137k | pdevn_params->page_spot_colors != page_spot_colors) { |
711 | 17.2k | int has_tags = device_encodes_tags(pdev); |
712 | 17.2k | pdevn_params->separations.num_separations = num_spot; |
713 | 17.2k | pdevn_params->num_separation_order_names = num_order; |
714 | 17.2k | pdevn_params->max_separations = max_sep; |
715 | 17.2k | pdevn_params->page_spot_colors = page_spot_colors; |
716 | 17.2k | if (max_sep != 0) |
717 | 8.60k | pdev->color_info.max_components = max_sep; |
718 | | /* |
719 | | * If we have SeparationOrder specified then the number of |
720 | | * components is given by the number of names in the list. |
721 | | * Otherwise check if the MaxSeparations parameter has specified |
722 | | * a value. If so then use that value, otherwise use the number |
723 | | * of ProcessColorModel components plus the number of |
724 | | * SeparationColorNames is used. |
725 | | */ |
726 | 17.2k | pdev->color_info.num_components = (num_order) |
727 | 17.2k | ? num_order |
728 | 17.2k | : (page_spot_colors >= 0) |
729 | 17.2k | ? npcmcolors + page_spot_colors |
730 | 17.2k | : pdev->color_info.max_components; |
731 | 17.2k | pdev->color_info.num_components += has_tags; |
732 | | |
733 | 17.2k | if (pdev->color_info.num_components > |
734 | 17.2k | pdev->color_info.max_components) |
735 | 0 | pdev->color_info.num_components = |
736 | 0 | pdev->color_info.max_components; |
737 | | |
738 | 17.2k | if (pdev->color_info.num_components > pdev->num_planar_planes) |
739 | 8.57k | pdev->num_planar_planes = pdev->color_info.num_components; |
740 | | |
741 | | /* |
742 | | * See earlier comment about the depth and non compressed |
743 | | * pixel encoding. |
744 | | */ |
745 | 17.2k | if (pdev->num_planar_planes) |
746 | 17.2k | pdev->color_info.depth = bpc_to_depth(pdev->num_planar_planes, |
747 | 17.2k | pdevn_params->bitspercomponent); |
748 | 0 | else |
749 | 0 | pdev->color_info.depth = bpc_to_depth(pdev->color_info.num_components, |
750 | 0 | pdevn_params->bitspercomponent); |
751 | 17.2k | } |
752 | 137k | } |
753 | 137k | if (code >= 0) |
754 | 137k | { |
755 | 137k | int ecode = dev_proc(pdev, dev_spec_op)(pdev, gxdso_adjust_colors, NULL, 0); |
756 | 137k | if (ecode < 0 && ecode != gs_error_undefined) |
757 | 0 | code = ecode; |
758 | 137k | } |
759 | 137k | return code; |
760 | 137k | } |
761 | | |
762 | | /* Free the copied deviceN parameters */ |
763 | | void |
764 | | devn_free_params(gx_device *thread_cdev) |
765 | 1.66M | { |
766 | 1.66M | gs_devn_params *devn_params; |
767 | 1.66M | int k; |
768 | | |
769 | 1.66M | devn_params = dev_proc(thread_cdev, ret_devn_params)(thread_cdev); |
770 | 1.66M | if (devn_params == NULL) return; |
771 | | |
772 | 1.66M | for (k = 0; k < devn_params->separations.num_separations; k++) { |
773 | 346 | gs_free_object(thread_cdev->memory, |
774 | 346 | devn_params->separations.names[k].data, |
775 | 346 | "devn_free_params"); |
776 | 346 | devn_params->separations.names[k].data = NULL; |
777 | 346 | } |
778 | | |
779 | 1.66M | for (k = 0; k < devn_params->pdf14_separations.num_separations; k++) { |
780 | 0 | gs_free_object(thread_cdev->memory, |
781 | 0 | devn_params->pdf14_separations.names[k].data, |
782 | 0 | "devn_free_params"); |
783 | 0 | devn_params->pdf14_separations.names[k].data = NULL; |
784 | 0 | } |
785 | 1.66M | } |
786 | | |
787 | | /* This is used to copy the deviceN parameters from the parent clist device to the |
788 | | individual thread clist devices for multi-threaded rendering */ |
789 | | int |
790 | | devn_copy_params(gx_device * psrcdev, gx_device * pdesdev) |
791 | 103k | { |
792 | 103k | gs_devn_params *src_devn_params, *des_devn_params; |
793 | 103k | int code = 0; |
794 | 103k | int k; |
795 | | |
796 | | /* Get pointers to the parameters */ |
797 | 103k | src_devn_params = dev_proc(psrcdev, ret_devn_params)(psrcdev); |
798 | 103k | des_devn_params = dev_proc(pdesdev, ret_devn_params)(pdesdev); |
799 | 103k | if (src_devn_params == NULL || des_devn_params == NULL) |
800 | 40 | return gs_note_error(gs_error_undefined); |
801 | | |
802 | | /* First the easy items */ |
803 | 103k | des_devn_params->bitspercomponent = src_devn_params->bitspercomponent; |
804 | 103k | des_devn_params->max_separations = src_devn_params->max_separations; |
805 | 103k | des_devn_params->num_separation_order_names = |
806 | 103k | src_devn_params->num_separation_order_names; |
807 | 103k | des_devn_params->num_std_colorant_names = |
808 | 103k | src_devn_params->num_std_colorant_names; |
809 | 103k | des_devn_params->page_spot_colors = src_devn_params->page_spot_colors; |
810 | 103k | des_devn_params->std_colorant_names = src_devn_params->std_colorant_names; |
811 | 103k | des_devn_params->separations.num_separations |
812 | 103k | = src_devn_params->separations.num_separations; |
813 | | /* Now the more complex structures */ |
814 | | /* Spot color names */ |
815 | 104k | for (k = 0; k < des_devn_params->separations.num_separations; k++) { |
816 | 330 | byte * sep_name; |
817 | 330 | int name_size = src_devn_params->separations.names[k].size; |
818 | 330 | sep_name = (byte *)gs_alloc_bytes(pdesdev->memory->stable_memory, |
819 | 330 | name_size, "devn_copy_params"); |
820 | 330 | if (sep_name == NULL) { |
821 | 0 | return_error(gs_error_VMerror); |
822 | 0 | } |
823 | 330 | memcpy(sep_name, src_devn_params->separations.names[k].data, name_size); |
824 | 330 | des_devn_params->separations.names[k].size = name_size; |
825 | 330 | des_devn_params->separations.names[k].data = sep_name; |
826 | 330 | } |
827 | | /* Order map */ |
828 | 103k | memcpy(des_devn_params->separation_order_map, |
829 | 103k | src_devn_params->separation_order_map, sizeof(gs_separation_map)); |
830 | | |
831 | | /* Handle the PDF14 items if they are there */ |
832 | 103k | des_devn_params->pdf14_separations.num_separations |
833 | 103k | = src_devn_params->pdf14_separations.num_separations; |
834 | 103k | for (k = 0; k < des_devn_params->pdf14_separations.num_separations; k++) { |
835 | 0 | byte * sep_name; |
836 | 0 | int name_size = src_devn_params->pdf14_separations.names[k].size; |
837 | 0 | sep_name = (byte *)gs_alloc_bytes(pdesdev->memory->stable_memory, |
838 | 0 | name_size, "devn_copy_params"); |
839 | 0 | if (sep_name == NULL) { |
840 | 0 | return_error(gs_error_VMerror); |
841 | 0 | } |
842 | 0 | memcpy(sep_name, src_devn_params->pdf14_separations.names[k].data, |
843 | 0 | name_size); |
844 | 0 | des_devn_params->pdf14_separations.names[k].size = name_size; |
845 | 0 | des_devn_params->pdf14_separations.names[k].data = sep_name; |
846 | 0 | } |
847 | 103k | return code; |
848 | 103k | } |
849 | | |
850 | | static int |
851 | | compare_equivalent_cmyk_color_params(const equivalent_cmyk_color_params *pequiv_colors1, const equivalent_cmyk_color_params *pequiv_colors2) |
852 | 109k | { |
853 | 109k | int i; |
854 | 109k | if (pequiv_colors1->all_color_info_valid != pequiv_colors2->all_color_info_valid) |
855 | 0 | return(1); |
856 | 7.10M | for (i=0; i<GX_DEVICE_MAX_SEPARATIONS; i++) { |
857 | 6.99M | if (pequiv_colors1->color[i].color_info_valid != pequiv_colors2->color[i].color_info_valid) |
858 | 0 | return(1); |
859 | 6.99M | if (pequiv_colors1->color[i].c != pequiv_colors2->color[i].c ) |
860 | 0 | return(1); |
861 | 6.99M | if (pequiv_colors1->color[i].m != pequiv_colors2->color[i].m ) |
862 | 0 | return(1); |
863 | 6.99M | if (pequiv_colors1->color[i].y != pequiv_colors2->color[i].y ) |
864 | 0 | return(1); |
865 | 6.99M | if (pequiv_colors1->color[i].k != pequiv_colors2->color[i].k ) |
866 | 0 | return(1); |
867 | 6.99M | } |
868 | 109k | return(0); |
869 | 109k | } |
870 | | |
871 | | static bool separations_equal(const gs_separations *p1, const gs_separations *p2) |
872 | 218k | { |
873 | 218k | int k; |
874 | | |
875 | 218k | if (p1->num_separations != p2->num_separations) |
876 | 0 | return false; |
877 | 218k | for (k = 0; k < p1->num_separations; k++) { |
878 | 0 | if (p1->names[k].size != p2->names[k].size) |
879 | 0 | return false; |
880 | 0 | else if (p1->names[k].size > 0) { |
881 | 0 | if (memcmp(p1->names[k].data, p2->names[k].data, p1->names[k].size) != 0) |
882 | 0 | return false; |
883 | 0 | } |
884 | 0 | } |
885 | 218k | return true; |
886 | 218k | } |
887 | | |
888 | | static bool devn_params_equal(const gs_devn_params *p1, const gs_devn_params *p2) |
889 | 113k | { |
890 | 113k | if (p1->bitspercomponent != p2->bitspercomponent) |
891 | 0 | return false; |
892 | 113k | if (p1->max_separations != p2->max_separations) |
893 | 42 | return false; |
894 | 113k | if (p1->num_separation_order_names != p2->num_separation_order_names) |
895 | 0 | return false; |
896 | 113k | if (p1->num_std_colorant_names != p2->num_std_colorant_names) |
897 | 0 | return false; |
898 | 113k | if (p1->page_spot_colors != p2->page_spot_colors) |
899 | 3.89k | return false; |
900 | 109k | if (!separations_equal(&p1->pdf14_separations, &p2->pdf14_separations)) |
901 | 0 | return false; |
902 | 109k | if (!separations_equal(&p1->separations, &p2->separations)) |
903 | 0 | return false; |
904 | 109k | if (memcmp(p1->separation_order_map, p2->separation_order_map, sizeof(gs_separation_map)) != 0) |
905 | 0 | return false; |
906 | 109k | if (p1->std_colorant_names != p2->std_colorant_names) |
907 | 0 | return false; |
908 | 109k | return true; |
909 | 109k | } |
910 | | |
911 | | int |
912 | | devn_generic_put_params(gx_device *pdev, gs_param_list *plist, |
913 | | gs_devn_params *pdevn_params, equivalent_cmyk_color_params *pequiv_colors, |
914 | | int is_printer) |
915 | 137k | { |
916 | 137k | int code; |
917 | | /* Save current data in case we have a problem */ |
918 | 137k | gx_device_color_info save_info = pdev->color_info; |
919 | 137k | gs_devn_params saved_devn_params = *pdevn_params; |
920 | 137k | equivalent_cmyk_color_params saved_equiv_colors; |
921 | 137k | int save_planes = pdev->num_planar_planes; |
922 | | |
923 | 137k | if (pequiv_colors != NULL) |
924 | 137k | saved_equiv_colors = *pequiv_colors; |
925 | | |
926 | | /* Use utility routine to handle parameters */ |
927 | 137k | code = devn_put_params(pdev, plist, pdevn_params, pequiv_colors); |
928 | | |
929 | | /* Check for default printer parameters */ |
930 | 137k | if (is_printer && code >= 0) |
931 | 137k | code = gdev_prn_put_params(pdev, plist); |
932 | | |
933 | | /* If we have an error then restore original data. */ |
934 | 137k | if (code < 0) { |
935 | 125 | pdev->color_info = save_info; |
936 | 125 | *pdevn_params = saved_devn_params; |
937 | 125 | if (pequiv_colors != NULL) |
938 | 125 | *pequiv_colors = saved_equiv_colors; |
939 | 125 | return code; |
940 | 125 | } |
941 | | |
942 | | /* If anything changed, then close the device, etc. */ |
943 | 136k | if (!gx_color_info_equal(&pdev->color_info, &save_info) || |
944 | 136k | !devn_params_equal(pdevn_params, &saved_devn_params) || |
945 | 136k | (pequiv_colors != NULL && |
946 | 109k | compare_equivalent_cmyk_color_params(pequiv_colors, &saved_equiv_colors)) || |
947 | 136k | pdev->num_planar_planes != save_planes) { |
948 | 27.7k | gx_device *parent_dev = pdev; |
949 | 27.7k | gx_device_color_info resave_info = pdev->color_info; |
950 | 27.7k | int resave_planes = pdev->num_planar_planes; |
951 | | |
952 | 27.7k | while (parent_dev->parent != NULL) |
953 | 0 | parent_dev = parent_dev->parent; |
954 | | |
955 | | /* Temporarily restore the old color_info, so the close happens with |
956 | | * the old version. In particular this allows Nup to flush properly. */ |
957 | 27.7k | pdev->color_info = save_info; |
958 | 27.7k | pdev->num_planar_planes = save_planes; |
959 | 27.7k | gs_closedevice(parent_dev); |
960 | | /* Then put the shiny new color_info back in. */ |
961 | 27.7k | pdev->color_info = resave_info; |
962 | 27.7k | pdev->num_planar_planes = resave_planes; |
963 | | /* Reset the separable and linear shift, masks, bits. */ |
964 | 27.7k | set_linear_color_bits_mask_shift(pdev); |
965 | 27.7k | } |
966 | | /* |
967 | | * Also check for parameters which are being passed from the PDF 1.4 |
968 | | * compositior clist write device. This device needs to pass info |
969 | | * to the PDF 1.4 compositor clist reader device. However this device |
970 | | * is not crated until the clist is being read. Thus we have to buffer |
971 | | * this info in the output device. (This is only needed for devices |
972 | | * which support spot colors.) |
973 | | */ |
974 | 136k | code = pdf14_put_devn_params(pdev, pdevn_params, plist); |
975 | 136k | return code; |
976 | 137k | } |
977 | | |
978 | | /* |
979 | | * Utility routine for handling DeviceN related parameters in a |
980 | | * standard raster printer type device. |
981 | | */ |
982 | | int |
983 | | devn_printer_put_params(gx_device *pdev, gs_param_list *plist, |
984 | | gs_devn_params *pdevn_params, equivalent_cmyk_color_params *pequiv_colors) |
985 | 137k | { |
986 | 137k | return devn_generic_put_params(pdev, plist, pdevn_params, pequiv_colors, 1); |
987 | 137k | } |
988 | | |
989 | | /* |
990 | | * Free a set of separation names |
991 | | */ |
992 | | void |
993 | | free_separation_names(gs_memory_t * mem, |
994 | | gs_separations * pseparation) |
995 | 13.4k | { |
996 | 13.4k | int i; |
997 | | |
998 | | /* Discard the sub levels. */ |
999 | 13.6k | for (i = 0; i < pseparation->num_separations; i++) { |
1000 | 208 | gs_free_object(mem->stable_memory, pseparation->names[i].data, |
1001 | 208 | "free_separation_names"); |
1002 | 208 | pseparation->names[i].data = NULL; |
1003 | 208 | pseparation->names[i].size = 0; |
1004 | 208 | } |
1005 | 13.4k | pseparation->num_separations = 0; |
1006 | 13.4k | return; |
1007 | 13.4k | } |
1008 | | |
1009 | | /* ***************** The spotcmyk and devicen devices ***************** */ |
1010 | | |
1011 | | /* Define the device parameters. */ |
1012 | | #ifndef X_DPI |
1013 | | # define X_DPI 72 |
1014 | | #endif |
1015 | | #ifndef Y_DPI |
1016 | | # define Y_DPI 72 |
1017 | | #endif |
1018 | | |
1019 | | /* The device descriptor */ |
1020 | | static dev_proc_open_device(spotcmyk_prn_open); |
1021 | | static dev_proc_print_page(spotcmyk_print_page); |
1022 | | |
1023 | | /* GC procedures */ |
1024 | | |
1025 | | static |
1026 | 1.08M | ENUM_PTRS_WITH(gx_devn_prn_device_enum_ptrs, gx_devn_prn_device *pdev) |
1027 | 1.08M | { |
1028 | 1.08M | if (index < pdev->devn_params.separations.num_separations) |
1029 | 16 | ENUM_RETURN(pdev->devn_params.separations.names[index].data); |
1030 | 1.08M | ENUM_PREFIX(st_device_printer, |
1031 | 1.08M | pdev->devn_params.separations.num_separations); |
1032 | 1.08M | } |
1033 | | |
1034 | 1.08M | ENUM_PTRS_END |
1035 | 21.2k | static RELOC_PTRS_WITH(gx_devn_prn_device_reloc_ptrs, gx_devn_prn_device *pdev) |
1036 | 21.2k | { |
1037 | 21.2k | RELOC_PREFIX(st_device_printer); |
1038 | 21.2k | { |
1039 | 21.2k | int i; |
1040 | | |
1041 | 21.2k | for (i = 0; i < pdev->devn_params.separations.num_separations; ++i) { |
1042 | 16 | RELOC_PTR(gx_devn_prn_device, devn_params.separations.names[i].data); |
1043 | 16 | } |
1044 | 21.2k | } |
1045 | 21.2k | } |
1046 | 21.2k | RELOC_PTRS_END |
1047 | | |
1048 | | void |
1049 | | gx_devn_prn_device_finalize(const gs_memory_t *cmem, void *vpdev) |
1050 | 16.6k | { |
1051 | 16.6k | devn_free_params((gx_device*) vpdev); |
1052 | 16.6k | gx_device_finalize(cmem, vpdev); |
1053 | 16.6k | } |
1054 | | |
1055 | | /* Even though gx_devn_prn_device_finalize is the same as gx_device_finalize, */ |
1056 | | /* we need to implement it separately because st_composite_final */ |
1057 | | /* declares all 3 procedures as private. */ |
1058 | | static void |
1059 | | static_gx_devn_prn_device_finalize(const gs_memory_t *cmem, void *vpdev) |
1060 | 0 | { |
1061 | 0 | gx_devn_prn_device_finalize(cmem, vpdev); |
1062 | 0 | } |
1063 | | |
1064 | | gs_public_st_composite_final(st_gx_devn_prn_device, gx_devn_prn_device, |
1065 | | "gx_devn_prn_device", gx_devn_prn_device_enum_ptrs, gx_devn_prn_device_reloc_ptrs, |
1066 | | static_gx_devn_prn_device_finalize); |
1067 | | |
1068 | | static void |
1069 | | devicen_initialize_device_procs(gx_device *dev) |
1070 | 0 | { |
1071 | 0 | set_dev_proc(dev, open_device, spotcmyk_prn_open); |
1072 | 0 | set_dev_proc(dev, output_page, gdev_prn_output_page_seekable); |
1073 | 0 | set_dev_proc(dev, close_device, gdev_prn_close); |
1074 | 0 | set_dev_proc(dev, get_params, gx_devn_prn_get_params); |
1075 | 0 | set_dev_proc(dev, put_params, gx_devn_prn_put_params); |
1076 | 0 | set_dev_proc(dev, get_page_device, gx_page_device_get_page_device); |
1077 | 0 | set_dev_proc(dev, get_color_mapping_procs, gx_devn_prn_get_color_mapping_procs); |
1078 | 0 | set_dev_proc(dev, get_color_comp_index, gx_devn_prn_get_color_comp_index); |
1079 | 0 | set_dev_proc(dev, encode_color, gx_devn_prn_encode_color); |
1080 | 0 | set_dev_proc(dev, decode_color, gx_devn_prn_decode_color); |
1081 | 0 | set_dev_proc(dev, update_spot_equivalent_colors, gx_devn_prn_update_spot_equivalent_colors); |
1082 | 0 | set_dev_proc(dev, ret_devn_params, gx_devn_prn_ret_devn_params); |
1083 | 0 | } |
1084 | | |
1085 | | fixed_colorant_name DeviceGrayComponents[] = { |
1086 | | "Gray", |
1087 | | 0 /* List terminator */ |
1088 | | }; |
1089 | | |
1090 | | fixed_colorant_name DeviceRGBComponents[] = { |
1091 | | "Red", |
1092 | | "Green", |
1093 | | "Blue", |
1094 | | 0 /* List terminator */ |
1095 | | }; |
1096 | | |
1097 | | fixed_colorant_name DeviceCMYKComponents[] = { |
1098 | | "Cyan", |
1099 | | "Magenta", |
1100 | | "Yellow", |
1101 | | "Black", |
1102 | | 0 /* List terminator */ |
1103 | | }; |
1104 | | |
1105 | | #define gx_devn_prn_device_body(init, dname, ncomp, pol, depth, mg, mc, cn)\ |
1106 | | std_device_full_body_type_extended(gx_devn_prn_device, init, dname,\ |
1107 | | &st_gx_devn_prn_device,\ |
1108 | | (int)((long)(DEFAULT_WIDTH_10THS) * (X_DPI) / 10),\ |
1109 | | (int)((long)(DEFAULT_HEIGHT_10THS) * (Y_DPI) / 10),\ |
1110 | | X_DPI, Y_DPI,\ |
1111 | | GX_DEVICE_COLOR_MAX_COMPONENTS, /* MaxComponents */\ |
1112 | | ncomp, /* NumComp */\ |
1113 | | pol, /* Polarity */\ |
1114 | | depth, 0, /* Depth, GrayIndex */\ |
1115 | | mg, mc, /* MaxGray, MaxColor */\ |
1116 | | mg + 1, mc + 1, /* DitherGray, DitherColor */\ |
1117 | | GX_CINFO_SEP_LIN, /* Linear & Separable */\ |
1118 | | cn, /* Process color model name */\ |
1119 | | 0, 0, /* offsets */\ |
1120 | | 0, 0, 0, 0 /* margins */\ |
1121 | | ),\ |
1122 | | prn_device_body_rest_(spotcmyk_print_page) |
1123 | | |
1124 | | /* |
1125 | | * Example device with CMYK and spot color support |
1126 | | */ |
1127 | | const gx_devn_prn_device gs_spotcmyk_device = |
1128 | | { |
1129 | | gx_devn_prn_device_body(devicen_initialize_device_procs, "spotcmyk", |
1130 | | 4, GX_CINFO_POLARITY_SUBTRACTIVE, 4, 1, 1, |
1131 | | "DeviceCMYK"), |
1132 | | /* DeviceN device specific parameters */ |
1133 | | { 1, /* Bits per color - must match ncomp, depth, etc. above */ |
1134 | | DeviceCMYKComponents, /* Names of color model colorants */ |
1135 | | 4, /* Number colorants for CMYK */ |
1136 | | 0, /* MaxSeparations has not been specified */ |
1137 | | -1, /* PageSpotColors has not been specified */ |
1138 | | {0}, /* SeparationNames */ |
1139 | | 0, /* SeparationOrder names */ |
1140 | | {0, 1, 2, 3, 4, 5, 6, 7 } /* Initial component SeparationOrder */ |
1141 | | } |
1142 | | }; |
1143 | | |
1144 | | /* |
1145 | | * Example DeviceN color device |
1146 | | */ |
1147 | | const gx_devn_prn_device gs_devicen_device = |
1148 | | { |
1149 | | gx_devn_prn_device_body(devicen_initialize_device_procs, "devicen", |
1150 | | 4, GX_CINFO_POLARITY_SUBTRACTIVE, 32, 255, 255, |
1151 | | "DeviceCMYK"), |
1152 | | /* DeviceN device specific parameters */ |
1153 | | { 8, /* Bits per color - must match ncomp, depth, etc. above */ |
1154 | | DeviceCMYKComponents, /* Names of color model colorants */ |
1155 | | 4, /* Number colorants for CMYK */ |
1156 | | 0, /* MaxSeparations has not been specified */ |
1157 | | -1, /* PageSpotColors has not been specified */ |
1158 | | {0}, /* SeparationNames */ |
1159 | | 0, /* SeparationOrder names */ |
1160 | | {0, 1, 2, 3, 4, 5, 6, 7 } /* Initial component SeparationOrder */ |
1161 | | } |
1162 | | }; |
1163 | | |
1164 | | /* Open the psd devices */ |
1165 | | int |
1166 | | spotcmyk_prn_open(gx_device * pdev) |
1167 | 0 | { |
1168 | 0 | int code = gdev_prn_open(pdev); |
1169 | |
|
1170 | 0 | while (pdev->child) |
1171 | 0 | pdev = pdev->child; |
1172 | |
|
1173 | 0 | set_linear_color_bits_mask_shift(pdev); |
1174 | 0 | pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN; |
1175 | 0 | return code; |
1176 | 0 | } |
1177 | | |
1178 | | /* Color mapping routines for the spotcmyk device */ |
1179 | | |
1180 | | static void |
1181 | | gray_cs_to_spotcmyk_cm(const gx_device * dev, frac gray, frac out[]) |
1182 | 0 | { |
1183 | 0 | int * map = ((gx_devn_prn_device *) dev)->devn_params.separation_order_map; |
1184 | |
|
1185 | 0 | gray_cs_to_devn_cm(dev, map, gray, out); |
1186 | 0 | } |
1187 | | |
1188 | | static void |
1189 | | rgb_cs_to_spotcmyk_cm(const gx_device * dev, const gs_gstate *pgs, |
1190 | | frac r, frac g, frac b, frac out[]) |
1191 | 0 | { |
1192 | 0 | int * map = ((gx_devn_prn_device *) dev)->devn_params.separation_order_map; |
1193 | |
|
1194 | 0 | rgb_cs_to_devn_cm(dev, map, pgs, r, g, b, out); |
1195 | 0 | } |
1196 | | |
1197 | | static void |
1198 | | cmyk_cs_to_spotcmyk_cm(const gx_device * dev, frac c, frac m, frac y, frac k, frac out[]) |
1199 | 0 | { |
1200 | 0 | int * map = ((gx_devn_prn_device *) dev)->devn_params.separation_order_map; |
1201 | |
|
1202 | 0 | cmyk_cs_to_devn_cm(dev, map, c, m, y, k, out); |
1203 | 0 | } |
1204 | | |
1205 | | static const gx_cm_color_map_procs spotCMYK_procs = { |
1206 | | gray_cs_to_spotcmyk_cm, rgb_cs_to_spotcmyk_cm, cmyk_cs_to_spotcmyk_cm |
1207 | | }; |
1208 | | |
1209 | | const gx_cm_color_map_procs * |
1210 | | gx_devn_prn_get_color_mapping_procs(const gx_device * dev, const gx_device **map_dev) |
1211 | 0 | { |
1212 | 0 | *map_dev = dev; |
1213 | 0 | return &spotCMYK_procs; |
1214 | 0 | } |
1215 | | |
1216 | | /* |
1217 | | * Encode a list of colorant values into a gx_color_index_value. |
1218 | | */ |
1219 | | gx_color_index |
1220 | | gx_devn_prn_encode_color(gx_device *dev, const gx_color_value colors[]) |
1221 | 36 | { |
1222 | 36 | int bpc = ((gx_devn_prn_device *)dev)->devn_params.bitspercomponent; |
1223 | 36 | gx_color_index color = 0; |
1224 | 36 | int i = 0; |
1225 | 36 | uchar ncomp = dev->color_info.num_components; |
1226 | 36 | COLROUND_VARS; |
1227 | | |
1228 | 36 | COLROUND_SETUP(bpc); |
1229 | 180 | for (; i<ncomp; i++) { |
1230 | 144 | color <<= bpc; |
1231 | 144 | color |= COLROUND_ROUND(colors[i]); |
1232 | 144 | } |
1233 | 36 | return (color == gx_no_color_index ? color ^ 1 : color); |
1234 | 36 | } |
1235 | | |
1236 | | /* |
1237 | | * Decode a gx_color_index value back to a list of colorant values. |
1238 | | */ |
1239 | | int |
1240 | | gx_devn_prn_decode_color(gx_device * dev, gx_color_index color, gx_color_value * out) |
1241 | 0 | { |
1242 | 0 | int bpc = ((gx_devn_prn_device *)dev)->devn_params.bitspercomponent; |
1243 | 0 | int mask = (1 << bpc) - 1; |
1244 | 0 | int i = 0; |
1245 | 0 | uchar ncomp = dev->color_info.num_components; |
1246 | 0 | COLDUP_VARS; |
1247 | |
|
1248 | 0 | COLDUP_SETUP(bpc); |
1249 | 0 | for (; i<ncomp; i++) { |
1250 | 0 | out[ncomp - i - 1] = COLDUP_DUP(color & mask); |
1251 | 0 | color >>= bpc; |
1252 | 0 | } |
1253 | 0 | return 0; |
1254 | 0 | } |
1255 | | |
1256 | | /* Get parameters. */ |
1257 | | int |
1258 | | gx_devn_prn_get_params(gx_device *dev, gs_param_list *plist) |
1259 | 337k | { |
1260 | 337k | gx_devn_prn_device *pdev = (gx_devn_prn_device *)dev; |
1261 | 337k | int code = gdev_prn_get_params(dev, plist); |
1262 | | |
1263 | 337k | if (code < 0) |
1264 | 0 | return code; |
1265 | 337k | return devn_get_params(dev, plist, &pdev->devn_params, |
1266 | 337k | &pdev->equiv_cmyk_colors); |
1267 | 337k | } |
1268 | | |
1269 | | /* Set parameters. */ |
1270 | | int |
1271 | | gx_devn_prn_put_params(gx_device *dev, gs_param_list *plist) |
1272 | 96.1k | { |
1273 | 96.1k | gx_devn_prn_device *pdev = (gx_devn_prn_device *)dev; |
1274 | | |
1275 | 96.1k | return devn_printer_put_params(dev, plist, &pdev->devn_params, |
1276 | 96.1k | &pdev->equiv_cmyk_colors); |
1277 | 96.1k | } |
1278 | | |
1279 | | /* |
1280 | | * Device proc for returning a pointer to DeviceN parameter structure |
1281 | | */ |
1282 | | gs_devn_params * |
1283 | | gx_devn_prn_ret_devn_params(gx_device * dev) |
1284 | 231k | { |
1285 | 231k | gx_devn_prn_device *pdev = (gx_devn_prn_device *)dev; |
1286 | | |
1287 | 231k | return &pdev->devn_params; |
1288 | 231k | } |
1289 | | |
1290 | | const gs_devn_params * |
1291 | | gx_devn_prn_ret_devn_params_const(const gx_device * dev) |
1292 | 7.29M | { |
1293 | 7.29M | const gx_devn_prn_device *pdev = (const gx_devn_prn_device *)dev; |
1294 | | |
1295 | 7.29M | return &pdev->devn_params; |
1296 | 7.29M | } |
1297 | | |
1298 | | /* |
1299 | | * Device proc for updating the equivalent CMYK color for spot colors. |
1300 | | */ |
1301 | | int |
1302 | | gx_devn_prn_update_spot_equivalent_colors(gx_device *dev, const gs_gstate * pgs, const gs_color_space *pcs) |
1303 | 5.37k | { |
1304 | 5.37k | gx_devn_prn_device *pdev = (gx_devn_prn_device *)dev; |
1305 | | |
1306 | 5.37k | return update_spot_equivalent_cmyk_colors(dev, pgs, pcs, &pdev->devn_params, |
1307 | 5.37k | &pdev->equiv_cmyk_colors); |
1308 | 5.37k | } |
1309 | | |
1310 | | /* |
1311 | | * This routine will check to see if the color component name match those |
1312 | | * that are available amoung the current device's color components. |
1313 | | * |
1314 | | * Parameters: |
1315 | | * dev - pointer to device data structure. |
1316 | | * pname - pointer to name (zero termination not required) |
1317 | | * nlength - length of the name |
1318 | | * |
1319 | | * This routine returns a positive value (0 to n) which is the device colorant |
1320 | | * number if the name is found. It returns GX_DEVICE_COLOR_MAX_COMPONENTS if |
1321 | | * the colorant is not being used due to a SeparationOrder device parameter. |
1322 | | * It returns a negative value if not found. |
1323 | | */ |
1324 | | int |
1325 | | gx_devn_prn_get_color_comp_index(gx_device * dev, const char * pname, |
1326 | | int name_size, int component_type) |
1327 | 441k | { |
1328 | 441k | gx_devn_prn_device *pdev = (gx_devn_prn_device *)dev; |
1329 | | |
1330 | 441k | return devn_get_color_comp_index(dev, |
1331 | 441k | &pdev->devn_params, |
1332 | 441k | &pdev->equiv_cmyk_colors, |
1333 | 441k | pname, |
1334 | 441k | name_size, |
1335 | 441k | component_type, |
1336 | 441k | ENABLE_AUTO_SPOT_COLORS); |
1337 | 441k | } |
1338 | | |
1339 | | /* |
1340 | | * This routine will extract a specified set of bits from a buffer and pack |
1341 | | * them into a given buffer. |
1342 | | * |
1343 | | * Parameters: |
1344 | | * source - The source of the data |
1345 | | * dest - The destination for the data |
1346 | | * depth - The size of the bits per pixel - must be a multiple of 8 |
1347 | | * first_bit - The location of the first data bit (LSB). |
1348 | | * bit_width - The number of bits to be extracted. |
1349 | | * npixel - The number of pixels. |
1350 | | * |
1351 | | * Returns: |
1352 | | * Length of the output line (in bytes) |
1353 | | * Data in dest. |
1354 | | */ |
1355 | | int |
1356 | | repack_data(byte * source, byte * dest, int depth, int first_bit, |
1357 | | int bit_width, int npixel) |
1358 | 0 | { |
1359 | 0 | int in_nbyte = depth >> 3; /* Number of bytes per input pixel */ |
1360 | 0 | int out_nbyte = bit_width >> 3; /* Number of bytes per output pixel */ |
1361 | 0 | gx_color_index mask = 1; |
1362 | 0 | gx_color_index data; |
1363 | 0 | int i, j, length = 0; |
1364 | 0 | byte temp; |
1365 | 0 | byte * out = dest; |
1366 | 0 | int in_bit_start = 8 - depth; |
1367 | 0 | int out_bit_start = 8 - bit_width; |
1368 | 0 | int in_byte_loc = in_bit_start, out_byte_loc = out_bit_start; |
1369 | |
|
1370 | 0 | mask = (mask << bit_width) - 1; |
1371 | 0 | for (i=0; i<npixel; i++) { |
1372 | | /* Get the pixel data */ |
1373 | 0 | if (!in_nbyte) { /* Multiple pixels per byte */ |
1374 | 0 | data = *source; |
1375 | 0 | data >>= in_byte_loc; |
1376 | 0 | in_byte_loc -= depth; |
1377 | 0 | if (in_byte_loc < 0) { /* If finished with byte */ |
1378 | 0 | in_byte_loc = in_bit_start; |
1379 | 0 | source++; |
1380 | 0 | } |
1381 | 0 | } |
1382 | 0 | else { /* One or more bytes per pixel */ |
1383 | 0 | data = *source++; |
1384 | 0 | for (j=1; j<in_nbyte; j++) |
1385 | 0 | data = (data << 8) + *source++; |
1386 | 0 | } |
1387 | 0 | data >>= first_bit; |
1388 | 0 | data &= mask; |
1389 | | |
1390 | | /* Put the output data */ |
1391 | 0 | if (!out_nbyte) { /* Multiple pixels per byte */ |
1392 | 0 | temp = (byte)(*out & ~(mask << out_byte_loc)); |
1393 | 0 | *out = (byte)(temp | (data << out_byte_loc)); |
1394 | 0 | out_byte_loc -= bit_width; |
1395 | 0 | if (out_byte_loc < 0) { /* If finished with byte */ |
1396 | 0 | out_byte_loc = out_bit_start; |
1397 | 0 | out++; |
1398 | 0 | } |
1399 | 0 | } |
1400 | 0 | else { /* One or more bytes per pixel */ |
1401 | 0 | *out++ = (byte)(data >> ((out_nbyte - 1) * 8)); |
1402 | 0 | for (j=1; j<out_nbyte; j++) { |
1403 | 0 | *out++ = (byte)(data >> ((out_nbyte - 1 - j) * 8)); |
1404 | 0 | } |
1405 | 0 | } |
1406 | 0 | } |
1407 | | /* Return the number of bytes in the destination buffer. */ |
1408 | 0 | if (out_byte_loc != out_bit_start) { /* If partially filled last byte */ |
1409 | 0 | *out = *out & ((~0) << out_byte_loc); /* Mask unused part of last byte */ |
1410 | 0 | out++; |
1411 | 0 | } |
1412 | 0 | length = out - dest; |
1413 | 0 | return length; |
1414 | 0 | } |
1415 | | |
1416 | | static int devn_write_pcx_file(gx_device_printer * pdev, char * filename, int ncomp, |
1417 | | int bpc, int pcmlinelength); |
1418 | | /* |
1419 | | * This is an example print page routine for a DeviceN device. This routine |
1420 | | * will handle a DeviceN, a CMYK with spot colors, or an RGB process color model. |
1421 | | * |
1422 | | * This routine creates several output files. If the process color model is |
1423 | | * RGB or CMYK then a bit image file is created which contains the data for the |
1424 | | * process color model data. This data is put into the given file stream. |
1425 | | * I.e. into the output file specified by the user. This file is not created |
1426 | | * for the DeviceN process color model. A separate bit image file is created |
1427 | | * is created for the data for each of the given spot colors. The names for |
1428 | | * these files are created by taking the given output file name and appending |
1429 | | * "sn" (where n is the spot color number 0 to ...) to the output file name. |
1430 | | * The results are unknown if the output file is stdout etc. |
1431 | | * |
1432 | | * After the bit image files are created, then a set of PCX format files are |
1433 | | * created from the bit image files. This files have a ".pcx" appended to the |
1434 | | * end of the files. Thus a CMYK process color model with two spot colors |
1435 | | * would end up with a total of six files being created. (xxx, xxxs0, xxxs1, |
1436 | | * xxx.pcx, xxxs0.pcx, and xxxs1.pcx). |
1437 | | * |
1438 | | * I do not assume that any users will actually want to create all of these |
1439 | | * different files. However I wanted to show an example of how each of the |
1440 | | * spot * colorants could be unpacked from the process color model colorants. |
1441 | | * The bit images files are an easy way to show this without the complication |
1442 | | * of trying to put the data into a specific format. However I do not have a |
1443 | | * tool which will display the bit image data directly so I needed to convert |
1444 | | * it to a form which I can view. Thus the PCX format files are being created. |
1445 | | * Note: The PCX implementation is not complete. There are many (most) |
1446 | | * combinations of bits per pixel and number of colorants that are not supported. |
1447 | | */ |
1448 | | static int |
1449 | | spotcmyk_print_page(gx_device_printer * pdev, gp_file * prn_stream) |
1450 | 0 | { |
1451 | 0 | int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev); |
1452 | 0 | byte *in = gs_alloc_bytes(pdev->memory, line_size, "spotcmyk_print_page(in)"); |
1453 | 0 | byte *buf = gs_alloc_bytes(pdev->memory, line_size + 3, "spotcmyk_print_page(buf)"); |
1454 | 0 | const gx_devn_prn_device * pdevn = (gx_devn_prn_device *) pdev; |
1455 | 0 | uint npcmcolors = pdevn->devn_params.num_std_colorant_names; |
1456 | 0 | uchar ncomp = pdevn->color_info.num_components; |
1457 | 0 | int depth = pdevn->color_info.depth; |
1458 | 0 | int nspot = pdevn->devn_params.separations.num_separations; |
1459 | 0 | int bpc = pdevn->devn_params.bitspercomponent; |
1460 | 0 | int lnum = 0, bottom = pdev->height; |
1461 | 0 | int width = pdev->width; |
1462 | 0 | gp_file * spot_file[GX_DEVICE_COLOR_MAX_COMPONENTS] = {0}; |
1463 | 0 | uint i; |
1464 | 0 | int code = 0; |
1465 | 0 | int first_bit; |
1466 | 0 | int pcmlinelength = 0; /* Initialize against indeterminizm in case of pdev->height == 0. */ |
1467 | 0 | int linelength[GX_DEVICE_COLOR_MAX_COMPONENTS]; |
1468 | 0 | byte *data; |
1469 | 0 | char *spotname = (char *)gs_alloc_bytes(pdev->memory, gp_file_name_sizeof, "spotcmyk_print_page(spotname)"); |
1470 | |
|
1471 | 0 | if (in == NULL || buf == NULL || spotname == NULL) { |
1472 | 0 | code = gs_note_error(gs_error_VMerror); |
1473 | 0 | goto prn_done; |
1474 | 0 | } |
1475 | | /* |
1476 | | * Check if the SeparationOrder list has changed the order of the process |
1477 | | * color model colorants. If so then we will treat all colorants as if they |
1478 | | * are spot colors. |
1479 | | */ |
1480 | 0 | for (i = 0; i < npcmcolors; i++) |
1481 | 0 | if (pdevn->devn_params.separation_order_map[i] != i) |
1482 | 0 | break; |
1483 | 0 | if (i < npcmcolors || ncomp < npcmcolors) { |
1484 | 0 | nspot = ncomp; |
1485 | 0 | npcmcolors = 0; |
1486 | 0 | } |
1487 | | |
1488 | | /* Open the output files for the spot colors */ |
1489 | 0 | for(i = 0; i < nspot; i++) { |
1490 | 0 | gs_snprintf(spotname, gp_file_name_sizeof, "%ss%d", pdevn->fname, i); |
1491 | 0 | code = gs_add_control_path(pdev->memory, gs_permit_file_writing, spotname); |
1492 | 0 | if (code < 0) |
1493 | 0 | goto prn_done; |
1494 | 0 | spot_file[i] = gp_fopen(pdev->memory, spotname, "wb"); |
1495 | 0 | (void)gs_remove_control_path(pdev->memory, gs_permit_file_writing, spotname); |
1496 | 0 | if (spot_file[i] == NULL) { |
1497 | 0 | code = gs_note_error(gs_error_VMerror); |
1498 | 0 | goto prn_done; |
1499 | 0 | } |
1500 | 0 | } |
1501 | | |
1502 | | /* Now create the output bit image files */ |
1503 | 0 | for (; lnum < bottom; ++lnum) { |
1504 | 0 | code = gdev_prn_get_bits(pdev, lnum, in, &data); |
1505 | 0 | if (code < 0) |
1506 | 0 | goto prn_done; |
1507 | | /* Now put the pcm data into the output file */ |
1508 | 0 | if (npcmcolors) { |
1509 | 0 | first_bit = bpc * (ncomp - npcmcolors); |
1510 | 0 | pcmlinelength = repack_data(data, buf, depth, first_bit, bpc * npcmcolors, width); |
1511 | 0 | gp_fwrite(buf, 1, pcmlinelength, prn_stream); |
1512 | 0 | } |
1513 | | /* Put spot color data into the output files */ |
1514 | 0 | for (i = 0; i < nspot; i++) { |
1515 | 0 | first_bit = bpc * (nspot - 1 - i); |
1516 | 0 | linelength[i] = repack_data(data, buf, depth, first_bit, bpc, width); |
1517 | 0 | gp_fwrite(buf, 1, linelength[i], spot_file[i]); |
1518 | 0 | } |
1519 | 0 | } |
1520 | | |
1521 | | /* Close the bit image files */ |
1522 | 0 | for(i = 0; i < nspot; i++) { |
1523 | 0 | gp_fclose(spot_file[i]); |
1524 | 0 | spot_file[i] = NULL; |
1525 | 0 | } |
1526 | | |
1527 | | /* Now convert the bit image files into PCX files */ |
1528 | 0 | if (npcmcolors) { |
1529 | 0 | code = devn_write_pcx_file(pdev, (char *) &pdevn->fname, |
1530 | 0 | npcmcolors, bpc, pcmlinelength); |
1531 | 0 | if (code < 0) |
1532 | 0 | goto prn_done; |
1533 | 0 | } |
1534 | 0 | for(i = 0; i < nspot; i++) { |
1535 | 0 | gs_snprintf(spotname, gp_file_name_sizeof, "%ss%d", pdevn->fname, i); |
1536 | 0 | code = devn_write_pcx_file(pdev, spotname, 1, bpc, linelength[i]); |
1537 | 0 | if (code < 0) |
1538 | 0 | goto prn_done; |
1539 | 0 | } |
1540 | | |
1541 | | /* Clean up and exit */ |
1542 | 0 | prn_done: |
1543 | 0 | for(i = 0; i < nspot; i++) { |
1544 | 0 | if (spot_file[i] != NULL) |
1545 | 0 | gp_fclose(spot_file[i]); |
1546 | 0 | } |
1547 | 0 | if (in != NULL) |
1548 | 0 | gs_free_object(pdev->memory, in, "spotcmyk_print_page(in)"); |
1549 | 0 | if (buf != NULL) |
1550 | 0 | gs_free_object(pdev->memory, buf, "spotcmyk_print_page(buf)"); |
1551 | 0 | if (spotname != NULL) |
1552 | 0 | gs_free_object(pdev->memory, spotname, "spotcmyk_print_page(spotname)"); |
1553 | 0 | return code; |
1554 | 0 | } |
1555 | | |
1556 | | /* |
1557 | | * We are using the PCX output format. This is done for simplicity. |
1558 | | * Much of the following code was copied from gdevpcx.c. |
1559 | | */ |
1560 | | |
1561 | | /* ------ Private definitions ------ */ |
1562 | | |
1563 | | /* All two-byte quantities are stored LSB-first! */ |
1564 | | #if ARCH_IS_BIG_ENDIAN |
1565 | | # define assign_ushort(a,v) a = ((v) >> 8) + ((v) << 8) |
1566 | | #else |
1567 | 0 | # define assign_ushort(a,v) a = (v) |
1568 | | #endif |
1569 | | |
1570 | | typedef struct pcx_header_s { |
1571 | | byte manuf; /* always 0x0a */ |
1572 | | byte version; |
1573 | | #define version_2_5 0 |
1574 | 0 | #define version_2_8_with_palette 2 |
1575 | | #define version_2_8_without_palette 3 |
1576 | 0 | #define version_3_0 /* with palette */ 5 |
1577 | | byte encoding; /* 1=RLE */ |
1578 | | byte bpp; /* bits per pixel per plane */ |
1579 | | ushort x1; /* X of upper left corner */ |
1580 | | ushort y1; /* Y of upper left corner */ |
1581 | | ushort x2; /* x1 + width - 1 */ |
1582 | | ushort y2; /* y1 + height - 1 */ |
1583 | | ushort hres; /* horz. resolution (dots per inch) */ |
1584 | | ushort vres; /* vert. resolution (dots per inch) */ |
1585 | | byte palette[16 * 3]; /* color palette */ |
1586 | | byte reserved; |
1587 | | byte nplanes; /* number of color planes */ |
1588 | | ushort bpl; /* number of bytes per line (uncompressed) */ |
1589 | | ushort palinfo; |
1590 | | #define palinfo_color 1 |
1591 | | #define palinfo_gray 2 |
1592 | | byte xtra[58]; /* fill out header to 128 bytes */ |
1593 | | } pcx_header; |
1594 | | |
1595 | | /* Define the prototype header. */ |
1596 | | static const pcx_header pcx_header_prototype = |
1597 | | { |
1598 | | 10, /* manuf */ |
1599 | | 0, /* version (variable) */ |
1600 | | 1, /* encoding */ |
1601 | | 0, /* bpp (variable) */ |
1602 | | 00, 00, /* x1, y1 */ |
1603 | | 00, 00, /* x2, y2 (variable) */ |
1604 | | 00, 00, /* hres, vres (variable) */ |
1605 | | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* palette (variable) */ |
1606 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1607 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1608 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
1609 | | 0, /* reserved */ |
1610 | | 0, /* nplanes (variable) */ |
1611 | | 00, /* bpl (variable) */ |
1612 | | 00, /* palinfo (variable) */ |
1613 | | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* xtra */ |
1614 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1615 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1616 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} |
1617 | | }; |
1618 | | |
1619 | | /* Forward declarations */ |
1620 | | static void devn_pcx_write_rle(const byte *, const byte *, int, gp_file *); |
1621 | | static int devn_pcx_write_page(gx_device_printer * pdev, gp_file * infile, |
1622 | | int linesize, gp_file * outfile, pcx_header * phdr, bool planar, int depth); |
1623 | | |
1624 | | static const byte pcx_cmyk_palette[16 * 3] = |
1625 | | { |
1626 | | 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x0f, 0x0f, 0x00, |
1627 | | 0xff, 0x00, 0xff, 0x0f, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x0f, 0x00, 0x00, |
1628 | | 0x00, 0xff, 0xff, 0x00, 0x0f, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x00, |
1629 | | 0x00, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x1f, 0x1f, 0x1f, 0x0f, 0x0f, 0x0f, |
1630 | | }; |
1631 | | |
1632 | | static const byte pcx_ega_palette[16 * 3] = |
1633 | | { |
1634 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0x00, 0xaa, 0xaa, |
1635 | | 0xaa, 0x00, 0x00, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, |
1636 | | 0x55, 0x55, 0x55, 0x55, 0x55, 0xff, 0x55, 0xff, 0x55, 0x55, 0xff, 0xff, |
1637 | | 0xff, 0x55, 0x55, 0xff, 0x55, 0xff, 0xff, 0xff, 0x55, 0xff, 0xff, 0xff |
1638 | | }; |
1639 | | |
1640 | | /* |
1641 | | * This routine will set up the revision and palatte for the output |
1642 | | * file. |
1643 | | * |
1644 | | * Please note that this routine does not currently handle all possible |
1645 | | * combinations of bits and planes. |
1646 | | * |
1647 | | * Input parameters: |
1648 | | * pdev - Pointer to device data structure |
1649 | | * file - output file |
1650 | | * header - The header structure to hold the data. |
1651 | | * bits_per_plane - The number of bits per plane. |
1652 | | * num_planes - The number of planes. |
1653 | | */ |
1654 | | static bool |
1655 | | devn_setup_pcx_header(gx_device_printer * pdev, pcx_header * phdr, int num_planes, int bits_per_plane) |
1656 | 0 | { |
1657 | 0 | bool planar = true; /* Invalid cases could cause an indeterminizm. */ |
1658 | |
|
1659 | 0 | *phdr = pcx_header_prototype; |
1660 | 0 | phdr->bpp = bits_per_plane; |
1661 | 0 | phdr->nplanes = num_planes; |
1662 | |
|
1663 | 0 | switch (num_planes) { |
1664 | 0 | case 1: |
1665 | 0 | switch (bits_per_plane) { |
1666 | 0 | case 1: |
1667 | 0 | phdr->version = version_2_8_with_palette; |
1668 | 0 | assign_ushort(phdr->palinfo, palinfo_gray); |
1669 | 0 | memcpy((byte *) phdr->palette, "\000\000\000\377\377\377", 6); |
1670 | 0 | planar = false; |
1671 | 0 | break; |
1672 | 0 | case 2: /* Not defined */ |
1673 | 0 | break; |
1674 | 0 | case 4: |
1675 | 0 | phdr->version = version_2_8_with_palette; |
1676 | 0 | memcpy((byte *) phdr->palette, pcx_ega_palette, sizeof(pcx_ega_palette)); |
1677 | 0 | planar = true; |
1678 | 0 | break; |
1679 | 0 | case 5: /* Not defined */ |
1680 | 0 | break; |
1681 | 0 | case 8: |
1682 | 0 | phdr->version = version_3_0; |
1683 | 0 | assign_ushort(phdr->palinfo, palinfo_gray); |
1684 | 0 | planar = false; |
1685 | 0 | break; |
1686 | 0 | case 16: /* Not defined */ |
1687 | 0 | break; |
1688 | 0 | } |
1689 | 0 | break; |
1690 | 0 | case 2: |
1691 | 0 | switch (bits_per_plane) { |
1692 | 0 | case 1: /* Not defined */ |
1693 | 0 | break; |
1694 | 0 | case 2: /* Not defined */ |
1695 | 0 | break; |
1696 | 0 | case 4: /* Not defined */ |
1697 | 0 | break; |
1698 | 0 | case 5: /* Not defined */ |
1699 | 0 | break; |
1700 | 0 | case 8: /* Not defined */ |
1701 | 0 | break; |
1702 | 0 | case 16: /* Not defined */ |
1703 | 0 | break; |
1704 | 0 | } |
1705 | 0 | break; |
1706 | 0 | case 3: |
1707 | 0 | switch (bits_per_plane) { |
1708 | 0 | case 1: /* Not defined */ |
1709 | 0 | break; |
1710 | 0 | case 2: /* Not defined */ |
1711 | 0 | break; |
1712 | 0 | case 4: /* Not defined */ |
1713 | 0 | break; |
1714 | 0 | case 5: /* Not defined */ |
1715 | 0 | break; |
1716 | 0 | case 8: |
1717 | 0 | phdr->version = version_3_0; |
1718 | 0 | assign_ushort(phdr->palinfo, palinfo_color); |
1719 | 0 | planar = true; |
1720 | 0 | break; |
1721 | 0 | case 16: /* Not defined */ |
1722 | 0 | break; |
1723 | 0 | } |
1724 | 0 | break; |
1725 | 0 | case 4: |
1726 | 0 | switch (bits_per_plane) { |
1727 | 0 | case 1: |
1728 | 0 | phdr->version = 2; |
1729 | 0 | memcpy((byte *) phdr->palette, pcx_cmyk_palette, |
1730 | 0 | sizeof(pcx_cmyk_palette)); |
1731 | 0 | planar = false; |
1732 | 0 | phdr->bpp = 4; |
1733 | 0 | phdr->nplanes = 1; |
1734 | 0 | break; |
1735 | 0 | case 2: /* Not defined */ |
1736 | 0 | break; |
1737 | 0 | case 4: /* Not defined */ |
1738 | 0 | break; |
1739 | 0 | case 5: /* Not defined */ |
1740 | 0 | break; |
1741 | 0 | case 8: /* Not defined */ |
1742 | 0 | break; |
1743 | 0 | case 16: /* Not defined */ |
1744 | 0 | break; |
1745 | 0 | } |
1746 | 0 | break; |
1747 | 0 | } |
1748 | 0 | return planar; |
1749 | 0 | } |
1750 | | |
1751 | | /* Write a palette on a file. */ |
1752 | | static int |
1753 | | pc_write_mono_palette(gx_device * dev, uint max_index, gp_file * file) |
1754 | 0 | { |
1755 | 0 | uint i, c; |
1756 | 0 | gx_color_value rgb[3]; |
1757 | |
|
1758 | 0 | for (i = 0; i < max_index; i++) { |
1759 | 0 | rgb[0] = rgb[1] = rgb[2] = i << 8; |
1760 | 0 | for (c = 0; c < 3; c++) { |
1761 | 0 | byte b = gx_color_value_to_byte(rgb[c]); |
1762 | |
|
1763 | 0 | gp_fputc(b, file); |
1764 | 0 | } |
1765 | 0 | } |
1766 | 0 | return 0; |
1767 | 0 | } |
1768 | | /* |
1769 | | * This routine will send any output data required at the end of a file |
1770 | | * for a particular combination of planes and bits per plane. |
1771 | | * |
1772 | | * Please note that most combinations do not require anything at the end |
1773 | | * of a data file. |
1774 | | * |
1775 | | * Input parameters: |
1776 | | * pdev - Pointer to device data structure |
1777 | | * file - output file |
1778 | | * header - The header structure to hold the data. |
1779 | | * bits_per_plane - The number of bits per plane. |
1780 | | * num_planes - The number of planes. |
1781 | | */ |
1782 | | static int |
1783 | | devn_finish_pcx_file(gx_device_printer * pdev, gp_file * file, pcx_header * header, int num_planes, int bits_per_plane) |
1784 | 0 | { |
1785 | 0 | switch (num_planes) { |
1786 | 0 | case 1: |
1787 | 0 | switch (bits_per_plane) { |
1788 | 0 | case 1: /* Do nothing */ |
1789 | 0 | break; |
1790 | 0 | case 2: /* Not defined */ |
1791 | 0 | break; |
1792 | 0 | case 4: /* Do nothing */ |
1793 | 0 | break; |
1794 | 0 | case 5: /* Not defined */ |
1795 | 0 | break; |
1796 | 0 | case 8: |
1797 | 0 | gp_fputc(0x0c, file); |
1798 | 0 | return pc_write_mono_palette((gx_device *) pdev, 256, file); |
1799 | 0 | case 16: /* Not defined */ |
1800 | 0 | break; |
1801 | 0 | } |
1802 | 0 | break; |
1803 | 0 | case 2: |
1804 | 0 | switch (bits_per_plane) { |
1805 | 0 | case 1: /* Not defined */ |
1806 | 0 | break; |
1807 | 0 | case 2: /* Not defined */ |
1808 | 0 | break; |
1809 | 0 | case 4: /* Not defined */ |
1810 | 0 | break; |
1811 | 0 | case 5: /* Not defined */ |
1812 | 0 | break; |
1813 | 0 | case 8: /* Not defined */ |
1814 | 0 | break; |
1815 | 0 | case 16: /* Not defined */ |
1816 | 0 | break; |
1817 | 0 | } |
1818 | 0 | break; |
1819 | 0 | case 3: |
1820 | 0 | switch (bits_per_plane) { |
1821 | 0 | case 1: /* Not defined */ |
1822 | 0 | break; |
1823 | 0 | case 2: /* Not defined */ |
1824 | 0 | break; |
1825 | 0 | case 4: /* Not defined */ |
1826 | 0 | break; |
1827 | 0 | case 5: /* Not defined */ |
1828 | 0 | break; |
1829 | 0 | case 8: /* Do nothing */ |
1830 | 0 | break; |
1831 | 0 | case 16: /* Not defined */ |
1832 | 0 | break; |
1833 | 0 | } |
1834 | 0 | break; |
1835 | 0 | case 4: |
1836 | 0 | switch (bits_per_plane) { |
1837 | 0 | case 1: /* Do nothing */ |
1838 | 0 | break; |
1839 | 0 | case 2: /* Not defined */ |
1840 | 0 | break; |
1841 | 0 | case 4: /* Not defined */ |
1842 | 0 | break; |
1843 | 0 | case 5: /* Not defined */ |
1844 | 0 | break; |
1845 | 0 | case 8: /* Not defined */ |
1846 | 0 | break; |
1847 | 0 | case 16: /* Not defined */ |
1848 | 0 | break; |
1849 | 0 | } |
1850 | 0 | break; |
1851 | 0 | } |
1852 | 0 | return 0; |
1853 | 0 | } |
1854 | | |
1855 | | /* Send the page to the printer. */ |
1856 | | static int |
1857 | | devn_write_pcx_file(gx_device_printer * pdev, char * filename, int ncomp, |
1858 | | int bpc, int linesize) |
1859 | 0 | { |
1860 | 0 | pcx_header header; |
1861 | 0 | int code; |
1862 | 0 | bool planar; |
1863 | 0 | char *outname = (char *)gs_alloc_bytes(pdev->memory, gp_file_name_sizeof, "devn_write_pcx_file(outname)"); |
1864 | 0 | gp_file * in = NULL; |
1865 | 0 | gp_file * out = NULL; |
1866 | 0 | int depth = bpc_to_depth(ncomp, bpc); |
1867 | |
|
1868 | 0 | if (outname == NULL) { |
1869 | 0 | code = gs_note_error(gs_error_VMerror); |
1870 | 0 | goto done; |
1871 | 0 | } |
1872 | | |
1873 | 0 | code = gs_add_control_path(pdev->memory, gs_permit_file_reading, filename); |
1874 | 0 | if (code < 0) |
1875 | 0 | goto done; |
1876 | | |
1877 | 0 | in = gp_fopen(pdev->memory, filename, "rb"); |
1878 | 0 | if (!in) { |
1879 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
1880 | 0 | goto done; |
1881 | 0 | } |
1882 | 0 | gs_snprintf(outname, gp_file_name_sizeof, "%s.pcx", filename); |
1883 | 0 | code = gs_add_control_path(pdev->memory, gs_permit_file_writing, outname); |
1884 | 0 | if (code < 0) |
1885 | 0 | goto done; |
1886 | 0 | out = gp_fopen(pdev->memory, outname, "wb"); |
1887 | 0 | if (!out) { |
1888 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
1889 | 0 | goto done; |
1890 | 0 | } |
1891 | | |
1892 | 0 | if (ncomp == 4 && bpc == 8) { |
1893 | 0 | ncomp = 3; /* we will convert 32-bit to 24-bit RGB */ |
1894 | 0 | } |
1895 | 0 | planar = devn_setup_pcx_header(pdev, &header, ncomp, bpc); |
1896 | 0 | code = devn_pcx_write_page(pdev, in, linesize, out, &header, planar, depth); |
1897 | 0 | if (code >= 0) |
1898 | 0 | code = devn_finish_pcx_file(pdev, out, &header, ncomp, bpc); |
1899 | |
|
1900 | 0 | done: |
1901 | 0 | (void)gs_remove_control_path(pdev->memory, gs_permit_file_reading, filename); |
1902 | 0 | (void)gs_remove_control_path(pdev->memory, gs_permit_file_writing, outname); |
1903 | 0 | if (in) |
1904 | 0 | gp_fclose(in); |
1905 | 0 | if (out) |
1906 | 0 | gp_fclose(out); |
1907 | |
|
1908 | 0 | if (outname) |
1909 | 0 | gs_free_object(pdev->memory, outname, "spotcmyk_print_page(outname)"); |
1910 | |
|
1911 | 0 | return code; |
1912 | 0 | } |
1913 | | |
1914 | | /* Write out a page in PCX format. */ |
1915 | | /* This routine is used for all formats. */ |
1916 | | /* The caller has set header->bpp, nplanes, and palette. */ |
1917 | | static int |
1918 | | devn_pcx_write_page(gx_device_printer * pdev, gp_file * infile, int linesize, gp_file * outfile, |
1919 | | pcx_header * phdr, bool planar, int depth) |
1920 | 0 | { |
1921 | 0 | int raster = linesize; |
1922 | 0 | uint rsize = ROUND_UP((pdev->width * phdr->bpp + 7) >> 3, 2); /* PCX format requires even */ |
1923 | 0 | int height = pdev->height; |
1924 | 0 | uint lsize = raster + rsize; |
1925 | 0 | byte *line = gs_alloc_bytes(pdev->memory, lsize, "pcx file buffer"); |
1926 | 0 | byte *rgb_buff = NULL; |
1927 | 0 | byte *plane = line + raster; |
1928 | 0 | bool convert_to_rgb = false; |
1929 | 0 | int y; |
1930 | 0 | int code = 0; /* return code */ |
1931 | |
|
1932 | 0 | if (line == 0) /* can't allocate line buffer */ |
1933 | 0 | return_error(gs_error_VMerror); |
1934 | 0 | if (pdev->color_info.num_components == 4 && depth == 32) { |
1935 | 0 | rgb_buff = gs_alloc_bytes(pdev->memory, lsize, "pcx_rgb_buff"); |
1936 | 0 | if (rgb_buff == 0) /* can't allocate line buffer */ |
1937 | 0 | return_error(gs_error_VMerror); |
1938 | 0 | raster = (raster * 3) / 4; /* will be rounded up to even later */ |
1939 | 0 | depth = 24; /* we will be writing 24-bit rgb */ |
1940 | 0 | convert_to_rgb = true; |
1941 | 0 | } |
1942 | | |
1943 | | /* Fill in the other variable entries in the header struct. */ |
1944 | | |
1945 | 0 | assign_ushort(phdr->x2, pdev->width - 1); |
1946 | 0 | assign_ushort(phdr->y2, height - 1); |
1947 | 0 | assign_ushort(phdr->hres, (int)pdev->x_pixels_per_inch); |
1948 | 0 | assign_ushort(phdr->vres, (int)pdev->y_pixels_per_inch); |
1949 | 0 | assign_ushort(phdr->bpl, (planar || depth == 1 ? rsize : |
1950 | 0 | raster + (raster & 1))); |
1951 | | |
1952 | | /* Write the header. */ |
1953 | |
|
1954 | 0 | if (gp_fwrite((const char *)phdr, 1, 128, outfile) < 128) { |
1955 | 0 | code = gs_error_ioerror; |
1956 | 0 | goto pcx_done; |
1957 | 0 | } |
1958 | | /* Write the contents of the image. */ |
1959 | 0 | for (y = 0; y < height; y++) { |
1960 | 0 | byte *row = line; |
1961 | 0 | byte *end; |
1962 | |
|
1963 | 0 | code = gp_fread(line, sizeof(byte), linesize, infile); |
1964 | 0 | if (code < 0) |
1965 | 0 | break; |
1966 | | /* If needed, convert to rgb */ |
1967 | 0 | if (convert_to_rgb) { |
1968 | 0 | int i; |
1969 | 0 | byte *row_in = line; |
1970 | | |
1971 | | /* Transform the data. */ |
1972 | 0 | row = rgb_buff; /* adjust to converted output buffer */ |
1973 | 0 | for (i=0; i < linesize; i += 4) { |
1974 | 0 | *row++ = ((255 - row_in[0]) * (255 - row_in[3])) / 255; |
1975 | 0 | *row++ = ((255 - row_in[1]) * (255 - row_in[3])) / 255; |
1976 | 0 | *row++ = ((255 - row_in[2]) * (255 - row_in[3])) / 255; |
1977 | 0 | row_in += 4; |
1978 | 0 | } |
1979 | 0 | row = rgb_buff; /* adjust to converted output buffer */ |
1980 | 0 | } |
1981 | 0 | end = row + raster; |
1982 | 0 | if (!planar) { /* Just write the bits. */ |
1983 | 0 | if (raster & 1) { /* Round to even, with predictable padding. */ |
1984 | 0 | *end = end[-1]; |
1985 | 0 | ++end; |
1986 | 0 | } |
1987 | 0 | devn_pcx_write_rle(row, end, 1, outfile); |
1988 | 0 | } else |
1989 | 0 | switch (depth) { |
1990 | | |
1991 | 0 | case 4: |
1992 | 0 | { |
1993 | 0 | byte *pend = plane + rsize; |
1994 | 0 | int shift; |
1995 | |
|
1996 | 0 | for (shift = 0; shift < 4; shift++) { |
1997 | 0 | register byte *from, *to; |
1998 | 0 | register int bright = 1 << shift; |
1999 | 0 | register int bleft = bright << 4; |
2000 | |
|
2001 | 0 | for (from = row, to = plane; |
2002 | 0 | from < end; from += 4 |
2003 | 0 | ) { |
2004 | 0 | *to++ = |
2005 | 0 | (from[0] & bleft ? 0x80 : 0) | |
2006 | 0 | (from[0] & bright ? 0x40 : 0) | |
2007 | 0 | (from[1] & bleft ? 0x20 : 0) | |
2008 | 0 | (from[1] & bright ? 0x10 : 0) | |
2009 | 0 | (from[2] & bleft ? 0x08 : 0) | |
2010 | 0 | (from[2] & bright ? 0x04 : 0) | |
2011 | 0 | (from[3] & bleft ? 0x02 : 0) | |
2012 | 0 | (from[3] & bright ? 0x01 : 0); |
2013 | 0 | } |
2014 | | /* We might be one byte short of rsize. */ |
2015 | 0 | if (to < pend) |
2016 | 0 | *to = to[-1]; |
2017 | 0 | devn_pcx_write_rle(plane, pend, 1, outfile); |
2018 | 0 | } |
2019 | 0 | } |
2020 | 0 | break; |
2021 | | |
2022 | 0 | case 24: |
2023 | 0 | { |
2024 | 0 | int pnum; |
2025 | |
|
2026 | 0 | for (pnum = 0; pnum < 3; ++pnum) { |
2027 | 0 | devn_pcx_write_rle(row + pnum, row + raster, 3, outfile); |
2028 | 0 | if (pdev->width & 1) |
2029 | 0 | gp_fputc(0, outfile); /* pad to even */ |
2030 | 0 | } |
2031 | 0 | } |
2032 | 0 | break; |
2033 | | |
2034 | 0 | default: |
2035 | 0 | code = gs_note_error(gs_error_rangecheck); |
2036 | 0 | goto pcx_done; |
2037 | |
|
2038 | 0 | } |
2039 | 0 | code = 0; |
2040 | 0 | } |
2041 | | |
2042 | 0 | pcx_done: |
2043 | 0 | if (rgb_buff != NULL) |
2044 | 0 | gs_free_object(pdev->memory, rgb_buff, "pcx_rgb_buff"); |
2045 | 0 | gs_free_object(pdev->memory, line, "pcx file buffer"); |
2046 | |
|
2047 | 0 | return code; |
2048 | 0 | } |
2049 | | |
2050 | | /* ------ Internal routines ------ */ |
2051 | | |
2052 | | /* Write one line in PCX run-length-encoded format. */ |
2053 | | static void |
2054 | | devn_pcx_write_rle(const byte * from, const byte * end, int step, gp_file * file) |
2055 | 0 | { /* |
2056 | | * The PCX format theoretically allows encoding runs of 63 |
2057 | | * identical bytes, but some readers can't handle repetition |
2058 | | * counts greater than 15. |
2059 | | */ |
2060 | 0 | #define MAX_RUN_COUNT 15 |
2061 | 0 | int max_run = step * MAX_RUN_COUNT; |
2062 | |
|
2063 | 0 | while (from < end) { |
2064 | 0 | byte data = *from; |
2065 | |
|
2066 | 0 | from += step; |
2067 | 0 | if (from >= end || data != *from) { |
2068 | 0 | if (data >= 0xc0) |
2069 | 0 | gp_fputc(0xc1, file); |
2070 | 0 | } else { |
2071 | 0 | const byte *start = from; |
2072 | |
|
2073 | 0 | while ((from < end) && (*from == data)) |
2074 | 0 | from += step; |
2075 | | /* Now (from - start) / step + 1 is the run length. */ |
2076 | 0 | while (from - start >= max_run) { |
2077 | 0 | gp_fputc(0xc0 + MAX_RUN_COUNT, file); |
2078 | 0 | gp_fputc(data, file); |
2079 | 0 | start += max_run; |
2080 | 0 | } |
2081 | 0 | if (from > start || data >= 0xc0) |
2082 | 0 | gp_fputc((from - start) / step + 0xc1, file); |
2083 | 0 | } |
2084 | 0 | gp_fputc(data, file); |
2085 | 0 | } |
2086 | 0 | #undef MAX_RUN_COUNT |
2087 | 0 | } |