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