diff options
author | das <das> | 2009-06-26 01:42:46 (GMT) |
---|---|---|
committer | das <das> | 2009-06-26 01:42:46 (GMT) |
commit | 501fcd66876991e90384cad2039ec6ee83d3c485 (patch) | |
tree | 2adb96ca0ec47497841adacd8b08b680f66112e5 /carbon/tkMacOSXDraw.c | |
parent | 24a50e27f48ca6c20da7657f1369203445367099 (diff) | |
download | tk-501fcd66876991e90384cad2039ec6ee83d3c485.zip tk-501fcd66876991e90384cad2039ec6ee83d3c485.tar.gz tk-501fcd66876991e90384cad2039ec6ee83d3c485.tar.bz2 |
* carbon/ (new directory): copy of current state of 'macosx'
source directory, to preserve legacy TkAqua implementation based on
Carbon API (with support for Mac OS X releases older than 10.5).
Diffstat (limited to 'carbon/tkMacOSXDraw.c')
-rw-r--r-- | carbon/tkMacOSXDraw.c | 2120 |
1 files changed, 2120 insertions, 0 deletions
diff --git a/carbon/tkMacOSXDraw.c b/carbon/tkMacOSXDraw.c new file mode 100644 index 0000000..1f90d2d --- /dev/null +++ b/carbon/tkMacOSXDraw.c @@ -0,0 +1,2120 @@ +/* + * 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, Apple Computer, Inc. + * Copyright (c) 2006-2007 Daniel A. Steffen <das@users.sourceforge.net> + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tkMacOSXDraw.c,v 1.1 2009/06/26 01:42:46 das Exp $ + */ + +#include "tkMacOSXPrivate.h" +#include "tkMacOSXDebug.h" +#include "xbytes.h" + +/* +#ifdef TK_MAC_DEBUG +#define TK_MAC_DEBUG_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 + +/* + * Temporary region that can be reused. + */ + +RgnHandle tkMacOSXtmpQdRgn = NULL; + +int tkMacOSXUseCGDrawing = 1; + +int tkPictureIsOpen; + +static PixPatHandle penPat = NULL, tmpPixPat = NULL; + +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 void NoQDClip(CGrafPtr port); + + +/* + *---------------------------------------------------------------------- + * + * 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::useCGDrawing", + (char *) &tkMacOSXUseCGDrawing, TCL_LINK_BOOLEAN) != TCL_OK) { + Tcl_ResetResult(interp); + } + tkMacOSXUseCGDrawing = enable; + + 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); + } + + if (tkMacOSXtmpQdRgn == NULL) { + tkMacOSXtmpQdRgn = NewRgn(); + } + } +#ifdef TK_MAC_DEBUG_DRAWING + TkMacOSXInitNamedDebugSymbol(QD, void, QD_DebugPrint, char*); + if (QD_DebugPrint) { + ; /* gdb: b *QD_DebugPrint */ + } +#endif /* TK_MAC_DEBUG_WINDOWS */ + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * XCopyArea -- + * + * Copies data from one drawable to another using block transfer + * routines. + * + * 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, *dstDraw = (MacDrawable *) dst; + + display->request++; + if (!width || !height) { + /* TkMacOSXDbgMsg("Drawing of emtpy area requested"); */ + return; + } + { + CGrafPtr srcPort; + + srcPort = TkMacOSXGetDrawablePort(src); + if (srcPort) { + Rect srcRect, dstRect, *srcPtr = &srcRect, *dstPtr = &dstRect; + const BitMap *srcBit, *dstBit; + RGBColor black = {0, 0, 0}, white = {0xffff, 0xffff, 0xffff}; + + if (!TkMacOSXSetupDrawingContext(dst, gc, 0, &dc)) { + return; + } + if (dc.context) { + TkMacOSXDbgMsg("Ignored CG drawing of QD drawable"); + goto end; + } + if (!dc.port) { + TkMacOSXDbgMsg("Invalid destination drawable"); + goto end; + } + srcBit = GetPortBitMapForCopyBits(srcPort); + dstBit = GetPortBitMapForCopyBits(dc.port); + SetRect(srcPtr, srcDraw->xOff + src_x, srcDraw->yOff + src_y, + srcDraw->xOff + src_x + width, + srcDraw->yOff + src_y + height); + if (tkPictureIsOpen) { + dstPtr = srcPtr; + } else { + SetRect(dstPtr, dstDraw->xOff + dest_x, dstDraw->yOff + dest_y, + dstDraw->xOff + dest_x + width, + dstDraw->yOff + dest_y + height); + } + RGBForeColor(&black); + RGBBackColor(&white); + CopyBits(srcBit, dstBit, srcPtr, dstPtr, srcCopy, NULL); +end: + TkMacOSXRestoreDrawingContext(&dc); + } else { + TkMacOSXDbgMsg("Invalid source drawable"); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * 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, *dstDraw = (MacDrawable *) dst; + + display->request++; + if (!width || !height) { + /* TkMacOSXDbgMsg("Drawing of emtpy area requested"); */ + return; + } + if (plane != 1) { + Tcl_Panic("Unexpected plane specified for XCopyPlane"); + } + { + CGrafPtr srcPort; + + srcPort = TkMacOSXGetDrawablePort(src); + if (srcPort) { + Rect srcRect, dstRect, *srcPtr = &srcRect, *dstPtr = &dstRect; + const BitMap *srcBit, *dstBit; + TkpClipMask *clipPtr = (TkpClipMask *) gc->clip_mask; + + if (!TkMacOSXSetupDrawingContext(dst, gc, 0, &dc)) { + return; + } + if (dc.context) { + TkMacOSXDbgMsg("Ignored CG drawing of QD drawable"); + goto end; + } + if (!dc.port) { + TkMacOSXDbgMsg("Invalid destination drawable"); + goto end; + } + srcBit = GetPortBitMapForCopyBits(srcPort); + dstBit = GetPortBitMapForCopyBits(dc.port); + SetRect(srcPtr, srcDraw->xOff + src_x, srcDraw->yOff + src_y, + srcDraw->xOff + src_x + width, + srcDraw->yOff + src_y + height); + if (tkPictureIsOpen) { + dstPtr = srcPtr; + } else { + SetRect(dstPtr, dstDraw->xOff + dest_x, dstDraw->yOff + dest_y, + dstDraw->xOff + dest_x + width, + dstDraw->yOff + dest_y + height); + } + TkMacOSXSetColorInPort(gc->foreground, 1, NULL, dc.port); + if (!clipPtr || clipPtr->type == TKP_CLIP_REGION) { + /* + * Opaque bitmaps. + */ + + TkMacOSXSetColorInPort(gc->background, 0, NULL, dc.port); + CopyBits(srcBit, dstBit, srcPtr, dstPtr, srcCopy, NULL); + } else if (clipPtr->type == TKP_CLIP_PIXMAP) { + if (clipPtr->value.pixmap == src) { + /* + * Transparent bitmaps. If it's color ignore the forecolor. + */ + short tmode = GetPixDepth(GetPortPixMap(srcPort)) == 1 ? + srcOr : transparent; + + CopyBits(srcBit, dstBit, srcPtr, dstPtr, tmode, NULL); + } else { + /* + * Two arbitrary bitmaps. + */ + + CGrafPtr mskPort = TkMacOSXGetDrawablePort( + clipPtr->value.pixmap); + const BitMap *mskBit = GetPortBitMapForCopyBits(mskPort); + + CopyDeepMask(srcBit, mskBit, dstBit, srcPtr, srcPtr, + dstPtr, srcCopy, NULL); + } + } +end: + TkMacOSXRestoreDrawingContext(&dc); + } else { + TkMacOSXDbgMsg("Invalid source drawable"); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * 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. + * + *---------------------------------------------------------------------- + */ + +void +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; + MacDrawable *dstDraw = (MacDrawable *) d; + + display->request++; + if (!TkMacOSXSetupDrawingContext(d, gc, 0, &dc)) { + return; + } + if (dc.context) { + TkMacOSXDbgMsg("Ignored CG drawing of XImage"); + } else { + Rect srcRect, dstRect, *srcPtr = &srcRect, *dstPtr = &dstRect; + const BitMap *dstBit; + RGBColor black = {0, 0, 0}, white = {0xffff, 0xffff, 0xffff}; + int i, j; + char *newData = NULL; + char *dataPtr, *newPtr, *oldPtr; + int rowBytes = image->bytes_per_line, sliceRowBytes, lastSliceRowBytes; + int slices, sliceWidth, lastSliceWidth; + + dstBit = GetPortBitMapForCopyBits(dc.port); + SetRect(srcPtr, src_x, src_y, src_x + width, src_y + height); + if (tkPictureIsOpen) { + dstPtr = srcPtr; + } else { + SetRect(dstPtr, dstDraw->xOff + dest_x, dstDraw->yOff + dest_y, + dstDraw->xOff + dest_x + width, + dstDraw->yOff + dest_y + height); + } + RGBForeColor(&black); + RGBBackColor(&white); + if (image->obdata) { + /* + * Image from XGetImage, copy from containing GWorld directly. + */ + + CopyBits(GetPortBitMapForCopyBits(TkMacOSXGetDrawablePort( + (Drawable)image->obdata)), dstBit, + srcPtr, dstPtr, srcCopy, NULL); + } else if (image->depth == 1) { + /* + * BW image + */ + + const int maxRowBytes = 0x3ffe; + BitMap bitmap; + int odd; + + if (rowBytes > maxRowBytes) { + slices = rowBytes / maxRowBytes; + sliceRowBytes = maxRowBytes; + lastSliceRowBytes = rowBytes - (slices * maxRowBytes); + if (!lastSliceRowBytes) { + slices--; + lastSliceRowBytes = maxRowBytes; + } + sliceWidth = (long) image->width * maxRowBytes / rowBytes; + lastSliceWidth = image->width - (sliceWidth * slices); + } else { + slices = 0; + sliceRowBytes = lastSliceRowBytes = rowBytes; + sliceWidth = lastSliceWidth = image->width; + } + bitmap.bounds.top = bitmap.bounds.left = 0; + bitmap.bounds.bottom = (short) image->height; + dataPtr = image->data + image->xoffset; + do { + if (slices) { + bitmap.bounds.right = bitmap.bounds.left + sliceWidth; + } else { + sliceRowBytes = lastSliceRowBytes; + bitmap.bounds.right = bitmap.bounds.left + lastSliceWidth; + } + oldPtr = dataPtr; + odd = sliceRowBytes % 2; + if (!newData) { + newData = ckalloc(image->height * (sliceRowBytes+odd)); + } + newPtr = newData; + if (image->bitmap_bit_order != MSBFirst) { + for (i = 0; i < image->height; i++) { + for (j = 0; j < sliceRowBytes; j++) { + *newPtr = xBitReverseTable[(unsigned char)*oldPtr]; + newPtr++; oldPtr++; + } + if (odd) { + *newPtr++ = 0; + } + oldPtr += rowBytes - sliceRowBytes; + } + } else { + for (i = 0; i < image->height; i++) { + memcpy(newPtr, oldPtr, sliceRowBytes); + newPtr += sliceRowBytes; + if (odd) { + *newPtr++ = 0; + } + oldPtr += rowBytes; + } + } + bitmap.baseAddr = newData; + bitmap.rowBytes = sliceRowBytes + odd; + CopyBits(&bitmap, dstBit, srcPtr, dstPtr, srcCopy, NULL); + if (slices) { + bitmap.bounds.left = bitmap.bounds.right; + dataPtr += sliceRowBytes; + } + } while (slices--); + ckfree(newData); + } else { + /* + * Color image + */ + + const int maxRowBytes = 0x3ffc; + PixMap pixmap; + + pixmap.bounds.left = 0; + pixmap.bounds.top = 0; + pixmap.bounds.bottom = (short) image->height; + pixmap.pixelType = RGBDirect; + pixmap.pmVersion = baseAddr32; /* 32bit clean */ + pixmap.packType = 0; + pixmap.packSize = 0; + pixmap.hRes = 0x00480000; + pixmap.vRes = 0x00480000; + pixmap.pixelSize = 32; + pixmap.cmpCount = 3; + pixmap.cmpSize = 8; + pixmap.pixelFormat = image->byte_order == MSBFirst ? + k32ARGBPixelFormat : k32BGRAPixelFormat; + pixmap.pmTable = NULL; + pixmap.pmExt = 0; + if (rowBytes > maxRowBytes) { + slices = rowBytes / maxRowBytes; + sliceRowBytes = maxRowBytes; + lastSliceRowBytes = rowBytes - (slices * maxRowBytes); + if (!lastSliceRowBytes) { + slices--; + lastSliceRowBytes = maxRowBytes; + } + sliceWidth = (long) image->width * maxRowBytes / rowBytes; + lastSliceWidth = image->width - (sliceWidth * slices); + dataPtr = image->data + image->xoffset; + newData = (char *) ckalloc(image->height * sliceRowBytes); + do { + if (slices) { + pixmap.bounds.right = pixmap.bounds.left + sliceWidth; + } else { + sliceRowBytes = lastSliceRowBytes; + pixmap.bounds.right = pixmap.bounds.left + lastSliceWidth; + } + oldPtr = dataPtr; + newPtr = newData; + for (i = 0; i < image->height; i++) { + memcpy(newPtr, oldPtr, sliceRowBytes); + oldPtr += rowBytes; + newPtr += sliceRowBytes; + } + pixmap.baseAddr = newData; + pixmap.rowBytes = sliceRowBytes | 0x8000; + CopyBits((BitMap*) &pixmap, dstBit, srcPtr, dstPtr, srcCopy, + NULL); + if (slices) { + pixmap.bounds.left = pixmap.bounds.right; + dataPtr += sliceRowBytes; + } + } while (slices--); + ckfree(newData); + } else { + pixmap.bounds.right = (short) image->width; + pixmap.baseAddr = image->data + image->xoffset; + pixmap.rowBytes = rowBytes | 0x8000; + CopyBits((BitMap*) &pixmap, dstBit, srcPtr, dstPtr, srcCopy, NULL); + } + } + } + TkMacOSXRestoreDrawingContext(&dc); +} + +/* + *---------------------------------------------------------------------- + * + * XDrawLines -- + * + * Draw connected lines. + * + * Results: + * None. + * + * Side effects: + * Renders a series of connected lines. + * + *---------------------------------------------------------------------- + */ + +void +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) { + /* + * TODO: generate BadValue error. + */ + + return; + } + + display->request++; + if (!TkMacOSXSetupDrawingContext(d, gc, tkMacOSXUseCGDrawing, &dc)) { + return; + } + 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); + } + } + CGContextStrokePath(dc.context); + } else { + int o = -lw/2; + + /* This is broken for fat lines, it is not possible to correctly + * imitate X11 drawing of oblique fat lines with QD line drawing, + * we should draw a filled polygon instead. */ + + MoveTo((short) (macWin->xOff + points[0].x + o), + (short) (macWin->yOff + points[0].y + o)); + for (i = 1; i < npoints; i++) { + if (mode == CoordModeOrigin) { + LineTo((short) (macWin->xOff + points[i].x + o), + (short) (macWin->yOff + points[i].y + o)); + } else { + Line((short) points[i].x, (short) points[i].y); + } + } + } + TkMacOSXRestoreDrawingContext(&dc); +} + +/* + *---------------------------------------------------------------------- + * + * XDrawSegments -- + * + * Draw unconnected lines. + * + * Results: + * None. + * + * Side effects: + * Renders a series of unconnected lines. + * + *---------------------------------------------------------------------- + */ + +void +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, tkMacOSXUseCGDrawing, &dc)) { + return; + } + 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); + } + } else { + int o = -lw/2; + + /* This is broken for fat lines, it is not possible to correctly + * imitate X11 drawing of oblique fat lines with QD line drawing, + * we should draw a filled polygon instead. */ + + for (i = 0; i < nsegments; i++) { + MoveTo((short) (macWin->xOff + segments[i].x1 + o), + (short) (macWin->yOff + segments[i].y1 + o)); + LineTo((short) (macWin->xOff + segments[i].x2 + o), + (short) (macWin->yOff + segments[i].y2 + o)); + } + } + TkMacOSXRestoreDrawingContext(&dc); +} + +/* + *---------------------------------------------------------------------- + * + * 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, tkMacOSXUseCGDrawing, &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); + } else { + PolyHandle polygon; + + polygon = OpenPoly(); + MoveTo((short) (macWin->xOff + points[0].x), + (short) (macWin->yOff + points[0].y)); + for (i = 1; i < npoints; i++) { + if (mode == CoordModeOrigin) { + LineTo((short) (macWin->xOff + points[i].x), + (short) (macWin->yOff + points[i].y)); + } else { + Line((short) points[i].x, (short) points[i].y); + } + } + ClosePoly(); + FillCPoly(polygon, dc.penPat); + KillPoly(polygon); + } + 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, tkMacOSXUseCGDrawing, &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); + } else { + Rect theRect; + int o = -lw/2; + + theRect.left = (short) (macWin->xOff + x + o); + theRect.top = (short) (macWin->yOff + y + o); + theRect.right = (short) (theRect.left + width + lw); + theRect.bottom = (short) (theRect.top + height + lw); + FrameRect(&theRect); + } + 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, tkMacOSXUseCGDrawing, &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); + } + } else { + Rect theRect; + int o = -lw/2; + + for (i = 0, rectPtr = rectArr; i < nRects;i++, rectPtr++) { + theRect.left = (short) (macWin->xOff + rectPtr->x + o); + theRect.top = (short) (macWin->yOff + rectPtr->y + o); + theRect.right = (short) (theRect.left + rectPtr->width + lw); + theRect.bottom = (short) (theRect.top + rectPtr->height + lw); + FrameRect(&theRect); + } + } + TkMacOSXRestoreDrawingContext(&dc); +} +#endif + +/* + *---------------------------------------------------------------------- + * + * XFillRectangles -- + * + * Fill multiple rectangular areas in the given drawable. + * + * Results: + * None. + * + * Side effects: + * Draws onto the specified drawable. + * + *---------------------------------------------------------------------- + */ + +void +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, tkMacOSXUseCGDrawing, &dc)) { + return; + } + 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); + } + } else { + Rect theRect; + + for (i = 0, rectPtr = rectangles; i < n_rectangles; i++, rectPtr++) { + theRect.left = (short) (macWin->xOff + rectPtr->x); + theRect.top = (short) (macWin->yOff + rectPtr->y); + theRect.right = (short) (theRect.left + rectPtr->width); + theRect.bottom = (short) (theRect.top + rectPtr->height); + FillCRect(&theRect, dc.penPat); + } + } + 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, tkMacOSXUseCGDrawing, &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); + TK_IF_MAC_OS_X_API_COND (4, CGContextStrokeEllipseInRect, + angle1 == 0 && angle2 == 23040, + CGContextStrokeEllipseInRect(dc.context, rect); + ) TK_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); + ) TK_ENDIF + } else { + Rect theRect; + short start, extent; + int o = -lw/2; + + theRect.left = (short) (macWin->xOff + x + o); + theRect.top = (short) (macWin->yOff + y + o); + theRect.right = (short) (theRect.left + width + lw); + theRect.bottom = (short) (theRect.top + height + lw); + start = (short) (90 - (angle1/64)); + extent = (short) (-(angle2/64)); + FrameArc(&theRect, start, extent); + } + 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, tkMacOSXUseCGDrawing, &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); + + TK_IF_MAC_OS_X_API_COND (4, CGContextStrokeEllipseInRect, + arcPtr->angle1 == 0 && arcPtr->angle2 == 23040, + CGContextStrokeEllipseInRect(dc.context, rect); + ) TK_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); + ) TK_ENDIF + } + } else { + Rect theRect; + short start, extent; + int o = -lw/2; + + for (i = 0, arcPtr = arcArr;i < nArcs;i++, arcPtr++) { + theRect.left = (short) (macWin->xOff + arcPtr->x + o); + theRect.top = (short) (macWin->yOff + arcPtr->y + o); + theRect.right = (short) (theRect.left + arcPtr->width + lw); + theRect.bottom = (short) (theRect.top + arcPtr->height + lw); + start = (short) (90 - (arcPtr->angle1/64)); + extent = (short) (-(arcPtr->angle2/64)); + FrameArc(&theRect, start, extent); + } + } + 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, tkMacOSXUseCGDrawing, &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); + + TK_IF_MAC_OS_X_API_COND (4, CGContextFillEllipseInRect, + angle1 == 0 && angle2 == 23040, + CGContextFillEllipseInRect(dc.context, rect); + ) TK_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); + ) TK_ENDIF + } else { + Rect theRect; + short start, extent; + int o = -lw/2; + PolyHandle polygon; + double sin1, cos1, sin2, cos2, angle; + double boxWidth, boxHeight; + double vertex[2], center1[2], center2[2]; + + theRect.left = (short) (macWin->xOff + x + o); + theRect.top = (short) (macWin->yOff + y + o); + theRect.right = (short) (theRect.left + width + lw); + theRect.bottom = (short) (theRect.top + height + lw); + start = (short) (90 - (angle1/64)); + extent = (short) (-(angle2/64)); + if (gc->arc_mode == ArcChord) { + boxWidth = theRect.right - theRect.left; + boxHeight = theRect.bottom - theRect.top; + angle = radians(-angle1/64.0); + sin1 = sin(angle); + cos1 = cos(angle); + angle -= radians(angle2/64.0); + sin2 = sin(angle); + cos2 = cos(angle); + vertex[0] = (theRect.left + theRect.right)/2.0; + vertex[1] = (theRect.top + theRect.bottom)/2.0; + center1[0] = vertex[0] + cos1*boxWidth/2.0; + center1[1] = vertex[1] + sin1*boxHeight/2.0; + center2[0] = vertex[0] + cos2*boxWidth/2.0; + center2[1] = vertex[1] + sin2*boxHeight/2.0; + + polygon = OpenPoly(); + MoveTo((short) ((theRect.left + theRect.right)/2), + (short) ((theRect.top + theRect.bottom)/2)); + LineTo((short) (center1[0] + .5), (short) (center1[1] + .5)); + LineTo((short) (center2[0] + .5), (short) (center2[1] + .5)); + ClosePoly(); + FillCArc(&theRect, start, extent, dc.penPat); + FillCPoly(polygon, dc.penPat); + KillPoly(polygon); + } else { + FillCArc(&theRect, start, extent, dc.penPat); + } + } + 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, tkMacOSXUseCGDrawing, &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); + TK_IF_MAC_OS_X_API_COND (4, CGContextFillEllipseInRect, + arcPtr->angle1 == 0 && arcPtr->angle2 == 23040, + CGContextFillEllipseInRect(dc.context, rect); + ) TK_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); + ) TK_ENDIF + } + } else { + Rect theRect; + short start, extent; + int o = -lw/2; + PolyHandle polygon; + double sin1, cos1, sin2, cos2, angle; + double boxWidth, boxHeight; + double vertex[2], center1[2], center2[2]; + + for (i = 0, arcPtr = arcArr;i<nArcs;i++, arcPtr++) { + theRect.left = (short) (macWin->xOff + arcPtr->x + o); + theRect.top = (short) (macWin->yOff + arcPtr->y + o); + theRect.right = (short) (theRect.left + arcPtr->width + lw); + theRect.bottom = (short) (theRect.top + arcPtr->height + lw); + start = (short) (90 - (arcPtr->angle1/64)); + extent = (short) (- (arcPtr->angle2/64)); + + if (gc->arc_mode == ArcChord) { + boxWidth = theRect.right - theRect.left; + boxHeight = theRect.bottom - theRect.top; + angle = radians(-arcPtr->angle1/64.0); + sin1 = sin(angle); + cos1 = cos(angle); + angle -= radians(arcPtr->angle2/64.0); + sin2 = sin(angle); + cos2 = cos(angle); + vertex[0] = (theRect.left + theRect.right)/2.0; + vertex[1] = (theRect.top + theRect.bottom)/2.0; + center1[0] = vertex[0] + cos1*boxWidth/2.0; + center1[1] = vertex[1] + sin1*boxHeight/2.0; + center2[0] = vertex[0] + cos2*boxWidth/2.0; + center2[1] = vertex[1] + sin2*boxHeight/2.0; + + polygon = OpenPoly(); + MoveTo((short) ((theRect.left + theRect.right)/2), + (short) ((theRect.top + theRect.bottom)/2)); + LineTo((short) (center1[0] + .5), (short) (center1[1] + .5)); + LineTo((short) (center2[0] + .5), (short) (center2[1] + .5)); + ClosePoly(); + FillCArc(&theRect, start, extent, dc.penPat); + FillCPoly(polygon, dc.penPat); + KillPoly(polygon); + } else { + FillCArc(&theRect, start, extent, dc.penPat); + } + } + } + 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. */ +{ + MacDrawable *destDraw = (MacDrawable *) Tk_WindowId(tkwin); + CGrafPtr destPort, savePort; + Boolean portChanged; + Rect scrollRect; + int result; + HIShapeRef dmgRgn; + + /* + * Due to the implementation below the behavior may be differnt + * than X in certain cases that should never occur in Tk. The + * scrollRect is the source rect extended by the offset (the union + * of the source rect and the offset rect). Everything + * in the extended scrollRect is scrolled. On X, it's possible + * to "skip" over an area if the offset makes the source and + * destination rects disjoint and non-aligned. + */ + + scrollRect.left = destDraw->xOff + x; + scrollRect.top = destDraw->yOff + y; + scrollRect.right = scrollRect.left + width; + scrollRect.bottom = scrollRect.top + height; + if (dx < 0) { + scrollRect.left += dx; + } else { + scrollRect.right += dx; + } + if (dy < 0) { + scrollRect.top += dy; + } else { + scrollRect.bottom += dy; + } + + destPort = TkMacOSXGetDrawablePort(Tk_WindowId(tkwin)); + TkMacOSXSetUpClippingRgn(Tk_WindowId(tkwin)); + TkMacOSXCheckTmpQdRgnEmpty(); + portChanged = QDSwapPort(destPort, &savePort); + ScrollRect(&scrollRect, dx, dy, tkMacOSXtmpQdRgn); + if (portChanged) { + QDSwapPort(savePort, NULL); + } + + /* + * Fortunately, the region returned by ScrollRect is semantically + * the same as what we need to return in this function. If the + * region is empty we return zero to denote that no damage was + * created. + */ + + dmgRgn = HIShapeCreateWithQDRgn(tkMacOSXtmpQdRgn); + SetEmptyRgn(tkMacOSXtmpQdRgn); + TkMacOSXSetWithNativeRegion(damageRgn, dmgRgn); + result = HIShapeIsEmpty(dmgRgn) ? 0 : 1; + CFRelease(dmgRgn); + + return result; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXSetUpGraphicsPort -- + * + * Set up the graphics port from the given GC. + * + * Results: + * None. + * + * Side effects: + * The current port is adjusted. + * + *---------------------------------------------------------------------- + */ + +void +TkMacOSXSetUpGraphicsPort( + GC gc, /* GC to apply to current port. */ + GWorldPtr destPort) +{ + CGrafPtr savePort; + Boolean portChanged; + + portChanged = QDSwapPort(destPort, &savePort); + PenNormal(); + if (gc) { + if (!penPat) { + if (!tmpPixPat) { + penPat = NewPixPat(); + } else { + penPat = tmpPixPat; + tmpPixPat = NULL; + } + } + TkMacOSXSetColorInPort(gc->foreground, 1, penPat, destPort); + PenPixPat(penPat); + if(gc->function == GXxor) { + PenMode(patXor); + } + if (gc->line_width > 1) { + PenSize(gc->line_width, gc->line_width); + } + if (gc->line_style != LineSolid) { + /* + * FIXME: Here the dash pattern should be set in the drawing + * environment. This is not possible with QuickDraw line drawing. + */ + } + } + if (portChanged) { + QDSwapPort(savePort, NULL); + } +} + +/* + *---------------------------------------------------------------------- + * + * 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; + TkMacOSXDrawingContext dc = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, + {SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX}, false}; + + if (tkPictureIsOpen) { + if (useCG) { + TkMacOSXDbgMsg("Ignored CG Drawing with QD Picture open"); + dontDraw = 1; + } + } else { + dc.clipRgn = TkMacOSXGetClipRgn(d); + } + if (!dontDraw) { + ClipToGC(d, gc, &dc.clipRgn); + dontDraw = dc.clipRgn ? HIShapeIsEmpty(dc.clipRgn) : 0; + } + if (dontDraw) { + if (dc.clipRgn) { + CFRelease(dc.clipRgn); + dc.clipRgn = NULL; + } + goto end; + } + if (useCG) { + dc.context = macDraw->context; + } + if (!dc.context || !(macDraw->flags & TK_IS_PIXMAP)) { + dc.port = TkMacOSXGetDrawablePort(d); + if (dc.port) { + GetPortBounds(dc.port, &dc.portBounds); + } + } + if (dc.context) { + if (!dc.port) { + CGRect r; + + TK_IF_MAC_OS_X_API (3, CGContextGetClipBoundingBox, + r = CGContextGetClipBoundingBox(dc.context); + ) TK_ELSE_MAC_OS_X (3, + r.origin = CGPointZero; + r.size = macDraw->size; + ) TK_ENDIF + SetRect(&dc.portBounds, r.origin.x + macDraw->xOff, + r.origin.y + macDraw->yOff, + r.origin.x + r.size.width + macDraw->xOff, + r.origin.y + r.size.height + macDraw->yOff); + } + CGContextSaveGState(dc.context); + dc.saveState = (void*)1; + dc.port = NULL; + } else if (dc.port) { + dc.portChanged = QDSwapPort(dc.port, &dc.savePort); + if (useCG && ChkErr(QDBeginCGContext, dc.port, &dc.context) == noErr) { + SyncCGContextOriginWithPort(dc.context, dc.port); + } else { + dc.context = NULL; + } + } else { + Tcl_Panic("TkMacOSXSetupDrawingContext(): " + "no port or context to draw into !"); + } + if (dc.context) { + CGContextConcatCTM(dc.context, CGAffineTransformMake(1.0, 0.0, 0.0, + -1.0, 0.0, dc.portBounds.bottom - dc.portBounds.top)); + 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.2); + CGContextEOFillPath(dc.context); + CGContextRestoreGState(dc.context); +#endif /* TK_MAC_DEBUG_DRAWING */ + 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->foreground, dc.context); + if (dc.port) { + CGContextSetPatternPhase(dc.context, CGSizeMake( + dc.portBounds.right - dc.portBounds.left, + dc.portBounds.bottom - dc.portBounds.top)); + } + 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); + double dashOffset = gc->dash_offset; + float 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]); + } + } + } else if (dc.port) { + PixPatHandle savePat = penPat; + + ChkErr(GetThemeDrawingState, &dc.saveState); + penPat = NULL; + TkMacOSXSetUpGraphicsPort(gc, dc.port); + dc.penPat = penPat; + penPat = savePat; + dc.saveClip = NewRgn(); + GetPortClipRegion(dc.port, dc.saveClip); + if (dc.clipRgn) { + ChkErr(HIShapeSetQDClip, dc.clipRgn, dc.port); + } else { + NoQDClip(dc.port); + } + if (!tkPictureIsOpen) { + ShowPen(); + } + } +end: + *dcPtr = dc; + return !dontDraw; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXRestoreDrawingContext -- + * + * Restore drawing context. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkMacOSXRestoreDrawingContext( + TkMacOSXDrawingContext *dcPtr) +{ + if (dcPtr->context) { + CGContextSynchronize(dcPtr->context); + if (dcPtr->saveState) { + CGContextRestoreGState(dcPtr->context); + } + if (dcPtr->port) { + ChkErr(QDEndCGContext, dcPtr->port, &(dcPtr->context)); + } + } else if (dcPtr->port) { + if (!tkPictureIsOpen) { + HidePen(); + } + PenNormal(); + if (dcPtr->saveClip) { + SetPortClipRegion(dcPtr->port, dcPtr->saveClip); + DisposeRgn(dcPtr->saveClip); + } + if (dcPtr->penPat) { + if (!tmpPixPat) { + tmpPixPat = dcPtr->penPat; + } else { + DisposePixPat(dcPtr->penPat); + } + } + if (dcPtr->saveState) { + ChkErr(SetThemeDrawingState, dcPtr->saveState, true); + } + } + if (dcPtr->clipRgn) { + CFRelease(dcPtr->clipRgn); + } + if (dcPtr->portChanged) { + QDSwapPort(dcPtr->savePort, NULL); + } +#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; + CGRect r; + + if (macDraw->winPtr && macDraw->flags & TK_CLIP_INVALID) { + TkMacOSXUpdateClipRgn(macDraw->winPtr); +#ifdef TK_MAC_DEBUG_DRAWING + TkMacOSXDbgMsg("%s visRgn ", macDraw->winPtr->pathName); + TkMacOSXDebugFlashRegion(drawable, macDraw->visRgn); +#endif /* TK_MAC_DEBUG_DRAWING */ + } + + if (macDraw->flags & TK_CLIPPED_DRAW) { + r = CGRectOffset(macDraw->drawRect, macDraw->xOff, macDraw->yOff); + } + if (macDraw->visRgn) { + if (macDraw->flags & TK_CLIPPED_DRAW) { + HIShapeRef rgn = HIShapeCreateWithRect(&r); + + clipRgn = HIShapeCreateIntersection(macDraw->visRgn, rgn); + CFRelease(rgn); + } else { + clipRgn = HIShapeCreateCopy(macDraw->visRgn); + } + } else if (macDraw->flags & TK_CLIPPED_DRAW) { + clipRgn = HIShapeCreateWithRect(&r); + } +#ifdef TK_MAC_DEBUG_DRAWING + TkMacOSXDbgMsg("%s clipRgn ", macDraw->winPtr->pathName); + TkMacOSXDebugFlashRegion(drawable, clipRgn); +#endif /* TK_MAC_DEBUG_DRAWING */ + + return clipRgn; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXSetUpClippingRgn -- + * + * Set up the clipping region so that drawing only occurs on the + * specified X subwindow. + * + * Results: + * None. + * + * Side effects: + * The clipping region in the current port is changed. + * + *---------------------------------------------------------------------- + */ + +void +TkMacOSXSetUpClippingRgn( + Drawable drawable) /* Drawable to update. */ +{ + CGrafPtr port = TkMacOSXGetDrawablePort(drawable); + + if (port) { + HIShapeRef clipRgn = TkMacOSXGetClipRgn(drawable); + + if (clipRgn) { + ChkErr(HIShapeSetQDClip, clipRgn, port); + CFRelease(clipRgn); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpClipDrawableToRect -- + * + * Clip all drawing into the drawable d to the given rectangle. + * If width and 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 (width < 0 && height < 0) { + macDraw->drawRect = CGRectNull; + macDraw->flags &= ~TK_CLIPPED_DRAW; + } else { + macDraw->drawRect = CGRectMake(x, y, width, height); + macDraw->flags |= TK_CLIPPED_DRAW; + } +} + +/* + *---------------------------------------------------------------------- + * + * 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; + + if (!tkPictureIsOpen) { + TkMacOSXOffsetRegion(gcClip, xOffset, yOffset); + } + gcClipRgn = TkMacOSXGetNativeRegion(gcClip); + if (clipRgn) { + *clipRgnPtr = HIShapeCreateIntersection(gcClipRgn, clipRgn); + CFRelease(clipRgn); + } else { + *clipRgnPtr = HIShapeCreateCopy(gcClipRgn); + } + CFRelease(gcClipRgn); + if (!tkPictureIsOpen) { + TkMacOSXOffsetRegion(gcClip, -xOffset, -yOffset); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * NoQDClip -- + * + * Helper function to setup a QD port to not clip anything. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +NoQDClip( + CGrafPtr port) +{ + static RgnHandle noClipRgn = NULL; + + if (!noClipRgn) { + noClipRgn = NewRgn(); + SetRectRgn(noClipRgn, SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX); + } + SetPortClipRegion(port, noClipRgn); +} + +/* + *---------------------------------------------------------------------- + * + * 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. + * + *---------------------------------------------------------------------- + */ + +BitMapPtr +TkMacOSXMakeStippleMap( + Drawable drawable, /* Window to apply stipple. */ + Drawable stipple) /* The stipple pattern. */ +{ + CGrafPtr stipplePort; + BitMapPtr bitmapPtr; + const BitMap *stippleBitmap; + Rect portRect; + int width, height, stippleHeight, stippleWidth, i, j; + Rect bounds; + + GetPortBounds(TkMacOSXGetDrawablePort(drawable), &portRect); + width = portRect.right - portRect.left; + height = portRect.bottom - portRect.top; + bitmapPtr = (BitMap *) ckalloc(sizeof(BitMap)); + bitmapPtr->bounds.top = bitmapPtr->bounds.left = 0; + bitmapPtr->bounds.right = (short) width; + bitmapPtr->bounds.bottom = (short) height; + bitmapPtr->rowBytes = (width / 8) + 1; + bitmapPtr->baseAddr = ckalloc(height * bitmapPtr->rowBytes); + + stipplePort = TkMacOSXGetDrawablePort(stipple); + stippleBitmap = GetPortBitMapForCopyBits(stipplePort); + GetPortBounds(stipplePort, &portRect); + stippleWidth = portRect.right - portRect.left; + stippleHeight = portRect.bottom - portRect.top; + + for (i = 0; i < height; i += stippleHeight) { + for (j = 0; j < width; j += stippleWidth) { + bounds.left = j; + bounds.top = i; + bounds.right = j + stippleWidth; + bounds.bottom = i + stippleHeight; + CopyBits(stippleBitmap, bitmapPtr, &portRect, &bounds, srcCopy, + NULL); + } + } + return bitmapPtr; +} + +/* + *---------------------------------------------------------------------- + * + * 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 request themeing, it draws with a 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); +} |