1 /* xscreensaver, Copyright (c) 1992-2008 Jamie Zawinski <jwz@jwz.org>
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
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.
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
23 It's too bad almost nothing uses blitter hardware these days,
24 or this might actually win.
27 #include "screenhack.h"
28 #include "xpm-pixmap.h"
31 #include "images/som.xbm"
36 XWindowAttributes xgwa;
37 int width, height, size;
39 Pixmap self, temp, mask;
40 GC SET, CLR, CPY, IOR, AND, XOR;
47 int qwad; /* fuckin' C, man... who needs namespaces? */
53 async_load_state *img_loader;
56 static void display (struct state *, Pixmap);
57 static void blitspin_init_2 (struct state *);
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))
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)
68 blitspin_draw (Display *dpy, Window window, void *closure)
70 struct state *st = (struct state *) closure;
71 int this_delay = st->delay;
73 if (st->img_loader) /* still loading */
75 st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
77 if (!st->img_loader) { /* just finished */
81 st->start_time = time ((time_t) 0);
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,
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;
109 display (st, st->self);
113 /* for (st->qwad = st->size>>1; st->qwad > 0; st->qwad>>=1) */
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);
133 if (st->qwad == 0) /* done with this round */
136 this_delay = st->delay2;
144 to_pow2(struct state *st, int n, Bool up)
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 };
150 if (n > 65536) st->size = 65536;
151 while (n >= powers_of_2[i]) i++;
152 if (n == powers_of_2[i-1])
155 return powers_of_2[up ? i : i-1];
159 blitspin_init (Display *d_arg, Window w_arg)
161 struct state *st = (struct state *) calloc (1, sizeof(*st));
167 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
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;
180 st->start_time = time ((time_t) 0);
182 bitmap_name = get_string_resource (st->dpy, "bitmap", "Bitmap");
183 if (! bitmap_name || !*bitmap_name)
184 bitmap_name = "(default)";
186 if (!strcasecmp (bitmap_name, "(default)") ||
187 !strcasecmp (bitmap_name, "default"))
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".
192 bitmap_name = "(builtin)";
194 bitmap_name = "(screen)";
197 if (!strcasecmp (bitmap_name, "(builtin)") ||
198 !strcasecmp (bitmap_name, "builtin"))
200 st->width = som_width;
201 st->height = som_height;
202 st->bitmap = XCreatePixmapFromBitmapData (st->dpy, st->window,
204 st->width, st->height,
207 st->scale_up = True; /* definitely. */
209 else if (!strcasecmp (bitmap_name, "(screen)") ||
210 !strcasecmp (bitmap_name, "screen"))
212 st->bitmap = XCreatePixmap (st->dpy, st->window,
213 st->xgwa.width, st->xgwa.height,
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,
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? */
233 blitspin_init_2 (struct state *st)
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;
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);
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);
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 */
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);*/
288 display (struct state *st, Pixmap pixmap)
290 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
292 if (st->xgwa.width != st->last_w ||
293 st->xgwa.height != st->last_h)
295 XClearWindow (st->dpy, st->window);
296 st->last_w = st->xgwa.width;
297 st->last_h = st->xgwa.height;
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);
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,
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);
317 blitspin_reshape (Display *dpy, Window window, void *closure,
318 unsigned int w, unsigned int h)
323 blitspin_event (Display *dpy, Window window, void *closure, XEvent *event)
329 blitspin_free (Display *dpy, Window window, void *closure)
334 static const char *blitspin_defaults [] = {
335 ".background: black",
336 ".foreground: white",
340 "*bitmap: (default)",
341 "*geometry: 512x512",
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 },
354 XSCREENSAVER_MODULE ("BlitSpin", blitspin)