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