1 /* ripples, Copyright (c) 1999 Ian McConnell <ian@emit.demon.co.uk>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
13 * "Water" ripples that can cross and interfere with each other.
15 * I can't remember where I got this idea from, but it's been around for a
16 * while in various demos. Some inspiration from
17 * water.txt by Tom Hammersley,tomh@globalnet.co.uk
20 * -delay usleep every iteration
21 * -rate Add one drop every "rate" iterations
22 * -box Add big square splash every "box" iters (not very good)
23 * -water Ripples on a grabbed background image
24 * -foreground Interpolate ripples between these two colors
26 * -oily Psychedelic colours like man
27 * -stir Add a regular pattern of drops
28 * -fluidity Between 0 and 16. 16 = big drops
29 * -light Hack to add lighting effect
31 * Code mainly hacked from xflame and decayscreen.
35 * 13 Oct 1999: Initial hack
36 * 30 Oct 1999: Speeded up graphics with dirty buffer. Returned to using
37 * putpixel for greater portability
38 * Added a variety of methods for splashing screen.
39 * 31 Oct 1999: Added in lighting hack
40 * 13 Nov 1999: Speed up tweaks
41 * Adjust "light" for different bits per colour (-water only)
46 #include "screenhack.h"
48 typedef enum {ripple_drop, ripple_blob, ripple_box, ripple_stir} ripple_mode;
50 #ifdef HAVE_XSHM_EXTENSION
52 #endif /* HAVE_XSHM_EXTENSION */
62 XImage *orig_map, *buffer_map;
69 int width, height; /* ripple size */
70 int bigwidth, bigheight; /* screen size */
73 short *bufferA, *bufferB, *temp;
76 double cos_tab[TABLE];
80 unsigned long rmask; /* This builds on the warp effect by adding */
81 unsigned long gmask; /* in a lighting effect: brighten pixels by an */
82 unsigned long bmask; /* amount corresponding to the vertical gradient */
93 int iterations, delay, rate, box, oily, stir, fluidity;
97 void (*draw_transparent) (struct state *st, short *src);
99 async_load_state *img_loader;
101 #ifdef HAVE_XSHM_EXTENSION
103 XShmSegmentInfo shm_info;
104 #endif /* HAVE_XSHM_EXTENSION */
108 /* Distribution of drops: many little ones and a few big ones. */
109 static const double drop_dist[] =
110 {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.2, 0.6};
113 /* How hard to hit the water */
116 #define MIN(x, y) ((x) < (y) ? (x) : (y))
118 #define MAX(x, y) ((x) > (y) ? (x) : (y))
120 #define DIRTY 3 /* dirty >= 2, 1 = restore original pixel, 0 = leave alone */
122 /* From fortune(6) */
123 /* -- really weird C code to count the number of bits in a word */
124 #define BITCOUNT(x) (((BX_(x)+(BX_(x)>>4)) & 0x0F0F0F0F) % 255)
125 #define BX_(x) ((x) - (((x)>>1)&0x77777777) \
126 - (((x)>>2)&0x33333333) \
127 - (((x)>>3)&0x11111111))
130 static unsigned long grayscale(struct state *st, unsigned long color);
132 /* ------------------------------------------- */
136 map_color(struct state *st, int grey)
139 grey = st->ncolors * abs(grey) / (SPLASH/4);
140 if (grey > st->ncolors)
144 return st->ctab[grey];
149 draw_ripple(struct state *st, short *src)
152 char *dirty = st->dirty_buffer;
154 for (down = 0; down < st->height - 1; down++, src += 1, dirty += 1)
155 for (across = 0; across < st->width - 1; across++, src++, dirty++) {
158 v2 = (int)*(src + 1);
159 v3 = (int)*(src + st->width);
160 v4 = (int)*(src + st->width + 1);
161 if ((v1 == 0 && v2 == 0 && v3 == 0 && v4 == 0)) {
170 dx = ((v3 - v1) + (v4 - v2)) << st->light; /* light from top */
173 XPutPixel(st->buffer_map,(across<<1), (down<<1), map_color(st, dx + v1));
174 XPutPixel(st->buffer_map,(across<<1)+1,(down<<1), map_color(st, dx + ((v1 + v2) >> 1)));
175 XPutPixel(st->buffer_map,(across<<1), (down<<1)+1,map_color(st, dx + ((v1 + v3) >> 1)));
176 XPutPixel(st->buffer_map,(across<<1)+1,(down<<1)+1,map_color(st, dx + ((v1 + v4) >> 1)));
182 /* ------------------------------------------- */
185 /* Uses the horizontal gradient as an offset to create a warp effect */
187 draw_transparent_vanilla(struct state *st, short *src)
189 int across, down, pixel;
190 char *dirty = st->dirty_buffer;
193 for (down = 0; down < st->height - 2; down++, pixel += 2)
194 for (across = 0; across < st->width-2; across++, pixel++) {
195 int gradx, grady, gradx1, grady1;
196 int x0, x1, x2, y1, y2;
201 y1 = src[pixel + st->width];
202 y2 = src[pixel + 2*st->width];
208 gradx1 = 1 + (gradx + gradx1) / 2;
209 grady1 = 1 + (grady + grady1) / 2;
211 if ((2*across+MIN(gradx,gradx1) < 0) ||
212 (2*across+MAX(gradx,gradx1) >= st->bigwidth)) {
216 if ((2*down+MIN(grady,grady1) < 0) ||
217 (2*down+MAX(grady,grady1) >= st->bigheight)) {
222 if ((gradx == 0 && gradx1 == 1 && grady == 0 && grady1 == 1)) {
223 if (dirty[pixel] > 0)
226 dirty[pixel] = DIRTY;
228 if (dirty[pixel] > 0) {
229 XPutPixel(st->buffer_map, (across<<1), (down<<1),
230 grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady)));
231 XPutPixel(st->buffer_map, (across<<1)+1,(down<<1),
232 grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady)));
233 XPutPixel(st->buffer_map, (across<<1), (down<<1)+1,
234 grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady1)));
235 XPutPixel(st->buffer_map, (across<<1)+1,(down<<1)+1,
236 grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady1)));
242 /* ------------------------------------------- */
246 set_mask(unsigned long color, unsigned long *mask, int *shift)
249 while (color != 0 && (color & 1) == 0) {
258 cadd(unsigned long color, int dx, unsigned long mask, int shift)
265 else if (x > (int)mask) x = mask;
267 return color << shift;
272 bright(struct state *st, int dx, unsigned long color)
274 return (cadd(color, dx, st->rmask, st->rshift) |
275 cadd(color, dx, st->gmask, st->gshift) |
276 cadd(color, dx, st->bmask, st->bshift));
281 grayscale(struct state *st, unsigned long color)
291 if (!st->grayscale_p)
293 if (!st->transparent)
295 if ((st->rmask == 0) || (st->gmask == 0) || (st->bmask == 0))
298 red = ((color >> st->rshift) & st->rmask);
299 green = ((color >> st->gshift) & st->gmask);
300 blue = ((color >> st->bshift) & st->bmask);
301 total = red * st->gmask * st->bmask + green * st->rmask * st->bmask + blue * st->rmask * st->gmask;
303 gray_r = total / (3 * st->gmask * st->bmask);
306 if (gray_r > st->rmask)
309 gray_g = total / (3 * st->rmask * st->bmask);
312 if (gray_g > st->gmask)
315 gray_b = total / (3 * st->rmask * st->gmask);
318 if (gray_b > st->bmask)
321 return ((unsigned long)
322 ((gray_r << st->rshift) | (gray_g << st->gshift) | (gray_b << st->bshift)));
327 draw_transparent_light(struct state *st, short *src)
329 int across, down, pixel;
330 char *dirty = st->dirty_buffer;
333 for (down = 0; down < st->height - 2; down++, pixel += 2)
334 for (across = 0; across < st->width-2; across++, pixel++) {
335 int gradx, grady, gradx1, grady1;
336 int x0, x1, x2, y1, y2;
341 y1 = src[pixel + st->width];
342 y2 = src[pixel + 2*st->width];
348 gradx1 = 1 + (gradx + gradx1) / 2;
349 grady1 = 1 + (grady + grady1) / 2;
351 if ((2*across+MIN(gradx,gradx1) < 0) ||
352 (2*across+MAX(gradx,gradx1) >= st->bigwidth)) {
356 if ((2*down+MIN(grady,grady1) < 0) ||
357 (2*down+MAX(grady,grady1) >= st->bigheight)) {
362 if ((gradx == 0 && gradx1 == 1 && grady == 0 && grady1 == 1)) {
363 if (dirty[pixel] > 0)
366 dirty[pixel] = DIRTY;
368 if (dirty[pixel] > 0) {
372 if (4-st->light >= 0)
373 dx = (grady + (src[pixel+st->width+1]-x1)) >> (4-st->light);
375 dx = (grady + (src[pixel+st->width+1]-x1)) << (st->light-4);
378 XPutPixel(st->buffer_map, (across<<1), (down<<1),
379 bright(st, dx, grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady))));
380 XPutPixel(st->buffer_map, (across<<1)+1,(down<<1),
381 bright(st, dx, grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady))));
382 XPutPixel(st->buffer_map, (across<<1), (down<<1)+1,
383 bright(st, dx, grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady1))));
384 XPutPixel(st->buffer_map, (across<<1)+1,(down<<1)+1,
385 bright(st, dx, grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady1))));
387 /* Could use XCopyArea, but XPutPixel is faster */
388 XPutPixel(st->buffer_map, (across<<1), (down<<1),
389 grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady)));
390 XPutPixel(st->buffer_map, (across<<1)+1,(down<<1),
391 grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady)));
392 XPutPixel(st->buffer_map, (across<<1), (down<<1)+1,
393 grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady1)));
394 XPutPixel(st->buffer_map, (across<<1)+1,(down<<1)+1,
395 grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady1)));
402 /* ------------------------------------------- */
406 /* Doesn't go any faster and doesn't work at all colour depths */
408 draw_transparent16l(short *src)
410 int across, down, bigpix, pixel;
411 char *dirty = st->dirty_buffer;
412 unsigned short *buffer, *orig;
414 buffer = (unsigned short *) st->buffer_map->data;
415 orig = (unsigned short *) st->orig_map->data;
417 for (pixel = bigpix = down = 0;
418 down < st->height - 2;
419 down++, pixel += 2, bigpix += st->bigwidth+4)
420 for (across = 0; across < st->width-2; across++, pixel++, bigpix+=2) {
421 int gradx, grady, gradx1, grady1;
422 int x0, x1, x2, y1, y2;
427 y1 = src[pixel + st->width];
428 y2 = src[pixel + 2*st->width];
434 gradx1 = 1 + (gradx + gradx1) / 2;
435 grady1 = 1 + (grady + grady1) / 2;
437 if ((2*across+MIN(gradx,gradx1) < 0) ||
438 (2*across+MAX(gradx,gradx1) >= st->bigwidth)) {
442 if ((2*down+MIN(grady,grady1) < 0) ||
443 (2*down+MAX(grady,grady1) >= st->bigheight)) {
448 if ((gradx == 0 && gradx1 == 1 && grady == 0 && grady1 == 1)) {
449 if (dirty[pixel] > 0)
452 dirty[pixel] = DIRTY;
454 if (dirty[pixel] > 0) {
455 unsigned short *dest = buffer + bigpix;
456 unsigned short *image = orig + bigpix;
460 if (4-st->light >= 0)
461 dx = (grady + (src[pixel+st->width+1]-x1)) >> (4-st->light);
463 dx = (grady + (src[pixel+st->width+1]-x1)) << (st->light-4);
465 grady *= st->bigwidth;
466 grady1*= st->bigwidth;
469 *dest++ = dobright(dx, *(image + gradx + grady));
470 *dest = dobright(dx, *(image + gradx1 + grady));
471 dest += st->bigwidth - 1;
472 *dest++ = dobright(dx, *(image + gradx + grady1));
473 *dest = dobright(dx, *(image + gradx1 + grady1));
475 *dest++ = *(image + gradx + grady);
476 *dest = *(image + gradx1 + grady);
477 dest += st->bigwidth - 1;
478 *dest++ = *(image + gradx + grady1);
479 *dest = *(image + gradx1 + grady1);
487 /* ------------------------------------------- */
491 setup_X(struct state *st)
493 XWindowAttributes xgwa;
496 XGetWindowAttributes(st->dpy, st->window, &xgwa);
498 st->colormap = xgwa.colormap;
499 st->screen = xgwa.screen;
500 st->bigwidth = xgwa.width;
501 st->bigheight = xgwa.height;
502 st->visual = xgwa.visual;
505 /* This causes buffer_map to be 1 pixel taller and wider than orig_map,
506 which can cause the two XImages to have different bytes-per-line,
507 which causes stair-stepping. So this better not be necessary...
510 #if 0 /* I'm not entirely sure if I need this */
511 if (st->bigwidth % 2)
513 if (st->bigheight % 2)
518 st->width = st->bigwidth / 2;
519 st->height = st->bigheight / 2;
521 if (st->transparent) {
525 gcv.function = GXcopy;
526 gcv.subwindow_mode = IncludeInferiors;
528 gcflags = GCFunction;
529 if (use_subwindow_mode_p(xgwa.screen, st->window)) /* see grabscreen.c */
530 gcflags |= GCSubwindowMode;
532 st->gc = XCreateGC(st->dpy, st->window, gcflags, &gcv);
534 st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
536 st->start_time = time ((time_t *) 0);
540 st->gc = XCreateGC(st->dpy, st->window, 0, &gcv);
545 fprintf(stderr, "XCreateGC failed\n");
551 #ifdef HAVE_XSHM_EXTENSION
553 st->buffer_map = create_xshm_image(st->dpy, xgwa.visual, depth,
554 ZPixmap, 0, &st->shm_info, st->bigwidth, st->bigheight);
555 if (!st->buffer_map) {
557 fprintf(stderr, "create_xshm_image failed\n");
560 #endif /* HAVE_XSHM_EXTENSION */
562 if (!st->buffer_map) {
563 st->buffer_map = XCreateImage(st->dpy, xgwa.visual,
564 depth, ZPixmap, 0, 0,
565 st->bigwidth, st->bigheight, 8, 0);
566 st->buffer_map->data = (char *)
567 calloc(st->buffer_map->height, st->buffer_map->bytes_per_line);
573 DisplayImage(struct state *st)
575 #ifdef HAVE_XSHM_EXTENSION
577 XShmPutImage(st->dpy, st->window, st->gc, st->buffer_map, 0, 0, 0, 0,
578 st->bigwidth, st->bigheight, False);
580 #endif /* HAVE_XSHM_EXTENSION */
581 XPutImage(st->dpy, st->window, st->gc, st->buffer_map, 0, 0, 0, 0,
582 st->bigwidth, st->bigheight);
586 /* ------------------------------------------- */
590 cinterp(double a, int bg, int fg)
593 result = (int)((1-a) * bg + a * fg + 0.5);
594 if (result < 0) result = 0;
595 if (result > 255) result = 255;
600 /* Interpolate the ripple colours between the background colour and
603 init_linear_colors(struct state *st)
605 int i, j, red, green, blue, bred, bgreen, bblue;
608 if (st->ncolors < 2 || mono_p)
610 if (st->ncolors <= 2)
613 /* Make it possible to set the color of the ripples,
614 Based on work by Raymond Medeiros <ray@stommel.marine.usf.edu> and jwz.
616 fg.pixel = get_pixel_resource(st->dpy, st->colormap, "foreground", "Foreground");
617 XQueryColor(st->dpy, st->colormap, &fg);
619 green = (fg.green >> 8);
620 blue = (fg.blue >> 8);
622 bg.pixel = get_pixel_resource(st->dpy, st->colormap, "background", "Background");
623 XQueryColor(st->dpy, st->colormap, &bg);
624 bred = (bg.red >> 8);
625 bgreen = (bg.green >> 8);
626 bblue = (bg.blue >> 8);
629 for (i = 0; i < st->ncolors+1; i++) {
631 double a = (double)i / st->ncolors;
632 int r = cinterp(a, bred, red);
633 int g = cinterp(a, bgreen, green);
634 int b = cinterp(a, bblue, blue);
636 xcl.red = (unsigned short) ((r << 8) | r);
637 xcl.green = (unsigned short) ((g << 8) | g);
638 xcl.blue = (unsigned short) ((b << 8) | b);
639 xcl.flags = DoRed | DoGreen | DoBlue;
641 XAllocColor(st->dpy, st->colormap, &xcl);
643 st->ctab[j++] = (int) xcl.pixel;
649 init_oily_colors(struct state *st)
651 XColor *colors = NULL;
653 if (st->ncolors < 2 || mono_p)
655 if (st->ncolors <= 2)
660 colors = (XColor *)malloc(sizeof(*colors) * (st->ncolors+1));
661 make_smooth_colormap(st->screen, st->visual, st->colormap,
662 colors, &st->ncolors,
664 False, /* not writable */
665 True); /* verbose (complain about failure) */
666 if (st->ncolors <= 2) {
675 for (i = 0; i < st->ncolors+1; i++) {
676 XAllocColor(st->dpy, st->colormap, colors+i);
677 st->ctab[j++] = (int) colors[i].pixel;
682 st->ctab[1] = get_pixel_resource(st->dpy, st->colormap, "foreground", "Foreground");
683 st->ctab[0] = get_pixel_resource(st->dpy, st->colormap, "background", "Background");
688 /* ------------------------------------------- */
692 init_cos_tab(struct state *st)
695 for (i = 0; i < TABLE; i++)
696 st->cos_tab[i] = cos(i * M_PI/2 / TABLE);
700 /* Shape of drop to add */
702 sinc(struct state *st, double x)
707 i = (int)(x * TABLE + 0.5);
708 if (i >= TABLE) i = (TABLE-1) - (i-(TABLE-1));
709 if (i < 0) return 0.;
710 return st->cos_tab[i];
712 return cos(x * M_PI/2);
723 add_circle_drop(struct state *st, int x, int y, int radius, int dheight)
726 short *buf = (random()&1) ? st->bufferA : st->bufferB;
728 r2 = radius * radius;
731 for (cy = -radius; cy <= radius; cy++)
732 for (cx = -radius; cx <= radius; cx++) {
735 if (xx < 0 || yy < 0 || xx >= st->width || yy >= st->height) {break;}
738 buf[xx + yy*st->width] =
739 (short)(dheight * sinc(st, (radius > 0) ? sqrt(r)/radius : 0));
745 add_drop(struct state *st, ripple_mode mode, int drop)
747 int newx, newy, dheight;
748 int radius = MIN(st->width, st->height) / 50;
749 /* Don't put drops too near the edge of the screen or they get stuck */
757 dheight = 1 + (random() % drop);
758 newx = border + (random() % (st->width - 2*border));
759 newy = border + (random() % (st->height - 2*border));
760 x = newy * st->width + newx;
761 st->bufferA[x + 1] = st->bufferA[x] = st->bufferA[x + st->width] = st->bufferA[x + st->width + 1] =
762 st->bufferB[x + 1] = st->bufferB[x] = st->bufferB[x + st->width] = st->bufferB[x + st->width + 1] =
770 power = drop_dist[random() % (sizeof(drop_dist)/sizeof(drop_dist[0]))]; /* clumsy */
771 dheight = (int)(drop * (power + 0.01));
772 tmp_i = (int)(st->width - 2*border - 2*radius*power);
773 tmp_j = (int)(st->height - 2*border - 2*radius*power);
774 newx = radius + border + ((tmp_i > 0) ? random() % tmp_i : 0);
775 newy = radius + border + ((tmp_j > 0) ? random() % tmp_j : 0);
776 add_circle_drop(st, newx, newy, radius, dheight);
779 /* Adding too many boxes too quickly (-box 1) doesn't give the waves time
780 to disperse and the waves build up (and overflow) */
784 short *buf = (random()&1) ? st->bufferA : st->bufferB;
787 radius = (1 + (random() % 5)) * (1 + (random() % 5));
788 dheight = drop / 128;
789 if (random() & 1) dheight = -dheight;
790 tmp_i = st->width - 2*border - 2*radius;
791 tmp_j = st->height - 2*border - 2*radius;
792 newx = radius + border + ((tmp_i > 0) ? random() % tmp_i : 0);
793 newy = radius + border + ((tmp_j > 0) ? random() % tmp_j : 0);
794 x = newy * st->width + newx;
795 for (cy = -radius; cy <= radius; cy++)
796 for (cx = -radius; cx <= radius; cx++)
797 buf[x + cx + cy*st->width] = (short)(dheight);
802 newx = border + (int)((st->width-2*border) * (1+cos(3*st->stir_ang)) / 2);
803 newy = border + (int)((st->height-2*border) * (1+sin(2*st->stir_ang)) / 2);
804 add_circle_drop(st, newx, newy, radius, drop / 10);
805 st->stir_ang += 0.02;
806 if (st->stir_ang > 12*M_PI) st->stir_ang = 0;
814 init_ripples(struct state *st, int ndrops, int splash)
818 st->bufferA = (short *)calloc(st->width * st->height, sizeof(*st->bufferA));
819 st->bufferB = (short *)calloc(st->width * st->height, sizeof(*st->bufferB));
820 st->temp = (short *)calloc(st->width * st->height, sizeof(*st->temp));
822 st->dirty_buffer = (char *)calloc(st->width * st->height, sizeof(*st->dirty_buffer));
824 for (i = 0; i < ndrops; i++)
825 add_drop(st, ripple_blob, splash);
827 if (st->transparent) {
831 for (down = 0; down < st->bigheight; down++)
832 for (across = 0; across < st->bigwidth; across++)
833 XPutPixel(st->buffer_map, across, down,
834 grayscale(st, XGetPixel(st->orig_map, across, down)));
838 /* There's got to be a better way of doing this XCopyArea? */
839 memcpy(st->buffer_map->data, st->orig_map->data,
840 st->bigheight * st->buffer_map->bytes_per_line);
843 int across, down, color;
845 color = map_color(st, 0); /* background colour */
846 for (down = 0; down < st->bigheight; down++)
847 for (across = 0; across < st->bigwidth; across++)
848 XPutPixel(st->buffer_map,across, down, color);
856 Explanation from hq_water.zip (Arturo R Montesinos (ARM) arami@fi.upm.es)
858 Differential equation is: u = a ( u + u )
861 Where a = tension * gravity / surface_density.
863 Approximating second derivatives by central differences:
865 [ u(t+1)-2u(t)+u(t-1) ] / dt = a [ u(x+1)+u(x-1)+u(y+1)+u(y-1)-4u ] / h
867 where dt = time step squared, h = dx*dy = mesh resolution squared.
869 From where u(t+1) may be calculated as:
872 u(t+1) = a -- | 1 0 1 |u - u(t-1) + (2-4a --)u
875 When a*dt/h = 1/2 last term vanishes, giving:
878 u(t+1) = - | 1 0 1 |u - u(t-1)
881 (note that u(t-1,x,y) is only used to calculate u(t+1,x,y) so
882 we can use the same array for both t-1 and t+1, needing only
883 two arrays, U[0] and U[1])
885 Dampening is simulated by subtracting 1/2^n of result.
886 n=4 gives best-looking result
887 n<4 (eg 2 or 3) thicker consistency, waves die out immediately
888 n>4 (eg 8 or 12) more fluid, waves die out slowly
892 ripple(struct state *st)
894 int across, down, pixel;
897 if (st->draw_toggle == 0) {
907 switch (st->draw_count) {
909 pixel = 1 * st->width + 1;
910 for (down = 1; down < st->height - 1; down++, pixel += 2 * 1)
911 for (across = 1; across < st->width - 1; across++, pixel++) {
913 (((src[pixel - 1] + src[pixel + 1] +
914 src[pixel - st->width] + src[pixel + st->width]) / 2)) - dest[pixel];
917 /* Smooth the output */
918 pixel = 1 * st->width + 1;
919 for (down = 1; down < st->height - 1; down++, pixel += 2 * 1)
920 for (across = 1; across < st->width - 1; across++, pixel++) {
921 if (st->temp[pixel] != 0) { /* Close enough for government work */
923 (st->temp[pixel - 1] + st->temp[pixel + 1] +
924 st->temp[pixel - st->width] + st->temp[pixel + st->width] +
925 st->temp[pixel - st->width - 1] + st->temp[pixel - st->width + 1] +
926 st->temp[pixel + st->width - 1] + st->temp[pixel + st->width + 1] +
927 st->temp[pixel]) / 9;
928 dest[pixel] = damp - (damp >> st->fluidity);
934 pixel = 1 * st->width + 1;
935 for (down = 1; down < st->height - 1; down++, pixel += 2 * 1)
936 for (across = 1; across < st->width - 1; across++, pixel++) {
938 (((src[pixel - 1] + src[pixel + 1] +
939 src[pixel - st->width] + src[pixel + st->width]) / 2)) - dest[pixel];
940 dest[pixel] = damp - (damp >> st->fluidity);
944 if (++st->draw_count > 3) st->draw_count = 0;
947 st->draw_transparent(st, dest);
949 draw_ripple(st, dest);
953 /* ------------------------------------------- */
956 ripples_init (Display *disp, Window win)
958 struct state *st = (struct state *) calloc (1, sizeof(*st));
962 st->delay = get_integer_resource(disp, "delay", "Integer");
963 st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
964 st->rate = get_integer_resource(disp, "rate", "Integer");
965 st->box = get_integer_resource(disp, "box", "Integer");
966 st->oily = get_boolean_resource(disp, "oily", "Boolean");
967 st->stir = get_boolean_resource(disp, "stir", "Boolean");
968 st->fluidity = get_integer_resource(disp, "fluidity", "Integer");
969 st->transparent = get_boolean_resource(disp, "water", "Boolean");
970 st->grayscale_p = get_boolean_resource(disp, "grayscale", "Boolean");
971 #ifdef HAVE_XSHM_EXTENSION
972 st->use_shm = get_boolean_resource(disp, "useSHM", "Boolean");
973 #endif /* HAVE_XSHM_EXTENSION */
974 st->light = get_integer_resource(disp, "light", "Integer");
976 if (st->delay < 0) st->delay = 0;
977 if (st->duration < 1) st->duration = 1;
978 if (st->fluidity <= 1) st->fluidity = 1;
979 if (st->fluidity > 16) st->fluidity = 16; /* 16 = sizeof(short) */
980 if (st->light < 0) st->light = 0;
985 st->ncolors = get_integer_resource (disp, "colors", "Colors");
986 if (0 == st->ncolors) /* English spelling? */
987 st->ncolors = get_integer_resource (disp, "colours", "Colors");
989 if (st->ncolors > sizeof(st->ctab)/sizeof(*st->ctab))
990 st->ncolors = sizeof(st->ctab)/sizeof(*st->ctab);
993 init_oily_colors(st);
995 init_linear_colors(st);
997 if (st->transparent && st->light > 0) {
999 st->draw_transparent = draw_transparent_light;
1000 set_mask(st->visual->red_mask, &st->rmask, &st->rshift);
1001 set_mask(st->visual->green_mask, &st->gmask, &st->gshift);
1002 set_mask(st->visual->blue_mask, &st->bmask, &st->bshift);
1003 if (st->rmask == 0) st->draw_transparent = draw_transparent_vanilla;
1005 /* Adjust the shift value "light" when we don't have 8 bits per colour */
1006 maxbits = MIN(MIN(BITCOUNT(st->rmask), BITCOUNT(st->gmask)), BITCOUNT(st->bmask));
1007 st->light -= 8-maxbits;
1008 if (st->light < 0) st->light = 0;
1010 if (st->grayscale_p)
1012 set_mask(st->visual->red_mask, &st->rmask, &st->rshift);
1013 set_mask(st->visual->green_mask, &st->gmask, &st->gshift);
1014 set_mask(st->visual->blue_mask, &st->bmask, &st->bshift);
1016 st->draw_transparent = draw_transparent_vanilla;
1019 if (!st->transparent)
1020 init_ripples(st, 0, -SPLASH); /* Start off without any drops */
1025 static unsigned long
1026 ripples_draw (Display *dpy, Window window, void *closure)
1028 struct state *st = (struct state *) closure;
1030 if (st->img_loader) /* still loading */
1032 st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
1033 if (! st->img_loader) { /* just finished */
1034 XWindowAttributes xgwa;
1035 XGetWindowAttributes(st->dpy, st->window, &xgwa);
1036 st->start_time = time ((time_t *) 0);
1037 st->orig_map = XGetImage (st->dpy, st->window, 0, 0,
1038 xgwa.width, xgwa.height,
1040 init_ripples(st, 0, -SPLASH); /* Start off without any drops */
1045 if (!st->img_loader &&
1046 st->start_time + st->duration < time ((time_t *) 0)) {
1047 XWindowAttributes xgwa;
1048 XGetWindowAttributes(st->dpy, st->window, &xgwa);
1049 st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
1051 st->start_time = time ((time_t *) 0);
1055 if (st->rate > 0 && (st->iterations % st->rate) == 0)
1056 add_drop(st, ripple_blob, -SPLASH);
1058 add_drop(st, ripple_stir, -SPLASH);
1059 if (st->box > 0 && (random() % st->box) == 0)
1060 add_drop(st, ripple_box, -SPLASH);
1072 ripples_reshape (Display *dpy, Window window, void *closure,
1073 unsigned int w, unsigned int h)
1078 ripples_event (Display *dpy, Window window, void *closure, XEvent *event)
1080 struct state *st = (struct state *) closure;
1081 if (screenhack_event_helper (dpy, window, event))
1090 ripples_free (Display *dpy, Window window, void *closure)
1092 struct state *st = (struct state *) closure;
1096 static const char *ripples_defaults[] =
1098 ".background: black",
1099 ".foreground: #FFAF5F",
1101 "*dontClearRoot: True",
1111 "*grayscale: False",
1112 #ifdef HAVE_XSHM_EXTENSION
1118 "*ignoreRotation: True",
1119 "*rotateImages: True",
1124 static XrmOptionDescRec ripples_options[] =
1126 { "-colors", ".colors", XrmoptionSepArg, 0},
1127 { "-colours", ".colors", XrmoptionSepArg, 0},
1128 {"-delay", ".delay", XrmoptionSepArg, 0},
1129 {"-duration", ".duration", XrmoptionSepArg, 0 },
1130 {"-rate", ".rate", XrmoptionSepArg, 0},
1131 {"-box", ".box", XrmoptionSepArg, 0},
1132 {"-water", ".water", XrmoptionNoArg, "True"},
1133 {"-oily", ".oily", XrmoptionNoArg, "True"},
1134 {"-stir", ".stir", XrmoptionNoArg, "True"},
1135 {"-fluidity", ".fluidity", XrmoptionSepArg, 0},
1136 {"-light", ".light", XrmoptionSepArg, 0},
1137 {"-grayscale", ".grayscale", XrmoptionNoArg, "True"},
1138 {"-shm", ".useSHM", XrmoptionNoArg, "True"},
1139 {"-no-shm", ".useSHM", XrmoptionNoArg, "False"},
1144 XSCREENSAVER_MODULE ("Ripples", ripples)