xref: /plan9/sys/src/cmd/gs/src/gdevstc4.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1995, 1996 Aladdin Enterprises.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: gdevstc4.c,v 1.4 2002/02/21 22:24:52 giles Exp $*/
18 /* Epson Stylus-Color Printer-Driver */
19 
20 /***
21      This file holds a byte-Implementation of the Floyd-Steinberg error
22      diffusion-algorithm. This algorithm is an alternative for high quality
23      printing in conjunction with the PostScript-Header stcolor.ps:
24 
25           gs -sDEVICE=stcolor -sDithering=fs2 <other options> stcolor.ps ...
26 
27      THIS ALGORIHM WAS WRITTEN BY STEVEN SINGER (S.Singer@ph.surrey.ac.uk)
28      AS PART OF escp2cfs2.
29      THIS IMPLEMENTATION INCORPORATES ONLY FEW CHANGES TO THE ORIGINAL CODE.
30 
31  ***/
32 
33 #include "gdevstc.h"
34 
35 /*
36  * escp2c_pick best scans for best matching color
37  */
38 private byte *
escp2c_pick_best(byte * col)39 escp2c_pick_best(byte *col)
40 {
41     static byte colour[8][3] = {
42        {  0,  0,  0},{255,  0,  0},{  0,255,  0},{255,255,  0},
43        {  0,  0,255},{255,  0,255},{  0,255,255},{255,255,255}};
44     register int x, y, z, dx, dy, dz, dz2, dx2, dx3, dx4;
45     register byte *p;
46     register long md, d;
47 
48     md = 16777216; /* plenty */
49 
50 /*
51    Note that I don't use a simple distance algorithm. That can lead to a block
52    of (130,127,127) being dithered as red-cyan. This algorithm should make
53    it use black-white-red. This is very important, as a coloured block in
54    the middle of a grey block can, via error diffusion, perturb the
55    surrounding colours sufficiently for this to happen.
56 */
57 
58 /*
59    The next bit is equivalent to this, but faster.
60 
61     x = col[0];
62     y = col[1];
63     z = col[2];
64     for(n=8; n--; )
65     {
66 	dx = x - colour[n][0];
67 	dy = y - colour[n][1];
68 	dz = z - colour[n][2];
69 	d = dx*(dx-(dy>>1)) + dy*(dy-(dz>>1)) + dz*(dz-(dx>>1));
70 	if (d < md)
71 	{
72 	    md = d;
73 	    p = n;
74 	}
75     }
76 */
77 
78 /*
79  * Test colours in gray code order to reduce number of recalculations.
80  * I bet you can't find an optimiser that would do this automatically.
81  */
82 
83     x = col[0];
84     y = col[1];
85     z = col[2];
86     dx = x*(x-(y>>1));
87     dy = y*(y-(z>>1));
88     dz = z*(z-(x>>1));
89     md = dx + dy + dz;
90     p = colour[0];
91     x -= 255;
92     dx2 = x*(x-(y>>1));
93     dz2 = z*(z-(x>>1));
94     if ((d = dx2 + dy + dz2) < md) {md = d; p = colour[1];}
95     y -= 255;
96     dx3 = x*(x-(y>>1));
97     dy = y*(y-(z>>1));
98     if ((d = dx3 + dy + dz2) < md) {md = d; p = colour[3];}
99     x += 255;
100     dx4 = x*(x-(y>>1));
101     if ((d = dx4 + dy + dz) < md) {md = d; p = colour[2];}
102     z -= 255;
103     dy = y*(y-(z>>1));
104     dz = z*(z-(x>>1));
105     if ((d = dx4 + dy + dz) < md) {md = d; p = colour[6];}
106     x -= 255;
107     dz2 = z*(z-(x>>1));
108     if ((d = dx3 + dy + dz2) < md) {md = d; p = colour[7];}
109     y += 255;
110     dy = y*(y-(z>>1));
111     if ((d = dx2 + dy + dz2) < md) {md = d; p = colour[5];}
112     if ((d = dx + dy + dz) < md) {p = colour[4];}
113     return(p);
114 }
115 
116 /*
117  * escp2c_conv_stc converts into the ouput format used by stcolor
118  */
119 private void
escp2c_conv_stc(byte * p,byte * q,int i)120 escp2c_conv_stc(byte *p, byte *q, int i)
121 {
122     for(; i; p+=3, i-=3)
123         *q++ = (*p & RED) | (p[1] & GREEN) | (p[2] & BLUE);
124 }
125 
126 
127 /*
128  * Limit byte-values
129  */
130 #define LIMIT(a) if (a > 255) a = 255; if (a < 0) a = 0
131 #define LIMIT2(a) if (a > 127) a = 127; if (a < -128) a = -128; \
132                 if (a < 0) a += 256
133 /*
134  * Main routine of the algorithm
135  */
136 int
stc_fs2(stcolor_device * sd,int npixel,byte * in,byte * buf,byte * out)137 stc_fs2(stcolor_device *sd,int npixel,byte *in,byte *buf,byte *out)
138 {
139    int fullcolor_line_size = npixel*3;
140 
141 /* ============================================================= */
142    if(npixel > 0) {  /* npixel >  0 -> scanline-processing       */
143 /* ============================================================= */
144 
145 /*    -------------------------------------------------------------------- */
146       if(in == NULL) { /* clear the error-buffer upon white-lines */
147 /*    -------------------------------------------------------------------- */
148 
149          memset(buf,0,fullcolor_line_size);
150 
151 /*    ------------------------------------------------------------------- */
152       } else {                 /* do the actual dithering                 */
153 /*    ------------------------------------------------------------------- */
154     int i, j, k, e, l, i2, below[3][3], *fb, *b, *bb, *tb;
155     byte *p, *q, *cp;
156     static int dir = 1;
157 
158     p = buf;
159     if (*p != 0 || memcmp((char *) p, (char *) p + 1, fullcolor_line_size - 1))
160     {
161 	for(p = in, q=buf, i=fullcolor_line_size;
162 	    i--; p++, q++ )
163 	{
164 	    j = *p + ((*q & 128) ? *q - 256 : *q);
165 	    LIMIT(j);
166 	    *p = j;
167 	}
168     }
169 
170     p = in;
171 
172 	fb = below[2];
173 	b = below[1];
174 	bb = below[0];
175 	*b = b[1] = b[2] = *bb = bb[1] = bb[2] = 0;
176 
177 	if (dir)
178 	{
179 	    for(p = in, q=buf-3,
180 		i=fullcolor_line_size; i; i-=3)
181 	    {
182 		cp = escp2c_pick_best(p);
183 		for(i2=3; i2--; p++, q++, fb++, b++, bb++)
184 		{
185 		    j = *p;
186 		    *p = *cp++;
187 		    j -= *p;
188 		    if (j != 0)
189 		    {
190 			l = (e = (j>>1)) - (*fb = (j>>4));
191 			if (i > 2)
192 			{
193 			    k = p[3] + l;
194 			    LIMIT(k);
195 			    p[3] = k;
196 			}
197 			*b += e - (l = (j>>2) - *fb);
198 			if (i < fullcolor_line_size)
199 			{
200 			    l += *bb;
201 			    LIMIT2(l);
202 			    *q = l;
203 			}
204 		    }
205 		    else
206 			*fb = 0;
207 		}
208 		tb = bb-3;
209 		bb = b-3;
210 		b = fb-3;
211 		fb = tb;
212 	    }
213 	    *q = *bb;
214 	    q[1] = bb[1];
215 	    q[2] = bb[2];
216 	    dir = 0;
217 	}
218 	else
219 	{
220 	    for(p = in+fullcolor_line_size-1,
221 		q = buf+fullcolor_line_size+2, i=fullcolor_line_size;
222                 i; i-=3)
223 	    {
224 		cp = escp2c_pick_best(p-2) + 2;
225 		for(i2=3; i2--; p--, q--, fb++, b++, bb++)
226 		{
227 		    j = *p;
228 		    *p = *cp--;
229 		    j -= *p;
230 		    if (j != 0)
231 		    {
232 			l = (e = (j>>1)) - (*fb = (j>>4));
233 			if (i > 2)
234 			{
235 			    k = p[-3] + l;
236 			    LIMIT(k);
237 			    p[-3] = k;
238 			}
239 			*b += e - (l = (j>>2) - *fb);
240 			if (i < fullcolor_line_size)
241 			{
242 			    l += *bb;
243 			    LIMIT2(l);
244 			    *q = l;
245 			}
246 		    }
247 		    else
248 			*fb = 0;
249 		}
250 		tb = bb-3;
251 		bb = b-3;
252 		b = fb-3;
253 		fb = tb;
254 	    }
255 	    *q = *bb;
256 	    q[1] = bb[1];
257 	    q[2] = bb[2];
258 	    dir = 1;
259 	}
260 
261     escp2c_conv_stc(in, out, fullcolor_line_size);
262 
263 /*    ------------------------------------------------------------------- */
264       }                        /* buffer-reset | dithering                */
265 /*    ------------------------------------------------------------------- */
266 
267 
268 /* ============================================================= */
269    } else {          /* npixel <= 0 -> initialisation            */
270 /* ============================================================= */
271 
272 
273 /*
274  * check wether the number of components is valid
275  */
276       if(sd->color_info.num_components != 3)                       return -1;
277 
278 /*
279  * check wether stcdither & TYPE are correct
280  */
281       if(( sd->stc.dither                    == NULL) ||
282          ((sd->stc.dither->flags & STC_TYPE) != STC_BYTE))         return -2;
283 
284 /*
285  * check wether the buffer-size is sufficiently large
286  */
287       if((sd->stc.dither->flags/STC_SCAN) < 1)                     return -3;
288 
289 /*
290  * finally clear the buffer
291  */
292       memset(buf,0,-fullcolor_line_size);
293 
294 /* ============================================================= */
295    } /* scanline-processing or initialisation */
296 /* ============================================================= */
297 
298    return 0;
299 }
300