1 /* xscreensaver, Copyright (c) 1992-2018 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 "ximage-loader.h"
33 #include "images/gen/som_png.h"
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"))
269 Pixmap pixmap = image_data_to_pixmap (st->dpy, st->window,
270 som_png, sizeof(som_png),
271 &st->width, &st->height, &mask);
274 gcv.foreground = st->bg;
275 gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
276 st->bitmap = XCreatePixmap (st->dpy, st->window,
277 st->xgwa.width, st->xgwa.height,
279 XFillRectangle (st->dpy, st->bitmap, gc, 0, 0, st->width, st->height);
280 XSetClipMask (st->dpy, gc, mask);
281 XCopyArea (st->dpy, pixmap, st->bitmap, gc, 0, 0, st->width, st->height,
283 XFreeGC (st->dpy, gc);
284 XFreePixmap (st->dpy, pixmap);
285 XFreePixmap (st->dpy, mask);
287 st->scale_up = True; /* definitely. */
289 blitspin_init_2 (st);
291 else if (!strcasecmp (bitmap_name, "(screen)") ||
292 !strcasecmp (bitmap_name, "screen"))
294 st->bitmap = XCreatePixmap (st->dpy, st->window,
295 st->xgwa.width, st->xgwa.height,
297 st->width = st->xgwa.width;
298 st->height = st->xgwa.height;
299 st->scale_up = True; /* maybe? */
300 st->load_ext_p = True;
301 st->img_loader = load_image_async_simple (0, st->xgwa.screen, st->window,
306 st->bitmap = file_to_pixmap (st->dpy, st->window, bitmap_name,
307 &st->width, &st->height, 0);
308 st->scale_up = True; /* probably? */
309 blitspin_init_2 (st);
317 blitspin_init_2 (struct state *st)
322 st->size = (st->width < st->height) ? st->height : st->width;
323 /* round up to power of 2 */
324 st->size = blitspin_to_pow2(st->size, st->scale_up);
325 { /* don't exceed screen size */
326 int s = XScreenNumberOfScreen(st->xgwa.screen);
327 int w = blitspin_to_pow2(XDisplayWidth(st->dpy, s), False);
328 int h = blitspin_to_pow2(XDisplayHeight(st->dpy, s), False);
329 if (st->size > w) st->size = w;
330 if (st->size > h) st->size = h;
333 if (st->self) XFreePixmap (st->dpy, st->self);
334 if (st->temp) XFreePixmap (st->dpy, st->temp);
335 if (st->mask) XFreePixmap (st->dpy, st->mask);
337 st->self = XCreatePixmap (st->dpy, st->window, st->size, st->size,
339 st->temp = XCreatePixmap (st->dpy, st->window, st->size, st->size,
341 st->mask = XCreatePixmap (st->dpy, st->window, st->size, st->size,
343 gcv.foreground = (st->xgwa.depth == 1 ? 1 : (~0));
345 # ifdef USE_XCOPYAREA
346 # define make_gc(op) \
347 gcv.function=GX##op; \
348 if (st->gc_##op) XFreeGC (st->dpy, st->gc_##op); \
349 st->gc_##op = XCreateGC (st->dpy, st->self, GCFunction|GCForeground, &gcv)
356 # endif /* USE_XCOPYAREA */
358 gcv.foreground = gcv.background = st->bg;
359 if (st->gc) XFreeGC (st->dpy, st->gc);
360 st->gc = XCreateGC (st->dpy, st->window, GCForeground|GCBackground, &gcv);
361 /* Clear st->self to the background color (not to 0, which 'clear' does.) */
362 XFillRectangle (st->dpy, st->self, st->gc, 0, 0, st->size, st->size);
363 XSetForeground (st->dpy, st->gc, st->fg);
365 XCopyArea (st->dpy, st->bitmap, st->self, st->gc, 0, 0,
366 st->width, st->height,
367 (st->size - st->width) >> 1,
368 (st->size - st->height) >> 1);
375 display (struct state *st, Pixmap pixmap)
377 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
379 if (st->xgwa.width != st->last_w ||
380 st->xgwa.height != st->last_h)
382 XClearWindow (st->dpy, st->window);
383 st->last_w = st->xgwa.width;
384 st->last_h = st->xgwa.height;
386 if (st->xgwa.depth != 1)
387 XCopyArea (st->dpy, pixmap, st->window, st->gc, 0, 0, st->size, st->size,
388 (st->xgwa.width - st->size) >> 1,
389 (st->xgwa.height - st->size) >> 1);
391 XCopyPlane (st->dpy, pixmap, st->window, st->gc, 0, 0, st->size, st->size,
392 (st->xgwa.width - st->size) >> 1,
393 (st->xgwa.height - st->size) >> 1,
396 XDrawRectangle (st->dpy, st->window, st->gc,
397 ((st->xgwa.width - st->size) >> 1) - 1,
398 ((st->xgwa.height - st->size) >> 1) - 1,
399 st->size+2, st->size+2);
404 blitspin_reshape (Display *dpy, Window window, void *closure,
405 unsigned int w, unsigned int h)
410 blitspin_event (Display *dpy, Window window, void *closure, XEvent *event)
412 struct state *st = (struct state *) closure;
413 if (screenhack_event_helper (dpy, window, event))
422 blitspin_free (Display *dpy, Window window, void *closure)
427 static const char *blitspin_defaults [] = {
428 ".background: black",
429 ".foreground: white",
434 "*bitmap: (default)",
435 "*geometry: 1080x1080",
437 "*ignoreRotation: True",
442 static XrmOptionDescRec blitspin_options [] = {
443 { "-delay", ".delay", XrmoptionSepArg, 0 },
444 { "-delay2", ".delay2", XrmoptionSepArg, 0 },
445 { "-duration", ".duration", XrmoptionSepArg, 0 },
446 { "-bitmap", ".bitmap", XrmoptionSepArg, 0 },
451 XSCREENSAVER_MODULE ("BlitSpin", blitspin)