diff options
author | Simon Rettberg | 2018-10-16 10:08:48 +0200 |
---|---|---|
committer | Simon Rettberg | 2018-10-16 10:08:48 +0200 |
commit | d3a98cf6cbc3bd0b9efc570f58e8812c03931c18 (patch) | |
tree | cbddf8e50f35a9c6e878a5bfe3c6d625d99e12ba /hacks/ripples.c | |
download | xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.tar.gz xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.tar.xz xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.zip |
Original 5.40
Diffstat (limited to 'hacks/ripples.c')
-rw-r--r-- | hacks/ripples.c | 1118 |
1 files changed, 1118 insertions, 0 deletions
diff --git a/hacks/ripples.c b/hacks/ripples.c new file mode 100644 index 0000000..447a455 --- /dev/null +++ b/hacks/ripples.c @@ -0,0 +1,1118 @@ +/* ripples, Copyright (c) 1999 Ian McConnell <ian@emit.demon.co.uk> + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +/* + * "Water" ripples that can cross and interfere with each other. + * + * I can't remember where I got this idea from, but it's been around for a + * while in various demos. Some inspiration from + * water.txt by Tom Hammersley,tomh@globalnet.co.uk + * + * Options + * -delay usleep every iteration + * -rate Add one drop every "rate" iterations + * -box Add big square splash every "box" iters (not very good) + * -water Ripples on a grabbed background image + * -foreground Interpolate ripples between these two colors + * -background + * -oily Psychedelic colours like man + * -stir Add a regular pattern of drops + * -fluidity Between 0 and 16. 16 = big drops + * -light Hack to add lighting effect + * + * Code mainly hacked from xflame and decayscreen. + */ + +/* Version history: + * 13 Oct 1999: Initial hack + * 30 Oct 1999: Speeded up graphics with dirty buffer. Returned to using + * putpixel for greater portability + * Added a variety of methods for splashing screen. + * 31 Oct 1999: Added in lighting hack + * 13 Nov 1999: Speed up tweaks + * Adjust "light" for different bits per colour (-water only) + * 09 Oct 2016: Updated for new xshm.c + * + */ + +#include <math.h> +#include "screenhack.h" + +typedef enum {ripple_drop, ripple_blob, ripple_box, ripple_stir} ripple_mode; + +#include "xshm.h" + +#define TABLE 256 + +struct state { + Display *dpy; + Window window; + GC gc; + Visual *visual; + + XImage *orig_map, *buffer_map; + int ctab[256]; + Colormap colormap; + Screen *screen; + int ncolors; + int light; + + int width, height; /* ripple size */ + int bigwidth, bigheight; /* screen size */ + + Bool transparent; + short *bufferA, *bufferB, *temp; + char *dirty_buffer; + + double cos_tab[TABLE]; + + Bool grayscale_p; + + unsigned long rmask; /* This builds on the warp effect by adding */ + unsigned long gmask; /* in a lighting effect: brighten pixels by an */ + unsigned long bmask; /* amount corresponding to the vertical gradient */ + + int rshift; + int gshift; + int bshift; + + double stir_ang; + + int draw_toggle; + int draw_count; + + int iterations, delay, rate, box, oily, stir, fluidity; + int duration; + time_t start_time; + + void (*draw_transparent) (struct state *st, short *src); + + async_load_state *img_loader; + + XShmSegmentInfo shm_info; +}; + + +/* Distribution of drops: many little ones and a few big ones. */ +static const double drop_dist[] = + {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.2, 0.6}; + + +/* How hard to hit the water */ +#define SPLASH 512 +#undef MIN +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#undef MAX +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#undef DIRTY +#define DIRTY 3 /* dirty >= 2, 1 = restore original pixel, 0 = leave alone */ + +/* From fortune(6) */ +/* -- really weird C code to count the number of bits in a word */ +#define BITCOUNT(x) (((BX_(x)+(BX_(x)>>4)) & 0x0F0F0F0F) % 255) +#define BX_(x) ((x) - (((x)>>1)&0x77777777) \ + - (((x)>>2)&0x33333333) \ + - (((x)>>3)&0x11111111)) + + +static unsigned long grayscale(struct state *st, unsigned long color); + +/* ------------------------------------------- */ + + +static int +map_color(struct state *st, int grey) +{ + /* Clip it */ + grey = st->ncolors * abs(grey) / (SPLASH/4); + if (grey > st->ncolors) + grey = st->ncolors; + + /* Display it */ + return st->ctab[grey]; +} + + +static void +draw_ripple(struct state *st, short *src) +{ + int across, down; + char *dirty = st->dirty_buffer; + + for (down = 0; down < st->height - 1; down++, src += 1, dirty += 1) + for (across = 0; across < st->width - 1; across++, src++, dirty++) { + int v1, v2, v3, v4; + v1 = (int)*src; + v2 = (int)*(src + 1); + v3 = (int)*(src + st->width); + v4 = (int)*(src + st->width + 1); + if ((v1 == 0 && v2 == 0 && v3 == 0 && v4 == 0)) { + if (*dirty > 0) + (*dirty)--; + } else + *dirty = DIRTY; + + if (*dirty > 0) { + int dx; + if (st->light > 0) { + dx = ((v3 - v1) + (v4 - v2)) << st->light; /* light from top */ + } else + dx = 0; + XPutPixel(st->buffer_map,(across<<1), (down<<1), map_color(st, dx + v1)); + XPutPixel(st->buffer_map,(across<<1)+1,(down<<1), map_color(st, dx + ((v1 + v2) >> 1))); + XPutPixel(st->buffer_map,(across<<1), (down<<1)+1,map_color(st, dx + ((v1 + v3) >> 1))); + XPutPixel(st->buffer_map,(across<<1)+1,(down<<1)+1,map_color(st, dx + ((v1 + v4) >> 1))); + } + } +} + + +/* ------------------------------------------- */ + + +/* Uses the horizontal gradient as an offset to create a warp effect */ +static void +draw_transparent_vanilla(struct state *st, short *src) +{ + int across, down, pixel; + char *dirty = st->dirty_buffer; + + pixel = 0; + for (down = 0; down < st->height - 2; down++, pixel += 2) + for (across = 0; across < st->width-2; across++, pixel++) { + int gradx, grady, gradx1, grady1; + int x0, x1, x2, y1, y2; + + x0 = src[pixel]; + x1 = src[pixel + 1]; + x2 = src[pixel + 2]; + y1 = src[pixel + st->width]; + y2 = src[pixel + 2*st->width]; + + gradx = (x1 - x0); + grady = (y1 - x0); + gradx1= (x2 - x1); + grady1= (y2 - y1); + gradx1 = 1 + (gradx + gradx1) / 2; + grady1 = 1 + (grady + grady1) / 2; + + if ((2*across+MIN(gradx,gradx1) < 0) || + (2*across+MAX(gradx,gradx1) >= st->bigwidth)) { + gradx = 0; + gradx1= 1; + } + if ((2*down+MIN(grady,grady1) < 0) || + (2*down+MAX(grady,grady1) >= st->bigheight)) { + grady = 0; + grady1 = 1; + } + + if ((gradx == 0 && gradx1 == 1 && grady == 0 && grady1 == 1)) { + if (dirty[pixel] > 0) + dirty[pixel]--; + } else + dirty[pixel] = DIRTY; + + if (dirty[pixel] > 0) { + XPutPixel(st->buffer_map, (across<<1), (down<<1), + grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady))); + XPutPixel(st->buffer_map, (across<<1)+1,(down<<1), + grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady))); + XPutPixel(st->buffer_map, (across<<1), (down<<1)+1, + grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady1))); + XPutPixel(st->buffer_map, (across<<1)+1,(down<<1)+1, + grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady1))); + } + } +} + + +/* ------------------------------------------- */ + + +static void +set_mask(unsigned long *mask, int *shift) +{ + unsigned long color = *mask; + *shift = 0; + while (color != 0 && (color & 1) == 0) { + (*shift)++; + color >>= 1; + } + *mask = color; +} + + +static unsigned long +cadd(unsigned long color, int dx, unsigned long mask, int shift) +{ + int x; + color >>= shift; + x = (color & mask); + x += dx; + if (x < 0) x = 0; + else if (x > (int)mask) x = mask; + color = x; + return color << shift; +} + + +static unsigned long +bright(struct state *st, int dx, unsigned long color) +{ + return (cadd(color, dx, st->rmask, st->rshift) | + cadd(color, dx, st->gmask, st->gshift) | + cadd(color, dx, st->bmask, st->bshift)); +} + + +static unsigned long +grayscale(struct state *st, unsigned long color) +{ + int red; + int green; + int blue; + int total; + int gray_r; + int gray_g; + int gray_b; + + if (!st->grayscale_p) + return color; + if (!st->transparent) + return color; + if ((st->rmask == 0) || (st->gmask == 0) || (st->bmask == 0)) + return color; + + red = ((color >> st->rshift) & st->rmask); + green = ((color >> st->gshift) & st->gmask); + blue = ((color >> st->bshift) & st->bmask); + total = red * st->gmask * st->bmask + green * st->rmask * st->bmask + blue * st->rmask * st->gmask; + + gray_r = total / (3 * st->gmask * st->bmask); + if (gray_r < 0) + gray_r = 0; + if (gray_r > st->rmask) + gray_r = st->rmask; + + gray_g = total / (3 * st->rmask * st->bmask); + if (gray_g < 0) + gray_g = 0; + if (gray_g > st->gmask) + gray_g = st->gmask; + + gray_b = total / (3 * st->rmask * st->gmask); + if (gray_b < 0) + gray_b = 0; + if (gray_b > st->bmask) + gray_b = st->bmask; + + return ((unsigned long) + ((gray_r << st->rshift) | (gray_g << st->gshift) | (gray_b << st->bshift))); +} + + +static void +draw_transparent_light(struct state *st, short *src) +{ + int across, down, pixel; + char *dirty = st->dirty_buffer; + + pixel = 0; + for (down = 0; down < st->height - 2; down++, pixel += 2) + for (across = 0; across < st->width-2; across++, pixel++) { + int gradx, grady, gradx1, grady1; + int x0, x1, x2, y1, y2; + + x0 = src[pixel]; + x1 = src[pixel + 1]; + x2 = src[pixel + 2]; + y1 = src[pixel + st->width]; + y2 = src[pixel + 2*st->width]; + + gradx = (x1 - x0); + grady = (y1 - x0); + gradx1= (x2 - x1); + grady1= (y2 - y1); + gradx1 = 1 + (gradx + gradx1) / 2; + grady1 = 1 + (grady + grady1) / 2; + + if ((2*across+MIN(gradx,gradx1) < 0) || + (2*across+MAX(gradx,gradx1) >= st->bigwidth)) { + gradx = 0; + gradx1= 1; + } + if ((2*down+MIN(grady,grady1) < 0) || + (2*down+MAX(grady,grady1) >= st->bigheight)) { + grady = 0; + grady1 = 1; + } + + if ((gradx == 0 && gradx1 == 1 && grady == 0 && grady1 == 1)) { + if (dirty[pixel] > 0) + dirty[pixel]--; + } else + dirty[pixel] = DIRTY; + + if (dirty[pixel] > 0) { + int dx; + + /* light from top */ + if (4-st->light >= 0) + dx = (grady + (src[pixel+st->width+1]-x1)) >> (4-st->light); + else + dx = (grady + (src[pixel+st->width+1]-x1)) << (st->light-4); + + if (dx != 0) { + XPutPixel(st->buffer_map, (across<<1), (down<<1), + bright(st, dx, grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady)))); + XPutPixel(st->buffer_map, (across<<1)+1,(down<<1), + bright(st, dx, grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady)))); + XPutPixel(st->buffer_map, (across<<1), (down<<1)+1, + bright(st, dx, grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady1)))); + XPutPixel(st->buffer_map, (across<<1)+1,(down<<1)+1, + bright(st, dx, grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady1)))); + } else { + /* Could use XCopyArea, but XPutPixel is faster */ + XPutPixel(st->buffer_map, (across<<1), (down<<1), + grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady))); + XPutPixel(st->buffer_map, (across<<1)+1,(down<<1), + grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady))); + XPutPixel(st->buffer_map, (across<<1), (down<<1)+1, + grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady1))); + XPutPixel(st->buffer_map, (across<<1)+1,(down<<1)+1, + grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady1))); + } + } + } +} + + +/* ------------------------------------------- */ + + +#if 0 +/* Doesn't go any faster and doesn't work at all colour depths */ +static void +draw_transparent16l(short *src) +{ + int across, down, bigpix, pixel; + char *dirty = st->dirty_buffer; + unsigned short *buffer, *orig; + + buffer = (unsigned short *) st->buffer_map->data; + orig = (unsigned short *) st->orig_map->data; + + for (pixel = bigpix = down = 0; + down < st->height - 2; + down++, pixel += 2, bigpix += st->bigwidth+4) + for (across = 0; across < st->width-2; across++, pixel++, bigpix+=2) { + int gradx, grady, gradx1, grady1; + int x0, x1, x2, y1, y2; + + x0 = src[pixel]; + x1 = src[pixel + 1]; + x2 = src[pixel + 2]; + y1 = src[pixel + st->width]; + y2 = src[pixel + 2*st->width]; + + gradx = (x1 - x0); + grady = (y1 - x0); + gradx1= (x2 - x1); + grady1= (y2 - y1); + gradx1 = 1 + (gradx + gradx1) / 2; + grady1 = 1 + (grady + grady1) / 2; + + if ((2*across+MIN(gradx,gradx1) < 0) || + (2*across+MAX(gradx,gradx1) >= st->bigwidth)) { + gradx = 0; + gradx1= 1; + } + if ((2*down+MIN(grady,grady1) < 0) || + (2*down+MAX(grady,grady1) >= st->bigheight)) { + grady = 0; + grady1 = 1; + } + + if ((gradx == 0 && gradx1 == 1 && grady == 0 && grady1 == 1)) { + if (dirty[pixel] > 0) + dirty[pixel]--; + } else + dirty[pixel] = DIRTY; + + if (dirty[pixel] > 0) { + unsigned short *dest = buffer + bigpix; + unsigned short *image = orig + bigpix; + int dx; + + /* light from top */ + if (4-st->light >= 0) + dx = (grady + (src[pixel+st->width+1]-x1)) >> (4-st->light); + else + dx = (grady + (src[pixel+st->width+1]-x1)) << (st->light-4); + + grady *= st->bigwidth; + grady1*= st->bigwidth; + + if (dx != 0) { + *dest++ = dobright(dx, *(image + gradx + grady)); + *dest = dobright(dx, *(image + gradx1 + grady)); + dest += st->bigwidth - 1; + *dest++ = dobright(dx, *(image + gradx + grady1)); + *dest = dobright(dx, *(image + gradx1 + grady1)); + } else { + *dest++ = *(image + gradx + grady); + *dest = *(image + gradx1 + grady); + dest += st->bigwidth - 1; + *dest++ = *(image + gradx + grady1); + *dest = *(image + gradx1 + grady1); + } + } + } +} +#endif + + +/* ------------------------------------------- */ + + +static void +setup_X(struct state *st) +{ + XWindowAttributes xgwa; + int depth; + + XGetWindowAttributes(st->dpy, st->window, &xgwa); + depth = xgwa.depth; + st->colormap = xgwa.colormap; + st->screen = xgwa.screen; + st->bigwidth = xgwa.width; + st->bigheight = xgwa.height; + st->visual = xgwa.visual; + + + /* This causes buffer_map to be 1 pixel taller and wider than orig_map, + which can cause the two XImages to have different bytes-per-line, + which causes stair-stepping. So this better not be necessary... + -jwz, 23-Nov-01 + */ +#if 0 /* I'm not entirely sure if I need this */ + if (st->bigwidth % 2) + st->bigwidth++; + if (st->bigheight % 2) + st->bigheight++; +#endif + + + st->width = st->bigwidth / 2; + st->height = st->bigheight / 2; + + if (st->transparent) { + XGCValues gcv; + long gcflags; + + gcv.function = GXcopy; + gcv.subwindow_mode = IncludeInferiors; + + gcflags = GCFunction; + if (use_subwindow_mode_p(xgwa.screen, st->window)) /* see grabscreen.c */ + gcflags |= GCSubwindowMode; + + st->gc = XCreateGC(st->dpy, st->window, gcflags, &gcv); + + st->img_loader = load_image_async_simple (0, xgwa.screen, st->window, + st->window, 0, 0); + st->start_time = time ((time_t *) 0); + } else { + XGCValues gcv; + + st->gc = XCreateGC(st->dpy, st->window, 0, &gcv); + st->orig_map = 0; + } + + if (!st->gc) { + fprintf(stderr, "XCreateGC failed\n"); + exit(1); + } + + st->buffer_map = create_xshm_image(st->dpy, xgwa.visual, depth, + ZPixmap, &st->shm_info, st->bigwidth, st->bigheight); +} + + +static void +DisplayImage(struct state *st) +{ + put_xshm_image(st->dpy, st->window, st->gc, st->buffer_map, 0, 0, 0, 0, + st->bigwidth, st->bigheight, &st->shm_info); +} + + +/* ------------------------------------------- */ + + +static int +cinterp(double a, int bg, int fg) +{ + int result; + result = (int)((1-a) * bg + a * fg + 0.5); + if (result < 0) result = 0; + if (result > 255) result = 255; + return result; +} + + +/* Interpolate the ripple colours between the background colour and + foreground colour */ +static void +init_linear_colors(struct state *st) +{ + int i, j, red, green, blue, bred, bgreen, bblue; + XColor fg, bg; + + if (st->ncolors < 2 || mono_p) + st->ncolors = 2; + if (st->ncolors <= 2) + mono_p = True; + + /* Make it possible to set the color of the ripples, + Based on work by Raymond Medeiros <ray@stommel.marine.usf.edu> and jwz. + */ + fg.pixel = get_pixel_resource(st->dpy, st->colormap, "foreground", "Foreground"); + XQueryColor(st->dpy, st->colormap, &fg); + red = (fg.red >> 8); + green = (fg.green >> 8); + blue = (fg.blue >> 8); + + bg.pixel = get_pixel_resource(st->dpy, st->colormap, "background", "Background"); + XQueryColor(st->dpy, st->colormap, &bg); + bred = (bg.red >> 8); + bgreen = (bg.green >> 8); + bblue = (bg.blue >> 8); + + j = 0; + for (i = 0; i < st->ncolors+1; i++) { + XColor xcl; + double a = (double)i / st->ncolors; + int r = cinterp(a, bred, red); + int g = cinterp(a, bgreen, green); + int b = cinterp(a, bblue, blue); + + xcl.red = (unsigned short) ((r << 8) | r); + xcl.green = (unsigned short) ((g << 8) | g); + xcl.blue = (unsigned short) ((b << 8) | b); + xcl.flags = DoRed | DoGreen | DoBlue; + + XAllocColor(st->dpy, st->colormap, &xcl); + + st->ctab[j++] = (int) xcl.pixel; + } +} + + +static void +init_oily_colors(struct state *st) +{ + XColor *colors = NULL; + + if (st->ncolors < 2 || mono_p) + st->ncolors = 2; + if (st->ncolors <= 2) + mono_p = True; + colors = 0; + + if (!mono_p) { + colors = (XColor *)malloc(sizeof(*colors) * (st->ncolors+1)); + make_smooth_colormap(st->screen, st->visual, st->colormap, + colors, &st->ncolors, + True, /* allocate */ + False, /* not writable */ + True); /* verbose (complain about failure) */ + if (st->ncolors <= 2) { + if (colors) + free (colors); + colors = 0; + mono_p = True; + } + } + if (!mono_p) { + int i, j = 0; + for (i = 0; i < st->ncolors+1; i++) { + XAllocColor(st->dpy, st->colormap, colors+i); + st->ctab[j++] = (int) colors[i].pixel; + } + free (colors); + } else { + st->ncolors = 2; + st->ctab[1] = get_pixel_resource(st->dpy, st->colormap, "foreground", "Foreground"); + st->ctab[0] = get_pixel_resource(st->dpy, st->colormap, "background", "Background"); + } +} + + +/* ------------------------------------------- */ + + +static void +init_cos_tab(struct state *st) +{ + int i; + for (i = 0; i < TABLE; i++) + st->cos_tab[i] = cos(i * M_PI/2 / TABLE); +} + + +/* Shape of drop to add */ +static double +sinc(struct state *st, double x) +{ +#if 1 + /* cosine hump */ + int i; + i = (int)(x * TABLE + 0.5); + if (i >= TABLE) i = (TABLE-1) - (i-(TABLE-1)); + if (i < 0) return 0.; + return st->cos_tab[i]; +#elif 0 + return cos(x * M_PI/2); +#else + if (fabs(x) < 0.1) + return 1 - x*x; + else + return sin(x) / x; +#endif +} + + +static void +add_circle_drop(struct state *st, int x, int y, int radius, int dheight) +{ + int r, r2, cx, cy; + short *buf = (random()&1) ? st->bufferA : st->bufferB; + + r2 = radius * radius; + + + for (cy = -radius; cy <= radius; cy++) + for (cx = -radius; cx <= radius; cx++) { + int xx = x+cx; + int yy = y+cy; + if (xx < 0 || yy < 0 || xx >= st->width || yy >= st->height) {break;} + r = cx*cx + cy*cy; + if (r > r2) break; + buf[xx + yy*st->width] = + (short)(dheight * sinc(st, (radius > 0) ? sqrt(r)/radius : 0)); + } +} + + +static void +add_drop(struct state *st, ripple_mode mode, int drop) +{ + int newx, newy, dheight; + int radius = MIN(st->width, st->height) / 50; + /* Don't put drops too near the edge of the screen or they get stuck */ + int border = 8; + + switch (mode) { + default: + case ripple_drop: { + int x; + + dheight = 1 + (random() % drop); + newx = border + (random() % (st->width - 2*border)); + newy = border + (random() % (st->height - 2*border)); + x = newy * st->width + newx; + st->bufferA[x + 1] = st->bufferA[x] = st->bufferA[x + st->width] = st->bufferA[x + st->width + 1] = + st->bufferB[x + 1] = st->bufferB[x] = st->bufferB[x + st->width] = st->bufferB[x + st->width + 1] = + dheight; + } + break; + case ripple_blob: { + double power; + + int tmp_i, tmp_j; + power = drop_dist[random() % (sizeof(drop_dist)/sizeof(drop_dist[0]))]; /* clumsy */ + dheight = (int)(drop * (power + 0.01)); + tmp_i = (int)(st->width - 2*border - 2*radius*power); + tmp_j = (int)(st->height - 2*border - 2*radius*power); + newx = radius + border + ((tmp_i > 0) ? random() % tmp_i : 0); + newy = radius + border + ((tmp_j > 0) ? random() % tmp_j : 0); + add_circle_drop(st, newx, newy, radius, dheight); + } + break; + /* Adding too many boxes too quickly (-box 1) doesn't give the waves time + to disperse and the waves build up (and overflow) */ + case ripple_box: { + int x; + int cx, cy; + short *buf = (random()&1) ? st->bufferA : st->bufferB; + int tmp_i, tmp_j; + + radius = (1 + (random() % 5)) * (1 + (random() % 5)); + dheight = drop / 128; + if (random() & 1) dheight = -dheight; + tmp_i = st->width - 2*border - 2*radius; + tmp_j = st->height - 2*border - 2*radius; + newx = radius + border + ((tmp_i > 0) ? random() % tmp_i : 0); + newy = radius + border + ((tmp_j > 0) ? random() % tmp_j : 0); + x = newy * st->width + newx; + for (cy = -radius; cy <= radius; cy++) + for (cx = -radius; cx <= radius; cx++) + buf[x + cx + cy*st->width] = (short)(dheight); + } + break; + case ripple_stir: { + border += radius; + newx = border + (int)((st->width-2*border) * (1+cos(3*st->stir_ang)) / 2); + newy = border + (int)((st->height-2*border) * (1+sin(2*st->stir_ang)) / 2); + add_circle_drop(st, newx, newy, radius, drop / 10); + st->stir_ang += 0.02; + if (st->stir_ang > 12*M_PI) st->stir_ang = 0; + } + break; + } +} + + +static void +init_ripples(struct state *st, int ndrops, int splash) +{ + int i; + + st->bufferA = (short *)calloc(st->width * st->height, sizeof(*st->bufferA)); + st->bufferB = (short *)calloc(st->width * st->height, sizeof(*st->bufferB)); + st->temp = (short *)calloc(st->width * st->height, sizeof(*st->temp)); + + st->dirty_buffer = (char *)calloc(st->width * st->height, sizeof(*st->dirty_buffer)); + + for (i = 0; i < ndrops; i++) + add_drop(st, ripple_blob, splash); + + if (st->transparent) { + if (st->grayscale_p) + { + int across, down; + for (down = 0; down < st->bigheight; down++) + for (across = 0; across < st->bigwidth; across++) + XPutPixel(st->buffer_map, across, down, + grayscale(st, XGetPixel(st->orig_map, across, down))); + } + else + { + /* There's got to be a better way of doing this XCopyArea? */ + memcpy(st->buffer_map->data, st->orig_map->data, + st->bigheight * st->buffer_map->bytes_per_line); + } + } else { + int across, down, color; + + color = map_color(st, 0); /* background colour */ + for (down = 0; down < st->bigheight; down++) + for (across = 0; across < st->bigwidth; across++) + XPutPixel(st->buffer_map,across, down, color); + } + + DisplayImage(st); +} + + +/* + Explanation from hq_water.zip (Arturo R Montesinos (ARM) arami@fi.upm.es) + + Differential equation is: u = a ( u + u ) + tt xx yy + + Where a = tension * gravity / surface_density. + + Approximating second derivatives by central differences: + + [ u(t+1)-2u(t)+u(t-1) ] / dt = a [ u(x+1)+u(x-1)+u(y+1)+u(y-1)-4u ] / h + + where dt = time step squared, h = dx*dy = mesh resolution squared. + + From where u(t+1) may be calculated as: + + dt | 1 | dt + u(t+1) = a -- | 1 0 1 |u - u(t-1) + (2-4a --)u + h | 1 | h + + When a*dt/h = 1/2 last term vanishes, giving: + + 1 | 1 | + u(t+1) = - | 1 0 1 |u - u(t-1) + 2 | 1 | + + (note that u(t-1,x,y) is only used to calculate u(t+1,x,y) so + we can use the same array for both t-1 and t+1, needing only + two arrays, U[0] and U[1]) + + Dampening is simulated by subtracting 1/2^n of result. + n=4 gives best-looking result + n<4 (eg 2 or 3) thicker consistency, waves die out immediately + n>4 (eg 8 or 12) more fluid, waves die out slowly + */ + +static void +ripple(struct state *st) +{ + int across, down, pixel; + short *src, *dest; + + if (st->draw_toggle == 0) { + src = st->bufferA; + dest = st->bufferB; + st->draw_toggle = 1; + } else { + src = st->bufferB; + dest = st->bufferA; + st->draw_toggle = 0; + } + + switch (st->draw_count) { + case 0: case 1: + pixel = 1 * st->width + 1; + for (down = 1; down < st->height - 1; down++, pixel += 2 * 1) + for (across = 1; across < st->width - 1; across++, pixel++) { + st->temp[pixel] = + (((src[pixel - 1] + src[pixel + 1] + + src[pixel - st->width] + src[pixel + st->width]) / 2)) - dest[pixel]; + } + + /* Smooth the output */ + pixel = 1 * st->width + 1; + for (down = 1; down < st->height - 1; down++, pixel += 2 * 1) + for (across = 1; across < st->width - 1; across++, pixel++) { + if (st->temp[pixel] != 0) { /* Close enough for government work */ + int damp = + (st->temp[pixel - 1] + st->temp[pixel + 1] + + st->temp[pixel - st->width] + st->temp[pixel + st->width] + + st->temp[pixel - st->width - 1] + st->temp[pixel - st->width + 1] + + st->temp[pixel + st->width - 1] + st->temp[pixel + st->width + 1] + + st->temp[pixel]) / 9; + dest[pixel] = damp - (damp >> st->fluidity); + } else + dest[pixel] = 0; + } + break; + case 2: case 3: + pixel = 1 * st->width + 1; + for (down = 1; down < st->height - 1; down++, pixel += 2 * 1) + for (across = 1; across < st->width - 1; across++, pixel++) { + int damp = + (((src[pixel - 1] + src[pixel + 1] + + src[pixel - st->width] + src[pixel + st->width]) / 2)) - dest[pixel]; + dest[pixel] = damp - (damp >> st->fluidity); + } + break; + } + if (++st->draw_count > 3) st->draw_count = 0; + + if (st->transparent) + st->draw_transparent(st, dest); + else + draw_ripple(st, dest); +} + + +/* ------------------------------------------- */ + +static void * +ripples_init (Display *disp, Window win) +{ + struct state *st = (struct state *) calloc (1, sizeof(*st)); + st->dpy = disp; + st->window = win; + st->iterations = 0; + st->delay = get_integer_resource(disp, "delay", "Integer"); + st->duration = get_integer_resource (st->dpy, "duration", "Seconds"); + st->rate = get_integer_resource(disp, "rate", "Integer"); + st->box = get_integer_resource(disp, "box", "Integer"); + st->oily = get_boolean_resource(disp, "oily", "Boolean"); + st->stir = get_boolean_resource(disp, "stir", "Boolean"); + st->fluidity = get_integer_resource(disp, "fluidity", "Integer"); + st->transparent = get_boolean_resource(disp, "water", "Boolean"); + st->grayscale_p = get_boolean_resource(disp, "grayscale", "Boolean"); + st->light = get_integer_resource(disp, "light", "Integer"); + + if (st->delay < 0) st->delay = 0; + if (st->duration < 1) st->duration = 1; + if (st->fluidity <= 1) st->fluidity = 1; + if (st->fluidity > 16) st->fluidity = 16; /* 16 = sizeof(short) */ + if (st->light < 0) st->light = 0; + + init_cos_tab(st); + setup_X(st); + + st->ncolors = get_integer_resource (disp, "colors", "Colors"); + if (0 == st->ncolors) /* English spelling? */ + st->ncolors = get_integer_resource (disp, "colours", "Colors"); + + if (st->ncolors > sizeof(st->ctab)/sizeof(*st->ctab)) + st->ncolors = sizeof(st->ctab)/sizeof(*st->ctab); + + if (st->oily) + init_oily_colors(st); + else + init_linear_colors(st); + + if (st->transparent && st->light > 0) { + int maxbits; + st->draw_transparent = draw_transparent_light; + visual_rgb_masks (st->screen, st->visual, + &st->rmask, &st->gmask, &st->bmask); + set_mask(&st->rmask, &st->rshift); + set_mask(&st->gmask, &st->gshift); + set_mask(&st->bmask, &st->bshift); + if (st->rmask == 0) st->draw_transparent = draw_transparent_vanilla; + + /* Adjust the shift value "light" when we don't have 8 bits per colour */ + maxbits = MIN(MIN(BITCOUNT(st->rmask), BITCOUNT(st->gmask)), BITCOUNT(st->bmask)); + st->light -= 8-maxbits; + if (st->light < 0) st->light = 0; + } else { + if (st->grayscale_p) + { + visual_rgb_masks (st->screen, st->visual, + &st->rmask, &st->gmask, &st->bmask); + set_mask(&st->rmask, &st->rshift); + set_mask(&st->gmask, &st->gshift); + set_mask(&st->bmask, &st->bshift); + } + st->draw_transparent = draw_transparent_vanilla; + } + + if (!st->transparent) + init_ripples(st, 0, -SPLASH); /* Start off without any drops */ + + return st; +} + +static unsigned long +ripples_draw (Display *dpy, Window window, void *closure) +{ + struct state *st = (struct state *) closure; + + if (st->img_loader) /* still loading */ + { + st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0); + if (! st->img_loader) { /* just finished */ + XWindowAttributes xgwa; + XGetWindowAttributes(st->dpy, st->window, &xgwa); + st->start_time = time ((time_t *) 0); + st->orig_map = XGetImage (st->dpy, st->window, 0, 0, + xgwa.width, xgwa.height, + ~0L, ZPixmap); + init_ripples(st, 0, -SPLASH); /* Start off without any drops */ + } + return st->delay; + } + + if (!st->img_loader && + st->start_time + st->duration < time ((time_t *) 0)) { + XWindowAttributes xgwa; + XGetWindowAttributes(st->dpy, st->window, &xgwa); + st->img_loader = load_image_async_simple (0, xgwa.screen, st->window, + st->window, 0, 0); + st->start_time = time ((time_t *) 0); + return st->delay; + } + + if (st->rate > 0 && (st->iterations % st->rate) == 0) + add_drop(st, ripple_blob, -SPLASH); + if (st->stir) + add_drop(st, ripple_stir, -SPLASH); + if (st->box > 0 && (random() % st->box) == 0) + add_drop(st, ripple_box, -SPLASH); + + ripple(st); + DisplayImage(st); + + st->iterations++; + + return st->delay; +} + + +static void +ripples_reshape (Display *dpy, Window window, void *closure, + unsigned int w, unsigned int h) +{ +} + +static Bool +ripples_event (Display *dpy, Window window, void *closure, XEvent *event) +{ + struct state *st = (struct state *) closure; + if (screenhack_event_helper (dpy, window, event)) + { + st->start_time = 0; + return True; + } + return False; +} + +static void +ripples_free (Display *dpy, Window window, void *closure) +{ + struct state *st = (struct state *) closure; + free (st); +} + +static const char *ripples_defaults[] = +{ + ".background: black", + ".foreground: #FFAF5F", + "*colors: 200", + "*dontClearRoot: True", + "*delay: 50000", + "*duration: 120", + "*rate: 5", + "*box: 0", + "*water: True", + "*oily: False", + "*stir: False", + "*fluidity: 6", + "*light: 4", + "*grayscale: False", +#ifdef HAVE_XSHM_EXTENSION + "*useSHM: True", +#else + "*useSHM: False", +#endif +#ifdef HAVE_MOBILE + "*ignoreRotation: True", + "*rotateImages: True", +#endif + 0 +}; + +static XrmOptionDescRec ripples_options[] = +{ + { "-colors", ".colors", XrmoptionSepArg, 0}, + { "-colours", ".colors", XrmoptionSepArg, 0}, + {"-delay", ".delay", XrmoptionSepArg, 0}, + {"-duration", ".duration", XrmoptionSepArg, 0 }, + {"-rate", ".rate", XrmoptionSepArg, 0}, + {"-box", ".box", XrmoptionSepArg, 0}, + {"-water", ".water", XrmoptionNoArg, "True"}, + {"-oily", ".oily", XrmoptionNoArg, "True"}, + {"-stir", ".stir", XrmoptionNoArg, "True"}, + {"-fluidity", ".fluidity", XrmoptionSepArg, 0}, + {"-light", ".light", XrmoptionSepArg, 0}, + {"-grayscale", ".grayscale", XrmoptionNoArg, "True"}, + {"-shm", ".useSHM", XrmoptionNoArg, "True"}, + {"-no-shm", ".useSHM", XrmoptionNoArg, "False"}, + {0, 0, 0, 0} +}; + + +XSCREENSAVER_MODULE ("Ripples", ripples) |