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