diff options
Diffstat (limited to 'devices/gdevcdj.c')
-rw-r--r-- | devices/gdevcdj.c | 3908 |
1 files changed, 3908 insertions, 0 deletions
diff --git a/devices/gdevcdj.c b/devices/gdevcdj.c new file mode 100644 index 00000000..8cf4e2e7 --- /dev/null +++ b/devices/gdevcdj.c @@ -0,0 +1,3908 @@ +/* Copyright (C) 2001-2019 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, + modified or distributed except as expressly authorized under the terms + of the license contained in the file LICENSE in this distribution. + + Refer to licensing information at http://www.artifex.com or contact + Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, + CA 94945, U.S.A., +1(415)492-9861, for further information. +*/ + +/* HP and Canon colour printer drivers */ + +/* + * HP DeskJet 505J Support (dj505j.dev) + * -- taken from dj505j driver for Ghostscript 2.6.1 by Kazunori Asayama + * NEC PICTY180 (PC-PR101J/180) Support (picty180.dev) + * HP LaserJet 4V/4LJ Pro dither Suport (lj4dithp.dev) + * + * Norihito Ohmori <ohmori@p.chiba-u.ac.jp> + * April 15, 1999 + */ + +/**************************************************************** + * The code in this file was contributed by the authors whose names and/or + * e-mail addresses appear below: Aladdin Enterprises takes no + * responsibility for it. In the past, we have tried to keep it working, + * but too many users have made too many "improvements" without regard to + * the overall structure; the last three "improvements" required me to spend + * several hours fixing each one so that the code worked again at all. For + * this reason, no further changes to this file will be accepted. We are + * planning eventually to get these drivers rewritten from scratch. + * + * L. Peter Deutsch + * Aladdin Enterprises + * February 28, 1996 + ****************************************************************/ + +/* + * Change history: + * 2000-08-20 Jonathan Kamens <jik@kamens.brookline.ma.us>: + * change to support printers with different X and Y resolution. + */ + +/* + * Important compilation notes (YA). + * + * You may also try the cdj550cmyk driver after having defined + * USE_CDJ550_CMYK and added the needed definition in devs.mak. Not tried! + * (I have a BJC!) Also note that modes descriptions of CMYK printing + * is done under the BJC section of devices.doc. + * + * CMYK to RGB conversion is made a la GhostScript unless you define + * the preprocessor symbol USE_ADOBE_CMYK_RGB. + * + * Ghostscript: R = (1.0 - C) * (1.0 - K) + * Adobe: R = 1.0 - min(1.0, C + K) + * + * (and similarly for G and B). Ghostscript claims its method achieves + * better results. + * + * For the BJC drivers, define BJC_DEFAULT_CENTEREDAREA if you want to + * have the same top and bottom margins (default to use the tallest + * imageable area available, usually with a top margin smaller than + * the bottom one). Defining USE_RECOMMENDED_MARGINS has the same + * effect and also sets these margins to 12.4 mm. Other compilation + * defines are explained in devices.doc. + * + * You can also define BJC_INIT_800_AS_600 to not use BJC-800-specific code + * in the page initialization sequence (normally not useful to you at all, + * just for my debugging of the driver margins). + * + */ + +#include "std.h" /* to stop stdlib.h redefining types */ +#include <stdlib.h> /* for rand() */ +#include "gdevprn.h" +#include "gdevpcl.h" +#include "gsparam.h" +#include "gsstate.h" + +/* Conversion stuff. */ +#include "gxlum.h" + +/* Canon stuff */ + +#include "gdevbjc.h" + +/*** + *** This file contains multiple drivers. The main body of code, and all + *** but the DesignJet driver, were contributed by George Cameron; + *** please contact g.cameron@biomed.abdn.ac.uk if you have questions. + * 1 - cdj500: HP DeskJet 500C + * 2 - cdj550: HP DeskJet 550C + * 3 - pjxl300: HP PaintJet XL300 + * 4 - pj: HP PaintJet + * 5 - pjxl: HP PaintJet XL + * 6 - declj250: DEC LJ250 + *** The DesignJet 650C driver was contributed by Koert Zeilstra; + *** please contact koert@zen.cais.com if you have questions. + * 7 - dnj650c HP DesignJet 650C + *** The LaserJet 4 driver with dithering was contributed by Eckhard + *** Rueggeberg; please contact eckhard@ts.go.dlr.de if you have questions. + * 8 - lj4dith: HP LaserJet 4 with dithering + *** The ESC/P driver (for Epson ESC/P compatible printers) was written by + *** Yoshio Kuniyoshi <yoshio@nak.math.keio.ac.jp>, but is not maintained at + *** the moment. + * 9 - esc/p: Epson ESC/P-compatible printers + *** The BJC600 driver (which also works for BJC4000) was written first + *** by Yoshio Kuniyoshi <yoshio@nak.math.keio.ac.jp> and later modified by + *** Yves Arrouye <yves.arrouye@usa.net>. The current driver has been + *** completely rewritten by me (YA) for good color handling. + * 10 - bjc600: BJC 600//4000 printers + *** The BJC800 driver is based on the bjc600 one. By YA too. + * 11 - bjc800: BJC 800 printer + * 12 - dj505j: HP DeskJet 505J + * 13 - pixty180: NEC PICTY 180 (PC-PR101J/180) + ***/ + +/* + * All of the HP-like drivers have 8-bit (monochrome), 16-bit and 24-bit + * (colour) and for the DJ 550C 32-bit, (colour, cmyk mode) + * options in addition to the usual 1-bit and 3-bit modes + * It is also possible to set various printer-specific parameters + * from the gs command line, eg. + * + * gs -sDEVICE=cdj550 -dBitsPerPixel=16 -dDepletion=1 -dShingling=2 tiger.eps + * + * Please consult the appropriate section in the devices.doc file for + * further details on all these drivers. + * + * All of the BJC-like drivers have 1-bit and 8-bit monochrome modes, 8-bit, + * 16-bit, 24-bit and 32-bit colour cmyk mode (the 8-bit monochrome mode + * is called "4-bit". If you want to add a CMYK printer, look at the + * bjc6000/bjc800 devices declarations and initialization. + * + * If you want to support different color components for the same depth + * on a non-CMYK printer, look how this is done for CMYK printers in + * cdj_set_bpp. + * + */ + +/* + * This taken from gsdparam.c. I hope it will be useable directly some day. + * + */ + +#define BEGIN_ARRAY_PARAM(pread, pname, pa, psize, e)\ + switch ( ncode = pread(plist, (oname = pname), &pa) )\ + {\ + case 0:\ + if ( pa.size != psize )\ + code = gs_error_rangecheck;\ + else { +/* The body of the processing code goes here. */ +/* If it succeeds, it should do a 'break'; */ +/* if it fails, it should set ecode and fall through. */ +#define END_PARAM(pa, e)\ + }\ + goto e;\ + default:\ + code = ncode;\ +e: param_signal_error(plist, oname, code);\ + case 1:\ + pa.data = 0; /* mark as not filled */\ + } + +static int cdj_param_check_bytes(gs_param_list *, gs_param_name, const byte *, uint, bool); +static int cdj_param_check_float(gs_param_list *, gs_param_name, double, bool); +#define cdj_param_check_string(plist, pname, str, is_defined)\ + cdj_param_check_bytes(plist, pname, (const byte *)(str), strlen(str),\ + is_defined) + +/* + * Drivers stuff. + * + */ + +#define DESKJET_PRINT_LIMIT 0.04 /* 'real' top margin? */ +#define PAINTJET_PRINT_LIMIT 0.0 /* This is a guess.. */ +#define ESC_P_PRINT_LIMIT 0.335 + +/* Margins are left, bottom, right, top. */ +#define DESKJET_MARGINS_LETTER (float)0.25, (float)0.50, (float)0.25, (float)0.167 +#define DESKJET_MARGINS_A4 (float)0.125, (float)0.50, (float)0.143, (float)0.167 +#define DESKJET_505J_MARGINS (float)0.125, (float)0.50, (float)0.125, (float)0.167 +#define DESKJET_505J_MARGINS_COLOR (float)0.125, (float)0.665, (float)0.125, (float)0.167 +#define LJET4_MARGINS (float)0.26, (float)0.0, (float)0.0, (float)0.0 +/* The PaintJet and DesignJet seem to have the same margins */ +/* regardless of paper size. */ +#define PAINTJET_MARGINS (float)0.167, (float)0.167, (float)0.167, (float)0.167 +#define DESIGNJET_MARGINS (float)0.167, (float)0.167, (float)0.167, (float)0.167 + +/* + * With ESC/P commands, BJC-600 can print no more than 8 inches width. + * So it can't use full width of letter size paper. Since non printable + * left side area is 0.134 inch, we set up margins as follows. + * + * Note to readers: the bjc drivers series do *not* use ESC/P commands + * but raster ops. Configuration of these drivers can be done through + * the gdevbjc.h file. + * + */ +#define ESC_P_MARGINS_LETTER (float)0.134, (float)(0.276+0.2), \ + (float)(0.366+0.01), (float)0.335 +#define ESC_P_MARGINS_A4 (float)0.134, (float)(0.276+0.2), \ + (float)(0.166+0.01), (float)0.335 + +/* Define bits-per-pixel for generic drivers - default is 24-bit mode */ +#ifndef BITSPERPIXEL +# define BITSPERPIXEL 24 +#endif + +/* + * The following use of size_of rather than sizeof is required to work + * around a bug in Microsoft Visual C++ 5.0, which considers the THRESHOLD + * value (128 << SHIFT) to be unsigned because SHIFT is unsigned (because + * sizeof() is unsigned). + */ +#define W size_of(word) +#define I size_of(int) + +#define invert_word(v)\ + ((v) >> 24) + (((v) >> 8) & 0xff00L) +\ + (((word)(v) << 8) & 0xff0000L) + ((word)(v) << 24) + +/* Printer types */ +#define DJ500C 0 +#define DJ550C 1 +#define DJ505J 2 +#define PJXL300 3 +#define PJ180 4 +#define PJXL180 5 +#define DECLJ250 6 +#define DNJ650C 7 +#define LJ4DITH 8 +#define ESC_P 9 +#define BJC600 10 +#define BJC800 11 + +/* No. of ink jets (used to minimise head movements) */ +#define HEAD_ROWS_MONO 50 +#define HEAD_ROWS_COLOUR 16 + +/* Colour mapping procedures */ +static dev_proc_map_cmyk_color (gdev_cmyk_map_cmyk_color); +static dev_proc_map_rgb_color (gdev_cmyk_map_rgb_color); + +static dev_proc_map_rgb_color (gdev_pcl_map_rgb_color); +static dev_proc_map_color_rgb (gdev_pcl_map_color_rgb); +static dev_proc_decode_color (gdev_cmyk_map_color_cmyk); + +/* Print-page, parameters and miscellaneous procedures */ +static dev_proc_open_device(dj500c_open); +static dev_proc_open_device(dj550c_open); +static dev_proc_open_device(dj505j_open); +static dev_proc_open_device(dnj650c_open); +static dev_proc_open_device(lj4dith_open); +static dev_proc_open_device(pj_open); +static dev_proc_open_device(pjxl_open); +static dev_proc_open_device(pjxl300_open); +static dev_proc_open_device(escp_open); +static dev_proc_open_device(bjc_open); + +static dev_proc_print_page(declj250_print_page); +static dev_proc_print_page(dj500c_print_page); +static dev_proc_print_page(dj550c_print_page); +static dev_proc_print_page(dj505j_print_page); +static dev_proc_print_page(picty180_print_page); +static dev_proc_print_page(dnj650c_print_page); +static dev_proc_print_page(lj4dith_print_page); +static dev_proc_print_page(lj4dithp_print_page); +static dev_proc_print_page(pj_print_page); +static dev_proc_print_page(pjxl_print_page); +static dev_proc_print_page(pjxl300_print_page); +static dev_proc_print_page(escp_print_page); +static dev_proc_print_page(bjc_print_page); + +static dev_proc_get_params(cdj_get_params); +static dev_proc_get_params(pjxl_get_params); +static dev_proc_get_params(bjc_get_params); +#define ep_get_params cdj_get_params + +static dev_proc_put_params(cdj_put_params); +static dev_proc_put_params(pj_put_params); +static dev_proc_put_params(pjxl_put_params); +static dev_proc_put_params(bjc_put_params); +#define ep_put_params cdj_put_params + +/* The device descriptors */ + +#define gx_prn_colour_device_common \ + gx_prn_device_common; \ + short cmyk; /* 0: not CMYK-capable, > 0: printing CMYK, */ \ + /* < 0 : CMYK-capable, not printing CMYK */ \ + uint default_depth; /* Used only for CMYK-capable printers now. */ \ + uint correction + +typedef struct gx_device_cdj_s gx_device_cdj; +struct gx_device_cdj_s { + gx_device_common; + gx_prn_colour_device_common; + int shingling; /* Interlaced, multi-pass printing */ + int depletion; /* 'Intelligent' dot-removal */ +}; + +typedef struct gx_device_pjxl_s gx_device_pjxl; +struct gx_device_pjxl_s { + gx_device_common; + gx_prn_colour_device_common; + int printqual; /* Mechanical print quality */ + int rendertype; /* Driver or printer dithering control */ +}; + +typedef struct gx_device_hp_s gx_device_hp; +struct gx_device_hp_s { + gx_device_common; + gx_prn_colour_device_common; +}; + +typedef struct gx_device_hp_s gx_device_pj; + +typedef struct gx_device_bjc600_s gx_device_bjc600; +typedef struct gx_device_bjc800_s gx_device_bjc800; + +typedef struct gx_device_bjc800_s gx_device_bjc; + +#define bjc_params_common \ + bool manualFeed; /* Use manual feed */ \ + int mediaType; /* Cf. strings below */ \ + bool mediaWeight_isSet; /* Say if weight is an integer or null */ \ + int mediaWeight; /* Weigth of the media */ \ + int printQuality; /* Cf. strings below */ \ + bool ditheringType; /* Do dithering */ \ + int colorComponents; /* The number of *desired* color comps */ \ + int printColors /* 0: Transparent, \ + 1: C, 2: M, 4: Y, 7: K (Color decomp). \ + if > 8, print in black ink. */ + +typedef struct { + bjc_params_common; + + bool monochromePrint; /* Print with black only */ +} bjc600_params; + +typedef struct { + bjc_params_common; +} bjc_params; + +typedef bjc_params bjc800_params; + +#define gx_bjc_device_common \ + gx_device_common; \ + gx_prn_colour_device_common; \ + int ptype; \ + float printLimit + +struct gx_device_bjc600_s { + gx_bjc_device_common; + bjc600_params bjc_p; +}; +struct gx_device_bjc800_s { + gx_bjc_device_common; + bjc800_params bjc_p; +}; + +typedef struct { + gx_device_common; + gx_prn_colour_device_common; +} gx_device_colour_prn; + +/* Use the cprn_device macro to access generic fields (like cmyk, + default_depth and correction), and specific macros for specific + devices. */ + +#define cprn_device ((gx_device_colour_prn*) pdev) + +#define cdj ((gx_device_cdj *)pdev) +#define pjxl ((gx_device_pjxl *)pdev) +#define pj ((gx_device_pj *)pdev) + +#define bjc ((gx_device_bjc*) pdev) +#define bjc600 ((gx_device_bjc600*) pdev) +#define bjc800 ((gx_device_bjc800*) pdev) + +#define bjcparams (bjc->bjc_p) +#define bjc600params (bjc600->bjc_p) +#define bjc800params (bjc800->bjc_p) + +#define bjcversion(p) (((gx_device_bjc*) pdev)->ptype == BJC800 ? \ + BJC_BJC800_VERSION : BJC_BJC600_VERSION) +#define bjcversionstring(p) (((gx_device_bjc*) pdev)->ptype == BJC800 ? \ + BJC_BJC800_VERSIONSTR : BJC_BJC600_VERSIONSTR) + +#define bjcthickpaper(l) \ + (bjcparams.mediaWeight_isSet && bjcparams.mediaWeight > l) +#define bjc600thickpaper() bjcthickpaper(BJC600_MEDIAWEIGHT_THICKLIMIT) +#define bjc800thickpaper() bjcthickpaper(BJC800_MEDIAWEIGHT_THICKLIMIT) + +/* The basic structure for all printers. Note the presence of the cmyk, depth + and correct fields even if soem are not used by all printers. */ + +#define prn_colour_device_body(dtype, procs, dname, w10, h10, xdpi, ydpi, lm, bm, rm, tm, ncomp, depth, mg, mc, dg, dc, print_page, cmyk, correct)\ + prn_device_body(dtype, procs, dname, w10, h10, xdpi, ydpi, lm, bm, rm, tm, ncomp, depth, mg, mc, dg, dc, print_page), cmyk, depth /* default */, correct + +/* Note: the computation of color_info values here must match */ +/* the computation in the cdj_set_bpp procedure below. */ + +#define prn_hp_colour_device(dtype, procs, dev_name, x_dpi, y_dpi, bpp, print_page, correct)\ + prn_colour_device_body(dtype, procs, dev_name,\ + DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, x_dpi, y_dpi, 0, 0, 0, 0,\ + (bpp == 32 ? 4 : (bpp == 1 || bpp == 8) ? 1 : 3), bpp,\ + (bpp >= 8 ? 255 : 1), (bpp >= 8 ? 255 : bpp > 1 ? 1 : 0),\ + (bpp >= 8 ? 256 : 2), (bpp >= 8 ? 256 : bpp > 1 ? 2 : 0),\ + print_page, 0 /* cmyk */, correct) + +#define prn_cmyk_colour_device(dtype, procs, dev_name, x_dpi, y_dpi, bpp, print_page, correct)\ + prn_colour_device_body(dtype, procs, dev_name,\ + DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, x_dpi, y_dpi, 0, 0, 0, 0,\ + ((bpp == 1 || bpp == 4) ? 1 : 4), bpp,\ + (bpp > 8 ? 255 : 1), (1 << (bpp >> 2)) - 1, /* max_gray, max_color */\ + (bpp > 8 ? 256 : 2), (bpp > 8 ? 256 : bpp > 1 ? 2 : 0),\ + print_page, 1 /* cmyk */, correct) + +#define bjc_device(dtype, p, d, x, y, b, pp, c) \ + prn_cmyk_colour_device(dtype, p, d, x, y, b, pp, c) + +#define cdj_device(procs, dev_name, x_dpi, y_dpi, bpp, print_page, correction, shingling, depletion)\ +{ prn_hp_colour_device(gx_device_cdj, procs, dev_name, x_dpi, y_dpi, bpp, print_page, correction),\ + shingling,\ + depletion\ +} + +#define pjxl_device(procs, dev_name, x_dpi, y_dpi, bpp, print_page, printqual, rendertype)\ +{ prn_hp_colour_device(gx_device_pjxl, procs, dev_name, x_dpi, y_dpi, bpp, print_page, 0), \ + printqual,\ + rendertype\ +} + +#define pj_device(procs, dev_name, x_dpi, y_dpi, bpp, print_page)\ +{ prn_hp_colour_device(gx_device_pj, procs, dev_name, x_dpi, y_dpi, bpp, print_page, 0) } + +#define bjc600_device(procs, dev_name, x_dpi, y_dpi, bpp, print_page, t, mf, mt, mws, mw, pq, dt, cc, pc, mp) \ +{ bjc_device(gx_device_bjc600, procs, dev_name, x_dpi, y_dpi, bpp, print_page, 0),\ + t, 0., { mf, mt, mws, mw, pq, dt, cc, pc, mp }\ +} +#define bjc800_device(procs, dev_name, x_dpi, y_dpi, bpp, print_page, t, mf, mt, mws, mw, pq, dt, cc, pc) \ +{ bjc_device(gx_device_bjc800, procs, dev_name, x_dpi, y_dpi, bpp, print_page, 0),\ + t, 0., { mf, mt, mws, mw, pq, dt, cc, pc }\ +} + +/* Since the print_page doesn't alter the device, this device can print in the background */ +#define hp_colour_procs(proc_colour_open, proc_get_params, proc_put_params) {\ + proc_colour_open,\ + gx_default_get_initial_matrix,\ + gx_default_sync_output,\ + gdev_prn_bg_output_page,\ + gdev_prn_close,\ + gdev_pcl_map_rgb_color,\ + gdev_pcl_map_color_rgb,\ + NULL, /* fill_rectangle */\ + NULL, /* tile_rectangle */\ + NULL, /* copy_mono */\ + NULL, /* copy_color */\ + NULL, /* draw_line */\ + gx_default_get_bits,\ + proc_get_params,\ + proc_put_params\ +} + +/* Since the print_page doesn't alter the device, this device can print in the background */ +#define cmyk_colour_procs(proc_colour_open, proc_get_params, proc_put_params) {\ + proc_colour_open,\ + gx_default_get_initial_matrix,\ + gx_default_sync_output,\ + gdev_prn_bg_output_page,\ + gdev_prn_close,\ + NULL /* map_rgb_color */,\ + NULL /* map_color_rgb */,\ + NULL /* fill_rectangle */,\ + NULL /* tile_rectangle */,\ + NULL /* copy_mono */,\ + NULL /* copy_color */,\ + NULL /* draw_line */,\ + gx_default_get_bits,\ + proc_get_params,\ + proc_put_params,\ + gdev_cmyk_map_cmyk_color,\ + NULL, /* get_xfont_procs */\ + NULL, /* get_xfont_device */\ + NULL, /* map_rgb_alpha_color */\ + NULL, /* get_page_device */\ + NULL, /* get_alpha_bits */\ + NULL, /* copy_alpha */\ + NULL, /* get_band */\ + NULL, /* copy_rop */\ + NULL, /* fill_path */\ + NULL, /* stroke_path */\ + NULL, /* fill_mask */\ + NULL, /* fill_trapezoid */\ + NULL, /* fill_parallelogram */\ + NULL, /* fill_triangle */\ + NULL, /* draw_thin_line */\ + NULL, /* begin_image */\ + NULL, /* image_data */\ + NULL, /* end_image */\ + NULL, /* strip_tile_rectangle */\ + NULL, /* strip_copy_rop */\ + NULL, /* get_clipping_box */\ + NULL, /* begin_typed_image */\ + NULL, /* get_bits_rectangle */\ + NULL, /* map_color_rgb_alpha */\ + NULL, /* create_compositor */\ + NULL, /* get_hardware_params */\ + NULL, /* text_begin */\ + NULL, /* finish_copydevice */\ + NULL, /* begin_transparency_group */\ + NULL, /* end_transparency_group */\ + NULL, /* begin_transparency_mask */\ + NULL, /* end_transparency_mask */\ + NULL, /* discard_transparency_layer */\ + NULL, /* get_color_mapping_procs */\ + NULL, /* get_color_comp_index */\ + gdev_cmyk_map_cmyk_color, /* encode_color */\ + gdev_cmyk_map_color_cmyk /* decode_color */\ +} + +static gx_device_procs cdj500_procs = +hp_colour_procs(dj500c_open, cdj_get_params, cdj_put_params); + +static gx_device_procs cdj550_procs = +hp_colour_procs(dj550c_open, cdj_get_params, cdj_put_params); + +#ifdef USE_CDJ550_CMYK +static gx_device_procs cdj550cmyk_procs = +cmyk_colour_procs(dj550c_open, cdj_get_params, cdj_put_params); +#endif + +static gx_device_procs dj505j_procs = +hp_colour_procs(dj505j_open, cdj_get_params, cdj_put_params); + +static gx_device_procs dnj650c_procs = +hp_colour_procs(dnj650c_open, cdj_get_params, cdj_put_params); + +static gx_device_procs lj4dith_procs = +hp_colour_procs(lj4dith_open, cdj_get_params, cdj_put_params); + +static gx_device_procs pj_procs = +hp_colour_procs(pj_open, gdev_prn_get_params, pj_put_params); + +static gx_device_procs pjxl_procs = +hp_colour_procs(pjxl_open, pjxl_get_params, pjxl_put_params); + +static gx_device_procs pjxl300_procs = +hp_colour_procs(pjxl300_open, pjxl_get_params, pjxl_put_params); + +static gx_device_procs bjc_procs = +cmyk_colour_procs(bjc_open, bjc_get_params, bjc_put_params); + +static gx_device_procs escp_procs = +hp_colour_procs(escp_open, ep_get_params, ep_put_params); + +gx_device_cdj far_data gs_cdjmono_device = +cdj_device(cdj500_procs, "cdjmono", 300, 300, 1, + dj500c_print_page, 4, 0, 1); + +gx_device_cdj far_data gs_cdeskjet_device = +cdj_device(cdj500_procs, "cdeskjet", 300, 300, 24, + dj500c_print_page, 4, 2, 1); + +gx_device_cdj far_data gs_cdjcolor_device = +cdj_device(cdj500_procs, "cdjcolor", 300, 300, 24, + dj500c_print_page, 4, 2, 1); + +gx_device_cdj far_data gs_cdj500_device = +cdj_device(cdj500_procs, "cdj500", 300, 300, BITSPERPIXEL, + dj500c_print_page, 4, 2, 1); + +gx_device_cdj far_data gs_cdj550_device = +cdj_device(cdj550_procs, "cdj550", 300, 300, BITSPERPIXEL, + dj550c_print_page, 0, 2, 1); + +#ifdef USE_CDJ550_CMYK +gx_device_cdj far_data gs_cdj550cmyk_device = { + prn_cmyk_colour_device(cdj550cmyk_procs, "cdj550cmyk", 300, 300, + BITSPERPIXEL, dj550c_print_page, 0), 2, 1 +}; +#endif + +gx_device_cdj far_data gs_picty180_device = +cdj_device(cdj550_procs, "picty180", 300, 300, BITSPERPIXEL, + picty180_print_page, 0, 2, 1); + +gx_device_cdj far_data gs_dj505j_device = +cdj_device(dj505j_procs, "dj505j", 300, 300, 1, + dj505j_print_page, 4, 0, 1); + +gx_device_pj far_data gs_declj250_device = +pj_device(pj_procs, "declj250", 180, 180, BITSPERPIXEL, + declj250_print_page); + +gx_device_cdj far_data gs_dnj650c_device = +cdj_device(dnj650c_procs, "dnj650c", 300, 300, BITSPERPIXEL, + dnj650c_print_page, 0, 2, 1); + +gx_device_cdj far_data gs_lj4dith_device = +cdj_device(lj4dith_procs, "lj4dith", 600, 600, 8, + lj4dith_print_page, 4, 0, 1); + +gx_device_cdj far_data gs_lj4dithp_device = +cdj_device(lj4dith_procs, "lj4dithp", 600, 600, 8, + lj4dithp_print_page, 4, 0, 1); + +gx_device_pj far_data gs_pj_device = +pj_device(pj_procs, "pj", 180, 180, BITSPERPIXEL, + pj_print_page); + +gx_device_pjxl far_data gs_pjxl_device = +pjxl_device(pjxl_procs, "pjxl", 180, 180, BITSPERPIXEL, + pjxl_print_page, 0, 0); + +gx_device_pjxl far_data gs_pjxl300_device = +pjxl_device(pjxl300_procs, "pjxl300", 300, 300, BITSPERPIXEL, + pjxl300_print_page, 0, 0); + +gx_device_cdj far_data gs_escp_device = +cdj_device(escp_procs, "escp", 360, 360, 8, + escp_print_page, 0, 0, 1); + +gx_device_cdj far_data gs_escpc_device = +cdj_device(escp_procs, "escpc", 360, 360, 24, + escp_print_page, 0, 0, 1); + +/* Args of bjc drivers are manualFeed, mediaType, printQuality, printColor, + mediaWeight_isSet, mediaWeight, (monochromePrint) */ + +gx_device_bjc600 far_data gs_bjc600_device = + bjc600_device( + bjc_procs, + BJC_BJC600, + BJC600_DEFAULT_RESOLUTION, + BJC600_DEFAULT_RESOLUTION, + BJC600_DEFAULT_BITSPERPIXEL, + bjc_print_page, + BJC600, + BJC600_DEFAULT_MANUALFEED, + BJC600_DEFAULT_MEDIATYPE, + BJC600_DEFAULT_SETMEDIAWEIGHT, + BJC600_DEFAULT_MEDIAWEIGHT, + BJC600_DEFAULT_PRINTQUALITY, + BJC600_DEFAULT_DITHERINGTYPE, + BJC600_DEFAULT_COLORCOMPONENTS, + BJC600_DEFAULT_PRINTCOLORS, + BJC600_DEFAULT_MONOCHROMEPRINT); + +gx_device_bjc800 far_data gs_bjc800_device = + bjc800_device( + bjc_procs, + BJC_BJC800, + BJC800_DEFAULT_RESOLUTION, + BJC800_DEFAULT_RESOLUTION, + BJC800_DEFAULT_BITSPERPIXEL, + bjc_print_page, + BJC800, + BJC800_DEFAULT_MANUALFEED, + BJC800_DEFAULT_MEDIATYPE, + BJC800_DEFAULT_SETMEDIAWEIGHT, + BJC800_DEFAULT_MEDIAWEIGHT, + BJC800_DEFAULT_PRINTQUALITY, + BJC800_DEFAULT_DITHERINGTYPE, + BJC600_DEFAULT_COLORCOMPONENTS, + BJC800_DEFAULT_PRINTCOLORS); + +/* Forward references */ +static int gdev_pcl_mode1compress(const byte *, const byte *, byte *); +static int hp_colour_open(gx_device *, int); +static int hp_colour_print_page(gx_device_printer *, gp_file *, int); +static int cdj_put_param_int(gs_param_list *, gs_param_name, int *, int, int, int); +static uint gdev_prn_rasterwidth(const gx_device_printer *, int); +static int cdj_put_param_bpp(gx_device *, gs_param_list *, int, int, int); +static int cdj_set_bpp(gx_device *, int, int); +static void cdj_expand_line(word *, int, short, int, int); +static int bjc_fscmyk(byte**, byte*[4][4], int**, int, int); + +/* String parameters manipulation */ + +typedef struct { + const char* p_name; + int p_value; +} stringParamDescription; + +static const byte* paramValueToString(const stringParamDescription*, int); +static int paramStringValue(const stringParamDescription*, + const byte*, int, int*); + +static int put_param_string(gs_param_list*, const byte*, + gs_param_string*, const stringParamDescription*, int *, int); +static int get_param_string(gs_param_list*, const byte*, + gs_param_string*, const stringParamDescription*, int, bool, int); + +/* Open the printer and set up the margins. */ +static int +dj500c_open(gx_device *pdev) +{ return hp_colour_open(pdev, DJ500C); +} + +static int +dj550c_open(gx_device *pdev) +{ return hp_colour_open(pdev, DJ550C); +} + +static int +dj505j_open(gx_device *pdev) +{ return hp_colour_open(pdev, DJ505J); +} + +static int +dnj650c_open(gx_device *pdev) +{ return hp_colour_open(pdev, DNJ650C); +} + +static int +lj4dith_open(gx_device *pdev) +{ return hp_colour_open(pdev, LJ4DITH); +} + +static int +pjxl300_open(gx_device *pdev) +{ return hp_colour_open(pdev, PJXL300); +} + +static int +pj_open(gx_device *pdev) +{ return hp_colour_open(pdev, PJ180); +} + +static int +pjxl_open(gx_device *pdev) +{ return hp_colour_open(pdev, PJXL180); +} + +static int +escp_open(gx_device *pdev) +{ return hp_colour_open(pdev, ESC_P); +} + +static int +bjc_open(gx_device *pdev) +{ return hp_colour_open(pdev, bjc->ptype); +} + +static int +hp_colour_open(gx_device *pdev, int ptype) +{ /* Change the margins if necessary. */ + static const float dj_a4[4] = { DESKJET_MARGINS_A4 }; + static const float dj_letter[4] = { DESKJET_MARGINS_LETTER }; + static const float dj_505j[4] = { DESKJET_505J_MARGINS }; + static const float dj_505jc[4] = { DESKJET_505J_MARGINS_COLOR }; + static const float lj4_all[4] = { LJET4_MARGINS }; + static const float pj_all[4] = { PAINTJET_MARGINS }; + static const float dnj_all[4] = { DESIGNJET_MARGINS }; + static const float ep_a4[4] = { ESC_P_MARGINS_A4 }; + static const float ep_letter[4] = { ESC_P_MARGINS_LETTER }; + + static float bjc_a3[4] = { BJC_MARGINS_A3 }; /* Not const! */ + static float bjc_letter[4] = { BJC_MARGINS_LETTER }; /* Not const! */ + static float bjc_a4[4] = { BJC_MARGINS_A4 }; /* Not const! */ + + const float *m = (float *) 0; + + /* Set up colour params if put_params has not already done so */ + if (pdev->color_info.num_components == 0) + { int code = cdj_set_bpp(pdev, pdev->color_info.depth, + pdev->color_info.num_components); + if ( code < 0 ) + return code; + } + + switch (ptype) { + case DJ500C: + case DJ550C: + m = (gdev_pcl_paper_size(pdev) == PAPER_SIZE_A4 ? dj_a4 : + dj_letter); + break; + case DJ505J: + m = pdev->color_info.num_components > 1 ? dj_505jc : dj_505j; + break; + case DNJ650C: + m = dnj_all; + break; + case LJ4DITH: + m = lj4_all; + break; + case PJ180: + case PJXL300: + case PJXL180: + m = pj_all; + break; + case ESC_P: + m = (gdev_pcl_paper_size(pdev) == PAPER_SIZE_A4 ? ep_a4 : + ep_letter); + break; + case BJC600: + case BJC800: + switch (gdev_pcl_paper_size(pdev)) { + case PAPER_SIZE_LEGAL: + case PAPER_SIZE_LETTER: + m = bjc_letter; + break; + + case PAPER_SIZE_A0: + case PAPER_SIZE_A1: + case PAPER_SIZE_A3: + m = bjc_a3; + break; + + default: + m = bjc_a4; + } + +#ifndef USE_FIXED_MARGINS + if (ptype == BJC800) { + ((float *) m)[1] = (float)BJC_HARD_LOWER_LIMIT; + } +#endif + + bjc->printLimit = m[3]; /* The real hardware limit. */ + +#ifdef BJC_DEFAULT_CENTEREDAREA + if (m[3] < m[1]) { + ((float *) m)[3] = m[1]; /* Top margin = bottom one. */ + } else { + ((float *) m)[1] = m[3]; /* Bottom margin = top one. */ + } +#endif + + break; + + } + if (m != 0) + gx_device_set_margins(pdev, m, true); + return gdev_prn_open(pdev); +} + +/* Added parameters for DeskJet 5xxC */ + +/* Get parameters. In addition to the standard and printer + * parameters, we supply shingling and depletion parameters, + * and control over the bits-per-pixel used in output rendering */ +static int +cdj_get_params(gx_device *pdev, gs_param_list *plist) +{ int code = gdev_prn_get_params(pdev, plist); + if ( code < 0 || + (code = param_write_int(plist, "BlackCorrect", (int *)&cdj->correction)) < 0 || + (code = param_write_int(plist, "Shingling", &cdj->shingling)) < 0 || + (code = param_write_int(plist, "Depletion", &cdj->depletion)) < 0 + ) + return code; + + return code; +} + +/* Put parameters. */ +static int +cdj_put_params(gx_device *pdev, gs_param_list *plist) +{ int correction = cdj->correction; + int shingling = cdj->shingling; + int depletion = cdj->depletion; + int bpp = 0; + int code = 0; + + code = cdj_put_param_int(plist, "BlackCorrect", &correction, 0, 9, code); + code = cdj_put_param_int(plist, "Shingling", &shingling, 0, 2, code); + code = cdj_put_param_int(plist, "Depletion", &depletion, 1, 3, code); + code = cdj_put_param_int(plist, "BitsPerPixel", &bpp, 1, 32, code); + + if ( code < 0 ) + return code; + code = cdj_put_param_bpp(pdev, plist, bpp, bpp, 0); + if ( code < 0 ) + return code; + + cdj->correction = correction; + cdj->shingling = shingling; + cdj->depletion = depletion; + return 0; +} + +/* Added parameters for PaintJet XL and PaintJet XL300 */ + +/* Get parameters. In addition to the standard and printer + * parameters, we supply print_quality and render_type + * parameters, together with bpp control. */ +static int +pjxl_get_params(gx_device *pdev, gs_param_list *plist) +{ int code = gdev_prn_get_params(pdev, plist); + if ( code < 0 || + (code = param_write_int(plist, "PrintQuality", &pjxl->printqual)) < 0 || + (code = param_write_int(plist, "RenderType", &pjxl->rendertype)) < 0 + ) + return code; + + return code; +} + +/* Put parameters. */ +static int +pjxl_put_params(gx_device *pdev, gs_param_list *plist) +{ int printqual = pjxl->printqual; + int rendertype = pjxl->rendertype; + int bpp = 0, real_bpp = 0; + int code = 0; + + code = cdj_put_param_int(plist, "PrintQuality", &printqual, -1, 1, code); + code = cdj_put_param_int(plist, "RenderType", &rendertype, 0, 10, code); + code = cdj_put_param_int(plist, "BitsPerPixel", &bpp, 1, 32, code); + + if ( code < 0 ) + return code; + real_bpp = bpp; + if ( rendertype > 0 ) + { /* If printer is doing the dithering, we must have a + * true-colour mode, ie. 16 or 24 bits per pixel */ + if ( bpp > 0 && bpp < 16 ) + real_bpp = 24; + } + code = cdj_put_param_bpp(pdev, plist, bpp, real_bpp, 0); + if ( code < 0 ) + return code; + + pjxl->printqual = printqual; + pjxl->rendertype = rendertype; + return 0; +} + +/* Added parameters for PaintJet */ + +/* Put parameters. In addition to the standard and printer */ +/* parameters, we allow control of the bits-per-pixel */ +static int +pj_put_params(gx_device *pdev, gs_param_list *plist) +{ int bpp = 0; + int code = cdj_put_param_int(plist, "BitsPerPixel", &bpp, 1, 32, 0); + + if ( code < 0 ) + return code; + return cdj_put_param_bpp(pdev, plist, bpp, bpp, 0); +} + +static stringParamDescription bjc_processColorsStrings[] = { + { "DeviceGray", 1 }, + { "DeviceRGB", 3 }, + { "DeviceCMYK", 4 }, + { 0 } +}; + +static stringParamDescription bjc_mediaTypeStrings[] = { + { "PlainPaper", BJC_MEDIA_PLAINPAPER }, + { "CoatedPaper", BJC_MEDIA_COATEDPAPER }, + { "TransparencyFilm", BJC_MEDIA_TRANSPARENCYFILM }, + { "Envelope", BJC_MEDIA_ENVELOPE }, + { "Card", BJC_MEDIA_CARD}, + { "Other", BJC_MEDIA_OTHER }, + { 0 } +}; + +static stringParamDescription bjc600_printQualityStrings[] = { + { "Normal", 0 }, + { "High", 1 }, + { "Draft", 2 }, + { 0 } +}; + +static stringParamDescription bjc800_printQualityStrings[] = { + { "Normal", 0 }, + { "High", 1 }, + { "Low", 3 }, + { "Draft", 4 }, + { 0 }, +}; + +static stringParamDescription bjc_ditheringTypeStrings[] = { + { "None", BJC_DITHER_NONE }, + { "Floyd-Steinberg", BJC_DITHER_FS }, + { 0 } +}; + +static int +bjc_get_params(gx_device *pdev, gs_param_list *plist) +{ + int code = gdev_prn_get_params(pdev, plist); + int ncode; + + gs_param_string pmedia; + gs_param_string pquality; + gs_param_string dithering; + + if (code < 0) return_error(code); + + if ((ncode = param_write_bool(plist, BJC_OPTION_MANUALFEED, + &bjcparams.manualFeed)) < 0) { + code = ncode; + } + + code = get_param_string(plist, (unsigned char *)BJC_OPTION_MEDIATYPE, &pmedia, + bjc_mediaTypeStrings, bjcparams.mediaType, true, code); + + code = get_param_string(plist, (unsigned char *)BJC_OPTION_PRINTQUALITY, &pquality, + (bjc->ptype == BJC800 ? bjc800_printQualityStrings : + bjc600_printQualityStrings), bjcparams.printQuality, + true, code); + + code = get_param_string(plist, (unsigned char *)BJC_OPTION_DITHERINGTYPE, &dithering, + bjc_ditheringTypeStrings, bjcparams.ditheringType, true, code); + + if ((ncode = param_write_int(plist, BJC_OPTION_PRINTCOLORS, + &bjcparams.printColors)) < 0) { + code = ncode; + } + + if ((ncode = (bjcparams.mediaWeight_isSet ? + param_write_int(plist, BJC_OPTION_MEDIAWEIGHT, + &bjcparams.mediaWeight) : + param_write_null(plist, BJC_OPTION_MEDIAWEIGHT))) < 0) { + code = ncode; + } + + if (bjc->ptype != BJC800) { + if ((ncode = param_write_bool(plist, BJC_OPTION_MONOCHROMEPRINT, + &bjc600params.monochromePrint)) < 0) { + code = ncode; + } + } + + /**/ { + float version; + gs_param_string versionString; + + bool bTrue = true; + + version = bjcversion(pdev); + versionString.data = (byte *)bjcversionstring(pdev); + + versionString.size = strlen((char *)versionString.data); + versionString.persistent = true; + + if ((ncode = param_write_float(plist, BJC_DEVINFO_VERSION, + &version)) < 0) { + code = ncode; + } + if ((ncode = param_write_string(plist, BJC_DEVINFO_VERSIONSTRING, + &versionString)) < 0) { + code = ncode; + } + + if ((ncode = param_write_bool(plist, BJC_DEVINFO_OUTPUTFACEUP, + &bTrue)) < 0) { + code = ncode; + } + } + + return code; +} + +/* Put properties for the bjc drivers. */ + +static int +bjc_put_params(gx_device *pdev, gs_param_list *plist) +{ + int bpp = 0, ccomps = 0; + + int code = 0; + int ncode; + + bool aBool = true; + + const char* oname = (const char*) 0; + + bjc600_params new600Params; + bjc800_params new800Params; + + bjc_params* params; + + gs_param_string pprocesscolors; + gs_param_string pmedia; + gs_param_string pquality; + gs_param_string dithering; + + gs_param_float_array hwra; + + if (bjc->ptype != BJC800) { + new600Params = bjc600params; + params = (bjc_params*) &new600Params; + } else { + new800Params = bjc800params; + params = (bjc_params*) &new800Params; + } + + if ((code = cdj_put_param_int(plist, "BitsPerPixel", + &bpp, 1, 32, code)) != 1) { + bpp = pdev->color_info.depth; + } + + if ((code = put_param_string(plist, (unsigned char *)"ProcessColorModel", + &pprocesscolors, bjc_processColorsStrings, &ccomps, code)) != 1) { + ccomps = pdev->color_info.num_components; + } + + if ((ncode = param_read_bool(plist, oname = BJC_OPTION_MANUALFEED, + ¶ms->manualFeed)) < 0) { + param_signal_error(plist, oname, code = ncode); + } + + code = put_param_string(plist, (unsigned char *)BJC_OPTION_MEDIATYPE, &pmedia, + bjc_mediaTypeStrings, ¶ms->mediaType, code); + + code = cdj_put_param_int(plist, BJC_OPTION_PRINTCOLORS, + ¶ms->printColors, 0, 15, code); + + code = put_param_string(plist, (unsigned char *)BJC_OPTION_PRINTQUALITY, &pquality, + (bjc->ptype == BJC800 ? bjc800_printQualityStrings : + bjc600_printQualityStrings), ¶ms->printQuality, code); + + code = put_param_string(plist, (unsigned char *)BJC_OPTION_DITHERINGTYPE, &dithering, + bjc_ditheringTypeStrings, ¶ms->ditheringType, code); + + switch (ncode = param_read_int(plist, + oname = BJC_OPTION_MEDIAWEIGHT, ¶ms->mediaWeight)) { + case 0: + if (params->mediaWeight <= 0) { + ncode = gs_error_rangecheck; + } else { + params->mediaWeight_isSet = 1; + break; + } + goto mwe; + + default: + if ((ncode = param_read_null(plist, oname)) == 0) { + params->mediaWeight_isSet = 0; + break; + } +mwe: param_signal_error(plist, oname, code = ncode); + + case 1: + break; + } + + if (bjc->ptype != BJC800) { + bjc600_params* params600 = (bjc600_params*) params; + if ((ncode = param_read_bool(plist, + oname = BJC_OPTION_MONOCHROMEPRINT, + ¶ms600->monochromePrint)) < 0) { + param_signal_error(plist, oname, code = ncode); + } + } + + if ((ncode = cdj_param_check_float(plist, BJC_DEVINFO_VERSION, + bjcversion(pdev), true)) < 0) { + code = ncode; + } + if ((ncode = cdj_param_check_string(plist, BJC_DEVINFO_VERSIONSTRING, + bjcversionstring(pdev), true)) < 0) { + code = ncode; + } + + if ((ncode = param_read_bool(plist, oname = BJC_DEVINFO_OUTPUTFACEUP, + &aBool)) < 0) { + param_signal_error(plist, oname, code = ncode); + } else if (aBool != true) { + param_signal_error(plist, oname, code = ncode = gs_error_rangecheck); + } + + /* Check for invalid resolution. The array macros are taken from + gsdparam.c and modified to use oname, ncode and code instead + of param_name, code and ecode respectively. */ + + BEGIN_ARRAY_PARAM(param_read_float_array, "HWResolution", hwra, 2, hwre) + if ( hwra.data[0] <= 0 || hwra.data[1] <= 0 || + hwra.data[0] != hwra.data[1] ) + ncode = gs_error_rangecheck; + else { +#ifdef BJC_STRICT + if (hwra.data[0] != BJC_RESOLUTION_LOW && + hwra.data[0] != BJC_RESOLUTION_NORMAL && + hwra.data[0] != BJC_RESOLUTION_HIGH) { + ncode = gs_error_rangecheck; + } +#else + /* A small hack for checking resolution without logarithms. */ + + /**/ { + int n; + + for (n = 0; n < 8 * sizeof(n); ++n) { + float res = (float)(BJC_RESOLUTION_BASE * n); + + if (res == hwra.data[0]) break; + + if (res > hwra.data[0]) { + ncode = gs_error_rangecheck; + } + } + + if (n == 8 * sizeof(n)) { + ncode = gs_error_rangecheck; + } + } +#endif + if (ncode < 0) { + code = ncode; + } else { + break; + } + } + END_PARAM(hwra, hwre) + + if ((ncode = cdj_put_param_bpp(pdev, plist, bpp, bpp, ccomps)) < 0) { + code = ncode; + } + + if (code < 0) + return code; + + if (bpp == 1) { + params->ditheringType = BJC_DITHER_NONE; + } + + /* Write values that did change */ + + if (bjc->ptype != BJC800) { + bjc600params = new600Params; + } else { + bjc800params = new800Params; + } + + return code; +} + +/* ------ Internal routines ------ */ + +/* The DeskJet500C can compress (mode 9) */ +static int +dj500c_print_page(gx_device_printer * pdev, gp_file * prn_stream) +{ + return hp_colour_print_page(pdev, prn_stream, DJ500C); +} + +/* The DeskJet550C can compress (mode 9) */ +static int +dj550c_print_page(gx_device_printer * pdev, gp_file * prn_stream) +{ + return hp_colour_print_page(pdev, prn_stream, DJ550C); +} + +/* The Picty180C can compress (mode 9) */ +/* This printer need switching mode using PJL */ +static int +picty180_print_page(gx_device_printer * pdev, gp_file * prn_stream) +{ int ret_code; + /* Ensure we're operating in PCL mode */ + gp_fputs("\033%-12345X@PJL ENTER LANGUAGE = PCLSLEEK\n", prn_stream); + ret_code = hp_colour_print_page(pdev, prn_stream, DJ550C); + /* Reenter switch-configured language */ + gp_fputs("\033%-12345X", prn_stream); + return ret_code; +} + +/* The DeskJet505J can compress */ +static int +dj505j_print_page(gx_device_printer * pdev, gp_file * prn_stream) +{ + return hp_colour_print_page(pdev, prn_stream, DJ505J); +} + +/* The DesignJet650C can compress (mode 1) */ +static int +dnj650c_print_page(gx_device_printer * pdev, gp_file * prn_stream) +{ + return hp_colour_print_page(pdev, prn_stream, DNJ650C); +} + +static int +lj4dith_print_page(gx_device_printer * pdev, gp_file * prn_stream) +{ + return hp_colour_print_page(pdev, prn_stream, LJ4DITH); +} + +static int +lj4dithp_print_page(gx_device_printer * pdev, gp_file * prn_stream) +{ int ret_code; + /* Ensure we're operating in PCL mode */ + gp_fputs("\033%-12345X@PJL\r\n@PJL ENTER LANGUAGE = PCL\r\n", prn_stream); + ret_code = hp_colour_print_page(pdev, prn_stream, LJ4DITH); + /* Reenter switch-configured language */ + gp_fputs("\033%-12345X", prn_stream); + return ret_code; +} + +/* The PJXL300 can compress (modes 2 & 3) */ +static int +pjxl300_print_page(gx_device_printer * pdev, gp_file * prn_stream) +{ int ret_code; + /* Ensure we're operating in PCL mode */ + gp_fputs("\033%-12345X@PJL enter language = PCL\n", prn_stream); + ret_code = hp_colour_print_page(pdev, prn_stream, PJXL300); + /* Reenter switch-configured language */ + gp_fputs("\033%-12345X", prn_stream); + return ret_code; +} + +/* The PaintJet XL can compress (modes 2 & 3) */ +static int +pjxl_print_page(gx_device_printer * pdev, gp_file * prn_stream) +{ + return hp_colour_print_page(pdev, prn_stream, PJXL180); +} + +/* The PaintJet can compress (mode 1) */ +static int +pj_print_page(gx_device_printer * pdev, gp_file * prn_stream) +{ + return hp_colour_print_page(pdev, prn_stream, PJ180); +} + +/* The LJ250 can compress (mode 1) */ +static int +declj250_print_page(gx_device_printer * pdev, gp_file * prn_stream) +{ int ret_code; + gp_fputs("\033%8", prn_stream); /* Enter PCL emulation mode */ + ret_code = hp_colour_print_page(pdev, prn_stream, DECLJ250); + gp_fputs("\033%@", prn_stream); /* Exit PCL emulation mode */ + return ret_code; +} + +/* The BJC-600 cannot compress w/o raster image commands. */ +static int +escp_print_page(gx_device_printer * pdev, gp_file * prn_stream) +{ + return hp_colour_print_page(pdev, prn_stream, ESC_P); +} + +/* The BJC-600 can compress w/ raster image commands. */ +static int +bjc_print_page(gx_device_printer * pdev, gp_file * prn_stream) +{ + return hp_colour_print_page(pdev, prn_stream, bjc->ptype); +} + +/* MACROS FOR DITHERING (we use macros for compact source and faster code) */ +/* Floyd-Steinberg dithering. Often results in a dramatic improvement in + * subjective image quality, but can also produce dramatic increases in + * amount of printer data generated and actual printing time!! Mode 9 2D + * compression is still useful for fairly flat colour or blank areas but its + * compression is much less effective in areas where the dithering has + * effectively randomised the dot distribution. */ + +#define SHIFT ((I * 8) - 13) +#define RSHIFT ((I * 8) - 16) +#define RANDOM (((rand() << RSHIFT) % (MAXVALUE / 2)) - MAXVALUE / 4); +#define MINVALUE 0 +#define MAXVALUE (255 << SHIFT) +#define THRESHOLD (128 << SHIFT) +#define C 8 + +#define FSdither(inP, out, errP, Err, Bit, Offset, Element)\ + oldErr = Err;\ + Err = (errP[Element] + ((Err * 7 + C) >> 4) + ((int)inP[Element] << SHIFT));\ + if (Err > THRESHOLD) {\ + out |= Bit;\ + Err -= MAXVALUE;\ + }\ + errP[Element + Offset] += ((Err * 3 + C) >> 4);\ + errP[Element] = ((Err * 5 + oldErr + C) >> 4); + +/* Here we rely on compiler optimisation to remove lines of the form + * (if (1 >= 4) {...}, ie. the constant boolean expressions */ + +/* The original code is in the #else part. Since by default NEW_DITHER + is not defined, the old code is used. No enhancement is visible for the + bjc600 drivers with the new code, anyway :-( */ + +#ifdef NEW_DITHER + +#define FSDline(scan, i, j, plane_size, cErr, mErr, yErr, kErr, cP, mP, yP, kP, n)\ +{\ + if (scan == 0) { /* going_up */\ + for (i = 0; i < plane_size; i++) {\ + byte c, y, m, k, bitmask;\ + int oldErr;\ + bitmask = 0x80;\ + for (c = m = y = k = 0; bitmask != 0; bitmask >>= 1) {\ + if (n >= 4) {\ + FSdither(dp, k, ep, kErr, bitmask, -n, 0);\ + }\ + if (n >= 3) {\ + FSdither(dp, c, ep, cErr, bitmask, -n, n - 3);\ + FSdither(dp, m, ep, mErr, bitmask, -n, n - 2);\ + }\ + FSdither(dp, y, ep, yErr, bitmask, -n, n - 1);\ + dp += n, ep += n;\ + }\ + if (n >= 4)\ + *kP++ = k;\ + if (n >= 3) {\ + *cP++ = c;\ + *mP++ = m;\ + }\ + *yP++ = y;\ + }\ + } else { /* going_down */\ + for (i = 0; i < plane_size; i++) {\ + byte c, y, m, k, bitmask;\ + int oldErr;\ + bitmask = 0x01;\ + for (c = m = y = k = 0; bitmask != 0; bitmask <<= 1) {\ + dp -= n, ep -= n;\ + FSdither(dp, y, ep, yErr, bitmask, n, n - 1);\ + if (n >= 3) {\ + FSdither(dp, m, ep, mErr, bitmask, n, n - 2);\ + FSdither(dp, c, ep, cErr, bitmask, n, n - 3);\ + }\ + if (n >= 4) {\ + FSdither(dp, k, ep, kErr, bitmask, n, 0);\ + }\ + }\ + *--yP = y;\ + if (n >= 3)\ + { *--mP = m;\ + *--cP = c;\ + }\ + if (n >= 4)\ + *--kP = k;\ + }\ + }\ +} + +#else + +#define FSDline(scan, i, j, plane_size, cErr, mErr, yErr, kErr, cP, mP, yP, kP, n)\ +{\ + if (scan == 0) { /* going_up */\ + for (i = 0; i < plane_size; i++) {\ + byte c, y, m, k, bitmask;\ + int oldErr;\ + bitmask = 0x80;\ + for (c = m = y = k = 0; bitmask != 0; bitmask >>= 1) {\ + if (n >= 4) {\ + if (*dp) {\ + FSdither(dp, k, ep, kErr, bitmask, -n, 0);\ + cErr = mErr = yErr = 0;\ + } else {\ + FSdither(dp, c, ep, cErr, bitmask, -n, n - 3);\ + FSdither(dp, m, ep, mErr, bitmask, -n, n - 2);\ + FSdither(dp, y, ep, yErr, bitmask, -n, n - 1);\ + }\ + } else {\ + if (n >= 3) {\ + FSdither(dp, c, ep, cErr, bitmask, -n, n - 3);\ + FSdither(dp, m, ep, mErr, bitmask, -n, n - 2);\ + }\ + FSdither(dp, y, ep, yErr, bitmask, -n, n - 1);\ + }\ + dp += n, ep += n;\ + }\ + if (n >= 4)\ + *kP++ = k;\ + if (n >= 3) {\ + *cP++ = c;\ + *mP++ = m;\ + }\ + *yP++ = y;\ + }\ + } else { /* going_down */\ + for (i = 0; i < plane_size; i++) {\ + byte c, y, m, k, bitmask;\ + int oldErr;\ + bitmask = 0x01;\ + for (c = m = y = k = 0; bitmask != 0; bitmask <<= 1) {\ + dp -= n, ep -= n;\ + if (n >= 4) {\ + if (*dp) {\ + FSdither(dp, k, ep, kErr, bitmask, n, 0);\ + cErr = mErr = yErr = 0;\ + } else {\ + FSdither(dp, y, ep, yErr, bitmask, n, n - 1);\ + FSdither(dp, m, ep, mErr, bitmask, n, n - 2);\ + FSdither(dp, c, ep, cErr, bitmask, n, n - 3);\ + }\ + } else {\ + FSdither(dp, y, ep, yErr, bitmask, n, n - 1);\ + if (n >= 3) {\ + FSdither(dp, m, ep, mErr, bitmask, n, n - 2);\ + FSdither(dp, c, ep, cErr, bitmask, n, n - 3);\ + }\ + }\ + }\ + *--yP = y;\ + if (n >= 3)\ + { *--mP = m;\ + *--cP = c;\ + }\ + if (n >= 4)\ + *--kP = k;\ + }\ + }\ +} + +#endif + +/* END MACROS FOR DITHERING */ + +#define CPbit(inP, out, Bit, Element)\ + if (inP[Element]) {\ + out |= Bit;\ + } + +#define COPYline(scan, i, j, plane_size, cP, mP, yP, kP, n)\ +{\ + if (scan == 0) { /* going_up */\ + for (i = 0; i < plane_size; i++) {\ + byte c, y, m, k, bitmask;\ + bitmask = 0x80;\ + for (c = m = y = k = 0; bitmask != 0; bitmask >>= 1) {\ + if (n >= 4) {\ + CPbit(dp, k, bitmask, 0);\ + } \ + if (n >= 3) {\ + CPbit(dp, c, bitmask, n - 3);\ + CPbit(dp, m, bitmask, n - 2);\ + }\ + CPbit(dp, y, bitmask, n - 1);\ + dp += n, ep += n;\ + }\ + if (n >= 4)\ + *kP++ = k;\ + if (n >= 3) {\ + *cP++ = c;\ + *mP++ = m;\ + }\ + *yP++ = y;\ + }\ + } else { /* going_down */\ + for (i = 0; i < plane_size; i++) {\ + byte c, y, m, k, bitmask;\ + bitmask = 0x01;\ + for (c = m = y = k = 0; bitmask != 0; bitmask <<= 1) {\ + dp -= n, ep -= n;\ + if (n >= 4) {\ + CPbit(dp, k, bitmask, 0);\ + }\ + if (n >= 3) {\ + CPbit(dp, m, bitmask, n - 2);\ + CPbit(dp, c, bitmask, n - 3);\ + }\ + CPbit(dp, y, bitmask, n - 1);\ + }\ + *--yP = y;\ + if (n >= 3)\ + { *--mP = m;\ + *--cP = c;\ + }\ + if (n >= 4)\ + *--kP = k;\ + }\ + }\ +} + +/* Some convenient shorthand .. */ +#define x_dpi (pdev->x_pixels_per_inch) +#define y_dpi (pdev->y_pixels_per_inch) +#define CONFIG_16BIT "\033*v6W\000\003\000\005\006\005" +#define CONFIG_24BIT "\033*v6W\000\003\000\010\010\010" + +/* To calculate buffer size as next greater multiple of both parameter and W */ +#define calc_buffsize(a, b) (((((a) + ((b) * W) - 1) / ((b) * W))) * W) + +/* + * Miscellaneous functions for Canon BJC-600 printers in raster command mode. + */ +#define fputshort(n, f) gp_fputc((n)%256,f);gp_fputc((n)/256,f) + +static int +bjc_cmd(byte cmd, int argsize, byte *arg, gx_device_printer *pdev, + gp_file *f) +{ + gp_fputs("\033(", f); + gp_fputc(cmd, f); + fputshort(argsize, f); + gp_fwrite(arg, sizeof(byte), argsize, f); + + return 0; +} + +static int +bjc_raster_cmd_sub(char c, int rastsize, byte *data, gp_file *f) +{ + gp_fputs("\033(A", f); + fputshort(rastsize + 1, f); + gp_fputc(c, f); + gp_fwrite(data, sizeof(byte), rastsize, f); + gp_fputc('\015', f); + + return 0; +} + +static int +bjc_raster_cmd(int c_id, int rastsize, byte *data, gx_device_printer *pdev, + gp_file *f) +{ + if (bjcparams.printColors == BJC_COLOR_ALLBLACK) { + bjc_raster_cmd_sub('K', rastsize, data, f); + } else if (pdev->color_info.num_components == 1) { + if (bjcparams.printColors & BJC_COLOR_BLACK) { + bjc_raster_cmd_sub('K', rastsize, data, f); + } else { + if (bjcparams.printColors & BJC_COLOR_YELLOW) + bjc_raster_cmd_sub('Y', rastsize, data, f); + if (bjcparams.printColors & BJC_COLOR_MAGENTA) + bjc_raster_cmd_sub('M', rastsize, data, f); + if (bjcparams.printColors & BJC_COLOR_CYAN) + bjc_raster_cmd_sub('C', rastsize, data, f); + } + }else { /* Color decomposition */ + static byte ymckCodes[] = { + BJC_COLOR_YELLOW, + BJC_COLOR_MAGENTA, + BJC_COLOR_CYAN, + BJC_COLOR_BLACK, + }; + + if (bjcparams.printColors & (int) ymckCodes[c_id]) { + bjc_raster_cmd_sub("YMCK"[c_id], rastsize, data, f); + } + } + + return 0; +} + +static int +bjc_init_page(gx_device_printer *pdev, gp_file *f) +{ + byte pagemargins[3], resolution[4], paperloading[2]; + + /* Compute page margins. */ + + pagemargins[0] = (byte) ((float) pdev->height / pdev->y_pixels_per_inch + * 10 + .5); + pagemargins[1] = (byte) 1; + pagemargins[2] = (byte) ((pdev->width / pdev->x_pixels_per_inch * 10) - + pdev->HWMargins[0] / 7.2 - pdev->HWMargins[2] / 7.2 + .5); + + /* Cheat to keep margins into bounds (while waiting to have the right + margins for big papers. */ + + switch (bjc->ptype) { + case BJC800: + if (pagemargins[2] > 114) pagemargins[2] = 114; + break; + + default: + if (pagemargins[2] > 80) pagemargins[2] = 80; + break; + } + + /* Initialize resolution argument. */ + + resolution[0] = (byte) ((int)pdev->y_pixels_per_inch / 256); + resolution[1] = (byte) ((int)pdev->y_pixels_per_inch % 256); + resolution[2] = (byte) ((int)pdev->x_pixels_per_inch / 256); + resolution[3] = (byte) ((int)pdev->x_pixels_per_inch % 256); + + /* Initialize paper loading argument. */ + + paperloading[0] = 0x10 + ((1 - bjcparams.manualFeed) << 2); + paperloading[1] = bjcparams.mediaType << 4; + + /* Reinitialize printer in raster mode. */ + + gp_fputs("\033[K", f); + fputshort(2, f); + gp_fputc(0x00, f); + gp_fputc(0x0f, f); + + /* Set page mode on (ignore data at end of page) */ + + bjc_cmd('a', 1, (byte*) "\001", pdev, f); + + /* Set page margins */ + + bjc_cmd('g', 3, pagemargins, pdev, f); + + /* Set compression on (this is PackBits compression a la TIFF/Mac) */ + + bjc_cmd('b', 1, (byte*) "\001", pdev, f); + + /* Set paper loading. */ + + bjc_cmd('l', 2, paperloading, pdev, f); + + /* Set printing method. */ + +#ifndef BJC_INIT_800_AS_600 + if (bjc->ptype == BJC800) { +#else + if (0) { +#endif + byte printmode[2]; + + printmode[0] = bjcparams.printQuality; + + /* Modes not used are 3 (CN, Color Normal) and 2 (TP+ (?)) */ + + switch (bjcparams.printQuality) { + case BJC_QUALITY_DRAFT: + printmode[0] = 4; /* Draft */ + break; + } + + printmode[1] = (bjcparams.mediaType >= BJC_MEDIA_ENVELOPE ? 1 : + bjc800thickpaper()); + + bjc_cmd('c', 2, printmode, pdev, f); + } else /* BJC600 */ { + byte printmeth[3]; + + printmeth[0] = 0x10 + ((1 - bjcparams.manualFeed) << 2); + printmeth[1] = (bjcparams.mediaType << 4) + bjcparams.printQuality; + printmeth[2] = (bjcparams.printQuality == BJC_QUALITY_HIGH ? + 0x10 : 0) + (bjcparams.mediaType >= BJC_MEDIA_ENVELOPE ? 1 : + bjc600thickpaper()); + + bjc_cmd('c', 3, printmeth, pdev, f); + } + + /* Set raster resolution */ + + bjc_cmd('d', 4, resolution, pdev, f); + + return 0; +} + +static int +bjc_v_skip(int n, gx_device_printer *pdev, gp_file *f) +{ + if (n) { + gp_fputs("\033(e", f); + gp_fputc(2, f); + gp_fputc(0, f); + gp_fputc(n / 256, f); + gp_fputc(n % 256, f); + } + + return 0; +} + +static int +bjc_finish_page(gx_device_printer *pdev, gp_file *f) +{ + bjc_cmd('a', 1, (byte*) "\000", pdev, f); + bjc_cmd('b', 1, (byte*) "\000", pdev, f); + gp_fputc('\014', f); + gp_fputs("\033@", f); + + return 0; +} + +/* 1D runlength compression for BJC-600 + * this code is borrowed from gdevpcl.c:gdev_pcl_mode2compress. + */ +static int +bjc_compress(const byte *row, const byte *end_row, byte *compressed) +{ + register const byte *exam = row; + register byte *cptr = compressed; /* output pointer into compressed bytes */ + + while ( exam < end_row ) { + /* Search ahead in the input looking for a run */ + /* of at least 4 identical bytes. */ + const byte *compr = exam; + const byte *end_dis; + const byte *next; + register byte test, test2; + + test = *exam; + while ( exam < end_row ) { + test2 = *++exam; + if ( test == test2 ) + break; + test = test2; + } + + /* Find out how long the run is */ + end_dis = exam - 1; + if ( exam == end_row ) { /* no run */ + next = --end_row; + } else { + + next = exam + 1; + while ( next < end_row && *next == test ) next++; + } + + /* Now [compr..end_dis) should be encoded as dissimilar, */ + /* and [end_dis..next) should be encoded as similar. */ + /* Note that either of these ranges may be empty. */ + + for ( ; ; ) { /* Encode up to 128 dissimilar bytes */ + uint count = end_dis - compr; /* uint for faster switch */ + switch ( count ) { /* Use memcpy only if it's worthwhile. */ + case 6: cptr[6] = compr[5]; + case 5: cptr[5] = compr[4]; + case 4: cptr[4] = compr[3]; + case 3: cptr[3] = compr[2]; + case 2: cptr[2] = compr[1]; + case 1: cptr[1] = compr[0]; + *cptr = count - 1; + cptr += count + 1; + case 0: /* all done */ + break; + default: + if ( count > 128 ) count = 128; + *cptr++ = count - 1; + memcpy(cptr, compr, count); + cptr += count, compr += count; + continue; + } + break; + } + + { /* Encode up to 128 similar bytes. */ + /* Note that count may be <0 at end of row. */ + int count = next - end_dis; + if (next < end_row || test != 0) + while ( count > 0 ) { + + int this = (count > 128 ? 128 : count); + *cptr++ = 257 - this; + *cptr++ = (byte)test; + count -= this; + } + exam = next; + } + } + return cptr - compressed; +} + +/* + * For the ESC/P mode, resolution is fixed as 360dpi and we must transform + * image data to serialized data. + */ +typedef struct { + word *storage; + uint storage_size_words; + byte *raster_buf[4][BJC_HEAD_ROWS]; + byte *print_buf; + int num_comps; + int plane_size; + int img_rows; + int ln_idx; + int vskip1; + int vskip2; + const gs_memory_t *mem; +} ep_globals; + +#define row_bytes (img_rows / 8) +#define min_rows (32) /* for optimization of text image printing */ + +static int +ep_print_image(gp_file *prn_stream, ep_globals *eg, char cmd, byte *data, int size) +{ + int i, real_rows; + static const char color[4] = {4,1,2,0}; + + switch (cmd) { + case 3: /* Black */ + case 2: /* Cyan */ + case 1: /* Magenta */ + case 0: /* Yellow */ + memcpy(eg->raster_buf[((int) cmd)][eg->ln_idx+eg->vskip2], data, size); + return 0; + case 'B': /* blank line skip */ + if (!eg->ln_idx) { + eg->vskip1 += size; + } else if (size >= eg->img_rows - (eg->ln_idx+eg->vskip2) || eg->ln_idx+eg->vskip2 >= min_rows) { + /* The 'I' cmd must precede 'B' cmd! */ + eg->vskip2 += size; + ep_print_image(prn_stream, eg, 'F', 0, 0); /* flush and reset status */ + } else { + eg->vskip2 += size; + } + return 0; + case 'I': /* Increment index */ + eg->ln_idx += eg->vskip2 + 1; + eg->vskip2 = 0; + if (eg->ln_idx < eg->img_rows) return 0; + /* if eg->raster_buf filled up, then fall through here and flush buffer */ + case 'F': /* flush print buffer */ + if (!eg->ln_idx) return 0; /* The end of the page. */ + + /* before print the image, perform vertical skip. */ + while (eg->vskip1 >= (255*2)) { + gp_fputs("\033J\377", prn_stream); /* n/180in. feeding */ + eg->vskip1 -= (255*2); + } + if (eg->vskip1 > 255) { + gp_fputs("\033J\200", prn_stream); + eg->vskip1 -= 256; + } + if (eg->vskip1) { + /* n/360in. feeding */ + gp_fputs("\033|J", prn_stream); gp_fputc(0, prn_stream); gp_fputc(eg->vskip1, prn_stream); + } + + /* Optimize the number of nozzles to be used. */ + if (eg->ln_idx > 56) { /* use 64 nozzles */ + real_rows = 64; + } else if (eg->ln_idx > 48) { /* use 56 nozzles */ + real_rows = 56; + } else if (eg->ln_idx > 32) { /* use 48 nozzles */ + real_rows = 48; + } else { /* use 32 nozzles */ + real_rows = 32; + } + + for (i = 0; i < eg->num_comps; i++) { + int lnum, hskip, print_size, img_rows; + byte *p0, *p1, *p2, *p3; + byte *inp, *inbuf, *outp, *outbuf; + + img_rows = real_rows; /* Note that this img_rows is not the one in + * the globals struct. */ + outbuf = eg->print_buf; + + /* Transpose raster image for serial printer image */ + for (lnum=0; lnum < img_rows; lnum+=8, outbuf++) { + inbuf = inp = eg->raster_buf[i][lnum]; + for (outp = outbuf; inp < inbuf+eg->plane_size; inp++, outp += img_rows) { + memflip8x8(inp, eg->plane_size, outp, row_bytes); + } + } + + /* Set color */ + if (eg->num_comps == 1) { + /* Don't set color (to enable user setting). */ + gp_fputc('\015', prn_stream); + } else { + /* set color to one of CMYK. */ + gp_fputs("\015\033r", prn_stream); + gp_fputc(color[i], prn_stream); + } + + *(outp = eg->print_buf + eg->plane_size * img_rows) = 1; /* sentinel */ + + p0 = p3 = eg->print_buf; + + /* print image p0 to p1 and h skip p1 to p2 if p2<outp, + * then make p0=p2 and continue */ + while (p0 < outp) { + static const word zeros[8] = {0,0,0,0,0,0,0,0}; + + if (p3 < outp) { + /* p1 is the head of running zeros. */ + /* note that h skip unit is 1/180inch */ + for (p1 = p3; !memcmp(p3, zeros, row_bytes*2); p3 += row_bytes*2); + /* p2 is the head of non zero image. */ + p2 = p3; + redo: + for (p3 += row_bytes; memcmp(p3, zeros, row_bytes); p3 += row_bytes); + if (p3 < outp && memcmp(p3+row_bytes, zeros, row_bytes)) goto redo; + } else p1 = p2 = outp; + + if (p0 < p1) { /* print the image between p0 and p1 */ + print_size = ((p1 < outp) ? p1 : outp) - p0; + gp_fputs("\033|B", prn_stream); gp_fputc(img_rows, prn_stream); + fputshort(print_size, prn_stream); + gp_fwrite(p0, sizeof(byte), print_size, prn_stream); + } + if (p1 < p2) { /* skip running zeros from p1 to p2 */ + hskip = (((p2 < outp) ? p2 : outp) - p1) / row_bytes / 2; + gp_fputs("\033\\", prn_stream); + fputshort(hskip, prn_stream); + } + p0 = p2; + } + } + return ep_print_image(prn_stream, eg, 'R', 0, eg->vskip2 + eg->ln_idx); + case 'R': /* Reset status */ + eg->ln_idx = 0; + eg->vskip1 = size; + eg->vskip2 = 0; + memset(eg->storage, 0, eg->storage_size_words * W); + return 0; + default: /* This should not happen */ + errprintf(eg->mem, "ep_print_image: illegal command character `%c'.\n", cmd); + return 1; + } + + /* NOT REACHED */ +} + +/* Send the page to the printer. Compress each scan line. */ +static int +hp_colour_print_page(gx_device_printer * pdev, gp_file * prn_stream, int ptype) +{ + uint raster_width = gdev_prn_rasterwidth(pdev, 1); +/* int line_size = gdev_prn_rasterwidth(pdev, 0); */ + int line_size = gdev_prn_raster(pdev); + int line_size_words = (line_size + W - 1) / W; + int paper_size = gdev_pcl_paper_size((gx_device *)pdev); + int num_comps = pdev->color_info.num_components; + int bits_per_pixel = pdev->color_info.depth; + int storage_bpp = bits_per_pixel; + int expanded_bpp = bits_per_pixel; + int plane_size, databuff_size; + int combined_escapes = 1; + int errbuff_size = 0; + int outbuff_size = 0; + int compression = 0; + int scan = 0; + int *errors[2]; + const char *cid_string = (const char*) 0; + byte *data[4], *plane_data[4][4], *out_data; + byte *out_row, *out_row_alt; + word *storage; + uint storage_size_words; + ep_globals eg; + + memset(&eg, 0, sizeof(eg)); + eg.img_rows=BJC_HEAD_ROWS; + eg.mem=pdev->memory; + + /* Tricks and cheats ... */ + switch (ptype) { + case DJ550C: + if (num_comps == 3 && !cprn_device->cmyk) + num_comps = 4; /* 4-component printing */ + break; + case ESC_P: + if (bits_per_pixel == 24) /* prefer 3-component printing for bpp=24. */ + num_comps = 3; + else + if (num_comps != 1) + num_comps = 4; + break; + case PJXL300: + case PJXL180: + if (pjxl->rendertype > 0) { + if (bits_per_pixel < 16) + pjxl->rendertype = 0; + else { + /* Control codes for CID sequence */ + cid_string = (bits_per_pixel == 16) ? CONFIG_16BIT : CONFIG_24BIT; + /* Pretend we're a monobit device so we send the data out unchanged */ + bits_per_pixel = storage_bpp = expanded_bpp = 1; + num_comps = 1; + } + } + break; + } + + if (cprn_device->cmyk <= 0) { + if (storage_bpp == 8 && num_comps >= 3) + bits_per_pixel = expanded_bpp = 3; /* Only 3 bits of each byte used */ + } + + plane_size = calc_buffsize(line_size, storage_bpp); + eg.plane_size = plane_size; + + if (bits_per_pixel == 1) { /* Data printed direct from i/p */ + databuff_size = 0; /* so no data buffer required, */ + outbuff_size = plane_size * 4; /* but need separate output buffers */ + } + + if (bits_per_pixel > 4) { /* Error buffer for FS dithering */ + storage_bpp = expanded_bpp = + num_comps * 8; /* 8, 24 or 32 bits */ + + if (cprn_device->cmyk > 0) { /* Use CMYK dithering algorithm. */ + errbuff_size = 4 * (5 + 1 + 1 + line_size + 1 + 2) * I; + } else { /* Use original (RGB) dithering. */ + errbuff_size = /* 4n extra values for line ends */ + calc_buffsize((plane_size * expanded_bpp + num_comps * 4) * I, 1); + } + } + + databuff_size = plane_size * storage_bpp; + + storage_size_words = ((plane_size + plane_size) * num_comps + + databuff_size + errbuff_size + outbuff_size) / W; + + storage = (ulong *) gs_malloc(pdev->memory, storage_size_words, W, "hp_colour_print_page"); + eg.storage_size_words = (plane_size * (num_comps + 1)) / W * eg.img_rows + + 16; /* Redundant space for sentinel and aligning. */ + eg.storage = (word *) gs_malloc(pdev->memory, eg.storage_size_words, W, "ep_print_buffer"); + + /* + * The principal data pointers are stored as pairs of values, with + * the selection being made by the 'scan' variable. The function of the + * scan variable is overloaded, as it controls both the alternating + * raster scan direction used in the Floyd-Steinberg dithering and also + * the buffer alternation required for line-difference compression. + * + * Thus, the number of pointers required is as follows: + * + * errors: 2 (scan direction only) + * data: 4 (scan direction and alternating buffers) + * plane_data: 4 (scan direction and alternating buffers) + */ + + if (storage == 0 || eg.storage == 0) /* can't allocate working area */ + return_error(gs_error_VMerror); + else { + int i, j; + byte *p = out_data = out_row = (byte *)storage; + byte *ep_p = (byte *)eg.storage; + data[0] = data[1] = data[2] = p; + data[3] = p + databuff_size; + out_row_alt = out_row + plane_size * 2; + if (bits_per_pixel > 1) { + p += databuff_size; + } + if (bits_per_pixel > 4) { + errors[0] = (int *)p + num_comps * 2; + errors[1] = errors[0] + databuff_size; + p += errbuff_size; + } + for (i = 0; i < num_comps; i++) { + plane_data[0][i] = plane_data[2][i] = p; + p += plane_size; + } + for (i = 0; i < num_comps; i++) { + plane_data[1][i] = p; + plane_data[3][i] = p + plane_size; + p += plane_size; + } + if (bits_per_pixel == 1) { + out_data = out_row = p; /* size is outbuff_size * 4 */ + out_row_alt = out_row + plane_size * 2; + data[1] += databuff_size; /* coincides with plane_data pointers */ + data[3] += databuff_size; + } + for (i = 0; i < num_comps; i++) { + for (j = 0; j < eg.img_rows; j++) { + eg.raster_buf[i][j] = ep_p; + ep_p += plane_size; + } + /* Make a sentinel and align to word size. */ + eg.print_buf = (byte *)((word)(ep_p + sizeof(word)) & ~(sizeof(word)-1)); + } + eg.num_comps = num_comps; + } + + /* Initialize printer. */ + if (ptype == DJ505J) { + gp_fputs("\033@",prn_stream); /* Reset printer */ + gp_fprintf(prn_stream,"\033_R%c%c", /* Set resolution */ + (int)x_dpi & 0xff,((int)x_dpi >> 8) & 0xff); + } else if (ptype == BJC600 || ptype == BJC800) { + bjc_init_page(pdev, prn_stream); + } else { + if (ptype == LJ4DITH) { + gp_fputs("\033*rB", prn_stream); + } else { + gp_fputs("\033*rbC", prn_stream); /* End raster graphics */ + } + gp_fprintf(prn_stream, "\033*t%dR", (int)x_dpi); + /* Set resolution */ + } + + /* Clear temp storage */ + memset(storage, 0, storage_size_words * W); + +#define DOFFSET (dev_t_margin(pdev) - DESKJET_PRINT_LIMIT) /* Print position */ +#define POFFSET (dev_t_margin(pdev) - PAINTJET_PRINT_LIMIT) +#define EOFFSET (dev_t_margin(pdev) - ESC_P_PRINT_LIMIT) +#define BOFFSET (dev_t_margin(pdev) - bjc->printLimit) + switch (ptype) { + case LJ4DITH: + /* Page size, orientation, top margin & perforation skip */ + gp_fprintf(prn_stream, "\033&l%dA\033&l0o0e0L\033*r0F", paper_size); + gp_fprintf(prn_stream, "\033*p0x0Y" ); /* These Offsets are hacked ! */ + gp_fprintf(prn_stream, "\033&u600D\033*r1A" ); + /* Select data compression */ + compression = 3; + combined_escapes = 0; + break; + case DJ500C: + case DJ550C: + /* Page size, orientation, top margin & perforation skip */ + gp_fprintf(prn_stream, "\033&l%daolE", paper_size); + /* Set depletion and shingling levels */ + gp_fprintf(prn_stream, "\033*o%dd%dQ", cdj->depletion, cdj->shingling); + /* Move to top left of printed area */ + gp_fprintf(prn_stream, "\033*p%dY", (int)(300 * DOFFSET)); + /* Set number of planes ((-)1 is mono, (-)3 is (cmy)rgb, -4 is cmyk), + * and raster width, then start raster graphics */ + gp_fprintf(prn_stream, "\033*r%ds-%du0A", raster_width, num_comps); + /* Select data compression */ + compression = 9; + break; + case DJ505J: + /* Set depletion and shingling levels */ + gp_fprintf(prn_stream, "\033_D%c\033_E%c", + cdj->depletion, cdj->shingling); + /* Move to top left of printed area */ + gp_fwrite("\033_N\000", 4, 1, prn_stream); + gp_fwrite("\033_J\xc4\xff", 5, 1, prn_stream); + /* Set number of planes ((-)1 is mono, (-)3 is (cmy)rgb, -4 is cmyk), + * and raster width, then start raster graphics */ + gp_fprintf(prn_stream, "\033_U%c%c", + (0xffff - num_comps + 1) & 0xff, ((0xffff - num_comps + 1) >> 8) & 0xff); + gp_fprintf(prn_stream, + "\033_S%c%c", raster_width & 0xff, (raster_width >> 8) & 0xff); + /* set origin */ + gp_fwrite("\033_A\001", 4, 1, prn_stream); + compression = 1; + combined_escapes = 0; + break; + case DNJ650C: + if (pdev->x_pixels_per_inch == 600) { + /* set resolution to 600dpi 1st through PJL command */ + gp_fprintf(prn_stream,"\033%%-12345X@PJL SET RESOLUTION = 600\n"); + } + gp_fprintf (prn_stream, "\033%%0B"); /* Enter HPGL/2 mode */ + gp_fprintf (prn_stream, "BP5,1"); /* Turn off autorotation */ + gp_fprintf (prn_stream, "PS%d,%d", + (int)((pdev->height/pdev->y_pixels_per_inch)*1016), + (int)((pdev->width/pdev->x_pixels_per_inch)*1016)); /* Set length/width of page */ + gp_fprintf (prn_stream, "PU"); /* Pen up */ + gp_fprintf (prn_stream, "PA%d,%d", 0, 0); /* Move pen to upper-left */ + gp_fprintf (prn_stream, "\033%%1A"); /* Enter HP-RTL mode */ + gp_fprintf (prn_stream, "\033&a1N"); /* No negative motion - allow plotting + while receiving */ + if (pdev->x_pixels_per_inch == 600) + gp_fprintf (prn_stream, "\033*t600R"); /* request 600dpi via HP RTL */ + { static const char temp[] = { + 033, '*', 'v', '6', 'W', + 000 /* color model */, + 000 /* pixel encoding mode */, + 003 /* number of bits per index */, + 010 /* bits red */, + 010 /* bits green */, + 010 /* bits blue */ + }; + gp_fwrite (temp, 1, sizeof(temp), prn_stream); + } + + /* Set raster width */ + gp_fprintf(prn_stream, "\033*r%dS", raster_width); + /* Start raster graphics */ + gp_fprintf(prn_stream, "\033*r1A"); + + /* Select data compression */ + compression = 1; + /* No combined escapes for raster transfers */ + combined_escapes = 0; + break; + case PJXL300: + /* Page size, orientation, top margin & perforation skip */ + gp_fprintf(prn_stream, "\033&l%daolE", paper_size); + /* Set no-negative-motion mode, for faster (unbuffered) printing */ + gp_fprintf(prn_stream, "\033&a1N"); + /* Set print quality */ + gp_fprintf(prn_stream, "\033*o%dQ", pjxl->printqual); + /* Move to top left of printed area */ + gp_fprintf(prn_stream, "\033*p%dY", (int)(300 * POFFSET)); + /* Configure colour setup */ + if (pjxl->rendertype > 0) { + /* Set render type */ + gp_fprintf(prn_stream, "\033*t%dJ", pjxl->rendertype); + /* Configure image data */ + gp_fputs(cid_string, prn_stream); + /* Set raster width, then start raster graphics */ + gp_fprintf(prn_stream, "\033*r%ds1A", raster_width); + } else { + /* Set number of planes (1 is mono, 3 is rgb), + * and raster width, then start raster graphics */ + gp_fprintf(prn_stream, "\033*r%ds-%du0A", raster_width, num_comps); + } + /* No combined escapes for raster transfers */ + combined_escapes = 0; + break; + case PJXL180: + /* Page size, orientation, top margin & perforation skip */ + gp_fprintf(prn_stream, "\033&l%daolE", paper_size); + /* Set print quality */ + gp_fprintf(prn_stream, "\033*o%dQ", pjxl->printqual); + /* Move to top left of printed area */ + gp_fprintf(prn_stream, "\033*p%dY", (int)(180 * POFFSET)); + /* Configure colour setup */ + if (pjxl->rendertype > 0) { + /* Set render type */ + gp_fprintf(prn_stream, "\033*t%dJ", pjxl->rendertype); + /* Configure image data */ + gp_fputs(cid_string, prn_stream); + /* Set raster width, then start raster graphics */ + gp_fprintf(prn_stream, "\033*r%ds1A", raster_width); + } else { + /* Set number of planes (1 is mono, 3 is rgb), + * and raster width, then start raster graphics */ + gp_fprintf(prn_stream, "\033*r%ds%du0A", raster_width, num_comps); + } + break; + case PJ180: + case DECLJ250: + /* Disable perforation skip */ + gp_fprintf(prn_stream, "\033&lL"); + /* Move to top left of printed area */ + gp_fprintf(prn_stream, "\033&a%dV", (int)(720 * POFFSET)); + /* Set number of planes (1 is mono, 3 is rgb), + * and raster width, then start raster graphics */ + gp_fprintf(prn_stream, "\033*r%ds%du0A", raster_width, num_comps); + if (ptype == DECLJ250) { + /* No combined escapes for raster transfers */ + combined_escapes = 0; + /* From here on, we're a standard Paintjet .. */ + ptype = PJ180; + } + /* Select data compression */ + compression = 1; + break; + case ESC_P: + /* Move to top left of printed area (must be modified for large movement(YK))*/ + if ((int)(EOFFSET*360)) gp_fprintf(prn_stream, "\033|J%c%c", 0, (int)(360*EOFFSET)); + combined_escapes = 0; + break; + case BJC600: + case BJC800: + /* Move to top left of printed area */ + bjc_v_skip((int)(pdev->HWResolution[1] * BOFFSET), pdev, prn_stream); + combined_escapes = 0; + compression = 2; /* BJC600 uses the same method as mode 2 compression */ + break; + } + + /* Unfortunately, the Paintjet XL300 PCL interpreter introduces a + * version of the PCL language which is different to all earlier HP + * colour and mono inkjets, in that it loses the very useful ability + * to use combined escape sequences with the raster transfer + * commands. In this respect, it is incompatible even with the older + * 180 dpi PaintJet and PaintJet XL printers! Another regrettable + * omission is that 'mode 9' compression is not supported, as this + * mode can give both computational and PCL file size advantages. */ + + if (ptype == DJ505J) { + gp_fprintf(prn_stream, "\033_M%c", compression); + } else if (combined_escapes) { + /* From now on, all escape commands start with \033*b, so we + * combine them (if the printer supports this). */ + gp_fputs("\033*b", prn_stream); + /* Set compression if the mode has been defined. */ + if (compression) + gp_fprintf(prn_stream, "%dm", compression); + } + else if (ptype == BJC600 || ptype == BJC800) + ; /* Currently, nothing to do. */ + else + if (compression) + gp_fprintf(prn_stream, "\033*b%dM", compression); + + /* Send each scan line in turn */ + { + int cErr, mErr, yErr, kErr; + int this_pass, lnum, i; + int start_rows; + int lend, num_blank_lines = 0; + + word rmask = ~(word) 0 << ((-pdev->width * storage_bpp) & (W * 8 - 1)); + + lend = pdev->height - + (int)((dev_t_margin(pdev) + dev_b_margin(pdev)) * y_dpi); + + switch (ptype) { + case BJC600: + case BJC800: + start_rows = BJC_HEAD_ROWS; + break; + + /* Inhibit blank line printing for RGB-only printers, since in + * this case 'blank' means black! Also disabled for XL300 due to + * an obscure bug in the printer's firmware */ + + case PJ180: + case PJXL180: + case PJXL300: + start_rows = -1; + break; + + default: + start_rows = (num_comps == 1) ? HEAD_ROWS_MONO - 1 : + HEAD_ROWS_COLOUR - 1; + break; + } + + cErr = mErr = yErr = kErr = 0; + + if (bits_per_pixel > 4) { /* Randomly seed initial error buffer */ + if (cprn_device->cmyk > 0 && expanded_bpp == 32) { + bjc_fscmyk(data, plane_data, errors, plane_size, -1); + } else { + int *ep = errors[0]; + for (i = 0; i < databuff_size; i++) { + *ep++ = RANDOM; + } + } + } + + this_pass = start_rows; + for (lnum = 0; lnum < lend; lnum++) { + word *data_words = (word *)data[scan]; + register word *end_data = data_words + line_size_words; + + gdev_prn_copy_scan_lines(pdev, lnum, data[scan], line_size); + + /* Mask off 1-bits beyond the line width. */ + end_data[-1] &= rmask; + + /* Remove trailing 0s. */ + while (end_data > data_words && end_data[-1] == 0) + end_data--; + if (ptype != DNJ650C) /* DesignJet can't skip blank lines ? ? */ + if (end_data == data_words) { /* Blank line */ + num_blank_lines++; + continue; + } + /* Skip blank lines if any */ + if (num_blank_lines > 0) { + if (ptype == DJ505J) { + gp_fprintf(prn_stream,"\033_Y%c%c", + num_blank_lines & 0xff, (num_blank_lines >> 8) & 0xff); + } else if (ptype == ESC_P) { + ep_print_image(prn_stream, &eg, 'B', 0, num_blank_lines); + } else if (ptype == BJC600 || ptype == BJC800) { + bjc_v_skip(num_blank_lines, pdev, prn_stream); + } else if (num_blank_lines < this_pass) { + /* Moving down from current position + * causes head motion on the DeskJets, so + * if the number of lines is within the + * current pass of the print head, we're + * better off printing blanks. */ + this_pass -= num_blank_lines; + if (combined_escapes) { + gp_fputc('y', prn_stream); /* Clear current and seed rows */ + for (; num_blank_lines; num_blank_lines--) + gp_fputc('w', prn_stream); + } else { +#if 0 +/**************** The following code has been proposed ****************/ +/**************** as a replacement: ****************/ + gp_fputs("\033*b1Y", prn_stream); /* Clear current and seed rows */ + if ( num_blank_lines > 1 ) + gp_fprintf(prn_stream, "\033*b%dY", num_blank_lines - 1); + num_blank_lines = 0; +#else + gp_fputs("\033*bY", prn_stream); /* Clear current and seed rows */ + if (ptype == DNJ650C) { + gp_fprintf (prn_stream, "\033*b%dY", num_blank_lines); + num_blank_lines = 0; + } + else { + for (; num_blank_lines; num_blank_lines--) + gp_fputs("\033*bW", prn_stream); + } +#endif + } + } else { + if (combined_escapes) + gp_fprintf(prn_stream, "%dy", num_blank_lines); + else + gp_fprintf(prn_stream, "\033*b%dY", num_blank_lines); + } + memset(plane_data[1 - scan][0], 0, plane_size * num_comps); + num_blank_lines = 0; + this_pass = start_rows; + } + { /* Printing non-blank lines */ + register byte *kP = plane_data[scan + 2][3]; + register byte *cP = plane_data[scan + 2][2]; + register byte *mP = plane_data[scan + 2][1]; + register byte *yP = plane_data[scan + 2][0]; + register byte *dp = data[scan + 2]; + register int *ep = errors[scan]; + int zero_row_count; + int i, j; + byte *odp; + + if (this_pass) + this_pass--; + else + this_pass = start_rows; + + if (expanded_bpp > bits_per_pixel) { /* Expand line if required */ + cdj_expand_line(data_words, line_size, + cprn_device->cmyk, + bits_per_pixel, expanded_bpp); + } + + /* In colour modes, we have some bit-shuffling to do before + * we can print the data; in FS mode we also have the + * dithering to take care of. */ + switch (expanded_bpp) { /* Can be 1, 3, 8, 24 or 32 */ + case 3: + /* Transpose the data to get pixel planes. */ + for (i = 0, odp = plane_data[scan][0]; i < databuff_size; + i += 8, odp++) { /* The following is for 16-bit + * machines */ +#define spread3(c)\ + { 0, c, c*0x100, c*0x101, c*0x10000L, c*0x10001L, c*0x10100L, c*0x10101L } + static ulong spr40[8] = spread3(0x40); + static ulong spr08[8] = spread3(8); + static ulong spr02[8] = spread3(2); + register byte *dp = data[scan] + i; + register ulong pword = + (spr40[dp[0]] << 1) + + (spr40[dp[1]]) + + (spr40[dp[2]] >> 1) + + (spr08[dp[3]] << 1) + + (spr08[dp[4]]) + + (spr08[dp[5]] >> 1) + + (spr02[dp[6]]) + + (spr02[dp[7]] >> 1); + odp[0] = (byte) (pword >> 16); + odp[plane_size] = (byte) (pword >> 8); + odp[plane_size * 2] = (byte) (pword); + } + break; + + case 8: + switch (ptype) { + case BJC600: + case BJC800: + if (bjcparams.ditheringType == BJC_DITHER_NONE) { + COPYline(scan, i, j, plane_size, cP, mP, yP, kP, 1); + break; + } + + default: + FSDline(scan, i, j, plane_size, cErr, mErr, yErr, kErr, + cP, mP, yP, kP, 1); + } + break; + case 24: + FSDline(scan, i, j, plane_size, cErr, mErr, yErr, kErr, + cP, mP, yP, kP, 3); + break; + case 32: + if (cprn_device->cmyk > 0) { + bjc_fscmyk(data, plane_data, errors, plane_size, scan); + } else { + FSDline(scan, i, j, plane_size, cErr, mErr, yErr, kErr, + cP, mP, yP, kP, 4); + } + break; + + } /* switch(expanded_bpp) */ + + /* Make sure all black is in the k plane */ + + if (num_comps == 4 && (cprn_device->cmyk <= 0 || expanded_bpp != 32)) { + register word *kp = (word *)plane_data[scan][3]; + register word *cp = (word *)plane_data[scan][2]; + register word *mp = (word *)plane_data[scan][1]; + register word *yp = (word *)plane_data[scan][0]; + if (bits_per_pixel > 4) { /* Done as 4 planes */ + for (i = 0; i < plane_size / W; i++) { + word bits = *cp & *mp & *yp; + *kp++ |= bits; + bits = ~bits; + *cp++ &= bits; + *mp++ &= bits; + *yp++ &= bits; + } + } else { /* This has really been done as 3 planes */ + for (i = 0; i < plane_size / W; i++) { + word bits = *cp & *mp & *yp; + *kp++ = bits; + bits = ~bits; + *cp++ &= bits; + *mp++ &= bits; + *yp++ &= bits; + } + } + } + + /* Transfer raster graphics in the order (K), C, M, Y */ + + for (zero_row_count = 0, i = num_comps - 1; i >= 0; i--) { + int output_plane = 1; + int out_count = 0; + + switch (ptype) { + case DJ500C: /* Always compress using mode 9 */ + case DJ550C: + out_count = gdev_pcl_mode9compress(plane_size, + plane_data[scan][i], + plane_data[1 - scan][i], + out_data); + + /* This optimisation allows early termination of the + * row, but this doesn't work correctly in an alternating + * mode 2 / mode 3 regime, so we only use it with mode 9 + * compression */ + if (out_count == 0) + { output_plane = 0; /* No further output for this plane */ + if (i == 0) + gp_fputc('w', prn_stream); + else + zero_row_count++; + } + else + { for (; zero_row_count; zero_row_count--) + gp_fputc('v', prn_stream); + } + break; + case DJ505J: + out_count = gdev_pcl_mode1compress((const byte *) + plane_data[scan][i], + (const byte *) + plane_data[scan][i] + plane_size - 1, + out_data); + break; + case PJ180: + case DNJ650C: + if (num_comps > 1) + { word *wp = (word *)plane_data[scan][i]; + for (j = 0; j < plane_size / W; j++, wp++) + *wp = ~*wp; + } + out_count = gdev_pcl_mode1compress((const byte *) + plane_data[scan][i], + (const byte *) + plane_data[scan][i] + plane_size - 1, + out_data); + break; + case PJXL180: /* Need to invert data as CMY not supported */ + if (num_comps > 1) + { word *wp = (word *)plane_data[scan][i]; + for (j = 0; j < plane_size / W; j++, wp++) + *wp = ~*wp; + } + /* fall through .. */ + case PJXL300: /* Compression modes 2 and 3 are both + * available. Try both and see which one + * produces the least output data. */ + case LJ4DITH: + { const byte *plane = plane_data[scan][i]; + byte *prev_plane = plane_data[1 - scan][i]; + const word *row = (word *)plane; + const word *end_row = row + plane_size/W; + int count2 = gdev_pcl_mode2compress(row, end_row, out_row_alt); + int count3 = gdev_pcl_mode3compress(plane_size, plane, prev_plane, out_row); + int penalty = combined_escapes ? strlen("#m") : strlen("\033*b#M"); + int penalty2 = (compression == 2 ? 0 : penalty); + int penalty3 = (compression == 3 ? 0 : penalty); + + if (count3 + penalty3 < count2 + penalty2) + { if ( compression != 3 ) { + if (combined_escapes) + gp_fputs("3m", prn_stream); + else + gp_fputs("\033*b3M", prn_stream); + compression = 3; + } + out_data = out_row; + out_count = count3; + } + else + { if ( compression != 2 ) { + if (combined_escapes) + gp_fputs("2m", prn_stream); + else + gp_fputs("\033*b2M", prn_stream); + compression = 2; + } + out_data = out_row_alt; + out_count = count2; + } + } + break; + case BJC600: + case BJC800: + { const byte *plane = (byte *)plane_data[scan][i]; + int count2 = bjc_compress(plane, plane + plane_size, out_row_alt); + + out_data = out_row_alt; + out_count = count2; + } + break; + } + if (output_plane) { + if (ptype == DJ505J) + gp_fprintf(prn_stream, "\033_%c%c%c", + "WVVV"[i], out_count & 0xff, (out_count >> 8) & 0xff); + else if (combined_escapes) + gp_fprintf(prn_stream, "%d%c", out_count, "wvvv"[i]); + else if (ptype == BJC600 || ptype == BJC800) { + if (out_count) + bjc_raster_cmd(num_comps == 1 ? 3 : i, + out_count, out_data, pdev, prn_stream); + if (i == 0) bjc_v_skip(1, pdev, prn_stream); + } else if (ptype == ESC_P) + ep_print_image(prn_stream, &eg, (char)i, plane_data[scan][i], plane_size); + else + gp_fprintf(prn_stream, "\033*b%d%c", out_count, "WVVV"[i]); + if (ptype < ESC_P) + gp_fwrite(out_data, sizeof(byte), out_count, prn_stream); + } + + } /* Transfer Raster Graphics ... */ + if (ptype == ESC_P) + ep_print_image(prn_stream, &eg, 'I', 0, 0); /* increment line index */ + scan = 1 - scan; /* toggle scan direction */ + } /* Printing non-blank lines */ + } /* for lnum ... */ + } /* send each scan line in turn */ + + if (combined_escapes) + gp_fputs("0M", prn_stream); + + /* end raster graphics */ + if (ptype == BJC600 || ptype == BJC800) { + bjc_finish_page(pdev, prn_stream); + } else if (ptype == DJ505J) + gp_fputs("\033_C", prn_stream); + else if (ptype != ESC_P) + gp_fputs("\033*rbC\033E", prn_stream); + + /* eject page */ + if (ptype == PJ180) + gp_fputc('\f', prn_stream); + else if (ptype == DJ505J) + gp_fputs("\f\033@", prn_stream); + else if (ptype == DNJ650C) + gp_fputs ("\033*rC\033%0BPG;", prn_stream); + else if (ptype == BJC600 || ptype == BJC800) + ; /* Already done */ + else if (ptype == ESC_P) { + ep_print_image(prn_stream, &eg, 'F', 0, 0); /* flush print buffer */ + gp_fputs("\014\033@", prn_stream); /* reset after eject page */ + } else + gp_fputs("\033&l0H", prn_stream); + + /* free temporary storage */ + gs_free(pdev->memory, (char *) eg.storage, eg.storage_size_words, W, "ep_print_buffer"); + gs_free(pdev->memory, (char *) storage, storage_size_words, W, "hp_colour_print_page"); + + return 0; +} + +/* + * Row compression for the H-P PaintJet. + * Compresses data from row up to end_row, storing the result + * starting at compressed. Returns the number of bytes stored. + * The compressed format consists of a byte N followed by a + * data byte that is to be repeated N+1 times. + * In the worst case, the `compressed' representation is + * twice as large as the input. + * We complement the bytes at the same time, because + * we accumulated the image in complemented form. + */ +static int +gdev_pcl_mode1compress(const byte *row, const byte *end_row, byte *compressed) +{ register const byte *in = row; + register byte *out = compressed; + while ( in < end_row ) + { byte test = *in++; + const byte *run = in; + while ( in < end_row && *in == test ) in++; + /* Note that in - run + 1 is the repetition count. */ + while ( in - run > 255 ) + { *out++ = 255; + *out++ = test; + run += 256; + } + *out++ = in - run; + *out++ = test; + } + return out - compressed; +} + +/* + * Map a CMYK color to a color index. We just use depth / 4 bits per color + * to produce the color index. + * + * Important note: CMYK values are stored in the order K, C, M, Y because of + * the way the HP drivers work. + * + */ + +#define gx_color_value_to_bits(cv, b) \ + ((cv) >> (gx_color_value_bits - (b))) +#define gx_bits_to_color_value(cv, b) \ + ((cv) << (gx_color_value_bits - (b))) + +#define gx_cmyk_value_bits(c, m, y, k, b) \ + (((COLROUND_ROUND(k)) << (3 * (b))) | \ + ((COLROUND_ROUND(c)) << (2 * (b))) | \ + ((COLROUND_ROUND(m)) << (b)) | \ + ((COLROUND_ROUND(y)))) + +#define gx_value_cmyk_bits(v, c, m, y, k, b) \ + (k) = COLDUP_DUP(((v) >> (3 * (b))) & ((1 << (b)) - 1)), \ + (c) = COLDUP_DUP(((v) >> (2 * (b))) & ((1 << (b)) - 1)), \ + (m) = COLDUP_DUP(((v) >> (b)) & ((1 << (b)) - 1)), \ + (y) = COLDUP_DUP((v) & ((1 << (b)) - 1)) + +static gx_color_index +gdev_cmyk_map_cmyk_color(gx_device* pdev, const gx_color_value cv[]) +{ + gx_color_value cyan, magenta, yellow, black; + gx_color_index color; + cyan = cv[0]; magenta = cv[1]; yellow = cv[2]; black = cv[3]; + switch (pdev->color_info.depth) { + case 1: + color = (cyan | magenta | yellow | black) > gx_max_color_value / 2 ? + (gx_color_index) 1 : (gx_color_index) 0; + break; + + default: { + COLROUND_VARS; + int nbits = pdev->color_info.depth>>2; + COLROUND_SETUP(nbits); + + color = gx_cmyk_value_bits(cyan, magenta, yellow, black, nbits); + } + } + + return color; +} + +/* Mapping of RGB colors to gray values. */ + +static gx_color_index +gdev_cmyk_map_rgb_color(gx_device *pdev, const gx_color_value cv[]) +{ + gx_color_value r, g, b; + r = cv[0]; g = cv[1]; b = cv[2]; + if (gx_color_value_to_byte(r & g & b) == 0xff) { + return (gx_color_index) 0; /* White */ + } else { + gx_color_value c = gx_max_color_value - r; + gx_color_value m = gx_max_color_value - g; + gx_color_value y = gx_max_color_value - b; + + switch (pdev->color_info.depth) { + case 1: + return (c | m | y) > gx_max_color_value / 2 ? + (gx_color_index) 1 : (gx_color_index) 0; + /*NOTREACHED*/ + break; + + case 8: + return ((ulong) c * lum_red_weight * 10 + + (ulong) m * lum_green_weight * 10 + + (ulong) y * lum_blue_weight * 10) + >> (gx_color_value_bits + 2); + /*NOTREACHED*/ + break; + } + } + + return (gx_color_index) 0; /* This should never happen. */ +} + +/* Mapping of CMYK colors. */ + +static int +gdev_cmyk_map_color_cmyk(gx_device *pdev, gx_color_index color, gx_color_value pcmyk[]) +{ + switch (pdev->color_info.depth) { + case 1: + pcmyk[0] = gx_max_color_value * (1 - color); + break; + + case 8: + if (pdev->color_info.num_components == 1) { + gx_color_value value = (gx_color_value) color ^ 0xff; + + pcmyk[0] = (value << 8) + value; + + break; + } + + default: { + unsigned long bcyan, bmagenta, byellow, black; + int nbits = pdev->color_info.depth>>2; + COLDUP_VARS; + + COLDUP_SETUP(nbits); + gx_value_cmyk_bits(color, bcyan, bmagenta, byellow, black, nbits); + + pcmyk[0] = bcyan; + pcmyk[1] = bmagenta; + pcmyk[2] = byellow; + pcmyk[3] = black; + } + } + + return 0; +} + +/* + * Map a r-g-b color to a color index. + * We complement the colours, since we're using cmy anyway, and + * because the buffering routines expect white to be zero. + * Includes colour balancing, following HP recommendations, to try + * and correct the greenish cast resulting from an equal mix of the + * c, m, y, inks by reducing the cyan component to give a truer black. + */ + +/* Simple black generation/under-color removal with BG(k) = UG(k) = k. YA. */ + +#define bg_and_ucr(c, c_v, m, m_v, y, y_v, k) \ + do { \ + register byte cv = c_v, mv = m_v, yv = y_v, kv; \ + \ + kv = (cv > mv ? mv : cv); \ + kv = (yv > k ? k : y); \ + y = yv - kv; m = mv - kv; c = cv -kv; k = kv; \ + } while (0) + +static gx_color_index +gdev_pcl_map_rgb_color(gx_device *pdev, const gx_color_value cv[]) +{ + gx_color_value r, g, b; + r = cv[0]; g = cv[1]; b = cv[2]; + if (gx_color_value_to_byte(r & g & b) == 0xff) + return (gx_color_index)0; /* white */ + else { + int correction = cprn_device->correction; + gx_color_value c = gx_max_color_value - r; + gx_color_value m = gx_max_color_value - g; + gx_color_value y = gx_max_color_value - b; + + /* Colour correction for better blacks when using the colour ink + * cartridge (on the DeskJet 500C only). We reduce the cyan component + * by some fraction (eg. 4/5) to correct the slightly greenish cast + * resulting from an equal mix of the three inks */ + if (correction) { + ulong maxval, minval, range; + + maxval = c >= m ? (c >= y ? c : y) : (m >= y ? m : y); + if (maxval > 0) { + minval = c <= m ? (c <= y ? c : y) : (m <= y? m : y); + range = maxval - minval; + +#define shift (gx_color_value_bits - 12) + c = ((c >> shift) * (range + (maxval * correction))) / + ((maxval * (correction + 1)) >> shift); + } + } + + switch (pdev->color_info.depth) { + case 1: + return ((c | m | y) > gx_max_color_value / 2 ? + (gx_color_index)1 : (gx_color_index)0); + case 8: + if (pdev->color_info.num_components >= 3) +#define gx_color_value_to_1bit(cv) ((cv) >> (gx_color_value_bits - 1)) + return (gx_color_value_to_1bit(c) + + (gx_color_value_to_1bit(m) << 1) + + (gx_color_value_to_1bit(y) << 2)); + else +#define red_weight 306 +#define green_weight 601 +#define blue_weight 117 + return ((((ulong)c * red_weight + + (ulong)m * green_weight + + (ulong)y * blue_weight) + >> (gx_color_value_bits + 2))); + case 16: + /* FIXME: Simple truncation is not ideal. Should round really. */ +#define gx_color_value_to_5bits(cv) ((cv) >> (gx_color_value_bits - 5)) +#define gx_color_value_to_6bits(cv) ((cv) >> (gx_color_value_bits - 6)) + return (gx_color_value_to_5bits(y) + + (gx_color_value_to_6bits(m) << 5) + + (gx_color_value_to_5bits(c) << 11)); + case 24: + return (gx_color_value_to_byte(y) + + (gx_color_value_to_byte(m) << 8) + + ((ulong)gx_color_value_to_byte(c) << 16)); + case 32: + { return ((c == m && c == y) ? ((ulong)gx_color_value_to_byte(c) << 24) + : (gx_color_value_to_byte(y) + + (gx_color_value_to_byte(m) << 8) + + ((ulong)gx_color_value_to_byte(c) << 16))); + } + } + } + return (gx_color_index)0; /* This never happens */ +} + +/* Map a color index to a r-g-b color. */ +static int +gdev_pcl_map_color_rgb(gx_device *pdev, gx_color_index color, + gx_color_value prgb[3]) +{ + /* For the moment, we simply ignore any black correction */ + switch (pdev->color_info.depth) { + case 1: + prgb[0] = prgb[1] = prgb[2] = -((gx_color_value)color ^ 1); + break; + case 8: + if (pdev->color_info.num_components >= 3) + { gx_color_value c = (gx_color_value)color ^ 7; + prgb[0] = -(c & 1); + prgb[1] = -((c >> 1) & 1); + prgb[2] = -(c >> 2); + } + else + { gx_color_value value = (gx_color_value)color ^ 0xff; + prgb[0] = prgb[1] = prgb[2] = (value << 8) + value; + } + break; + case 16: + { gx_color_value c = (gx_color_value)color ^ 0xffff; + ushort value = c >> 11; + prgb[0] = ((value << 11) + (value << 6) + (value << 1) + + (value >> 4)) >> (16 - gx_color_value_bits); + value = (c >> 6) & 0x3f; + prgb[1] = ((value << 10) + (value << 4) + (value >> 2)) + >> (16 - gx_color_value_bits); + value = c & 0x1f; + prgb[2] = ((value << 11) + (value << 6) + (value << 1) + + (value >> 4)) >> (16 - gx_color_value_bits); + } + break; + case 24: + { gx_color_index c = color ^ 0xffffff; + prgb[0] = gx_color_value_from_byte((gx_color_value)(c >> 16)); + prgb[1] = gx_color_value_from_byte((gx_color_value)((c >> 8) & 0xff)); + prgb[2] = gx_color_value_from_byte((gx_color_value)(c & 0xff)); + } + break; + case 32: +#define gx_maxcol gx_color_value_from_byte(gx_color_value_to_byte(gx_max_color_value)) + { gx_color_value w = gx_maxcol - gx_color_value_from_byte(color >> 24); + prgb[0] = w - gx_color_value_from_byte((color >> 16) & 0xff); + prgb[1] = w - gx_color_value_from_byte((color >> 8) & 0xff); + prgb[2] = w - gx_color_value_from_byte(color & 0xff); + } + break; + } + return 0; +} + +/* + * Convert and expand scanlines: + * + * For devices with 3 components: + * + * (a) 16 -> 24 bit (1-stage) + * (b) 16 -> 32 bit (2-stage) + * or (c) 24 -> 32 bit (1-stage) + * + * For devices with 4 components: + * + * (a) 16 -> 32 bit (1-stage) + * (b) 8 -> 32 bit (2-stage) + * or (c) 24 -> 32 bit (1-stage) + * + */ + +static void +cdj_expand_line(word *line, int linesize, short cmyk, int bpp, int ebpp) +{ + int endline = linesize; + byte *start = (byte *)line; + register byte *in, *out; + + if (cmyk > 0) { + if (bpp == 8) { + in = start + endline; + out = start + (endline *= 2); + + while (in > start) { + register byte b0; + register byte bs0, bs1, bs2, bs3; + + b0 = *--in; + + bs0 = b0 & 0x03; + bs1 = (b0 >> 2) & 0x03; + bs2 = (b0 >> 4) & 0x03; + bs3 = (b0 >> 6) & 0x03; + + *--out = (bs0 << 2) + bs0 + (bs1 << 6) + (bs1 << 4); + *--out = (bs2 << 2) + bs2 + (bs3 << 6) + (bs3 << 4); + } + } + + if (bpp == 24) { + endline = (endline + 2) / 3; + + in = start + endline * 3; + out = start + endline * 4; + + while (in > start) { + register byte b0, b1, b2; + + b0 = *--in; + b1 = *--in; + b2 = *--in; + + *--out = (b0 << 2) + ((b0 >> 4) & 0x03); + *--out = ((b1 & 0x0f) << 4) + ((b0 >> 6) << 2) + + ((b1 >> 2) & 0x03); + *--out = ((b2 & 0x03) << 6) + ((b1 >> 4) << 2) + (b2 & 0x03); + *--out = (b2 & 0xfc) + ((b2 >> 6) & 0x03); + } + } else if (ebpp == 32) { + endline = (endline + 1) / 2; + + in = start + endline * 2; + out = start + (endline *= 4); + + while (in > start) { + register byte b0, b1; + + b0 = *--in; + b1 = *--in; + + *--out = (b0 << 4) + ((b0 >> 4) & 0x07); + *--out = (b0 & 0xf0) + ((b0 >> 4) & 0xf); + *--out = (b1 << 4) + ((b1 >> 4) & 0x0f); + *--out = (b1 & 0xf0) + ((b1 >> 4) & 0xf); + } + } + } else /* cmyk > 0 */ { + if (bpp == 16) /* 16 to 24 (cmy) if required */ + { register byte b0, b1; + endline = ((endline + 1) / 2); + in = start + endline * 2; + out = start + (endline *= 3); + + while (in > start) + { b0 = *--in; + b1 = *--in; + *--out = (b0 << 3) + ((b0 >> 2) & 0x7); + *--out = (b1 << 5) + ((b0 >> 3) & 0x1c) + ((b1 >> 1) & 0x3); + *--out = (b1 & 0xf8) + (b1 >> 5); + } + } + + if (ebpp == 32) /* 24/32 (cmy) to 32 (cmyk) if required */ + { register byte c, m, y; + endline = ((endline + 2) / 3); + in = start + endline * 3; + out = start + endline * 4; + + while (in > start) + { + y = *--in; + m = *--in; + c = *--in; + + if (c == y && c == m) { + *--out = 0, *--out = 0, *--out = 0; + *--out = c; + } else { + *--out = y, *--out = m, *--out = c; + *--out = 0; + } + } + } + } +} + +static int +cdj_put_param_int(gs_param_list *plist, gs_param_name pname, int *pvalue, + int minval, int maxval, int ecode) +{ int code, value; + switch ( code = param_read_int(plist, pname, &value) ) + { + default: + return code; + case 1: + return ecode; + case 0: + if ( value < minval || value > maxval ) + param_signal_error(plist, pname, gs_error_rangecheck); + *pvalue = value; + return (ecode < 0 ? ecode : 1); + } +} + +static int +cdj_set_bpp(gx_device *pdev, int bpp, int ccomps) +{ gx_device_color_info *ci = &pdev->color_info; + + if (ccomps && bpp == 0) { + if (cprn_device->cmyk) { + switch (ccomps) { + default: + return_error(gs_error_rangecheck); + /*NOTREACHED*/ + break; + + case 1: + bpp = 1; + break; + + case 3: + bpp = 24; + break; + + case 4: + switch (ci->depth) { + case 8: + case 16: + case 24: + case 32: + break; + + default: + bpp = cprn_device->default_depth; + break; + } + break; + } + } + } + + if (bpp == 0) { + bpp = ci->depth; /* Use the current setting. */ + } + + if (cprn_device->cmyk < 0) { + + /* Reset procedures because we may have been in another mode. */ + + dev_proc(pdev, encode_color) = gdev_cmyk_map_cmyk_color; + dev_proc(pdev, map_rgb_color) = NULL; + dev_proc(pdev, decode_color) = gdev_cmyk_map_color_cmyk; + + if (pdev->is_open) gs_closedevice(pdev); + } + + /* Check for valid bpp values */ + + switch ( bpp ) + { + case 16: + case 32: + if (cprn_device->cmyk && ccomps && ccomps != 4) goto bppe; + break; + + case 24: + if (!cprn_device->cmyk || ccomps == 0 || ccomps == 4) { + break; + } else if (ccomps == 1) { + goto bppe; + } else { + + /* 3 components 24 bpp printing for CMYK device. */ + + cprn_device->cmyk = -1; + } + break; + + case 8: + if (cprn_device->cmyk) { + if (ccomps) { + if (ccomps == 3) { + cprn_device->cmyk = -1; + bpp = 3; + } else if (ccomps != 1 && ccomps != 4) { + goto bppe; + } + } + if (ccomps != 1) break; + } else { + break; + } + + case 1: + if (ccomps != 1) goto bppe; + + if (cprn_device->cmyk && bpp != pdev->color_info.depth) { + dev_proc(pdev, map_cmyk_color) = NULL; + dev_proc(pdev, map_rgb_color) = gdev_cmyk_map_rgb_color; + + if (pdev->is_open) { + gs_closedevice(pdev); + } + } + break; + + case 3: + if (!cprn_device->cmyk) { + break; + } + + default: + bppe: return_error(gs_error_rangecheck); + } + + if (cprn_device->cmyk == -1) { + dev_proc(pdev, map_cmyk_color) = NULL; + dev_proc(pdev, map_rgb_color) = gdev_pcl_map_rgb_color; + dev_proc(pdev, map_color_rgb) = gdev_pcl_map_color_rgb; + + if (pdev->is_open) { + gs_closedevice(pdev); + } + if (pdev->is_open) { + int code; /* Return code */ + gdev_prn_space_params sp; /* Space parameter data */ + + /* Reallocate memory for device */ + sp = ((gx_device_printer *)pdev)->space_params; + + if ((code = gdev_prn_reallocate_memory(pdev, &sp, pdev->width, + pdev->height)) < 0) + return (code); + } + } + + switch (ccomps) { + case 0: + break; + + case 1: + if (bpp != 1 && bpp != 8) goto cce; + break; + + case 4: + if (cprn_device->cmyk) { + if (bpp >= 8) break; + } + + case 3: + if (bpp == 1 || bpp == 3 || bpp == 8 || bpp == 16 + || bpp == 24 || bpp == 32) { + break; + } + + cce: default: return_error(gs_error_rangecheck); + } + + if (cprn_device->cmyk) { + if (cprn_device->cmyk > 0) { + ci->num_components = ccomps ? ccomps : (bpp < 8 ? 1 : 4); + } else { + ci->num_components = ccomps ? ccomps : (bpp < 8 ? 1 : 3); + } + if (bpp != 1 && ci->num_components == 1) { /* We do dithered grays. */ + bpp = bpp < 8 ? 8 : bpp; + } + + ci->max_color = (1 << (bpp >> 2)) - 1; + ci->max_gray = (bpp >= 8 ? 255 : 1); + + if (ci->num_components == 1) { + ci->dither_grays = (bpp >= 8 ? 256 : 2); + ci->dither_colors = (bpp >= 8 ? 256 : bpp > 1 ? 2 : 0); + } else { + ci->dither_grays = (bpp > 8 ? 256 : 2); + ci->dither_colors = (bpp > 8 ? 256 : bpp > 1 ? 2 : 0); + } + } else { + ci->num_components = (bpp == 1 || bpp == 8 ? 1 : 3); + ci->max_color = (bpp >= 8 ? 255 : bpp > 1 ? 1 : 0); + ci->max_gray = (bpp >= 8 ? 255 : 1); + ci->dither_grays = (bpp >= 8 ? 256 : 2); + ci->dither_colors = (bpp >= 8 ? 256 : bpp > 1 ? 2 : 0); + } + + ci->depth = ((bpp > 1) && (bpp < 8) ? 8 : bpp); + + return 0; +} + +/* new_bpp == save_bpp or new_bpp == 0 means don't change bpp. + ccomps == 0 means don't change number of color comps. + If new_bpp != 0, it must be the value of the BitsPerPixel element of + the plist; real_bpp may differ from new_bpp. +*/ +static int +cdj_put_param_bpp(gx_device *pdev, gs_param_list *plist, int new_bpp, + int real_bpp, int ccomps) +{ + if (new_bpp == 0 && ccomps == 0) + return gdev_prn_put_params(pdev, plist); + else + { + gx_device_color_info save_info; + int save_bpp; + int code; + + save_info = pdev->color_info; + save_bpp = save_info.depth; +#define save_ccomps save_info.num_components + if ( save_bpp == 8 && save_ccomps == 3 && !cprn_device->cmyk) + save_bpp = 3; + code = cdj_set_bpp(pdev, real_bpp, ccomps); + if ( code < 0 ) { + param_signal_error(plist, "BitsPerPixel", code); + param_signal_error(plist, "ProcessColorModel", code); + return code; + } + pdev->color_info.depth = new_bpp; /* cdj_set_bpp maps 3/6 to 8 */ + code = gdev_prn_put_params(pdev, plist); + if ( code < 0 ) + { cdj_set_bpp(pdev, save_bpp, save_ccomps); + return code; + } + cdj_set_bpp(pdev, real_bpp, ccomps); /* reset depth if needed */ + if ((cdj->color_info.depth != save_bpp || + (ccomps != 0 && ccomps != save_ccomps)) + && pdev->is_open ) + return gs_closedevice(pdev); + return 0; +#undef save_ccomps + } +} + +/* This returns either the number of pixels in a scan line, or the number + * of bytes required to store the line, both clipped to the page margins */ +static uint +gdev_prn_rasterwidth(const gx_device_printer *pdev, int pixelcount) +{ + ulong raster_width = (ulong)(pdev->width - + pdev->x_pixels_per_inch * (dev_l_margin(pdev) + dev_r_margin(pdev))); + return (pixelcount ? + (uint)raster_width : + (uint)((raster_width * pdev->color_info.depth + 7) >> 3)); +} + +/* Functions for manipulation params strings */ + +static const byte* +paramValueToString(const stringParamDescription* params, int value) +{ + + for (; params->p_name; ++params) { + if (params->p_value == value) { + return (const byte *)params->p_name; + } + } + + return (const byte*) 0; +} + +static int +paramStringValue(const stringParamDescription* params, + const byte* name, int namelen, int* value) +{ + + for (; params->p_name; ++params) { + if (strncmp(params->p_name, (char *)name, namelen) == 0 && + params->p_name[namelen] == 0) { + *value = params->p_value; + return 1; + } + } + + return 0; +} + +static int +put_param_string(gs_param_list* plist, + const byte* pname, gs_param_string* pstring, + const stringParamDescription* params, int *pvalue, int code) +{ + + int ncode; + + if ((ncode = param_read_string(plist, (char *)pname, pstring)) < 0) { + param_signal_error(plist, (char *)pname, code = ncode); + } else if (ncode == 1) { + pstring->data = 0, pstring->size = 0; + } else { + int value = 0; + + if (paramStringValue(params, pstring->data, pstring->size, + &value) == 0) { + param_signal_error(plist, (char *)pname, code = gs_error_rangecheck); + } else { + *pvalue = value; + } + } + + return code; +} + +static int +get_param_string(gs_param_list* plist, + const byte* pname, gs_param_string* pstring, + const stringParamDescription* params, int pvalue, bool persist, int code) +{ + + int ncode; + + pstring->data = paramValueToString(params, pvalue); + + if (pstring->data == (byte*) 0) { + param_signal_error(plist, (char *)pname, ncode = gs_error_unknownerror); + } else { + pstring->size = strlen((char *)pstring->data); + pstring->persistent = persist; + } + + if ((ncode = param_write_string(plist, (char *)pname, pstring)) < 0) { + code = ncode; + } + + return code; +} + +/* + * This taken from gsdparam.c. I hope it will be useable directly some day. + * + */ + +static int +cdj_param_check_bytes(gs_param_list *plist, gs_param_name pname, + const byte *str, uint size, bool is_defined) +{ int code; + gs_param_string new_value; + switch ( code = param_read_string(plist, pname, &new_value) ) + { + case 0: + if ( is_defined && new_value.size == size && + !memcmp((const char *)str, (const char *)new_value.data, + size) + ) + break; + code = gs_note_error(gs_error_rangecheck); + goto e; + default: + if ( param_read_null(plist, pname) == 0 ) + return 1; +e: param_signal_error(plist, pname, code); + case 1: + ; + } + return code; +} + +/* This is original code. */ + +static int +cdj_param_check_float(gs_param_list *plist, gs_param_name pname, double fval, + bool is_defined) +{ int code; + float new_value; + switch ( code = param_read_float(plist, pname, &new_value) ) + { + case 0: + if ( is_defined && new_value == (float)fval) + break; + code = gs_note_error(gs_error_rangecheck); + goto e; + default: + if ( param_read_null(plist, pname) == 0 ) + return 1; +e: param_signal_error(plist, pname, code); + case 1: + ; + } + return code; +} + +/* The following dithering algorithm has been kindly given to me (YA) by + * Klaus-Gunther Hess, I just adapted it for use with the code here. */ + +/* + +(From KGH:) + +Just about the features of the code: + + - Stored Color-Values are BYTES in the order C-M-Y-K. + (Indices need to change with gdevcdj.c) + + - There are individual THRESHOLDs and SPOTSIZEs for + the color-components. The following relation should + be maintained: + SPOTSIZE = 2 * THRESHOLD + 1 + (The internal calculation is dedicated for limiting + ink-density at the 720x720DpI-Resolution of the + Epson-Printers, without loss of dynamic color-range) + + - In addition to that there are EMIN & EMAX-Values + for the components. The Values are computed from + the dithering-algorithm and can be replaced by + constants, if neither the implementation nor + THRESHOLD and SPOTSIZE can change. + + - The algorithm is tuned for speed. (K-only, if gray- + levels are detected, with EMIN/EMAX-clipping of + stored CMY-Errors. [Notice: cerr, merr, yerr are + *not* reset to zero! Clearing them would cause + regular patterns & "Halos" to appear!]) + +*/ + +/* + * Macros, that represent the undisturbed dithering-algorithm + * + * FSerror: compute the desired Value + * FSdecide: decision based on the value computed by FSerror + * FSdiffuse: distribute remaining error among pixels + */ + +#define FSerror(Val,Erow,Ecol) (Val + Erow + ((7 * Ecol)>>4)) + +#define FSdecide(Error,Threshold,Spotsize,Pixel,Bit) \ + if(Error > Threshold) {\ + Pixel |= Bit;\ + Error -= Spotsize;\ + } + +#define FSdiffuse(Error,Erow,Ecol,Eprev)\ + Eprev += (3 * Error + 8)>>4;\ + Erow = (5 * Error + Ecol + 8)>>4;\ + Ecol = Error; + +/* + * some aliases for values from the device-structure + */ +#define DIRECTION direction[0] +#define CMYK_THRESHOLD(I) threshold[I] +#define SPOTSIZE(I) spotsize[I] +#define EMIN(I) emin[I] +#define EMAX(I) emax[I] +#define NPIXEL (plane_size * 8) + +#define IDX_C 1 +#define IDX_M 2 +#define IDX_Y 3 +#define IDX_K 0 + +#define ODX_C 2 +#define ODX_M 1 +#define ODX_Y 0 +#define ODX_K 3 + +static int +bjc_fscmyk(byte** inplanes, byte* outplanes[4][4], int** errplanes, + int plane_size, int scan) { + + byte* err = (byte*) errplanes[0]; + +/* =========================================================== */ + if(scan < 0) { /* scan < 0 -> initialize private buffer */ +/* =========================================================== */ + + int p,i,v; + int *direction,*threshold,*spotsize,*emin,*emax; + int *errv,*errc; +/* + * allocate the error-buffer + */ + /*KGHorig + i = 4 * (5 + 1 + 1 + sd->stc.prt_pixels + 1) * sizeof(errv[0]); + if((sd->stc.err_size < i) || (NULL == sd->stc.err)) { + if(NULL != sd->stc.err) + gs_free(sd->stc.err,sd->stc.err_size,1,"stcm/err"); + sd->stc.err_size = i; + sd->stc.err = gs_malloc(sd->stc.err_size,1,"stcm/err"); + if(sd->stc.err == NULL) return_error(gs_error_VMerror); + } + */ + + direction = (int *) err; + threshold = direction + 4; + spotsize = threshold + 4; + emin = spotsize + 4; + emax = emin + 4; + errc = emax + 4; + errv = errc + 2*4; +/* + * compute initial values + */ + DIRECTION = -1; + for(i = 0; i < 4; ++i) { + float maxv = 1.0; + /*KGHorig + int j; + if((sd->stc.xfer[i].size < 1) || (sd->stc.xfer[i].data == NULL)) { + maxv = 1.0; + } else { + maxv = 1.0/255.0; + for(j = 0; j < sd->stc.xfer[i].size; ++j) + if(maxv < sd->stc.xfer[i].data[j]) + maxv = sd->stc.xfer[i].data[j]; + } + */ + CMYK_THRESHOLD(i) = (int)(127.0 / maxv + 0.5); + SPOTSIZE(i) = ((int) CMYK_THRESHOLD(i)<<1)+1; + errc[3] = 0; + FSdiffuse(CMYK_THRESHOLD(i),errv[0],errc[0],errv[-4]); + FSdiffuse(CMYK_THRESHOLD(i),errv[0],errc[0],errv[-4]); + EMAX(i) = errv[0]; + errc[0] = 0; + FSdiffuse((-CMYK_THRESHOLD(i)),errv[0],errc[0],errv[-4]); + FSdiffuse((-CMYK_THRESHOLD(i)),errv[0],errc[0],errv[-4]); + EMIN(i) = errv[0]; + } + +#ifdef CDJ_DEBUG_FS + for(i = 0; i < 4; ++i) errprintf_nomem( + "CMYK_THRESHOLD(%d)=%5d, spotsize(%d)=%5d, emin(%d)=%5d, emax(%d)=%5d\n", + i,CMYK_THRESHOLD(i),i,SPOTSIZE(i),i,EMIN(i),i,EMAX(i)); +#endif + + for(i = 0; i < 4; ++i) errc[i] = 0; + + for(p = 0; p < NPIXEL; ++p) { + for(i = 0; i < 4; ++i) { + /*KHGOrig + if(sd->stc.flags & STCDFLAG0) v = 0; + */ + if (0) v = 0; /* Must provide a default for that. */ + else v = (rand() % SPOTSIZE(i)) - CMYK_THRESHOLD(i); + FSdiffuse(v,errv[i],errc[i],errv[i-4]); + } + errv += i; + } + +/* =========================================================== */ + } else { /* scan >= 0 -> scanline-processing */ +/* =========================================================== */ + + int w,p,dir,thedir; + byte *out[4],pixel[4],bit; + /*KGHorig + int *width = outplanes[scan]; + */ + int *direction = (int *) err; + int *threshold = direction + 4; + int *spotsize = threshold + 4; + int *emin = spotsize + 4; + int *emax = emin + 4; + int *errc = emax + 4; + int *errv = errc + 2*4; + int kerr,cerr,merr,yerr; + + byte* in; + + /*KGHorig + if(sd->stc.flags & STCDFLAG1) { + */ + if (0) { /* Eventually will provide a flag for this. */ + cerr = merr = yerr = kerr = 0; + } else { + cerr = errc[0]; + merr = errc[1]; + yerr = errc[2]; + kerr = errc[3]; + } + + out[0] = outplanes[scan + 2][ODX_C]; + out[1] = outplanes[scan + 2][ODX_M]; + out[2] = outplanes[scan + 2][ODX_Y]; + out[3] = outplanes[scan + 2][ODX_K]; + pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0; + + if(DIRECTION < 0) { /* scan == 0, run backward */ + w = NPIXEL; + in = inplanes[2] + 4 * (NPIXEL - 1); + errv += (w-1)<<2; + dir = -4; + /*KGHorig + if(w > 8) for(p = 0; p < 4; ++p) out[p] += (w-1)>>3; + */ + thedir = -1; + for (p = 0; p < 4; ++p) { + out[p] += plane_size - 1; + } + } else { /* run forward */ + w = 1; + in = inplanes[3] - 4 * NPIXEL; + dir = 4; + thedir = 1; + for (p = 0; p < 4; ++p) { + out[p] -= plane_size; + } + } /* run backward/forward */ + + /*KGHorig + if(0 == (sd->stc.flags & STCDFLAG1)) DIRECTION = -DIRECTION; + */ + if (1) DIRECTION = -DIRECTION; /* Scan in other direction. */ + + bit = 0x80>>((w-1) & 7); + w = (w+7)>>3; + + for(p = NPIXEL; p; --p) { /* loop over pixels */ + + int cmy = in[IDX_C] | in[IDX_M] | in[IDX_Y]; + int kv = FSerror(in[IDX_K],errv[3],kerr); + int cv; + + FSdecide(kv,CMYK_THRESHOLD(3),SPOTSIZE(3),pixel[3],bit); + + if(cmy) { + + if(pixel[3] & bit) { /* black known to fire */ + + FSdiffuse(kv,errv[3],kerr,errv[3-dir]); + + cv = FSerror(in[IDX_C],errv[0],cerr); + cv -= SPOTSIZE(0); + if ((cv+CMYK_THRESHOLD(0)) < 0) cv = -CMYK_THRESHOLD(0); + FSdiffuse(cv,errv[0],cerr,errv[0-dir]); + + cv = FSerror(in[IDX_M],errv[1],merr); + cv -= SPOTSIZE(1); + if ((cv+CMYK_THRESHOLD(1)) < 0) cv = -CMYK_THRESHOLD(1); + + FSdiffuse(cv,errv[1],merr,errv[1-dir]); + + cv = FSerror(in[IDX_Y],errv[2],yerr); + cv -= SPOTSIZE(2); + if ((cv+CMYK_THRESHOLD(2)) < 0) cv = -CMYK_THRESHOLD(2); + FSdiffuse(cv,errv[2],yerr,errv[2-dir]); + + } else { + + cv = FSerror(in[IDX_C],errv[0],cerr); + FSdecide(cv,CMYK_THRESHOLD(0),SPOTSIZE(0),pixel[0],bit); + FSdiffuse(cv,errv[0],cerr,errv[0-dir]); + + cv = FSerror(in[IDX_M],errv[1],merr); + FSdecide(cv,CMYK_THRESHOLD(1),SPOTSIZE(1),pixel[1],bit); + FSdiffuse(cv,errv[1],merr,errv[1-dir]); + + cv = FSerror(in[IDX_Y],errv[2],yerr); + FSdecide(cv,CMYK_THRESHOLD(2),SPOTSIZE(2),pixel[2],bit); + FSdiffuse(cv,errv[2],yerr,errv[2-dir]); + + if(pixel[0] & pixel[1] & pixel[2] & bit) { + pixel[0] &= ~bit; + pixel[1] &= ~bit; + pixel[2] &= ~bit; + pixel[3] |= bit; + kv -= SPOTSIZE(3); + if ((kv+CMYK_THRESHOLD(3)) < 0) kv = -CMYK_THRESHOLD(0); + FSdiffuse(kv,errv[3],kerr,errv[3-dir]); + } + } + + } else { + + FSdiffuse(kv,errv[3],kerr,errv[3-dir]); + + if( errv[0] > EMAX(0)) errv[0] = EMAX(0); + else if(errv[0] < EMIN(0)) errv[0] = EMIN(0); + + if( errv[1] > EMAX(1)) errv[1] = EMAX(1); + else if(errv[1] < EMIN(1)) errv[1] = EMIN(1); + + if( errv[2] > EMAX(2)) errv[2] = EMAX(2); + else if(errv[2] < EMIN(2)) errv[2] = EMIN(2); + + } + +/* + * Adjust indices + */ + bit = dir > 0 ? (bit>>1) : (bit<<1); + if(bit == 0) { + /*KGHorig + if(((*out[0] = pixel[0]) != 0) && (width[0] < w)) width[0] = w; + if(((*out[1] = pixel[1]) != 0) && (width[1] < w)) width[1] = w; + if(((*out[2] = pixel[2]) != 0) && (width[2] < w)) width[2] = w; + if(((*out[3] = pixel[3]) != 0) && (width[3] < w)) width[3] = w; + */ + *out[0] = pixel[0]; + *out[1] = pixel[1]; + *out[2] = pixel[2]; + *out[3] = pixel[3]; + out[0] += thedir; out[1] += thedir; + out[2] += thedir; out[3] += thedir; + pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0; + + if(dir > 0) bit = 0x80; + else bit = 0x01; + w += dir>>2; + } + + in += dir; + errv += dir; + } /* loop over pixels */ + + /*KGHorig + if(0 == (sd->stc.flags & STCDFLAG1)) { + */ + if (1) { + cerr = errc[0] = cerr; + merr = errc[1] = merr; + yerr = errc[2] = yerr; + kerr = errc[3] = kerr; + } + +/* =========================================================== */ + } /* initialization or scanline-Processing */ +/* =========================================================== */ + + return 0; +} |