]> git.hungrycats.org Git - xscreensaver/blob - hacks/strange.c
From https://www.jwz.org/xscreensaver/xscreensaver-6.09.tar.gz
[xscreensaver] / hacks / strange.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* strange --- strange attractors */
3
4 #if 0
5 static const char sccsid[] = "@(#)strange.c     5.00 2000/11/01 xlockmore";
6 #endif
7
8 /*-
9 * Copyright (c) 1997 by Massimino Pascal <Pascal.Massimon@ens.fr>
10 *
11 * Permission to use, copy, modify, and distribute this software and its
12 * documentation for any purpose and without fee is hereby granted,
13 * provided that the above copyright notice appear in all copies and that
14 * both that copyright notice and this permission notice appear in
15 * supporting documentation.
16 *
17 * This file is provided AS IS with no warranties of any kind.  The author
18 * shall have no liability with respect to the infringement of copyrights,
19 * trade secrets or any patents by this file or any part thereof.  In no
20 * event will the author be liable for any lost revenue or profits or
21 * other special, indirect and consequential damages.
22 *
23 * Revision History:
24 * 10-Apr-2017: dmo2118@gmail.com: Enhancements for accumulator mode:
25 *              Performance tuning, varying colors, fixed wobbliness.
26 *              New options: point size, zoom, brightness, motion blur.
27 * 08-Apr-2017: dmo2118@gmail.com: Merged with current xlockmore strange.c.
28 *              Also resurrected the -curve parameter from XScreenSaver 2.31.
29 *              Multi-monitor fixes.
30 *              More allocation checks.
31 * 13-Apr-2010: Added useAccumulator, VARY_SPEED_TO_AVOID_BOREDOM.
32 * 22-Dec-2004: TDA: Replace Gauss_Rand with a real Gaussian.
33 * 01-Nov-2000: Allocation checks
34 * 30-Jul-1998: sineswiper@resonatorsoft.com: added curve factor (discovered
35 *              while experimenting with the Gauss_Rand function).
36 * 10-May-1997: jwz AT jwz.org: turned into a standalone program.
37 *              Made it render into an offscreen bitmap and then copy
38 *              that onto the screen, to reduce flicker.
39 *
40 * strange attractors are not so hard to find...
41 */
42
43 /* Be sure to try:
44 * ./strange -points 500000 -delay 0 -point-size 3
45 * ./strange -points 500000 -delay 0 -point-size 2 -curve 5 -zoom 1.5 -brightness 0.75
46 */
47
48 /* TODO: Can anything be done about the overflow with -point-size 32? */
49
50 #ifdef STANDALONE
51 # define MODE_strange
52 # define DEFAULTS       "*delay: 10000 \n" \
53                                         "*ncolors: 100 \n" \
54                                         "*fpsSolid: True \n" \
55                                         "*ignoreRotation: True \n" \
56                                         "*useSHM: True \n" \
57                                         "*useThreads: True \n" \
58
59 /*                                  "*lowrez: True \n" \ */
60
61 # define SMOOTH_COLORS
62 # define release_strange 0
63 # define reshape_strange 0
64 # define strange_handle_event 0
65 # include "xlockmore.h"         /* from the xscreensaver distribution */
66 #else /* !STANDALONE */
67 # include "xlock.h"             /* from the xlockmore distribution */
68 # define ENTRYPOINT
69 #endif /* !STANDALONE */
70
71 #include <errno.h>
72 #include "pow2.h"
73 #include "thread_util.h"
74 #include "xshm.h"
75
76 #ifdef HAVE_INTTYPES_H
77 # include <inttypes.h>
78 #endif
79
80 #ifdef MODE_strange
81 #define DEF_CURVE       "10"
82 #define DEF_POINTS      "5500"
83 #define DEF_POINT_SIZE  "1"
84 #define DEF_ZOOM        "0.9" /* approx. 1 / 1.1 */
85 #define DEF_BRIGHTNESS  "1.0"
86 #define DEF_MOTION_BLUR "3.0" /* Formerly MERGE_FRAMES, but it's IIR now. */
87
88 static int curve;
89 static int points;
90 static int pointSize;
91 static float zoom;
92 static float brightness;
93 static float motionBlur;
94
95 static XrmOptionDescRec opts[] =
96 {
97                 {"-curve",        ".strange.curve",      XrmoptionSepArg, 0},
98                 {"-points",       ".strange.points",     XrmoptionSepArg, 0},
99                 {"-point-size",   ".strange.pointSize",  XrmoptionSepArg, 0},
100                 {"-zoom",         ".strange.zoom",       XrmoptionSepArg, 0},
101                 {"-brightness",   ".strange.brightness", XrmoptionSepArg, 0},
102                 {"-motion-blur",  ".strange.motionBlur", XrmoptionSepArg, 0},
103                 THREAD_OPTIONS
104 };
105 static argtype vars[] =
106 {
107                 {&curve,      "curve",      "Curve",      DEF_CURVE,       t_Int},
108                 {&points,     "points",     "Points",     DEF_POINTS,      t_Int},
109                 {&pointSize,  "pointSize",  "PointSize",  DEF_POINT_SIZE,  t_Int},
110                 {&zoom,       "zoom",       "Zoom",       DEF_ZOOM,        t_Float},
111                 {&brightness, "brightness", "Brightness", DEF_BRIGHTNESS,  t_Float},
112                 {&motionBlur, "motionBlur", "MotionBlur", DEF_MOTION_BLUR, t_Float},
113 };
114 static OptionStruct desc[] =
115 {
116                 {"-curve", "set the curve factor of the attractors"},
117                 {"-points", "change the number of points/iterations each frame"},
118                 {"-point-size", "change the size of individual points"},
119                 {"-zoom", "zoom in or out"},
120                 {"-brightness", "adjust the brightness for accumulator mode"},
121                 {"-motion-blur", "adds motion blur"},
122 };
123 ENTRYPOINT ModeSpecOpt strange_opts =
124 {sizeof opts / sizeof opts[0], opts,
125 sizeof vars / sizeof vars[0], vars, desc};
126
127 #ifdef USE_MODULES
128 ModStruct   strange_description =
129 {"strange", "init_strange", "draw_strange", (char *) NULL,
130 "init_strange", "init_strange", "free_strange", &strange_opts,
131 10000, 1, 1, 1, 64, 1.0, "",
132 "Shows strange attractors", 0, NULL};
133 #endif
134
135 #ifdef HAVE_JWXYZ
136 # define NO_DBUF
137 #endif
138
139 typedef float DBL;
140 typedef int PRM;
141
142 #define UNIT_BITS 12
143 #define UNIT (1<<UNIT_BITS)
144 #define UNIT2 (1<<14)
145 #define COLOR_BITS 16
146 /* #define UNIT2 (3140*UNIT/1000) */
147
148 #define SKIP_FIRST      100
149 #define DBL_To_PRM(x)  (PRM)( (DBL)(UNIT)*(x) )
150
151
152 #define DO_FOLD(a) (a)<0 ? -A->Fold[ (-(a))&(UNIT2-1) ] : A->Fold[ (a)&(UNIT2-1) ]
153
154 #if 0
155 #define DO_FOLD(a) (a)<-UNIT2 ? -A->Fold[(-(a))%UNIT2] : (a)<0 ? -A->Fold[ -(a) ] :\
156 (a)>UNIT2 ? A->Fold[ (a)%UNIT2 ] : A->Fold[ (a) ]
157 #define DO_FOLD(a) DBL_To_PRM( sin( (DBL)(a)/UNIT ) )
158 #define DO_FOLD(a) (a)<0 ? DBL_To_PRM( exp( 16.0*(a)/UNIT2 ) )-1.0 : \
159 DBL_To_PRM( 1.0-exp( -16.0*(a)/UNIT2 ) )
160 #endif
161
162 /* useAccumulator performs two functions:
163 * If it is defined, then support for the accumulator will be compiled.
164 * It is also the condition for which the accumulator renderer will engage.
165 */
166 #define useAccumulator (A->Max_Pt > 6000)
167 #define ACC_GAMMA 10.0
168 #define DEF_NUM_COLS 150
169 /* Extra options: */
170 #define VARY_SPEED_TO_AVOID_BOREDOM
171 /*#define AUTO_ZOOM*/ /* Works funny, but try it with -curve 5. */
172
173 /******************************************************************/
174
175 #define MAX_PRM 3*5
176
177 #if defined(__BIGGEST_ALIGNMENT__) \
178         && (defined(__GNUC__) \
179     && (__GNUC__ == 4 && __GNUC_MINOR__ >= 7 || __GNUC__ >= 5) \
180         || defined(__clang__))
181 # define ALIGNED __attribute__((aligned(__BIGGEST_ALIGNMENT__)))
182 # define ALIGN_HINT(ptr) __builtin_assume_aligned((ptr), __BIGGEST_ALIGNMENT__)
183 #else
184 # define ALIGNED
185 # define ALIGN_HINT(ptr) (ptr)
186 # ifndef __BIGGEST_ALIGNMENT__
187 #  define __BIGGEST_ALIGNMENT__ (sizeof(void *))
188 # endif
189 #endif
190
191
192 #ifdef HAVE_INTTYPES_H
193 typedef uint16_t ALIGNED PIXEL0;
194 typedef uint32_t PIXEL0X;
195 typedef uint32_t ALIGNED PIXEL1;
196 #else
197 typedef unsigned short ALIGNED PIXEL0;
198 typedef unsigned long PIXEL0X;
199 typedef unsigned long ALIGNED PIXEL1;
200 #endif
201
202 static const union {
203 #ifdef HAVE_INTTYPES_H
204         uint16_t signature;
205         uint8_t bytes[2];
206 #else
207         unsigned short signature;
208         unsigned char bytes[2];
209 #endif
210 } byte_order_union = {MSBFirst};
211
212 #define LOCAL_BYTE_ORDER byte_order_union.bytes[1]
213
214 typedef struct _ATTRACTOR {
215         DBL         Prm1[MAX_PRM], Prm2[MAX_PRM];
216         PRM         Prm[MAX_PRM], *Fold;
217         void        (*Iterate) (const struct _ATTRACTOR *, PRM, PRM, PRM *, PRM *);
218         void       *Buffer1, *Buffer2; /* Either XPoint or XRectangle. */
219         int         Cur_Pt, Max_Pt;
220         int         Col, Count, Speed;
221         int         Width, Height;
222         Pixmap      dbuf;       /* jwz */
223         GC          dbuf_gc;
224         #ifdef useAccumulator
225                 int visualClass;
226                 size_t alignedWidth;
227                 unsigned long rMask, gMask, bMask;
228                 unsigned rShift, gShift, bShift;
229                 PIXEL0 blurFac; /* == 0 when no motion blur is taking place. */
230                 double colorFac;
231                 XColor *palette;
232                 unsigned numCols;
233                 unsigned long *cols;
234                 XImage *accImage;
235                 XShmSegmentInfo shmInfo;
236                 struct _THREAD **threads;
237                 struct threadpool pool;
238         #endif
239 } ATTRACTOR;
240
241 static ATTRACTOR *Root = (ATTRACTOR *) NULL;
242
243 #ifdef useAccumulator
244 typedef struct _THREAD {
245         const ATTRACTOR *Attractor;
246         unsigned long Rnd;
247         size_t y0, y1, y2;
248
249         PIXEL0 **accMap;
250         PIXEL0 *bloomRows;
251         PIXEL1 *colorRow;
252         PIXEL0 *motionBlur;
253
254         PRM xmin, xmax, ymin, ymax;
255         unsigned pixelCount;
256 } THREAD;
257 #endif
258
259 static DBL  Amp_Prm[MAX_PRM] =
260 {
261         1.0, 3.5, 3.5, 2.5, 4.7,
262         1.0, 3.5, 3.6, 2.5, 4.7,
263         1.0, 1.5, 2.2, 2.1, 3.5
264 };
265 static DBL  Mid_Prm[MAX_PRM] =
266 {
267         0.0, 1.5, 0.0, .5, 1.5,
268         0.0, 1.5, 0.0, .5, 1.5,
269         0.0, 1.5, -1.0, -.5, 2.5,
270 };
271
272 #if 0
273
274 static inline uint64_t frq(void)
275 {
276         return 1000000;
277 }
278
279 static inline uint64_t tick(void)
280 {
281         struct timeval tv;
282         gettimeofday(&tv, NULL);
283         return tv.tv_sec * frq() + tv.tv_usec;
284 }
285
286 #endif
287
288 static      DBL
289 Old_Gauss_Rand(DBL c, DBL A, DBL S)
290 {
291         DBL         y,z;
292
293         y = (DBL) LRAND() / MAXRAND;
294         z = curve / 10;
295         y = A * (z - exp(-y * y * S)) / (z - exp(-S));
296         if (NRAND(2))
297                 return (c + y);
298         else
299                 return (c - y);
300 }
301
302 #if 0
303 /* dmo2118: seems to be responsible for lots of boring-looking rings */
304
305 /* I don't know that it makes a difference, but this one actually *is*
306    a Gaussian.  [TDA] */
307
308 /* Generate Gaussian random number: mean c, "amplitude" A (actually
309    A is 3*standard deviation).  'S' is unused.  */
310
311 /* Note this generates a pair of gaussian variables, so it saves one
312    to give out next time it's called */
313
314 static double
315 Gauss_Rand(DBL c, DBL A, DBL S)
316 {
317         static double d;
318         static Bool ready = 0;
319         if(ready) {
320                 ready = 0;
321                 return c + A/3 * d;
322         } else {
323                 double x, y, w;
324                 do {
325                         x = 2.0 * (double)LRAND() / MAXRAND - 1.0;
326                         y = 2.0 * (double)LRAND() / MAXRAND - 1.0;
327                         w = x*x + y*y;
328                 } while(w >= 1.0);
329
330                 w = sqrt((-2 * log(w))/w);
331                 ready = 1;
332                 d =          x * w;
333                 return c + A/3 * y * w;
334         }
335 }
336 #endif
337
338 static void
339 Random_Prm(DBL * Prm)
340 {
341         int         i;
342
343         for (i = 0; i < MAX_PRM; ++i) {
344 #if 0
345                 if (curve == 10)
346                         Prm[i] = Gauss_Rand (Mid_Prm[i], Amp_Prm[i], 4.0);
347                 else
348 #endif
349                         Prm[i] = Old_Gauss_Rand (Mid_Prm[i], Amp_Prm[i], 4.0);
350         }
351 }
352
353 /***************************************************************/
354
355   /* 2 examples of non-linear map */
356
357 static void
358 Iterate_X2(const ATTRACTOR * A, PRM x, PRM y, PRM * xo, PRM * yo)
359 {
360         PRM         xx, yy, xy, x2y, y2x, Tmp;
361
362         xx = (x * x) >> UNIT_BITS;
363         x2y = (xx * y) >> UNIT_BITS;
364         yy = (y * y) >> UNIT_BITS;
365         y2x = (yy * x) >> UNIT_BITS;
366         xy = (x * y) >> UNIT_BITS;
367
368         Tmp = A->Prm[1] * xx + A->Prm[2] * xy + A->Prm[3] * yy + A->Prm[4] * x2y;
369         Tmp = A->Prm[0] - y + (Tmp >> UNIT_BITS);
370         *xo = DO_FOLD(Tmp);
371         Tmp = A->Prm[6] * xx + A->Prm[7] * xy + A->Prm[8] * yy + A->Prm[9] * y2x;
372         Tmp = A->Prm[5] + x + (Tmp >> UNIT_BITS);
373         *yo = DO_FOLD(Tmp);
374 }
375
376 static void
377 Iterate_X3(const ATTRACTOR * A, PRM x, PRM y, PRM * xo, PRM * yo)
378 {
379         PRM         xx, yy, xy, x2y, y2x, Tmp_x, Tmp_y, Tmp_z;
380
381         xx = (x * x) >> UNIT_BITS;
382         x2y = (xx * y) >> UNIT_BITS;
383         yy = (y * y) >> UNIT_BITS;
384         y2x = (yy * x) >> UNIT_BITS;
385         xy = (x * y) >> UNIT_BITS;
386
387         Tmp_x = A->Prm[1] * xx + A->Prm[2] * xy + A->Prm[3] * yy + A->Prm[4] * x2y;
388         Tmp_x = A->Prm[0] - y + (Tmp_x >> UNIT_BITS);
389         Tmp_x = DO_FOLD(Tmp_x);
390
391         Tmp_y = A->Prm[6] * xx + A->Prm[7] * xy + A->Prm[8] * yy + A->Prm[9] * y2x;
392         Tmp_y = A->Prm[5] + x + (Tmp_y >> UNIT_BITS);
393
394         Tmp_y = DO_FOLD(Tmp_y);
395
396         Tmp_z = A->Prm[11] * xx + A->Prm[12] * xy + A->Prm[13] * yy + A->Prm[14] * y2x;
397         Tmp_z = A->Prm[10] + x + (Tmp_z >> UNIT_BITS);
398 #ifdef __GNUC__
399         // Without this 'volatile', GCC incorrectly assumes that Tmp_z1
400         // cannot == 0, and optimizes away the 'if (!Tmp_z0)'.
401         volatile
402 #endif
403     PRM Tmp_z0 = UNIT + ((Tmp_z * Tmp_z) >> UNIT_BITS);
404
405         /* Can happen with -curve 9. */
406         if (!Tmp_z0)
407                 Tmp_z0 = 1;
408
409 #ifdef HAVE_INTTYPES_H
410         {
411                 uint64_t Tmp_z1 = (1 << 30) / Tmp_z0;
412                 *xo = (Tmp_x * Tmp_z1) >> (30 - UNIT_BITS);
413                 *yo = (Tmp_y * Tmp_z1) >> (30 - UNIT_BITS);
414         }
415 #else
416     *xo = (Tmp_x * UNIT) / Tmp_z0;
417     *yo = (Tmp_y * UNIT) / Tmp_z0;
418 #endif
419 }
420
421 static void (*Funcs[2]) (const ATTRACTOR *, PRM, PRM, PRM *, PRM *) = {
422         Iterate_X2, Iterate_X3
423 };
424
425 /***************************************************************/
426
427 ENTRYPOINT void
428 free_strange(ModeInfo *mi)
429 {
430         Display *display = MI_DISPLAY(mi);
431         ATTRACTOR  *A = &Root[MI_SCREEN(mi)];
432
433         if (A->Buffer1 != NULL) {
434                 free(A->Buffer1);
435                 A->Buffer1 = (XPoint *) NULL;
436         }
437         if (A->Buffer2 != NULL) {
438                 free(A->Buffer2);
439                 A->Buffer2 = (XPoint *) NULL;
440         }
441         if (A->dbuf) {
442                 XFreePixmap(display, A->dbuf);
443                 A->dbuf = None;
444         }
445         if (A->dbuf_gc) {
446                 XFreeGC(display, A->dbuf_gc);
447                 A->dbuf_gc = None;
448         }
449         if (A->Fold != NULL) {
450                 free(A->Fold);
451                 A->Fold = (PRM *) NULL;
452         }
453
454 #ifdef useAccumulator
455         if (useAccumulator) {
456                 if (A->pool.count) {
457                         threadpool_destroy (&A->pool);
458                         A->pool.count = 0;
459                 }
460
461                 free (A->threads);
462                 A->threads = NULL;
463
464                 if (A->accImage) {
465                         destroy_xshm_image (display, A->accImage, &A->shmInfo);
466                         A->accImage = NULL;
467                 }
468
469                 free (A->palette);
470                 A->palette = NULL;
471
472                 if (A->cols) {
473                         if (A->visualClass != TrueColor && A->numCols > 2)
474                                 XFreeColors (display, MI_COLORMAP(mi), A->cols, A->numCols, 0);
475                         free (A->cols);
476                         A->cols = NULL;
477                 }
478         }
479 #endif
480 }
481
482 /* NRAND() is also in use; making three PRNGs in total here. */
483
484 /* ISO/IEC 9899 suggests this one. Doesn't require 64-bit math. */
485 #define GOODRND(seed) ((seed) = (((seed) * 1103515245 + 12345) & 0x7fffffff))
486 #define GOODRND_BITS 31
487
488 /* Extremely cheap entropy: this is often a single LEA instruction. */
489 #define CHEAPRND(seed) ((seed) = (seed) * 5)
490
491 static void
492 init_draw (const ATTRACTOR *A, PRM *x, PRM *y,
493            PRM *xmin, PRM *ymin, PRM *xmax, PRM *ymax, unsigned long *rnd)
494 {
495         int         n;
496         PRM         xo, yo;
497
498         *xmin = UNIT;
499         *ymin = UNIT;
500         *xmax = -UNIT;
501         *ymax = -UNIT;
502
503         *x = *y = DBL_To_PRM(.0);
504         for (n = SKIP_FIRST; n; --n) {
505                 (*A->Iterate) (A, *x, *y, &xo, &yo);
506
507 #ifdef AUTO_ZOOM
508                 if (xo > *xmax)
509                         *xmax = xo;
510                 if (xo < *xmin)
511                         *xmin = xo;
512                 if (yo > *ymax)
513                         *ymax = yo;
514                 if (yo < *ymin)
515                         *ymin = yo;
516 #endif
517
518                 /* Can't use NRAND(), because that modifies global state in a
519                  * thread-unsafe way.
520                  */
521                 *x = xo + (GOODRND(*rnd) >> (GOODRND_BITS - 3)) - 4;
522                 *y = yo + (GOODRND(*rnd) >> (GOODRND_BITS - 3)) - 4;
523         }
524 }
525
526 static void
527 recalc_scale (const ATTRACTOR *A, PRM xmin, PRM ymin, PRM xmax, PRM ymax,
528               DBL *Lx, DBL *Ly, PRM *mx, PRM *my)
529 {
530 #ifndef AUTO_ZOOM
531         xmin = -UNIT;
532         ymin = -UNIT;
533         xmax = UNIT;
534         ymax = UNIT;
535 #endif
536
537         *Lx = zoom * (DBL) A->Width / (xmax - xmin);
538         *Ly = -zoom * (DBL) A->Height / (ymax - ymin);
539         *mx = A->Width/2 - (xmax + xmin) * *Lx / 2;
540         *my = A->Height/2 - (ymax + ymin) * *Ly / 2;
541 }
542
543 #ifdef useAccumulator
544
545 static void
546 thread_destroy (void *Self_Raw)
547 {
548         THREAD     *T = (THREAD *)Self_Raw;
549
550         aligned_free (T->accMap[0]);
551         (void) free((void *) T->accMap);
552         aligned_free (T->bloomRows);
553         aligned_free (T->colorRow);
554         aligned_free (T->motionBlur);
555 }
556
557 static int
558 thread_create (void *Self_Raw, struct threadpool *pool, unsigned id)
559 {
560         THREAD     *T = (THREAD *)Self_Raw;
561         int         i;
562         const ATTRACTOR *A = GET_PARENT_OBJ(ATTRACTOR, pool, pool);
563
564         memset (T, 0, sizeof(*T));
565
566         T->Attractor = A;
567         A->threads[id] = T;
568
569         T->Rnd = random();
570
571         /* The gap between y0 and y1 is to preheat the box blur. */
572         T->y1 = A->Height * id / pool->count;
573         T->y2 = A->Height * (id + 1) / pool->count;
574         T->y0 = T->y1 < pointSize ? 0 : T->y1 - pointSize;
575
576         T->accMap = (PIXEL0**)calloc(A->Height,sizeof(PIXEL0*));
577         if (!T->accMap) {
578                 thread_destroy (T);
579                 return ENOMEM;
580         }
581         T->accMap[0] = NULL;
582         if (aligned_malloc ((void **)&T->accMap[0], __BIGGEST_ALIGNMENT__,
583                 A->alignedWidth * A->Height * sizeof(PIXEL0))) {
584                 thread_destroy (T);
585                 return ENOMEM;
586         }
587         for (i=0;i<A->Height;i++)
588                 T->accMap[i] = T->accMap[0] + A->alignedWidth * i;
589
590         if (aligned_malloc ((void **)&T->bloomRows, __BIGGEST_ALIGNMENT__,
591                 A->alignedWidth * (pointSize + 2) * sizeof(*T->bloomRows))) {
592                 thread_destroy (T);
593                 return ENOMEM;
594         }
595         if (aligned_malloc ((void **)&T->colorRow, __BIGGEST_ALIGNMENT__,
596                 A->alignedWidth * sizeof(*T->colorRow))) {
597                 thread_destroy (T);
598                 return ENOMEM;
599         }
600         if (A->blurFac) {
601                 if (aligned_malloc ((void **)&T->motionBlur, __BIGGEST_ALIGNMENT__,
602                         A->alignedWidth * (T->y2 - T->y1) * sizeof(*T->motionBlur))) {
603                         thread_destroy (T);
604                         return ENOMEM;
605                 }
606
607                 memset (T->motionBlur, 0, A->alignedWidth * (T->y2 - T->y1) * sizeof(*T->motionBlur));
608         }
609
610         return 0;
611 }
612
613 static void
614 points_thread (void *Self_Raw)
615 {
616         /* Restricts viewable area to 2^(32-L_Bits). */
617         const unsigned L_Bits = 19; /* Max. image size: 8192x8192. */
618
619         THREAD     *T = (THREAD *)Self_Raw;
620         const ATTRACTOR *A = T->Attractor;
621         int         n;
622         PRM         x, y, xo, yo;
623         DBL         Lx, Ly;
624         PRM         iLx, iLy, cx, cy;
625         void        (*Iterate) (const ATTRACTOR *, PRM, PRM, PRM *, PRM *);
626         unsigned    Rnd;
627         PRM         xmax, xmin, ymax, ymin;
628
629         Iterate = A->Iterate;
630
631         if (useAccumulator) {
632                 memset (T->accMap[0], 0, sizeof(PIXEL0) * A->alignedWidth * A->Height);
633         }
634
635         /* Using CHEAPRND() by itself occasionally gets stuck at 0 mod 8, so seed it
636          * from GOODRND().
637          */
638         init_draw (A, &x, &y, &xmin, &ymin, &xmax, &ymax, &T->Rnd);
639         recalc_scale (A, xmin, ymin, xmax, ymax, &Lx, &Ly, &cx, &cy);
640
641         Rnd = GOODRND(T->Rnd);
642
643         iLx = Lx * (1 << L_Bits);
644         iLy = Ly * (1 << L_Bits);
645         if (!iLx) /* Can happen with small windows. */
646                 iLx = 1;
647         if (!iLy)
648                 iLy = 1;
649
650         for (n = T->Attractor->Max_Pt / A->pool.count; n; --n) {
651                 unsigned mx,my;
652                 (*Iterate) (T->Attractor, x, y, &xo, &yo);
653                 mx = ((iLx * x) >> L_Bits) + cx;
654                 my = ((iLy * y) >> L_Bits) + cy;
655                 /* Fun trick: making m[x|y] unsigned means we can skip mx<0 && my<0. */
656                 if (mx<A->Width && my<A->Height)
657                         T->accMap[my][mx]++;
658
659                 #ifdef AUTO_ZOOM
660                 if (xo > xmax)
661                         xmax = xo;
662                 if (xo < xmin)
663                         xmin = xo;
664                 if (yo > ymax)
665                         ymax = yo;
666                 if (yo < ymin)
667                         ymin = yo;
668
669                 if (!(n & 0xfff)) {
670                         recalc_scale (A, xmin, ymin, xmax, ymax, &Lx, &Ly, &cx, &cy);
671                 }
672                 #endif
673
674                 /* Skimp on the randomness. */
675                 x = xo + (CHEAPRND(Rnd) >> (sizeof(Rnd) * 8 - 3)) - 4;
676                 y = yo + (CHEAPRND(Rnd) >> (sizeof(Rnd) * 8 - 3)) - 4;
677         }
678 }
679
680 static void
681 rasterize_thread (void *Self_Raw)
682 {
683         THREAD     *T = (THREAD *)Self_Raw;
684         const ATTRACTOR *A = T->Attractor;
685         unsigned    i, j, k;
686         PRM         xmax = 0, xmin = A->Width, ymax = 0, ymin = A->Height;
687         unsigned long colorScale =
688                 (double)A->Width * A->Height
689                 * (1 << COLOR_BITS) * brightness
690                 * A->colorFac
691                 * (zoom * zoom) / (0.9 * 0.9)
692                 / 640.0 / 480.0
693                 / (pointSize * pointSize)
694                 * 800000.0
695                 / (float)A->Max_Pt
696                 * (float)A->numCols/256;
697         #ifdef VARY_SPEED_TO_AVOID_BOREDOM
698         unsigned    pixelCount = 0;
699         #endif
700         PIXEL0     *motionBlurRow = T->motionBlur;
701         void       *outRow = (char *)A->accImage->data
702                              + A->accImage->bytes_per_line * T->y1;
703
704         /* Clang needs these for loop-vectorizing; A->Width doesn't work. */
705         unsigned    w = A->Width, aw = A->alignedWidth;
706
707         if (A->numCols == 2) /* Brighter for monochrome. */
708                 colorScale *= 4;
709
710         /* bloomRows: row ring buffer, bloom accumulator, in that order. */
711         memset (T->bloomRows, 0, (pointSize + 1) * aw * sizeof(*T->bloomRows));
712
713         /* This code is highly amenable to auto-vectorization; on GCC use -O3 to get
714          * that. On x86-32, also add -msse2.
715          */
716         for (j=T->y0;j<T->y2;j++) {
717                 Bool has_col = False;
718                 int accum = 0;
719
720                 PIXEL0 *bloomRow =
721                         ALIGN_HINT(T->bloomRows + A->alignedWidth * (j % pointSize));
722                 PIXEL0 *accumRow =
723                         ALIGN_HINT(T->bloomRows + A->alignedWidth * pointSize);
724                 PIXEL1 *colRow = T->colorRow;
725
726                 /* Moderately fast bloom.  */
727
728                 for (i=0;i<aw;i++) {
729                         accumRow[i] -= bloomRow[i];
730                         bloomRow[i] = 0;
731                 }
732
733                 for (k=0;k<A->pool.count;k++) {
734                         const PIXEL0 *inRow = ALIGN_HINT(A->threads[k]->accMap[j]);
735                         for (i=0;i<aw;i++) {
736                                 /* Lots of last-level cache misses. Such is life. */
737                                 bloomRow[i] += inRow[i];
738                         }
739                 }
740
741                 /* Hardware prefetching works better going forwards than going backwards.
742                  * Since this blurs/blooms/convolves in-place, it expands points to the
743                  * right instead of to the left.
744                  */
745                 for (i=0;i<pointSize-1;i++)
746                         accum += bloomRow[i];
747
748                 for (i=0;i<aw;i++) {
749                         PIXEL0 oldBloom = bloomRow[i];
750
751                         /* alignedWidth has extra padding for this one line. */
752                         accum += bloomRow[i+pointSize-1];
753
754                         bloomRow[i] = accum;
755                         accumRow[i] += accum;
756                         accum -= oldBloom;
757                 }
758
759                 if (j>=T->y1) {
760                         PIXEL0 *accumRow1 = A->blurFac ? motionBlurRow : accumRow;
761                         PIXEL0 blurFac = A->blurFac;
762
763                         if (blurFac) {
764                                 /* TODO: Do I vectorize OK? */
765                                 if (blurFac == 0x8000) {
766                                         /* Optimization for the default. */
767                                         for (i=0;i<aw;i++)
768                                                 motionBlurRow[i] = (motionBlurRow[i] >> 1) + accumRow[i];
769                                 } else {
770                                         for (i=0;i<aw;i++)
771                                                 motionBlurRow[i] = (PIXEL0)(((PIXEL0X)motionBlurRow[i] * blurFac) >> 16) + accumRow[i];
772                                 }
773
774                                 motionBlurRow += aw;
775                         }
776
777                         for (i=0;i<aw;i++) {
778                                 unsigned col = (accumRow1[i] * colorScale) >> COLOR_BITS;
779                                 if (col>A->numCols-1) {
780                                         col = A->numCols-1;
781                                 }
782                                 #ifdef VARY_SPEED_TO_AVOID_BOREDOM
783                                 if (col>0) {
784                                         if (col<A->numCols-1)  /* we don't count maxxed out pixels */
785                                                 pixelCount++;
786                                         if (i > xmax)
787                                                 xmax = i;
788                                         if (i < xmin)
789                                                 xmin = i;
790                                         has_col = True;
791                                 }
792                                 #endif
793                                 colRow[i] = A->cols[col];
794                         }
795
796                         #ifdef VARY_SPEED_TO_AVOID_BOREDOM
797                         if (has_col) {
798                                 if (j > ymax)
799                                         ymax = j;
800                                 if (j < ymin)
801                                         ymin = j;
802                         }
803                         #endif
804
805 #if 0
806                         for (i=0;i<aw;i++) {
807                                 if (MI_NPIXELS(mi) < 2)
808                                         XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
809                                 else
810                                         /*XSetForeground(display, gc, MI_PIXEL(mi, A->Col % MI_NPIXELS(mi)));*/
811                                         XSetForeground(display, gc, cols[col].pixel);
812
813                                 if (A->dbuf != None) {
814                                         XSetForeground(display, A->dbuf_gc, cols[col].pixel);
815                                         XDrawPoint(display, A->dbuf, A->dbuf_gc, i, j);
816                                 } else {
817                                         XSetForeground(display, gc, cols[col].pixel);
818                                         XDrawPoint(display, window, gc, i, j);
819                                 }
820                         }
821 #endif
822
823                         if (A->accImage->bits_per_pixel==32 &&
824                                 A->accImage->byte_order==LOCAL_BYTE_ORDER) {
825                                 for (i=0;i<aw;i++)
826                                         ((uint32_t *)outRow)[i] = colRow[i];
827                         } else if (A->accImage->bits_per_pixel==16 &&
828                                 A->accImage->byte_order==LOCAL_BYTE_ORDER) {
829                                 for (i=0;i<aw;i++)
830                                         ((uint16_t *)outRow)[i] = colRow[i];
831                         } else if (A->accImage->bits_per_pixel==8) {
832                                 for (i=0;i<aw;i++)
833                                         ((uint8_t *)outRow)[i] = colRow[i];
834                         } else {
835                                 for (i=0;i<w;i++)
836                                         XPutPixel (A->accImage, i, j, colRow[i]);
837                         }
838
839                         outRow = (char *)outRow + A->accImage->bytes_per_line;
840                 }
841         }
842
843         /*
844         uint64_t dt = 0;
845         uint64_t t0 = tick();
846         dt += tick() - t0;
847
848         printf("B %g\t(%d,%d)\t%dx%d\t%d\n", 1000.0 * dt / frq(),
849                 xmin, ymin, xmax - xmin, ymax - ymin, pixelCount);
850         */
851
852         T->xmax = xmax;
853         T->xmin = xmin;
854         T->ymax = ymax;
855         T->ymin = ymin;
856         T->pixelCount = pixelCount;
857 }
858
859 static void
860 ramp_color (const XColor *color_in, XColor *color_out, unsigned i, unsigned n)
861 {
862         float li;
863         #define MINBLUE 1
864         #define FULLBLUE 128
865         #define LOW_COLOR(c) ((c)*li/FULLBLUE)
866         #define HIGH_COLOR(c) ((65535-(c))*(li-FULLBLUE)/(256-FULLBLUE)+(c))
867         li = MINBLUE
868                 + (255.0-MINBLUE) * log(1.0 + ACC_GAMMA*(float)i/n)
869                 / log(1.0 + ACC_GAMMA);
870         if (li<FULLBLUE) {
871                 color_out->red = LOW_COLOR(color_in->red);
872                 color_out->green = LOW_COLOR(color_in->green);
873                 color_out->blue = LOW_COLOR(color_in->blue);
874         } else {
875                 color_out->red = HIGH_COLOR(color_in->red);
876                 color_out->green = HIGH_COLOR(color_in->green);
877                 color_out->blue = HIGH_COLOR(color_in->blue);
878         }
879 }
880
881 #endif
882
883 static void
884 draw_points (Display *display, Drawable d, GC gc, const void *Buf,
885              unsigned Count)
886 {
887         if (pointSize == 1)
888                 XDrawPoints(display, d, gc, (XPoint *)Buf, Count, CoordModeOrigin);
889         else
890                 XFillRectangles(display, d, gc, (XRectangle *)Buf, Count);
891 }
892
893 ENTRYPOINT void
894 draw_strange(ModeInfo * mi)
895 {
896         int         i, j, n, Cur_Pt;
897         PRM         x, y, xo, yo;
898         DBL         u;
899         void       *Buf;
900         Display    *display = MI_DISPLAY(mi);
901         Window      window = MI_WINDOW(mi);
902         GC          gc = MI_GC(mi);
903         DBL         Lx, Ly;
904         void        (*Iterate) (const ATTRACTOR *, PRM, PRM, PRM *, PRM *);
905         PRM         xmin, xmax, ymin, ymax;
906         ATTRACTOR  *A;
907         unsigned long Rnd = random();
908         int         cx, cy;
909
910         if (Root == NULL)
911                 return;
912         A = &Root[MI_SCREEN(mi)];
913         if (A->Fold == NULL)
914                 return;
915
916         Cur_Pt = A->Cur_Pt;
917         Iterate = A->Iterate;
918
919         u = (DBL) (A->Count) / 40000.0;
920         for (j = MAX_PRM - 1; j >= 0; --j)
921                 A->Prm[j] = DBL_To_PRM((1.0 - u) * A->Prm1[j] + u * A->Prm2[j]);
922
923         /* We collect the accumulation of the orbits in the 2d int array field. */
924
925         init_draw (A, &x, &y, &xmin, &ymin, &xmax, &ymax, &Rnd);
926         recalc_scale (A, xmin, ymin, xmax, ymax, &Lx, &Ly, &cx, &cy);
927
928         A->Cur_Pt = 0;
929
930         xmax = 0;
931         xmin = A->Width;
932         ymax = 0;
933         ymin = A->Height;
934
935         MI_IS_DRAWN(mi) = True;
936
937         #ifdef useAccumulator
938         if (useAccumulator) {
939                 int pixelCount = 0;
940
941                 threadpool_run (&A->pool, points_thread);
942
943                 if (A->visualClass == TrueColor) {
944                         XColor *src_color = &A->palette[A->Col % MI_NPIXELS(mi)];
945
946                         for (i=0;i<A->numCols;i++) {
947                                 XColor color;
948                                 ramp_color (src_color, &color, i, A->numCols);
949                                 A->cols[i] =
950                                         ((((unsigned long)color.red   << 16) >> A->rShift) & A->rMask) |
951                                         ((((unsigned long)color.green << 16) >> A->gShift) & A->gMask) |
952                                         ((((unsigned long)color.blue  << 16) >> A->bShift) & A->bMask);
953                         }
954                 }
955                 threadpool_wait (&A->pool);
956
957                 threadpool_run(&A->pool, rasterize_thread);
958                 threadpool_wait(&A->pool);
959
960                 for (i=0; i!=A->pool.count; ++i) {
961                         THREAD *T = A->threads[i];
962                         if (T->xmax > xmax)
963                                 xmax = T->xmax;
964                         if (T->xmin < xmin)
965                                 xmin = T->xmin;
966                         if (T->ymax > ymax)
967                                 ymax = T->ymax;
968                         if (T->ymin < ymin)
969                                 ymin = T->ymin;
970                         pixelCount += T->pixelCount;
971                 }
972
973                 put_xshm_image (display, A->dbuf != None ? A->dbuf : window,
974                                 A->dbuf != None ? A->dbuf_gc : gc, A->accImage,
975                                 0, 0, 0, 0, A->accImage->width, A->accImage->height,
976                                 &A->shmInfo);
977
978                 if (A->dbuf != None) {
979                         XCopyArea(display, A->dbuf, window, gc, 0, 0, A->Width, A->Height, 0, 0);
980                 }
981                 #ifdef VARY_SPEED_TO_AVOID_BOREDOM
982                         /* Increase the rate of change of the parameters if the attractor has become visually boring. */
983                         if ((xmax - xmin < Lx * DBL_To_PRM(.2)) && (ymax - ymin < Ly * DBL_To_PRM(.2))) {
984                                 A->Speed *= 1.25;
985                         } else if (pixelCount>0 && pixelCount<A->Width*A->Height/1000) {
986                                 A->Speed *= 1.25;  /* A->Count = 1000; */
987                         } else {
988                                 A->Speed = 4; /* reset to normal/default */
989                         }
990                         if (A->Speed > 32)
991                                 A->Speed = 32;
992                         A->Count += A->Speed;
993                         if (A->Count >= 1000) {
994                                 for (i = MAX_PRM - 1; i >= 0; --i)
995                                         A->Prm1[i] = A->Prm2[i];
996                                 Random_Prm(A->Prm2);
997                                 A->Count = 0;
998                         }
999                 #endif
1000         } else {
1001         #endif
1002         for (n = 0; n != A->Max_Pt; ++n) {
1003                 int x1, y1;
1004                 (*Iterate) (A, x, y, &xo, &yo);
1005                 x1 = (int) (Lx * x) + cx;
1006                 y1 = (int) (Ly * y) + cy;
1007                 if (pointSize == 1) {
1008                         XPoint *Buf = &((XPoint *)A->Buffer2)[n];
1009                         Buf->x = x1;
1010                         Buf->y = y1;
1011                 } else {
1012                         XRectangle *Buf = &((XRectangle *)A->Buffer2)[n];
1013                         /* Position matches bloom in accumulator mode. */
1014                         Buf->x = x1 - pointSize + 1;
1015                         Buf->y = y1;
1016                         Buf->width = pointSize;
1017                         Buf->height = pointSize;
1018                 }
1019
1020                 if (x1 > xmax)
1021                         xmax = x1;
1022                 if (x1 < xmin)
1023                         xmin = x1;
1024                 if (y1 > ymax)
1025                         ymax = y1;
1026                 if (y1 < ymin)
1027                         ymin = y1;
1028
1029                 A->Cur_Pt++;
1030                 /* (void) fprintf( stderr, "X,Y: %d %d    ", Buf->x, Buf->y ); */
1031                 x = xo + NRAND(8) - 4;
1032                 y = yo + NRAND(8) - 4;
1033         }
1034
1035         XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
1036
1037         if (A->dbuf != None) {          /* jwz */
1038                 XSetForeground(display, A->dbuf_gc, 0);
1039 /* XDrawPoints(display, A->dbuf, A->dbuf_gc, A->Buffer1,
1040   Cur_Pt,CoordModeOrigin); */
1041                 XFillRectangle(display, A->dbuf, A->dbuf_gc, 0, 0, A->Width, A->Height);
1042         } else {
1043                 draw_points(display, window, gc, A->Buffer1, Cur_Pt);
1044         }
1045
1046         if (MI_NPIXELS(mi) <= 2)
1047                 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
1048         else
1049                 XSetForeground(display, gc, MI_PIXEL(mi, A->Col % MI_NPIXELS(mi)));
1050
1051         if (A->dbuf != None) {
1052                 XSetForeground(display, A->dbuf_gc, 1);
1053                 draw_points(display, A->dbuf, A->dbuf_gc, A->Buffer2, A->Cur_Pt);
1054                 XCopyPlane(display, A->dbuf, window, gc, 0, 0, A->Width, A->Height, 0, 0, 1);
1055         } else
1056                 draw_points(display, window, gc, A->Buffer2, A->Cur_Pt);
1057
1058         #ifdef useAccumulator
1059         }
1060         #endif
1061
1062         Buf = A->Buffer1;
1063         A->Buffer1 = A->Buffer2;
1064         A->Buffer2 = Buf;
1065
1066         if ((xmax - xmin < Lx * DBL_To_PRM(.2)) && (ymax - ymin < Ly * DBL_To_PRM(.2)))
1067                 A->Count += 4 * A->Speed;
1068         else
1069                 A->Count += A->Speed;
1070         if (A->Count >= 1000) {
1071                 for (i = MAX_PRM - 1; i >= 0; --i)
1072                         A->Prm1[i] = A->Prm2[i];
1073                 Random_Prm(A->Prm2);
1074                 A->Count = 0;
1075         }
1076         A->Col++;
1077 #ifdef STANDALONE
1078     mi->recursion_depth = A->Count;
1079 #endif
1080 }
1081
1082
1083 /***************************************************************/
1084
1085 ENTRYPOINT void
1086 init_strange(ModeInfo * mi)
1087 {
1088         Display    *display = MI_DISPLAY(mi);
1089 #ifndef NO_DBUF
1090         GC          gc = MI_GC(mi);
1091 #endif
1092         ATTRACTOR  *Attractor;
1093         size_t      pointStructSize;
1094
1095     if (MI_WIDTH(mi) > 2560 || MI_HEIGHT(mi) > 2560)
1096       pointSize *= 3;  /* Retina displays */
1097
1098     pointStructSize =
1099                 pointSize == 1 ? sizeof (XPoint) : sizeof (XRectangle);
1100
1101         if (curve <= 0) curve = 10;
1102
1103         MI_INIT (mi, Root);
1104         Attractor = &Root[MI_SCREEN(mi)];
1105
1106         if (Attractor->Fold == NULL) {
1107                 int         i;
1108
1109                 if ((Attractor->Fold = (PRM *) calloc(UNIT2 + 1,
1110                                 sizeof (PRM))) == NULL) {
1111                         free_strange(mi);
1112                         return;
1113                 }
1114                 for (i = 0; i <= UNIT2; ++i) {
1115                         DBL         x;
1116
1117                         /* x = ( DBL )(i)/UNIT2; */
1118                         /* x = sin( M_PI/2.0*x ); */
1119                         /* x = sqrt( x ); */
1120                         /* x = x*x; */
1121                         /* x = x*(1.0-x)*4.0; */
1122                         x = (DBL) (i) / UNIT;
1123                         x = sin(x);
1124                         Attractor->Fold[i] = DBL_To_PRM(x);
1125                 }
1126         }
1127
1128         Attractor->Max_Pt = points;
1129
1130         if (Attractor->Buffer1 == NULL)
1131                 if ((Attractor->Buffer1 = calloc(Attractor->Max_Pt,
1132                                 pointStructSize)) == NULL) {
1133                         free_strange(mi);
1134                         return;
1135                 }
1136         if (Attractor->Buffer2 == NULL)
1137                 if ((Attractor->Buffer2 = calloc(Attractor->Max_Pt,
1138                                 pointStructSize)) == NULL) {
1139                         free_strange(mi);
1140                         return;
1141                 }
1142
1143         Attractor->Width = MI_WIDTH(mi);
1144         Attractor->Height = MI_HEIGHT(mi);
1145         Attractor->Cur_Pt = 0;
1146         Attractor->Count = 0;
1147         Attractor->Col = NRAND(MI_NPIXELS(mi));
1148         Attractor->Speed = 4;
1149
1150         Attractor->Iterate = Funcs[NRAND(2)];
1151         if (curve < 10) /* Avoid boring Iterate_X2. */
1152                 Attractor->Iterate = Iterate_X3;
1153
1154         Random_Prm(Attractor->Prm1);
1155         Random_Prm(Attractor->Prm2);
1156 #ifndef NO_DBUF
1157         if (Attractor->dbuf != None)
1158                 XFreePixmap(display, Attractor->dbuf);
1159         #ifdef useAccumulator
1160         #define A Attractor
1161         if (useAccumulator)
1162         {
1163                 Attractor->dbuf = None;
1164         }
1165         else
1166         #undef A
1167         #endif
1168         {
1169                 Attractor->dbuf = XCreatePixmap (display, MI_WINDOW(mi),
1170                         Attractor->Width, Attractor->Height,
1171                         /* useAccumulator ? MI_DEPTH(mi) : */ 1);
1172         }
1173         /* Allocation checked */
1174         if (Attractor->dbuf != None) {
1175                 XGCValues   gcv;
1176
1177                 gcv.foreground = 0;
1178                 gcv.background = 0;
1179 #ifndef HAVE_JWXYZ
1180                 gcv.graphics_exposures = False;
1181 #endif /* HAVE_JWXYZ */
1182                 gcv.function = GXcopy;
1183
1184                 if (Attractor->dbuf_gc != None)
1185                         XFreeGC(display, Attractor->dbuf_gc);
1186
1187                 if ((Attractor->dbuf_gc = XCreateGC(display, Attractor->dbuf,
1188 #ifndef HAVE_JWXYZ
1189                                 GCGraphicsExposures |
1190 #endif /* HAVE_JWXYZ */
1191                                GCFunction | GCForeground | GCBackground,
1192                                 &gcv)) == None) {
1193                         XFreePixmap(display, Attractor->dbuf);
1194                         Attractor->dbuf = None;
1195                 } else {
1196                         XFillRectangle(display, Attractor->dbuf, Attractor->dbuf_gc,
1197                                 0, 0, Attractor->Width, Attractor->Height);
1198                         XSetBackground(display, gc, MI_BLACK_PIXEL(mi));
1199                         XSetFunction(display, gc, GXcopy);
1200                 }
1201         }
1202 #endif
1203
1204
1205 #ifdef useAccumulator
1206         #define A Attractor
1207         if (useAccumulator) {
1208                 static const struct threadpool_class threadClass = {
1209                         sizeof(THREAD),
1210                         thread_create,
1211                         thread_destroy
1212                 };
1213                 Screen *screen = MI_SCREENPTR(mi);
1214                 int i;
1215                 unsigned maxThreads, threadCount;
1216                 unsigned bpp = visual_pixmap_depth (screen, MI_VISUAL(mi));
1217                 size_t threadAlign1 = 8 * thread_memory_alignment(display) - 1;
1218                 if (A->cols) {
1219                         if (A->visualClass != TrueColor && A->numCols > 2)
1220                                 XFreeColors (display, MI_COLORMAP(mi), A->cols, A->numCols, 0);
1221                         free (A->cols);
1222                 }
1223                 if (MI_NPIXELS(mi) <= 2) {
1224                         A->numCols = 2;
1225                         A->visualClass = StaticColor;
1226                 } else {
1227                         A->numCols = DEF_NUM_COLS;
1228                         A->visualClass = visual_class(screen, MI_VISUAL(mi));
1229                 }
1230
1231                 A->cols = calloc (A->numCols,sizeof(*A->cols));
1232                 if (!A->cols) {
1233                         free_strange(mi);
1234                         return;
1235                 }
1236
1237                 if (A->visualClass == TrueColor) {
1238                         /* Rebuilds ramp every frame. No need for XAllocColor. */
1239                         /* TODO: This could also include PseudoColor. */
1240                         visual_rgb_masks (screen, MI_VISUAL(mi),
1241                                 &A->rMask, &A->gMask, &A->bMask);
1242                         A->rShift = 31 - i_log2 (A->rMask);
1243                         A->gShift = 31 - i_log2 (A->gMask);
1244                         A->bShift = 31 - i_log2 (A->bMask);
1245
1246                         free (A->palette);
1247                         A->palette = malloc(MI_NPIXELS(mi) * sizeof(XColor));
1248                         if (!A->palette) {
1249                                 free_strange (mi);
1250                                 return;
1251                         }
1252
1253                         for (i=0;i<MI_NPIXELS(mi);i++)
1254                                 A->palette[i].pixel = MI_PIXEL(mi,i);
1255
1256                         XQueryColors (display, MI_COLORMAP(mi), A->palette, MI_NPIXELS(mi));
1257                 } else if (A->numCols == 2) {
1258                         A->cols[0] = MI_BLACK_PIXEL (mi);
1259                         A->cols[1] = MI_WHITE_PIXEL (mi);
1260                 } else {
1261                         /* No changing colors, unfortunately. */
1262                         XColor color;
1263
1264                         color.pixel = MI_PIXEL(mi,NRAND(MI_NPIXELS(mi)));
1265                         XQueryColor (display, MI_COLORMAP(mi), &color);
1266
1267                         for (;;) {
1268                                 for (i=0;i<A->numCols;i++) {
1269                                         XColor out_color;
1270                                         ramp_color (&color, &out_color, i, A->numCols);
1271                                         if (!XAllocColor (display, MI_COLORMAP(mi), &out_color))
1272                                                 break;
1273                                         A->cols[i] = out_color.pixel;
1274                                         /*
1275                                         if (!XAllocColor(MI_DISPLAY(mi), cmap, &cols[i])) {
1276                                         if (!XAllocColor(display, cmap, &cols[i])) {
1277                                                 cols[i].pixel = WhitePixel (display, DefaultScreen (display));
1278                                                 cols[i].red = cols[i].green = cols[i].blue = 0xFFFF;
1279                                         }
1280                                         */
1281                                 }
1282
1283                                 if (i==A->numCols)
1284                                         break;
1285
1286                                 XFreeColors (display, MI_COLORMAP(mi), A->cols, i, 0);
1287                                 A->numCols = A->numCols * 11 / 12;
1288                                 if (A->numCols < 2) {
1289                                         A->numCols = 0;
1290                                         free_strange (mi);
1291                                         abort();
1292                                         return;
1293                                 }
1294                         }
1295                 }
1296
1297                 /* Add slack for horizontal blur, then round up to the platform's SIMD
1298                  * alignment.
1299                  */
1300                 A->alignedWidth =
1301                         (((A->Width + pointSize) * sizeof(PIXEL0) + (__BIGGEST_ALIGNMENT__ - 1)) &
1302                                 ~(__BIGGEST_ALIGNMENT__ - 1)) / sizeof(PIXEL0);
1303
1304                 if (A->accImage)
1305                         destroy_xshm_image (display, A->accImage, &A->shmInfo);
1306                 A->accImage = create_xshm_image(display, MI_VISUAL(mi),
1307                         MI_DEPTH(mi), ZPixmap, &A->shmInfo,
1308                         ((A->alignedWidth * bpp + threadAlign1) & ~threadAlign1) / bpp,
1309                         A->Height);
1310
1311                 A->blurFac = (PIXEL0)(65536 * (motionBlur - 1) / (motionBlur + 1));
1312                 A->colorFac = 2 / (motionBlur + 1);
1313
1314                 /* Don't overdose on threads. */
1315                 threadCount = hardware_concurrency (display);
1316                 maxThreads = A->Height / (pointSize * 4);
1317                 if (maxThreads < 1)
1318                         maxThreads = 1;
1319                 if (threadCount > maxThreads)
1320                         threadCount = maxThreads;
1321
1322                 if (A->threads)
1323                         free (A->threads);
1324                 A->threads = malloc (threadCount * sizeof(*A->threads));
1325                 if (!A->threads) {
1326                         free_strange (mi);
1327                         return;
1328                 }
1329
1330                 if (A->pool.count)
1331                         threadpool_destroy (&A->pool);
1332                 if (threadpool_create (&A->pool, &threadClass, display, threadCount)) {
1333                         A->pool.count = 0;
1334                         free_strange (mi);
1335                         return;
1336                 }
1337         }
1338         #undef A
1339 #endif
1340         MI_CLEARWINDOW(mi);
1341
1342         /* Do not want any exposure events from XCopyPlane */
1343         XSetGraphicsExposures(display, MI_GC(mi), False);
1344 }
1345
1346 /***************************************************************/
1347
1348 #ifdef STANDALONE
1349 XSCREENSAVER_MODULE ("Strange", strange)
1350 #endif
1351
1352 #endif /* MODE_strange */