1877243dcf00c2bce88dddb93a0fda7871f73fe1
[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       if (st->ncolors < 1) st->ncolors = 1;
190       st->colors = (XColor *) malloc (st->ncolors * sizeof(*st->colors));
191
192       make_smooth_colormap (st->xgwa.screen, st->xgwa.visual, st->cmap,
193                             st->colors, &st->ncolors,
194                             True, 0, False);
195       if (st->ncolors <= 2)
196         mono_p = 1;
197     }
198
199   if (mono_p)
200     {
201       int i;
202       unsigned long fg_pixel = 
203         get_pixel_resource (st->dpy, st->xgwa.colormap, 
204                             "foreground", "Foreground");
205       unsigned long bg_pixel = 
206         get_pixel_resource (st->dpy, st->xgwa.colormap, 
207                             "background", "Background");
208       if (!st->colors)
209         {
210           st->ncolors = 50;
211           st->colors = (XColor *) calloc (st->ncolors, sizeof(*st->colors));
212         }
213       st->colors[0].pixel = fg_pixel;
214       for (i = 1; i < st->ncolors; i++)
215         st->colors[i].pixel = bg_pixel;
216     }
217
218   XSetForeground (st->dpy, st->gc, st->colors[1].pixel);
219   XFillRectangle (st->dpy, st->window, st->gc, 0, 0, 
220                   st->xgwa.width, st->xgwa.height);
221
222   if (st->flip_xy)
223     {
224       st->xmax = st->xgwa.height;
225       st->ymax = st->xgwa.width;
226     }
227   else
228     {
229       st->xmax = st->xgwa.width;
230       st->ymax = st->xgwa.height;
231     }
232
233   if (st->cell) free (st->cell);
234   st->cell = (signed char *) calloc (st->xmax * st->ymax, 1);
235
236   CELL (0, 0) = 0;
237   st->xstep = COUNT;
238   st->ystep = COUNT;
239
240   st->iteration = 0;
241   st->cx = 0;
242 }
243
244
245 static void *
246 imsmap_init (Display *dpy, Window window)
247 {
248   struct state *st = (struct state *) calloc (1, sizeof(*st));
249   st->dpy = dpy;
250   st->window = window;
251   init_map (st);
252   return st;
253 }
254
255
256 static unsigned long
257 imsmap_draw (Display *dpy, Window window, void *closure)
258 {
259   struct state *st = (struct state *) closure;
260   int this_delay = st->delay2;
261   int i;
262
263   /* do this many lines at a time without pausing */
264   int col_chunk = st->iteration * 2 + 1;
265
266   if (st->iteration > st->iterations)
267     init_map (st);
268
269   if (st->cx == 0)
270     {
271       st->xnextStep = st->xstep / 2;
272       st->ynextStep = st->ystep / 2;
273     }
274
275   for (i = 0; i < col_chunk; i++)
276     {
277       int x1, x2, y1, y2;
278       int y;
279       int x = st->cx;
280
281       x1 = x + st->xnextStep;
282       if (x1 < 0)
283         x1 = st->xmax-1;
284       else if (x1 >= st->xmax)
285         x1 = 0;
286
287       x2 = x + st->xstep;
288       if (x2 < 0)
289         x2 = st->xmax-1;
290       else if (x2 >= st->xmax)
291         x2 = 0;
292
293       for (y = 0; y < st->ymax; y += st->ystep)
294         {
295           unsigned int pixel, qpixels [4];
296
297           y1 = y + st->ynextStep;
298           if (y1 < 0)
299             y1 = st->ymax-1;
300           else if (y1 >= st->ymax)
301             y1 = 0;
302
303           y2 = y + st->ystep;
304           if (y2 < 0)
305             y2 = st->ymax-1;
306           else if (y2 >= st->ymax)
307             y2 = 0;
308
309           qpixels [0] = st->colors [HEIGHT_TO_PIXEL (CELL (x, y))].pixel;
310           qpixels [1] = st->colors [HEIGHT_TO_PIXEL (CELL (x, y2))].pixel;
311           qpixels [2] = st->colors [HEIGHT_TO_PIXEL (CELL (x2, y))].pixel;
312           qpixels [3] = st->colors [HEIGHT_TO_PIXEL (CELL (x2, y2))].pixel;
313
314           pixel = set (st, x, y1, st->iteration,
315                        ((int) CELL (x, y) + (int) CELL (x, y2) + 1) / 2);
316
317           if (! mono_p &&
318               (pixel != qpixels[0] || pixel != qpixels[1] || 
319                pixel != qpixels[2] || pixel != qpixels[3]))
320             draw (st, x, y1, pixel, st->ynextStep);
321
322           pixel = set (st, x1, y, st->iteration,
323                        ((int) CELL (x, y) + (int) CELL (x2, y) + 1) / 2);
324           if (! mono_p &&
325               (pixel != qpixels[0] || pixel != qpixels[1] || 
326                pixel != qpixels[2] || pixel != qpixels[3]))
327             draw (st, x1, y, pixel, st->ynextStep);
328
329           pixel = set (st, x1, y1, st->iteration,
330                        ((int) CELL (x, y) + (int) CELL (x, y2) +
331                         (int) CELL (x2, y) + (int) CELL (x2, y2) + 2)
332                        / 4);
333           if (! mono_p &&
334               (pixel != qpixels[0] || pixel != qpixels[1] || 
335                pixel != qpixels[2] || pixel != qpixels[3]))
336             draw (st, x1, y1, pixel, st->ynextStep);
337         }
338
339       st->cx += st->xstep;
340       if (st->cx >= st->xmax)
341         break;
342     }
343
344   if (st->cx >= st->xmax)
345     {
346       st->cx = 0;
347       st->xstep = st->xnextStep;
348       st->ystep = st->ynextStep;
349
350       st->iteration++;
351
352       if (st->iteration > st->iterations)
353         this_delay = st->delay * 1000000;
354
355       if (mono_p)
356         floyd_steinberg (st);  /* in mono, do all drawing at the end */
357     }
358
359   return this_delay;
360 }
361
362
363 static void
364 imsmap_reshape (Display *dpy, Window window, void *closure, 
365                  unsigned int w, unsigned int h)
366 {
367   struct state *st = (struct state *) closure;
368   init_map (st);
369 }
370
371
372 static Bool
373 imsmap_event (Display *dpy, Window window, void *closure, XEvent *event)
374 {
375   struct state *st = (struct state *) closure;
376   if (screenhack_event_helper (dpy, window, event))
377     {
378       init_map (st);
379       return True;
380     }
381
382   return False;
383 }
384
385
386 static void
387 imsmap_free (Display *dpy, Window window, void *closure)
388 {
389   struct state *st = (struct state *) closure;
390   if (st->colors) free (st->colors);
391   if (st->cell) free (st->cell);
392   free (st);
393 }
394
395
396 static const char *imsmap_defaults [] = {
397   ".background: #000066",
398   ".foreground: #FF00FF",
399   "*fpsSolid:   true",
400   "*mode:       random",
401   "*ncolors:    50",
402   "*iterations: 7",
403   "*delay:      5",
404   "*delay2:     20000",
405 #ifdef HAVE_MOBILE
406   "*ignoreRotation: True",
407 #endif
408   0
409 };
410
411 static XrmOptionDescRec imsmap_options [] = {
412   { "-ncolors",         ".ncolors",     XrmoptionSepArg, 0 },
413   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
414   { "-delay2",          ".delay2",      XrmoptionSepArg, 0 },
415   { "-mode",            ".mode",        XrmoptionSepArg, 0 },
416   { "-iterations",      ".iterations",  XrmoptionSepArg, 0 },
417   { 0, 0, 0, 0 }
418 };
419
420 XSCREENSAVER_MODULE ("IMSMap", imsmap)