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))
91 /* returns number of pixels that the pixack produces. called once. */
93 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");
98 sz_base = 80 + (R%40);
99 if (st->width <= 0) st->width = (R%20) ? sz_base : (28 + R%10);
100 if (st->height <= 0) st->height = (R%20) ? sz_base : (28 + R%10);
102 /* jwz: when (and only when) XSHM is in use on an SGI 8-bit visual,
103 we get shear unless st->width is a multiple of 4. I don't understand
104 why. This is undoubtedly the wrong fix... */
108 if (st->width < 10) st->width = 10;
109 if (st->height < 10) st->height = 10;
110 st->epoch_time = get_integer_resource (st->dpy, "epoch", "Integer");
111 st->npix = (st->width + 2) * (st->height + 2);
112 st->r1 = (unsigned short *) malloc(sizeof(unsigned short) * st->npix);
113 st->r2 = (unsigned short *) malloc(sizeof(unsigned short) * st->npix);
114 st->r1b = (unsigned short *) malloc(sizeof(unsigned short) * st->npix);
115 st->r2b = (unsigned short *) malloc(sizeof(unsigned short) * st->npix);
117 if (!st->r1 || !st->r2 || !st->r1b || !st->r2b) {
118 fprintf(stderr, "not enough memory for %d pixels.\n", st->npix);
123 *size_v = st->height;
126 #define test_pattern_hyper 0
129 /* returns the pixels. called many times. */
131 pixack_frame(struct state *st, char *pix_buf)
134 int w2 = st->width + 2;
136 #if test_pattern_hyper
143 if (!(st->frame%100)) {
145 #ifdef GETTIMEOFDAY_TWO_ARGS
147 gettimeofday(&tp, &tzp);
151 tm2 = tp.tv_sec + tp.tv_usec * 1e-6;
153 printf("fps = %2.4g\n", 100.0 / (tm2 - tm));
157 if (!(st->frame%st->epoch_time)) {
159 if (0 != st->frame) {
160 int tt = st->epoch_time / 500;
166 for (i = 0; i < st->npix; i++) {
174 XSetWindowBackground(st->dpy, st->window, st->colors[255 % st->ncolors].pixel);
175 XClearWindow(st->dpy, st->window);
177 s = w2 * (st->height/2) + st->width/2;
178 st->radius = get_integer_resource (st->dpy, "radius", "Integer");
180 int maxr = st->width/2-2;
181 int maxr2 = st->height/2-2;
182 if (maxr2 < maxr) maxr = maxr2;
185 st->radius = 1 + ((R%10) ? (R%5) : (R % maxr));
186 if (st->radius > maxr) st->radius = maxr;
188 for (i = -st->radius; i < (st->radius+1); i++)
189 for (j = -st->radius; j < (st->radius+1); j++)
190 st->r2[s + i + j*w2] = mx - (R&63);
191 st->reaction = get_integer_resource (st->dpy, "reaction", "Integer");
192 if (st->reaction < 0 || st->reaction > 2) st->reaction = R&1;
193 st->diffusion = get_integer_resource (st->dpy, "diffusion", "Integer");
194 if (st->diffusion < 0 || st->diffusion > 2)
195 st->diffusion = (R%5) ? ((R%3)?0:1) : 2;
196 if (2 == st->reaction && 2 == st->diffusion)
197 st->reaction = st->diffusion = 0;
200 printf("reaction = %d\ndiffusion = %d\nradius = %d\n",
201 st->reaction, st->diffusion, st->radius);
203 for (i = 0; i <= st->width+1; i++) {
204 st->r1[i] = st->r1[i + w2 * st->height];
205 st->r2[i] = st->r2[i + w2 * st->height];
206 st->r1[i + w2 * (st->height + 1)] = st->r1[i + w2];
207 st->r2[i + w2 * (st->height + 1)] = st->r2[i + w2];
209 for (i = 0; i <= st->height+1; i++) {
210 st->r1[w2 * i] = st->r1[st->width + w2 * i];
211 st->r2[w2 * i] = st->r2[st->width + w2 * i];
212 st->r1[w2 * i + st->width + 1] = st->r1[w2 * i + 1];
213 st->r2[w2 * i + st->width + 1] = st->r2[w2 * i + 1];
215 for (i = 0; i < st->height; i++) {
217 char *q = pix_buf + st->width * i;
218 short *qq = ((short *) pix_buf) + st->width * i;
219 /* long *qqq = ((long *) pix_buf) + st->width * i; -- crashes on Alpha */
220 int *qqq = ((int *) pix_buf) + st->width * i;
221 unsigned short *i1 = st->r1 + 1 + w2 * ii;
222 unsigned short *i2 = st->r2 + 1 + w2 * ii;
223 unsigned short *o1 = st->r1b + 1 + w2 * ii;
224 unsigned short *o2 = st->r2b + 1 + w2 * ii;
225 for (j = 0; j < st->width; j++) {
226 #if test_pattern_hyper
227 int r1 = (i * j + (st->frame&127)*frame)&65535;
229 int uvv, r1 = 0, r2 = 0;
230 switch (st->diffusion) {
232 r1 = i1[j] + i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
234 r2 = (i2[j]<<3) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
238 r1 = i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
240 r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
244 r1 = (i1[j]<<1) + (i1[j+1]<<1) + (i1[j-1]<<1) + i1[j+w2] + i1[j-w2];
246 r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
251 /* John E. Pearson "Complex Patterns in a Simple System"
252 Science, July 1993 */
254 uvv = (((r1 * r2) >> bps) * r2) >> bps;
255 switch (st->reaction) { /* costs 4% */
257 r1 += 4 * (((28 * (mx-r1)) >> 10) - uvv);
258 r2 += 4 * (uvv - ((80 * r2) >> 10));
261 r1 += 3 * (((27 * (mx-r1)) >> 10) - uvv);
262 r2 += 3 * (uvv - ((80 * r2) >> 10));
265 r1 += 2 * (((28 * (mx-r1)) >> 10) - uvv);
266 r2 += 3 * (uvv - ((80 * r2) >> 10));
269 if (r1 > mx) r1 = mx;
270 if (r2 > mx) r2 = mx;
277 /* this is terrible. here i want to assume ncolors = 256.
278 should lose double indirection */
281 #if dither_when_mapped
282 q[j] = st->colors[st->mc[r1] % st->ncolors].pixel;
284 q[j] = st->colors[(r1>>8) % st->ncolors].pixel;
286 else if (st->pdepth == 8)
287 q[j] = st->colors[(r1>>8) % st->ncolors].pixel;
288 else if (st->pdepth == 16)
289 #if dither_when_mapped
290 qq[j] = st->colors[st->mc[r1] % st->ncolors].pixel;
292 qq[j] = st->colors[(r1>>8) % st->ncolors].pixel;
294 else if (st->pdepth == 32)
295 #if dither_when_mapped
296 qqq[j] = st->colors[st->mc[r1] % st->ncolors].pixel;
298 qqq[j] = st->colors[(r1>>8) % st->ncolors].pixel;
304 t = st->r1; st->r1 = st->r1b; st->r1b = t;
305 t = st->r2; st->r2 = st->r2b; st->r2b = t;
309 /* ------------- xscreensaver rendering -------------- */
311 static const char *rd_defaults [] = {
312 ".background: black",
313 ".foreground: white",
314 "*width: 0", /* tried to use -1 but it complained */
325 #ifdef HAVE_XSHM_EXTENSION
333 static XrmOptionDescRec rd_options [] = {
334 { "-width", ".width", XrmoptionSepArg, 0 },
335 { "-height", ".height", XrmoptionSepArg, 0 },
336 { "-epoch", ".epoch", XrmoptionSepArg, 0 },
337 { "-reaction", ".reaction", XrmoptionSepArg, 0 },
338 { "-diffusion", ".diffusion", XrmoptionSepArg, 0 },
339 { "-verbose", ".verbose", XrmoptionNoArg, "True" },
340 { "-radius", ".radius", XrmoptionSepArg, 0 },
341 { "-speed", ".speed", XrmoptionSepArg, 0 },
342 { "-size", ".size", XrmoptionSepArg, 0 },
343 { "-delay", ".delay", XrmoptionSepArg, 0 },
344 { "-ncolors", ".colors", XrmoptionSepArg, 0 },
345 { "-shm", ".useSHM", XrmoptionNoArg, "True" },
346 { "-no-shm", ".useSHM", XrmoptionNoArg, "False" },
352 random_colors(struct state *st)
354 memset(st->colors, 0, st->ncolors*sizeof(*st->colors));
355 make_smooth_colormap (st->dpy, st->visual, st->cmap, st->colors, &st->ncolors,
357 if (st->ncolors <= 2) {
360 st->colors[0].flags = DoRed|DoGreen|DoBlue;
361 st->colors[0].red = st->colors[0].green = st->colors[0].blue = 0;
362 XAllocColor(st->dpy, st->cmap, &st->colors[0]);
363 st->colors[1].flags = DoRed|DoGreen|DoBlue;
364 st->colors[1].red = st->colors[1].green = st->colors[1].blue = 0xFFFF;
365 XAllocColor(st->dpy, st->cmap, &st->colors[1]);
368 /* Scale it up so that there are exactly 255 colors -- that keeps the
369 animation speed consistent, even when there aren't many allocatable
370 colors, and prevents the -mono mode from looking like static. */
371 if (st->ncolors != 255) {
373 double scale = (double) st->ncolors / (double) (n+1);
374 XColor *c2 = (XColor *) malloc(sizeof(*c2) * (n+1));
375 for (i = 0; i < n; i++)
376 c2[i] = st->colors[(int) (i * scale)];
385 /* should factor into RD-specfic and compute-every-pixel general */
387 rd_init (Display *dpy, Window win)
389 struct state *st = (struct state *) calloc (1, sizeof(*st));
397 st->delay = get_integer_resource (st->dpy, "delay", "Float");
399 #ifdef HAVE_XSHM_EXTENSION
400 st->use_shm = get_boolean_resource(st->dpy, "useSHM", "Boolean");
403 XGetWindowAttributes (st->dpy, win, &st->xgwa);
404 st->visual = st->xgwa.visual;
405 pixack_init(st, &st->width, &st->height);
407 double s = get_float_resource (st->dpy, "size", "Float");
408 double p = get_float_resource (st->dpy, "speed", "Float");
409 if (s < 0.0 || s > 1.0)
412 st->array_width = st->xgwa.width * s;
413 st->array_height = st->xgwa.height * s;
415 st->array_width = (st->array_width / st->width) * st->width;
416 st->array_height = (st->array_height / st->height) * st->height;
418 if (st->array_width < st->width) st->array_width = st->width;
419 if (st->array_height < st->height) st->array_height = st->height;
420 st->array_x = (st->xgwa.width - st->array_width)/2;
421 st->array_y = (st->xgwa.height - st->array_height)/2;
423 st->array_dy = .31415926 * p;
425 /* start in a random direction */
426 if (random() & 1) st->array_dx = -st->array_dx;
427 if (random() & 1) st->array_dy = -st->array_dy;
430 st->verbose = get_boolean_resource (st->dpy, "verbose", "Boolean");
431 st->npix = (st->width + 2) * (st->height + 2);
433 /* gcv.function = GXcopy;*/
434 st->gc = XCreateGC(st->dpy, win, 0 /*GCFunction*/, &gcv);
435 vdepth = visual_depth(DefaultScreenOfDisplay(st->dpy), st->xgwa.visual);
437 /* This code only deals with pixmap depths of 1, 8, 16, and 32.
438 Therefore, we assume that those depths will be supported by the
439 coresponding visual depths (that depth-24 dpys accept depth-32
440 pixmaps, and that depth-12 dpys accept depth-16 pixmaps.) */
441 st->pdepth = (vdepth == 1 ? 1 :
446 /* Ok, this like, sucks and stuff. There are some XFree86 systems
447 that have depth-24 visuals, that do not accept depth-32 XImages!
448 Which if you ask me is just absurd, since all it would take is
449 for the server to truncate the bits in that case. So, this crap
450 here detects the specific case of: we have chosen depth 32;
451 and the server does not support depth 32. In that case, we
452 try and use depth 16 instead.
454 The real fix would be to rewrite this program to deal with
455 depth 24 directly (or even better, arbitrary depths, but that
456 would mean going through the XImage routines instead of messing
457 with the XImage->data directly.)
459 jwz, 18-Mar-99: well, the X servers I have access to these days do
460 support 32-deep images on deep visuals, so I no longer have the
461 ability to test this code -- but it was causing problems on the
462 visuals that I do have, and I think that's because I mistakenly
463 wrote `pfv[i].depth' when I meant to write `pfv[i].bits_per_pixel'.
464 The symptom I was seeing was that the grid was 64x64, but the
465 images were being drawn 32x32 -- so there was a black stripe on
466 every other row. Wow, this code sucks so much.
468 if (st->pdepth == 32)
472 XPixmapFormatValues *pfv = XListPixmapFormats (st->dpy, &pfvc);
473 for (i = 0; i < pfvc; i++)
474 if (pfv[i].bits_per_pixel == st->pdepth)
480 st->cmap = st->xgwa.colormap;
481 st->ncolors = get_integer_resource (st->dpy, "colors", "Integer");
483 if (st->ncolors <= 0) {
490 if (mono_p || st->ncolors < 2) st->ncolors = 2;
491 if (st->ncolors <= 2) mono_p = True;
492 st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1));
494 st->mapped = (vdepth <= 8 &&
495 has_writable_cells(st->xgwa.screen, st->xgwa.visual));
499 st->mc = (unsigned char *) malloc(1<<16);
500 for (i = 0; i < (1<<16); i++) {
501 di = (i + (random()&255))>>8;
502 if (di > 255) di = 255;
507 st->pd = malloc(st->npix * (st->pdepth == 1 ? 1 : (st->pdepth / 8)));
509 fprintf(stderr, "not enough memory for %d pixels.\n", st->npix);
515 #ifdef HAVE_XSHM_EXTENSION
518 st->image = create_xshm_image(st->dpy, st->xgwa.visual, vdepth,
519 ZPixmap, 0, &st->shm_info, st->width, st->height);
525 st->pd = st->image->data;
528 #endif /* HAVE_XSHM_EXTENSION */
532 st->image = XCreateImage(st->dpy, st->xgwa.visual, vdepth,
534 st->width, st->height, 8, 0);
541 rd_draw (Display *dpy, Window win, void *closure)
543 struct state *st = (struct state *) closure;
547 pixack_frame(st, st->pd);
548 for (i = 0; i < st->array_width; i += st->width)
549 for (j = 0; j < st->array_height; j += st->height)
550 #ifdef HAVE_XSHM_EXTENSION
552 XShmPutImage(st->dpy, st->window, st->gc, st->image, 0, 0, i+st->array_x, j+st->array_y,
553 st->width, st->height, False);
556 XPutImage(st->dpy, win, st->gc, st->image, 0, 0, i+st->array_x, j+st->array_y,
557 st->width, st->height);
559 st->array_x += st->array_dx;
560 st->array_y += st->array_dy;
561 if (st->array_x < 0) {
563 st->array_dx = -st->array_dx;
565 } else if (st->array_x > (st->xgwa.width - st->array_width)) {
566 st->array_x = (st->xgwa.width - st->array_width);
567 st->array_dx = -st->array_dx;
570 if (st->array_y < 0) {
572 st->array_dy = -st->array_dy;
574 } else if (st->array_y > (st->xgwa.height - st->array_height)) {
575 st->array_y = (st->xgwa.height - st->array_height);
576 st->array_dy = -st->array_dy;
582 double swap = st->array_dx;
583 st->array_dx = st->array_dy;
594 rd_reshape (Display *dpy, Window window, void *closure,
595 unsigned int w, unsigned int h)
600 rd_event (Display *dpy, Window window, void *closure, XEvent *event)
606 rd_free (Display *dpy, Window window, void *closure)
610 XSCREENSAVER_MODULE_2 ("RDbomb", rdbomb, rd)