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, ¶ms);
961
962 rinkj_device_init (cmyk_dev, ¶ms);
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