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 [] = {
106 # ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */
107 "*doubleBuffer: False",
109 "*doubleBuffer: True",
112 "*ignoreRotation: True",
117 static XrmOptionDescRec ifs_options [] = {
118 { "-detail", ".length", XrmoptionSepArg, 0 },
119 { "-delay", ".delay", XrmoptionSepArg, 0 },
120 { "-mode", ".mode", XrmoptionSepArg, 0 },
121 { "-colors", ".colors", XrmoptionSepArg, 0 },
122 { "-functions", ".lensnum", XrmoptionSepArg, 0 },
123 { "-no-translate", ".translate", XrmoptionNoArg, "False" },
124 { "-no-scale", ".scale", XrmoptionNoArg, "False" },
125 { "-no-rotate", ".rotate", XrmoptionNoArg, "False" },
126 { "-recurse", ".recurse", XrmoptionNoArg, "True" },
127 { "-iterate", ".recurse", XrmoptionNoArg, "False" },
128 { "-multi", ".multi", XrmoptionNoArg, "True" },
129 { "-no-multi", ".multi", XrmoptionNoArg, "False" },
130 { "-db", ".doubleBuffer",XrmoptionNoArg, "True" },
131 { "-no-db", ".doubleBuffer",XrmoptionNoArg, "False" },
136 /* Draw all the queued points on the backbuffer */
138 drawpoints(struct state *st)
140 XDrawPoints(st->dpy, st->backbuffer, st->gc, st->pointbuf, st->npoints,
145 /* Set a point to be drawn, if it hasn't been already.
146 * Expects coordinates in 256ths of a pixel. */
148 sp(struct state *st, int x, int y)
150 if (x < 0 || x >= st->width8 || y < 0 || y >= st->height8)
156 if (getdot(x, y)) return;
159 if (x < st->xmin) st->xmin = x;
160 if (x > st->xmax) st->xmax = x;
161 if (y < st->ymin) st->ymin = y;
162 if (y > st->ymax) st->ymax = y;
164 st->pointbuf[st->npoints].x = x;
165 st->pointbuf[st->npoints].y = y;
168 if (st->npoints >= countof(st->pointbuf)) {
174 /* Precompute integer values for matrix multiplication and vector
175 * addition. The matrix multiplication will go like this (see iterate()):
176 * |x2| |ua ub| |x| |utx|
177 * | | = | | * | | + | |
178 * |y2| |uc ud| |y| |uty|
180 * There is an extra factor of 2^10 in these values, and an extra factor of
181 * 2^8 in the coordinates, in order to implement fixed-point arithmetic.
184 lensmatrix(struct state *st, Lens *l)
186 l->ua = 1024.0 * l->s * cos(l->r);
187 l->ub = -1024.0 * l->s * sin(l->r);
190 l->utx = 131072.0 * st->width * (l->s * (sin(l->r) - cos(l->r))
192 l->uty = -131072.0 * st->height * (l->s * (sin(l->r) + cos(l->r))
197 CreateLens(struct state *st,
204 newlens->sa = newlens->txa = newlens->tya = 0;
206 newlens->r = newlens->ro = newlens->rt = nr;
212 newlens->s = newlens->so = newlens->st = ns;
215 else newlens->s = 0.5;
220 lensmatrix(st, newlens);
224 mutate(struct state *st, Lens *l)
231 l->rt = myrandom(4) - 2;
233 factor = (sin((-M_PI / 2.0) + M_PI * l->rc) + 1.0) / 2.0;
234 l->r = l->ro + (l->rt - l->ro) * factor;
240 /* Reset counter, obtain new target value */
243 l->st = myrandom(2) - 1;
245 factor = (sin((-M_PI / 2.0) + M_PI * l->sc) + 1.0) / 2.0;
246 /* Take average of old target and new target, using factor to *
247 * weight. It's computed sinusoidally, resulting in smooth, *
248 * rhythmic transitions. */
249 l->s = l->so + (l->st - l->so) * factor;
253 l->txa += myrandom(0.004) - 0.002;
254 l->tya += myrandom(0.004) - 0.002;
257 if (l->tx > 6) l->txa -= 0.004;
258 if (l->ty > 6) l->tya -= 0.004;
259 if (l->tx < -6) l->txa += 0.004;
260 if (l->ty < -6) l->tya += 0.004;
261 if (l->txa > 0.05 || l->txa < -0.05) l->txa /= 1.7;
262 if (l->tya > 0.05 || l->tya < -0.05) l->tya /= 1.7;
264 if (st->rotate || st->scale || st->translate) {
270 #define STEPX(l,x,y) (((l)->ua * (x) + (l)->ub * (y) + (l)->utx) >> 10)
271 #define STEPY(l,x,y) (((l)->uc * (x) + (l)->ud * (y) + (l)->uty) >> 10)
272 /*#define STEPY(l,x,y) (((l)->ua * (y) - (l)->ub * (x) + (l)->uty) >> 10)*/
274 /* Calls itself <lensnum> times - with results from each lens/function. *
275 * After <length> calls to itself, it stops iterating and draws a point. */
277 recurse(struct state *st, int x, int y, int length, int p)
287 sp(st, STEPX(l, x, y), STEPY(l, x, y));
291 for (i = 0; i < st->lensnum; i++) {
293 recurse(st, STEPX(l, x, y), STEPY(l, x, y), length - 1, p);
298 /* Performs <count> random lens transformations, drawing a point at each
299 * iteration after the first 10.
302 iterate(struct state *st, int count, int p)
311 l = &st->lenses[random() % st->lensnum]; \
312 tx = STEPX(l, x, y); \
313 y = STEPY(l, x, y); \
316 for (i = 0; i < 10; i++) {
320 for ( ; i < count; i++) {
327 sp(st, STEPX(l, x, y), STEPY(l, x, y));
337 /* Come on and iterate, iterate, iterate and sing... *
338 * Yeah, this function just calls iterate, mutate, *
339 * and then draws everything. */
341 ifs_draw (Display *dpy, Window window, void *closure)
343 struct state *st = (struct state *) closure;
345 int xmin = st->xmin, xmax = st->xmax, ymin = st->ymin, ymax = st->ymax;
349 /* erase whatever was drawn in the previous frame */
350 if (xmin <= xmax && ymin <= ymax) {
351 XSetForeground(st->dpy, st->gc, st->blackColor);
352 XFillRectangle(st->dpy, st->backbuffer, st->gc,
354 xmax - xmin + 1, ymax - ymin + 1);
355 st->xmin = st->width + 1;
356 st->xmax = st->ymax = -1;
357 st->ymin = st->height + 1;
361 st->ccolour %= st->ncolours;
363 /* calculate and draw points for this frame */
368 for (i = 0; i < st->lensnum; i++) {
369 partcolor = st->ccolour * (i+1);
370 partcolor %= st->ncolours;
371 XSetForeground(st->dpy, st->gc, st->colours[partcolor].pixel);
372 memset(st->board, 0, st->widthb * st->height * sizeof(*st->board));
374 recurse(st, x, y, st->length - 1, i);
376 iterate(st, pow(st->lensnum, st->length - 1), i);
383 XSetForeground(st->dpy, st->gc, st->colours[st->ccolour].pixel);
384 memset(st->board, 0, st->widthb * st->height * sizeof(*st->board));
386 recurse(st, x, y, st->length, 0);
388 iterate(st, pow(st->lensnum, st->length), 0);
393 /* if we just drew into a buffer, copy the changed area (including
394 * erased area) to screen */
395 if (st->backbuffer != st->window
396 && ((st->xmin <= st->xmax && st->ymin <= st->ymax)
397 || (xmin <= xmax && ymin <= ymax))) {
398 if (st->xmin < xmin) xmin = st->xmin;
399 if (st->xmax > xmax) xmax = st->xmax;
400 if (st->ymin < ymin) ymin = st->ymin;
401 if (st->ymax > ymax) ymax = st->ymax;
402 XCopyArea(st->dpy, st->backbuffer, st->window, st->gc,
404 xmax - xmin + 1, ymax - ymin + 1,
408 for(i = 0; i < st->lensnum; i++) {
409 mutate(st, &st->lenses[i]);
416 ifs_reshape (Display *, Window, void *, unsigned int, unsigned int);
419 ifs_init (Display *d_arg, Window w_arg)
421 struct state *st = (struct state *) calloc (1, sizeof(*st));
423 XWindowAttributes xgwa;
425 /* Initialise all this X shizzle */
429 st->blackColor = BlackPixel(st->dpy, DefaultScreen(st->dpy));
430 st->whiteColor = WhitePixel(st->dpy, DefaultScreen(st->dpy));
431 st->gc = XCreateGC(st->dpy, st->window, 0, NULL);
433 XGetWindowAttributes (st->dpy, st->window, &xgwa);
434 ifs_reshape(st->dpy, st->window, st, xgwa.width, xgwa.height);
436 st->ncolours = get_integer_resource(st->dpy, "colors", "Colors");
437 if (st->ncolours < st->lensnum)
438 st->ncolours = st->lensnum;
439 if (st->colours) free(st->colours);
440 if (st->ncolours < 1) st->ncolours = 1;
441 st->colours = (XColor *)calloc(st->ncolours, sizeof(XColor));
442 if (!st->colours) exit(1);
443 make_smooth_colormap (xgwa.screen, xgwa.visual, xgwa.colormap,
444 st->colours, &st->ncolours,
447 /* Initialize IFS data */
449 st->delay = get_integer_resource(st->dpy, "delay", "Delay");
450 st->length = get_integer_resource(st->dpy, "length", "Detail");
451 if (st->length < 0) st->length = 0;
452 st->mode = get_integer_resource(st->dpy, "mode", "Mode");
454 st->rotate = get_boolean_resource(st->dpy, "rotate", "Boolean");
455 st->scale = get_boolean_resource(st->dpy, "scale", "Boolean");
456 st->translate = get_boolean_resource(st->dpy, "translate", "Boolean");
457 st->recurse = get_boolean_resource(st->dpy, "recurse", "Boolean");
458 st->multi = get_boolean_resource(st->dpy, "multi", "Boolean");
460 st->lensnum = get_integer_resource(st->dpy, "lensnum", "Functions");
461 if (st->lenses) free (st->lenses);
462 st->lenses = (Lens *)calloc(st->lensnum, sizeof(Lens));
463 if (!st->lenses) exit(1);
464 for (i = 0; i < st->lensnum; i++) {
477 ifs_reshape (Display *dpy, Window window, void *closure,
478 unsigned int w, unsigned int h)
480 struct state *st = (struct state *)closure;
481 XWindowAttributes xgwa;
483 /* oh well, we need the screen depth anyway */
484 XGetWindowAttributes (st->dpy, st->window, &xgwa);
486 st->width = xgwa.width;
487 st->widthb = ((xgwa.width + 31) >> 5);
488 st->height = xgwa.height;
489 st->width8 = xgwa.width << 8;
490 st->height8 = xgwa.height << 8;
492 if (!st->xmax && !st->ymax && !st->xmin && !st->ymin) {
493 st->xmin = xgwa.width + 1;
494 st->xmax = st->ymax = -1;
495 st->ymin = xgwa.height + 1;
498 if (st->backbuffer != None && st->backbuffer != st->window) {
499 XFreePixmap(st->dpy, st->backbuffer);
500 st->backbuffer = None;
503 if (get_boolean_resource (st->dpy, "doubleBuffer", "Boolean")) {
504 st->backbuffer = XCreatePixmap(st->dpy, st->window, st->width, st->height, xgwa.depth);
505 XSetForeground(st->dpy, st->gc, st->blackColor);
506 XFillRectangle(st->dpy, st->backbuffer, st->gc,
507 0, 0, st->width, st->height);
509 st->backbuffer = st->window;
510 XClearWindow(st->dpy, st->window);
513 if (st->board) free(st->board);
514 st->board = (unsigned int *)calloc(st->widthb * st->height, sizeof(unsigned int));
515 if (!st->board) exit(1);
519 ifs_event (Display *dpy, Window window, void *closure, XEvent *event)
521 struct state *st = (struct state *)closure;
522 if (screenhack_event_helper (dpy, window, event))
525 for (i = 0; i < st->lensnum; i++) {
539 ifs_free (Display *dpy, Window window, void *closure)
541 struct state *st = (struct state *) closure;
543 if (st->board) free(st->board);
544 if (st->lenses) free(st->lenses);
545 if (st->colours) free(st->colours);
546 if (st->backbuffer != None && st->backbuffer != st->window)
547 XFreePixmap(st->dpy, st->backbuffer);
551 XSCREENSAVER_MODULE ("IFS", ifs)