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