1 /* -*- mode: C; tab-width: 4 -*-
2 * xscreensaver, Copyright (c) 1992, 1993, 1994, 1996, 1997, 1998, 2002, 2003
3 * Jamie Zawinski <jwz@jwz.org>
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation. No representations are made about the suitability of this
10 * software for any purpose. It is provided "as is" without express or
15 * by Jonas Munsin (jmunsin@iki.fi) and Jamie Zawinski <jwz@jwz.org>
17 * -check the allocations in init_round_lense again, maybe make it possible again
18 * to use swamp without pre-allocating/calculating (although that
19 * makes it slower) - -swamp is memory hungry
20 * -more distortion matrices (fortunately, I'm out of ideas :)
21 * Stuff that would be cool but probably too much of a resource hog:
22 * -some kind of interpolation to avoid jaggies
23 * -large speed values leaves the image distorted
24 * program idea borrowed from a screensaver on a non-*NIX OS,
26 * 28 Sep 1999 Jonas Munsin (jmunsin@iki.fi)
27 * Added about 10x faster algortim for 8, 16 and 32 bpp (modifies pixels
28 * directly avoiding costly XPutPixle(XGetPixel()) calls, inspired by
29 * xwhirl made by horvai@clipper.ens.fr (Peter Horvai) and the XFree86
31 * This piece of code is really horrible, but it works, and at the moment
32 * I don't have time or inspiration to fix something that works (knock
34 * 08 Oct 1999 Jonas Munsin (jmunsin@iki.fi)
35 * Corrected several bugs causing references beyond allocated memory.
39 #include "screenhack.h"
40 #include <X11/Xutil.h>
43 #ifdef HAVE_XSHM_EXTENSION
45 static Bool use_shm = False;
46 static XShmSegmentInfo shm_info;
47 #endif /* HAVE_XSHM_EXTENSION */
55 static struct coo xy_coo[10];
57 static int delay, radius, speed, number, blackhole, vortex, magnify, reflect, slow;
58 static XWindowAttributes xgwa;
60 static Window g_window;
61 static Display *g_dpy;
62 static unsigned long black_pixel;
64 static XImage *orig_map, *buffer_map;
65 static unsigned long *buffer_map_cache;
68 static int ****from_array;
69 static int *fast_from = NULL;
70 static void (*effect) (int) = NULL;
71 static void move_lense(int);
72 static void swamp_thing(int);
73 static void new_rnd_coo(int);
74 static void init_round_lense(void);
75 static void (*draw) (int) = NULL;
76 static void reflect_draw(int);
77 static void plain_draw(int);
79 static void (*draw_routine)(XImage *, XImage *, int, int, int *) = NULL;
80 static void fast_draw_8(XImage *, XImage *, int, int, int *);
81 static void fast_draw_16(XImage *, XImage *, int, int, int *);
82 static void fast_draw_32(XImage *, XImage *, int, int, int *);
83 static void generic_draw(XImage *, XImage *, int, int, int *);
84 static int bpp_size = 0;
87 static void init_distort(Display *dpy, Window window)
96 delay = get_integer_resource("delay", "Integer");
97 radius = get_integer_resource("radius", "Integer");
98 speed = get_integer_resource("speed", "Integer");
99 number = get_integer_resource("number", "Integer");
101 #ifdef HAVE_XSHM_EXTENSION
102 use_shm = get_boolean_resource("useSHM", "Boolean");
103 #endif /* HAVE_XSHM_EXTENSION */
105 blackhole = get_boolean_resource("blackhole", "Boolean");
106 vortex = get_boolean_resource("vortex", "Boolean");
107 magnify = get_boolean_resource("magnify", "Boolean");
108 reflect = get_boolean_resource("reflect", "Boolean");
109 slow = get_boolean_resource("slow", "Boolean");
111 if (get_boolean_resource("swamp", "Boolean"))
112 effect = &swamp_thing;
113 if (get_boolean_resource("bounce", "Boolean"))
114 effect = &move_lense;
116 XGetWindowAttributes (dpy, window, &xgwa);
118 if (effect == NULL && radius == 0 && speed == 0 && number == 0
119 && !blackhole && !vortex && !magnify && !reflect) {
120 /* if no cmdline options are given, randomly choose one of:
121 * -radius 50 -number 4 -speed 1 -bounce
122 * -radius 50 -number 4 -speed 1 -blackhole
123 * -radius 50 -number 4 -speed 1 -vortex
124 * -radius 50 -number 4 -speed 1 -vortex -magnify
125 * -radius 50 -number 4 -speed 1 -vortex -magnify -blackhole
126 * -radius 100 -number 1 -speed 2 -bounce
127 * -radius 100 -number 1 -speed 2 -blackhole
128 * -radius 100 -number 1 -speed 2 -vortex
129 * -radius 100 -number 1 -speed 2 -vortex -magnify
130 * -radius 100 -number 1 -speed 2 -vortex -magnify -blackhole
131 * -radius 80 -number 1 -speed 2 -reflect
132 * -radius 50 -number 3 -speed 2 -reflect
134 * -radius 50 -number 4 -speed 2 -swamp
135 * -radius 50 -number 4 -speed 2 -swamp -blackhole
136 * -radius 50 -number 4 -speed 2 -swamp -vortex
137 * -radius 50 -number 4 -speed 2 -swamp -vortex -magnify
138 * -radius 50 -number 4 -speed 2 -swamp -vortex -magnify -blackhole
141 i = (random() % 12 /* 17 */);
147 radius=50;number=4;speed=1;
148 effect=&move_lense;break;
150 radius=50;number=4;speed=1;blackhole=1;
151 effect=&move_lense;break;
153 radius=50;number=4;speed=1;vortex=1;
154 effect=&move_lense;break;
156 radius=50;number=4;speed=1;vortex=1;magnify=1;
157 effect=&move_lense;break;
159 radius=50;number=4;speed=1;vortex=1;magnify=1;blackhole=1;
160 effect=&move_lense;break;
162 radius=100;number=1;speed=2;
163 effect=&move_lense;break;
165 radius=100;number=1;speed=2;blackhole=1;
166 effect=&move_lense;break;
168 radius=100;number=1;speed=2;vortex=1;
169 effect=&move_lense;break;
171 radius=100;number=1;speed=2;vortex=1;magnify=1;
172 effect=&move_lense;break;
174 radius=100;number=1;speed=2;vortex=1;magnify=1;blackhole=1;
175 effect=&move_lense;break;
178 radius=80;number=1;speed=2;reflect=1;
179 draw = &reflect_draw;effect = &move_lense;break;
181 radius=50;number=4;speed=2;reflect=1;
182 draw = &reflect_draw;effect = &move_lense;break;
184 #if 0 /* jwz: not these */
186 radius=50;number=4;speed=2;
187 effect=&swamp_thing;break;
189 radius=50;number=4;speed=2;blackhole=1;
190 effect=&swamp_thing;break;
192 radius=50;number=4;speed=2;vortex=1;
193 effect=&swamp_thing;break;
195 radius=50;number=4;speed=2;vortex=1;magnify=1;
196 effect=&swamp_thing;break;
198 radius=50;number=4;speed=2;vortex=1;magnify=1;blackhole=1;
199 effect=&swamp_thing;break;
206 /* but if the window is small, reduce default radius */
207 if (xgwa.width < radius * 8)
208 radius = xgwa.width/8;
211 /* never allow the radius to be too close to the min window dimension
213 if (radius >= xgwa.width * 0.45) radius = xgwa.width * 0.45;
214 if (radius >= xgwa.height * 0.45) radius = xgwa.height * 0.45;
217 /* -swamp mode consumes vast amounts of memory, proportional to radius --
218 so throttle radius to a small-ish value (60 => ~30MB.)
220 if (effect == &swamp_thing && radius > 60)
234 effect = &move_lense;
236 draw = &reflect_draw;
237 effect = &move_lense;
242 black_pixel = BlackPixelOfScreen( xgwa.screen );
244 gcv.function = GXcopy;
245 gcv.subwindow_mode = IncludeInferiors;
246 gcflags = GCForeground |GCFunction;
247 if (use_subwindow_mode_p(xgwa.screen, window)) /* see grabscreen.c */
248 gcflags |= GCSubwindowMode;
249 gc = XCreateGC (dpy, window, gcflags, &gcv);
251 load_random_image (xgwa.screen, window, window, NULL);
254 orig_map = XGetImage(dpy, window, 0, 0, xgwa.width, xgwa.height,
256 buffer_map_cache = malloc(sizeof(unsigned long)*(2*radius+speed+2)*(2*radius+speed+2));
258 if (buffer_map_cache == NULL) {
263 # ifdef HAVE_XSHM_EXTENSION
267 buffer_map = create_xshm_image(dpy, xgwa.visual, orig_map->depth,
268 ZPixmap, 0, &shm_info,
269 2*radius + speed + 2,
270 2*radius + speed + 2);
274 # endif /* HAVE_XSHM_EXTENSION */
278 buffer_map = XCreateImage(dpy, xgwa.visual,
279 orig_map->depth, ZPixmap, 0, 0,
280 2*radius + speed + 2, 2*radius + speed + 2,
282 buffer_map->data = (char *)
283 calloc(buffer_map->height, buffer_map->bytes_per_line);
286 if ((buffer_map->byte_order == orig_map->byte_order)
287 && (buffer_map->depth == orig_map->depth)
288 && (buffer_map->format == ZPixmap)
289 && (orig_map->format == ZPixmap)
291 switch (orig_map->bits_per_pixel) {
293 draw_routine = &fast_draw_32;
294 bpp_size = sizeof(CARD32);
297 draw_routine = &fast_draw_16;
298 bpp_size = sizeof(CARD16);
301 draw_routine = &fast_draw_8;
302 bpp_size = sizeof(CARD8);
305 draw_routine = &generic_draw;
309 draw_routine = &generic_draw;
313 for (i = 0; i < number; i++) {
316 xy_coo[i].r = (i*radius)/(number-1); /* "randomize" initial */
319 xy_coo[i].r_change = speed + (i%2)*2*(-speed); /* values a bit */
320 xy_coo[i].xmove = speed + (i%2)*2*(-speed);
321 xy_coo[i].ymove = speed + (i%2)*2*(-speed);
326 /* example: initializes a "see-trough" matrix */
327 /* static void make_null_lense(void)
330 for (i = 0; i < 2*radius+speed+2; i++) {
331 for (j = 0 ; j < 2*radius+speed+2 ; j++) {
338 static void convert(void) {
341 fast_from = calloc(1, sizeof(int)*((buffer_map->bytes_per_line/bpp_size)*(2*radius+speed+2) + 2*radius+speed+2));
342 if (fast_from == NULL) {
347 for (i = 0; i < 2*radius+speed+2; i++) {
348 for (j = 0; j < 2*radius+speed+2; j++) {
349 *(p + i + j*buffer_map->bytes_per_line/bpp_size)
350 = from[i][j][0] + xgwa.width*from[i][j][1];
351 if (*(p + i + j*buffer_map->bytes_per_line/bpp_size) < 0
352 || *(p + i + j*buffer_map->bytes_per_line/bpp_size) >= orig_map->height*orig_map->width) {
353 *(p + i + j*buffer_map->bytes_per_line/bpp_size) = 0;
359 /* makes a lense with the Radius=loop and centred in
360 * the point (radius, radius)
362 static void make_round_lense(int radius, int loop)
366 for (i = 0; i < 2*radius+speed+2; i++) {
367 for(j = 0; j < ((0 == bpp_size) ? (2*radius+speed+2) : (buffer_map->bytes_per_line/bpp_size)); j++) {
369 r = sqrt ((i-radius)*(i-radius)+(j-radius)*(j-radius));
377 if (vortex) { /* vortex-twist effect */
379 /* this one-line formula for getting a nice rotation angle is borrowed
380 * (with permission) from the whirl plugin for gimp,
381 * Copyright (C) 1996 Federico Mena Quintero
383 /* 5 is just a constant used because it looks good :) */
384 angle = 5*(1-d)*(1-d);
386 /* Avoid atan2: DOMAIN error message */
387 if ((radius-j) == 0.0 && (radius-i) == 0.0) {
388 from[i][j][0] = radius + cos(angle)*r;
389 from[i][j][1] = radius + sin(angle)*r;
391 from[i][j][0] = radius +
392 cos(angle - atan2(radius-j, -(radius-i)))*r;
393 from[i][j][1] = radius +
394 sin(angle - atan2(radius-j, -(radius-i)))*r;
398 if (blackhole && r != 0) /* blackhole effect */
400 from[i][j][0] = radius + (from[i][j][0]-radius)*r;
401 from[i][j][1] = radius + (from[i][j][1]-radius)*r;
403 } else { /* default is to magnify */
406 /* raising r to different power here gives different amounts of
407 * distortion, a negative value sucks everything into a black hole
410 if (blackhole && r != 0) /* blackhole effect */
412 /* bubble effect (and blackhole) */
413 from[i][j][0] = radius + (i-radius)*r;
414 from[i][j][1] = radius + (j-radius)*r;
416 } else { /* not inside loop */
423 /* this is really just a quick hack to keep both the compability mode with all depths and still
424 * allow the custom optimized draw routines with the minimum amount of work */
431 # define EXIT_FAILURE -1
434 static void allocate_lense(void)
437 int s = ((0 != bpp_size) ? (buffer_map->bytes_per_line/bpp_size) : (2*radius+speed+2));
438 /* maybe this should be redone so that from[][][] is in one block;
439 * then pointers could be used instead of arrays in some places (and
440 * maybe give a speedup - maybe also consume less memory)
442 from = (int ***)malloc(s*sizeof(int **));
447 for (i = 0; i < s; i++) {
448 from[i] = (int **)malloc((2*radius+speed+2) * sizeof(int *));
449 if (from[i] == NULL) {
453 for (j = 0; j < s; j++) {
454 from[i][j] = (int *)malloc(2 * sizeof(int));
455 if (from[i][j] == NULL) {
463 /* from_array in an array containing precalculated from matrices,
464 * this is a double faced mem vs speed trade, it's faster, but eats
465 * _a lot_ of mem for large radius (is there a bug here? I can't see it)
467 static void init_round_lense(void)
471 if (effect == &swamp_thing) {
472 from_array = (int ****)malloc((radius+1)*sizeof(int ***));
473 for (k=0; k <= radius; k++) {
475 make_round_lense(radius, k);
476 from_array[k] = from;
478 } else { /* just allocate one from[][][] */
480 make_round_lense(radius,radius);
484 /* If fast_draw_8, fast_draw_16 or fast_draw_32 are to be used, the following properties
485 * of the src and dest XImages must hold (otherwise the generic, slooow, method provided
486 * by X is to be used):
487 * src->byte_order == dest->byte_order
488 * src->format == ZPixmap && dest->format == ZPixmap
489 * src->depth == dest->depth == the depth the function in question asumes
490 * x and y is the coordinates in src from where to cut out the image from,
491 * distort_matrix is a precalculated array of how to distort the matrix
494 static void fast_draw_8(XImage *src, XImage *dest, int x, int y, int *distort_matrix) {
495 CARD8 *u = (CARD8 *)dest->data;
496 CARD8 *t = (CARD8 *)src->data + x + y*src->bytes_per_line/sizeof(CARD8);
498 while (u < (CARD8 *)(dest->data + sizeof(CARD8)*dest->height
499 *dest->bytes_per_line/sizeof(CARD8))) {
500 *u++ = t[*distort_matrix++];
504 static void fast_draw_16(XImage *src, XImage *dest, int x, int y, int *distort_matrix) {
505 CARD16 *u = (CARD16 *)dest->data;
506 CARD16 *t = (CARD16 *)src->data + x + y*src->bytes_per_line/sizeof(CARD16);
508 while (u < (CARD16 *)(dest->data + sizeof(CARD16)*dest->height
509 *dest->bytes_per_line/sizeof(CARD16))) {
510 *u++ = t[*distort_matrix++];
514 static void fast_draw_32(XImage *src, XImage *dest, int x, int y, int *distort_matrix) {
515 CARD32 *u = (CARD32 *)dest->data;
516 CARD32 *t = (CARD32 *)src->data + x + y*src->bytes_per_line/sizeof(CARD32);
518 while (u < (CARD32 *)(dest->data + sizeof(CARD32)*dest->height
519 *dest->bytes_per_line/sizeof(CARD32))) {
520 *u++ = t[*distort_matrix++];
524 static void generic_draw(XImage *src, XImage *dest, int x, int y, int *distort_matrix) {
526 for (i = 0; i < dest->width; i++)
527 for (j = 0; j < dest->height; j++)
528 if (from[i][j][0] + x >= 0 &&
529 from[i][j][0] + x < src->width &&
530 from[i][j][1] + y >= 0 &&
531 from[i][j][1] + y < src->height)
532 XPutPixel(dest, i, j,
538 /* generate an XImage of from[][][] and draw it on the screen */
539 static void plain_draw(int k)
541 if (xy_coo[k].x+2*radius+speed+2 > orig_map->width ||
542 xy_coo[k].y+2*radius+speed+2 > orig_map->height)
545 draw_routine(orig_map, buffer_map, xy_coo[k].x, xy_coo[k].y, fast_from);
547 # ifdef HAVE_XSHM_EXTENSION
549 XShmPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y,
550 2*radius+speed+2, 2*radius+speed+2, False);
555 XPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y,
556 2*radius+speed+2, 2*radius+speed+2);
561 /* generate an XImage from the reflect algoritm submitted by
562 * Randy Zack <randy@acucorp.com>
563 * draw really got too big and ugly so I split it up
564 * it should be possible to use the from[][] to speed it up
565 * (once I figure out the algorithm used :)
567 static void reflect_draw(int k)
571 int ly, lysq, lx, ny, dist, rsq = radius * radius;
574 if (xy_coo[k].ymove > 0)
576 if (xy_coo[k].xmove > 0)
579 for(i = 0 ; i < 2*radius+speed+2; i++) {
582 ny = xy_coo[k].y + i;
583 if (ny >= orig_map->height) ny = orig_map->height-1;
584 for(j = 0 ; j < 2*radius+speed+2 ; j++) {
586 dist = lx * lx + lysq;
588 ly < -radius || ly > radius ||
589 lx < -radius || lx > radius)
590 XPutPixel( buffer_map, j, i,
591 XGetPixel( orig_map, xy_coo[k].x + j, ny ));
593 XPutPixel( buffer_map, j, i, black_pixel );
595 int x = xy_coo[k].x + cx + (lx * rsq / dist);
596 int y = xy_coo[k].y + cy + (ly * rsq / dist);
597 if (x < 0 || x >= xgwa.width ||
598 y < 0 || y >= xgwa.height)
599 XPutPixel( buffer_map, j, i, black_pixel );
601 XPutPixel( buffer_map, j, i,
602 XGetPixel( orig_map, x, y ));
607 XPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y,
608 2*radius+speed+2, 2*radius+speed+2);
611 /* create a new, random coordinate, that won't interfer with any other
612 * coordinates, as the drawing routines would be significantly slowed
613 * down if they were to handle serveral layers of distortions
615 static void new_rnd_coo(int k)
619 xy_coo[k].x = (random() % (xgwa.width-2*radius));
620 xy_coo[k].y = (random() % (xgwa.height-2*radius));
622 for (i = 0; i < number; i++) {
624 if ((abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius+speed+2)
625 && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius+speed+2)) {
626 xy_coo[k].x = (random() % (xgwa.width-2*radius));
627 xy_coo[k].y = (random() % (xgwa.height-2*radius));
634 /* move lens and handle bounces with walls and other lenses */
635 static void move_lense(int k)
639 if (xy_coo[k].x + 2*radius + speed + 2 >= xgwa.width)
640 xy_coo[k].xmove = -abs(xy_coo[k].xmove);
641 if (xy_coo[k].x <= speed)
642 xy_coo[k].xmove = abs(xy_coo[k].xmove);
643 if (xy_coo[k].y + 2*radius + speed + 2 >= xgwa.height)
644 xy_coo[k].ymove = -abs(xy_coo[k].ymove);
645 if (xy_coo[k].y <= speed)
646 xy_coo[k].ymove = abs(xy_coo[k].ymove);
648 xy_coo[k].x = xy_coo[k].x + xy_coo[k].xmove;
649 xy_coo[k].y = xy_coo[k].y + xy_coo[k].ymove;
651 /* bounce against othe lenses */
652 for (i = 0; i < number; i++) {
655 /* This commented test is for rectangular lenses (not currently used) and
656 * the one used is for circular ones
657 && (abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius)
658 && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius)) { */
660 && ((xy_coo[k].x - xy_coo[i].x)*(xy_coo[k].x - xy_coo[i].x)
661 + (xy_coo[k].y - xy_coo[i].y)*(xy_coo[k].y - xy_coo[i].y)
662 <= 2*radius*2*radius)) {
667 xy_coo[k].xmove = xy_coo[i].xmove;
668 xy_coo[k].ymove = xy_coo[i].ymove;
676 /* make xy_coo[k] grow/shrink */
677 static void swamp_thing(int k)
679 if (xy_coo[k].r >= radius)
680 xy_coo[k].r_change = -abs(xy_coo[k].r_change);
682 if (xy_coo[k].r <= 0) {
683 from = from_array[0];
685 xy_coo[k].r_change = abs(xy_coo[k].r_change);
687 xy_coo[k].r=xy_coo[k].r_change;
691 xy_coo[k].r = xy_coo[k].r + xy_coo[k].r_change;
693 if (xy_coo[k].r >= radius)
694 xy_coo[k].r = radius;
695 if (xy_coo[k].r <= 0)
698 from = from_array[xy_coo[k].r];
704 char *progclass = "Distort";
706 char *defaults [] = {
707 "*dontClearRoot: True",
708 #ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
722 #ifdef HAVE_XSHM_EXTENSION
723 "*useSHM: False", /* xshm turns out not to help. */
724 #endif /* HAVE_XSHM_EXTENSION */
728 XrmOptionDescRec options [] = {
729 { "-delay", ".delay", XrmoptionSepArg, 0 },
730 { "-radius", ".radius", XrmoptionSepArg, 0 },
731 { "-speed", ".speed", XrmoptionSepArg, 0 },
732 { "-number", ".number", XrmoptionSepArg, 0 },
733 { "-swamp", ".swamp", XrmoptionNoArg, "True" },
734 { "-bounce", ".bounce", XrmoptionNoArg, "True" },
735 { "-reflect", ".reflect", XrmoptionNoArg, "True" },
736 { "-vortex", ".vortex", XrmoptionNoArg, "True" },
737 { "-magnify", ".magnify", XrmoptionNoArg, "True" },
738 { "-blackhole", ".blackhole", XrmoptionNoArg, "True" },
739 { "-slow", ".slow", XrmoptionNoArg, "True" },
740 #ifdef HAVE_XSHM_EXTENSION
741 { "-shm", ".useSHM", XrmoptionNoArg, "True" },
742 { "-no-shm", ".useSHM", XrmoptionNoArg, "False" },
743 #endif /* HAVE_XSHM_EXTENSION */
748 void screenhack(Display *dpy, Window window)
752 init_distort (dpy, window);
754 for (k = 0; k < number; k++) {
760 screenhack_handle_events (dpy);
761 if (delay) usleep(delay);