diff options
Diffstat (limited to 'devices/rinkj/rinkj-epson870.c')
-rw-r--r-- | devices/rinkj/rinkj-epson870.c | 1093 |
1 files changed, 1093 insertions, 0 deletions
diff --git a/devices/rinkj/rinkj-epson870.c b/devices/rinkj/rinkj-epson870.c new file mode 100644 index 000000000..3130b7100 --- /dev/null +++ b/devices/rinkj/rinkj-epson870.c @@ -0,0 +1,1093 @@ +/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael, + CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + + +/* A Rinkj driver for a number of variable-dot Epson devices. */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "rinkj-byte-stream.h" +#include "rinkj-device.h" +#include "rinkj-config.h" +#include "rinkj-epson870.h" + +typedef struct _RinkjEscp RinkjEscp; + +struct _RinkjEscp { + RinkjDevice super; + RinkjByteStream *out; + int width; + int height; + int y; + + char *manufacturer; + char *model; + + int num_chan; + int bps; /* bits per sample */ + + int xres; /* resolution of input image */ + int yres; + + int head_bps; + int head_xres; /* x resolution of printhead in dpi */ + int head_yres; /* y resolution of printhead in dpi */ + + /* Number of passes for a single scanline */ + int passes_per_scan; + + /* parameters for controlling microweaving */ + int spacing; + int n_pins; + + int plane_offsets[6]; + int max_offset; + + char *buf; + unsigned char *buf_linevalid; + int bufheight; + int rowstride; + int planestride; + int pass; + + int vertpos; /* for ESC ( v */ + + /* parameters passed into ESCP2 */ + int autocut; + int blankskip; + int microdot; + int unidir; + int printer_weave; +}; + +static int +rinkj_escp_set (RinkjDevice *self, const char *config) +{ + RinkjEscp *z = (RinkjEscp *)self; + const char *p, *next; + char *key, *val; + + for (p = config; (key = rinkj_config_keyval (p, &val, &next)); p = next) + { + if (!strcmp (key, "Resolution")) + { + char *p; + + z->xres = atoi (val); + p = strchr (val, 'x'); + if (p != NULL) + z->yres = atoi (p + 1); + else + z->yres = z->xres; +#ifdef VERBOSE + fprintf (stderr, "Resolution = %d x %d\n", z->xres, z->yres); +#endif + } + else if (!strcmp (key, "Manufacturer")) + { + if (z->manufacturer) + free (z->manufacturer); + z->manufacturer = val; + val = NULL; + } + else if (!strcmp (key, "Model")) + { + if (z->model) + free (z->model); + z->model = val; + val = NULL; + } + else if (!strcmp (key, "BitsPerSample")) + { + z->bps = atoi (val); +#ifdef VERBOSE + fprintf (stderr, "BitsPerSample = %d\n", z->bps); +#endif + } + else if (!strcmp (key, "NumChan")) + { + /* This is in params, but should become a setting. */ + z->num_chan = atoi (val); +#ifdef VERBOSE + fprintf (stderr, "NumChan = %d\n", z->num_chan); +#endif + } + else if (!strcmp (key, "PrinterWeave")) + { + z->printer_weave = atoi (val); + } + else if (!strcmp (key, "Microdot")) + { + z->microdot = atoi (val); + } + else if (!strcmp (key, "Unidirectional")) + { + z->unidir = atoi (val); + } + else if (!strcmp (key, "AutoCut")) + { + z->autocut = atoi (val); + } + else if (!strcmp (key, "BlankSkip")) + { + z->blankskip = atoi (val); + } + free (key); + if (val) + free (val); + } + return 0; +} + +static int +rinkj_escp_ytop (RinkjEscp *z, int pass, int *p_x_pass) +{ + int ytop; + int x_pass, y_pass; + int passes_per_scan = z->passes_per_scan; + int spacing = z->spacing; + int n_cycle = spacing * passes_per_scan; + int mod_pass = pass % n_cycle; + int y_modulo; + const int four[4] = { 0, 3, 1, 2 }; + const int six[6] = { 0, 3, 2, 5, 1, 4 }; + + ytop = mod_pass * z->n_pins * spacing / n_cycle; +#if 1 + x_pass = mod_pass / spacing; + y_pass = mod_pass % spacing; + if (passes_per_scan == 4) + x_pass = four[x_pass]; +#else + x_pass = mod_pass % passes_per_scan; + y_pass = (mod_pass / passes_per_scan + x_pass * (spacing - 1)) % spacing; +#endif + + switch (spacing) + { + case 4: + y_modulo = four[y_pass]; + break; + case 6: + y_modulo = six[y_pass]; + break; + case 8: + y_modulo = y_pass * 3; + break; + default: + y_modulo = y_pass; + break; + } + + ytop += (spacing + y_modulo - ytop % spacing) % spacing; + ytop += (pass / n_cycle) * spacing * z->n_pins; + + if (spacing == 4 && passes_per_scan == 4 && z->n_pins == 96) + { + const int sixteen[] = { 0, 3, 1, 0, 3, 1, 2, 3, 1, 2, 0, 1, 2, 0, 3, 2 }; + x_pass = sixteen[mod_pass & 15]; + ytop = pass * 23; + } + +#ifdef VERBOSE + fprintf (stderr, "pass %d: ytop = %d, x_pass = %d\n", pass, ytop, x_pass); +#endif + + if (p_x_pass) + *p_x_pass = x_pass; + return ytop; +} + +static int +rinkj_epson_headres (RinkjEscp *z, int baseres) +{ + return rinkj_byte_stream_printf (z->out, "\033(D\004%c%c%c%c%c", 0, + baseres & 255, baseres >> 8, + baseres / z->head_yres, + baseres / z->head_xres); +} + +static int +rinkj_epson_units (RinkjEscp *z, int xres, int yres, int baseres) +{ + return rinkj_byte_stream_printf (z->out, "\033(U\005%c%c%c%c%c%c", 0, + baseres / yres, + baseres / yres, + baseres / xres, + baseres & 255, baseres >> 8); +} + +static int +rinkj_epson_set_common (RinkjEscp *z) +{ + int status = 0; + + if (z->printer_weave >= 0) + /* set microweave */ + status = rinkj_byte_stream_printf (z->out, "\033(i\001%c%c", 0, + z->printer_weave); + + if (status == 0 && z->unidir >= 0) + /* set unidirectional */ + status = rinkj_byte_stream_printf (z->out, "\033U%c", z->unidir); + + if (status == 0 && z->microdot >= 0) + /* set dot size */ + status = rinkj_byte_stream_printf (z->out, "\033(e\002%c%c%c", 0, 0, + z->microdot); + + return status; +} + +/** + * Spit out a command string to resemble the gimp-print output as much + * as possible. + **/ +static int +rinkj_epson870_init (RinkjDevice *self, const RinkjDeviceParams *params) +{ + RinkjEscp *z = (RinkjEscp *)self; + + rinkj_byte_stream_printf (z->out, + "%c%c%c\033\001@EJL 1284.4\n", 0, 0, 0); + rinkj_byte_stream_printf (z->out, "@EJL \n\033@\033@"); + + /* remote string goes here, but is probably optional */ + + /* set graphics mode */ + rinkj_byte_stream_printf (z->out, "\033(G\001%c\001", 0); + + /* set units to 1/720" */ + rinkj_byte_stream_printf (z->out, "\033(U\005%c\002\002\002\240\005", 0); + + rinkj_epson_set_common(z); + + /* set page length to about 22.377 inches */ + rinkj_byte_stream_printf (z->out, "\033(C\002%cx\037", 0); + + /* ESC ( c */ + + /* ESC ( S */ + + /* set resolution to 360 x 120 dpi - should probably generate this + string from head_xres and head_yres */ + rinkj_byte_stream_printf (z->out, "\033(D\004%c@8x(", 0); + + /* ESC ( v */ + + /* ESC \ */ + return 0; +} + +/** + * Spit out a command string to resemble the gimp-print output as much + * as possible. + **/ +static int +rinkj_epson2200_init (RinkjDevice *self, const RinkjDeviceParams *params) +{ + RinkjEscp *z = (RinkjEscp *)self; + + rinkj_byte_stream_printf (z->out, + "%c%c%c\033\001@EJL 1284.4\n", 0, 0, 0); + rinkj_byte_stream_printf (z->out, "@EJL \n\033@\033@"); + + /* remote string */ + rinkj_byte_stream_printf( z->out, + "\033(R%c%c%cREMOTE1", 8, 0, 0); + rinkj_byte_stream_printf( z->out, + "PP\003%c%c\002%cPH\002%c%c\001SN\003%c%c\004k", + 0, 0, 0, 0, 0, 0, 0); + rinkj_byte_stream_printf( z->out, + "\033%c%c%c", 0, 0, 0); + + /* set graphics mode */ + rinkj_byte_stream_printf (z->out, "\033(G\001%c\001", 0); + + rinkj_epson_units(z, z->xres, z->yres, 2880); + + rinkj_epson_set_common(z); + + /* set page length to 40 inches */ + rinkj_byte_stream_printf (z->out, "\033(C\002%c\200p", 0); + + /* ESC ( c */ + + /* ESC ( S */ + + rinkj_epson_headres (z, 2880); + + /* ESC ( v */ + + /* ESC \ */ + return 0; +} + +/** + * Spit out a command string to resemble the gimp-print output as much + * as possible. + **/ +static int +rinkj_epson7600_init (RinkjDevice *self, const RinkjDeviceParams *params) +{ + RinkjEscp *z = (RinkjEscp *)self; + int pl; + int pw = 720 * 24; /* hardcode to 24 inch */ + +#if 0 + rinkj_byte_stream_printf (z->out, + "%c%c%c\033\001@EJL 1284.4\n", 0, 0, 0); + rinkj_byte_stream_printf (z->out, "@EJL \n"); +#endif + rinkj_byte_stream_printf (z->out, "\033@\033@"); + +#if 1 + /* remote string */ + rinkj_byte_stream_printf( z->out, + "\033(R%c%c%cREMOTE1", 8, 0, 0); + rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 1, 0); + rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 2, 6); + rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 3, 0); + rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 4, 129); + rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 5, 51); + rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 8, 0); + rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 9, 2); + rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 10, 0); + rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 128, 1); + rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 129, 0); + if (z->autocut >= 0) + rinkj_byte_stream_printf( z->out, "AC%c%c%c%c", 2, 0, 0, z->autocut); + if (z->blankskip >= 0) + rinkj_byte_stream_printf( z->out, "AC%c%c%c%c", 2, 0, 0, 64 + z->blankskip); + rinkj_byte_stream_printf( z->out, "DR%c%c%c%c%c%c", 4, 0, 0, 1, 0, 0); + rinkj_byte_stream_printf( z->out, "DR%c%c%c%c%c%c", 4, 0, 0, 0, 0, 0); + rinkj_byte_stream_printf( z->out, "PH%c%c%c%c", 2, 0, 0, 0); + rinkj_byte_stream_printf( z->out, "FP%c%c%c%c%c", 3, 0, 0, 0, 0); + rinkj_byte_stream_printf( z->out, "AC%c%c%c%c", 2, 0, 0, 64); + rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 132, 1); + rinkj_byte_stream_printf( z->out, "PP%c%c%c%c%c", 3, 0, 0, 3, 0); + rinkj_byte_stream_printf( z->out, "IK%c%c%c%c", 2, 0, 0, 1); + rinkj_byte_stream_printf( z->out, "EX%c%c%c%c%c%c%c%c", 6, 0, 0, 0, 0, 0, 20, 0); + rinkj_byte_stream_printf( z->out, + "\033%c%c%c", 0, 0, 0); +#endif + + /* set graphics mode */ + rinkj_byte_stream_printf (z->out, "\033(G\001%c\001", 0); + + /* set units to 1/720" */ + rinkj_byte_stream_printf (z->out, "\033(U\005%c\002\002\002\240\005", 0); + + rinkj_epson_set_common(z); + + pl = z->height * 720 / z->yres + 180; + /* set page length to page height + 1/4 inch */ + rinkj_byte_stream_printf (z->out, "\033(S\010%c%c%c%c%c%c%c%c%c", 0, + pw & 255, (pw >> 8) & 255, (pw >> 16) & 255, pw >> 24, + pl & 255, (pl >> 8) & 255, (pl >> 16) & 255, pl >> 24); + + rinkj_byte_stream_printf (z->out, "\033(c\010%c%c%c%c%c%c%c%c%c", 0, + 0, 0, 0, 0, + pl & 255, (pl >> 8) & 255, (pl >> 16) & 255, pl >> 24); + + rinkj_epson_headres (z, 2880); + + /* ESC ( v */ + + /* ESC \ */ + return 0; +} + +/** + * Spit out a command string to resemble the gimp-print output as much + * as possible. + **/ +static int +rinkj_epsonc80_init (RinkjDevice *self, const RinkjDeviceParams *params) +{ + RinkjEscp *z = (RinkjEscp *)self; + + rinkj_byte_stream_printf (z->out, + "%c%c%c\033\001@EJL 1284.4\n", 0, 0, 0); + rinkj_byte_stream_printf (z->out, "@EJL \n\033@\033@"); + + /* remote string goes here, but is probably optional */ + + /* set graphics mode */ + rinkj_byte_stream_printf (z->out, "\033(G\001%c\001", 0); + + /* set units to 1/720" */ + rinkj_byte_stream_printf (z->out, "\033(U\005%c\002\002\002\240\005", 0); + + rinkj_epson_set_common(z); + + /* set page length to about 22.377 inches */ + rinkj_byte_stream_printf (z->out, "\033(C\002%cx\037", 0); + + /* set margins (magic) */ + rinkj_byte_stream_printf (z->out, "\033(c\010%c\040\376\377\377\376\036%c%c", + 0, 0, 0); + + /* ESC ( c */ + + /* ESC ( S */ + + /* set resolution to 360 x 180 dpi - should probably generate this + string from head_xres and head_yres */ + rinkj_byte_stream_printf (z->out, "\033(D\004%c@8P(", 0); + + /* ESC ( v */ + + /* ESC \ */ + return 0; +} + +static int +rinkj_escp_init (RinkjDevice *self, const RinkjDeviceParams *params) +{ + RinkjEscp *z = (RinkjEscp *)self; + int resolution = 720; /* todo: make settable */ + int uweave; + int height, top, bottom; + int i; + + z->width = params->width; + z->height = params->height; + z->num_chan = params->n_planes; + + /* weaving stuff */ + z->pass = 0; + + /* 0 inch margins on top, 0.5 on bottom */ + top = 0 * resolution; + bottom = params->height + 0.5 * resolution; + height = params->height * resolution + resolution; + + /* some defaults */ + for (i = 0; i < sizeof(z->plane_offsets) / sizeof(z->plane_offsets[0]); i++) + z->plane_offsets[i] = 0; + +#ifdef VERBOSE + fprintf (stderr, "Manufacturer: %s; Model; %s\n", z->manufacturer, + z->model); +#endif + + if (z->model && !strcmp (z->model, "Stylus Photo 870")) + { + z->head_xres = 360; + z->head_yres = 120; + z->head_bps = 2; + z->n_pins = 48; + z->printer_weave = 0; + } + else if (z->model && !strcmp (z->model, "Stylus Photo 2200")) + { + z->head_xres = 360; + z->head_yres = 180; + z->head_bps = 2; + if (z->xres == 2880) + { + z->head_xres = 720; + z->head_bps = 1; + } + z->n_pins = 96; + z->printer_weave = 0; + z->plane_offsets[3] = z->yres / 360; + z->plane_offsets[4] = z->yres / 360; + z->plane_offsets[5] = z->yres / 360; + } + else if (z->model && !strcmp (z->model, "Stylus Photo 7600")) + { + z->head_xres = z->xres; + z->head_yres = z->yres; + z->head_bps = z->bps; + z->n_pins = 1; + } + else if (z->model && !strcmp (z->model, "Stylus C80")) + { + z->head_xres = 360; + z->head_yres = 180; + z->head_bps = 2; + z->n_pins = 60; + z->printer_weave = 0; + z->plane_offsets[0] = 480; + z->plane_offsets[1] = 240; + z->plane_offsets[2] = 480; + } + else + { + z->spacing = 1; + z->n_pins = 1; + } + + z->spacing = z->yres / z->head_yres; + z->passes_per_scan = z->xres / z->head_xres; + + /* microweave */ + uweave = (z->n_pins == 1); + + z->max_offset = 0; + for (i = 0; i < sizeof(z->plane_offsets) / sizeof(z->plane_offsets[0]); i++) + if (z->plane_offsets[i] > z->max_offset) + z->max_offset = z->plane_offsets[i]; + + z->y = rinkj_escp_ytop (z, z->spacing * z->passes_per_scan - 1, NULL) - + (z->spacing - 1) + z->max_offset; + + z->planestride = (z->width * z->bps + 7) >> 3; + z->rowstride = z->planestride * z->num_chan; + z->bufheight = 2048; /* todo: compute */ + z->buf = (char *)calloc (z->rowstride, z->bufheight); + z->buf_linevalid = (unsigned char *)calloc (z->num_chan, z->bufheight); + z->vertpos = -1; + + if (z->model && !strcmp (z->model, "Stylus Photo 870")) + rinkj_epson870_init (self, params); + else if (z->model && !strcmp (z->model, "Stylus Photo 2200")) + rinkj_epson2200_init (self, params); + else if (z->model && !strcmp (z->model, "Stylus Photo 7600")) + rinkj_epson7600_init (self, params); + else if (z->model && !strcmp (z->model, "Stylus C80")) + rinkj_epsonc80_init (self, params); + + /* todo: error checking */ + return 0; + +} + +/** + * rinkj_escp_shuffle_dblx: Shuffle bits for doubled X resolution. + * @dst: Where to store shuffled bits. + * @src: Source of bits. + * @pass: Pass number. + * @n_bytes: Number of bytes in @src. + * + * Samples half of the bits in @src, finely interleaved for Epson Stylus + * Color 1440 x 720 modes. If @pass is 0, then it selects bits 7, 5, 3, 1. + * If @pass is 1, then 6, 4, 2, 0. + * + * The number of bytes in @dst should be (@n_bytes + 1) >> 1. + **/ +static void +rinkj_escp_shuffle_dblx (char *dst, const char *src, int pass, int n_bytes) +{ + int i; + int n_dst; + unsigned char s0, s1; + + n_dst = n_bytes >> 1; + for (i = 0; i < n_dst; i++) + { + s0 = src[i * 2] << pass; + s1 = src[i * 2 + 1] << pass; + dst[i] = (s0 & 0x80) | ((s0 & 0x20) << 1) | ((s0 & 8) << 2) | ((s0 & 2) << 3) | + ((s1 & 0x80) >> 4) | ((s1 & 0x20) >> 3) | ((s1 & 8) >> 2) | ((s1 & 2) >> 1); + } + if (n_bytes & 1) + { + s0 = src[n_bytes - 1] << pass; + dst[n_dst] = (s0 & 0x80) | ((s0 & 0x20) << 1) | ((s0 & 8) << 2) | ((s0 & 2) << 3); + } +} + +/** + * rinkj_escp_shuffle_4pass_2bit: Shuffle bits for quadrupled X resolution. + * @dst: Where to store shuffled bits. + * @src: Source of bits. + * @pass: Pass number. + * @n_bytes: Number of bytes in @src. + * + * Samples the bits in @src for 4-pass, 1-bit operation. + * + * The number of bytes in @dst should be (@n_bytes + 1) >> 1. + **/ +static void +rinkj_escp_shuffle_4pass_1bit (char *dst, const char *src, int pass, int n_bytes) +{ + int i; + int n_dst; + unsigned char s0, s1, s2, s3; + int shift = pass; + + n_dst = n_bytes >> 2; + for (i = 0; i < n_dst; i++) + { + s0 = src[i * 4] << shift; + s1 = src[i * 4 + 1] << shift; + s2 = src[i * 4 + 2] << shift; + s3 = src[i * 4 + 3] << shift; + dst[i] = (s0 & 0x80) | ((s0 & 8) << 3) | + ((s1 & 0x80) >> 2) | ((s1 & 8) << 1) | + ((s2 & 0x80) >> 4) | ((s2 & 8) >> 1) | + ((s3 & 0x80) >> 6) | ((s3 & 8) >> 3); + } + if (n_bytes & 3) + { + char d = 0; + + for (i = 0; i < (n_bytes & 3); i++) + { + s0 = src[n_dst * 4 + i] << shift; + d |= ((s0 & 0x80) | ((s0 & 8) << 3)) >> (i << 1); + } + dst[n_dst] = d; + } +} + +/** + * rinkj_escp_shuffle_2pass_2bit: Shuffle bits for doubled X resolution. + * @dst: Where to store shuffled bits. + * @src: Source of bits. + * @pass: Pass number. + * @n_bytes: Number of bytes in @src. + * + * Samples the bits in @src for 2-pass, 2-bit operation. + * + * The number of bytes in @dst should be (@n_bytes + 1) >> 1. + **/ +static void +rinkj_escp_shuffle_2pass_2bit (char *dst, const char *src, int pass, int n_bytes) +{ + int i; + int n_dst; + unsigned char s0, s1; + int shift = pass << 1; + + n_dst = n_bytes >> 1; + for (i = 0; i < n_dst; i++) + { + s0 = src[i * 2] << shift; + s1 = src[i * 2 + 1] << shift; + dst[i] = (s0 & 0xc0) | ((s0 & 0x0c) << 2) | + ((s1 & 0xc0) >> 4) | ((s1 & 0x0c) >> 2); + } + if (n_bytes & 1) + { + s0 = src[n_bytes - 1] << shift; + dst[i] = (s0 & 0xc0) | ((s0 & 0x0c) << 2); + } +} + +/** + * rinkj_escp_shuffle_4pass_2bit: Shuffle bits for quadrupled X resolution. + * @dst: Where to store shuffled bits. + * @src: Source of bits. + * @pass: Pass number. + * @n_bytes: Number of bytes in @src. + * + * Samples the bits in @src for 4-pass, 2-bit operation. + * + * The number of bytes in @dst should be (@n_bytes + 1) >> 1. + **/ +static void +rinkj_escp_shuffle_4pass_2bit (char *dst, const char *src, int pass, int n_bytes) +{ + int i; + int n_dst; + unsigned char s0, s1, s2, s3; + int shift = pass << 1; + + n_dst = n_bytes >> 2; + for (i = 0; i < n_dst; i++) + { + s0 = src[i * 4] << shift; + s1 = src[i * 4 + 1] << shift; + s2 = src[i * 4 + 2] << shift; + s3 = src[i * 4 + 3] << shift; + dst[i] = (s0 & 0xc0) | ((s1 & 0xc0) >> 2) | + ((s2 & 0xc0) >> 4) | ((s3 & 0xc0) >> 6); + } + if (n_bytes & 3) + { + char d = 0; + + for (i = 0; i < (n_bytes & 3); i++) + d |= ((src[n_dst * 4 + i] << shift) & 0xc0) >> (i << 1); + dst[n_dst] = d; + } +} + +#define DOT 3 + +static void +rinkj_escp_1pass_dblx (char *dst, const char *src, int n_bytes) +{ + int i; + + /* todo: may need half-byte fixup */ + for (i = 0; i < n_bytes; i ++) + { + unsigned char s = src[i]; + dst[i * 2] = (((s & 0x80) >> 1) | ((s & 0x40) >> 2) | + ((s & 0x20) >> 3) | ((s & 0x10) >> 4)) * DOT; + dst[i * 2 + 1] = (((s & 8) << 3) | ((s & 4) << 2) | + ((s & 2) << 1) | (s & 1)) * DOT; + } +} + +static void +rinkj_escp_select_dblx (char *dst, const char *src, int x_pass, int n_bytes) +{ + int i; + + for (i = 0; i < n_bytes; i++) + dst[i] = (src[i] >> (1 - x_pass) & 0x55) * DOT; +} + +static void +rinkj_escp_sel_shuffle_dblx (char *dst, const char *src, int pass, int n_bytes) +{ + int i; + int n_dst; + unsigned char s0, s1; + + n_dst = n_bytes >> 1; + for (i = 0; i < n_dst; i++) + { + s0 = src[i * 2] << pass; + s1 = src[i * 2 + 1] << pass; + dst[i] = (((s0 & 0x80) >> 1) | ((s0 & 8) << 1) | + ((s1 & 0x80) >> 5) | ((s1 & 0x8) >> 3)) * DOT; + } + if (n_bytes & 1) + { + s0 = src[n_bytes - 1] << pass; + dst[n_dst] = (((s0 & 0x80) >> 1) | ((s0 & 8) << 1)) * DOT; + } +} + +static void +rinkj_escp_shuffle (char *dst, const char *src, int pass, int n_bytes, + int passes_per_scan, int bps, int head_bps) +{ + if (bps == 2 && head_bps == 2) + { + if (passes_per_scan == 1) + memcpy (dst, src, n_bytes); + else if (passes_per_scan == 2) + rinkj_escp_shuffle_2pass_2bit (dst, src, pass, n_bytes); + else if (passes_per_scan == 4) + rinkj_escp_shuffle_4pass_2bit (dst, src, pass, n_bytes); + } + else if (bps == 1 && head_bps == 2) + { + if (passes_per_scan == 1) + rinkj_escp_1pass_dblx (dst, src, n_bytes); + if (passes_per_scan == 2) + rinkj_escp_select_dblx (dst, src, pass, n_bytes); + else if (passes_per_scan == 4) + rinkj_escp_sel_shuffle_dblx (dst, src, pass, n_bytes); + } + else if (bps == 1 && head_bps == 1) + { + if (passes_per_scan == 1) + memcpy (dst, src, n_bytes); + else if (passes_per_scan == 2) + rinkj_escp_shuffle_dblx (dst, src, pass, n_bytes); + else if (passes_per_scan == 4) + rinkj_escp_shuffle_4pass_1bit (dst, src, pass, n_bytes); + } +} + +/** + * rinkj_escp_compress_rle: Compress a chunk of data using runlengths. + * @dst: Where to store compressed data. + * @src: Source of data. + * @n: Size of @src in bytes. + * + * Compresses a chunk of data using ESCP/2 runlength encoding. @dst must be + * at least @n + ((@n + 127) >> 7) bytes long. + * + * Return value: size of @dst in bytes. + **/ +static int +rinkj_escp_compress_rle (char *dst, const char *src, int n) +{ + int i, j; + int b; + int run; + + j = 0; + for (i = 0; i < n; i += run) + { + b = src[i]; + for (run = 1; run < 129 && i + run < n; run++) + if (b != src[i + run]) + break; + if (run > 2) + { + dst[j++] = 257 - run; + dst[j++] = b; + } + else + { + for (run = 1; run < 128 && i + run < n; run++) + { + b = src[i + run]; + if (i + run + 2 < n && + b == src[i + run + 1] && b == src[i + run + 2]) + break; + } + dst[j++] = run - 1; + memcpy (dst + j, src + i, run); + j += run; + } + } + + return j; +} + +static int +rinkj_escp_flush (RinkjEscp *z) +{ + int xsb, xsb_out; + int xs_out; + int status; + const int plane[7] = {3, 1, 0, 2, 5, 4, 6}; + const int color[7] = {0, 1, 2, 4, 17, 18, 16}; + int i, j; + int ytop, ysc; + int bufy; + char *thisbuf; + char *compress_buf = NULL; + int rle = 1; + int x_pass; + char pass_mask; + int m; + + ytop = rinkj_escp_ytop (z, z->pass, &x_pass); + pass_mask = 1 << x_pass; + +#ifdef VERBOSE + fprintf (stderr, "flush pass %d: ytop = %d (= %d mod %d), x_pass = %d\n", + z->pass, ytop, ytop % z->spacing, z->spacing, x_pass); +#endif + + xsb = (z->width * z->bps + 7) >> 3; + + xs_out = (z->width + z->passes_per_scan - 1) / (z->passes_per_scan); + xsb_out = (((z->width * z->head_bps + 7) >> 3) + z->passes_per_scan - 1) / + (z->passes_per_scan); + + thisbuf = malloc (xsb_out); + if (rle) + compress_buf = malloc (xsb_out + ((xsb_out + 127) >> 7)); + ysc = ytop; + if (z->vertpos == -1) + status = rinkj_byte_stream_printf (z->out, "\033(V%c%c%c%c", + 2, 0, ysc & 0xff, (ysc >> 8) & 0xff); + else + { + int yrel = ysc - z->vertpos; + status = rinkj_byte_stream_printf (z->out, "\033(v%c%c%c%c%c%c", + 4, 0, yrel & 0xff, (yrel >> 8) & 0xff, (yrel >> 16) & 0xff, (yrel >> 24) & 0xff); + } + + z->vertpos = ysc; + + if (status < 0) + { + free (thisbuf); + if (rle) + free (compress_buf); + return status; + } + + for (i = 0; i < z->num_chan; i++) + { + int plane_off = z->plane_offsets[i]; + + m = (z->y - ytop + z->spacing - 1 - plane_off) / z->spacing; + if (m > z->n_pins) + m = z->n_pins; + + if (m <= 0) + continue; + + if (1) + { + /* todo: make this calculation fully agile */ + int x = (x_pass & 3); + status = rinkj_byte_stream_printf (z->out, "\033($\4%c%c%c%c%c", + 0, x & 0xff, + (x >> 8) & 0xff, + (x >> 16) & 0xff, + (x >> 24) & 0xff); + + if (status < 0) + { + free(thisbuf); + if (rle) + free(compress_buf); + return status; + } + } + + status = rinkj_byte_stream_printf (z->out, "\033i%c%c%c%c%c%c%c", + color[i], + rle, + z->head_bps, + xsb_out & 0xff, + (xsb_out >> 8) & 0xff, + m & 0xff, m >> 8); + if (status < 0) + { + free(thisbuf); + if (rle) + free(compress_buf); + return status; + } + for (j = 0; j < m; j++) + { + const char *line; + + bufy = (ytop + j * z->spacing + plane_off) % z->bufheight; + line = z->buf + bufy * z->rowstride + plane[i] * z->planestride; + if (z->buf_linevalid[bufy * z->num_chan + i] & pass_mask) + rinkj_escp_shuffle (thisbuf, line, x_pass, xsb, + z->passes_per_scan, z->bps, z->head_bps); + else + memset (thisbuf, 0, xsb_out); + z->buf_linevalid[bufy * z->num_chan + i] &= ~pass_mask; +#ifdef VERBOSE + if (i == 0 && j == 0) + { + fprintf (stderr, "flush line[0] = %d shuffled[0] = %d\n", + line[0], thisbuf[0]); + } +#endif + if (rle) + { + int nbytes; + + nbytes = rinkj_escp_compress_rle (compress_buf, thisbuf, xsb_out); + status = rinkj_byte_stream_write (z->out, compress_buf, nbytes); + } + else + status = rinkj_byte_stream_write (z->out, thisbuf, xsb_out); + } + if (status < 0) return status; +#if 0 + status = rinkj_byte_stream_puts (z->out, "\r"); + if (status < 0) return status; +#endif + } + + z->pass++; + if (rle) + free (compress_buf); + free (thisbuf); + return status; +} + +static int +rinkj_escp_flush_bottom (RinkjEscp *z) +{ + int ytop; + int status; + + for (;;) + { + ytop = rinkj_escp_ytop (z, z->pass, NULL); + if (ytop >= z->y) + break; + status = rinkj_escp_flush (z); + if (status != 0) + return status; + } + return 0; +} + +static void +rinkj_escp_free (RinkjEscp *z) +{ + if (z->manufacturer) + free (z->manufacturer); + if (z->model) + free (z->model); + free (z->buf); + free (z->buf_linevalid); + free (z); +} + +static int +rinkj_escp_write (RinkjDevice *self, const char **data) +{ + RinkjEscp *z = (RinkjEscp *)self; + int xsb; + int status; + int i; + int ytop; + int bufy; + int dblx_pass; + + if (data == NULL) + { + status = rinkj_escp_flush_bottom (z); + /* todo: check error */ + status = rinkj_byte_stream_puts (z->out, "\f\033@"); + /* todo: check error */ + status = rinkj_byte_stream_close (z->out); + rinkj_escp_free (z); + return status; + } + + xsb = (z->width * z->bps + 7) >> 3; + bufy = z->y % z->bufheight; + for (i = 0; i < z->num_chan; i++) + { + memcpy (z->buf + bufy * z->rowstride + i * z->planestride, data[i], xsb); + z->buf_linevalid[bufy * z->num_chan + i] = 0xff; + } + + z->y++; + + ytop = rinkj_escp_ytop (z, z->pass, &dblx_pass); + + if (z->y < ytop + (z->n_pins - 1) * z->spacing + 1 + z->max_offset) + return 0; + + return rinkj_escp_flush (z); +} + +RinkjDevice * +rinkj_epson870_new (RinkjByteStream *out) +{ + RinkjEscp *result; + + result = (RinkjEscp *)malloc (sizeof (RinkjEscp)); + + result->super.set = rinkj_escp_set; + result->super.write = rinkj_escp_write; + result->super.init = rinkj_escp_init; + result->super.init_happened = 0; + result->width = 0; + result->out = out; + + result->num_chan = 4; + result->bps = 1; + + result->manufacturer = NULL; + result->model = NULL; + + result->autocut = -1; + result->microdot = -1; + result->unidir = -1; + result->printer_weave = -1; + + return &result->super; +} |