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"
33 /* Implementing this using XCopyArea doesn't work with color images on OSX.
34 This means that the Cocoa implementation of XCopyArea in jwxyz.m is
35 broken with the GXor, GXand, and/or the GXxor GC operations. This
36 probably means that (e.g.) "kCGBlendModeDarken" is not close enough
37 to being "GXand" to use for that. (It works with monochrome images,
40 So, on OSX, we implement the blitter by hand. It is correct, but
41 orders of magnitude slower.
44 # define USE_XCOPYAREA
50 XWindowAttributes xgwa;
51 int width, height, size;
53 Pixmap self, temp, mask;
55 GC gc_set, gc_clear, gc_copy, gc_and, gc_or, gc_xor;
63 int qwad; /* fuckin' C, man... who needs namespaces? */
70 async_load_state *img_loader;
73 static void display (struct state *, Pixmap);
74 static void blitspin_init_2 (struct state *);
76 #define copy_to(from, xoff, yoff, to, op) \
77 bitblt (st, st->from, st->to, op, 0, 0, \
78 st->size-(xoff), st->size-(yoff), (xoff), (yoff))
80 #define copy_from(to, xoff, yoff, from, op) \
81 bitblt (st, st->from, st->to, op, (xoff), (yoff), \
82 st->size-(xoff), st->size-(yoff), 0, 0)
86 # define bitblt(st, from, to, op, src_x, src_y, w, h, dst_x, dst_y) \
87 XCopyArea((st)->dpy, (from), (to), (st)->gc_##op, \
88 (src_x), (src_y), (w), (h), (dst_x), (dst_y))
89 #else /* !USE_XCOPYAREA */
91 # define bitblt(st, from, to, op, src_x, src_y, w, h, dst_x, dst_y) \
92 do_bitblt((st)->dpy, (from), (to), st->gc, GX##op, \
93 (src_x), (src_y), (w), (h), (dst_x), (dst_y))
96 do_bitblt (Display *dpy, Drawable src, Drawable dst, GC gc, int op,
98 unsigned int width, unsigned int height,
103 XSetForeground (dpy, gc, 0xFF000000); /* ARGB black for Cocoa */
104 XFillRectangle (dpy, dst, gc, dst_x, dst_y, width, height);
106 else if (op == GXset)
108 XSetForeground (dpy, gc, ~0L);
109 XFillRectangle (dpy, dst, gc, dst_x, dst_y, width, height);
111 else if (op == GXcopy)
113 XCopyArea (dpy, src, dst, gc, src_x, src_y, width, height, dst_x, dst_y);
117 XImage *srci = XGetImage (dpy, src, src_x, src_y, width, height,
119 XImage *dsti = XGetImage (dpy, dst, dst_x, dst_y, width, height,
121 unsigned long *out = (unsigned long *) dsti->data;
122 unsigned long *in = (unsigned long *) srci->data;
123 unsigned long *end = (in + (height * srci->bytes_per_line
124 / sizeof(unsigned long)));
127 case GXor: while (in < end) { *out++ |= *in++; } break;
128 case GXand: while (in < end) { *out++ &= *in++; } break;
129 case GXxor: while (in < end) { *out++ ^= *in++; } break;
132 XPutImage (dpy, dst, gc, dsti, 0, 0, dst_x, dst_y, width, height);
133 XDestroyImage (srci);
134 XDestroyImage (dsti);
138 #endif /* !USE_XCOPYAREA */
143 blitspin_draw (Display *dpy, Window window, void *closure)
145 struct state *st = (struct state *) closure;
146 int this_delay = st->delay;
149 if (st->img_loader) /* still loading */
151 st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
153 if (!st->img_loader) { /* just finished */
157 st->start_time = time ((time_t) 0);
158 blitspin_init_2 (st);
161 /* Rotate nothing if the very first image is not yet loaded */
166 if (!st->img_loader &&
168 st->start_time + st->duration < time ((time_t) 0)) {
169 /* Start a new image loading, but keep rotating the old image
170 until the new one arrives. */
171 st->img_loader = load_image_async_simple (0, st->xgwa.screen, st->window,
177 bitblt(st, st->mask, st->mask, clear,0,0, st->size, st->size, 0,0);
178 bitblt(st, st->mask, st->mask, set, 0,0, st->size>>1, st->size>>1, 0,0);
179 st->qwad = st->size>>1;
185 display (st, st->self);
189 /* for (st->qwad = st->size>>1; st->qwad > 0; st->qwad>>=1) */
193 copy_to (mask, 0, 0, temp, copy); /* 1 */
194 copy_to (mask, 0, qwad, temp, or); /* 2 */
195 copy_to (self, 0, 0, temp, and); /* 3 */
196 copy_to (temp, 0, 0, self, xor); /* 4 */
197 copy_from (temp, qwad, 0, self, xor); /* 5 */
198 copy_from (self, qwad, 0, self, or); /* 6 */
199 copy_to (temp, qwad, 0, self, xor); /* 7 */
200 copy_to (self, 0, 0, temp, copy); /* 8 */
201 copy_from (temp, qwad, qwad, self, xor); /* 9 */
202 copy_to (mask, 0, 0, temp, and); /* A */
203 copy_to (temp, 0, 0, self, xor); /* B */
204 copy_to (temp, qwad, qwad, self, xor); /* C */
205 copy_from (mask, qwad>>1, qwad>>1, mask, and); /* D */
206 copy_to (mask, qwad, 0, mask, or); /* E */
207 copy_to (mask, 0, qwad, mask, or); /* F */
208 display (st, st->self);
211 if (st->qwad == 0) /* done with this round */
214 this_delay = st->delay2;
222 to_pow2(struct state *st, int n, Bool up)
224 int powers_of_2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
225 2048, 4096, 8192, 16384, 32768, 65536 };
227 if (n > 65536) st->size = 65536;
228 while (n >= powers_of_2[i]) i++;
229 if (n == powers_of_2[i-1])
232 return powers_of_2[up ? i : i-1];
236 blitspin_init (Display *d_arg, Window w_arg)
238 struct state *st = (struct state *) calloc (1, sizeof(*st));
244 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
246 st->fg = get_pixel_resource (st->dpy, st->xgwa.colormap,
247 "foreground", "Foreground");
248 st->bg = get_pixel_resource (st->dpy, st->xgwa.colormap,
249 "background", "Background");
250 st->delay = get_integer_resource (st->dpy, "delay", "Integer");
251 st->delay2 = get_integer_resource (st->dpy, "delay2", "Integer");
252 st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
253 if (st->delay < 0) st->delay = 0;
254 if (st->delay2 < 0) st->delay2 = 0;
255 if (st->duration < 1) st->duration = 1;
257 st->start_time = time ((time_t) 0);
259 bitmap_name = get_string_resource (st->dpy, "bitmap", "Bitmap");
260 if (! bitmap_name || !*bitmap_name)
261 bitmap_name = "(default)";
263 if (!strcasecmp (bitmap_name, "(default)") ||
264 !strcasecmp (bitmap_name, "default"))
265 bitmap_name = "(screen)";
267 if (!strcasecmp (bitmap_name, "(builtin)") ||
268 !strcasecmp (bitmap_name, "builtin"))
270 st->width = som_width;
271 st->height = som_height;
272 st->bitmap = XCreatePixmapFromBitmapData (st->dpy, st->window,
274 st->width, st->height,
277 st->scale_up = True; /* definitely. */
279 blitspin_init_2 (st);
281 else if (!strcasecmp (bitmap_name, "(screen)") ||
282 !strcasecmp (bitmap_name, "screen"))
284 st->bitmap = XCreatePixmap (st->dpy, st->window,
285 st->xgwa.width, st->xgwa.height,
287 st->width = st->xgwa.width;
288 st->height = st->xgwa.height;
289 st->scale_up = True; /* maybe? */
290 st->load_ext_p = True;
291 st->img_loader = load_image_async_simple (0, st->xgwa.screen, st->window,
296 st->bitmap = xpm_file_to_pixmap (st->dpy, st->window, bitmap_name,
297 &st->width, &st->height, 0);
298 st->scale_up = True; /* probably? */
306 blitspin_init_2 (struct state *st)
311 st->size = (st->width < st->height) ? st->height : st->width;
312 st->size = to_pow2(st, st->size, st->scale_up); /* round up to power of 2 */
313 { /* don't exceed screen size */
314 int s = XScreenNumberOfScreen(st->xgwa.screen);
315 int w = to_pow2(st, XDisplayWidth(st->dpy, s), False);
316 int h = to_pow2(st, XDisplayHeight(st->dpy, s), False);
317 if (st->size > w) st->size = w;
318 if (st->size > h) st->size = h;
321 if (st->self) XFreePixmap (st->dpy, st->self);
322 if (st->temp) XFreePixmap (st->dpy, st->temp);
323 if (st->mask) XFreePixmap (st->dpy, st->mask);
325 st->self = XCreatePixmap (st->dpy, st->window, st->size, st->size,
327 st->temp = XCreatePixmap (st->dpy, st->window, st->size, st->size,
329 st->mask = XCreatePixmap (st->dpy, st->window, st->size, st->size,
331 gcv.foreground = (st->xgwa.depth == 1 ? 1 : (~0));
333 # ifdef USE_XCOPYAREA
334 # define make_gc(op) \
335 gcv.function=GX##op; \
336 if (st->gc_##op) XFreeGC (st->dpy, st->gc_##op); \
337 st->gc_##op = XCreateGC (st->dpy, st->self, GCFunction|GCForeground, &gcv)
344 # endif /* USE_XCOPYAREA */
346 gcv.foreground = gcv.background = st->bg;
347 if (st->gc) XFreeGC (st->dpy, st->gc);
348 st->gc = XCreateGC (st->dpy, st->window, GCForeground|GCBackground, &gcv);
349 /* Clear st->self to the background color (not to 0, which 'clear' does.) */
350 XFillRectangle (st->dpy, st->self, st->gc, 0, 0, st->size, st->size);
351 XSetForeground (st->dpy, st->gc, st->fg);
353 XCopyArea (st->dpy, st->bitmap, st->self, st->gc, 0, 0,
354 st->width, st->height,
355 (st->size - st->width) >> 1,
356 (st->size - st->height) >> 1);
363 display (struct state *st, Pixmap pixmap)
365 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
367 if (st->xgwa.width != st->last_w ||
368 st->xgwa.height != st->last_h)
370 XClearWindow (st->dpy, st->window);
371 st->last_w = st->xgwa.width;
372 st->last_h = st->xgwa.height;
374 if (st->xgwa.depth != 1)
375 XCopyArea (st->dpy, pixmap, st->window, st->gc, 0, 0, st->size, st->size,
376 (st->xgwa.width - st->size) >> 1,
377 (st->xgwa.height - st->size) >> 1);
379 XCopyPlane (st->dpy, pixmap, st->window, st->gc, 0, 0, st->size, st->size,
380 (st->xgwa.width - st->size) >> 1,
381 (st->xgwa.height - st->size) >> 1,
384 XDrawRectangle (st->dpy, st->window, st->gc,
385 ((st->xgwa.width - st->size) >> 1) - 1,
386 ((st->xgwa.height - st->size) >> 1) - 1,
387 st->size+2, st->size+2);
392 blitspin_reshape (Display *dpy, Window window, void *closure,
393 unsigned int w, unsigned int h)
398 blitspin_event (Display *dpy, Window window, void *closure, XEvent *event)
404 blitspin_free (Display *dpy, Window window, void *closure)
409 static const char *blitspin_defaults [] = {
410 ".background: black",
411 ".foreground: white",
416 "*bitmap: (default)",
417 "*geometry: 512x512",
421 static XrmOptionDescRec blitspin_options [] = {
422 { "-delay", ".delay", XrmoptionSepArg, 0 },
423 { "-delay2", ".delay2", XrmoptionSepArg, 0 },
424 { "-duration", ".duration", XrmoptionSepArg, 0 },
425 { "-bitmap", ".bitmap", XrmoptionSepArg, 0 },
430 XSCREENSAVER_MODULE ("BlitSpin", blitspin)