http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.06.tar.gz
[xscreensaver] / hacks / blitspin.c
1 /* xscreensaver, Copyright (c) 1992-2008 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /* Rotate a bitmap using using bitblts.
13    The bitmap must be square, and must be a power of 2 in size.
14    This was translated from SmallTalk code which appeared in the
15    August 1981 issue of Byte magazine.
16
17    The input bitmap may be non-square, it is padded and centered
18    with the background color.  Another way would be to subdivide
19    the bitmap into square components and rotate them independently
20    (and preferably in parallel), but I don't think that would be as
21    interesting looking.
22
23    It's too bad almost nothing uses blitter hardware these days,
24    or this might actually win.
25  */
26
27 #include "screenhack.h"
28 #include "xpm-pixmap.h"
29 #include <stdio.h>
30
31 #include "images/som.xbm"
32
33 struct state {
34   Display *dpy;
35   Window window;
36   XWindowAttributes xgwa;
37   int width, height, size;
38   Bool scale_up;
39   Pixmap self, temp, mask;
40   GC SET, CLR, CPY, IOR, AND, XOR;
41   GC gc;
42   int delay, delay2;
43   int duration;
44   Pixmap bitmap;
45   unsigned int fg, bg;
46
47   int qwad; /* fuckin' C, man... who needs namespaces? */
48   int first_time;
49   int last_w, last_h;
50
51   time_t start_time;
52   Bool loaded_p;
53   async_load_state *img_loader;
54 };
55
56 static void display (struct state *, Pixmap);
57 static void blitspin_init_2 (struct state *);
58
59 #define copy_all_to(from, xoff, yoff, to, gc)                   \
60   XCopyArea (st->dpy, (from), (to), (gc), 0, 0,                 \
61              st->size-(xoff), st->size-(yoff), (xoff), (yoff))
62
63 #define copy_all_from(to, xoff, yoff, from, gc)                 \
64   XCopyArea (st->dpy, (from), (to), (gc), (xoff), (yoff),       \
65              st->size-(xoff), st->size-(yoff), 0, 0)
66
67 static unsigned long
68 blitspin_draw (Display *dpy, Window window, void *closure)
69 {
70   struct state *st = (struct state *) closure;
71   int this_delay = st->delay;
72
73   if (st->img_loader)   /* still loading */
74     {
75       st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
76
77       if (!st->img_loader) { /* just finished */
78         st->first_time = 0;
79         st->loaded_p = False;
80         st->qwad = -1;
81         st->start_time = time ((time_t) 0);
82       }
83
84       return this_delay;
85     }
86
87   if (!st->img_loader &&
88       st->start_time + st->duration < time ((time_t) 0)) {
89     /* Start it loading, but keep rotating the old image until it arrives. */
90     st->img_loader = load_image_async_simple (0, st->xgwa.screen, st->window,
91                                               st->bitmap, 0, 0);
92   }
93
94   if (! st->loaded_p) {
95     blitspin_init_2 (st);
96     st->loaded_p = True;
97   }
98
99   if (st->qwad == -1) 
100     {
101       XFillRectangle (st->dpy, st->mask, st->CLR, 0, 0, st->size, st->size);
102       XFillRectangle (st->dpy, st->mask, st->SET, 0, 0, st->size>>1, st->size>>1);
103       st->qwad = st->size>>1;
104     }
105
106   if (st->first_time)
107     {
108       st->first_time = 0;
109       display (st, st->self);
110       return st->delay2;
111     }
112
113   /* for (st->qwad = st->size>>1; st->qwad > 0; st->qwad>>=1) */
114
115   copy_all_to  (st->mask, 0,        0,        st->temp, st->CPY);  /* 1 */
116   copy_all_to  (st->mask, 0,        st->qwad, st->temp, st->IOR);  /* 2 */
117   copy_all_to  (st->self, 0,        0,        st->temp, st->AND);  /* 3 */
118   copy_all_to  (st->temp, 0,        0,        st->self, st->XOR);  /* 4 */
119   copy_all_from(st->temp, st->qwad, 0,        st->self, st->XOR);  /* 5 */
120   copy_all_from(st->self, st->qwad, 0,        st->self, st->IOR);  /* 6 */
121   copy_all_to  (st->temp, st->qwad, 0,        st->self, st->XOR);  /* 7 */
122   copy_all_to  (st->self, 0,        0,        st->temp, st->CPY);  /* 8 */
123   copy_all_from(st->temp, st->qwad,           st->qwad, st->self,st->XOR);/*9*/
124   copy_all_to  (st->mask, 0,        0,        st->temp, st->AND);  /* A */
125   copy_all_to  (st->temp, 0,        0,        st->self, st->XOR);  /* B */
126   copy_all_to  (st->temp, st->qwad, st->qwad, st->self, st->XOR);  /* C */
127   copy_all_from(st->mask, st->qwad>>1,st->qwad>>1,st->mask,st->AND); /* D */
128   copy_all_to  (st->mask, st->qwad, 0,        st->mask, st->IOR);  /* E */
129   copy_all_to  (st->mask, 0,        st->qwad, st->mask, st->IOR);  /* F */
130   display (st, st->self);
131
132   st->qwad >>= 1;
133   if (st->qwad == 0)  /* done with this round */
134     {
135       st->qwad = -1;
136       this_delay = st->delay2;
137     }
138
139   return this_delay;
140 }
141
142
143 static int 
144 to_pow2(struct state *st, int n, Bool up)
145 {
146   /* sizeof(Dimension) == 2. */
147   int powers_of_2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
148                         2048, 4096, 8192, 16384, 32768, 65536 };
149   int i = 0;
150   if (n > 65536) st->size = 65536;
151   while (n >= powers_of_2[i]) i++;
152   if (n == powers_of_2[i-1])
153     return n;
154   else
155     return powers_of_2[up ? i : i-1];
156 }
157
158 static void *
159 blitspin_init (Display *d_arg, Window w_arg)
160 {
161   struct state *st = (struct state *) calloc (1, sizeof(*st));
162   char *bitmap_name;
163
164   st->dpy = d_arg;
165   st->window = w_arg;
166
167   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
168
169   st->fg = get_pixel_resource (st->dpy, st->xgwa.colormap,
170                                "foreground", "Foreground");
171   st->bg = get_pixel_resource (st->dpy, st->xgwa.colormap,
172                                "background", "Background");
173   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
174   st->delay2 = get_integer_resource (st->dpy, "delay2", "Integer");
175   st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
176   if (st->delay < 0) st->delay = 0;
177   if (st->delay2 < 0) st->delay2 = 0;
178   if (st->duration < 1) st->duration = 1;
179
180   st->start_time = time ((time_t) 0);
181
182   bitmap_name = get_string_resource (st->dpy, "bitmap", "Bitmap");
183   if (! bitmap_name || !*bitmap_name)
184     bitmap_name = "(default)";
185
186   if (!strcasecmp (bitmap_name, "(default)") ||
187       !strcasecmp (bitmap_name, "default"))
188 # ifdef HAVE_COCOA
189     /* I don't understand why it doesn't work with color images on OSX.
190        I guess "kCGBlendModeDarken" is not close enough to being "GXand".
191      */
192     bitmap_name = "(builtin)";
193 # else
194     bitmap_name = "(screen)";
195 # endif
196
197   if (!strcasecmp (bitmap_name, "(builtin)") ||
198       !strcasecmp (bitmap_name, "builtin"))
199     {
200       st->width = som_width;
201       st->height = som_height;
202       st->bitmap = XCreatePixmapFromBitmapData (st->dpy, st->window,
203                                                 (char *) som_bits,
204                                                 st->width, st->height, 
205                                                 st->fg, st->bg, 
206                                                 st->xgwa.depth);
207       st->scale_up = True; /* definitely. */
208     }
209   else if (!strcasecmp (bitmap_name, "(screen)") ||
210            !strcasecmp (bitmap_name, "screen"))
211     {
212       st->bitmap = XCreatePixmap (st->dpy, st->window, 
213                                   st->xgwa.width, st->xgwa.height,
214                                   st->xgwa.depth);
215       st->width = st->xgwa.width;
216       st->height = st->xgwa.height;
217       st->scale_up = True; /* maybe? */
218       st->img_loader = load_image_async_simple (0, st->xgwa.screen, st->window,
219                                             st->bitmap, 0, 0);
220     }
221   else
222     {
223       st->bitmap = xpm_file_to_pixmap (st->dpy, st->window, bitmap_name,
224                                    &st->width, &st->height, 0);
225       st->scale_up = True; /* probably? */
226     }
227
228   return st;
229 }
230
231
232 static void
233 blitspin_init_2 (struct state *st)
234 {
235   XGCValues gcv;
236
237   /* make it square */
238   st->size = (st->width < st->height) ? st->height : st->width;
239   st->size = to_pow2(st, st->size, st->scale_up); /* round up to power of 2 */
240   {                                             /* don't exceed screen size */
241     int s = XScreenNumberOfScreen(st->xgwa.screen);
242     int w = to_pow2(st, XDisplayWidth(st->dpy, s), False);
243     int h = to_pow2(st, XDisplayHeight(st->dpy, s), False);
244     if (st->size > w) st->size = w;
245     if (st->size > h) st->size = h;
246   }
247
248   st->self = XCreatePixmap (st->dpy, st->window, st->size, st->size, st->xgwa.depth);
249   st->temp = XCreatePixmap (st->dpy, st->window, st->size, st->size, st->xgwa.depth);
250   st->mask = XCreatePixmap (st->dpy, st->window, st->size, st->size, st->xgwa.depth);
251   gcv.foreground = (st->xgwa.depth == 1 ? 1 : (~0));
252   gcv.function=GXset;  st->SET = XCreateGC(st->dpy,st->self,GCFunction|GCForeground,&gcv);
253   gcv.function=GXclear;st->CLR = XCreateGC(st->dpy,st->self,GCFunction|GCForeground,&gcv);
254   gcv.function=GXcopy; st->CPY = XCreateGC(st->dpy,st->self,GCFunction|GCForeground,&gcv);
255   gcv.function=GXor;   st->IOR = XCreateGC(st->dpy,st->self,GCFunction|GCForeground,&gcv);
256   gcv.function=GXand;  st->AND = XCreateGC(st->dpy,st->self,GCFunction|GCForeground,&gcv);
257   gcv.function=GXxor;  st->XOR = XCreateGC(st->dpy,st->self,GCFunction|GCForeground,&gcv);
258
259   gcv.foreground = gcv.background = st->bg;
260   st->gc = XCreateGC (st->dpy, st->window, GCForeground|GCBackground, &gcv);
261   /* Clear st->self to the background color (not to 0, which st->CLR does.) */
262   XFillRectangle (st->dpy, st->self, st->gc, 0, 0, st->size, st->size);
263   XSetForeground (st->dpy, st->gc, st->fg);
264
265 #if 0
266 #ifdef HAVE_COCOA
267   jwxyz_XSetAntiAliasing (st->dpy, st->gc,  False);
268   jwxyz_XSetAntiAliasing (st->dpy, st->SET, False);
269   jwxyz_XSetAntiAliasing (st->dpy, st->CLR, False);
270   jwxyz_XSetAntiAliasing (st->dpy, st->CPY, False);
271   jwxyz_XSetAntiAliasing (st->dpy, st->IOR, False);
272   jwxyz_XSetAntiAliasing (st->dpy, st->AND, False);
273   jwxyz_XSetAntiAliasing (st->dpy, st->XOR, False);
274 #endif /* HAVE_COCOA */
275 #endif
276
277   XCopyArea (st->dpy, st->bitmap, st->self, st->CPY, 0, 0, 
278              st->width, st->height,
279              (st->size - st->width)  >> 1,
280              (st->size - st->height) >> 1);
281 /*  XFreePixmap(st->dpy, st->bitmap);*/
282
283   st->qwad = -1;
284   st->first_time = 1;
285 }
286
287 static void
288 display (struct state *st, Pixmap pixmap)
289 {
290   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
291
292   if (st->xgwa.width != st->last_w || 
293       st->xgwa.height != st->last_h)
294     {
295       XClearWindow (st->dpy, st->window);
296       st->last_w = st->xgwa.width;
297       st->last_h = st->xgwa.height;
298     }
299   if (st->xgwa.depth != 1)
300     XCopyArea (st->dpy, pixmap, st->window, st->gc, 0, 0, st->size, st->size,
301                (st->xgwa.width - st->size) >> 1,
302                (st->xgwa.height - st->size) >> 1);
303   else
304     XCopyPlane (st->dpy, pixmap, st->window, st->gc, 0, 0, st->size, st->size,
305                 (st->xgwa.width - st->size) >> 1,
306                 (st->xgwa.height - st->size) >> 1,
307                 1);
308 /*
309   XDrawRectangle (st->dpy, st->window, st->gc,
310                   ((st->xgwa.width - st->size) >> 1) - 1,
311                   ((st->xgwa.height - st->size) >> 1) - 1,
312                   st->size+2, st->size+2);
313 */
314 }
315
316 static void
317 blitspin_reshape (Display *dpy, Window window, void *closure, 
318                   unsigned int w, unsigned int h)
319 {
320 }
321
322 static Bool
323 blitspin_event (Display *dpy, Window window, void *closure, XEvent *event)
324 {
325   return False;
326 }
327
328 static void
329 blitspin_free (Display *dpy, Window window, void *closure)
330 {
331 }
332
333 \f
334 static const char *blitspin_defaults [] = {
335   ".background: black",
336   ".foreground: white",
337   "*delay:      500000",
338   "*delay2:     500000",
339   "*duration:   120",
340   "*bitmap:     (default)",
341   "*geometry:   512x512",
342   0
343 };
344
345 static XrmOptionDescRec blitspin_options [] = {
346   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
347   { "-delay2",          ".delay2",      XrmoptionSepArg, 0 },
348   { "-duration",        ".duration",    XrmoptionSepArg, 0 },
349   { "-bitmap",          ".bitmap",      XrmoptionSepArg, 0 },
350   { 0, 0, 0, 0 }
351 };
352
353
354 XSCREENSAVER_MODULE ("BlitSpin", blitspin)