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