0c074ea49f442e928539ab323da3f939e9af6383
[xscreensaver] / hacks / rd-bomb.c
1 /* xscreensaver, Copyright (c) 1992-2008 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   0
313 };
314
315 static XrmOptionDescRec rd_options [] = {
316   { "-width",           ".width",       XrmoptionSepArg, 0 },
317   { "-height",          ".height",      XrmoptionSepArg, 0 },
318   { "-epoch",           ".epoch",       XrmoptionSepArg, 0 },
319   { "-reaction",        ".reaction",    XrmoptionSepArg, 0 },
320   { "-diffusion",       ".diffusion",   XrmoptionSepArg, 0 },
321   { "-radius",          ".radius",      XrmoptionSepArg, 0 },
322   { "-speed",           ".speed",       XrmoptionSepArg, 0 },
323   { "-size",            ".size",        XrmoptionSepArg, 0 },
324   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
325   { "-ncolors",         ".colors",      XrmoptionSepArg, 0 },
326   { "-shm",             ".useSHM",      XrmoptionNoArg, "True" },
327   { "-no-shm",          ".useSHM",      XrmoptionNoArg, "False" },
328   { 0, 0, 0, 0 }
329 };
330
331
332 static void
333 random_colors(struct state *st)
334 {
335   memset(st->colors, 0, st->ncolors*sizeof(*st->colors));
336   make_smooth_colormap (st->dpy, st->visual, st->cmap, st->colors, &st->ncolors,
337                         True, 0, True);
338   if (st->ncolors <= 2) {
339     mono_p = True;
340     st->ncolors = 2;
341     st->colors[0].flags = DoRed|DoGreen|DoBlue;
342     st->colors[0].red = st->colors[0].green = st->colors[0].blue = 0;
343     XAllocColor(st->dpy, st->cmap, &st->colors[0]);
344     st->colors[1].flags = DoRed|DoGreen|DoBlue;
345     st->colors[1].red = st->colors[1].green = st->colors[1].blue = 0xFFFF;
346     XAllocColor(st->dpy, st->cmap, &st->colors[1]);
347   }
348
349   /* Scale it up so that there are exactly 255 colors -- that keeps the
350      animation speed consistent, even when there aren't many allocatable
351      colors, and prevents the -mono mode from looking like static. */
352   if (st->ncolors != 255) {
353     int i, n = 255;
354     double scale = (double) st->ncolors / (double) (n+1);
355     XColor *c2 = (XColor *) malloc(sizeof(*c2) * (n+1));
356     for (i = 0; i < n; i++)
357       c2[i] = st->colors[(int) (i * scale)];
358     free(st->colors);
359     st->colors = c2;
360     st->ncolors = n;
361   }
362
363 }
364
365
366 /* should factor into RD-specfic and compute-every-pixel general */
367 static void *
368 rd_init (Display *dpy, Window win)
369 {
370   struct state *st = (struct state *) calloc (1, sizeof(*st));
371   XGCValues gcv;
372   int w2;
373   int vdepth;
374
375   st->dpy = dpy;
376   st->window = win;
377
378   st->delay = get_integer_resource (st->dpy, "delay", "Float");
379
380 #ifdef HAVE_XSHM_EXTENSION
381   st->use_shm = get_boolean_resource(st->dpy, "useSHM", "Boolean");
382 #endif
383
384   XGetWindowAttributes (st->dpy, win, &st->xgwa);
385   st->visual = st->xgwa.visual;
386   pixack_init(st, &st->width, &st->height);
387   {
388     double s = get_float_resource (st->dpy, "size", "Float");
389     double p = get_float_resource (st->dpy, "speed", "Float");
390     if (s < 0.0 || s > 1.0)
391       s = 1.0;
392     s = sqrt(s);
393     st->array_width = st->xgwa.width * s;
394     st->array_height = st->xgwa.height * s;
395     if (s < 0.99) {
396       st->array_width = (st->array_width / st->width) * st->width;
397       st->array_height = (st->array_height / st->height) * st->height;
398     }
399     if (st->array_width < st->width) st->array_width = st->width;
400     if (st->array_height < st->height) st->array_height = st->height;
401     st->array_x = (st->xgwa.width - st->array_width)/2;
402     st->array_y = (st->xgwa.height - st->array_height)/2;
403     st->array_dx = p;
404     st->array_dy = .31415926 * p;
405
406     /* start in a random direction */
407     if (random() & 1) st->array_dx = -st->array_dx;
408     if (random() & 1) st->array_dy = -st->array_dy;
409
410   }
411   st->npix = (st->width + 2) * (st->height + 2);
412   w2 = st->width + 2;
413 /*  gcv.function = GXcopy;*/
414   st->gc = XCreateGC(st->dpy, win, 0 /*GCFunction*/, &gcv);
415   vdepth = visual_depth(DefaultScreenOfDisplay(st->dpy), st->xgwa.visual);
416
417   /* This code only deals with pixmap depths of 1, 8, 16, and 32.
418      Therefore, we assume that those depths will be supported by the
419      coresponding visual depths (that depth-24 dpys accept depth-32
420      pixmaps, and that depth-12 dpys accept depth-16 pixmaps.) */
421   st->pdepth = (vdepth == 1 ? 1 :
422             vdepth <= 8 ? 8 :
423             vdepth <= 16 ? 16 :
424             32);
425
426   /* Ok, this like, sucks and stuff.  There are some XFree86 systems
427      that have depth-24 visuals, that do not accept depth-32 XImages!
428      Which if you ask me is just absurd, since all it would take is
429      for the server to truncate the bits in that case.  So, this crap
430      here detects the specific case of: we have chosen depth 32;
431      and the server does not support depth 32.  In that case, we
432      try and use depth 16 instead.
433
434      The real fix would be to rewrite this program to deal with
435      depth 24 directly (or even better, arbitrary depths, but that
436      would mean going through the XImage routines instead of messing
437      with the XImage->data directly.)
438
439      jwz, 18-Mar-99: well, the X servers I have access to these days do
440      support 32-deep images on deep visuals, so I no longer have the
441      ability to test this code -- but it was causing problems on the
442      visuals that I do have, and I think that's because I mistakenly
443      wrote `pfv[i].depth' when I meant to write `pfv[i].bits_per_pixel'.
444      The symptom I was seeing was that the grid was 64x64, but the
445      images were being drawn 32x32 -- so there was a black stripe on
446      every other row.  Wow, this code sucks so much.
447   */
448   if (st->pdepth == 32)
449     {
450       int i, pfvc = 0;
451       Bool ok = False;
452       XPixmapFormatValues *pfv = XListPixmapFormats (st->dpy, &pfvc);
453       for (i = 0; i < pfvc; i++)
454         if (pfv[i].bits_per_pixel == st->pdepth)
455           ok = True;
456       if (!ok)
457         st->pdepth = 16;
458     }
459
460   st->cmap = st->xgwa.colormap;
461   st->ncolors = get_integer_resource (st->dpy, "colors", "Integer");
462
463   if (st->ncolors <= 0 || st->ncolors >= 255) {
464     if (vdepth > 8)
465       st->ncolors = 2047;
466     else
467       st->ncolors = 255;
468   }
469
470   if (mono_p || st->ncolors < 2) st->ncolors = 2;
471   if (st->ncolors <= 2) mono_p = True;
472   st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1));
473
474   st->mapped = (vdepth <= 8 &&
475                 has_writable_cells(st->xgwa.screen, st->xgwa.visual));
476
477   {
478     int i, di;
479     st->mc = (unsigned char *) malloc(1<<16);
480     for (i = 0; i < (1<<16); i++) {
481       di = (i + (random()&255))>>8;
482       if (di > 255) di = 255;
483       st->mc[i] = di;
484     }
485   }
486
487   st->pd = malloc(st->npix * (st->pdepth == 1 ? 1 : (st->pdepth / 8)));
488   if (!st->pd) {
489     fprintf(stderr, "not enough memory for %d pixels.\n", st->npix);
490     exit(1);
491   }
492
493   st->image = 0;
494
495 #ifdef HAVE_XSHM_EXTENSION
496   if (st->use_shm)
497     {
498       st->image = create_xshm_image(st->dpy, st->xgwa.visual, vdepth,
499                                 ZPixmap, 0, &st->shm_info, st->width, st->height);
500       if (!st->image)
501         st->use_shm = False;
502       else
503         {
504           free(st->pd);
505           st->pd = st->image->data;
506         }
507     }
508 #endif /* HAVE_XSHM_EXTENSION */
509
510   if (!st->image)
511     {
512       st->image = XCreateImage(st->dpy, st->xgwa.visual, vdepth,
513                            ZPixmap, 0, st->pd,
514                            st->width, st->height, 8, 0);
515     }
516
517   return st;
518 }
519
520 static unsigned long
521 rd_draw (Display *dpy, Window win, void *closure)
522 {
523   struct state *st = (struct state *) closure;
524   Bool bump = False;
525
526   int i, j;
527   pixack_frame(st, st->pd);
528   for (i = 0; i < st->array_width; i += st->width)
529     for (j = 0; j < st->array_height; j += st->height)
530 #ifdef HAVE_XSHM_EXTENSION
531       if (st->use_shm)
532         XShmPutImage(st->dpy, st->window, st->gc, st->image, 0, 0, i+st->array_x, j+st->array_y,
533                      st->width, st->height, False);
534       else
535 #endif
536         XPutImage(st->dpy, win, st->gc, st->image, 0, 0, i+st->array_x, j+st->array_y,
537                   st->width, st->height);
538
539   st->array_x += st->array_dx;
540   st->array_y += st->array_dy;
541   if (st->array_x < 0) {
542     st->array_x = 0;
543     st->array_dx = -st->array_dx;
544     bump = True;
545   } else if (st->array_x > (st->xgwa.width - st->array_width)) {
546     st->array_x = (st->xgwa.width - st->array_width);
547     st->array_dx = -st->array_dx;
548     bump = True;
549   }
550   if (st->array_y < 0) {
551     st->array_y = 0;
552     st->array_dy = -st->array_dy;
553     bump = True;
554   } else if (st->array_y > (st->xgwa.height - st->array_height)) {
555     st->array_y = (st->xgwa.height - st->array_height);
556     st->array_dy = -st->array_dy;
557     bump = True;
558   }
559
560   if (bump) {
561     if (random() & 1) {
562       double swap = st->array_dx;
563       st->array_dx = st->array_dy;
564       st->array_dy = swap;
565     }
566   }
567
568   st->frame++;
569
570   return st->delay;
571 }
572
573 static void
574 rd_reshape (Display *dpy, Window window, void *closure, 
575                  unsigned int w, unsigned int h)
576 {
577 }
578
579 static Bool
580 rd_event (Display *dpy, Window window, void *closure, XEvent *event)
581 {
582   return False;
583 }
584
585 static void
586 rd_free (Display *dpy, Window window, void *closure)
587 {
588 }
589
590 XSCREENSAVER_MODULE_2 ("RDbomb", rdbomb, rd)