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