source: trunk/third/xscreensaver/hacks/ripples.c @ 20148

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