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