/* Copyright (C) 1995, 1996 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gdevstc4.c,v 1.2.6.1.2.1 2003/01/17 00:49:01 giles Exp $*/ /* Epson Stylus-Color Printer-Driver */ /*** This file holds a byte-Implementation of the Floyd-Steinberg error diffusion-algorithm. This algorithm is an alternative for high quality printing in conjunction with the PostScript-Header stcolor.ps: gs -sDEVICE=stcolor -sDithering=fs2 stcolor.ps ... THIS ALGORIHM WAS WRITTEN BY STEVEN SINGER (S.Singer@ph.surrey.ac.uk) AS PART OF escp2cfs2. THIS IMPLEMENTATION INCORPORATES ONLY FEW CHANGES TO THE ORIGINAL CODE. ***/ #include "gdevstc.h" /* * escp2c_pick best scans for best matching color */ private byte * escp2c_pick_best(byte *col) { static byte colour[8][3] = { { 0, 0, 0},{255, 0, 0},{ 0,255, 0},{255,255, 0}, { 0, 0,255},{255, 0,255},{ 0,255,255},{255,255,255}}; register int x, y, z, dx, dy, dz, dz2, dx2, dx3, dx4; register byte *p; register long md, d; md = 16777216; /* plenty */ /* Note that I don't use a simple distance algorithm. That can lead to a block of (130,127,127) being dithered as red-cyan. This algorithm should make it use black-white-red. This is very important, as a coloured block in the middle of a grey block can, via error diffusion, perturb the surrounding colours sufficiently for this to happen. */ /* The next bit is equivalent to this, but faster. x = col[0]; y = col[1]; z = col[2]; for(n=8; n--; ) { dx = x - colour[n][0]; dy = y - colour[n][1]; dz = z - colour[n][2]; d = dx*(dx-(dy>>1)) + dy*(dy-(dz>>1)) + dz*(dz-(dx>>1)); if (d < md) { md = d; p = n; } } */ /* * Test colours in gray code order to reduce number of recalculations. * I bet you can't find an optimiser that would do this automatically. */ x = col[0]; y = col[1]; z = col[2]; dx = x*(x-(y>>1)); dy = y*(y-(z>>1)); dz = z*(z-(x>>1)); md = dx + dy + dz; p = colour[0]; x -= 255; dx2 = x*(x-(y>>1)); dz2 = z*(z-(x>>1)); if ((d = dx2 + dy + dz2) < md) {md = d; p = colour[1];} y -= 255; dx3 = x*(x-(y>>1)); dy = y*(y-(z>>1)); if ((d = dx3 + dy + dz2) < md) {md = d; p = colour[3];} x += 255; dx4 = x*(x-(y>>1)); if ((d = dx4 + dy + dz) < md) {md = d; p = colour[2];} z -= 255; dy = y*(y-(z>>1)); dz = z*(z-(x>>1)); if ((d = dx4 + dy + dz) < md) {md = d; p = colour[6];} x -= 255; dz2 = z*(z-(x>>1)); if ((d = dx3 + dy + dz2) < md) {md = d; p = colour[7];} y += 255; dy = y*(y-(z>>1)); if ((d = dx2 + dy + dz2) < md) {md = d; p = colour[5];} if ((d = dx + dy + dz) < md) {p = colour[4];} return(p); } /* * escp2c_conv_stc converts into the ouput format used by stcolor */ private void escp2c_conv_stc(byte *p, byte *q, int i) { for(; i; p+=3, i-=3) *q++ = (*p & RED) | (p[1] & GREEN) | (p[2] & BLUE); } /* * Limit byte-values */ #define LIMIT(a) if (a > 255) a = 255; if (a < 0) a = 0 #define LIMIT2(a) if (a > 127) a = 127; if (a < -128) a = -128; \ if (a < 0) a += 256 /* * Main routine of the algorithm */ int stc_fs2(stcolor_device *sd,int npixel,byte *in,byte *buf,byte *out) { int fullcolor_line_size = npixel*3; /* ============================================================= */ if(npixel > 0) { /* npixel > 0 -> scanline-processing */ /* ============================================================= */ /* -------------------------------------------------------------------- */ if(in == NULL) { /* clear the error-buffer upon white-lines */ /* -------------------------------------------------------------------- */ memset(buf,0,fullcolor_line_size); /* ------------------------------------------------------------------- */ } else { /* do the actual dithering */ /* ------------------------------------------------------------------- */ int i, j, k, e, l, i2, below[3][3], *fb, *b, *bb, *tb; byte *p, *q, *cp; static int dir = 1; p = buf; if (*p != 0 || memcmp((char *) p, (char *) p + 1, fullcolor_line_size - 1)) { for(p = in, q=buf, i=fullcolor_line_size; i--; p++, q++ ) { j = *p + ((*q & 128) ? *q - 256 : *q); LIMIT(j); *p = j; } } p = in; fb = below[2]; b = below[1]; bb = below[0]; *b = b[1] = b[2] = *bb = bb[1] = bb[2] = 0; if (dir) { for(p = in, q=buf-3, i=fullcolor_line_size; i; i-=3) { cp = escp2c_pick_best(p); for(i2=3; i2--; p++, q++, fb++, b++, bb++) { j = *p; *p = *cp++; j -= *p; if (j != 0) { l = (e = (j>>1)) - (*fb = (j>>4)); if (i > 2) { k = p[3] + l; LIMIT(k); p[3] = k; } *b += e - (l = (j>>2) - *fb); if (i < fullcolor_line_size) { l += *bb; LIMIT2(l); *q = l; } } else *fb = 0; } tb = bb-3; bb = b-3; b = fb-3; fb = tb; } *q = *bb; q[1] = bb[1]; q[2] = bb[2]; dir = 0; } else { for(p = in+fullcolor_line_size-1, q = buf+fullcolor_line_size+2, i=fullcolor_line_size; i; i-=3) { cp = escp2c_pick_best(p-2) + 2; for(i2=3; i2--; p--, q--, fb++, b++, bb++) { j = *p; *p = *cp--; j -= *p; if (j != 0) { l = (e = (j>>1)) - (*fb = (j>>4)); if (i > 2) { k = p[-3] + l; LIMIT(k); p[-3] = k; } *b += e - (l = (j>>2) - *fb); if (i < fullcolor_line_size) { l += *bb; LIMIT2(l); *q = l; } } else *fb = 0; } tb = bb-3; bb = b-3; b = fb-3; fb = tb; } *q = *bb; q[1] = bb[1]; q[2] = bb[2]; dir = 1; } escp2c_conv_stc(in, out, fullcolor_line_size); /* ------------------------------------------------------------------- */ } /* buffer-reset | dithering */ /* ------------------------------------------------------------------- */ /* ============================================================= */ } else { /* npixel <= 0 -> initialisation */ /* ============================================================= */ /* * check wether the number of components is valid */ if(sd->color_info.num_components != 3) return -1; /* * check wether stcdither & TYPE are correct */ if(( sd->stc.dither == NULL) || ((sd->stc.dither->flags & STC_TYPE) != STC_BYTE)) return -2; /* * check wether the buffer-size is sufficiently large */ if((sd->stc.dither->flags/STC_SCAN) < 1) return -3; /* * finally clear the buffer */ memset(buf,0,-fullcolor_line_size); /* ============================================================= */ } /* scanline-processing or initialisation */ /* ============================================================= */ return 0; }