1 /* MetaBalls, Copyright (c) 2002-2003 W.P. van Paassen <peter@paassen.tmfweb.nl>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or
9  * implied warranty.
10  *
11  * Module - "metaballs.c"
12  *
13  * [01/24/03] - W.P. van Paassen: Additional features
14  * [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
15  * [12/26/02] - W.P. van Paassen: Creation for the Demo Effects Collection (http://demo-effects.sourceforge.net)
16  */
17 
18 #include <math.h>
19 #include "screenhack.h"
20 
21 /*#define VERBOSE*/
22 
23 /*blob structure*/
24 typedef struct
25 {
26   short xpos,ypos;
27 } BLOB;
28 
29 struct state {
30   Display *dpy;
31   Window window;
32 
33   unsigned short iWinWidth, iWinHeight;
34   char *sColor;
35 
36   unsigned int nBlobCount;
37   unsigned char radius;
38   unsigned char delta;
39   unsigned char dradius;
40   unsigned short sradius;
41   unsigned char **blob;
42   BLOB *blobs;
43   unsigned char **blub;
44 
45   int delay, cycles;
46   signed short iColorCount;
47   unsigned long *aiColorVals;
48   XImage *pImage;
49   GC gc;
50   int draw_i;
51 };
52 
53 
54 #undef BELLRAND
55 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
56 
init_blob(struct state * st,BLOB * blob)57 static void init_blob(struct state *st, BLOB *blob)
58 {
59   blob->xpos = st->iWinWidth/4  + BELLRAND(st->iWinWidth/2)  - st->radius;
60   blob->ypos = st->iWinHeight/4 + BELLRAND(st->iWinHeight/2) - st->radius;
61 }
62 
Execute(struct state * st)63 static void Execute( struct state *st )
64 {
65 	int i, j, k;
66 
67 	/* clear st->blub array */
68 	for (i = 0; i < st->iWinHeight; ++i)
69 	  memset(st->blub[i], 0, st->iWinWidth * sizeof(unsigned char));
70 
71 	/* move st->blobs */
72 	for (i = 0; i < st->nBlobCount; i++)
73 	{
74 	  st->blobs[i].xpos += -st->delta + (int)((st->delta + .5f) * frand(2.0));
75 	  st->blobs[i].ypos += -st->delta + (int)((st->delta + .5f) * frand(2.0));
76 	}
77 
78 	/* draw st->blobs to st->blub array */
79 	for (k = 0; k < st->nBlobCount; ++k)
80 	  {
81 	    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)
82 	      {
83 		for (i = 0; i < st->dradius; ++i)
84 		  {
85 		    if (st->blobs[k].ypos + i >= 0 && st->blobs[k].ypos + i < st->iWinHeight)
86 		      {
87 			for (j = 0; j < st->dradius; ++j)
88 			  {
89 			    if (st->blobs[k].xpos + j >= 0 && st->blobs[k].xpos + j < st->iWinWidth)
90 			      {
91 				if (st->blub[st->blobs[k].ypos + i][st->blobs[k].xpos + j] < st->iColorCount-1)
92 				  {
93 				    if (st->blub[st->blobs[k].ypos + i][st->blobs[k].xpos + j] + st->blob[i][j] > st->iColorCount-1)
94 				      st->blub[st->blobs[k].ypos + i][st->blobs[k].xpos + j] = st->iColorCount-1;
95 				    else
96 				      st->blub[st->blobs[k].ypos + i][st->blobs[k].xpos + j] += st->blob[i][j];
97 				  }
98 			      }
99 			  }
100 		      }
101 		  }
102 	      }
103 	    else
104 	      init_blob(st, st->blobs + k);
105 	  }
106 
107 	memset( st->pImage->data, 0, st->pImage->bytes_per_line * st->pImage->height);
108 
109 	/* draw st->blub array to screen */
110 	for (i = 0; i < st->iWinHeight; ++i)
111 	  {
112 	    for (j = 0; j < st->iWinWidth; ++j)
113 	      {
114 		if (st->aiColorVals[st->blub[i][j]] > 0)
115 		  XPutPixel( st->pImage, j, i, st->aiColorVals[st->blub[i][j]] );
116 	      }
117 	  }
118 
119 	XPutImage( st->dpy, st->window, st->gc, st->pImage,
120 		   0, 0, 0, 0, st->iWinWidth, st->iWinHeight );
121 }
122 
SetPalette(struct state * st)123 static unsigned long * SetPalette(struct state *st )
124 {
125 	XWindowAttributes XWinAttribs;
126 	XColor Color, *aColors;
127 	signed short iColor;
128 	float nHalfColors;
129 
130 	XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
131 
132 	Color.red =   random() % 0xFFFF;
133 	Color.green = random() % 0xFFFF;
134 	Color.blue =  random() % 0xFFFF;
135 
136 	if( strcasecmp( st->sColor, "random" ) && !XParseColor( st->dpy, XWinAttribs.colormap, st->sColor, &Color ) )
137 		fprintf( stderr, "%s: color %s not found in database. Choosing to random...\n", progname, st->sColor );
138 
139 #ifdef VERBOSE
140 	printf( "%s: Base color (RGB): <%d, %d, %d>\n", progname, Color.red, Color.green, Color.blue );
141 #endif  /*  VERBOSE */
142 
143 	st->iColorCount = get_integer_resource(st->dpy,  "ncolors", "Integer" );
144 	if( st->iColorCount <   2 )	st->iColorCount = 2;
145 	if( st->iColorCount > 255 )	st->iColorCount = 255;
146 
147 	aColors    = calloc( st->iColorCount, sizeof(XColor) );
148 	st->aiColorVals = calloc( st->iColorCount, sizeof(unsigned long) );
149 
150 	for( iColor=0; iColor < st->iColorCount; iColor++ )
151 	{
152 		nHalfColors = st->iColorCount / 2.0F;
153 		/* Black -> Base Color */
154 		if( iColor < (st->iColorCount/2) )
155 		{
156 			aColors[ iColor ].red   = ( Color.red   / nHalfColors ) * iColor;
157 			aColors[ iColor ].green = ( Color.green / nHalfColors ) * iColor;
158 			aColors[ iColor ].blue  = ( Color.blue  / nHalfColors ) * iColor;
159 		}
160 		/* Base Color -> White */
161 		else
162 		{
163 			aColors[ iColor ].red   = ( ( ( 0xFFFF - Color.red )   / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.red;
164 			aColors[ iColor ].green = ( ( ( 0xFFFF - Color.green ) / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.green;
165 			aColors[ iColor ].blue  = ( ( ( 0xFFFF - Color.blue )  / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.blue;
166 		}
167 
168 		if( !XAllocColor( st->dpy, XWinAttribs.colormap, &aColors[ iColor ] ) )
169 		{
170 			/* start all over with less colors */
171 			XFreeColors( st->dpy, XWinAttribs.colormap, st->aiColorVals, iColor, 0 );
172 			free( aColors );
173 			free( st->aiColorVals );
174 			(st->iColorCount)--;
175 
176                         if (st->iColorCount < 6)
177                           {
178                             fprintf (stderr, "%s: insufficient colors!\n",
179                                      progname);
180                             exit (1);
181                           }
182 
183 			aColors     = calloc( st->iColorCount, sizeof(XColor) );
184 			st->aiColorVals = calloc( st->iColorCount, sizeof(unsigned long) );
185 			iColor = -1;
186 		}
187 		else
188 			st->aiColorVals[ iColor ] = aColors[ iColor ].pixel;
189 	}
190 
191 	free( aColors );
192 
193 	XSetWindowBackground( st->dpy, st->window, st->aiColorVals[ 0 ] );
194 
195 	return st->aiColorVals;
196 }
197 
198 
Initialize(struct state * st)199 static void Initialize( struct state *st )
200 {
201 	XGCValues gcValues;
202 	XWindowAttributes XWinAttribs;
203 	int /*iBitsPerPixel,*/ i, j;
204 	unsigned int distance_squared;
205 	float fraction;
206 
207 	/* Create the Image for drawing */
208 	XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
209 
210 	/* Find the preferred bits-per-pixel. (jwz) */
211 #if 0
212 	{
213 		int pfvc = 0;
214 		XPixmapFormatValues *pfv = XListPixmapFormats( st->dpy, &pfvc );
215 		for( i=0; i<pfvc; i++ )
216 			if( pfv[ i ].depth == XWinAttribs.depth )
217 			{
218 				iBitsPerPixel = pfv[ i ].bits_per_pixel;
219 				break;
220 			}
221 		if( pfv )
222 			XFree (pfv);
223 	}
224 #endif
225 
226 	/*  Create the GC. */
227 	st->gc = XCreateGC( st->dpy, st->window, 0, &gcValues );
228 
229 	st->pImage = XCreateImage( st->dpy, XWinAttribs.visual, XWinAttribs.depth, ZPixmap, 0, NULL,
230 							  XWinAttribs.width, XWinAttribs.height, BitmapPad( st->dpy ), 0 );
231 	(st->pImage)->data = calloc((st->pImage)->bytes_per_line, (st->pImage)->height);
232 
233 	st->iWinWidth = XWinAttribs.width;
234 	st->iWinHeight = XWinAttribs.height;
235 
236 	/*  Get the base color. */
237 	st->sColor = get_string_resource(st->dpy,  "color", "Color" );
238 
239 	/*  Get the st->delta. */
240 	st->delta = get_integer_resource(st->dpy,  "delta", "Integer" );
241 	if (st->delta < 1)
242 	  st->delta = 1;
243 	else if (st->delta > 20)
244 	  st->delta = 20;
245 
246 	/*  Get the st->radius. */
247 	st->radius = get_integer_resource(st->dpy,  "radius", "Integer" );
248 	if (st->radius < 2)
249 	  st->radius = 2;
250 	if (st->radius > 100)
251 	  st->radius = 100;
252 
253 	st->radius = (st->radius / 100.0) * (st->iWinHeight >> 3);
254         if (st->radius >= 128) /* should use UCHAR_MAX? */
255           st->radius = 127; /* st->dradius should fit in u_char */
256 
257         if (st->iWinWidth < 100 || st->iWinHeight < 100) /* tiny window */
258           if (st->radius < 20)
259             st->radius = 20;
260 
261 	st->dradius = st->radius * 2;
262 	st->sradius = st->radius * st->radius;
263 
264 	/* create st->blob */
265 	st->blob = malloc ( st->dradius * sizeof(unsigned char*));
266 	for (i = 0; i < st->dradius; ++i)
267 	  st->blob[i] = malloc( st->dradius * sizeof(unsigned char));
268 
269 	/* create st->blub array */
270 	st->blub = malloc( st->iWinHeight * sizeof(unsigned char*));
271 	for (i = 0; i < st->iWinHeight; ++i)
272 	  st->blub[i] = malloc( st->iWinWidth * sizeof(unsigned char));
273 
274 	/* create st->blob */
275 	for (i = -st->radius; i < st->radius; ++i)
276 	  {
277 	    for (j = -st->radius; j < st->radius; ++j)
278 	      {
279 		distance_squared = i * i + j * j;
280 		if (distance_squared <= st->sradius)
281 		  {
282 		    /* compute density */
283 		    fraction = (float)distance_squared / (float)st->sradius;
284 		    st->blob[i + st->radius][j + st->radius] = pow((1.0 - (fraction * fraction)),4.0) * 255.0;
285 		  }
286 		else
287 		  {
288 		    st->blob[i + st->radius][j + st->radius] = 0;
289 		  }
290 	      }
291 	  }
292 
293 	for (i = 0; i < st->nBlobCount; i++)
294 	  {
295 	    init_blob(st, st->blobs + i);
296 	  }
297 }
298 
299 static void *
metaballs_init(Display * dpy,Window window)300 metaballs_init (Display *dpy, Window window)
301 {
302   struct state *st = (struct state *) calloc (1, sizeof(*st));
303 #ifdef VERBOSE
304   time_t nTime = time( NULL );
305   unsigned short iFrame = 0;
306 #endif  /*  VERBOSE */
307 
308   st->dpy = dpy;
309   st->window = window;
310 
311   st->nBlobCount = get_integer_resource(st->dpy,  "count", "Integer" );
312   if( st->nBlobCount > 255 ) st->nBlobCount = 255;
313   if( st->nBlobCount <  2 ) st->nBlobCount = 2;
314 
315   if( ( st->blobs = calloc( st->nBlobCount, sizeof(BLOB) ) ) == NULL )
316     {
317       fprintf( stderr, "%s: Could not allocate %d Blobs\n", progname, st->nBlobCount );
318       abort();
319     }
320 #ifdef VERBOSE
321   printf( "%s: Allocated %d Blobs\n", progname, st->nBlobCount );
322 #endif  /*  VERBOSE */
323 
324   Initialize( st );
325 
326   st->delay = get_integer_resource(st->dpy,  "delay", "Integer" );
327   st->cycles = get_integer_resource(st->dpy,  "cycles", "Integer" );
328 
329   st->draw_i = -1;
330   return st;
331 }
332 
333 
334 
335 static unsigned long
metaballs_draw(Display * dpy,Window window,void * closure)336 metaballs_draw (Display *dpy, Window window, void *closure)
337 {
338   struct state *st = (struct state *) closure;
339 
340   if( st->draw_i < 0 || st->draw_i++ >= st->cycles )
341     {
342       int i;
343       XWindowAttributes XWinAttribs;
344       XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
345 
346       memset( st->pImage->data, 0, st->pImage->bytes_per_line * st->pImage->height );
347       XFreeColors( st->dpy, XWinAttribs.colormap, st->aiColorVals, st->iColorCount, 0 );
348       free( st->aiColorVals );
349       st->aiColorVals = SetPalette( st );
350       XClearWindow( st->dpy, st->window );
351       for (i = 0; i < st->nBlobCount; i++)
352         {
353           init_blob(st, st->blobs + i);
354         }
355       st->draw_i = 0;
356     }
357 
358   Execute( st );
359 
360 #ifdef VERBOSE
361   iFrame++;
362   if( nTime - time( NULL ) )
363     {
364       printf( "%s: %d FPS\n", progname, iFrame );
365       nTime = time( NULL );
366       iFrame = 0;
367     }
368 #endif  /*  VERBOSE */
369 
370   return st->delay;
371 }
372 
373 
374 static void
metaballs_reshape(Display * dpy,Window window,void * closure,unsigned int w,unsigned int h)375 metaballs_reshape (Display *dpy, Window window, void *closure,
376                  unsigned int w, unsigned int h)
377 {
378 }
379 
380 static Bool
metaballs_event(Display * dpy,Window window,void * closure,XEvent * event)381 metaballs_event (Display *dpy, Window window, void *closure, XEvent *event)
382 {
383   return False;
384 }
385 
386 static void
metaballs_free(Display * dpy,Window window,void * closure)387 metaballs_free (Display *dpy, Window window, void *closure)
388 {
389   struct state *st = (struct state *) closure;
390   int i;
391   if (st->pImage) XDestroyImage (st->pImage);
392   free (st->aiColorVals);
393   free (st->blobs);
394   for (i = 0; i < st->iWinHeight; ++i)
395     free (st->blub[i]);
396   free (st->blub);
397   for (i = 0; i < st->dradius; ++i)
398     free (st->blob[i]);
399   if (st->sColor) free (st->sColor);
400   free (st->blob);
401   XFreeGC (dpy, st->gc);
402   free (st);
403 }
404 
405 
406 static const char *metaballs_defaults [] = {
407   ".background: black",
408   ".foreground: white",
409   "*color:    random",
410   "*count:    10",
411   "*cycles:   1000",
412   "*ncolors:  256",
413   "*delay:    10000",
414   "*radius:   100",
415   "*delta:   3",
416 #ifdef HAVE_MOBILE
417   "*ignoreRotation: True",
418 #endif
419   0
420 };
421 
422 static XrmOptionDescRec metaballs_options [] = {
423   { "-color",   ".color",   XrmoptionSepArg, 0 },
424   { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
425   { "-count",   ".count",   XrmoptionSepArg, 0 },
426   { "-delay",   ".delay",   XrmoptionSepArg, 0 },
427   { "-cycles",  ".cycles",  XrmoptionSepArg, 0 },
428   { "-radius",  ".radius",  XrmoptionSepArg, 0 },
429   { "-delta",  ".delta",  XrmoptionSepArg, 0 },
430   { 0, 0, 0, 0 }
431 };
432 
433 
434 XSCREENSAVER_MODULE ("MetaBalls", metaballs)
435 
436 /* End of Module - "metaballs.c" */
437 
438