1 /*
2  * Filter routine to convert a 24-bit color AVS file on stdin
3  * to a dithered monochrome PostScript file on stdout.
4  * The intended use is to produce black&white pictures from Raster3D,
5  * but I see no reason it shouldn't work with any other AVS input file.
6  * Ethan A Merritt - Aug 1994
7  */
8 #include	<stdio.h>
9 #include	<time.h>
10 #include	<math.h>
11 #include	<string.h>
12 #include	<stdlib.h>
13 #include	<netinet/in.h>
14 
15 #ifdef WIN32
16 #define random rand
17 #endif
18 
19 /*
20  * I added a lookup table for warping, since calculating it on the fly
21  * was taking about 20% of the CPU time on a DECstation 5000.
22  * If you want to play with the warp function, just replace the following
23  * define with		define warp(pixval) warpf(pixval)
24  * and edit the function warpf() at the end of this file.
25  */
26 #define		warp(pixval)	warpm[pixval]
27 
28 #define	NO	(0)
29 #define	YES	(1)
30 
main(argc,argv)31 int main(argc,argv)
32 int  argc;
33 char     *argv[];
34 {
35 int		 xsize, ysize;
36 int		 i, j;
37 float		 red, green, blue, alpha;
38 extern float	 warpf();
39 extern float	 warpm[];
40 extern void	 dither();
41 float		*inbuf1, *inbuf2, *tbuf;
42 int		*index;
43 int		*done;
44 unsigned char	*obuf1, *out1;
45 unsigned char	 obyte;
46 
47 float   pixinch = 300;		/* default to 300dpi printer resolution */
48 int	bflag   = NO;		/* default to no border around image */
49 int	usagerr = NO;
50 int	ny, nx, nb;
51 float	x_origin, y_origin, x_scale, y_scale;
52 time_t	date;
53 
54 (void)alpha;
55 
56 /* Command line options */
57     for (i=1; i<argc; i++)
58 	{
59 	if (strncmp(argv[i],"-dpi",4) == 0)
60 	    pixinch = (float)atoi(argv[++i]);
61 	else if (strncmp(argv[i],"-b",2) == 0)
62 	    bflag = YES;
63 	else
64 	    usagerr = YES;
65 	}
66     if (pixinch <= 72) usagerr = YES;
67     if (usagerr)
68 	{
69 	fprintf( stderr,
70 	    "\nusage:  avs2ps [-dpi xxx] [-b] < infile.avs > outfile.ps\n");
71 	exit(-1);
72 	}
73 
74 /* Read in size of image from first two words on input stream */
75 	xsize = getw( stdin );
76 	ysize = getw( stdin );
77 #ifdef NETWORKBYTEORDER
78 	xsize = htonl(xsize);
79 	ysize = htonl(ysize);
80 #endif
81 
82 /* Allocate input buffers accordingly */
83     inbuf1 = (float *)calloc( xsize, sizeof(float) );
84     inbuf2 = (float *)calloc( xsize, sizeof(float) );
85     if (inbuf1 == NULL || inbuf2 == NULL)
86 	exit(-1);
87 
88 /* Allocate output buffer too */
89     nb = (xsize+7) / 8;
90     nx = nb * 8;
91     ny = ysize;
92     obuf1 = (unsigned char *)calloc( nx, sizeof(char) );
93     if (obuf1 == NULL)
94 	exit(-1);
95 
96 /* Allocate bookkeeping arrays */
97     index  = (int *)calloc( xsize, sizeof(int) );
98     done   = (int *)calloc( xsize+2, sizeof(int) );
99     if (index == NULL || done == NULL)
100 	exit(-1);
101     done++ ;
102 
103 /* write header of PostScript file */
104     x_origin = 36.0;
105     y_origin = 36.0;
106     x_scale = 72.0 * xsize / pixinch;
107     y_scale = 72.0 * ysize / pixinch;
108     date = time(0);
109 
110     printf ("%%!PS-Adobe-2.0\n");
111     printf ("%%%%Title: Raster3d picture dithered to monochrome\n");
112     printf ("%%%%Creator: %s using avs2ps V2.5\n",
113   	   (char *)getenv("USER"));
114     printf ("%%%%CreationDate: %s", ctime(&date));
115     printf ("%%%%BoundingBox: %d %d %d %d\n", (int)x_origin,
116   	   (int)y_origin, (int)(x_origin + x_scale + 1.0),
117   	   (int)(y_origin + y_scale + 1.0));
118     printf ("%%%%Requirements: resolution(%d,%d)\n",(int)pixinch,(int)pixinch);
119     printf ("%%%%EndComments\n");
120     printf ("initgraphics\n");
121     printf ("/picstr %d string def\n", nb);
122     printf ("gsave\n");
123     printf ("%f %f translate\n", x_origin, (y_origin + y_scale));
124     printf ("%f %f scale\n", x_scale, -y_scale);
125     printf ("%d %d 1\n", nx, ny);
126     printf ("[%d 0 0 %d 0 0]\n", nx, ny);
127     printf ("{ currentfile picstr readhexstring pop }\n");
128     printf ("image\n");
129 
130 
131 /* Read in first row of image, converting to greyscale image as we go */
132     for (i=0; i<xsize; i++)
133 	{
134 	alpha = getchar(); 			/* skip alpha byte */
135 	red   = warp( getchar() );
136 	green = warp( getchar() );
137 	blue  = warp( getchar() );
138 	inbuf1[i] = sqrt( 0.299 * red + 0.587 * green + 0.111 * blue );
139 	inbuf1[i] = sqrt( inbuf1[i] );
140 	}
141 
142 /* Now for grand loop over image lines */
143     for (j=1; j<=ysize; j++)
144 	{
145 	/* Read next row into inbuf2 */
146 	if (j != ysize)			/* No read on last+1th row */
147 	for (i=0; i<xsize; i++)
148 	    {
149 	    alpha = getchar(); 			/* skip alpha byte */
150 	    red   = warp( getchar() );
151 	    green = warp( getchar() );
152 	    blue  = warp( getchar() );
153 	    inbuf2[i] = sqrt( 0.299 * red + 0.587 * green + 0.111 * blue );
154 	    inbuf2[i] = sqrt( inbuf2[i] );
155 	    }
156 
157 	/* Call dithering function to produce pixels for previous line */
158 	    dither( xsize, inbuf1, inbuf2, obuf1, index, done );
159 
160 	/* Set any extra pixels on right margin to white */
161 	    for (i=xsize; i<nx; i++)
162 		obuf1[i] = 1;
163 
164 	/* Now pack and dump output pixels for PostScript */
165 	    out1 = obuf1;
166 	    for (i=0; i<2*nb; i++)
167 		{
168 		obyte  = *out1++ << 3; obyte |= *out1++ << 2;
169 		obyte |= *out1++ << 1; obyte |= *out1++ << 0;
170 		if ((i%64) == 0) printf("\n");
171 		printf("%x",obyte);
172 		}
173 
174 	/* Swap input buffer pointers in preparation for next row */
175 	    tbuf = inbuf1;  inbuf1 = inbuf2;  inbuf2 = tbuf;
176 
177 	}
178 
179 /* PostScript trailer */
180     printf ("\ngrestore\n");
181     if (bflag)
182 	{
183    	printf ("newpath %d %d moveto %d %d lineto\n",
184 		(int)x_origin, (int)y_origin,
185 		(int)x_origin, (int)(y_origin+y_scale+1.) );
186    	printf ("        %d %d lineto %d %d lineto\n",
187 		(int)(x_origin+x_scale+1.), (int)(y_origin+y_scale+1.),
188 		(int)(x_origin+x_scale+1.), (int)y_origin);
189 	printf ("closepath stroke\n");
190 	}
191     printf("\nshowpage\n");
192 
193 exit(0);
194 }
195 
196 
197 /*
198  * This is a translation of the "new improved" dithering routine taken from
199  * hdither1.f.   It is a variation on the theme of Floyd-Steinberg error
200  * diffusion, with the wrinkle that the individual columns within a row
201  * are traversed in random order.
202  * (EAM - Aug 1994)
203  * The following partial explaination is taken from comments in hdither.f:
204  */
205 /*
206  * What I am doing is choosing the pixels in LINE1 for processing in a
207  * shuffled order.  I'm also changing how the error is distributed
208  * slightly.  For a pixel in line 1, there can be up to 5 neighbours where
209  * the error can be sent.  I take it that neighbouring pixels on the
210  * diagonal are worth 1/sqrt(2) as much as vertically or horizontally
211  * adjacent pixels.  All three neighbours on line 2 are always available,
212  * and either or both of the adjacent pixels on line 1 may be, depending on
213  * whether we've already chosen values for them.  As for edge effects, I'm
214  * going to continue to throw away errors that I would normally throw at
215  * unprocessed pixels, rather than trying to distribute the error "fairly"
216  * among the pixels I still have available, because I don't want to end up
217  * with strange edge effects arising from the fact that I'm not processing
218  * the rasters in a random order!
219  */
220 
221 static float avail[4][5] =	{
222 		        {0.226541, 0.160189, 0.226541, 0.160189, 0.226541},
223 		        {0.000000, 0.207107, 0.292893, 0.207107, 0.292893},
224 		        {0.292893, 0.207107, 0.292893, 0.207107, 0.000000},
225 		        {0.000000, 0.292893, 0.414214, 0.292893, 0.000000}
226 			};
227 
228 
229 void
dither(nx,line1,line2,pixels,index,done)230 dither( nx, line1,  line2, pixels, index,  done )
231 int   		 nx;
232 float		*line1, *line2;
233 unsigned char	*pixels;
234 int		*index, *done;
235 {
236     int 	i, j, k;
237     int 	it;
238     int 	iavail;
239     float	error;
240     float	propag[5];
241 
242     /* pick a random order in which to process pixels from this row */
243 	for (i=0; i<nx; i++)
244 	    {
245 	    index[i] = i;
246 	    done[i]  = NO ;
247 	    }
248 	done[-1] = done[nx] = NO ;
249 
250 	for (i=0; i<nx; i++)
251 	    {
252 	    j  = random() % nx ;
253 	    it = index[i] ;
254 	    index[i] = index[j] ;
255 	    index[j] = it ;
256 	    }
257 
258     /* Now dither them, keeping track of which ones have been done */
259 	for (i=0; i<nx; i++)
260 	    {
261 	    k = index[i];
262 	    iavail = 0;
263 	    if (done[k-1]) iavail += 1;
264 	    if (done[k+1]) iavail += 2;
265 	    done[k] = YES ;
266 
267 	    /* set output pixel to either 0 or 1 */
268 	    if (line1[k] > 0.5)
269 		pixels[k] = 1, error = line1[k] - 1.0 ;
270 	    else
271 		pixels[k] = 0, error = line1[k] - 0.0 ;
272 	    if (fabs(error) < 1.e-9)
273 		continue ;
274 	    for (j=0; j<5; j++)
275 		propag[j] = avail[iavail][j] * error ;
276 
277 	    if (k > 0)
278 		{
279 		line1[k-1] = line1[k-1] + propag[0] ;
280 		line2[k-1] = line2[k-1] + propag[1] ;
281 		}
282 	    line2[k]       = line2[k]   + propag[2] ;
283 	    if (k < nx-1)
284 		{
285 		line1[k+1] = line1[k+1] + propag[3] ;
286 		line2[k+1] = line2[k+1] + propag[4] ;
287 		}
288 	    }
289 
290 }
291 
292 /*
293  * This is the warping function which hdither.f was using.
294  * It's computationally expensive (applied 3 times per pixel),
295  * so I dumped it into a lookup table for speed.
296  */
warpf(rgbval)297 float warpf( rgbval )
298 unsigned char rgbval;
299 {
300 float temp;
301 
302     temp = (float)rgbval / 255. ;
303     temp = (((0.533 * temp) - 1.657) * temp + 2.124) * temp ;
304     temp *= 1.01;		/* Used to be 1.10 */
305     temp = (temp < 0.) ? 0. :
306 	   (temp > 1.) ? 1. :
307 	    temp * temp ;
308     return( temp );
309 }
310 
311 float warpm[256] =
312  {
313  0.000000, 0.000070, 0.000280, 0.000625, 0.001105, 0.001716, 0.002456, 0.003322,
314  0.004313, 0.005425, 0.006656, 0.008005, 0.009468, 0.011043, 0.012729, 0.014522,
315  0.016422, 0.018425, 0.020529, 0.022732, 0.025033, 0.027429, 0.029918, 0.032498,
316  0.035167, 0.037923, 0.040765, 0.043689, 0.046695, 0.049780, 0.052943, 0.056182,
317  0.059495, 0.062880, 0.066335, 0.069859, 0.073450, 0.077106, 0.080826, 0.084608,
318  0.088451, 0.092352, 0.096310, 0.100324, 0.104392, 0.108512, 0.112684, 0.116905,
319  0.121175, 0.125491, 0.129852, 0.134258, 0.138706, 0.143195, 0.147724, 0.152291,
320  0.156896, 0.161537, 0.166212, 0.170921, 0.175662, 0.180434, 0.185236, 0.190067,
321  0.194926, 0.199810, 0.204721, 0.209655, 0.214613, 0.219592, 0.224593, 0.229614,
322  0.234653, 0.239711, 0.244786, 0.249877, 0.254983, 0.260104, 0.265237, 0.270383,
323  0.275541, 0.280710, 0.285888, 0.291075, 0.296271, 0.301473, 0.306683, 0.311898,
324  0.317119, 0.322343, 0.327572, 0.332803, 0.338036, 0.343271, 0.348507, 0.353743,
325  0.358979, 0.364213, 0.369446, 0.374676, 0.379904, 0.385128, 0.390347, 0.395563,
326  0.400773, 0.405977, 0.411175, 0.416367, 0.421551, 0.426728, 0.431896, 0.437056,
327  0.442207, 0.447348, 0.452479, 0.457600, 0.462711, 0.467810, 0.472897, 0.477973,
328  0.483037, 0.488087, 0.493125, 0.498150, 0.503161, 0.508159, 0.513142, 0.518110,
329  0.523064, 0.528003, 0.532927, 0.537835, 0.542728, 0.547604, 0.552464, 0.557308,
330  0.562136, 0.566946, 0.571739, 0.576516, 0.581275, 0.586016, 0.590740, 0.595446,
331  0.600134, 0.604804, 0.609456, 0.614089, 0.618705, 0.623301, 0.627880, 0.632439,
332  0.636980, 0.641502, 0.646005, 0.650489, 0.654955, 0.659401, 0.663828, 0.668237,
333  0.672626, 0.676996, 0.681347, 0.685680, 0.689993, 0.694287, 0.698562, 0.702818,
334  0.707055, 0.711273, 0.715473, 0.719654, 0.723815, 0.727959, 0.732083, 0.736190,
335  0.740277, 0.744347, 0.748398, 0.752431, 0.756446, 0.760443, 0.764422, 0.768383,
336  0.772327, 0.776254, 0.780163, 0.784055, 0.787930, 0.791788, 0.795629, 0.799454,
337  0.803262, 0.807054, 0.810830, 0.814590, 0.818335, 0.822064, 0.825777, 0.829476,
338  0.833159, 0.836828, 0.840482, 0.844122, 0.847748, 0.851360, 0.854958, 0.858543,
339  0.862114, 0.865673, 0.869219, 0.872752, 0.876273, 0.879782, 0.883280, 0.886766,
340  0.890240, 0.893704, 0.897157, 0.900599, 0.904032, 0.907454, 0.910867, 0.914270,
341  0.917665, 0.921051, 0.924428, 0.927797, 0.931158, 0.934512, 0.937858, 0.941197,
342  0.944530, 0.947856, 0.951177, 0.954491, 0.957801, 0.961105, 0.964404, 0.967699,
343  0.970990, 0.974277, 0.977561, 0.980842, 0.984120, 0.987396, 0.990669, 0.993941,
344  0.997212, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000
345  };
346