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