db1513189dcaf45cb195d73935f3ed391cdcd394
[xscreensaver] / hacks / interference.c
1 /* interference.c --- colored fields via decaying sinusoidal waves.
2  * An entry for the RHAD Labs Screensaver Contest.
3  *
4  * Author: Hannu Mallat <hmallat@cs.hut.fi>
5  *
6  * Copyright (C) 1998 Hannu Mallat.
7  *
8  * Permission to use, copy, modify, distribute, and sell this software and its
9  * documentation for any purpose is hereby granted without fee, provided that
10  * the above copyright notice appear in all copies and that both that
11  * copyright notice and this permission notice appear in supporting
12  * documentation.  No representations are made about the suitability of this
13  * software for any purpose.  It is provided "as is" without express or 
14  * implied warranty.
15  *
16  * decaying sinusoidal waves, which extend spherically from their
17  * respective origins, move around the plane. a sort of interference
18  * between them is calculated and the resulting twodimensional wave
19  * height map is plotted in a grid, using softly changing colours.
20  *
21  * not physically (or in any sense) accurate, but fun to look at for 
22  * a while. you may tune the speed/resolution/interestingness tradeoff 
23  * with X resources, see below.
24  *
25  * Created      : Wed Apr 22 09:30:30 1998, hmallat
26  * Last modified: Wed Apr 22 09:30:30 1998, hmallat
27  * Last modified: Sun Aug 31 23:40:14 2003, 
28  *              david slimp <rock808@DavidSlimp.com>
29  *              added -hue option to specify base color hue
30  * Last modified: Wed May 15 00:04:43 2013,
31  *              Dave Odell <dmo2118@gmail.com>
32  *              Tuned performance; double-buffering is now off by default.
33  *              Made animation speed independent of FPS.
34  *              Added cleanup code, fixed a few glitches.
35  *              Added gratuitous #ifdefs.
36  */
37
38 #include <math.h>
39 #include <errno.h>
40
41 #include "screenhack.h"
42
43 #ifdef HAVE_STDINT_H
44 # include <stdint.h>
45 #else
46
47 typedef unsigned int uint32_t;
48 typedef unsigned short uint16_t;
49 typedef unsigned char uint8_t;
50
51 #endif
52
53 /*
54 Tested on an Intel(R) Pentium(R) 4 CPU 3.00GHz (family 15, model 6, 2 cores),
55 1 GB PC2-4200, nouveau - Gallium 0.4 on NV44, X.Org version: 1.13.3. A very
56 modest system by current standards.
57
58 Does double-buffering make sense? (gridsize = 2)
59 USE_XIMAGE is off: Yes (-db: 4.1 FPS, -no-db: 2.9 FPS)
60 XPutImage in strips: No (-db: 35.9 FPS, -no-db: 38.7 FPS)
61 XPutImage, whole image: No (-db: 32.3 FPS, -no-db: 33.7 FPS)
62 MIT-SHM, whole image: Doesn't work anyway: (37.3 FPS)
63
64 If gridsize = 1, XPutImage is slow when the XImage is one line at a time.
65 XPutImage in strips: -db: 21.2 FPS, -no-db: 19.7 FPS
66 XPutimage, whole image: -db: 23.2 FPS, -no-db: 23.4 FPS
67 MIT-SHM: 26.0 FPS
68
69 So XPutImage in strips is very slightly faster when gridsize >= 2, but
70 quite a bit worse when gridsize = 1.
71 */
72
73 /* I thought it would be faster this way, but it turns out not to be... -jwz */
74 /* It's a lot faster for me, though - D.O. */
75 #define USE_XIMAGE
76
77 /* i.e. make the XImage the size of the screen. This is much faster when
78  * gridsize = 1. (And SHM is turned off.) */
79 #define USE_BIG_XIMAGE
80
81 /* Numbers are wave_table size, measured in unsigned integers.
82  * FPS/radius = 50/radius = 800/radius = 1500/Big-O memory usage
83  *
84  * Use at most one of the following:
85  * Both off = regular sqrt() - 13.5 FPS, 50/800/1500. */
86
87 /* #define USE_FAST_SQRT_HACKISH */ /* 17.8 FPS/2873/4921/5395/O(lg(radius)) */
88 #define USE_FAST_SQRT_BIGTABLE2 /* 26.1 FPS/156/2242/5386/O(radius^2) */
89
90 #ifndef USE_XIMAGE
91 # undef HAVE_XSHM_EXTENSION  /* only applicable when using XImages */
92 #endif /* USE_XIMAGE */
93
94 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
95 # include "xdbe.h"
96 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
97
98 #ifdef HAVE_XSHM_EXTENSION
99 # include "xshm.h"
100 #endif /* HAVE_XSHM_EXTENSION */
101
102 static const char *interference_defaults [] = {
103   ".background:  black",
104   ".foreground:  white",
105   "*count:       3",     /* number of waves */
106   "*gridsize:    4",     /* pixel size, smaller values for better resolution */
107   "*ncolors:     128",   /* number of colours used */
108   "*hue:         0",     /* hue to use for base color (0-360) */
109   "*speed:       30",    /* speed of wave origins moving around */
110   "*delay:       30000", /* or something */
111   "*color-shift: 60",    /* h in hsv space, smaller values for smaller
112                           * color gradients */
113   "*radius:      800",   /* wave extent */
114   "*gray:        false", /* color or grayscale */
115   "*mono:        false", /* monochrome, not very much fun */
116
117   "*doubleBuffer: False", /* doubleBuffer slows things down for me - D.O. */
118 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
119   "*useDBE:      True", /* use double buffering extension */
120 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
121
122 #ifdef HAVE_XSHM_EXTENSION
123   "*useSHM:      True", /* use shared memory extension */
124 #endif /*  HAVE_XSHM_EXTENSION */
125 #ifdef USE_IPHONE
126   "*ignoreRotation: True",
127 #endif
128   0
129 };
130
131 static XrmOptionDescRec interference_options [] = {
132   { "-count",       ".count",       XrmoptionSepArg, 0 }, 
133   { "-ncolors",     ".ncolors",     XrmoptionSepArg, 0 }, 
134   { "-gridsize",    ".gridsize",    XrmoptionSepArg, 0 }, 
135   { "-hue",         ".hue",         XrmoptionSepArg, 0 },
136   { "-speed",       ".speed",       XrmoptionSepArg, 0 },
137   { "-delay",       ".delay",       XrmoptionSepArg, 0 },
138   { "-color-shift", ".color-shift", XrmoptionSepArg, 0 },
139   { "-radius",      ".radius",      XrmoptionSepArg, 0 },
140   { "-gray",        ".gray",        XrmoptionNoArg,  "True" },
141   { "-mono",        ".mono",        XrmoptionNoArg,  "True" },
142   { "-db",          ".doubleBuffer", XrmoptionNoArg,  "True" },
143   { "-no-db",       ".doubleBuffer", XrmoptionNoArg,  "False" },
144 #ifdef HAVE_XSHM_EXTENSION
145   { "-shm",     ".useSHM",      XrmoptionNoArg, "True" },
146   { "-no-shm",  ".useSHM",      XrmoptionNoArg, "False" },
147 #endif /*  HAVE_XSHM_EXTENSION */
148   { 0, 0, 0, 0 }
149 };
150
151 struct inter_source {
152   int x;
153   int y;
154   double x_theta;
155   double y_theta;
156 };
157
158 struct inter_context {
159   /*
160    * Display-related entries
161    */
162   Display* dpy;
163   Window   win;
164
165 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
166   XdbeBackBuffer back_buf;
167 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
168   Pixmap   pix_buf;
169
170   GC       copy_gc;
171 #ifdef USE_XIMAGE
172   XImage  *ximage;
173 #endif /* USE_XIMAGE */
174
175 #ifdef HAVE_XSHM_EXTENSION
176   Bool use_shm, shm_can_draw;
177   XShmSegmentInfo shm_info;
178 #endif /* HAVE_XSHM_EXTENSION */
179
180   /*
181    * Resources
182    */
183   int count;
184   int grid_size;
185   int colors;
186   float hue;
187   int speed;
188   int delay;
189   int shift;
190
191   /*
192    * Drawing-related entries
193    */
194   int w;
195   int h;
196   Colormap cmap;
197   Screen *screen;
198   XColor* pal;
199 #ifndef USE_XIMAGE
200   GC* gcs;
201 #endif
202   int radius; /* Not always the same as the X resource. */
203   double last_frame;
204 #ifdef USE_XIMAGE
205   uint32_t* row;
206 #endif
207
208   /*
209    * lookup tables
210    */
211   unsigned* wave_height;
212     
213   /*
214    * Interference sources
215    */
216   struct inter_source* source;
217 };
218
219 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
220 # define TARGET(c) ((c)->back_buf ? (c)->back_buf : \
221                     (c)->pix_buf ? (c)->pix_buf : (c)->win)
222 #else  /* HAVE_DOUBLE_BUFFER_EXTENSION */
223 # define TARGET(c) ((c)->pix_buf ? (c)->pix_buf : (c)->win)
224 #endif /* !HAVE_DOUBLE_BUFFER_EXTENSION */
225
226 #ifdef USE_FAST_SQRT_HACKISH
227 /* Based loosely on code from Wikipedia:
228  * https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Approximations_that_depend_on_IEEE_representation
229  */
230
231 /* FAST_SQRT_EXTRA_BITS = 3: Smallest useful value
232  * = 5/6: A little bit of banding, wave_height table is on par with regular
233  *        sqrt() code.
234  * = 7: No apparent difference with original @ radius = 800.
235  * = 8: One more just to be comfortable.
236  */
237
238 # define FAST_SQRT_EXTRA_BITS 8
239
240 union int_float
241 {
242   uint32_t i;
243   float f;
244 };
245
246 static unsigned fast_log2(unsigned x)
247 {
248   union int_float u;
249   if(!x)
250     return x;
251   u.f = x;
252   return ((u.i - 0x3f800000) >> (23 - FAST_SQRT_EXTRA_BITS)) + 1;
253 }
254
255 static float fast_inv_log2(unsigned x)
256 {
257   union int_float u;
258   if(!x)
259     return 0.0f;
260   u.i = ((x - 1) << (23 - FAST_SQRT_EXTRA_BITS)) + 0x3f800000;
261   return u.f;
262 }
263
264 #endif
265
266 #ifdef USE_FAST_SQRT_BIGTABLE2
267
268 /* I eyeballed these figures. They could be improved. - D.O. */
269
270 # define FAST_SQRT_DISCARD_BITS1 4
271 /* = 5: Dot in center is almost invisible at radius = 800. */
272 /* = 4: Dot in center looks OK at radius = 50. */
273
274 /* 156/2740/9029 */
275 /* # define FAST_SQRT_DISCARD_BITS2 8 */
276 /* # define FAST_SQRT_CUTOFF 64 * 64 */
277
278 /* 156/2242/5386 */
279 # define FAST_SQRT_DISCARD_BITS2 9
280 # define FAST_SQRT_CUTOFF 128 * 128
281
282 /*
283  * This is a little faster:
284  * 44.5 FPS, 19/5000/17578
285  *
286  * # define FAST_SQRT_DISCARD_BITS1 7
287  * # define FAST_SQRT_DISCARD_BITS2 7
288  * # define FAST_SQRT_CUTOFF 0
289  *
290  * For radius = 800, FAST_SQRT_DISCARD_BITS2 =
291  * = 9/10: Approximately the original table size, some banding near origins.
292  * = 7: wave_height is 20 KB, and just fits inside a 32K L1 cache.
293  * = 6: Nearly indistinguishable from original
294  */
295
296 /*
297   FAST_TABLE(x) is equivalent to, but slightly faster than:
298   x < FAST_SQRT_CUTOFF ?
299     (x >> FAST_SQRT_DISCARD_BITS1) :
300     ((x - FAST_SQRT_CUTOFF) >> FAST_SQRT_DISCARD_BITS2) +
301       (FAST_SQRT_CUTOFF >> FAST_SQRT_DISCARD_BITS1);
302 */
303
304 #define FAST_TABLE(x) \
305   ((x) < FAST_SQRT_CUTOFF ? \
306     ((x) >> FAST_SQRT_DISCARD_BITS1) : \
307     (((x) + \
308       ((FAST_SQRT_CUTOFF << (FAST_SQRT_DISCARD_BITS2 - \
309         FAST_SQRT_DISCARD_BITS1)) - FAST_SQRT_CUTOFF)) >> \
310       FAST_SQRT_DISCARD_BITS2))
311
312 static double fast_inv_table(unsigned x)
313 {
314   return x < (FAST_SQRT_CUTOFF >> FAST_SQRT_DISCARD_BITS1) ?
315     (x << FAST_SQRT_DISCARD_BITS1) :
316     ((x - (FAST_SQRT_CUTOFF >> FAST_SQRT_DISCARD_BITS1)) <<
317       FAST_SQRT_DISCARD_BITS2) + FAST_SQRT_CUTOFF;
318 }
319
320 #endif
321
322 /* Also destroys c->row. */
323 static void destroy_image(Display* dpy, struct inter_context* c)
324 {
325 #ifdef USE_XIMAGE
326   if(c->ximage) {
327 # ifdef HAVE_XSHM_EXTENSION
328     if(c->use_shm) {
329       destroy_xshm_image(dpy, c->ximage, &c->shm_info);
330     } else
331 # endif
332     {
333       /* Also frees c->ximage->data, which isn't allocated by XCreateImage. */
334       XDestroyImage(c->ximage);
335     }
336   }
337
338   free(c->row);
339 #endif
340 }
341
342 static void inter_free(Display* dpy, struct inter_context* c)
343 {
344 #ifndef USE_XIMAGE
345   unsigned i;
346 #endif
347
348   if(c->pix_buf)
349     XFreePixmap(dpy, c->pix_buf);
350
351   if(c->copy_gc)
352     XFreeGC(dpy, c->copy_gc);
353
354   destroy_image(dpy, c);
355
356   if(c->colors <= 2)
357     free(c->pal);
358   else
359     free_colors(c->screen, c->cmap, c->pal, c->colors);
360
361 #ifndef USE_XIMAGE
362   for(i = 0; i != c->colors; ++i)
363     XFreeGC(dpy, c->gcs[i]);
364   free(c->gcs);
365 #endif
366
367   free(c->wave_height);
368   free(c->source);
369 }
370
371 static void abort_no_mem(void)
372 {
373   fprintf(stderr, "interference: %s\n", strerror(ENOMEM));
374   exit(1);
375 }
376
377 static void check_no_mem(Display* dpy, struct inter_context* c, void* ptr)
378 {
379   if(!ptr) {
380     inter_free(dpy, c);
381     abort_no_mem();
382   }
383 }
384
385 /* On allocation error, c->row == NULL. */
386 static void create_image(
387   Display* dpy, 
388   struct inter_context* c, 
389   const XWindowAttributes* xgwa)
390 {
391 #ifdef USE_XIMAGE
392   c->row = malloc((c->w / c->grid_size) * sizeof(uint32_t));
393   check_no_mem(dpy, c, c->row);
394
395 # ifdef HAVE_XSHM_EXTENSION
396   /*
397    * interference used to put one row at a time to the X server. This changes
398    * today.
399    *
400    * XShmPutImage is asynchronous; the contents of the XImage must not be
401    * modified until the server has placed the data on the screen. Waiting for
402    * an XShmCompletionEvent after every line of pixels is a little nutty, so
403    * shared-memory XImages will cover the entire screen, and it only has to be
404    * sent once per frame.
405    *
406    * The non-SHM code, on the other hand is noticeably slower when
407    * gridsize = 1 with one row at a time. If, on the other hand, gridsize >= 2,
408    * there's a slight speed increase with one row at a time.
409    *
410    * This uses a lot more RAM than the single line approach. Users with only
411    * 4 MB of RAM may wish to disable USE_BIG_XIMAGE and specify -no-shm on the
412    * command line. Since this is 2013 and desktop computers are shipping with
413    * 8 GB of RAM, I doubt that this will be a major issue. - D.O.
414    */
415
416   if (c->use_shm)
417     {
418       c->ximage = create_xshm_image(dpy, xgwa->visual, xgwa->depth,
419                                     ZPixmap, 0, &c->shm_info,
420                                     xgwa->width, xgwa->height);
421       if (!c->ximage)
422         c->use_shm = False;
423       /* If create_xshm_image fails, it will not be attempted again. */
424
425       c->shm_can_draw = True;
426     }
427 # endif /* HAVE_XSHM_EXTENSION */
428
429   if (!c->ximage)
430     {
431       c->ximage =
432         XCreateImage(dpy, xgwa->visual,
433                      xgwa->depth, ZPixmap, 0, 0, /* depth, fmt, offset, data */
434                      xgwa->width,                /* width */
435 # ifdef USE_BIG_XIMAGE
436                      xgwa->height,               /* height */
437 # else
438                      c->grid_size,               /* height */
439 # endif
440                      8, 0);                      /* pad, bpl */
441
442       if(c->ximage)
443         {
444           c->ximage->data = (char *)
445             calloc(c->ximage->height, c->ximage->bytes_per_line);
446
447           if(!c->ximage->data)
448             {
449               free(c->ximage);
450               c->ximage = NULL;
451             }
452         }
453     }
454
455   if(!c->ximage)
456     {
457       free(c->row);
458       c->row = 0;
459     }
460
461   check_no_mem(dpy, c, c->row);
462 #endif /* USE_XIMAGE */
463 }
464
465 static void create_pix_buf(Display* dpy, Window win, struct inter_context *c,
466                            const XWindowAttributes* xgwa)
467 {
468 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
469   if(c->back_buf)
470     return;
471 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
472   c->pix_buf = XCreatePixmap(dpy, win, xgwa->width, xgwa->height, xgwa->depth);
473 }
474
475 static double float_time(void)
476 {
477   struct timeval result;
478   gettimeofday(
479     &result
480 #ifdef GETTIMEOFDAY_TWO_ARGS
481     , NULL
482 #endif
483     );
484
485   return result.tv_usec * 1.0e-6 + result.tv_sec;
486 }
487
488 static void inter_init(Display* dpy, Window win, struct inter_context* c) 
489 {
490   XWindowAttributes xgwa;
491   double H[3], S[3], V[3];
492   int i;
493   int mono;
494   int gray;
495   int radius;
496   XGCValues val;
497   Bool dbuf = get_boolean_resource (dpy, "doubleBuffer", "Boolean");
498
499 #ifndef USE_XIMAGE
500   unsigned long valmask = 0;
501 #endif
502
503 # ifdef HAVE_COCOA      /* Don't second-guess Quartz's double-buffering */
504   dbuf = False;
505 # endif
506
507   memset (c, 0, sizeof(*c));
508
509   c->dpy = dpy;
510   c->win = win;
511
512   c->delay = get_integer_resource(dpy, "delay", "Integer");
513
514   XGetWindowAttributes(c->dpy, c->win, &xgwa);
515   c->w = xgwa.width;
516   c->h = xgwa.height;
517   c->cmap = xgwa.colormap;
518   c->screen = xgwa.screen;
519
520 #ifdef HAVE_XSHM_EXTENSION
521   c->use_shm = get_boolean_resource(dpy, "useSHM", "Boolean");
522 #endif /*  HAVE_XSHM_EXTENSION */
523
524   val.function = GXcopy;
525   c->copy_gc = XCreateGC(c->dpy, TARGET(c), GCFunction, &val);
526
527   c->count = get_integer_resource(dpy, "count", "Integer");
528   if(c->count < 1)
529     c->count = 1;
530   c->grid_size = get_integer_resource(dpy, "gridsize", "Integer");
531   if(c->grid_size < 1)
532     c->grid_size = 1;
533   mono = get_boolean_resource(dpy, "mono", "Boolean");
534   if(!mono) {
535     c->colors = get_integer_resource(dpy, "ncolors", "Integer");
536     if(c->colors < 2)
537       c->colors = 2;
538   }
539   c->hue = get_integer_resource(dpy, "hue", "Float");
540   while (c->hue < 0 || c->hue >= 360)
541     c->hue = frand(360.0);
542   c->speed = get_integer_resource(dpy, "speed", "Integer");
543   c->shift = get_float_resource(dpy, "color-shift", "Float");
544   while(c->shift >= 360.0)
545     c->shift -= 360.0;
546   while(c->shift <= -360.0)
547     c->shift += 360.0;
548   radius = get_integer_resource(dpy, "radius", "Integer");;
549   if(radius < 1)
550     radius = 1;
551
552   create_image(dpy, c, &xgwa);
553
554   if(!mono) {
555     c->pal = calloc(c->colors, sizeof(XColor));
556     check_no_mem(dpy, c, c->pal);
557
558     gray = get_boolean_resource(dpy, "gray", "Boolean");
559     if(!gray) {
560       H[0] = c->hue;
561       H[1] = H[0] + c->shift < 360.0 ? H[0]+c->shift : H[0] + c->shift-360.0; 
562       H[2] = H[1] + c->shift < 360.0 ? H[1]+c->shift : H[1] + c->shift-360.0; 
563       S[0] = S[1] = S[2] = 1.0;
564       V[0] = V[1] = V[2] = 1.0;
565     } else {
566       H[0] = H[1] = H[2] = 0.0;
567       S[0] = S[1] = S[2] = 0.0;
568       V[0] = 1.0; V[1] = 0.5; V[2] = 0.0;
569     }
570
571     make_color_loop(c->screen, xgwa.visual, c->cmap, 
572                     H[0], S[0], V[0], 
573                     H[1], S[1], V[1], 
574                     H[2], S[2], V[2], 
575                     c->pal, &(c->colors), 
576                     True, False);
577     if(c->colors < 2) { /* color allocation failure */
578       mono = 1;
579       free(c->pal);
580     }
581   }
582
583   if(mono) { /* DON'T else this with the previous if! */
584     c->colors = 2;
585     c->pal = calloc(2, sizeof(XColor));
586     check_no_mem(dpy, c, c->pal);
587     c->pal[0].pixel = BlackPixel(c->dpy, DefaultScreen(c->dpy));
588     c->pal[1].pixel = WhitePixel(c->dpy, DefaultScreen(c->dpy));
589   }
590
591 #ifdef HAVE_XSHM_EXTENSION
592   if(c->use_shm)
593     dbuf = False;
594   /* Double-buffering doesn't work with MIT-SHM: XShmPutImage must draw to the
595    * window. Otherwise, XShmCompletion events will have the XAnyEvent::window
596    * field set to the back buffer, and XScreenSaver will ignore the event. */
597 #endif
598
599   if (dbuf)
600     {
601 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
602       c->back_buf = xdbe_get_backbuffer (c->dpy, c->win, XdbeUndefined);
603 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
604
605       create_pix_buf(dpy, win, c, &xgwa);
606     }
607
608 #ifndef USE_XIMAGE
609   valmask = GCForeground;
610   c->gcs = calloc(c->colors, sizeof(GC));
611   check_no_mem(dpy, c, c->gcs);
612   for(i = 0; i < c->colors; i++) {
613     val.foreground = c->pal[i].pixel;    
614     c->gcs[i] = XCreateGC(c->dpy, TARGET(c), valmask, &val);
615   }
616 #endif
617
618 #if defined USE_FAST_SQRT_HACKISH
619   c->radius = fast_log2(radius * radius);
620 #elif defined USE_FAST_SQRT_BIGTABLE2
621   c->radius = radius * radius;
622   c->radius = FAST_TABLE(c->radius);
623 #else
624   c->radius = radius;
625 #endif
626
627   c->wave_height = calloc(c->radius, sizeof(unsigned));
628   check_no_mem(dpy, c, c->wave_height);
629
630   for(i = 0; i < c->radius; i++) {
631     float max, fi;
632 #if defined USE_FAST_SQRT_HACKISH
633     fi = sqrt(fast_inv_log2(i));
634 #elif defined USE_FAST_SQRT_BIGTABLE2
635     fi = sqrt(fast_inv_table(i));
636 #else
637     fi = i;
638 #endif
639     max = 
640       ((float)c->colors) * 
641       ((float)radius - fi) /
642       ((float)radius);
643     c->wave_height[i] = 
644       (unsigned)
645       ((max + max*cos(fi/50.0)) / 2.0);
646   }
647
648   c->source = calloc(c->count, sizeof(struct inter_source));
649   check_no_mem(dpy, c, c->source);
650
651   for(i = 0; i < c->count; i++) {
652     c->source[i].x_theta = frand(2.0)*3.14159;
653     c->source[i].y_theta = frand(2.0)*3.14159;
654   }
655
656   c->last_frame = float_time();
657 }
658
659 #define source_x(c, i) \
660   (c->w/2 + ((int)(cos(c->source[i].x_theta)*((float)c->w/2.0))))
661 #define source_y(c, i) \
662   (c->h/2 + ((int)(cos(c->source[i].y_theta)*((float)c->h/2.0))))
663
664 /*
665  * This is rather suboptimal. Calculating the distance per-pixel is going to
666  * be a lot slower than using now-ubiquitous SIMD CPU instructions to do four
667  * or eight pixels at a time. Plus, this could be almost trivially
668  * parallelized, what with all the multi-core hardware nowadays.
669  */
670
671 #ifdef TEST_PATTERN
672 static uint32_t
673 _alloc_color(struct inter_context *c, uint16_t r, uint16_t g, uint16_t b)
674 {
675         XColor color;
676         color.red = r;
677         color.green = g;
678         color.blue = b;
679         XAllocColor(c->dpy, c->cmap, &color);
680         return color.pixel;
681 }
682
683 static void _copy_test(Display *dpy, Drawable src, Drawable dst, GC gc, int x, int y, uint32_t cells)
684 {
685         XCopyArea(dpy, src, dst, gc, 0, 0, 3, 2, x, y);
686         
687         {
688                 XImage *image = XGetImage(dpy, src, 0, 0, 3, 2, cells, ZPixmap);
689                 XPutImage(dpy, dst, gc, image, 0, 0, x, y + 2, 3, 2);
690                 XDestroyImage(image);
691         }
692 }
693
694 static void _test_pattern(Display *dpy, Drawable d, GC gc, const uint32_t *rgb)
695 {
696         unsigned x;
697         for(x = 0; x != 3; ++x)
698         {
699                 XSetForeground(dpy, gc, rgb[x]);
700                 XDrawPoint(dpy, d, gc, x, 0);
701                 XSetForeground(dpy, gc, rgb[2 - x]);
702                 XFillRectangle(dpy, d, gc, x, 1, 1, 1);
703         }
704         
705         _copy_test(dpy, d, d, gc, 0, 2, rgb[0] | rgb[1] | rgb[2]);
706 }
707 #endif /* TEST_PATTERN */
708
709 static unsigned long do_inter(struct inter_context* c)
710 {
711   int i, j, k;
712   unsigned result;
713   int dist;
714   int g = c->grid_size;
715   unsigned w_div_g = c->w/g;
716
717   int dx, dy;
718   int px, py;
719
720 #ifdef USE_XIMAGE
721   unsigned img_y = 0;
722   void *scanline = c->ximage->data;
723 #endif
724
725   double now;
726   float elapsed;
727
728 #if defined USE_XIMAGE && defined HAVE_XSHM_EXTENSION
729   /* Wait a little while for the XServer to become ready if necessary. */
730   if(c->use_shm && !c->shm_can_draw)
731     return 2000;
732 #endif
733
734   now = float_time();
735   elapsed = (now - c->last_frame) * 10.0;
736
737   c->last_frame = now;
738
739   for(i = 0; i < c->count; i++) {
740     c->source[i].x_theta += (elapsed*c->speed/1000.0);
741     if(c->source[i].x_theta > 2.0*3.14159)
742       c->source[i].x_theta -= 2.0*3.14159;
743     c->source[i].y_theta += (elapsed*c->speed/1000.0);
744     if(c->source[i].y_theta > 2.0*3.14159)
745       c->source[i].y_theta -= 2.0*3.14159;
746     c->source[i].x = source_x(c, i);
747     c->source[i].y = source_y(c, i);
748   }
749
750   for(j = 0; j < c->h/g; j++) {
751     for(i = 0; i < w_div_g; i++) {
752       result = 0;
753       px = i*g + g/2;
754       py = j*g + g/2;
755       for(k = 0; k < c->count; k++) {
756
757         dx = px - c->source[k].x;
758         dy = py - c->source[k].y;
759
760         /*
761          * Other possibilities for improving performance here:
762          * 1. Using octagon-based distance estimation
763          *    (Which causes giant octagons to appear.)
764          * 2. Square root approximation by reinterpret-casting IEEE floats to
765          *    integers.
766          *    (Which causes angles to appear when two waves interfere.)
767          */
768
769 /*      int_float u;
770         u.f = dx*dx + dy*dy;
771         u.i = (1 << 29) + (u.i >> 1) - (1 << 22);
772         dist = u.f; */
773
774 #if defined USE_FAST_SQRT_BIGTABLE2
775         dist = dx*dx + dy*dy;
776         dist = FAST_TABLE(dist);
777 #elif defined USE_FAST_SQRT_HACKISH
778         dist = fast_log2(dx*dx + dy*dy);
779 #else
780         dist = sqrt(dx*dx + dy*dy);
781 #endif
782
783         result += (dist >= c->radius ? 0 : c->wave_height[dist]);
784       }
785
786       /* It's slightly faster to do a subtraction or two before calculating the
787        * modulus. - D.O. */
788       if(result >= c->colors)
789       {
790         result -= c->colors;
791         if(result >= c->colors)
792           result %= (unsigned)c->colors;
793       }
794
795 #ifdef USE_XIMAGE
796       c->row[i] = c->pal[result].pixel;
797 #else
798       XFillRectangle(c->dpy, TARGET(c), c->gcs[result], g*i, g*j, g, g);
799 #endif /* USE_XIMAGE */
800     }
801
802 #ifdef USE_XIMAGE
803     /* Fill in these `gridsize' horizontal bits in the scanline */
804     if(c->ximage->bits_per_pixel == 32)
805     {
806       uint32_t *ptr = (uint32_t *)scanline;
807       for(i = 0; i < w_div_g; i++) {
808         for(k = 0; k < g; k++)
809           ptr[g*i+k] = c->row[i];
810       }
811     }
812     else if(c->ximage->bits_per_pixel == 24)
813     {
814       uint8_t *ptr = (uint8_t *)scanline;
815       for(i = 0; i < w_div_g; i++) {
816         for(k = 0; k < g; k++) {
817           uint32_t pixel = c->row[i];
818           /* Might not work on big-endian. */
819           ptr[0] = pixel;
820           ptr[1] = (pixel & 0x0000ff00) >> 8;
821           ptr[2] = (pixel & 0x00ff0000) >> 16;
822           ptr += 3;
823         }
824       }
825     }
826     else if(c->ximage->bits_per_pixel == 16)
827     {
828       uint16_t *ptr = (uint16_t *)scanline;
829       for(i = 0; i < w_div_g; i++) {
830         for(k = 0; k < g; k++)
831           ptr[g*i+k] = c->row[i];
832       }
833     }
834     else if(c->ximage->bits_per_pixel == 8)
835     {
836       uint8_t *ptr = (uint8_t *)scanline;
837       for(i = 0; i < w_div_g; i++) {
838         for(k = 0; k < g; k++)
839           ptr[g*i+k] = c->row[i];
840       }
841     }
842     else
843     {
844       for(i = 0; i < w_div_g; i++) {
845         for(k = 0; k < g; k++)
846           XPutPixel(c->ximage, (g*i)+k, img_y, c->row[i]);
847       }
848     }
849
850     /* Only the first scanline of the image has been filled in; clone that
851        scanline to the rest of the `gridsize' lines in the ximage */
852     for(k = 0; k < (g-1); k++)
853       memcpy(c->ximage->data + (c->ximage->bytes_per_line * (img_y + k + 1)),
854              c->ximage->data + (c->ximage->bytes_per_line * img_y),
855              c->ximage->bytes_per_line);
856
857 # ifndef USE_BIG_XIMAGE
858     /* Move the bits for this horizontal stripe to the server. */
859 #  ifdef HAVE_XSHM_EXTENSION
860     if (!c->use_shm)
861 #  endif /*  HAVE_XSHM_EXTENSION */
862       XPutImage(c->dpy, TARGET(c), c->copy_gc, c->ximage,
863                 0, 0, 0, g*j, c->ximage->width, c->ximage->height);
864 # endif
865
866 # if defined HAVE_XSHM_EXTENSION && !defined USE_BIG_XIMAGE
867     if (c->use_shm)
868 # endif
869     {
870 # if defined HAVE_XSHM_EXTENSION || defined USE_BIG_XIMAGE
871       scanline = (char *)scanline + c->ximage->bytes_per_line * g;
872       img_y += g;
873 # endif
874     }
875
876 #endif /* USE_XIMAGE */
877   }
878
879 #ifdef HAVE_XSHM_EXTENSION
880   if (c->use_shm)
881   {
882     XShmPutImage(c->dpy, c->win, c->copy_gc, c->ximage,
883                  0, 0, 0, 0, c->ximage->width, c->ximage->height,
884                  True);
885     c->shm_can_draw = False;
886   }
887 #endif
888 #if defined HAVE_XSHM_EXTENSION && defined USE_BIG_XIMAGE
889   else
890 #endif
891 #ifdef USE_BIG_XIMAGE
892   {
893     XPutImage(c->dpy, TARGET(c), c->copy_gc, c->ximage,
894               0, 0, 0, 0, c->ximage->width, c->ximage->height);
895   }
896 #endif
897
898 #ifdef TEST_PATTERN
899         {
900 /*              XWindowAttributes xgwa;
901                 XGetWindowAttributes(c->dpy, c->win, &xgwa); */
902                 
903                 // if(xgwa.width >= 9 && xgwa.height >= 10)
904                 {
905                         Screen *screen = ScreenOfDisplay(c->dpy, DefaultScreen(c->dpy));
906                         Visual *visual = DefaultVisualOfScreen(screen);
907                         Pixmap pixmap = XCreatePixmap(c->dpy, TARGET(c), 3, 10, visual_depth(screen, visual));
908                         
909                         {
910                                 XSetForeground(c->dpy, c->copy_gc, _alloc_color(c, 0xffff, 0x7fff, 0x7fff));
911                                 XDrawPoint(c->dpy, TARGET(c), c->copy_gc, 0, c->h - 1);
912                         }
913                         
914                         uint32_t rgb[3], cells;
915                         rgb[0] = _alloc_color(c, 0xffff, 0, 0);
916                         rgb[1] = _alloc_color(c, 0, 0xffff, 0);
917                         rgb[2] = _alloc_color(c, 0, 0, 0xffff);
918                         cells = rgb[0] | rgb[1] | rgb[2];
919                         
920                         _test_pattern(c->dpy, TARGET(c), c->copy_gc, rgb);
921                         _test_pattern(c->dpy, pixmap, c->copy_gc, rgb);
922                         // Here's a good spot to verify that the pixmap contains the right colors at the top. 
923                         _copy_test(c->dpy, TARGET(c), pixmap, c->copy_gc, 0, 6, cells);
924                         
925                         XCopyArea(c->dpy, pixmap, TARGET(c), c->copy_gc, 0, 0, 3, 10, 3, 0);
926                         {
927                                 XImage *image = XGetImage(c->dpy, pixmap, 0, 0, 3, 10, cells, ZPixmap);
928                                 XPutImage(c->dpy, TARGET(c), c->copy_gc, image, 0, 0, 6, 0, 3, 10);
929                                 XDestroyImage(image);
930                         }
931                         
932                         XFreePixmap(c->dpy, pixmap);
933                         XSync(c->dpy, False);
934                 }
935         }
936 #endif /* TEST_PATTERN */
937
938 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
939   if (c->back_buf)
940     {
941       XdbeSwapInfo info[1];
942       info[0].swap_window = c->win;
943       info[0].swap_action = XdbeUndefined;
944       XdbeSwapBuffers(c->dpy, info, 1);
945     }
946   else
947 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
948     if (c->pix_buf)
949       {
950         XCopyArea (c->dpy, c->pix_buf, c->win, c->copy_gc,
951                    0, 0, c->w, c->h, 0, 0);
952       }
953
954   return c->delay;
955 }
956
957 static void *
958 interference_init (Display *dpy, Window win)
959 {
960   struct inter_context *c = (struct inter_context *) calloc (1, sizeof(*c));
961   if(!c)
962     abort_no_mem();
963   inter_init(dpy, win, c);
964   return c;
965 }
966
967 static unsigned long
968 interference_draw (Display *dpy, Window win, void *closure)
969 {
970   struct inter_context *c = (struct inter_context *) closure;
971   return do_inter(c);
972 }
973
974 static void
975 interference_reshape (Display *dpy, Window window, void *closure, 
976                  unsigned int w, unsigned int h)
977 {
978   struct inter_context *c = (struct inter_context *) closure;
979   XWindowAttributes xgwa;
980   Bool dbuf = (c->pix_buf
981 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
982                || c->back_buf
983 # endif
984                );
985
986   c->w = w;
987   c->h = h;
988
989 #ifdef USE_XIMAGE
990   destroy_image(dpy, c);
991   c->ximage = 0;
992 #endif
993
994   if(c->pix_buf)
995     XFreePixmap(dpy, c->pix_buf);
996   c->pix_buf = None;
997
998   XGetWindowAttributes(dpy, window, &xgwa);
999   xgwa.width = w;
1000   xgwa.height = h;
1001   create_image(dpy, c, &xgwa);
1002   if(dbuf)
1003     create_pix_buf(dpy, window, c, &xgwa);
1004 }
1005
1006 static Bool
1007 interference_event (Display *dpy, Window window, void *closure, XEvent *event)
1008 {
1009 #if HAVE_XSHM_EXTENSION
1010   struct inter_context *c = (struct inter_context *) closure;
1011
1012   if(c->use_shm && event->type == XShmGetEventBase(dpy) + ShmCompletion)
1013   {
1014     c->shm_can_draw = True;
1015     return True;
1016   }
1017 #endif
1018   return False;
1019 }
1020
1021 static void
1022 interference_free (Display *dpy, Window window, void *closure)
1023 {
1024   struct inter_context *c = (struct inter_context *) closure;
1025   inter_free(dpy, c);
1026 }
1027
1028 XSCREENSAVER_MODULE ("Interference", interference)
1029