1 /* Copyright (C) 2001-2019 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
17 /* Support for rinkj (resplendent inkjet) drivers. */
18
19 #include "math_.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 "gsicc_cache.h"
29 #include "gsicc_manage.h"
30 #include "gsicc_cms.h"
31 #include "gdevdevn.h"
32
33 #include "rinkj/rinkj-device.h"
34 #include "rinkj/rinkj-byte-stream.h"
35 #include "rinkj/rinkj-screen-eb.h"
36 #include "rinkj/rinkj-epson870.h"
37
38 #ifndef MAX_CHAN
39 # define MAX_CHAN 15
40 #endif
41
42 /* Define the device parameters. */
43 #ifndef X_DPI
44 # define X_DPI 720
45 #endif
46 #ifndef Y_DPI
47 # define Y_DPI 720
48 #endif
49
50 /* The device descriptor */
51 static dev_proc_get_params(rinkj_get_params);
52 static dev_proc_close_device(rinkj_close_device);
53 static dev_proc_put_params(rinkj_put_params);
54 static dev_proc_print_page(rinkj_print_page);
55 static dev_proc_map_color_rgb(rinkj_map_color_rgb);
56 static dev_proc_get_color_mapping_procs(get_rinkj_color_mapping_procs);
57 static dev_proc_get_color_comp_index(rinkj_get_color_comp_index);
58 static dev_proc_encode_color(rinkj_encode_color);
59 static dev_proc_decode_color(rinkj_decode_color);
60
61 /*
62 * Structure for holding SeparationNames and SeparationOrder elements.
63 */
64 typedef struct gs_separation_names_s {
65 int num_names;
66 const gs_param_string * names[GX_DEVICE_COLOR_MAX_COMPONENTS];
67 } gs_separation_names;
68
69 /* This is redundant with color_info.cm_name. We may eliminate this
70 typedef and use the latter string for everything. */
71 typedef enum {
72 RINKJ_DEVICE_GRAY,
73 RINKJ_DEVICE_RGB,
74 RINKJ_DEVICE_CMYK,
75 RINKJ_DEVICE_N
76 } rinkj_color_model;
77
78 /*
79 * A structure definition for a DeviceN type device
80 */
81 typedef struct rinkj_device_s {
82 gx_device_common;
83 gx_prn_device_common;
84
85 /* ... device-specific parameters ... */
86
87 rinkj_color_model color_model;
88
89 /*
90 * Bits per component (device colorant). Currently only 1 and 8 are
91 * supported.
92 */
93 int bitspercomponent;
94 int n_planes_out; /* actual number of channels in device */
95
96 /*
97 * Pointer to the colorant names for the color model. This will be
98 * null if we have DeviceN type device. The actual possible colorant
99 * names are those in this list plus those in the separation_names
100 * list (below).
101 */
102 fixed_colorant_names_list std_colorant_names;
103 int num_std_colorant_names; /* Number of names in list */
104
105 /*
106 * Separation names (if any).
107 */
108 gs_separation_names separation_names;
109
110 /*
111 * Separation Order (if specified).
112 */
113 gs_separation_names separation_order;
114
115 /* This device can use a device link ICC profile to map
116 the colors to the appropriate color space. Not
117 as flexible as having source and destination profiles
118 and creating the link on the fly, but I am doing
119 the minimal changes on this device to make it work
120 with the new ICC architecture. No optimizations yet. */
121 gcmmhlink_t icc_link;
122 cmm_profile_t *link_profile;
123
124 /* ICC color profile objects, for color conversion. */
125 char profile_out_fn[256];
126 char setup_fn[256];
127 } rinkj_device;
128
129 /*
130 * Macro definition for DeviceN procedures
131 */
132 #define device_procs(get_color_mapping_procs)\
133 { gdev_prn_open,\
134 gx_default_get_initial_matrix,\
135 NULL, /* sync_output */\
136 /* Since the print_page doesn't alter the device, this device can print in the background */\
137 gdev_prn_bg_output_page, /* output_page */\
138 rinkj_close_device, /* close */\
139 NULL, /* map_rgb_color - not used */\
140 rinkj_map_color_rgb, /* map_color_rgb */\
141 NULL, /* fill_rectangle */\
142 NULL, /* tile_rectangle */\
143 NULL, /* copy_mono */\
144 NULL, /* copy_color */\
145 NULL, /* draw_line */\
146 NULL, /* get_bits */\
147 rinkj_get_params, /* get_params */\
148 rinkj_put_params, /* put_params */\
149 NULL, /* map_cmyk_color - not used */\
150 NULL, /* get_xfont_procs */\
151 NULL, /* get_xfont_device */\
152 NULL, /* map_rgb_alpha_color */\
153 gx_page_device_get_page_device, /* get_page_device */\
154 NULL, /* get_alpha_bits */\
155 NULL, /* copy_alpha */\
156 NULL, /* get_band */\
157 NULL, /* copy_rop */\
158 NULL, /* fill_path */\
159 NULL, /* stroke_path */\
160 NULL, /* fill_mask */\
161 NULL, /* fill_trapezoid */\
162 NULL, /* fill_parallelogram */\
163 NULL, /* fill_triangle */\
164 NULL, /* draw_thin_line */\
165 NULL, /* begin_image */\
166 NULL, /* image_data */\
167 NULL, /* end_image */\
168 NULL, /* strip_tile_rectangle */\
169 NULL, /* strip_copy_rop */\
170 NULL, /* get_clipping_box */\
171 NULL, /* begin_typed_image */\
172 NULL, /* get_bits_rectangle */\
173 NULL, /* map_color_rgb_alpha */\
174 NULL, /* create_compositor */\
175 NULL, /* get_hardware_params */\
176 NULL, /* text_begin */\
177 NULL, /* finish_copydevice */\
178 NULL, /* begin_transparency_group */\
179 NULL, /* end_transparency_group */\
180 NULL, /* begin_transparency_mask */\
181 NULL, /* end_transparency_mask */\
182 NULL, /* discard_transparency_layer */\
183 get_color_mapping_procs, /* get_color_mapping_procs */\
184 rinkj_get_color_comp_index, /* get_color_comp_index */\
185 rinkj_encode_color, /* encode_color */\
186 rinkj_decode_color /* decode_color */\
187 }
188
189 static const gx_device_procs spot_cmyk_procs = device_procs(get_rinkj_color_mapping_procs);
190
191 const rinkj_device gs_rinkj_device =
192 {
193 prn_device_body_extended(rinkj_device, spot_cmyk_procs, "rinkj",
194 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
195 X_DPI, Y_DPI, /* X and Y hardware resolution */
196 0, 0, 0, 0, /* margins */
197 GX_DEVICE_COLOR_MAX_COMPONENTS, 4, /* MaxComponents, NumComp */
198 GX_CINFO_POLARITY_SUBTRACTIVE, /* Polarity */
199 32, 0, /* Depth, Gray_index, */
200 255, 255, 1, 1, /* MaxGray, MaxColor, DitherGray, DitherColor */
201 GX_CINFO_SEP_LIN, /* Linear & Separable */
202 "DeviceN", /* Process color model name */
203 rinkj_print_page), /* Printer page print routine */
204 /* DeviceN device specific parameters */
205 RINKJ_DEVICE_CMYK, /* Color model */
206 8, /* Bits per color - must match ncomp, depth, etc. above */
207 4, /* Number of output color planes, overwritten below. */
208 DeviceCMYKComponents, /* Names of color model colorants */
209 4, /* Number colorants for CMYK */
210 {0}, /* SeparationNames */
211 {0}, /* SeparationOrder names */
212 0, /* icc_link (link handle) */
213 0 /* link_profile (device link profile) */
214 };
215
216 /*
217 * The following procedures are used to map the standard color spaces into
218 * the color components for the spotrgb device.
219 */
220 static void
gray_cs_to_spotrgb_cm(gx_device * dev,frac gray,frac out[])221 gray_cs_to_spotrgb_cm(gx_device * dev, frac gray, frac out[])
222 {
223 /* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
224 int i = ((rinkj_device *)dev)->separation_names.num_names;
225
226 out[0] = out[1] = out[2] = gray;
227 for(; i>0; i--) /* Clear spot colors */
228 out[2 + i] = 0;
229 }
230
231 static void
rgb_cs_to_spotrgb_cm(gx_device * dev,const gs_gstate * pgs,frac r,frac g,frac b,frac out[])232 rgb_cs_to_spotrgb_cm(gx_device * dev, const gs_gstate *pgs,
233 frac r, frac g, frac b, frac out[])
234 {
235 /* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
236 int i = ((rinkj_device *)dev)->separation_names.num_names;
237
238 out[0] = r;
239 out[1] = g;
240 out[2] = b;
241 for(; i>0; i--) /* Clear spot colors */
242 out[2 + i] = 0;
243 }
244
245 static void
cmyk_cs_to_spotrgb_cm(gx_device * dev,frac c,frac m,frac y,frac k,frac out[])246 cmyk_cs_to_spotrgb_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[])
247 {
248 /* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
249 int i = ((rinkj_device *)dev)->separation_names.num_names;
250
251 color_cmyk_to_rgb(c, m, y, k, NULL, out, dev->memory);
252 for(; i>0; i--) /* Clear spot colors */
253 out[2 + i] = 0;
254 }
255
256 static void
gray_cs_to_spotcmyk_cm(gx_device * dev,frac gray,frac out[])257 gray_cs_to_spotcmyk_cm(gx_device * dev, frac gray, frac out[])
258 {
259 /* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
260 int i = ((rinkj_device *)dev)->separation_names.num_names;
261
262 out[0] = out[1] = out[2] = 0;
263 out[3] = frac_1 - gray;
264 for(; i>0; i--) /* Clear spot colors */
265 out[3 + i] = 0;
266 }
267
268 static void
rgb_cs_to_spotcmyk_cm(gx_device * dev,const gs_gstate * pgs,frac r,frac g,frac b,frac out[])269 rgb_cs_to_spotcmyk_cm(gx_device * dev, const gs_gstate *pgs,
270 frac r, frac g, frac b, frac out[])
271 {
272 /* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
273 rinkj_device *rdev = (rinkj_device *)dev;
274 int n = rdev->separation_names.num_names;
275 int i;
276
277 color_rgb_to_cmyk(r, g, b, pgs, out, dev->memory);
278 for(i = 0; i < n; i++) /* Clear spot colors */
279 out[4 + i] = 0;
280 }
281
282 static void
cmyk_cs_to_spotcmyk_cm(gx_device * dev,frac c,frac m,frac y,frac k,frac out[])283 cmyk_cs_to_spotcmyk_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[])
284 {
285 /* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
286 rinkj_device *rdev = (rinkj_device *)dev;
287 int n = rdev->separation_names.num_names;
288 int i;
289
290 out[0] = c;
291 out[1] = m;
292 out[2] = y;
293 out[3] = k;
294 for(i = 0; i < n; i++) /* Clear spot colors */
295 out[4 + i] = 0;
296 };
297
298 static void
cmyk_cs_to_spotn_cm(gx_device * dev,frac c,frac m,frac y,frac k,frac out[])299 cmyk_cs_to_spotn_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[])
300 {
301 /* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
302 rinkj_device *rdev = (rinkj_device *)dev;
303 int n = rdev->separation_names.num_names;
304 int i;
305
306 /* If no profile given, assume CMYK */
307 out[0] = c;
308 out[1] = m;
309 out[2] = y;
310 out[3] = k;
311 for(i = 0; i < n; i++) /* Clear spot colors */
312 out[4 + i] = 0;
313 };
314
315 static void
gray_cs_to_spotn_cm(gx_device * dev,frac gray,frac out[])316 gray_cs_to_spotn_cm(gx_device * dev, frac gray, frac out[])
317 {
318 /* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
319
320 cmyk_cs_to_spotn_cm(dev, 0, 0, 0, frac_1 - gray, out);
321 }
322
323 static void
rgb_cs_to_spotn_cm(gx_device * dev,const gs_gstate * pgs,frac r,frac g,frac b,frac out[])324 rgb_cs_to_spotn_cm(gx_device * dev, const gs_gstate *pgs,
325 frac r, frac g, frac b, frac out[])
326 {
327 /* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
328 frac cmyk[4];
329
330 color_rgb_to_cmyk(r, g, b, pgs, cmyk, dev->memory);
331 cmyk_cs_to_spotn_cm(dev, cmyk[0], cmyk[1], cmyk[2], cmyk[3],
332 out);
333 }
334
335 static const gx_cm_color_map_procs spotRGB_procs = {
336 gray_cs_to_spotrgb_cm, rgb_cs_to_spotrgb_cm, cmyk_cs_to_spotrgb_cm
337 };
338
339 static const gx_cm_color_map_procs spotCMYK_procs = {
340 gray_cs_to_spotcmyk_cm, rgb_cs_to_spotcmyk_cm, cmyk_cs_to_spotcmyk_cm
341 };
342
343 static const gx_cm_color_map_procs spotN_procs = {
344 gray_cs_to_spotn_cm, rgb_cs_to_spotn_cm, cmyk_cs_to_spotn_cm
345 };
346
347 /*
348 * These are the handlers for returning the list of color space
349 * to color model conversion routines.
350 */
351
352 static const gx_cm_color_map_procs *
get_rinkj_color_mapping_procs(const gx_device * dev)353 get_rinkj_color_mapping_procs(const gx_device * dev)
354 {
355 const rinkj_device *rdev = (const rinkj_device *)dev;
356
357 if (rdev->color_model == RINKJ_DEVICE_RGB)
358 return &spotRGB_procs;
359 else if (rdev->color_model == RINKJ_DEVICE_CMYK)
360 return &spotCMYK_procs;
361 else if (rdev->color_model == RINKJ_DEVICE_N)
362 return &spotN_procs;
363 else
364 return NULL;
365 }
366
367 /*
368 * Encode a list of colorant values into a gx_color_index_value.
369 */
370 static gx_color_index
rinkj_encode_color(gx_device * dev,const gx_color_value colors[])371 rinkj_encode_color(gx_device *dev, const gx_color_value colors[])
372 {
373 int bpc = ((rinkj_device *)dev)->bitspercomponent;
374 gx_color_index color = 0;
375 int i = 0;
376 int ncomp = dev->color_info.num_components;
377 COLROUND_VARS;
378
379 COLROUND_SETUP(bpc);
380 for (; i<ncomp; i++) {
381 color <<= bpc;
382 color |= COLROUND_ROUND(colors[i]);
383 }
384 return (color == gx_no_color_index ? color ^ 1 : color);
385 }
386
387 /*
388 * Decode a gx_color_index value back to a list of colorant values.
389 */
390 static int
rinkj_decode_color(gx_device * dev,gx_color_index color,gx_color_value * out)391 rinkj_decode_color(gx_device * dev, gx_color_index color, gx_color_value * out)
392 {
393 int bpc = ((rinkj_device *)dev)->bitspercomponent;
394 int drop = sizeof(gx_color_value) * 8 - bpc;
395 int mask = (1 << bpc) - 1;
396 int i = 0;
397 int ncomp = dev->color_info.num_components;
398
399 for (; i<ncomp; i++) {
400 out[ncomp - i - 1] = (color & mask) << drop;
401 color >>= bpc;
402 }
403 return 0;
404 }
405
406 /*
407 * Convert a gx_color_index to RGB.
408 */
409 static int
rinkj_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value rgb[3])410 rinkj_map_color_rgb(gx_device *dev, gx_color_index color, gx_color_value rgb[3])
411 {
412 rinkj_device *rdev = (rinkj_device *)dev;
413
414 if (rdev->color_model == RINKJ_DEVICE_RGB)
415 return rinkj_decode_color(dev, color, rgb);
416 /* TODO: return reasonable values. */
417 rgb[0] = 0;
418 rgb[1] = 0;
419 rgb[2] = 0;
420 return 0;
421 }
422
423 static int
rinkj_open_profile(rinkj_device * rdev)424 rinkj_open_profile(rinkj_device *rdev)
425 {
426 gsicc_rendering_param_t rendering_params;
427
428 if (rdev->link_profile == NULL && rdev->profile_out_fn[0]) {
429
430 rdev->link_profile = gsicc_get_profile_handle_file(rdev->profile_out_fn,
431 strlen(rdev->profile_out_fn), rdev->memory);
432
433 if (rdev->link_profile == NULL)
434 return gs_throw(-1, "Could not create output profile for rinkj device");
435
436 /* Set up the rendering parameters */
437
438 rendering_params.black_point_comp = gsBPNOTSPECIFIED;
439 rendering_params.graphics_type_tag = GS_UNKNOWN_TAG; /* Already rendered */
440 rendering_params.rendering_intent = gsPERCEPTUAL;
441
442 /* Call with a NULL destination profile since we are using a device link profile here */
443 rdev->icc_link = gscms_get_link(rdev->link_profile,
444 NULL, &rendering_params, 0, rdev->memory);
445
446 if (rdev->icc_link == NULL)
447 return gs_throw(-1, "Could not create link handle for rinkj device");
448 }
449 return(0);
450 }
451
452 #define set_param_array(a, d, s)\
453 (a.data = d, a.size = s, a.persistent = false);
454
455 /* Get parameters. We provide a default CRD. */
456 static int
rinkj_get_params(gx_device * pdev,gs_param_list * plist)457 rinkj_get_params(gx_device * pdev, gs_param_list * plist)
458 {
459 rinkj_device *rdev = (rinkj_device *)pdev;
460 int code;
461 bool seprs = false;
462 gs_param_string_array scna;
463 gs_param_string pos;
464 gs_param_string sfs;
465
466 set_param_array(scna, NULL, 0);
467
468 if ( (code = gdev_prn_get_params(pdev, plist)) < 0 ||
469 (code = sample_device_crd_get_params(pdev, plist, "CRDDefault")) < 0 ||
470 (code = param_write_name_array(plist, "SeparationColorNames", &scna)) < 0 ||
471 (code = param_write_bool(plist, "Separations", &seprs)) < 0)
472 return code;
473
474 pos.data = (const byte *)rdev->profile_out_fn,
475 pos.size = strlen(rdev->profile_out_fn),
476 pos.persistent = false;
477 code = param_write_string(plist, "ProfileOut", &pos);
478 if (code < 0)
479 return code;
480
481 sfs.data = (const byte *)rdev->setup_fn,
482 sfs.size = strlen(rdev->setup_fn),
483 sfs.persistent = false;
484 code = param_write_string(plist, "SetupFile", &sfs);
485
486 return code;
487 }
488 #undef set_param_array
489
490 #define compare_color_names(name, name_size, str, str_size) \
491 (name_size == str_size && \
492 (strncmp((const char *)name, (const char *)str, name_size) == 0))
493
494 /*
495 * This routine will check if a name matches any item in a list of process model
496 * color component names.
497 */
498 static bool
check_process_color_names(fixed_colorant_names_list plist,const gs_param_string * pstring)499 check_process_color_names(fixed_colorant_names_list plist,
500 const gs_param_string * pstring)
501 {
502 if (plist) {
503 uint size = pstring->size;
504
505 while( *plist) {
506 if (compare_color_names(*plist, strlen(*plist), pstring->data, size)) {
507 return true;
508 }
509 plist++;
510 }
511 }
512 return false;
513 }
514
515 #define BEGIN_ARRAY_PARAM(pread, pname, pa, psize, e)\
516 BEGIN\
517 switch (code = pread(plist, (param_name = pname), &(pa))) {\
518 case 0:\
519 if ((pa).size != psize) {\
520 ecode = gs_note_error(gs_error_rangecheck);\
521 (pa).data = 0; /* mark as not filled */\
522 } else
523 #define END_ARRAY_PARAM(pa, e)\
524 goto e;\
525 default:\
526 ecode = code;\
527 e: param_signal_error(plist, param_name, ecode);\
528 case 1:\
529 (pa).data = 0; /* mark as not filled */\
530 }\
531 END
532
533 static int
rinkj_param_read_fn(gs_param_list * plist,const char * name,gs_param_string * pstr,int max_len)534 rinkj_param_read_fn(gs_param_list *plist, const char *name,
535 gs_param_string *pstr, int max_len)
536 {
537 int code = param_read_string(plist, name, pstr);
538
539 if (code == 0) {
540 if (pstr->size >= max_len)
541 param_signal_error(plist, name, code = gs_error_rangecheck);
542 } else {
543 pstr->data = 0;
544 }
545 return code;
546 }
547
548 /* Compare a C string and a gs_param_string. */
549 static bool
param_string_eq(const gs_param_string * pcs,const char * str)550 param_string_eq(const gs_param_string *pcs, const char *str)
551 {
552 return (strlen(str) == pcs->size &&
553 !strncmp(str, (const char *)pcs->data, pcs->size));
554 }
555
556 static int
rinkj_set_color_model(rinkj_device * rdev,rinkj_color_model color_model)557 rinkj_set_color_model(rinkj_device *rdev, rinkj_color_model color_model)
558 {
559 int bpc = 8;
560
561 rdev->color_model = color_model;
562 if (color_model == RINKJ_DEVICE_GRAY) {
563 rdev->std_colorant_names = DeviceGrayComponents;
564 rdev->num_std_colorant_names = 1;
565 rdev->color_info.cm_name = "DeviceGray";
566 rdev->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE;
567 } else if (color_model == RINKJ_DEVICE_RGB) {
568 rdev->std_colorant_names = DeviceRGBComponents;
569 rdev->num_std_colorant_names = 3;
570 rdev->color_info.cm_name = "DeviceRGB";
571 rdev->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE;
572 } else if (color_model == RINKJ_DEVICE_CMYK) {
573 rdev->std_colorant_names = DeviceCMYKComponents;
574 rdev->num_std_colorant_names = 4;
575 rdev->color_info.cm_name = "DeviceCMYK";
576 rdev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
577 } else if (color_model == RINKJ_DEVICE_N) {
578 rdev->std_colorant_names = DeviceCMYKComponents;
579 rdev->num_std_colorant_names = 4;
580 rdev->color_info.cm_name = "DeviceN";
581 rdev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
582 } else {
583 return -1;
584 }
585
586 rdev->color_info.max_components = rdev->num_std_colorant_names;
587 rdev->color_info.num_components = rdev->num_std_colorant_names;
588 rdev->color_info.depth = bpc * rdev->num_std_colorant_names;
589 return 0;
590 }
591
592 /* Set parameters. We allow setting the number of bits per component. */
593 static int
rinkj_put_params(gx_device * pdev,gs_param_list * plist)594 rinkj_put_params(gx_device * pdev, gs_param_list * plist)
595 {
596 rinkj_device * const pdevn = (rinkj_device *) pdev;
597 gx_device_color_info save_info;
598 gs_param_name param_name;
599 int npcmcolors;
600 int num_spot = pdevn->separation_names.num_names;
601 int ecode = 0;
602 int code;
603 gs_param_string_array scna;
604 gs_param_string po;
605 gs_param_string sf;
606 gs_param_string pcm;
607 rinkj_color_model color_model = pdevn->color_model;
608
609 BEGIN_ARRAY_PARAM(param_read_name_array, "SeparationColorNames", scna, scna.size, scne) {
610 break;
611 } END_ARRAY_PARAM(scna, scne);
612
613 if (code >= 0)
614 code = rinkj_param_read_fn(plist, "ProfileOut", &po,
615 sizeof(pdevn->profile_out_fn));
616
617 if (code >= 0)
618 code = rinkj_param_read_fn(plist, "SetupFile", &sf,
619 sizeof(pdevn->setup_fn));
620
621 if (code >= 0)
622 code = param_read_name(plist, "ProcessColorModel", &pcm);
623 if (code == 0) {
624 if (param_string_eq (&pcm, "DeviceGray"))
625 color_model = RINKJ_DEVICE_GRAY;
626 else if (param_string_eq (&pcm, "DeviceRGB"))
627 color_model = RINKJ_DEVICE_RGB;
628 else if (param_string_eq (&pcm, "DeviceCMYK"))
629 color_model = RINKJ_DEVICE_CMYK;
630 else if (param_string_eq (&pcm, "DeviceN"))
631 color_model = RINKJ_DEVICE_N;
632 else {
633 param_signal_error(plist, "ProcessColorModel",
634 code = gs_error_rangecheck);
635 }
636 }
637 if (code < 0)
638 return code;
639
640 /*
641 * Save the color_info in case gdev_prn_put_params fails, and for
642 * comparison.
643 */
644 save_info = pdevn->color_info;
645 ecode = rinkj_set_color_model(pdevn, color_model);
646 if (ecode == 0)
647 ecode = gdev_prn_put_params(pdev, plist);
648 if (ecode < 0) {
649 pdevn->color_info = save_info;
650 return ecode;
651 }
652
653 /* Separations are only valid with a subtractive color model */
654 if (pdev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE) {
655 /*
656 * Process the separation color names. Remove any names that already
657 * match the process color model colorant names for the device.
658 */
659 if (scna.data != 0) {
660 int i;
661 int num_names = scna.size;
662 fixed_colorant_names_list pcomp_names =
663 ((rinkj_device *)pdev)->std_colorant_names;
664
665 for (i = num_spot = 0; i < num_names; i++) {
666 if (!check_process_color_names(pcomp_names, &scna.data[i]))
667 pdevn->separation_names.names[num_spot++] = &scna.data[i];
668 }
669 pdevn->separation_names.num_names = num_spot;
670 if (pdevn->is_open)
671 gs_closedevice(pdev);
672 }
673 }
674 npcmcolors = pdevn->num_std_colorant_names;
675 pdevn->color_info.num_components = npcmcolors + num_spot;
676 /*
677 * The DeviceN device can have zero components if nothing has been
678 * specified. This causes some problems so force at least one
679 * component until something is specified.
680 */
681 if (!pdevn->color_info.num_components)
682 pdevn->color_info.num_components = 1;
683 pdevn->color_info.depth = bpc_to_depth(pdevn->color_info.num_components,
684 pdevn->bitspercomponent);
685 if (pdevn->color_info.depth != save_info.depth) {
686 gs_closedevice(pdev);
687 }
688
689 if (po.data != 0) {
690 memcpy(pdevn->profile_out_fn, po.data, po.size);
691 pdevn->profile_out_fn[po.size] = 0;
692 }
693 if (sf.data != 0) {
694 memcpy(pdevn->setup_fn, sf.data, sf.size);
695 pdevn->setup_fn[sf.size] = 0;
696 }
697 code = rinkj_open_profile(pdevn);
698
699 return code;
700 }
701
702 /*
703 * Close device and clean up ICC structures.
704 */
705
706 static int
rinkj_close_device(gx_device * dev)707 rinkj_close_device(gx_device *dev)
708 {
709 rinkj_device * const rdev = (rinkj_device *) dev;
710
711 /* ICC link profile only used (and set) if specified on command line */
712 if (rdev->icc_link != NULL)
713 gscms_release_link(rdev->icc_link);
714 rc_decrement(rdev->link_profile, "rinkj_close_device");
715
716 return gdev_prn_close(dev);
717 }
718
719 /*
720 * This routine will check to see if the color component name match those
721 * that are available amoung the current device's color components.
722 *
723 * Parameters:
724 * dev - pointer to device data structure.
725 * pname - pointer to name (zero termination not required)
726 * nlength - length of the name
727 *
728 * This routine returns a positive value (0 to n) which is the device colorant
729 * number if the name is found. It returns a negative value if not found.
730 */
731 static int
rinkj_get_color_comp_index(gx_device * dev,const char * pname,int name_size,int src_index)732 rinkj_get_color_comp_index(gx_device * dev, const char * pname, int name_size,
733 int src_index)
734 {
735 /* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
736 fixed_colorant_name * pcolor = ((const rinkj_device *)dev)->std_colorant_names;
737 int color_component_number = 0;
738 int i;
739
740 /* Check if the component is in the implied list. */
741 if (pcolor) {
742 while( *pcolor) {
743 if (compare_color_names(pname, name_size, *pcolor, strlen(*pcolor)))
744 return color_component_number;
745 pcolor++;
746 color_component_number++;
747 }
748 }
749
750 /* Check if the component is in the separation names list. */
751 {
752 const gs_separation_names * separations = &((const rinkj_device *)dev)->separation_names;
753 int num_spot = separations->num_names;
754
755 for (i=0; i<num_spot; i++) {
756 if (compare_color_names((const char *)separations->names[i]->data,
757 separations->names[i]->size, pname, name_size)) {
758 return color_component_number;
759 }
760 color_component_number++;
761 }
762 }
763
764 return -1;
765 }
766
767 /* simple linear interpolation */
768 static double
rinkj_graph_lookup(const double * graph_x,const double * graph_y,int n_graph,double x)769 rinkj_graph_lookup (const double *graph_x, const double *graph_y, int n_graph, double x)
770 {
771 int i;
772
773 for (i = 0; i < n_graph - 1; i++)
774 {
775 if (graph_x[i + 1] > x)
776 break;
777 }
778 return graph_y[i] + (x - graph_x[i]) * (graph_y[i + 1] - graph_y[i]) /
779 (graph_x[i + 1] - graph_x[i]);
780 }
781
782 typedef struct rinkj_lutset_s rinkj_lutset;
783 typedef struct rinkj_lutchain_s rinkj_lutchain;
784
785 struct rinkj_lutset_s {
786 const char *plane_names;
787 rinkj_lutchain *lut[MAX_CHAN];
788 };
789
790 struct rinkj_lutchain_s {
791 rinkj_lutchain *next;
792 int n_graph;
793 double *graph_x;
794 double *graph_y;
795 };
796
797 static int
rinkj_add_lut(rinkj_device * rdev,rinkj_lutset * lutset,char plane,gp_file * f)798 rinkj_add_lut(rinkj_device *rdev, rinkj_lutset *lutset, char plane, gp_file *f)
799 {
800 char linebuf[256];
801 rinkj_lutchain *chain;
802 int n_graph;
803 int plane_ix;
804 int i;
805 rinkj_lutchain **pp;
806
807 for (plane_ix = 0; lutset->plane_names[plane_ix]; plane_ix++)
808 if (lutset->plane_names[plane_ix] == plane)
809 break;
810 if (lutset->plane_names[plane_ix] != plane)
811 return -1;
812 pp = &lutset->lut[plane_ix];
813
814 if (gp_fgets(linebuf, sizeof(linebuf), f) == NULL)
815 return -1;
816 if (sscanf(linebuf, "%d", &n_graph) != 1)
817 return -1;
818 if (n_graph < 0 || n_graph > 256)
819 return -1;
820 chain = (rinkj_lutchain *)gs_alloc_bytes(rdev->memory, sizeof(rinkj_lutchain), "rinkj_add_lut");
821 if (chain == NULL) {
822 return -1;
823 }
824 chain->next = NULL;
825 chain->n_graph = n_graph;
826 chain->graph_x = (double *)gs_alloc_bytes(rdev->memory, sizeof(double) * n_graph, "rinkj_add_lut");
827 chain->graph_y = (double *)gs_alloc_bytes(rdev->memory, sizeof(double) * n_graph, "rinkj_add_lut");
828 for (i = 0; i < n_graph; i++) {
829 double x, y;
830
831 if (gp_fgets(linebuf, sizeof(linebuf), f) == NULL)
832 return -1;
833 if (sscanf(linebuf, "%lf %lf", &y, &x) != 2)
834 return -1;
835 chain->graph_x[i] = x / 1.0;
836 chain->graph_y[i] = y / 1.0;
837 }
838 /* add at end of chain */
839 while (*pp) {
840 pp = &((*pp)->next);
841 }
842 *pp = chain;
843 return 0;
844 }
845
846 static int
rinkj_apply_luts(rinkj_device * rdev,RinkjDevice * cmyk_dev,const rinkj_lutset * lutset)847 rinkj_apply_luts(rinkj_device *rdev, RinkjDevice *cmyk_dev, const rinkj_lutset *lutset)
848 {
849 int plane_ix;
850 double lut[256];
851
852 for (plane_ix = 0; plane_ix < 7; plane_ix++) {
853 int i;
854 for (i = 0; i < 256; i++) {
855 double g = i / 255.0;
856 rinkj_lutchain *chain;
857
858 for (chain = lutset->lut[plane_ix]; chain; chain = chain->next) {
859 g = rinkj_graph_lookup(chain->graph_x, chain->graph_y,
860 chain->n_graph, g);
861 }
862 lut[i] = g;
863 }
864 rinkj_screen_eb_set_lut(cmyk_dev, plane_ix, lut);
865 }
866 return 0;
867 }
868
869 static int
rinkj_set_luts(rinkj_device * rdev,RinkjDevice * printer_dev,RinkjDevice * cmyk_dev,const char * config_fn,const RinkjDeviceParams * params)870 rinkj_set_luts(rinkj_device *rdev,
871 RinkjDevice *printer_dev, RinkjDevice *cmyk_dev,
872 const char *config_fn, const RinkjDeviceParams *params)
873 {
874 gp_file *f = gp_fopen(rdev->memory, config_fn, "r");
875 char linebuf[256];
876 char key[256];
877 char *val;
878 rinkj_lutset lutset;
879 int i;
880
881 lutset.plane_names = "KkCMcmY";
882 for (i = 0; i < MAX_CHAN; i++) {
883 lutset.lut[i] = NULL;
884 }
885 for (;;) {
886 if (gp_fgets(linebuf, sizeof(linebuf), f) == NULL)
887 break;
888 for (i = 0; linebuf[i]; i++)
889 if (linebuf[i] == ':') break;
890 if (linebuf[i] != ':') {
891 continue;
892 }
893 memcpy(key, linebuf, i);
894 key[i] = 0;
895 for (i++; linebuf[i] == ' '; i++);
896 val = linebuf + i;
897
898 if (!strcmp(key, "AddLut")) {
899 if_debug1m('r', rdev->memory, "[r]%s", linebuf);
900 rinkj_add_lut(rdev, &lutset, val[0], f);
901 } else if (!strcmp(key, "Dither") || !strcmp(key, "Aspect")) {
902 rinkj_device_set_param_string(cmyk_dev, key, val);
903 } else {
904 rinkj_device_set_param_string(printer_dev, key, val);
905 }
906 }
907
908 gp_fclose(f);
909
910 rinkj_apply_luts(rdev, cmyk_dev, &lutset);
911 /* todo: free lutset contents */
912
913 return 0;
914 }
915
916 static RinkjDevice *
rinkj_init(rinkj_device * rdev,gp_file * file)917 rinkj_init(rinkj_device *rdev, gp_file *file)
918 {
919 RinkjByteStream *bs;
920 RinkjDevice *epson_dev;
921 RinkjDevice *cmyk_dev;
922 RinkjDeviceParams params;
923
924 bs = rinkj_byte_stream_file_new(file);
925 epson_dev = rinkj_epson870_new(bs);
926 cmyk_dev = rinkj_screen_eb_new(epson_dev);
927
928 params.width = rdev->width;
929 params.height = rdev->height;
930 params.n_planes = 7;
931 params.plane_names = "CMYKcmk";
932 rdev->n_planes_out = params.n_planes;
933
934 rinkj_set_luts(rdev, epson_dev, cmyk_dev, rdev->setup_fn, ¶ms);
935
936 rinkj_device_init (cmyk_dev, ¶ms);
937
938 return cmyk_dev;
939 }
940
941 typedef struct rinkj_color_cache_entry_s rinkj_color_cache_entry;
942
943 struct rinkj_color_cache_entry_s {
944 bits32 key;
945 bits32 value;
946 };
947
948 #define RINKJ_CCACHE_LOGSIZE 16
949 #define RINKJ_CCACHE_SIZE (1 << RINKJ_CCACHE_LOGSIZE)
950
951 static inline bits32
rinkj_color_hash(bits32 color)952 rinkj_color_hash(bits32 color)
953 {
954 /* This is somewhat arbitrary */
955 return (color ^ (color >> 10) ^ (color >> 20)) & (RINKJ_CCACHE_SIZE - 1);
956 }
957
958 static int
rinkj_write_image_data(gx_device_printer * pdev,RinkjDevice * cmyk_dev)959 rinkj_write_image_data(gx_device_printer *pdev, RinkjDevice *cmyk_dev)
960 {
961 rinkj_device *rdev = (rinkj_device *)pdev;
962 int raster = gdev_prn_raster(rdev);
963 byte *line;
964 byte *plane_data[MAX_CHAN];
965 const byte *split_plane_data[MAX_CHAN];
966 int xsb;
967 int n_planes;
968 int n_planes_in = pdev->color_info.num_components;
969 int n_planes_out = 4;
970 int i;
971 int y;
972 int code = 0;
973 rinkj_color_cache_entry *cache = NULL;
974
975 n_planes = n_planes_in + rdev->separation_names.num_names;
976 if_debug1m('r', rdev->memory, "[r]n_planes = %d\n", n_planes);
977 xsb = pdev->width;
978 for (i = 0; i < n_planes_out; i++) {
979 plane_data[i] = gs_alloc_bytes(pdev->memory, xsb, "rinkj_write_image_data");
980 if (plane_data[i] == NULL) {
981 while (--i >= 0)
982 gs_free_object(pdev->memory, plane_data[i], "rinkj_write_image_data");
983 return_error(gs_error_VMerror);
984 }
985 }
986 if (rdev->icc_link != NULL) {
987
988 cache = (rinkj_color_cache_entry *)gs_alloc_bytes(pdev->memory, RINKJ_CCACHE_SIZE * sizeof(rinkj_color_cache_entry), "rinkj_write_image_data");
989 if (cache == NULL) {
990 /* i == n_planes_out from above */
991 while (--i >= 0)
992 gs_free_object(pdev->memory, plane_data[i], "rinkj_write_image_data");
993 return_error(gs_error_VMerror);
994 }
995
996 /* Set up cache so that none of the keys will hit. */
997
998 cache[0].key = 1;
999 for (i = 1; i < RINKJ_CCACHE_SIZE; i++)
1000 cache[i].key = 0;
1001
1002 }
1003
1004 /* do CMYK -> CMYKcmk ink split by plane replication */
1005 split_plane_data[0] = plane_data[0];
1006 split_plane_data[1] = plane_data[1];
1007 split_plane_data[2] = plane_data[2];
1008 split_plane_data[3] = plane_data[3];
1009 split_plane_data[4] = plane_data[0];
1010 split_plane_data[5] = plane_data[1];
1011 split_plane_data[6] = plane_data[3];
1012
1013 line = gs_alloc_bytes(pdev->memory, raster, "rinkj_write_image_data");
1014 if (line == NULL)
1015 goto xit;
1016 for (y = 0; y < pdev->height; y++) {
1017 byte *row;
1018 int x;
1019
1020 code = gdev_prn_get_bits(pdev, y, line, &row);
1021 if (code < 0)
1022 goto xit;
1023
1024 if (rdev->icc_link == NULL) {
1025 int rowix = 0;
1026 for (x = 0; x < pdev->width; x++) {
1027 for (i = 0; i < n_planes_in; i++)
1028 plane_data[i][x] = row[rowix + i];
1029 rowix += n_planes;
1030 }
1031 } else if (n_planes == 3) {
1032 int rowix = 0;
1033 for (x = 0; x < pdev->width; x++) {
1034 byte cbuf[4] = {0, 0, 0, 0};
1035 bits32 color;
1036 bits32 hash;
1037 byte vbuf[4];
1038
1039 memcpy(cbuf, row + rowix, 3);
1040 color = ((bits32 *)cbuf)[0];
1041 hash = rinkj_color_hash(color);
1042
1043 if (cache[hash].key != color) {
1044
1045 /* 3 channel to CMYK */
1046 gscms_transform_color((gx_device *)rdev, rdev->icc_link,
1047 &cbuf, &(vbuf), 1);
1048 cache[hash].key = color;
1049 cache[hash].value = ((bits32 *)vbuf)[0];
1050
1051 } else {
1052 ((bits32 *)vbuf)[0] = cache[hash].value;
1053 }
1054 plane_data[0][x] = vbuf[0];
1055 plane_data[1][x] = vbuf[1];
1056 plane_data[2][x] = vbuf[2];
1057 plane_data[3][x] = vbuf[3];
1058 rowix += n_planes;
1059 }
1060 } else if (n_planes == 4) {
1061 for (x = 0; x < pdev->width; x++) {
1062 bits32 color = ((bits32 *)row)[x];
1063 bits32 hash = rinkj_color_hash(color);
1064 byte vbuf[4];
1065
1066 if (cache[hash].key != color) {
1067 byte cbuf[4];
1068
1069 ((bits32 *)cbuf)[0] = color;
1070
1071 /* 4 channel to CMYK */
1072 gscms_transform_color((gx_device *)rdev, rdev->icc_link,
1073 &cbuf, &(vbuf), 1);
1074 cache[hash].key = color;
1075 cache[hash].value = ((bits32 *)vbuf)[0];
1076 } else {
1077 ((bits32 *)vbuf)[0] = cache[hash].value;
1078 }
1079 plane_data[0][x] = vbuf[0];
1080 plane_data[1][x] = vbuf[1];
1081 plane_data[2][x] = vbuf[2];
1082 plane_data[3][x] = vbuf[3];
1083 }
1084 } else if (n_planes == 5) {
1085 int rowix = 0;
1086 for (x = 0; x < pdev->width; x++) {
1087 byte cbuf[4];
1088 bits32 color;
1089 bits32 hash;
1090 byte vbuf[4];
1091 byte spot;
1092 int scolor[4] = { 0x08, 0xc0, 0x80, 0 };
1093
1094 memcpy(cbuf, row + rowix, 4);
1095 color = ((bits32 *)cbuf)[0];
1096 hash = rinkj_color_hash(color);
1097
1098 if (cache[hash].key != color) {
1099
1100 /* Not sure what is going on here. Old
1101 code was still working with 4 to 4
1102 conversion. Replacing with new ICC AMP call */
1103
1104 gscms_transform_color((gx_device *) rdev, rdev->icc_link,
1105 &cbuf, &(vbuf), 1);
1106 cache[hash].key = color;
1107 cache[hash].value = ((bits32 *)vbuf)[0];
1108 } else {
1109 ((bits32 *)vbuf)[0] = cache[hash].value;
1110 }
1111 spot = row[rowix + 4];
1112 if (spot != 0) {
1113 for (i = 0; i < 4; i++) {
1114 int cmyk = vbuf[i], sp_i = spot;
1115 int tmp = (cmyk << 8) - cmyk;
1116 tmp += (sp_i * scolor[i] * (255 - cmyk)) >> 8;
1117 tmp += 0x80;
1118 plane_data[i][x] = (tmp + (tmp >> 8)) >> 8;
1119 }
1120 } else {
1121 plane_data[0][x] = vbuf[0];
1122 plane_data[1][x] = vbuf[1];
1123 plane_data[2][x] = vbuf[2];
1124 plane_data[3][x] = vbuf[3];
1125 }
1126 rowix += n_planes;
1127 }
1128 }
1129
1130 code = rinkj_device_write(cmyk_dev, (const char **)split_plane_data);
1131 }
1132
1133 rinkj_device_write(cmyk_dev, NULL);
1134 xit:
1135 for (i = 0; i < n_planes_in; i++)
1136 gs_free_object(pdev->memory, plane_data[i], "rinkj_write_image_data");
1137 gs_free_object(pdev->memory, line, "rinkj_write_image_data");
1138 gs_free_object(pdev->memory, cache, "rinkj_write_image_data");
1139
1140 return code;
1141 }
1142
1143 static int
rinkj_print_page(gx_device_printer * pdev,gp_file * file)1144 rinkj_print_page(gx_device_printer *pdev, gp_file *file)
1145 {
1146 rinkj_device *rdev = (rinkj_device *)pdev;
1147 int code = 0;
1148 RinkjDevice *cmyk_dev;
1149
1150 if (rdev->setup_fn[0] == 0) {
1151 emprintf(rdev->memory, "Error, SetupFile not defined, output aborted\n");
1152 return 0;
1153 }
1154 cmyk_dev = rinkj_init(rdev, file);
1155 if (cmyk_dev == 0)
1156 return gs_note_error(gs_error_ioerror);
1157
1158 code = rinkj_write_image_data(pdev, cmyk_dev);
1159 return code;
1160 }
1161