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