http://svn.poeml.de/viewvc/ppc/src-unpacked/xscreensaver/xscreensaver-4.12.tar.bz2...
[xscreensaver] / hacks / rd-bomb.c
1 /* xscreensaver, Copyright (c) 1992, 1995, 1997, 1998, 1999, 2003
2  *  Jamie Zawinski <jwz@jwz.org>
3  *
4  *  reaction/diffusion textures
5  *  Copyright (c) 1997 Scott Draves spot@transmeta.com
6  *  this code is derived from Bomb
7  *  see http://www.cs.cmu.edu/~spot/bomb.html
8  *
9  * Permission to use, copy, modify, distribute, and sell this software and its
10  * documentation for any purpose is hereby granted without fee, provided that
11  * the above copyright notice appear in all copies and that both that
12  * copyright notice and this permission notice appear in supporting
13  * documentation.  No representations are made about the suitability of this
14  * software for any purpose.  It is provided "as is" without express or 
15  * implied warranty.
16  *
17  * And remember: X Windows is to graphics hacking as roman numerals are to
18  * the square root of pi.
19  */
20
21 #include <math.h>
22
23 #include "screenhack.h"
24 #include <X11/Xutil.h>
25
26 #ifdef HAVE_XSHM_EXTENSION
27 # include "xshm.h"
28 #endif /* HAVE_XSHM_EXTENSION */
29
30 /* costs ~6% speed */
31 #define dither_when_mapped 1
32
33 static int verbose;
34 static int ncolors = 0;
35 static XColor *colors = 0;
36 static Display *display;
37 static Visual *visual;
38 #if dither_when_mapped
39 static unsigned char *mc = 0;
40 #endif
41 static Colormap cmap = 0;
42 static Window window;
43 static int mapped;
44 static int pdepth;
45 static void random_colors(void);
46
47 /* -----------------------------------------------------------
48    pixel hack, 8-bit pixel grid, first/next frame interface
49
50    pixack_init(int *size_h, int *size_v)
51    pixack_frame(char *pix_buf)
52    */
53
54
55 #define bps 16
56 #define mx ((1<<16)-1)
57
58 /* you can replace integer mults wish shift/adds with these,
59    but it doesn't help on my 586 */
60 #define x5(n) ((n<<2)+n)
61 #define x7(n) ((n<<3)-n)
62
63 /* why strip bit? */
64 #define R (ya_random()&((1<<30)-1))
65
66 static int frame = 0, epoch_time;
67 static ushort *r1, *r2, *r1b, *r2b;
68 static int width, height, npix;
69 static int radius;
70 static int reaction = 0;
71 static int diffusion = 0;
72
73 /* returns number of pixels that the pixack produces.  called once. */
74 static void
75 pixack_init(int *size_h, int *size_v) 
76 {
77   int sz_base;
78   width = get_integer_resource ("width", "Integer");
79   height = get_integer_resource ("height", "Integer");
80   sz_base = 80 + (R%40);
81   if (width <= 0) width = (R%20) ? sz_base : (28 + R%10);
82   if (height <= 0) height = (R%20) ? sz_base : (28 + R%10);
83
84   /* jwz: when (and only when) XSHM is in use on an SGI 8-bit visual,
85      we get shear unless width is a multiple of 4.  I don't understand
86      why.  This is undoubtedly the wrong fix... */
87   width &= ~0x7;
88
89   /* don't go there */
90   if (width < 10) width = 10;
91   if (height < 10) height = 10;
92   epoch_time = get_integer_resource ("epoch", "Integer");
93   npix = (width + 2) * (height + 2);
94   r1 = (ushort *) malloc(sizeof(ushort) * npix);
95   r2 = (ushort *) malloc(sizeof(ushort) * npix);
96   r1b = (ushort *) malloc(sizeof(ushort) * npix);
97   r2b = (ushort *) malloc(sizeof(ushort) * npix);
98
99   if (!r1 || !r2 || !r1b || !r2b) {
100     fprintf(stderr, "not enough memory for %d pixels.\n", npix);
101     exit(1);
102   }
103
104   *size_h = width;
105   *size_v = height;
106 }
107
108 #define test_pattern_hyper 0
109
110
111 /* returns the pixels.  called many times. */
112 static void
113 pixack_frame(char *pix_buf) 
114 {
115   int i, j;
116   int w2 = width + 2;
117   ushort *t;
118 #if test_pattern_hyper
119   if (frame&0x100)
120     sleep(1);
121 #endif
122   if (verbose) {
123     double tm = 0;
124     struct timeval tp;
125     if (!(frame%100)) {
126       double tm2;
127 #ifdef GETTIMEOFDAY_TWO_ARGS
128       struct timezone tzp;
129       gettimeofday(&tp, &tzp);
130 #else
131       gettimeofday(&tp);
132 #endif
133       tm2 = tp.tv_sec + tp.tv_usec * 1e-6;
134       if (frame > 0)
135         printf("fps = %2.4g\n", 100.0 / (tm2 - tm));
136       tm = tm2;
137     }
138   }
139   if (!(frame%epoch_time)) {
140     int s;
141     if (0 != frame) {
142       int tt = epoch_time / 500;
143       if (tt > 15)
144         tt = 15;
145       sleep(tt);
146     }
147           
148     for (i = 0; i < npix; i++) {
149       /* equilibrium */
150       r1[i] = 65500;
151       r2[i] = 11;
152     }
153
154     random_colors();
155
156     XSetWindowBackground(display, window, colors[255 % ncolors].pixel);
157     XClearWindow(display, window);
158
159     s = w2 * (height/2) + width/2;
160     radius = get_integer_resource ("radius", "Integer");
161     {
162       int maxr = width/2-2;
163       int maxr2 = height/2-2;
164       if (maxr2 < maxr) maxr = maxr2;
165
166       if (radius < 0)
167         radius = 1 + ((R%10) ? (R%5) : (R % maxr));
168       if (radius > maxr) radius = maxr;
169     }
170     for (i = -radius; i < (radius+1); i++)
171       for (j = -radius; j < (radius+1); j++)
172         r2[s + i + j*w2] = mx - (R&63);
173     reaction = get_integer_resource ("reaction", "Integer");
174     if (reaction < 0 || reaction > 2) reaction = R&1;
175     diffusion = get_integer_resource ("diffusion", "Integer");
176     if (diffusion < 0 || diffusion > 2)
177       diffusion = (R%5) ? ((R%3)?0:1) : 2;
178     if (2 == reaction && 2 == diffusion)
179       reaction = diffusion = 0;
180       
181     if (verbose)
182       printf("reaction = %d\ndiffusion = %d\nradius = %d\n",
183              reaction, diffusion, radius);
184   }
185   for (i = 0; i <= width+1; i++) {
186     r1[i] = r1[i + w2 * height];
187     r2[i] = r2[i + w2 * height];
188     r1[i + w2 * (height + 1)] = r1[i + w2];
189     r2[i + w2 * (height + 1)] = r2[i + w2];
190   }
191   for (i = 0; i <= height+1; i++) {
192     r1[w2 * i] = r1[width + w2 * i];
193     r2[w2 * i] = r2[width + w2 * i];
194     r1[w2 * i + width + 1] = r1[w2 * i + 1];
195     r2[w2 * i + width + 1] = r2[w2 * i + 1];
196   }
197   for (i = 0; i < height; i++) {
198     int ii = i + 1;
199     char *q = pix_buf + width * i;
200     short *qq = ((short *) pix_buf) + width * i;
201     long  *qqq = ((long *) pix_buf) + width * i;
202     ushort *i1 = r1 + 1 + w2 * ii;
203     ushort *i2 = r2 + 1 + w2 * ii;
204     ushort *o1 = r1b + 1 + w2 * ii;
205     ushort *o2 = r2b + 1 + w2 * ii;
206     for (j = 0; j < width; j++) {
207 #if test_pattern_hyper
208       int r1 = (i * j + (frame&127)*frame)&65535;
209 #else
210       int uvv, r1 = 0, r2 = 0;
211       switch (diffusion) {
212       case 0:
213         r1 = i1[j] + i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
214         r1 = r1 / 5;
215         r2 = (i2[j]<<3) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
216         r2 = r2 / 12;
217         break;
218       case 1:
219         r1 = i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
220         r1 = r1 >> 2;
221         r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
222         r2 = r2 >> 3;
223         break;
224       case 2:
225         r1 = (i1[j]<<1) + (i1[j+1]<<1) + (i1[j-1]<<1) + i1[j+w2] + i1[j-w2];
226         r1 = r1 >> 3;
227         r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
228         r2 = r2 >> 3;
229         break;
230       }
231
232       /* John E. Pearson "Complex Patterns in a Simple System"
233          Science, July 1993 */
234
235       uvv = (((r1 * r2) >> bps) * r2) >> bps;
236       switch (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 (mapped)
262 #if dither_when_mapped
263         q[j] = colors[mc[r1]  % ncolors].pixel;
264 #else
265       q[j] = colors[(r1>>8) % ncolors].pixel;
266 #endif
267       else if (pdepth == 8)
268         q[j] = colors[(r1>>8) % ncolors].pixel;
269       else if (pdepth == 16)
270 #if dither_when_mapped
271         qq[j] = colors[mc[r1] % ncolors].pixel;
272 #else
273         qq[j] = colors[(r1>>8) % ncolors].pixel;
274 #endif
275       else if (pdepth == 32)
276 #if dither_when_mapped
277         qqq[j] = colors[mc[r1] % ncolors].pixel;
278 #else
279         qqq[j] = colors[(r1>>8) % ncolors].pixel;
280 #endif
281       else
282         abort();
283     }
284   }
285   t = r1; r1 = r1b; r1b = t;
286   t = r2; r2 = r2b; r2b = t;  
287 }
288
289
290 /* ------------- xscreensaver rendering -------------- */
291
292
293
294 char *progclass = "RD";
295
296
297 char *defaults [] = {
298   ".background: black",
299   ".foreground: white",
300   "*width:      0",                     /* tried to use -1 but it complained */
301   "*height:     0",
302   "*epoch:      40000",
303   "*reaction:   -1",
304   "*diffusion:  -1",
305   "*verbose:    off",
306   "*radius:     -1",
307   "*speed:      0.0",
308   "*size:       1.0",
309   "*delay:      1",
310   "*colors:     -1",
311 #ifdef HAVE_XSHM_EXTENSION
312   "*useSHM:     True",
313 #endif /* HAVE_XSHM_EXTENSION */
314   0
315 };
316
317 XrmOptionDescRec 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   { "-verbose",         ".verbose",     XrmoptionNoArg, "True" },
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 #ifdef HAVE_XSHM_EXTENSION
330   { "-shm",             ".useSHM",      XrmoptionNoArg, "True" },
331   { "-no-shm",          ".useSHM",      XrmoptionNoArg, "False" },
332 #endif /* HAVE_XSHM_EXTENSION */
333   { 0, 0, 0, 0 }
334 };
335
336
337 static void
338 random_colors(void)
339 {
340   memset(colors, 0, ncolors*sizeof(*colors));
341   make_smooth_colormap (display, visual, cmap, colors, &ncolors,
342                         True, 0, True);
343   if (ncolors <= 2) {
344     mono_p = True;
345     ncolors = 2;
346     colors[0].flags = DoRed|DoGreen|DoBlue;
347     colors[0].red = colors[0].green = colors[0].blue = 0;
348     XAllocColor(display, cmap, &colors[0]);
349     colors[1].flags = DoRed|DoGreen|DoBlue;
350     colors[1].red = colors[1].green = colors[1].blue = 0xFFFF;
351     XAllocColor(display, cmap, &colors[1]);
352   }
353
354   /* Scale it up so that there are exactly 255 colors -- that keeps the
355      animation speed consistent, even when there aren't many allocatable
356      colors, and prevents the -mono mode from looking like static. */
357   if (ncolors != 255) {
358     int i, n = 255;
359     double scale = (double) ncolors / (double) (n+1);
360     XColor *c2 = (XColor *) malloc(sizeof(*c2) * (n+1));
361     for (i = 0; i < n; i++)
362       c2[i] = colors[(int) (i * scale)];
363     free(colors);
364     colors = c2;
365     ncolors = n;
366   }
367
368 }
369
370 /* should factor into RD-specfic and compute-every-pixel general */
371 void
372 screenhack (Display *dpy, Window win)
373 {
374   GC gc;
375   XGCValues gcv;
376   XWindowAttributes xgwa;
377   XImage *image;
378   int array_width, array_height;
379   double array_x, array_y;
380   double array_dx, array_dy;
381   int w2;
382   char *pd;
383   int vdepth;
384   int npix;
385 #ifdef HAVE_XSHM_EXTENSION
386   Bool use_shm = get_boolean_resource("useSHM", "Boolean");
387   XShmSegmentInfo shm_info;
388 #endif
389
390   double delay = get_float_resource ("delay", "Float");
391
392   display = dpy;
393   window = win;
394
395
396   XGetWindowAttributes (dpy, win, &xgwa);
397   visual = xgwa.visual;
398   pixack_init(&width, &height);
399   {
400     double s = get_float_resource ("size", "Float");
401     double p = get_float_resource ("speed", "Float");
402     if (s < 0.0 || s > 1.0)
403       s = 1.0;
404     s = sqrt(s);
405     array_width = xgwa.width * s;
406     array_height = xgwa.height * s;
407     if (s < 0.99) {
408       array_width = (array_width / width) * width;
409       array_height = (array_height / height) * height;
410     }
411     if (array_width < width) array_width = width;
412     if (array_height < height) array_height = height;
413     array_x = (xgwa.width - array_width)/2;
414     array_y = (xgwa.height - array_height)/2;
415     array_dx = p;
416     array_dy = .31415926 * p;
417
418     /* start in a random direction */
419     if (random() & 1) array_dx = -array_dx;
420     if (random() & 1) array_dy = -array_dy;
421
422   }
423   verbose = get_boolean_resource ("verbose", "Boolean");
424   npix = (width + 2) * (height + 2);
425   w2 = width + 2;
426   gcv.function = GXcopy;
427   gc = XCreateGC(dpy, win, GCFunction, &gcv);
428   vdepth = visual_depth(DefaultScreenOfDisplay(dpy), xgwa.visual);
429
430   /* This code only deals with pixmap depths of 1, 8, 16, and 32.
431      Therefore, we assume that those depths will be supported by the
432      coresponding visual depths (that depth-24 displays accept depth-32
433      pixmaps, and that depth-12 displays accept depth-16 pixmaps.) */
434   pdepth = (vdepth == 1 ? 1 :
435             vdepth <= 8 ? 8 :
436             vdepth <= 16 ? 16 :
437             32);
438
439   /* Ok, this like, sucks and stuff.  There are some XFree86 systems
440      that have depth-24 visuals, that do not accept depth-32 XImages!
441      Which if you ask me is just absurd, since all it would take is
442      for the server to truncate the bits in that case.  So, this crap
443      here detects the specific case of: we have chosen depth 32;
444      and the server does not support depth 32.  In that case, we
445      try and use depth 16 instead.
446
447      The real fix would be to rewrite this program to deal with
448      depth 24 directly (or even better, arbitrary depths, but that
449      would mean going through the XImage routines instead of messing
450      with the XImage->data directly.)
451
452      jwz, 18-Mar-99: well, the X servers I have access to these days do
453      support 32-deep images on deep visuals, so I no longer have the
454      ability to test this code -- but it was causing problems on the
455      visuals that I do have, and I think that's because I mistakenly
456      wrote `pfv[i].depth' when I meant to write `pfv[i].bits_per_pixel'.
457      The symptom I was seeing was that the grid was 64x64, but the
458      images were being drawn 32x32 -- so there was a black stripe on
459      every other row.  Wow, this code sucks so much.
460    */
461   if (pdepth == 32)
462     {
463       int i, pfvc = 0;
464       Bool ok = False;
465       XPixmapFormatValues *pfv = XListPixmapFormats (dpy, &pfvc);
466       for (i = 0; i < pfvc; i++)
467         if (pfv[i].bits_per_pixel == pdepth)
468           ok = True;
469       if (!ok)
470         pdepth = 16;
471     }
472
473   cmap = xgwa.colormap;
474   ncolors = get_integer_resource ("colors", "Integer");
475
476   if (ncolors <= 0) {
477     if (vdepth > 8)
478       ncolors = 2047;
479     else
480       ncolors = 255;
481   }
482
483   if (mono_p || ncolors < 2) ncolors = 2;
484   if (ncolors <= 2) mono_p = True;
485   colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
486
487   mapped = (vdepth <= 8 &&
488             has_writable_cells(xgwa.screen, xgwa.visual));
489
490   {
491     int i, di;
492     mc = (unsigned char *) malloc(1<<16);
493     for (i = 0; i < (1<<16); i++) {
494       di = (i + (random()&255))>>8;
495       if (di > 255) di = 255;
496       mc[i] = di;
497     }
498   }
499
500   pd = malloc(npix * (pdepth == 1 ? 1 : (pdepth / 8)));
501   if (!pd) {
502     fprintf(stderr, "not enough memory for %d pixels.\n", npix);
503     exit(1);
504   }
505
506   image = 0;
507
508 #ifdef HAVE_XSHM_EXTENSION
509   if (use_shm)
510     {
511       image = create_xshm_image(dpy, xgwa.visual, vdepth,
512                                 ZPixmap, 0, &shm_info, width, height);
513       if (!image)
514         use_shm = False;
515       else
516         {
517           free(pd);
518           pd = image->data;
519         }
520     }
521 #endif /* HAVE_XSHM_EXTENSION */
522
523   if (!image)
524     {
525       image = XCreateImage(dpy, xgwa.visual, vdepth,
526                            ZPixmap, 0, pd,
527                            width, height, 8, 0);
528     }
529
530   while (1) {
531     Bool bump = False;
532
533     int i, j;
534     pixack_frame(pd);
535     for (i = 0; i < array_width; i += width)
536       for (j = 0; j < array_height; j += height)
537 #ifdef HAVE_XSHM_EXTENSION
538         if (use_shm)
539           XShmPutImage(dpy, win, gc, image, 0, 0, i+array_x, j+array_y,
540                        width, height, False);
541         else
542 #endif
543           XPutImage(dpy, win, gc, image, 0, 0, i+array_x, j+array_y,
544                     width, height);
545
546     array_x += array_dx;
547     array_y += array_dy;
548     if (array_x < 0) {
549       array_x = 0;
550       array_dx = -array_dx;
551       bump = True;
552     } else if (array_x > (xgwa.width - array_width)) {
553       array_x = (xgwa.width - array_width);
554       array_dx = -array_dx;
555       bump = True;
556     }
557     if (array_y < 0) {
558       array_y = 0;
559       array_dy = -array_dy;
560       bump = True;
561     } else if (array_y > (xgwa.height - array_height)) {
562       array_y = (xgwa.height - array_height);
563       array_dy = -array_dy;
564       bump = True;
565     }
566
567     if (bump) {
568       if (random() & 1) {
569         double swap = array_dx;
570         array_dx = array_dy;
571         array_dy = swap;
572       }
573     }
574
575     frame++;
576
577     XSync(dpy, False);
578     screenhack_handle_events (dpy);
579     if (delay > 0)
580       usleep(1000 * delay);
581   }
582 }