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