0a7cee75a1ab7949adbb4a294aa55b44d04b6409
[xscreensaver] / hacks / rd-bomb.c
1 /* xscreensaver, Copyright (c) 1992, 1995, 1997
2  *  Jamie Zawinski <jwz@netscape.com>
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
25 /* costs ~6% speed */
26 #define dither_when_mapped 1
27
28 int verbose;
29 int ncolors = 0;
30 XColor *colors = 0;
31 Display *display;
32 Visual *visual;
33 #if dither_when_mapped
34 unsigned char *mc = 0;
35 #endif
36 Colormap cmap = 0;
37 Window window;
38 int mapped;
39 int pdepth;
40 void random_colors(void);
41
42 /* -----------------------------------------------------------
43    pixel hack, 8-bit pixel grid, first/next frame interface
44
45    pixack_init(int *size_h, int *size_v)
46    pixack_frame(char *pix_buf)
47    */
48
49
50 #define bps 16
51 #define mx ((1<<16)-1)
52
53 /* you can replace integer mults wish shift/adds with these,
54    but it doesn't help on my 586 */
55 #define x5(n) ((n<<2)+n)
56 #define x7(n) ((n<<3)-n)
57
58 /* why strip bit? */
59 #define R (ya_random()&((1<<30)-1))
60
61 int frame = 0, epoch_time;
62 ushort *r1, *r2, *r1b, *r2b;
63 int width, height, npix;
64 int radius;
65 int reaction = 0;
66 int diffusion = 0;
67
68 /* returns number of pixels that the pixack produces.  called once. */
69 void
70 pixack_init(int *size_h, int *size_v) {
71   int sz_base;
72   width = get_integer_resource ("width", "Integer");
73   height = get_integer_resource ("height", "Integer");
74   sz_base = 80 + (R%40);
75   if (width <= 0) width = (R%20) ? sz_base : (28 + R%10);
76   if (height <= 0) height = (R%20) ? sz_base : (28 + R%10);
77   /* don't go there */
78   if (width < 10) width = 10;
79   if (height < 10) height = 10;
80   epoch_time = get_integer_resource ("epoch", "Integer");
81   npix = (width + 2) * (height + 2);
82   r1 = (ushort *) malloc(sizeof(ushort) * npix);
83   r2 = (ushort *) malloc(sizeof(ushort) * npix);
84   r1b = (ushort *) malloc(sizeof(ushort) * npix);
85   r2b = (ushort *) malloc(sizeof(ushort) * npix);
86
87   if (!r1 || !r2 || !r1b || !r2b) {
88     fprintf(stderr, "not enough memory for %d pixels.\n", npix);
89     exit(1);
90   }
91
92   *size_h = width;
93   *size_v = height;
94 }
95
96 #define test_pattern_hyper 0
97
98
99 /* returns the pixels.  called many times. */
100 void
101 pixack_frame(char *pix_buf) {
102   int i, j;
103   int w2 = width + 2;
104   ushort *t;
105 #if test_pattern_hyper
106   if (frame&0x100)
107     sleep(1);
108 #endif
109   if (verbose) {
110     double tm = 0;
111     struct timeval tp;
112     if (!(frame%100)) {
113       double tm2;
114 #ifdef GETTIMEOFDAY_TWO_ARGS
115       struct timezone tzp;
116       gettimeofday(&tp, &tzp);
117 #else
118       gettimeofday(&tp);
119 #endif
120       tm2 = tp.tv_sec + tp.tv_usec * 1e-6;
121       if (frame > 0)
122         printf("fps = %2.4g\n", 100.0 / (tm2 - tm));
123       tm = tm2;
124     }
125   }
126   if (!(frame%epoch_time)) {
127     int s;
128     if (0 != frame) {
129       int t = epoch_time / 500;
130       if (t > 15)
131         t = 15;
132       sleep(t);
133     }
134           
135     for (i = 0; i < npix; i++) {
136       /* equilibrium */
137       r1[i] = 65500;
138       r2[i] = 11;
139     }
140
141     random_colors();
142
143     XSetWindowBackground(display, window, colors[255 % ncolors].pixel);
144     XClearWindow(display, window);
145
146     s = w2 * (height/2) + width/2;
147     radius = get_integer_resource ("radius", "Integer");
148     {
149       int maxr = width/2-2;
150       int maxr2 = height/2-2;
151       if (maxr2 < maxr) maxr = maxr2;
152
153       if (radius < 0)
154         radius = 1 + ((R%10) ? (R%5) : (R % maxr));
155       if (radius > maxr) radius = maxr;
156     }
157     for (i = -radius; i < (radius+1); i++)
158       for (j = -radius; j < (radius+1); j++)
159         r2[s + i + j*w2] = mx - (R&63);
160     reaction = get_integer_resource ("reaction", "Integer");
161     if (reaction < 0 || reaction > 2) reaction = R&1;
162     diffusion = get_integer_resource ("diffusion", "Integer");
163     if (diffusion < 0 || diffusion > 2)
164       diffusion = (R%5) ? ((R%3)?0:1) : 2;
165     if (2 == reaction && 2 == diffusion)
166       reaction = diffusion = 0;
167       
168     if (verbose)
169       printf("reaction = %d\ndiffusion = %d\nradius = %d\n",
170              reaction, diffusion, radius);
171   }
172   for (i = 0; i <= width+1; i++) {
173     r1[i] = r1[i + w2 * height];
174     r2[i] = r2[i + w2 * height];
175     r1[i + w2 * (height + 1)] = r1[i + w2];
176     r2[i + w2 * (height + 1)] = r2[i + w2];
177   }
178   for (i = 0; i <= height+1; i++) {
179     r1[w2 * i] = r1[width + w2 * i];
180     r2[w2 * i] = r2[width + w2 * i];
181     r1[w2 * i + width + 1] = r1[w2 * i + 1];
182     r2[w2 * i + width + 1] = r2[w2 * i + 1];
183   }
184   for (i = 0; i < height; i++) {
185     int ii = i + 1;
186     char *q = pix_buf + width * i;
187     short *qq = ((short *) pix_buf) + width * i;
188     long  *qqq = ((long *) pix_buf) + width * i;
189     ushort *i1 = r1 + 1 + w2 * ii;
190     ushort *i2 = r2 + 1 + w2 * ii;
191     ushort *o1 = r1b + 1 + w2 * ii;
192     ushort *o2 = r2b + 1 + w2 * ii;
193     for (j = 0; j < width; j++) {
194 #if test_pattern_hyper
195       int r1 = (i * j + (frame&127)*frame)&65535;
196 #else
197       int uvv, r1 = 0, r2 = 0;
198       switch (diffusion) {
199       case 0:
200         r1 = i1[j] + i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
201         r1 = r1 / 5;
202         r2 = (i2[j]<<3) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
203         r2 = r2 / 12;
204         break;
205       case 1:
206         r1 = i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
207         r1 = r1 >> 2;
208         r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
209         r2 = r2 >> 3;
210         break;
211       case 2:
212         r1 = (i1[j]<<1) + (i1[j+1]<<1) + (i1[j-1]<<1) + i1[j+w2] + i1[j-w2];
213         r1 = r1 >> 3;
214         r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
215         r2 = r2 >> 3;
216         break;
217       }
218
219       /* John E. Pearson "Complex Patterns in a Simple System"
220          Science, July 1993 */
221
222       uvv = (((r1 * r2) >> bps) * r2) >> bps;
223       switch (reaction) {  /* costs 4% */
224       case 0:
225         r1 += 4 * (((28 * (mx-r1)) >> 10) - uvv);
226         r2 += 4 * (uvv - ((80 * r2) >> 10));
227         break;
228       case 1:
229         r1 += 3 * (((27 * (mx-r1)) >> 10) - uvv);
230         r2 += 3 * (uvv - ((80 * r2) >> 10));
231         break;
232       case 2:
233         r1 += 2 * (((28 * (mx-r1)) >> 10) - uvv);
234         r2 += 3 * (uvv - ((80 * r2) >> 10));
235         break;
236       }
237       if (r1 > mx) r1 = mx;
238       if (r2 > mx) r2 = mx;
239       if (r1 < 0) r1 = 0;
240       if (r2 < 0) r2 = 0;
241       o1[j] = r1;
242       o2[j] = r2;
243 #endif
244
245       /* this is terrible.  here i want to assume ncolors = 256.
246          should lose double indirection */
247
248       if (mapped)
249 #if dither_when_mapped
250         q[j] = colors[mc[r1]  % ncolors].pixel;
251 #else
252       q[j] = colors[(r1>>8) % ncolors].pixel;
253 #endif
254       else if (pdepth == 8)
255         q[j] = colors[(r1>>8) % ncolors].pixel;
256       else if (pdepth == 16)
257 #if dither_when_mapped
258         qq[j] = colors[mc[r1] % ncolors].pixel;
259 #else
260         qq[j] = colors[(r1>>8) % ncolors].pixel;
261 #endif
262       else if (pdepth == 32)
263 #if dither_when_mapped
264         qqq[j] = colors[mc[r1] % ncolors].pixel;
265 #else
266         qqq[j] = colors[(r1>>8) % ncolors].pixel;
267 #endif
268       else
269         abort();
270     }
271   }
272   t = r1; r1 = r1b; r1b = t;
273   t = r2; r2 = r2b; r2b = t;  
274 }
275
276
277 /* ------------- xscreensaver rendering -------------- */
278
279
280
281 char *progclass = "RD";
282
283
284 char *defaults [] = {
285   "RD.background:       black",         /* to placate SGI */
286   "RD.foreground:       white",
287   "*width:      0",                     /* tried to use -1 but it complained */
288   "*height:     0",
289   "*epoch:      40000",
290   "*reaction:   -1",
291   "*diffusion:  -1",
292   "*verbose:    off",
293   "*radius:     -1",
294   "*speed:      0.0",
295   "*size:       0.66",
296   "*delay:      1",
297   "*colors:     -1",
298   0
299 };
300
301 XrmOptionDescRec options [] = {
302   { "-width",           ".width",       XrmoptionSepArg, 0 },
303   { "-height",          ".height",      XrmoptionSepArg, 0 },
304   { "-epoch",           ".epoch",       XrmoptionSepArg, 0 },
305   { "-reaction",        ".reaction",    XrmoptionSepArg, 0 },
306   { "-diffusion",       ".diffusion",   XrmoptionSepArg, 0 },
307   { "-verbose",         ".verbose",     XrmoptionNoArg, "True" },
308   { "-radius",          ".radius",      XrmoptionSepArg, 0 },
309   { "-speed",           ".speed",       XrmoptionSepArg, 0 },
310   { "-size",            ".size",        XrmoptionSepArg, 0 },
311   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
312   { "-ncolors",         ".colors",      XrmoptionSepArg, 0 },
313   { 0, 0, 0, 0 }
314 };
315
316
317 /* why doesn't this work???  and more importantly, do i really still have
318    to do this in X? */
319    
320 #ifdef HAVE_XSHM_EXTENSION
321 #include <sys/ipc.h>
322 #include <sys/shm.h>
323 #include <X11/extensions/XShm.h>
324 #endif
325
326
327 void
328 random_colors() {
329   memset(colors, 0, ncolors*sizeof(*colors));
330   make_smooth_colormap (display, visual, cmap, colors, &ncolors,
331                         True, 0, True);
332   if (ncolors <= 2) {
333     mono_p = True;
334     ncolors = 2;
335     colors[0].flags = DoRed|DoGreen|DoBlue;
336     colors[0].red = colors[0].green = colors[0].blue = 0;
337     XAllocColor(display, cmap, &colors[0]);
338     colors[1].flags = DoRed|DoGreen|DoBlue;
339     colors[1].red = colors[1].green = colors[1].blue = 0xFFFF;
340     XAllocColor(display, cmap, &colors[1]);
341   }
342
343   /* Scale it up so that there are exactly 255 colors -- that keeps the
344      animation speed consistent, even when there aren't many allocatable
345      colors, and prevents the -mono mode from looking like static. */
346   if (ncolors != 255) {
347     int i, n = 255;
348     double scale = (double) ncolors / (double) (n+1);
349     XColor *c2 = (XColor *) malloc(sizeof(*c2) * (n+1));
350     for (i = 0; i < n; i++)
351       c2[i] = colors[(int) (i * scale)];
352     free(colors);
353     colors = c2;
354     ncolors = n;
355   }
356
357 }
358
359 /* should factor into RD-specfic and compute-every-pixel general */
360 void
361 screenhack (Display *dpy, Window win)
362 {
363   GC gc;
364   XGCValues gcv;
365   XWindowAttributes xgwa;
366   XImage *image;
367   int array_width, array_height;
368   double array_x, array_y;
369   double array_dx, array_dy;
370   int w2;
371   char *p;
372   int vdepth;
373   int npix;
374   int *m = 0;
375 #ifdef HAVE_XSHM_EXTENSION
376   int use_shm = 0;
377   XShmSegmentInfo shm_info;
378 #endif
379
380   double delay = get_float_resource ("delay", "Float");
381
382   display = dpy;
383   window = win;
384
385
386   XGetWindowAttributes (dpy, win, &xgwa);
387   visual = xgwa.visual;
388   pixack_init(&width, &height);
389   {
390     double s = get_float_resource ("size", "Float");
391     double p = get_float_resource ("speed", "Float");
392     if (s < 0.0 || s > 1.0)
393       s = 1.0;
394     s = sqrt(s);
395     array_width = xgwa.width * s;
396     array_height = xgwa.height * s;
397     if (s < 0.99) {
398       array_width = (array_width / width) * width;
399       array_height = (array_height / height) * height;
400     }
401     if (array_width < width) array_width = width;
402     if (array_height < height) array_height = height;
403     array_x = (xgwa.width - array_width)/2;
404     array_y = (xgwa.height - array_height)/2;
405     array_dx = p;
406     array_dy = .31415926 * p;
407   }
408   verbose = get_boolean_resource ("verbose", "Boolean");
409   npix = (width + 2) * (height + 2);
410   w2 = width + 2;
411   gcv.function = GXcopy;
412   gc = XCreateGC(dpy, win, GCFunction, &gcv);
413   vdepth = visual_depth(DefaultScreenOfDisplay(dpy), xgwa.visual);
414
415   /* This code only deals with pixmap depths of 1, 8, 16, and 32.
416      Therefore, we assume that those depths will be supported by the
417      coresponding visual depths (that depth-24 displays accept depth-32
418      pixmaps, and that depth-12 displays accept depth-16 pixmaps.) */
419   pdepth = (vdepth == 1 ? 1 :
420             vdepth <= 8 ? 8 :
421             vdepth <= 16 ? 16 :
422             32);
423
424   cmap = xgwa.colormap;
425   ncolors = get_integer_resource ("colors", "Integer");
426
427   if (ncolors <= 0) {
428     if (vdepth > 8)
429       ncolors = 2047;
430     else
431       ncolors = 255;
432   }
433
434   if (mono_p || ncolors < 2) ncolors = 2;
435   if (ncolors <= 2) mono_p = True;
436   colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
437
438   mapped = (vdepth <= 8 &&
439             has_writable_cells(xgwa.screen, xgwa.visual));
440
441   {
442     int i, di;
443     mc = (unsigned char *) malloc(1<<16);
444     for (i = 0; i < (1<<16); i++) {
445       di = (i + (ya_random()&255))>>8;
446       if (di > 255) di = 255;
447       mc[i] = di;
448     }
449   }
450
451   p = malloc(npix * (pdepth == 1 ? 1 : (pdepth / 8)));
452   if (!p) {
453     fprintf(stderr, "not enough memory for %d pixels.\n", npix);
454     exit(1);
455   }
456
457 #ifdef HAVE_XSHM_EXTENSION
458   if (use_shm) {
459     printf("p=%X\n", p);
460     free(p);
461     image = XShmCreateImage(dpy, xgwa.visual, vdepth,
462                             ZPixmap, 0, &shm_info, width, height);
463     shm_info.shmid = shmget(IPC_PRIVATE,
464                             image->bytes_per_line * image->height,
465                             IPC_CREAT | 0777);
466     if (shm_info.shmid == -1)
467       printf ("shmget failed!");
468     shm_info.readOnly = False;
469     p = shmat(shm_info.shmid, 0, 0);
470     printf("p=%X %d\n", p, image->bytes_per_line);
471     XShmAttach(dpy, &shm_info);
472     XSync(dpy, False);
473   } else
474 #endif
475   image = XCreateImage(dpy, xgwa.visual, vdepth,
476                        ZPixmap, 0, p,
477                        width, height, 8, 0);
478
479   while (1) {
480     int i, j;
481     pixack_frame(p);
482     for (i = 0; i < array_width; i += width)
483       for (j = 0; j < array_height; j += height)
484 #ifdef HAVE_XSHM_EXTENSION
485         if (use_shm)
486           XShmPutImage(dpy, win, gc, image, 0, 0, i, j,
487                        width, height, False);
488         else
489 #endif
490           XPutImage(dpy, win, gc, image, 0, 0, i+array_x, j+array_y, width, height);
491
492     array_x += array_dx;
493     array_y += array_dy;
494     if (array_x < 0) {
495       array_x = 0;
496       array_dx = -array_dx;
497     } else if (array_x > (xgwa.width - array_width)) {
498       array_x = (xgwa.width - array_width);
499       array_dx = -array_dx;
500     }
501     if (array_y < 0) {
502       array_y = 0;
503       array_dy = -array_dy;
504     } else if (array_y > (xgwa.height - array_height)) {
505       array_y = (xgwa.height - array_height);
506       array_dy = -array_dy;
507     }
508     frame++;
509
510     XSync(dpy, False);
511     if (delay > 0)
512       usleep(1000 * delay);
513   }
514 }