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