diff options
Diffstat (limited to 'tk8.6/macosx/tkMacOSXDraw.c')
-rw-r--r-- | tk8.6/macosx/tkMacOSXDraw.c | 2078 |
1 files changed, 2078 insertions, 0 deletions
diff --git a/tk8.6/macosx/tkMacOSXDraw.c b/tk8.6/macosx/tkMacOSXDraw.c new file mode 100644 index 0000000..1793b64 --- /dev/null +++ b/tk8.6/macosx/tkMacOSXDraw.c @@ -0,0 +1,2078 @@ +/* + * tkMacOSXDraw.c -- + * + * This file contains functions that perform drawing to Xlib windows. Most + * of the functions simply emulate Xlib functions. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * Copyright 2001-2009, Apple Inc. + * Copyright (c) 2006-2009 Daniel A. Steffen <das@users.sourceforge.net> + * Copyright 2014 Marc Culler. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkMacOSXPrivate.h" +#include "tkMacOSXDebug.h" +#include "tkButton.h" + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101400 +#define GET_CGCONTEXT [[NSGraphicsContext currentContext] CGContext] +#else +#define GET_CGCONTEXT [[NSGraphicsContext currentContext] graphicsPort] +#endif + +/* +#ifdef TK_MAC_DEBUG +#define TK_MAC_DEBUG_DRAWING +#define TK_MAC_DEBUG_IMAGE_DRAWING +#endif +*/ + +#define radians(d) ((d) * (M_PI/180.0)) + +/* + * Non-antialiased CG drawing looks better and more like X11 drawing when using + * very fine lines, so decrease all linewidths by the following constant. + */ +#define NON_AA_CG_OFFSET .999 + +static int cgAntiAliasLimit = 0; +#define notAA(w) ((w) < cgAntiAliasLimit) + +static int useThemedToplevel = 0; +static int useThemedFrame = 0; + +/* + * Prototypes for functions used only in this file. + */ + +static void ClipToGC(Drawable d, GC gc, HIShapeRef *clipRgnPtr); + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXInitCGDrawing -- + * + * Initializes link vars that control CG drawing. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +MODULE_SCOPE int +TkMacOSXInitCGDrawing( + Tcl_Interp *interp, + int enable, + int limit) +{ + static Boolean initialized = FALSE; + + if (!initialized) { + initialized = TRUE; + + if (Tcl_CreateNamespace(interp, "::tk::mac", NULL, NULL) == NULL) { + Tcl_ResetResult(interp); + } + + if (Tcl_LinkVar(interp, "::tk::mac::CGAntialiasLimit", + (char *) &cgAntiAliasLimit, TCL_LINK_INT) != TCL_OK) { + Tcl_ResetResult(interp); + } + cgAntiAliasLimit = limit; + + /* + * Piggy-back the themed drawing var init here. + */ + + if (Tcl_LinkVar(interp, "::tk::mac::useThemedToplevel", + (char *) &useThemedToplevel, TCL_LINK_BOOLEAN) != TCL_OK) { + Tcl_ResetResult(interp); + } + if (Tcl_LinkVar(interp, "::tk::mac::useThemedFrame", + (char *) &useThemedFrame, TCL_LINK_BOOLEAN) != TCL_OK) { + Tcl_ResetResult(interp); + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXBitmapRepFromDrawableRect + * + * Extract bitmap data from a MacOSX drawable as an NSBitmapImageRep. + * + * This is only used by XGetImage, which is never called. And this + * implementation does not work correctly. Originally it relied on + * [NSBitmapImageRep initWithFocusedViewRect:view_rect] which was + * deprecated by Apple in OSX 10.14 and also required the use of other + * deprecated functions such as [NSView lockFocus]. Apple's suggested + * replacement is [NSView cacheDisplayInRect: toBitmapImageRep:] and that + * is what is being used here. However, that method only works when the + * view has a valid CGContext, and a view is only guaranteed to have a + * valid context during a call to [NSView drawRect]. To further complicate + * matters, cacheDisplayInRect calls [NSView drawRect]. Essentially it is + * asking the view to draw a subrectangle of itself into a special + * graphics context which is linked to the BitmapImageRep. But our + * implementation of [NSView drawRect] does not allow recursive calls. If + * called recursively it returns immediately without doing any drawing. + * So the bottom line is that this function either returns a NULL pointer + * or a black image. To make it useful would require a significant amount + * of rewriting of the drawRect method. Perhaps the next release of OSX + * will include some more helpful ways of doing this. + * + * Results: + * Returns an NSBitmapRep representing the image of the given rectangle of + * the given drawable. This object is retained. The caller is responsible + * for releasing it. + * + * NOTE: The x,y coordinates should be relative to a coordinate system + * with origin at the top left, as used by XImage and CGImage, not bottom + * left as used by NSView. + * + * Side effects: + * None + * + *---------------------------------------------------------------------- + */ + +NSBitmapImageRep * +TkMacOSXBitmapRepFromDrawableRect( + Drawable drawable, + int x, + int y, + unsigned int width, + unsigned int height) +{ + MacDrawable *mac_drawable = (MacDrawable *) drawable; + CGContextRef cg_context = NULL; + CGImageRef cg_image = NULL, sub_cg_image = NULL; + NSBitmapImageRep *bitmap_rep = NULL; + NSView *view = NULL; + if (mac_drawable->flags & TK_IS_PIXMAP) { + /* + * This MacDrawable is a bitmap, so its view is NULL. + */ + + CGRect image_rect = CGRectMake(x, y, width, height); + + cg_context = TkMacOSXGetCGContextForDrawable(drawable); + cg_image = CGBitmapContextCreateImage((CGContextRef) cg_context); + sub_cg_image = CGImageCreateWithImageInRect(cg_image, image_rect); + if (sub_cg_image) { + bitmap_rep = [NSBitmapImageRep alloc]; + [bitmap_rep initWithCGImage:sub_cg_image]; + } + if (cg_image) { + CGImageRelease(cg_image); + } + } else if ((view = TkMacOSXDrawableView(mac_drawable)) != NULL) { + /* + * Convert Tk top-left to NSView bottom-left coordinates. + */ + + int view_height = [view bounds].size.height; + NSRect view_rect = NSMakeRect(x + mac_drawable->xOff, + view_height - height - y - mac_drawable->yOff, + width, height); + + /* + * Attempt to copy from the view to a bitmapImageRep. If the view does + * not have a valid CGContext, doing this will silently corrupt memory + * and make a big mess. So, in that case, we mark the view as needing + * display and return NULL. + */ + + if (view == [NSView focusView]) { + bitmap_rep = [view bitmapImageRepForCachingDisplayInRect: view_rect]; + [bitmap_rep retain]; + [view cacheDisplayInRect:view_rect toBitmapImageRep:bitmap_rep]; + } else { + TkMacOSXDbgMsg("No CGContext - cannot copy from screen to bitmap."); + [view setNeedsDisplay:YES]; + return NULL; + } + } else { + TkMacOSXDbgMsg("Invalid source drawable"); + } + return bitmap_rep; +} + +/* + *---------------------------------------------------------------------- + * + * XCopyArea -- + * + * Copies data from one drawable to another. + * + * Results: + * None. + * + * Side effects: + * Data is moved from a window or bitmap to a second window or bitmap. + * + *---------------------------------------------------------------------- + */ + +void +XCopyArea( + Display *display, /* Display. */ + Drawable src, /* Source drawable. */ + Drawable dst, /* Destination drawable. */ + GC gc, /* GC to use. */ + int src_x, /* X & Y, width & height */ + int src_y, /* define the source rectangle */ + unsigned int width, /* that will be copied. */ + unsigned int height, + int dest_x, /* Dest X & Y on dest rect. */ + int dest_y) +{ + TkMacOSXDrawingContext dc; + MacDrawable *srcDraw = (MacDrawable *) src; + NSBitmapImageRep *bitmap_rep = NULL; + CGImageRef img = NULL; + CGRect bounds, srcRect, dstRect; + + display->request++; + if (!width || !height) { + return; + } + + if (!TkMacOSXSetupDrawingContext(dst, gc, 1, &dc)) { + TkMacOSXDbgMsg("Failed to setup drawing context."); + return; + } + + if (!dc.context) { + TkMacOSXDbgMsg("Invalid destination drawable - no context."); + return; + } + + if (srcDraw->flags & TK_IS_PIXMAP) { + img = TkMacOSXCreateCGImageWithDrawable(src); + } else if (TkMacOSXDrawableWindow(src)) { + bitmap_rep = TkMacOSXBitmapRepFromDrawableRect(src, + src_x, src_y, width, height); + if (bitmap_rep) { + img = [bitmap_rep CGImage]; + } + } else { + TkMacOSXDbgMsg("Invalid source drawable - neither window nor pixmap."); + } + + if (img) { + bounds = CGRectMake(0, 0, srcDraw->size.width, srcDraw->size.height); + srcRect = CGRectMake(src_x, src_y, width, height); + dstRect = CGRectMake(dest_x, dest_y, width, height); + TkMacOSXDrawCGImage(dst, gc, dc.context, img, + gc->foreground, gc->background, bounds, srcRect, dstRect); + CFRelease(img); + } else { + TkMacOSXDbgMsg("Failed to construct CGImage."); + } + + TkMacOSXRestoreDrawingContext(&dc); +} + +/* + *---------------------------------------------------------------------- + * + * XCopyPlane -- + * + * Copies a bitmap from a source drawable to a destination drawable. The + * plane argument specifies which bit plane of the source contains the + * bitmap. Note that this implementation ignores the gc->function. + * + * Results: + * None. + * + * Side effects: + * Changes the destination drawable. + * + *---------------------------------------------------------------------- + */ + +void +XCopyPlane( + Display *display, /* Display. */ + Drawable src, /* Source drawable. */ + Drawable dst, /* Destination drawable. */ + GC gc, /* GC to use. */ + int src_x, /* X & Y, width & height */ + int src_y, /* define the source rectangle */ + unsigned int width, /* that will be copied. */ + unsigned int height, + int dest_x, /* Dest X & Y on dest rect. */ + int dest_y, + unsigned long plane) /* Which plane to copy. */ +{ + TkMacOSXDrawingContext dc; + MacDrawable *srcDraw = (MacDrawable *) src; + MacDrawable *dstDraw = (MacDrawable *) dst; + CGRect bounds, srcRect, dstRect; + display->request++; + if (!width || !height) { + /* TkMacOSXDbgMsg("Drawing of empty area requested"); */ + return; + } + if (plane != 1) { + Tcl_Panic("Unexpected plane specified for XCopyPlane"); + } + if (srcDraw->flags & TK_IS_PIXMAP) { + if (!TkMacOSXSetupDrawingContext(dst, gc, 1, &dc)) { + return; + } + + CGContextRef context = dc.context; + + if (context) { + CGImageRef img = TkMacOSXCreateCGImageWithDrawable(src); + + if (img) { + TkpClipMask *clipPtr = (TkpClipMask *) gc->clip_mask; + unsigned long imageBackground = gc->background; + + if (clipPtr && clipPtr->type == TKP_CLIP_PIXMAP) { + srcRect = CGRectMake(src_x, src_y, width, height); + CGImageRef mask = TkMacOSXCreateCGImageWithDrawable( + clipPtr->value.pixmap); + CGImageRef submask = CGImageCreateWithImageInRect( + img, srcRect); + CGRect rect = CGRectMake(dest_x, dest_y, width, height); + + rect = CGRectOffset(rect, dstDraw->xOff, dstDraw->yOff); + CGContextSaveGState(context); + + /* + * Move the origin of the destination to top left. + */ + + CGContextTranslateCTM(context, + 0, rect.origin.y + CGRectGetMaxY(rect)); + CGContextScaleCTM(context, 1, -1); + + /* + * Fill with the background color, clipping to the mask. + */ + + CGContextClipToMask(context, rect, submask); + TkMacOSXSetColorInContext(gc, gc->background, dc.context); + CGContextFillRect(context, rect); + + /* + * Fill with the foreground color, clipping to the + * intersection of img and mask. + */ + + CGImageRef subimage = CGImageCreateWithImageInRect( + img, srcRect); + CGContextClipToMask(context, rect, subimage); + TkMacOSXSetColorInContext(gc, gc->foreground, context); + CGContextFillRect(context, rect); + CGContextRestoreGState(context); + CGImageRelease(img); + CGImageRelease(mask); + CGImageRelease(submask); + CGImageRelease(subimage); + } else { + bounds = CGRectMake(0, 0, + srcDraw->size.width, srcDraw->size.height); + srcRect = CGRectMake(src_x, src_y, width, height); + dstRect = CGRectMake(dest_x, dest_y, width, height); + TkMacOSXDrawCGImage(dst, gc, dc.context, img, + gc->foreground, imageBackground, bounds, + srcRect, dstRect); + CGImageRelease(img); + } + } else { + /* no image */ + TkMacOSXDbgMsg("Invalid source drawable"); + } + } else { + TkMacOSXDbgMsg("Invalid destination drawable - " + "could not get a bitmap context."); + } + TkMacOSXRestoreDrawingContext(&dc); + } else { + /* + * Source drawable is a Window, not a Pixmap. + */ + + XCopyArea(display, src, dst, gc, src_x, src_y, width, height, + dest_x, dest_y); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXCreateCGImageWithDrawable -- + * + * Create a CGImage from the given Drawable. + * + * Results: + * CGImage, release after use. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +CGImageRef +TkMacOSXCreateCGImageWithDrawable( + Drawable drawable) +{ + CGImageRef img = NULL; + CGContextRef context = TkMacOSXGetCGContextForDrawable(drawable); + + if (context) { + img = CGBitmapContextCreateImage(context); + } + return img; +} + +/* + *---------------------------------------------------------------------- + * + * CreateNSImageWithPixmap -- + * + * Create NSImage for Pixmap. + * + * Results: + * NSImage. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static NSImage * +CreateNSImageWithPixmap( + Pixmap pixmap, + int width, + int height) +{ + CGImageRef cgImage; + NSImage *nsImage; + NSBitmapImageRep *bitmapImageRep; + + cgImage = TkMacOSXCreateCGImageWithDrawable(pixmap); + nsImage = [[NSImage alloc] initWithSize:NSMakeSize(width, height)]; + bitmapImageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage]; + [nsImage addRepresentation:bitmapImageRep]; + [bitmapImageRep release]; + CFRelease(cgImage); + + return nsImage; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXGetNSImageWithTkImage -- + * + * Get autoreleased NSImage for Tk_Image. + * + * Results: + * NSImage. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +NSImage * +TkMacOSXGetNSImageWithTkImage( + Display *display, + Tk_Image image, + int width, + int height) +{ + Pixmap pixmap = Tk_GetPixmap(display, None, width, height, 0); + NSImage *nsImage; + + Tk_RedrawImage(image, 0, 0, width, height, pixmap, 0, 0); + nsImage = CreateNSImageWithPixmap(pixmap, width, height); + Tk_FreePixmap(display, pixmap); + + return [nsImage autorelease]; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXGetNSImageWithBitmap -- + * + * Get autoreleased NSImage for Bitmap. + * + * Results: + * NSImage. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +NSImage * +TkMacOSXGetNSImageWithBitmap( + Display *display, + Pixmap bitmap, + GC gc, + int width, + int height) +{ + Pixmap pixmap = Tk_GetPixmap(display, None, width, height, 0); + NSImage *nsImage; + + unsigned long origBackground = gc->background; + + gc->background = TRANSPARENT_PIXEL << 24; + XSetClipOrigin(display, gc, 0, 0); + XCopyPlane(display, bitmap, pixmap, gc, 0, 0, width, height, 0, 0, 1); + gc->background = origBackground; + nsImage = CreateNSImageWithPixmap(pixmap, width, height); + Tk_FreePixmap(display, pixmap); + + return [nsImage autorelease]; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXGetCGContextForDrawable -- + * + * Get CGContext for given Drawable, creating one if necessary. + * + * Results: + * CGContext. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +CGContextRef +TkMacOSXGetCGContextForDrawable( + Drawable drawable) +{ + MacDrawable *macDraw = (MacDrawable *) drawable; + + if (macDraw && (macDraw->flags & TK_IS_PIXMAP) && !macDraw->context) { + const size_t bitsPerComponent = 8; + size_t bitsPerPixel, bytesPerRow, len; + CGColorSpaceRef colorspace = NULL; + CGBitmapInfo bitmapInfo = +#ifdef __LITTLE_ENDIAN__ + kCGBitmapByteOrder32Host; +#else + kCGBitmapByteOrderDefault; +#endif + char *data; + CGRect bounds = CGRectMake(0, 0, + macDraw->size.width, macDraw->size.height); + + if (macDraw->flags & TK_IS_BW_PIXMAP) { + bitsPerPixel = 8; + bitmapInfo = (CGBitmapInfo) kCGImageAlphaOnly; + } else { + colorspace = CGColorSpaceCreateDeviceRGB(); + bitsPerPixel = 32; + bitmapInfo |= kCGImageAlphaPremultipliedFirst; + } + bytesPerRow = ((size_t) + macDraw->size.width * bitsPerPixel + 127) >> 3 & ~15; + len = macDraw->size.height * bytesPerRow; + data = ckalloc(len); + bzero(data, len); + macDraw->context = CGBitmapContextCreate(data, macDraw->size.width, + macDraw->size.height, bitsPerComponent, bytesPerRow, + colorspace, bitmapInfo); + if (macDraw->context) { + CGContextClearRect(macDraw->context, bounds); + } + if (colorspace) { + CFRelease(colorspace); + } + } + + return (macDraw ? macDraw->context : NULL); +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXDrawCGImage -- + * + * Draw CG image into drawable. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkMacOSXDrawCGImage( + Drawable d, + GC gc, + CGContextRef context, + CGImageRef image, + unsigned long imageForeground, + unsigned long imageBackground, + CGRect imageBounds, + CGRect srcBounds, + CGRect dstBounds) +{ + MacDrawable *macDraw = (MacDrawable *) d; + + if (macDraw && context && image) { + CGImageRef subImage = NULL; + + if (!CGRectEqualToRect(imageBounds, srcBounds)) { + if (!CGRectContainsRect(imageBounds, srcBounds)) { + TkMacOSXDbgMsg("Mismatch of sub CGImage bounds"); + } + subImage = CGImageCreateWithImageInRect(image, CGRectOffset( + srcBounds, -imageBounds.origin.x, -imageBounds.origin.y)); + if (subImage) { + image = subImage; + } + } + dstBounds = CGRectOffset(dstBounds, macDraw->xOff, macDraw->yOff); + if (CGImageIsMask(image)) { + if (macDraw->flags & TK_IS_BW_PIXMAP) { + /* + * Set fill color to black; background comes from the context, + * or is transparent. + */ + + if (imageBackground != TRANSPARENT_PIXEL << 24) { + CGContextClearRect(context, dstBounds); + } + CGContextSetRGBFillColor(context, 0.0, 0.0, 0.0, 1.0); + } else { + if (imageBackground != TRANSPARENT_PIXEL << 24) { + TkMacOSXSetColorInContext(gc, imageBackground, context); + CGContextFillRect(context, dstBounds); + } + TkMacOSXSetColorInContext(gc, imageForeground, context); + } + } + +#ifdef TK_MAC_DEBUG_IMAGE_DRAWING + CGContextSaveGState(context); + CGContextSetLineWidth(context, 1.0); + CGContextSetRGBStrokeColor(context, 0, 0, 0, 0.1); + CGContextSetRGBFillColor(context, 0, 1, 0, 0.1); + CGContextFillRect(context, dstBounds); + CGContextStrokeRect(context, dstBounds); + + CGPoint p[4] = {dstBounds.origin, + CGPointMake(CGRectGetMaxX(dstBounds), CGRectGetMaxY(dstBounds)), + CGPointMake(CGRectGetMinX(dstBounds), CGRectGetMaxY(dstBounds)), + CGPointMake(CGRectGetMaxX(dstBounds), CGRectGetMinY(dstBounds)) + }; + + CGContextStrokeLineSegments(context, p, 4); + CGContextRestoreGState(context); + TkMacOSXDbgMsg("Drawing CGImage at (x=%f, y=%f), (w=%f, h=%f)", + dstBounds.origin.x, dstBounds.origin.y, + dstBounds.size.width, dstBounds.size.height); +#else /* TK_MAC_DEBUG_IMAGE_DRAWING */ + CGContextSaveGState(context); + CGContextTranslateCTM(context, 0, dstBounds.origin.y + CGRectGetMaxY(dstBounds)); + CGContextScaleCTM(context, 1, -1); + CGContextDrawImage(context, dstBounds, image); + CGContextRestoreGState(context); +#endif /* TK_MAC_DEBUG_IMAGE_DRAWING */ + if (subImage) { + CFRelease(subImage); + } + } else { + TkMacOSXDbgMsg("Drawing of empty CGImage requested"); + } +} + +/* + *---------------------------------------------------------------------- + * + * XDrawLines -- + * + * Draw connected lines. + * + * Results: + * None. + * + * Side effects: + * Renders a series of connected lines. + * + *---------------------------------------------------------------------- + */ + +int +XDrawLines( + Display *display, /* Display. */ + Drawable d, /* Draw on this. */ + GC gc, /* Use this GC. */ + XPoint *points, /* Array of points. */ + int npoints, /* Number of points. */ + int mode) /* Line drawing mode. */ +{ + MacDrawable *macWin = (MacDrawable *) d; + TkMacOSXDrawingContext dc; + int i, lw = gc->line_width; + + if (npoints < 2) { + return BadValue; + } + + display->request++; + if (!TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { + return BadDrawable; + } + if (dc.context) { + double prevx, prevy; + double o = (lw % 2) ? .5 : 0; + + CGContextBeginPath(dc.context); + prevx = macWin->xOff + points[0].x + o; + prevy = macWin->yOff + points[0].y + o; + CGContextMoveToPoint(dc.context, prevx, prevy); + for (i = 1; i < npoints; i++) { + if (mode == CoordModeOrigin) { + CGContextAddLineToPoint(dc.context, + macWin->xOff + points[i].x + o, + macWin->yOff + points[i].y + o); + } else { + prevx += points[i].x; + prevy += points[i].y; + CGContextAddLineToPoint(dc.context, prevx, prevy); + } + } + + /* + * In the case of closed polylines, the first and last points are the + * same. We want miter or bevel join be rendered also at this point, + * this needs telling CoreGraphics that the path is closed. + */ + + if ((points[0].x == points[npoints-1].x) && + (points[0].y == points[npoints-1].y)) { + CGContextClosePath(dc.context); + } + CGContextStrokePath(dc.context); + } + TkMacOSXRestoreDrawingContext(&dc); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XDrawSegments -- + * + * Draw unconnected lines. + * + * Results: + * None. + * + * Side effects: + * Renders a series of unconnected lines. + * + *---------------------------------------------------------------------- + */ + +int +XDrawSegments( + Display *display, + Drawable d, + GC gc, + XSegment *segments, + int nsegments) +{ + MacDrawable *macWin = (MacDrawable *) d; + TkMacOSXDrawingContext dc; + int i, lw = gc->line_width; + + display->request++; + if (!TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { + return BadDrawable; + } + if (dc.context) { + double o = (lw % 2) ? .5 : 0; + + for (i = 0; i < nsegments; i++) { + CGContextBeginPath(dc.context); + CGContextMoveToPoint(dc.context, + macWin->xOff + segments[i].x1 + o, + macWin->yOff + segments[i].y1 + o); + CGContextAddLineToPoint(dc.context, + macWin->xOff + segments[i].x2 + o, + macWin->yOff + segments[i].y2 + o); + CGContextStrokePath(dc.context); + } + } + TkMacOSXRestoreDrawingContext(&dc); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XFillPolygon -- + * + * Draws a filled polygon. + * + * Results: + * None. + * + * Side effects: + * Draws a filled polygon on the specified drawable. + * + *---------------------------------------------------------------------- + */ + +void +XFillPolygon( + Display *display, /* Display. */ + Drawable d, /* Draw on this. */ + GC gc, /* Use this GC. */ + XPoint *points, /* Array of points. */ + int npoints, /* Number of points. */ + int shape, /* Shape to draw. */ + int mode) /* Drawing mode. */ +{ + MacDrawable *macWin = (MacDrawable *) d; + TkMacOSXDrawingContext dc; + int i; + + display->request++; + if (!TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { + return; + } + if (dc.context) { + double prevx, prevy; + double o = (gc->line_width % 2) ? .5 : 0; + + CGContextBeginPath(dc.context); + prevx = macWin->xOff + points[0].x + o; + prevy = macWin->yOff + points[0].y + o; + CGContextMoveToPoint(dc.context, prevx, prevy); + for (i = 1; i < npoints; i++) { + if (mode == CoordModeOrigin) { + CGContextAddLineToPoint(dc.context, + macWin->xOff + points[i].x + o, + macWin->yOff + points[i].y + o); + } else { + prevx += points[i].x; + prevy += points[i].y; + CGContextAddLineToPoint(dc.context, prevx, prevy); + } + } + CGContextEOFillPath(dc.context); + } + TkMacOSXRestoreDrawingContext(&dc); +} + +/* + *---------------------------------------------------------------------- + * + * XDrawRectangle -- + * + * Draws a rectangle. + * + * Results: + * None. + * + * Side effects: + * Draws a rectangle on the specified drawable. + * + *---------------------------------------------------------------------- + */ + +void +XDrawRectangle( + Display *display, /* Display. */ + Drawable d, /* Draw on this. */ + GC gc, /* Use this GC. */ + int x, int y, /* Upper left corner. */ + unsigned int width, /* Width & height of rect. */ + unsigned int height) +{ + MacDrawable *macWin = (MacDrawable *) d; + TkMacOSXDrawingContext dc; + int lw = gc->line_width; + + if (width == 0 || height == 0) { + return; + } + + display->request++; + if (!TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { + return; + } + if (dc.context) { + CGRect rect; + double o = (lw % 2) ? .5 : 0; + + rect = CGRectMake( + macWin->xOff + x + o, macWin->yOff + y + o, + width, height); + CGContextStrokeRect(dc.context, rect); + } + TkMacOSXRestoreDrawingContext(&dc); +} + +#ifdef TK_MACOSXDRAW_UNUSED +/* + *---------------------------------------------------------------------- + * + * XDrawRectangles -- + * + * Draws the outlines of the specified rectangles as if a five-point + * PolyLine protocol request were specified for each rectangle: + * + * [x,y] [x+width,y] [x+width,y+height] [x,y+height] [x,y] + * + * For the specified rectangles, these functions do not draw a pixel more + * than once. XDrawRectangles draws the rectangles in the order listed in + * the array. If rectangles intersect, the intersecting pixels are drawn + * multiple times. Draws a rectangle. + * + * Results: + * None. + * + * Side effects: + * Draws rectangles on the specified drawable. + * + *---------------------------------------------------------------------- + */ + +void +XDrawRectangles( + Display *display, + Drawable drawable, + GC gc, + XRectangle *rectArr, + int nRects) +{ + MacDrawable *macWin = (MacDrawable *) drawable; + TkMacOSXDrawingContext dc; + XRectangle * rectPtr; + int i, lw = gc->line_width; + + display->request++; + if (!TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { + return; + } + if (dc.context) { + CGRect rect; + double o = (lw % 2) ? .5 : 0; + + for (i = 0, rectPtr = rectArr; i < nRects; i++, rectPtr++) { + if (rectPtr->width == 0 || rectPtr->height == 0) { + continue; + } + rect = CGRectMake( + macWin->xOff + rectPtr->x + o, + macWin->yOff + rectPtr->y + o, + rectPtr->width, rectPtr->height); + CGContextStrokeRect(dc.context, rect); + } + } + TkMacOSXRestoreDrawingContext(&dc); +} +#endif + +/* + *---------------------------------------------------------------------- + * + * XFillRectangles -- + * + * Fill multiple rectangular areas in the given drawable. + * + * Results: + * None. + * + * Side effects: + * Draws onto the specified drawable. + * + *---------------------------------------------------------------------- + */ + +int +XFillRectangles( + Display *display, /* Display. */ + Drawable d, /* Draw on this. */ + GC gc, /* Use this GC. */ + XRectangle *rectangles, /* Rectangle array. */ + int n_rectangles) /* Number of rectangles. */ +{ + MacDrawable *macWin = (MacDrawable *) d; + TkMacOSXDrawingContext dc; + XRectangle * rectPtr; + int i; + + display->request++; + if (!TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { + return BadDrawable; + } + if (dc.context) { + CGRect rect; + + for (i = 0, rectPtr = rectangles; i < n_rectangles; i++, rectPtr++) { + if (rectPtr->width == 0 || rectPtr->height == 0) { + continue; + } + rect = CGRectMake( + macWin->xOff + rectPtr->x, + macWin->yOff + rectPtr->y, + rectPtr->width, rectPtr->height); + CGContextFillRect(dc.context, rect); + } + } + TkMacOSXRestoreDrawingContext(&dc); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXDrawSolidBorder -- + * + * Draws a border rectangle of specified thickness inside the bounding + * rectangle of a Tk Window. The border rectangle can be inset within the + * bounding rectangle. For a highlight border the inset should be 0, but + * for a solid border around the actual window the inset should equal the + * thickness of the highlight border. The color of the border rectangle + * is the foreground color of the graphics context passed to the function. + * + * Results: + * None. + * + * Side effects: + * Draws a rectangular border inside the bounding rectangle of a window. + * + *---------------------------------------------------------------------- + */ + +MODULE_SCOPE void +TkMacOSXDrawSolidBorder( + Tk_Window tkwin, + GC gc, + int inset, + int thickness) +{ + Drawable d = Tk_WindowId(tkwin); + TkMacOSXDrawingContext dc; + CGRect outerRect, innerRect; + + if (!TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { + return; + } + if (dc.context) { + outerRect = CGRectMake(Tk_X(tkwin), Tk_Y(tkwin), + Tk_Width(tkwin), Tk_Height(tkwin)); + outerRect = CGRectInset(outerRect, inset, inset); + innerRect = CGRectInset(outerRect, thickness, thickness); + CGContextBeginPath(dc.context); + CGContextAddRect(dc.context, outerRect); + CGContextAddRect(dc.context, innerRect); + CGContextEOFillPath(dc.context); + } + TkMacOSXRestoreDrawingContext(&dc); +} + +/* + *---------------------------------------------------------------------- + * + * XDrawArc -- + * + * Draw an arc. + * + * Results: + * None. + * + * Side effects: + * Draws an arc on the specified drawable. + * + *---------------------------------------------------------------------- + */ + +void +XDrawArc( + Display *display, /* Display. */ + Drawable d, /* Draw on this. */ + GC gc, /* Use this GC. */ + int x, int y, /* Upper left of bounding rect. */ + unsigned int width, /* Width & height. */ + unsigned int height, + int angle1, /* Staring angle of arc. */ + int angle2) /* Extent of arc. */ +{ + MacDrawable *macWin = (MacDrawable *) d; + TkMacOSXDrawingContext dc; + int lw = gc->line_width; + + if (width == 0 || height == 0 || angle2 == 0) { + return; + } + + display->request++; + if (!TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { + return; + } + if (dc.context) { + CGRect rect; + double o = (lw % 2) ? .5 : 0; + + rect = CGRectMake( + macWin->xOff + x + o, + macWin->yOff + y + o, + width, height); + if (angle1 == 0 && angle2 == 23040) { + CGContextStrokeEllipseInRect(dc.context, rect); + } else { + CGMutablePathRef p = CGPathCreateMutable(); + CGAffineTransform t = CGAffineTransformIdentity; + CGPoint c = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); + double w = CGRectGetWidth(rect); + + if (width != height) { + t = CGAffineTransformMakeScale(1.0, CGRectGetHeight(rect)/w); + c = CGPointApplyAffineTransform(c, CGAffineTransformInvert(t)); + } + CGPathAddArc(p, &t, c.x, c.y, w/2, radians(-angle1/64.0), + radians(-(angle1 + angle2)/64.0), angle2 > 0); + CGContextAddPath(dc.context, p); + CGPathRelease(p); + CGContextStrokePath(dc.context); + } + } + TkMacOSXRestoreDrawingContext(&dc); +} + +#ifdef TK_MACOSXDRAW_UNUSED +/* + *---------------------------------------------------------------------- + * + * XDrawArcs -- + * + * Draws multiple circular or elliptical arcs. Each arc is specified by a + * rectangle and two angles. The center of the circle or ellipse is the + * center of the rect- angle, and the major and minor axes are specified + * by the width and height. Positive angles indicate counterclock- wise + * motion, and negative angles indicate clockwise motion. If the magnitude + * of angle2 is greater than 360 degrees, XDrawArcs truncates it to 360 + * degrees. + * + * Results: + * None. + * + * Side effects: + * Draws an arc for each array element on the specified drawable. + * + *---------------------------------------------------------------------- + */ + +void +XDrawArcs( + Display *display, + Drawable d, + GC gc, + XArc *arcArr, + int nArcs) +{ + MacDrawable *macWin = (MacDrawable *) d; + TkMacOSXDrawingContext dc; + XArc *arcPtr; + int i, lw = gc->line_width; + + display->request++; + if (!TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { + return; + } + if (dc.context) { + CGRect rect; + double o = (lw % 2) ? .5 : 0; + + for (i=0, arcPtr = arcArr; i < nArcs; i++, arcPtr++) { + if (arcPtr->width == 0 || arcPtr->height == 0 + || arcPtr->angle2 == 0) { + continue; + } + rect = CGRectMake( + macWin->xOff + arcPtr->x + o, + macWin->yOff + arcPtr->y + o, + arcPtr->width, arcPtr->height); + + if (arcPtr->angle1 == 0 && arcPtr->angle2 == 23040) { + CGContextStrokeEllipseInRect(dc.context, rect); + } else { + CGMutablePathRef p = CGPathCreateMutable(); + CGAffineTransform t = CGAffineTransformIdentity; + CGPoint c = CGPointMake(CGRectGetMidX(rect), + CGRectGetMidY(rect)); + double w = CGRectGetWidth(rect); + + if (arcPtr->width != arcPtr->height) { + t = CGAffineTransformMakeScale(1, CGRectGetHeight(rect)/w); + c = CGPointApplyAffineTransform(c, + CGAffineTransformInvert(t)); + } + CGPathAddArc(p, &t, c.x, c.y, w/2, + radians(-arcPtr->angle1/64.0), + radians(-(arcPtr->angle1 + arcPtr->angle2)/64.0), + arcPtr->angle2 > 0); + CGContextAddPath(dc.context, p); + CGPathRelease(p); + CGContextStrokePath(dc.context); + } + } + } + TkMacOSXRestoreDrawingContext(&dc); +} +#endif + +/* + *---------------------------------------------------------------------- + * + * XFillArc -- + * + * Draw a filled arc. + * + * Results: + * None. + * + * Side effects: + * Draws a filled arc on the specified drawable. + * + *---------------------------------------------------------------------- + */ + +void +XFillArc( + Display *display, /* Display. */ + Drawable d, /* Draw on this. */ + GC gc, /* Use this GC. */ + int x, int y, /* Upper left of bounding rect. */ + unsigned int width, /* Width & height. */ + unsigned int height, + int angle1, /* Staring angle of arc. */ + int angle2) /* Extent of arc. */ +{ + MacDrawable *macWin = (MacDrawable *) d; + TkMacOSXDrawingContext dc; + int lw = gc->line_width; + + if (width == 0 || height == 0 || angle2 == 0) { + return; + } + + display->request++; + if (!TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { + return; + } + if (dc.context) { + CGRect rect; + double o = (lw % 2) ? .5 : 0, u = 0; + + if (notAA(lw)) { + o += NON_AA_CG_OFFSET/2; + u += NON_AA_CG_OFFSET; + } + rect = CGRectMake( + macWin->xOff + x + o, + macWin->yOff + y + o, + width - u, height - u); + + if (angle1 == 0 && angle2 == 23040) { + CGContextFillEllipseInRect(dc.context, rect); + } else { + CGMutablePathRef p = CGPathCreateMutable(); + CGAffineTransform t = CGAffineTransformIdentity; + CGPoint c = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); + double w = CGRectGetWidth(rect); + + if (width != height) { + t = CGAffineTransformMakeScale(1, CGRectGetHeight(rect)/w); + c = CGPointApplyAffineTransform(c, CGAffineTransformInvert(t)); + } + if (gc->arc_mode == ArcPieSlice) { + CGPathMoveToPoint(p, &t, c.x, c.y); + } + CGPathAddArc(p, &t, c.x, c.y, w/2, radians(-angle1/64.0), + radians(-(angle1 + angle2)/64.0), angle2 > 0); + CGPathCloseSubpath(p); + CGContextAddPath(dc.context, p); + CGPathRelease(p); + CGContextFillPath(dc.context); + } + } + TkMacOSXRestoreDrawingContext(&dc); +} + +#ifdef TK_MACOSXDRAW_UNUSED +/* + *---------------------------------------------------------------------- + * + * XFillArcs -- + * + * Draw a filled arc. + * + * Results: + * None. + * + * Side effects: + * Draws a filled arc for each array element on the specified drawable. + * + *---------------------------------------------------------------------- + */ + +void +XFillArcs( + Display *display, + Drawable d, + GC gc, + XArc *arcArr, + int nArcs) +{ + MacDrawable *macWin = (MacDrawable *) d; + TkMacOSXDrawingContext dc; + XArc * arcPtr; + int i, lw = gc->line_width; + + display->request++; + if (!TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { + return; + } + if (dc.context) { + CGRect rect; + double o = (lw % 2) ? .5 : 0, u = 0; + + if (notAA(lw)) { + o += NON_AA_CG_OFFSET/2; + u += NON_AA_CG_OFFSET; + } + for (i = 0, arcPtr = arcArr; i < nArcs; i++, arcPtr++) { + if (arcPtr->width == 0 || arcPtr->height == 0 + || arcPtr->angle2 == 0) { + continue; + } + rect = CGRectMake( + macWin->xOff + arcPtr->x + o, + macWin->yOff + arcPtr->y + o, + arcPtr->width - u, arcPtr->height - u); + if (arcPtr->angle1 == 0 && arcPtr->angle2 == 23040) { + CGContextFillEllipseInRect(dc.context, rect); + } else { + CGMutablePathRef p = CGPathCreateMutable(); + CGAffineTransform t = CGAffineTransformIdentity; + CGPoint c = CGPointMake(CGRectGetMidX(rect), + CGRectGetMidY(rect)); + double w = CGRectGetWidth(rect); + + if (arcPtr->width != arcPtr->height) { + t = CGAffineTransformMakeScale(1, CGRectGetHeight(rect)/w); + c = CGPointApplyAffineTransform(c, + CGAffineTransformInvert(t)); + } + if (gc->arc_mode == ArcPieSlice) { + CGPathMoveToPoint(p, &t, c.x, c.y); + } + CGPathAddArc(p, &t, c.x, c.y, w/2, + radians(-arcPtr->angle1/64.0), + radians(-(arcPtr->angle1 + arcPtr->angle2)/64.0), + arcPtr->angle2 > 0); + CGPathCloseSubpath(p); + CGContextAddPath(dc.context, p); + CGPathRelease(p); + CGContextFillPath(dc.context); + } + } + } + TkMacOSXRestoreDrawingContext(&dc); +} +#endif + +#ifdef TK_MACOSXDRAW_UNUSED +/* + *---------------------------------------------------------------------- + * + * XMaxRequestSize -- + * + *---------------------------------------------------------------------- + */ + +long +XMaxRequestSize( + Display *display) +{ + return (SHRT_MAX / 4); +} +#endif + +/* + *---------------------------------------------------------------------- + * + * TkScrollWindow -- + * + * Scroll a rectangle of the specified window and accumulate a damage + * region. + * + * Results: + * Returns 0 if the scroll generated no additional damage. Otherwise, sets + * the region that needs to be repainted after scrolling and returns 1. + * + * Side effects: + * Scrolls the bits in the window. + * + *---------------------------------------------------------------------- + */ + +int +TkScrollWindow( + Tk_Window tkwin, /* The window to be scrolled. */ + GC gc, /* GC for window to be scrolled. */ + int x, int y, /* Position rectangle to be scrolled. */ + int width, int height, + int dx, int dy, /* Distance rectangle should be moved. */ + TkRegion damageRgn) /* Region to accumulate damage in. */ +{ + Drawable drawable = Tk_WindowId(tkwin); + MacDrawable *macDraw = (MacDrawable *) drawable; + TKContentView *view = (TKContentView *) TkMacOSXDrawableView(macDraw); + CGRect srcRect, dstRect; + HIShapeRef dmgRgn = NULL, extraRgn = NULL; + NSRect bounds, visRect, scrollSrc, scrollDst; + int result = 0; + + if (view) { + /* + * Get the scroll area in NSView coordinates (origin at bottom left). + */ + + bounds = [view bounds]; + scrollSrc = NSMakeRect(macDraw->xOff + x, + bounds.size.height - height - (macDraw->yOff + y), + width, height); + scrollDst = NSOffsetRect(scrollSrc, dx, -dy); + + /* + * Limit scrolling to the window content area. + */ + + visRect = [view visibleRect]; + scrollSrc = NSIntersectionRect(scrollSrc, visRect); + scrollDst = NSIntersectionRect(scrollDst, visRect); + if (!NSIsEmptyRect(scrollSrc) && !NSIsEmptyRect(scrollDst)) { + /* + * Mark the difference between source and destination as damaged. + * This region is described in NSView coordinates (y=0 at the + * bottom) and converted to Tk coordinates later. + */ + + srcRect = CGRectMake(x, y, width, height); + dstRect = CGRectOffset(srcRect, dx, dy); + + /* + * Compute the damage. + */ + + dmgRgn = HIShapeCreateMutableWithRect(&srcRect); + extraRgn = HIShapeCreateWithRect(&dstRect); + ChkErr(HIShapeDifference, dmgRgn, extraRgn, + (HIMutableShapeRef) dmgRgn); + result = HIShapeIsEmpty(dmgRgn) ? 0 : 1; + + /* + * Convert to Tk coordinates, offset by the window origin. + */ + + TkMacOSXSetWithNativeRegion(damageRgn, dmgRgn); + if (extraRgn) { + CFRelease(extraRgn); + } + + /* + * Scroll the rectangle. + */ + + [view scrollRect:scrollSrc by:NSMakeSize(dx, -dy)]; + } + } else { + dmgRgn = HIShapeCreateEmpty(); + TkMacOSXSetWithNativeRegion(damageRgn, dmgRgn); + } + + if (dmgRgn) { + CFRelease(dmgRgn); + } + return result; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXSetUpGraphicsPort -- + * + * Set up the graphics port from the given GC. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkMacOSXSetUpGraphicsPort( + GC gc, /* GC to apply to current port. */ + void *destPort) +{ + Tcl_Panic("TkMacOSXSetUpGraphicsPort: Obsolete, no more QD!"); +} + + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXSetUpDrawingContext -- + * + * Set up a drawing context for the given drawable and GC. + * + * Results: + * Boolean indicating whether it is ok to draw; if false, drawing context + * was not setup, so do not attempt to draw and do not call + * TkMacOSXRestoreDrawingContext(). + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Bool +TkMacOSXSetupDrawingContext( + Drawable d, + GC gc, + int useCG, /* advisory only ! */ + TkMacOSXDrawingContext *dcPtr) +{ + MacDrawable *macDraw = (MacDrawable *) d; + Bool canDraw = true; + NSWindow *win = NULL; + TkMacOSXDrawingContext dc = {}; + CGRect clipBounds; + + /* + * If the drawable is not a pixmap and it has an associated NSWindow then + * we know we are drawing to a window. + */ + + if (!(macDraw->flags & TK_IS_PIXMAP)) { + win = TkMacOSXDrawableWindow(d); + } + + /* + * Check that we have a non-empty clipping region. + */ + + dc.clipRgn = TkMacOSXGetClipRgn(d); + ClipToGC(d, gc, &dc.clipRgn); + if (dc.clipRgn && HIShapeIsEmpty(dc.clipRgn)) { + canDraw = false; + goto end; + } + + /* + * If we already have a CGContext, use it. Otherwise, if we are drawing to + * a window then we can get one from the window. + */ + + dc.context = TkMacOSXGetCGContextForDrawable(d); + if (dc.context) { + dc.portBounds = clipBounds = CGContextGetClipBoundingBox(dc.context); + } else if (win) { + NSView *view = TkMacOSXDrawableView(macDraw); + + if (!view) { + Tcl_Panic("TkMacOSXSetupDrawingContext(): " + "no NSView to draw into !"); + } + + /* + * We can only draw into the view when the current CGContext is valid + * and belongs to the view. Validity can only be guaranteed inside of + * a view's drawRect or setFrame methods. The isDrawing attribute + * tells us whether we are being called from one of those methods. + * + * If the CGContext is not valid, or belongs to a different View, then + * we mark our view as needing display and return failure. It should + * get drawn in a later call to drawRect. + */ + + if (view != [NSView focusView]) { + [view setNeedsDisplay:YES]; + canDraw = false; + goto end; + } + dc.view = view; + dc.context = GET_CGCONTEXT; + dc.portBounds = NSRectToCGRect([view bounds]); + if (dc.clipRgn) { + clipBounds = CGContextGetClipBoundingBox(dc.context); + } + } else { + Tcl_Panic("TkMacOSXSetupDrawingContext(): " + "no context to draw into !"); + } + + /* + * Configure the drawing context. + */ + + if (dc.context) { + CGAffineTransform t = { + .a = 1, .b = 0, + .c = 0, .d = -1, + .tx = 0, + .ty = dc.portBounds.size.height + }; + + dc.portBounds.origin.x += macDraw->xOff; + dc.portBounds.origin.y += macDraw->yOff; + CGContextSaveGState(dc.context); + CGContextSetTextDrawingMode(dc.context, kCGTextFill); + CGContextConcatCTM(dc.context, t); + if (dc.clipRgn) { +#ifdef TK_MAC_DEBUG_DRAWING + CGContextSaveGState(dc.context); + ChkErr(HIShapeReplacePathInCGContext, dc.clipRgn, dc.context); + CGContextSetRGBFillColor(dc.context, 1.0, 0.0, 0.0, 0.1); + CGContextEOFillPath(dc.context); + CGContextRestoreGState(dc.context); +#endif /* TK_MAC_DEBUG_DRAWING */ + CGRect r; + + if (!HIShapeIsRectangular(dc.clipRgn) || !CGRectContainsRect( + *HIShapeGetBounds(dc.clipRgn, &r), + CGRectApplyAffineTransform(clipBounds, t))) { + ChkErr(HIShapeReplacePathInCGContext, dc.clipRgn, dc.context); + CGContextEOClip(dc.context); + } + } + if (gc) { + static const CGLineCap cgCap[] = { + [CapNotLast] = kCGLineCapButt, + [CapButt] = kCGLineCapButt, + [CapRound] = kCGLineCapRound, + [CapProjecting] = kCGLineCapSquare, + }; + static const CGLineJoin cgJoin[] = { + [JoinMiter] = kCGLineJoinMiter, + [JoinRound] = kCGLineJoinRound, + [JoinBevel] = kCGLineJoinBevel, + }; + bool shouldAntialias; + double w = gc->line_width; + + TkMacOSXSetColorInContext(gc, gc->foreground, dc.context); + if (win) { + CGContextSetPatternPhase(dc.context, CGSizeMake( + dc.portBounds.size.width, dc.portBounds.size.height)); + } + if (gc->function != GXcopy) { + TkMacOSXDbgMsg("Logical functions other than GXcopy are " + "not supported for CG drawing!"); + } + + /* + * When should we antialias? + */ + + shouldAntialias = !notAA(gc->line_width); + if (!shouldAntialias) { + /* + * Make non-antialiased CG drawing look more like X11. + */ + + w -= (gc->line_width ? NON_AA_CG_OFFSET : 0); + } + CGContextSetShouldAntialias(dc.context, shouldAntialias); + CGContextSetLineWidth(dc.context, w); + if (gc->line_style != LineSolid) { + int num = 0; + char *p = &gc->dashes; + CGFloat dashOffset = gc->dash_offset; + CGFloat lengths[10]; + + while (p[num] != '\0' && num < 10) { + lengths[num] = p[num]; + num++; + } + CGContextSetLineDash(dc.context, dashOffset, lengths, num); + } + if ((unsigned) gc->cap_style < sizeof(cgCap)/sizeof(CGLineCap)) { + CGContextSetLineCap(dc.context, + cgCap[(unsigned) gc->cap_style]); + } + if ((unsigned)gc->join_style < sizeof(cgJoin)/sizeof(CGLineJoin)) { + CGContextSetLineJoin(dc.context, + cgJoin[(unsigned) gc->join_style]); + } + } + } + +end: +#ifdef TK_MAC_DEBUG_DRAWING + if (!canDraw && win != NULL) { + TkWindow *winPtr = TkMacOSXGetTkWindow(win); + + if (winPtr) { + fprintf(stderr, "Cannot draw in %s - postponing.\n", + Tk_PathName(winPtr)); + } + } +#endif + if (!canDraw && dc.clipRgn) { + CFRelease(dc.clipRgn); + dc.clipRgn = NULL; + } + *dcPtr = dc; + return canDraw; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXRestoreDrawingContext -- + * + * Restore drawing context. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkMacOSXRestoreDrawingContext( + TkMacOSXDrawingContext *dcPtr) +{ + if (dcPtr->context) { + CGContextSynchronize(dcPtr->context); + CGContextRestoreGState(dcPtr->context); + } + if (dcPtr->clipRgn) { + CFRelease(dcPtr->clipRgn); + } +#ifdef TK_MAC_DEBUG + bzero(dcPtr, sizeof(TkMacOSXDrawingContext)); +#endif /* TK_MAC_DEBUG */ +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXGetClipRgn -- + * + * Get the clipping region needed to restrict drawing to the given + * drawable. + * + * Results: + * Clipping region. If non-NULL, CFRelease it when done. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +HIShapeRef +TkMacOSXGetClipRgn( + Drawable drawable) /* Drawable. */ +{ + MacDrawable *macDraw = (MacDrawable *) drawable; + HIShapeRef clipRgn = NULL; + + if (macDraw->winPtr && macDraw->flags & TK_CLIP_INVALID) { + TkMacOSXUpdateClipRgn(macDraw->winPtr); +#ifdef TK_MAC_DEBUG_DRAWING + TkMacOSXDbgMsg("%s", macDraw->winPtr->pathName); + + NSView *view = TkMacOSXDrawableView(macDraw); + + CGContextSaveGState(context); + CGContextConcatCTM(context, CGAffineTransformMake(1.0, 0.0, 0.0, + -1.0, 0.0, [view bounds].size.height)); + ChkErr(HIShapeReplacePathInCGContext, macDraw->visRgn, context); + CGContextSetRGBFillColor(context, 0.0, 1.0, 0.0, 0.1); + CGContextEOFillPath(context); + CGContextRestoreGState(context); +#endif /* TK_MAC_DEBUG_DRAWING */ + } + + if (macDraw->drawRgn) { + clipRgn = HIShapeCreateCopy(macDraw->drawRgn); + } else if (macDraw->visRgn) { + clipRgn = HIShapeCreateCopy(macDraw->visRgn); + } + return clipRgn; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXSetUpClippingRgn -- + * + * Set up the clipping region so that drawing only occurs on the specified + * X subwindow. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkMacOSXSetUpClippingRgn( + Drawable drawable) /* Drawable to update. */ +{ +} + +/* + *---------------------------------------------------------------------- + * + * TkpClipDrawableToRect -- + * + * Clip all drawing into the drawable d to the given rectangle. If width + * or height are negative, reset to no clipping. + * + * Results: + * None. + * + * Side effects: + * Subsequent drawing into d is offset and clipped as specified. + * + *---------------------------------------------------------------------- + */ + +void +TkpClipDrawableToRect( + Display *display, + Drawable d, + int x, int y, + int width, int height) +{ + MacDrawable *macDraw = (MacDrawable *) d; + + if (macDraw->drawRgn) { + CFRelease(macDraw->drawRgn); + macDraw->drawRgn = NULL; + } + if (width >= 0 && height >= 0) { + CGRect clipRect = CGRectMake(x + macDraw->xOff, y + macDraw->yOff, + width, height); + HIShapeRef drawRgn = HIShapeCreateWithRect(&clipRect); + + if (macDraw->winPtr && macDraw->flags & TK_CLIP_INVALID) { + TkMacOSXUpdateClipRgn(macDraw->winPtr); + } + if (macDraw->visRgn) { + macDraw->drawRgn = HIShapeCreateIntersection(macDraw->visRgn, + drawRgn); + CFRelease(drawRgn); + } else { + macDraw->drawRgn = drawRgn; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * ClipToGC -- + * + * Helper function to intersect given region with gc clip region. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +ClipToGC( + Drawable d, + GC gc, + HIShapeRef *clipRgnPtr) /* must point to initialized variable */ +{ + if (gc && gc->clip_mask && + ((TkpClipMask *) gc->clip_mask)->type == TKP_CLIP_REGION) { + TkRegion gcClip = ((TkpClipMask *) gc->clip_mask)->value.region; + int xOffset = ((MacDrawable *) d)->xOff + gc->clip_x_origin; + int yOffset = ((MacDrawable *) d)->yOff + gc->clip_y_origin; + HIShapeRef clipRgn = *clipRgnPtr, gcClipRgn; + + TkMacOSXOffsetRegion(gcClip, xOffset, yOffset); + gcClipRgn = TkMacOSXGetNativeRegion(gcClip); + if (clipRgn) { + *clipRgnPtr = HIShapeCreateIntersection(gcClipRgn, clipRgn); + CFRelease(clipRgn); + } else { + *clipRgnPtr = HIShapeCreateCopy(gcClipRgn); + } + CFRelease(gcClipRgn); + TkMacOSXOffsetRegion(gcClip, -xOffset, -yOffset); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXMakeStippleMap -- + * + * Given a drawable and a stipple pattern this function draws the pattern + * repeatedly over the drawable. The drawable can then be used as a mask + * for bit-bliting a stipple pattern over an object. + * + * Results: + * A BitMap data structure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void * +TkMacOSXMakeStippleMap( + Drawable drawable, /* Window to apply stipple. */ + Drawable stipple) /* The stipple pattern. */ +{ + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * TkpDrawHighlightBorder -- + * + * This procedure draws a rectangular ring around the outside of a widget + * to indicate that it has received the input focus. + * + * On the Macintosh, this puts a 1 pixel border in the bgGC color between + * the widget and the focus ring, except in the case where highlightWidth + * is 1, in which case the border is left out. + * + * For proper Mac L&F, use highlightWidth of 3. + * + * Results: + * None. + * + * Side effects: + * A rectangle "width" pixels wide is drawn in "drawable", corresponding + * to the outer area of "tkwin". + * + *---------------------------------------------------------------------- + */ + +void +TkpDrawHighlightBorder ( + Tk_Window tkwin, + GC fgGC, + GC bgGC, + int highlightWidth, + Drawable drawable) +{ + if (highlightWidth == 1) { + TkDrawInsetFocusHighlight (tkwin, fgGC, highlightWidth, drawable, 0); + } else { + TkDrawInsetFocusHighlight (tkwin, bgGC, highlightWidth, drawable, 0); + if (fgGC != bgGC) { + TkDrawInsetFocusHighlight (tkwin, fgGC, highlightWidth - 1, + drawable, 0); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpDrawFrame -- + * + * This procedure draws the rectangular frame area. If the user has + * requested themeing, it draws with the background theme. + * + * Results: + * None. + * + * Side effects: + * Draws inside the tkwin area. + * + *---------------------------------------------------------------------- + */ + +void +TkpDrawFrame( + Tk_Window tkwin, + Tk_3DBorder border, + int highlightWidth, + int borderWidth, + int relief) +{ + if (useThemedToplevel && Tk_IsTopLevel(tkwin)) { + static Tk_3DBorder themedBorder = NULL; + + if (!themedBorder) { + themedBorder = Tk_Get3DBorder(NULL, tkwin, + "systemWindowHeaderBackground"); + } + if (themedBorder) { + border = themedBorder; + } + } + + Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), + border, highlightWidth, highlightWidth, + Tk_Width(tkwin) - 2 * highlightWidth, + Tk_Height(tkwin) - 2 * highlightWidth, + borderWidth, relief); +} + +/* + * Local Variables: + * mode: objc + * c-basic-offset: 4 + * fill-column: 79 + * coding: utf-8 + * End: + */ |