From http://www.jwz.org/xscreensaver/xscreensaver-5.22.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  *
43  */
44
45 #include <math.h>
46 #include "screenhack.h"
47
48 typedef enum {ripple_drop, ripple_blob, ripple_box, ripple_stir} ripple_mode;
49
50 #ifdef HAVE_XSHM_EXTENSION
51 #include "xshm.h"
52 #endif /* HAVE_XSHM_EXTENSION */
53
54 #define TABLE 256
55
56 struct state {
57   Display *dpy;
58   Window window;
59   GC gc;
60   Visual *visual;
61
62   XImage *orig_map, *buffer_map;
63   int ctab[256];
64   Colormap colormap;
65   Screen *screen;
66   int ncolors;
67   int light;
68
69   int width, height; /* ripple size */
70   int bigwidth, bigheight; /* screen size */
71
72   Bool transparent;
73   short *bufferA, *bufferB, *temp;
74   char *dirty_buffer;
75
76   double cos_tab[TABLE];
77
78   Bool grayscale_p;
79
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 */
83
84   int rshift;
85   int gshift;
86   int bshift;
87
88   double stir_ang;
89
90   int draw_toggle;
91   int draw_count;
92
93   int iterations, delay, rate, box, oily, stir, fluidity;
94   int duration;
95   time_t start_time;
96
97   void (*draw_transparent) (struct state *st, short *src);
98
99   async_load_state *img_loader;
100
101 #ifdef HAVE_XSHM_EXTENSION
102   Bool use_shm;
103   XShmSegmentInfo shm_info;
104 #endif /* HAVE_XSHM_EXTENSION */
105 };
106
107
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};
111
112
113 /* How hard to hit the water */
114 #define SPLASH 512
115 #undef  MIN
116 #define MIN(x, y) ((x) < (y) ? (x) : (y))
117 #undef  MAX
118 #define MAX(x, y) ((x) > (y) ? (x) : (y))
119 #undef  DIRTY
120 #define DIRTY 3 /* dirty >= 2, 1 = restore original pixel, 0 = leave alone */
121
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))
128
129
130 static unsigned long grayscale(struct state *st, unsigned long color);
131
132 /*      -------------------------------------------             */
133
134
135 static int
136 map_color(struct state *st, int grey)
137 {
138   /* Clip it */
139   grey = st->ncolors * abs(grey) / (SPLASH/4);
140   if (grey > st->ncolors)
141     grey = st->ncolors;
142
143   /* Display it */
144   return st->ctab[grey];
145 }
146
147
148 static void
149 draw_ripple(struct state *st, short *src)
150 {
151   int across, down;
152   char *dirty = st->dirty_buffer;
153
154   for (down = 0; down < st->height - 1; down++, src += 1, dirty += 1)
155     for (across = 0; across < st->width - 1; across++, src++, dirty++) {
156       int v1, v2, v3, v4;
157       v1 = (int)*src;
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)) {
162         if (*dirty > 0)
163           (*dirty)--;
164       } else
165         *dirty = DIRTY;
166
167       if (*dirty > 0) {
168         int dx;
169         if (st->light > 0) {
170           dx = ((v3 - v1) + (v4 - v2)) << st->light; /* light from top */
171         } else
172           dx = 0;
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)));
177       }
178     }
179 }
180
181
182 /*      -------------------------------------------             */
183
184
185 /* Uses the horizontal gradient as an offset to create a warp effect  */
186 static void
187 draw_transparent_vanilla(struct state *st, short *src)
188 {
189   int across, down, pixel;
190   char *dirty = st->dirty_buffer;
191
192   pixel = 0;
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;
197
198       x0 = src[pixel];
199       x1 = src[pixel + 1];
200       x2 = src[pixel + 2];
201       y1 = src[pixel + st->width];
202       y2 = src[pixel + 2*st->width];
203
204       gradx = (x1 - x0);
205       grady = (y1 - x0);
206       gradx1= (x2 - x1);
207       grady1= (y2 - y1);
208       gradx1 = 1 + (gradx + gradx1) / 2;
209       grady1 = 1 + (grady + grady1) / 2;
210
211       if ((2*across+MIN(gradx,gradx1) < 0) ||
212           (2*across+MAX(gradx,gradx1) >= st->bigwidth)) {
213         gradx = 0;
214         gradx1= 1;
215       }
216       if ((2*down+MIN(grady,grady1) < 0) ||
217           (2*down+MAX(grady,grady1) >= st->bigheight)) {
218         grady = 0;
219         grady1 = 1;
220       }
221
222       if ((gradx == 0 && gradx1 == 1 && grady == 0 && grady1 == 1)) {
223         if (dirty[pixel] > 0)
224           dirty[pixel]--;
225       } else
226         dirty[pixel] = DIRTY;
227
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)));
237       }
238     }
239 }
240
241
242 /*      -------------------------------------------             */
243
244
245 static void
246 set_mask(unsigned long color, unsigned long *mask, int *shift)
247 {
248   *shift = 0;
249   while (color != 0 && (color & 1) == 0) {
250     (*shift)++;
251     color >>= 1;
252   }
253   *mask = color;
254 }
255
256
257 static unsigned long
258 cadd(unsigned long color, int dx, unsigned long mask, int shift)
259 {
260   int x;
261   color >>= shift;
262   x = (color & mask);
263   x += dx;
264   if (x < 0) x = 0;
265   else if (x > (int)mask) x = mask;
266   color = x;
267   return color << shift;
268 }
269
270
271 static unsigned long
272 bright(struct state *st, int dx, unsigned long color)
273 {
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));
277 }
278
279
280 static unsigned long
281 grayscale(struct state *st, unsigned long color)
282 {
283   int red;
284   int green;
285   int blue;
286   int total;
287   int gray_r;
288   int gray_g;
289   int gray_b;
290
291   if (!st->grayscale_p)
292     return color;
293   if (!st->transparent)
294     return color;
295   if ((st->rmask == 0) || (st->gmask == 0) || (st->bmask == 0))
296     return color;
297
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;
302
303   gray_r = total / (3 * st->gmask * st->bmask);
304   if (gray_r < 0)
305     gray_r = 0;
306   if (gray_r > st->rmask)
307     gray_r = st->rmask;
308   
309   gray_g = total / (3 * st->rmask * st->bmask);
310   if (gray_g < 0)
311     gray_g = 0;
312   if (gray_g > st->gmask)
313     gray_g = st->gmask;
314
315   gray_b = total / (3 * st->rmask * st->gmask);
316   if (gray_b < 0)
317     gray_b = 0;
318   if (gray_b > st->bmask)
319     gray_b = st->bmask;
320
321   return ((unsigned long)
322           ((gray_r << st->rshift) | (gray_g << st->gshift) | (gray_b << st->bshift)));
323 }
324
325
326 static void
327 draw_transparent_light(struct state *st, short *src)
328 {
329   int across, down, pixel;
330   char *dirty = st->dirty_buffer;
331
332   pixel = 0;
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;
337
338       x0 = src[pixel];
339       x1 = src[pixel + 1];
340       x2 = src[pixel + 2];
341       y1 = src[pixel + st->width];
342       y2 = src[pixel + 2*st->width];
343
344       gradx = (x1 - x0);
345       grady = (y1 - x0);
346       gradx1= (x2 - x1);
347       grady1= (y2 - y1);
348       gradx1 = 1 + (gradx + gradx1) / 2;
349       grady1 = 1 + (grady + grady1) / 2;
350
351       if ((2*across+MIN(gradx,gradx1) < 0) ||
352           (2*across+MAX(gradx,gradx1) >= st->bigwidth)) {
353         gradx = 0;
354         gradx1= 1;
355       }
356       if ((2*down+MIN(grady,grady1) < 0) ||
357           (2*down+MAX(grady,grady1) >= st->bigheight)) {
358         grady = 0;
359         grady1 = 1;
360       }
361
362       if ((gradx == 0 && gradx1 == 1 && grady == 0 && grady1 == 1)) {
363         if (dirty[pixel] > 0)
364           dirty[pixel]--;
365       } else
366         dirty[pixel] = DIRTY;
367
368       if (dirty[pixel] > 0) {
369         int dx;
370
371         /* light from top */
372         if (4-st->light >= 0)
373           dx = (grady + (src[pixel+st->width+1]-x1)) >> (4-st->light);
374         else
375           dx = (grady + (src[pixel+st->width+1]-x1)) << (st->light-4);
376
377         if (dx != 0) {
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))));
386         } else {
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)));
396         }
397       }
398     }
399 }
400
401
402 /*      -------------------------------------------             */
403
404
405 #if 0
406 /* Doesn't go any faster and doesn't work at all colour depths */
407 static void
408 draw_transparent16l(short *src)
409 {
410   int across, down, bigpix, pixel;
411   char *dirty = st->dirty_buffer;
412   unsigned short *buffer, *orig;
413
414   buffer = (unsigned short *) st->buffer_map->data;
415   orig = (unsigned short *) st->orig_map->data;
416
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;
423
424       x0 = src[pixel];
425       x1 = src[pixel + 1];
426       x2 = src[pixel + 2];
427       y1 = src[pixel + st->width];
428       y2 = src[pixel + 2*st->width];
429
430       gradx = (x1 - x0);
431       grady = (y1 - x0);
432       gradx1= (x2 - x1);
433       grady1= (y2 - y1);
434       gradx1 = 1 + (gradx + gradx1) / 2;
435       grady1 = 1 + (grady + grady1) / 2;
436
437       if ((2*across+MIN(gradx,gradx1) < 0) ||
438           (2*across+MAX(gradx,gradx1) >= st->bigwidth)) {
439         gradx = 0;
440         gradx1= 1;
441       }
442       if ((2*down+MIN(grady,grady1) < 0) ||
443           (2*down+MAX(grady,grady1) >= st->bigheight)) {
444         grady = 0;
445         grady1 = 1;
446       }
447
448       if ((gradx == 0 && gradx1 == 1 && grady == 0 && grady1 == 1)) {
449         if (dirty[pixel] > 0)
450           dirty[pixel]--;
451       } else
452         dirty[pixel] = DIRTY;
453
454       if (dirty[pixel] > 0) {
455         unsigned short *dest = buffer + bigpix;
456         unsigned short *image = orig + bigpix;
457         int dx;
458
459         /* light from top */
460         if (4-st->light >= 0)
461           dx = (grady + (src[pixel+st->width+1]-x1)) >> (4-st->light);
462         else
463           dx = (grady + (src[pixel+st->width+1]-x1)) << (st->light-4);
464
465         grady *= st->bigwidth;
466         grady1*= st->bigwidth;
467
468         if (dx != 0) {
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));
474         } else {
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);
480         }
481       }
482     }
483 }
484 #endif
485
486
487 /*      -------------------------------------------             */
488
489
490 static void
491 setup_X(struct state *st)
492 {
493   XWindowAttributes xgwa;
494   int depth;
495
496   XGetWindowAttributes(st->dpy, st->window, &xgwa);
497   depth = xgwa.depth;
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;
503
504
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...
508      -jwz, 23-Nov-01
509    */
510 #if 0 /* I'm not entirely sure if I need this */
511   if (st->bigwidth % 2)
512     st->bigwidth++;
513   if (st->bigheight % 2)
514     st->bigheight++;
515 #endif
516
517
518   st->width = st->bigwidth / 2;
519   st->height = st->bigheight / 2;
520
521   if (st->transparent) {
522     XGCValues gcv;
523     long gcflags;
524
525     gcv.function = GXcopy;
526     gcv.subwindow_mode = IncludeInferiors;
527
528     gcflags = GCFunction;
529     if (use_subwindow_mode_p(xgwa.screen, st->window))  /* see grabscreen.c */
530       gcflags |= GCSubwindowMode;
531
532     st->gc = XCreateGC(st->dpy, st->window, gcflags, &gcv);
533
534     st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
535                                               st->window, 0, 0);
536     st->start_time = time ((time_t) 0);
537   } else {
538     XGCValues gcv;
539
540     st->gc = XCreateGC(st->dpy, st->window, 0, &gcv);
541     st->orig_map = 0;
542   }
543
544   if (!st->gc) {
545     fprintf(stderr, "XCreateGC failed\n");
546     exit(1);
547   }
548
549   st->buffer_map = 0;
550
551 #ifdef HAVE_XSHM_EXTENSION
552   if (st->use_shm) {
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) {
556       st->use_shm = False;
557       fprintf(stderr, "create_xshm_image failed\n");
558     }
559   }
560 #endif /* HAVE_XSHM_EXTENSION */
561
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);
568   }
569 }
570
571
572 static void
573 DisplayImage(struct state *st)
574 {
575 #ifdef HAVE_XSHM_EXTENSION
576   if (st->use_shm)
577     XShmPutImage(st->dpy, st->window, st->gc, st->buffer_map, 0, 0, 0, 0,
578                  st->bigwidth, st->bigheight, False);
579   else
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);
583 }
584
585
586 /*      -------------------------------------------             */
587
588
589 static int
590 cinterp(double a, int bg, int fg)
591 {
592   int result;
593   result = (int)((1-a) * bg + a * fg + 0.5);
594   if (result < 0) result = 0;
595   if (result > 255) result = 255;
596   return result;
597 }
598
599
600 /* Interpolate the ripple colours between the background colour and
601    foreground colour */
602 static void
603 init_linear_colors(struct state *st)
604 {
605   int i, j, red, green, blue, bred, bgreen, bblue;
606   XColor fg, bg;
607
608   if (st->ncolors < 2 || mono_p)
609     st->ncolors = 2;
610   if (st->ncolors <= 2)
611     mono_p = True;
612
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.
615    */
616   fg.pixel = get_pixel_resource(st->dpy, st->colormap, "foreground", "Foreground");
617   XQueryColor(st->dpy, st->colormap, &fg);
618   red = (fg.red >> 8);
619   green = (fg.green >> 8);
620   blue = (fg.blue >> 8);
621
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);
627
628   j = 0;
629   for (i = 0; i < st->ncolors+1; i++) {
630     XColor xcl;
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);
635
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;
640
641     XAllocColor(st->dpy, st->colormap, &xcl);
642
643     st->ctab[j++] = (int) xcl.pixel;
644   }
645 }
646
647
648 static void
649 init_oily_colors(struct state *st)
650 {
651   XColor *colors = NULL;
652
653   if (st->ncolors < 2 || mono_p)
654     st->ncolors = 2;
655   if (st->ncolors <= 2)
656     mono_p = True;
657   colors = 0;
658
659   if (!mono_p) {
660     colors = (XColor *)malloc(sizeof(*colors) * (st->ncolors+1));
661     make_smooth_colormap(st->screen, st->visual, st->colormap,
662                          colors, &st->ncolors,
663                          True, /* allocate */
664                          False, /* not writable */
665                          True); /* verbose (complain about failure) */
666     if (st->ncolors <= 2) {
667       if (colors)
668         free (colors);
669       colors = 0;
670       mono_p = True;
671     }
672   }
673   if (!mono_p) {
674     int i, j = 0;
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;
678     }
679     free (colors);
680   } else {
681     st->ncolors = 2;
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");
684   }
685 }
686
687
688 /*      -------------------------------------------             */
689
690
691 static void
692 init_cos_tab(struct state *st)
693 {
694   int i;
695   for (i = 0; i < TABLE; i++)
696     st->cos_tab[i] = cos(i * M_PI/2 / TABLE);
697 }
698
699
700 /* Shape of drop to add */
701 static double
702 sinc(struct state *st, double x)
703 {
704 #if 1
705   /* cosine hump */
706   int i;
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];
711 #elif 0
712   return cos(x * M_PI/2);
713 #else
714   if (fabs(x) < 0.1)
715     return 1 - x*x;
716   else
717     return sin(x) / x;
718 #endif
719 }
720
721
722 static void
723 add_circle_drop(struct state *st, int x, int y, int radius, int dheight)
724 {
725   int r, r2, cx, cy;
726   short *buf = (random()&1) ? st->bufferA : st->bufferB;
727
728   r2 = radius * radius;
729
730
731   for (cy = -radius; cy <= radius; cy++)
732     for (cx = -radius; cx <= radius; cx++) {
733       int xx = x+cx;
734       int yy = y+cy;
735       if (xx < 0 || yy < 0 || xx >= st->width || yy >= st->height) {break;}
736       r = cx*cx + cy*cy;
737       if (r > r2) break;
738       buf[xx + yy*st->width] =
739         (short)(dheight * sinc(st, (radius > 0) ? sqrt(r)/radius : 0));
740     }
741 }
742
743
744 static void
745 add_drop(struct state *st, ripple_mode mode, int drop)
746 {
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 */
750   int border = 8;
751
752   switch (mode) {
753   default:
754   case ripple_drop: {
755     int x;
756
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] =
763       dheight;
764   }
765   break;
766   case ripple_blob: {
767     double power;
768
769     int tmp_i, tmp_j;
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);
777   }
778   break;
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) */
781   case ripple_box: {
782     int x;
783     int cx, cy;
784     short *buf = (random()&1) ? st->bufferA : st->bufferB;
785     int tmp_i, tmp_j;
786
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);
798   }
799   break;
800   case ripple_stir: {
801     border += radius;
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;
807   }
808   break;
809   }
810 }
811
812
813 static void
814 init_ripples(struct state *st, int ndrops, int splash)
815 {
816   int i;
817
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));
821
822   st->dirty_buffer = (char *)calloc(st->width * st->height, sizeof(*st->dirty_buffer));
823
824   for (i = 0; i < ndrops; i++)
825     add_drop(st, ripple_blob, splash);
826
827   if (st->transparent) {
828     if (st->grayscale_p)
829     {
830       int across, down;
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)));
835     }
836     else
837     {  
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);
841     }
842   } else {
843     int across, down, color;
844
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);
849   }
850
851   DisplayImage(st);
852 }
853
854
855 /*
856  Explanation from hq_water.zip (Arturo R Montesinos (ARM) arami@fi.upm.es)
857
858  Differential equation is:  u  = a ( u  + u  )
859                              tt       xx   yy
860
861  Where a = tension * gravity / surface_density.
862
863  Approximating second derivatives by central differences:
864
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
866
867  where dt = time step squared, h = dx*dy = mesh resolution squared.
868
869  From where u(t+1) may be calculated as:
870
871             dt  |   1   |                   dt
872  u(t+1) = a --  | 1 0 1 |u - u(t-1) + (2-4a --)u
873             h   |   1   |                    h
874
875  When a*dt/h = 1/2 last term vanishes, giving:
876
877                  1 |   1   |
878         u(t+1) = - | 1 0 1 |u - u(t-1)
879                  2 |   1   |
880
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])
884
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
889  */
890
891 static void
892 ripple(struct state *st)
893 {
894   int across, down, pixel;
895   short *src, *dest;
896
897   if (st->draw_toggle == 0) {
898     src = st->bufferA;
899     dest = st->bufferB;
900     st->draw_toggle = 1;
901   } else {
902     src = st->bufferB;
903     dest = st->bufferA;
904     st->draw_toggle = 0;
905   }
906
907   switch (st->draw_count) {
908   case 0: case 1:
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++) {
912         st->temp[pixel] =
913           (((src[pixel - 1] + src[pixel + 1] +
914              src[pixel - st->width] + src[pixel + st->width]) / 2)) - dest[pixel];
915       }
916
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 */
922           int damp =
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);
929         } else
930           dest[pixel] = 0;
931       }
932     break;
933   case 2: case 3:
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++) {
937         int damp =
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);
941       }
942     break;
943   }
944   if (++st->draw_count > 3) st->draw_count = 0;
945
946   if (st->transparent)
947     st->draw_transparent(st, dest);
948   else
949     draw_ripple(st, dest);
950 }
951
952
953 /*      -------------------------------------------             */
954
955 static void *
956 ripples_init (Display *disp, Window win)
957 {
958   struct state *st = (struct state *) calloc (1, sizeof(*st));
959   st->dpy = disp;
960   st->window = win;
961   st->iterations = 0;
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");
975
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;
981
982   init_cos_tab(st);
983   setup_X(st);
984
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");
988
989   if (st->ncolors > sizeof(st->ctab)/sizeof(*st->ctab))
990     st->ncolors = sizeof(st->ctab)/sizeof(*st->ctab);
991
992   if (st->oily)
993     init_oily_colors(st);
994   else
995     init_linear_colors(st);
996
997   if (st->transparent && st->light > 0) {
998     int maxbits;
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;
1004
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;
1009   } else {
1010     if (st->grayscale_p)
1011     { 
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);
1015     }
1016     st->draw_transparent = draw_transparent_vanilla;
1017   }
1018   
1019   if (!st->transparent)
1020     init_ripples(st, 0, -SPLASH); /* Start off without any drops */
1021
1022   return st;
1023 }
1024
1025 static unsigned long
1026 ripples_draw (Display *dpy, Window window, void *closure)
1027 {
1028   struct state *st = (struct state *) closure;
1029
1030   if (st->img_loader)   /* still loading */
1031     {
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,
1039                                   ~0L, ZPixmap);
1040         init_ripples(st, 0, -SPLASH); /* Start off without any drops */
1041       }
1042       return st->delay;
1043     }
1044
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,
1050                                                 st->window, 0, 0);
1051       st->start_time = time ((time_t) 0);
1052       return st->delay;
1053     }
1054
1055     if (st->rate > 0 && (st->iterations % st->rate) == 0)
1056       add_drop(st, ripple_blob, -SPLASH);
1057     if (st->stir)
1058       add_drop(st, ripple_stir, -SPLASH);
1059     if (st->box > 0 && (random() % st->box) == 0)
1060       add_drop(st, ripple_box, -SPLASH);
1061
1062     ripple(st);
1063     DisplayImage(st);
1064
1065     st->iterations++;
1066
1067     return st->delay;
1068 }
1069
1070
1071 static void
1072 ripples_reshape (Display *dpy, Window window, void *closure, 
1073                  unsigned int w, unsigned int h)
1074 {
1075 }
1076
1077 static Bool
1078 ripples_event (Display *dpy, Window window, void *closure, XEvent *event)
1079 {
1080   return False;
1081 }
1082
1083 static void
1084 ripples_free (Display *dpy, Window window, void *closure)
1085 {
1086   struct state *st = (struct state *) closure;
1087   free (st);
1088 }
1089
1090 static const char *ripples_defaults[] =
1091 {
1092   ".background:         black",
1093   ".foreground:         #FFAF5F",
1094   "*colors:             200",
1095   "*dontClearRoot:      True",
1096   "*delay:              50000",
1097   "*duration:           120",
1098   "*rate:               5",
1099   "*box:                0",
1100   "*water:              True",
1101   "*oily:               False",
1102   "*stir:               False",
1103   "*fluidity:           6",
1104   "*light:              4",
1105   "*grayscale:          False",
1106 #ifdef HAVE_XSHM_EXTENSION
1107   "*useSHM: True",
1108 #else
1109   "*useSHM: False",
1110 #endif
1111 #ifdef USE_IPHONE
1112   "*ignoreRotation: True",
1113 #endif
1114   0
1115 };
1116
1117 static XrmOptionDescRec ripples_options[] =
1118 {
1119   { "-colors",  ".colors",      XrmoptionSepArg, 0},
1120   { "-colours", ".colors",      XrmoptionSepArg, 0},
1121   {"-delay",    ".delay",       XrmoptionSepArg, 0},
1122   {"-duration", ".duration",    XrmoptionSepArg, 0 },
1123   {"-rate",     ".rate",        XrmoptionSepArg, 0},
1124   {"-box",      ".box",         XrmoptionSepArg, 0},
1125   {"-water",    ".water",       XrmoptionNoArg, "True"},
1126   {"-oily",     ".oily",        XrmoptionNoArg, "True"},
1127   {"-stir",     ".stir",        XrmoptionNoArg, "True"},
1128   {"-fluidity", ".fluidity",    XrmoptionSepArg, 0},
1129   {"-light",    ".light",       XrmoptionSepArg, 0},
1130   {"-grayscale",        ".grayscale",   XrmoptionNoArg, "True"},
1131   {"-shm",      ".useSHM",      XrmoptionNoArg, "True"},
1132   {"-no-shm",   ".useSHM",      XrmoptionNoArg, "False"},
1133   {0, 0, 0, 0}
1134 };
1135
1136
1137 XSCREENSAVER_MODULE ("Ripples", ripples)