1 /* xscreensaver, Copyright (c) 1992-2014 Jamie Zawinski <jwz@jwz.org>
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
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
16 * And remember: X Windows is to graphics hacking as roman numerals are to
17 * the square root of pi.
22 #include "screenhack.h"
26 #define dither_when_mapped 1
35 #if dither_when_mapped
42 int frame, epoch_time;
43 unsigned short *r1, *r2, *r1b, *r2b;
44 int width, height, npix;
50 int array_width, array_height;
52 XShmSegmentInfo shm_info;
56 double array_x, array_y;
57 double array_dx, array_dy;
58 XWindowAttributes xgwa;
62 static void random_colors(struct state *st);
64 /* -----------------------------------------------------------
65 pixel hack, 8-bit pixel grid, first/next frame interface
67 pixack_init(int *size_h, int *size_v)
68 pixack_frame(char *pix_buf)
73 #define mx ((1<<16)-1)
75 /* you can replace integer mults wish shift/adds with these,
76 but it doesn't help on my 586 */
77 #define x5(n) ((n<<2)+n)
78 #define x7(n) ((n<<3)-n)
81 #define R (random()&((1<<30)-1))
82 #define BELLRAND(x) (((random()%(x)) + (random()%(x)) + (random()%(x)))/3)
84 /* returns number of pixels that the pixack produces. called once. */
86 pixack_init(struct state *st, int *size_h, int *size_v)
88 st->width = get_integer_resource (st->dpy, "width", "Integer");
89 st->height = get_integer_resource (st->dpy, "height", "Integer");
91 if (st->width <= 0 && st->height <= 0 && (R & 1))
92 st->width = st->height = 64 + BELLRAND(512);
94 if (st->width <= 0) st->width = 64 + BELLRAND(512);
95 if (st->height <= 0) st->height = 64 + BELLRAND(512);
97 if (st->width > st->xgwa.width) st->width = st->xgwa.width;
98 if (st->height > st->xgwa.height) st->height = st->xgwa.height;
100 /* jwz: when (and only when) XSHM is in use on an SGI 8-bit visual,
101 we get shear unless st->width is a multiple of 4. I don't understand
102 why. This is undoubtedly the wrong fix... */
103 if (visual_depth (st->xgwa.screen, st->xgwa.visual) == 8)
107 if (st->width < 10) st->width = 10;
108 if (st->height < 10) st->height = 10;
109 st->epoch_time = get_integer_resource (st->dpy, "epoch", "Integer");
110 st->npix = (st->width + 2) * (st->height + 2);
111 st->r1 = (unsigned short *) malloc(sizeof(unsigned short) * st->npix);
112 st->r2 = (unsigned short *) malloc(sizeof(unsigned short) * st->npix);
113 st->r1b = (unsigned short *) malloc(sizeof(unsigned short) * st->npix);
114 st->r2b = (unsigned short *) malloc(sizeof(unsigned short) * st->npix);
116 if (!st->r1 || !st->r2 || !st->r1b || !st->r2b) {
117 fprintf(stderr, "not enough memory for %d pixels.\n", st->npix);
122 *size_v = st->height;
125 #define test_pattern_hyper 0
128 /* returns the pixels. called many times. */
130 pixack_frame(struct state *st, char *pix_buf)
133 int w2 = st->width + 2;
135 #if test_pattern_hyper
140 if (!(st->frame%st->epoch_time)) {
143 for (i = 0; i < st->npix; i++) {
151 XSetWindowBackground(st->dpy, st->window, st->colors[255 % st->ncolors].pixel);
152 XClearWindow(st->dpy, st->window);
154 s = w2 * (st->height/2) + st->width/2;
155 st->radius = get_integer_resource (st->dpy, "radius", "Integer");
157 int maxr = st->width/2-2;
158 int maxr2 = st->height/2-2;
159 if (maxr2 < maxr) maxr = maxr2;
162 st->radius = 1 + ((R%10) ? (R%5) : (R % maxr));
163 if (st->radius > maxr) st->radius = maxr;
165 for (i = -st->radius; i < (st->radius+1); i++)
166 for (j = -st->radius; j < (st->radius+1); j++)
167 st->r2[s + i + j*w2] = mx - (R&63);
168 st->reaction = get_integer_resource (st->dpy, "reaction", "Integer");
169 if (st->reaction < 0 || st->reaction > 2) st->reaction = R&1;
170 st->diffusion = get_integer_resource (st->dpy, "diffusion", "Integer");
171 if (st->diffusion < 0 || st->diffusion > 2)
172 st->diffusion = (R%5) ? ((R%3)?0:1) : 2;
173 if (2 == st->reaction && 2 == st->diffusion)
174 st->reaction = st->diffusion = 0;
176 for (i = 0; i <= st->width+1; i++) {
177 st->r1[i] = st->r1[i + w2 * st->height];
178 st->r2[i] = st->r2[i + w2 * st->height];
179 st->r1[i + w2 * (st->height + 1)] = st->r1[i + w2];
180 st->r2[i + w2 * (st->height + 1)] = st->r2[i + w2];
182 for (i = 0; i <= st->height+1; i++) {
183 st->r1[w2 * i] = st->r1[st->width + w2 * i];
184 st->r2[w2 * i] = st->r2[st->width + w2 * i];
185 st->r1[w2 * i + st->width + 1] = st->r1[w2 * i + 1];
186 st->r2[w2 * i + st->width + 1] = st->r2[w2 * i + 1];
188 for (i = 0; i < st->height; i++) {
190 char *q = pix_buf + st->width * i;
191 short *qq = ((short *) pix_buf) + st->width * i;
192 /* long *qqq = ((long *) pix_buf) + st->width * i; -- crashes on Alpha */
193 int *qqq = ((int *) pix_buf) + st->width * i;
194 unsigned short *i1 = st->r1 + 1 + w2 * ii;
195 unsigned short *i2 = st->r2 + 1 + w2 * ii;
196 unsigned short *o1 = st->r1b + 1 + w2 * ii;
197 unsigned short *o2 = st->r2b + 1 + w2 * ii;
198 for (j = 0; j < st->width; j++) {
199 #if test_pattern_hyper
200 int r1 = (i * j + (st->frame&127)*frame)&65535;
202 int uvv, r1 = 0, r2 = 0;
203 switch (st->diffusion) {
205 r1 = i1[j] + i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
207 r2 = (i2[j]<<3) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
211 r1 = i1[j+1] + i1[j-1] + i1[j+w2] + i1[j-w2];
213 r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
217 r1 = (i1[j]<<1) + (i1[j+1]<<1) + (i1[j-1]<<1) + i1[j+w2] + i1[j-w2];
219 r2 = (i2[j]<<2) + i2[j+1] + i2[j-1] + i2[j+w2] + i2[j-w2];
224 /* John E. Pearson "Complex Patterns in a Simple System"
225 Science, July 1993 */
227 /* uvv = (((r1 * r2) >> bps) * r2) >> bps; */
228 /* avoid signed integer overflow */
229 uvv = ((((r1 >> 1)* r2) >> bps) * r2) >> (bps - 1);
230 switch (st->reaction) { /* costs 4% */
232 r1 += 4 * (((28 * (mx-r1)) >> 10) - uvv);
233 r2 += 4 * (uvv - ((80 * r2) >> 10));
236 r1 += 3 * (((27 * (mx-r1)) >> 10) - uvv);
237 r2 += 3 * (uvv - ((80 * r2) >> 10));
240 r1 += 2 * (((28 * (mx-r1)) >> 10) - uvv);
241 r2 += 3 * (uvv - ((80 * r2) >> 10));
244 if (r1 > mx) r1 = mx;
245 if (r2 > mx) r2 = mx;
252 /* this is terrible. here i want to assume ncolors = 256.
253 should lose double indirection */
256 #if dither_when_mapped
257 q[j] = st->colors[st->mc[r1] % st->ncolors].pixel;
259 q[j] = st->colors[(r1>>8) % st->ncolors].pixel;
261 else if (st->pdepth == 8)
262 q[j] = st->colors[(r1>>8) % st->ncolors].pixel;
263 else if (st->pdepth == 16)
264 #if dither_when_mapped
265 qq[j] = st->colors[st->mc[r1] % st->ncolors].pixel;
267 qq[j] = st->colors[(r1>>8) % st->ncolors].pixel;
269 else if (st->pdepth == 32)
270 #if dither_when_mapped
271 qqq[j] = st->colors[st->mc[r1] % st->ncolors].pixel;
273 qqq[j] = st->colors[(r1>>8) % st->ncolors].pixel;
279 t = st->r1; st->r1 = st->r1b; st->r1b = t;
280 t = st->r2; st->r2 = st->r2b; st->r2b = t;
284 /* ------------- xscreensaver rendering -------------- */
286 static const char *rd_defaults [] = {
287 ".background: black",
288 ".foreground: white",
290 "*width: 0", /* tried to use -1 but it complained */
300 #ifdef HAVE_XSHM_EXTENSION
306 "*ignoreRotation: True",
311 static XrmOptionDescRec rd_options [] = {
312 { "-width", ".width", XrmoptionSepArg, 0 },
313 { "-height", ".height", XrmoptionSepArg, 0 },
314 { "-epoch", ".epoch", XrmoptionSepArg, 0 },
315 { "-reaction", ".reaction", XrmoptionSepArg, 0 },
316 { "-diffusion", ".diffusion", XrmoptionSepArg, 0 },
317 { "-radius", ".radius", XrmoptionSepArg, 0 },
318 { "-speed", ".speed", XrmoptionSepArg, 0 },
319 { "-size", ".size", XrmoptionSepArg, 0 },
320 { "-delay", ".delay", XrmoptionSepArg, 0 },
321 { "-ncolors", ".colors", XrmoptionSepArg, 0 },
322 { "-shm", ".useSHM", XrmoptionNoArg, "True" },
323 { "-no-shm", ".useSHM", XrmoptionNoArg, "False" },
329 random_colors(struct state *st)
331 memset(st->colors, 0, st->ncolors*sizeof(*st->colors));
332 make_smooth_colormap (st->xgwa.screen, st->visual, st->cmap,
333 st->colors, &st->ncolors,
335 if (st->ncolors <= 2) {
338 st->colors[0].flags = DoRed|DoGreen|DoBlue;
339 st->colors[0].red = st->colors[0].green = st->colors[0].blue = 0;
340 XAllocColor(st->dpy, st->cmap, &st->colors[0]);
341 st->colors[1].flags = DoRed|DoGreen|DoBlue;
342 st->colors[1].red = st->colors[1].green = st->colors[1].blue = 0xFFFF;
343 XAllocColor(st->dpy, st->cmap, &st->colors[1]);
346 /* Scale it up so that there are exactly 255 colors -- that keeps the
347 animation speed consistent, even when there aren't many allocatable
348 colors, and prevents the -mono mode from looking like static. */
349 if (st->ncolors != 255) {
351 double scale = (double) st->ncolors / (double) (n+1);
352 XColor *c2 = (XColor *) malloc(sizeof(*c2) * (n+1));
353 for (i = 0; i < n; i++)
354 c2[i] = st->colors[(int) (i * scale)];
363 /* should factor into RD-specfic and compute-every-pixel general */
365 rd_init (Display *dpy, Window win)
367 struct state *st = (struct state *) calloc (1, sizeof(*st));
374 st->delay = get_integer_resource (st->dpy, "delay", "Float");
376 XGetWindowAttributes (st->dpy, win, &st->xgwa);
377 st->visual = st->xgwa.visual;
378 pixack_init(st, &st->width, &st->height);
380 double s = get_float_resource (st->dpy, "size", "Float");
381 double p = get_float_resource (st->dpy, "speed", "Float");
382 if (s < 0.0 || s > 1.0)
385 st->array_width = st->xgwa.width * s;
386 st->array_height = st->xgwa.height * s;
388 st->array_width = (st->array_width / st->width) * st->width;
389 st->array_height = (st->array_height / st->height) * st->height;
391 if (st->array_width < st->width) st->array_width = st->width;
392 if (st->array_height < st->height) st->array_height = st->height;
393 st->array_x = (st->xgwa.width - st->array_width)/2;
394 st->array_y = (st->xgwa.height - st->array_height)/2;
396 st->array_dy = .31415926 * p;
398 /* start in a random direction */
399 if (random() & 1) st->array_dx = -st->array_dx;
400 if (random() & 1) st->array_dy = -st->array_dy;
403 st->npix = (st->width + 2) * (st->height + 2);
404 /* gcv.function = GXcopy;*/
405 st->gc = XCreateGC(st->dpy, win, 0 /*GCFunction*/, &gcv);
406 vdepth = visual_depth(DefaultScreenOfDisplay(st->dpy), st->xgwa.visual);
408 /* This code only deals with pixmap depths of 1, 8, 16, and 32.
409 Therefore, we assume that those depths will be supported by the
410 coresponding visual depths (that depth-24 dpys accept depth-32
411 pixmaps, and that depth-12 dpys accept depth-16 pixmaps.) */
412 st->pdepth = (vdepth == 1 ? 1 :
417 /* Ok, this like, sucks and stuff. There are some XFree86 systems
418 that have depth-24 visuals, that do not accept depth-32 XImages!
419 Which if you ask me is just absurd, since all it would take is
420 for the server to truncate the bits in that case. So, this crap
421 here detects the specific case of: we have chosen depth 32;
422 and the server does not support depth 32. In that case, we
423 try and use depth 16 instead.
425 The real fix would be to rewrite this program to deal with
426 depth 24 directly (or even better, arbitrary depths, but that
427 would mean going through the XImage routines instead of messing
428 with the XImage->data directly.)
430 jwz, 18-Mar-99: well, the X servers I have access to these days do
431 support 32-deep images on deep visuals, so I no longer have the
432 ability to test this code -- but it was causing problems on the
433 visuals that I do have, and I think that's because I mistakenly
434 wrote `pfv[i].depth' when I meant to write `pfv[i].bits_per_pixel'.
435 The symptom I was seeing was that the grid was 64x64, but the
436 images were being drawn 32x32 -- so there was a black stripe on
437 every other row. Wow, this code sucks so much.
439 if (st->pdepth == 32)
443 XPixmapFormatValues *pfv = XListPixmapFormats (st->dpy, &pfvc);
444 for (i = 0; i < pfvc; i++)
445 if (pfv[i].bits_per_pixel == st->pdepth)
451 st->cmap = st->xgwa.colormap;
452 st->ncolors = get_integer_resource (st->dpy, "colors", "Integer");
454 if (st->ncolors <= 0 || st->ncolors >= 255) {
461 if (mono_p || st->ncolors < 2) st->ncolors = 2;
462 if (st->ncolors <= 2) mono_p = True;
463 st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1));
465 st->mapped = (vdepth <= 8 &&
466 has_writable_cells(st->xgwa.screen, st->xgwa.visual));
470 st->mc = (unsigned char *) malloc(1<<16);
471 for (i = 0; i < (1<<16); i++) {
472 di = (i + (random()&255))>>8;
473 if (di > 255) di = 255;
478 st->image = create_xshm_image(st->dpy, st->xgwa.visual, vdepth,
479 ZPixmap, &st->shm_info, st->width, st->height);
480 st->pd = st->image->data;
486 rd_draw (Display *dpy, Window win, void *closure)
488 struct state *st = (struct state *) closure;
492 pixack_frame(st, st->pd);
493 for (i = 0; i < st->array_width; i += st->width)
494 for (j = 0; j < st->array_height; j += st->height)
495 put_xshm_image(st->dpy, st->window, st->gc, st->image, 0, 0, i+st->array_x, j+st->array_y,
496 st->width, st->height, &st->shm_info);
498 st->array_x += st->array_dx;
499 st->array_y += st->array_dy;
500 if (st->array_x < 0) {
502 st->array_dx = -st->array_dx;
504 } else if (st->array_x > (st->xgwa.width - st->array_width)) {
505 st->array_x = (st->xgwa.width - st->array_width);
506 st->array_dx = -st->array_dx;
509 if (st->array_y < 0) {
511 st->array_dy = -st->array_dy;
513 } else if (st->array_y > (st->xgwa.height - st->array_height)) {
514 st->array_y = (st->xgwa.height - st->array_height);
515 st->array_dy = -st->array_dy;
521 double swap = st->array_dx;
522 st->array_dx = st->array_dy;
533 rd_reshape (Display *dpy, Window window, void *closure,
534 unsigned int w, unsigned int h)
539 rd_event (Display *dpy, Window window, void *closure, XEvent *event)
545 rd_free (Display *dpy, Window window, void *closure)
549 XSCREENSAVER_MODULE_2 ("RDbomb", rdbomb, rd)