X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=OSX%2Fjwxyz.m;h=15d82135ade30bf2d4a10395924854db03ba365d;hp=b7e99525b6be63c925dd25c8cd03cf3f11e1dd0d;hb=019de959b265701cd0c3fccbb61f2b69f06bf9ee;hpb=6f5482d73adb0165c0130bb47d852644ab0c4869 diff --git a/OSX/jwxyz.m b/OSX/jwxyz.m index b7e99525..15d82135 100644 --- a/OSX/jwxyz.m +++ b/OSX/jwxyz.m @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1991-2012 Jamie Zawinski +/* xscreensaver, Copyright (c) 1991-2013 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 @@ -18,6 +18,7 @@ #import #import +#import #ifdef USE_IPHONE # import @@ -44,6 +45,8 @@ #import "jwxyz-timers.h" #import "yarandom.h" +# define USE_BACKBUFFER /* must be in sync with XScreenSaverView.h */ + #undef Assert #define Assert(C,S) do { if (!(C)) jwxyz_abort ("%s",(S)); } while(0) @@ -194,7 +197,7 @@ jwxyz_free_display (Display *dpy) void * jwxyz_window_view (Window w) { - Assert (w->type == WINDOW, "not a window"); + Assert (w && w->type == WINDOW, "not a window"); return w->window.view; } @@ -221,7 +224,7 @@ jwxyz_window_resized (Display *dpy, Window w, void *cgc_arg) { CGContextRef cgc = (CGContextRef) cgc_arg; - Assert (w->type == WINDOW, "not a window"); + Assert (w && w->type == WINDOW, "not a window"); w->frame.origin.x = new_x; w->frame.origin.y = new_y; w->frame.size.width = new_width; @@ -241,11 +244,19 @@ jwxyz_window_resized (Display *dpy, Window w, CGDisplayCount n; dpy->cgdpy = 0; CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n); + // Auuugh! + if (! dpy->cgdpy) { + p.x = p.y = 0; + CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n); + } Assert (dpy->cgdpy, "unable to find CGDisplay"); } # endif // USE_IPHONE -#if 0 +# ifndef USE_BACKBUFFER + // Funny thing: As of OS X 10.9, if USE_BACKBUFFER is turned off, + // then this one's faster. + { // Figure out this screen's colorspace, and use that for every CGImage. // @@ -255,12 +266,12 @@ jwxyz_window_resized (Display *dpy, Window w, dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile); Assert (dpy->colorspace, "unable to find colorspace"); } -# else +# else // USE_BACKBUFFER // WTF? It's faster if we *do not* use the screen's colorspace! // dpy->colorspace = CGColorSpaceCreateDeviceRGB(); -# endif +# endif // USE_BACKBUFFER invalidate_drawable_cache (w); } @@ -270,13 +281,19 @@ jwxyz_window_resized (Display *dpy, Window w, void jwxyz_mouse_moved (Display *dpy, Window w, int x, int y) { - Assert (w->type == WINDOW, "not a window"); + Assert (w && w->type == WINDOW, "not a window"); w->window.last_mouse_x = x; w->window.last_mouse_y = y; } #endif // USE_IPHONE +void +jwxyz_flush_context (Display *dpy) +{ + // This is only used when USE_BACKBUFFER is off. + CGContextFlush(dpy->main_window->cgc); // CGContextSynchronize is another possibility. +} jwxyz_sources_data * display_sources_data (Display *dpy) @@ -475,6 +492,12 @@ push_bg_gc (Drawable d, GC gc, Bool fill_p) */ #define XDRAWPOINTS_IMAGES +/* Update, 2012: Kurt Revis points out that diddling + the bitmap data directly is faster. This only works on Pixmaps, though, + not Windows. (Fortunately, on iOS, the Window is really a Pixmap.) + */ +#define XDRAWPOINTS_CGDATA + int XDrawPoints (Display *dpy, Drawable d, GC gc, XPoint *points, int count, int mode) @@ -484,68 +507,125 @@ XDrawPoints (Display *dpy, Drawable d, GC gc, push_fg_gc (d, gc, YES); -# ifdef XDRAWPOINTS_IMAGES +# ifdef XDRAWPOINTS_CGDATA - unsigned int argb = gc->gcv.foreground; - validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p); - if (gc->depth == 1) - argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0)); - - CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4, NULL); - CGImageRef cgi = CGImageCreate (1, 1, - 8, 32, 4, - dpy->colorspace, - /* Host-ordered, since we're using the - address of an int as the color data. */ - (kCGImageAlphaNoneSkipFirst | - kCGBitmapByteOrder32Host), - prov, - NULL, /* decode[] */ - NO, /* interpolate */ - kCGRenderingIntentDefault); - CGDataProviderRelease (prov); +# ifdef USE_BACKBUFFER + if (1) // Because of the backbuffer, all iPhone Windows work like Pixmaps. +# else + if (d->type == PIXMAP) +# endif + { + CGContextRef cgc = d->cgc; + void *data = CGBitmapContextGetData (cgc); + size_t bpr = CGBitmapContextGetBytesPerRow (cgc); + size_t w = CGBitmapContextGetWidth (cgc); + size_t h = CGBitmapContextGetHeight (cgc); - CGContextRef cgc = d->cgc; - CGRect rect; - rect.size.width = rect.size.height = 1; - for (i = 0; i < count; i++) { - if (i > 0 && mode == CoordModePrevious) { - rect.origin.x += points->x; - rect.origin.x -= points->y; + Assert (data, "no bitmap data in Drawable"); + + unsigned int argb = gc->gcv.foreground; + validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p); + if (gc->depth == 1) + argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0)); + + CGFloat x0 = wr.origin.x; + CGFloat y0 = wr.origin.y; // Y axis is refreshingly not flipped. + + // It's uglier, but faster, to hoist the conditional out of the loop. + if (mode == CoordModePrevious) { + CGFloat x = x0, y = y0; + for (i = 0; i < count; i++, points++) { + x += points->x; + y += points->y; + + if (x >= 0 && x < w && y >= 0 && y < h) { + unsigned int *p = (unsigned int *) + ((char *) data + (size_t) y * bpr + (size_t) x * 4); + *p = argb; + } + } } else { - rect.origin.x = wr.origin.x + points->x; - rect.origin.y = wr.origin.y + wr.size.height - points->y - 1; + for (i = 0; i < count; i++, points++) { + CGFloat x = x0 + points->x; + CGFloat y = y0 + points->y; + + if (x >= 0 && x < w && y >= 0 && y < h) { + unsigned int *p = (unsigned int *) + ((char *) data + (size_t) y * bpr + (size_t) x * 4); + *p = argb; + } + } } - //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace"); - CGContextDrawImage (cgc, rect, cgi); - points++; - } + } else /* d->type == WINDOW */ + +# endif /* XDRAWPOINTS_CGDATA */ + { + +# ifdef XDRAWPOINTS_IMAGES - CGImageRelease (cgi); + unsigned int argb = gc->gcv.foreground; + validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p); + if (gc->depth == 1) + argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0)); + + CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4, + NULL); + CGImageRef cgi = CGImageCreate (1, 1, + 8, 32, 4, + dpy->colorspace, + /* Host-ordered, since we're using the + address of an int as the color data. */ + (kCGImageAlphaNoneSkipFirst | + kCGBitmapByteOrder32Host), + prov, + NULL, /* decode[] */ + NO, /* interpolate */ + kCGRenderingIntentDefault); + CGDataProviderRelease (prov); + + CGContextRef cgc = d->cgc; + CGRect rect; + rect.size.width = rect.size.height = 1; + for (i = 0; i < count; i++) { + if (i > 0 && mode == CoordModePrevious) { + rect.origin.x += points->x; + rect.origin.x -= points->y; + } else { + rect.origin.x = wr.origin.x + points->x; + rect.origin.y = wr.origin.y + wr.size.height - points->y - 1; + } + + //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace"); + CGContextDrawImage (cgc, rect, cgi); + points++; + } + + CGImageRelease (cgi); # else /* ! XDRAWPOINTS_IMAGES */ - CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect)); - CGRect *r = rects; + CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect)); + CGRect *r = rects; - for (i = 0; i < count; i++) { - r->size.width = r->size.height = 1; - if (i > 0 && mode == CoordModePrevious) { - r->origin.x = r[-1].origin.x + points->x; - r->origin.y = r[-1].origin.x - points->y; - } else { - r->origin.x = wr.origin.x + points->x; - r->origin.y = wr.origin.y + wr.size.height - points->y; + for (i = 0; i < count; i++) { + r->size.width = r->size.height = 1; + if (i > 0 && mode == CoordModePrevious) { + r->origin.x = r[-1].origin.x + points->x; + r->origin.y = r[-1].origin.x - points->y; + } else { + r->origin.x = wr.origin.x + points->x; + r->origin.y = wr.origin.y + wr.size.height - points->y; + } + points++; + r++; } - points++; - r++; - } - CGContextFillRects (d->cgc, rects, count); - free (rects); + CGContextFillRects (d->cgc, rects, count); + free (rects); # endif /* ! XDRAWPOINTS_IMAGES */ + } pop_gc (d, gc); invalidate_drawable_cache (d); @@ -568,6 +648,45 @@ static void draw_rect (Display *, Drawable, GC, int x, int y, unsigned int width, unsigned int height, BOOL foreground_p, BOOL fill_p); +static Bool +bitmap_context_p (Drawable d) +{ +# ifdef USE_BACKBUFFER + return True; +# else + // Because of the backbuffer, all iPhone Windows work like Pixmaps. + return d->type == PIXMAP; +# endif +} + +static void +fill_rect_memset (void *dst, size_t dst_pitch, uint32_t fill_data, + size_t fill_width, size_t fill_height) +{ + Assert(sizeof(wchar_t) == 4, "somebody changed the ABI"); + while (fill_height) { + // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday. + wmemset (dst, fill_data, fill_width); + --fill_height; + dst = (char *) dst + dst_pitch; + } +} + +static void * +seek_xy (void *dst, size_t dst_pitch, unsigned x, unsigned y) +{ + return (char *)dst + dst_pitch * y + x * 4; +} + +static unsigned int +drawable_depth (Drawable d) +{ + return (d->type == WINDOW + ? visual_depth (NULL, NULL) + : d->pixmap.depth); +} + + int XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, int src_x, int src_y, @@ -626,8 +745,6 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, // Clip rects to frames... // -// CGRect orig_src_rect = src_rect; - CGRect orig_dst_rect = dst_rect; # define CLIP(THIS,THAT,VAL,SIZE) do { \ float off = THIS##_rect.origin.VAL; \ @@ -648,12 +765,31 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, CLIP (dst, src, x, width); CLIP (dst, src, y, height); + + // Not actually the original dst_rect, just the one before it's clipped to + // the src_frame. + CGRect orig_dst_rect = dst_rect; + CLIP (src, dst, x, width); CLIP (src, dst, y, height); # undef CLIP - if (src_rect.size.width <= 0 || src_rect.size.height <= 0) + if (orig_dst_rect.size.width <= 0 || orig_dst_rect.size.height <= 0) return 0; + + // Sort-of-special case where no pixels can be grabbed from the source, + // and the whole destination is filled with the background color. + if (src_rect.size.width < 0 || src_rect.size.height < 0) { + + Assert((int)src_rect.size.width == (int)dst_rect.size.width || + (int)src_rect.size.height == (int)dst_rect.size.height, + "size mismatch"); + + src_rect.size.width = 0; + src_rect.size.height = 0; + dst_rect.size.width = 0; + dst_rect.size.height = 0; + } NSObject *releaseme = 0; CGImageRef cgi; @@ -661,11 +797,116 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, BOOL free_cgi_p = NO; -#ifndef USE_IPHONE - // Because of the backbuffer, all iPhone Windows work like Pixmaps. - if (src->type == PIXMAP) -# endif - { + /* If we're copying from a bitmap to a bitmap, and there's nothing funny + going on with clipping masks or depths or anything, optimize it by + just doing a memcpy instead of going through a CGI. + */ + if (bitmap_context_p (src)) { + + if (bitmap_context_p (dst) && + gc->gcv.function == GXcopy && + !gc->gcv.clip_mask && + drawable_depth (src) == drawable_depth (dst)) { + + Assert(!(int)src_frame.origin.x && + !(int)src_frame.origin.y && + !(int)dst_frame.origin.x && + !(int)dst_frame.origin.y, + "unexpected non-zero origin"); + + char *src_data = CGBitmapContextGetData(src->cgc); + char *dst_data = CGBitmapContextGetData(dst->cgc); + size_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc); + size_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc); + + // Int to float and back again. It's not very safe, but it seems to work. + int src_x0 = src_rect.origin.x; + int dst_x0 = dst_rect.origin.x; + + // Flip the Y-axis a second time. + int src_y0 = (src_frame.origin.y + src_frame.size.height - + src_rect.size.height - src_rect.origin.y); + int dst_y0 = (dst_frame.origin.y + dst_frame.size.height - + dst_rect.size.height - dst_rect.origin.y); + + unsigned width0 = (int) src_rect.size.width; + unsigned height0 = (int) src_rect.size.height; + + Assert((int)src_rect.size.width == (int)dst_rect.size.width || + (int)src_rect.size.height == (int)dst_rect.size.height, + "size mismatch"); + { + char *src_data0 = seek_xy(src_data, src_pitch, src_x0, src_y0); + char *dst_data0 = seek_xy(dst_data, dst_pitch, dst_x0, dst_y0); + size_t src_pitch0 = src_pitch; + size_t dst_pitch0 = dst_pitch; + size_t bytes = width0 * 4; + + if (src == dst && dst_y0 > src_y0) { + // Copy upwards if the areas might overlap. + src_data0 += src_pitch0 * (height0 - 1); + dst_data0 += dst_pitch0 * (height0 - 1); + src_pitch0 = -src_pitch0; + dst_pitch0 = -dst_pitch0; + } + + size_t lines0 = height0; + while (lines0) { + // memcpy is an alias for memmove on OS X. + memmove(dst_data0, src_data0, bytes); + src_data0 += src_pitch0; + dst_data0 += dst_pitch0; + --lines0; + } + } + + if (clipped) { + int orig_dst_x = orig_dst_rect.origin.x; + int orig_dst_y = (dst_frame.origin.y + dst_frame.size.height - + orig_dst_rect.origin.y - orig_dst_rect.size.height); + int orig_width = orig_dst_rect.size.width; + int orig_height = orig_dst_rect.size.height; + + Assert (orig_dst_x >= 0 && + orig_dst_x + orig_width <= (int) dst_frame.size.width && + orig_dst_y >= 0 && + orig_dst_y + orig_height <= (int) dst_frame.size.height, + "wrong dimensions"); + + if (orig_dst_y < dst_y0) { + fill_rect_memset (seek_xy (dst_data, dst_pitch, + orig_dst_x, orig_dst_y), dst_pitch, + gc->gcv.background, orig_width, + dst_y0 - orig_dst_y); + } + + if (orig_dst_y + orig_height > dst_y0 + height0) { + fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x, + dst_y0 + height0), + dst_pitch, + gc->gcv.background, orig_width, + orig_dst_y + orig_height - dst_y0 - height0); + } + + if (orig_dst_x < dst_x0) { + fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x, dst_y0), + dst_pitch, gc->gcv.background, + dst_x0 - orig_dst_x, height0); + } + + if (dst_x0 + width0 < orig_dst_x + orig_width) { + fill_rect_memset (seek_xy (dst_data, dst_pitch, dst_x0 + width0, + dst_y0), + dst_pitch, gc->gcv.background, + orig_dst_x + orig_width - dst_x0 - width0, + height0); + } + } + + invalidate_drawable_cache (dst); + return 0; + } + // If we are copying from a Pixmap to a Pixmap or Window, we must first // copy the bits to an intermediary CGImage object, then copy that to the @@ -701,7 +942,7 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, if (src->type == PIXMAP && src->pixmap.depth == 1) mask_p = YES; -# ifndef USE_IPHONE +# ifndef USE_BACKBUFFER } else { /* (src->type == WINDOW) */ NSRect nsfrom; // NSRect != CGRect on 10.4 @@ -759,7 +1000,7 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, CGDataProviderRelease (prov); } -# endif // !USE_IPHONE +# endif // !USE_BACKBUFFER } CGContextRef cgc = dst->cgc; @@ -805,10 +1046,10 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, } else { // No cgi means src == dst, and both are Windows. -# ifdef USE_IPHONE +# ifdef USE_BACKBUFFER Assert (0, "NSCopyBits unimplemented"); // shouldn't be reached anyway return 0; -# else // !USE_IPHONE +# else // !USE_BACKBUFFER NSRect nsfrom; nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4 nsfrom.origin.y = src_rect.origin.y; @@ -818,7 +1059,7 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, nsto.x = dst_rect.origin.x; nsto.y = dst_rect.origin.y; NSCopyBits (0, nsfrom, nsto); -# endif // !USE_IPHONE +# endif // !USE_BACKBUFFER } pop_gc (dst, gc); @@ -954,7 +1195,7 @@ XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count) int XClearWindow (Display *dpy, Window win) { - Assert (win->type == WINDOW, "not a window"); + Assert (win && win->type == WINDOW, "not a window"); CGRect wr = win->frame; return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0); } @@ -962,7 +1203,7 @@ XClearWindow (Display *dpy, Window win) int XSetWindowBackground (Display *dpy, Window w, unsigned long pixel) { - Assert (w->type == WINDOW, "not a window"); + Assert (w && w->type == WINDOW, "not a window"); validate_pixel (pixel, 32, NO); w->window.background = pixel; return 0; @@ -1043,7 +1284,7 @@ XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n) int XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp) { - Assert (win->type == WINDOW, "not a window"); + Assert (win && win->type == WINDOW, "not a window"); CGContextRef cgc = win->cgc; set_color (cgc, win->window.background, 32, NO, YES); draw_rect (dpy, win, 0, x, y, w, h, NO, YES); @@ -1279,7 +1520,7 @@ XFreeGC (Display *dpy, GC gc) Status XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa) { - Assert (w->type == WINDOW, "not a window"); + Assert (w && w->type == WINDOW, "not a window"); memset (xgwa, 0, sizeof(*xgwa)); xgwa->x = w->frame.origin.x; xgwa->y = w->frame.origin.y; @@ -1760,8 +2001,9 @@ XGetImage (Display *dpy, Drawable d, int x, int y, unsigned long plane_mask, int format) { const unsigned char *data = 0; - int depth, ibpp, ibpl, alpha_first_p; -# ifndef USE_IPHONE + int depth, ibpp, ibpl; + enum { RGBA, ARGB, BGRA } src_format; // As bytes. +# ifndef USE_BACKBUFFER NSBitmapImageRep *bm = 0; # endif @@ -1772,7 +2014,7 @@ XGetImage (Display *dpy, Drawable d, int x, int y, CGContextRef cgc = d->cgc; -#ifndef USE_IPHONE +#ifndef USE_BACKBUFFER // Because of the backbuffer, all iPhone Windows work like Pixmaps. if (d->type == PIXMAP) # endif @@ -1780,31 +2022,31 @@ XGetImage (Display *dpy, Drawable d, int x, int y, depth = (d->type == PIXMAP ? d->pixmap.depth : 32); - // If it's a pixmap, we created it with kCGImageAlphaNoneSkipFirst. - // If it's an iPhone window, it's the other way around. - alpha_first_p = (d->type == PIXMAP); + // We create pixmaps and iPhone backbuffers with kCGImageAlphaNoneSkipFirst. + src_format = BGRA; // #### Should this be ARGB on PPC? ibpp = CGBitmapContextGetBitsPerPixel (cgc); ibpl = CGBitmapContextGetBytesPerRow (cgc); data = CGBitmapContextGetData (cgc); Assert (data, "CGBitmapContextGetData failed"); -# ifndef USE_IPHONE +# ifndef USE_BACKBUFFER } else { /* (d->type == WINDOW) */ // get the bits (desired sub-rectangle) out of the NSView NSRect nsfrom; nsfrom.origin.x = x; - nsfrom.origin.y = y; +// nsfrom.origin.y = y; + nsfrom.origin.y = d->frame.size.height - height - y; nsfrom.size.width = width; nsfrom.size.height = height; bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom]; depth = 32; - alpha_first_p = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat); + src_format = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? ARGB : RGBA; ibpp = [bm bitsPerPixel]; ibpl = [bm bytesPerRow]; data = [bm bitmapData]; Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed"); -# endif // !USE_IPHONE +# endif // !USE_BACKBUFFER } // data points at (x,y) with ibpl rowstride. ignore x,y from now on. @@ -1851,7 +2093,8 @@ XGetImage (Display *dpy, Drawable d, int x, int y, const unsigned char *iline2 = iline; unsigned char *oline2 = oline; - if (alpha_first_p) // ARGB + switch (src_format) { + case ARGB: for (xx = 0; xx < width; xx++) { unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF); unsigned char r = *iline2++; @@ -1864,7 +2107,8 @@ XGetImage (Display *dpy, Drawable d, int x, int y, *((uint32_t *) oline2) = pixel; oline2 += 4; } - else // RGBA + break; + case RGBA: for (xx = 0; xx < width; xx++) { unsigned char r = *iline2++; unsigned char g = *iline2++; @@ -1877,13 +2121,32 @@ XGetImage (Display *dpy, Drawable d, int x, int y, *((uint32_t *) oline2) = pixel; oline2 += 4; } + break; + case BGRA: + for (xx = 0; xx < width; xx++) { + unsigned char b = *iline2++; + unsigned char g = *iline2++; + unsigned char r = *iline2++; + unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF); + uint32_t pixel = ((a << 24) | + (r << 16) | + (g << 8) | + (b << 0)); + *((uint32_t *) oline2) = pixel; + oline2 += 4; + } + break; + default: + abort(); + break; + } oline += obpl; iline += ibpl; } } -# ifndef USE_IPHONE +# ifndef USE_BACKBUFFER if (bm) [bm release]; # endif @@ -2090,7 +2353,8 @@ XCreatePixmap (Display *dpy, Drawable d, width * 4, /* bpl */ dpy->colorspace, // Without this, it returns 0... - kCGImageAlphaNoneSkipFirst + (kCGImageAlphaNoneSkipFirst | + kCGBitmapByteOrder32Host) ); Assert (p->cgc, "could not create CGBitmapContext"); return p; @@ -2100,7 +2364,7 @@ XCreatePixmap (Display *dpy, Drawable d, int XFreePixmap (Display *d, Pixmap p) { - Assert (p->type == PIXMAP, "not a pixmap"); + Assert (p && p->type == PIXMAP, "not a pixmap"); invalidate_drawable_cache (p); CGContextRelease (p->cgc); if (p->pixmap.cgc_buffer) @@ -2132,7 +2396,8 @@ copy_pixmap (Display *dpy, Pixmap p) width * 4, /* bpl */ dpy->colorspace, // Without this, it returns 0... - kCGImageAlphaNoneSkipFirst + (kCGImageAlphaNoneSkipFirst | + kCGBitmapByteOrder32Host) ); Assert (p2->cgc, "could not create CGBitmapContext"); @@ -2182,7 +2447,7 @@ query_font (Font fid) XCharStruct *min = &f->min_bounds; XCharStruct *max = &f->max_bounds; -#define CEIL(F) ((F) < 0 ? floor(F) : ceil(F)) +#define CEIL(F) ((F) < 0 ? floor(F) : ceil(F)) f->fid = fid; f->min_char_or_byte2 = first; @@ -2290,11 +2555,31 @@ query_font (Font fid) advancement.x = adv.width; advancement.y = adv.height; - // Seems to be clipping by a pixel or two. Add a margin to be safe. - bbox.origin.x -= 2; - bbox.origin.y -= 2; - bbox.size.width += 4; - bbox.size.height += 4; + /* A bug that existed was that the GL FPS display was truncating + characters slightly: commas looked like periods. + + At one point, I believed the bounding box was being rounded + wrong and we needed to add padding to it here. + + I think what was actually going on was, I was computing rbearing + wrong. Also there was an off-by-one error in texfont.c, displaying + too little of the bitmap. + + Adding arbitrarily large padding to the bbox is fine in fontglide + and FPS display, but screws up BSOD. Increasing bbox width makes + inverted text print too wide; decreasing origin makes characters + clip. + + I think that all 3 states are correct now with the new lbearing + computation plus the texfont fix. + */ +# if 0 + double kludge = 2; + bbox.origin.x -= kludge; + bbox.origin.y -= kludge; + bbox.size.width += kludge; + bbox.size.height += kludge; +# endif } # endif // USE_IPHONE @@ -2305,12 +2590,13 @@ query_font (Font fid) cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height); cs->descent = CEIL(-bbox.origin.y); - cs->lbearing = CEIL (bbox.origin.x); - cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width); + cs->lbearing = floor (bbox.origin.x); +// cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width); + cs->rbearing = CEIL (bbox.origin.x + bbox.size.width) - cs->lbearing; cs->width = CEIL (advancement.x); - Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width), - "bbox w wrong"); +// Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width), +// "bbox w wrong"); Assert (cs->ascent + cs->descent == CEIL(bbox.size.height), "bbox h wrong"); @@ -2330,12 +2616,12 @@ query_font (Font fid) #if 0 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d " - " bb=%3d x %3d @ %3d %3d adv=%3d %3d\n", + " bb=%5.1f x %5.1f @ %5.1f %5.1f adv=%5.1f %5.1f\n", i, i, cs->width, cs->lbearing, cs->rbearing, cs->ascent, cs->descent, - (int) bbox.size.width, (int) bbox.size.height, - (int) bbox.origin.x, (int) bbox.origin.y, - (int) advancement.x, (int) advancement.y); + bbox.size.width, bbox.size.height, + bbox.origin.x, bbox.origin.y, + advancement.x, advancement.y); #endif } @@ -3009,7 +3295,7 @@ XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret, int *root_x_ret, int *root_y_ret, int *win_x_ret, int *win_y_ret, unsigned int *mask_ret) { - Assert (w->type == WINDOW, "not a window"); + Assert (w && w->type == WINDOW, "not a window"); # ifdef USE_IPHONE int x = w->window.last_mouse_x; @@ -3081,7 +3367,7 @@ XTranslateCoordinates (Display *dpy, Window w, Window dest_w, int *dest_x_ret, int *dest_y_ret, Window *child_ret) { - Assert (w->type == WINDOW, "not a window"); + Assert (w && w->type == WINDOW, "not a window"); # ifdef USE_IPHONE @@ -3151,11 +3437,25 @@ XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret, XComposeStatus *xc) { KeySym ks = XKeycodeToKeysym (0, e->keycode, 0); - char c = (char) ks; // could be smarter about modifiers here... + char c = 0; + // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string. + if ((unsigned int) ks <= 255) + c = (char) ks; + + // Put control characters in the string. Not meta. + if (e->state & ControlMask) { + if (c >= 'a' && c <= 'z') // Upcase control. + c -= 'a'-'A'; + if (c >= '@' && c <= '_') // Shift to control page. + c -= '@'; + if (c == ' ') // C-SPC is NULL. + c = 0; + } + if (k_ret) *k_ret = ks; if (size > 0) buf[0] = c; if (size > 1) buf[1] = 0; - return 0; + return (size > 0 ? 1 : 0); }