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"
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? */
299 blitspin_init_2 (st);
307 blitspin_init_2 (struct state *st)
312 st->size = (st->width < st->height) ? st->height : st->width;
313 st->size = to_pow2(st, st->size, st->scale_up); /* round up to power of 2 */
314 { /* don't exceed screen size */
315 int s = XScreenNumberOfScreen(st->xgwa.screen);
316 int w = to_pow2(st, XDisplayWidth(st->dpy, s), False);
317 int h = to_pow2(st, XDisplayHeight(st->dpy, s), False);
318 if (st->size > w) st->size = w;
319 if (st->size > h) st->size = h;
322 if (st->self) XFreePixmap (st->dpy, st->self);
323 if (st->temp) XFreePixmap (st->dpy, st->temp);
324 if (st->mask) XFreePixmap (st->dpy, st->mask);
326 st->self = XCreatePixmap (st->dpy, st->window, st->size, st->size,
328 st->temp = XCreatePixmap (st->dpy, st->window, st->size, st->size,
330 st->mask = XCreatePixmap (st->dpy, st->window, st->size, st->size,
332 gcv.foreground = (st->xgwa.depth == 1 ? 1 : (~0));
334 # ifdef USE_XCOPYAREA
335 # define make_gc(op) \
336 gcv.function=GX##op; \
337 if (st->gc_##op) XFreeGC (st->dpy, st->gc_##op); \
338 st->gc_##op = XCreateGC (st->dpy, st->self, GCFunction|GCForeground, &gcv)
345 # endif /* USE_XCOPYAREA */
347 gcv.foreground = gcv.background = st->bg;
348 if (st->gc) XFreeGC (st->dpy, st->gc);
349 st->gc = XCreateGC (st->dpy, st->window, GCForeground|GCBackground, &gcv);
350 /* Clear st->self to the background color (not to 0, which 'clear' does.) */
351 XFillRectangle (st->dpy, st->self, st->gc, 0, 0, st->size, st->size);
352 XSetForeground (st->dpy, st->gc, st->fg);
354 XCopyArea (st->dpy, st->bitmap, st->self, st->gc, 0, 0,
355 st->width, st->height,
356 (st->size - st->width) >> 1,
357 (st->size - st->height) >> 1);
364 display (struct state *st, Pixmap pixmap)
366 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
368 if (st->xgwa.width != st->last_w ||
369 st->xgwa.height != st->last_h)
371 XClearWindow (st->dpy, st->window);
372 st->last_w = st->xgwa.width;
373 st->last_h = st->xgwa.height;
375 if (st->xgwa.depth != 1)
376 XCopyArea (st->dpy, pixmap, st->window, st->gc, 0, 0, st->size, st->size,
377 (st->xgwa.width - st->size) >> 1,
378 (st->xgwa.height - st->size) >> 1);
380 XCopyPlane (st->dpy, pixmap, st->window, st->gc, 0, 0, st->size, st->size,
381 (st->xgwa.width - st->size) >> 1,
382 (st->xgwa.height - st->size) >> 1,
385 XDrawRectangle (st->dpy, st->window, st->gc,
386 ((st->xgwa.width - st->size) >> 1) - 1,
387 ((st->xgwa.height - st->size) >> 1) - 1,
388 st->size+2, st->size+2);
393 blitspin_reshape (Display *dpy, Window window, void *closure,
394 unsigned int w, unsigned int h)
399 blitspin_event (Display *dpy, Window window, void *closure, XEvent *event)
401 struct state *st = (struct state *) closure;
402 if (screenhack_event_helper (dpy, window, event))
411 blitspin_free (Display *dpy, Window window, void *closure)
416 static const char *blitspin_defaults [] = {
417 ".background: black",
418 ".foreground: white",
423 "*bitmap: (default)",
424 "*geometry: 1080x1080",
426 "*ignoreRotation: True",
431 static XrmOptionDescRec blitspin_options [] = {
432 { "-delay", ".delay", XrmoptionSepArg, 0 },
433 { "-delay2", ".delay2", XrmoptionSepArg, 0 },
434 { "-duration", ".duration", XrmoptionSepArg, 0 },
435 { "-bitmap", ".bitmap", XrmoptionSepArg, 0 },
440 XSCREENSAVER_MODULE ("BlitSpin", blitspin)