http://svn.poeml.de/viewvc/ppc/src-unpacked/xscreensaver/xscreensaver-4.12.tar.bz2...
[xscreensaver] / hacks / eruption.c
1 /* Eruption, 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 - "eruption.c"
12  *
13  * [02-2003] - W.P. van Paassen: Improvements, added some code of jwz from the pyro hack for a spherical distribution of the particles
14  * [01-2003] - W.P. van Paassen: Port to X for use with XScreenSaver, the shadebob hack by Shane Smit was used as a template
15  * [04-2002] - 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 = "Eruption";
25
26 char *defaults [] = {
27   ".background: black",
28   ".foreground: white",
29   "*cycles:   80",
30   "*ncolors:  256",
31   "*delay:    5000",
32   "*particles: 300",
33   "*cooloff: 2",
34   "*gravity: 1",
35   "*heat: 256",
36   0
37 };
38
39 XrmOptionDescRec options [] = {
40   { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
41   { "-delay",   ".delay",   XrmoptionSepArg, 0 },
42   { "-cycles",  ".cycles",  XrmoptionSepArg, 0 },
43   { "-particles",  ".particles",  XrmoptionSepArg, 0 },
44   { "-cooloff",  ".cooloff",  XrmoptionSepArg, 0 },
45   { "-gravity",  ".gravity",  XrmoptionSepArg, 0 },
46   { "-heat",  ".heat",  XrmoptionSepArg, 0 },
47   { 0, 0, 0, 0 }
48 };
49
50 /* Slightly whacked, for better explosions
51  */
52 #define PI_2000 6284
53 #define SPREAD 15
54
55 static int sin_cache[PI_2000];
56 static int cos_cache[PI_2000];
57
58 /*particle structure*/
59 typedef struct 
60 {
61   short xpos, ypos, xdir, ydir;
62   unsigned char colorindex;
63   unsigned char dead;
64 } PARTICLE;
65
66 static PARTICLE *particles;
67 static unsigned short iWinWidth, iWinHeight;
68 static unsigned char **fire;
69 static unsigned short nParticleCount;
70 static unsigned char xdelta, ydelta, decay;
71 static signed char gravity;
72 static signed short heat;
73
74 static void
75 cache(void) /* jwz */
76 {               /*needs to be run once. Could easily be */
77   int i;        /*reimplemented to run and cache at compile-time,*/
78   double dA;    
79   for (i=0; i<PI_2000; i++)
80     {
81       dA=sin(((double) (random() % (PI_2000/2)))/1000.0);
82       /*Emulation of spherical distribution*/
83       dA+=asin(frand(1.0))/M_PI_2*0.1;
84       /*Approximating the integration of the binominal, for
85         well-distributed randomness*/
86       cos_cache[i]=-abs((int) (cos(((double)i)/1000.0)*dA*ydelta));
87       sin_cache[i]=(int) (sin(((double)i)/1000.0)*dA*xdelta);
88     }
89 }
90
91 void init_particle(PARTICLE* particle, signed short iColorCount, unsigned short xcenter, unsigned short ycenter)
92 {
93   int v = random() % PI_2000;
94   particle->xpos = xcenter - SPREAD + (random() % (SPREAD * 2));
95   particle->ypos = ycenter - SPREAD + (random() % (SPREAD * 2));;
96   particle->xdir = sin_cache[v];
97   particle->ydir = cos_cache[v];
98   particle->colorindex = iColorCount;
99   particle->dead = 0;
100 }
101
102 static void Execute( Display *pDisplay,
103                      Window MainWindow,
104                      GC *pGC, XImage *pImage,
105                      signed short iColorCount, unsigned long *aiColorVals )
106 {
107   int i, j;
108   unsigned int temp;
109
110   /* move and draw particles into fire array */
111   
112   for (i = 0; i < nParticleCount; i++)
113     {
114       if (!particles[i].dead)
115         {
116           particles[i].xpos += particles[i].xdir;
117           particles[i].ypos += particles[i].ydir;
118           
119           /* is particle dead? */
120           
121           if (particles[i].colorindex == 0)
122             {
123               particles[i].dead = 1;
124               continue;
125             }
126           
127           if (particles[i].xpos < 1)
128             {
129               particles[i].xpos = 1;
130               particles[i].xdir = -particles[i].xdir - 4;
131               particles[i].colorindex = iColorCount;
132             }
133           else if (particles[i].xpos >= iWinWidth - 2)
134             {
135               particles[i].xpos = iWinWidth - 2;
136               particles[i].xdir = -particles[i].xdir + 4;
137               particles[i].colorindex = iColorCount;
138             }
139           
140           if (particles[i].ypos < 1)
141             {
142               particles[i].ypos = 1;
143               particles[i].ydir = -particles[i].ydir;
144               particles[i].colorindex = iColorCount;
145             }
146           else if (particles[i].ypos >= iWinHeight - 3)
147             {
148               particles[i].ypos = iWinHeight- 3;
149               particles[i].ydir = (-particles[i].ydir >> 2) - (random() % 2);
150               particles[i].colorindex = iColorCount;
151             }
152           
153           /* gravity kicks in */
154           particles[i].ydir += gravity;
155           
156           /* particle cools off */
157           particles[i].colorindex--;
158           
159           /* draw particle */
160           fire[particles[i].ypos][particles[i].xpos] = particles[i].colorindex;
161           fire[particles[i].ypos][particles[i].xpos - 1] = particles[i].colorindex;
162           fire[particles[i].ypos + 1][particles[i].xpos] = particles[i].colorindex;
163           fire[particles[i].ypos - 1][particles[i].xpos] = particles[i].colorindex;
164           fire[particles[i].ypos][particles[i].xpos + 1] = particles[i].colorindex;
165         }
166     }
167   
168   /* create fire effect */ 
169   for (i = 0; i < iWinHeight; i++)
170     {
171       for (j = 0; j < iWinWidth; j++)
172         {
173           if (j + 1 >= iWinWidth)
174             temp = 0;
175           else
176             temp = fire[i][j + 1];
177
178           if (j - 1 >= 0)  
179             temp += fire[i][j - 1];
180
181           if (i - 1 >= 0)
182             {
183               temp += fire[i - 1][j];
184               if (j - 1 >= 0)
185                 temp += fire[i - 1][j - 1];
186               if (j + 1 < iWinWidth)
187                 temp += fire[i - 1][j + 1];
188             }
189           
190           if (i + 1 < iWinHeight)
191             {
192               temp += fire[i + 1][j];
193               if (j + 1 < iWinWidth)
194                 temp += fire[i + 1][j + 1];
195               if (j - 1 >= 0)
196                 temp += fire[i + 1][j - 1];
197             }
198           
199           temp >>= 3;
200           
201           if (temp > decay)
202             {
203               temp -= decay;
204             }
205           else
206             temp = 0;
207           
208           fire[i][j] = temp;
209         }
210     }
211   
212   memset( pImage->data, 0, pImage->bytes_per_line * pImage->height );
213   
214   /* draw fire array to screen */
215   for (i = 0; i < iWinHeight; ++i)
216     {
217       for (j = 0; j < iWinWidth; ++j)
218         {
219           if (fire[i][j] > 0)
220             XPutPixel( pImage, j, i, aiColorVals[ fire[i][j] ] );
221         }
222     }
223   XPutImage( pDisplay, MainWindow, *pGC, pImage,
224              0,0,0,0, iWinWidth, iWinHeight );
225   XSync( pDisplay, False );
226 }
227
228 static unsigned long * SetPalette(Display *pDisplay, Window Win, signed short *piColorCount )
229 {
230         XWindowAttributes XWinAttribs;
231         XColor Color, *aColors;
232         unsigned long *aiColorVals;
233         signed short iColor;
234
235         XGetWindowAttributes( pDisplay, Win, &XWinAttribs );
236         
237         *piColorCount = get_integer_resource( "ncolors", "Integer" );
238         if( *piColorCount < 16 )        *piColorCount = 16;
239         if( *piColorCount > 255 )       *piColorCount = 256;
240
241         aColors    = calloc( *piColorCount, sizeof(XColor) );
242         aiColorVals = calloc( *piColorCount, sizeof(unsigned long) );
243         
244         Color.red = Color.green = Color.blue = 65535 / *piColorCount;
245
246         /* create fire palette */
247         for( iColor=0; iColor < *piColorCount; iColor++ )
248         {
249           if (iColor < *piColorCount >> 3)
250             {
251               /* black to blue */
252               aColors[iColor].red = 0;
253               aColors[iColor].green = 0;
254               aColors[iColor].blue = Color.blue * (iColor << 1);
255             }
256           else if (iColor < *piColorCount >> 2)
257             {
258               /* blue to red */
259               signed short temp = (iColor - (*piColorCount >> 3));
260               aColors[iColor].red = Color.red * (temp << 3);
261               aColors[iColor].green = 0;
262               aColors[iColor].blue = 16383 - Color.blue * (temp << 1);
263             }
264           else if (iColor < (*piColorCount >> 2) + (*piColorCount >> 3))
265             {
266               /* red to yellow */
267               signed short temp = (iColor - (*piColorCount >> 2)) << 3;
268               aColors[iColor].red = 65535;
269               aColors[iColor].green = Color.green * temp;
270               aColors[iColor].blue = 0;
271             }
272           else if (iColor < *piColorCount >> 1)
273             {
274               /* yellow to white */
275               signed int temp = (iColor - ((*piColorCount >> 2) + (*piColorCount >> 3))) << 3;
276               aColors[iColor].red = 65535;
277               aColors[iColor].green = 65535;
278               aColors[iColor].blue = Color.blue * temp;
279             }
280           else
281             {
282               /* white */
283               aColors[iColor].red = aColors[iColor].green = aColors[iColor].blue = 65535;
284             }
285
286           if( !XAllocColor( pDisplay, XWinAttribs.colormap, &aColors[ iColor ] ) )
287             {
288               /* start all over with less colors */     
289               XFreeColors( pDisplay, XWinAttribs.colormap, aiColorVals, iColor, 0 );
290               free( aColors );
291               free( aiColorVals );
292               (*piColorCount)--;
293               aColors     = calloc( *piColorCount, sizeof(XColor) );
294               aiColorVals = calloc( *piColorCount, sizeof(unsigned long) );
295               iColor = -1;
296             }
297           else
298             aiColorVals[ iColor ] = aColors[ iColor ].pixel;
299         }
300
301         if (heat < *piColorCount)
302           *piColorCount = heat;
303         
304         free( aColors );
305
306         XSetWindowBackground( pDisplay, Win, aiColorVals[ 0 ] );
307
308         return aiColorVals;
309 }
310
311
312 static void Initialize( Display *pDisplay, Window Win, GC *pGC, XImage **ppImage )
313 {
314         XGCValues gcValues;
315         XWindowAttributes XWinAttribs;
316         int iBitsPerPixel, i;
317
318         /* Create the Image for drawing */
319         XGetWindowAttributes( pDisplay, Win, &XWinAttribs );
320
321         /* Find the preferred bits-per-pixel. (jwz) */
322         {
323                 int pfvc = 0;
324                 XPixmapFormatValues *pfv = XListPixmapFormats( pDisplay, &pfvc );
325                 for( i=0; i<pfvc; i++ )
326                         if( pfv[ i ].depth == XWinAttribs.depth )
327                         {
328                                 iBitsPerPixel = pfv[ i ].bits_per_pixel;
329                                 break;
330                         }
331                 if( pfv )
332                         XFree (pfv);
333         }
334
335         /*  Create the GC. */
336         *pGC = XCreateGC( pDisplay, Win, 0, &gcValues );
337
338         *ppImage = XCreateImage( pDisplay, XWinAttribs.visual, XWinAttribs.depth, ZPixmap, 0, NULL,
339                                                           XWinAttribs.width, XWinAttribs.height, BitmapPad( pDisplay ), 0 );
340         (*ppImage)->data = calloc((*ppImage)->bytes_per_line, (*ppImage)->height);
341
342         iWinWidth = XWinAttribs.width;
343         iWinHeight = XWinAttribs.height;
344
345         /* create fire array */
346         fire = calloc( iWinHeight, sizeof(unsigned char*));
347         for (i = 0; i < iWinHeight; ++i)
348           fire[i] = calloc( iWinWidth, sizeof(unsigned char));
349
350         /*create particles */
351         particles = malloc (nParticleCount * sizeof(PARTICLE));
352 }
353
354 void screenhack(Display *pDisplay, Window Win )
355 {
356         XWindowAttributes XWinAttribs;
357         GC gc;
358         signed short iColorCount = 0;
359         unsigned long *aiColorVals = NULL;
360         unsigned short sum = 0;
361         XImage *pImage = NULL;
362 #ifdef VERBOSE
363         time_t nTime = time( NULL );
364         unsigned short iFrame = 0;
365 #endif  /*  VERBOSE */
366         int delay, cycles, i;
367
368         nParticleCount = get_integer_resource( "particles", "Integer" );
369         if (nParticleCount < 100)
370           nParticleCount = 100;
371         if (nParticleCount > 2000)
372           nParticleCount = 2000;
373
374         decay = get_integer_resource( "cooloff", "Integer" );
375         if (decay <= 0)
376           decay = 0;
377         if (decay > 10)
378           decay = 10;
379
380         gravity = get_integer_resource( "gravity", "Integer" );
381         if (gravity < -5)
382           gravity = -5;
383         if (gravity > 5)
384           gravity = 5;
385
386         heat = get_integer_resource( "heat", "Integer" );
387         if (heat < 64)
388           heat = 64;
389         if (heat > 256)
390           heat = 256;
391
392 #ifdef VERBOSE 
393         printf( "%s: Allocated %d particles\n", progclass, nParticleCount );
394 #endif  /*  VERBOSE */
395
396         Initialize( pDisplay, Win, &gc, &pImage );
397
398         ydelta = 0;
399         while (sum < (iWinHeight >> 1) - SPREAD)
400           {
401             ydelta++;
402             sum += ydelta;
403           }
404
405         sum = 0;
406         while (sum < (iWinWidth >> 3))
407           {
408             xdelta++;
409             sum += xdelta;
410           }
411
412         delay = get_integer_resource( "delay", "Integer" );
413         cycles = get_integer_resource( "cycles", "Integer" );
414         i = cycles;
415
416         cache();
417         
418         XGetWindowAttributes( pDisplay, Win, &XWinAttribs );
419         XFreeColors( pDisplay, XWinAttribs.colormap, aiColorVals, iColorCount, 0 );
420         free( aiColorVals );
421         aiColorVals = SetPalette( pDisplay, Win, &iColorCount );
422         XClearWindow( pDisplay, Win );
423         memset( pImage->data, 0, pImage->bytes_per_line * pImage->height );
424
425         while( 1 )
426           {
427             screenhack_handle_events( pDisplay );
428             
429             if( i++ >= cycles )
430               {
431                 /* compute random center */
432                 unsigned short xcenter, ycenter;
433                 xcenter = random() % iWinWidth;
434                 ycenter = random() % iWinHeight;
435                 
436                 for (i = 0; i < nParticleCount; i++)
437                   init_particle(particles + i, iColorCount - 1, xcenter, ycenter);
438                 i = 0;
439               }
440             
441             Execute( pDisplay, Win, &gc, pImage, iColorCount - 1, aiColorVals );
442             
443             if( delay && !(i % 4) )
444               usleep(delay);
445             
446 #ifdef VERBOSE
447             iFrame++;
448             if( nTime - time( NULL ) )
449               {
450                 printf( "%s: %d FPS\n", progclass, iFrame );
451                 nTime = time( NULL );
452                 iFrame = 0;
453               }
454 #endif  /*  VERBOSE */
455           }
456         
457         free( pImage->data );
458         XDestroyImage( pImage );
459         free( aiColorVals );
460         for (i = 0; i < iWinHeight; ++i)
461           free( fire[i] );
462         free( fire );
463         free( particles );
464 }
465
466 /* End of Module - "eruption.c" */
467