xref: /plan9/sys/src/cmd/gs/src/gdevstc4.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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