From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / ripples.c
1 /* ripples, Copyright (c) 1999 Ian McConnell <ian@emit.demon.co.uk>
2  *
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
9  * implied warranty.
10  */
11
12 /*
13  * "Water" ripples that can cross and interfere with each other.
14  *
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
18  *
19  * Options
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
25  * -background
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
30  *
31  * Code mainly hacked from xflame and decayscreen.
32  */
33
34 /* Version history:
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)
42  * 09 Oct 2016: Updated for new xshm.c
43  *
44  */
45
46 #include <math.h>
47 #include "screenhack.h"
48
49 typedef enum {ripple_drop, ripple_blob, ripple_box, ripple_stir} ripple_mode;
50
51 #include "xshm.h"
52
53 #define TABLE 256
54
55 struct state {
56   Display *dpy;
57   Window window;
58   GC gc;
59   Visual *visual;
60
61   XImage *orig_map, *buffer_map;
62   int ctab[256];
63   Colormap colormap;
64   Screen *screen;
65   int ncolors;
66   int light;
67
68   int width, height; /* ripple size */
69   int bigwidth, bigheight; /* screen size */
70
71   Bool transparent;
72   short *bufferA, *bufferB, *temp;
73   char *dirty_buffer;
74
75   double cos_tab[TABLE];
76
77   Bool grayscale_p;
78
79   unsigned long rmask;  /* This builds on the warp effect by adding */
80   unsigned long gmask;  /* in a lighting effect: brighten pixels by an */
81   unsigned long bmask;  /* amount corresponding to the vertical gradient */
82
83   int rshift;
84   int gshift;
85   int bshift;
86
87   double stir_ang;
88
89   int draw_toggle;
90   int draw_count;
91
92   int iterations, delay, rate, box, oily, stir, fluidity;
93   int duration;
94   time_t start_time;
95
96   void (*draw_transparent) (struct state *st, short *src);
97
98   async_load_state *img_loader;
99
100   XShmSegmentInfo shm_info;
101 };
102
103
104 /* Distribution of drops: many little ones and a few big ones. */
105 static const double drop_dist[] =
106   {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.2, 0.6};
107
108
109 /* How hard to hit the water */
110 #define SPLASH 512
111 #undef  MIN
112 #define MIN(x, y) ((x) < (y) ? (x) : (y))
113 #undef  MAX
114 #define MAX(x, y) ((x) > (y) ? (x) : (y))
115 #undef  DIRTY
116 #define DIRTY 3 /* dirty >= 2, 1 = restore original pixel, 0 = leave alone */
117
118 /* From fortune(6) */
119 /* -- really weird C code to count the number of bits in a word */
120 #define BITCOUNT(x)     (((BX_(x)+(BX_(x)>>4)) & 0x0F0F0F0F) % 255)
121 #define BX_(x)          ((x) - (((x)>>1)&0x77777777) \
122                              - (((x)>>2)&0x33333333) \
123                              - (((x)>>3)&0x11111111))
124
125
126 static unsigned long grayscale(struct state *st, unsigned long color);
127
128 /*      -------------------------------------------             */
129
130
131 static int
132 map_color(struct state *st, int grey)
133 {
134   /* Clip it */
135   grey = st->ncolors * abs(grey) / (SPLASH/4);
136   if (grey > st->ncolors)
137     grey = st->ncolors;
138
139   /* Display it */
140   return st->ctab[grey];
141 }
142
143
144 static void
145 draw_ripple(struct state *st, short *src)
146 {
147   int across, down;
148   char *dirty = st->dirty_buffer;
149
150   for (down = 0; down < st->height - 1; down++, src += 1, dirty += 1)
151     for (across = 0; across < st->width - 1; across++, src++, dirty++) {
152       int v1, v2, v3, v4;
153       v1 = (int)*src;
154       v2 = (int)*(src + 1);
155       v3 = (int)*(src + st->width);
156       v4 = (int)*(src + st->width + 1);
157       if ((v1 == 0 && v2 == 0 && v3 == 0 && v4 == 0)) {
158         if (*dirty > 0)
159           (*dirty)--;
160       } else
161         *dirty = DIRTY;
162
163       if (*dirty > 0) {
164         int dx;
165         if (st->light > 0) {
166           dx = ((v3 - v1) + (v4 - v2)) << st->light; /* light from top */
167         } else
168           dx = 0;
169         XPutPixel(st->buffer_map,(across<<1),  (down<<1),  map_color(st, dx + v1));
170         XPutPixel(st->buffer_map,(across<<1)+1,(down<<1),  map_color(st, dx + ((v1 + v2) >> 1)));
171         XPutPixel(st->buffer_map,(across<<1),  (down<<1)+1,map_color(st, dx + ((v1 + v3) >> 1)));
172         XPutPixel(st->buffer_map,(across<<1)+1,(down<<1)+1,map_color(st, dx + ((v1 + v4) >> 1)));
173       }
174     }
175 }
176
177
178 /*      -------------------------------------------             */
179
180
181 /* Uses the horizontal gradient as an offset to create a warp effect  */
182 static void
183 draw_transparent_vanilla(struct state *st, short *src)
184 {
185   int across, down, pixel;
186   char *dirty = st->dirty_buffer;
187
188   pixel = 0;
189   for (down = 0; down < st->height - 2; down++, pixel += 2)
190     for (across = 0; across < st->width-2; across++, pixel++) {
191       int gradx, grady, gradx1, grady1;
192       int x0, x1, x2, y1, y2;
193
194       x0 = src[pixel];
195       x1 = src[pixel + 1];
196       x2 = src[pixel + 2];
197       y1 = src[pixel + st->width];
198       y2 = src[pixel + 2*st->width];
199
200       gradx = (x1 - x0);
201       grady = (y1 - x0);
202       gradx1= (x2 - x1);
203       grady1= (y2 - y1);
204       gradx1 = 1 + (gradx + gradx1) / 2;
205       grady1 = 1 + (grady + grady1) / 2;
206
207       if ((2*across+MIN(gradx,gradx1) < 0) ||
208           (2*across+MAX(gradx,gradx1) >= st->bigwidth)) {
209         gradx = 0;
210         gradx1= 1;
211       }
212       if ((2*down+MIN(grady,grady1) < 0) ||
213           (2*down+MAX(grady,grady1) >= st->bigheight)) {
214         grady = 0;
215         grady1 = 1;
216       }
217
218       if ((gradx == 0 && gradx1 == 1 && grady == 0 && grady1 == 1)) {
219         if (dirty[pixel] > 0)
220           dirty[pixel]--;
221       } else
222         dirty[pixel] = DIRTY;
223
224       if (dirty[pixel] > 0) {
225         XPutPixel(st->buffer_map, (across<<1),  (down<<1),
226                   grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady)));
227         XPutPixel(st->buffer_map, (across<<1)+1,(down<<1),
228                   grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady)));
229         XPutPixel(st->buffer_map, (across<<1),  (down<<1)+1,
230                   grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady1)));
231         XPutPixel(st->buffer_map, (across<<1)+1,(down<<1)+1,
232                   grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady1)));
233       }
234     }
235 }
236
237
238 /*      -------------------------------------------             */
239
240
241 static void
242 set_mask(unsigned long *mask, int *shift)
243 {
244   unsigned long color = *mask;
245   *shift = 0;
246   while (color != 0 && (color & 1) == 0) {
247     (*shift)++;
248     color >>= 1;
249   }
250   *mask = color;
251 }
252
253
254 static unsigned long
255 cadd(unsigned long color, int dx, unsigned long mask, int shift)
256 {
257   int x;
258   color >>= shift;
259   x = (color & mask);
260   x += dx;
261   if (x < 0) x = 0;
262   else if (x > (int)mask) x = mask;
263   color = x;
264   return color << shift;
265 }
266
267
268 static unsigned long
269 bright(struct state *st, int dx, unsigned long color)
270 {
271   return (cadd(color, dx, st->rmask, st->rshift) |
272           cadd(color, dx, st->gmask, st->gshift) |
273           cadd(color, dx, st->bmask, st->bshift));
274 }
275
276
277 static unsigned long
278 grayscale(struct state *st, unsigned long color)
279 {
280   int red;
281   int green;
282   int blue;
283   int total;
284   int gray_r;
285   int gray_g;
286   int gray_b;
287
288   if (!st->grayscale_p)
289     return color;
290   if (!st->transparent)
291     return color;
292   if ((st->rmask == 0) || (st->gmask == 0) || (st->bmask == 0))
293     return color;
294
295   red = ((color >> st->rshift) & st->rmask);
296   green =  ((color >> st->gshift) & st->gmask);
297   blue =  ((color >> st->bshift) & st->bmask);
298   total = red * st->gmask * st->bmask + green * st->rmask * st->bmask + blue * st->rmask * st->gmask;
299
300   gray_r = total / (3 * st->gmask * st->bmask);
301   if (gray_r < 0)
302     gray_r = 0;
303   if (gray_r > st->rmask)
304     gray_r = st->rmask;
305   
306   gray_g = total / (3 * st->rmask * st->bmask);
307   if (gray_g < 0)
308     gray_g = 0;
309   if (gray_g > st->gmask)
310     gray_g = st->gmask;
311
312   gray_b = total / (3 * st->rmask * st->gmask);
313   if (gray_b < 0)
314     gray_b = 0;
315   if (gray_b > st->bmask)
316     gray_b = st->bmask;
317
318   return ((unsigned long)
319           ((gray_r << st->rshift) | (gray_g << st->gshift) | (gray_b << st->bshift)));
320 }
321
322
323 static void
324 draw_transparent_light(struct state *st, short *src)
325 {
326   int across, down, pixel;
327   char *dirty = st->dirty_buffer;
328
329   pixel = 0;
330   for (down = 0; down < st->height - 2; down++, pixel += 2)
331     for (across = 0; across < st->width-2; across++, pixel++) {
332       int gradx, grady, gradx1, grady1;
333       int x0, x1, x2, y1, y2;
334
335       x0 = src[pixel];
336       x1 = src[pixel + 1];
337       x2 = src[pixel + 2];
338       y1 = src[pixel + st->width];
339       y2 = src[pixel + 2*st->width];
340
341       gradx = (x1 - x0);
342       grady = (y1 - x0);
343       gradx1= (x2 - x1);
344       grady1= (y2 - y1);
345       gradx1 = 1 + (gradx + gradx1) / 2;
346       grady1 = 1 + (grady + grady1) / 2;
347
348       if ((2*across+MIN(gradx,gradx1) < 0) ||
349           (2*across+MAX(gradx,gradx1) >= st->bigwidth)) {
350         gradx = 0;
351         gradx1= 1;
352       }
353       if ((2*down+MIN(grady,grady1) < 0) ||
354           (2*down+MAX(grady,grady1) >= st->bigheight)) {
355         grady = 0;
356         grady1 = 1;
357       }
358
359       if ((gradx == 0 && gradx1 == 1 && grady == 0 && grady1 == 1)) {
360         if (dirty[pixel] > 0)
361           dirty[pixel]--;
362       } else
363         dirty[pixel] = DIRTY;
364
365       if (dirty[pixel] > 0) {
366         int dx;
367
368         /* light from top */
369         if (4-st->light >= 0)
370           dx = (grady + (src[pixel+st->width+1]-x1)) >> (4-st->light);
371         else
372           dx = (grady + (src[pixel+st->width+1]-x1)) << (st->light-4);
373
374         if (dx != 0) {
375           XPutPixel(st->buffer_map, (across<<1),  (down<<1),
376                     bright(st, dx, grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady))));
377           XPutPixel(st->buffer_map, (across<<1)+1,(down<<1),
378                     bright(st, dx, grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady))));
379           XPutPixel(st->buffer_map, (across<<1),  (down<<1)+1,
380                     bright(st, dx, grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady1))));
381           XPutPixel(st->buffer_map, (across<<1)+1,(down<<1)+1,
382                     bright(st, dx, grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady1))));
383         } else {
384           /* Could use XCopyArea, but XPutPixel is faster */
385           XPutPixel(st->buffer_map, (across<<1),  (down<<1),
386                     grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady)));
387           XPutPixel(st->buffer_map, (across<<1)+1,(down<<1),
388                     grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady)));
389           XPutPixel(st->buffer_map, (across<<1),  (down<<1)+1,
390                     grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx, (down<<1) + grady1)));
391           XPutPixel(st->buffer_map, (across<<1)+1,(down<<1)+1,
392                     grayscale(st, XGetPixel(st->orig_map, (across<<1) + gradx1,(down<<1) + grady1)));
393         }
394       }
395     }
396 }
397
398
399 /*      -------------------------------------------             */
400
401
402 #if 0
403 /* Doesn't go any faster and doesn't work at all colour depths */
404 static void
405 draw_transparent16l(short *src)
406 {
407   int across, down, bigpix, pixel;
408   char *dirty = st->dirty_buffer;
409   unsigned short *buffer, *orig;
410
411   buffer = (unsigned short *) st->buffer_map->data;
412   orig = (unsigned short *) st->orig_map->data;
413
414   for (pixel = bigpix = down = 0;
415        down < st->height - 2;
416        down++, pixel += 2, bigpix += st->bigwidth+4)
417     for (across = 0; across < st->width-2; across++, pixel++, bigpix+=2) {
418       int gradx, grady, gradx1, grady1;
419       int x0, x1, x2, y1, y2;
420
421       x0 = src[pixel];
422       x1 = src[pixel + 1];
423       x2 = src[pixel + 2];
424       y1 = src[pixel + st->width];
425       y2 = src[pixel + 2*st->width];
426
427       gradx = (x1 - x0);
428       grady = (y1 - x0);
429       gradx1= (x2 - x1);
430       grady1= (y2 - y1);
431       gradx1 = 1 + (gradx + gradx1) / 2;
432       grady1 = 1 + (grady + grady1) / 2;
433
434       if ((2*across+MIN(gradx,gradx1) < 0) ||
435           (2*across+MAX(gradx,gradx1) >= st->bigwidth)) {
436         gradx = 0;
437         gradx1= 1;
438       }
439       if ((2*down+MIN(grady,grady1) < 0) ||
440           (2*down+MAX(grady,grady1) >= st->bigheight)) {
441         grady = 0;
442         grady1 = 1;
443       }
444
445       if ((gradx == 0 && gradx1 == 1 && grady == 0 && grady1 == 1)) {
446         if (dirty[pixel] > 0)
447           dirty[pixel]--;
448       } else
449         dirty[pixel] = DIRTY;
450
451       if (dirty[pixel] > 0) {
452         unsigned short *dest = buffer + bigpix;
453         unsigned short *image = orig + bigpix;
454         int dx;
455
456         /* light from top */
457         if (4-st->light >= 0)
458           dx = (grady + (src[pixel+st->width+1]-x1)) >> (4-st->light);
459         else
460           dx = (grady + (src[pixel+st->width+1]-x1)) << (st->light-4);
461
462         grady *= st->bigwidth;
463         grady1*= st->bigwidth;
464
465         if (dx != 0) {
466           *dest++ = dobright(dx, *(image + gradx  + grady));
467           *dest   = dobright(dx, *(image + gradx1 + grady));
468           dest   += st->bigwidth - 1;
469           *dest++ = dobright(dx, *(image + gradx  + grady1));
470           *dest   = dobright(dx, *(image + gradx1 + grady1));
471         } else {
472           *dest++ = *(image + gradx  + grady);
473           *dest   = *(image + gradx1 + grady);
474           dest   += st->bigwidth - 1;
475           *dest++ = *(image + gradx  + grady1);
476           *dest   = *(image + gradx1 + grady1);
477         }
478       }
479     }
480 }
481 #endif
482
483
484 /*      -------------------------------------------             */
485
486
487 static void
488 setup_X(struct state *st)
489 {
490   XWindowAttributes xgwa;
491   int depth;
492
493   XGetWindowAttributes(st->dpy, st->window, &xgwa);
494   depth = xgwa.depth;
495   st->colormap = xgwa.colormap;
496   st->screen = xgwa.screen;
497   st->bigwidth = xgwa.width;
498   st->bigheight = xgwa.height;
499   st->visual = xgwa.visual;
500
501
502   /* This causes buffer_map to be 1 pixel taller and wider than orig_map,
503      which can cause the two XImages to have different bytes-per-line,
504      which causes stair-stepping.  So this better not be necessary...
505      -jwz, 23-Nov-01
506    */
507 #if 0 /* I'm not entirely sure if I need this */
508   if (st->bigwidth % 2)
509     st->bigwidth++;
510   if (st->bigheight % 2)
511     st->bigheight++;
512 #endif
513
514
515   st->width = st->bigwidth / 2;
516   st->height = st->bigheight / 2;
517
518   if (st->transparent) {
519     XGCValues gcv;
520     long gcflags;
521
522     gcv.function = GXcopy;
523     gcv.subwindow_mode = IncludeInferiors;
524
525     gcflags = GCFunction;
526     if (use_subwindow_mode_p(xgwa.screen, st->window))  /* see grabscreen.c */
527       gcflags |= GCSubwindowMode;
528
529     st->gc = XCreateGC(st->dpy, st->window, gcflags, &gcv);
530
531     st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
532                                               st->window, 0, 0);
533     st->start_time = time ((time_t *) 0);
534   } else {
535     XGCValues gcv;
536
537     st->gc = XCreateGC(st->dpy, st->window, 0, &gcv);
538     st->orig_map = 0;
539   }
540
541   if (!st->gc) {
542     fprintf(stderr, "XCreateGC failed\n");
543     exit(1);
544   }
545
546   st->buffer_map = create_xshm_image(st->dpy, xgwa.visual, depth,
547                                      ZPixmap, &st->shm_info, st->bigwidth, st->bigheight);
548 }
549
550
551 static void
552 DisplayImage(struct state *st)
553 {
554   put_xshm_image(st->dpy, st->window, st->gc, st->buffer_map, 0, 0, 0, 0,
555                  st->bigwidth, st->bigheight, &st->shm_info);
556 }
557
558
559 /*      -------------------------------------------             */
560
561
562 static int
563 cinterp(double a, int bg, int fg)
564 {
565   int result;
566   result = (int)((1-a) * bg + a * fg + 0.5);
567   if (result < 0) result = 0;
568   if (result > 255) result = 255;
569   return result;
570 }
571
572
573 /* Interpolate the ripple colours between the background colour and
574    foreground colour */
575 static void
576 init_linear_colors(struct state *st)
577 {
578   int i, j, red, green, blue, bred, bgreen, bblue;
579   XColor fg, bg;
580
581   if (st->ncolors < 2 || mono_p)
582     st->ncolors = 2;
583   if (st->ncolors <= 2)
584     mono_p = True;
585
586   /* Make it possible to set the color of the ripples,
587      Based on work by Raymond Medeiros <ray@stommel.marine.usf.edu> and jwz.
588    */
589   fg.pixel = get_pixel_resource(st->dpy, st->colormap, "foreground", "Foreground");
590   XQueryColor(st->dpy, st->colormap, &fg);
591   red = (fg.red >> 8);
592   green = (fg.green >> 8);
593   blue = (fg.blue >> 8);
594
595   bg.pixel = get_pixel_resource(st->dpy, st->colormap, "background", "Background");
596   XQueryColor(st->dpy, st->colormap, &bg);
597   bred = (bg.red >> 8);
598   bgreen = (bg.green >> 8);
599   bblue = (bg.blue >> 8);
600
601   j = 0;
602   for (i = 0; i < st->ncolors+1; i++) {
603     XColor xcl;
604     double a = (double)i / st->ncolors;
605     int r = cinterp(a, bred, red);
606     int g = cinterp(a, bgreen, green);
607     int b = cinterp(a, bblue, blue);
608
609     xcl.red = (unsigned short) ((r << 8) | r);
610     xcl.green = (unsigned short) ((g << 8) | g);
611     xcl.blue = (unsigned short) ((b << 8) | b);
612     xcl.flags = DoRed | DoGreen | DoBlue;
613
614     XAllocColor(st->dpy, st->colormap, &xcl);
615
616     st->ctab[j++] = (int) xcl.pixel;
617   }
618 }
619
620
621 static void
622 init_oily_colors(struct state *st)
623 {
624   XColor *colors = NULL;
625
626   if (st->ncolors < 2 || mono_p)
627     st->ncolors = 2;
628   if (st->ncolors <= 2)
629     mono_p = True;
630   colors = 0;
631
632   if (!mono_p) {
633     colors = (XColor *)malloc(sizeof(*colors) * (st->ncolors+1));
634     make_smooth_colormap(st->screen, st->visual, st->colormap,
635                          colors, &st->ncolors,
636                          True, /* allocate */
637                          False, /* not writable */
638                          True); /* verbose (complain about failure) */
639     if (st->ncolors <= 2) {
640       if (colors)
641         free (colors);
642       colors = 0;
643       mono_p = True;
644     }
645   }
646   if (!mono_p) {
647     int i, j = 0;
648     for (i = 0; i < st->ncolors+1; i++) {
649       XAllocColor(st->dpy, st->colormap, colors+i);
650       st->ctab[j++] = (int) colors[i].pixel;
651     }
652     free (colors);
653   } else {
654     st->ncolors = 2;
655     st->ctab[1] = get_pixel_resource(st->dpy, st->colormap, "foreground", "Foreground");
656     st->ctab[0] = get_pixel_resource(st->dpy, st->colormap, "background", "Background");
657   }
658 }
659
660
661 /*      -------------------------------------------             */
662
663
664 static void
665 init_cos_tab(struct state *st)
666 {
667   int i;
668   for (i = 0; i < TABLE; i++)
669     st->cos_tab[i] = cos(i * M_PI/2 / TABLE);
670 }
671
672
673 /* Shape of drop to add */
674 static double
675 sinc(struct state *st, double x)
676 {
677 #if 1
678   /* cosine hump */
679   int i;
680   i = (int)(x * TABLE + 0.5);
681   if (i >= TABLE) i = (TABLE-1) - (i-(TABLE-1));
682   if (i < 0) return 0.;
683   return st->cos_tab[i];
684 #elif 0
685   return cos(x * M_PI/2);
686 #else
687   if (fabs(x) < 0.1)
688     return 1 - x*x;
689   else
690     return sin(x) / x;
691 #endif
692 }
693
694
695 static void
696 add_circle_drop(struct state *st, int x, int y, int radius, int dheight)
697 {
698   int r, r2, cx, cy;
699   short *buf = (random()&1) ? st->bufferA : st->bufferB;
700
701   r2 = radius * radius;
702
703
704   for (cy = -radius; cy <= radius; cy++)
705     for (cx = -radius; cx <= radius; cx++) {
706       int xx = x+cx;
707       int yy = y+cy;
708       if (xx < 0 || yy < 0 || xx >= st->width || yy >= st->height) {break;}
709       r = cx*cx + cy*cy;
710       if (r > r2) break;
711       buf[xx + yy*st->width] =
712         (short)(dheight * sinc(st, (radius > 0) ? sqrt(r)/radius : 0));
713     }
714 }
715
716
717 static void
718 add_drop(struct state *st, ripple_mode mode, int drop)
719 {
720   int newx, newy, dheight;
721   int radius = MIN(st->width, st->height) / 50;
722   /* Don't put drops too near the edge of the screen or they get stuck */
723   int border = 8;
724
725   switch (mode) {
726   default:
727   case ripple_drop: {
728     int x;
729
730     dheight = 1 + (random() % drop);
731     newx = border + (random() % (st->width - 2*border));
732     newy = border + (random() % (st->height - 2*border));
733     x = newy * st->width + newx;
734     st->bufferA[x + 1] = st->bufferA[x] = st->bufferA[x + st->width] = st->bufferA[x + st->width + 1] =
735       st->bufferB[x + 1] = st->bufferB[x] = st->bufferB[x + st->width] = st->bufferB[x + st->width + 1] =
736       dheight;
737   }
738   break;
739   case ripple_blob: {
740     double power;
741
742     int tmp_i, tmp_j;
743     power = drop_dist[random() % (sizeof(drop_dist)/sizeof(drop_dist[0]))]; /* clumsy */
744     dheight = (int)(drop * (power + 0.01));
745     tmp_i = (int)(st->width - 2*border - 2*radius*power);
746     tmp_j = (int)(st->height - 2*border - 2*radius*power);
747     newx = radius + border + ((tmp_i > 0) ? random() % tmp_i : 0);
748     newy = radius + border + ((tmp_j > 0) ? random() % tmp_j : 0);
749     add_circle_drop(st, newx, newy, radius, dheight);
750   }
751   break;
752   /* Adding too many boxes too quickly (-box 1) doesn't give the waves time
753      to disperse and the waves build up (and overflow) */
754   case ripple_box: {
755     int x;
756     int cx, cy;
757     short *buf = (random()&1) ? st->bufferA : st->bufferB;
758     int tmp_i, tmp_j;
759
760     radius = (1 + (random() % 5)) * (1 + (random() % 5));
761     dheight = drop / 128;
762     if (random() & 1) dheight = -dheight;
763     tmp_i = st->width - 2*border - 2*radius;
764     tmp_j = st->height - 2*border - 2*radius;
765     newx = radius + border + ((tmp_i > 0) ? random() % tmp_i : 0);
766     newy = radius + border + ((tmp_j > 0) ? random() % tmp_j : 0);
767     x = newy * st->width + newx;
768     for (cy = -radius; cy <= radius; cy++)
769       for (cx = -radius; cx <= radius; cx++)
770         buf[x + cx + cy*st->width] = (short)(dheight);
771   }
772   break;
773   case ripple_stir: {
774     border += radius;
775     newx = border + (int)((st->width-2*border) * (1+cos(3*st->stir_ang)) / 2);
776     newy = border + (int)((st->height-2*border) * (1+sin(2*st->stir_ang)) / 2);
777     add_circle_drop(st, newx, newy, radius, drop / 10);
778     st->stir_ang += 0.02;
779     if (st->stir_ang > 12*M_PI) st->stir_ang = 0;
780   }
781   break;
782   }
783 }
784
785
786 static void
787 init_ripples(struct state *st, int ndrops, int splash)
788 {
789   int i;
790
791   st->bufferA = (short *)calloc(st->width * st->height, sizeof(*st->bufferA));
792   st->bufferB = (short *)calloc(st->width * st->height, sizeof(*st->bufferB));
793   st->temp = (short *)calloc(st->width * st->height, sizeof(*st->temp));
794
795   st->dirty_buffer = (char *)calloc(st->width * st->height, sizeof(*st->dirty_buffer));
796
797   for (i = 0; i < ndrops; i++)
798     add_drop(st, ripple_blob, splash);
799
800   if (st->transparent) {
801     if (st->grayscale_p)
802     {
803       int across, down;
804       for (down = 0; down < st->bigheight; down++)
805         for (across = 0; across < st->bigwidth; across++)
806           XPutPixel(st->buffer_map, across, down,
807                     grayscale(st, XGetPixel(st->orig_map, across, down)));
808     }
809     else
810     {  
811     /* There's got to be a better way of doing this  XCopyArea? */
812     memcpy(st->buffer_map->data, st->orig_map->data,
813            st->bigheight * st->buffer_map->bytes_per_line);
814     }
815   } else {
816     int across, down, color;
817
818     color = map_color(st, 0); /* background colour */
819     for (down = 0; down < st->bigheight; down++)
820       for (across = 0; across < st->bigwidth; across++)
821         XPutPixel(st->buffer_map,across,  down,  color);
822   }
823
824   DisplayImage(st);
825 }
826
827
828 /*
829  Explanation from hq_water.zip (Arturo R Montesinos (ARM) arami@fi.upm.es)
830
831  Differential equation is:  u  = a ( u  + u  )
832                              tt       xx   yy
833
834  Where a = tension * gravity / surface_density.
835
836  Approximating second derivatives by central differences:
837
838   [ u(t+1)-2u(t)+u(t-1) ] / dt = a [ u(x+1)+u(x-1)+u(y+1)+u(y-1)-4u ] / h
839
840  where dt = time step squared, h = dx*dy = mesh resolution squared.
841
842  From where u(t+1) may be calculated as:
843
844             dt  |   1   |                   dt
845  u(t+1) = a --  | 1 0 1 |u - u(t-1) + (2-4a --)u
846             h   |   1   |                    h
847
848  When a*dt/h = 1/2 last term vanishes, giving:
849
850                  1 |   1   |
851         u(t+1) = - | 1 0 1 |u - u(t-1)
852                  2 |   1   |
853
854  (note that u(t-1,x,y) is only used to calculate u(t+1,x,y) so
855   we can use the same array for both t-1 and t+1, needing only
856   two arrays, U[0] and U[1])
857
858  Dampening is simulated by subtracting 1/2^n of result.
859  n=4 gives best-looking result
860  n<4 (eg 2 or 3) thicker consistency, waves die out immediately
861  n>4 (eg 8 or 12) more fluid, waves die out slowly
862  */
863
864 static void
865 ripple(struct state *st)
866 {
867   int across, down, pixel;
868   short *src, *dest;
869
870   if (st->draw_toggle == 0) {
871     src = st->bufferA;
872     dest = st->bufferB;
873     st->draw_toggle = 1;
874   } else {
875     src = st->bufferB;
876     dest = st->bufferA;
877     st->draw_toggle = 0;
878   }
879
880   switch (st->draw_count) {
881   case 0: case 1:
882     pixel = 1 * st->width + 1;
883     for (down = 1; down < st->height - 1; down++, pixel += 2 * 1)
884       for (across = 1; across < st->width - 1; across++, pixel++) {
885         st->temp[pixel] =
886           (((src[pixel - 1] + src[pixel + 1] +
887              src[pixel - st->width] + src[pixel + st->width]) / 2)) - dest[pixel];
888       }
889
890     /* Smooth the output */
891     pixel = 1 * st->width + 1;
892     for (down = 1; down < st->height - 1; down++, pixel += 2 * 1)
893       for (across = 1; across < st->width - 1; across++, pixel++) {
894         if (st->temp[pixel] != 0) { /* Close enough for government work */
895           int damp =
896             (st->temp[pixel - 1] + st->temp[pixel + 1] +
897              st->temp[pixel - st->width] + st->temp[pixel + st->width] +
898              st->temp[pixel - st->width - 1] + st->temp[pixel - st->width + 1] +
899              st->temp[pixel + st->width - 1] + st->temp[pixel + st->width + 1] +
900              st->temp[pixel]) / 9;
901           dest[pixel] = damp - (damp >> st->fluidity);
902         } else
903           dest[pixel] = 0;
904       }
905     break;
906   case 2: case 3:
907     pixel = 1 * st->width + 1;
908     for (down = 1; down < st->height - 1; down++, pixel += 2 * 1)
909       for (across = 1; across < st->width - 1; across++, pixel++) {
910         int damp =
911           (((src[pixel - 1] + src[pixel + 1] +
912              src[pixel - st->width] + src[pixel + st->width]) / 2)) - dest[pixel];
913         dest[pixel] = damp - (damp >> st->fluidity);
914       }
915     break;
916   }
917   if (++st->draw_count > 3) st->draw_count = 0;
918
919   if (st->transparent)
920     st->draw_transparent(st, dest);
921   else
922     draw_ripple(st, dest);
923 }
924
925
926 /*      -------------------------------------------             */
927
928 static void *
929 ripples_init (Display *disp, Window win)
930 {
931   struct state *st = (struct state *) calloc (1, sizeof(*st));
932   st->dpy = disp;
933   st->window = win;
934   st->iterations = 0;
935   st->delay = get_integer_resource(disp, "delay", "Integer");
936   st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
937   st->rate = get_integer_resource(disp, "rate", "Integer");
938   st->box = get_integer_resource(disp, "box", "Integer");
939   st->oily = get_boolean_resource(disp, "oily", "Boolean");
940   st->stir = get_boolean_resource(disp, "stir", "Boolean");
941   st->fluidity = get_integer_resource(disp, "fluidity", "Integer");
942   st->transparent = get_boolean_resource(disp, "water", "Boolean");
943   st->grayscale_p = get_boolean_resource(disp, "grayscale", "Boolean");
944   st->light = get_integer_resource(disp, "light", "Integer");
945
946   if (st->delay < 0) st->delay = 0;
947   if (st->duration < 1) st->duration = 1;
948   if (st->fluidity <= 1) st->fluidity = 1;
949   if (st->fluidity > 16) st->fluidity = 16; /* 16 = sizeof(short) */
950   if (st->light < 0) st->light = 0;
951
952   init_cos_tab(st);
953   setup_X(st);
954
955   st->ncolors = get_integer_resource (disp, "colors", "Colors");
956   if (0 == st->ncolors)         /* English spelling? */
957     st->ncolors = get_integer_resource (disp, "colours", "Colors");
958
959   if (st->ncolors > sizeof(st->ctab)/sizeof(*st->ctab))
960     st->ncolors = sizeof(st->ctab)/sizeof(*st->ctab);
961
962   if (st->oily)
963     init_oily_colors(st);
964   else
965     init_linear_colors(st);
966
967   if (st->transparent && st->light > 0) {
968     int maxbits;
969     st->draw_transparent = draw_transparent_light;
970     visual_rgb_masks (st->screen, st->visual,
971                       &st->rmask, &st->gmask, &st->bmask);
972     set_mask(&st->rmask, &st->rshift);
973     set_mask(&st->gmask, &st->gshift);
974     set_mask(&st->bmask, &st->bshift);
975     if (st->rmask == 0) st->draw_transparent = draw_transparent_vanilla;
976
977     /* Adjust the shift value "light" when we don't have 8 bits per colour */
978     maxbits = MIN(MIN(BITCOUNT(st->rmask), BITCOUNT(st->gmask)), BITCOUNT(st->bmask));
979     st->light -= 8-maxbits;
980     if (st->light < 0) st->light = 0;
981   } else {
982     if (st->grayscale_p)
983     { 
984       visual_rgb_masks (st->screen, st->visual,
985                         &st->rmask, &st->gmask, &st->bmask);
986       set_mask(&st->rmask, &st->rshift);
987       set_mask(&st->gmask, &st->gshift);
988       set_mask(&st->bmask, &st->bshift);
989     }
990     st->draw_transparent = draw_transparent_vanilla;
991   }
992   
993   if (!st->transparent)
994     init_ripples(st, 0, -SPLASH); /* Start off without any drops */
995
996   return st;
997 }
998
999 static unsigned long
1000 ripples_draw (Display *dpy, Window window, void *closure)
1001 {
1002   struct state *st = (struct state *) closure;
1003
1004   if (st->img_loader)   /* still loading */
1005     {
1006       st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
1007       if (! st->img_loader) {  /* just finished */
1008         XWindowAttributes xgwa;
1009         XGetWindowAttributes(st->dpy, st->window, &xgwa);
1010         st->start_time = time ((time_t *) 0);
1011         st->orig_map = XGetImage (st->dpy, st->window, 0, 0, 
1012                                   xgwa.width, xgwa.height,
1013                                   ~0L, ZPixmap);
1014         init_ripples(st, 0, -SPLASH); /* Start off without any drops */
1015       }
1016       return st->delay;
1017     }
1018
1019     if (!st->img_loader &&
1020         st->start_time + st->duration < time ((time_t *) 0)) {
1021       XWindowAttributes xgwa;
1022       XGetWindowAttributes(st->dpy, st->window, &xgwa);
1023       st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
1024                                                 st->window, 0, 0);
1025       st->start_time = time ((time_t *) 0);
1026       return st->delay;
1027     }
1028
1029     if (st->rate > 0 && (st->iterations % st->rate) == 0)
1030       add_drop(st, ripple_blob, -SPLASH);
1031     if (st->stir)
1032       add_drop(st, ripple_stir, -SPLASH);
1033     if (st->box > 0 && (random() % st->box) == 0)
1034       add_drop(st, ripple_box, -SPLASH);
1035
1036     ripple(st);
1037     DisplayImage(st);
1038
1039     st->iterations++;
1040
1041     return st->delay;
1042 }
1043
1044
1045 static void
1046 ripples_reshape (Display *dpy, Window window, void *closure, 
1047                  unsigned int w, unsigned int h)
1048 {
1049 }
1050
1051 static Bool
1052 ripples_event (Display *dpy, Window window, void *closure, XEvent *event)
1053 {
1054   struct state *st = (struct state *) closure;
1055   if (screenhack_event_helper (dpy, window, event))
1056     {
1057       st->start_time = 0;
1058       return True;
1059     }
1060   return False;
1061 }
1062
1063 static void
1064 ripples_free (Display *dpy, Window window, void *closure)
1065 {
1066   struct state *st = (struct state *) closure;
1067   free (st);
1068 }
1069
1070 static const char *ripples_defaults[] =
1071 {
1072   ".background:         black",
1073   ".foreground:         #FFAF5F",
1074   "*colors:             200",
1075   "*dontClearRoot:      True",
1076   "*delay:              50000",
1077   "*duration:           120",
1078   "*rate:               5",
1079   "*box:                0",
1080   "*water:              True",
1081   "*oily:               False",
1082   "*stir:               False",
1083   "*fluidity:           6",
1084   "*light:              4",
1085   "*grayscale:          False",
1086 #ifdef HAVE_XSHM_EXTENSION
1087   "*useSHM: True",
1088 #else
1089   "*useSHM: False",
1090 #endif
1091 #ifdef HAVE_MOBILE
1092   "*ignoreRotation: True",
1093   "*rotateImages:   True",
1094 #endif
1095   0
1096 };
1097
1098 static XrmOptionDescRec ripples_options[] =
1099 {
1100   { "-colors",  ".colors",      XrmoptionSepArg, 0},
1101   { "-colours", ".colors",      XrmoptionSepArg, 0},
1102   {"-delay",    ".delay",       XrmoptionSepArg, 0},
1103   {"-duration", ".duration",    XrmoptionSepArg, 0 },
1104   {"-rate",     ".rate",        XrmoptionSepArg, 0},
1105   {"-box",      ".box",         XrmoptionSepArg, 0},
1106   {"-water",    ".water",       XrmoptionNoArg, "True"},
1107   {"-oily",     ".oily",        XrmoptionNoArg, "True"},
1108   {"-stir",     ".stir",        XrmoptionNoArg, "True"},
1109   {"-fluidity", ".fluidity",    XrmoptionSepArg, 0},
1110   {"-light",    ".light",       XrmoptionSepArg, 0},
1111   {"-grayscale",        ".grayscale",   XrmoptionNoArg, "True"},
1112   {"-shm",      ".useSHM",      XrmoptionNoArg, "True"},
1113   {"-no-shm",   ".useSHM",      XrmoptionNoArg, "False"},
1114   {0, 0, 0, 0}
1115 };
1116
1117
1118 XSCREENSAVER_MODULE ("Ripples", ripples)