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