-/* xscreensaver, Copyright (c) 1992-2006 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1992-2012 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
#include "images/som.xbm"
+/* Implementing this using XCopyArea doesn't work with color images on OSX.
+ This means that the Cocoa implementation of XCopyArea in jwxyz.m is
+ broken with the GXor, GXand, and/or the GXxor GC operations. This
+ probably means that (e.g.) "kCGBlendModeDarken" is not close enough
+ to being "GXand" to use for that. (It works with monochrome images,
+ just not color ones).
+
+ So, on OSX, we implement the blitter by hand. It is correct, but
+ orders of magnitude slower.
+ */
+#ifndef HAVE_COCOA
+# define USE_XCOPYAREA
+#endif
+
struct state {
Display *dpy;
Window window;
int width, height, size;
Bool scale_up;
Pixmap self, temp, mask;
- GC SET, CLR, CPY, IOR, AND, XOR;
+# ifdef USE_XCOPYAREA
+ GC gc_set, gc_clear, gc_copy, gc_and, gc_or, gc_xor;
+# endif
GC gc;
int delay, delay2;
+ int duration;
Pixmap bitmap;
unsigned int fg, bg;
int first_time;
int last_w, last_h;
+ time_t start_time;
Bool loaded_p;
+ Bool load_ext_p;
async_load_state *img_loader;
};
static void display (struct state *, Pixmap);
static void blitspin_init_2 (struct state *);
-#define copy_all_to(from, xoff, yoff, to, gc) \
- XCopyArea (st->dpy, (from), (to), (gc), 0, 0, \
- st->size-(xoff), st->size-(yoff), (xoff), (yoff))
+#define copy_to(from, xoff, yoff, to, op) \
+ bitblt (st, st->from, st->to, op, 0, 0, \
+ st->size-(xoff), st->size-(yoff), (xoff), (yoff))
+
+#define copy_from(to, xoff, yoff, from, op) \
+ bitblt (st, st->from, st->to, op, (xoff), (yoff), \
+ st->size-(xoff), st->size-(yoff), 0, 0)
+
+
+#ifdef USE_XCOPYAREA
+# define bitblt(st, from, to, op, src_x, src_y, w, h, dst_x, dst_y) \
+ XCopyArea((st)->dpy, (from), (to), (st)->gc_##op, \
+ (src_x), (src_y), (w), (h), (dst_x), (dst_y))
+#else /* !USE_XCOPYAREA */
+
+# define bitblt(st, from, to, op, src_x, src_y, w, h, dst_x, dst_y) \
+ do_bitblt((st)->dpy, (from), (to), st->gc, GX##op, \
+ (src_x), (src_y), (w), (h), (dst_x), (dst_y))
+
+static void
+do_bitblt (Display *dpy, Drawable src, Drawable dst, GC gc, int op,
+ int src_x, int src_y,
+ unsigned int width, unsigned int height,
+ int dst_x, int dst_y)
+{
+ if (op == GXclear)
+ {
+ XSetForeground (dpy, gc, 0xFF000000); /* ARGB black for Cocoa */
+ XFillRectangle (dpy, dst, gc, dst_x, dst_y, width, height);
+ }
+ else if (op == GXset)
+ {
+ XSetForeground (dpy, gc, ~0L);
+ XFillRectangle (dpy, dst, gc, dst_x, dst_y, width, height);
+ }
+ else if (op == GXcopy)
+ {
+ XCopyArea (dpy, src, dst, gc, src_x, src_y, width, height, dst_x, dst_y);
+ }
+ else
+ {
+ XImage *srci = XGetImage (dpy, src, src_x, src_y, width, height,
+ ~0L, ZPixmap);
+ XImage *dsti = XGetImage (dpy, dst, dst_x, dst_y, width, height,
+ ~0L, ZPixmap);
+ unsigned long *out = (unsigned long *) dsti->data;
+ unsigned long *in = (unsigned long *) srci->data;
+ unsigned long *end = (in + (height * srci->bytes_per_line
+ / sizeof(unsigned long)));
+ switch (op)
+ {
+ case GXor: while (in < end) { *out++ |= *in++; } break;
+ case GXand: while (in < end) { *out++ &= *in++; } break;
+ case GXxor: while (in < end) { *out++ ^= *in++; } break;
+ default: abort();
+ }
+ XPutImage (dpy, dst, gc, dsti, 0, 0, dst_x, dst_y, width, height);
+ XDestroyImage (srci);
+ XDestroyImage (dsti);
+ }
+}
+
+#endif /* !USE_XCOPYAREA */
+
-#define copy_all_from(to, xoff, yoff, from, gc) \
- XCopyArea (st->dpy, (from), (to), (gc), (xoff), (yoff), \
- st->size-(xoff), st->size-(yoff), 0, 0)
static unsigned long
blitspin_draw (Display *dpy, Window window, void *closure)
{
struct state *st = (struct state *) closure;
int this_delay = st->delay;
+ int qwad;
if (st->img_loader) /* still loading */
{
st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
- return this_delay;
+
+ if (!st->img_loader) { /* just finished */
+ st->first_time = 0;
+ st->loaded_p = True;
+ st->qwad = -1;
+ st->start_time = time ((time_t) 0);
+ blitspin_init_2 (st);
+ }
+
+ /* Rotate nothing if the very first image is not yet loaded */
+ if (! st->loaded_p)
+ return this_delay;
}
- if (! st->loaded_p) {
- blitspin_init_2 (st);
- st->loaded_p = True;
+ if (!st->img_loader &&
+ st->load_ext_p &&
+ st->start_time + st->duration < time ((time_t) 0)) {
+ /* Start a new image loading, but keep rotating the old image
+ until the new one arrives. */
+ st->img_loader = load_image_async_simple (0, st->xgwa.screen, st->window,
+ st->bitmap, 0, 0);
}
if (st->qwad == -1)
{
- XFillRectangle (st->dpy, st->mask, st->CLR, 0, 0, st->size, st->size);
- XFillRectangle (st->dpy, st->mask, st->SET, 0, 0, st->size>>1, st->size>>1);
+ bitblt(st, st->mask, st->mask, clear,0,0, st->size, st->size, 0,0);
+ bitblt(st, st->mask, st->mask, set, 0,0, st->size>>1, st->size>>1, 0,0);
st->qwad = st->size>>1;
}
/* for (st->qwad = st->size>>1; st->qwad > 0; st->qwad>>=1) */
- copy_all_to (st->mask, 0, 0, st->temp, st->CPY); /* 1 */
- copy_all_to (st->mask, 0, st->qwad, st->temp, st->IOR); /* 2 */
- copy_all_to (st->self, 0, 0, st->temp, st->AND); /* 3 */
- copy_all_to (st->temp, 0, 0, st->self, st->XOR); /* 4 */
- copy_all_from (st->temp, st->qwad, 0, st->self, st->XOR); /* 5 */
- copy_all_from (st->self, st->qwad, 0, st->self, st->IOR); /* 6 */
- copy_all_to (st->temp, st->qwad, 0, st->self, st->XOR); /* 7 */
- copy_all_to (st->self, 0, 0, st->temp, st->CPY); /* 8 */
- copy_all_from (st->temp, st->qwad, st->qwad, st->self, st->XOR); /* 9 */
- copy_all_to (st->mask, 0, 0, st->temp, st->AND); /* A */
- copy_all_to (st->temp, 0, 0, st->self, st->XOR); /* B */
- copy_all_to (st->temp, st->qwad, st->qwad, st->self, st->XOR); /* C */
- copy_all_from (st->mask, st->qwad>>1, st->qwad>>1, st->mask, st->AND); /* D */
- copy_all_to (st->mask, st->qwad, 0, st->mask, st->IOR); /* E */
- copy_all_to (st->mask, 0, st->qwad, st->mask, st->IOR); /* F */
- display (st, st->self);
+ qwad = st->qwad;
+
+ copy_to (mask, 0, 0, temp, copy); /* 1 */
+ copy_to (mask, 0, qwad, temp, or); /* 2 */
+ copy_to (self, 0, 0, temp, and); /* 3 */
+ copy_to (temp, 0, 0, self, xor); /* 4 */
+ copy_from (temp, qwad, 0, self, xor); /* 5 */
+ copy_from (self, qwad, 0, self, or); /* 6 */
+ copy_to (temp, qwad, 0, self, xor); /* 7 */
+ copy_to (self, 0, 0, temp, copy); /* 8 */
+ copy_from (temp, qwad, qwad, self, xor); /* 9 */
+ copy_to (mask, 0, 0, temp, and); /* A */
+ copy_to (temp, 0, 0, self, xor); /* B */
+ copy_to (temp, qwad, qwad, self, xor); /* C */
+ copy_from (mask, qwad>>1, qwad>>1, mask, and); /* D */
+ copy_to (mask, qwad, 0, mask, or); /* E */
+ copy_to (mask, 0, qwad, mask, or); /* F */
+ display (st, st->self);
st->qwad >>= 1;
if (st->qwad == 0) /* done with this round */
static int
to_pow2(struct state *st, int n, Bool up)
{
- /* sizeof(Dimension) == 2. */
int powers_of_2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
2048, 4096, 8192, 16384, 32768, 65536 };
int i = 0;
"background", "Background");
st->delay = get_integer_resource (st->dpy, "delay", "Integer");
st->delay2 = get_integer_resource (st->dpy, "delay2", "Integer");
+ st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
if (st->delay < 0) st->delay = 0;
if (st->delay2 < 0) st->delay2 = 0;
+ if (st->duration < 1) st->duration = 1;
+
+ st->start_time = time ((time_t) 0);
+
bitmap_name = get_string_resource (st->dpy, "bitmap", "Bitmap");
if (! bitmap_name || !*bitmap_name)
bitmap_name = "(default)";
if (!strcasecmp (bitmap_name, "(default)") ||
!strcasecmp (bitmap_name, "default"))
-# ifdef HAVE_COCOA
- bitmap_name = "(builtin)";
-# else
bitmap_name = "(screen)";
-# endif
if (!strcasecmp (bitmap_name, "(builtin)") ||
!strcasecmp (bitmap_name, "builtin"))
st->fg, st->bg,
st->xgwa.depth);
st->scale_up = True; /* definitely. */
+ st->loaded_p = True;
+ blitspin_init_2 (st);
}
else if (!strcasecmp (bitmap_name, "(screen)") ||
!strcasecmp (bitmap_name, "screen"))
st->width = st->xgwa.width;
st->height = st->xgwa.height;
st->scale_up = True; /* maybe? */
+ st->load_ext_p = True;
st->img_loader = load_image_async_simple (0, st->xgwa.screen, st->window,
st->bitmap, 0, 0);
}
st->bitmap = xpm_file_to_pixmap (st->dpy, st->window, bitmap_name,
&st->width, &st->height, 0);
st->scale_up = True; /* probably? */
+ blitspin_init_2 (st);
}
return st;
if (st->size > h) st->size = h;
}
- st->self = XCreatePixmap (st->dpy, st->window, st->size, st->size, st->xgwa.depth);
- st->temp = XCreatePixmap (st->dpy, st->window, st->size, st->size, st->xgwa.depth);
- st->mask = XCreatePixmap (st->dpy, st->window, st->size, st->size, st->xgwa.depth);
+ if (st->self) XFreePixmap (st->dpy, st->self);
+ if (st->temp) XFreePixmap (st->dpy, st->temp);
+ if (st->mask) XFreePixmap (st->dpy, st->mask);
+
+ st->self = XCreatePixmap (st->dpy, st->window, st->size, st->size,
+ st->xgwa.depth);
+ st->temp = XCreatePixmap (st->dpy, st->window, st->size, st->size,
+ st->xgwa.depth);
+ st->mask = XCreatePixmap (st->dpy, st->window, st->size, st->size,
+ st->xgwa.depth);
gcv.foreground = (st->xgwa.depth == 1 ? 1 : (~0));
- gcv.function=GXset; st->SET = XCreateGC(st->dpy,st->self,GCFunction|GCForeground,&gcv);
- gcv.function=GXclear;st->CLR = XCreateGC(st->dpy,st->self,GCFunction|GCForeground,&gcv);
- gcv.function=GXcopy; st->CPY = XCreateGC(st->dpy,st->self,GCFunction|GCForeground,&gcv);
- gcv.function=GXor; st->IOR = XCreateGC(st->dpy,st->self,GCFunction|GCForeground,&gcv);
- gcv.function=GXand; st->AND = XCreateGC(st->dpy,st->self,GCFunction|GCForeground,&gcv);
- gcv.function=GXxor; st->XOR = XCreateGC(st->dpy,st->self,GCFunction|GCForeground,&gcv);
+
+# ifdef USE_XCOPYAREA
+# define make_gc(op) \
+ gcv.function=GX##op; \
+ if (st->gc_##op) XFreeGC (st->dpy, st->gc_##op); \
+ st->gc_##op = XCreateGC (st->dpy, st->self, GCFunction|GCForeground, &gcv)
+ make_gc(set);
+ make_gc(clear);
+ make_gc(copy);
+ make_gc(and);
+ make_gc(or);
+ make_gc(xor);
+# endif /* USE_XCOPYAREA */
gcv.foreground = gcv.background = st->bg;
+ if (st->gc) XFreeGC (st->dpy, st->gc);
st->gc = XCreateGC (st->dpy, st->window, GCForeground|GCBackground, &gcv);
- /* Clear st->self to the background color (not to 0, which st->CLR does.) */
+ /* Clear st->self to the background color (not to 0, which 'clear' does.) */
XFillRectangle (st->dpy, st->self, st->gc, 0, 0, st->size, st->size);
XSetForeground (st->dpy, st->gc, st->fg);
- XCopyArea (st->dpy, st->bitmap, st->self, st->CPY, 0, 0,
+ XCopyArea (st->dpy, st->bitmap, st->self, st->gc, 0, 0,
st->width, st->height,
(st->size - st->width) >> 1,
(st->size - st->height) >> 1);
- XFreePixmap(st->dpy, st->bitmap);
st->qwad = -1;
st->first_time = 1;
static const char *blitspin_defaults [] = {
".background: black",
".foreground: white",
+ ".fpsSolid: true",
"*delay: 500000",
"*delay2: 500000",
+ "*duration: 120",
"*bitmap: (default)",
"*geometry: 512x512",
+#ifdef USE_IPHONE
+ "*ignoreRotation: True",
+#endif
0
};
static XrmOptionDescRec blitspin_options [] = {
{ "-delay", ".delay", XrmoptionSepArg, 0 },
{ "-delay2", ".delay2", XrmoptionSepArg, 0 },
+ { "-duration", ".duration", XrmoptionSepArg, 0 },
{ "-bitmap", ".bitmap", XrmoptionSepArg, 0 },
{ 0, 0, 0, 0 }
};