http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.tar.gz
[xscreensaver] / hacks / metaballs.c
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 #include <X11/Xutil.h>
21
22 /*#define VERBOSE*/ 
23
24 char *progclass = "Metaballs";
25
26 char *defaults [] = {
27   ".background: black",
28   ".foreground: white",
29   "*color:    random",
30   "*count:    10",
31   "*cycles:   1000",
32   "*ncolors:  256",
33   "*delay:    5000",
34   "*radius:   100",
35   "*delta:   3",
36   0
37 };
38
39 XrmOptionDescRec options [] = {
40   { "-color",   ".color",   XrmoptionSepArg, 0 },
41   { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
42   { "-count",   ".count",   XrmoptionSepArg, 0 },
43   { "-delay",   ".delay",   XrmoptionSepArg, 0 },
44   { "-cycles",  ".cycles",  XrmoptionSepArg, 0 },
45   { "-radius",  ".radius",  XrmoptionSepArg, 0 },
46   { "-delta",  ".delta",  XrmoptionSepArg, 0 },
47   { 0, 0, 0, 0 }
48 };
49
50 static unsigned short iWinWidth, iWinHeight;
51 static char *sColor;
52
53 /*blob structure*/
54 typedef struct 
55 {
56   short xpos,ypos;
57 } BLOB;
58
59 static unsigned int nBlobCount;
60 static unsigned char radius;
61 static unsigned char delta;
62 static unsigned char dradius;
63 static unsigned short sradius;
64 static unsigned char **blob;
65 static BLOB *blobs;
66 static unsigned char **blub;
67
68 static void init_blob(BLOB *blob)
69 {
70   blob->xpos =  (iWinWidth>> 1) - radius;
71   blob->ypos =  (iWinHeight >> 1) - radius;
72 }
73
74 static void Execute( Display *pDisplay,
75                      Window MainWindow,
76                      GC *pGC, XImage *pImage,
77                      signed short iColorCount, unsigned long *aiColorVals )
78 {
79         unsigned int i, j, k;
80
81         /* clear blub array */
82         for (i = 0; i < iWinHeight; ++i)
83           memset(blub[i], 0, iWinWidth * sizeof(unsigned char));
84
85         /* move blobs */
86         for (i = 0; i < nBlobCount; i++)
87         {
88           blobs[i].xpos += -delta + (int)((delta + .5f) * frand(2.0));
89           blobs[i].ypos += -delta + (int)((delta + .5f) * frand(2.0));
90         }
91
92         /* draw blobs to blub array */
93         for (k = 0; k < nBlobCount; ++k)
94           { 
95             if (blobs[k].ypos > -dradius && blobs[k].xpos > -dradius && blobs[k].ypos < iWinHeight && blobs[k].xpos < iWinWidth)
96               {
97                 for (i = 0; i < dradius; ++i)
98                   {
99                     if (blobs[k].ypos + i >= 0 && blobs[k].ypos + i < iWinHeight)
100                       {
101                         for (j = 0; j < dradius; ++j)
102                           {
103                             if (blobs[k].xpos + j >= 0 && blobs[k].xpos + j < iWinWidth)
104                               {
105                                 if (blub[blobs[k].ypos + i][blobs[k].xpos + j] < iColorCount)
106                                   {
107                                     if (blub[blobs[k].ypos + i][blobs[k].xpos + j] + blob[i][j] > iColorCount)
108                                       blub[blobs[k].ypos + i][blobs[k].xpos + j] = iColorCount;
109                                     else 
110                                       blub[blobs[k].ypos + i][blobs[k].xpos + j] += blob[i][j];     
111                                   }
112                               }
113                           }
114                       }
115                   }
116               }
117             else
118               init_blob(blobs + k);
119           }
120
121         memset( pImage->data, 0, pImage->bytes_per_line * pImage->height);
122
123         /* draw blub array to screen */
124         for (i = 0; i < iWinHeight; ++i)
125           {
126             for (j = 0; j < iWinWidth; ++j)
127               {
128                 if (aiColorVals[blub[i][j]] > 0)
129                   XPutPixel( pImage, j, i, aiColorVals[blub[i][j]] );
130               }
131           }
132
133         XPutImage( pDisplay, MainWindow, *pGC, pImage,
134                    0, 0, 0, 0, iWinWidth, iWinHeight );
135         XSync( pDisplay, False );
136 }
137
138 static unsigned long * SetPalette(Display *pDisplay, Window Win, char *sColor, signed short *piColorCount )
139 {
140         XWindowAttributes XWinAttribs;
141         XColor Color, *aColors;
142         unsigned long *aiColorVals;
143         signed short iColor;
144         float nHalfColors;
145         
146         XGetWindowAttributes( pDisplay, Win, &XWinAttribs );
147         
148         Color.red =   random() % 0xFFFF;
149         Color.green = random() % 0xFFFF;
150         Color.blue =  random() % 0xFFFF;
151
152         if( strcasecmp( sColor, "random" ) && !XParseColor( pDisplay, XWinAttribs.colormap, sColor, &Color ) )
153                 fprintf( stderr, "%s: color %s not found in database. Choosing to random...\n", progname, sColor );
154
155 #ifdef VERBOSE
156         printf( "%s: Base color (RGB): <%d, %d, %d>\n", progclass, Color.red, Color.green, Color.blue );
157 #endif  /*  VERBOSE */
158
159         *piColorCount = get_integer_resource( "ncolors", "Integer" );
160         if( *piColorCount <   2 )       *piColorCount = 2;
161         if( *piColorCount > 255 )       *piColorCount = 255;
162
163         aColors    = calloc( *piColorCount, sizeof(XColor) );
164         aiColorVals = calloc( *piColorCount, sizeof(unsigned long) );
165         
166         for( iColor=0; iColor<*piColorCount; iColor++ )
167         {
168                 nHalfColors = *piColorCount / 2.0F;
169                 /* Black -> Base Color */
170                 if( iColor < (*piColorCount/2) )
171                 {
172                         aColors[ iColor ].red   = ( Color.red   / nHalfColors ) * iColor;
173                         aColors[ iColor ].green = ( Color.green / nHalfColors ) * iColor;
174                         aColors[ iColor ].blue  = ( Color.blue  / nHalfColors ) * iColor;
175                 }
176                 /* Base Color -> White */
177                 else
178                 {
179                         aColors[ iColor ].red   = ( ( ( 0xFFFF - Color.red )   / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.red;
180                         aColors[ iColor ].green = ( ( ( 0xFFFF - Color.green ) / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.green;
181                         aColors[ iColor ].blue  = ( ( ( 0xFFFF - Color.blue )  / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.blue;
182                 }
183
184                 if( !XAllocColor( pDisplay, XWinAttribs.colormap, &aColors[ iColor ] ) )
185                 {
186                         /* start all over with less colors */   
187                         XFreeColors( pDisplay, XWinAttribs.colormap, aiColorVals, iColor, 0 );
188                         free( aColors );
189                         free( aiColorVals );
190                         (*piColorCount)--;
191
192                         if (*piColorCount < 6)
193                           {
194                             fprintf (stderr, "%s: insufficient colors!\n",
195                                      progname);
196                             exit (1);
197                           }
198
199                         aColors     = calloc( *piColorCount, sizeof(XColor) );
200                         aiColorVals = calloc( *piColorCount, sizeof(unsigned long) );
201                         iColor = -1;
202                 }
203                 else
204                         aiColorVals[ iColor ] = aColors[ iColor ].pixel;
205         }
206
207         free( aColors );
208
209         XSetWindowBackground( pDisplay, Win, aiColorVals[ 0 ] );
210
211         return aiColorVals;
212 }
213
214
215 static void Initialize( Display *pDisplay, Window Win, GC *pGC, XImage **ppImage )
216 {
217         XGCValues gcValues;
218         XWindowAttributes XWinAttribs;
219         int iBitsPerPixel, i, j;
220         unsigned int distance_squared;
221         float fraction;
222
223         /* Create the Image for drawing */
224         XGetWindowAttributes( pDisplay, Win, &XWinAttribs );
225
226         /* Find the preferred bits-per-pixel. (jwz) */
227         {
228                 int pfvc = 0;
229                 XPixmapFormatValues *pfv = XListPixmapFormats( pDisplay, &pfvc );
230                 for( i=0; i<pfvc; i++ )
231                         if( pfv[ i ].depth == XWinAttribs.depth )
232                         {
233                                 iBitsPerPixel = pfv[ i ].bits_per_pixel;
234                                 break;
235                         }
236                 if( pfv )
237                         XFree (pfv);
238         }
239
240         /*  Create the GC. */
241         *pGC = XCreateGC( pDisplay, Win, 0, &gcValues );
242
243         *ppImage = XCreateImage( pDisplay, XWinAttribs.visual, XWinAttribs.depth, ZPixmap, 0, NULL,
244                                                           XWinAttribs.width, XWinAttribs.height, BitmapPad( pDisplay ), 0 );
245         (*ppImage)->data = calloc((*ppImage)->bytes_per_line, (*ppImage)->height);
246
247         iWinWidth = XWinAttribs.width;
248         iWinHeight = XWinAttribs.height;
249
250         /*  Get the base color. */
251         sColor = get_string_resource( "color", "Color" );
252
253         /*  Get the delta. */
254         delta = get_integer_resource( "delta", "Integer" );
255         if (delta < 1)
256           delta = 1;
257         else if (delta > 20)
258           delta = 20;
259         
260         /*  Get the radius. */
261         radius = get_integer_resource( "radius", "Integer" );
262         if (radius < 2)
263           radius = 2;
264         if (radius > 100)
265           radius = 100;
266         
267         radius = (radius / 100.0) * (iWinHeight >> 3);
268         if (radius >= 128) /* should use UCHAR_MAX? */
269           radius = 127; /* dradius should fit in u_char */
270
271         dradius = radius * 2;
272         sradius = radius * radius;
273
274         /* create blob */
275         blob = malloc ( dradius * sizeof(unsigned char*));
276         for (i = 0; i < dradius; ++i)
277           blob[i] = malloc( dradius * sizeof(unsigned char));
278
279         /* create blub array */
280         blub = malloc( iWinHeight * sizeof(unsigned char*));
281         for (i = 0; i < iWinHeight; ++i)
282           blub[i] = malloc( iWinWidth * sizeof(unsigned char));
283
284         /* create blob */
285         for (i = -radius; i < radius; ++i)
286           {
287             for (j = -radius; j < radius; ++j)
288               {
289                 distance_squared = i * i + j * j;
290                 if (distance_squared <= sradius)
291                   {
292                     /* compute density */     
293                     fraction = (float)distance_squared / (float)sradius;
294                     blob[i + radius][j + radius] = pow((1.0 - (fraction * fraction)),4.0) * 255.0;
295                   }
296                 else
297                   {
298                     blob[i + radius][j + radius] = 0;
299                   }
300               }    
301           }
302         
303         for (i = 0; i < nBlobCount; i++)
304           {
305             init_blob(blobs + i);
306           }
307 }
308
309 void screenhack(Display *pDisplay, Window Win )
310 {
311         GC gc;
312         signed short iColorCount = 0;
313         unsigned long *aiColorVals = NULL;
314         XImage *pImage = NULL;
315 #ifdef VERBOSE
316         time_t nTime = time( NULL );
317         unsigned short iFrame = 0;
318 #endif  /*  VERBOSE */
319         int delay, cycles, i;
320
321         nBlobCount = get_integer_resource( "count", "Integer" );
322         if( nBlobCount > 255 ) nBlobCount = 255;
323         if( nBlobCount <  2 ) nBlobCount = 2;
324
325         if( ( blobs = calloc( nBlobCount, sizeof(BLOB) ) ) == NULL )
326         {
327                 fprintf( stderr, "%s: Could not allocate %d Blobs\n", progclass, nBlobCount );
328                 return;
329         }
330 #ifdef VERBOSE 
331         printf( "%s: Allocated %d Blobs\n", progclass, nBlobCount );
332 #endif  /*  VERBOSE */
333
334         Initialize( pDisplay, Win, &gc, &pImage );
335
336         delay = get_integer_resource( "delay", "Integer" );
337         cycles = get_integer_resource( "cycles", "Integer" );
338         i = cycles;
339
340         while( 1 )
341         {
342                 screenhack_handle_events( pDisplay );
343
344                 if( i++ >= cycles )
345                 {
346                         XWindowAttributes XWinAttribs;
347                         XGetWindowAttributes( pDisplay, Win, &XWinAttribs );
348
349                         memset( pImage->data, 0, pImage->bytes_per_line * pImage->height );
350                         XFreeColors( pDisplay, XWinAttribs.colormap, aiColorVals, iColorCount, 0 );
351                         free( aiColorVals );
352                         aiColorVals = SetPalette( pDisplay, Win, sColor, &iColorCount );
353                         XClearWindow( pDisplay, Win );
354                         for (i = 0; i < nBlobCount; i++)
355                           {
356                             init_blob(blobs + i);
357                           }
358                         i = 0;
359                 }
360
361                 Execute( pDisplay, Win, &gc, pImage, iColorCount - 1, aiColorVals );
362
363                 if( delay && !(i % 4) )
364                         usleep(delay);
365
366 #ifdef VERBOSE
367                 iFrame++;
368                 if( nTime - time( NULL ) )
369                 {
370                         printf( "%s: %d FPS\n", progclass, iFrame );
371                         nTime = time( NULL );
372                         iFrame = 0;
373                 }
374 #endif  /*  VERBOSE */
375         }
376
377         free( pImage->data );
378         XDestroyImage( pImage );
379         free( aiColorVals );
380         free( blobs );
381         for (i = 0; i < iWinHeight; ++i)
382           free( blub[i] );
383         free( blub );
384         for (i = 0; i < dradius; ++i)
385           free( blob[i] );
386         free( blob );
387 }
388
389
390 /* End of Module - "metaballs.c" */
391