From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / imsmap.c
1 /* imsmap, Copyright (c) 1992-2013 Juergen Nickelsen and Jamie Zawinski.
2  * Derived from code by Markus Schirmer, TU Berlin.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  *
12  * Revision History:
13  * 24-aug-92: jwz: hacked.
14  * 17-May-97: jwz: hacked more.
15  */
16
17 #include <stdio.h>
18 #include <math.h>
19
20 #include "screenhack.h"
21
22 #define NSTEPS 7
23 #define COUNT (1 << NSTEPS)
24 #define CELL(c, r) st->cell[((unsigned int)(c)) + ((unsigned int) (r)) * st->xmax]
25
26 #if defined(sun) && !__STDC__   /* sun cc doesn't know "signed char" */
27 #define signed /**/
28 #endif
29
30 struct state {
31   Display *dpy;
32   Window window;
33   Colormap cmap;
34   int ncolors;
35   XColor *colors;
36   Bool extra_krinkly_p;
37
38   int delay, delay2;
39   signed char *cell;
40   int xmax, ymax;
41   int iteration, iterations;
42
43   int cx, xstep, ystep, xnextStep, ynextStep;
44
45   unsigned int last_pixel, last_valid;
46   int flip_x;
47   int flip_xy;
48
49   GC gc, gc2;
50   XWindowAttributes xgwa;
51
52   struct timeval then;
53 };
54
55
56 #define HEIGHT_TO_PIXEL(height)                                 \
57         ((height) < 0                                           \
58          ? (st->extra_krinkly_p                                 \
59             ? st->ncolors - 1 -  ((-(height)) % st->ncolors)    \
60             : 0)                                                \
61          : ((height) >= st->ncolors                             \
62             ? (st->extra_krinkly_p                              \
63                ? (height) % st->ncolors                         \
64                : st->ncolors-1)                                 \
65             : (height)))
66
67
68 static unsigned int
69 set (struct state *st,
70      unsigned int l,
71      unsigned int c,
72      unsigned int size,
73      int height)
74 {
75   int rang = 1 << (NSTEPS - size);
76   height = height + (random () % rang) - rang / 2;
77   height = HEIGHT_TO_PIXEL(height);
78   CELL (l, c) = height;
79   return st->colors[height].pixel;
80 }
81
82
83 static void
84 floyd_steinberg (struct state *st)
85 {
86   int x, y, err;
87
88   /* Instead of repeatedly calling XPutPixel(), we make an Image and then
89      send its bits over all at once.  This consumes much less network
90      bandwidth.  The image we create is Wx1 intead of WxH, so that we
91      don't use enormous amounts of memory.
92    */
93   XImage *image =
94     XCreateImage (st->dpy, st->xgwa.visual,
95                   1, XYBitmap, 0,               /* depth, format, offset */
96                   (char *) calloc ((st->xmax + 8) / 8, 1),      /* data */
97                   st->xmax, 1, 8, 0);           /* w, h, pad, bpl */
98
99   XSetForeground (st->dpy, st->gc, st->colors[0].pixel);
100   XSetBackground (st->dpy, st->gc, st->colors[1].pixel);
101
102   for (y = 0; y < st->ymax - 1; y++)
103     {
104       for (x = 0; x < st->xmax - 1; x++)
105         {
106           if (CELL(x, y) < 0)
107             {
108               err = CELL (x, y);
109               XPutPixel (image, x, 0, 1);
110             }
111           else
112             {
113               err = CELL (x, y) - 1;
114               XPutPixel (image, x, 0, 0);
115             }
116           /* distribute error */
117           CELL (x,   y+1) += (int) (((float) err) * 3.0/8.0);
118           CELL (x+1, y)   += (int) (((float) err) * 3.0/8.0);
119           CELL (x+1, y+1) += (int) (((float) err) * 1.0/4.0);
120         }
121       XPutImage (st->dpy, st->window, st->gc, image, 0, 0, 0, y, st->xmax, 1);
122     }
123   XDestroyImage (image);
124 }
125
126
127 static void
128 draw (struct state *st,
129       int x, int y, unsigned long pixel, int grid_size)
130 {
131   if (st->flip_x)
132     x = st->xmax - x;
133
134   if (st->flip_xy)
135     {
136       int swap = x;
137       x = y;
138       y = swap;
139     }
140
141   if (! (st->last_valid && pixel == st->last_pixel))
142     XSetForeground (st->dpy, st->gc, pixel);
143   st->last_valid = 1, st->last_pixel = pixel;
144   if (grid_size == 1)
145     XDrawPoint (st->dpy, st->window, st->gc, x, y);
146   else
147     XFillRectangle (st->dpy, st->window, st->gc, x, y, grid_size, grid_size);
148 }
149
150
151 static void
152 init_map (struct state *st)
153 {
154   XGCValues gcv;
155
156   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
157   st->cmap = st->xgwa.colormap;
158
159   st->flip_x  = (random() % 2);
160   st->flip_xy = (random() % 2);
161
162   if (mono_p)
163     st->flip_xy = 0;
164   else if (st->colors)
165     free_colors (st->xgwa.screen, st->cmap, st->colors, st->ncolors);
166   st->colors = 0;
167
168   st->ncolors = get_integer_resource (st->dpy, "ncolors", "Integer");
169   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
170   st->delay2 = get_integer_resource (st->dpy, "delay2", "Integer");
171   st->iterations = get_integer_resource (st->dpy, "iterations", "Integer");
172   if (st->iterations < 0) st->iterations = 0;
173   else if (st->iterations > 7) st->iterations = 7;
174
175   if (st->ncolors <= 2) st->ncolors = 0;
176   if (st->ncolors == 0) mono_p = True;
177   if (st->ncolors > 255) st->ncolors = 255;  /* too many look bad */
178
179   if (!st->gc)  st->gc  = XCreateGC (st->dpy, st->window, 0, &gcv);
180   if (!st->gc2) st->gc2 = XCreateGC (st->dpy, st->window, 0, &gcv);
181
182   if (mono_p)
183     st->extra_krinkly_p = !(random() % 15);
184   else
185     st->extra_krinkly_p = !(random() % 5);
186
187   if (!mono_p)
188     {
189       st->colors = (XColor *) malloc (st->ncolors * sizeof(*st->colors));
190
191       make_smooth_colormap (st->xgwa.screen, st->xgwa.visual, st->cmap,
192                             st->colors, &st->ncolors,
193                             True, 0, False);
194       if (st->ncolors <= 2)
195         mono_p = 1;
196     }
197
198   if (mono_p)
199     {
200       int i;
201       unsigned long fg_pixel = 
202         get_pixel_resource (st->dpy, st->xgwa.colormap, 
203                             "foreground", "Foreground");
204       unsigned long bg_pixel = 
205         get_pixel_resource (st->dpy, st->xgwa.colormap, 
206                             "background", "Background");
207       if (!st->colors)
208         {
209           st->ncolors = 50;
210           st->colors = (XColor *) calloc (st->ncolors, sizeof(*st->colors));
211         }
212       st->colors[0].pixel = fg_pixel;
213       for (i = 1; i < st->ncolors; i++)
214         st->colors[i].pixel = bg_pixel;
215     }
216
217   XSetForeground (st->dpy, st->gc, st->colors[1].pixel);
218   XFillRectangle (st->dpy, st->window, st->gc, 0, 0, 
219                   st->xgwa.width, st->xgwa.height);
220
221   if (st->flip_xy)
222     {
223       st->xmax = st->xgwa.height;
224       st->ymax = st->xgwa.width;
225     }
226   else
227     {
228       st->xmax = st->xgwa.width;
229       st->ymax = st->xgwa.height;
230     }
231
232   if (st->cell) free (st->cell);
233   st->cell = (signed char *) calloc (st->xmax * st->ymax, 1);
234
235   CELL (0, 0) = 0;
236   st->xstep = COUNT;
237   st->ystep = COUNT;
238
239   st->iteration = 0;
240   st->cx = 0;
241 }
242
243
244 static void *
245 imsmap_init (Display *dpy, Window window)
246 {
247   struct state *st = (struct state *) calloc (1, sizeof(*st));
248   st->dpy = dpy;
249   st->window = window;
250   init_map (st);
251   return st;
252 }
253
254
255 static unsigned long
256 imsmap_draw (Display *dpy, Window window, void *closure)
257 {
258   struct state *st = (struct state *) closure;
259   int this_delay = st->delay2;
260   int i;
261
262   /* do this many lines at a time without pausing */
263   int col_chunk = st->iteration * 2 + 1;
264
265   if (st->iteration > st->iterations)
266     init_map (st);
267
268   if (st->cx == 0)
269     {
270       st->xnextStep = st->xstep / 2;
271       st->ynextStep = st->ystep / 2;
272     }
273
274   for (i = 0; i < col_chunk; i++)
275     {
276       int x1, x2, y1, y2;
277       int y;
278       int x = st->cx;
279
280       x1 = x + st->xnextStep;
281       if (x1 < 0)
282         x1 = st->xmax-1;
283       else if (x1 >= st->xmax)
284         x1 = 0;
285
286       x2 = x + st->xstep;
287       if (x2 < 0)
288         x2 = st->xmax-1;
289       else if (x2 >= st->xmax)
290         x2 = 0;
291
292       for (y = 0; y < st->ymax; y += st->ystep)
293         {
294           unsigned int pixel, qpixels [4];
295
296           y1 = y + st->ynextStep;
297           if (y1 < 0)
298             y1 = st->ymax-1;
299           else if (y1 >= st->ymax)
300             y1 = 0;
301
302           y2 = y + st->ystep;
303           if (y2 < 0)
304             y2 = st->ymax-1;
305           else if (y2 >= st->ymax)
306             y2 = 0;
307
308           qpixels [0] = st->colors [HEIGHT_TO_PIXEL (CELL (x, y))].pixel;
309           qpixels [1] = st->colors [HEIGHT_TO_PIXEL (CELL (x, y2))].pixel;
310           qpixels [2] = st->colors [HEIGHT_TO_PIXEL (CELL (x2, y))].pixel;
311           qpixels [3] = st->colors [HEIGHT_TO_PIXEL (CELL (x2, y2))].pixel;
312
313           pixel = set (st, x, y1, st->iteration,
314                        ((int) CELL (x, y) + (int) CELL (x, y2) + 1) / 2);
315
316           if (! mono_p &&
317               (pixel != qpixels[0] || pixel != qpixels[1] || 
318                pixel != qpixels[2] || pixel != qpixels[3]))
319             draw (st, x, y1, pixel, st->ynextStep);
320
321           pixel = set (st, x1, y, st->iteration,
322                        ((int) CELL (x, y) + (int) CELL (x2, y) + 1) / 2);
323           if (! mono_p &&
324               (pixel != qpixels[0] || pixel != qpixels[1] || 
325                pixel != qpixels[2] || pixel != qpixels[3]))
326             draw (st, x1, y, pixel, st->ynextStep);
327
328           pixel = set (st, x1, y1, st->iteration,
329                        ((int) CELL (x, y) + (int) CELL (x, y2) +
330                         (int) CELL (x2, y) + (int) CELL (x2, y2) + 2)
331                        / 4);
332           if (! mono_p &&
333               (pixel != qpixels[0] || pixel != qpixels[1] || 
334                pixel != qpixels[2] || pixel != qpixels[3]))
335             draw (st, x1, y1, pixel, st->ynextStep);
336         }
337
338       st->cx += st->xstep;
339       if (st->cx >= st->xmax)
340         break;
341     }
342
343   if (st->cx >= st->xmax)
344     {
345       st->cx = 0;
346       st->xstep = st->xnextStep;
347       st->ystep = st->ynextStep;
348
349       st->iteration++;
350
351       if (st->iteration > st->iterations)
352         this_delay = st->delay * 1000000;
353
354       if (mono_p)
355         floyd_steinberg (st);  /* in mono, do all drawing at the end */
356     }
357
358   return this_delay;
359 }
360
361
362 static void
363 imsmap_reshape (Display *dpy, Window window, void *closure, 
364                  unsigned int w, unsigned int h)
365 {
366   struct state *st = (struct state *) closure;
367   init_map (st);
368 }
369
370
371 static Bool
372 imsmap_event (Display *dpy, Window window, void *closure, XEvent *event)
373 {
374   struct state *st = (struct state *) closure;
375   if (screenhack_event_helper (dpy, window, event))
376     {
377       init_map (st);
378       return True;
379     }
380
381   return False;
382 }
383
384
385 static void
386 imsmap_free (Display *dpy, Window window, void *closure)
387 {
388   struct state *st = (struct state *) closure;
389   if (st->colors) free (st->colors);
390   if (st->cell) free (st->cell);
391   free (st);
392 }
393
394
395 static const char *imsmap_defaults [] = {
396   ".background: #000066",
397   ".foreground: #FF00FF",
398   "*fpsSolid:   true",
399   "*mode:       random",
400   "*ncolors:    50",
401   "*iterations: 7",
402   "*delay:      5",
403   "*delay2:     20000",
404 #ifdef USE_IPHONE
405   "*ignoreRotation: True",
406 #endif
407   0
408 };
409
410 static XrmOptionDescRec imsmap_options [] = {
411   { "-ncolors",         ".ncolors",     XrmoptionSepArg, 0 },
412   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
413   { "-delay2",          ".delay2",      XrmoptionSepArg, 0 },
414   { "-mode",            ".mode",        XrmoptionSepArg, 0 },
415   { "-iterations",      ".iterations",  XrmoptionSepArg, 0 },
416   { 0, 0, 0, 0 }
417 };
418
419 XSCREENSAVER_MODULE ("IMSMap", imsmap)