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