http://www.jwz.org/xscreensaver/xscreensaver-5.08.tar.gz
[xscreensaver] / hacks / fireworkx.c
1 /*
2  * Fireworkx 1.6 - pyrotechnics simulation program (XScreensaver version)
3  * Copyright (c) 1999-2007 Rony B Chandran <ronybc@asia.com>
4  *
5  * From Kerala, INDIA
6  * 
7  * url: http://www.ronybc.8k.com
8  *
9  * Permission to use, copy, modify, distribute, and sell this software and its
10  * documentation for any purpose is hereby granted without fee, provided that
11  * the above copyright notice appear in all copies and that both that
12  * copyright notice and this permission notice appear in supporting
13  * documentation.  No representations are made about the suitability of this
14  * software for any purpose.  It is provided "as is" without express or 
15  * implied warranty.
16  *
17  * Additional programming: 
18  * ------------------------
19  * Support for different dpy color modes: 
20  * Jean-Pierre Demailly <Jean-Pierre.Demailly@ujf-grenoble.fr>
21  *
22  * Fixed array access problems by beating on it with a large hammer.
23  * Nicholas Miell <nmiell@gmail.com>
24  *
25  * Freed 'free'ing up of memory by adding the forgotten 'XSync's
26  * Renuka S <renuka@local.net>
27  *
28  */
29
30 #include <math.h>
31 #include "screenhack.h"
32
33 #define FWXVERSION "1.6"
34
35 #define SHELLCOUNT 3                   /* 3 ; see light() before changing this value */
36 #define PIXCOUNT 500                   /* 500     */
37 #define POWER 5                        /* 5       */
38 #define FTWEAK 12                      /* 12      */
39
40 #if HAVE_X86_MMX
41 void mmx_blur(char *a, int b, int c, int d); 
42 void mmx_glow(char *a, int b, int c, int d, char *e);
43 #endif
44
45 #define rnd(x) ((x) ? (int)(random() % (x)) : 0)
46
47 typedef struct {
48   unsigned int burn;
49   float x;
50   float y;
51   float xv;
52   float yv;}firepix;
53
54 typedef struct {
55   unsigned int cx,cy;
56   unsigned int life;
57   unsigned int color;
58   unsigned int special;
59   unsigned int cshift;
60   unsigned int vgn,shy;
61   float air,flash;
62   firepix *fpix; }fireshell;
63
64 struct state {
65   Display *dpy;
66   Window window;
67
68   fireshell *fshell, *ffshell;
69   GC gc;
70
71   int depth;
72   int bigendian;
73   Bool flash_on;
74   Bool glow_on;
75   Bool verbose;
76   Bool shoot;
77   int width;
78   int height;
79   int rndlife;
80   int minlife;
81   int delay;
82   float flash_fade;
83   unsigned char *palaka1;
84   unsigned char *palaka2;
85   XImage *xim;
86   XColor *colors;
87   int ncolors;
88 };
89
90 static int explode(struct state *st, fireshell *fs)
91 {
92   float air,adg = 0.001;         /* gravity */
93   unsigned int n,c;
94   unsigned int h = st->height;
95   unsigned int w = st->width;
96   unsigned int *prgb;
97   unsigned char *palaka = st->palaka1;
98   firepix *fp = fs->fpix;
99   if(fs->vgn){
100     if(--fs->cy == fs->shy){  
101       fs->vgn   = 0;
102       fs->flash = rnd(30000)+25000;
103       fs->flash = fs->flash*fs->flash; }
104     else{  
105       fs->flash = 200000+(fs->cy - fs->shy)*(fs->cy - fs->shy)*8;
106       prgb=(unsigned int *)(palaka + (fs->cy * w + fs->cx + rnd(5)-2)*4);
107      *prgb=(rnd(8)+8)*0x000f0f10;
108       return(1);}}    
109   if(fs->cshift) --fs->cshift;
110   if((fs->cshift+1)%50==0) fs->color = ~fs->color;
111   c = fs->color;
112   air = fs->air;
113   fs->flash *= st->flash_fade;
114   for(n=PIXCOUNT;n;n--){
115   if(fp->burn){ --fp->burn; 
116   if(fs->special){
117   fp->x += fp->xv = fp->xv * air + (float)(rnd(200)-100)/2000;
118   fp->y += fp->yv = fp->yv * air + (float)(rnd(200)-100)/2000+ adg;}
119   else{
120   fp->x += fp->xv = fp->xv * air + (float)(rnd(200)-100)/20000;
121   fp->y += fp->yv = fp->yv * air + (float)(rnd(200)-100)/40000+ adg; }
122   if(fp->y > h){
123   if(rnd(5)==3) {fp->yv *= -0.24; fp->y = h;}
124   else fp->burn=0;} /* touch muddy ground :) */
125   if(fp->x < w && fp->x > 0 && fp->y < h && fp->y > 0){
126      prgb = (unsigned int *)(palaka + ((int)fp->y * w + (int)fp->x)*4);
127     *prgb = c; }
128   } fp++;
129   } return(--fs->life); }
130
131 static void recycle(struct state *st, fireshell *fs,int x,int y)
132 {
133   unsigned int n,pixlife;
134   firepix *fp = fs->fpix;
135   fs->vgn = st->shoot;
136   fs->shy = y;
137   fs->cx = x;
138   fs->cy = st->shoot ? st->height : y ;
139   fs->color = (rnd(155)+100) <<16 |
140               (rnd(155)+100) <<8  |
141                rnd(255);
142   fs->life = rnd(st->rndlife)+st->minlife;
143   fs->air  = 1-(float)(rnd(200))/10000;
144   fs->flash   = rnd(30000)+25000;       /*  million jouls...              */
145   fs->flash   = fs->flash*fs->flash;
146   fs->cshift  = !rnd(5) ? 120:0; 
147   fs->special = !rnd(10) ? 1:0; 
148   if(st->verbose)
149   printf("recycle(): color = %x air = %f life = %d \n",fs->color,fs->air,fs->life);
150   pixlife = rnd(fs->life)+fs->life/10+1;    /* ! */
151   for(n=0;n<PIXCOUNT;n++){
152   fp->burn = rnd(pixlife)+32;
153   fp->xv = POWER*(float)(rnd(20000)-10000)/10000;
154   fp->yv = sqrt(POWER*POWER - fp->xv * fp->xv) *
155                (float)(rnd(20000)-10000)/10000;
156   fp->x = x;
157   fp->y = y; 
158   fp++;             }}
159
160 static void glow(struct state *st)
161 {
162   unsigned int n,q;
163   unsigned int w = st->width;
164   unsigned int h = st->height;
165   unsigned char *pa, *pb, *pm, *po;
166   if (!st->xim) return;
167   pm = st->palaka1;
168   po = st->palaka2;
169   for(n=0;n<w*4;n++) 
170   {pm[n]=0; po[n]=0;}    /* clean first line */
171   pm+=n; po+=n; h-=2; 
172   pa = pm-(w*4);
173   pb = pm+(w*4);
174   for(n=4;n<w*h*4-4;n++){
175   q    = pm[n-4] + (pm[n]*8) + pm[n+4] + 
176          pa[n-4] + pa[n] + pa[n+4] + 
177          pb[n-4] + pb[n] + pb[n+4];
178   q    -= q>8 ? 8:q;
179   pm[n] = q>>4;
180   q     = q>>3;
181   po[n] = q>255 ? 255 : q;}
182   pm+=n; po+=n;
183   for(n=0;n<w*4;n++)
184   {pm[n]=0; po[n]=0;}}   /* clean last line */
185
186 static void blur(struct state *st)
187 {
188   unsigned int n,q;
189   unsigned int w = st->width;
190   unsigned int h = st->height;
191   unsigned char *pa, *pb, *pm;
192   pm = st->palaka1;
193   pm += w*4; h-=2;  /* line 0&h */
194   pa = pm-(w*4);
195   pb = pm+(w*4);
196   for(n=4;n<w*h*4-4;n++){
197   q    = pm[n-4] + (pm[n]*8) + pm[n+4] + 
198          pa[n-4] + pa[n] + pa[n+4] + 
199          pb[n-4] + pb[n] + pb[n+4];
200   q    -= q>8 ? 8:q;
201   pm[n] = q>>4;}
202   pm += n-4;    /* last line */
203   for(n=0;n<w*4+4;n++) pm[n]=0;
204   pm = st->palaka1; /* first line */
205   for(n=0;n<w*4+4;n++) pm[n]=pm[n+w*4]; }
206
207 static void light_2x2(struct state *st, fireshell *fss)
208 {
209   unsigned int l,t,x,y;
210   float s;
211   int w = st->width;
212   int h = st->height;
213   unsigned char *dim = st->palaka2;
214   unsigned char *sim = st->palaka1;
215   int nl = w*4;
216   fireshell *f;
217   if(st->glow_on) sim=dim;
218   for(y=0;y<h;y+=2){
219   for(x=0;x<w;x+=2){
220   f = fss;
221
222 /* Note: The follwing loop is unrolled for speed.
223          check this before changing the value of SHELLCOUNT
224   s = 0;
225   for(n=SHELLCOUNT;n;n--,f++){
226   s += f->flash/(1+(f->cx - x)*(f->cx - x)+(f->cy - y)*(f->cy - y));} */
227
228   s  = f->flash/(1+(f->cx - x)*(f->cx - x)+(f->cy - y)*(f->cy - y));
229   f++;
230   s += f->flash/(1+(f->cx - x)*(f->cx - x)+(f->cy - y)*(f->cy - y));
231   f++;
232   s += f->flash/(1+(f->cx - x)*(f->cx - x)+(f->cy - y)*(f->cy - y));
233
234   l = sqrtf(s);
235
236   t = l + sim[0];
237   dim[0] = (t > 255 ? 255 : t); 
238   t = l + sim[1];
239   dim[1] = (t > 255 ? 255 : t);
240   t = l + sim[2];
241   dim[2] = (t > 255 ? 255 : t);
242
243   t = l + sim[4];
244   dim[4] = (t > 255 ? 255 : t);
245   t = l + sim[5];
246   dim[5] = (t > 255 ? 255 : t);
247   t = l + sim[6];
248   dim[6] = (t > 255 ? 255 : t);
249
250   t = l + sim[nl+0];
251   dim[nl+0] = (t > 255 ? 255 : t);
252   t = l + sim[nl+1];
253   dim[nl+1] = (t > 255 ? 255 : t);
254   t = l + sim[nl+2];
255   dim[nl+2] = (t > 255 ? 255 : t);
256
257   t = l + sim[nl+4];
258   dim[nl+4] = (t > 255 ? 255 : t);
259   t = l + sim[nl+5];
260   dim[nl+5] = (t > 255 ? 255 : t);
261   t = l + sim[nl+6];
262   dim[nl+6] = (t > 255 ? 255 : t);
263
264   sim += 8; dim += 8; } sim += nl; dim += nl;}}
265
266 static void resize(struct state *st)
267 {
268   XWindowAttributes xwa;
269   XGetWindowAttributes (st->dpy, st->window, &xwa);
270   xwa.width  -= xwa.width % 2;
271   xwa.height -= xwa.height % 2;
272   if(xwa.height != st->height || xwa.width != st->width) {
273   st->width  = xwa.width;
274   st->height = xwa.height;
275   if (st->verbose)
276   printf("sky size: %dx%d ------------------------------\n",st->width,st->height);
277   if (st->xim) {
278   if (st->xim->data==(char *)st->palaka2) st->xim->data=NULL;  
279   XDestroyImage(st->xim);
280   if (st->palaka2!=st->palaka1) free(st->palaka2 - 8);
281   free(st->palaka1 - 8); 
282   }
283   st->palaka1 = NULL;
284   st->palaka2 = NULL;
285   st->xim = XCreateImage(st->dpy, xwa.visual, xwa.depth, ZPixmap, 0, 0,
286                      st->width, st->height, 8, 0);
287   if (!st->xim) return;
288   st->palaka1 = (unsigned char *) calloc(st->xim->height+1,st->xim->width*4) + 8;
289   if(st->flash_on|st->glow_on)
290   st->palaka2 = (unsigned char *) calloc(st->xim->height+1,st->xim->width*4) + 8;
291   else
292   st->palaka2 = st->palaka1;
293   if (st->depth>=24)
294   st->xim->data = (char *)st->palaka2;
295   else
296   st->xim->data = calloc(st->xim->height,st->xim->bytes_per_line); }}
297
298 static void put_image(struct state *st)
299 {
300   int x,y,i,j;
301   unsigned char r, g, b;
302   if (!st->xim) return;
303   i = 0;
304   j = 0;
305   if (st->depth==16) {
306      if(st->bigendian)
307      for (y=0;y<st->xim->height; y++)
308      for (x=0;x<st->xim->width; x++) {
309      r = st->palaka2[j++];
310      g = st->palaka2[j++];
311      b = st->palaka2[j++];
312      j++;
313      st->xim->data[i++] = (g&224)>>5 | (r&248);
314      st->xim->data[i++] = (b&248)>>3 | (g&28)<<3;
315      }
316      else
317      for (y=0;y<st->xim->height; y++)
318      for (x=0;x<st->xim->width; x++) {
319      r = st->palaka2[j++];
320      g = st->palaka2[j++];
321      b = st->palaka2[j++];
322      j++;
323      st->xim->data[i++] = (b&248)>>3 | (g&28)<<3;
324      st->xim->data[i++] = (g&224)>>5 | (r&248);
325      }
326   }
327   if (st->depth==15) {
328      if(st->bigendian)
329      for (y=0;y<st->xim->height; y++)
330      for (x=0;x<st->xim->width; x++) {
331      r = st->palaka2[j++];
332      g = st->palaka2[j++];
333      b = st->palaka2[j++];
334      j++;
335      st->xim->data[i++] = (g&192)>>6 | (r&248)>>1;
336      st->xim->data[i++] = (b&248)>>3 | (g&56)<<2;
337      }
338      else
339      for (y=0;y<st->xim->height; y++)
340      for (x=0;x<st->xim->width; x++) {
341      r = st->palaka2[j++];
342      g = st->palaka2[j++];
343      b = st->palaka2[j++];
344      j++;
345      st->xim->data[i++] = (b&248)>>3 | (g&56)<<2;
346      st->xim->data[i++] = (g&192)>>6 | (r&248)>>1;
347      }
348   }
349   if (st->depth==8) {
350      for (y=0;y<st->xim->height; y++)
351      for (x=0;x<st->xim->width; x++) {
352      r = st->palaka2[j++];
353      g = st->palaka2[j++];
354      b = st->palaka2[j++];
355      j++;     
356      st->xim->data[i++] = (((7*g)/256)*36)+(((6*r)/256)*6)+((6*b)/256);
357      }
358   }
359   XPutImage(st->dpy,st->window,st->gc,st->xim,0,0,0,0,st->xim->width,st->xim->height); }
360
361
362 static void *
363 fireworkx_init (Display *dpy, Window win)
364 {
365   struct state *st = (struct state *) calloc (1, sizeof(*st));
366   unsigned int n;
367   Visual *vi;
368   Colormap cmap;
369   Bool writable;
370   XWindowAttributes xwa;
371   XGCValues gcv;
372   firepix *fpix, *ffpix;
373
374   st->dpy = dpy;
375   st->window = win;
376
377   st->glow_on  = get_boolean_resource(st->dpy, "glow"    , "Boolean");
378   st->flash_on = get_boolean_resource(st->dpy, "flash"   , "Boolean");
379   st->shoot    = get_boolean_resource(st->dpy, "shoot"   , "Boolean");
380   st->verbose  = get_boolean_resource(st->dpy, "verbose" , "Boolean");
381   st->rndlife  = get_integer_resource(st->dpy, "maxlife" , "Integer");
382   st->delay    = get_integer_resource(st->dpy, "delay"   , "Integer");
383   st->minlife  = st->rndlife/4;
384   st->flash_fade=0.98;
385   if(st->rndlife < 1000) st->flash_fade=0.96;
386   if(st->rndlife <  500) st->flash_fade=0.94;
387   if(st->verbose){
388   printf("Fireworkx %s - pyrotechnics simulation program \n", FWXVERSION);
389   printf("Copyright (c) 1999-2007 Rony B Chandran <ronybc@asia.com> \n\n");
390   printf("url: http://www.ronybc.8k.com \n\n");}
391
392   XGetWindowAttributes(st->dpy,win,&xwa);
393   st->depth = xwa.depth;
394   vi        = xwa.visual;
395   cmap      = xwa.colormap;
396   st->bigendian = (ImageByteOrder(st->dpy) == MSBFirst);
397  
398   if(st->depth==8){
399   if(st->verbose){
400   printf("Pseudocolor color: use '-no-flash' for better results.\n");}
401   st->colors = (XColor *) calloc(sizeof(XColor),st->ncolors+1);
402   writable = False;
403   make_smooth_colormap(st->dpy, vi, cmap, st->colors, &st->ncolors,
404                                 False, &writable, True);
405   }
406   st->gc = XCreateGC(st->dpy, win, 0, &gcv);
407
408   resize(st);   /* initialize palakas */ 
409   
410   ffpix = malloc(sizeof(firepix) * PIXCOUNT * SHELLCOUNT);
411   st->ffshell = malloc(sizeof(fireshell) * SHELLCOUNT);
412   st->fshell = st->ffshell;
413   fpix = ffpix;
414   for (n=0;n<SHELLCOUNT;n++){
415   st->fshell->fpix = fpix;
416   recycle (st, st->fshell,rnd(st->width),rnd(st->height));
417   st->fshell++; 
418   fpix += PIXCOUNT; }
419   
420   return st;
421 }
422
423
424 static unsigned long
425 fireworkx_draw (Display *dpy, Window win, void *closure)
426 {
427   struct state *st = (struct state *) closure;
428   int q,n;
429   for(q=FTWEAK;q;q--){
430     st->fshell=st->ffshell;
431     for(n=SHELLCOUNT;n;n--){
432       if (!explode(st, st->fshell)){
433         recycle(st, st->fshell,rnd(st->width),rnd(st->height)); }
434       st->fshell++; }}
435 #if HAVE_X86_MMX
436   if(st->glow_on) mmx_glow(st->palaka1,st->width,st->height,8,st->palaka2);
437 #else
438   if(st->glow_on) glow(st);
439 #endif
440   if(st->flash_on) light_2x2(st, st->ffshell);
441   put_image(st);
442 #if HAVE_X86_MMX
443   if(!st->glow_on) mmx_blur(st->palaka1,st->width,st->height,8);
444 #else
445   if(!st->glow_on) blur(st);
446 #endif
447
448   return st->delay;
449 }
450
451
452 static void
453 fireworkx_reshape (Display *dpy, Window window, void *closure, 
454                  unsigned int w, unsigned int h)
455 {
456   struct state *st = (struct state *) closure;
457   resize(st);
458 }
459
460 static Bool
461 fireworkx_event (Display *dpy, Window window, void *closure, XEvent *event)
462 {
463   struct state *st = (struct state *) closure;
464   if (event->type == ButtonPress) {
465     recycle(st, st->ffshell, event->xbutton.x, event->xbutton.y);
466     return True;
467   }
468   return False;
469 }
470
471 static void
472 fireworkx_free (Display *dpy, Window window, void *closure)
473 {
474 }
475
476
477 static const char *fireworkx_defaults [] = {
478   ".background: black",
479   ".foreground: white",
480   "*delay:      20000",  /* never default to zero! */
481   "*maxlife:    2000",
482   "*flash:      True",
483   "*glow:       True",
484   "*shoot:      False",
485   "*verbose:    False",
486   0
487 };
488
489 static XrmOptionDescRec fireworkx_options [] = {
490   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
491   { "-maxlife",         ".maxlife",     XrmoptionSepArg, 0 },
492   { "-no-flash",        ".flash",       XrmoptionNoArg, "False" },
493   { "-no-glow",         ".glow",        XrmoptionNoArg, "False" },
494   { "-shoot",           ".shoot",       XrmoptionNoArg, "True" },
495   { "-verbose",         ".verbose",     XrmoptionNoArg, "True" },
496   { 0, 0, 0, 0 }
497 };
498
499 XSCREENSAVER_MODULE ("Fireworkx", fireworkx)