1 /* Copyright (C) 1991, 1992, 1993 Aladdin Enterprises. All rights reserved.
2
3 This file is part of Ghostscript.
4
5 Ghostscript is distributed in the hope that it will be useful, but
6 WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
7 to anyone for the consequences of using it or for whether it serves any
8 particular purpose or works at all, unless he says so in writing. Refer
9 to the Ghostscript General Public License for full details.
10
11 Everyone is granted permission to copy, modify and redistribute
12 Ghostscript, but only under the conditions described in the Ghostscript
13 General Public License. A copy of this license is supposed to have been
14 given to you along with Ghostscript so you can know your rights and
15 responsibilities. It should be in a file named COPYING. Among other
16 things, the copyright notice and this notice must be preserved on all
17 copies. */
18
19 /* gdevmjc.c */
20 /* Many parts of this file are copied from gdevcdj.c and gdevescp.c */
21
22 /* EPSON MJ-700V2C colour printer drivers for Ghostscript */
23
24 /*
25 These drivers may also work with EPSON Stylus color printer,
26 though the author hasn't tried.
27 */
28
29 /*
30 Any comments, suggestions, and bug reports are welcomed.
31 The e-mail address of the author Kubo, Hiroshi is
32
33 h-kubo@kuee.kyoto-u.ac.jp
34
35 or
36
37 JBD02172@niftyserve.or.jp
38
39 */
40
41 /*
42
43 Modified by Norihito Ohmori.
44 Support -r / -dBitsPerPixel option.
45 Change -dMicroweave / -dUnidirectional option.
46
47 */
48
49 #include "std.h" /* to stop stdlib.h redefining types */
50 #include <stdlib.h> /* for rand() */
51 #include <limits.h>
52 #include "gdevprn.h"
53 #include "gdevpcl.h"
54 #include "gdevmjc.h"
55
56 #ifndef __WORDSIZE
57 #ifdef __DragonFly__
58 #define __WORDSIZE 64
59 #endif
60 #endif
61
62 /***
63 *** Note: Original driver gdevcdj.c for HP color printer was written
64 *** by a user, George Cameron.
65 ***
66 *** An idea of Kuniyoshi Yoshio to borrow the codes of gdevcdj.c
67 *** for another color printer BJC-600J inspired the author.
68 ***
69 *** Basic control sequences and compression algorithm for ESC/P
70 *** V2 printer are taken from gdevescp.c, written by Richard Brown.
71 ***
72 *** The author Kubo, Hiroshi gathered necessary codes for EPSON
73 *** MJ-700V2C and Sylus color printer in gdevmjc.c.
74 ***/
75
76 /*
77 available drivers
78
79 1. mj700v2c EPSON Mach Jet Color, for ALL CMYK printer
80
81 2. mj500c EPSON Mach Jet Color,
82 CMY or K printer: MJ-500C, MJ-510C,
83 CMY + K printer: MJ-800C (for plane paper),
84 MJ-3000C (for plane paper)
85 3. mj6000c EPSON MJ-830C, MJ-930C, MJ-6000C
86 Use "High Performance Monochrome Mode"
87 4. mj8000c EPSON MJ-8000C
88 Use "High Performance Monochrome Mode"
89 A2 Paper Size Margin is different from mj6000c
90 */
91
92 /*
93 ** Options **
94 *
95 name type description
96
97 Density int Controls densty of dots. 1024 is normal setting
98 for 360dpi.
99 Cyan int Controls depth of Cyan. 1024 is normal setting.
100 Magenta int Controls depth of Magenta. 1024 is normal setting.
101 Yellow int Controls depth of Yellow. 1024 is normal setting.
102 Black int Controls depth of Black. 1024 is normal setting.
103 Unidirectional bool if true, Force unidirectional printing.
104 Microweave bool if true, set micro weave mode.
105 DotSize int Controls the size of the dots. 0 means default,
106 1 means small, 2 means normal.
107
108 ** Examples **
109
110 % gs -sDEVICE=mj700v2c -sOutputFile=tiger.mj gdevmjc.ps tiger.ps
111
112 % gs -sDEVICE=mj700v2c -dDensity=1152 -dCyan=1000 -dMagenta=896 \
113 -dYellow=1024 -dBlack=512 -dUnidirectional -sOutputFile=tiger.mj \
114 gdevmjc.ps tiger.ps
115
116 */
117
118 #define MJ700V2C_PRINT_LIMIT 0.34 /* taken from gdevescp.c */
119
120 /* Margins are left, bottom, right, top. */
121 /* left bottom right top */
122 #define MJ700V2C_MARGINS_A4 0.118f, 0.52f, 0.118f, 0.33465f
123 #define MJ6000C_MARGINS_A2 0.948f, 0.52f, 1.969f, 0.33465f
124 #define MJ8000C_MARGINS_A2 0.194f, 0.52f, 0.194f, 0.33465f
125
126 /* Define bits-per-pixel for generic drivers - default is 24-bit mode */
127 #ifndef BITSPERPIXEL
128 #define BITSPERPIXEL 32
129 #endif
130
131 #define W sizeof(word)
132 #define I sizeof(int)
133
134 /* Printer types */
135 #define MJ700V2C 1
136 #define MJ500C 2
137 #define MJ6000C 3
138 #define MJ8000C 4
139
140 /* No. of ink jets (used to minimise head movements) */
141 #define HEAD_ROWS_MONO 50
142 #define HEAD_ROWS_COLOUR 16
143 /* only for mj700v2c */
144 #define MJ_HEAD_ROWS_MONO 64
145 #define MJ_HEAD_ROWS_COLOUR 16
146
147 /* Colour mapping procedures */
148 static dev_proc_map_rgb_color (gdev_mjc_map_rgb_color);
149 static dev_proc_map_color_rgb (gdev_mjc_map_color_rgb);
150 static dev_proc_encode_color(gdev_mjc_encode_color);
151 static dev_proc_decode_color(gdev_mjc_decode_color);
152
153 /* Print-page, properties and miscellaneous procedures */
154 static dev_proc_open_device(mj700v2c_open);
155 static dev_proc_open_device(mj500c_open);
156 static dev_proc_open_device(mj6000c_open);
157 static dev_proc_open_device(mj8000c_open);
158 static dev_proc_print_page(mj700v2c_print_page);
159 static dev_proc_print_page(mj500c_print_page);
160 static dev_proc_print_page(mj6000c_print_page);
161 static dev_proc_print_page(mj8000c_print_page);
162
163 static dev_proc_get_params(mj_get_params);
164 static dev_proc_put_params(mj700v2c_put_params);
165 static dev_proc_put_params(mj500c_put_params);
166
167 static int mj_open(gx_device * pdev, int ptype);
168 static int mj_put_params(gx_device * pdev, gs_param_list * plist, int ptype);
169 static int mj_print_page(gx_device_printer * pdev, gp_file * prn_stream, int ptype);
170 static void expand_line(word *, int, int, int);
171 static int put_param_int(gs_param_list *, gs_param_name, int *, int, int, int);
172 static void set_bpp(gx_device *, int);
173 static void mj500c_set_bpp(gx_device *, int);
174 static gx_color_index mjc_correct_color(gx_device_printer *, gx_color_index);
175
176 /* The device descriptors */
177 struct gx_device_mj_s {
178 gx_device_common;
179 gx_prn_device_common;
180 int colorcomp; /* 1: grayscale 3: CMY 4: CMYK */
181 int density; /* (color depth) * density/1024 = otuput */
182 int cyan; /* weight for cyan */
183 int magenta; /* weight for magenta */
184 int yellow; /* weight for yellow */
185 int black; /* weight for black */
186 bool direction; /* direction of the head. 1: one way 2: both way */
187 bool microweave; /* Micro weave switch. 0: off 1: on */
188 int dotsize; /* dot size switch.
189 0: default, 1: small, 2: normal */
190 };
191
192 typedef struct gx_device_mj_s gx_device_mj;
193
194 #define mj ((gx_device_mj *) pdev)
195
196 #define prn_hp_colour_device(procs, dev_name, x_dpi, y_dpi, bpp, print_page)\
197 prn_device_body(gx_device_mj, procs, dev_name,\
198 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, x_dpi, y_dpi,\
199 0.118, 0.52, 0.118, 0.33465,\
200 (bpp == 32 ? 4 : (bpp == 1 || bpp == 8) ? 1 : 3), bpp,\
201 (bpp >= 8 ? 255 : 1), (bpp >= 8 ? 255 : bpp > 1 ? 1 : 0),\
202 (bpp >= 8 ? 5 : 2), (bpp >= 8 ? 5 : bpp > 1 ? 2 : 0),\
203 print_page)
204
205 #define mjcmyk_device(procs, dev_name, x_dpi, y_dpi, bpp, print_page,\
206 dns, r, g, b, k, drct, mcrwv, dtsz)\
207 { prn_hp_colour_device(procs, dev_name, x_dpi, y_dpi, bpp, print_page),\
208 (bpp == 32 ? 4 : 1), dns, r, g, b, k, drct, mcrwv, dtsz \
209 }
210
211 #define mjcmy_device(procs, dev_name, x_dpi, y_dpi, bpp, print_page,\
212 dns, r, g, b, k, drct, mcrwv, dtsz)\
213 { prn_hp_colour_device(procs, dev_name, x_dpi, y_dpi, bpp, print_page),\
214 (bpp == 32 ? 3 : 1), dns, r, g, b, k, drct, mcrwv, dtsz \
215 }
216
217 #define mj_colour_procs(proc_colour_open, proc_get_params, proc_put_params)\
218 { proc_colour_open,\
219 gx_default_get_initial_matrix,\
220 gx_default_sync_output,\
221 gdev_prn_output_page,\
222 gdev_prn_close,\
223 gdev_mjc_map_rgb_color,\
224 gdev_mjc_map_color_rgb,\
225 NULL, /* fill_rectangle */\
226 NULL, /* tile_rectangle */\
227 NULL, /* copy_mono */\
228 NULL, /* copy_color */\
229 NULL, /* draw_line */\
230 gx_default_get_bits,\
231 proc_get_params,\
232 proc_put_params,\
233 NULL, /* map_cmyk_color */\
234 NULL, /* get_xfont_procs */\
235 NULL, /* get_xfont_device */\
236 NULL, /* map_rgb_alpha_color */\
237 NULL, /* get_page_device */\
238 NULL, /* get_alpha_bits */\
239 NULL, /* copy_alpha */\
240 NULL, /* get_band */\
241 NULL, /* copy_rop */\
242 NULL, /* fill_path */\
243 NULL, /* stroke_path */\
244 NULL, /* fill_mask */\
245 NULL, /* fill_trapezoid */\
246 NULL, /* fill_parallelogram */\
247 NULL, /* fill_triangle */\
248 NULL, /* draw_thin_line */\
249 NULL, /* begin_image */\
250 NULL, /* image_data */\
251 NULL, /* end_image */\
252 NULL, /* strip_tile_rectangle */\
253 NULL, /* strip_copy_rop, */\
254 NULL, /* get_clipping_box */\
255 NULL, /* begin_typed_image */\
256 NULL, /* get_bits_rectangle */\
257 NULL, /* map_color_rgb_alpha */\
258 NULL, /* create_compositor */\
259 NULL, /* get_hardware_params */\
260 NULL, /* text_begin */\
261 NULL, /* finish_copydevice */\
262 NULL, /* begin_transparency_group */\
263 NULL, /* end_transparency_group */\
264 NULL, /* begin_transparency_mask */\
265 NULL, /* end_transparency_mask */\
266 NULL, /* discard_transparency_layer */\
267 NULL, /* get_color_mapping_procs */\
268 NULL, /* get_color_comp_index */\
269 gdev_mjc_encode_color,\
270 gdev_mjc_decode_color\
271 }
272
273 static gx_device_procs mj700v2c_procs =
274 mj_colour_procs(mj700v2c_open, mj_get_params, mj700v2c_put_params);
275
276 static gx_device_procs mj500c_procs =
277 mj_colour_procs(mj500c_open, mj_get_params, mj500c_put_params);
278
279 static gx_device_procs mj6000c_procs =
280 mj_colour_procs(mj6000c_open, mj_get_params, mj700v2c_put_params);
281
282 static gx_device_procs mj8000c_procs =
283 mj_colour_procs(mj8000c_open, mj_get_params, mj700v2c_put_params);
284
285 gx_device_mj far_data gs_mj700v2c_device =
286 mjcmyk_device(mj700v2c_procs, "mj700v2c", 360, 360, BITSPERPIXEL,
287 mj700v2c_print_page, 1024, 1024, 1024, 1024, 1024, 0, 1, 1);
288
289 gx_device_mj far_data gs_mj500c_device =
290 mjcmy_device(mj500c_procs, "mj500c", 360, 360, BITSPERPIXEL,
291 mj500c_print_page, 1024, 1024, 1024, 1024, 1024, 0, 1, 1);
292
293 gx_device_mj far_data gs_mj6000c_device =
294 mjcmyk_device(mj6000c_procs, "mj6000c", 360, 360, BITSPERPIXEL,
295 mj6000c_print_page, 1024, 1024, 1024, 1024, 1024, 0, 1, 1);
296
297 gx_device_mj far_data gs_mj8000c_device =
298 mjcmyk_device(mj8000c_procs, "mj8000c", 360, 360, BITSPERPIXEL,
299 mj8000c_print_page, 1024, 1024, 1024, 1024, 1024, 0, 1, 1);
300
301 /* Get the paper size code, based on width and height. */
302 static int
gdev_mjc_paper_size(gx_device * dev)303 gdev_mjc_paper_size(gx_device *dev)
304 {
305 int width = (int)dev->MediaSize[0];
306 int height = (int)dev->MediaSize[1];
307
308 if (width == 1190 && height == 1684)
309 return PAPER_SIZE_A2;
310 else
311 return PAPER_SIZE_A4;
312 }
313
314 static int
mj700v2c_open(gx_device * pdev)315 mj700v2c_open(gx_device * pdev)
316 {
317 return mj_open(pdev, MJ700V2C);
318 }
319
320 static int
mj500c_open(gx_device * pdev)321 mj500c_open(gx_device * pdev)
322 {
323 return mj_open(pdev, MJ700V2C);
324 }
325
326 static int
mj6000c_open(gx_device * pdev)327 mj6000c_open(gx_device * pdev)
328 {
329 return mj_open(pdev, MJ700V2C);
330 }
331
332 static int
mj8000c_open(gx_device * pdev)333 mj8000c_open(gx_device * pdev)
334 {
335 return mj_open(pdev, MJ700V2C);
336 }
337
338 /* Open the printer and set up the margins. */
339 static int
mj_open(gx_device * pdev,int ptype)340 mj_open(gx_device *pdev, int ptype)
341 { /* Change the margins if necessary. */
342 int xdpi = (int)pdev->x_pixels_per_inch;
343 int ydpi = (int)pdev->y_pixels_per_inch;
344
345 static const float mj_margin[4] = { MJ700V2C_MARGINS_A4 };
346 static const float mj6000c_a2[4] = { MJ6000C_MARGINS_A2 };
347 static const float mj8000c_a2[4] = { MJ8000C_MARGINS_A2 };
348
349 const float *m;
350
351 int paper_size;
352
353 #if 0
354 /* Set up colour params if put_props has not already done so */
355 if (pdev->color_info.num_components == 0)
356 set_bpp(pdev, pdev->color_info.depth);
357 #endif
358
359 paper_size = gdev_mjc_paper_size(pdev);
360 if (paper_size == PAPER_SIZE_A2 ) {
361 if (ptype == MJ6000C)
362 m = mj6000c_a2;
363 else if (ptype == MJ8000C)
364 m = mj8000c_a2;
365 else
366 m = mj_margin;
367 } else {
368 m = mj_margin;
369 }
370
371 gx_device_set_margins(pdev, m, true);
372
373 if (mj->colorcomp == 3)
374 mj->density = (int)(mj->density * 720 / ydpi) * 1.5;
375 else
376 mj->density = mj->density * 720 / ydpi;
377
378 /* Print Resolution Check */
379 if (!((xdpi == 180 && ydpi == 180) ||
380 (xdpi == 360 && ydpi == 360) ||
381 (xdpi == 720 && ydpi == 720) ||
382 (xdpi == 360 && ydpi == 720) ||
383 (xdpi == 720 && ydpi == 360)))
384 return_error(gs_error_rangecheck);
385
386 return gdev_prn_open(pdev);
387 }
388
389 /* Get properties. In addition to the standard and printer
390 * properties, we supply shingling and depletion parameters,
391 * and control over the bits-per-pixel used in output rendering */
392 /* Added properties for DeskJet 5xxC */
393
394 static int
mj_get_params(gx_device * pdev,gs_param_list * plist)395 mj_get_params(gx_device *pdev, gs_param_list *plist)
396 {
397 int code = gdev_prn_get_params(pdev, plist);
398
399 if ( code < 0 ||
400 (code = param_write_int(plist, "Density", &mj->density)) < 0 ||
401 (code = param_write_int(plist, "Cyan", &mj->cyan)) < 0 ||
402 (code = param_write_int(plist, "Magenta", &mj->magenta)) < 0 ||
403 (code = param_write_int(plist, "Yellow", &mj->yellow)) < 0 ||
404 (code = param_write_int(plist, "Black", &mj->black)) < 0 ||
405 (code = param_write_bool(plist, "Unidirectional", &mj->direction)) < 0 ||
406 (code = param_write_bool(plist, "Microweave", &mj->microweave)) < 0 ||
407 (code = param_write_int(plist, "DotSize", &mj->dotsize)) < 0
408 )
409 return code;
410
411 return code;
412 }
413
414 /* Put properties. */
415 static int
mj700v2c_put_params(gx_device * pdev,gs_param_list * plist)416 mj700v2c_put_params(gx_device *pdev, gs_param_list *plist)
417 {
418 return mj_put_params(pdev, plist, MJ700V2C);
419 }
420
421 static int
mj500c_put_params(gx_device * pdev,gs_param_list * plist)422 mj500c_put_params(gx_device *pdev, gs_param_list *plist)
423 {
424 return mj_put_params(pdev, plist, MJ500C);
425 }
426
427 static int
mj_put_params(gx_device * pdev,gs_param_list * plist,int ptype)428 mj_put_params(gx_device *pdev, gs_param_list *plist, int ptype)
429 {
430 int old_bpp = mj->color_info.depth;
431 int bpp = 0;
432 int code = 0;
433 int density = mj->density;
434 int cyan = mj->cyan;
435 int magenta = mj->magenta;
436 int yellow = mj->yellow;
437 int black = mj->black;
438 bool direction = mj->direction;
439 bool microweave = mj->microweave;
440 int dotsize = mj->dotsize;
441 gs_param_name param_name;
442 int ecode = 0;
443
444 code = put_param_int(plist, "Density", &density, 0, INT_MAX, code);
445 code = put_param_int(plist, "Cyan", &cyan, 0, INT_MAX, code);
446 code = put_param_int(plist, "Magenta", &magenta, 0, INT_MAX, code);
447 code = put_param_int(plist, "Yellow", &yellow, 0, INT_MAX, code);
448 code = put_param_int(plist, "Black", &black, 0, INT_MAX, code);
449 (void) code;
450
451 if ((code = param_read_bool(plist,
452 (param_name = "Unidirectional"),
453 &direction))< 0) {
454 param_signal_error(plist, param_name, ecode = code);
455 }
456
457 if ((code = param_read_bool(plist,
458 (param_name = "Microweave"),
459 µweave))< 0) {
460 param_signal_error(plist, param_name, ecode = code);
461 }
462 if (ecode < 0)
463 return code;
464
465 if (microweave && pdev->x_pixels_per_inch == 180)
466 return_error(gs_error_rangecheck);
467
468 code = put_param_int(plist, "DotSize", &dotsize, 0, 1, code);
469 code = put_param_int(plist, "BitsPerPixel", &bpp, 1, 32, code);
470
471 if ( code < 0 )
472 return code;
473
474 mj->density = density;
475 mj->cyan = cyan;
476 mj->magenta = magenta;
477 mj->yellow = yellow;
478 mj->black = black;
479 mj->direction = direction;
480 mj->microweave = microweave;
481 mj->dotsize = dotsize;
482
483 if ( bpp != 0 ) {
484 if (bpp != 8 && bpp != 32)
485 return_error(gs_error_rangecheck);
486
487 if (ptype == MJ500C)
488 mj500c_set_bpp(pdev, bpp);
489 else
490 set_bpp(pdev, bpp);
491 gdev_prn_put_params(pdev, plist);
492 if ( bpp != old_bpp && pdev->is_open )
493 return gs_closedevice(pdev);
494 return 0;
495 }
496 else
497 return gdev_prn_put_params(pdev, plist);
498 }
499
500 /* ------ Internal routines ------ */
501 /* MACROS FOR DITHERING (we use macros for compact source and faster code) */
502 /* Floyd-Steinberg dithering. Often results in a dramatic improvement in
503 * subjective image quality, but can also produce dramatic increases in
504 * amount of printer data generated and actual printing time!! Mode 9 2D
505 * compression is still useful for fairly flat colour or blank areas but its
506 * compression is much less effective in areas where the dithering has
507 * effectively randomised the dot distribution. */
508
509 #define SHIFT (I * I)
510 #define MINVALUE 0
511 #define MAXVALUE ((256 << SHIFT) - 1)
512 #define THRESHOLD (128 << SHIFT)
513
514 #define FSditherI(inP, out, errP, Err, Bit, Offset)\
515 oldErr = Err;\
516 Err = (*errP + ((Err * 7) >> 4) + (*inP++ << SHIFT));\
517 if (Err > MAXVALUE) Err = MAXVALUE;\
518 else if (Err < MINVALUE) Err = MINVALUE;\
519 if (Err > THRESHOLD) {\
520 out |= Bit;\
521 Err -= MAXVALUE;\
522 }\
523 errP[Offset] += ((Err * 3) >> 4);\
524 *errP++ = ((Err * 5 + oldErr) >> 4);
525
526 #define FSditherD(inP, out, errP, Err, Bit, Offset)\
527 oldErr = Err;\
528 Err = (*--errP + ((Err * 7) >> 4) + (*--inP << SHIFT));\
529 if (Err > MAXVALUE) Err = MAXVALUE;\
530 else if (Err < MINVALUE) Err = MINVALUE;\
531 if (Err > THRESHOLD) {\
532 out |= Bit;\
533 Err -= MAXVALUE;\
534 }\
535 errP[Offset] += ((Err * 3) >> 4);\
536 *errP = ((Err * 5 + oldErr) >> 4);
537
538 #define MATRIX_I(inP, out, Bit, Offset)\
539 if ((*inP++ << 6) > Offset) {\
540 out |= Bit;\
541 }
542
543 #define MATRIX_D(inP, out, Bit, Offset)\
544 if ((*--inP << 6) > Offset) {\
545 out |= Bit;\
546 }
547
548 /* Here we rely on compiler optimisation to remove lines of the form
549 * (if (1 >= 4) {...}, ie. the constant boolean expressions */
550
551 #define FSDline(scan, i, j, plane_size, cErr, mErr, yErr, kErr, cP, mP, yP, kP, n)\
552 {\
553 unsigned short *mat = matrix2 + (lnum & 127)*128;\
554 int x;\
555 (void)cErr; /* Stop compiler warning */\
556 if (scan == 0) { /* going_up */\
557 x = 0;\
558 for (i = 0; i < plane_size; i++) {\
559 byte c, y, m, k, bitmask;\
560 int val;\
561 bitmask = 0x80;\
562 for (c = m = y = k = j = 0; j < 8; j++) {\
563 val = *(mat + (x++ & 127));\
564 if (n >= 4)\
565 {\
566 MATRIX_I(dp, k, bitmask, val);\
567 }\
568 if (n >= 3)\
569 { MATRIX_I(dp, c, bitmask, val);\
570 MATRIX_I(dp, m, bitmask, val);\
571 }\
572 MATRIX_I(dp, y, bitmask, val);\
573 bitmask >>= 1;\
574 }\
575 if (n >= 4)\
576 *kP++ = k;\
577 if (n >= 3)\
578 { *cP++ = c;\
579 *mP++ = m;\
580 }\
581 *yP++ = y;\
582 }\
583 } else { /* going_down */\
584 x = plane_size*8;\
585 for (i = 0; i < plane_size; i++) {\
586 byte c, y, m, k, bitmask;\
587 int val;\
588 bitmask = 0x01;\
589 for (c = m = y = k = j = 0; j < 8; j++) {\
590 val = *(mat + (--x & 127));\
591 MATRIX_D(dp, y, bitmask, val);\
592 if (n >= 3)\
593 { MATRIX_D(dp, m, bitmask, val);\
594 MATRIX_D(dp, c, bitmask, val);\
595 }\
596 if (n >= 4)\
597 { MATRIX_D(dp, k, bitmask, val);\
598 }\
599 bitmask <<= 1;\
600 }\
601 *--yP = y;\
602 if (n >= 3)\
603 { *--mP = m;\
604 *--cP = c;\
605 }\
606 if (n >= 4)\
607 *--kP = k;\
608 }\
609 }\
610 }
611 /* END MACROS FOR DITHERING */
612
613 /* Some convenient shorthand .. */
614 #define x_dpi (pdev->x_pixels_per_inch)
615 #define y_dpi (pdev->y_pixels_per_inch)
616 #define CONFIG_16BIT "\033*v6W\000\003\000\005\006\005"
617 #define CONFIG_24BIT "\033*v6W\000\003\000\010\010\010"
618
619 /* To calculate buffer size as next greater multiple of both parameter and W */
620 #define calc_buffsize(a, b) (((((a) + ((b) * W) - 1) / ((b) * W))) * W)
621
622 /*
623 * Miscellaneous functions for Canon BJC-600J printers in raster command mode.
624 */
625 #define fputshort(n, f) gp_fputc((n)%256,f);gp_fputc((n)/256,f)
626
627 #define row_bytes (img_rows / 8)
628 #define row_words (row_bytes / sizeof(word))
629 #define min_rows (32) /* for optimization of text image printing */
630
631 static int
mj_raster_cmd(int c_id,int in_size,byte * in,byte * buf2,gx_device_printer * pdev,gp_file * prn_stream)632 mj_raster_cmd(int c_id, int in_size, byte* in, byte* buf2,
633 gx_device_printer* pdev, gp_file* prn_stream)
634 {
635 int band_size = 1; /* 1, 8, or 24 */
636
637 byte *out = buf2;
638
639 int width = in_size;
640 int count;
641
642 byte* in_end = in + in_size;
643
644 static char colour_number[] = "\004\001\002\000"; /* color ID for MJ700V2C */
645
646 byte *inp = in;
647 byte *outp = out;
648 register byte *p, *q;
649
650 /* specifying a colour */
651
652 gp_fputs("\033r",prn_stream); /* secape sequence to specify a color */
653 gp_fputc(colour_number[c_id], prn_stream);
654
655 /* end of specifying a colour */
656
657 /* Following codes for compression are borrowed from gdevescp.c */
658
659 for( p = inp, q = inp + 1 ; q < in_end ; ) {
660
661 if( *p != *q ) {
662 p += 2;
663 q += 2;
664 } else {
665
666 /*
667 ** Check behind us, just in case:
668 */
669
670 if( p > inp && *p == *(p-1) )
671 p--;
672
673 /*
674 ** walk forward, looking for matches:
675 */
676
677 for( q++ ; q < in_end && *q == *p ; q++ ) {
678 if( (q-p) >= 128 ) {
679 if( p > inp ) {
680 count = p - inp;
681 while( count > 128 ) {
682 *outp++ = '\177';
683 memcpy(outp, inp, 128); /* data */
684 inp += 128;
685 outp += 128;
686 count -= 128;
687 }
688 *outp++ = (char) (count - 1); /* count */
689 memcpy(outp, inp, count); /* data */
690 outp += count;
691 }
692 *outp++ = '\201'; /* Repeat 128 times */
693 *outp++ = *p;
694 p += 128;
695 inp = p;
696 }
697 }
698
699 if( (q - p) > 2 ) { /* output this sequence */
700 if( p > inp ) {
701 count = p - inp;
702 while( count > 128 ) {
703 *outp++ = '\177';
704 memcpy(outp, inp, 128); /* data */
705 inp += 128;
706 outp += 128;
707 count -= 128;
708 }
709 *outp++ = (char) (count - 1); /* byte count */
710 memcpy(outp, inp, count); /* data */
711 outp += count;
712 }
713 count = q - p;
714 *outp++ = (char) (256 - count + 1);
715 *outp++ = *p;
716 p += count;
717 inp = p;
718 } else /* add to non-repeating data list */
719 p = q;
720 if( q < in_end )
721 q++;
722 }
723 }
724
725 /*
726 ** copy remaining part of line:
727 */
728
729 if( inp < in_end ) {
730
731 count = in_end - inp;
732
733 /*
734 ** If we've had a long run of varying data followed by a
735 ** sequence of repeated data and then hit the end of line,
736 ** it's possible to get data counts > 128.
737 */
738
739 while( count > 128 ) {
740 *outp++ = '\177';
741 memcpy(outp, inp, 128); /* data */
742 inp += 128;
743 outp += 128;
744 count -= 128;
745 }
746
747 *outp++ = (char) (count - 1); /* byte count */
748 memcpy(outp, inp, count); /* data */
749 outp += count;
750 }
751 /*
752 ** Output data:
753 */
754
755 gp_fwrite("\033.\001", 1, 3, prn_stream);
756
757 if(pdev->y_pixels_per_inch == 720)
758 gp_fputc('\005', prn_stream);
759 else if(pdev->y_pixels_per_inch == 180)
760 gp_fputc('\024', prn_stream);
761 else /* pdev->y_pixels_per_inch == 360 */
762 gp_fputc('\012', prn_stream);
763
764 if(pdev->x_pixels_per_inch == 720)
765 gp_fputc('\005', prn_stream);
766 else if(pdev->x_pixels_per_inch == 180)
767 gp_fputc('\024', prn_stream);
768 else /* pdev->x_pixels_per_inch == 360 */
769 gp_fputc('\012', prn_stream);
770
771 gp_fputc(band_size, prn_stream);
772
773 gp_fputc((width << 3) & 0xff, prn_stream);
774 gp_fputc( width >> 5, prn_stream);
775
776 gp_fwrite(out, 1, (outp - out), prn_stream);
777
778 gp_fputc('\r', prn_stream);
779
780 return 0;
781 }
782
783 static int
mj_v_skip(int n,gx_device_printer * pdev,gp_file * stream)784 mj_v_skip(int n, gx_device_printer *pdev, gp_file *stream)
785 {
786 /* This is a kind of magic number. */
787 static const int max_y_step = (256 * 15 + 255);
788
789 int l = n - max_y_step;
790 for (; l > 0; l -= max_y_step) { /* move 256 * 15 + 255 dots at once*/
791 gp_fwrite("\033(v\2\0\xff\x0f", sizeof(byte), 7, stream);
792 }
793 l += max_y_step;
794 /* move to the end. */
795 {
796 int n2 = l / 256;
797 int n1 = l - n2 * 256;
798 gp_fwrite("\033(v\2\0", sizeof(byte) ,5 ,stream);
799 gp_fputc(n1, stream);
800 gp_fputc(n2, stream);
801 gp_fputc('\r', stream);
802 }
803 return 0;
804 }
805
806 /* NOZ */
807
808 static void
bld_barrier(short ** bar,int x)809 bld_barrier( short **bar , int x )
810 {
811 int i , j;
812
813 short t;
814 short *p;
815 short *b;
816 short *dat = barrier_dat + 1;
817
818 p = *bar++ + x + 1;
819
820 for ( i = 0 ; i < 11 ; i++ ) {
821 t = *dat++;
822 if (*p < t )
823 *p = t;
824 p++;
825 }
826
827 for ( j = 0 ; j < 11 ; j++ ) {
828 p = *bar++ + x;
829 b = p;
830
831 t = *dat++;
832 if (*p < t )
833 *p = t;
834 p++;
835 for ( i = 0 ; i < 11 ; i++ ) {
836 t = *dat++;
837 if (*p < t )
838 *p = t;
839 p++;
840
841 if (*(--b) < t )
842 *b = t;
843 }
844 }
845 }
846
847 static void
DifSubK(int d0,short * a4,short * a5)848 DifSubK( int d0 , short *a4 , short *a5 )
849 {
850 /*
851 +---+---+---+
852 | | X |1/2|
853 +---+---+---+
854 |1/4|1/8|1/8|
855 +---+---+---+
856 */
857 *a4++ = 0;
858 d0 >>= 1;
859 *a4 += d0;
860 d0 >>= 1;
861 *(a5-1) += d0;
862 d0 >>= 1;
863 *a5++ += d0;
864 *a5 += d0;
865 }
866
867 /* a4.w , a5.w , */
868 static byte
Xtal(byte bitmask,short d0,int x,short ** bar,short * b1,short * b2)869 Xtal( byte bitmask , short d0 , int x , short **bar , short *b1 , short *b2 )
870 {
871 short *a2;
872
873 if (d0 != 0)
874 d0 += *b1;
875
876 a2 = *bar + x;
877
878 /*dprintf1("[%02X]",*a2);*/
879 if (*a2 < d0) {
880 d0 -= 16400;
881 if (-4096 >= d0) {
882 DifSubK( d0 , b1 , b2 );
883 bld_barrier( bar , x );
884 } else {
885 DifSubK( d0 , b1 , b2 );
886 }
887 return( bitmask );
888 } else {
889 if (d0 > 56)
890 d0 -= 56;
891 DifSubK( d0 , b1 , b2 );
892 return( 0 );
893 }
894 }
895
896 static void
xtal_plane(byte * dp,short * buf[],byte * oP,short ** bar,int plane_size,int xtalbuff_size)897 xtal_plane( byte *dp , short *buf[] , byte *oP , short **bar , int plane_size , int xtalbuff_size )
898 {
899 int i;
900 int j;
901 int x = 0;
902 byte bitmask;
903 byte out;
904 short *p;
905 short *b1 , *b2;
906
907 b1 = buf[0];
908 b2 = buf[1];
909 /*
910 for ( i = 0 ; i < 100 ; i++ ) {
911 dprintf1("[%04X]",bar[0][i]);
912 }
913 dprintf("\n");
914 */
915 for ( i = 0 ; i < plane_size ; i++ ) {
916 bitmask = 0x80;
917 out = 0;
918 for ( j = 0 ; j < 8 ; j++ ) {
919 out |= Xtal( bitmask , (short)(*dp) << 6 , x++ , bar , b1++ , b2++ );
920 dp += 4;
921 bitmask >>= 1;
922 }
923 *oP++ = out;
924 }
925 /*dprintf("\n");*/
926 p = buf[0];
927 /* dprintf("\n"); */
928 buf[0] = buf[1];
929 buf[1] = p;
930
931 p = bar[0];
932 for ( i = 0 ; i < plane_size*8 ; i++ )
933 *p++ = 0;
934
935 /* memset( p, 0, (xtalbuff_size-16) * W);*/
936 p = bar[0];
937 for ( i = 0 ; i <= 14 ; i++ )
938 bar[i] = bar[i+1];
939 bar[15] = p;
940 }
941
942 static int
mj700v2c_print_page(gx_device_printer * pdev,gp_file * prn_stream)943 mj700v2c_print_page(gx_device_printer * pdev, gp_file * prn_stream)
944 {
945 return mj_print_page(pdev, prn_stream, MJ700V2C);
946 }
947
948 static int
mj500c_print_page(gx_device_printer * pdev,gp_file * prn_stream)949 mj500c_print_page(gx_device_printer * pdev, gp_file * prn_stream)
950 {
951 return mj_print_page(pdev, prn_stream, MJ500C);
952 }
953
954 static int
mj6000c_print_page(gx_device_printer * pdev,gp_file * prn_stream)955 mj6000c_print_page(gx_device_printer * pdev, gp_file * prn_stream)
956 {
957 return mj_print_page(pdev, prn_stream, MJ6000C);
958 }
959
960 static int
mj8000c_print_page(gx_device_printer * pdev,gp_file * prn_stream)961 mj8000c_print_page(gx_device_printer * pdev, gp_file * prn_stream)
962 {
963 return mj_print_page(pdev, prn_stream, MJ8000C);
964 }
965
966 /* Send the page to the printer. Compress each scan line. */
967 static int
mj_print_page(gx_device_printer * pdev,gp_file * prn_stream,int ptype)968 mj_print_page(gx_device_printer * pdev, gp_file * prn_stream, int ptype)
969 {
970 /* int line_size = gdev_prn_rasterwidth(pdev, 0); */
971 int line_size = gdev_prn_raster(pdev);
972 int line_size_words = (line_size + W - 1) / W;
973 int num_comps = pdev->color_info.num_components;
974 int bits_per_pixel = pdev->color_info.depth;
975 int storage_bpp = bits_per_pixel;
976 int expanded_bpp = bits_per_pixel;
977 int plane_size, databuff_size;
978 int errbuff_size = 0;
979 int outbuff_size = 0;
980 int scan = 0;
981 int *errors[2];
982 byte *data[4], *plane_data[4][4], *out_data;
983 byte *out_row;
984 word *storage;
985 uint storage_size_words;
986 uint mj_tmp_buf_size;
987 byte* mj_tmp_buf;
988 int xtalbuff_size;
989 short *xtalbuff;
990 short *Cbar[16];
991 short *Mbar[16];
992 short *Ybar[16];
993 short *Kbar[16];
994 short *Cbuf[2];
995 short *Mbuf[2];
996 short *Ybuf[2];
997 short *Kbuf[2];
998
999 /* Tricks and cheats ... */
1000 if (num_comps == 3) num_comps = 4; /* print CMYK */
1001
1002 if (storage_bpp == 8 && num_comps >= 3)
1003 bits_per_pixel = expanded_bpp = 3; /* Only 3 bits of each byte used */
1004
1005 plane_size = calc_buffsize(line_size, storage_bpp);
1006
1007 if (bits_per_pixel == 1) { /* Data printed direct from i/p */
1008 outbuff_size = plane_size * 4; /* but need separate output buffers */
1009 }
1010
1011 if (bits_per_pixel > 4) { /* Error buffer for FS dithering */
1012 expanded_bpp = storage_bpp = /* 8, 24 or 32 bits */
1013 num_comps * 8;
1014 errbuff_size = /* 4n extra values for line ends */
1015 calc_buffsize((plane_size * expanded_bpp + num_comps * 4) * I, 1);
1016 }
1017
1018 databuff_size = plane_size * storage_bpp;
1019
1020 storage_size_words = ((plane_size + plane_size) * num_comps +
1021 databuff_size + errbuff_size + outbuff_size) / W;
1022
1023 /* NOZ */
1024 xtalbuff_size = plane_size*8 + 64;
1025 xtalbuff = (short *) gs_malloc(pdev->memory->non_gc_memory, xtalbuff_size*(16*4+2*4) , W, "mj_colour_print_barrier");
1026 memset(xtalbuff, 0, xtalbuff_size*(16*4+2*4) * W);
1027 {
1028 int i;
1029 short *p = xtalbuff + 16;
1030 for ( i = 0 ; i < 16 ; i++ ) {
1031 Cbar[i] = p;
1032 p += xtalbuff_size;
1033 }
1034 for ( i = 0 ; i < 16 ; i++ ) {
1035 Mbar[i] = p;
1036 p += xtalbuff_size;
1037 }
1038 for ( i = 0 ; i < 16 ; i++ ) {
1039 Ybar[i] = p;
1040 p += xtalbuff_size;
1041 }
1042 for ( i = 0 ; i < 16 ; i++ ) {
1043 Kbar[i] = p;
1044 p += xtalbuff_size;
1045 }
1046 Cbuf[0] = p;
1047 p += xtalbuff_size;
1048 Cbuf[1] = p;
1049 p += xtalbuff_size;
1050 Mbuf[0] = p;
1051 p += xtalbuff_size;
1052 Mbuf[1] = p;
1053 p += xtalbuff_size;
1054 Ybuf[0] = p;
1055 p += xtalbuff_size;
1056 Ybuf[1] = p;
1057 p += xtalbuff_size;
1058 Kbuf[0] = p;
1059 p += xtalbuff_size;
1060 Kbuf[1] = p;
1061 p += xtalbuff_size;
1062 (void) p;
1063 }
1064
1065 storage = (word *) gs_malloc(pdev->memory->non_gc_memory, storage_size_words, W, "mj_colour_print_page");
1066
1067 /* prepare a temporary buffer for mj_raster_cmd */
1068
1069 mj_tmp_buf_size = plane_size;
1070 mj_tmp_buf = (byte *) gs_malloc(pdev->memory->non_gc_memory, mj_tmp_buf_size, W ,"mj_raster_buffer");
1071
1072 #if 0
1073 dprintf1("storage_size_words :%d\n", storage_size_words);
1074 dprintf1("mj_tmp_buf_size :%d\n", mj_tmp_buf_size);
1075 #endif
1076 /*
1077 * The principal data pointers are stored as pairs of values, with
1078 * the selection being made by the 'scan' variable. The function of the
1079 * scan variable is overloaded, as it controls both the alternating
1080 * raster scan direction used in the Floyd-Steinberg dithering and also
1081 * the buffer alternation required for line-difference compression.
1082 *
1083 * Thus, the number of pointers required is as follows:
1084 *
1085 * errors: 2 (scan direction only)
1086 * data: 4 (scan direction and alternating buffers)
1087 * plane_data: 4 (scan direction and alternating buffers)
1088 */
1089
1090 if (storage == NULL || mj_tmp_buf == NULL) /* can't allocate working area */
1091 return_error(gs_error_VMerror);
1092 else {
1093 int i;
1094 byte *p = out_data = out_row = (byte *)storage;
1095 data[0] = data[1] = data[2] = p;
1096 data[3] = p + databuff_size;
1097 if (bits_per_pixel > 1) {
1098 p += databuff_size;
1099 }
1100 if (bits_per_pixel > 4) {
1101 errors[0] = (int *)p + num_comps * 2;
1102 errors[1] = errors[0] + databuff_size;
1103 p += errbuff_size;
1104 }
1105 for (i = 0; i < num_comps; i++) {
1106 plane_data[0][i] = plane_data[2][i] = p;
1107 p += plane_size;
1108 }
1109 for (i = 0; i < num_comps; i++) {
1110 plane_data[1][i] = p;
1111 plane_data[3][i] = p + plane_size;
1112 p += plane_size;
1113 }
1114 if (bits_per_pixel == 1) {
1115 data[1] += databuff_size; /* coincides with plane_data pointers */
1116 data[3] += databuff_size;
1117 }
1118 }
1119
1120 /* Clear temp storage */
1121 memset(storage, 0, storage_size_words * W);
1122
1123 /* Initialize printer. */
1124 {
1125 /** Reset printer, enter graphics mode: */
1126
1127 gp_fwrite("\033@\033(G\001\000\001", sizeof(byte), 8, prn_stream);
1128
1129 /** Micro-weave-Mode */
1130 if (mj->microweave) {
1131 gp_fwrite("\033(i\001\000\001", sizeof(byte), 6, prn_stream);
1132 }
1133 /** Dot-Size define */
1134 if (mj->dotsize) {
1135 gp_fwrite("\033(e\002\000\000\001", sizeof(byte), 7, prn_stream);
1136 }
1137
1138 if (ptype == MJ6000C || ptype == MJ8000C) {
1139 /* Select Monochrome/Color Printing Mode Command */
1140 if (pdev->color_info.depth == 8)
1141 gp_fwrite("\033(K\002\000\000\001", sizeof(byte), 7, prn_stream);
1142 }
1143
1144 if (mj->direction) /* set the direction of the head */
1145 gp_fwrite("\033U\1", 1, 3, prn_stream); /* Unidirectional Printing */
1146 else
1147 gp_fwrite("\033U\0", 1, 3, prn_stream);
1148
1149 #if 0
1150 #ifdef A4
1151 /*
1152 ** After reset, the Stylus is set up for US letter paper.
1153 ** We need to set the page size appropriately for A4 paper.
1154 ** For some bizarre reason the ESC/P2 language wants the bottom
1155 ** margin measured from the *top* of the page:
1156 */
1157
1158 gp_fwrite("\033(U\001\0\n\033(C\002\0t\020\033(c\004\0\0\0t\020",
1159 1, 22, prn_stream);
1160 #endif
1161 #endif
1162
1163 /*
1164 ** Set the line spacing to match the band height:
1165 */
1166
1167 if( pdev->y_pixels_per_inch >= 720 )
1168 gp_fwrite("\033(U\001\0\005\033+\001", sizeof(byte), 9, prn_stream);
1169 else if( pdev->y_pixels_per_inch >= 360 )
1170 gp_fwrite("\033(U\001\0\012\033+\001", sizeof(byte), 9, prn_stream);
1171 else /* 180 dpi */
1172 gp_fwrite("\033(U\001\0\024\033+\002", sizeof(byte), 9, prn_stream);
1173
1174 /* set the length of the page */
1175 gp_fwrite("\033(C\2\0", sizeof(byte), 5, prn_stream);
1176 gp_fputc(((pdev->height) % 256), prn_stream);
1177 gp_fputc(((pdev->height) / 256), prn_stream);
1178 }
1179
1180 #define MOFFSET (pdev->t_margin - MJ700V2C_PRINT_LIMIT) /* Print position */
1181
1182 {
1183 gp_fwrite("\033(V\2\0\0\0",sizeof(byte), 7, prn_stream);
1184 gp_fwrite("\033(v\2\0\0\xff",sizeof(byte), 7, prn_stream);
1185 }
1186
1187 /* Send each scan line in turn */
1188 {
1189 long int lend = (int)(pdev->height -
1190 (dev_t_margin_points(pdev) + dev_b_margin_points(pdev)));
1191 int cErr, mErr, yErr, kErr;
1192 int this_pass, i;
1193 long int lnum;
1194 int num_blank_lines = 0;
1195 int start_rows = (num_comps == 1) ?
1196 HEAD_ROWS_MONO - 1 : HEAD_ROWS_COLOUR - 1;
1197 word rmask = ~(word) 0 << ((-pdev->width * storage_bpp) & (W * 8 - 1));
1198
1199 cErr = mErr = yErr = kErr = 0;
1200
1201 if (bits_per_pixel > 4) { /* Randomly seed initial error buffer */
1202 int *ep = errors[0];
1203 for (i = 0; i < databuff_size; i++) {
1204 *ep++ = (rand() % (MAXVALUE / 2)) - MAXVALUE / 4;
1205 }
1206 }
1207
1208 this_pass = start_rows;
1209
1210 lnum = 0;
1211
1212 /* for Debug */
1213
1214 for (; lnum < lend; lnum++) {
1215 word *data_words = (word *)data[scan];
1216 register word *end_data = data_words + line_size_words;
1217 gx_color_index *p_data;
1218
1219 gdev_prn_copy_scan_lines(pdev, lnum, data[scan], line_size);
1220
1221 /* Mask off 1-bits beyond the line width. */
1222 end_data[-1] &= rmask;
1223
1224 /* Remove trailing 0s. */
1225 while (end_data > data_words && end_data[-1] == 0)
1226 end_data--;
1227 if (end_data == data_words) { /* Blank line */
1228 num_blank_lines++;
1229 continue; /* skip to for (lnum) loop */
1230 }
1231 /* Skip blank lines if any */
1232 if (num_blank_lines > 0 ) {
1233 mj_v_skip(num_blank_lines, pdev, prn_stream);
1234 memset(plane_data[1 - scan][0], 0, plane_size * num_comps);
1235 num_blank_lines = 0;
1236 this_pass = start_rows;
1237 }
1238
1239 /* Correct color depth. */
1240 if (mj->density != 1024 || mj->yellow != 1024 || mj->cyan != 1024
1241 || mj->magenta != 1024 || mj->black != 1024 ) {
1242 for (p_data = (gx_color_index*) data_words; p_data < (gx_color_index *)end_data; p_data++) {
1243 *p_data = mjc_correct_color(pdev, *p_data);
1244 }
1245 }
1246
1247 { /* Printing non-blank lines */
1248 register byte *kP = plane_data[scan + 2][3];
1249 register byte *cP = plane_data[scan + 2][2];
1250 register byte *mP = plane_data[scan + 2][1];
1251 register byte *yP = plane_data[scan + 2][0];
1252 register byte *dp = data[scan + 2];
1253 int i, j;
1254 byte *odp;
1255
1256 if (this_pass)
1257 this_pass--;
1258 else
1259 this_pass = start_rows;
1260
1261 if (expanded_bpp > bits_per_pixel) /* Expand line if required */
1262 expand_line(data_words, line_size, bits_per_pixel, expanded_bpp);
1263
1264 /* In colour modes, we have some bit-shuffling to do before
1265 * we can print the data; in FS mode we also have the
1266 * dithering to take care of. */
1267 switch (expanded_bpp) { /* Can be 1, 3, 8, 24 or 32 */
1268 case 3:
1269 /* Transpose the data to get pixel planes. */
1270 for (i = 0, odp = plane_data[scan][0]; i < databuff_size;
1271 i += 8, odp++) { /* The following is for 16-bit
1272 * machines */
1273 #define spread3(c)\
1274 { 0, c, c*0x100, c*0x101, c*0x10000L, c*0x10001L, c*0x10100L, c*0x10101L }
1275 static word spr40[8] = spread3(0x40);
1276 static word spr08[8] = spread3(8);
1277 static word spr02[8] = spread3(2);
1278 register byte *dp = data[scan] + i;
1279 register word pword =
1280 (spr40[dp[0]] << 1) +
1281 (spr40[dp[1]]) +
1282 (spr40[dp[2]] >> 1) +
1283 (spr08[dp[3]] << 1) +
1284 (spr08[dp[4]]) +
1285 (spr08[dp[5]] >> 1) +
1286 (spr02[dp[6]]) +
1287 (spr02[dp[7]] >> 1);
1288 odp[0] = (byte) (pword >> 16);
1289 odp[plane_size] = (byte) (pword >> 8);
1290 odp[plane_size * 2] = (byte) (pword);
1291 }
1292 break;
1293
1294 case 8:
1295 FSDline(scan, i, j, plane_size, cErr, mErr, yErr, kErr,
1296 cP, mP, yP, kP, 1);
1297 break;
1298 case 24:
1299 FSDline(scan, i, j, plane_size, cErr, mErr, yErr, kErr,
1300 cP, mP, yP, kP, 3);
1301 break;
1302 case 32:
1303 if (scan == 1) {
1304 dp -= plane_size*8*4;
1305 cP -= plane_size;
1306 mP -= plane_size;
1307 yP -= plane_size;
1308 kP -= plane_size;
1309 }
1310 /*
1311 {
1312 byte *p = dp;
1313 int i;
1314 for ( i = 0 ; i < plane_size ; i++ ) {
1315 dprintf4 ( "[%02X%02X%02X%02X]" , p[0] , p[1] , p[2] , p[3] );
1316 p += 4;
1317 }
1318 dprintf("\n");
1319
1320 }
1321 */
1322 /*
1323 FSDline(scan, i, j, plane_size, cErr, mErr, yErr, kErr,
1324 cP, mP, yP, kP, 4);
1325 */
1326 /* NOZ */
1327 xtal_plane( dp++ , Kbuf , kP , Kbar , plane_size , xtalbuff_size );
1328 xtal_plane( dp++ , Cbuf , cP , Cbar , plane_size , xtalbuff_size );
1329 xtal_plane( dp++ , Mbuf , mP , Mbar , plane_size , xtalbuff_size );
1330 xtal_plane( dp++ , Ybuf , yP , Ybar , plane_size , xtalbuff_size );
1331
1332 break;
1333
1334 } /* switch(expanded_bpp) */
1335
1336 /* Make sure all black is in the k plane */
1337 if (num_comps == 4 ) {
1338 if (mj->colorcomp > 3 ) {
1339 register word *kp = (word *)plane_data[scan][3];
1340 register word *cp = (word *)plane_data[scan][2];
1341 register word *mp = (word *)plane_data[scan][1];
1342 register word *yp = (word *)plane_data[scan][0];
1343 if (bits_per_pixel > 4) { /* This has been done as 4 planes */
1344 #if 0
1345 for (i = 0; i < plane_size / W; i++) {
1346 word bits = ~*kp++;
1347 *cp++ &= bits;
1348 *mp++ &= bits;
1349 *yp++ &= bits;
1350 }
1351 #endif
1352 } else { /* This has really been done as 3 planes */
1353 for (i = 0; i < plane_size / W; i++) {
1354 word bits = *cp & *mp & *yp;
1355 *kp++ = bits;
1356 bits = ~bits;
1357 *cp++ &= bits;
1358 *mp++ &= bits;
1359 *yp++ &= bits;
1360 }
1361 }
1362 } else if (mj->colorcomp == 3 ) {
1363 register word *kp = (word *)plane_data[scan][3];
1364 register word *cp = (word *)plane_data[scan][2];
1365 register word *mp = (word *)plane_data[scan][1];
1366 register word *yp = (word *)plane_data[scan][0];
1367 if (bits_per_pixel > 4) { /* This has been done as 4 planes */
1368 for (i = 0; i < plane_size / W; i++) {
1369 word bits = *kp++; /* kp will not be used when printing */
1370 *cp++ |= bits;
1371 *mp++ |= bits;
1372 *yp++ |= bits;
1373 }
1374 } else { /* This has really been done as 3 planes */
1375 }
1376 }
1377 }
1378
1379 /* Transfer raster graphics
1380 * in the order (K), C, M, Y. */
1381 switch (mj->colorcomp) {
1382 case 1:
1383 out_data = (byte*) plane_data[scan][0];
1384 /* 3 for balck */
1385 mj_raster_cmd(3, plane_size, out_data, mj_tmp_buf, pdev, prn_stream);
1386 break;
1387 case 3:
1388 for (i = 3 - 1; i >= 0; i--) {
1389 out_data = (byte*) plane_data[scan][i];
1390 mj_raster_cmd(i, plane_size, out_data, mj_tmp_buf, pdev, prn_stream);
1391 }
1392 break;
1393 default:
1394 for (i = num_comps - 1; i >= 0; i--) {
1395 out_data = (byte*) plane_data[scan][i];
1396 mj_raster_cmd(i, plane_size, out_data, mj_tmp_buf, pdev, prn_stream);
1397 }
1398 break;
1399 } /* Transfer Raster Graphics ... */
1400
1401 {
1402 if ( pdev->y_pixels_per_inch > 360 ) {
1403 gp_fwrite("\033(v\2\0\1\0",sizeof(byte),7, prn_stream);
1404 } else {
1405 gp_fputc('\n', prn_stream);
1406 }
1407 }
1408 scan = 1 - scan; /* toggle scan direction */
1409 } /* Printing non-blank lines */
1410 } /* for lnum ... */
1411 } /* send each scan line in turn */
1412
1413 /* end raster graphics & reset printer */
1414
1415 /* eject page */
1416 {
1417 gp_fputs("\f\033@", prn_stream);
1418 gp_fflush(prn_stream);
1419 }
1420 /* free temporary storage */
1421 gs_free(pdev->memory->non_gc_memory, (char *) storage, storage_size_words, W, "mj_colour_print_page");
1422 gs_free(pdev->memory->non_gc_memory, (char *) mj_tmp_buf, mj_tmp_buf_size, W, "mj_raster_buffer");
1423 gs_free(pdev->memory->non_gc_memory, (char *) xtalbuff , xtalbuff_size*(16*4+2*4) , W, "mj_colour_print_barrier");
1424
1425 return 0;
1426 }
1427
1428 static void
mj_color_correct(gx_color_value * Rptr,gx_color_value * Gptr,gx_color_value * Bptr)1429 mj_color_correct(gx_color_value *Rptr ,gx_color_value *Gptr , gx_color_value *Bptr )
1430 /* R,G,B : 0 to 255 */
1431 {
1432 short R,G,B; /* R,G,B : 0 to 255 */
1433 short C,M,Y; /* C,M,Y : 0 to 1023 */
1434 short H,D,Wa; /* ese-HSV */
1435 long S; /* HSV */
1436
1437 R = *Rptr;
1438 G = *Gptr;
1439 B = *Bptr;
1440 if (R==G) {
1441 if (G==B) { /* R=G=B */
1442 C=M=Y=1023-v_tbl[R];
1443 *Rptr = C;
1444 *Gptr = M;
1445 *Bptr = Y;
1446 return;
1447 }
1448 }
1449
1450 if (R>G) {
1451 if (G>=B) { /* R>G>B */
1452 Wa=R;
1453 D=R-B;
1454 H=(G-B)*256/D;
1455 } else if (R>B) { /* R>B>G */
1456 Wa=R;
1457 D=R-G;
1458 H=1536-(B-G)*256/D;
1459 } else { /* B>R>G */
1460 Wa=B;
1461 D=B-G;
1462 H=1024+(R-G)*256/D;
1463 }
1464 } else {
1465 if (R>B) { /* G>R>B */
1466 Wa=G;
1467 D=G-B;
1468 H=512-(R-B)*256/D;
1469 } else if (G>B) { /* G>B>R */
1470 Wa=G;
1471 D=G-R;
1472 H=512+(B-R)*256/D;
1473 } else { /* B>G>R */
1474 Wa=B;
1475 D=B-R;
1476 H=1024-(G-R)*256/D;
1477 }
1478 }
1479
1480 if(Wa!=0){
1481 if(Wa==D){
1482 Wa=v_tbl[Wa];
1483 D=Wa/4;
1484 } else {
1485 S=((long)D<<16)/Wa;
1486 Wa=v_tbl[Wa];
1487 D= ( ((long)S*Wa)>>18 );
1488 }
1489 }
1490 Wa=1023-Wa;
1491
1492 C=(HtoCMY[H*3 ])*D/256+Wa;
1493 M=(HtoCMY[H*3+1])*D/256+Wa;
1494 Y=(HtoCMY[H*3+2])*D/256+Wa;
1495 if (C<0)
1496 C=0;
1497 if (M<0)
1498 M=0;
1499 if (Y<0)
1500 Y=0;
1501
1502 /* 2019-10-29 this used to be 'if(H>256 && H<1024)', which can then go
1503 beyond bounds of the 512-element grnsep2[]. So have patched up to avoid
1504 this, but without any proper idea about what's going on. */
1505 if(H>256 && H<768){ /* green correct */
1506 short work;
1507 work=(((long)grnsep[M]*(long)grnsep2[H-256])>>16);
1508 C+=work;
1509 Y+=work+work;
1510 M-=work+work;
1511 if(C>1023) C=1023;
1512 if(Y>1023) Y=1023;
1513 }
1514
1515 *Rptr = C;
1516 *Gptr = M;
1517 *Bptr = Y;
1518 }
1519
1520 /*
1521 * Map a r-g-b color to a color index.
1522 * We complement the colours, since we're using cmy anyway, and
1523 * because the buffering routines expect white to be zero.
1524 * Includes colour balancing, following HP recommendations, to try
1525 * and correct the greenish cast resulting from an equal mix of the
1526 * c, m, y, inks by reducing the cyan component to give a truer black.
1527 */
1528 static gx_color_index
gdev_mjc_map_rgb_color(gx_device * pdev,const gx_color_value cv[])1529 gdev_mjc_map_rgb_color(gx_device *pdev, const gx_color_value cv[])
1530 {
1531 gx_color_value r, g, b;
1532
1533 r = cv[0]; g = cv[1]; b = cv[2];
1534 if (gx_color_value_to_byte(r & g & b) == 0xff)
1535 return (gx_color_index)0; /* white */
1536 else {
1537 gx_color_value c = gx_max_color_value - r;
1538 gx_color_value m = gx_max_color_value - g;
1539 gx_color_value y = gx_max_color_value - b;
1540
1541 switch (pdev->color_info.depth) {
1542 case 1:
1543 return ((c | m | y) > gx_max_color_value / 2 ?
1544 (gx_color_index)1 : (gx_color_index)0);
1545 case 8:
1546 if (pdev->color_info.num_components >= 3)
1547 #define gx_color_value_to_1bit(cv) ((cv) >> (gx_color_value_bits - 1))
1548 return (gx_color_value_to_1bit(c) +
1549 (gx_color_value_to_1bit(m) << 1) +
1550 (gx_color_value_to_1bit(y) << 2));
1551 else
1552 #define red_weight 306
1553 #define green_weight 601
1554 #define blue_weight 117
1555 return ((((word)c * red_weight +
1556 (word)m * green_weight +
1557 (word)y * blue_weight)
1558 >> (gx_color_value_bits + 2)));
1559 case 16:
1560 #define gx_color_value_to_5bits(cv) ((cv) >> (gx_color_value_bits - 5))
1561 #define gx_color_value_to_6bits(cv) ((cv) >> (gx_color_value_bits - 6))
1562 return (gx_color_value_to_5bits(y) +
1563 (gx_color_value_to_6bits(m) << 5) +
1564 (gx_color_value_to_5bits(c) << 11));
1565 case 24:
1566 return (gx_color_value_to_byte(y) +
1567 (gx_color_value_to_byte(m) << 8) +
1568 ((word)gx_color_value_to_byte(c) << 16));
1569 case 32:
1570 {
1571 gx_color_value k;
1572 c = gx_color_value_to_byte(r);
1573 m = gx_color_value_to_byte(g);
1574 y = gx_color_value_to_byte(b);
1575
1576 mj_color_correct( &c , &m , &y );
1577
1578 c = esp_dat_c[c];
1579 m = esp_dat_m[m];
1580 y = esp_dat_y[y];
1581
1582 k = c <= m ? (c <= y ? c : y) : (m <= y ? m : y);
1583 k = black_sep[ k >> 4 ] >> 6;
1584 c >>= 6;
1585 m >>= 6;
1586 y >>= 6;
1587
1588 return ( (y - k) + ((m - k) << 8) +
1589 ((word)(c - k) << 16) + ((word)(k) << 24) );
1590 }
1591 }
1592 }
1593 return (gx_color_index)0; /* This never happens */
1594 }
1595
1596 /* Map a color index to a r-g-b color. */
1597 static int
gdev_mjc_map_color_rgb(gx_device * pdev,gx_color_index color,gx_color_value prgb[3])1598 gdev_mjc_map_color_rgb(gx_device *pdev, gx_color_index color,
1599 gx_color_value prgb[3])
1600 {
1601 /* For the moment, we simply ignore any black correction */
1602 switch (pdev->color_info.depth) {
1603 case 1:
1604 prgb[0] = prgb[1] = prgb[2] = -((gx_color_value)color ^ 1);
1605 break;
1606 case 8:
1607 if (pdev->color_info.num_components >= 3)
1608 { gx_color_value c = (gx_color_value)color ^ 7;
1609 prgb[0] = -(c & 1);
1610 prgb[1] = -((c >> 1) & 1);
1611 prgb[2] = -(c >> 2);
1612 }
1613 else
1614 { gx_color_index value = (gx_color_index)color ^ 0xff;
1615 prgb[0] = prgb[1] = prgb[2] = (value << 8) + value;
1616 }
1617 break;
1618 case 16:
1619 { gx_color_index c = (gx_color_index)color ^ 0xffff;
1620 ushort value = c >> 11;
1621 prgb[0] = ((value << 11) + (value << 6) + (value << 1) +
1622 (value >> 4)) >> (16 - gx_color_value_bits);
1623 value = (c >> 6) & 0x3f;
1624 prgb[1] = ((value << 10) + (value << 4) + (value >> 2))
1625 >> (16 - gx_color_value_bits);
1626 value = c & 0x1f;
1627 prgb[2] = ((value << 11) + (value << 6) + (value << 1) +
1628 (value >> 4)) >> (16 - gx_color_value_bits);
1629 }
1630 break;
1631 case 24:
1632 { gx_color_index c = (gx_color_index)color ^ 0xffffff;
1633 prgb[0] = gx_color_value_from_byte(c >> 16);
1634 prgb[1] = gx_color_value_from_byte((c >> 8) & 0xff);
1635 prgb[2] = gx_color_value_from_byte(c & 0xff);
1636 }
1637 break;
1638 case 32:
1639 #define gx_maxcol gx_color_value_from_byte(gx_color_value_to_byte(gx_max_color_value))
1640 { gx_color_value w = gx_maxcol - gx_color_value_from_byte(color >> 24);
1641 prgb[0] = w - gx_color_value_from_byte((color >> 16) & 0xff);
1642 prgb[1] = w - gx_color_value_from_byte((color >> 8) & 0xff);
1643 prgb[2] = w - gx_color_value_from_byte(color & 0xff);
1644 }
1645 break;
1646 }
1647 return 0;
1648 }
1649
1650 /*
1651 * Encode a list of colorant values into a gx_color_index_value.
1652 */
1653 static gx_color_index
gdev_mjc_encode_color(gx_device * dev,const gx_color_value colors[])1654 gdev_mjc_encode_color(gx_device *dev, const gx_color_value colors[])
1655 {
1656 return gdev_mjc_map_rgb_color(dev, colors);
1657 }
1658
1659 /*
1660 * Decode a gx_color_index value back to a list of colorant values.
1661 */
1662 static int
gdev_mjc_decode_color(gx_device * dev,gx_color_index color,gx_color_value * out)1663 gdev_mjc_decode_color(gx_device * dev, gx_color_index color, gx_color_value * out)
1664 {
1665 return gdev_mjc_map_color_rgb(dev, color, out);
1666 }
1667
1668 /*
1669 * Convert and expand scanlines:
1670 *
1671 * (a) 16 -> 24 bit (1-stage)
1672 * (b) 16 -> 32 bit (2-stage)
1673 * or (c) 24 -> 32 bit (1-stage)
1674 */
1675 static void
expand_line(word * line,int linesize,int bpp,int ebpp)1676 expand_line(word *line, int linesize, int bpp, int ebpp)
1677 {
1678 int endline = linesize;
1679 byte *start = (byte *)line;
1680 register byte *in, *out;
1681
1682 if (bpp == 16) /* 16 to 24 (cmy) if required */
1683 { register byte b0, b1;
1684 endline = ((endline + 1) / 2);
1685 in = start + endline * 2;
1686 out = start + (endline *= 3);
1687
1688 while (in > start)
1689 { b0 = *--in;
1690 b1 = *--in;
1691 *--out = (b0 << 3) + ((b0 >> 2) & 0x7);
1692 *--out = (b1 << 5) + ((b0 >> 3) & 0x1c) + ((b1 >> 1) & 0x3);
1693 *--out = (b1 & 0xf8) + (b1 >> 5);
1694 }
1695 }
1696
1697 if (ebpp == 32) /* 24 (cmy) to 32 (cmyk) if required */
1698 { register byte c, m, y, k;
1699 endline = ((endline + 2) / 3);
1700 in = start + endline * 3;
1701 out = start + endline * 4;
1702
1703 while (in > start)
1704 { y = *--in;
1705 m = *--in;
1706 c = *--in;
1707 k = c < m ? (c < y ? c : y) : (m < y ? m : y);
1708 *--out = y - k;
1709 *--out = m - k;
1710 *--out = c - k;
1711 *--out = k;
1712 }
1713 }
1714 }
1715
1716 static int
put_param_int(gs_param_list * plist,gs_param_name pname,int * pvalue,int minval,int maxval,int ecode)1717 put_param_int(gs_param_list *plist, gs_param_name pname, int *pvalue, int minval, int maxval, int ecode)
1718 { int code, value;
1719 switch ( code = param_read_int(plist, pname, &value) )
1720 {
1721 default:
1722 return code;
1723 case 1:
1724 return ecode;
1725 case 0:
1726 if ( value < minval || value > maxval )
1727 param_signal_error(plist, pname, gs_error_rangecheck);
1728 *pvalue = value;
1729 return (ecode < 0 ? ecode : 1);
1730 }
1731 }
1732
1733 static void
set_bpp(gx_device * pdev,int bits_per_pixel)1734 set_bpp(gx_device *pdev, int bits_per_pixel)
1735 { gx_device_color_info *ci = &pdev->color_info;
1736 /* Only valid bits-per-pixel are 1, 3, 8, 16, 24, 32 */
1737 int bpp = bits_per_pixel < 3 ? 1 : bits_per_pixel < 8 ? 3 :
1738 (bits_per_pixel >> 3) << 3;
1739 ci->num_components = ((bpp == 1) || (bpp == 8) ? 1 : 3);
1740 ci->depth = ((bpp > 1) && (bpp < 8) ? 8 : bpp);
1741 ci->max_gray = (bpp >= 8 ? 255 : 1);
1742 ci->max_color = (bpp >= 8 ? 255 : bpp > 1 ? 1 : 0);
1743 ci->dither_grays = (bpp >= 8 ? 5 : 2);
1744 ci->dither_colors = (bpp >= 8 ? 5 : bpp > 1 ? 2 : 0);
1745 mj->colorcomp = (bpp == 8 ? 1 : 4);
1746 }
1747
1748 static void
mj500c_set_bpp(gx_device * pdev,int bits_per_pixel)1749 mj500c_set_bpp(gx_device *pdev, int bits_per_pixel)
1750 { gx_device_color_info *ci = &pdev->color_info;
1751 /* Only valid bits-per-pixel are 1, 3, 8, 16, 24, 32 */
1752 int bpp = bits_per_pixel < 3 ? 1 : bits_per_pixel < 8 ? 3 :
1753 (bits_per_pixel >> 3) << 3;
1754 ci->num_components = ((bpp == 1) || (bpp == 8) ? 1 : 3);
1755 ci->depth = ((bpp > 1) && (bpp < 8) ? 8 : bpp);
1756 ci->max_gray = (bpp >= 8 ? 255 : 1);
1757 ci->max_color = (bpp >= 8 ? 255 : bpp > 1 ? 1 : 0);
1758 ci->dither_grays = (bpp >= 8 ? 5 : 2);
1759 ci->dither_colors = (bpp >= 8 ? 5 : bpp > 1 ? 2 : 0);
1760 mj->colorcomp = (bpp == 8 ? 1 : 3);
1761 }
1762
1763 static gx_color_index
mjc_correct_color(gx_device_printer * pdev,gx_color_index ci)1764 mjc_correct_color(gx_device_printer *pdev, gx_color_index ci)
1765 {
1766 gx_color_index c, m, y, k, co;
1767 gx_color_index k2, k3, k4;
1768 #if __WORDSIZE == 64
1769 gx_color_index c2, m2, y2, k5, k6, k7, k8;
1770 #endif
1771 const uint cmask = 0xff;
1772 uint dn = mj->density;
1773 uint mjy = mj->yellow;
1774 uint mjc = mj->cyan;
1775 uint mjm = mj->magenta;
1776 uint mjb = mj->black;
1777 switch (pdev->color_info.depth) {
1778 case 8:
1779 k = ((ci & cmask) * (mjb)) >> 10;
1780 k = (k < cmask) ? k : cmask;
1781 k2 = (((ci >> 8) & cmask) * (mjb)) >> 10;
1782 k2 = (k2 < cmask) ? k2 : cmask;
1783 k3 = (((ci >> 16) & cmask) * (mjb)) >> 10;
1784 k3 = (k3 < cmask) ? k3 : cmask;
1785 k4 = (((ci >> 24) & cmask) * (mjb)) >> 10;
1786 k4 = (k4 < cmask) ? k4 : cmask;
1787 #if __WORDSIZE == 64
1788 /* This code is ugly... (for 64 bit OS) */
1789 if (sizeof(word) == 8)
1790 {
1791 k5 = (((ci >> 32) & cmask) * (mjb)) >> 10;
1792 k5 = (k5 < cmask) ? k5 : cmask;
1793 k6 = (((ci >> 40) & cmask) * (mjb)) >> 10;
1794 k6 = (k6 < cmask) ? k6 : cmask;
1795 k7 = (((ci >> 48) & cmask) * (mjb)) >> 10;
1796 k7 = (k7 < cmask) ? k7 : cmask;
1797 k8 = (((ci >> 56) & cmask) * (mjb)) >> 10;
1798 k8 = (k8 < cmask) ? k8 : cmask;
1799 co = k + (k2 << 8) + (k3 << 16) + (k4 << 24)
1800 + (k5 << 32) + (k6 << 40) + (k7 << 48) + (k8 << 56);
1801 if (ci != co)
1802 dprintf1("%s\n", "error");
1803 return co;
1804 }
1805 #endif
1806 return k + (k2 << 8) + (k3 << 16) + (k << 24);
1807 break;
1808 case 32:
1809 y = ((ci & cmask) * mjy * dn) >> 20;
1810 y = (y < cmask) ? y : cmask;
1811 m = (((ci >> 8) & cmask) * mjm * dn) >> 20;
1812 m = (m < cmask) ? m : cmask;
1813 c = (((ci >> 16) & cmask) * mjc * dn) >> 20;
1814 c = (c < cmask) ? c : cmask;
1815 k = (((ci >> 24) & cmask) * mjb * dn) >> 20;
1816 k = (k < cmask) ? k : cmask;
1817 #if __WORDSIZE == 64
1818 /* This code is ugly... (for 64 bit OS) */
1819 if (sizeof(word) == 8)
1820 {
1821 y2 = (((ci >> 32) & cmask) * mjy * dn) >> 20;
1822 y2 = (y2 < cmask) ? y2 : cmask;
1823 m2 = (((ci >> 40) & cmask) * mjm * dn) >> 20;
1824 m2 = (m2 < cmask) ? m2 : cmask;
1825 c2 = (((ci >> 48) & cmask) * mjc * dn) >> 20;
1826 c2 = (c2 < cmask) ? c2 : cmask;
1827 k2 = (((ci >> 56) & cmask) * mjb * dn) >> 20;
1828 k2 = (k2 < cmask) ? k2 : cmask;
1829
1830 co = y + (m << 8) + (c << 16) + (k << 24)
1831 + (y2 << 32) + (m2 << 40) + (c2 << 48) + (k2 << 56);
1832
1833 return co;
1834 }
1835 #endif
1836 co = (y + (m << 8) + (c << 16) + (k << 24));
1837 /* dprintf6("%d,%d:%d,%d,%d,%d\n", ci,co, y, m, c, k); */
1838 return co;
1839 /* return (gx_color_value_to_byte(y) +
1840 (gx_color_value_to_byte(m) << 8) +
1841 ((word)gx_color_value_to_byte(c) << 16) +
1842 ((word)gx_color_value_to_byte(k) << 24)); */
1843 break;
1844 }
1845 return ci;
1846 }
1847