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