X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=OSX%2Fjwxyz.m;fp=OSX%2Fjwxyz.m;h=8d181279e84b990311f0d48a2d4b7c9df95db311;hp=d7847c8f497f3ca11a7e324c223443381b8171ee;hb=88cfe534a698a0562e81345957a50714af1453bc;hpb=d1ae2829ff0fd2a96c16a0c8c5420efaa47d7b30 diff --git a/OSX/jwxyz.m b/OSX/jwxyz.m index d7847c8f..8d181279 100644 --- a/OSX/jwxyz.m +++ b/OSX/jwxyz.m @@ -858,6 +858,17 @@ push_bg_gc (Display *dpy, Drawable d, GC gc, Bool fill_p) push_color_gc (dpy, d, gc, gc->gcv.background, gc->gcv.antialias_p, 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 +} + /* You've got to be fucking kidding me! @@ -885,11 +896,7 @@ XDrawPoints (Display *dpy, Drawable d, GC gc, # ifdef XDRAWPOINTS_CGDATA -# ifdef USE_BACKBUFFER - if (1) // Because of the backbuffer, all iPhone Windows work like Pixmaps. -# else - if (d->type == PIXMAP) -# endif + if (bitmap_context_p (d)) { CGContextRef cgc = d->cgc; void *data = CGBitmapContextGetData (cgc); @@ -1021,33 +1028,13 @@ XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y) } +static void draw_rects (Display *dpy, Drawable d, GC gc, + const XRectangle *rectangles, unsigned nrectangles, + unsigned long pixel, BOOL fill_p); + 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; - } -} + unsigned long pixel, BOOL fill_p); static void * seek_xy (void *dst, size_t dst_pitch, unsigned x, unsigned y) @@ -1085,12 +1072,10 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, gc->gcv.function == GXclear) { // "set" and "clear" are dumb drawing modes that ignore the source // bits and just draw solid rectangles. - set_color (dpy, dst->cgc, + draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, (gc->gcv.function == GXset ? (gc->depth == 1 ? 1 : WhitePixel(dpy,0)) - : (gc->depth == 1 ? 0 : BlackPixel(dpy,0))), - gc->depth, gc->gcv.alpha_allowed_p, YES); - draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES); + : (gc->depth == 1 ? 0 : BlackPixel(dpy,0))), YES); return 0; } @@ -1168,176 +1153,141 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, dst_rect.size.height = 0; } - NSObject *releaseme = 0; - CGImageRef cgi; - BOOL mask_p = NO; - BOOL free_cgi_p = NO; + BOOL mask_p = src->type == PIXMAP && src->pixmap.depth == 1; /* 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, - (uint32_t) 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, - (uint32_t) gc->gcv.background, orig_width, - orig_dst_y + orig_height - dst_y0 - height0); - } + if (bitmap_context_p (src) && + 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; - if (orig_dst_x < dst_x0) { - fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x, dst_y0), - dst_pitch, (uint32_t) 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, (uint32_t) gc->gcv.background, - orig_dst_x + orig_width - dst_x0 - width0, - height0); - } + 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; } - invalidate_drawable_cache (dst); - return 0; + 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; + } } +# ifndef USE_BACKBUFFER + } else if (src->type == WINDOW && src == dst && !mask_p) { - - // 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 - // destination drawable's CGContext. + // If we are copying from a window to itself, we can use NSCopyBits() + // without first copying the rectangle to an intermediary CGImage. + // This is ~28% faster (but I *expected* it to be twice as fast...) + // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack) // - // (It doesn't seem to be possible to use NSCopyBits() to optimize the - // case of copying from a Pixmap back to itself, but I don't think that - // happens very often anyway.) - // - // First we get a CGImage out of the pixmap CGContext -- it's the whole - // pixmap, but it presumably shares the data pointer instead of copying - // it. We then cache that CGImage it inside the Pixmap object. Note: - // invalidate_drawable_cache() must be called to discard this any time a - // modification is made to the pixmap, or we'll end up re-using old bits. - // - if (!src->cgi) - src->cgi = CGBitmapContextCreateImage (src->cgc); - cgi = src->cgi; - - // if doing a sub-rect, trim it down. - if (src_rect.origin.x != src_frame.origin.x || - src_rect.origin.y != src_frame.origin.y || - src_rect.size.width != src_frame.size.width || - src_rect.size.height != src_frame.size.height) { - // #### I don't understand why this is needed... - src_rect.origin.y = (src_frame.size.height - - src_rect.size.height - src_rect.origin.y); - // This does not copy image data, so it should be fast. - cgi = CGImageCreateWithImageInRect (cgi, src_rect); - free_cgi_p = YES; - } - if (src->type == PIXMAP && src->pixmap.depth == 1) - mask_p = YES; + push_gc (dst, gc); -# ifndef USE_BACKBUFFER - } else { /* (src->type == WINDOW) */ - - NSRect nsfrom; // NSRect != CGRect on 10.4 - nsfrom.origin.x = src_rect.origin.x; + NSRect nsfrom; + nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4 nsfrom.origin.y = src_rect.origin.y; nsfrom.size.width = src_rect.size.width; nsfrom.size.height = src_rect.size.height; + NSPoint nsto; + nsto.x = dst_rect.origin.x; + nsto.y = dst_rect.origin.y; + NSCopyBits (0, nsfrom, nsto); + + pop_gc (dst, gc); + +# endif + } else { - if (src == dst) { + NSObject *releaseme = 0; + CGImageRef cgi; + BOOL free_cgi_p = NO; - // If we are copying from a window to itself, we can use NSCopyBits() - // without first copying the rectangle to an intermediary CGImage. - // This is ~28% faster (but I *expected* it to be twice as fast...) - // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack) + if (bitmap_context_p (src)) { + + // 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 + // destination drawable's CGContext. + // + // (It doesn't seem to be possible to use NSCopyBits() to optimize the + // case of copying from a Pixmap back to itself, but I don't think that + // happens very often anyway.) // - cgi = 0; + // First we get a CGImage out of the pixmap CGContext -- it's the whole + // pixmap, but it presumably shares the data pointer instead of copying + // it. We then cache that CGImage it inside the Pixmap object. Note: + // invalidate_drawable_cache() must be called to discard this any time a + // modification is made to the pixmap, or we'll end up re-using old bits. + // + if (!src->cgi) + src->cgi = CGBitmapContextCreateImage (src->cgc); + cgi = src->cgi; + + // if doing a sub-rect, trim it down. + if (src_rect.origin.x != src_frame.origin.x || + src_rect.origin.y != src_frame.origin.y || + src_rect.size.width != src_frame.size.width || + src_rect.size.height != src_frame.size.height) { + // #### I don't understand why this is needed... + src_rect.origin.y = (src_frame.size.height - + src_rect.size.height - src_rect.origin.y); + // This does not copy image data, so it should be fast. + cgi = CGImageCreateWithImageInRect (cgi, src_rect); + free_cgi_p = YES; + } - } else { +# ifndef USE_BACKBUFFER + } else { /* (src->type == WINDOW) */ + + NSRect nsfrom; // NSRect != CGRect on 10.4 + nsfrom.origin.x = src_rect.origin.x; + nsfrom.origin.y = src_rect.origin.y; + nsfrom.size.width = src_rect.size.width; + nsfrom.size.height = src_rect.size.height; // If we are copying from a Window to a Pixmap, we must first copy // the bits to an intermediary CGImage object, then copy that to the @@ -1360,7 +1310,7 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, NULL); cgi = CGImageCreate (src_rect.size.width, src_rect.size.height, bps, bpp, bpl, - dpy->colorspace, + dpy->colorspace, /* Use whatever default bit ordering we got from initWithFocusedViewRect. I would have assumed that it was (kCGImageAlphaNoneSkipFirst | @@ -1368,83 +1318,127 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, it's not! */ 0, - prov, + prov, NULL, /* decode[] */ NO, /* interpolate */ kCGRenderingIntentDefault); free_cgi_p = YES; //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace"); CGDataProviderRelease (prov); - } # endif // !USE_BACKBUFFER - } - - CGContextRef cgc = dst->cgc; + } - if (mask_p) { // src depth == 1 + CGContextRef cgc = dst->cgc; - push_bg_gc (dpy, dst, gc, YES); + if (mask_p) { // src depth == 1 - // fill the destination rectangle with solid background... - CGContextFillRect (cgc, orig_dst_rect); + push_bg_gc (dpy, dst, gc, YES); - Assert (cgc, "no CGC with 1-bit XCopyArea"); + // fill the destination rectangle with solid background... + CGContextFillRect (cgc, dst_rect); - // then fill in a solid rectangle of the fg color, using the image as an - // alpha mask. (the image has only values of BlackPixel or WhitePixel.) - set_color (dpy, cgc, gc->gcv.foreground, gc->depth, - gc->gcv.alpha_allowed_p, YES); - CGContextClipToMask (cgc, dst_rect, cgi); - CGContextFillRect (cgc, dst_rect); + Assert (cgc, "no CGC with 1-bit XCopyArea"); - pop_gc (dst, gc); + // then fill in a solid rectangle of the fg color, using the image as an + // alpha mask. (the image has only values of BlackPixel or WhitePixel.) + set_color (dpy, cgc, gc->gcv.foreground, gc->depth, + gc->gcv.alpha_allowed_p, YES); + CGContextClipToMask (cgc, dst_rect, cgi); + CGContextFillRect (cgc, dst_rect); - } else { // src depth > 1 + pop_gc (dst, gc); - push_gc (dst, gc); + } else { // src depth > 1 - // If either the src or dst rects did not lie within their drawables, - // then we have adjusted both the src and dst rects to account for - // the clipping; that means we need to first clear to the background, - // so that clipped bits end up in the bg color instead of simply not - // being copied. - // - if (clipped) { - set_color (dpy, cgc, gc->gcv.background, gc->depth, - gc->gcv.alpha_allowed_p, YES); - CGContextFillRect (cgc, orig_dst_rect); - } + push_gc (dst, gc); - if (cgi) { // copy the CGImage onto the destination CGContext //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace"); CGContextDrawImage (cgc, dst_rect, cgi); - } else { - // No cgi means src == dst, and both are Windows. -# ifdef USE_BACKBUFFER - Assert (0, "NSCopyBits unimplemented"); // shouldn't be reached anyway - return 0; -# else // !USE_BACKBUFFER - NSRect nsfrom; - nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4 - nsfrom.origin.y = src_rect.origin.y; - nsfrom.size.width = src_rect.size.width; - nsfrom.size.height = src_rect.size.height; - NSPoint nsto; - nsto.x = dst_rect.origin.x; - nsto.y = dst_rect.origin.y; - NSCopyBits (0, nsfrom, nsto); -# endif // !USE_BACKBUFFER + pop_gc (dst, gc); } - pop_gc (dst, gc); + if (free_cgi_p) CGImageRelease (cgi); + + if (releaseme) [releaseme release]; } - if (free_cgi_p) CGImageRelease (cgi); + // If either the src or dst rects did not lie within their drawables, then + // we have adjusted both the src and dst rects to account for the clipping; + // that means we need to clear to the background, so that clipped bits end + // up in the bg color instead of simply not being copied. + // + // This has to happen after the copy, because if it happens before, the + // cleared area will get grabbed if it overlaps with the source rectangle. + // + if (clipped && dst->type == WINDOW) { + // Int to float and back again. It's not very safe, but it seems to work. + int dst_x0 = dst_rect.origin.x; + + // Flip the Y-axis a second time. + 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; + + 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"); + + XRectangle rects[4]; + XRectangle *rects_end = rects; + + if (orig_dst_y < dst_y0) { + rects_end->x = orig_dst_x; + rects_end->y = orig_dst_y; + rects_end->width = orig_width; + rects_end->height = dst_y0 - orig_dst_y; + ++rects_end; + } + + if (orig_dst_y + orig_height > dst_y0 + height0) { + rects_end->x = orig_dst_x; + rects_end->y = dst_y0 + height0; + rects_end->width = orig_width; + rects_end->height = orig_dst_y + orig_height - dst_y0 - height0; + ++rects_end; + } + + if (orig_dst_x < dst_x0) { + rects_end->x = orig_dst_x; + rects_end->y = dst_y0; + rects_end->width = dst_x0 - orig_dst_x; + rects_end->height = height0; + ++rects_end; + } + + if (dst_x0 + width0 < orig_dst_x + orig_width) { + rects_end->x = dst_x0 + width0; + rects_end->y = dst_y0; + rects_end->width = orig_dst_x + orig_width - dst_x0 - width0; + rects_end->height = height0; + ++rects_end; + } + + unsigned long old_function = gc->gcv.function; + gc->gcv.function = GXcopy; + draw_rects (dpy, dst, gc, rects, rects_end - rects, + dst->window.background, + YES); + gc->gcv.function = old_function; + } - if (releaseme) [releaseme release]; invalidate_drawable_cache (dst); return 0; } @@ -1465,6 +1459,52 @@ XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc, } +static CGPoint +map_point (Drawable d, int x, int y) +{ + const CGRect *wr = &d->frame; + CGPoint p; + p.x = wr->origin.x + x; + p.y = wr->origin.y + wr->size.height - y; + return p; +} + + +static void +adjust_point_for_line (GC gc, CGPoint *p) +{ + // Here's the authoritative discussion on how X draws lines: + // http://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:CreateGC:line-width + if (gc->gcv.line_width <= 1) { + /* Thin lines are "drawn using an unspecified, device-dependent + algorithm", but seriously though, Bresenham's algorithm. Bresenham's + algorithm runs to and from pixel centers. + + There's a few screenhacks (Maze, at the very least) that set line_width + to 1 when it probably should be set to 0, so it's line_width <= 1 + instead of < 1. + */ + p->x += 0.5; + p->y -= 0.5; + } else { + /* Thick lines OTOH run from the upper-left corners of pixels. This means + that a horizontal thick line of width 1 straddles two scan lines. + Aliasing requires one of these scan lines be chosen; the following + nudges the point so that the right choice is made. */ + p->y -= 1e-3; + } +} + + +static CGPoint +point_for_line (Drawable d, GC gc, int x, int y) +{ + CGPoint result = map_point (d, x, y); + adjust_point_for_line (gc, &result); + return result; +} + + int XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2) { @@ -1475,14 +1515,14 @@ XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2) y1 -= w/2; if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound) return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64); - else + else { + if (!w) + w = 1; // Actually show zero-length lines. return XFillRectangle (dpy, d, gc, x1, y1, w, w); + } } - CGRect wr = d->frame; - NSPoint p; - p.x = wr.origin.x + x1; - p.y = wr.origin.y + wr.size.height - y1; + CGPoint p = point_for_line (d, gc, x1, y1); push_fg_gc (dpy, d, gc, NO); @@ -1490,8 +1530,7 @@ XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2) set_line_mode (cgc, &gc->gcv); CGContextBeginPath (cgc); CGContextMoveToPoint (cgc, p.x, p.y); - p.x = wr.origin.x + x2; - p.y = wr.origin.y + wr.size.height - y2; + p = point_for_line(d, gc, x2, y2); CGContextAddLineToPoint (cgc, p.x, p.y); CGContextStrokePath (cgc); pop_gc (d, gc); @@ -1504,8 +1543,7 @@ XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count, int mode) { int i; - NSPoint p; - CGRect wr = d->frame; + CGPoint p; push_fg_gc (dpy, d, gc, NO); CGContextRef cgc = d->cgc; @@ -1518,8 +1556,7 @@ XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count, points[0].y == points[count-1].y); if (closed_p) count--; - p.x = wr.origin.x + points->x; - p.y = wr.origin.y + wr.size.height - points->y; + p = point_for_line(d, gc, points->x, points->y); points++; CGContextBeginPath (cgc); CGContextMoveToPoint (cgc, p.x, p.y); @@ -1528,8 +1565,7 @@ XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count, p.x += points->x; p.y -= points->y; } else { - p.x = wr.origin.x + points->x; - p.y = wr.origin.y + wr.size.height - points->y; + p = point_for_line(d, gc, points->x, points->y); } CGContextAddLineToPoint (cgc, p.x, p.y); points++; @@ -1546,7 +1582,6 @@ int XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count) { int i; - CGRect wr = d->frame; CGContextRef cgc = d->cgc; @@ -1554,12 +1589,10 @@ XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count) set_line_mode (cgc, &gc->gcv); CGContextBeginPath (cgc); for (i = 0; i < count; i++) { - CGContextMoveToPoint (cgc, - wr.origin.x + segments->x1, - wr.origin.y + wr.size.height - segments->y1); - CGContextAddLineToPoint (cgc, - wr.origin.x + segments->x2, - wr.origin.y + wr.size.height - segments->y2); + CGPoint p = point_for_line (d, gc, segments->x1, segments->y1); + CGContextMoveToPoint (cgc, p.x, p.y); + p = point_for_line (d, gc, segments->x2, segments->y2); + CGContextAddLineToPoint (cgc, p.x, p.y); segments++; } CGContextStrokePath (cgc); @@ -1587,44 +1620,115 @@ XSetWindowBackground (Display *dpy, Window w, unsigned long pixel) } static void -draw_rect (Display *dpy, Drawable d, GC gc, - int x, int y, unsigned int width, unsigned int height, - BOOL foreground_p, BOOL fill_p) +draw_rects (Display *dpy, Drawable d, GC gc, + const XRectangle *rectangles, unsigned nrectangles, + unsigned long pixel, BOOL fill_p) { - CGRect wr = d->frame; - CGRect r; - r.origin.x = wr.origin.x + x; - r.origin.y = wr.origin.y + wr.size.height - y - height; - r.size.width = width; - r.size.height = height; - - if (gc) { - if (foreground_p) - push_fg_gc (dpy, d, gc, fill_p); - else - push_bg_gc (dpy, d, gc, fill_p); - } + Assert (!gc || gc->depth == drawable_depth (d), "depth mismatch"); CGContextRef cgc = d->cgc; - if (fill_p) - CGContextFillRect (cgc, r); - else { - if (gc) - set_line_mode (cgc, &gc->gcv); - CGContextStrokeRect (cgc, r); + + Bool fast_fill_p = + fill_p && + bitmap_context_p (d) && + (!gc || (gc->gcv.function == GXcopy && + !gc->gcv.alpha_allowed_p && + !gc->gcv.clip_mask)); + + if (!fast_fill_p) { + if (gc) { + push_color_gc (dpy, d, gc, pixel, gc->gcv.antialias_p, fill_p); + if (!fill_p) + set_line_mode (cgc, &gc->gcv); + } else { + set_color (dpy, d->cgc, pixel, drawable_depth (d), NO, fill_p); + } + } + + for (unsigned i = 0; i != nrectangles; ++i) { + + int x = rectangles[i].x; + int y = rectangles[i].y; + int width = rectangles[i].width; + int height = rectangles[i].height; + + if (fast_fill_p) { + int + dw = CGBitmapContextGetWidth (cgc), + dh = CGBitmapContextGetHeight (cgc); + + if (x >= dw || y >= dh) + continue; + + if (x < 0) { + width += x; + x = 0; + } + + if (y < 0) { + height += y; + y = 0; + } + + if (width <= 0 || height <= 0) + continue; + + int max_width = dw - x; + if (width > max_width) + width = max_width; + int max_height = dh - y; + if (height > max_height) + height = max_height; + + if (drawable_depth (d) == 1) + pixel = pixel ? WhitePixel(dpy, 0) : BlackPixel(dpy, 0); + + size_t dst_bytes_per_row = CGBitmapContextGetBytesPerRow (d->cgc); + void *dst = seek_xy (CGBitmapContextGetData (d->cgc), + dst_bytes_per_row, x, y); + + Assert(sizeof(wchar_t) == 4, "somebody changed the ABI"); + while (height) { + // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday. + wmemset (dst, pixel, width); + --height; + dst = (char *) dst + dst_bytes_per_row; + } + + } else { + CGRect r; + r.origin = map_point (d, x, y); + r.origin.y -= height; + r.size.width = width; + r.size.height = height; + if (fill_p) + CGContextFillRect (cgc, r); + else { + adjust_point_for_line (gc, &r.origin); + CGContextStrokeRect (cgc, r); + } + } } - if (gc) + if (!fast_fill_p && gc) pop_gc (d, gc); invalidate_drawable_cache (d); } +static void +draw_rect (Display *dpy, Drawable d, GC gc, + int x, int y, unsigned int width, unsigned int height, + unsigned long pixel, BOOL fill_p) +{ + XRectangle r = {x, y, width, height}; + draw_rects (dpy, d, gc, &r, 1, pixel, fill_p); +} int XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y, unsigned int width, unsigned int height) { - draw_rect (dpy, d, gc, x, y, width, height, YES, YES); + draw_rect (dpy, d, gc, x, y, width, height, gc->gcv.foreground, YES); return 0; } @@ -1632,28 +1736,14 @@ int XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y, unsigned int width, unsigned int height) { - draw_rect (dpy, d, gc, x, y, width, height, YES, NO); + draw_rect (dpy, d, gc, x, y, width, height, gc->gcv.foreground, NO); return 0; } int XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n) { - CGRect wr = d->frame; - int i; - CGContextRef cgc = d->cgc; - push_fg_gc (dpy, d, gc, YES); - for (i = 0; i < n; i++) { - CGRect r; - r.origin.x = wr.origin.x + rects->x; - r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height; - r.size.width = rects->width; - r.size.height = rects->height; - CGContextFillRect (cgc, r); - rects++; - } - pop_gc (d, gc); - invalidate_drawable_cache (d); + draw_rects (dpy, d, gc, rects, n, gc->gcv.foreground, YES); return 0; } @@ -1662,9 +1752,7 @@ int XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp) { Assert (win && win->type == WINDOW, "not a window"); - CGContextRef cgc = win->cgc; - set_color (dpy, cgc, win->window.background, 32, NO, YES); - draw_rect (dpy, win, 0, x, y, w, h, NO, YES); + draw_rect (dpy, win, 0, x, y, w, h, win->window.background, YES); return 0; } @@ -1858,11 +1946,7 @@ GC XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv) { struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc)); - if (d->type == WINDOW) { - gc->depth = 32; - } else { /* (d->type == PIXMAP) */ - gc->depth = d->pixmap.depth; - } + gc->depth = drawable_depth (d); gcv_defaults (dpy, &gc->gcv, gc->depth); set_gcv (dpy, gc, xgcv, mask); @@ -1919,7 +2003,7 @@ XGetGeometry (Display *dpy, Drawable d, Window *root_ret, *y_ret = d->frame.origin.y; *w_ret = d->frame.size.width; *h_ret = d->frame.size.height; - *d_ret = (d->type == WINDOW ? 32 : d->pixmap.depth); + *d_ret = drawable_depth (d); *root_ret = RootWindow (dpy, 0); *bw_ret = 0; return True; @@ -2283,11 +2367,10 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage, gc->gcv.function == GXclear) { // "set" and "clear" are dumb drawing modes that ignore the source // bits and just draw solid rectangles. - set_color (dpy, cgc, (gc->gcv.function == GXset - ? (gc->depth == 1 ? 1 : WhitePixel(dpy,0)) - : (gc->depth == 1 ? 0 : BlackPixel(dpy,0))), - gc->depth, gc->gcv.alpha_allowed_p, YES); - draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES); + draw_rect (dpy, d, 0, dest_x, dest_y, w, h, + (gc->gcv.function == GXset + ? (gc->depth == 1 ? 1 : WhitePixel(dpy,0)) + : (gc->depth == 1 ? 0 : BlackPixel(dpy,0))), YES); return 0; } @@ -2392,9 +2475,7 @@ XGetImage (Display *dpy, Drawable d, int x, int y, if (d->type == PIXMAP) # endif { - depth = (d->type == PIXMAP - ? d->pixmap.depth - : 32); + depth = drawable_depth (d); mode = convert_mode_to_rgba (dpy->screen->bitmap_info); ibpp = CGBitmapContextGetBitsPerPixel (cgc); ibpl = CGBitmapContextGetBytesPerRow (cgc); @@ -2601,8 +2682,8 @@ jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d, if (d->type == WINDOW) XClearWindow (dpy, d); else { - set_color (dpy, cgc, BlackPixel(dpy,0), 32, NO, YES); - draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES); + draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, + drawable_depth (d) == 1 ? 0 : BlackPixel(dpy,0), YES); } CGAffineTransform trans = @@ -2704,23 +2785,25 @@ copy_pixmap (Display *dpy, Pixmap p) if (!p) return 0; Assert (p->type == PIXMAP, "not a pixmap"); - int width = p->frame.size.width; - int height = p->frame.size.height; - char *data = (char *) malloc (width * height * 4); - if (! data) return 0; - - memcpy (data, p->pixmap.cgc_buffer, width * height * 4); + Pixmap p2 = 0; + + Window root; + int x, y; + unsigned int width, height, border_width, depth; + if (XGetGeometry (dpy, p, &root, + &x, &y, &width, &height, &border_width, &depth)) { + XGCValues gcv; + gcv.function = GXcopy; + GC gc = XCreateGC (dpy, p, GCFunction, &gcv); + if (gc) { + p2 = XCreatePixmap (dpy, p, width, height, depth); + if (p2) + XCopyArea (dpy, p, p2, gc, 0, 0, width, height, 0, 0); + XFreeGC (dpy, gc); + } + } - Pixmap p2 = (Pixmap) malloc (sizeof (*p2)); - *p2 = *p; - p2->cgi = 0; - p2->pixmap.cgc_buffer = data; - p2->cgc = CGBitmapContextCreate (data, width, height, - 8, /* bits per component */ - width * 4, /* bpl */ - dpy->colorspace, - dpy->screen->bitmap_info); - Assert (p2->cgc, "could not create CGBitmapContext"); + Assert (p2, "could not copy pixmap"); return p2; } @@ -3070,6 +3153,11 @@ try_font (NSFontTraitMask traits, NSFontTraitMask mask, return NULL; } + +/* On Cocoa and iOS, fonts may be specified as "Georgia Bold 24" instead + of XLFD strings; also they can be comma-separated strings with multiple + font names. First one that exists wins. + */ static NSFont * try_native_font (const char *name, float scale, char **name_ret, float *size_ret, char **xa_font) @@ -3077,28 +3165,46 @@ try_native_font (const char *name, float scale, if (!name) return 0; const char *spc = strrchr (name, ' '); if (!spc) return 0; - int dsize = 0; - if (1 != sscanf (spc, " %d ", &dsize)) return 0; - float size = dsize; - if (size <= 4) return 0; + NSFont *f = 0; + char *token = strdup (name); + char *name2; - size *= scale; + while ((name2 = strtok (token, ","))) { + token = 0; - char *name2 = strdup (name); - name2[strlen(name2) - strlen(spc)] = 0; - NSString *nsname = [NSString stringWithCString:name2 - encoding:NSUTF8StringEncoding]; - NSFont *f = [NSFont fontWithName:nsname size:size]; - if (f) { - *name_ret = name2; - *size_ret = size; - *xa_font = strdup (name); // Maybe this should be an XLFD? - return f; - } else { - free (name2); - return 0; + while (*name2 == ' ' || *name2 == '\t' || *name2 == '\n') + name2++; + + spc = strrchr (name2, ' '); + if (!spc) continue; + + int dsize = 0; + if (1 != sscanf (spc, " %d ", &dsize)) + continue; + float size = dsize; + + if (size <= 4) continue; + + size *= scale; + + name2[strlen(name2) - strlen(spc)] = 0; + + NSString *nsname = [NSString stringWithCString:name2 + encoding:NSUTF8StringEncoding]; + f = [NSFont fontWithName:nsname size:size]; + if (f) { + *name_ret = name2; + *size_ret = size; + *xa_font = strdup (name); // Maybe this should be an XLFD? + break; + } else { + NSLog(@"No native font: \"%@\" %.0f", nsname, size); + } } + + free (token); + return f; } @@ -3797,7 +3903,7 @@ XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y, MIN (0, cs.lbearing), cs.width), MAX (0, ascent) + MAX (0, descent), - NO, YES); + gc->gcv.background, YES); return XDrawString (dpy, d, gc, x, y, str, len); } @@ -3895,20 +4001,18 @@ XSetClipOrigin (Display *dpy, GC gc, int x, int y) } -Bool -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) +static void +get_pos (Window w, NSPoint *vpos, NSPoint *p) { - Assert (w && w->type == WINDOW, "not a window"); - # ifdef USE_IPHONE - int x = w->window.last_mouse_x; - int y = w->window.last_mouse_y; - if (root_x_ret) *root_x_ret = x; - if (root_y_ret) *root_y_ret = y; - if (win_x_ret) *win_x_ret = x; - if (win_y_ret) *win_y_ret = y; + + vpos->x = 0; + vpos->y = 0; + + if (p) { + p->x = w->window.last_mouse_x; + p->y = w->window.last_mouse_y; + } # else // !USE_IPHONE @@ -3918,17 +4022,16 @@ XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret, wpos.x = wpos.y = 0; wpos = [nsw convertBaseToScreen:wpos]; - NSPoint vpos; // get bottom left of view on window, from bottom left - vpos.x = vpos.y = 0; - vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]]; + vpos->x = vpos->y = 0; + *vpos = [w->window.view convertPoint:*vpos toView:[nsw contentView]]; // get bottom left of view on screen, from bottom left - vpos.x += wpos.x; - vpos.y += wpos.y; + vpos->x += wpos.x; + vpos->y += wpos.y; // get top left of view on screen, from bottom left - vpos.y += w->frame.size.height; + vpos->y += w->frame.size.height; // get top left of view on screen, from top left NSArray *screens = [NSScreen screens]; @@ -3936,25 +4039,38 @@ XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret, ? [screens objectAtIndex:0] : [NSScreen mainScreen]); NSRect srect = [screen frame]; - vpos.y = srect.size.height - vpos.y; - - // get the mouse position on window, from bottom left - NSEvent *e = [NSApp currentEvent]; - NSPoint p = [e locationInWindow]; - - // get mouse position on screen, from bottom left - p.x += wpos.x; - p.y += wpos.y; - - // get mouse position on screen, from top left - p.y = srect.size.height - p.y; + vpos->y = srect.size.height - vpos->y; + + if (p) { + // get the mouse position on window, from bottom left + NSEvent *e = [NSApp currentEvent]; + *p = [e locationInWindow]; + + // get mouse position on screen, from bottom left + p->x += wpos.x; + p->y += wpos.y; + + // get mouse position on screen, from top left + p->y = srect.size.height - p->y; + } + +# endif // !USE_IPHONE +} + +Bool +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 && w->type == WINDOW, "not a window"); + + NSPoint vpos, p; + get_pos (w, &vpos, &p); if (root_x_ret) *root_x_ret = (int) p.x; if (root_y_ret) *root_y_ret = (int) p.y; if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x); if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y); -# endif // !USE_IPHONE - if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers? if (root_ret) *root_ret = 0; if (child_ret) *child_ret = 0; @@ -3969,49 +4085,16 @@ XTranslateCoordinates (Display *dpy, Window w, Window dest_w, { Assert (w && w->type == WINDOW, "not a window"); -# ifdef USE_IPHONE - - NSPoint p; - p.x = src_x; - p.y = src_y; - -# else // !USE_IPHONE - - NSWindow *nsw = [w->window.view window]; - NSPoint wpos; - // get bottom left of window on screen, from bottom left - wpos.x = wpos.y = 0; - wpos = [nsw convertBaseToScreen:wpos]; - - NSPoint vpos; - // get bottom left of view on window, from bottom left - vpos.x = vpos.y = 0; - vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]]; + NSPoint vpos, p; + get_pos (w, &vpos, NULL); - // get bottom left of view on screen, from bottom left - vpos.x += wpos.x; - vpos.y += wpos.y; - - // get top left of view on screen, from bottom left - vpos.y += w->frame.size.height; - - // get top left of view on screen, from top left - NSArray *screens = [NSScreen screens]; - NSScreen *screen = (screens && [screens count] > 0 - ? [screens objectAtIndex:0] - : [NSScreen mainScreen]); - NSRect srect = [screen frame]; - vpos.y = srect.size.height - vpos.y; - // point starts out relative to top left of view - NSPoint p; p.x = src_x; p.y = src_y; - + // get point relative to top left of screen p.x += vpos.x; p.y += vpos.y; -# endif // !USE_IPHONE *dest_x_ret = p.x; *dest_y_ret = p.y;