X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fblitspin.c;h=af3ea752e76a51ab35cb9863b6f04d673b1d8582;hb=4361b69d3178d7fc98d0388f9a223af6c2651aba;hp=38d82285c710549017dc588521aa0f063a9efe74;hpb=49f5b54f312fe4ac2e9bc47581a72451bd0e8439;p=xscreensaver diff --git a/hacks/blitspin.c b/hacks/blitspin.c index 38d82285..af3ea752 100644 --- a/hacks/blitspin.c +++ b/hacks/blitspin.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1992-2006 Jamie Zawinski +/* xscreensaver, Copyright (c) 1992-2014 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -25,11 +25,27 @@ */ #include "screenhack.h" +#include "pow2.h" #include "xpm-pixmap.h" #include +#include #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_JWXYZ +# define USE_XCOPYAREA +#endif + struct state { Display *dpy; Window window; @@ -37,9 +53,12 @@ struct state { 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; @@ -47,42 +66,118 @@ struct state { 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; } @@ -95,22 +190,24 @@ blitspin_draw (Display *dpy, Window window, void *closure) /* 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 */ @@ -124,18 +221,13 @@ blitspin_draw (Display *dpy, Window window, void *closure) static int -to_pow2(struct state *st, int n, Bool up) +blitspin_to_pow2(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; - if (n > 65536) st->size = 65536; - while (n >= powers_of_2[i]) i++; - if (n == powers_of_2[i-1]) + int pow2 = to_pow2 (n); + if (n == pow2) return n; else - return powers_of_2[up ? i : i-1]; + return up ? pow2 : pow2 >> 1; } static void * @@ -155,19 +247,20 @@ blitspin_init (Display *d_arg, Window w_arg) "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")) @@ -180,6 +273,8 @@ blitspin_init (Display *d_arg, Window w_arg) 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")) @@ -190,6 +285,7 @@ blitspin_init (Display *d_arg, Window w_arg) 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); } @@ -198,6 +294,7 @@ blitspin_init (Display *d_arg, Window w_arg) 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; @@ -211,37 +308,52 @@ blitspin_init_2 (struct state *st) /* make it square */ st->size = (st->width < st->height) ? st->height : st->width; - st->size = to_pow2(st, st->size, st->scale_up); /* round up to power of 2 */ + /* round up to power of 2 */ + st->size = blitspin_to_pow2(st->size, st->scale_up); { /* don't exceed screen size */ int s = XScreenNumberOfScreen(st->xgwa.screen); - int w = to_pow2(st, XDisplayWidth(st->dpy, s), False); - int h = to_pow2(st, XDisplayHeight(st->dpy, s), False); + int w = blitspin_to_pow2(XDisplayWidth(st->dpy, s), False); + int h = blitspin_to_pow2(XDisplayHeight(st->dpy, s), False); if (st->size > w) st->size = w; 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; @@ -285,6 +397,12 @@ blitspin_reshape (Display *dpy, Window window, void *closure, static Bool blitspin_event (Display *dpy, Window window, void *closure, XEvent *event) { + struct state *st = (struct state *) closure; + if (screenhack_event_helper (dpy, window, event)) + { + st->start_time = 0; + return True; + } return False; } @@ -297,16 +415,22 @@ blitspin_free (Display *dpy, Window window, void *closure) static const char *blitspin_defaults [] = { ".background: black", ".foreground: white", + ".fpsSolid: true", "*delay: 500000", "*delay2: 500000", + "*duration: 120", "*bitmap: (default)", - "*geometry: 512x512", + "*geometry: 1080x1080", +#ifdef HAVE_MOBILE + "*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 } };