http://ftp.x.org/contrib/applications/xscreensaver-2.24.tar.gz
[xscreensaver] / hacks / distort.c
1 /* xscreensaver, Copyright (c) 1992, 1993, 1994, 1996, 1997, 1998
2  * Jamie Zawinski <jwz@netscape.com>
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)
15  * it's a bit of a resource hog at the moment
16  * TODO:
17  *      -optimize for speed
18  *      -mutiple spheres/lenses (with bounces/layering)
19  *      -different distortion matrices
20  *      -randomize movement a bit
21  * program idea borrowed from a screensaver on a non-*NIX OS,
22  * code based on decayscreen by Jamie Zawinski
23  */
24
25 #include <math.h>
26 #include "screenhack.h"
27
28 static int delay, radius, speed, size_x, size_y;
29 static XWindowAttributes xgwa;
30 static GC gc;
31 static Pixmap orig_map, buffer_map;
32
33 static int ***from;
34
35 static void init_distort (Display *dpy, Window window) {
36         XGCValues gcv;
37         long gcflags;
38         int i, j;
39     
40         delay = get_integer_resource ("delay", "Integer");
41         radius = get_integer_resource ("radius", "Integer");
42         speed = get_integer_resource ("speed", "Integer");
43
44         if (delay < 0)
45                 delay = 0;
46         if (radius <= 0)
47                 radius = 60;
48         if (speed == 0) 
49                 speed = 2;
50
51         XGetWindowAttributes (dpy, window, &xgwa);
52
53         gcv.function = GXcopy;
54         gcv.subwindow_mode = IncludeInferiors;
55         gcflags = GCForeground |GCFunction;
56         if (use_subwindow_mode_p(xgwa.screen, window)) /* see grabscreen.c */
57                 gcflags |= GCSubwindowMode;
58         gc = XCreateGC (dpy, window, gcflags, &gcv);
59
60         size_x = xgwa.width;
61         size_y = xgwa.height;
62     
63         grab_screen_image (xgwa.screen, window);
64
65         orig_map = XCreatePixmap(dpy, window,
66                         xgwa.width, xgwa.height, xgwa.depth);
67         XCopyArea(dpy, window,
68                         orig_map, gc, 0, 0, xgwa.width, xgwa.height, 0, 0);
69         buffer_map = XCreatePixmap(dpy, window,
70                         2*radius + speed, 2*radius + speed,
71                         xgwa.depth);
72
73         from = (int ***)malloc ((2*radius+1) * sizeof(int **));
74         for(i = 0; i <= 2*radius; i++) {
75                 from[i] = (int **)malloc((2*radius+1) * sizeof(int *));
76                 for (j = 0; j <= 2*radius; j++)
77                         from[i][j] = (int *)malloc(2*sizeof(int));
78         }
79
80         /* initialize a "see-trough" matrix */
81         for (i = 0; i <= 2*radius; i++) {
82                 for (j = 0 ; j <= 2*radius ; j++) {
83                         from[i][j][0]=i-radius/2;
84                         from[i][j][1]=j-radius/2;
85                 }
86         }
87
88         /* initialize the distort matrix */
89         for (i = 0; i <= 2*radius; i++) {
90                 for(j = 0; j <= 2*radius; j++) {
91                         double r;
92                         r = sqrt ((i-radius)*(i-radius)+(j-radius)*(j-radius));
93                         if (r < radius) {
94                                 r = sin(r*(M_PI_2)/radius);
95                                 if (i < radius)
96                                         from[i][j][0] = radius/2 + (i-radius)*r;
97                                 else
98                                         from[i][j][0] = radius/2 + (i-radius)*r;
99                                 if (j < radius)
100                                         from[i][j][1] = radius/2 + (j-radius)*r;
101                                 else
102                                         from[i][j][1] = radius/2 + (j-radius)*r;
103                         }
104                 }
105         }
106
107         XSetGraphicsExposures(dpy, gc, False); /* stop events from XCopyArea */
108 }
109
110 static void
111 move_lens (int *x, int *y, int *xmove, int *ymove) {
112         if (*xmove==0)
113                 *xmove=speed;
114         if (*ymove==0)
115                 *ymove=speed;
116         if (*x==0)
117                 *x = radius + (random() % (size_x-2*radius));
118         if (*y==0)
119                 *y = radius + (random() % (size_y-2*radius));
120         if (*x + 3*radius/2 >= size_x)
121                 *xmove = -abs(*xmove);
122         if (*x - radius/2 <= 0) 
123                 *xmove = abs(*xmove);
124         if (*y + 3*radius/2 >= size_y)
125                 *ymove = -abs(*ymove);
126         if (*y - radius/2 <= 0)
127                 *ymove = abs(*ymove);
128
129         *x = *x + *xmove;
130         *y = *y + *ymove;
131 }
132
133 static void distort (Display *dpy, Window window)
134 {
135         static int x, y, xmove=0, ymove=0;
136         int i,j;
137
138         move_lens (&x, &y, &xmove, &ymove);
139
140         XCopyArea(dpy, orig_map, buffer_map, gc,
141                         x-radius/2 - xmove, y-radius/2 - ymove,
142                         2*radius + abs(xmove), 2*radius + abs(ymove),
143                         0,0);
144
145         /* it's possible to lower the number of loop iterations by a factor
146          * of 4, but since it's the XCopyArea's which eat resources, and
147          * I've only supplied one distortion routine (which is circular),
148          * here's a check-if-inside circle variation of this for loop.
149          * Using both optimizations turns the matrix rendering into one
150          * ugly mess... I'm counting on gcc optimization ;)
151          */
152
153         for(i = 0 ; i <= 2*radius ; i++) {
154                 for(j = 0 ; j <= 2*radius ; j++) {
155                         if (((radius-i)*(radius-i) + (j-radius)*(j-radius))
156                                 < radius*radius) {
157                         XCopyArea (dpy, orig_map, buffer_map, gc,
158                                         x+from[i][j][0],
159                                         y+from[i][j][1],
160                                         1, 1, i + xmove, j+ymove);
161                         }
162                 }
163         }
164
165         XCopyArea(dpy, buffer_map, window, gc, 0, 0,
166                         2*radius + abs(xmove), 2*radius + abs(ymove),
167                         x-radius/2 - xmove, y-radius/2 - ymove);
168 }
169
170 \f
171
172 char *progclass = "Distort";
173
174 char *defaults [] = {
175         "*dontClearRoot:                True",
176 #ifdef __sgi    /* really, HAVE_READ_DISPLAY_EXTENSION */
177         "*visualID:                     Best",
178 #endif
179
180         "*delay:                        10000",
181         "*radius:                       60",
182         "*speed:                        2",
183         0
184 };
185
186 XrmOptionDescRec options [] = {
187         { "-delay",     ".delay",       XrmoptionSepArg, 0 },
188         { "-radius",    ".radius",      XrmoptionSepArg, 0 },
189         { "-speed",     ".speed",       XrmoptionSepArg, 0 },
190         { 0, 0, 0, 0 }
191 };
192                 
193
194 void screenhack (Display *dpy, Window window) {
195         init_distort (dpy, window);
196         while (1) {
197                 distort (dpy, window);
198                 XSync (dpy, True);
199                 if (delay) usleep (delay);
200         }
201 }