1 /* Copyright © Chris Le Sueur and Robby Griffin, 2005-2006
3 Permission is hereby granted, free of charge, to any person obtaining
4 a copy of this software and associated documentation files (the
5 "Software"), to deal in the Software without restriction, including
6 without limitation the rights to use, copy, modify, merge, publish,
7 distribute, sublicense, and/or sell copies of the Software, and to
8 permit persons to whom the Software is furnished to do so, subject to
9 the following conditions:
11 The above copyright notice and this permission notice shall be included
12 in all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 OTHER DEALINGS IN THE SOFTWARE.
22 Ultimate thanks go to Massimino Pascal, who created the original
23 xscreensaver hack, and inspired me with it's swirly goodness. This
24 version adds things like variable quality, number of functions and also
25 a groovier colouring mode.
27 This version by Chris Le Sueur <thefishface@gmail.com>, Feb 2005
28 Many improvements by Robby Griffin <rmg@terc.edu>, Mar 2006
29 Multi-coloured mode added by Jack Grahl <j.grahl@ucl.ac.uk>, Jan 2007
38 #include "screenhack.h"
41 #define countof(x) (sizeof((x)) / sizeof(*(x)))
44 float r, s, tx, ty; /* Rotation, Scale, Translation X & Y */
45 float ro, rt, rc; /* Old Rotation, Rotation Target, Rotation Counter */
46 float so, st, sc; /* Old Scale, Scale Target, Scale Counter */
47 float sa, txa, tya; /* Scale change, Translation change */
49 int ua, ub, utx; /* Precomputed combined r,s,t values */
50 int uc, ud, uty; /* Precomputed combined r,s,t values */
62 int blackColor, whiteColor;
64 int width, widthb, height;
67 XPoint pointbuf[1000];
69 int xmin, xmax, ymin, ymax;
80 Bool translate, scale, rotate;
83 #define getdot(x,y) (st->board[((y)*st->widthb)+((x)>>5)] & (1<<((x) & 31)))
84 #define setdot(x,y) (st->board[((y)*st->widthb)+((x)>>5)] |= (1<<((x) & 31)))
89 return (((float)random() / RAND_MAX) * up);
92 static const char *ifs_defaults [] = {
105 # ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
106 "*doubleBuffer: False",
108 "*doubleBuffer: True",
111 "*ignoreRotation: True",
116 static XrmOptionDescRec ifs_options [] = {
117 { "-detail", ".length", XrmoptionSepArg, 0 },
118 { "-delay", ".delay", XrmoptionSepArg, 0 },
119 { "-mode", ".mode", XrmoptionSepArg, 0 },
120 { "-colors", ".colors", XrmoptionSepArg, 0 },
121 { "-functions", ".lensnum", XrmoptionSepArg, 0 },
122 { "-no-translate", ".translate", XrmoptionNoArg, "False" },
123 { "-no-scale", ".scale", XrmoptionNoArg, "False" },
124 { "-no-rotate", ".rotate", XrmoptionNoArg, "False" },
125 { "-recurse", ".recurse", XrmoptionNoArg, "True" },
126 { "-iterate", ".recurse", XrmoptionNoArg, "False" },
127 { "-multi", ".multi", XrmoptionNoArg, "True" },
128 { "-no-multi", ".multi", XrmoptionNoArg, "False" },
129 { "-db", ".doubleBuffer",XrmoptionNoArg, "True" },
130 { "-no-db", ".doubleBuffer",XrmoptionNoArg, "False" },
135 /* Draw all the queued points on the backbuffer */
137 drawpoints(struct state *st)
139 XDrawPoints(st->dpy, st->backbuffer, st->gc, st->pointbuf, st->npoints,
144 /* Set a point to be drawn, if it hasn't been already.
145 * Expects coordinates in 256ths of a pixel. */
147 sp(struct state *st, int x, int y)
149 if (x < 0 || x >= st->width8 || y < 0 || y >= st->height8)
155 if (getdot(x, y)) return;
158 if (x < st->xmin) st->xmin = x;
159 if (x > st->xmax) st->xmax = x;
160 if (y < st->ymin) st->ymin = y;
161 if (y > st->ymax) st->ymax = y;
163 st->pointbuf[st->npoints].x = x;
164 st->pointbuf[st->npoints].y = y;
167 if (st->npoints >= countof(st->pointbuf)) {
173 /* Precompute integer values for matrix multiplication and vector
174 * addition. The matrix multiplication will go like this (see iterate()):
175 * |x2| |ua ub| |x| |utx|
176 * | | = | | * | | + | |
177 * |y2| |uc ud| |y| |uty|
179 * There is an extra factor of 2^10 in these values, and an extra factor of
180 * 2^8 in the coordinates, in order to implement fixed-point arithmetic.
183 lensmatrix(struct state *st, Lens *l)
185 l->ua = 1024.0 * l->s * cos(l->r);
186 l->ub = -1024.0 * l->s * sin(l->r);
189 l->utx = 131072.0 * st->width * (l->s * (sin(l->r) - cos(l->r))
191 l->uty = -131072.0 * st->height * (l->s * (sin(l->r) + cos(l->r))
196 CreateLens(struct state *st,
203 newlens->sa = newlens->txa = newlens->tya = 0;
205 newlens->r = newlens->ro = newlens->rt = nr;
211 newlens->s = newlens->so = newlens->st = ns;
214 else newlens->s = 0.5;
219 lensmatrix(st, newlens);
223 mutate(struct state *st, Lens *l)
230 l->rt = myrandom(4) - 2;
232 factor = (sin((-M_PI / 2.0) + M_PI * l->rc) + 1.0) / 2.0;
233 l->r = l->ro + (l->rt - l->ro) * factor;
239 /* Reset counter, obtain new target value */
242 l->st = myrandom(2) - 1;
244 factor = (sin((-M_PI / 2.0) + M_PI * l->sc) + 1.0) / 2.0;
245 /* Take average of old target and new target, using factor to *
246 * weight. It's computed sinusoidally, resulting in smooth, *
247 * rhythmic transitions. */
248 l->s = l->so + (l->st - l->so) * factor;
252 l->txa += myrandom(0.004) - 0.002;
253 l->tya += myrandom(0.004) - 0.002;
256 if (l->tx > 6) l->txa -= 0.004;
257 if (l->ty > 6) l->tya -= 0.004;
258 if (l->tx < -6) l->txa += 0.004;
259 if (l->ty < -6) l->tya += 0.004;
260 if (l->txa > 0.05 || l->txa < -0.05) l->txa /= 1.7;
261 if (l->tya > 0.05 || l->tya < -0.05) l->tya /= 1.7;
263 if (st->rotate || st->scale || st->translate) {
269 #define STEPX(l,x,y) (((l)->ua * (x) + (l)->ub * (y) + (l)->utx) >> 10)
270 #define STEPY(l,x,y) (((l)->uc * (x) + (l)->ud * (y) + (l)->uty) >> 10)
271 /*#define STEPY(l,x,y) (((l)->ua * (y) - (l)->ub * (x) + (l)->uty) >> 10)*/
273 /* Calls itself <lensnum> times - with results from each lens/function. *
274 * After <length> calls to itself, it stops iterating and draws a point. */
276 recurse(struct state *st, int x, int y, int length, int p)
286 sp(st, STEPX(l, x, y), STEPY(l, x, y));
290 for (i = 0; i < st->lensnum; i++) {
292 recurse(st, STEPX(l, x, y), STEPY(l, x, y), length - 1, p);
297 /* Performs <count> random lens transformations, drawing a point at each
298 * iteration after the first 10.
301 iterate(struct state *st, int count, int p)
310 l = &st->lenses[random() % st->lensnum]; \
311 tx = STEPX(l, x, y); \
312 y = STEPY(l, x, y); \
315 for (i = 0; i < 10; i++) {
319 for ( ; i < count; i++) {
326 sp(st, STEPX(l, x, y), STEPY(l, x, y));
336 /* Come on and iterate, iterate, iterate and sing... *
337 * Yeah, this function just calls iterate, mutate, *
338 * and then draws everything. */
340 ifs_draw (Display *dpy, Window window, void *closure)
342 struct state *st = (struct state *) closure;
344 int xmin = st->xmin, xmax = st->xmax, ymin = st->ymin, ymax = st->ymax;
348 /* erase whatever was drawn in the previous frame */
349 if (xmin <= xmax && ymin <= ymax) {
350 XSetForeground(st->dpy, st->gc, st->blackColor);
351 XFillRectangle(st->dpy, st->backbuffer, st->gc,
353 xmax - xmin + 1, ymax - ymin + 1);
354 st->xmin = st->width + 1;
355 st->xmax = st->ymax = -1;
356 st->ymin = st->height + 1;
360 st->ccolour %= st->ncolours;
362 /* calculate and draw points for this frame */
367 for (i = 0; i < st->lensnum; i++) {
368 partcolor = st->ccolour * (i+1);
369 partcolor %= st->ncolours;
370 XSetForeground(st->dpy, st->gc, st->colours[partcolor].pixel);
371 memset(st->board, 0, st->widthb * st->height * sizeof(*st->board));
373 recurse(st, x, y, st->length - 1, i);
375 iterate(st, pow(st->lensnum, st->length - 1), i);
382 XSetForeground(st->dpy, st->gc, st->colours[st->ccolour].pixel);
383 memset(st->board, 0, st->widthb * st->height * sizeof(*st->board));
385 recurse(st, x, y, st->length, 0);
387 iterate(st, pow(st->lensnum, st->length), 0);
392 /* if we just drew into a buffer, copy the changed area (including
393 * erased area) to screen */
394 if (st->backbuffer != st->window
395 && ((st->xmin <= st->xmax && st->ymin <= st->ymax)
396 || (xmin <= xmax && ymin <= ymax))) {
397 if (st->xmin < xmin) xmin = st->xmin;
398 if (st->xmax > xmax) xmax = st->xmax;
399 if (st->ymin < ymin) ymin = st->ymin;
400 if (st->ymax > ymax) ymax = st->ymax;
401 XCopyArea(st->dpy, st->backbuffer, st->window, st->gc,
403 xmax - xmin + 1, ymax - ymin + 1,
407 for(i = 0; i < st->lensnum; i++) {
408 mutate(st, &st->lenses[i]);
415 ifs_reshape (Display *, Window, void *, unsigned int, unsigned int);
418 ifs_init (Display *d_arg, Window w_arg)
420 struct state *st = (struct state *) calloc (1, sizeof(*st));
422 XWindowAttributes xgwa;
424 /* Initialise all this X shizzle */
428 st->blackColor = BlackPixel(st->dpy, DefaultScreen(st->dpy));
429 st->whiteColor = WhitePixel(st->dpy, DefaultScreen(st->dpy));
430 st->gc = XCreateGC(st->dpy, st->window, 0, NULL);
432 XGetWindowAttributes (st->dpy, st->window, &xgwa);
433 ifs_reshape(st->dpy, st->window, st, xgwa.width, xgwa.height);
435 st->ncolours = get_integer_resource(st->dpy, "colors", "Colors");
436 if (st->ncolours < st->lensnum)
437 st->ncolours = st->lensnum;
438 if (st->colours) free(st->colours);
439 st->colours = (XColor *)calloc(st->ncolours, sizeof(XColor));
440 if (!st->colours) exit(1);
441 make_smooth_colormap (xgwa.screen, xgwa.visual, xgwa.colormap,
442 st->colours, &st->ncolours,
445 /* Initialize IFS data */
447 st->delay = get_integer_resource(st->dpy, "delay", "Delay");
448 st->length = get_integer_resource(st->dpy, "length", "Detail");
449 if (st->length < 0) st->length = 0;
450 st->mode = get_integer_resource(st->dpy, "mode", "Mode");
452 st->rotate = get_boolean_resource(st->dpy, "rotate", "Boolean");
453 st->scale = get_boolean_resource(st->dpy, "scale", "Boolean");
454 st->translate = get_boolean_resource(st->dpy, "translate", "Boolean");
455 st->recurse = get_boolean_resource(st->dpy, "recurse", "Boolean");
456 st->multi = get_boolean_resource(st->dpy, "multi", "Boolean");
458 st->lensnum = get_integer_resource(st->dpy, "lensnum", "Functions");
459 if (st->lenses) free (st->lenses);
460 st->lenses = (Lens *)calloc(st->lensnum, sizeof(Lens));
461 if (!st->lenses) exit(1);
462 for (i = 0; i < st->lensnum; i++) {
475 ifs_reshape (Display *dpy, Window window, void *closure,
476 unsigned int w, unsigned int h)
478 struct state *st = (struct state *)closure;
479 XWindowAttributes xgwa;
481 /* oh well, we need the screen depth anyway */
482 XGetWindowAttributes (st->dpy, st->window, &xgwa);
484 st->width = xgwa.width;
485 st->widthb = ((xgwa.width + 31) >> 5);
486 st->height = xgwa.height;
487 st->width8 = xgwa.width << 8;
488 st->height8 = xgwa.height << 8;
490 if (!st->xmax && !st->ymax && !st->xmin && !st->ymin) {
491 st->xmin = xgwa.width + 1;
492 st->xmax = st->ymax = -1;
493 st->ymin = xgwa.height + 1;
496 if (st->backbuffer != None && st->backbuffer != st->window) {
497 XFreePixmap(st->dpy, st->backbuffer);
498 st->backbuffer = None;
501 if (get_boolean_resource (st->dpy, "doubleBuffer", "Boolean")) {
502 st->backbuffer = XCreatePixmap(st->dpy, st->window, st->width, st->height, xgwa.depth);
503 XSetForeground(st->dpy, st->gc, st->blackColor);
504 XFillRectangle(st->dpy, st->backbuffer, st->gc,
505 0, 0, st->width, st->height);
507 st->backbuffer = st->window;
508 XClearWindow(st->dpy, st->window);
511 if (st->board) free(st->board);
512 st->board = (unsigned int *)calloc(st->widthb * st->height, sizeof(unsigned int));
513 if (!st->board) exit(1);
517 ifs_event (Display *dpy, Window window, void *closure, XEvent *event)
519 struct state *st = (struct state *)closure;
520 if (screenhack_event_helper (dpy, window, event))
523 for (i = 0; i < st->lensnum; i++) {
537 ifs_free (Display *dpy, Window window, void *closure)
539 struct state *st = (struct state *) closure;
541 if (st->board) free(st->board);
542 if (st->lenses) free(st->lenses);
543 if (st->colours) free(st->colours);
544 if (st->backbuffer != None && st->backbuffer != st->window)
545 XFreePixmap(st->dpy, st->backbuffer);
549 XSCREENSAVER_MODULE ("IFS", ifs)