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