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"
28 #include "xpm-pixmap.h"
32 #include "images/som.xbm"
34 /* Implementing this using XCopyArea doesn't work with color images on OSX.
35 This means that the Cocoa implementation of XCopyArea in jwxyz.m is
36 broken with the GXor, GXand, and/or the GXxor GC operations. This
37 probably means that (e.g.) "kCGBlendModeDarken" is not close enough
38 to being "GXand" to use for that. (It works with monochrome images,
41 So, on OSX, we implement the blitter by hand. It is correct, but
42 orders of magnitude slower.
45 # define USE_XCOPYAREA
51 XWindowAttributes xgwa;
52 int width, height, size;
54 Pixmap self, temp, mask;
56 GC gc_set, gc_clear, gc_copy, gc_and, gc_or, gc_xor;
64 int qwad; /* fuckin' C, man... who needs namespaces? */
71 async_load_state *img_loader;
74 static void display (struct state *, Pixmap);
75 static void blitspin_init_2 (struct state *);
77 #define copy_to(from, xoff, yoff, to, op) \
78 bitblt (st, st->from, st->to, op, 0, 0, \
79 st->size-(xoff), st->size-(yoff), (xoff), (yoff))
81 #define copy_from(to, xoff, yoff, from, op) \
82 bitblt (st, st->from, st->to, op, (xoff), (yoff), \
83 st->size-(xoff), st->size-(yoff), 0, 0)
87 # define bitblt(st, from, to, op, src_x, src_y, w, h, dst_x, dst_y) \
88 XCopyArea((st)->dpy, (from), (to), (st)->gc_##op, \
89 (src_x), (src_y), (w), (h), (dst_x), (dst_y))
90 #else /* !USE_XCOPYAREA */
92 # define bitblt(st, from, to, op, src_x, src_y, w, h, dst_x, dst_y) \
93 do_bitblt((st)->dpy, (from), (to), st->gc, GX##op, \
94 (src_x), (src_y), (w), (h), (dst_x), (dst_y))
97 do_bitblt (Display *dpy, Drawable src, Drawable dst, GC gc, int op,
99 unsigned int width, unsigned int height,
100 int dst_x, int dst_y)
104 XSetForeground (dpy, gc, 0xFF000000); /* ARGB black for Cocoa */
105 XFillRectangle (dpy, dst, gc, dst_x, dst_y, width, height);
107 else if (op == GXset)
109 XSetForeground (dpy, gc, ~0L);
110 XFillRectangle (dpy, dst, gc, dst_x, dst_y, width, height);
112 else if (op == GXcopy)
114 XCopyArea (dpy, src, dst, gc, src_x, src_y, width, height, dst_x, dst_y);
118 XImage *srci = XGetImage (dpy, src, src_x, src_y, width, height,
120 XImage *dsti = XGetImage (dpy, dst, dst_x, dst_y, width, height,
122 unsigned long *out = (unsigned long *) dsti->data;
123 unsigned long *in = (unsigned long *) srci->data;
124 unsigned long *end = (in + (height * srci->bytes_per_line
125 / sizeof(unsigned long)));
128 case GXor: while (in < end) { *out++ |= *in++; } break;
129 case GXand: while (in < end) { *out++ &= *in++; } break;
130 case GXxor: while (in < end) { *out++ ^= *in++; } break;
133 XPutImage (dpy, dst, gc, dsti, 0, 0, dst_x, dst_y, width, height);
134 XDestroyImage (srci);
135 XDestroyImage (dsti);
139 #endif /* !USE_XCOPYAREA */
144 blitspin_draw (Display *dpy, Window window, void *closure)
146 struct state *st = (struct state *) closure;
147 int this_delay = st->delay;
150 if (st->img_loader) /* still loading */
152 st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
154 if (!st->img_loader) { /* just finished */
158 st->start_time = time ((time_t *) 0);
159 blitspin_init_2 (st);
162 /* Rotate nothing if the very first image is not yet loaded */
167 if (!st->img_loader &&
169 st->start_time + st->duration < time ((time_t *) 0)) {
170 /* Start a new image loading, but keep rotating the old image
171 until the new one arrives. */
172 st->img_loader = load_image_async_simple (0, st->xgwa.screen, st->window,
178 bitblt(st, st->mask, st->mask, clear,0,0, st->size, st->size, 0,0);
179 bitblt(st, st->mask, st->mask, set, 0,0, st->size>>1, st->size>>1, 0,0);
180 st->qwad = st->size>>1;
186 display (st, st->self);
190 /* for (st->qwad = st->size>>1; st->qwad > 0; st->qwad>>=1) */
194 copy_to (mask, 0, 0, temp, copy); /* 1 */
195 copy_to (mask, 0, qwad, temp, or); /* 2 */
196 copy_to (self, 0, 0, temp, and); /* 3 */
197 copy_to (temp, 0, 0, self, xor); /* 4 */
198 copy_from (temp, qwad, 0, self, xor); /* 5 */
199 copy_from (self, qwad, 0, self, or); /* 6 */
200 copy_to (temp, qwad, 0, self, xor); /* 7 */
201 copy_to (self, 0, 0, temp, copy); /* 8 */
202 copy_from (temp, qwad, qwad, self, xor); /* 9 */
203 copy_to (mask, 0, 0, temp, and); /* A */
204 copy_to (temp, 0, 0, self, xor); /* B */
205 copy_to (temp, qwad, qwad, self, xor); /* C */
206 copy_from (mask, qwad>>1, qwad>>1, mask, and); /* D */
207 copy_to (mask, qwad, 0, mask, or); /* E */
208 copy_to (mask, 0, qwad, mask, or); /* F */
209 display (st, st->self);
212 if (st->qwad == 0) /* done with this round */
215 this_delay = st->delay2;
223 to_pow2(struct state *st, int n, Bool up)
225 int powers_of_2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
226 2048, 4096, 8192, 16384, 32768, 65536 };
228 if (n > 65536) st->size = 65536;
229 while (n >= powers_of_2[i]) i++;
230 if (n == powers_of_2[i-1])
233 return powers_of_2[up ? i : i-1];
237 blitspin_init (Display *d_arg, Window w_arg)
239 struct state *st = (struct state *) calloc (1, sizeof(*st));
245 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
247 st->fg = get_pixel_resource (st->dpy, st->xgwa.colormap,
248 "foreground", "Foreground");
249 st->bg = get_pixel_resource (st->dpy, st->xgwa.colormap,
250 "background", "Background");
251 st->delay = get_integer_resource (st->dpy, "delay", "Integer");
252 st->delay2 = get_integer_resource (st->dpy, "delay2", "Integer");
253 st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
254 if (st->delay < 0) st->delay = 0;
255 if (st->delay2 < 0) st->delay2 = 0;
256 if (st->duration < 1) st->duration = 1;
258 st->start_time = time ((time_t *) 0);
260 bitmap_name = get_string_resource (st->dpy, "bitmap", "Bitmap");
261 if (! bitmap_name || !*bitmap_name)
262 bitmap_name = "(default)";
264 if (!strcasecmp (bitmap_name, "(default)") ||
265 !strcasecmp (bitmap_name, "default"))
266 bitmap_name = "(screen)";
268 if (!strcasecmp (bitmap_name, "(builtin)") ||
269 !strcasecmp (bitmap_name, "builtin"))
271 st->width = som_width;
272 st->height = som_height;
273 st->bitmap = XCreatePixmapFromBitmapData (st->dpy, st->window,
275 st->width, st->height,
278 st->scale_up = True; /* definitely. */
280 blitspin_init_2 (st);
282 else if (!strcasecmp (bitmap_name, "(screen)") ||
283 !strcasecmp (bitmap_name, "screen"))
285 st->bitmap = XCreatePixmap (st->dpy, st->window,
286 st->xgwa.width, st->xgwa.height,
288 st->width = st->xgwa.width;
289 st->height = st->xgwa.height;
290 st->scale_up = True; /* maybe? */
291 st->load_ext_p = True;
292 st->img_loader = load_image_async_simple (0, st->xgwa.screen, st->window,
297 st->bitmap = xpm_file_to_pixmap (st->dpy, st->window, bitmap_name,
298 &st->width, &st->height, 0);
299 st->scale_up = True; /* probably? */
300 blitspin_init_2 (st);
308 blitspin_init_2 (struct state *st)
313 st->size = (st->width < st->height) ? st->height : st->width;
314 st->size = to_pow2(st, st->size, st->scale_up); /* round up to power of 2 */
315 { /* don't exceed screen size */
316 int s = XScreenNumberOfScreen(st->xgwa.screen);
317 int w = to_pow2(st, XDisplayWidth(st->dpy, s), False);
318 int h = to_pow2(st, XDisplayHeight(st->dpy, s), False);
319 if (st->size > w) st->size = w;
320 if (st->size > h) st->size = h;
323 if (st->self) XFreePixmap (st->dpy, st->self);
324 if (st->temp) XFreePixmap (st->dpy, st->temp);
325 if (st->mask) XFreePixmap (st->dpy, st->mask);
327 st->self = XCreatePixmap (st->dpy, st->window, st->size, st->size,
329 st->temp = XCreatePixmap (st->dpy, st->window, st->size, st->size,
331 st->mask = XCreatePixmap (st->dpy, st->window, st->size, st->size,
333 gcv.foreground = (st->xgwa.depth == 1 ? 1 : (~0));
335 # ifdef USE_XCOPYAREA
336 # define make_gc(op) \
337 gcv.function=GX##op; \
338 if (st->gc_##op) XFreeGC (st->dpy, st->gc_##op); \
339 st->gc_##op = XCreateGC (st->dpy, st->self, GCFunction|GCForeground, &gcv)
346 # endif /* USE_XCOPYAREA */
348 gcv.foreground = gcv.background = st->bg;
349 if (st->gc) XFreeGC (st->dpy, st->gc);
350 st->gc = XCreateGC (st->dpy, st->window, GCForeground|GCBackground, &gcv);
351 /* Clear st->self to the background color (not to 0, which 'clear' does.) */
352 XFillRectangle (st->dpy, st->self, st->gc, 0, 0, st->size, st->size);
353 XSetForeground (st->dpy, st->gc, st->fg);
355 XCopyArea (st->dpy, st->bitmap, st->self, st->gc, 0, 0,
356 st->width, st->height,
357 (st->size - st->width) >> 1,
358 (st->size - st->height) >> 1);
365 display (struct state *st, Pixmap pixmap)
367 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
369 if (st->xgwa.width != st->last_w ||
370 st->xgwa.height != st->last_h)
372 XClearWindow (st->dpy, st->window);
373 st->last_w = st->xgwa.width;
374 st->last_h = st->xgwa.height;
376 if (st->xgwa.depth != 1)
377 XCopyArea (st->dpy, pixmap, st->window, st->gc, 0, 0, st->size, st->size,
378 (st->xgwa.width - st->size) >> 1,
379 (st->xgwa.height - st->size) >> 1);
381 XCopyPlane (st->dpy, pixmap, st->window, st->gc, 0, 0, st->size, st->size,
382 (st->xgwa.width - st->size) >> 1,
383 (st->xgwa.height - st->size) >> 1,
386 XDrawRectangle (st->dpy, st->window, st->gc,
387 ((st->xgwa.width - st->size) >> 1) - 1,
388 ((st->xgwa.height - st->size) >> 1) - 1,
389 st->size+2, st->size+2);
394 blitspin_reshape (Display *dpy, Window window, void *closure,
395 unsigned int w, unsigned int h)
400 blitspin_event (Display *dpy, Window window, void *closure, XEvent *event)
402 struct state *st = (struct state *) closure;
403 if (screenhack_event_helper (dpy, window, event))
412 blitspin_free (Display *dpy, Window window, void *closure)
417 static const char *blitspin_defaults [] = {
418 ".background: black",
419 ".foreground: white",
424 "*bitmap: (default)",
425 "*geometry: 1080x1080",
427 "*ignoreRotation: True",
432 static XrmOptionDescRec blitspin_options [] = {
433 { "-delay", ".delay", XrmoptionSepArg, 0 },
434 { "-delay2", ".delay2", XrmoptionSepArg, 0 },
435 { "-duration", ".duration", XrmoptionSepArg, 0 },
436 { "-bitmap", ".bitmap", XrmoptionSepArg, 0 },
441 XSCREENSAVER_MODULE ("BlitSpin", blitspin)