/* MetaBalls, Copyright (c) 2002-2003 W.P. van Paassen * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. * * Module - "metaballs.c" * * [01/24/03] - W.P. van Paassen: Additional features * [12/29/02] - W.P. van Paassen: Port to X for use with XScreenSaver, the shadebob hack by Shane Smit was used as a template * [12/26/02] - W.P. van Paassen: Creation for the Demo Effects Collection (http://demo-effects.sourceforge.net) */ #include #include "screenhack.h" /*#define VERBOSE*/ /*blob structure*/ typedef struct { short xpos,ypos; } BLOB; struct state { Display *dpy; Window window; unsigned short iWinWidth, iWinHeight; char *sColor; unsigned int nBlobCount; unsigned char radius; unsigned char delta; unsigned char dradius; unsigned short sradius; unsigned char **blob; BLOB *blobs; unsigned char **blub; int delay, cycles; signed short iColorCount; unsigned long *aiColorVals; XImage *pImage; GC gc; int draw_i; }; #undef BELLRAND #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3) static void init_blob(struct state *st, BLOB *blob) { blob->xpos = st->iWinWidth/4 + BELLRAND(st->iWinWidth/2) - st->radius; blob->ypos = st->iWinHeight/4 + BELLRAND(st->iWinHeight/2) - st->radius; } static void Execute( struct state *st ) { int i, j, k; /* clear st->blub array */ for (i = 0; i < st->iWinHeight; ++i) memset(st->blub[i], 0, st->iWinWidth * sizeof(unsigned char)); /* move st->blobs */ for (i = 0; i < st->nBlobCount; i++) { st->blobs[i].xpos += -st->delta + (int)((st->delta + .5f) * frand(2.0)); st->blobs[i].ypos += -st->delta + (int)((st->delta + .5f) * frand(2.0)); } /* draw st->blobs to st->blub array */ for (k = 0; k < st->nBlobCount; ++k) { if (st->blobs[k].ypos > -st->dradius && st->blobs[k].xpos > -st->dradius && st->blobs[k].ypos < st->iWinHeight && st->blobs[k].xpos < st->iWinWidth) { for (i = 0; i < st->dradius; ++i) { if (st->blobs[k].ypos + i >= 0 && st->blobs[k].ypos + i < st->iWinHeight) { for (j = 0; j < st->dradius; ++j) { if (st->blobs[k].xpos + j >= 0 && st->blobs[k].xpos + j < st->iWinWidth) { if (st->blub[st->blobs[k].ypos + i][st->blobs[k].xpos + j] < st->iColorCount-1) { if (st->blub[st->blobs[k].ypos + i][st->blobs[k].xpos + j] + st->blob[i][j] > st->iColorCount-1) st->blub[st->blobs[k].ypos + i][st->blobs[k].xpos + j] = st->iColorCount-1; else st->blub[st->blobs[k].ypos + i][st->blobs[k].xpos + j] += st->blob[i][j]; } } } } } } else init_blob(st, st->blobs + k); } memset( st->pImage->data, 0, st->pImage->bytes_per_line * st->pImage->height); /* draw st->blub array to screen */ for (i = 0; i < st->iWinHeight; ++i) { for (j = 0; j < st->iWinWidth; ++j) { if (st->aiColorVals[st->blub[i][j]] > 0) XPutPixel( st->pImage, j, i, st->aiColorVals[st->blub[i][j]] ); } } XPutImage( st->dpy, st->window, st->gc, st->pImage, 0, 0, 0, 0, st->iWinWidth, st->iWinHeight ); } static unsigned long * SetPalette(struct state *st ) { XWindowAttributes XWinAttribs; XColor Color, *aColors; signed short iColor; float nHalfColors; XGetWindowAttributes( st->dpy, st->window, &XWinAttribs ); Color.red = random() % 0xFFFF; Color.green = random() % 0xFFFF; Color.blue = random() % 0xFFFF; if( strcasecmp( st->sColor, "random" ) && !XParseColor( st->dpy, XWinAttribs.colormap, st->sColor, &Color ) ) fprintf( stderr, "%s: color %s not found in database. Choosing to random...\n", progname, st->sColor ); #ifdef VERBOSE printf( "%s: Base color (RGB): <%d, %d, %d>\n", progname, Color.red, Color.green, Color.blue ); #endif /* VERBOSE */ st->iColorCount = get_integer_resource(st->dpy, "ncolors", "Integer" ); if( st->iColorCount < 2 ) st->iColorCount = 2; if( st->iColorCount > 255 ) st->iColorCount = 255; aColors = calloc( st->iColorCount, sizeof(XColor) ); st->aiColorVals = calloc( st->iColorCount, sizeof(unsigned long) ); for( iColor=0; iColor < st->iColorCount; iColor++ ) { nHalfColors = st->iColorCount / 2.0F; /* Black -> Base Color */ if( iColor < (st->iColorCount/2) ) { aColors[ iColor ].red = ( Color.red / nHalfColors ) * iColor; aColors[ iColor ].green = ( Color.green / nHalfColors ) * iColor; aColors[ iColor ].blue = ( Color.blue / nHalfColors ) * iColor; } /* Base Color -> White */ else { aColors[ iColor ].red = ( ( ( 0xFFFF - Color.red ) / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.red; aColors[ iColor ].green = ( ( ( 0xFFFF - Color.green ) / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.green; aColors[ iColor ].blue = ( ( ( 0xFFFF - Color.blue ) / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.blue; } if( !XAllocColor( st->dpy, XWinAttribs.colormap, &aColors[ iColor ] ) ) { /* start all over with less colors */ XFreeColors( st->dpy, XWinAttribs.colormap, st->aiColorVals, iColor, 0 ); free( aColors ); free( st->aiColorVals ); (st->iColorCount)--; if (st->iColorCount < 6) { fprintf (stderr, "%s: insufficient colors!\n", progname); exit (1); } aColors = calloc( st->iColorCount, sizeof(XColor) ); st->aiColorVals = calloc( st->iColorCount, sizeof(unsigned long) ); iColor = -1; } else st->aiColorVals[ iColor ] = aColors[ iColor ].pixel; } free( aColors ); XSetWindowBackground( st->dpy, st->window, st->aiColorVals[ 0 ] ); return st->aiColorVals; } static void Initialize( struct state *st ) { XGCValues gcValues; XWindowAttributes XWinAttribs; int /*iBitsPerPixel,*/ i, j; unsigned int distance_squared; float fraction; /* Create the Image for drawing */ XGetWindowAttributes( st->dpy, st->window, &XWinAttribs ); /* Find the preferred bits-per-pixel. (jwz) */ #if 0 { int pfvc = 0; XPixmapFormatValues *pfv = XListPixmapFormats( st->dpy, &pfvc ); for( i=0; igc = XCreateGC( st->dpy, st->window, 0, &gcValues ); st->pImage = XCreateImage( st->dpy, XWinAttribs.visual, XWinAttribs.depth, ZPixmap, 0, NULL, XWinAttribs.width, XWinAttribs.height, BitmapPad( st->dpy ), 0 ); (st->pImage)->data = calloc((st->pImage)->bytes_per_line, (st->pImage)->height); st->iWinWidth = XWinAttribs.width; st->iWinHeight = XWinAttribs.height; /* Get the base color. */ st->sColor = get_string_resource(st->dpy, "color", "Color" ); /* Get the st->delta. */ st->delta = get_integer_resource(st->dpy, "delta", "Integer" ); if (st->delta < 1) st->delta = 1; else if (st->delta > 20) st->delta = 20; /* Get the st->radius. */ st->radius = get_integer_resource(st->dpy, "radius", "Integer" ); if (st->radius < 2) st->radius = 2; if (st->radius > 100) st->radius = 100; st->radius = (st->radius / 100.0) * (st->iWinHeight >> 3); if (st->radius >= 128) /* should use UCHAR_MAX? */ st->radius = 127; /* st->dradius should fit in u_char */ if (st->iWinWidth < 100 || st->iWinHeight < 100) /* tiny window */ if (st->radius < 20) st->radius = 20; st->dradius = st->radius * 2; st->sradius = st->radius * st->radius; /* create st->blob */ st->blob = malloc ( st->dradius * sizeof(unsigned char*)); for (i = 0; i < st->dradius; ++i) st->blob[i] = malloc( st->dradius * sizeof(unsigned char)); /* create st->blub array */ st->blub = malloc( st->iWinHeight * sizeof(unsigned char*)); for (i = 0; i < st->iWinHeight; ++i) st->blub[i] = malloc( st->iWinWidth * sizeof(unsigned char)); /* create st->blob */ for (i = -st->radius; i < st->radius; ++i) { for (j = -st->radius; j < st->radius; ++j) { distance_squared = i * i + j * j; if (distance_squared <= st->sradius) { /* compute density */ fraction = (float)distance_squared / (float)st->sradius; st->blob[i + st->radius][j + st->radius] = pow((1.0 - (fraction * fraction)),4.0) * 255.0; } else { st->blob[i + st->radius][j + st->radius] = 0; } } } for (i = 0; i < st->nBlobCount; i++) { init_blob(st, st->blobs + i); } } static void * metaballs_init (Display *dpy, Window window) { struct state *st = (struct state *) calloc (1, sizeof(*st)); #ifdef VERBOSE time_t nTime = time( NULL ); unsigned short iFrame = 0; #endif /* VERBOSE */ st->dpy = dpy; st->window = window; st->nBlobCount = get_integer_resource(st->dpy, "count", "Integer" ); if( st->nBlobCount > 255 ) st->nBlobCount = 255; if( st->nBlobCount < 2 ) st->nBlobCount = 2; if( ( st->blobs = calloc( st->nBlobCount, sizeof(BLOB) ) ) == NULL ) { fprintf( stderr, "%s: Could not allocate %d Blobs\n", progname, st->nBlobCount ); abort(); } #ifdef VERBOSE printf( "%s: Allocated %d Blobs\n", progname, st->nBlobCount ); #endif /* VERBOSE */ Initialize( st ); st->delay = get_integer_resource(st->dpy, "delay", "Integer" ); st->cycles = get_integer_resource(st->dpy, "cycles", "Integer" ); st->draw_i = -1; return st; } static unsigned long metaballs_draw (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; if( st->draw_i < 0 || st->draw_i++ >= st->cycles ) { int i; XWindowAttributes XWinAttribs; XGetWindowAttributes( st->dpy, st->window, &XWinAttribs ); memset( st->pImage->data, 0, st->pImage->bytes_per_line * st->pImage->height ); XFreeColors( st->dpy, XWinAttribs.colormap, st->aiColorVals, st->iColorCount, 0 ); free( st->aiColorVals ); st->aiColorVals = SetPalette( st ); XClearWindow( st->dpy, st->window ); for (i = 0; i < st->nBlobCount; i++) { init_blob(st, st->blobs + i); } st->draw_i = 0; } Execute( st ); #ifdef VERBOSE iFrame++; if( nTime - time( NULL ) ) { printf( "%s: %d FPS\n", progname, iFrame ); nTime = time( NULL ); iFrame = 0; } #endif /* VERBOSE */ return st->delay; } static void metaballs_reshape (Display *dpy, Window window, void *closure, unsigned int w, unsigned int h) { } static Bool metaballs_event (Display *dpy, Window window, void *closure, XEvent *event) { return False; } static void metaballs_free (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; int i; if (st->pImage) XDestroyImage (st->pImage); free (st->aiColorVals); free (st->blobs); for (i = 0; i < st->iWinHeight; ++i) free (st->blub[i]); free (st->blub); for (i = 0; i < st->dradius; ++i) free (st->blob[i]); if (st->sColor) free (st->sColor); free (st->blob); XFreeGC (dpy, st->gc); free (st); } static const char *metaballs_defaults [] = { ".background: black", ".foreground: white", "*color: random", "*count: 10", "*cycles: 1000", "*ncolors: 256", "*delay: 10000", "*radius: 100", "*delta: 3", #ifdef HAVE_MOBILE "*ignoreRotation: True", #endif 0 }; static XrmOptionDescRec metaballs_options [] = { { "-color", ".color", XrmoptionSepArg, 0 }, { "-ncolors", ".ncolors", XrmoptionSepArg, 0 }, { "-count", ".count", XrmoptionSepArg, 0 }, { "-delay", ".delay", XrmoptionSepArg, 0 }, { "-cycles", ".cycles", XrmoptionSepArg, 0 }, { "-radius", ".radius", XrmoptionSepArg, 0 }, { "-delta", ".delta", XrmoptionSepArg, 0 }, { 0, 0, 0, 0 } }; XSCREENSAVER_MODULE ("MetaBalls", metaballs) /* End of Module - "metaballs.c" */