3641282f700da31ff161edca61acde5155653987
[xscreensaver] / hacks / rd-bomb.c
1 /* xscreensaver, Copyright (c) 1992, 1995, 1997, 1998, 1999
2  *  Jamie Zawinski <jwz@jwz.org>
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 #include <X11/Xutil.h>
25
26 #ifdef HAVE_XSHM_EXTENSION
27 # include "xshm.h"
28 #endif /* HAVE_XSHM_EXTENSION */
29
30 /* costs ~6% speed */
31 #define dither_when_mapped 1
32
33 static int verbose;
34 static int ncolors = 0;
35 static XColor *colors = 0;
36 static Display *display;
37 static Visual *visual;
38 #if dither_when_mapped
39 static unsigned char *mc = 0;
40 #endif
41 static Colormap cmap = 0;
42 static Window window;
43 static int mapped;
44 static int pdepth;
45 static void random_colors(void);
46
47 /* -----------------------------------------------------------
48    pixel hack, 8-bit pixel grid, first/next frame interface
49
50    pixack_init(int *size_h, int *size_v)
51    pixack_frame(char *pix_buf)
52    */
53
54
55 #define bps 16
56 #define mx ((1<<16)-1)
57
58 /* you can replace integer mults wish shift/adds with these,
59    but it doesn't help on my 586 */
60 #define x5(n) ((n<<2)+n)
61 #define x7(n) ((n<<3)-n)
62
63 /* why strip bit? */
64 #define R (ya_random()&((1<<30)-1))
65
66 static int frame = 0, epoch_time;
67 static ushort *r1, *r2, *r1b, *r2b;
68 static int width, height, npix;
69 static int radius;
70 static int reaction = 0;
71 static int diffusion = 0;
72
73 /* returns number of pixels that the pixack produces.  called once. */
74 static void
75 pixack_init(int *size_h, int *size_v) 
76 {
77   int sz_base;
78   width = get_integer_resource ("width", "Integer");
79   height = get_integer_resource ("height", "Integer");
80   sz_base = 80 + (R%40);
81   if (width <= 0) width = (R%20) ? sz_base : (28 + R%10);
82   if (height <= 0) height = (R%20) ? sz_base : (28 + R%10);
83
84   /* jwz: when (and only when) XSHM is in use on an SGI 8-bit visual,
85      we get shear unless width is a multiple of 4.  I don't understand
86      why.  This is undoubtedly the wrong fix... */
87   width &= ~0x7;
88
89   /* don't go there */
90   if (width < 10) width = 10;
91   if (height < 10) height = 10;
92   epoch_time = get_integer_resource ("epoch", "Integer");
93   npix = (width + 2) * (height + 2);
94   r1 = (ushort *) malloc(sizeof(ushort) * npix);
95   r2 = (ushort *) malloc(sizeof(ushort) * npix);
96   r1b = (ushort *) malloc(sizeof(ushort) * npix);
97   r2b = (ushort *) malloc(sizeof(ushort) * npix);
98
99   if (!r1 || !r2 || !r1b || !r2b) {
100     fprintf(stderr, "not enough memory for %d pixels.\n", npix);
101     exit(1);
102   }
103
104   *size_h = width;
105   *size_v = height;
106 }
107
108 #define test_pattern_hyper 0
109
110
111 /* returns the pixels.  called many times. */
112 static void
113 pixack_frame(char *pix_buf) 
114 {
115   int i, j;
116   int w2 = width + 2;
117   ushort *t;
118 #if test_pattern_hyper
119   if (frame&0x100)
120     sleep(1);
121 #endif
122   if (verbose) {
123     double tm = 0;
124     struct timeval tp;
125     if (!(frame%100)) {
126       double tm2;
127 #ifdef GETTIMEOFDAY_TWO_ARGS
128       struct timezone tzp;
129       gettimeofday(&tp, &tzp);
130 #else
131       gettimeofday(&tp);
132 #endif
133       tm2 = tp.tv_sec + tp.tv_usec * 1e-6;
134       if (frame > 0)
135         printf("fps = %2.4g\n", 100.0 / (tm2 - tm));
136       tm = tm2;
137     }
138   }
139   if (!(frame%epoch_time)) {
140     int s;
141     if (0 != frame) {
142       int t = epoch_time / 500;
143       if (t > 15)
144         t = 15;
145       sleep(t);
146     }
147           
148     for (i = 0; i < npix; i++) {
149       /* equilibrium */
150       r1[i] = 65500;
151       r2[i] = 11;
152     }
153
154     random_colors();
155
156     XSetWindowBackground(display, window, colors[255 % ncolors].pixel);
157     XClearWindow(display, window);
158
159     s = w2 * (height/2) + width/2;
160     radius = get_integer_resource ("radius", "Integer");
161     {
162       int maxr = width/2-2;
163       int maxr2 = height/2-2;
164       if (maxr2 < maxr) maxr = maxr2;
165
166       if (radius < 0)
167         radius = 1 + ((R%10) ? (R%5) : (R % maxr));
168       if (radius > maxr) radius = maxr;
169     }
170     for (i = -radius; i < (radius+1); i++)
171       for (j = -radius; j < (radius+1); j++)
172         r2[s + i + j*w2] = mx - (R&63);
173     reaction = get_integer_resource ("reaction", "Integer");
174     if (reaction < 0 || reaction > 2) reaction = R&1;
175     diffusion = get_integer_resource ("diffusion", "Integer");
176     if (diffusion < 0 || diffusion > 2)
177       diffusion = (R%5) ? ((R%3)?0:1) : 2;
178     if (2 == reaction && 2 == diffusion)
179       reaction = diffusion = 0;
180       
181     if (verbose)
182       printf("reaction = %d\ndiffusion = %d\nradius = %d\n",
183              reaction, diffusion, radius);
184   }
185   for (i = 0; i <= width+1; i++) {
186     r1[i] = r1[i + w2 * height];
187     r2[i] = r2[i + w2 * height];
188     r1[i + w2 * (height + 1)] = r1[i + w2];
189     r2[i + w2 * (height + 1)] = r2[i + w2];
190   }
191   for (i = 0; i <= height+1; i++) {
192     r1[w2 * i] = r1[width + w2 * i];
193     r2[w2 * i] = r2[width + w2 * i];
194     r1[w2 * i + width + 1] = r1[w2 * i + 1];
195     r2[w2 * i + width + 1] = r2[w2 * i + 1];
196   }
197   for (i = 0; i < height; i++) {
198     int ii = i + 1;
199     char *q = pix_buf + width * i;
200     short *qq = ((short *) pix_buf) + width * i;
201     long  *qqq = ((long *) pix_buf) + width * i;
202     ushort *i1 = r1 + 1 + w2 * ii;
203     ushort *i2 = r2 + 1 + w2 * ii;
204     ushort *o1 = r1b + 1 + w2 * ii;
205     ushort *o2 = r2b + 1 + w2 * ii;
206     for (j = 0; j < width; j++) {
207 #if test_pattern_hyper
208       int r1 = (i * j + (frame&127)*frame)&65535;
209 #else
210       int uvv, r1 = 0, r2 = 0;
211       switch (diffusion) {
212       case 0:
213         r1 = i1[j] + i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
214         r1 = r1 / 5;
215         r2 = (i2[j]<<3) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
216         r2 = r2 / 12;
217         break;
218       case 1:
219         r1 = i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
220         r1 = r1 >> 2;
221         r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
222         r2 = r2 >> 3;
223         break;
224       case 2:
225         r1 = (i1[j]<<1) + (i1[j+1]<<1) + (i1[j-1]<<1) + i1[j+w2] + i1[j-w2];
226         r1 = r1 >> 3;
227         r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
228         r2 = r2 >> 3;
229         break;
230       }
231
232       /* John E. Pearson "Complex Patterns in a Simple System"
233          Science, July 1993 */
234
235       uvv = (((r1 * r2) >> bps) * r2) >> bps;
236       switch (reaction) {  /* costs 4% */
237       case 0:
238         r1 += 4 * (((28 * (mx-r1)) >> 10) - uvv);
239         r2 += 4 * (uvv - ((80 * r2) >> 10));
240         break;
241       case 1:
242         r1 += 3 * (((27 * (mx-r1)) >> 10) - uvv);
243         r2 += 3 * (uvv - ((80 * r2) >> 10));
244         break;
245       case 2:
246         r1 += 2 * (((28 * (mx-r1)) >> 10) - uvv);
247         r2 += 3 * (uvv - ((80 * r2) >> 10));
248         break;
249       }
250       if (r1 > mx) r1 = mx;
251       if (r2 > mx) r2 = mx;
252       if (r1 < 0) r1 = 0;
253       if (r2 < 0) r2 = 0;
254       o1[j] = r1;
255       o2[j] = r2;
256 #endif
257
258       /* this is terrible.  here i want to assume ncolors = 256.
259          should lose double indirection */
260
261       if (mapped)
262 #if dither_when_mapped
263         q[j] = colors[mc[r1]  % ncolors].pixel;
264 #else
265       q[j] = colors[(r1>>8) % ncolors].pixel;
266 #endif
267       else if (pdepth == 8)
268         q[j] = colors[(r1>>8) % ncolors].pixel;
269       else if (pdepth == 16)
270 #if dither_when_mapped
271         qq[j] = colors[mc[r1] % ncolors].pixel;
272 #else
273         qq[j] = colors[(r1>>8) % ncolors].pixel;
274 #endif
275       else if (pdepth == 32)
276 #if dither_when_mapped
277         qqq[j] = colors[mc[r1] % ncolors].pixel;
278 #else
279         qqq[j] = colors[(r1>>8) % ncolors].pixel;
280 #endif
281       else
282         abort();
283     }
284   }
285   t = r1; r1 = r1b; r1b = t;
286   t = r2; r2 = r2b; r2b = t;  
287 }
288
289
290 /* ------------- xscreensaver rendering -------------- */
291
292
293
294 char *progclass = "RD";
295
296
297 char *defaults [] = {
298   ".background: black",
299   ".foreground: white",
300   "*width:      0",                     /* tried to use -1 but it complained */
301   "*height:     0",
302   "*epoch:      40000",
303   "*reaction:   -1",
304   "*diffusion:  -1",
305   "*verbose:    off",
306   "*radius:     -1",
307   "*speed:      0.0",
308   "*size:       1.0",
309   "*delay:      1",
310   "*colors:     -1",
311 #ifdef HAVE_XSHM_EXTENSION
312   "*useSHM:     True",
313 #endif /* HAVE_XSHM_EXTENSION */
314   0
315 };
316
317 XrmOptionDescRec options [] = {
318   { "-width",           ".width",       XrmoptionSepArg, 0 },
319   { "-height",          ".height",      XrmoptionSepArg, 0 },
320   { "-epoch",           ".epoch",       XrmoptionSepArg, 0 },
321   { "-reaction",        ".reaction",    XrmoptionSepArg, 0 },
322   { "-diffusion",       ".diffusion",   XrmoptionSepArg, 0 },
323   { "-verbose",         ".verbose",     XrmoptionNoArg, "True" },
324   { "-radius",          ".radius",      XrmoptionSepArg, 0 },
325   { "-speed",           ".speed",       XrmoptionSepArg, 0 },
326   { "-size",            ".size",        XrmoptionSepArg, 0 },
327   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
328   { "-ncolors",         ".colors",      XrmoptionSepArg, 0 },
329 #ifdef HAVE_XSHM_EXTENSION
330   { "-shm",             ".useSHM",      XrmoptionNoArg, "True" },
331   { "-no-shm",          ".useSHM",      XrmoptionNoArg, "False" },
332 #endif /* HAVE_XSHM_EXTENSION */
333   { 0, 0, 0, 0 }
334 };
335
336
337 static void
338 random_colors(void)
339 {
340   memset(colors, 0, ncolors*sizeof(*colors));
341   make_smooth_colormap (display, visual, cmap, colors, &ncolors,
342                         True, 0, True);
343   if (ncolors <= 2) {
344     mono_p = True;
345     ncolors = 2;
346     colors[0].flags = DoRed|DoGreen|DoBlue;
347     colors[0].red = colors[0].green = colors[0].blue = 0;
348     XAllocColor(display, cmap, &colors[0]);
349     colors[1].flags = DoRed|DoGreen|DoBlue;
350     colors[1].red = colors[1].green = colors[1].blue = 0xFFFF;
351     XAllocColor(display, cmap, &colors[1]);
352   }
353
354   /* Scale it up so that there are exactly 255 colors -- that keeps the
355      animation speed consistent, even when there aren't many allocatable
356      colors, and prevents the -mono mode from looking like static. */
357   if (ncolors != 255) {
358     int i, n = 255;
359     double scale = (double) ncolors / (double) (n+1);
360     XColor *c2 = (XColor *) malloc(sizeof(*c2) * (n+1));
361     for (i = 0; i < n; i++)
362       c2[i] = colors[(int) (i * scale)];
363     free(colors);
364     colors = c2;
365     ncolors = n;
366   }
367
368 }
369
370 /* should factor into RD-specfic and compute-every-pixel general */
371 void
372 screenhack (Display *dpy, Window win)
373 {
374   GC gc;
375   XGCValues gcv;
376   XWindowAttributes xgwa;
377   XImage *image;
378   int array_width, array_height;
379   double array_x, array_y;
380   double array_dx, array_dy;
381   int w2;
382   char *p;
383   int vdepth;
384   int npix;
385 #ifdef HAVE_XSHM_EXTENSION
386   Bool use_shm = get_boolean_resource("useSHM", "Boolean");
387   XShmSegmentInfo shm_info;
388 #endif
389
390   double delay = get_float_resource ("delay", "Float");
391
392   display = dpy;
393   window = win;
394
395
396   XGetWindowAttributes (dpy, win, &xgwa);
397   visual = xgwa.visual;
398   pixack_init(&width, &height);
399   {
400     double s = get_float_resource ("size", "Float");
401     double p = get_float_resource ("speed", "Float");
402     if (s < 0.0 || s > 1.0)
403       s = 1.0;
404     s = sqrt(s);
405     array_width = xgwa.width * s;
406     array_height = xgwa.height * s;
407     if (s < 0.99) {
408       array_width = (array_width / width) * width;
409       array_height = (array_height / height) * height;
410     }
411     if (array_width < width) array_width = width;
412     if (array_height < height) array_height = height;
413     array_x = (xgwa.width - array_width)/2;
414     array_y = (xgwa.height - array_height)/2;
415     array_dx = p;
416     array_dy = .31415926 * p;
417
418     /* start in a random direction */
419     if (random() & 1) array_dx = -array_dx;
420     if (random() & 1) array_dy = -array_dy;
421
422   }
423   verbose = get_boolean_resource ("verbose", "Boolean");
424   npix = (width + 2) * (height + 2);
425   w2 = width + 2;
426   gcv.function = GXcopy;
427   gc = XCreateGC(dpy, win, GCFunction, &gcv);
428   vdepth = visual_depth(DefaultScreenOfDisplay(dpy), xgwa.visual);
429
430   /* This code only deals with pixmap depths of 1, 8, 16, and 32.
431      Therefore, we assume that those depths will be supported by the
432      coresponding visual depths (that depth-24 displays accept depth-32
433      pixmaps, and that depth-12 displays accept depth-16 pixmaps.) */
434   pdepth = (vdepth == 1 ? 1 :
435             vdepth <= 8 ? 8 :
436             vdepth <= 16 ? 16 :
437             32);
438
439   /* Ok, this like, sucks and stuff.  There are some XFree86 systems
440      that have depth-24 visuals, that do not accept depth-32 XImages!
441      Which if you ask me is just absurd, since all it would take is
442      for the server to truncate the bits in that case.  So, this crap
443      here detects the specific case of: we have chosen depth 32;
444      and the server does not support depth 32.  In that case, we
445      try and use depth 16 instead.
446
447      The real fix would be to rewrite this program to deal with
448      depth 24 directly (or even better, arbitrary depths, but that
449      would mean going through the XImage routines instead of messing
450      with the XImage->data directly.)
451    */
452   if (pdepth == 32)
453     {
454       int i, pfvc = 0;
455       Bool ok = False;
456       XPixmapFormatValues *pfv = XListPixmapFormats (dpy, &pfvc);
457       for (i = 0; i < pfvc; i++)
458         if (pfv[i].depth == pdepth)
459           ok = True;
460       if (!ok)
461         pdepth = 16;
462     }
463
464   cmap = xgwa.colormap;
465   ncolors = get_integer_resource ("colors", "Integer");
466
467   if (ncolors <= 0) {
468     if (vdepth > 8)
469       ncolors = 2047;
470     else
471       ncolors = 255;
472   }
473
474   if (mono_p || ncolors < 2) ncolors = 2;
475   if (ncolors <= 2) mono_p = True;
476   colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
477
478   mapped = (vdepth <= 8 &&
479             has_writable_cells(xgwa.screen, xgwa.visual));
480
481   {
482     int i, di;
483     mc = (unsigned char *) malloc(1<<16);
484     for (i = 0; i < (1<<16); i++) {
485       di = (i + (random()&255))>>8;
486       if (di > 255) di = 255;
487       mc[i] = di;
488     }
489   }
490
491   p = malloc(npix * (pdepth == 1 ? 1 : (pdepth / 8)));
492   if (!p) {
493     fprintf(stderr, "not enough memory for %d pixels.\n", npix);
494     exit(1);
495   }
496
497   image = 0;
498
499 #ifdef HAVE_XSHM_EXTENSION
500   if (use_shm)
501     {
502       image = create_xshm_image(dpy, xgwa.visual, vdepth,
503                                 ZPixmap, 0, &shm_info, width, height);
504       if (!image)
505         use_shm = False;
506       else
507         {
508           free(p);
509           p = image->data;
510         }
511     }
512 #endif /* HAVE_XSHM_EXTENSION */
513
514   if (!image)
515     {
516       image = XCreateImage(dpy, xgwa.visual, vdepth,
517                            ZPixmap, 0, p,
518                            width, height, 8, 0);
519     }
520
521   while (1) {
522     Bool bump = False;
523
524     int i, j;
525     pixack_frame(p);
526     for (i = 0; i < array_width; i += width)
527       for (j = 0; j < array_height; j += height)
528 #ifdef HAVE_XSHM_EXTENSION
529         if (use_shm)
530           XShmPutImage(dpy, win, gc, image, 0, 0, i+array_x, j+array_y,
531                        width, height, False);
532         else
533 #endif
534           XPutImage(dpy, win, gc, image, 0, 0, i+array_x, j+array_y,
535                     width, height);
536
537     array_x += array_dx;
538     array_y += array_dy;
539     if (array_x < 0) {
540       array_x = 0;
541       array_dx = -array_dx;
542       bump = True;
543     } else if (array_x > (xgwa.width - array_width)) {
544       array_x = (xgwa.width - array_width);
545       array_dx = -array_dx;
546       bump = True;
547     }
548     if (array_y < 0) {
549       array_y = 0;
550       array_dy = -array_dy;
551       bump = True;
552     } else if (array_y > (xgwa.height - array_height)) {
553       array_y = (xgwa.height - array_height);
554       array_dy = -array_dy;
555       bump = True;
556     }
557
558     if (bump) {
559       if (random() & 1) {
560         double swap = array_dx;
561         array_dx = array_dy;
562         array_dy = swap;
563       }
564     }
565
566     frame++;
567
568     XSync(dpy, False);
569     screenhack_handle_events (dpy);
570     if (delay > 0)
571       usleep(1000 * delay);
572   }
573 }