From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / fireworkx.c
1 /*
2  Fireworkx 2.2 - Pyrotechnic explosions simulation,
3  an eyecandy, live animating colorful fireworks super-blasts..!
4  Copyright (GPL) 1999-2013 Rony B Chandran <ronybc@gmail.com>
5
6  From Kerala, INDIA
7  Website: http://www.ronybc.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  2004-OCT: ronybc: Landed on Xscreensaver..!
18  2012-DEC: ronybc: Almost rewrite of the last version (>4 years old)
19            with SSE2 optimization, colored light flashes,
20            HSV color and many visual and speed improvements.
21
22  Additional coding:
23  ---------------------------------------------------------------------------------
24  Support for different display color modes: put_image()
25  Jean-Pierre Demailly <Jean-Pierre.Demailly@ujf-grenoble.fr>
26
27  Fixed array access problems by beating on it with a large hammer.
28  Nicholas Miell <nmiell@gmail.com>
29
30  Help 'free'ing up of memory with needed 'XSync's.
31  Renuka S <renuka@ronybc.com>
32  Rugmini R Chandran <rugmini@ronybc.com>
33 \
34  */
35
36 #include "screenhack.h"
37
38 #ifdef __SSE2__
39 # include <emmintrin.h>
40 #endif
41
42 #define FWXVERSION "2.2"
43
44 #define WIDTH 1024                /* 888     */
45 #define HEIGHT 632                /* 548     */
46 #define SHELLCOUNT 4              /* FIXED NUMBER; for SSE optimization */
47 #define PIXCOUNT 500              /* 500     */
48 #define SHELL_LIFE_DEFAULT 32     /* 32      */
49 #define SHELL_LIFE_RATIO 6        /* 6       */
50 #define POWDER 5.0                /* 5.0     */
51 #define FTWEAK 12                 /* 12      */
52 #define FLASH_ZOOM 0.8            /* 1.0     */
53 #define G_ACCELERATION 0.001      /* GRAVITY */
54
55 typedef struct
56 {
57         unsigned int burn;
58         float x, y;
59         float xv, yv;
60 } firepix;
61
62 typedef struct
63 {
64         unsigned int cx, cy;
65         unsigned int seq_number, life;
66         unsigned int bicolor, flies, hshift, vshift;
67         unsigned int mortar_fired, explode_y;
68         float air_drag, vshift_phase;
69         float flash_r, flash_g, flash_b;
70         unsigned int h;
71         double s, v;
72         unsigned char r, g, b;
73         firepix *fpix;
74 } fireshell;
75
76 struct state
77 {
78         unsigned int fps_on;
79         unsigned int flash_on;
80         unsigned int shoot;
81         unsigned int verbose;
82         unsigned int width;
83         unsigned int height;
84         unsigned int fullscreen;
85         unsigned int max_shell_life;
86         unsigned int delay;
87         float flash_fade;
88         float *light_map;
89         unsigned char *palaka1;
90         unsigned char *palaka2;
91         void *mem1;
92         void *mem2;
93         fireshell *fireshell_array;
94
95         Display *dpy;
96         Window window;
97         XImage *xim;
98         GC gc;
99         XColor *colors;
100         int depth;
101         int bigendian;
102         int ncolors;
103         Bool button_down_p;
104         int deferred;
105
106 };
107
108 /*
109         will return zero.. divide with care.
110 */
111 static unsigned int rnd(unsigned int x)
112 {
113         return(random() % x);
114 }
115
116 static void fs_roll_rgb(fireshell *fs)
117 {
118         unsigned short r, g, b;
119         hsv_to_rgb (fs->h, fs->s, fs->v, &r, &g, &b);
120         fs->r = (unsigned char) (r >> 8);
121         fs->g = (unsigned char) (g >> 8);
122         fs->b = (unsigned char) (b >> 8);
123 }
124
125 static void mix_colors(fireshell *fs)
126 {
127         float flash;
128         fs->h = rnd(360);
129         fs->s = frand(0.4) + 0.6;
130         fs->v = 1.0;
131         fs_roll_rgb(fs);
132
133         flash = rnd(444) + 111; /* Mega Jouls ! */
134         fs->flash_r = fs->r * flash;
135         fs->flash_g = fs->g * flash;
136         fs->flash_b = fs->b * flash;
137 }
138
139 static void render_light_map(struct state *st, fireshell *fs)
140 {
141         signed int x, y, v = 0;
142         for (y = 0, v = fs->seq_number; y < st->height; y += 2)
143         {
144                 for (x = 0; x < st->width; x += 2, v += SHELLCOUNT)
145                 {
146                         double f;
147                         f = sqrt((fs->cx - x) * (fs->cx - x) + (fs->cy - y) * (fs->cy - y)) + 4.0;
148                         f = FLASH_ZOOM / f;
149                         f += pow(f,0.1) * frand(0.0001); /* dither */
150                         st->light_map[v] = f;
151                 }
152         }
153 }
154
155 static void recycle(struct state *st, fireshell *fs, unsigned int x, unsigned int y)
156 {
157         unsigned int n, pixlife;
158         firepix *fp = fs->fpix;
159         fs->mortar_fired = st->shoot;
160         fs->explode_y = y;
161         fs->cx = x;
162         fs->cy = st->shoot ? st->height : y ;
163         fs->life = rnd(st->max_shell_life) + (st->max_shell_life/SHELL_LIFE_RATIO);
164         fs->life += !rnd(25) ? st->max_shell_life * 5 : 0;
165         fs->air_drag = 1.0 - (float)(rnd(200)) / (10000.0 + fs->life);
166         fs->bicolor = !rnd(5) ? 120 : 0;
167         fs->flies = !rnd(10) ? 1 : 0; /* flies' motion */
168         fs->hshift = !rnd(5) ? 1 : 0; /* hue shifting  */
169         fs->vshift = !rnd(10) ? 1 : 0; /* value shifting */
170         fs->vshift_phase = M_PI/2.0;
171         pixlife = rnd(fs->life) + fs->life / 10 + 1;    /* ! */
172         for (n = 0; n < PIXCOUNT; n++)
173         {
174                 fp->burn = rnd(pixlife) + 32;
175                 fp->xv = frand(2.0) * POWDER - POWDER;
176                 fp->yv = sqrt(POWDER * POWDER - fp->xv * fp->xv) * (frand(2.0) - 1.0);
177                 fp->x = x;
178                 fp->y = y;
179                 fp++;
180         }
181         mix_colors(fs);
182         render_light_map(st, fs);
183 }
184
185 static void recycle_oldest(struct state *st, unsigned int x, unsigned int y)
186 {
187         unsigned int n;
188         fireshell *fs, *oldest;
189         fs = oldest = st->fireshell_array;
190         for (n = 0; n < SHELLCOUNT; n++)
191         {
192                 if(fs[n].life < oldest->life) oldest = &fs[n];
193         }
194         recycle(st, oldest, x, y);
195 }
196
197 static void rotate_hue(fireshell *fs, int dh)
198 {
199         fs->h = fs->h + dh;
200         fs->s = fs->s - 0.001;
201         fs_roll_rgb(fs);
202 }
203
204 static void wave_value(fireshell *fs)
205 {
206         fs->vshift_phase = fs->vshift_phase + 0.008;
207         fs->v = fabs(sin(fs->vshift_phase));
208         fs_roll_rgb(fs);
209 }
210
211 static int explode(struct state *st, fireshell *fs)
212 {
213         float air_drag;
214         unsigned int n;
215         unsigned int h = st->height;
216         unsigned int w = st->width;
217         unsigned char r, g, b;
218         unsigned char *prgba;
219         unsigned char *palaka = st->palaka1;
220         firepix *fp = fs->fpix;
221         if (fs->mortar_fired)
222         {
223                 if (--fs->cy == fs->explode_y)
224                 {
225                         fs->mortar_fired = 0;
226                         mix_colors(fs);
227                         render_light_map(st, fs);
228                 }
229                 else
230                 {
231                         fs->flash_r =
232                             fs->flash_g =
233                                 fs->flash_b = 50 + (fs->cy - fs->explode_y) * 10;
234                         prgba = palaka + (fs->cy * w + fs->cx + rnd(5) - 2) * 4;
235                         prgba[0] = (rnd(32) + 128);
236                         prgba[1] = (rnd(32) + 128);
237                         prgba[2] = (rnd(32) + 128);
238                         return(1);
239                 }
240         }
241         if ((fs->bicolor + 1) % 50 == 0) rotate_hue(fs, 180);
242         if (fs->bicolor) --fs->bicolor;
243         if (fs->hshift) rotate_hue(fs, rnd(8));
244         if (fs->vshift) wave_value(fs);
245         if (fs->flash_r > 1.0) fs->flash_r *= st->flash_fade;
246         if (fs->flash_g > 1.0) fs->flash_g *= st->flash_fade;
247         if (fs->flash_b > 1.0) fs->flash_b *= st->flash_fade;
248         air_drag = fs->air_drag;
249         r = fs->r;
250         g = fs->g;
251         b = fs->b;
252         for (n = 0; n < PIXCOUNT; n++, fp++)
253         {
254                 if (!fp->burn) continue;
255                 --fp->burn;
256                 if (fs->flies)
257                 {
258                         fp->x += fp->xv = fp->xv * air_drag + frand(0.1) - 0.05;
259                         fp->y += fp->yv = fp->yv * air_drag + frand(0.1) - 0.05 + G_ACCELERATION;
260                 }
261                 else
262                 {
263                         fp->x += fp->xv = fp->xv * air_drag + frand(0.01) - 0.005;
264                         fp->y += fp->yv = fp->yv * air_drag + frand(0.005) - 0.0025 + G_ACCELERATION;
265                 }
266                 if (fp->y > h)
267                 {
268                         if (rnd(5) == 3)
269                         {
270                                 fp->yv *= -0.24;
271                                 fp->y = h;
272                         }
273                         /* touch muddy ground :) */
274                         else fp->burn = 0;
275                 }
276                 if (fp->x < w && fp->x > 0 && fp->y < h && fp->y > 0)
277                 {
278                         prgba = palaka + ((int)fp->y * w + (int)fp->x) * 4;
279                         prgba[0] = b;
280                         prgba[1] = g;
281                         prgba[2] = r;
282                 }
283         }
284         return(--fs->life);
285 }
286
287 #ifdef __SSE2__
288
289 /* SSE2 optimized versions of glow_blur() and chromo_2x2_light() */
290
291 static void glow_blur(struct state *st)
292 {
293         unsigned int n, nn;
294         unsigned char *ps = st->palaka1;
295         unsigned char *pd = st->palaka2;
296         unsigned char *pa = st->palaka1 - (st->width * 4);
297         unsigned char *pb = st->palaka1 + (st->width * 4);
298         __m128i xmm0, xmm1, xmm2, xmm3, xmm4;
299
300         xmm0 = _mm_setzero_si128();
301         nn = st->width * st->height * 4;
302         for (n = 0; n < nn; n+=16)
303         {
304                 _mm_prefetch((const void *)&ps[n+16],_MM_HINT_T0);
305                 _mm_prefetch((const void *)&pa[n+16],_MM_HINT_T0);
306                 _mm_prefetch((const void *)&pb[n+16],_MM_HINT_T0);
307
308                 xmm1 = _mm_load_si128((const __m128i*)&ps[n]);
309                 xmm2 = xmm1;
310                 xmm1 = _mm_unpacklo_epi8(xmm1,xmm0);
311                 xmm2 = _mm_unpackhi_epi8(xmm2,xmm0);
312                 xmm3 = _mm_loadu_si128((const __m128i*)&ps[n+4]);
313                 xmm4 = xmm3;
314                 xmm3 = _mm_unpacklo_epi8(xmm3,xmm0);
315                 xmm4 = _mm_unpackhi_epi8(xmm4,xmm0);
316                 xmm3 = _mm_slli_epi16(xmm3,3);
317                 xmm4 = _mm_slli_epi16(xmm4,3);
318                 xmm1 = _mm_add_epi16(xmm1,xmm3);
319                 xmm2 = _mm_add_epi16(xmm2,xmm4);
320                 xmm3 = _mm_loadu_si128((const __m128i*)&ps[n+8]);
321                 xmm4 = xmm3;
322                 xmm3 = _mm_unpacklo_epi8(xmm3,xmm0);
323                 xmm4 = _mm_unpackhi_epi8(xmm4,xmm0);
324                 xmm1 = _mm_add_epi16(xmm1,xmm3);
325                 xmm2 = _mm_add_epi16(xmm2,xmm4);
326
327                 xmm3 = _mm_load_si128((const __m128i*)&pa[n]);
328                 xmm4 = xmm3;
329                 xmm3 = _mm_unpacklo_epi8(xmm3,xmm0);
330                 xmm4 = _mm_unpackhi_epi8(xmm4,xmm0);
331                 xmm1 = _mm_add_epi16(xmm1,xmm3);
332                 xmm2 = _mm_add_epi16(xmm2,xmm4);
333                 xmm3 = _mm_loadu_si128((const __m128i*)&pa[n+4]);
334                 xmm4 = xmm3;
335                 xmm3 = _mm_unpacklo_epi8(xmm3,xmm0);
336                 xmm4 = _mm_unpackhi_epi8(xmm4,xmm0);
337                 xmm1 = _mm_add_epi16(xmm1,xmm3);
338                 xmm2 = _mm_add_epi16(xmm2,xmm4);
339                 xmm3 = _mm_loadu_si128((const __m128i*)&pa[n+8]);
340                 xmm4 = xmm3;
341                 xmm3 = _mm_unpacklo_epi8(xmm3,xmm0);
342                 xmm4 = _mm_unpackhi_epi8(xmm4,xmm0);
343                 xmm1 = _mm_add_epi16(xmm1,xmm3);
344                 xmm2 = _mm_add_epi16(xmm2,xmm4);
345
346                 xmm3 = _mm_load_si128((const __m128i*)&pb[n]);
347                 xmm4 = xmm3;
348                 xmm3 = _mm_unpacklo_epi8(xmm3,xmm0);
349                 xmm4 = _mm_unpackhi_epi8(xmm4,xmm0);
350                 xmm1 = _mm_add_epi16(xmm1,xmm3);
351                 xmm2 = _mm_add_epi16(xmm2,xmm4);
352                 xmm3 = _mm_loadu_si128((const __m128i*)&pb[n+4]);
353                 xmm4 = xmm3;
354                 xmm3 = _mm_unpacklo_epi8(xmm3,xmm0);
355                 xmm4 = _mm_unpackhi_epi8(xmm4,xmm0);
356                 xmm1 = _mm_add_epi16(xmm1,xmm3);
357                 xmm2 = _mm_add_epi16(xmm2,xmm4);
358                 xmm3 = _mm_loadu_si128((const __m128i*)&pb[n+8]);
359                 xmm4 = xmm3;
360                 xmm3 = _mm_unpacklo_epi8(xmm3,xmm0);
361                 xmm4 = _mm_unpackhi_epi8(xmm4,xmm0);
362                 xmm1 = _mm_add_epi16(xmm1,xmm3);
363                 xmm2 = _mm_add_epi16(xmm2,xmm4);
364
365                 xmm3 = xmm1;
366                 xmm4 = xmm2;
367                 xmm1 = _mm_srli_epi16(xmm1,4);
368                 xmm2 = _mm_srli_epi16(xmm2,4);
369                 xmm3 = _mm_srli_epi16(xmm3,3);
370                 xmm4 = _mm_srli_epi16(xmm4,3);
371                 xmm1 = _mm_packus_epi16(xmm1,xmm2);
372                 xmm3 = _mm_packus_epi16(xmm3,xmm4);
373
374                 _mm_storeu_si128((__m128i*)&ps[n+4], xmm1);
375                 _mm_storeu_si128((__m128i*)&pd[n+4], xmm3);
376         }
377 }
378
379 static void chromo_2x2_light(struct state *st)
380 {
381         __m128 xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6;
382         __m128i xmi4, xmi5, xmi6, xmi7;
383
384         unsigned int x, y, v = 0;
385         unsigned int nl = st->width * 4;
386         unsigned char *mem = st->palaka2;
387         fireshell *fs = st->fireshell_array;
388
389         xmm0 = _mm_setr_ps(fs[0].flash_b, fs[0].flash_g, fs[0].flash_r, 0.0);
390         xmm1 = _mm_setr_ps(fs[1].flash_b, fs[1].flash_g, fs[1].flash_r, 0.0);
391         xmm2 = _mm_setr_ps(fs[2].flash_b, fs[2].flash_g, fs[2].flash_r, 0.0);
392         xmm3 = _mm_setr_ps(fs[3].flash_b, fs[3].flash_g, fs[3].flash_r, 0.0);
393
394         for (y = st->height/2; y; y--, mem += nl)
395         {
396                 for (x = st->width/4; x; x--, v += 8, mem += 16)
397                 {
398                         xmm4 = _mm_set1_ps(st->light_map[v+0]);
399                         xmm5 = xmm0;
400                         xmm5 = _mm_mul_ps(xmm5,xmm4);
401                         xmm4 = _mm_set1_ps(st->light_map[v+1]);
402                         xmm4 = _mm_mul_ps(xmm4,xmm1);
403                         xmm5 = _mm_add_ps(xmm5,xmm4);
404                         xmm4 = _mm_set1_ps(st->light_map[v+2]);
405                         xmm4 = _mm_mul_ps(xmm4,xmm2);
406                         xmm5 = _mm_add_ps(xmm5,xmm4);
407                         xmm4 = _mm_set1_ps(st->light_map[v+3]);
408                         xmm4 = _mm_mul_ps(xmm4,xmm3);
409                         xmm5 = _mm_add_ps(xmm5,xmm4);
410
411                         xmm4 = _mm_set1_ps(st->light_map[v+4]);
412                         xmm6 = xmm0;
413                         xmm6 = _mm_mul_ps(xmm6,xmm4);
414                         xmm4 = _mm_set1_ps(st->light_map[v+5]);
415                         xmm4 = _mm_mul_ps(xmm4,xmm1);
416                         xmm6 = _mm_add_ps(xmm6,xmm4);
417                         xmm4 = _mm_set1_ps(st->light_map[v+6]);
418                         xmm4 = _mm_mul_ps(xmm4,xmm2);
419                         xmm6 = _mm_add_ps(xmm6,xmm4);
420                         xmm4 = _mm_set1_ps(st->light_map[v+7]);
421                         xmm4 = _mm_mul_ps(xmm4,xmm3);
422                         xmm6 = _mm_add_ps(xmm6,xmm4);
423
424                         xmi6 = _mm_cvtps_epi32(xmm5);
425                         xmi7 = _mm_cvtps_epi32(xmm6);
426                         xmi6 = _mm_packs_epi32(xmi6,xmi6);
427                         xmi7 = _mm_packs_epi32(xmi7,xmi7);
428
429                         xmi4 = _mm_load_si128((const __m128i*) mem);
430                         xmi5 = _mm_unpacklo_epi8(xmi5,xmi4);
431                         xmi5 = _mm_srli_epi16(xmi5,8);
432                         xmi4 = _mm_unpackhi_epi8(xmi4,xmi4);
433                         xmi4 = _mm_srli_epi16(xmi4,8);
434                         xmi5 = _mm_add_epi16(xmi5,xmi6);
435                         xmi4 = _mm_add_epi16(xmi4,xmi7);
436                         xmi5 = _mm_packus_epi16(xmi5,xmi4);
437                         _mm_store_si128((__m128i*) mem, xmi5);
438
439                         xmi4 = _mm_load_si128((const __m128i*) &mem[nl]);
440                         xmi5 = _mm_unpacklo_epi8(xmi5,xmi4);
441                         xmi5 = _mm_srli_epi16(xmi5,8);
442                         xmi4 = _mm_unpackhi_epi8(xmi4,xmi4);
443                         xmi4 = _mm_srli_epi16(xmi4,8);
444                         xmi5 = _mm_add_epi16(xmi5,xmi6);
445                         xmi4 = _mm_add_epi16(xmi4,xmi7);
446                         xmi5 = _mm_packus_epi16(xmi5,xmi4);
447                         _mm_store_si128((__m128i*) &mem[nl], xmi5);
448                 }
449         }
450 }
451
452 #else
453
454 static void glow_blur(struct state *st)
455 {
456         unsigned int n, q;
457         unsigned char *pm = st->palaka1;
458         unsigned char *po = st->palaka2;
459         unsigned char *pa = pm - (st->width * 4);
460         unsigned char *pb = pm + (st->width * 4);
461         /*
462                 unsigned int rgba = 0;
463                 for (n = st->width*st->height*4; n; n--, pm++, pa++, pb++, po++)
464                 {
465                         if(++rgba > 3)
466                         {
467                                 rgba = 0;
468                                 continue;
469                         }
470                         q     = pm[0] + pm[4] * 8 + pm[8] +
471                                 pa[0] + pa[4] + pa[8] +
472                                 pb[0] + pb[4] + pb[8];
473                         pm[4] = q >> 4;
474                         po[4] = q > 2047 ? 255 : q >> 3;
475                 }
476                         --- using unrolled version ------------
477         */
478         for (n = st->width*st->height*4; n; n-=4)
479         {
480                 q = pm[0] + pm[4] * 8 + pm[8] +
481                     pa[0] + pa[4] + pa[8] +
482                     pb[0] + pb[4] + pb[8];
483                 pm[4] = q >> 4;
484                 po[4] = q > 2047 ? 255 : q >> 3;
485                 q = pm[1] + pm[5] * 8 + pm[9] +
486                     pa[1] + pa[5] + pa[9] +
487                     pb[1] + pb[5] + pb[9];
488                 pm[5] = q >> 4;
489                 po[5] = q > 2047 ? 255 : q >> 3;
490                 q = pm[2] + pm[6] * 8 + pm[10] +
491                     pa[2] + pa[6] + pa[10] +
492                     pb[2] + pb[6] + pb[10];
493                 pm[6] = q >> 4;
494                 po[6] = q > 2047 ? 255 : q >> 3;
495
496                 pm+=4, pa+=4, pb+=4, po+=4;
497         }
498 }
499
500 static inline unsigned char addbs(unsigned char c, unsigned int i)
501 {
502         i += c;
503         return(i > 255 ? 255 : i);
504 }
505
506 static void chromo_2x2_light(struct state *st)
507 {
508         unsigned int n, x, y, v = 0;
509         unsigned int nl = st->width * 4;
510         unsigned char *mem = st->palaka2;
511         float r, g, b;
512         float rgb[SHELLCOUNT*4];
513         fireshell *fs = st->fireshell_array;
514
515         for (n = 0, x = 0; n < SHELLCOUNT; n++, x += 4, fs++)
516         {
517                 rgb[x  ] = fs->flash_r;
518                 rgb[x+1] = fs->flash_g;
519                 rgb[x+2] = fs->flash_b;
520         }
521
522         for (y = st->height/2; y; y--)
523         {
524                 for (x = st->width/2; x; x--, v += 4)
525                 {
526                         r = rgb[0] * st->light_map[v] + rgb[4] * st->light_map[v+1]
527                             + rgb[ 8] * st->light_map[v+2] + rgb[12] * st->light_map[v+3];
528                         g = rgb[1] * st->light_map[v] + rgb[5] * st->light_map[v+1]
529                             + rgb[ 9] * st->light_map[v+2] + rgb[13] * st->light_map[v+3];
530                         b = rgb[2] * st->light_map[v] + rgb[6] * st->light_map[v+1]
531                             + rgb[10] * st->light_map[v+2] + rgb[14] * st->light_map[v+3];
532
533                         mem[0] = addbs(mem[0], b);
534                         mem[1] = addbs(mem[1], g);
535                         mem[2] = addbs(mem[2], r);
536                         mem[4] = addbs(mem[4], b);
537                         mem[5] = addbs(mem[5], g);
538                         mem[6] = addbs(mem[6], r);
539
540                         mem += nl;
541
542                         mem[0] = addbs(mem[0], b);
543                         mem[1] = addbs(mem[1], g);
544                         mem[2] = addbs(mem[2], r);
545                         mem[4] = addbs(mem[4], b);
546                         mem[5] = addbs(mem[5], g);
547                         mem[6] = addbs(mem[6], r);
548
549                         mem -= nl - 8;
550                 }
551                 mem += nl;
552         }
553 }
554
555 #endif
556
557 static void resize(struct state *st)
558 {
559         unsigned int n;
560         fireshell *fs = st->fireshell_array;
561         XWindowAttributes xwa;
562         XGetWindowAttributes (st->dpy, st->window, &xwa);
563         xwa.width  -= xwa.width % 4;
564         xwa.height -= xwa.height % 2;
565         st->width  = xwa.width;
566         st->height = xwa.height;
567         if (st->verbose)
568         {
569                 printf("resolution: %d x %d \n",st->width,st->height);
570         }
571         XSync(st->dpy, 0);
572         if (st->xim)
573         {
574                 if (st->xim->data == (char *)st->palaka2) st->xim->data = NULL;
575                 XDestroyImage(st->xim);
576                 XSync(st->dpy, 0);
577                 free(st->mem2);
578                 free(st->mem1);
579         }
580         st->xim = XCreateImage(st->dpy, xwa.visual, xwa.depth, ZPixmap, 0, 0,
581                                st->width, st->height, 8, 0);
582         if (!st->xim) return;
583
584 #ifdef __SSE2___ABANDONED /* causes __ERROR_use_memset_not_bzero_in_xscreensaver__ */
585         st->mem1 = _mm_malloc(((st->height + 2) * st->width + 8)*4, 16);
586         bzero(st->mem1, ((st->height + 2) * st->width + 8)*4);
587         st->mem2 = _mm_malloc(((st->height + 2) * st->width + 8)*4, 16);
588         bzero(st->mem2, ((st->height + 2) * st->width + 8)*4);
589 #else
590         st->mem1 = calloc((st->height + 2) * st->width + 8, 4);
591         st->mem2 = calloc((st->height + 2) * st->width + 8, 4);
592 #endif
593         st->palaka1 = (unsigned char *) st->mem1 + (st->width * 4 + 16);
594         st->palaka2 = (unsigned char *) st->mem2 + (st->width * 4 + 16);
595
596         if (xwa.depth >= 24)
597         {
598                 st->xim->data = (char *)st->palaka2;
599         }
600         else
601         {
602                 st->xim->data = calloc(st->height, st->xim->bytes_per_line);
603         }
604
605         if (st->light_map) free(st->light_map);
606         st->light_map = calloc((st->width * st->height * SHELLCOUNT)/4, sizeof(float));
607         for (n = 0; n < SHELLCOUNT; n++, fs++)
608         {
609                 render_light_map(st, fs);
610         }
611 }
612
613 static void put_image(struct state *st)
614 {
615         int x,y,i,j;
616         unsigned char r, g, b;
617         if (!st->xim) return;
618         i = 0;
619         j = 0;
620         if (st->depth==16)
621         {
622                 if(st->bigendian)
623                         for (y=0; y<st->xim->height; y++)
624                                 for (x=0; x<st->xim->width; x++)
625                                 {
626                                         r = st->palaka2[j++];
627                                         g = st->palaka2[j++];
628                                         b = st->palaka2[j++];
629                                         j++;
630                                         st->xim->data[i++] = (g&224)>>5 | (r&248);
631                                         st->xim->data[i++] = (b&248)>>3 | (g&28)<<3;
632                                 }
633                 else
634                         for (y=0; y<st->xim->height; y++)
635                                 for (x=0; x<st->xim->width; x++)
636                                 {
637                                         r = st->palaka2[j++];
638                                         g = st->palaka2[j++];
639                                         b = st->palaka2[j++];
640                                         j++;
641                                         st->xim->data[i++] = (b&248)>>3 | (g&28)<<3;
642                                         st->xim->data[i++] = (g&224)>>5 | (r&248);
643                                 }
644         }
645         if (st->depth==15)
646         {
647                 if(st->bigendian)
648                         for (y=0; y<st->xim->height; y++)
649                                 for (x=0; x<st->xim->width; x++)
650                                 {
651                                         r = st->palaka2[j++];
652                                         g = st->palaka2[j++];
653                                         b = st->palaka2[j++];
654                                         j++;
655                                         st->xim->data[i++] = (g&192)>>6 | (r&248)>>1;
656                                         st->xim->data[i++] = (b&248)>>3 | (g&56)<<2;
657                                 }
658                 else
659                         for (y=0; y<st->xim->height; y++)
660                                 for (x=0; x<st->xim->width; x++)
661                                 {
662                                         r = st->palaka2[j++];
663                                         g = st->palaka2[j++];
664                                         b = st->palaka2[j++];
665                                         j++;
666                                         st->xim->data[i++] = (b&248)>>3 | (g&56)<<2;
667                                         st->xim->data[i++] = (g&192)>>6 | (r&248)>>1;
668                                 }
669         }
670         if (st->depth==8)
671         {
672                 for (y=0; y<st->xim->height; y++)
673                         for (x=0; x<st->xim->width; x++)
674                         {
675                                 r = st->palaka2[j++];
676                                 g = st->palaka2[j++];
677                                 b = st->palaka2[j++];
678                                 j++;
679                                 st->xim->data[i++] = (((7*g)/256)*36)+(((6*r)/256)*6)+((6*b)/256);
680                         }
681         }
682         XPutImage(st->dpy,st->window,st->gc,st->xim,0,0,0,0,st->xim->width,st->xim->height);
683 }
684
685 static void *
686 fireworkx_init (Display *dpy, Window win)
687 {
688         struct state *st = (struct state *) calloc (1, sizeof(*st));
689         unsigned int n;
690         Visual *vi;
691         Colormap cmap;
692         Bool writable;
693         XWindowAttributes xwa;
694         XGCValues gcv;
695         firepix *fp;
696         fireshell *fs;
697
698         st->dpy = dpy;
699         st->window = win;
700         st->xim = NULL;
701         st->flash_on = 1;
702         st->shoot = 0;
703         st->width = 0;
704         st->height = 0;
705         st->max_shell_life = SHELL_LIFE_DEFAULT;
706         st->flash_fade = 0.995;
707         st->light_map = NULL;
708         st->palaka1 = NULL;
709         st->palaka2 = NULL;
710
711         st->flash_on       = get_boolean_resource(st->dpy, "flash"   , "Boolean");
712         st->shoot          = get_boolean_resource(st->dpy, "shoot"   , "Boolean");
713         st->verbose        = get_boolean_resource(st->dpy, "verbose" , "Boolean");
714         st->max_shell_life = get_integer_resource(st->dpy, "maxlife" , "Integer");
715         /* transition from xscreensaver <= 5.20 */
716         if (st->max_shell_life > 100) st->max_shell_life = 100;
717
718         st->delay          = get_integer_resource(st->dpy, "delay"   , "Integer");
719
720         st->max_shell_life = pow(10.0,(st->max_shell_life/50.0)+2.7);
721         if(st->max_shell_life < 1000) st->flash_fade = 0.998;
722
723         if(st->verbose)
724         {
725                 printf("Fireworkx %s - Pyrotechnics explosions simulation \n", FWXVERSION);
726                 printf("Copyright (GPL) 1999-2013 Rony B Chandran <ronybc@gmail.com> \n\n");
727                 printf("url: http://www.ronybc.com \n\n");
728                 printf("Life = %u\n", st->max_shell_life);
729 #ifdef __SSE2__
730                 printf("Using SSE2 optimization.\n");
731 #endif
732         }
733
734         XGetWindowAttributes(st->dpy,win,&xwa);
735         st->depth = xwa.depth;
736         vi        = xwa.visual;
737         cmap      = xwa.colormap;
738         st->bigendian = (ImageByteOrder(st->dpy) == MSBFirst);
739
740         if(st->depth==8)
741         {
742                 st->colors = (XColor *) calloc(sizeof(XColor),st->ncolors+1);
743                 writable = False;
744                 make_smooth_colormap(xwa.screen, vi, cmap,
745                                      st->colors, &st->ncolors,
746                                      False, &writable, True);
747         }
748         st->gc = XCreateGC(st->dpy, win, 0, &gcv);
749
750         fs = calloc(SHELLCOUNT, sizeof(fireshell));
751         fp = calloc(PIXCOUNT * SHELLCOUNT, sizeof(firepix));
752         st->fireshell_array = fs;
753
754         XGetWindowAttributes (st->dpy, st->window, &xwa);
755         st->depth = xwa.depth;
756
757         resize(st);   /* initialize palakas */
758
759         for (n = 0; n < SHELLCOUNT; n++, fs++)
760         {
761                 fs->seq_number = n;
762                 fs->fpix = fp;
763                 recycle (st, fs, rnd(st->width), rnd(st->height));
764                 fp += PIXCOUNT;
765         }
766
767         return st;
768 }
769
770 static unsigned long
771 fireworkx_draw (Display *dpy, Window win, void *closure)
772 {
773         struct state *st = (struct state *) closure;
774         fireshell *fs;
775         unsigned int n, q;
776         for (q = FTWEAK; q; q--)
777         {
778                 fs = st->fireshell_array;
779                 for (n = 0; n < SHELLCOUNT; n++, fs++)
780                 {
781                         if (!explode(st, fs))
782                         {
783                                 if (st->button_down_p)
784                                   st->deferred++;
785                                 else
786                                   recycle(st, fs, rnd(st->width), rnd(st->height));
787                         }
788                 }
789         }
790
791         while (!st->button_down_p && st->deferred) {
792           st->deferred--;
793           recycle_oldest(st, rnd(st->width), rnd(st->height));
794         }
795
796         glow_blur(st);
797
798         if (st->flash_on)
799         {
800                 chromo_2x2_light(st);
801         }
802
803         put_image(st);
804         return st->delay;
805 }
806
807 static void
808 fireworkx_reshape (Display *dpy, Window window, void *closure,
809                    unsigned int w, unsigned int h)
810 {
811         struct state *st = (struct state *) closure;
812         st->width  = w;
813         st->height = h;
814         resize(st);
815 }
816
817 static Bool
818 fireworkx_event (Display *dpy, Window window, void *closure, XEvent *event)
819 {
820         struct state *st = (struct state *) closure;
821         if (event->type == ButtonPress)
822         {
823                 recycle_oldest(st, event->xbutton.x, event->xbutton.y);
824                 st->button_down_p = True;
825                 return True;
826         }
827         else if (event->type == ButtonRelease)
828         {
829                 st->button_down_p = False;
830                 return True;
831         }
832
833         return False;
834 }
835
836 static void
837 fireworkx_free (Display *dpy, Window window, void *closure)
838 {
839         struct state *st = (struct state *) closure;
840         free(st->mem2);
841         free(st->mem1);
842         free(st->fireshell_array->fpix);
843         free(st->fireshell_array);
844 }
845
846 static const char *fireworkx_defaults [] =
847 {
848         ".background: black",
849         ".foreground: white",
850         "*delay: 10000",  /* never default to zero! */
851         "*maxlife: 32",
852         "*flash: True",
853         "*shoot: False",
854         "*verbose: False",
855         0
856 };
857
858 static XrmOptionDescRec fireworkx_options [] =
859 {
860         { "-delay", ".delay", XrmoptionSepArg, 0 },
861         { "-maxlife", ".maxlife", XrmoptionSepArg, 0 },
862         { "-no-flash", ".flash", XrmoptionNoArg, "False" },
863         { "-shoot", ".shoot", XrmoptionNoArg, "True" },
864         { "-verbose", ".verbose", XrmoptionNoArg, "True" },
865         { 0, 0, 0, 0 }
866 };
867
868 XSCREENSAVER_MODULE ("Fireworkx", fireworkx)