17dd7cddfSDavid du Colombier /* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
27dd7cddfSDavid du Colombier
3*593dc095SDavid du Colombier This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier implied.
57dd7cddfSDavid du Colombier
6*593dc095SDavid du Colombier This software is distributed under license and may not be copied,
7*593dc095SDavid du Colombier modified or distributed except as expressly authorized under the terms
8*593dc095SDavid du Colombier of the license contained in the file LICENSE in this distribution.
97dd7cddfSDavid du Colombier
10*593dc095SDavid du Colombier For more information about licensing, please refer to
11*593dc095SDavid du Colombier http://www.ghostscript.com/licensing/. For information on
12*593dc095SDavid du Colombier commercial licensing, go to http://www.artifex.com/licensing/ or
13*593dc095SDavid du Colombier contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14*593dc095SDavid du Colombier San Rafael, CA 94903, U.S.A., +1(415)492-9861.
157dd7cddfSDavid du Colombier */
167dd7cddfSDavid du Colombier
17*593dc095SDavid du Colombier /* $Id: gdevstc4.c,v 1.4 2002/02/21 22:24:52 giles Exp $*/
187dd7cddfSDavid du Colombier /* Epson Stylus-Color Printer-Driver */
197dd7cddfSDavid du Colombier
207dd7cddfSDavid du Colombier /***
217dd7cddfSDavid du Colombier This file holds a byte-Implementation of the Floyd-Steinberg error
227dd7cddfSDavid du Colombier diffusion-algorithm. This algorithm is an alternative for high quality
237dd7cddfSDavid du Colombier printing in conjunction with the PostScript-Header stcolor.ps:
247dd7cddfSDavid du Colombier
257dd7cddfSDavid du Colombier gs -sDEVICE=stcolor -sDithering=fs2 <other options> stcolor.ps ...
267dd7cddfSDavid du Colombier
277dd7cddfSDavid du Colombier THIS ALGORIHM WAS WRITTEN BY STEVEN SINGER (S.Singer@ph.surrey.ac.uk)
287dd7cddfSDavid du Colombier AS PART OF escp2cfs2.
297dd7cddfSDavid du Colombier THIS IMPLEMENTATION INCORPORATES ONLY FEW CHANGES TO THE ORIGINAL CODE.
307dd7cddfSDavid du Colombier
317dd7cddfSDavid du Colombier ***/
327dd7cddfSDavid du Colombier
337dd7cddfSDavid du Colombier #include "gdevstc.h"
347dd7cddfSDavid du Colombier
357dd7cddfSDavid du Colombier /*
367dd7cddfSDavid du Colombier * escp2c_pick best scans for best matching color
377dd7cddfSDavid du Colombier */
387dd7cddfSDavid du Colombier private byte *
escp2c_pick_best(byte * col)397dd7cddfSDavid du Colombier escp2c_pick_best(byte *col)
407dd7cddfSDavid du Colombier {
417dd7cddfSDavid du Colombier static byte colour[8][3] = {
427dd7cddfSDavid du Colombier { 0, 0, 0},{255, 0, 0},{ 0,255, 0},{255,255, 0},
437dd7cddfSDavid du Colombier { 0, 0,255},{255, 0,255},{ 0,255,255},{255,255,255}};
447dd7cddfSDavid du Colombier register int x, y, z, dx, dy, dz, dz2, dx2, dx3, dx4;
457dd7cddfSDavid du Colombier register byte *p;
467dd7cddfSDavid du Colombier register long md, d;
477dd7cddfSDavid du Colombier
487dd7cddfSDavid du Colombier md = 16777216; /* plenty */
497dd7cddfSDavid du Colombier
507dd7cddfSDavid du Colombier /*
517dd7cddfSDavid du Colombier Note that I don't use a simple distance algorithm. That can lead to a block
527dd7cddfSDavid du Colombier of (130,127,127) being dithered as red-cyan. This algorithm should make
537dd7cddfSDavid du Colombier it use black-white-red. This is very important, as a coloured block in
547dd7cddfSDavid du Colombier the middle of a grey block can, via error diffusion, perturb the
557dd7cddfSDavid du Colombier surrounding colours sufficiently for this to happen.
567dd7cddfSDavid du Colombier */
577dd7cddfSDavid du Colombier
587dd7cddfSDavid du Colombier /*
597dd7cddfSDavid du Colombier The next bit is equivalent to this, but faster.
607dd7cddfSDavid du Colombier
617dd7cddfSDavid du Colombier x = col[0];
627dd7cddfSDavid du Colombier y = col[1];
637dd7cddfSDavid du Colombier z = col[2];
647dd7cddfSDavid du Colombier for(n=8; n--; )
657dd7cddfSDavid du Colombier {
667dd7cddfSDavid du Colombier dx = x - colour[n][0];
677dd7cddfSDavid du Colombier dy = y - colour[n][1];
687dd7cddfSDavid du Colombier dz = z - colour[n][2];
697dd7cddfSDavid du Colombier d = dx*(dx-(dy>>1)) + dy*(dy-(dz>>1)) + dz*(dz-(dx>>1));
707dd7cddfSDavid du Colombier if (d < md)
717dd7cddfSDavid du Colombier {
727dd7cddfSDavid du Colombier md = d;
737dd7cddfSDavid du Colombier p = n;
747dd7cddfSDavid du Colombier }
757dd7cddfSDavid du Colombier }
767dd7cddfSDavid du Colombier */
777dd7cddfSDavid du Colombier
787dd7cddfSDavid du Colombier /*
797dd7cddfSDavid du Colombier * Test colours in gray code order to reduce number of recalculations.
807dd7cddfSDavid du Colombier * I bet you can't find an optimiser that would do this automatically.
817dd7cddfSDavid du Colombier */
827dd7cddfSDavid du Colombier
837dd7cddfSDavid du Colombier x = col[0];
847dd7cddfSDavid du Colombier y = col[1];
857dd7cddfSDavid du Colombier z = col[2];
867dd7cddfSDavid du Colombier dx = x*(x-(y>>1));
877dd7cddfSDavid du Colombier dy = y*(y-(z>>1));
887dd7cddfSDavid du Colombier dz = z*(z-(x>>1));
897dd7cddfSDavid du Colombier md = dx + dy + dz;
907dd7cddfSDavid du Colombier p = colour[0];
917dd7cddfSDavid du Colombier x -= 255;
927dd7cddfSDavid du Colombier dx2 = x*(x-(y>>1));
937dd7cddfSDavid du Colombier dz2 = z*(z-(x>>1));
947dd7cddfSDavid du Colombier if ((d = dx2 + dy + dz2) < md) {md = d; p = colour[1];}
957dd7cddfSDavid du Colombier y -= 255;
967dd7cddfSDavid du Colombier dx3 = x*(x-(y>>1));
977dd7cddfSDavid du Colombier dy = y*(y-(z>>1));
987dd7cddfSDavid du Colombier if ((d = dx3 + dy + dz2) < md) {md = d; p = colour[3];}
997dd7cddfSDavid du Colombier x += 255;
1007dd7cddfSDavid du Colombier dx4 = x*(x-(y>>1));
1017dd7cddfSDavid du Colombier if ((d = dx4 + dy + dz) < md) {md = d; p = colour[2];}
1027dd7cddfSDavid du Colombier z -= 255;
1037dd7cddfSDavid du Colombier dy = y*(y-(z>>1));
1047dd7cddfSDavid du Colombier dz = z*(z-(x>>1));
1057dd7cddfSDavid du Colombier if ((d = dx4 + dy + dz) < md) {md = d; p = colour[6];}
1067dd7cddfSDavid du Colombier x -= 255;
1077dd7cddfSDavid du Colombier dz2 = z*(z-(x>>1));
1087dd7cddfSDavid du Colombier if ((d = dx3 + dy + dz2) < md) {md = d; p = colour[7];}
1097dd7cddfSDavid du Colombier y += 255;
1107dd7cddfSDavid du Colombier dy = y*(y-(z>>1));
1117dd7cddfSDavid du Colombier if ((d = dx2 + dy + dz2) < md) {md = d; p = colour[5];}
1127dd7cddfSDavid du Colombier if ((d = dx + dy + dz) < md) {p = colour[4];}
1137dd7cddfSDavid du Colombier return(p);
1147dd7cddfSDavid du Colombier }
1157dd7cddfSDavid du Colombier
1167dd7cddfSDavid du Colombier /*
1177dd7cddfSDavid du Colombier * escp2c_conv_stc converts into the ouput format used by stcolor
1187dd7cddfSDavid du Colombier */
1197dd7cddfSDavid du Colombier private void
escp2c_conv_stc(byte * p,byte * q,int i)1207dd7cddfSDavid du Colombier escp2c_conv_stc(byte *p, byte *q, int i)
1217dd7cddfSDavid du Colombier {
1227dd7cddfSDavid du Colombier for(; i; p+=3, i-=3)
1237dd7cddfSDavid du Colombier *q++ = (*p & RED) | (p[1] & GREEN) | (p[2] & BLUE);
1247dd7cddfSDavid du Colombier }
1257dd7cddfSDavid du Colombier
1267dd7cddfSDavid du Colombier
1277dd7cddfSDavid du Colombier /*
1287dd7cddfSDavid du Colombier * Limit byte-values
1297dd7cddfSDavid du Colombier */
1307dd7cddfSDavid du Colombier #define LIMIT(a) if (a > 255) a = 255; if (a < 0) a = 0
1317dd7cddfSDavid du Colombier #define LIMIT2(a) if (a > 127) a = 127; if (a < -128) a = -128; \
1327dd7cddfSDavid du Colombier if (a < 0) a += 256
1337dd7cddfSDavid du Colombier /*
1347dd7cddfSDavid du Colombier * Main routine of the algorithm
1357dd7cddfSDavid du Colombier */
1367dd7cddfSDavid du Colombier int
stc_fs2(stcolor_device * sd,int npixel,byte * in,byte * buf,byte * out)1377dd7cddfSDavid du Colombier stc_fs2(stcolor_device *sd,int npixel,byte *in,byte *buf,byte *out)
1387dd7cddfSDavid du Colombier {
1397dd7cddfSDavid du Colombier int fullcolor_line_size = npixel*3;
1407dd7cddfSDavid du Colombier
1417dd7cddfSDavid du Colombier /* ============================================================= */
1427dd7cddfSDavid du Colombier if(npixel > 0) { /* npixel > 0 -> scanline-processing */
1437dd7cddfSDavid du Colombier /* ============================================================= */
1447dd7cddfSDavid du Colombier
1457dd7cddfSDavid du Colombier /* -------------------------------------------------------------------- */
1467dd7cddfSDavid du Colombier if(in == NULL) { /* clear the error-buffer upon white-lines */
1477dd7cddfSDavid du Colombier /* -------------------------------------------------------------------- */
1487dd7cddfSDavid du Colombier
1497dd7cddfSDavid du Colombier memset(buf,0,fullcolor_line_size);
1507dd7cddfSDavid du Colombier
1517dd7cddfSDavid du Colombier /* ------------------------------------------------------------------- */
1527dd7cddfSDavid du Colombier } else { /* do the actual dithering */
1537dd7cddfSDavid du Colombier /* ------------------------------------------------------------------- */
1547dd7cddfSDavid du Colombier int i, j, k, e, l, i2, below[3][3], *fb, *b, *bb, *tb;
1557dd7cddfSDavid du Colombier byte *p, *q, *cp;
1567dd7cddfSDavid du Colombier static int dir = 1;
1577dd7cddfSDavid du Colombier
1587dd7cddfSDavid du Colombier p = buf;
1597dd7cddfSDavid du Colombier if (*p != 0 || memcmp((char *) p, (char *) p + 1, fullcolor_line_size - 1))
1607dd7cddfSDavid du Colombier {
1617dd7cddfSDavid du Colombier for(p = in, q=buf, i=fullcolor_line_size;
1627dd7cddfSDavid du Colombier i--; p++, q++ )
1637dd7cddfSDavid du Colombier {
1647dd7cddfSDavid du Colombier j = *p + ((*q & 128) ? *q - 256 : *q);
1657dd7cddfSDavid du Colombier LIMIT(j);
1667dd7cddfSDavid du Colombier *p = j;
1677dd7cddfSDavid du Colombier }
1687dd7cddfSDavid du Colombier }
1697dd7cddfSDavid du Colombier
1707dd7cddfSDavid du Colombier p = in;
1717dd7cddfSDavid du Colombier
1727dd7cddfSDavid du Colombier fb = below[2];
1737dd7cddfSDavid du Colombier b = below[1];
1747dd7cddfSDavid du Colombier bb = below[0];
1757dd7cddfSDavid du Colombier *b = b[1] = b[2] = *bb = bb[1] = bb[2] = 0;
1767dd7cddfSDavid du Colombier
1777dd7cddfSDavid du Colombier if (dir)
1787dd7cddfSDavid du Colombier {
1797dd7cddfSDavid du Colombier for(p = in, q=buf-3,
1807dd7cddfSDavid du Colombier i=fullcolor_line_size; i; i-=3)
1817dd7cddfSDavid du Colombier {
1827dd7cddfSDavid du Colombier cp = escp2c_pick_best(p);
1837dd7cddfSDavid du Colombier for(i2=3; i2--; p++, q++, fb++, b++, bb++)
1847dd7cddfSDavid du Colombier {
1857dd7cddfSDavid du Colombier j = *p;
1867dd7cddfSDavid du Colombier *p = *cp++;
1877dd7cddfSDavid du Colombier j -= *p;
1887dd7cddfSDavid du Colombier if (j != 0)
1897dd7cddfSDavid du Colombier {
1907dd7cddfSDavid du Colombier l = (e = (j>>1)) - (*fb = (j>>4));
1917dd7cddfSDavid du Colombier if (i > 2)
1927dd7cddfSDavid du Colombier {
1937dd7cddfSDavid du Colombier k = p[3] + l;
1947dd7cddfSDavid du Colombier LIMIT(k);
1957dd7cddfSDavid du Colombier p[3] = k;
1967dd7cddfSDavid du Colombier }
1977dd7cddfSDavid du Colombier *b += e - (l = (j>>2) - *fb);
1987dd7cddfSDavid du Colombier if (i < fullcolor_line_size)
1997dd7cddfSDavid du Colombier {
2007dd7cddfSDavid du Colombier l += *bb;
2017dd7cddfSDavid du Colombier LIMIT2(l);
2027dd7cddfSDavid du Colombier *q = l;
2037dd7cddfSDavid du Colombier }
2047dd7cddfSDavid du Colombier }
2057dd7cddfSDavid du Colombier else
2067dd7cddfSDavid du Colombier *fb = 0;
2077dd7cddfSDavid du Colombier }
2087dd7cddfSDavid du Colombier tb = bb-3;
2097dd7cddfSDavid du Colombier bb = b-3;
2107dd7cddfSDavid du Colombier b = fb-3;
2117dd7cddfSDavid du Colombier fb = tb;
2127dd7cddfSDavid du Colombier }
2137dd7cddfSDavid du Colombier *q = *bb;
2147dd7cddfSDavid du Colombier q[1] = bb[1];
2157dd7cddfSDavid du Colombier q[2] = bb[2];
2167dd7cddfSDavid du Colombier dir = 0;
2177dd7cddfSDavid du Colombier }
2187dd7cddfSDavid du Colombier else
2197dd7cddfSDavid du Colombier {
2207dd7cddfSDavid du Colombier for(p = in+fullcolor_line_size-1,
2217dd7cddfSDavid du Colombier q = buf+fullcolor_line_size+2, i=fullcolor_line_size;
2227dd7cddfSDavid du Colombier i; i-=3)
2237dd7cddfSDavid du Colombier {
2247dd7cddfSDavid du Colombier cp = escp2c_pick_best(p-2) + 2;
2257dd7cddfSDavid du Colombier for(i2=3; i2--; p--, q--, fb++, b++, bb++)
2267dd7cddfSDavid du Colombier {
2277dd7cddfSDavid du Colombier j = *p;
2287dd7cddfSDavid du Colombier *p = *cp--;
2297dd7cddfSDavid du Colombier j -= *p;
2307dd7cddfSDavid du Colombier if (j != 0)
2317dd7cddfSDavid du Colombier {
2327dd7cddfSDavid du Colombier l = (e = (j>>1)) - (*fb = (j>>4));
2337dd7cddfSDavid du Colombier if (i > 2)
2347dd7cddfSDavid du Colombier {
2357dd7cddfSDavid du Colombier k = p[-3] + l;
2367dd7cddfSDavid du Colombier LIMIT(k);
2377dd7cddfSDavid du Colombier p[-3] = k;
2387dd7cddfSDavid du Colombier }
2397dd7cddfSDavid du Colombier *b += e - (l = (j>>2) - *fb);
2407dd7cddfSDavid du Colombier if (i < fullcolor_line_size)
2417dd7cddfSDavid du Colombier {
2427dd7cddfSDavid du Colombier l += *bb;
2437dd7cddfSDavid du Colombier LIMIT2(l);
2447dd7cddfSDavid du Colombier *q = l;
2457dd7cddfSDavid du Colombier }
2467dd7cddfSDavid du Colombier }
2477dd7cddfSDavid du Colombier else
2487dd7cddfSDavid du Colombier *fb = 0;
2497dd7cddfSDavid du Colombier }
2507dd7cddfSDavid du Colombier tb = bb-3;
2517dd7cddfSDavid du Colombier bb = b-3;
2527dd7cddfSDavid du Colombier b = fb-3;
2537dd7cddfSDavid du Colombier fb = tb;
2547dd7cddfSDavid du Colombier }
2557dd7cddfSDavid du Colombier *q = *bb;
2567dd7cddfSDavid du Colombier q[1] = bb[1];
2577dd7cddfSDavid du Colombier q[2] = bb[2];
2587dd7cddfSDavid du Colombier dir = 1;
2597dd7cddfSDavid du Colombier }
2607dd7cddfSDavid du Colombier
2617dd7cddfSDavid du Colombier escp2c_conv_stc(in, out, fullcolor_line_size);
2627dd7cddfSDavid du Colombier
2637dd7cddfSDavid du Colombier /* ------------------------------------------------------------------- */
2647dd7cddfSDavid du Colombier } /* buffer-reset | dithering */
2657dd7cddfSDavid du Colombier /* ------------------------------------------------------------------- */
2667dd7cddfSDavid du Colombier
2677dd7cddfSDavid du Colombier
2687dd7cddfSDavid du Colombier /* ============================================================= */
2697dd7cddfSDavid du Colombier } else { /* npixel <= 0 -> initialisation */
2707dd7cddfSDavid du Colombier /* ============================================================= */
2717dd7cddfSDavid du Colombier
2727dd7cddfSDavid du Colombier
2737dd7cddfSDavid du Colombier /*
2747dd7cddfSDavid du Colombier * check wether the number of components is valid
2757dd7cddfSDavid du Colombier */
2767dd7cddfSDavid du Colombier if(sd->color_info.num_components != 3) return -1;
2777dd7cddfSDavid du Colombier
2787dd7cddfSDavid du Colombier /*
2797dd7cddfSDavid du Colombier * check wether stcdither & TYPE are correct
2807dd7cddfSDavid du Colombier */
2817dd7cddfSDavid du Colombier if(( sd->stc.dither == NULL) ||
2827dd7cddfSDavid du Colombier ((sd->stc.dither->flags & STC_TYPE) != STC_BYTE)) return -2;
2837dd7cddfSDavid du Colombier
2847dd7cddfSDavid du Colombier /*
2857dd7cddfSDavid du Colombier * check wether the buffer-size is sufficiently large
2867dd7cddfSDavid du Colombier */
2877dd7cddfSDavid du Colombier if((sd->stc.dither->flags/STC_SCAN) < 1) return -3;
2887dd7cddfSDavid du Colombier
2897dd7cddfSDavid du Colombier /*
2907dd7cddfSDavid du Colombier * finally clear the buffer
2917dd7cddfSDavid du Colombier */
2927dd7cddfSDavid du Colombier memset(buf,0,-fullcolor_line_size);
2937dd7cddfSDavid du Colombier
2947dd7cddfSDavid du Colombier /* ============================================================= */
2957dd7cddfSDavid du Colombier } /* scanline-processing or initialisation */
2967dd7cddfSDavid du Colombier /* ============================================================= */
2977dd7cddfSDavid du Colombier
2987dd7cddfSDavid du Colombier return 0;
2997dd7cddfSDavid du Colombier }
300