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