/*
 * 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.
 */

#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);
}
