X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fdistort.c;h=7eed6281c8b9c4d3ecef180c77956506ae080fd9;hb=c31d10b6605cd8dc1a7b61fef4256f06198767e5;hp=a26dfbb27aac09aa325a1fd0daaa6c01adc4b504;hpb=481b95e2617b69e6fd4444432747d7e1e0c3dc85;p=xscreensaver diff --git a/hacks/distort.c b/hacks/distort.c index a26dfbb2..7eed6281 100644 --- a/hacks/distort.c +++ b/hacks/distort.c @@ -1,5 +1,6 @@ -/* xscreensaver, Copyright (c) 1992, 1993, 1994, 1996, 1997, 1998 - * Jamie Zawinski +/* -*- mode: C; tab-width: 4 -*- + * xscreensaver, Copyright (c) 1992, 1993, 1994, 1996, 1997, 1998 + * Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -11,44 +12,186 @@ */ /* distort - * by Jonas Munsin (jmunsin@iki.fi) - * it's a bit of a resource hog at the moment + * by Jonas Munsin (jmunsin@iki.fi) and Jamie Zawinski * TODO: - * -optimize for speed - * -mutiple spheres/lenses (with bounces/layering) - * -different distortion matrices - * -randomize movement a bit + * -check the allocations in init_round_lense again, maybe make it possible again + * to use swamp without pre-allocating/calculating (although that + * makes it slower) - -swamp is memory hungry + * -more distortion matrices (fortunately, I'm out of ideas :) + * Stuff that would be cool but probably too much of a resource hog: + * -some kind of interpolation to avoid jaggies * program idea borrowed from a screensaver on a non-*NIX OS, - * code based on decayscreen by Jamie Zawinski */ #include #include "screenhack.h" +#include -static int delay, radius, speed, size_x, size_y; +#ifdef HAVE_XSHM_EXTENSION +# include "xshm.h" +static Bool use_shm; +static XShmSegmentInfo shm_info; +#endif /* HAVE_XSHM_EXTENSION */ + +struct coo { + int x; + int y; + int r, r_change; + int xmove, ymove; +}; +static struct coo xy_coo[10]; + +static int delay, radius, speed, number, blackhole, vortex, magnify, reflect; static XWindowAttributes xgwa; static GC gc; -static Pixmap orig_map, buffer_map; +static Window g_window; +static Display *g_dpy; +static unsigned long black_pixel; + +static XImage *orig_map, *buffer_map; static int ***from; +static int ****from_array; +static void (*effect) (int) = NULL; +static void move_lense(int); +static void swamp_thing(int); +static void new_rnd_coo(int); +static void init_round_lense(void); +static void (*draw) (int) = NULL; +static void reflect_draw(int); +static void plain_draw(int); -static void init_distort (Display *dpy, Window window) { +static void init_distort(Display *dpy, Window window) +{ XGCValues gcv; long gcflags; - int i, j; - - delay = get_integer_resource ("delay", "Integer"); - radius = get_integer_resource ("radius", "Integer"); - speed = get_integer_resource ("speed", "Integer"); + int i; + + g_window=window; + g_dpy=dpy; + + delay = get_integer_resource("delay", "Integer"); + radius = get_integer_resource("radius", "Integer"); + speed = get_integer_resource("speed", "Integer"); + number = get_integer_resource("number", "Integer"); + +#ifdef HAVE_XSHM_EXTENSION + use_shm = get_boolean_resource("useSHM", "Boolean"); +#endif /* HAVE_XSHM_EXTENSION */ + + blackhole = get_boolean_resource("blackhole", "Boolean"); + vortex = get_boolean_resource("vortex", "Boolean"); + magnify = get_boolean_resource("magnify", "Boolean"); + reflect = get_boolean_resource("reflect", "Boolean"); + + if (get_boolean_resource("swamp", "Boolean")) + effect = &swamp_thing; + if (get_boolean_resource("bounce", "Boolean")) + effect = &move_lense; + + if (effect == NULL && radius == 0 && speed == 0 && number == 0 + && !blackhole && !vortex && !magnify && !reflect) { +/* if no cmdline options are given, randomly choose one of: + * -radius 50 -number 4 -speed 1 -bounce + * -radius 50 -number 4 -speed 1 -blackhole + * -radius 50 -number 4 -speed 1 -vortex + * -radius 50 -number 4 -speed 1 -vortex -magnify + * -radius 50 -number 4 -speed 1 -vortex -magnify -blackhole + * -radius 100 -number 1 -speed 2 -bounce + * -radius 100 -number 1 -speed 2 -blackhole + * -radius 100 -number 1 -speed 2 -vortex + * -radius 100 -number 1 -speed 2 -vortex -magnify + * -radius 100 -number 1 -speed 2 -vortex -magnify -blackhole + * -radius 50 -number 4 -speed 2 -swamp + * -radius 50 -number 4 -speed 2 -swamp -blackhole + * -radius 50 -number 4 -speed 2 -swamp -vortex + * -radius 50 -number 4 -speed 2 -swamp -vortex -magnify + * -radius 50 -number 4 -speed 2 -swamp -vortex -magnify -blackhole + * -radius 80 -number 1 -speed 2 -reflect + * -radius 50 -number 3 -speed 2 -reflect + */ + + i = (random() % 17); + + draw = &plain_draw; + + switch (i) { + case 0: + radius=50;number=4;speed=1; + effect=&move_lense;break; + case 1: + radius=50;number=4;speed=1;blackhole=1; + effect=&move_lense;break; + case 2: + radius=50;number=4;speed=1;vortex=1; + effect=&move_lense;break; + case 3: + radius=50;number=4;speed=1;vortex=1;magnify=1; + effect=&move_lense;break; + case 4: + radius=50;number=4;speed=1;vortex=1;magnify=1;blackhole=1; + effect=&move_lense;break; + case 5: + radius=100;number=1;speed=2; + effect=&move_lense;break; + case 6: + radius=100;number=1;speed=2;blackhole=1; + effect=&move_lense;break; + case 7: + radius=100;number=1;speed=2;vortex=1; + effect=&move_lense;break; + case 8: + radius=100;number=1;speed=2;vortex=1;magnify=1; + effect=&move_lense;break; + case 9: + radius=100;number=1;speed=2;vortex=1;magnify=1;blackhole=1; + effect=&move_lense;break; + case 10: + radius=50;number=4;speed=2; + effect=&swamp_thing;break; + case 11: + radius=50;number=4;speed=2;blackhole=1; + effect=&swamp_thing;break; + case 12: + radius=50;number=4;speed=2;vortex=1; + effect=&swamp_thing;break; + case 13: + radius=50;number=4;speed=2;vortex=1;magnify=1; + effect=&swamp_thing;break; + case 14: + radius=50;number=4;speed=2;vortex=1;magnify=1;blackhole=1; + effect=&swamp_thing;break; + case 15: + radius=80;number=1;speed=2;reflect=1; + draw = &reflect_draw;effect = &move_lense;break; + case 16: default: + radius=50;number=4;speed=2;reflect=1; + draw = &reflect_draw;effect = &move_lense;break; + } + + } if (delay < 0) delay = 0; if (radius <= 0) radius = 60; - if (speed == 0) + if (speed <= 0) speed = 2; + if (number <= 0) + number=1; + if (number >= 10) + number=1; + if (effect == NULL) + effect = &move_lense; + if (reflect) { + draw = &reflect_draw; + effect = &move_lense; + } + if (draw == NULL) + draw = &plain_draw; XGetWindowAttributes (dpy, window, &xgwa); + black_pixel = BlackPixelOfScreen( xgwa.screen ); gcv.function = GXcopy; gcv.subwindow_mode = IncludeInferiors; @@ -57,116 +200,342 @@ static void init_distort (Display *dpy, Window window) { gcflags |= GCSubwindowMode; gc = XCreateGC (dpy, window, gcflags, &gcv); - size_x = xgwa.width; - size_y = xgwa.height; - grab_screen_image (xgwa.screen, window); - orig_map = XCreatePixmap(dpy, window, - xgwa.width, xgwa.height, xgwa.depth); - XCopyArea(dpy, window, - orig_map, gc, 0, 0, xgwa.width, xgwa.height, 0, 0); - buffer_map = XCreatePixmap(dpy, window, - 2*radius + speed, 2*radius + speed, - xgwa.depth); - - from = (int ***)malloc ((2*radius+1) * sizeof(int **)); - for(i = 0; i <= 2*radius; i++) { - from[i] = (int **)malloc((2*radius+1) * sizeof(int *)); - for (j = 0; j <= 2*radius; j++) - from[i][j] = (int *)malloc(2*sizeof(int)); + buffer_map = 0; + orig_map = XGetImage(dpy, window, 0, 0, xgwa.width, xgwa.height, + ~0L, ZPixmap); + +# ifdef HAVE_XSHM_EXTENSION + + if (use_shm) + { + buffer_map = create_xshm_image(dpy, xgwa.visual, orig_map->depth, + ZPixmap, 0, &shm_info, + 2*radius + speed + 2, + 2*radius + speed + 2); + if (!buffer_map) + use_shm = False; + } +# endif /* HAVE_XSHM_EXTENSION */ + + if (!buffer_map) + { + buffer_map = XCreateImage(dpy, xgwa.visual, + orig_map->depth, ZPixmap, 0, 0, + 2*radius + speed + 2, 2*radius + speed + 2, + 8, 0); + buffer_map->data = (char *) + calloc(buffer_map->height, buffer_map->bytes_per_line); } - /* initialize a "see-trough" matrix */ - for (i = 0; i <= 2*radius; i++) { - for (j = 0 ; j <= 2*radius ; j++) { - from[i][j][0]=i-radius/2; - from[i][j][1]=j-radius/2; - } + init_round_lense(); + + for (i = 0; i < number; i++) { + new_rnd_coo(i); + if (number != 1) + xy_coo[i].r = (i*radius)/(number-1); /* "randomize" initial */ + else + xy_coo[i].r = 0; + xy_coo[i].r_change = speed + (i%2)*2*(-speed); /* values a bit */ + xy_coo[i].xmove = speed + (i%2)*2*(-speed); + xy_coo[i].ymove = speed + (i%2)*2*(-speed); } +} + +/* example: initializes a "see-trough" matrix */ +/* static void make_null_lense(void) +{ + int i, j; + for (i = 0; i < 2*radius+speed+2; i++) { + for (j = 0 ; j < 2*radius+speed+2 ; j++) { + from[i][j][0]=i; + from[i][j][1]=j; + } + } +} +*/ + +/* makes a lense with the Radius=loop and centred in + * the point (radius, radius) + */ +static void make_round_lense(int radius, int loop) +{ + int i, j; - /* initialize the distort matrix */ - for (i = 0; i <= 2*radius; i++) { - for(j = 0; j <= 2*radius; j++) { - double r; + for (i = 0; i < 2*radius+speed+2; i++) { + for(j = 0; j < 2*radius+speed+2; j++) { + double r, d; r = sqrt ((i-radius)*(i-radius)+(j-radius)*(j-radius)); - if (r < radius) { - r = sin(r*(M_PI_2)/radius); - if (i < radius) - from[i][j][0] = radius/2 + (i-radius)*r; - else - from[i][j][0] = radius/2 + (i-radius)*r; - if (j < radius) - from[i][j][1] = radius/2 + (j-radius)*r; - else - from[i][j][1] = radius/2 + (j-radius)*r; + if (loop == 0) + d=0.0; + else + d=r/loop; + + if (r < loop-1) { + + if (vortex) { /* vortex-twist effect */ + double angle; + /* this one-line formula for getting a nice rotation angle is borrowed + * (with permission) from the whirl plugin for gimp, + * Copyright (C) 1996 Federico Mena Quintero + */ + /* 2.5 is just a constant used because it looks good :) */ + angle = 2.5*(1-d)*(1-d); + + /* Avoid atan2: DOMAIN error message */ + if ((radius-j) == 0.0 && (radius-i) == 0.0) { + from[i][j][0] = radius + cos(angle)*r; + from[i][j][1] = radius + sin(angle)*r; + } else { + from[i][j][0] = radius + + cos(angle - atan2(radius-j, -(radius-i)))*r; + from[i][j][1] = radius + + sin(angle - atan2(radius-j, -(radius-i)))*r; + } + if (magnify) { + r = sin(d*M_PI_2); + if (blackhole && r != 0) /* blackhole effect */ + r = 1/r; + from[i][j][0] = radius + (from[i][j][0]-radius)*r; + from[i][j][1] = radius + (from[i][j][1]-radius)*r; + } + } else { /* default is to magnify */ + r = sin(d*M_PI_2); + + /* raising r to different power here gives different amounts of + * distortion, a negative value sucks everything into a black hole + */ + /* r = r*r; */ + if (blackhole && r != 0) /* blackhole effect */ + r = 1/r; + /* bubble effect (and blackhole) */ + from[i][j][0] = radius + (i-radius)*r; + from[i][j][1] = radius + (j-radius)*r; + } + } else { /* not inside loop */ + from[i][j][0] = i; + from[i][j][1] = j; } } } +} + +#ifndef EXIT_FAILURE +# define EXIT_FAILURE -1 +#endif + +static void allocate_lense(void) +{ + int i, j; + /* maybe this should be redone so that from[][][] is in one block; + * then pointers could be used instead of arrays in some places (and + * maybe give a speedup - maybe also consume less memory) + */ - XSetGraphicsExposures(dpy, gc, False); /* stop events from XCopyArea */ + from = (int ***)malloc((2*radius+speed+2) * sizeof(int **)); + if (from == NULL) { + perror("distort"); + exit(EXIT_FAILURE); + } + for (i = 0; i < 2*radius+speed+2; i++) { + from[i] = (int **)malloc((2*radius+speed+2) * sizeof(int *)); + if (from[i] == NULL) { + perror("distort"); + exit(EXIT_FAILURE); + } + for (j = 0; j < 2*radius+speed+2; j++) { + from[i][j] = (int *)malloc(2 * sizeof(int)); + if (from[i][j] == NULL) { + perror("distort"); + exit(EXIT_FAILURE); + } + } + } } -static void -move_lens (int *x, int *y, int *xmove, int *ymove) { - if (*xmove==0) - *xmove=speed; - if (*ymove==0) - *ymove=speed; - if (*x==0) - *x = radius + (random() % (size_x-2*radius)); - if (*y==0) - *y = radius + (random() % (size_y-2*radius)); - if (*x + 3*radius/2 >= size_x) - *xmove = -abs(*xmove); - if (*x - radius/2 <= 0) - *xmove = abs(*xmove); - if (*y + 3*radius/2 >= size_y) - *ymove = -abs(*ymove); - if (*y - radius/2 <= 0) - *ymove = abs(*ymove); - - *x = *x + *xmove; - *y = *y + *ymove; +/* from_array in an array containing precalculated from matrices, + * this is a double faced mem vs speed trade, it's faster, but eats + * _a lot_ of mem for large radius (is there a bug here? I can't see it) + */ +static void init_round_lense(void) +{ + int k; + + if (effect == &swamp_thing) { + from_array = (int ****)malloc((radius+1)*sizeof(int ***)); + for (k=0; k <= radius; k++) { + allocate_lense(); + make_round_lense(radius, k); + from_array[k] = from; + } + } else { /* just allocate one from[][][] */ + allocate_lense(); + make_round_lense(radius,radius); + } } -static void distort (Display *dpy, Window window) + +/* generate an XImage of from[][][] and draw it on the screen */ +void plain_draw(int k) { - static int x, y, xmove=0, ymove=0; - int i,j; - - move_lens (&x, &y, &xmove, &ymove); - - XCopyArea(dpy, orig_map, buffer_map, gc, - x-radius/2 - xmove, y-radius/2 - ymove, - 2*radius + abs(xmove), 2*radius + abs(ymove), - 0,0); - - /* it's possible to lower the number of loop iterations by a factor - * of 4, but since it's the XCopyArea's which eat resources, and - * I've only supplied one distortion routine (which is circular), - * here's a check-if-inside circle variation of this for loop. - * Using both optimizations turns the matrix rendering into one - * ugly mess... I'm counting on gcc optimization ;) - */ + int i, j; + for(i = 0 ; i < 2*radius+speed+2; i++) { + for(j = 0 ; j < 2*radius+speed+2 ; j++) { + if (xy_coo[k].x+from[i][j][0] >= 0 && + xy_coo[k].x+from[i][j][0] < xgwa.width && + xy_coo[k].y+from[i][j][1] >= 0 && + xy_coo[k].y+from[i][j][1] < xgwa.height) + XPutPixel(buffer_map, i, j, + XGetPixel(orig_map, + xy_coo[k].x+from[i][j][0], + xy_coo[k].y+from[i][j][1])); + } + } + + XPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y, + 2*radius+speed+2, 2*radius+speed+2); +} + +/* generate an XImage from the reflect algoritm submitted by + * Randy Zack + * draw really got too big and ugly so I split it up + * it should be possible to use the from[][] to speed it up + * (once I figure out the algorithm used :) + */ +void reflect_draw(int k) +{ + int i, j; + int cx, cy; + int ly, lysq, lx, ny, dist, rsq = radius * radius; - for(i = 0 ; i <= 2*radius ; i++) { - for(j = 0 ; j <= 2*radius ; j++) { - if (((radius-i)*(radius-i) + (j-radius)*(j-radius)) - < radius*radius) { - XCopyArea (dpy, orig_map, buffer_map, gc, - x+from[i][j][0], - y+from[i][j][1], - 1, 1, i + xmove, j+ymove); + cx = cy = radius; + if (xy_coo[k].ymove > 0) + cy += speed; + if (xy_coo[k].xmove > 0) + cx += speed; + + for(i = 0 ; i < 2*radius+speed+2; i++) { + ly = i - cy; + lysq = ly * ly; + ny = xy_coo[k].y + i; + for(j = 0 ; j < 2*radius+speed+2 ; j++) { + lx = j - cx; + dist = lx * lx + lysq; + if (dist > rsq || + ly < -radius || ly > radius || + lx < -radius || lx > radius) + XPutPixel( buffer_map, j, i, + XGetPixel( orig_map, xy_coo[k].x + j, ny )); + else if (dist == 0) + XPutPixel( buffer_map, j, i, black_pixel ); + else { + int x = xy_coo[k].x + cx + (lx * rsq / dist); + int y = xy_coo[k].y + cy + (ly * rsq / dist); + if (x < 0 || x > xgwa.width || + y < 0 || y > xgwa.height) + XPutPixel( buffer_map, j, i, black_pixel ); + else + XPutPixel( buffer_map, j, i, + XGetPixel( orig_map, x, y )); } } } - XCopyArea(dpy, buffer_map, window, gc, 0, 0, - 2*radius + abs(xmove), 2*radius + abs(ymove), - x-radius/2 - xmove, y-radius/2 - ymove); + XPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y, + 2*radius+speed+2, 2*radius+speed+2); } +/* create a new, random coordinate, that won't interfer with any other + * coordinates, as the drawing routines would be significantly slowed + * down if they were to handle serveral layers of distortions + */ +static void new_rnd_coo(int k) +{ + int i; + + xy_coo[k].x = (random() % (xgwa.width-2*radius)); + xy_coo[k].y = (random() % (xgwa.height-2*radius)); + + for (i = 0; i < number; i++) { + if (i != k) { + if ((abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius+speed+2) + && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius+speed+2)) { + xy_coo[k].x = (random() % (xgwa.width-2*radius)); + xy_coo[k].y = (random() % (xgwa.height-2*radius)); + i=-1; /* ugly */ + } + } + } +} + +/* move lens and handle bounces with walls and other lenses */ +static void move_lense(int k) +{ + int i; + + if (xy_coo[k].x + 2*radius + speed + 2 >= xgwa.width) + xy_coo[k].xmove = -abs(xy_coo[k].xmove); + if (xy_coo[k].x <= speed) + xy_coo[k].xmove = abs(xy_coo[k].xmove); + if (xy_coo[k].y + 2*radius + speed + 2 >= xgwa.height) + xy_coo[k].ymove = -abs(xy_coo[k].ymove); + if (xy_coo[k].y <= speed) + xy_coo[k].ymove = abs(xy_coo[k].ymove); + + xy_coo[k].x = xy_coo[k].x + xy_coo[k].xmove; + xy_coo[k].y = xy_coo[k].y + xy_coo[k].ymove; + + for (i = 0; i < number; i++) { + if ((i != k) + +/* This commented test is for rectangular lenses (not currently used) and + * the one used is for circular ones + && (abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius) + && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius)) { */ + + && ((xy_coo[k].x - xy_coo[i].x)*(xy_coo[k].x - xy_coo[i].x) + + (xy_coo[k].y - xy_coo[i].y)*(xy_coo[k].y - xy_coo[i].y) + <= 2*radius*2*radius)) { + + int x, y; + x = xy_coo[k].xmove; + y = xy_coo[k].ymove; + xy_coo[k].xmove = xy_coo[i].xmove; + xy_coo[k].ymove = xy_coo[i].ymove; + xy_coo[i].xmove = x; + xy_coo[i].ymove = y; + } + } + +} + +/* make xy_coo[k] grow/shrink */ +void swamp_thing(int k) +{ + if (xy_coo[k].r >= radius) + xy_coo[k].r_change = -abs(xy_coo[k].r_change); + + if (xy_coo[k].r <= 0) { + from = from_array[0]; + draw(k); + xy_coo[k].r_change = abs(xy_coo[k].r_change); + new_rnd_coo(k); + xy_coo[k].r=xy_coo[k].r_change; + return; + } + + xy_coo[k].r = xy_coo[k].r + xy_coo[k].r_change; + + if (xy_coo[k].r >= radius) + xy_coo[k].r = radius; + if (xy_coo[k].r <= 0) + xy_coo[k].r=0; + + from = from_array[xy_coo[k].r]; +} + + char *progclass = "Distort"; @@ -178,8 +547,18 @@ char *defaults [] = { #endif "*delay: 10000", - "*radius: 60", - "*speed: 2", + "*radius: 0", + "*speed: 0", + "*number: 0", + "*vortex: False", + "*magnify: False", + "*swamp: False", + "*bounce: False", + "*reflect: False", + "*blackhole: False", +#ifdef HAVE_XSHM_EXTENSION + "*useSHM: False", /* xshm turns out not to help. */ +#endif /* HAVE_XSHM_EXTENSION */ 0 }; @@ -187,15 +566,34 @@ XrmOptionDescRec options [] = { { "-delay", ".delay", XrmoptionSepArg, 0 }, { "-radius", ".radius", XrmoptionSepArg, 0 }, { "-speed", ".speed", XrmoptionSepArg, 0 }, + { "-number", ".number", XrmoptionSepArg, 0 }, + { "-swamp", ".swamp", XrmoptionNoArg, "True" }, + { "-bounce", ".bounce", XrmoptionNoArg, "True" }, + { "-reflect", ".reflect", XrmoptionNoArg, "True" }, + { "-vortex", ".vortex", XrmoptionNoArg, "True" }, + { "-magnify", ".magnify", XrmoptionNoArg, "True" }, + { "-blackhole", ".blackhole", XrmoptionNoArg, "True" }, +#ifdef HAVE_XSHM_EXTENSION + { "-shm", ".useSHM", XrmoptionNoArg, "True" }, + { "-no-shm", ".useSHM", XrmoptionNoArg, "False" }, +#endif /* HAVE_XSHM_EXTENSION */ { 0, 0, 0, 0 } }; - -void screenhack (Display *dpy, Window window) { + +void screenhack(Display *dpy, Window window) +{ + int k; + init_distort (dpy, window); while (1) { - distort (dpy, window); - XSync (dpy, True); - if (delay) usleep (delay); + for (k = 0; k < number; k++) { + effect(k); + draw(k); + } + + XSync(dpy, True); + if (delay) usleep(delay); } + }