From http://www.jwz.org/xscreensaver/xscreensaver-5.32.tar.gz
[xscreensaver] / hacks / binaryring.c
1 /*
2  * Binary Ring
3  * Copyright (c) 2006-2014 Emilio Del Tessandoro <emilio.deltessa@gmail.com>
4  *
5  *  Directly ported code from complexification.net Binary Ring art
6  *  http://www.complexification.net/gallery/machines/binaryRing/appletm/BinaryRing_m.pde
7  *
8  *  Binary Ring code:
9  *  j.tarbell   June, 2004
10  *  Albuquerque, New Mexico
11  *  complexification.net
12  *
13  * Directly based the hacks of: 
14  * 
15  * xscreensaver, Copyright (c) 1997, 1998, 2002 Jamie Zawinski <jwz@jwz.org>
16  *
17  * Permission to use, copy, modify, distribute, and sell this software and its
18  * documentation for any purpose is hereby granted without fee, provided that
19  * the above copyright notice appear in all copies and that both that
20  * copyright notice and this permission notice appear in supporting
21  * documentation.  No representations are made about the suitability of this
22  * software for any purpose.  It is provided "as is" without express or 
23  * implied warranty.
24  */
25
26 #include "screenhack.h"
27 #include "colors.h"
28 #include "hsv.h"
29
30 #if HAVE_STDINT_H
31 # include <stdint.h>
32 #else
33 typedef unsigned long uint32_t;
34 #endif
35
36 #define ANTIALIAS   1
37 #define BLACK       0
38 #define WHITE       1
39
40 #define swap(a, b)  do { __typeof__(a) tmp;  tmp = a; a = b; b = tmp; } while(0)
41 #define frand1()    ((((int) random() ) << 1) * 4.65661287524579692411E-10)
42 #define min(a,b) ((a)<(b)?(a):(b))
43 #define max(a,b) ((a)>(b)?(a):(b))
44
45 /* better if signed */
46 typedef uint32_t pixel_t;
47
48
49 typedef struct {
50     float x, y;
51     float xx, yy;
52     float vx, vy;
53     float color;
54     int age;            /* age from 0 to ageMax */
55 } particle_t;
56
57
58 struct state {
59     int epoch;
60     int growth_delay;
61     int ring_radius;
62     int max_age;
63     float curliness;
64     int particles_number;
65     particle_t* particles;
66
67
68     Display *display;               /* variables for the X stuff */
69     Window window;
70     GC gc;
71     XGCValues gcv;
72     Visual* visual;
73     int depth;
74
75     int width;
76     int height;
77
78     Pixmap pix;                         /* for reshape */
79     XImage* buf;                            /* buffer */
80     pixel_t* buffer;
81     pixel_t colors[2];
82
83     Bool color;
84 };
85
86
87
88 static void point2rgb(int depth, pixel_t c, int *r, int *g, int *b) 
89 {
90     switch(depth) {
91     case 32:
92     case 24:
93 #ifdef HAVE_COCOA
94         /* This program idiotically does not go through a color map, so
95                we have to hardcode in knowledge of how jwxyz.a packs pixels!
96                Fix it to go through st->colors[st->ncolors] instead!
97              */
98         *r = (c & 0x00ff0000) >> 16;
99         *g = (c & 0x0000ffff) >>  8;
100         *b = (c & 0x000000ff);
101 #else
102         *g = (c & 0xff00) >> 8;
103         *r = (c & 0xff0000) >> 16;
104         *b = c & 0xff;
105 #endif
106         break;
107     case 16:
108         *g = ((c >> 5) & 0x3f) << 2;
109         *r = ((c >> 11) & 0x1f) << 3;
110         *b = (c & 0x1f) << 3;
111         break;
112     case 15:
113         *g = ((c >> 5) & 0x1f) << 3;
114         *r = ((c >> 10) & 0x1f) << 3;
115         *b = (c & 0x1f) << 3;
116         break;
117     }
118 }
119
120 static pixel_t rgb2point(int depth, int r, int g, int b) 
121 {
122     pixel_t ret = 0;
123
124     switch(depth) {
125     case 32:
126     case 24:
127 #ifdef HAVE_COCOA
128         /* This program idiotically does not go through a color map, so
129                we have to hardcode in knowledge of how jwxyz.a packs pixels!
130                Fix it to go through st->colors[st->ncolors] instead!
131              */
132         ret = 0xFF000000 | (r << 16) | (g << 8) | b;
133 #else
134         ret |= (r << 16) | (g << 8) | b;
135 #endif
136         break;
137     case 16:
138         ret = ((r>>3) << 11) | ((g>>2)<<5) | (b>>3);
139         break;
140     case 15:
141         ret = ((r>>3) << 10) | ((g>>3)<<5) | (b>>3);
142         break;
143     }
144
145     return ret;
146 }
147
148 void print_color ( struct state* st, pixel_t color );
149 /* alpha blended point drawing -- this is Not Right and will likely fail on 
150  * non-intel platforms as it is now, needs fixing
151  */
152
153 static
154 void draw_point ( struct state* st,
155                  int x, int y, pixel_t myc, float a ) {
156
157     int or = 0, og = 0, ob = 0;
158     int r = 0, g = 0, b = 0;
159     int nr, ng, nb;
160     register pixel_t c;
161
162     /*or = st->buffer[ 3 * ( y * st->width + x ) + 0 ];
163     og = st->buffer[ 3 * ( y * st->width + x ) + 1 ];
164     ob = st->buffer[ 3 * ( y * st->width + x ) + 2 ];*/
165     
166     c = st->buffer[ y * st->width + x ];
167     point2rgb( st->depth, c, &or, &og, &ob );
168     point2rgb( st->depth, myc, &r, &g, &b );
169
170     nr = or + (r - or) * a;
171     ng = og + (g - og) * a;
172     nb = ob + (b - ob) * a;
173
174     /*st->buffer[ 3 * ( y * st->width + x ) + 0 ] = nr;
175     st->buffer[ 3 * ( y * st->width + x ) + 1 ] = ng;
176     st->buffer[ 3 * ( y * st->width + x ) + 2 ] = nb;*/
177     c = rgb2point(st->depth, nr, ng, nb);
178     st->buffer[ y * st->width + x ] = c;
179
180     XPutPixel( st->buf, x, y, c );
181 }
182
183
184
185
186 void print_color ( struct state* st, pixel_t color ) {
187     int r, g, b;
188     point2rgb(st->depth, color, &r, &g, &b);
189     printf( "%d %d %d\n", r, g, b);
190 }
191
192
193
194 #if ANTIALIAS
195 #define plot_(X,Y,D) do{    \
196     _dla_plot(st, (X), (Y), color, (D) * alpha); }while(0)
197
198 static
199 void _dla_plot(struct state* st, int x, int y, pixel_t col, float br)
200 {
201     if ( ( x >= 0 && x < st->width ) && ( y >= 0 && y < st->height ) ) {
202         if ( br > 1.0f ) br = 1.0f;
203         draw_point( st, x, y, col, br );
204     }
205 }
206
207 #define ipart_(X) ((int)(X))
208 #define round_(X) ((int)(((float)(X))+0.5))
209 #define fpart_(X) (((float)(X))-(float)ipart_(X))
210 #define rfpart_(X) (1.0-fpart_(X))
211
212 static
213 void draw_line_antialias(
214         struct state* st,
215         int x1, int y1,
216         int x2, int y2,
217         pixel_t color, float alpha )
218 {
219     float dx = (float)x2 - (float)x1;
220     float dy = (float)y2 - (float)y1;
221     float gradient;
222     float xend;
223     float yend;
224     float xgap;
225     float ygap;
226     int xpxl1;
227     int ypxl1;
228     float intery;
229     float interx;
230     int xpxl2;
231     int ypxl2;
232     int x;
233     int y;
234
235     /* hard clipping, because this routine has some problems with negative coordinates */
236     /* TODO: fix the alpha for that boundary cases. Using fabs() could solve? */
237     if ( ( x1 < 0 || x1 > st->width ) ||
238          ( x2 < 0 || x2 > st->width ) ||
239          ( y1 < 0 || y1 > st->height ) ||
240          ( y2 < 0 || y2 > st->height ) )
241         return;
242
243     if ( fabs(dx) > fabs(dy) ) {
244         if ( x2 < x1 ) {
245             swap(x1, x2);
246             swap(y1, y2);
247         }
248         gradient = dy / dx;
249         xend = round_(x1);
250         yend = y1 + gradient*(xend - x1);
251         xgap = rfpart_(x1 + 0.5);
252         xpxl1 = xend;
253         ypxl1 = ipart_(yend);
254         plot_(xpxl1, ypxl1, rfpart_(yend)*xgap);
255         plot_(xpxl1, ypxl1+1, fpart_(yend)*xgap);
256         intery = yend + gradient;
257
258         xend = round_(x2);
259         yend = y2 + gradient*(xend - x2);
260         xgap = fpart_(x2+0.5);
261         xpxl2 = xend;
262         ypxl2 = ipart_(yend);
263         plot_(xpxl2, ypxl2, rfpart_(yend) * xgap);
264         plot_(xpxl2, ypxl2 + 1, fpart_(yend) * xgap);
265
266         /*if ( rfpart_(yend) * xgap < 0 || fpart_(yend) * xgap < 0)
267         printf("%f %f\n", rfpart_(yend) * xgap, fpart_(yend) * xgap);*/
268         for(x=xpxl1+1; x <= (xpxl2-1); x++) {
269             plot_(x, ipart_(intery), rfpart_(intery));
270             plot_(x, ipart_(intery) + 1, fpart_(intery));
271             /*if ( rfpart_(intery) < 0 || fpart_(intery) < 0)
272             printf("%f %f\n", rfpart_(intery), fpart_(intery));*/
273             intery += gradient;
274         }
275     } else {
276         if ( y2 < y1 ) {
277             swap(x1, x2);
278             swap(y1, y2);
279         }
280         gradient = dx / dy;
281         yend = round_(y1);
282         xend = x1 + gradient*(yend - y1);
283         ygap = rfpart_(y1 + 0.5);
284         ypxl1 = yend;
285         xpxl1 = ipart_(xend);
286         plot_(xpxl1, ypxl1, rfpart_(xend)*ygap);
287         plot_(xpxl1, ypxl1+1, fpart_(xend)*ygap);
288         interx = xend + gradient;
289
290         yend = round_(y2);
291         xend = x2 + gradient*(yend - y2);
292         ygap = fpart_(y2+0.5);
293         ypxl2 = yend;
294         xpxl2 = ipart_(xend);
295         plot_(xpxl2, ypxl2, rfpart_(xend) * ygap);
296         plot_(xpxl2, ypxl2 + 1, fpart_(xend) * ygap);
297
298         for(y=ypxl1+1; y <= (ypxl2-1); y++) {
299             plot_(ipart_(interx), y, rfpart_(interx));
300             plot_(ipart_(interx) + 1, y, fpart_(interx));
301             interx += gradient;
302         }
303     }
304 }
305 #undef plot_
306 #undef ipart_
307 #undef fpart_
308 #undef round_
309 #undef rfpart_
310
311 #else
312
313 static
314 void draw_line ( struct state* st, int x0, int y0, int x1, int y1, int color, float a ) {
315     register int steep;
316     register int deltax;
317     register int deltay;
318     register int error;
319     register int ystep;
320     register int y;
321     register int x;
322
323
324     steep = abs ( y1 - y0 ) > abs ( x1 - x0 );
325     if ( steep ) { swap(x0,y0); swap(x1,y1); }
326     if ( x0 > x1 ) { swap(x0,x1); swap(y0,y1); }
327     deltax = x1 - x0;
328     deltay = abs(y1-y0);
329     error = 0;
330     y = y0;
331     ystep = y0 < y1 ? 1 : -1;
332     if ( a > 1.0f ) a = 1.0f;
333
334     for ( x = x0; x < x1; x++ ) {
335         if ( steep ) {
336             if ( ( y >= 0 && y < st->width ) && ( x >= 0 && x < st->height ) )
337                 draw_point( st, y, x, color, a );
338         } else {
339             if ( ( x >= 0 && x < st->width ) && ( y >= 0 && y < st->height ) )
340                 draw_point( st, x, y, color, a );
341         }
342         error += deltay;
343         if ( ( error << 1 ) > deltax ) {
344             y += ystep;
345             error -= deltax;
346         }
347     }
348 }
349 #endif
350
351 static void create_buffers ( struct state* st, Display* display, Screen* screen, Window window, GC gc ) {
352     
353     XWindowAttributes xgwa;
354     XGetWindowAttributes( display, window, &xgwa );
355
356     /* Initialize the pixmap */
357     if ( st->buf != NULL ) XFreePixmap( display, st->pix );
358     
359     XSetForeground( display, gc, st->colors[BLACK] );
360     st->pix = XCreatePixmap( display, window, st->width, st->height,
361                              xgwa.depth );
362     XFillRectangle( display, st->pix, gc, 0, 0, st->width, st->height);
363
364     /* Initialize the bitmap */
365     if ( st->buf != NULL ) XDestroyImage ( st->buf );
366     st->buf = XGetImage( display, st->pix, 0, 0, st->width, st->height, visual_depth(screen, st->visual), ZPixmap );
367     st->buffer = (pixel_t*) calloc(sizeof(pixel_t), st->width * st->height);
368     /*int i;
369     for ( i = 0; i < st->width * st->height; ++i ) st->buffer[i] = st->colors[BLACK];*/
370 }
371
372 static void init_particle ( particle_t* p, float dx, float dy, float direction, int color, int max_age ) {
373     float max_initial_velocity = 2.0f;
374     p->x = -dx;
375     p->y = -dy;
376     p->xx = 0;
377     p->yy = 0;
378     p->vx = max_initial_velocity * cos(direction);
379     p->vy = max_initial_velocity * sin(direction);
380
381     p->age = random() % max_age;
382
383     p->color = color;
384 }
385
386 static void clamp ( int* value, int l, int h ) {
387     if ( *value < l ) *value = l;
388     if ( *value > h ) *value = h;
389 }
390
391 static pixel_t next_color ( struct state* st, pixel_t current ) {
392     int r, g, b;
393
394     point2rgb(st->depth, current, &r, &g, &b);
395     r += random() % 5 - 2;
396     g += random() % 5 - 2;
397     b += random() % 5 - 2;
398     clamp(&r, 0, 255);
399     clamp(&g, 0, 255);
400     clamp(&b, 0, 255);
401
402     return rgb2point( st->depth, r, g, b );
403 }
404
405
406 static void create_particles ( struct state* st ) {
407     int i;
408     float emitx, emity;
409     float direction;
410
411     for ( i = 0; i < st->particles_number; i++ ) {
412         emitx = ( st->ring_radius * sinf( M_PI * 2 * ( (float) i / st->particles_number ) ) );
413         emity = ( st->ring_radius * cosf( M_PI * 2 * ( (float) i / st->particles_number ) ) );
414         direction = (M_PI * i) / st->particles_number;
415
416         if ( st->epoch == WHITE && st->color )
417             st->colors[WHITE] = next_color(st, st->colors[WHITE]);
418
419         init_particle(st->particles + i, emitx, emity, direction, st->colors[WHITE], st->max_age);
420     }
421 }
422
423
424 /* randomly move one particle and draw it */
425 static void move ( particle_t* p, struct state * st ) {
426     int w = st->width / 2;
427     int h = st->height / 2;
428     float max_dv = 1.0f;
429
430     p->xx = p->x;
431     p->yy = p->y;
432     p->x += p->vx;
433     p->y += p->vy;
434     p->vx += frand1() * st->curliness * max_dv;
435     p->vy += frand1() * st->curliness * max_dv;
436
437
438 #if ANTIALIAS
439     draw_line_antialias( st,
440         w+p->xx, h+p->yy, w+p->x, h+p->y, p->color, 0.15 );
441     draw_line_antialias( st,
442         w-p->xx, h+p->yy, w-p->x, h+p->y, p->color, 0.15 );
443 #else
444     draw_line( st, w+p->xx, h+p->yy, w+p->x, h+p->y, p->color, 0.15 );
445     draw_line( st, w-p->xx, h+p->yy, w-p->x, h+p->y, p->color, 0.15 );
446 #endif
447
448     p->age++;
449     /* if this is too old, die and reborn */
450     if ( p->age > st->max_age ) {
451         float dir = frand1() * 2 * M_PI;
452         p->x = st->ring_radius * sin(dir);
453         p->y = st->ring_radius * cos(dir);
454         p->xx = p->yy = p->vx = p->vy = 0;
455         p->age = 0;
456
457         if ( st->epoch == WHITE && st->color )
458             st->colors[WHITE] = next_color(st, st->colors[WHITE] );
459         
460         p->color = st->colors[st->epoch];
461     }
462 }
463
464
465
466
467 /* Initialize Everything */
468 static void* binaryring_init ( Display* display, Window window ) {
469     XWindowAttributes xgwa;
470
471     struct state *st = ( struct state* ) calloc ( 1, sizeof( *st ) );
472
473     XGetWindowAttributes( display, window, &xgwa );
474
475     st->epoch = WHITE;
476     st->display = display;
477     st->window = window;
478     st->particles_number = (get_integer_resource(st->display, "particles_number", "Integer"));
479     st->growth_delay = (get_integer_resource(st->display, "growth_delay", "Integer"));
480     st->ring_radius = (get_integer_resource(st->display, "ring_radius", "Integer"));
481     st->max_age = (get_integer_resource(st->display, "max_age", "Integer"));
482     st->color =  get_boolean_resource(st->display, "color", "Boolean");
483     st->height = xgwa.height;
484     st->width = xgwa.width;
485     st->visual = xgwa.visual;
486     st->curliness = 0.5;
487
488
489     st->depth = visual_depth(xgwa.screen, st->visual);
490     st->colors[0] = rgb2point(st->depth, 0, 0, 0);
491     st->colors[1] = rgb2point(st->depth, 255, 255, 255);
492
493     /*describe_visual (stdout, xgwa.screen, xgwa.visual, False);*/
494
495     st->particles = malloc (st->particles_number * sizeof( particle_t ) );
496     create_particles ( st );
497
498     st->gc = XCreateGC( st->display, st->window, 0, &st->gcv );
499     create_buffers ( st, display, xgwa.screen, window, st->gc );
500
501
502     return st;
503 }
504
505
506 static unsigned long binaryring_draw ( Display* display, Window win, void *closure ) {
507     struct state *st = (struct state *) closure;
508     int i;
509
510     for ( i = 0; i < st->particles_number; i++ )
511         move( &(st->particles[i]), st );
512
513     /* draw the XImage in the Pixmap and the put the Pixmap on the screen */
514     XPutImage( display, st->pix, st->gc, st->buf, 0, 0, 0, 0, st->width, st->height);
515     XCopyArea( display, st->pix, win, st->gc, 0, 0, st->width, st->height, 0, 0 );
516
517     /* randomly switch ageColor periods */
518     if ( random() % 10000 > 9950 ) 
519         st->epoch = (st->epoch == WHITE) ? BLACK : WHITE;
520
521     return st->growth_delay;
522 }
523
524
525
526 static void binaryring_reshape ( Display* display, Window win, void *closure, unsigned int w, unsigned int h ) {
527     struct state *st = (struct state *) closure;
528
529     XWindowAttributes tmp;
530     XGetWindowAttributes(display, win, &tmp);
531
532     if ( tmp.height != st->height || tmp.width != st->width ) {
533         st->height = tmp.height;
534         st->width = tmp.width;
535
536
537         st->epoch = WHITE;
538         create_particles ( st );
539
540         create_buffers ( st, display, tmp.screen, win, st->gc );
541     }
542 }
543
544
545 /* if someone press a key switch the color */
546 static Bool binaryring_event ( Display* display, Window win, void *closure, XEvent* event ) {
547     struct state *st = (struct state *) closure;
548
549     if ( (*event).xany.type == KeyPress ) {
550         st->epoch = (st->epoch == WHITE) ? BLACK : WHITE;
551     }
552
553     return False;
554 }
555
556
557 /* delete everything */
558 static void binaryring_free ( Display* display, Window win, void *closure ) {
559     struct state *st = (struct state *) closure;
560     XWindowAttributes tmp;
561     XGetWindowAttributes(display, win, &tmp);
562
563     free( st->buffer );
564     free( st->particles );
565
566     free ( st );
567 }
568
569
570 /* Default resources of the program */
571 static const char* binaryring_defaults [] = {
572     ".background:       black",
573     ".foreground:       white",
574     "*growth_delay:     10000",
575     "*particles_number: 5000",
576     "*ring_radius:      40",
577     "*max_age:          400",
578     "*color:            True",
579     "*ignoreRotation:   True",
580     0
581 };
582
583 static XrmOptionDescRec binaryring_options [] = {
584     { "-particles-number", ".particles_number", XrmoptionSepArg, 0 },
585     { "-growth-delay",     ".growth_delay",     XrmoptionSepArg, 0 },
586     { "-ring-radius",      ".ring_radius",      XrmoptionSepArg, 0 },
587     { "-max-age",          ".max_age",          XrmoptionSepArg, 0 },
588     { "-color",            ".color",            XrmoptionNoArg,  "True"  },
589     { "-no-color",         ".color",            XrmoptionNoArg,  "False" },
590     { 0, 0, 0, 0}
591 };
592
593 XSCREENSAVER_MODULE("BinaryRing", binaryring)