1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* strange --- strange attractors */
5 static const char sccsid[] = "@(#)strange.c 5.00 2000/11/01 xlockmore";
9 * Copyright (c) 1997 by Massimino Pascal <Pascal.Massimon@ens.fr>
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.
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.
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.
40 * strange attractors are not so hard to find...
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
48 /* TODO: Can anything be done about the overflow with -point-size 32? */
52 # define DEFAULTS "*delay: 10000 \n" \
54 "*fpsSolid: True \n" \
55 "*ignoreRotation: True \n" \
57 "*useThreads: True \n" \
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 */
66 #endif /* !STANDALONE */
70 #include "thread_util.h"
72 #ifdef HAVE_INTTYPES_H
73 # include <inttypes.h>
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. */
88 static float brightness;
89 static float motionBlur;
91 static XrmOptionDescRec opts[] =
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},
101 static argtype vars[] =
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},
110 static OptionStruct desc[] =
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"},
119 ENTRYPOINT ModeSpecOpt strange_opts =
120 {sizeof opts / sizeof opts[0], opts,
121 sizeof vars / sizeof vars[0], vars, desc};
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};
139 #define UNIT (1<<UNIT_BITS)
140 #define UNIT2 (1<<14)
141 #define COLOR_BITS 16
142 /* #define UNIT2 (3140*UNIT/1000) */
144 #define SKIP_FIRST 100
145 #define DBL_To_PRM(x) (PRM)( (DBL)(UNIT)*(x) )
148 #define DO_FOLD(a) (a)<0 ? -A->Fold[ (-(a))&(UNIT2-1) ] : A->Fold[ (a)&(UNIT2-1) ]
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 ) )
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.
162 #define useAccumulator (A->Max_Pt > 6000)
163 #define ACC_GAMMA 10.0
164 #define DEF_NUM_COLS 150
166 #define VARY_SPEED_TO_AVOID_BOREDOM
167 /*#define AUTO_ZOOM*/ /* Works funny, but try it with -curve 5. */
169 /******************************************************************/
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__)
181 # define ALIGN_HINT(ptr) (ptr)
182 # ifndef __BIGGEST_ALIGNMENT__
183 # define __BIGGEST_ALIGNMENT__ (sizeof(void *))
188 #ifdef HAVE_INTTYPES_H
189 typedef uint16_t ALIGNED PIXEL0;
190 typedef uint32_t PIXEL0X;
191 typedef uint32_t ALIGNED PIXEL1;
193 typedef unsigned short ALIGNED PIXEL0;
194 typedef unsigned long PIXEL0X;
195 typedef unsigned long ALIGNED PIXEL1;
199 #ifdef HAVE_INTTYPES_H
203 unsigned short signature;
204 unsigned char bytes[2];
206 } byte_order_union = {MSBFirst};
208 #define LOCAL_BYTE_ORDER byte_order_union.bytes[1]
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. */
216 int Col, Count, Speed;
218 Pixmap dbuf; /* jwz */
220 #ifdef useAccumulator
223 unsigned long rMask, gMask, bMask;
224 unsigned rShift, gShift, bShift;
225 PIXEL0 blurFac; /* == 0 when no motion blur is taking place. */
231 XShmSegmentInfo shmInfo;
232 struct _THREAD **threads;
233 struct threadpool pool;
237 static ATTRACTOR *Root = (ATTRACTOR *) NULL;
239 #ifdef useAccumulator
240 typedef struct _THREAD {
241 const ATTRACTOR *Attractor;
250 PRM xmin, xmax, ymin, ymax;
255 static DBL Amp_Prm[MAX_PRM] =
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
261 static DBL Mid_Prm[MAX_PRM] =
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,
270 static inline uint64_t frq(void)
275 static inline uint64_t tick(void)
278 gettimeofday(&tv, NULL);
279 return tv.tv_sec * frq() + tv.tv_usec;
285 Old_Gauss_Rand(DBL c, DBL A, DBL S)
289 y = (DBL) LRAND() / MAXRAND;
291 y = A * (z - exp(-y * y * S)) / (z - exp(-S));
299 /* dmo2118: seems to be responsible for lots of boring-looking rings */
301 /* I don't know that it makes a difference, but this one actually *is*
304 /* Generate Gaussian random number: mean c, "amplitude" A (actually
305 A is 3*standard deviation). 'S' is unused. */
307 /* Note this generates a pair of gaussian variables, so it saves one
308 to give out next time it's called */
311 Gauss_Rand(DBL c, DBL A, DBL S)
314 static Bool ready = 0;
321 x = 2.0 * (double)LRAND() / MAXRAND - 1.0;
322 y = 2.0 * (double)LRAND() / MAXRAND - 1.0;
326 w = sqrt((-2 * log(w))/w);
329 return c + A/3 * y * w;
335 Random_Prm(DBL * Prm)
339 for (i = 0; i < MAX_PRM; ++i) {
342 Prm[i] = Gauss_Rand (Mid_Prm[i], Amp_Prm[i], 4.0);
345 Prm[i] = Old_Gauss_Rand (Mid_Prm[i], Amp_Prm[i], 4.0);
349 /***************************************************************/
351 /* 2 examples of non-linear map */
354 Iterate_X2(const ATTRACTOR * A, PRM x, PRM y, PRM * xo, PRM * yo)
356 PRM xx, yy, xy, x2y, y2x, Tmp;
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;
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);
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);
373 Iterate_X3(const ATTRACTOR * A, PRM x, PRM y, PRM * xo, PRM * yo)
375 PRM xx, yy, xy, x2y, y2x, Tmp_x, Tmp_y, Tmp_z;
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;
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);
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);
390 Tmp_y = DO_FOLD(Tmp_y);
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);
396 /* Can happen with -curve 9. */
400 #ifdef HAVE_INTTYPES_H
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);
407 *xo = (Tmp_x * UNIT) / Tmp_z;
408 *yo = (Tmp_y * UNIT) / Tmp_z;
412 static void (*Funcs[2]) (const ATTRACTOR *, PRM, PRM, PRM *, PRM *) = {
413 Iterate_X2, Iterate_X3
416 /***************************************************************/
419 free_strange(ModeInfo *mi)
421 Display *display = MI_DISPLAY(mi);
422 ATTRACTOR *A = &Root[MI_SCREEN(mi)];
424 if (A->Buffer1 != NULL) {
426 A->Buffer1 = (XPoint *) NULL;
428 if (A->Buffer2 != NULL) {
430 A->Buffer2 = (XPoint *) NULL;
433 XFreePixmap(display, A->dbuf);
437 XFreeGC(display, A->dbuf_gc);
440 if (A->Fold != NULL) {
442 A->Fold = (PRM *) NULL;
445 #ifdef useAccumulator
446 if (useAccumulator) {
448 threadpool_destroy (&A->pool);
456 destroy_xshm_image (display, A->accImage, &A->shmInfo);
464 if (A->visualClass != TrueColor && A->numCols > 2)
465 XFreeColors (display, MI_COLORMAP(mi), A->cols, A->numCols, 0);
473 /* NRAND() is also in use; making three PRNGs in total here. */
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
479 /* Extremely cheap entropy: this is often a single LEA instruction. */
480 #define CHEAPRND(seed) ((seed) = (seed) * 5)
483 init_draw (const ATTRACTOR *A, PRM *x, PRM *y,
484 PRM *xmin, PRM *ymin, PRM *xmax, PRM *ymax, unsigned long *rnd)
494 *x = *y = DBL_To_PRM(.0);
495 for (n = SKIP_FIRST; n; --n) {
496 (*A->Iterate) (A, *x, *y, &xo, &yo);
509 /* Can't use NRAND(), because that modifies global state in a
512 *x = xo + (GOODRND(*rnd) >> (GOODRND_BITS - 3)) - 4;
513 *y = yo + (GOODRND(*rnd) >> (GOODRND_BITS - 3)) - 4;
518 recalc_scale (const ATTRACTOR *A, PRM xmin, PRM ymin, PRM xmax, PRM ymax,
519 DBL *Lx, DBL *Ly, PRM *mx, PRM *my)
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;
534 #ifdef useAccumulator
537 thread_destroy (void *Self_Raw)
539 THREAD *T = (THREAD *)Self_Raw;
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);
549 thread_create (void *Self_Raw, struct threadpool *pool, unsigned id)
551 THREAD *T = (THREAD *)Self_Raw;
553 const ATTRACTOR *A = GET_PARENT_OBJ(ATTRACTOR, pool, pool);
555 memset (T, 0, sizeof(*T));
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;
567 T->accMap = (PIXEL0**)calloc(A->Height,sizeof(PIXEL0*));
573 if (aligned_malloc ((void **)&T->accMap[0], __BIGGEST_ALIGNMENT__,
574 A->alignedWidth * A->Height * sizeof(PIXEL0))) {
578 for (i=0;i<A->Height;i++)
579 T->accMap[i] = T->accMap[0] + A->alignedWidth * i;
581 if (aligned_malloc ((void **)&T->bloomRows, __BIGGEST_ALIGNMENT__,
582 A->alignedWidth * (pointSize + 2) * sizeof(*T->bloomRows))) {
586 if (aligned_malloc ((void **)&T->colorRow, __BIGGEST_ALIGNMENT__,
587 A->alignedWidth * sizeof(*T->colorRow))) {
592 if (aligned_malloc ((void **)&T->motionBlur, __BIGGEST_ALIGNMENT__,
593 A->alignedWidth * (T->y2 - T->y1) * sizeof(*T->motionBlur))) {
598 memset (T->motionBlur, 0, A->alignedWidth * (T->y2 - T->y1) * sizeof(*T->motionBlur));
605 points_thread (void *Self_Raw)
607 /* Restricts viewable area to 2^(32-L_Bits). */
608 const unsigned L_Bits = 19; /* Max. image size: 8192x8192. */
610 THREAD *T = (THREAD *)Self_Raw;
611 const ATTRACTOR *A = T->Attractor;
615 PRM iLx, iLy, cx, cy;
616 void (*Iterate) (const ATTRACTOR *, PRM, PRM, PRM *, PRM *);
618 PRM xmax, xmin, ymax, ymin;
620 Iterate = A->Iterate;
622 if (useAccumulator) {
623 memset (T->accMap[0], 0, sizeof(PIXEL0) * A->alignedWidth * A->Height);
626 /* Using CHEAPRND() by itself occasionally gets stuck at 0 mod 8, so seed it
629 init_draw (A, &x, &y, &xmin, &ymin, &xmax, &ymax, &T->Rnd);
630 recalc_scale (A, xmin, ymin, xmax, ymax, &Lx, &Ly, &cx, &cy);
632 Rnd = GOODRND(T->Rnd);
634 iLx = Lx * (1 << L_Bits);
635 iLy = Ly * (1 << L_Bits);
636 if (!iLx) /* Can happen with small windows. */
641 for (n = T->Attractor->Max_Pt / A->pool.count; n; --n) {
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)
661 recalc_scale (A, xmin, ymin, xmax, ymax, &Lx, &Ly, &cx, &cy);
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;
672 rasterize_thread (void *Self_Raw)
674 THREAD *T = (THREAD *)Self_Raw;
675 const ATTRACTOR *A = T->Attractor;
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
682 * (zoom * zoom) / (0.9 * 0.9)
684 / (pointSize * pointSize)
687 * (float)A->numCols/256;
688 #ifdef VARY_SPEED_TO_AVOID_BOREDOM
689 unsigned pixelCount = 0;
691 PIXEL0 *motionBlurRow = T->motionBlur;
692 void *outRow = (char *)A->accImage->data
693 + A->accImage->bytes_per_line * T->y1;
695 /* Clang needs these for loop-vectorizing; A->Width doesn't work. */
696 unsigned w = A->Width, aw = A->alignedWidth;
698 if (A->numCols == 2) /* Brighter for monochrome. */
701 /* bloomRows: row ring buffer, bloom accumulator, in that order. */
702 memset (T->bloomRows, 0, (pointSize + 1) * aw * sizeof(*T->bloomRows));
704 /* This code is highly amenable to auto-vectorization; on GCC use -O3 to get
705 * that. On x86-32, also add -msse2.
707 for (j=T->y0;j<T->y2;j++) {
708 Bool has_col = False;
712 ALIGN_HINT(T->bloomRows + A->alignedWidth * (j % pointSize));
714 ALIGN_HINT(T->bloomRows + A->alignedWidth * pointSize);
715 PIXEL1 *colRow = T->colorRow;
717 /* Moderately fast bloom. */
720 accumRow[i] -= bloomRow[i];
724 for (k=0;k<A->pool.count;k++) {
725 const PIXEL0 *inRow = ALIGN_HINT(A->threads[k]->accMap[j]);
727 /* Lots of last-level cache misses. Such is life. */
728 bloomRow[i] += inRow[i];
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.
736 for (i=0;i<pointSize-1;i++)
737 accum += bloomRow[i];
740 PIXEL0 oldBloom = bloomRow[i];
742 /* alignedWidth has extra padding for this one line. */
743 accum += bloomRow[i+pointSize-1];
746 accumRow[i] += accum;
751 PIXEL0 *accumRow1 = A->blurFac ? motionBlurRow : accumRow;
752 PIXEL0 blurFac = A->blurFac;
755 /* TODO: Do I vectorize OK? */
756 if (blurFac == 0x8000) {
757 /* Optimization for the default. */
759 motionBlurRow[i] = (motionBlurRow[i] >> 1) + accumRow[i];
762 motionBlurRow[i] = (PIXEL0)(((PIXEL0X)motionBlurRow[i] * blurFac) >> 16) + accumRow[i];
769 unsigned col = (accumRow1[i] * colorScale) >> COLOR_BITS;
770 if (col>A->numCols-1) {
773 #ifdef VARY_SPEED_TO_AVOID_BOREDOM
775 if (col<A->numCols-1) /* we don't count maxxed out pixels */
784 colRow[i] = A->cols[col];
787 #ifdef VARY_SPEED_TO_AVOID_BOREDOM
798 if (MI_NPIXELS(mi) < 2)
799 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
801 /*XSetForeground(display, gc, MI_PIXEL(mi, A->Col % MI_NPIXELS(mi)));*/
802 XSetForeground(display, gc, cols[col].pixel);
804 if (A->dbuf != None) {
805 XSetForeground(display, A->dbuf_gc, cols[col].pixel);
806 XDrawPoint(display, A->dbuf, A->dbuf_gc, i, j);
808 XSetForeground(display, gc, cols[col].pixel);
809 XDrawPoint(display, window, gc, i, j);
814 if (A->accImage->bits_per_pixel==32 &&
815 A->accImage->byte_order==LOCAL_BYTE_ORDER) {
817 ((uint32_t *)outRow)[i] = colRow[i];
818 } else if (A->accImage->bits_per_pixel==16 &&
819 A->accImage->byte_order==LOCAL_BYTE_ORDER) {
821 ((uint16_t *)outRow)[i] = colRow[i];
822 } else if (A->accImage->bits_per_pixel==8) {
824 ((uint8_t *)outRow)[i] = colRow[i];
827 XPutPixel (A->accImage, i, j, colRow[i]);
830 outRow = (char *)outRow + A->accImage->bytes_per_line;
836 uint64_t t0 = tick();
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);
847 T->pixelCount = pixelCount;
851 ramp_color (const XColor *color_in, XColor *color_out, unsigned i, unsigned n)
856 #define LOW_COLOR(c) ((c)*li/FULLBLUE)
857 #define HIGH_COLOR(c) ((65535-(c))*(li-FULLBLUE)/(256-FULLBLUE)+(c))
859 + (255.0-MINBLUE) * log(1.0 + ACC_GAMMA*(float)i/n)
860 / log(1.0 + ACC_GAMMA);
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);
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);
875 draw_points (Display *display, Drawable d, GC gc, const void *Buf,
879 XDrawPoints(display, d, gc, (XPoint *)Buf, Count, CoordModeOrigin);
881 XFillRectangles(display, d, gc, (XRectangle *)Buf, Count);
885 draw_strange(ModeInfo * mi)
891 Display *display = MI_DISPLAY(mi);
892 Window window = MI_WINDOW(mi);
895 void (*Iterate) (const ATTRACTOR *, PRM, PRM, PRM *, PRM *);
896 PRM xmin, xmax, ymin, ymax;
898 unsigned long Rnd = random();
903 A = &Root[MI_SCREEN(mi)];
908 Iterate = A->Iterate;
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]);
914 /* We collect the accumulation of the orbits in the 2d int array field. */
916 init_draw (A, &x, &y, &xmin, &ymin, &xmax, &ymax, &Rnd);
917 recalc_scale (A, xmin, ymin, xmax, ymax, &Lx, &Ly, &cx, &cy);
926 MI_IS_DRAWN(mi) = True;
928 #ifdef useAccumulator
929 if (useAccumulator) {
932 threadpool_run (&A->pool, points_thread);
934 if (A->visualClass == TrueColor) {
935 XColor *src_color = &A->palette[A->Col % MI_NPIXELS(mi)];
937 for (i=0;i<A->numCols;i++) {
939 ramp_color (src_color, &color, i, A->numCols);
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);
946 threadpool_wait (&A->pool);
948 threadpool_run(&A->pool, rasterize_thread);
949 threadpool_wait(&A->pool);
951 for (i=0; i!=A->pool.count; ++i) {
952 THREAD *T = A->threads[i];
961 pixelCount += T->pixelCount;
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,
969 if (A->dbuf != None) {
970 XCopyArea(display, A->dbuf, window, gc, 0, 0, A->Width, A->Height, 0, 0);
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))) {
976 } else if (pixelCount>0 && pixelCount<A->Width*A->Height/1000) {
977 A->Speed *= 1.25; /* A->Count = 1000; */
979 A->Speed = 4; /* reset to normal/default */
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];
993 for (n = 0; n != A->Max_Pt; ++n) {
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];
1003 XRectangle *Buf = &((XRectangle *)A->Buffer2)[n];
1004 /* Position matches bloom in accumulator mode. */
1005 Buf->x = x1 - pointSize + 1;
1007 Buf->width = pointSize;
1008 Buf->height = pointSize;
1021 /* (void) fprintf( stderr, "X,Y: %d %d ", Buf->x, Buf->y ); */
1022 x = xo + NRAND(8) - 4;
1023 y = yo + NRAND(8) - 4;
1026 XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
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);
1034 draw_points(display, window, gc, A->Buffer1, Cur_Pt);
1037 if (MI_NPIXELS(mi) <= 2)
1038 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
1040 XSetForeground(display, gc, MI_PIXEL(mi, A->Col % MI_NPIXELS(mi)));
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);
1047 draw_points(display, window, gc, A->Buffer2, A->Cur_Pt);
1049 #ifdef useAccumulator
1054 A->Buffer1 = A->Buffer2;
1057 if ((xmax - xmin < Lx * DBL_To_PRM(.2)) && (ymax - ymin < Ly * DBL_To_PRM(.2)))
1058 A->Count += 4 * A->Speed;
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);
1069 mi->recursion_depth = A->Count;
1074 /***************************************************************/
1077 init_strange(ModeInfo * mi)
1079 Display *display = MI_DISPLAY(mi);
1083 ATTRACTOR *Attractor;
1084 size_t pointStructSize =
1085 pointSize == 1 ? sizeof (XPoint) : sizeof (XRectangle);
1087 if (curve <= 0) curve = 10;
1089 MI_INIT (mi, Root, free_strange);
1090 Attractor = &Root[MI_SCREEN(mi)];
1092 if (Attractor->Fold == NULL) {
1095 if ((Attractor->Fold = (PRM *) calloc(UNIT2 + 1,
1096 sizeof (PRM))) == NULL) {
1100 for (i = 0; i <= UNIT2; ++i) {
1103 /* x = ( DBL )(i)/UNIT2; */
1104 /* x = sin( M_PI/2.0*x ); */
1105 /* x = sqrt( x ); */
1107 /* x = x*(1.0-x)*4.0; */
1108 x = (DBL) (i) / UNIT;
1110 Attractor->Fold[i] = DBL_To_PRM(x);
1114 Attractor->Max_Pt = points;
1116 if (Attractor->Buffer1 == NULL)
1117 if ((Attractor->Buffer1 = calloc(Attractor->Max_Pt,
1118 pointStructSize)) == NULL) {
1122 if (Attractor->Buffer2 == NULL)
1123 if ((Attractor->Buffer2 = calloc(Attractor->Max_Pt,
1124 pointStructSize)) == NULL) {
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;
1136 Attractor->Iterate = Funcs[NRAND(2)];
1137 if (curve < 10) /* Avoid boring Iterate_X2. */
1138 Attractor->Iterate = Iterate_X3;
1140 Random_Prm(Attractor->Prm1);
1141 Random_Prm(Attractor->Prm2);
1143 if (Attractor->dbuf != None)
1144 XFreePixmap(display, Attractor->dbuf);
1145 #ifdef useAccumulator
1149 Attractor->dbuf = None;
1155 Attractor->dbuf = XCreatePixmap (display, MI_WINDOW(mi),
1156 Attractor->Width, Attractor->Height,
1157 /* useAccumulator ? MI_DEPTH(mi) : */ 1);
1159 /* Allocation checked */
1160 if (Attractor->dbuf != None) {
1166 gcv.graphics_exposures = False;
1167 #endif /* HAVE_JWXYZ */
1168 gcv.function = GXcopy;
1170 if (Attractor->dbuf_gc != None)
1171 XFreeGC(display, Attractor->dbuf_gc);
1173 if ((Attractor->dbuf_gc = XCreateGC(display, Attractor->dbuf,
1175 GCGraphicsExposures |
1176 #endif /* HAVE_JWXYZ */
1177 GCFunction | GCForeground | GCBackground,
1179 XFreePixmap(display, Attractor->dbuf);
1180 Attractor->dbuf = None;
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);
1191 #ifdef useAccumulator
1193 if (useAccumulator) {
1194 static const struct threadpool_class threadClass = {
1199 Screen *screen = MI_SCREENPTR(mi);
1201 unsigned maxThreads, threadCount;
1202 unsigned bpp = visual_pixmap_depth (screen, MI_VISUAL(mi));
1203 size_t threadAlign1 = 8 * thread_memory_alignment(display) - 1;
1205 if (A->visualClass != TrueColor && A->numCols > 2)
1206 XFreeColors (display, MI_COLORMAP(mi), A->cols, A->numCols, 0);
1209 if (MI_NPIXELS(mi) <= 2) {
1211 A->visualClass = StaticColor;
1213 A->numCols = DEF_NUM_COLS;
1214 A->visualClass = visual_class(screen, MI_VISUAL(mi));
1217 A->cols = calloc (A->numCols,sizeof(*A->cols));
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);
1233 A->palette = malloc(MI_NPIXELS(mi) * sizeof(XColor));
1239 for (i=0;i<MI_NPIXELS(mi);i++)
1240 A->palette[i].pixel = MI_PIXEL(mi,i);
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);
1247 /* No changing colors, unfortunately. */
1250 color.pixel = MI_PIXEL(mi,NRAND(MI_NPIXELS(mi)));
1251 XQueryColor (display, MI_COLORMAP(mi), &color);
1254 for (i=0;i<A->numCols;i++) {
1256 ramp_color (&color, &out_color, i, A->numCols);
1257 if (!XAllocColor (display, MI_COLORMAP(mi), &out_color))
1259 A->cols[i] = out_color.pixel;
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;
1272 XFreeColors (display, MI_COLORMAP(mi), A->cols, i, 0);
1273 A->numCols = A->numCols * 11 / 12;
1274 if (A->numCols < 2) {
1283 /* Add slack for horizontal blur, then round up to the platform's SIMD
1287 (((A->Width + pointSize) * sizeof(PIXEL0) + (__BIGGEST_ALIGNMENT__ - 1)) &
1288 ~(__BIGGEST_ALIGNMENT__ - 1)) / sizeof(PIXEL0);
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,
1297 A->blurFac = (PIXEL0)(65536 * (motionBlur - 1) / (motionBlur + 1));
1298 A->colorFac = 2 / (motionBlur + 1);
1300 /* Don't overdose on threads. */
1301 threadCount = hardware_concurrency (display);
1302 maxThreads = A->Height / (pointSize * 4);
1305 if (threadCount > maxThreads)
1306 threadCount = maxThreads;
1310 A->threads = malloc (threadCount * sizeof(*A->threads));
1317 threadpool_destroy (&A->pool);
1318 if (threadpool_create (&A->pool, &threadClass, display, threadCount)) {
1328 /* Do not want any exposure events from XCopyPlane */
1329 XSetGraphicsExposures(display, MI_GC(mi), False);
1333 reshape_strange(ModeInfo * mi, int width, int height)
1335 XClearWindow (MI_DISPLAY (mi), MI_WINDOW(mi));
1339 /***************************************************************/
1343 strange_handle_event (ModeInfo *mi, XEvent *event)
1345 if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
1347 reshape_strange (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1354 XSCREENSAVER_MODULE ("Strange", strange)
1357 #endif /* MODE_strange */