X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fdistort.c;h=54a12fa9d3e6623a10ff7bf91f914f31b47c4605;hb=96a411663168b0ba5432b407a83be55f3df0c802;hp=a18df51bc598dd5948957b6fa4538b8ce8543293;hpb=ce3185de9d9705e259f2b60dd4b5509007fa17d4;p=xscreensaver diff --git a/hacks/distort.c b/hacks/distort.c index a18df51b..54a12fa9 100644 --- a/hacks/distort.c +++ b/hacks/distort.c @@ -1,5 +1,5 @@ /* -*- mode: C; tab-width: 4 -*- - * xscreensaver, Copyright (c) 1992, 1993, 1994, 1996, 1997, 1998 + * xscreensaver, Copyright (c) 1992, 1993, 1994, 1996, 1997, 1998, 2002, 2003 * Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its @@ -20,16 +20,29 @@ * -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 + * -large speed values leaves the image distorted * program idea borrowed from a screensaver on a non-*NIX OS, + * + * 28 Sep 1999 Jonas Munsin (jmunsin@iki.fi) + * Added about 10x faster algortim for 8, 16 and 32 bpp (modifies pixels + * directly avoiding costly XPutPixle(XGetPixel()) calls, inspired by + * xwhirl made by horvai@clipper.ens.fr (Peter Horvai) and the XFree86 + * Xlib sources. + * This piece of code is really horrible, but it works, and at the moment + * I don't have time or inspiration to fix something that works (knock + * on wood). + * 08 Oct 1999 Jonas Munsin (jmunsin@iki.fi) + * Corrected several bugs causing references beyond allocated memory. */ #include #include "screenhack.h" #include +#include #ifdef HAVE_XSHM_EXTENSION # include "xshm.h" -static Bool use_shm; +static Bool use_shm = False; static XShmSegmentInfo shm_info; #endif /* HAVE_XSHM_EXTENSION */ @@ -41,21 +54,35 @@ struct coo { }; static struct coo xy_coo[10]; -static int delay, radius, speed, number, blackhole, vortex, magnify; +static int delay, radius, speed, number, blackhole, vortex, magnify, reflect, slow; static XWindowAttributes xgwa; static GC gc; static Window g_window; -static Display *g_dpy; +static Display *g_dpy; +static unsigned long black_pixel; static XImage *orig_map, *buffer_map; +static unsigned long *buffer_map_cache; static int ***from; static int ****from_array; +static int *fast_from = NULL; 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 (*draw_routine)(XImage *, XImage *, int, int, int *) = NULL; +static void fast_draw_8(XImage *, XImage *, int, int, int *); +static void fast_draw_16(XImage *, XImage *, int, int, int *); +static void fast_draw_32(XImage *, XImage *, int, int, int *); +static void generic_draw(XImage *, XImage *, int, int, int *); +static int bpp_size = 0; + static void init_distort(Display *dpy, Window window) { @@ -66,10 +93,10 @@ static void init_distort(Display *dpy, Window window) 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"); + 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"); @@ -78,14 +105,18 @@ static void init_distort(Display *dpy, Window window) blackhole = get_boolean_resource("blackhole", "Boolean"); vortex = get_boolean_resource("vortex", "Boolean"); magnify = get_boolean_resource("magnify", "Boolean"); + reflect = get_boolean_resource("reflect", "Boolean"); + slow = get_boolean_resource("slow", "Boolean"); - if (get_boolean_resource ("swamp", "Boolean")) + if (get_boolean_resource("swamp", "Boolean")) effect = &swamp_thing; - if (get_boolean_resource ("bounce", "Boolean")) + if (get_boolean_resource("bounce", "Boolean")) effect = &move_lense; + XGetWindowAttributes (dpy, window, &xgwa); + if (effect == NULL && radius == 0 && speed == 0 && number == 0 - && !blackhole && !vortex && !magnify) { + && !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 @@ -97,14 +128,19 @@ static void init_distort(Display *dpy, Window window) * -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 + * jwz: not these + * -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 */ - i = (random() % 15); + i = (random() % 12 /* 17 */); + + draw = &plain_draw; switch (i) { case 0: @@ -137,25 +173,53 @@ static void init_distort(Display *dpy, Window window) case 9: radius=100;number=1;speed=2;vortex=1;magnify=1;blackhole=1; effect=&move_lense;break; + case 10: + radius=80;number=1;speed=2;reflect=1; + draw = &reflect_draw;effect = &move_lense;break; + case 11: + radius=50;number=4;speed=2;reflect=1; + draw = &reflect_draw;effect = &move_lense;break; + +#if 0 /* jwz: not these */ + case 12: radius=50;number=4;speed=2; effect=&swamp_thing;break; - case 11: + case 13: radius=50;number=4;speed=2;blackhole=1; effect=&swamp_thing;break; - case 12: + case 14: radius=50;number=4;speed=2;vortex=1; effect=&swamp_thing;break; - case 13: + case 15: radius=50;number=4;speed=2;vortex=1;magnify=1; effect=&swamp_thing;break; - case 14: default: + case 16: radius=50;number=4;speed=2;vortex=1;magnify=1;blackhole=1; effect=&swamp_thing;break; +#endif + + default: + abort(); break; } + /* but if the window is small, reduce default radius */ + if (xgwa.width < radius * 8) + radius = xgwa.width/8; } + /* never allow the radius to be too close to the min window dimension + */ + if (radius >= xgwa.width * 0.45) radius = xgwa.width * 0.45; + if (radius >= xgwa.height * 0.45) radius = xgwa.height * 0.45; + + + /* -swamp mode consumes vast amounts of memory, proportional to radius -- + so throttle radius to a small-ish value (60 => ~30MB.) + */ + if (effect == &swamp_thing && radius > 60) + radius = 60; + if (delay < 0) delay = 0; if (radius <= 0) @@ -168,8 +232,14 @@ static void init_distort(Display *dpy, Window window) 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; @@ -178,11 +248,17 @@ static void init_distort(Display *dpy, Window window) gcflags |= GCSubwindowMode; gc = XCreateGC (dpy, window, gcflags, &gcv); - grab_screen_image (xgwa.screen, window); + load_random_image (xgwa.screen, window, window, NULL); buffer_map = 0; orig_map = XGetImage(dpy, window, 0, 0, xgwa.width, xgwa.height, ~0L, ZPixmap); + buffer_map_cache = malloc(sizeof(unsigned long)*(2*radius+speed+2)*(2*radius+speed+2)); + + if (buffer_map_cache == NULL) { + perror("distort"); + exit(EXIT_FAILURE); + } # ifdef HAVE_XSHM_EXTENSION @@ -207,6 +283,31 @@ static void init_distort(Display *dpy, Window window) calloc(buffer_map->height, buffer_map->bytes_per_line); } + if ((buffer_map->byte_order == orig_map->byte_order) + && (buffer_map->depth == orig_map->depth) + && (buffer_map->format == ZPixmap) + && (orig_map->format == ZPixmap) + && !slow) { + switch (orig_map->bits_per_pixel) { + case 32: + draw_routine = &fast_draw_32; + bpp_size = sizeof(CARD32); + break; + case 16: + draw_routine = &fast_draw_16; + bpp_size = sizeof(CARD16); + break; + case 8: + draw_routine = &fast_draw_8; + bpp_size = sizeof(CARD8); + break; + default: + draw_routine = &generic_draw; + break; + } + } else { + draw_routine = &generic_draw; + } init_round_lense(); for (i = 0; i < number; i++) { @@ -219,6 +320,7 @@ static void init_distort(Display *dpy, Window window) xy_coo[i].xmove = speed + (i%2)*2*(-speed); xy_coo[i].ymove = speed + (i%2)*2*(-speed); } + } /* example: initializes a "see-trough" matrix */ @@ -233,6 +335,26 @@ static void init_distort(Display *dpy, Window window) } } */ +static void convert(void) { + int *p; + int i, j; + fast_from = calloc(1, sizeof(int)*((buffer_map->bytes_per_line/bpp_size)*(2*radius+speed+2) + 2*radius+speed+2)); + if (fast_from == NULL) { + perror("distort"); + exit(EXIT_FAILURE); + } + p = fast_from; + for (i = 0; i < 2*radius+speed+2; i++) { + for (j = 0; j < 2*radius+speed+2; j++) { + *(p + i + j*buffer_map->bytes_per_line/bpp_size) + = from[i][j][0] + xgwa.width*from[i][j][1]; + if (*(p + i + j*buffer_map->bytes_per_line/bpp_size) < 0 + || *(p + i + j*buffer_map->bytes_per_line/bpp_size) >= orig_map->height*orig_map->width) { + *(p + i + j*buffer_map->bytes_per_line/bpp_size) = 0; + } + } + } +} /* makes a lense with the Radius=loop and centred in * the point (radius, radius) @@ -242,10 +364,13 @@ static void make_round_lense(int radius, int loop) int i, j; for (i = 0; i < 2*radius+speed+2; i++) { - for(j = 0; j < 2*radius+speed+2; j++) { + for(j = 0; j < ((0 == bpp_size) ? (2*radius+speed+2) : (buffer_map->bytes_per_line/bpp_size)); j++) { double r, d; r = sqrt ((i-radius)*(i-radius)+(j-radius)*(j-radius)); - d=r/loop; + if (loop == 0) + d=0.0; + else + d=r/loop; if (r < loop-1) { @@ -255,14 +380,19 @@ static void make_round_lense(int radius, int loop) * (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-r/loop)*(1-r/loop); - - 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; - + /* 5 is just a constant used because it looks good :) */ + angle = 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 */ @@ -277,7 +407,7 @@ static void make_round_lense(int radius, int loop) * distortion, a negative value sucks everything into a black hole */ /* r = r*r; */ - if (blackhole) /* blackhole effect */ + if (blackhole && r != 0) /* blackhole effect */ r = 1/r; /* bubble effect (and blackhole) */ from[i][j][0] = radius + (i-radius)*r; @@ -289,28 +419,38 @@ static void make_round_lense(int radius, int loop) } } } + + /* this is really just a quick hack to keep both the compability mode with all depths and still + * allow the custom optimized draw routines with the minimum amount of work */ + if (0 != bpp_size) { + convert(); + } } +#ifndef EXIT_FAILURE +# define EXIT_FAILURE -1 +#endif + static void allocate_lense(void) { int i, j; + int s = ((0 != bpp_size) ? (buffer_map->bytes_per_line/bpp_size) : (2*radius+speed+2)); /* 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) */ - - from = (int ***)malloc((2*radius+speed+2) * sizeof(int **)); + from = (int ***)malloc(s*sizeof(int **)); if (from == NULL) { perror("distort"); exit(EXIT_FAILURE); } - for (i = 0; i < 2*radius+speed+2; i++) { + for (i = 0; i < s; 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++) { + for (j = 0; j < s; j++) { from[i][j] = (int *)malloc(2 * sizeof(int)); if (from[i][j] == NULL) { perror("distort"); @@ -341,21 +481,126 @@ static void init_round_lense(void) } } +/* If fast_draw_8, fast_draw_16 or fast_draw_32 are to be used, the following properties + * of the src and dest XImages must hold (otherwise the generic, slooow, method provided + * by X is to be used): + * src->byte_order == dest->byte_order + * src->format == ZPixmap && dest->format == ZPixmap + * src->depth == dest->depth == the depth the function in question asumes + * x and y is the coordinates in src from where to cut out the image from, + * distort_matrix is a precalculated array of how to distort the matrix + */ + +static void fast_draw_8(XImage *src, XImage *dest, int x, int y, int *distort_matrix) { + CARD8 *u = (CARD8 *)dest->data; + CARD8 *t = (CARD8 *)src->data + x + y*src->bytes_per_line/sizeof(CARD8); + + while (u < (CARD8 *)(dest->data + sizeof(CARD8)*dest->height + *dest->bytes_per_line/sizeof(CARD8))) { + *u++ = t[*distort_matrix++]; + } +} + +static void fast_draw_16(XImage *src, XImage *dest, int x, int y, int *distort_matrix) { + CARD16 *u = (CARD16 *)dest->data; + CARD16 *t = (CARD16 *)src->data + x + y*src->bytes_per_line/sizeof(CARD16); + + while (u < (CARD16 *)(dest->data + sizeof(CARD16)*dest->height + *dest->bytes_per_line/sizeof(CARD16))) { + *u++ = t[*distort_matrix++]; + } +} + +static void fast_draw_32(XImage *src, XImage *dest, int x, int y, int *distort_matrix) { + CARD32 *u = (CARD32 *)dest->data; + CARD32 *t = (CARD32 *)src->data + x + y*src->bytes_per_line/sizeof(CARD32); + + while (u < (CARD32 *)(dest->data + sizeof(CARD32)*dest->height + *dest->bytes_per_line/sizeof(CARD32))) { + *u++ = t[*distort_matrix++]; + } +} + +static void generic_draw(XImage *src, XImage *dest, int x, int y, int *distort_matrix) { + int i, j; + for (i = 0; i < dest->width; i++) + for (j = 0; j < dest->height; j++) + if (from[i][j][0] + x >= 0 && + from[i][j][0] + x < src->width && + from[i][j][1] + y >= 0 && + from[i][j][1] + y < src->height) + XPutPixel(dest, i, j, + XGetPixel(src, + from[i][j][0] + x, + from[i][j][1] + y)); +} /* generate an XImage of from[][][] and draw it on the screen */ -void draw(int k) +static void plain_draw(int k) +{ + if (xy_coo[k].x+2*radius+speed+2 > orig_map->width || + xy_coo[k].y+2*radius+speed+2 > orig_map->height) + return; + + draw_routine(orig_map, buffer_map, xy_coo[k].x, xy_coo[k].y, fast_from); + +# ifdef HAVE_XSHM_EXTENSION + if (use_shm) + XShmPutImage(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, False); + else + + if (!use_shm) +# endif + 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 :) + */ +static void reflect_draw(int k) { int i, j; + int cx, cy; + int ly, lysq, lx, ny, dist, rsq = radius * radius; + + 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; + if (ny >= orig_map->height) ny = orig_map->height-1; 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])); + 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 )); + } } } @@ -391,11 +636,11 @@ static void move_lense(int k) { int i; - if (xy_coo[k].x + 4*radius/2 >= xgwa.width) + 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 + 4*radius/2 >= xgwa.height) + 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); @@ -403,10 +648,11 @@ static void move_lense(int k) xy_coo[k].x = xy_coo[k].x + xy_coo[k].xmove; xy_coo[k].y = xy_coo[k].y + xy_coo[k].ymove; + /* bounce against othe lenses */ for (i = 0; i < number; i++) { if ((i != k) -/* This commented test is for rectangular lenses (not presently used) and +/* 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)) { */ @@ -428,7 +674,7 @@ static void move_lense(int k) } /* make xy_coo[k] grow/shrink */ -void swamp_thing(int k) +static void swamp_thing(int k) { if (xy_coo[k].r >= radius) xy_coo[k].r_change = -abs(xy_coo[k].r_change); @@ -463,7 +709,7 @@ char *defaults [] = { "*visualID: Best", #endif - "*delay: 10000", + "*delay: 1000", "*radius: 0", "*speed: 0", "*number: 0", @@ -471,6 +717,7 @@ char *defaults [] = { "*magnify: False", "*swamp: False", "*bounce: False", + "*reflect: False", "*blackhole: False", #ifdef HAVE_XSHM_EXTENSION "*useSHM: False", /* xshm turns out not to help. */ @@ -485,9 +732,11 @@ XrmOptionDescRec options [] = { { "-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" }, + { "-slow", ".slow", XrmoptionNoArg, "True" }, #ifdef HAVE_XSHM_EXTENSION { "-shm", ".useSHM", XrmoptionNoArg, "True" }, { "-no-shm", ".useSHM", XrmoptionNoArg, "False" }, @@ -507,7 +756,8 @@ void screenhack(Display *dpy, Window window) draw(k); } - XSync(dpy, True); + XSync(dpy, False); + screenhack_handle_events (dpy); if (delay) usleep(delay); }