http://ftp.aanet.ru/pub/Linux/X11/apps/xscreensaver-2.31.tar.gz
[xscreensaver] / hacks / distort.c
1 /* -*- mode: C; tab-width: 4 -*-
2  * xscreensaver, Copyright (c) 1992, 1993, 1994, 1996, 1997, 1998
3  * Jamie Zawinski <jwz@jwz.org>
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that
8  * copyright notice and this permission notice appear in supporting
9  * documentation.  No representations are made about the suitability of this
10  * software for any purpose.  It is provided "as is" without express or 
11  * implied warranty.
12  */
13
14 /* distort
15  * by Jonas Munsin (jmunsin@iki.fi) and Jamie Zawinski <jwz@jwz.org>
16  * TODO:
17  *      -check the allocations in init_round_lense again, maybe make it possible again
18  *       to use swamp without pre-allocating/calculating (although that
19  *       makes it slower) - -swamp is memory hungry
20  *      -more distortion matrices (fortunately, I'm out of ideas :)
21  * Stuff that would be cool but probably too much of a resource hog:
22  *      -some kind of interpolation to avoid jaggies
23  * program idea borrowed from a screensaver on a non-*NIX OS,
24  */
25
26 #include <math.h>
27 #include "screenhack.h"
28 #include <X11/Xutil.h>
29
30 #ifdef HAVE_XSHM_EXTENSION
31 # include "xshm.h"
32 static Bool use_shm;
33 static XShmSegmentInfo shm_info;
34 #endif /* HAVE_XSHM_EXTENSION */
35
36 struct coo {
37         int x;
38         int y;
39         int r, r_change;
40         int xmove, ymove;
41 };
42 static struct coo xy_coo[10];
43
44 static int delay, radius, speed, number, blackhole, vortex, magnify;
45 static XWindowAttributes xgwa;
46 static GC gc;
47 static Window g_window;
48 static Display *g_dpy; 
49
50 static XImage *orig_map, *buffer_map;
51
52 static int ***from;
53 static int ****from_array;
54 static void (*effect) (int) = NULL;
55 static void move_lense(int);
56 static void swamp_thing(int);
57 static void new_rnd_coo(int);
58 static void init_round_lense(void);
59
60 static void init_distort(Display *dpy, Window window) 
61 {
62         XGCValues gcv;
63         long gcflags;
64         int i;
65
66         g_window=window;
67         g_dpy=dpy;
68
69         delay = get_integer_resource ("delay", "Integer");
70         radius = get_integer_resource ("radius", "Integer");
71         speed = get_integer_resource ("speed", "Integer");
72         number = get_integer_resource ("number", "Integer");
73
74 #ifdef HAVE_XSHM_EXTENSION
75         use_shm = get_boolean_resource("useSHM", "Boolean");
76 #endif /* HAVE_XSHM_EXTENSION */
77         
78         blackhole = get_boolean_resource("blackhole", "Boolean");
79         vortex = get_boolean_resource("vortex", "Boolean");
80         magnify = get_boolean_resource("magnify", "Boolean");
81         
82         if (get_boolean_resource ("swamp", "Boolean"))
83                 effect = &swamp_thing;
84         if (get_boolean_resource ("bounce", "Boolean"))
85                 effect = &move_lense;
86
87         if (effect == NULL && radius == 0 && speed == 0 && number == 0
88                 && !blackhole && !vortex && !magnify) {
89 /* if no cmdline options are given, randomly choose one of:
90  * -radius 50 -number 4 -speed 1 -bounce
91  * -radius 50 -number 4 -speed 1 -blackhole
92  * -radius 50 -number 4 -speed 1 -vortex
93  * -radius 50 -number 4 -speed 1 -vortex -magnify
94  * -radius 50 -number 4 -speed 1 -vortex -magnify -blackhole
95  * -radius 100 -number 1 -speed 2 -bounce
96  * -radius 100 -number 1 -speed 2 -blackhole
97  * -radius 100 -number 1 -speed 2 -vortex
98  * -radius 100 -number 1 -speed 2 -vortex -magnify
99  * -radius 100 -number 1 -speed 2 -vortex -magnify -blackhole
100  * -radius 50 -number 4 -speed 2 -swamp
101  * -radius 50 -number 4 -speed 2 -swamp -blackhole
102  * -radius 50 -number 4 -speed 2 -swamp -vortex
103  * -radius 50 -number 4 -speed 2 -swamp -vortex -magnify
104  * -radius 50 -number 4 -speed 2 -swamp -vortex -magnify -blackhole
105  */
106                 
107                 i = (random() % 15);
108
109                 switch (i) {
110                         case 0:
111                                 radius=50;number=4;speed=1;
112                                 effect=&move_lense;break;
113                         case 1:
114                                 radius=50;number=4;speed=1;blackhole=1;
115                                 effect=&move_lense;break;
116                         case 2:
117                                 radius=50;number=4;speed=1;vortex=1;
118                                 effect=&move_lense;break;
119                         case 3:
120                                 radius=50;number=4;speed=1;vortex=1;magnify=1;
121                                 effect=&move_lense;break;
122                         case 4:
123                                 radius=50;number=4;speed=1;vortex=1;magnify=1;blackhole=1;
124                                 effect=&move_lense;break;
125                         case 5:
126                                 radius=100;number=1;speed=2;
127                                 effect=&move_lense;break;
128                         case 6:
129                                 radius=100;number=1;speed=2;blackhole=1;
130                                 effect=&move_lense;break;
131                         case 7:
132                                 radius=100;number=1;speed=2;vortex=1;
133                                 effect=&move_lense;break;
134                         case 8:
135                                 radius=100;number=1;speed=2;vortex=1;magnify=1;
136                                 effect=&move_lense;break;
137                         case 9:
138                                 radius=100;number=1;speed=2;vortex=1;magnify=1;blackhole=1;
139                                 effect=&move_lense;break;
140                         case 10:
141                                 radius=50;number=4;speed=2;
142                                 effect=&swamp_thing;break;
143                         case 11:
144                                 radius=50;number=4;speed=2;blackhole=1;
145                                 effect=&swamp_thing;break;
146                         case 12:
147                                 radius=50;number=4;speed=2;vortex=1;
148                                 effect=&swamp_thing;break;
149                         case 13:
150                                 radius=50;number=4;speed=2;vortex=1;magnify=1;
151                                 effect=&swamp_thing;break;
152                         case 14: default:
153                                 radius=50;number=4;speed=2;vortex=1;magnify=1;blackhole=1;
154                                 effect=&swamp_thing;break;
155                 }
156
157         }
158
159         if (delay < 0)
160                 delay = 0;
161         if (radius <= 0)
162                 radius = 60;
163         if (speed <= 0) 
164                 speed = 2;
165         if (number <= 0)
166                 number=1;
167         if (number >= 10)
168                 number=1;
169         if (effect == NULL)
170                 effect = &move_lense;
171
172         XGetWindowAttributes (dpy, window, &xgwa);
173
174         gcv.function = GXcopy;
175         gcv.subwindow_mode = IncludeInferiors;
176         gcflags = GCForeground |GCFunction;
177         if (use_subwindow_mode_p(xgwa.screen, window)) /* see grabscreen.c */
178                 gcflags |= GCSubwindowMode;
179         gc = XCreateGC (dpy, window, gcflags, &gcv);
180
181         grab_screen_image (xgwa.screen, window);
182
183         buffer_map = 0;
184         orig_map = XGetImage(dpy, window, 0, 0, xgwa.width, xgwa.height,
185                                                  ~0L, ZPixmap);
186
187 # ifdef HAVE_XSHM_EXTENSION
188
189         if (use_shm)
190           {
191                 buffer_map = create_xshm_image(dpy, xgwa.visual, orig_map->depth,
192                                                                            ZPixmap, 0, &shm_info,
193                                                                            2*radius + speed + 2,
194                                                                            2*radius + speed + 2);
195                 if (!buffer_map)
196                   use_shm = False;
197           }
198 # endif /* HAVE_XSHM_EXTENSION */
199
200         if (!buffer_map)
201           {
202                 buffer_map = XCreateImage(dpy, xgwa.visual,
203                                                                   orig_map->depth, ZPixmap, 0, 0,
204                                                                   2*radius + speed + 2, 2*radius + speed + 2,
205                                                                   8, 0);
206                 buffer_map->data = (char *)
207                   calloc(buffer_map->height, buffer_map->bytes_per_line);
208         }
209
210         init_round_lense();
211
212         for (i = 0; i < number; i++) {
213                 new_rnd_coo(i);
214                 if (number != 1)
215                         xy_coo[i].r = (i*radius)/(number-1); /* "randomize" initial */
216                 else
217                          xy_coo[i].r = 0;
218                 xy_coo[i].r_change = speed + (i%2)*2*(-speed);  /* values a bit */
219                 xy_coo[i].xmove = speed + (i%2)*2*(-speed);
220                 xy_coo[i].ymove = speed + (i%2)*2*(-speed);
221         }
222 }
223
224 /* example: initializes a "see-trough" matrix */
225 /* static void make_null_lense(void)
226 {
227         int i, j;
228         for (i = 0; i < 2*radius+speed+2; i++) {
229                 for (j = 0 ; j < 2*radius+speed+2 ; j++) {
230                         from[i][j][0]=i;
231                         from[i][j][1]=j;
232                 }
233         } 
234 }
235 */
236
237 /* makes a lense with the Radius=loop and centred in
238  * the point (radius, radius)
239  */
240 static void make_round_lense(int radius, int loop)
241 {
242         int i, j;
243
244         for (i = 0; i < 2*radius+speed+2; i++) {
245                 for(j = 0; j < 2*radius+speed+2; j++) {
246                         double r, d;
247                         r = sqrt ((i-radius)*(i-radius)+(j-radius)*(j-radius));
248                         d=r/loop;
249
250                         if (r < loop-1) {
251
252                                 if (vortex) { /* vortex-twist effect */
253                                         double angle;
254                 /* this one-line formula for getting a nice rotation angle is borrowed
255                  * (with permission) from the whirl plugin for gimp,
256                  * Copyright (C) 1996 Federico Mena Quintero
257                  */
258                 /* 2.5 is just a constant used because it looks good :) */
259                                         angle = 2.5*(1-r/loop)*(1-r/loop);
260
261                                         from[i][j][0] = radius + cos(angle -
262                                                 atan2(radius-j,-(radius-i)))*r;
263                                         from[i][j][1] = radius + sin(angle -
264                                                 atan2(radius-j,-(radius-i)))*r;
265
266                                         if (magnify) {
267                                                 r = sin(d*M_PI_2);
268                                                 if (blackhole && r != 0) /* blackhole effect */
269                                                         r = 1/r;
270                                                 from[i][j][0] = radius + (from[i][j][0]-radius)*r;
271                                                 from[i][j][1] = radius + (from[i][j][1]-radius)*r;
272                                         }
273                                 } else { /* default is to magnify */
274                                         r = sin(d*M_PI_2);
275                                 
276         /* raising r to different power here gives different amounts of
277          * distortion, a negative value sucks everything into a black hole
278          */
279                                 /*      r = r*r; */
280                                         if (blackhole) /* blackhole effect */
281                                                 r = 1/r;
282                                                                         /* bubble effect (and blackhole) */
283                                         from[i][j][0] = radius + (i-radius)*r;
284                                         from[i][j][1] = radius + (j-radius)*r;
285                                 }
286                         } else { /* not inside loop */
287                                 from[i][j][0] = i;
288                                 from[i][j][1] = j;
289                         }
290                 }
291         }
292 }
293
294 static void allocate_lense(void)
295 {
296         int i, j;
297         /* maybe this should be redone so that from[][][] is in one block;
298          * then pointers could be used instead of arrays in some places (and
299          * maybe give a speedup - maybe also consume less memory)
300          */
301
302         from = (int ***)malloc((2*radius+speed+2) * sizeof(int **));
303         if (from == NULL) {
304                 perror("distort");
305                 exit(EXIT_FAILURE);
306         }
307         for (i = 0; i < 2*radius+speed+2; i++) {
308                 from[i] = (int **)malloc((2*radius+speed+2) * sizeof(int *));
309                 if (from[i] == NULL) {
310                         perror("distort");
311                         exit(EXIT_FAILURE);
312                 }
313                 for (j = 0; j < 2*radius+speed+2; j++) {
314                         from[i][j] = (int *)malloc(2 * sizeof(int));
315                         if (from[i][j] == NULL) {
316                                 perror("distort");
317                                 exit(EXIT_FAILURE);
318                         }
319                 }
320         }
321 }
322
323 /* from_array in an array containing precalculated from matrices,
324  * this is a double faced mem vs speed trade, it's faster, but eats
325  * _a lot_ of mem for large radius (is there a bug here? I can't see it)
326  */
327 static void init_round_lense(void)
328 {
329         int k;
330
331         if (effect == &swamp_thing) {
332                 from_array = (int ****)malloc((radius+1)*sizeof(int ***));
333                 for (k=0; k <= radius; k++) {
334                         allocate_lense();
335                         make_round_lense(radius, k);
336                         from_array[k] = from;
337                 }
338         } else { /* just allocate one from[][][] */
339                 allocate_lense();
340                 make_round_lense(radius,radius);
341         }
342 }
343
344
345 /* generate an XImage of from[][][] and draw it on the screen */
346 void draw(int k)
347 {
348         int i, j;
349         for(i = 0 ; i < 2*radius+speed+2; i++) {
350                 for(j = 0 ; j < 2*radius+speed+2 ; j++) {
351                         if (xy_coo[k].x+from[i][j][0] >= 0 &&
352                                         xy_coo[k].x+from[i][j][0] < xgwa.width &&
353                                         xy_coo[k].y+from[i][j][1] >= 0 &&
354                                         xy_coo[k].y+from[i][j][1] < xgwa.height)
355                                 XPutPixel(buffer_map, i, j,
356                                                 XGetPixel(orig_map,
357                                                         xy_coo[k].x+from[i][j][0],
358                                                         xy_coo[k].y+from[i][j][1]));
359                 }
360         }
361
362         XPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y,
363                         2*radius+speed+2, 2*radius+speed+2);
364 }
365
366 /* create a new, random coordinate, that won't interfer with any other
367  * coordinates, as the drawing routines would be significantly slowed
368  * down if they were to handle serveral layers of distortions
369  */
370 static void new_rnd_coo(int k)
371 {
372         int i;
373
374         xy_coo[k].x = (random() % (xgwa.width-2*radius));
375         xy_coo[k].y = (random() % (xgwa.height-2*radius));
376         
377         for (i = 0; i < number; i++) {
378                 if (i != k) {
379                         if ((abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius+speed+2)
380                          && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius+speed+2)) {
381                                 xy_coo[k].x = (random() % (xgwa.width-2*radius));
382                                 xy_coo[k].y = (random() % (xgwa.height-2*radius));
383                                 i=-1; /* ugly */
384                         } 
385                 }
386         }
387 }
388
389 /* move lens and handle bounces with walls and other lenses */
390 static void move_lense(int k)
391 {
392         int i;
393
394         if (xy_coo[k].x + 4*radius/2 >= xgwa.width)
395                 xy_coo[k].xmove = -abs(xy_coo[k].xmove);
396         if (xy_coo[k].x <= speed) 
397                 xy_coo[k].xmove = abs(xy_coo[k].xmove);
398         if (xy_coo[k].y + 4*radius/2 >= xgwa.height)
399                 xy_coo[k].ymove = -abs(xy_coo[k].ymove);
400         if (xy_coo[k].y <= speed)
401                 xy_coo[k].ymove = abs(xy_coo[k].ymove);
402
403         xy_coo[k].x = xy_coo[k].x + xy_coo[k].xmove;
404         xy_coo[k].y = xy_coo[k].y + xy_coo[k].ymove;
405
406         for (i = 0; i < number; i++) {
407                 if ((i != k)
408                 
409 /* This commented test is for rectangular lenses (not presently used) and
410  * the one used is for circular ones
411                 && (abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius)
412                 && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius)) { */
413
414                 && ((xy_coo[k].x - xy_coo[i].x)*(xy_coo[k].x - xy_coo[i].x)
415                   + (xy_coo[k].y - xy_coo[i].y)*(xy_coo[k].y - xy_coo[i].y)
416                         <= 2*radius*2*radius)) {
417
418                         int x, y;
419                         x = xy_coo[k].xmove;
420                         y = xy_coo[k].ymove;
421                         xy_coo[k].xmove = xy_coo[i].xmove;
422                         xy_coo[k].ymove = xy_coo[i].ymove;
423                         xy_coo[i].xmove = x;
424                         xy_coo[i].ymove = y;
425                 }
426         }
427
428 }
429
430 /* make xy_coo[k] grow/shrink */
431 void swamp_thing(int k)
432 {
433         if (xy_coo[k].r >= radius)
434                 xy_coo[k].r_change = -abs(xy_coo[k].r_change);
435         
436         if (xy_coo[k].r <= 0) {
437                 from = from_array[0];
438                 draw(k); 
439                 xy_coo[k].r_change = abs(xy_coo[k].r_change);
440                 new_rnd_coo(k);
441                 xy_coo[k].r=xy_coo[k].r_change;
442                 return;
443         }
444
445         xy_coo[k].r = xy_coo[k].r + xy_coo[k].r_change;
446
447         if (xy_coo[k].r >= radius)
448                 xy_coo[k].r = radius;
449         if (xy_coo[k].r <= 0)
450                 xy_coo[k].r=0;
451
452         from = from_array[xy_coo[k].r];
453 }
454
455
456 \f
457
458 char *progclass = "Distort";
459
460 char *defaults [] = {
461         "*dontClearRoot:                True",
462 #ifdef __sgi    /* really, HAVE_READ_DISPLAY_EXTENSION */
463         "*visualID:                     Best",
464 #endif
465
466         "*delay:                        10000",
467         "*radius:                       0",
468         "*speed:                        0",
469         "*number:                       0",
470         "*vortex:                       False",
471         "*magnify:                      False",
472         "*swamp:                        False",
473         "*bounce:                       False",
474         "*blackhole:            False",
475 #ifdef HAVE_XSHM_EXTENSION
476         "*useSHM:                       False",         /* xshm turns out not to help. */
477 #endif /* HAVE_XSHM_EXTENSION */
478         0
479 };
480
481 XrmOptionDescRec options [] = {
482         { "-delay",     ".delay",       XrmoptionSepArg, 0 },
483         { "-radius",    ".radius",      XrmoptionSepArg, 0 },
484         { "-speed",     ".speed",       XrmoptionSepArg, 0 },
485         { "-number",    ".number",      XrmoptionSepArg, 0 },
486         { "-swamp",     ".swamp",       XrmoptionNoArg, "True" },
487         { "-bounce",    ".bounce",      XrmoptionNoArg, "True" },
488         { "-vortex",    ".vortex",      XrmoptionNoArg, "True" },
489         { "-magnify",   ".magnify",     XrmoptionNoArg, "True" },
490         { "-blackhole", ".blackhole",   XrmoptionNoArg, "True" },
491 #ifdef HAVE_XSHM_EXTENSION
492         { "-shm",               ".useSHM",      XrmoptionNoArg, "True" },
493         { "-no-shm",    ".useSHM",      XrmoptionNoArg, "False" },
494 #endif /* HAVE_XSHM_EXTENSION */
495         { 0, 0, 0, 0 }
496 };
497
498
499 void screenhack(Display *dpy, Window window)
500 {
501         int k;
502
503         init_distort (dpy, window);
504         while (1) {
505                 for (k = 0; k < number; k++) {
506                         effect(k);
507                         draw(k);
508                 }
509
510                 XSync(dpy, True);
511                 if (delay) usleep(delay);
512         }
513
514 }