http://ftp.x.org/contrib/applications/xscreensaver-2.23.tar.gz
[xscreensaver] / hacks / rd-bomb.c
1 /* xscreensaver, Copyright (c) 1992, 1995, 1997
2  *  Jamie Zawinski <jwz@netscape.com>
3  *
4  *  reaction/diffusion textures
5  *  Copyright (c) 1997 Scott Draves spot@transmeta.com
6  *  this code is derived from Bomb
7  *  see http://www.cs.cmu.edu/~spot/bomb.html
8  *
9  * Permission to use, copy, modify, distribute, and sell this software and its
10  * documentation for any purpose is hereby granted without fee, provided that
11  * the above copyright notice appear in all copies and that both that
12  * copyright notice and this permission notice appear in supporting
13  * documentation.  No representations are made about the suitability of this
14  * software for any purpose.  It is provided "as is" without express or 
15  * implied warranty.
16  *
17  * And remember: X Windows is to graphics hacking as roman numerals are to
18  * the square root of pi.
19  */
20
21 #include <math.h>
22
23 #include "screenhack.h"
24
25 /* costs ~6% speed */
26 #define dither_when_mapped 1
27
28 int verbose;
29 int ncolors = 0;
30 XColor *colors = 0;
31 Display *display;
32 Visual *visual;
33 #if dither_when_mapped
34 unsigned char *mc = 0;
35 #endif
36 Colormap cmap = 0;
37 Window window;
38 int mapped;
39 int pdepth;
40 void random_colors(void);
41
42 /* -----------------------------------------------------------
43    pixel hack, 8-bit pixel grid, first/next frame interface
44
45    pixack_init(int *size_h, int *size_v)
46    pixack_frame(char *pix_buf)
47    */
48
49
50 #define bps 16
51 #define mx ((1<<16)-1)
52
53 /* you can replace integer mults wish shift/adds with these,
54    but it doesn't help on my 586 */
55 #define x5(n) ((n<<2)+n)
56 #define x7(n) ((n<<3)-n)
57
58 /* why strip bit? */
59 #define R (ya_random()&((1<<30)-1))
60
61 int frame = 0, epoch_time;
62 ushort *r1, *r2, *r1b, *r2b;
63 int width, height, npix;
64 int radius;
65 int reaction = 0;
66 int diffusion = 0;
67
68 /* returns number of pixels that the pixack produces.  called once. */
69 void
70 pixack_init(int *size_h, int *size_v) {
71   int sz_base;
72   width = get_integer_resource ("width", "Integer");
73   height = get_integer_resource ("height", "Integer");
74   sz_base = 80 + (R%40);
75   if (width <= 0) width = (R%20) ? sz_base : (28 + R%10);
76   if (height <= 0) height = (R%20) ? sz_base : (28 + R%10);
77   /* don't go there */
78   if (width < 10) width = 10;
79   if (height < 10) height = 10;
80   epoch_time = get_integer_resource ("epoch", "Integer");
81   npix = (width + 2) * (height + 2);
82   r1 = (ushort *) malloc(sizeof(ushort) * npix);
83   r2 = (ushort *) malloc(sizeof(ushort) * npix);
84   r1b = (ushort *) malloc(sizeof(ushort) * npix);
85   r2b = (ushort *) malloc(sizeof(ushort) * npix);
86
87   if (!r1 || !r2 || !r1b || !r2b) {
88     fprintf(stderr, "not enough memory for %d pixels.\n", npix);
89     exit(1);
90   }
91
92   *size_h = width;
93   *size_v = height;
94 }
95
96 #define test_pattern_hyper 0
97
98
99 /* returns the pixels.  called many times. */
100 void
101 pixack_frame(char *pix_buf) {
102   int i, j;
103   int w2 = width + 2;
104   ushort *t;
105 #if test_pattern_hyper
106   if (frame&0x100)
107     sleep(1);
108 #endif
109   if (verbose) {
110     double tm = 0;
111     struct timeval tp;
112     if (!(frame%100)) {
113       double tm2;
114 #ifdef GETTIMEOFDAY_TWO_ARGS
115       struct timezone tzp;
116       gettimeofday(&tp, &tzp);
117 #else
118       gettimeofday(&tp);
119 #endif
120       tm2 = tp.tv_sec + tp.tv_usec * 1e-6;
121       if (frame > 0)
122         printf("fps = %2.4g\n", 100.0 / (tm2 - tm));
123       tm = tm2;
124     }
125   }
126   if (!(frame%epoch_time)) {
127     int s;
128     if (0 != frame) {
129       int t = epoch_time / 500;
130       if (t > 15)
131         t = 15;
132       sleep(t);
133     }
134           
135     for (i = 0; i < npix; i++) {
136       /* equilibrium */
137       r1[i] = 65500;
138       r2[i] = 11;
139     }
140
141     random_colors();
142
143     XSetWindowBackground(display, window, colors[255 % ncolors].pixel);
144     XClearWindow(display, window);
145
146     s = w2 * (height/2) + width/2;
147     radius = get_integer_resource ("radius", "Integer");
148     {
149       int maxr = width/2-2;
150       int maxr2 = height/2-2;
151       if (maxr2 < maxr) maxr = maxr2;
152
153       if (radius < 0)
154         radius = 1 + ((R%10) ? (R%5) : (R % maxr));
155       if (radius > maxr) radius = maxr;
156     }
157     for (i = -radius; i < (radius+1); i++)
158       for (j = -radius; j < (radius+1); j++)
159         r2[s + i + j*w2] = mx - (R&63);
160     reaction = get_integer_resource ("reaction", "Integer");
161     if (reaction < 0 || reaction > 2) reaction = R&1;
162     diffusion = get_integer_resource ("diffusion", "Integer");
163     if (diffusion < 0 || diffusion > 2)
164       diffusion = (R%5) ? ((R%3)?0:1) : 2;
165     if (2 == reaction && 2 == diffusion)
166       reaction = diffusion = 0;
167       
168     if (verbose)
169       printf("reaction = %d\ndiffusion = %d\nradius = %d\n",
170              reaction, diffusion, radius);
171   }
172   for (i = 0; i <= width+1; i++) {
173     r1[i] = r1[i + w2 * height];
174     r2[i] = r2[i + w2 * height];
175     r1[i + w2 * (height + 1)] = r1[i + w2];
176     r2[i + w2 * (height + 1)] = r2[i + w2];
177   }
178   for (i = 0; i <= height+1; i++) {
179     r1[w2 * i] = r1[width + w2 * i];
180     r2[w2 * i] = r2[width + w2 * i];
181     r1[w2 * i + width + 1] = r1[w2 * i + 1];
182     r2[w2 * i + width + 1] = r2[w2 * i + 1];
183   }
184   for (i = 0; i < height; i++) {
185     int ii = i + 1;
186     char *q = pix_buf + width * i;
187     short *qq = ((short *) pix_buf) + width * i;
188     long  *qqq = ((long *) pix_buf) + width * i;
189     ushort *i1 = r1 + 1 + w2 * ii;
190     ushort *i2 = r2 + 1 + w2 * ii;
191     ushort *o1 = r1b + 1 + w2 * ii;
192     ushort *o2 = r2b + 1 + w2 * ii;
193     for (j = 0; j < width; j++) {
194 #if test_pattern_hyper
195       int r1 = (i * j + (frame&127)*frame)&65535;
196 #else
197       int uvv, r1 = 0, r2 = 0;
198       switch (diffusion) {
199       case 0:
200         r1 = i1[j] + i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
201         r1 = r1 / 5;
202         r2 = (i2[j]<<3) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
203         r2 = r2 / 12;
204         break;
205       case 1:
206         r1 = i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
207         r1 = r1 >> 2;
208         r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
209         r2 = r2 >> 3;
210         break;
211       case 2:
212         r1 = (i1[j]<<1) + (i1[j+1]<<1) + (i1[j-1]<<1) + i1[j+w2] + i1[j-w2];
213         r1 = r1 >> 3;
214         r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
215         r2 = r2 >> 3;
216         break;
217       }
218
219       /* John E. Pearson "Complex Patterns in a Simple System"
220          Science, July 1993 */
221
222       uvv = (((r1 * r2) >> bps) * r2) >> bps;
223       switch (reaction) {  /* costs 4% */
224       case 0:
225         r1 += 4 * (((28 * (mx-r1)) >> 10) - uvv);
226         r2 += 4 * (uvv - ((80 * r2) >> 10));
227         break;
228       case 1:
229         r1 += 3 * (((27 * (mx-r1)) >> 10) - uvv);
230         r2 += 3 * (uvv - ((80 * r2) >> 10));
231         break;
232       case 2:
233         r1 += 2 * (((28 * (mx-r1)) >> 10) - uvv);
234         r2 += 3 * (uvv - ((80 * r2) >> 10));
235         break;
236       }
237       if (r1 > mx) r1 = mx;
238       if (r2 > mx) r2 = mx;
239       if (r1 < 0) r1 = 0;
240       if (r2 < 0) r2 = 0;
241       o1[j] = r1;
242       o2[j] = r2;
243 #endif
244
245       /* this is terrible.  here i want to assume ncolors = 256.
246          should lose double indirection */
247
248       if (mapped)
249 #if dither_when_mapped
250         q[j] = colors[mc[r1]  % ncolors].pixel;
251 #else
252       q[j] = colors[(r1>>8) % ncolors].pixel;
253 #endif
254       else if (pdepth == 8)
255         q[j] = colors[(r1>>8) % ncolors].pixel;
256       else if (pdepth == 16)
257 #if dither_when_mapped
258         qq[j] = colors[mc[r1] % ncolors].pixel;
259 #else
260         qq[j] = colors[(r1>>8) % ncolors].pixel;
261 #endif
262       else if (pdepth == 32)
263 #if dither_when_mapped
264         qqq[j] = colors[mc[r1] % ncolors].pixel;
265 #else
266         qqq[j] = colors[(r1>>8) % ncolors].pixel;
267 #endif
268       else
269         abort();
270     }
271   }
272   t = r1; r1 = r1b; r1b = t;
273   t = r2; r2 = r2b; r2b = t;  
274 }
275
276
277 /* ------------- xscreensaver rendering -------------- */
278
279
280
281 char *progclass = "RD";
282
283
284 char *defaults [] = {
285   ".background: black",
286   ".foreground: white",
287   "*width:      0",                     /* tried to use -1 but it complained */
288   "*height:     0",
289   "*epoch:      40000",
290   "*reaction:   -1",
291   "*diffusion:  -1",
292   "*verbose:    off",
293   "*radius:     -1",
294   "*speed:      0.0",
295   "*size:       0.66",
296   "*delay:      1",
297   "*colors:     -1",
298   0
299 };
300
301 XrmOptionDescRec options [] = {
302   { "-width",           ".width",       XrmoptionSepArg, 0 },
303   { "-height",          ".height",      XrmoptionSepArg, 0 },
304   { "-epoch",           ".epoch",       XrmoptionSepArg, 0 },
305   { "-reaction",        ".reaction",    XrmoptionSepArg, 0 },
306   { "-diffusion",       ".diffusion",   XrmoptionSepArg, 0 },
307   { "-verbose",         ".verbose",     XrmoptionNoArg, "True" },
308   { "-radius",          ".radius",      XrmoptionSepArg, 0 },
309   { "-speed",           ".speed",       XrmoptionSepArg, 0 },
310   { "-size",            ".size",        XrmoptionSepArg, 0 },
311   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
312   { "-ncolors",         ".colors",      XrmoptionSepArg, 0 },
313   { 0, 0, 0, 0 }
314 };
315
316
317 /* why doesn't this work???  and more importantly, do i really still have
318    to do this in X? */
319    
320 #ifdef HAVE_XSHM_EXTENSION
321 #include <sys/ipc.h>
322 #include <sys/shm.h>
323 #include <X11/extensions/XShm.h>
324 #endif
325
326
327 void
328 random_colors() {
329   memset(colors, 0, ncolors*sizeof(*colors));
330   make_smooth_colormap (display, visual, cmap, colors, &ncolors,
331                         True, 0, True);
332   if (ncolors <= 2) {
333     mono_p = True;
334     ncolors = 2;
335     colors[0].flags = DoRed|DoGreen|DoBlue;
336     colors[0].red = colors[0].green = colors[0].blue = 0;
337     XAllocColor(display, cmap, &colors[0]);
338     colors[1].flags = DoRed|DoGreen|DoBlue;
339     colors[1].red = colors[1].green = colors[1].blue = 0xFFFF;
340     XAllocColor(display, cmap, &colors[1]);
341   }
342
343   /* Scale it up so that there are exactly 255 colors -- that keeps the
344      animation speed consistent, even when there aren't many allocatable
345      colors, and prevents the -mono mode from looking like static. */
346   if (ncolors != 255) {
347     int i, n = 255;
348     double scale = (double) ncolors / (double) (n+1);
349     XColor *c2 = (XColor *) malloc(sizeof(*c2) * (n+1));
350     for (i = 0; i < n; i++)
351       c2[i] = colors[(int) (i * scale)];
352     free(colors);
353     colors = c2;
354     ncolors = n;
355   }
356
357 }
358
359 /* should factor into RD-specfic and compute-every-pixel general */
360 void
361 screenhack (Display *dpy, Window win)
362 {
363   GC gc;
364   XGCValues gcv;
365   XWindowAttributes xgwa;
366   XImage *image;
367   int array_width, array_height;
368   double array_x, array_y;
369   double array_dx, array_dy;
370   int w2;
371   char *p;
372   int vdepth;
373   int npix;
374 #ifdef HAVE_XSHM_EXTENSION
375   int use_shm = 0;
376   XShmSegmentInfo shm_info;
377 #endif
378
379   double delay = get_float_resource ("delay", "Float");
380
381   display = dpy;
382   window = win;
383
384
385   XGetWindowAttributes (dpy, win, &xgwa);
386   visual = xgwa.visual;
387   pixack_init(&width, &height);
388   {
389     double s = get_float_resource ("size", "Float");
390     double p = get_float_resource ("speed", "Float");
391     if (s < 0.0 || s > 1.0)
392       s = 1.0;
393     s = sqrt(s);
394     array_width = xgwa.width * s;
395     array_height = xgwa.height * s;
396     if (s < 0.99) {
397       array_width = (array_width / width) * width;
398       array_height = (array_height / height) * height;
399     }
400     if (array_width < width) array_width = width;
401     if (array_height < height) array_height = height;
402     array_x = (xgwa.width - array_width)/2;
403     array_y = (xgwa.height - array_height)/2;
404     array_dx = p;
405     array_dy = .31415926 * p;
406   }
407   verbose = get_boolean_resource ("verbose", "Boolean");
408   npix = (width + 2) * (height + 2);
409   w2 = width + 2;
410   gcv.function = GXcopy;
411   gc = XCreateGC(dpy, win, GCFunction, &gcv);
412   vdepth = visual_depth(DefaultScreenOfDisplay(dpy), xgwa.visual);
413
414   /* This code only deals with pixmap depths of 1, 8, 16, and 32.
415      Therefore, we assume that those depths will be supported by the
416      coresponding visual depths (that depth-24 displays accept depth-32
417      pixmaps, and that depth-12 displays accept depth-16 pixmaps.) */
418   pdepth = (vdepth == 1 ? 1 :
419             vdepth <= 8 ? 8 :
420             vdepth <= 16 ? 16 :
421             32);
422
423   cmap = xgwa.colormap;
424   ncolors = get_integer_resource ("colors", "Integer");
425
426   if (ncolors <= 0) {
427     if (vdepth > 8)
428       ncolors = 2047;
429     else
430       ncolors = 255;
431   }
432
433   if (mono_p || ncolors < 2) ncolors = 2;
434   if (ncolors <= 2) mono_p = True;
435   colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
436
437   mapped = (vdepth <= 8 &&
438             has_writable_cells(xgwa.screen, xgwa.visual));
439
440   {
441     int i, di;
442     mc = (unsigned char *) malloc(1<<16);
443     for (i = 0; i < (1<<16); i++) {
444       di = (i + (random()&255))>>8;
445       if (di > 255) di = 255;
446       mc[i] = di;
447     }
448   }
449
450   p = malloc(npix * (pdepth == 1 ? 1 : (pdepth / 8)));
451   if (!p) {
452     fprintf(stderr, "not enough memory for %d pixels.\n", npix);
453     exit(1);
454   }
455
456 #ifdef HAVE_XSHM_EXTENSION
457   if (use_shm) {
458     printf("p=%X\n", p);
459     free(p);
460     image = XShmCreateImage(dpy, xgwa.visual, vdepth,
461                             ZPixmap, 0, &shm_info, width, height);
462     shm_info.shmid = shmget(IPC_PRIVATE,
463                             image->bytes_per_line * image->height,
464                             IPC_CREAT | 0777);
465     if (shm_info.shmid == -1)
466       printf ("shmget failed!");
467     shm_info.readOnly = False;
468     p = shmat(shm_info.shmid, 0, 0);
469     printf("p=%X %d\n", p, image->bytes_per_line);
470     XShmAttach(dpy, &shm_info);
471     XSync(dpy, False);
472   } else
473 #endif
474   image = XCreateImage(dpy, xgwa.visual, vdepth,
475                        ZPixmap, 0, p,
476                        width, height, 8, 0);
477
478   while (1) {
479     int i, j;
480     pixack_frame(p);
481     for (i = 0; i < array_width; i += width)
482       for (j = 0; j < array_height; j += height)
483 #ifdef HAVE_XSHM_EXTENSION
484         if (use_shm)
485           XShmPutImage(dpy, win, gc, image, 0, 0, i, j,
486                        width, height, False);
487         else
488 #endif
489           XPutImage(dpy, win, gc, image, 0, 0, i+array_x, j+array_y, width, height);
490
491     array_x += array_dx;
492     array_y += array_dy;
493     if (array_x < 0) {
494       array_x = 0;
495       array_dx = -array_dx;
496     } else if (array_x > (xgwa.width - array_width)) {
497       array_x = (xgwa.width - array_width);
498       array_dx = -array_dx;
499     }
500     if (array_y < 0) {
501       array_y = 0;
502       array_dy = -array_dy;
503     } else if (array_y > (xgwa.height - array_height)) {
504       array_y = (xgwa.height - array_height);
505       array_dy = -array_dy;
506     }
507     frame++;
508
509     XSync(dpy, False);
510     if (delay > 0)
511       usleep(1000 * delay);
512   }
513 }