From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / distort.c
1 /* -*- mode: C; tab-width: 4 -*-
2  * xscreensaver, Copyright (c) 1992-2014 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  * 09 Oct 2016 Dave Odell (dmo2118@gmail.com)
36  *  Updated for new xshm.c.
37  */
38
39 #include <math.h>
40 #include <time.h>
41 #include "screenhack.h"
42 /*#include <X11/Xmd.h>*/
43 # include "xshm.h"
44
45 #define CARD32 unsigned int
46 #define CARD16 unsigned short
47 #define CARD8  unsigned char
48
49
50 struct coo {
51         int x;
52         int y;
53         int r, r_change;
54         int xmove, ymove;
55 };
56
57 struct state {
58   Display *dpy;
59   Window window;
60
61   struct coo xy_coo[10];
62
63   int delay, radius, speed, number, blackhole, vortex, magnify, reflect, slow;
64   int duration;
65   time_t start_time;
66
67   XWindowAttributes xgwa;
68   GC gc;
69   unsigned long black_pixel;
70
71   XImage *orig_map, *buffer_map;
72   unsigned long *buffer_map_cache;
73
74   int ***from;
75   int ****from_array;
76   int *fast_from;
77
78   int bpp_size;
79
80   XShmSegmentInfo shm_info;
81
82   void (*effect) (struct state *, int);
83   void (*draw) (struct state *, int);
84   void (*draw_routine) (struct state *st, XImage *, XImage *, int, int, int *);
85
86   async_load_state *img_loader;
87   Pixmap pm;
88 };
89
90
91 static void move_lense(struct state *, int);
92 static void swamp_thing(struct state *, int);
93 static void new_rnd_coo(struct state *, int);
94 static void init_round_lense(struct state *st);
95 static void reflect_draw(struct state *, int);
96 static void plain_draw(struct state *, int);
97
98 static void fast_draw_8 (struct state *st, XImage *, XImage *, int, int, int *);
99 static void fast_draw_16(struct state *st, XImage *, XImage *, int, int, int *);
100 static void fast_draw_32(struct state *st, XImage *, XImage *, int, int, int *);
101 static void generic_draw(struct state *st, XImage *, XImage *, int, int, int *);
102
103
104 static void distort_finish_loading (struct state *);
105
106 static void
107 distort_reset (struct state *st)
108 {
109     char *s;
110     int i;
111
112     st->start_time = 0;
113
114         XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
115
116         st->delay = get_integer_resource(st->dpy, "delay", "Integer");
117     st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
118         st->radius = get_integer_resource(st->dpy, "radius", "Integer");
119         st->speed = get_integer_resource(st->dpy, "speed", "Integer");
120         st->number = get_integer_resource(st->dpy, "number", "Integer");
121
122         st->blackhole = get_boolean_resource(st->dpy, "blackhole", "Boolean");
123         st->vortex = get_boolean_resource(st->dpy, "vortex", "Boolean");
124         st->magnify = get_boolean_resource(st->dpy, "magnify", "Boolean");
125         st->reflect = get_boolean_resource(st->dpy, "reflect", "Boolean");
126         st->slow = get_boolean_resource(st->dpy, "slow", "Boolean");
127         
128     if (st->delay < 0) st->delay = 0;
129     if (st->duration < 1) st->duration = 1;
130
131     st->effect = NULL;
132     s = get_string_resource(st->dpy, "effect", "String");
133         if (s && !strcasecmp(s,"swamp"))
134       st->effect = &swamp_thing;
135     else if (s && !strcasecmp(s,"bounce"))
136       st->effect = &move_lense;
137     else if (s && !strcasecmp(s,"none"))
138       ;
139     else if (s && *s)
140       fprintf(stderr,"%s: bogus effect: %s\n", progname, s);
141
142         if (st->effect == NULL && st->radius == 0 && st->speed == 0 && st->number == 0
143                 && !st->blackhole && !st->vortex && !st->magnify && !st->reflect) {
144 /* if no cmdline options are given, randomly choose one of:
145  * -radius 125 -number 4 -speed 1 -bounce
146  * -radius 125 -number 4 -speed 1 -blackhole
147  * -radius 125 -number 4 -speed 1 -vortex
148  * -radius 125 -number 4 -speed 1 -vortex -magnify
149  * -radius 125 -number 4 -speed 1 -vortex -magnify -blackhole
150  * -radius 250 -number 1 -speed 2 -bounce
151  * -radius 250 -number 1 -speed 2 -blackhole
152  * -radius 250 -number 1 -speed 2 -vortex
153  * -radius 250 -number 1 -speed 2 -vortex -magnify
154  * -radius 250 -number 1 -speed 2 -vortex -magnify -blackhole
155  * -radius 80 -number 1 -speed 2 -reflect
156  * -radius 125 -number 3 -speed 2 -reflect
157  * jwz: not these
158  *   -radius 125 -number 4 -speed 2 -swamp
159  *   -radius 125 -number 4 -speed 2 -swamp -blackhole
160  *   -radius 125 -number 4 -speed 2 -swamp -vortex
161  *   -radius 125 -number 4 -speed 2 -swamp -vortex -magnify
162  *   -radius 125 -number 4 -speed 2 -swamp -vortex -magnify -blackhole
163  */
164                 
165                 i = (random() % 12 /* 17 */);
166
167                 st->draw = &plain_draw;
168
169                 switch (i) {
170                         case 0:
171                                 st->radius=125;st->number=4;st->speed=1;
172                                 st->effect=&move_lense;break;
173                         case 1:
174                                 st->radius=125;st->number=4;st->speed=1;st->blackhole=1;
175                                 st->effect=&move_lense;break;
176                         case 2:
177                                 st->radius=125;st->number=4;st->speed=1;st->vortex=1;
178                                 st->effect=&move_lense;break;
179                         case 3:
180                                 st->radius=125;st->number=4;st->speed=1;st->vortex=1;st->magnify=1;
181                                 st->effect=&move_lense;break;
182                         case 4:
183                                 st->radius=125;st->number=4;st->speed=1;st->vortex=1;st->magnify=1;st->blackhole=1;
184                                 st->effect=&move_lense;break;
185                         case 5:
186                                 st->radius=250;st->number=1;st->speed=2;
187                                 st->effect=&move_lense;break;
188                         case 6:
189                                 st->radius=250;st->number=1;st->speed=2;st->blackhole=1;
190                                 st->effect=&move_lense;break;
191                         case 7:
192                                 st->radius=250;st->number=1;st->speed=2;st->vortex=1;
193                                 st->effect=&move_lense;break;
194                         case 8:
195                                 st->radius=250;st->number=1;st->speed=2;st->vortex=1;st->magnify=1;
196                                 st->effect=&move_lense;break;
197                         case 9:
198                                 st->radius=250;st->number=1;st->speed=2;st->vortex=1;st->magnify=1;st->blackhole=1;
199                                 st->effect=&move_lense;break;
200
201                         case 10:
202                                 st->radius=80;st->number=1;st->speed=2;st->reflect=1;
203                                 st->draw = &reflect_draw;st->effect = &move_lense;break;
204                         case 11:
205                                 st->radius=125;st->number=4;st->speed=2;st->reflect=1;
206                                 st->draw = &reflect_draw;st->effect = &move_lense;break;
207
208 #if 0 /* jwz: not these */
209                         case 12:
210                                 st->radius=125;st->number=4;st->speed=2;
211                                 effect=&swamp_thing;break;
212                         case 13:
213                                 st->radius=125;st->number=4;st->speed=2;st->blackhole=1;
214                                 effect=&swamp_thing;break;
215                         case 14:
216                                 st->radius=125;st->number=4;st->speed=2;st->vortex=1;
217                                 effect=&swamp_thing;break;
218                         case 15:
219                                 st->radius=125;st->number=4;st->speed=2;st->vortex=1;st->magnify=1;
220                                 effect=&swamp_thing;break;
221                         case 16:
222                                 st->radius=125;st->number=4;st->speed=2;st->vortex=1;st->magnify=1;st->blackhole=1;
223                                 effect=&swamp_thing;break;
224 #endif
225
226             default:
227                 abort(); break;
228                 }
229         }
230
231     /* never allow the radius to be too close to the min window dimension
232      */
233     if (st->radius > st->xgwa.width  * 0.3) st->radius = st->xgwa.width  * 0.3;
234     if (st->radius > st->xgwa.height * 0.3) st->radius = st->xgwa.height * 0.3;
235
236
237     /* -swamp mode consumes vast amounts of memory, proportional to radius --
238        so throttle radius to a small-ish value (60 => ~30MB.)
239      */
240     if (st->effect == &swamp_thing && st->radius > 60)
241       st->radius = 60;
242
243         if (st->delay < 0)
244                 st->delay = 0;
245         if (st->radius <= 0)
246                 st->radius = 60;
247         if (st->speed <= 0) 
248                 st->speed = 2;
249         if (st->number <= 0)
250                 st->number=1;
251         if (st->number >= 10)
252                 st->number=1;
253         if (st->effect == NULL)
254                 st->effect = &move_lense;
255         if (st->reflect) {
256                 st->draw = &reflect_draw;
257                 st->effect = &move_lense;
258         }
259         if (st->draw == NULL)
260                 st->draw = &plain_draw;
261 }
262
263 static void *
264 distort_init (Display *dpy, Window window)
265 {
266   struct state *st = (struct state *) calloc (1, sizeof(*st));
267         XGCValues gcv;
268         long gcflags;
269
270     st->dpy = dpy;
271     st->window = window;
272
273     distort_reset (st);
274
275         st->black_pixel = BlackPixelOfScreen( st->xgwa.screen );
276
277         gcv.function = GXcopy;
278         gcv.subwindow_mode = IncludeInferiors;
279         gcflags = GCFunction;
280         if (use_subwindow_mode_p(st->xgwa.screen, st->window)) /* see grabscreen.c */
281                 gcflags |= GCSubwindowMode;
282         st->gc = XCreateGC (st->dpy, st->window, gcflags, &gcv);
283
284     /* On MacOS X11, XGetImage on a Window often gets an inexplicable BadMatch,
285        possibly due to the window manager having occluded something?  It seems
286        nondeterministic. Loading the image into a pixmap instead fixes it. */
287     if (st->pm) XFreePixmap (st->dpy, st->pm);
288     st->pm = XCreatePixmap (st->dpy, st->window,
289                             st->xgwa.width, st->xgwa.height, st->xgwa.depth);
290
291     st->img_loader = load_image_async_simple (0, st->xgwa.screen, st->window,
292                                               st->pm, 0, 0);
293     st->start_time = time ((time_t *) 0);
294     return st;
295 }
296
297 static void
298 distort_finish_loading (struct state *st)
299 {
300     int i;
301
302     st->start_time = time ((time_t *) 0);
303
304     if (! st->pm) abort();
305     XClearWindow (st->dpy, st->window);
306     XCopyArea (st->dpy, st->pm, st->window, st->gc, 
307                0, 0, st->xgwa.width, st->xgwa.height, 0, 0);
308         st->orig_map = XGetImage(st->dpy, st->pm, 0, 0,
309                              st->xgwa.width, st->xgwa.height,
310                              ~0L, ZPixmap);
311         st->buffer_map_cache = malloc(sizeof(unsigned long)*(2*st->radius+st->speed+2)*(2*st->radius+st->speed+2));
312
313         if (st->buffer_map_cache == NULL) {
314                 perror("distort");
315                 exit(EXIT_FAILURE);
316         }
317
318         st->buffer_map = create_xshm_image(st->dpy, st->xgwa.visual, st->orig_map->depth,
319                                            ZPixmap, &st->shm_info,
320                                            2*st->radius + st->speed + 2,
321                                            2*st->radius + st->speed + 2);
322
323         if ((st->buffer_map->byte_order == st->orig_map->byte_order)
324                         && (st->buffer_map->depth == st->orig_map->depth)
325                         && (st->buffer_map->format == ZPixmap)
326                         && (st->orig_map->format == ZPixmap)
327                         && !st->slow) {
328                 switch (st->orig_map->bits_per_pixel) {
329                         case 32:
330                                 st->draw_routine = &fast_draw_32;
331                                 st->bpp_size = sizeof(CARD32);
332                                 break;
333                         case 16:
334                                 st->draw_routine = &fast_draw_16;
335                                 st->bpp_size = sizeof(CARD16);
336                                 break;
337                         case 8:
338                                 st->draw_routine = &fast_draw_8;
339                                 st->bpp_size = sizeof(CARD8);
340                                 break;
341                         default:
342                                 st->draw_routine = &generic_draw;
343                                 break;
344                 }
345         } else {
346                 st->draw_routine = &generic_draw;
347         }
348         init_round_lense(st);
349
350         for (i = 0; i < st->number; i++) {
351                 new_rnd_coo(st,i);
352                 if (st->number != 1)
353                         st->xy_coo[i].r = (i*st->radius)/(st->number-1); /* "randomize" initial */
354                 else
355                          st->xy_coo[i].r = 0;
356                 st->xy_coo[i].r_change = st->speed + (i%2)*2*(-st->speed);      /* values a bit */
357                 st->xy_coo[i].xmove = st->speed + (i%2)*2*(-st->speed);
358                 st->xy_coo[i].ymove = st->speed + (i%2)*2*(-st->speed);
359         }
360 }
361
362 /* example: initializes a "see-trough" matrix */
363 /* static void make_null_lense(struct state *st)
364 {
365         int i, j;
366         for (i = 0; i < 2*radius+speed+2; i++) {
367                 for (j = 0 ; j < 2*radius+speed+2 ; j++) {
368                         from[i][j][0]=i;
369                         from[i][j][1]=j;
370                 }
371         } 
372 }
373 */
374 static void convert(struct state *st) 
375 {
376         int *p;
377         int i, j;
378         st->fast_from = calloc(1, sizeof(int)*((st->buffer_map->bytes_per_line/st->bpp_size)*(2*st->radius+st->speed+2) + 2*st->radius+st->speed+2));
379         if (st->fast_from == NULL) {
380                 perror("distort");
381                 exit(EXIT_FAILURE);
382         }
383         p = st->fast_from;
384         for (i = 0; i < 2*st->radius+st->speed+2; i++) {
385                 for (j = 0; j < 2*st->radius+st->speed+2; j++) {
386                         *(p + i + j*st->buffer_map->bytes_per_line/st->bpp_size)
387                                 = st->from[i][j][0] + st->xgwa.width*st->from[i][j][1];
388                         if (*(p + i + j*st->buffer_map->bytes_per_line/st->bpp_size) < 0
389                                         || *(p + i + j*st->buffer_map->bytes_per_line/st->bpp_size) >= st->orig_map->height*st->orig_map->width) {
390                                 *(p + i + j*st->buffer_map->bytes_per_line/st->bpp_size) = 0;
391                         }
392                 }
393         }
394 }
395
396 /* makes a lense with the Radius=loop and centred in
397  * the point (radius, radius)
398  */
399 static void make_round_lense(struct state *st, int radius, int loop)
400 {
401         int i, j;
402
403         for (i = 0; i < 2*radius+st->speed+2; i++) {
404                 for(j = 0; j < ((0 == st->bpp_size) ? (2*radius+st->speed+2) : (st->buffer_map->bytes_per_line/st->bpp_size)); j++) {
405                         double r, d;
406                         r = sqrt ((i-radius)*(i-radius)+(j-radius)*(j-radius));
407                         if (loop == 0)
408                           d=0.0;
409                         else
410                           d=r/loop;
411
412                         if (r < loop-1) {
413
414                                 if (st->vortex) { /* vortex-twist effect */
415                                         double angle;
416                 /* this one-line formula for getting a nice rotation angle is borrowed
417                  * (with permission) from the whirl plugin for gimp,
418                  * Copyright (C) 1996 Federico Mena Quintero
419                  */
420                 /* 5 is just a constant used because it looks good :) */
421                                         angle = 5*(1-d)*(1-d);
422
423         /* Avoid atan2: DOMAIN error message */
424                                         if ((radius-j) == 0.0 && (radius-i) == 0.0) {
425                                                 st->from[i][j][0] = radius + cos(angle)*r;
426                                                 st->from[i][j][1] = radius + sin(angle)*r;
427                                         } else {
428                                                 st->from[i][j][0] = radius +
429                                                         cos(angle - atan2(radius-j, -(radius-i)))*r;
430                                                 st->from[i][j][1] = radius +
431                                                         sin(angle - atan2(radius-j, -(radius-i)))*r;
432                                         }
433                                         if (st->magnify) {
434                                                 r = sin(d*M_PI_2);
435                                                 if (st->blackhole && r != 0) /* blackhole effect */
436                                                         r = 1/r;
437                                                 st->from[i][j][0] = radius + (st->from[i][j][0]-radius)*r;
438                                                 st->from[i][j][1] = radius + (st->from[i][j][1]-radius)*r;
439                                         }
440                                 } else { /* default is to magnify */
441                                         r = sin(d*M_PI_2);
442                                 
443         /* raising r to different power here gives different amounts of
444          * distortion, a negative value sucks everything into a black hole
445          */
446                                 /*      r = r*r; */
447                                         if (st->blackhole && r != 0) /* blackhole effect */
448                                                 r = 1/r;
449                                                                         /* bubble effect (and blackhole) */
450                                         st->from[i][j][0] = radius + (i-radius)*r;
451                                         st->from[i][j][1] = radius + (j-radius)*r;
452                                 }
453                         } else { /* not inside loop */
454                                 st->from[i][j][0] = i;
455                                 st->from[i][j][1] = j;
456                         }
457                 }
458         }
459
460         /* this is really just a quick hack to keep both the compability mode with all depths and still
461          * allow the custom optimized draw routines with the minimum amount of work */
462         if (0 != st->bpp_size) {
463                 convert(st);
464         }
465 }
466
467 #ifndef EXIT_FAILURE
468 # define EXIT_FAILURE -1
469 #endif
470
471 static void allocate_lense(struct state *st)
472 {
473         int i, j;
474         int s = ((0 != st->bpp_size) ? (st->buffer_map->bytes_per_line/st->bpp_size) : (2*st->radius+st->speed+2));
475         /* maybe this should be redone so that from[][][] is in one block;
476          * then pointers could be used instead of arrays in some places (and
477          * maybe give a speedup - maybe also consume less memory)
478          */
479         st->from = (int ***)malloc(s*sizeof(int **));
480         if (st->from == NULL) {
481                 perror("distort");
482                 exit(EXIT_FAILURE);
483         }
484         for (i = 0; i < s; i++) {
485                 st->from[i] = (int **)malloc((2*st->radius+st->speed+2) * sizeof(int *));
486                 if (st->from[i] == NULL) {
487                         perror("distort");
488                         exit(EXIT_FAILURE);
489                 }
490                 for (j = 0; j < s; j++) {
491                         st->from[i][j] = (int *)malloc(2 * sizeof(int));
492                         if (st->from[i][j] == NULL) {
493                                 perror("distort");
494                                 exit(EXIT_FAILURE);
495                         }
496                 }
497         }
498 }
499
500 /* from_array in an array containing precalculated from matrices,
501  * this is a double faced mem vs speed trade, it's faster, but eats
502  * _a lot_ of mem for large radius (is there a bug here? I can't see it)
503  */
504 static void init_round_lense(struct state *st)
505 {
506         int k;
507
508         if (st->effect == &swamp_thing) {
509                 st->from_array = (int ****)malloc((st->radius+1)*sizeof(int ***));
510                 for (k=0; k <= st->radius; k++) {
511                         allocate_lense(st);
512                         make_round_lense(st, st->radius, k);
513                         st->from_array[k] = st->from;
514                 }
515         } else { /* just allocate one from[][][] */
516                 allocate_lense(st);
517                 make_round_lense(st, st->radius,st->radius);
518         }
519 }
520
521 /* If fast_draw_8, fast_draw_16 or fast_draw_32 are to be used, the following properties
522  * of the src and dest XImages must hold (otherwise the generic, slooow, method provided
523  * by X is to be used):
524  *      src->byte_order == dest->byte_order
525  *      src->format == ZPixmap && dest->format == ZPixmap
526  *      src->depth == dest->depth == the depth the function in question asumes
527  * x and y is the coordinates in src from where to cut out the image from,
528  * distort_matrix is a precalculated array of how to distort the matrix
529  */
530
531 static void fast_draw_8(struct state *st, XImage *src, XImage *dest, int x, int y, int *distort_matrix)
532 {
533         CARD8 *u = (CARD8 *)dest->data;
534         CARD8 *t = (CARD8 *)src->data + x + y*src->bytes_per_line/sizeof(CARD8);
535
536         while (u < (CARD8 *)(dest->data + sizeof(CARD8)*dest->height
537                                 *dest->bytes_per_line/sizeof(CARD8))) {
538                 *u++ = t[*distort_matrix++];
539         }
540 }
541
542 static void fast_draw_16(struct state *st, XImage *src, XImage *dest, int x, int y, int *distort_matrix)
543 {
544         CARD16 *u = (CARD16 *)dest->data;
545         CARD16 *t = (CARD16 *)src->data + x + y*src->bytes_per_line/sizeof(CARD16);
546
547         while (u < (CARD16 *)(dest->data + sizeof(CARD16)*dest->height
548                                 *dest->bytes_per_line/sizeof(CARD16))) {
549                 *u++ = t[*distort_matrix++];
550         }
551 }
552
553 static void fast_draw_32(struct state *st, XImage *src, XImage *dest, int x, int y, int *distort_matrix)
554 {
555         CARD32 *u = (CARD32 *)dest->data;
556         CARD32 *t = (CARD32 *)src->data + x + y*src->bytes_per_line/sizeof(CARD32);
557
558         while (u < (CARD32 *)(dest->data + sizeof(CARD32)*dest->height
559                                 *dest->bytes_per_line/sizeof(CARD32))) {
560                 *u++ = t[*distort_matrix++];
561         }
562 }
563
564 static void generic_draw(struct state *st, XImage *src, XImage *dest, int x, int y, int *distort_matrix)
565 {
566         int i, j;
567         for (i = 0; i < dest->width; i++)
568                 for (j = 0; j < dest->height; j++)
569                         if (st->from[i][j][0] + x >= 0 &&
570                                         st->from[i][j][0] + x < src->width &&
571                                         st->from[i][j][1] + y >= 0 &&
572                                         st->from[i][j][1] + y < src->height)
573                                 XPutPixel(dest, i, j,
574                                                 XGetPixel(src,
575                                                         st->from[i][j][0] + x,
576                                                         st->from[i][j][1] + y));
577 }
578
579 /* generate an XImage of from[][][] and draw it on the screen */
580 static void plain_draw(struct state *st, int k)
581 {
582         if (st->xy_coo[k].x+2*st->radius+st->speed+2 > st->orig_map->width ||
583                         st->xy_coo[k].y+2*st->radius+st->speed+2 > st->orig_map->height)
584                 return;
585
586         st->draw_routine(st, st->orig_map, st->buffer_map, st->xy_coo[k].x, st->xy_coo[k].y, st->fast_from);
587
588         put_xshm_image(st->dpy, st->window, st->gc, st->buffer_map, 0, 0, st->xy_coo[k].x, st->xy_coo[k].y,
589                        2*st->radius+st->speed+2, 2*st->radius+st->speed+2, &st->shm_info);
590 }
591
592
593 /* generate an XImage from the reflect algoritm submitted by
594  * Randy Zack <randy@acucorp.com>
595  * draw really got too big and ugly so I split it up
596  * it should be possible to use the from[][] to speed it up
597  * (once I figure out the algorithm used :)
598  */
599 static void reflect_draw(struct state *st, int k)
600 {
601         int i, j;
602         int     cx, cy;
603         int     ly, lysq, lx, ny, dist, rsq = st->radius * st->radius;
604
605         cx = cy = st->radius;
606         if (st->xy_coo[k].ymove > 0)
607                 cy += st->speed;
608         if (st->xy_coo[k].xmove > 0)
609                 cx += st->speed;
610
611         for(i = 0 ; i < 2*st->radius+st->speed+2; i++) {
612                 ly = i - cy;
613                 lysq = ly * ly;
614                 ny = st->xy_coo[k].y + i;
615                 if (ny >= st->orig_map->height) ny = st->orig_map->height-1;
616                 for(j = 0 ; j < 2*st->radius+st->speed+2 ; j++) {
617                         lx = j - cx;
618                         dist = lx * lx + lysq;
619                         if (dist > rsq ||
620                                 ly < -st->radius || ly > st->radius ||
621                                 lx < -st->radius || lx > st->radius)
622                                 XPutPixel( st->buffer_map, j, i,
623                                                    XGetPixel( st->orig_map, st->xy_coo[k].x + j, ny ));
624                         else if (dist == 0)
625                                 XPutPixel( st->buffer_map, j, i, st->black_pixel );
626                         else {
627                                 int     x = st->xy_coo[k].x + cx + (lx * rsq / dist);
628                                 int     y = st->xy_coo[k].y + cy + (ly * rsq / dist);
629                                 if (x < 0 || x >= st->xgwa.width ||
630                                         y < 0 || y >= st->xgwa.height)
631                                         XPutPixel( st->buffer_map, j, i, st->black_pixel );
632                                 else
633                                         XPutPixel( st->buffer_map, j, i,
634                                                            XGetPixel( st->orig_map, x, y ));
635                         }
636                 }
637         }
638
639         XPutImage(st->dpy, st->window, st->gc, st->buffer_map, 0, 0, st->xy_coo[k].x, st->xy_coo[k].y,
640                         2*st->radius+st->speed+2, 2*st->radius+st->speed+2);
641 }
642
643 /* create a new, random coordinate, that won't interfer with any other
644  * coordinates, as the drawing routines would be significantly slowed
645  * down if they were to handle serveral layers of distortions
646  */
647 static void new_rnd_coo(struct state *st, int k)
648 {
649         int i;
650     int loop = 0;
651
652         st->xy_coo[k].x = (random() % (st->xgwa.width-2*st->radius));
653         st->xy_coo[k].y = (random() % (st->xgwa.height-2*st->radius));
654         
655         for (i = 0; i < st->number; i++) {
656                 if (i != k) {
657                         if ((abs(st->xy_coo[k].x - st->xy_coo[i].x) <= 2*st->radius+st->speed+2)
658                          && (abs(st->xy_coo[k].y - st->xy_coo[i].y) <= 2*st->radius+st->speed+2)) {
659                                 st->xy_coo[k].x = (random() % (st->xgwa.width-2*st->radius));
660                                 st->xy_coo[k].y = (random() % (st->xgwa.height-2*st->radius));
661                                 i=-1; /* ugly */
662                         } 
663                 }
664         if (loop++ > 1000) return;  /* let's not get stuck */
665         }
666 }
667
668 /* move lens and handle bounces with walls and other lenses */
669 static void move_lense(struct state *st, int k)
670 {
671         int i;
672
673         if (st->xy_coo[k].x + 2*st->radius + st->speed + 2 >= st->xgwa.width)
674                 st->xy_coo[k].xmove = -abs(st->xy_coo[k].xmove);
675         if (st->xy_coo[k].x <= st->speed) 
676                 st->xy_coo[k].xmove = abs(st->xy_coo[k].xmove);
677         if (st->xy_coo[k].y + 2*st->radius + st->speed + 2 >= st->xgwa.height)
678                 st->xy_coo[k].ymove = -abs(st->xy_coo[k].ymove);
679         if (st->xy_coo[k].y <= st->speed)
680                 st->xy_coo[k].ymove = abs(st->xy_coo[k].ymove);
681
682         st->xy_coo[k].x = st->xy_coo[k].x + st->xy_coo[k].xmove;
683         st->xy_coo[k].y = st->xy_coo[k].y + st->xy_coo[k].ymove;
684
685         /* bounce against othe lenses */
686         for (i = 0; i < st->number; i++) {
687                 if ((i != k)
688                 
689 /* This commented test is for rectangular lenses (not currently used) and
690  * the one used is for circular ones
691                 && (abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius)
692                 && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius)) { */
693
694                 && ((st->xy_coo[k].x - st->xy_coo[i].x)*(st->xy_coo[k].x - st->xy_coo[i].x)
695                   + (st->xy_coo[k].y - st->xy_coo[i].y)*(st->xy_coo[k].y - st->xy_coo[i].y)
696                         <= 2*st->radius*2*st->radius)) {
697
698                         int x, y;
699                         x = st->xy_coo[k].xmove;
700                         y = st->xy_coo[k].ymove;
701                         st->xy_coo[k].xmove = st->xy_coo[i].xmove;
702                         st->xy_coo[k].ymove = st->xy_coo[i].ymove;
703                         st->xy_coo[i].xmove = x;
704                         st->xy_coo[i].ymove = y;
705                 }
706         }
707
708 }
709
710 /* make xy_coo[k] grow/shrink */
711 static void swamp_thing(struct state *st, int k)
712 {
713         if (st->xy_coo[k].r >= st->radius)
714                 st->xy_coo[k].r_change = -abs(st->xy_coo[k].r_change);
715         
716         if (st->xy_coo[k].r <= 0) {
717                 st->from = st->from_array[0];
718                 st->draw(st,k); 
719                 st->xy_coo[k].r_change = abs(st->xy_coo[k].r_change);
720                 new_rnd_coo(st,k);
721                 st->xy_coo[k].r=st->xy_coo[k].r_change;
722                 return;
723         }
724
725         st->xy_coo[k].r = st->xy_coo[k].r + st->xy_coo[k].r_change;
726
727         if (st->xy_coo[k].r >= st->radius)
728                 st->xy_coo[k].r = st->radius;
729         if (st->xy_coo[k].r <= 0)
730                 st->xy_coo[k].r=0;
731
732         st->from = st->from_array[st->xy_coo[k].r];
733 }
734
735
736 static unsigned long
737 distort_draw (Display *dpy, Window window, void *closure)
738 {
739   struct state *st = (struct state *) closure;
740   int k;
741
742   if (st->img_loader)   /* still loading */
743     {
744       st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
745       if (! st->img_loader) {  /* just finished */
746         distort_finish_loading (st);
747       }
748       return st->delay;
749     }
750
751   if (!st->img_loader &&
752       st->start_time + st->duration < time ((time_t *) 0)) {
753     if (st->pm) XFreePixmap (st->dpy, st->pm);
754     st->pm = XCreatePixmap (st->dpy, st->window,
755                             st->xgwa.width, st->xgwa.height, st->xgwa.depth);
756     st->img_loader = load_image_async_simple (0, st->xgwa.screen, st->window,
757                                               st->pm, 0, 0);
758     return st->delay;
759   }
760
761   for (k = 0; k < st->number; k++) {
762     st->effect(st,k);
763     st->draw(st,k);
764   }
765   return st->delay;
766 }
767
768 static void
769 distort_reshape (Display *dpy, Window window, void *closure, 
770                  unsigned int w, unsigned int h)
771 {
772   struct state *st = (struct state *) closure;
773   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
774   /* XClearWindow (dpy, window); */
775   /* Why doesn't this work? */
776   if (st->orig_map)  /* created in distort_finish_loading, might be early */
777     XPutImage (st->dpy, st->window, st->gc, st->orig_map,
778                0, 0, st->orig_map->width, st->orig_map->height, 0, 0);
779 }
780
781 static Bool
782 distort_event (Display *dpy, Window window, void *closure, XEvent *event)
783 {
784   struct state *st = (struct state *) closure;
785   if (screenhack_event_helper (dpy, window, event))
786     {
787       distort_reset(st);
788       return True;
789     }
790   return False;
791 }
792
793 static void
794 distort_free (Display *dpy, Window window, void *closure)
795 {
796   struct state *st = (struct state *) closure;
797   XFreeGC (st->dpy, st->gc);
798   if (st->pm) XFreePixmap (dpy, st->pm);
799   if (st->orig_map) XDestroyImage (st->orig_map);
800   if (st->buffer_map) destroy_xshm_image (st->dpy, st->buffer_map, &st->shm_info);
801   if (st->from) free (st->from);
802   if (st->fast_from) free (st->fast_from);
803   if (st->from_array) free (st->from_array);
804   free (st);
805 }
806
807
808 \f
809
810 static const char *distort_defaults [] = {
811         "*dontClearRoot:                True",
812         "*background:                   Black",
813     "*fpsSolid:                         true",
814 #ifdef __sgi    /* really, HAVE_READ_DISPLAY_EXTENSION */
815         "*visualID:                     Best",
816 #endif
817
818         "*delay:                        20000",
819     "*duration:                 120",
820         "*radius:                       0",
821         "*speed:                        0",
822         "*number:                       0",
823         "*slow:                         False",
824         "*vortex:                       False",
825         "*magnify:                      False",
826         "*reflect:                      False",
827         "*blackhole:            False",
828         "*effect:                   none",
829 #ifdef HAVE_XSHM_EXTENSION
830         "*useSHM:                       False",         /* xshm turns out not to help. */
831 #endif /* HAVE_XSHM_EXTENSION */
832 #ifdef HAVE_MOBILE
833   "*ignoreRotation:     True",
834   "*rotateImages:       True",
835 #endif
836         0
837 };
838
839 static XrmOptionDescRec distort_options [] = {
840   { "-delay",     ".delay",       XrmoptionSepArg, 0 },
841   { "-duration",  ".duration",    XrmoptionSepArg, 0 },
842   { "-radius",    ".radius",      XrmoptionSepArg, 0 },
843   { "-speed",     ".speed",       XrmoptionSepArg, 0 },
844   { "-number",    ".number",      XrmoptionSepArg, 0 },
845
846   { "-effect",    ".effect",      XrmoptionSepArg, 0 },
847   { "-swamp",     ".effect",      XrmoptionNoArg, "swamp"  },
848   { "-bounce",    ".effect",      XrmoptionNoArg, "bounce" },
849
850   { "-reflect",   ".reflect",     XrmoptionNoArg, "True" },
851   { "-vortex",    ".vortex",      XrmoptionNoArg, "True" },
852   { "-magnify",   ".magnify",     XrmoptionNoArg, "True" },
853   { "-blackhole", ".blackhole",   XrmoptionNoArg, "True" },
854   { "-slow",      ".slow",        XrmoptionNoArg, "True" },
855 #ifdef HAVE_XSHM_EXTENSION
856   { "-shm",       ".useSHM",      XrmoptionNoArg, "True" },
857   { "-no-shm",    ".useSHM",      XrmoptionNoArg, "False" },
858 #endif /* HAVE_XSHM_EXTENSION */
859   { 0, 0, 0, 0 }
860 };
861
862 XSCREENSAVER_MODULE ("Distort", distort)