1 /* xscreensaver, Copyright (c) 1992, 1995, 1997, 1998, 1999, 2003, 2006
2 * Jamie Zawinski <jwz@jwz.org>
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
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
17 * And remember: X Windows is to graphics hacking as roman numerals are to
18 * the square root of pi.
23 #include "screenhack.h"
25 #ifdef HAVE_XSHM_EXTENSION
27 #endif /* HAVE_XSHM_EXTENSION */
30 #define dither_when_mapped 1
40 #if dither_when_mapped
47 int frame, epoch_time;
48 unsigned short *r1, *r2, *r1b, *r2b;
49 int width, height, npix;
55 int array_width, array_height;
57 #ifdef HAVE_XSHM_EXTENSION
59 XShmSegmentInfo shm_info;
64 double array_x, array_y;
65 double array_dx, array_dy;
66 XWindowAttributes xgwa;
70 static void random_colors(struct state *st);
72 /* -----------------------------------------------------------
73 pixel hack, 8-bit pixel grid, first/next frame interface
75 pixack_init(int *size_h, int *size_v)
76 pixack_frame(char *pix_buf)
81 #define mx ((1<<16)-1)
83 /* you can replace integer mults wish shift/adds with these,
84 but it doesn't help on my 586 */
85 #define x5(n) ((n<<2)+n)
86 #define x7(n) ((n<<3)-n)
89 #define R (random()&((1<<30)-1))
90 #define BELLRAND(x) (((random()%(x)) + (random()%(x)) + (random()%(x)))/3)
92 /* returns number of pixels that the pixack produces. called once. */
94 pixack_init(struct state *st, int *size_h, int *size_v)
96 st->width = get_integer_resource (st->dpy, "width", "Integer");
97 st->height = get_integer_resource (st->dpy, "height", "Integer");
99 if (st->width <= 0 && st->height <= 0 && (R & 1))
100 st->width = st->height = 48 + BELLRAND(256);
102 if (st->width <= 0) st->width = 48 + BELLRAND(256);
103 if (st->height <= 0) st->height = 48 + BELLRAND(256);
105 /* jwz: when (and only when) XSHM is in use on an SGI 8-bit visual,
106 we get shear unless st->width is a multiple of 4. I don't understand
107 why. This is undoubtedly the wrong fix... */
108 if (visual_depth (st->xgwa.screen, st->xgwa.visual) == 8)
112 if (st->width < 10) st->width = 10;
113 if (st->height < 10) st->height = 10;
114 st->epoch_time = get_integer_resource (st->dpy, "epoch", "Integer");
115 st->npix = (st->width + 2) * (st->height + 2);
116 st->r1 = (unsigned short *) malloc(sizeof(unsigned short) * st->npix);
117 st->r2 = (unsigned short *) malloc(sizeof(unsigned short) * st->npix);
118 st->r1b = (unsigned short *) malloc(sizeof(unsigned short) * st->npix);
119 st->r2b = (unsigned short *) malloc(sizeof(unsigned short) * st->npix);
121 if (!st->r1 || !st->r2 || !st->r1b || !st->r2b) {
122 fprintf(stderr, "not enough memory for %d pixels.\n", st->npix);
127 *size_v = st->height;
130 #define test_pattern_hyper 0
133 /* returns the pixels. called many times. */
135 pixack_frame(struct state *st, char *pix_buf)
138 int w2 = st->width + 2;
140 #if test_pattern_hyper
147 if (!(st->frame%100)) {
149 #ifdef GETTIMEOFDAY_TWO_ARGS
151 gettimeofday(&tp, &tzp);
155 tm2 = tp.tv_sec + tp.tv_usec * 1e-6;
157 printf("fps = %2.4g\n", 100.0 / (tm2 - tm));
161 if (!(st->frame%st->epoch_time)) {
163 if (0 != st->frame) {
164 int tt = st->epoch_time / 500;
170 for (i = 0; i < st->npix; i++) {
178 XSetWindowBackground(st->dpy, st->window, st->colors[255 % st->ncolors].pixel);
179 XClearWindow(st->dpy, st->window);
181 s = w2 * (st->height/2) + st->width/2;
182 st->radius = get_integer_resource (st->dpy, "radius", "Integer");
184 int maxr = st->width/2-2;
185 int maxr2 = st->height/2-2;
186 if (maxr2 < maxr) maxr = maxr2;
189 st->radius = 1 + ((R%10) ? (R%5) : (R % maxr));
190 if (st->radius > maxr) st->radius = maxr;
192 for (i = -st->radius; i < (st->radius+1); i++)
193 for (j = -st->radius; j < (st->radius+1); j++)
194 st->r2[s + i + j*w2] = mx - (R&63);
195 st->reaction = get_integer_resource (st->dpy, "reaction", "Integer");
196 if (st->reaction < 0 || st->reaction > 2) st->reaction = R&1;
197 st->diffusion = get_integer_resource (st->dpy, "diffusion", "Integer");
198 if (st->diffusion < 0 || st->diffusion > 2)
199 st->diffusion = (R%5) ? ((R%3)?0:1) : 2;
200 if (2 == st->reaction && 2 == st->diffusion)
201 st->reaction = st->diffusion = 0;
204 printf("reaction = %d\ndiffusion = %d\nradius = %d\n",
205 st->reaction, st->diffusion, st->radius);
207 for (i = 0; i <= st->width+1; i++) {
208 st->r1[i] = st->r1[i + w2 * st->height];
209 st->r2[i] = st->r2[i + w2 * st->height];
210 st->r1[i + w2 * (st->height + 1)] = st->r1[i + w2];
211 st->r2[i + w2 * (st->height + 1)] = st->r2[i + w2];
213 for (i = 0; i <= st->height+1; i++) {
214 st->r1[w2 * i] = st->r1[st->width + w2 * i];
215 st->r2[w2 * i] = st->r2[st->width + w2 * i];
216 st->r1[w2 * i + st->width + 1] = st->r1[w2 * i + 1];
217 st->r2[w2 * i + st->width + 1] = st->r2[w2 * i + 1];
219 for (i = 0; i < st->height; i++) {
221 char *q = pix_buf + st->width * i;
222 short *qq = ((short *) pix_buf) + st->width * i;
223 /* long *qqq = ((long *) pix_buf) + st->width * i; -- crashes on Alpha */
224 int *qqq = ((int *) pix_buf) + st->width * i;
225 unsigned short *i1 = st->r1 + 1 + w2 * ii;
226 unsigned short *i2 = st->r2 + 1 + w2 * ii;
227 unsigned short *o1 = st->r1b + 1 + w2 * ii;
228 unsigned short *o2 = st->r2b + 1 + w2 * ii;
229 for (j = 0; j < st->width; j++) {
230 #if test_pattern_hyper
231 int r1 = (i * j + (st->frame&127)*frame)&65535;
233 int uvv, r1 = 0, r2 = 0;
234 switch (st->diffusion) {
236 r1 = i1[j] + i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
238 r2 = (i2[j]<<3) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
242 r1 = i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
244 r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
248 r1 = (i1[j]<<1) + (i1[j+1]<<1) + (i1[j-1]<<1) + i1[j+w2] + i1[j-w2];
250 r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
255 /* John E. Pearson "Complex Patterns in a Simple System"
256 Science, July 1993 */
258 uvv = (((r1 * r2) >> bps) * r2) >> bps;
259 switch (st->reaction) { /* costs 4% */
261 r1 += 4 * (((28 * (mx-r1)) >> 10) - uvv);
262 r2 += 4 * (uvv - ((80 * r2) >> 10));
265 r1 += 3 * (((27 * (mx-r1)) >> 10) - uvv);
266 r2 += 3 * (uvv - ((80 * r2) >> 10));
269 r1 += 2 * (((28 * (mx-r1)) >> 10) - uvv);
270 r2 += 3 * (uvv - ((80 * r2) >> 10));
273 if (r1 > mx) r1 = mx;
274 if (r2 > mx) r2 = mx;
281 /* this is terrible. here i want to assume ncolors = 256.
282 should lose double indirection */
285 #if dither_when_mapped
286 q[j] = st->colors[st->mc[r1] % st->ncolors].pixel;
288 q[j] = st->colors[(r1>>8) % st->ncolors].pixel;
290 else if (st->pdepth == 8)
291 q[j] = st->colors[(r1>>8) % st->ncolors].pixel;
292 else if (st->pdepth == 16)
293 #if dither_when_mapped
294 qq[j] = st->colors[st->mc[r1] % st->ncolors].pixel;
296 qq[j] = st->colors[(r1>>8) % st->ncolors].pixel;
298 else if (st->pdepth == 32)
299 #if dither_when_mapped
300 qqq[j] = st->colors[st->mc[r1] % st->ncolors].pixel;
302 qqq[j] = st->colors[(r1>>8) % st->ncolors].pixel;
308 t = st->r1; st->r1 = st->r1b; st->r1b = t;
309 t = st->r2; st->r2 = st->r2b; st->r2b = t;
313 /* ------------- xscreensaver rendering -------------- */
315 static const char *rd_defaults [] = {
316 ".background: black",
317 ".foreground: white",
318 "*width: 0", /* tried to use -1 but it complained */
329 #ifdef HAVE_XSHM_EXTENSION
337 static XrmOptionDescRec rd_options [] = {
338 { "-width", ".width", XrmoptionSepArg, 0 },
339 { "-height", ".height", XrmoptionSepArg, 0 },
340 { "-epoch", ".epoch", XrmoptionSepArg, 0 },
341 { "-reaction", ".reaction", XrmoptionSepArg, 0 },
342 { "-diffusion", ".diffusion", XrmoptionSepArg, 0 },
343 { "-verbose", ".verbose", XrmoptionNoArg, "True" },
344 { "-radius", ".radius", XrmoptionSepArg, 0 },
345 { "-speed", ".speed", XrmoptionSepArg, 0 },
346 { "-size", ".size", XrmoptionSepArg, 0 },
347 { "-delay", ".delay", XrmoptionSepArg, 0 },
348 { "-ncolors", ".colors", XrmoptionSepArg, 0 },
349 { "-shm", ".useSHM", XrmoptionNoArg, "True" },
350 { "-no-shm", ".useSHM", XrmoptionNoArg, "False" },
356 random_colors(struct state *st)
358 memset(st->colors, 0, st->ncolors*sizeof(*st->colors));
359 make_smooth_colormap (st->dpy, st->visual, st->cmap, st->colors, &st->ncolors,
361 if (st->ncolors <= 2) {
364 st->colors[0].flags = DoRed|DoGreen|DoBlue;
365 st->colors[0].red = st->colors[0].green = st->colors[0].blue = 0;
366 XAllocColor(st->dpy, st->cmap, &st->colors[0]);
367 st->colors[1].flags = DoRed|DoGreen|DoBlue;
368 st->colors[1].red = st->colors[1].green = st->colors[1].blue = 0xFFFF;
369 XAllocColor(st->dpy, st->cmap, &st->colors[1]);
372 /* Scale it up so that there are exactly 255 colors -- that keeps the
373 animation speed consistent, even when there aren't many allocatable
374 colors, and prevents the -mono mode from looking like static. */
375 if (st->ncolors != 255) {
377 double scale = (double) st->ncolors / (double) (n+1);
378 XColor *c2 = (XColor *) malloc(sizeof(*c2) * (n+1));
379 for (i = 0; i < n; i++)
380 c2[i] = st->colors[(int) (i * scale)];
389 /* should factor into RD-specfic and compute-every-pixel general */
391 rd_init (Display *dpy, Window win)
393 struct state *st = (struct state *) calloc (1, sizeof(*st));
401 st->delay = get_integer_resource (st->dpy, "delay", "Float");
403 #ifdef HAVE_XSHM_EXTENSION
404 st->use_shm = get_boolean_resource(st->dpy, "useSHM", "Boolean");
407 XGetWindowAttributes (st->dpy, win, &st->xgwa);
408 st->visual = st->xgwa.visual;
409 pixack_init(st, &st->width, &st->height);
411 double s = get_float_resource (st->dpy, "size", "Float");
412 double p = get_float_resource (st->dpy, "speed", "Float");
413 if (s < 0.0 || s > 1.0)
416 st->array_width = st->xgwa.width * s;
417 st->array_height = st->xgwa.height * s;
419 st->array_width = (st->array_width / st->width) * st->width;
420 st->array_height = (st->array_height / st->height) * st->height;
422 if (st->array_width < st->width) st->array_width = st->width;
423 if (st->array_height < st->height) st->array_height = st->height;
424 st->array_x = (st->xgwa.width - st->array_width)/2;
425 st->array_y = (st->xgwa.height - st->array_height)/2;
427 st->array_dy = .31415926 * p;
429 /* start in a random direction */
430 if (random() & 1) st->array_dx = -st->array_dx;
431 if (random() & 1) st->array_dy = -st->array_dy;
434 st->verbose = get_boolean_resource (st->dpy, "verbose", "Boolean");
435 st->npix = (st->width + 2) * (st->height + 2);
437 /* gcv.function = GXcopy;*/
438 st->gc = XCreateGC(st->dpy, win, 0 /*GCFunction*/, &gcv);
439 vdepth = visual_depth(DefaultScreenOfDisplay(st->dpy), st->xgwa.visual);
441 /* This code only deals with pixmap depths of 1, 8, 16, and 32.
442 Therefore, we assume that those depths will be supported by the
443 coresponding visual depths (that depth-24 dpys accept depth-32
444 pixmaps, and that depth-12 dpys accept depth-16 pixmaps.) */
445 st->pdepth = (vdepth == 1 ? 1 :
450 /* Ok, this like, sucks and stuff. There are some XFree86 systems
451 that have depth-24 visuals, that do not accept depth-32 XImages!
452 Which if you ask me is just absurd, since all it would take is
453 for the server to truncate the bits in that case. So, this crap
454 here detects the specific case of: we have chosen depth 32;
455 and the server does not support depth 32. In that case, we
456 try and use depth 16 instead.
458 The real fix would be to rewrite this program to deal with
459 depth 24 directly (or even better, arbitrary depths, but that
460 would mean going through the XImage routines instead of messing
461 with the XImage->data directly.)
463 jwz, 18-Mar-99: well, the X servers I have access to these days do
464 support 32-deep images on deep visuals, so I no longer have the
465 ability to test this code -- but it was causing problems on the
466 visuals that I do have, and I think that's because I mistakenly
467 wrote `pfv[i].depth' when I meant to write `pfv[i].bits_per_pixel'.
468 The symptom I was seeing was that the grid was 64x64, but the
469 images were being drawn 32x32 -- so there was a black stripe on
470 every other row. Wow, this code sucks so much.
472 if (st->pdepth == 32)
476 XPixmapFormatValues *pfv = XListPixmapFormats (st->dpy, &pfvc);
477 for (i = 0; i < pfvc; i++)
478 if (pfv[i].bits_per_pixel == st->pdepth)
484 st->cmap = st->xgwa.colormap;
485 st->ncolors = get_integer_resource (st->dpy, "colors", "Integer");
487 if (st->ncolors <= 0) {
494 if (mono_p || st->ncolors < 2) st->ncolors = 2;
495 if (st->ncolors <= 2) mono_p = True;
496 st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1));
498 st->mapped = (vdepth <= 8 &&
499 has_writable_cells(st->xgwa.screen, st->xgwa.visual));
503 st->mc = (unsigned char *) malloc(1<<16);
504 for (i = 0; i < (1<<16); i++) {
505 di = (i + (random()&255))>>8;
506 if (di > 255) di = 255;
511 st->pd = malloc(st->npix * (st->pdepth == 1 ? 1 : (st->pdepth / 8)));
513 fprintf(stderr, "not enough memory for %d pixels.\n", st->npix);
519 #ifdef HAVE_XSHM_EXTENSION
522 st->image = create_xshm_image(st->dpy, st->xgwa.visual, vdepth,
523 ZPixmap, 0, &st->shm_info, st->width, st->height);
529 st->pd = st->image->data;
532 #endif /* HAVE_XSHM_EXTENSION */
536 st->image = XCreateImage(st->dpy, st->xgwa.visual, vdepth,
538 st->width, st->height, 8, 0);
545 rd_draw (Display *dpy, Window win, void *closure)
547 struct state *st = (struct state *) closure;
551 pixack_frame(st, st->pd);
552 for (i = 0; i < st->array_width; i += st->width)
553 for (j = 0; j < st->array_height; j += st->height)
554 #ifdef HAVE_XSHM_EXTENSION
556 XShmPutImage(st->dpy, st->window, st->gc, st->image, 0, 0, i+st->array_x, j+st->array_y,
557 st->width, st->height, False);
560 XPutImage(st->dpy, win, st->gc, st->image, 0, 0, i+st->array_x, j+st->array_y,
561 st->width, st->height);
563 st->array_x += st->array_dx;
564 st->array_y += st->array_dy;
565 if (st->array_x < 0) {
567 st->array_dx = -st->array_dx;
569 } else if (st->array_x > (st->xgwa.width - st->array_width)) {
570 st->array_x = (st->xgwa.width - st->array_width);
571 st->array_dx = -st->array_dx;
574 if (st->array_y < 0) {
576 st->array_dy = -st->array_dy;
578 } else if (st->array_y > (st->xgwa.height - st->array_height)) {
579 st->array_y = (st->xgwa.height - st->array_height);
580 st->array_dy = -st->array_dy;
586 double swap = st->array_dx;
587 st->array_dx = st->array_dy;
598 rd_reshape (Display *dpy, Window window, void *closure,
599 unsigned int w, unsigned int h)
604 rd_event (Display *dpy, Window window, void *closure, XEvent *event)
610 rd_free (Display *dpy, Window window, void *closure)
614 XSCREENSAVER_MODULE_2 ("RDbomb", rdbomb, rd)