/* * tkMacOSXDraw.c -- * * This file contains functions that perform drawing to * Xlib windows. Most of the functions simple emulate * Xlib functions. * * Copyright (c) 1995-1997 Sun Microsystems, Inc. * Copyright 2001-2009, Apple Inc. * Copyright (c) 2006-2009 Daniel A. Steffen * 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 "xbytes.h" #include "tkButton.h" /* #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); static CGImageRef CreateCGImageWithXImage(XImage *ximage); static CGContextRef GetCGContextForDrawable(Drawable d); static void DrawCGImage(Drawable d, GC gc, CGContextRef context, CGImageRef image, unsigned long imageForeground, unsigned long imageBackground, CGRect imageBounds, CGRect srcBounds, CGRect dstBounds); /* *---------------------------------------------------------------------- * * 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; } /* *---------------------------------------------------------------------- * * BitmapRepFromDrawableRect * * Extract bitmap data from a MacOSX drawable as an NSBitmapImageRep. * * Results: * Returns an autoreleased NSBitmapRep representing the image of the given * rectangle of the given drawable. * * 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* BitmapRepFromDrawableRect( 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 means that the MacDrawable is functioning as a Tk Pixmap, so its view field is NULL. */ cg_context = GetCGContextForDrawable(drawable); CGRect image_rect = CGRectMake(x, y, width, height); cg_image = CGBitmapContextCreateImage( (CGContextRef) cg_context); sub_cg_image = CGImageCreateWithImageInRect(cg_image, image_rect); if ( sub_cg_image ) { /*This can be dealloc'ed prematurely if set for autorelease, causing crashes.*/ bitmap_rep = [NSBitmapImageRep alloc]; [bitmap_rep initWithCGImage:sub_cg_image]; } if ( cg_image ) { CGImageRelease(cg_image); } } else if ( (view = TkMacOSXDrawableView(mac_drawable)) ) { /* convert top-left coordinates to NSView 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); if ( [view lockFocusIfCanDraw] ) { /*This can be dealloc'ed prematurely if set for autorelease, causing crashes.*/ bitmap_rep = [NSBitmapImageRep alloc]; bitmap_rep = [bitmap_rep initWithFocusedViewRect:view_rect]; [view unlockFocus]; } else { TkMacOSXDbgMsg("Could not lock focus on view."); } } 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; display->request++; if (!width || !height) { /* This happens all the time. TkMacOSXDbgMsg("Drawing of empty area requested"); */ return; } if (!TkMacOSXSetupDrawingContext(dst, gc, 1, &dc)) { return; /*TkMacOSXDbgMsg("Failed to setup drawing context.");*/ } if ( dc.context ) { if (srcDraw->flags & TK_IS_PIXMAP) { img = TkMacOSXCreateCGImageWithDrawable(src); }else if (TkMacOSXDrawableWindow(src)) { bitmap_rep = BitmapRepFromDrawableRect(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) { DrawCGImage(dst, gc, dc.context, img, gc->foreground, gc->background, CGRectMake(0, 0, srcDraw->size.width, srcDraw->size.height), CGRectMake(src_x, src_y, width, height), CGRectMake(dest_x, dest_y, width, height)); CFRelease(img); } else { TkMacOSXDbgMsg("Failed to construct CGImage."); } } else { TkMacOSXDbgMsg("Invalid destination drawable - no context."); return; } 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; 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){ CGRect 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 { DrawCGImage(dst, gc, dc.context, img, gc->foreground, imageBackground, CGRectMake(0, 0, srcDraw->size.width, srcDraw->size.height), CGRectMake(src_x, src_y, width, height), CGRectMake(dest_x, dest_y, width, height)); 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); } } /* *---------------------------------------------------------------------- * * TkPutImage -- * * Copies a subimage from an in-memory image to a rectangle of * of the specified drawable. * * Results: * None. * * Side effects: * Draws the image on the specified drawable. * *---------------------------------------------------------------------- */ int TkPutImage( unsigned long *colors, /* Unused on Macintosh. */ int ncolors, /* Unused on Macintosh. */ Display* display, /* Display. */ Drawable d, /* Drawable to place image on. */ GC gc, /* GC to use. */ XImage* image, /* Image to place. */ int src_x, /* Source X & Y. */ int src_y, int dest_x, /* Destination X & Y. */ int dest_y, unsigned int width, /* Same width & height for both */ unsigned int height) /* distination and source. */ { TkMacOSXDrawingContext dc; display->request++; if (!TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { return BadDrawable; } if (dc.context) { CGImageRef img = CreateCGImageWithXImage(image); if (img) { /* If the XImage has big pixels, rescale the source dimensions.*/ int pp = image->pixelpower; DrawCGImage(d, gc, dc.context, img, gc->foreground, gc->background, CGRectMake(0, 0, image->width<height<bytes_per_line * image->height; const CGFloat *decode = NULL; CGBitmapInfo bitmapInfo; CGDataProviderRef provider = NULL; char *data = NULL; CGDataProviderReleaseDataCallback releaseData = ReleaseData; if (image->bits_per_pixel == 1) { /* * BW image */ /* Reverses the sense of the bits */ static const CGFloat decodeWB[2] = {1, 0}; decode = decodeWB; bitsPerComponent = 1; bitsPerPixel = 1; if (image->bitmap_bit_order != MSBFirst) { char *srcPtr = image->data + image->xoffset; char *endPtr = srcPtr + len; char *destPtr = (data = ckalloc(len)); while (srcPtr < endPtr) { *destPtr++ = xBitReverseTable[(unsigned char)(*(srcPtr++))]; } } else { data = memcpy(ckalloc(len), image->data + image->xoffset, len); } if (data) { provider = CGDataProviderCreateWithData(data, data, len, releaseData); } if (provider) { img = CGImageMaskCreate(image->width, image->height, bitsPerComponent, bitsPerPixel, image->bytes_per_line, provider, decode, 0); } } else if (image->format == ZPixmap && image->bits_per_pixel == 32) { /* * Color image */ CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); bitsPerComponent = 8; bitsPerPixel = 32; bitmapInfo = (image->byte_order == MSBFirst ? kCGBitmapByteOrder32Big : kCGBitmapByteOrder32Little) | kCGImageAlphaNoneSkipFirst; data = memcpy(ckalloc(len), image->data + image->xoffset, len); if (data) { provider = CGDataProviderCreateWithData(data, data, len, releaseData); } if (provider) { img = CGImageCreate(image->width, image->height, bitsPerComponent, bitsPerPixel, image->bytes_per_line, colorspace, bitmapInfo, provider, decode, 0, kCGRenderingIntentDefault); CFRelease(provider); } if (colorspace) { CFRelease(colorspace); } } else { TkMacOSXDbgMsg("Unsupported image type"); } return img; } /* *---------------------------------------------------------------------- * * 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 = GetCGContextForDrawable(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]; } /* *---------------------------------------------------------------------- * * GetCGContextForDrawable -- * * Get CGContext for given Drawable, creating one if necessary. * * Results: * CGContext. * * Side effects: * None. * *---------------------------------------------------------------------- */ CGContextRef GetCGContextForDrawable( Drawable d) { MacDrawable *macDraw = (MacDrawable *) d; 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 = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); 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); } /* *---------------------------------------------------------------------- * * DrawCGImage -- * * Draw CG image into drawable. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void DrawCGImage( 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)) { /*CGContextSaveGState(context);*/ 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; } /* *---------------------------------------------------------------------- * * 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 genereated 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. */ TkMacOSXSetWithNativeRegion(damageRgn, dmgRgn); if (extraRgn) { CFRelease(extraRgn); } /* Scroll the rectangle. */ [view scrollRect:scrollSrc by:NSMakeSize(dx, -dy)]; /* Shift the Tk children which meet the source rectangle. */ TkWindow *winPtr = (TkWindow *)tkwin; TkWindow *childPtr; CGRect childBounds; for (childPtr = winPtr->childList; childPtr != NULL; childPtr = childPtr->nextPtr) { if (Tk_IsMapped(childPtr) && !Tk_IsTopLevel(childPtr)) { TkMacOSXWinCGBounds(childPtr, &childBounds); if (CGRectIntersectsRect(srcRect, childBounds)) { MacDrawable *macChild = childPtr->privatePtr; if (macChild) { macChild->yOff += dy; macChild->xOff += dx; childPtr->changes.y = macChild->yOff; childPtr->changes.x = macChild->xOff; } } } } /* Queue up Expose events for the damage region. */ int oldMode = Tcl_SetServiceMode(TCL_SERVICE_NONE); [view generateExposeEvents:dmgRgn childrenOnly:1]; Tcl_SetServiceMode(oldMode); } } 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. * *---------------------------------------------------------------------- */ int TkMacOSXSetupDrawingContext( Drawable d, GC gc, int useCG, /* advisory only ! */ TkMacOSXDrawingContext *dcPtr) { MacDrawable *macDraw = ((MacDrawable*)d); int dontDraw = 0, isWin = 0; TkMacOSXDrawingContext dc = {}; CGRect clipBounds; dc.clipRgn = TkMacOSXGetClipRgn(d); if (!dontDraw) { ClipToGC(d, gc, &dc.clipRgn); dontDraw = dc.clipRgn ? HIShapeIsEmpty(dc.clipRgn) : 0; } if (dontDraw) { goto end; } if (useCG) { dc.context = GetCGContextForDrawable(d); } if (!dc.context || !(macDraw->flags & TK_IS_PIXMAP)) { isWin = (TkMacOSXDrawableWindow(d) != nil); } if (dc.context) { dc.portBounds = clipBounds = CGContextGetClipBoundingBox(dc.context); } else if (isWin) { NSView *view = TkMacOSXDrawableView(macDraw); if (view) { if (view != [NSView focusView]) { dc.focusLocked = [view lockFocusIfCanDraw]; dontDraw = !dc.focusLocked; } else { dontDraw = ![view canDraw]; } if (dontDraw) { goto end; } [[view window] disableFlushWindow]; dc.view = view; dc.context = [[NSGraphicsContext currentContext] graphicsPort]; dc.portBounds = NSRectToCGRect([view bounds]); if (dc.clipRgn) { clipBounds = CGContextGetClipBoundingBox(dc.context); } } else { Tcl_Panic("TkMacOSXSetupDrawingContext(): " "no NSView to draw into !"); } } else { Tcl_Panic("TkMacOSXSetupDrawingContext(): " "no context to draw into !"); } 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; if (!dc.focusLocked) { 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 (isWin) { 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: if (dontDraw && dc.clipRgn) { CFRelease(dc.clipRgn); dc.clipRgn = NULL; } *dcPtr = dc; return !dontDraw; } /* *---------------------------------------------------------------------- * * TkMacOSXRestoreDrawingContext -- * * Restore drawing context. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void TkMacOSXRestoreDrawingContext( TkMacOSXDrawingContext *dcPtr) { if (dcPtr->context) { CGContextSynchronize(dcPtr->context); [[dcPtr->view window] setViewsNeedDisplay:YES]; [[dcPtr->view window] enableFlushWindow]; if (dcPtr->focusLocked) { [dcPtr->view unlockFocus]; } else { 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); if ([view lockFocusIfCanDraw]) { CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; 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); [view unlockFocus]; } #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; NSView *view = TkMacOSXDrawableView(macDraw); 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; } if (view && view != [NSView focusView] && [view lockFocusIfCanDraw]) { clipRect.origin.y = [view bounds].size.height - (clipRect.origin.y + clipRect.size.height); NSRectClip(NSRectFromCGRect(clipRect)); macDraw->flags |= TK_FOCUSED_VIEW; } } else { if (view && (macDraw->flags & TK_FOCUSED_VIEW)) { [view unlockFocus]; macDraw->flags &= ~TK_FOCUSED_VIEW; } } } /* *---------------------------------------------------------------------- * * 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: */