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