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