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