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