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, &params);
935 
936     rinkj_device_init (cmyk_dev, &params);
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