http://ftp.ksu.edu.tw/FTP/FreeBSD/distfiles/xscreensaver-4.20.tar.gz
[xscreensaver] / hacks / distort.c
1 /* -*- mode: C; tab-width: 4 -*-
2  * xscreensaver, Copyright (c) 1992-2005 Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 /* distort
14  * by Jonas Munsin (jmunsin@iki.fi) and Jamie Zawinski <jwz@jwz.org>
15  * TODO:
16  *      -check the allocations in init_round_lense again, maybe make it possible again
17  *       to use swamp without pre-allocating/calculating (although that
18  *       makes it slower) - -swamp is memory hungry
19  *      -more distortion matrices (fortunately, I'm out of ideas :)
20  * Stuff that would be cool but probably too much of a resource hog:
21  *      -some kind of interpolation to avoid jaggies
22  *    -large speed values leaves the image distorted
23  * program idea borrowed from a screensaver on a non-*NIX OS,
24  *
25  * 28 Sep 1999 Jonas Munsin (jmunsin@iki.fi)
26  *    Added about 10x faster algortim for 8, 16 and 32 bpp (modifies pixels
27  *    directly avoiding costly XPutPixle(XGetPixel()) calls, inspired by
28  *    xwhirl made by horvai@clipper.ens.fr (Peter Horvai) and the XFree86
29  *    Xlib sources.
30  *    This piece of code is really horrible, but it works, and at the moment
31  *    I don't have time or inspiration to fix something that works (knock
32  *    on wood).
33  * 08 Oct 1999 Jonas Munsin (jmunsin@iki.fi)
34  *      Corrected several bugs causing references beyond allocated memory.
35  */
36
37 #include <math.h>
38 #include "screenhack.h"
39 #include <X11/Xutil.h>
40 #include <X11/Xmd.h>
41
42 #ifdef HAVE_XSHM_EXTENSION
43 # include "xshm.h"
44 static Bool use_shm = False;
45 static XShmSegmentInfo shm_info;
46 #endif /* HAVE_XSHM_EXTENSION */
47
48 struct coo {
49         int x;
50         int y;
51         int r, r_change;
52         int xmove, ymove;
53 };
54 static struct coo xy_coo[10];
55
56 static int delay, radius, speed, number, blackhole, vortex, magnify, reflect, slow;
57 static XWindowAttributes xgwa;
58 static GC gc;
59 static Window g_window;
60 static Display *g_dpy;
61 static unsigned long black_pixel;
62
63 static XImage *orig_map, *buffer_map;
64 static unsigned long *buffer_map_cache;
65
66 static int ***from;
67 static int ****from_array;
68 static int *fast_from = NULL;
69 static void (*effect) (int) = NULL;
70 static void move_lense(int);
71 static void swamp_thing(int);
72 static void new_rnd_coo(int);
73 static void init_round_lense(void);
74 static void (*draw) (int) = NULL;
75 static void reflect_draw(int);
76 static void plain_draw(int);
77
78 static void (*draw_routine)(XImage *, XImage *, int, int, int *) = NULL;
79 static void fast_draw_8(XImage *, XImage *, int, int, int *);
80 static void fast_draw_16(XImage *, XImage *, int, int, int *);
81 static void fast_draw_32(XImage *, XImage *, int, int, int *);
82 static void generic_draw(XImage *, XImage *, int, int, int *);
83 static int bpp_size = 0;
84
85
86 static void init_distort(Display *dpy, Window window) 
87 {
88         XGCValues gcv;
89         long gcflags;
90         int i;
91
92         g_window=window;
93         g_dpy=dpy;
94
95         delay = get_integer_resource("delay", "Integer");
96         radius = get_integer_resource("radius", "Integer");
97         speed = get_integer_resource("speed", "Integer");
98         number = get_integer_resource("number", "Integer");
99
100 #ifdef HAVE_XSHM_EXTENSION
101         use_shm = get_boolean_resource("useSHM", "Boolean");
102 #endif /* HAVE_XSHM_EXTENSION */
103         
104         blackhole = get_boolean_resource("blackhole", "Boolean");
105         vortex = get_boolean_resource("vortex", "Boolean");
106         magnify = get_boolean_resource("magnify", "Boolean");
107         reflect = get_boolean_resource("reflect", "Boolean");
108         slow = get_boolean_resource("slow", "Boolean");
109         
110         if (get_boolean_resource("swamp", "Boolean"))
111                 effect = &swamp_thing;
112         if (get_boolean_resource("bounce", "Boolean"))
113                 effect = &move_lense;
114
115         XGetWindowAttributes (dpy, window, &xgwa);
116
117         if (effect == NULL && radius == 0 && speed == 0 && number == 0
118                 && !blackhole && !vortex && !magnify && !reflect) {
119 /* if no cmdline options are given, randomly choose one of:
120  * -radius 50 -number 4 -speed 1 -bounce
121  * -radius 50 -number 4 -speed 1 -blackhole
122  * -radius 50 -number 4 -speed 1 -vortex
123  * -radius 50 -number 4 -speed 1 -vortex -magnify
124  * -radius 50 -number 4 -speed 1 -vortex -magnify -blackhole
125  * -radius 100 -number 1 -speed 2 -bounce
126  * -radius 100 -number 1 -speed 2 -blackhole
127  * -radius 100 -number 1 -speed 2 -vortex
128  * -radius 100 -number 1 -speed 2 -vortex -magnify
129  * -radius 100 -number 1 -speed 2 -vortex -magnify -blackhole
130  * -radius 80 -number 1 -speed 2 -reflect
131  * -radius 50 -number 3 -speed 2 -reflect
132  * jwz: not these
133  *   -radius 50 -number 4 -speed 2 -swamp
134  *   -radius 50 -number 4 -speed 2 -swamp -blackhole
135  *   -radius 50 -number 4 -speed 2 -swamp -vortex
136  *   -radius 50 -number 4 -speed 2 -swamp -vortex -magnify
137  *   -radius 50 -number 4 -speed 2 -swamp -vortex -magnify -blackhole
138  */
139                 
140                 i = (random() % 12 /* 17 */);
141
142                 draw = &plain_draw;
143
144                 switch (i) {
145                         case 0:
146                                 radius=50;number=4;speed=1;
147                                 effect=&move_lense;break;
148                         case 1:
149                                 radius=50;number=4;speed=1;blackhole=1;
150                                 effect=&move_lense;break;
151                         case 2:
152                                 radius=50;number=4;speed=1;vortex=1;
153                                 effect=&move_lense;break;
154                         case 3:
155                                 radius=50;number=4;speed=1;vortex=1;magnify=1;
156                                 effect=&move_lense;break;
157                         case 4:
158                                 radius=50;number=4;speed=1;vortex=1;magnify=1;blackhole=1;
159                                 effect=&move_lense;break;
160                         case 5:
161                                 radius=100;number=1;speed=2;
162                                 effect=&move_lense;break;
163                         case 6:
164                                 radius=100;number=1;speed=2;blackhole=1;
165                                 effect=&move_lense;break;
166                         case 7:
167                                 radius=100;number=1;speed=2;vortex=1;
168                                 effect=&move_lense;break;
169                         case 8:
170                                 radius=100;number=1;speed=2;vortex=1;magnify=1;
171                                 effect=&move_lense;break;
172                         case 9:
173                                 radius=100;number=1;speed=2;vortex=1;magnify=1;blackhole=1;
174                                 effect=&move_lense;break;
175
176                         case 10:
177                                 radius=80;number=1;speed=2;reflect=1;
178                                 draw = &reflect_draw;effect = &move_lense;break;
179                         case 11:
180                                 radius=50;number=4;speed=2;reflect=1;
181                                 draw = &reflect_draw;effect = &move_lense;break;
182
183 #if 0 /* jwz: not these */
184                         case 12:
185                                 radius=50;number=4;speed=2;
186                                 effect=&swamp_thing;break;
187                         case 13:
188                                 radius=50;number=4;speed=2;blackhole=1;
189                                 effect=&swamp_thing;break;
190                         case 14:
191                                 radius=50;number=4;speed=2;vortex=1;
192                                 effect=&swamp_thing;break;
193                         case 15:
194                                 radius=50;number=4;speed=2;vortex=1;magnify=1;
195                                 effect=&swamp_thing;break;
196                         case 16:
197                                 radius=50;number=4;speed=2;vortex=1;magnify=1;blackhole=1;
198                                 effect=&swamp_thing;break;
199 #endif
200
201             default:
202                 abort(); break;
203                 }
204
205         /* but if the window is small, reduce default radius */
206         if (xgwa.width < radius * 8)
207           radius = xgwa.width/8;
208         }
209
210     /* never allow the radius to be too close to the min window dimension
211      */
212     if (radius >= xgwa.width  * 0.45) radius = xgwa.width  * 0.45;
213     if (radius >= xgwa.height * 0.45) radius = xgwa.height * 0.45;
214
215
216     /* -swamp mode consumes vast amounts of memory, proportional to radius --
217        so throttle radius to a small-ish value (60 => ~30MB.)
218      */
219     if (effect == &swamp_thing && radius > 60)
220       radius = 60;
221
222         if (delay < 0)
223                 delay = 0;
224         if (radius <= 0)
225                 radius = 60;
226         if (speed <= 0) 
227                 speed = 2;
228         if (number <= 0)
229                 number=1;
230         if (number >= 10)
231                 number=1;
232         if (effect == NULL)
233                 effect = &move_lense;
234         if (reflect) {
235                 draw = &reflect_draw;
236                 effect = &move_lense;
237         }
238         if (draw == NULL)
239                 draw = &plain_draw;
240
241         black_pixel = BlackPixelOfScreen( xgwa.screen );
242
243         gcv.function = GXcopy;
244         gcv.subwindow_mode = IncludeInferiors;
245         gcflags = GCForeground |GCFunction;
246         if (use_subwindow_mode_p(xgwa.screen, window)) /* see grabscreen.c */
247                 gcflags |= GCSubwindowMode;
248         gc = XCreateGC (dpy, window, gcflags, &gcv);
249
250     load_random_image (xgwa.screen, window, window, NULL, NULL);
251
252         buffer_map = 0;
253         orig_map = XGetImage(dpy, window, 0, 0, xgwa.width, xgwa.height,
254                                                  ~0L, ZPixmap);
255         buffer_map_cache = malloc(sizeof(unsigned long)*(2*radius+speed+2)*(2*radius+speed+2));
256
257         if (buffer_map_cache == NULL) {
258                 perror("distort");
259                 exit(EXIT_FAILURE);
260         }
261
262 # ifdef HAVE_XSHM_EXTENSION
263
264         if (use_shm)
265           {
266                 buffer_map = create_xshm_image(dpy, xgwa.visual, orig_map->depth,
267                                                                            ZPixmap, 0, &shm_info,
268                                                                            2*radius + speed + 2,
269                                                                            2*radius + speed + 2);
270                 if (!buffer_map)
271                   use_shm = False;
272           }
273 # endif /* HAVE_XSHM_EXTENSION */
274
275         if (!buffer_map)
276           {
277                 buffer_map = XCreateImage(dpy, xgwa.visual,
278                                                                   orig_map->depth, ZPixmap, 0, 0,
279                                                                   2*radius + speed + 2, 2*radius + speed + 2,
280                                                                   8, 0);
281                 buffer_map->data = (char *)
282                   calloc(buffer_map->height, buffer_map->bytes_per_line);
283         }
284
285         if ((buffer_map->byte_order == orig_map->byte_order)
286                         && (buffer_map->depth == orig_map->depth)
287                         && (buffer_map->format == ZPixmap)
288                         && (orig_map->format == ZPixmap)
289                         && !slow) {
290                 switch (orig_map->bits_per_pixel) {
291                         case 32:
292                                 draw_routine = &fast_draw_32;
293                                 bpp_size = sizeof(CARD32);
294                                 break;
295                         case 16:
296                                 draw_routine = &fast_draw_16;
297                                 bpp_size = sizeof(CARD16);
298                                 break;
299                         case 8:
300                                 draw_routine = &fast_draw_8;
301                                 bpp_size = sizeof(CARD8);
302                                 break;
303                         default:
304                                 draw_routine = &generic_draw;
305                                 break;
306                 }
307         } else {
308                 draw_routine = &generic_draw;
309         }
310         init_round_lense();
311
312         for (i = 0; i < number; i++) {
313                 new_rnd_coo(i);
314                 if (number != 1)
315                         xy_coo[i].r = (i*radius)/(number-1); /* "randomize" initial */
316                 else
317                          xy_coo[i].r = 0;
318                 xy_coo[i].r_change = speed + (i%2)*2*(-speed);  /* values a bit */
319                 xy_coo[i].xmove = speed + (i%2)*2*(-speed);
320                 xy_coo[i].ymove = speed + (i%2)*2*(-speed);
321         }
322
323 }
324
325 /* example: initializes a "see-trough" matrix */
326 /* static void make_null_lense(void)
327 {
328         int i, j;
329         for (i = 0; i < 2*radius+speed+2; i++) {
330                 for (j = 0 ; j < 2*radius+speed+2 ; j++) {
331                         from[i][j][0]=i;
332                         from[i][j][1]=j;
333                 }
334         } 
335 }
336 */
337 static void convert(void) {
338         int *p;
339         int i, j;
340         fast_from = calloc(1, sizeof(int)*((buffer_map->bytes_per_line/bpp_size)*(2*radius+speed+2) + 2*radius+speed+2));
341         if (fast_from == NULL) {
342                 perror("distort");
343                 exit(EXIT_FAILURE);
344         }
345         p = fast_from;
346         for (i = 0; i < 2*radius+speed+2; i++) {
347                 for (j = 0; j < 2*radius+speed+2; j++) {
348                         *(p + i + j*buffer_map->bytes_per_line/bpp_size)
349                                 = from[i][j][0] + xgwa.width*from[i][j][1];
350                         if (*(p + i + j*buffer_map->bytes_per_line/bpp_size) < 0
351                                         || *(p + i + j*buffer_map->bytes_per_line/bpp_size) >= orig_map->height*orig_map->width) {
352                                 *(p + i + j*buffer_map->bytes_per_line/bpp_size) = 0;
353                         }
354                 }
355         }
356 }
357
358 /* makes a lense with the Radius=loop and centred in
359  * the point (radius, radius)
360  */
361 static void make_round_lense(int radius, int loop)
362 {
363         int i, j;
364
365         for (i = 0; i < 2*radius+speed+2; i++) {
366                 for(j = 0; j < ((0 == bpp_size) ? (2*radius+speed+2) : (buffer_map->bytes_per_line/bpp_size)); j++) {
367                         double r, d;
368                         r = sqrt ((i-radius)*(i-radius)+(j-radius)*(j-radius));
369                         if (loop == 0)
370                           d=0.0;
371                         else
372                           d=r/loop;
373
374                         if (r < loop-1) {
375
376                                 if (vortex) { /* vortex-twist effect */
377                                         double angle;
378                 /* this one-line formula for getting a nice rotation angle is borrowed
379                  * (with permission) from the whirl plugin for gimp,
380                  * Copyright (C) 1996 Federico Mena Quintero
381                  */
382                 /* 5 is just a constant used because it looks good :) */
383                                         angle = 5*(1-d)*(1-d);
384
385         /* Avoid atan2: DOMAIN error message */
386                                         if ((radius-j) == 0.0 && (radius-i) == 0.0) {
387                                                 from[i][j][0] = radius + cos(angle)*r;
388                                                 from[i][j][1] = radius + sin(angle)*r;
389                                         } else {
390                                                 from[i][j][0] = radius +
391                                                         cos(angle - atan2(radius-j, -(radius-i)))*r;
392                                                 from[i][j][1] = radius +
393                                                         sin(angle - atan2(radius-j, -(radius-i)))*r;
394                                         }
395                                         if (magnify) {
396                                                 r = sin(d*M_PI_2);
397                                                 if (blackhole && r != 0) /* blackhole effect */
398                                                         r = 1/r;
399                                                 from[i][j][0] = radius + (from[i][j][0]-radius)*r;
400                                                 from[i][j][1] = radius + (from[i][j][1]-radius)*r;
401                                         }
402                                 } else { /* default is to magnify */
403                                         r = sin(d*M_PI_2);
404                                 
405         /* raising r to different power here gives different amounts of
406          * distortion, a negative value sucks everything into a black hole
407          */
408                                 /*      r = r*r; */
409                                         if (blackhole && r != 0) /* blackhole effect */
410                                                 r = 1/r;
411                                                                         /* bubble effect (and blackhole) */
412                                         from[i][j][0] = radius + (i-radius)*r;
413                                         from[i][j][1] = radius + (j-radius)*r;
414                                 }
415                         } else { /* not inside loop */
416                                 from[i][j][0] = i;
417                                 from[i][j][1] = j;
418                         }
419                 }
420         }
421
422         /* this is really just a quick hack to keep both the compability mode with all depths and still
423          * allow the custom optimized draw routines with the minimum amount of work */
424         if (0 != bpp_size) {
425                 convert();
426         }
427 }
428
429 #ifndef EXIT_FAILURE
430 # define EXIT_FAILURE -1
431 #endif
432
433 static void allocate_lense(void)
434 {
435         int i, j;
436         int s = ((0 != bpp_size) ? (buffer_map->bytes_per_line/bpp_size) : (2*radius+speed+2));
437         /* maybe this should be redone so that from[][][] is in one block;
438          * then pointers could be used instead of arrays in some places (and
439          * maybe give a speedup - maybe also consume less memory)
440          */
441         from = (int ***)malloc(s*sizeof(int **));
442         if (from == NULL) {
443                 perror("distort");
444                 exit(EXIT_FAILURE);
445         }
446         for (i = 0; i < s; i++) {
447                 from[i] = (int **)malloc((2*radius+speed+2) * sizeof(int *));
448                 if (from[i] == NULL) {
449                         perror("distort");
450                         exit(EXIT_FAILURE);
451                 }
452                 for (j = 0; j < s; j++) {
453                         from[i][j] = (int *)malloc(2 * sizeof(int));
454                         if (from[i][j] == NULL) {
455                                 perror("distort");
456                                 exit(EXIT_FAILURE);
457                         }
458                 }
459         }
460 }
461
462 /* from_array in an array containing precalculated from matrices,
463  * this is a double faced mem vs speed trade, it's faster, but eats
464  * _a lot_ of mem for large radius (is there a bug here? I can't see it)
465  */
466 static void init_round_lense(void)
467 {
468         int k;
469
470         if (effect == &swamp_thing) {
471                 from_array = (int ****)malloc((radius+1)*sizeof(int ***));
472                 for (k=0; k <= radius; k++) {
473                         allocate_lense();
474                         make_round_lense(radius, k);
475                         from_array[k] = from;
476                 }
477         } else { /* just allocate one from[][][] */
478                 allocate_lense();
479                 make_round_lense(radius,radius);
480         }
481 }
482
483 /* If fast_draw_8, fast_draw_16 or fast_draw_32 are to be used, the following properties
484  * of the src and dest XImages must hold (otherwise the generic, slooow, method provided
485  * by X is to be used):
486  *      src->byte_order == dest->byte_order
487  *      src->format == ZPixmap && dest->format == ZPixmap
488  *      src->depth == dest->depth == the depth the function in question asumes
489  * x and y is the coordinates in src from where to cut out the image from,
490  * distort_matrix is a precalculated array of how to distort the matrix
491  */
492
493 static void fast_draw_8(XImage *src, XImage *dest, int x, int y, int *distort_matrix) {
494         CARD8 *u = (CARD8 *)dest->data;
495         CARD8 *t = (CARD8 *)src->data + x + y*src->bytes_per_line/sizeof(CARD8);
496
497         while (u < (CARD8 *)(dest->data + sizeof(CARD8)*dest->height
498                                 *dest->bytes_per_line/sizeof(CARD8))) {
499                 *u++ = t[*distort_matrix++];
500         }
501 }
502
503 static void fast_draw_16(XImage *src, XImage *dest, int x, int y, int *distort_matrix) {
504         CARD16 *u = (CARD16 *)dest->data;
505         CARD16 *t = (CARD16 *)src->data + x + y*src->bytes_per_line/sizeof(CARD16);
506
507         while (u < (CARD16 *)(dest->data + sizeof(CARD16)*dest->height
508                                 *dest->bytes_per_line/sizeof(CARD16))) {
509                 *u++ = t[*distort_matrix++];
510         }
511 }
512
513 static void fast_draw_32(XImage *src, XImage *dest, int x, int y, int *distort_matrix) {
514         CARD32 *u = (CARD32 *)dest->data;
515         CARD32 *t = (CARD32 *)src->data + x + y*src->bytes_per_line/sizeof(CARD32);
516
517         while (u < (CARD32 *)(dest->data + sizeof(CARD32)*dest->height
518                                 *dest->bytes_per_line/sizeof(CARD32))) {
519                 *u++ = t[*distort_matrix++];
520         }
521 }
522
523 static void generic_draw(XImage *src, XImage *dest, int x, int y, int *distort_matrix) {
524         int i, j;
525         for (i = 0; i < dest->width; i++)
526                 for (j = 0; j < dest->height; j++)
527                         if (from[i][j][0] + x >= 0 &&
528                                         from[i][j][0] + x < src->width &&
529                                         from[i][j][1] + y >= 0 &&
530                                         from[i][j][1] + y < src->height)
531                                 XPutPixel(dest, i, j,
532                                                 XGetPixel(src,
533                                                         from[i][j][0] + x,
534                                                         from[i][j][1] + y));
535 }
536
537 /* generate an XImage of from[][][] and draw it on the screen */
538 static void plain_draw(int k)
539 {
540         if (xy_coo[k].x+2*radius+speed+2 > orig_map->width ||
541                         xy_coo[k].y+2*radius+speed+2 > orig_map->height)
542                 return;
543
544         draw_routine(orig_map, buffer_map, xy_coo[k].x, xy_coo[k].y, fast_from);
545
546 # ifdef HAVE_XSHM_EXTENSION
547         if (use_shm)
548                 XShmPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y,
549                                 2*radius+speed+2, 2*radius+speed+2, False);
550         else
551
552         if (!use_shm)
553 # endif
554                 XPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y,
555                                 2*radius+speed+2, 2*radius+speed+2);
556
557 }
558
559
560 /* generate an XImage from the reflect algoritm submitted by
561  * Randy Zack <randy@acucorp.com>
562  * draw really got too big and ugly so I split it up
563  * it should be possible to use the from[][] to speed it up
564  * (once I figure out the algorithm used :)
565  */
566 static void reflect_draw(int k)
567 {
568         int i, j;
569         int     cx, cy;
570         int     ly, lysq, lx, ny, dist, rsq = radius * radius;
571
572         cx = cy = radius;
573         if (xy_coo[k].ymove > 0)
574                 cy += speed;
575         if (xy_coo[k].xmove > 0)
576                 cx += speed;
577
578         for(i = 0 ; i < 2*radius+speed+2; i++) {
579                 ly = i - cy;
580                 lysq = ly * ly;
581                 ny = xy_coo[k].y + i;
582                 if (ny >= orig_map->height) ny = orig_map->height-1;
583                 for(j = 0 ; j < 2*radius+speed+2 ; j++) {
584                         lx = j - cx;
585                         dist = lx * lx + lysq;
586                         if (dist > rsq ||
587                                 ly < -radius || ly > radius ||
588                                 lx < -radius || lx > radius)
589                                 XPutPixel( buffer_map, j, i,
590                                                    XGetPixel( orig_map, xy_coo[k].x + j, ny ));
591                         else if (dist == 0)
592                                 XPutPixel( buffer_map, j, i, black_pixel );
593                         else {
594                                 int     x = xy_coo[k].x + cx + (lx * rsq / dist);
595                                 int     y = xy_coo[k].y + cy + (ly * rsq / dist);
596                                 if (x < 0 || x >= xgwa.width ||
597                                         y < 0 || y >= xgwa.height)
598                                         XPutPixel( buffer_map, j, i, black_pixel );
599                                 else
600                                         XPutPixel( buffer_map, j, i,
601                                                            XGetPixel( orig_map, x, y ));
602                         }
603                 }
604         }
605
606         XPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y,
607                         2*radius+speed+2, 2*radius+speed+2);
608 }
609
610 /* create a new, random coordinate, that won't interfer with any other
611  * coordinates, as the drawing routines would be significantly slowed
612  * down if they were to handle serveral layers of distortions
613  */
614 static void new_rnd_coo(int k)
615 {
616         int i;
617
618         xy_coo[k].x = (random() % (xgwa.width-2*radius));
619         xy_coo[k].y = (random() % (xgwa.height-2*radius));
620         
621         for (i = 0; i < number; i++) {
622                 if (i != k) {
623                         if ((abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius+speed+2)
624                          && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius+speed+2)) {
625                                 xy_coo[k].x = (random() % (xgwa.width-2*radius));
626                                 xy_coo[k].y = (random() % (xgwa.height-2*radius));
627                                 i=-1; /* ugly */
628                         } 
629                 }
630         }
631 }
632
633 /* move lens and handle bounces with walls and other lenses */
634 static void move_lense(int k)
635 {
636         int i;
637
638         if (xy_coo[k].x + 2*radius + speed + 2 >= xgwa.width)
639                 xy_coo[k].xmove = -abs(xy_coo[k].xmove);
640         if (xy_coo[k].x <= speed) 
641                 xy_coo[k].xmove = abs(xy_coo[k].xmove);
642         if (xy_coo[k].y + 2*radius + speed + 2 >= xgwa.height)
643                 xy_coo[k].ymove = -abs(xy_coo[k].ymove);
644         if (xy_coo[k].y <= speed)
645                 xy_coo[k].ymove = abs(xy_coo[k].ymove);
646
647         xy_coo[k].x = xy_coo[k].x + xy_coo[k].xmove;
648         xy_coo[k].y = xy_coo[k].y + xy_coo[k].ymove;
649
650         /* bounce against othe lenses */
651         for (i = 0; i < number; i++) {
652                 if ((i != k)
653                 
654 /* This commented test is for rectangular lenses (not currently used) and
655  * the one used is for circular ones
656                 && (abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius)
657                 && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius)) { */
658
659                 && ((xy_coo[k].x - xy_coo[i].x)*(xy_coo[k].x - xy_coo[i].x)
660                   + (xy_coo[k].y - xy_coo[i].y)*(xy_coo[k].y - xy_coo[i].y)
661                         <= 2*radius*2*radius)) {
662
663                         int x, y;
664                         x = xy_coo[k].xmove;
665                         y = xy_coo[k].ymove;
666                         xy_coo[k].xmove = xy_coo[i].xmove;
667                         xy_coo[k].ymove = xy_coo[i].ymove;
668                         xy_coo[i].xmove = x;
669                         xy_coo[i].ymove = y;
670                 }
671         }
672
673 }
674
675 /* make xy_coo[k] grow/shrink */
676 static void swamp_thing(int k)
677 {
678         if (xy_coo[k].r >= radius)
679                 xy_coo[k].r_change = -abs(xy_coo[k].r_change);
680         
681         if (xy_coo[k].r <= 0) {
682                 from = from_array[0];
683                 draw(k); 
684                 xy_coo[k].r_change = abs(xy_coo[k].r_change);
685                 new_rnd_coo(k);
686                 xy_coo[k].r=xy_coo[k].r_change;
687                 return;
688         }
689
690         xy_coo[k].r = xy_coo[k].r + xy_coo[k].r_change;
691
692         if (xy_coo[k].r >= radius)
693                 xy_coo[k].r = radius;
694         if (xy_coo[k].r <= 0)
695                 xy_coo[k].r=0;
696
697         from = from_array[xy_coo[k].r];
698 }
699
700
701 \f
702
703 char *progclass = "Distort";
704
705 char *defaults [] = {
706         "*dontClearRoot:                True",
707 #ifdef __sgi    /* really, HAVE_READ_DISPLAY_EXTENSION */
708         "*visualID:                     Best",
709 #endif
710
711         "*delay:                        20000",
712         "*radius:                       0",
713         "*speed:                        0",
714         "*number:                       0",
715         "*vortex:                       False",
716         "*magnify:                      False",
717         "*swamp:                        False",
718         "*bounce:                       False",
719         "*reflect:                      False",
720         "*blackhole:            False",
721 #ifdef HAVE_XSHM_EXTENSION
722         "*useSHM:                       False",         /* xshm turns out not to help. */
723 #endif /* HAVE_XSHM_EXTENSION */
724         0
725 };
726
727 XrmOptionDescRec options [] = {
728         { "-delay",     ".delay",       XrmoptionSepArg, 0 },
729         { "-radius",    ".radius",      XrmoptionSepArg, 0 },
730         { "-speed",     ".speed",       XrmoptionSepArg, 0 },
731         { "-number",    ".number",      XrmoptionSepArg, 0 },
732         { "-swamp",     ".swamp",       XrmoptionNoArg, "True" },
733         { "-bounce",    ".bounce",      XrmoptionNoArg, "True" },
734         { "-reflect",   ".reflect",     XrmoptionNoArg, "True" },
735         { "-vortex",    ".vortex",      XrmoptionNoArg, "True" },
736         { "-magnify",   ".magnify",     XrmoptionNoArg, "True" },
737         { "-blackhole", ".blackhole",   XrmoptionNoArg, "True" },
738         { "-slow",      ".slow",        XrmoptionNoArg, "True" },
739 #ifdef HAVE_XSHM_EXTENSION
740         { "-shm",               ".useSHM",      XrmoptionNoArg, "True" },
741         { "-no-shm",    ".useSHM",      XrmoptionNoArg, "False" },
742 #endif /* HAVE_XSHM_EXTENSION */
743         { 0, 0, 0, 0 }
744 };
745
746
747 void screenhack(Display *dpy, Window window)
748 {
749         int k;
750
751         init_distort (dpy, window);
752         while (1) {
753                 for (k = 0; k < number; k++) {
754                         effect(k);
755                         draw(k);
756                 }
757
758                 XSync(dpy, False);
759         screenhack_handle_events (dpy);
760                 if (delay) usleep(delay);
761         }
762
763 }