xref: /plan9/sys/src/cmd/gs/src/gdevstc2.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
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: gdevstc2.c,v 1.2 2000/09/19 19:00:22 lpd Exp $*/
20 /* Epson Stylus-Color Printer-Driver */
21 
22 /***
23      This file holds two implementations of the Floyd-Steinberg error
24      diffusion-algorithm. This algorithms are intended for high quality
25      printing in conjunction with the PostScript-Header stcolor.ps:
26 
27           gs -sDEVICE=stcolor <other options> stcolor.ps ...
28 
29      Most prominent option is -sDithering=xxx, to select the algorithm:
30 
31      fsmono - monochrome Floyd-Steinberg
32      fsrgb  - 3-Component Floyd-Steinberg
33      fsx4   - 4-Component Floyd-Steinberg (Bad results)
34 
35      fscmyk - Modified 4-Component Floyd-Steinberg
36               (Algorithmically identical with hscmyk, but slower)
37 
38  ***/
39 
40 #include "gdevstc.h"
41 
42 #include <stdlib.h>     /* for rand */
43 
44 /*
45    Both algorithms require an error-buffer of
46 
47        3 + 3*num_components +1*scan long-items.
48 
49    and must consequently set up to work with longs.
50    It is just a Floyd-Steinberg-algorithm applied to each component.
51 
52  */
53 
54 /*
55  * Due to the -selfdefined- ugly coding of the output-data, we need
56  * some conversion. But since this includes the black-separation, I
57  * did not change the definition.
58  *
59  * This algorithm stores the 1st component in the LSB, thus it
60  * reverts the order used by the basic driver.
61  */
62 
63 static const byte grayvals[2]  = { 0, BLACK };
64 
65 static const byte  rgbvals[8]  = {
66    0, RED, GREEN, RED|GREEN, BLUE, BLUE|RED, BLUE|GREEN, BLUE|RED|GREEN};
67 
68 static const byte cmykvals[16] = {
69       0, CYAN,MAGENTA,CYAN|MAGENTA,YELLOW,YELLOW|CYAN,YELLOW|MAGENTA,BLACK,
70   BLACK,BLACK,  BLACK,       BLACK, BLACK,      BLACK,         BLACK,BLACK};
71 
72 static const byte  *const pixelconversion[5] = {
73    NULL, grayvals, NULL, rgbvals, cmykvals};
74 
75 
76 int
77 stc_fs(stcolor_device *sdev,int npixel,byte *bin,byte *bbuf,byte *out)
78 {
79 
80      long *in  = (long *) bin;
81      long *buf = (long *) bbuf;
82 
83 /* ============================================================= */
84    if(npixel > 0) {  /* npixel >  0 -> scanline-processing       */
85 /* ============================================================= */
86 
87       int bstep,pstart,pstop,pstep,p;
88       long spotsize,threshold,*errc,*errv;
89       const byte *pixel2stc;
90 
91       if(buf[0] >= 0) { /* run forward */
92         buf[0] = -1;
93         bstep  = 1;
94         pstep  = sdev->color_info.num_components;
95         pstart = 0;
96         pstop  = npixel * pstep;
97 
98       } else {                  /* run backward */
99         buf[0] =  1;
100         bstep  = -1;
101         pstep  = -sdev->color_info.num_components;
102         pstop  = pstep;
103         pstart = (1-npixel) * pstep;
104         out   += npixel-1;
105       }                   /* forward / backward */
106 
107 /*    --------------------------------------------------------------------- */
108       if(in == NULL) return 0;  /* almost ignore the 'white calls' */
109 /*    --------------------------------------------------------------------- */
110 
111       spotsize  = buf[1];
112       threshold = buf[2];
113       errc      = buf+3;
114       errv      = errc + 2*sdev->color_info.num_components;
115       pixel2stc = pixelconversion[sdev->color_info.num_components];
116 
117       for(p = pstart; p != pstop; p += pstep) { /* loop over pixels */
118          int c;     /* component-number */
119          int pixel; /* internal pxel-value */
120 
121          pixel = 0;
122 
123          for(c = 0; c < sdev->color_info.num_components; c++) { /* comp */
124             long cv; /* component value */
125 
126             cv = in[p+c] + errv[p+c] + errc[c] - ((errc[c]+4)>>3);
127             if(cv > threshold) {
128                pixel |= 1<<c;
129                cv    -= spotsize;
130             }
131             errv[p+c-pstep] += ((3*cv+8)>>4);        /* 3/16 */
132             errv[p+c      ]  = ((5*cv  )>>4)         /* 5/16 */
133                              + ((errc[c]+4)>>3);     /* 1/16 (rest) */
134             errc[c]          = cv                    /* 8/16 (neu) */
135                              - ((5*cv  )>>4)
136                              - ((3*cv+8)>>4);
137          }                                                      /* comp */
138 
139          *out = pixel2stc[pixel];
140          out += bstep;
141       }                                         /* loop over pixels */
142 
143 
144 /* ============================================================= */
145    } else {          /* npixel <= 0 -> initialisation            */
146 /* ============================================================= */
147 
148       int i,i2do;
149       long rand_max;
150       double offset,scale;
151 
152 /*
153  * check wether the number of components is valid
154  */
155       if((sdev->color_info.num_components < 0)                         ||
156          (sdev->color_info.num_components >= countof(pixelconversion)) ||
157          (pixelconversion[sdev->color_info.num_components] == NULL)) return -1;
158 
159 /*
160  * check wether stcdither & TYPE are correct
161  */
162       if(( sdev->stc.dither                    == NULL) ||
163          ((sdev->stc.dither->flags & STC_TYPE) != STC_LONG))         return -2;
164 
165 /*
166  * check wether the buffer-size is sufficiently large
167  */
168       if(((sdev->stc.dither->flags/STC_SCAN) < 1) ||
169          ( sdev->stc.dither->bufadd          <
170           (3 + 3*sdev->color_info.num_components)))                  return -3;
171 /*
172  * must neither have STC_DIRECT nor STC_WHITE
173  */
174       if(sdev->stc.dither->flags & (STC_DIRECT | STC_WHITE))         return -4;
175 
176 /*
177  * compute initial values
178  */
179 /* -- direction */
180      buf[0] = 1;
181 
182 /* -- "spotsize" */
183      scale  = sdev->stc.dither->minmax[1];
184      buf[1] = scale + (scale > 0.0 ? 0.5 : -0.5);
185 
186 /* -- "threshold" */
187      offset = sdev->stc.dither->minmax[0];
188      scale -= offset;
189      if((offset+0.5*scale) > 0.0) buf[2] = offset + 0.5*scale + 0.5;
190      else                         buf[2] = offset + 0.5*scale - 0.5;
191 
192 /*
193  *   random values, that do not exceed half of normal value
194  */
195      i2do  = sdev->color_info.num_components * (3-npixel);
196      rand_max = 0;
197 
198      if(sdev->stc.flags & STCDFLAG0) {
199 
200         for(i = 0; i < i2do; ++i) buf[i+3] = 0;
201 
202      } else {
203 
204         for(i = 0; i < i2do; ++i) {
205            buf[i+3] = rand();
206            if(buf[i+3] > rand_max) rand_max = buf[i+3];
207         }
208 
209         scale = (double) buf[1] / (double) rand_max;
210 
211         for(i = 0; i < sdev->color_info.num_components; ++ i)
212            buf[i+3] = 0.25000*scale*(buf[i+3]-rand_max/2);
213 
214         for(     ; i < i2do; ++i) /* includes 2 additional pixels ! */
215            buf[i+3] = 0.28125*scale*(buf[i+3]-rand_max/2);
216 
217      }
218 
219 /* ============================================================= */
220    } /* scanline-processing or initialisation */
221 /* ============================================================= */
222 
223    return 0;
224 }
225 
226 /*
227  * Experimental CMYK-Algorithm
228  */
229 
230 int
231 stc_fscmyk(stcolor_device *sdev,int npixel,byte *bin,byte *bbuf,byte *out)
232 {
233       long *in  = (long *) bin;
234       long *buf = (long *) bbuf;
235 
236 /* ============================================================= */
237    if(npixel > 0) {  /* npixel >  0 -> scanline-processing       */
238 /* ============================================================= */
239 
240       int bstep,pstart,pstop,pstep,p;
241       long spotsize,threshold,*errc,*errv;
242 
243       if(buf[0] >= 0) { /* run forward */
244         buf[0] = -1;
245         bstep  = 1;
246         pstep  = 4;
247         pstart = 0;
248         pstop  = npixel * pstep;
249 
250       } else {                  /* run backward */
251         buf[0] =  1;
252         bstep  = -1;
253         pstep  = -4;
254         pstop  = pstep;
255         pstart = (1-npixel) * pstep;
256         out   += npixel-1;
257       }                   /* forward / backward */
258 
259       spotsize  = buf[1];
260       threshold = buf[2];
261       errc      = buf+3;
262       errv      = errc + 2*4;
263 
264       for(p = 0; p < 4; ++p) errc[p] = 0;
265 
266       for(p = pstart; p != pstop; p += pstep) { /* loop over pixels */
267          int c;     /* component-number */
268          int pixel; /* internal pxel-value */
269          long cv,k;
270 
271 /*
272  * Black is treated first, with conventional Floyd-Steinberg
273  */
274          k  = in[p+3];
275          cv = k + errv[p+3] + errc[3] - ((errc[3]+4)>>3);
276 
277          if(cv > threshold) {
278             pixel  = BLACK;
279             cv    -= spotsize;
280          } else {
281             pixel  = 0;
282          }
283 
284          errv[p+3-pstep] += ((3*cv+8)>>4);        /* 3/16 */
285          errv[p+3      ]  = ((5*cv  )>>4)         /* 5/16 */
286                           + ((errc[3]+4)>>3);     /* 1/16 (rest) */
287          errc[3]          = cv                    /* 8/16 (neu) */
288                           - ((5*cv  )>>4)
289                           - ((3*cv+8)>>4);
290 
291 /*
292  * color-handling changes with black fired or not
293  */
294          if(pixel) {
295 
296 /* -------- firing of black causes all colors to fire too */
297 
298             for(c = 0; c < 3; ++c) {
299                cv  = in[p+c] > k ? in[p+c] : k;
300                cv += errv[p+c] + errc[c] - ((errc[c]+4)>>3)-spotsize;
301                if(cv <= (threshold-spotsize)) cv = threshold-spotsize+1;
302 
303                errv[p+c-pstep] += ((3*cv+8)>>4);        /* 3/16 */
304                errv[p+c      ]  = ((5*cv  )>>4)         /* 5/16 */
305                                 + ((errc[c]+4)>>3);     /* 1/16 (rest) */
306                errc[c]          = cv                    /* 8/16 (neu) */
307                                 - ((5*cv  )>>4)
308                                 - ((3*cv+8)>>4);
309             }
310 
311          } else {
312 
313 /* -------- if black did not fire, only colors w. larger values may fire */
314 
315             for(c = 0; c < 3; ++c) {
316 
317                cv  = in[p+c];
318 
319                if(cv > k) { /* May Fire */
320                   cv += errv[p+c] + errc[c] - ((errc[c]+4)>>3);
321                   if(cv > threshold) {
322                      cv -= spotsize;
323                      pixel |= CYAN>>c;
324                   }
325                } else {     /* Must not fire */
326                   cv = k + errv[p+c] + errc[c] - ((errc[c]+4)>>3);
327                   if(cv > threshold ) cv =  threshold;
328                }
329 
330                errv[p+c-pstep] += ((3*cv+8)>>4);        /* 3/16 */
331                errv[p+c      ]  = ((5*cv  )>>4)         /* 5/16 */
332                                 + ((errc[c]+4)>>3);     /* 1/16 (rest) */
333                errc[c]          = cv                    /* 8/16 (neu) */
334                                 - ((5*cv  )>>4)
335                                 - ((3*cv+8)>>4);
336             }
337          }
338 
339          *out = pixel;
340          out += bstep;
341       }                                         /* loop over pixels */
342 
343 
344 /* ============================================================= */
345    } else {          /* npixel <= 0 -> initialisation            */
346 /* ============================================================= */
347 
348       int i,i2do;
349       long rand_max;
350       double offset,scale;
351 
352 /*
353  * check wether the number of components is valid
354  */
355       if(sdev->color_info.num_components != 4)                       return -1;
356 
357 /*
358  * check wether stcdither & TYPE are correct
359  */
360       if(( sdev->stc.dither                    == NULL) ||
361          ((sdev->stc.dither->flags & STC_TYPE) != STC_LONG))         return -2;
362 
363 /*
364  * check wether the buffer-size is sufficiently large
365  */
366       if(((sdev->stc.dither->flags/STC_SCAN) < 1) ||
367          ( sdev->stc.dither->bufadd          <
368           (3 + 3*sdev->color_info.num_components)))                  return -3;
369 /*
370  * must neither have STC_DIRECT nor STC_WHITE
371  */
372       if(sdev->stc.dither->flags & (STC_DIRECT | STC_WHITE))         return -4;
373 
374 /*
375  * compute initial values
376  */
377 /* -- direction */
378      buf[0] = 1;
379 
380 /* -- "spotsize" */
381      scale  = sdev->stc.dither->minmax[1];
382      buf[1] = scale + (scale > 0.0 ? 0.5 : -0.5);
383 
384 /* -- "threshold" */
385      offset = sdev->stc.dither->minmax[0];
386      scale -= offset;
387      if(sdev->stc.flags & STCDFLAG1) {
388         buf[2] = (sdev->stc.extv[0][sdev->stc.sizv[0]-1] - sdev->stc.extv[0][0])
389                * scale / 2.0 + offset;
390      } else {
391         if((offset+0.5*scale) > 0.0) buf[2] = offset + 0.5*scale + 0.5;
392         else                         buf[2] = offset + 0.5*scale - 0.5;
393      }
394 
395 /*
396  *   random values, that do not exceed half of normal value
397  */
398      i2do  = sdev->color_info.num_components * (3-npixel);
399      rand_max = 0;
400 
401      if(sdev->stc.flags & STCDFLAG0) {
402 
403         for(i = 0; i < i2do; ++i) buf[i+3] = 0;
404 
405      } else {
406 
407         for(i = 0; i < i2do; ++i) {
408            buf[i+3] = rand();
409            if(buf[i+3] > rand_max) rand_max = buf[i+3];
410         }
411 
412         scale = (double) buf[1] / (double) rand_max;
413 
414         for(i = 0; i < sdev->color_info.num_components; ++ i)
415            buf[i+3] = 0.25000*scale*(buf[i+3]-rand_max/2);
416 
417         for(     ; i < i2do; ++i) /* includes 2 additional pixels ! */
418            buf[i+3] = 0.28125*scale*(buf[i+3]-rand_max/2);
419 
420      }
421 
422 /* ============================================================= */
423    } /* scanline-processing or initialisation */
424 /* ============================================================= */
425 
426    return 0;
427 }
428