1 /* xscreensaver, Copyright (c) 1992-2014 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"
29 #include "xpm-pixmap.h"
33 #include "images/som.xbm"
35 /* Implementing this using XCopyArea doesn't work with color images on OSX.
36 This means that the Cocoa implementation of XCopyArea in jwxyz.m is
37 broken with the GXor, GXand, and/or the GXxor GC operations. This
38 probably means that (e.g.) "kCGBlendModeDarken" is not close enough
39 to being "GXand" to use for that. (It works with monochrome images,
42 So, on OSX, we implement the blitter by hand. It is correct, but
43 orders of magnitude slower.
46 # define USE_XCOPYAREA
52 XWindowAttributes xgwa;
53 int width, height, size;
55 Pixmap self, temp, mask;
57 GC gc_set, gc_clear, gc_copy, gc_and, gc_or, gc_xor;
65 int qwad; /* fuckin' C, man... who needs namespaces? */
72 async_load_state *img_loader;
75 static void display (struct state *, Pixmap);
76 static void blitspin_init_2 (struct state *);
78 #define copy_to(from, xoff, yoff, to, op) \
79 bitblt (st, st->from, st->to, op, 0, 0, \
80 st->size-(xoff), st->size-(yoff), (xoff), (yoff))
82 #define copy_from(to, xoff, yoff, from, op) \
83 bitblt (st, st->from, st->to, op, (xoff), (yoff), \
84 st->size-(xoff), st->size-(yoff), 0, 0)
88 # define bitblt(st, from, to, op, src_x, src_y, w, h, dst_x, dst_y) \
89 XCopyArea((st)->dpy, (from), (to), (st)->gc_##op, \
90 (src_x), (src_y), (w), (h), (dst_x), (dst_y))
91 #else /* !USE_XCOPYAREA */
93 # define bitblt(st, from, to, op, src_x, src_y, w, h, dst_x, dst_y) \
94 do_bitblt((st)->dpy, (from), (to), st->gc, GX##op, \
95 (src_x), (src_y), (w), (h), (dst_x), (dst_y))
98 do_bitblt (Display *dpy, Drawable src, Drawable dst, GC gc, int op,
100 unsigned int width, unsigned int height,
101 int dst_x, int dst_y)
105 XSetForeground (dpy, gc, 0xFF000000); /* ARGB black for Cocoa */
106 XFillRectangle (dpy, dst, gc, dst_x, dst_y, width, height);
108 else if (op == GXset)
110 XSetForeground (dpy, gc, ~0L);
111 XFillRectangle (dpy, dst, gc, dst_x, dst_y, width, height);
113 else if (op == GXcopy)
115 XCopyArea (dpy, src, dst, gc, src_x, src_y, width, height, dst_x, dst_y);
119 XImage *srci = XGetImage (dpy, src, src_x, src_y, width, height,
121 XImage *dsti = XGetImage (dpy, dst, dst_x, dst_y, width, height,
123 unsigned long *out = (unsigned long *) dsti->data;
124 unsigned long *in = (unsigned long *) srci->data;
125 unsigned long *end = (in + (height * srci->bytes_per_line
126 / sizeof(unsigned long)));
129 case GXor: while (in < end) { *out++ |= *in++; } break;
130 case GXand: while (in < end) { *out++ &= *in++; } break;
131 case GXxor: while (in < end) { *out++ ^= *in++; } break;
134 XPutImage (dpy, dst, gc, dsti, 0, 0, dst_x, dst_y, width, height);
135 XDestroyImage (srci);
136 XDestroyImage (dsti);
140 #endif /* !USE_XCOPYAREA */
145 blitspin_draw (Display *dpy, Window window, void *closure)
147 struct state *st = (struct state *) closure;
148 int this_delay = st->delay;
151 if (st->img_loader) /* still loading */
153 st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
155 if (!st->img_loader) { /* just finished */
159 st->start_time = time ((time_t *) 0);
160 blitspin_init_2 (st);
163 /* Rotate nothing if the very first image is not yet loaded */
168 if (!st->img_loader &&
170 st->start_time + st->duration < time ((time_t *) 0)) {
171 /* Start a new image loading, but keep rotating the old image
172 until the new one arrives. */
173 st->img_loader = load_image_async_simple (0, st->xgwa.screen, st->window,
179 bitblt(st, st->mask, st->mask, clear,0,0, st->size, st->size, 0,0);
180 bitblt(st, st->mask, st->mask, set, 0,0, st->size>>1, st->size>>1, 0,0);
181 st->qwad = st->size>>1;
187 display (st, st->self);
191 /* for (st->qwad = st->size>>1; st->qwad > 0; st->qwad>>=1) */
195 copy_to (mask, 0, 0, temp, copy); /* 1 */
196 copy_to (mask, 0, qwad, temp, or); /* 2 */
197 copy_to (self, 0, 0, temp, and); /* 3 */
198 copy_to (temp, 0, 0, self, xor); /* 4 */
199 copy_from (temp, qwad, 0, self, xor); /* 5 */
200 copy_from (self, qwad, 0, self, or); /* 6 */
201 copy_to (temp, qwad, 0, self, xor); /* 7 */
202 copy_to (self, 0, 0, temp, copy); /* 8 */
203 copy_from (temp, qwad, qwad, self, xor); /* 9 */
204 copy_to (mask, 0, 0, temp, and); /* A */
205 copy_to (temp, 0, 0, self, xor); /* B */
206 copy_to (temp, qwad, qwad, self, xor); /* C */
207 copy_from (mask, qwad>>1, qwad>>1, mask, and); /* D */
208 copy_to (mask, qwad, 0, mask, or); /* E */
209 copy_to (mask, 0, qwad, mask, or); /* F */
210 display (st, st->self);
213 if (st->qwad == 0) /* done with this round */
216 this_delay = st->delay2;
224 blitspin_to_pow2(int n, Bool up)
226 int pow2 = to_pow2 (n);
230 return up ? pow2 : pow2 >> 1;
234 blitspin_init (Display *d_arg, Window w_arg)
236 struct state *st = (struct state *) calloc (1, sizeof(*st));
242 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
244 st->fg = get_pixel_resource (st->dpy, st->xgwa.colormap,
245 "foreground", "Foreground");
246 st->bg = get_pixel_resource (st->dpy, st->xgwa.colormap,
247 "background", "Background");
248 st->delay = get_integer_resource (st->dpy, "delay", "Integer");
249 st->delay2 = get_integer_resource (st->dpy, "delay2", "Integer");
250 st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
251 if (st->delay < 0) st->delay = 0;
252 if (st->delay2 < 0) st->delay2 = 0;
253 if (st->duration < 1) st->duration = 1;
255 st->start_time = time ((time_t *) 0);
257 bitmap_name = get_string_resource (st->dpy, "bitmap", "Bitmap");
258 if (! bitmap_name || !*bitmap_name)
259 bitmap_name = "(default)";
261 if (!strcasecmp (bitmap_name, "(default)") ||
262 !strcasecmp (bitmap_name, "default"))
263 bitmap_name = "(screen)";
265 if (!strcasecmp (bitmap_name, "(builtin)") ||
266 !strcasecmp (bitmap_name, "builtin"))
268 st->width = som_width;
269 st->height = som_height;
270 st->bitmap = XCreatePixmapFromBitmapData (st->dpy, st->window,
272 st->width, st->height,
275 st->scale_up = True; /* definitely. */
277 blitspin_init_2 (st);
279 else if (!strcasecmp (bitmap_name, "(screen)") ||
280 !strcasecmp (bitmap_name, "screen"))
282 st->bitmap = XCreatePixmap (st->dpy, st->window,
283 st->xgwa.width, st->xgwa.height,
285 st->width = st->xgwa.width;
286 st->height = st->xgwa.height;
287 st->scale_up = True; /* maybe? */
288 st->load_ext_p = True;
289 st->img_loader = load_image_async_simple (0, st->xgwa.screen, st->window,
294 st->bitmap = xpm_file_to_pixmap (st->dpy, st->window, bitmap_name,
295 &st->width, &st->height, 0);
296 st->scale_up = True; /* probably? */
297 blitspin_init_2 (st);
305 blitspin_init_2 (struct state *st)
310 st->size = (st->width < st->height) ? st->height : st->width;
311 /* round up to power of 2 */
312 st->size = blitspin_to_pow2(st->size, st->scale_up);
313 { /* don't exceed screen size */
314 int s = XScreenNumberOfScreen(st->xgwa.screen);
315 int w = blitspin_to_pow2(XDisplayWidth(st->dpy, s), False);
316 int h = blitspin_to_pow2(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)
400 struct state *st = (struct state *) closure;
401 if (screenhack_event_helper (dpy, window, event))
410 blitspin_free (Display *dpy, Window window, void *closure)
415 static const char *blitspin_defaults [] = {
416 ".background: black",
417 ".foreground: white",
422 "*bitmap: (default)",
423 "*geometry: 1080x1080",
425 "*ignoreRotation: True",
430 static XrmOptionDescRec blitspin_options [] = {
431 { "-delay", ".delay", XrmoptionSepArg, 0 },
432 { "-delay2", ".delay2", XrmoptionSepArg, 0 },
433 { "-duration", ".duration", XrmoptionSepArg, 0 },
434 { "-bitmap", ".bitmap", XrmoptionSepArg, 0 },
439 XSCREENSAVER_MODULE ("BlitSpin", blitspin)