OSDN Git Service

Please enter the commit message for your changes. Lines starting
[eos/base.git] / util / src / TclTk / tk8.6.12 / win / tkWinDraw.c
diff --git a/util/src/TclTk/tk8.6.12/win/tkWinDraw.c b/util/src/TclTk/tk8.6.12/win/tkWinDraw.c
new file mode 100644 (file)
index 0000000..4ed8ab2
--- /dev/null
@@ -0,0 +1,1520 @@
+/*
+ * tkWinDraw.c --
+ *
+ *     This file contains the Xlib emulation functions pertaining to actually
+ *     drawing objects on a window.
+ *
+ * Copyright (c) 1995 Sun Microsystems, Inc.
+ * Copyright (c) 1994 Software Research Associates, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution of
+ * this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "tkWinInt.h"
+
+/*
+ * These macros convert between X's bizarre angle units to radians.
+ */
+
+#define XAngleToRadians(a) ((double)(a) / 64 * PI / 180);
+
+/*
+ * Translation table between X gc functions and Win32 raster op modes.
+ */
+
+const int tkpWinRopModes[] = {
+    R2_BLACK,                  /* GXclear */
+    R2_MASKPEN,                        /* GXand */
+    R2_MASKPENNOT,             /* GXandReverse */
+    R2_COPYPEN,                        /* GXcopy */
+    R2_MASKNOTPEN,             /* GXandInverted */
+    R2_NOT,                    /* GXnoop */
+    R2_XORPEN,                 /* GXxor */
+    R2_MERGEPEN,               /* GXor */
+    R2_NOTMERGEPEN,            /* GXnor */
+    R2_NOTXORPEN,              /* GXequiv */
+    R2_NOT,                    /* GXinvert */
+    R2_MERGEPENNOT,            /* GXorReverse */
+    R2_NOTCOPYPEN,             /* GXcopyInverted */
+    R2_MERGENOTPEN,            /* GXorInverted */
+    R2_NOTMASKPEN,             /* GXnand */
+    R2_WHITE                   /* GXset */
+};
+
+/*
+ * Translation table between X gc functions and Win32 BitBlt op modes. Some of
+ * the operations defined in X don't have names, so we have to construct new
+ * opcodes for those functions. This is arcane and probably not all that
+ * useful, but at least it's accurate.
+ */
+
+#define NOTSRCAND      (DWORD)0x00220326 /* dest = (NOT source) AND dest */
+#define NOTSRCINVERT   (DWORD)0x00990066 /* dest = (NOT source) XOR dest */
+#define SRCORREVERSE   (DWORD)0x00DD0228 /* dest = source OR (NOT dest) */
+#define SRCNAND                (DWORD)0x007700E6 /* dest = NOT (source AND dest) */
+
+const int tkpWinBltModes[] = {
+    BLACKNESS,                 /* GXclear */
+    SRCAND,                    /* GXand */
+    SRCERASE,                  /* GXandReverse */
+    SRCCOPY,                   /* GXcopy */
+    NOTSRCAND,                 /* GXandInverted */
+    PATCOPY,                   /* GXnoop */
+    SRCINVERT,                 /* GXxor */
+    SRCPAINT,                  /* GXor */
+    NOTSRCERASE,               /* GXnor */
+    NOTSRCINVERT,              /* GXequiv */
+    DSTINVERT,                 /* GXinvert */
+    SRCORREVERSE,              /* GXorReverse */
+    NOTSRCCOPY,                        /* GXcopyInverted */
+    MERGEPAINT,                        /* GXorInverted */
+    SRCNAND,                   /* GXnand */
+    WHITENESS                  /* GXset */
+};
+
+/*
+ * The following raster op uses the source bitmap as a mask for the pattern.
+ * This is used to draw in a foreground color but leave the background color
+ * transparent.
+ */
+
+#define MASKPAT                0x00E20746 /* dest = (src & pat) | (!src & dst) */
+
+/*
+ * The following two raster ops are used to copy the foreground and background
+ * bits of a source pattern as defined by a stipple used as the pattern.
+ */
+
+#define COPYFG         0x00CA0749 /* dest = (pat & src) | (!pat & dst) */
+#define COPYBG         0x00AC0744 /* dest = (!pat & src) | (pat & dst) */
+
+/*
+ * Macros used later in the file.
+ */
+#ifndef MIN
+#   define MIN(a,b)    ((a>b) ? b : a)
+#   define MAX(a,b)    ((a<b) ? b : a)
+#endif
+
+/*
+ * The followng typedef is used to pass Windows GDI drawing functions.
+ */
+
+typedef BOOL (CALLBACK *WinDrawFunc)(HDC dc, const POINT *points, int npoints);
+
+typedef struct {
+    POINT *winPoints;          /* Array of points that is reused. */
+    int nWinPoints;            /* Current size of point array. */
+} ThreadSpecificData;
+static Tcl_ThreadDataKey dataKey;
+
+/*
+ * Forward declarations for functions defined in this file:
+ */
+
+static POINT *         ConvertPoints(XPoint *points, int npoints, int mode,
+                           RECT *bbox);
+static int             DrawOrFillArc(Display *display, Drawable d, GC gc,
+                           int x, int y, unsigned int width,
+                           unsigned int height, int start, int extent,
+                           int fill);
+static void            RenderObject(HDC dc, GC gc, XPoint* points,
+                           int npoints, int mode, HPEN pen, WinDrawFunc func);
+static HPEN            SetUpGraphicsPort(GC gc);
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinGetDrawableDC --
+ *
+ *     Retrieve the DC from a drawable.
+ *
+ * Results:
+ *     Returns the window DC for windows. Returns a new memory DC for
+ *     pixmaps.
+ *
+ * Side effects:
+ *     Sets up the palette for the device context, and saves the old device
+ *     context state in the passed in TkWinDCState structure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+HDC
+TkWinGetDrawableDC(
+    Display *display,
+    Drawable d,
+    TkWinDCState *state)
+{
+    HDC dc;
+    TkWinDrawable *twdPtr = (TkWinDrawable *)d;
+    Colormap cmap;
+
+    if (twdPtr->type == TWD_WINDOW) {
+       TkWindow *winPtr = twdPtr->window.winPtr;
+
+       dc = GetDC(twdPtr->window.handle);
+       if (winPtr == NULL) {
+           cmap = DefaultColormap(display, DefaultScreen(display));
+       } else {
+           cmap = winPtr->atts.colormap;
+       }
+    } else if (twdPtr->type == TWD_WINDC) {
+       dc = twdPtr->winDC.hdc;
+       cmap = DefaultColormap(display, DefaultScreen(display));
+    } else {
+       dc = CreateCompatibleDC(NULL);
+       SelectObject(dc, twdPtr->bitmap.handle);
+       cmap = twdPtr->bitmap.colormap;
+    }
+    state->palette = TkWinSelectPalette(dc, cmap);
+    state->bkmode  = GetBkMode(dc);
+    return dc;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinReleaseDrawableDC --
+ *
+ *     Frees the resources associated with a drawable's DC.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Restores the old bitmap handle to the memory DC for pixmaps.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkWinReleaseDrawableDC(
+    Drawable d,
+    HDC dc,
+    TkWinDCState *state)
+{
+    TkWinDrawable *twdPtr = (TkWinDrawable *)d;
+
+    SetBkMode(dc, state->bkmode);
+    SelectPalette(dc, state->palette, TRUE);
+    RealizePalette(dc);
+    if (twdPtr->type == TWD_WINDOW) {
+       ReleaseDC(TkWinGetHWND(d), dc);
+    } else if (twdPtr->type == TWD_BITMAP) {
+       DeleteDC(dc);
+    }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConvertPoints --
+ *
+ *     Convert an array of X points to an array of Win32 points.
+ *
+ * Results:
+ *     Returns the converted array of POINTs.
+ *
+ * Side effects:
+ *     Allocates a block of memory in thread local storage that should not be
+ *     freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static POINT *
+ConvertPoints(
+    XPoint *points,
+    int npoints,
+    int mode,                  /* CoordModeOrigin or CoordModePrevious. */
+    RECT *bbox)                        /* Bounding box of points. */
+{
+    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
+           Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+    int i;
+
+    /*
+     * To avoid paying the cost of a malloc on every drawing routine, we reuse
+     * the last array if it is large enough.
+     */
+
+    if (npoints > tsdPtr->nWinPoints) {
+       if (tsdPtr->winPoints != NULL) {
+           ckfree(tsdPtr->winPoints);
+       }
+       tsdPtr->winPoints = ckalloc(sizeof(POINT) * npoints);
+       if (tsdPtr->winPoints == NULL) {
+           tsdPtr->nWinPoints = -1;
+           return NULL;
+       }
+       tsdPtr->nWinPoints = npoints;
+    }
+
+    bbox->left = bbox->right = points[0].x;
+    bbox->top = bbox->bottom = points[0].y;
+
+    if (mode == CoordModeOrigin) {
+       for (i = 0; i < npoints; i++) {
+           tsdPtr->winPoints[i].x = points[i].x;
+           tsdPtr->winPoints[i].y = points[i].y;
+           bbox->left = MIN(bbox->left, tsdPtr->winPoints[i].x);
+           bbox->right = MAX(bbox->right, tsdPtr->winPoints[i].x);
+           bbox->top = MIN(bbox->top, tsdPtr->winPoints[i].y);
+           bbox->bottom = MAX(bbox->bottom, tsdPtr->winPoints[i].y);
+       }
+    } else {
+       tsdPtr->winPoints[0].x = points[0].x;
+       tsdPtr->winPoints[0].y = points[0].y;
+       for (i = 1; i < npoints; i++) {
+           tsdPtr->winPoints[i].x = tsdPtr->winPoints[i-1].x + points[i].x;
+           tsdPtr->winPoints[i].y = tsdPtr->winPoints[i-1].y + points[i].y;
+           bbox->left = MIN(bbox->left, tsdPtr->winPoints[i].x);
+           bbox->right = MAX(bbox->right, tsdPtr->winPoints[i].x);
+           bbox->top = MIN(bbox->top, tsdPtr->winPoints[i].y);
+           bbox->bottom = MAX(bbox->bottom, tsdPtr->winPoints[i].y);
+       }
+    }
+    return tsdPtr->winPoints;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+XCopyArea(
+    Display *display,
+    Drawable src,
+    Drawable dest,
+    GC gc,
+    int src_x, int src_y,
+    unsigned int width, unsigned int height,
+    int dest_x, int dest_y)
+{
+    HDC srcDC, destDC;
+    TkWinDCState srcState, destState;
+    TkpClipMask *clipPtr = (TkpClipMask*)gc->clip_mask;
+
+    srcDC = TkWinGetDrawableDC(display, src, &srcState);
+
+    if (src != dest) {
+       destDC = TkWinGetDrawableDC(display, dest, &destState);
+    } else {
+       destDC = srcDC;
+    }
+
+    if (clipPtr && clipPtr->type == TKP_CLIP_REGION) {
+       SelectClipRgn(destDC, (HRGN) clipPtr->value.region);
+       OffsetClipRgn(destDC, gc->clip_x_origin, gc->clip_y_origin);
+    }
+
+    BitBlt(destDC, dest_x, dest_y, (int) width, (int) height, srcDC,
+           src_x, src_y, (DWORD) tkpWinBltModes[gc->function]);
+
+    SelectClipRgn(destDC, NULL);
+
+    if (src != dest) {
+       TkWinReleaseDrawableDC(dest, destDC, &destState);
+    }
+    TkWinReleaseDrawableDC(src, srcDC, &srcState);
+    return Success;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+XCopyPlane(
+    Display *display,
+    Drawable src,
+    Drawable dest,
+    GC gc,
+    int src_x, int src_y,
+    unsigned int width, unsigned int height,
+    int dest_x, int dest_y,
+    unsigned long plane)
+{
+    HDC srcDC, destDC;
+    TkWinDCState srcState, destState;
+    HBRUSH bgBrush, fgBrush, oldBrush;
+    TkpClipMask *clipPtr = (TkpClipMask*)gc->clip_mask;
+
+    display->request++;
+
+    if (plane != 1) {
+       Tcl_Panic("Unexpected plane specified for XCopyPlane");
+    }
+
+    srcDC = TkWinGetDrawableDC(display, src, &srcState);
+
+    if (src != dest) {
+       destDC = TkWinGetDrawableDC(display, dest, &destState);
+    } else {
+       destDC = srcDC;
+    }
+
+    if (clipPtr == NULL || clipPtr->type == TKP_CLIP_REGION) {
+       /*
+        * Case 1: opaque bitmaps. Windows handles the conversion from one bit
+        * to multiple bits by setting 0 to the foreground color, and 1 to the
+        * background color (seems backwards, but there you are).
+        */
+
+       if (clipPtr && clipPtr->type == TKP_CLIP_REGION) {
+           SelectClipRgn(destDC, (HRGN) clipPtr->value.region);
+           OffsetClipRgn(destDC, gc->clip_x_origin, gc->clip_y_origin);
+       }
+
+       SetBkMode(destDC, OPAQUE);
+       SetBkColor(destDC, gc->foreground);
+       SetTextColor(destDC, gc->background);
+       BitBlt(destDC, dest_x, dest_y, (int) width, (int) height, srcDC,
+               src_x, src_y, SRCCOPY);
+
+       SelectClipRgn(destDC, NULL);
+    } else if (clipPtr->type == TKP_CLIP_PIXMAP) {
+       if (clipPtr->value.pixmap == src) {
+
+           /*
+            * Case 2: transparent bitmaps are handled by setting the
+            * destination to the foreground color whenever the source pixel
+            * is set.
+            */
+
+           fgBrush = CreateSolidBrush(gc->foreground);
+           oldBrush = SelectObject(destDC, fgBrush);
+           SetBkColor(destDC, RGB(255,255,255));
+           SetTextColor(destDC, RGB(0,0,0));
+           BitBlt(destDC, dest_x, dest_y, (int) width, (int) height, srcDC,
+                   src_x, src_y, MASKPAT);
+           SelectObject(destDC, oldBrush);
+           DeleteObject(fgBrush);
+       } else {
+
+           /*
+            * Case 3: two arbitrary bitmaps. Copy the source rectangle into a
+            * color pixmap. Use the result as a brush when copying the clip
+            * mask into the destination.
+            */
+
+           HDC memDC, maskDC;
+           HBITMAP bitmap;
+           TkWinDCState maskState;
+
+           fgBrush = CreateSolidBrush(gc->foreground);
+           bgBrush = CreateSolidBrush(gc->background);
+           maskDC = TkWinGetDrawableDC(display, clipPtr->value.pixmap,
+                   &maskState);
+           memDC = CreateCompatibleDC(destDC);
+           bitmap = CreateBitmap((int) width, (int) height, 1, 1, NULL);
+           SelectObject(memDC, bitmap);
+
+           /*
+            * Set foreground bits. We create a new bitmap containing (source
+            * AND mask), then use it to set the foreground color into the
+            * destination.
+            */
+
+           BitBlt(memDC, 0, 0, (int) width, (int) height, srcDC, src_x, src_y,
+                   SRCCOPY);
+           BitBlt(memDC, 0, 0, (int) width, (int) height, maskDC,
+                   dest_x - gc->clip_x_origin, dest_y - gc->clip_y_origin,
+                   SRCAND);
+           oldBrush = SelectObject(destDC, fgBrush);
+           BitBlt(destDC, dest_x, dest_y, (int) width, (int) height, memDC,
+                   0, 0, MASKPAT);
+
+           /*
+            * Set background bits. Same as foreground, except we use ((NOT
+            * source) AND mask) and the background brush.
+            */
+
+           BitBlt(memDC, 0, 0, (int) width, (int) height, srcDC, src_x, src_y,
+                   NOTSRCCOPY);
+           BitBlt(memDC, 0, 0, (int) width, (int) height, maskDC,
+                   dest_x - gc->clip_x_origin, dest_y - gc->clip_y_origin,
+                   SRCAND);
+           SelectObject(destDC, bgBrush);
+           BitBlt(destDC, dest_x, dest_y, (int) width, (int) height, memDC,
+                   0, 0, MASKPAT);
+
+           TkWinReleaseDrawableDC(clipPtr->value.pixmap, maskDC, &maskState);
+           SelectObject(destDC, oldBrush);
+           DeleteDC(memDC);
+           DeleteObject(bitmap);
+           DeleteObject(fgBrush);
+           DeleteObject(bgBrush);
+       }
+    }
+    if (src != dest) {
+       TkWinReleaseDrawableDC(dest, destDC, &destState);
+    }
+    TkWinReleaseDrawableDC(src, srcDC, &srcState);
+    return Success;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkPutImage, XPutImage --
+ *
+ *     Copies a subimage from an in-memory image to a rectangle of of the
+ *     specified drawable.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Draws the image on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkPutImage(
+    unsigned long *colors,     /* Array of pixel values used by this image.
+                                * May be NULL. */
+    int ncolors,               /* Number of colors used, or 0. */
+    Display *display,
+    Drawable d,                        /* Destination drawable. */
+    GC gc,
+    XImage *image,             /* Source image. */
+    int src_x, int src_y,      /* Offset of subimage. */
+    int dest_x, int dest_y,    /* Position of subimage origin in drawable. */
+    unsigned int width, unsigned int height)
+                               /* Dimensions of subimage. */
+{
+    HDC dc, dcMem;
+    TkWinDCState state;
+    BITMAPINFO *infoPtr;
+    HBITMAP bitmap;
+    char *data;
+
+    display->request++;
+
+    dc = TkWinGetDrawableDC(display, d, &state);
+    SetROP2(dc, tkpWinRopModes[gc->function]);
+    dcMem = CreateCompatibleDC(dc);
+
+    if (image->bits_per_pixel == 1) {
+       /*
+        * If the image isn't in the right format, we have to copy it into a
+        * new buffer in MSBFirst and word-aligned format.
+        */
+
+       if ((image->bitmap_bit_order != MSBFirst)
+               || (image->bitmap_pad != sizeof(WORD))) {
+           data = TkAlignImageData(image, sizeof(WORD), MSBFirst);
+           bitmap = CreateBitmap(image->width, image->height, 1, 1, data);
+           ckfree(data);
+       } else {
+           bitmap = CreateBitmap(image->width, image->height, 1, 1,
+                   image->data);
+       }
+       SetTextColor(dc, gc->foreground);
+       SetBkColor(dc, gc->background);
+    } else {
+       int i, usePalette;
+
+       /*
+        * Do not use a palette for TrueColor images.
+        */
+
+       usePalette = (image->bits_per_pixel < 16);
+
+       if (usePalette) {
+           infoPtr = ckalloc(sizeof(BITMAPINFOHEADER)
+                   + sizeof(RGBQUAD)*ncolors);
+       } else {
+           infoPtr = ckalloc(sizeof(BITMAPINFOHEADER));
+       }
+
+       infoPtr->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+       infoPtr->bmiHeader.biWidth = image->width;
+       infoPtr->bmiHeader.biHeight = -image->height; /* Top-down order */
+       infoPtr->bmiHeader.biPlanes = 1;
+       infoPtr->bmiHeader.biBitCount = image->bits_per_pixel;
+       infoPtr->bmiHeader.biCompression = BI_RGB;
+       infoPtr->bmiHeader.biSizeImage = 0;
+       infoPtr->bmiHeader.biXPelsPerMeter = 0;
+       infoPtr->bmiHeader.biYPelsPerMeter = 0;
+       infoPtr->bmiHeader.biClrImportant = 0;
+
+       if (usePalette) {
+           infoPtr->bmiHeader.biClrUsed = ncolors;
+           for (i = 0; i < ncolors; i++) {
+               infoPtr->bmiColors[i].rgbBlue = GetBValue(colors[i]);
+               infoPtr->bmiColors[i].rgbGreen = GetGValue(colors[i]);
+               infoPtr->bmiColors[i].rgbRed = GetRValue(colors[i]);
+               infoPtr->bmiColors[i].rgbReserved = 0;
+           }
+       } else {
+           infoPtr->bmiHeader.biClrUsed = 0;
+       }
+       bitmap = CreateDIBitmap(dc, &infoPtr->bmiHeader, CBM_INIT,
+               image->data, infoPtr, DIB_RGB_COLORS);
+       ckfree(infoPtr);
+    }
+    if (!bitmap) {
+       Tcl_Panic("Fail to allocate bitmap");
+    }
+    bitmap = SelectObject(dcMem, bitmap);
+    BitBlt(dc, dest_x, dest_y, (int) width, (int) height, dcMem, src_x, src_y,
+           SRCCOPY);
+    DeleteObject(SelectObject(dcMem, bitmap));
+    DeleteDC(dcMem);
+    TkWinReleaseDrawableDC(d, dc, &state);
+    return Success;
+}
+
+int
+XPutImage(
+    Display *display,
+    Drawable d,                        /* Destination drawable. */
+    GC gc,
+    XImage *image,             /* Source image. */
+    int src_x, int src_y,      /* Offset of subimage. */
+    int dest_x, int dest_y,    /* Position of subimage origin in drawable. */
+    unsigned int width, unsigned int height)
+                               /* Dimensions of subimage. */
+{
+    return TkPutImage(NULL, 0, display, d, gc, image,
+               src_x, src_y, dest_x, dest_y, width, height);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * XFillRectangles --
+ *
+ *     Fill multiple rectangular areas in the given drawable.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Draws onto the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+XFillRectangles(
+    Display *display,
+    Drawable d,
+    GC gc,
+    XRectangle *rectangles,
+    int nrectangles)
+{
+    HDC dc;
+    RECT rect;
+    TkWinDCState state;
+    HBRUSH brush, oldBrush;
+
+    if (d == None) {
+       return BadDrawable;
+    }
+
+    dc = TkWinGetDrawableDC(display, d, &state);
+    SetROP2(dc, tkpWinRopModes[gc->function]);
+    brush = CreateSolidBrush(gc->foreground);
+
+    if ((gc->fill_style == FillStippled
+           || gc->fill_style == FillOpaqueStippled)
+           && gc->stipple != None) {
+       TkWinDrawable *twdPtr = (TkWinDrawable *)gc->stipple;
+       HBRUSH stipple;
+       HBITMAP oldBitmap, bitmap;
+       HDC dcMem;
+       HBRUSH bgBrush = CreateSolidBrush(gc->background);
+
+       if (twdPtr->type != TWD_BITMAP) {
+           Tcl_Panic("unexpected drawable type in stipple");
+       }
+
+       /*
+        * Select stipple pattern into destination dc.
+        */
+
+       stipple = CreatePatternBrush(twdPtr->bitmap.handle);
+       SetBrushOrgEx(dc, gc->ts_x_origin, gc->ts_y_origin, NULL);
+       oldBrush = SelectObject(dc, stipple);
+       dcMem = CreateCompatibleDC(dc);
+
+       /*
+        * For each rectangle, create a drawing surface which is the size of
+        * the rectangle and fill it with the background color. Then merge the
+        * result with the stipple pattern.
+        */
+
+       while (nrectangles-- > 0) {
+           bitmap = CreateCompatibleBitmap(dc, rectangles[0].width,
+                   rectangles[0].height);
+           oldBitmap = SelectObject(dcMem, bitmap);
+           rect.left = 0;
+           rect.top = 0;
+           rect.right = rectangles[0].width;
+           rect.bottom = rectangles[0].height;
+           FillRect(dcMem, &rect, brush);
+           BitBlt(dc, rectangles[0].x, rectangles[0].y, rectangles[0].width,
+                   rectangles[0].height, dcMem, 0, 0, COPYFG);
+           if (gc->fill_style == FillOpaqueStippled) {
+               FillRect(dcMem, &rect, bgBrush);
+               BitBlt(dc, rectangles[0].x, rectangles[0].y,
+                       rectangles[0].width, rectangles[0].height, dcMem,
+                       0, 0, COPYBG);
+           }
+           SelectObject(dcMem, oldBitmap);
+           DeleteObject(bitmap);
+           ++rectangles;
+       }
+
+       DeleteDC(dcMem);
+       SelectObject(dc, oldBrush);
+       DeleteObject(stipple);
+       DeleteObject(bgBrush);
+    } else {
+       if (gc->function == GXcopy) {
+           while (nrectangles-- > 0) {
+               rect.left = rectangles[0].x;
+               rect.right = rect.left + rectangles[0].width;
+               rect.top = rectangles[0].y;
+               rect.bottom = rect.top + rectangles[0].height;
+               FillRect(dc, &rect, brush);
+               ++rectangles;
+           }
+       } else {
+           HPEN newPen = CreatePen(PS_NULL, 0, gc->foreground);
+           HPEN oldPen = SelectObject(dc, newPen);
+           oldBrush = SelectObject(dc, brush);
+
+           while (nrectangles-- > 0) {
+               Rectangle(dc, rectangles[0].x, rectangles[0].y,
+                   rectangles[0].x + rectangles[0].width + 1,
+                   rectangles[0].y + rectangles[0].height + 1);
+               ++rectangles;
+           }
+
+           SelectObject(dc, oldBrush);
+           SelectObject(dc, oldPen);
+           DeleteObject(newPen);
+       }
+    }
+    DeleteObject(brush);
+    TkWinReleaseDrawableDC(d, dc, &state);
+    return Success;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * MakeAndStrokePath --
+ *
+ *     This function draws a shape using a list of points, a stipple pattern,
+ *     and the specified drawing function. It does it through creation of a
+ *     so-called 'path' (see GDI documentation on MSDN).
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MakeAndStrokePath(
+    HDC dc,
+    POINT *winPoints,
+    int npoints,
+    WinDrawFunc func)        /* Name of the Windows GDI drawing function:
+                                this is either Polyline or Polygon. */
+{
+    BeginPath(dc);
+    func(dc, winPoints, npoints);
+    /*
+     * In the case of closed polylines, the first and last points
+     * are the same. We want miter or bevel join be rendered also
+     * at this point, this needs telling the Windows GDI that the
+     * path is closed.
+     */
+    if (func == Polyline) {
+        if ((winPoints[0].x == winPoints[npoints-1].x) &&
+                (winPoints[0].y == winPoints[npoints-1].y)) {
+            CloseFigure(dc);
+        }
+        EndPath(dc);
+        StrokePath(dc);
+    } else {
+        EndPath(dc);
+        StrokeAndFillPath(dc);
+    }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * RenderObject --
+ *
+ *     This function draws a shape using a list of points, a stipple pattern,
+ *     and the specified drawing function.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RenderObject(
+    HDC dc,
+    GC gc,
+    XPoint *points,
+    int npoints,
+    int mode,
+    HPEN pen,
+    WinDrawFunc func)
+{
+    RECT rect = {0,0,0,0};
+    HPEN oldPen;
+    HBRUSH oldBrush;
+    POINT *winPoints = ConvertPoints(points, npoints, mode, &rect);
+
+    if ((gc->fill_style == FillStippled
+           || gc->fill_style == FillOpaqueStippled)
+           && gc->stipple != None) {
+
+       TkWinDrawable *twdPtr = (TkWinDrawable *)gc->stipple;
+       HDC dcMem;
+       LONG width, height;
+       HBITMAP oldBitmap;
+       int i;
+       HBRUSH oldMemBrush;
+
+       if (twdPtr->type != TWD_BITMAP) {
+           Tcl_Panic("unexpected drawable type in stipple");
+       }
+
+       /*
+        * Grow the bounding box enough to account for line width.
+        */
+
+       rect.left -= gc->line_width;
+       rect.top -= gc->line_width;
+       rect.right += gc->line_width;
+       rect.bottom += gc->line_width;
+
+       width = rect.right - rect.left;
+       height = rect.bottom - rect.top;
+
+       /*
+        * Select stipple pattern into destination dc.
+        */
+
+       SetBrushOrgEx(dc, gc->ts_x_origin, gc->ts_y_origin, NULL);
+       oldBrush = SelectObject(dc, CreatePatternBrush(twdPtr->bitmap.handle));
+
+       /*
+        * Create temporary drawing surface containing a copy of the
+        * destination equal in size to the bounding box of the object.
+        */
+
+       dcMem = CreateCompatibleDC(dc);
+       oldBitmap = SelectObject(dcMem, CreateCompatibleBitmap(dc, width,
+               height));
+       oldPen = SelectObject(dcMem, pen);
+       BitBlt(dcMem, 0, 0, width, height, dc, rect.left, rect.top, SRCCOPY);
+
+       /*
+        * Translate the object for rendering in the temporary drawing
+        * surface.
+        */
+
+       for (i = 0; i < npoints; i++) {
+           winPoints[i].x -= rect.left;
+           winPoints[i].y -= rect.top;
+       }
+
+       /*
+        * Draw the object in the foreground color and copy it to the
+        * destination wherever the pattern is set.
+        */
+
+       SetPolyFillMode(dcMem, (gc->fill_rule == EvenOddRule) ? ALTERNATE
+               : WINDING);
+       oldMemBrush = SelectObject(dcMem, CreateSolidBrush(gc->foreground));
+        MakeAndStrokePath(dcMem, winPoints, npoints, func);
+       BitBlt(dc, rect.left, rect.top, width, height, dcMem, 0, 0, COPYFG);
+
+       /*
+        * If we are rendering an opaque stipple, then draw the polygon in the
+        * background color and copy it to the destination wherever the
+        * pattern is clear.
+        */
+
+       if (gc->fill_style == FillOpaqueStippled) {
+           DeleteObject(SelectObject(dcMem,
+                   CreateSolidBrush(gc->background)));
+            MakeAndStrokePath(dcMem, winPoints, npoints, func);
+           BitBlt(dc, rect.left, rect.top, width, height, dcMem, 0, 0,
+                   COPYBG);
+       }
+
+       SelectObject(dcMem, oldPen);
+       DeleteObject(SelectObject(dcMem, oldMemBrush));
+       DeleteObject(SelectObject(dcMem, oldBitmap));
+       DeleteDC(dcMem);
+    } else {
+       oldPen = SelectObject(dc, pen);
+       oldBrush = SelectObject(dc, CreateSolidBrush(gc->foreground));
+       SetROP2(dc, tkpWinRopModes[gc->function]);
+
+       SetPolyFillMode(dc, (gc->fill_rule == EvenOddRule) ? ALTERNATE
+               : WINDING);
+        MakeAndStrokePath(dc, winPoints, npoints, func);
+       SelectObject(dc, oldPen);
+    }
+    DeleteObject(SelectObject(dc, oldBrush));
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * XDrawLines --
+ *
+ *     Draw connected lines.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Renders a series of connected lines.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+XDrawLines(
+    Display *display,
+    Drawable d,
+    GC gc,
+    XPoint *points,
+    int npoints,
+    int mode)
+{
+    HPEN pen;
+    TkWinDCState state;
+    HDC dc;
+
+    if (d == None) {
+       return BadDrawable;
+    }
+
+    dc = TkWinGetDrawableDC(display, d, &state);
+
+    pen = SetUpGraphicsPort(gc);
+    SetBkMode(dc, TRANSPARENT);
+    RenderObject(dc, gc, points, npoints, mode, pen, Polyline);
+    DeleteObject(pen);
+
+    TkWinReleaseDrawableDC(d, dc, &state);
+    return Success;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * XFillPolygon --
+ *
+ *     Draws a filled polygon.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Draws a filled polygon on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+XFillPolygon(
+    Display *display,
+    Drawable d,
+    GC gc,
+    XPoint *points,
+    int npoints,
+    int shape,
+    int mode)
+{
+    HPEN pen;
+    TkWinDCState state;
+    HDC dc;
+
+    if (d == None) {
+       return BadDrawable;
+    }
+
+    dc = TkWinGetDrawableDC(display, d, &state);
+
+    pen = GetStockObject(NULL_PEN);
+    RenderObject(dc, gc, points, npoints, mode, pen, Polygon);
+
+    TkWinReleaseDrawableDC(d, dc, &state);
+    return Success;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * XDrawRectangle, XDrawRectangles --
+ *
+ *     Draws a rectangle.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Draws a rectangle on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+XDrawRectangle(
+    Display *display,
+    Drawable d,
+    GC gc,
+    int x, int y,
+    unsigned int width, unsigned int height)
+{
+    HPEN pen, oldPen;
+    TkWinDCState state;
+    HBRUSH oldBrush;
+    HDC dc;
+
+    if (d == None) {
+       return BadDrawable;
+    }
+
+    dc = TkWinGetDrawableDC(display, d, &state);
+
+    pen = SetUpGraphicsPort(gc);
+    SetBkMode(dc, TRANSPARENT);
+    oldPen = SelectObject(dc, pen);
+    oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH));
+    SetROP2(dc, tkpWinRopModes[gc->function]);
+
+    Rectangle(dc, x, y, (int) x+width+1, (int) y+height+1);
+
+    DeleteObject(SelectObject(dc, oldPen));
+    SelectObject(dc, oldBrush);
+    TkWinReleaseDrawableDC(d, dc, &state);
+    return Success;
+}
+
+int
+XDrawRectangles(
+    Display *display,
+    Drawable d,
+    GC gc,
+    XRectangle rects[],
+    int nrects)
+{
+    int ret = Success;
+
+    while (nrects-- > 0) {
+       ret = XDrawRectangle(display, d, gc, rects[0].x, rects[0].y,
+                   rects[0].width, rects[0].height);
+       if (ret != Success) {
+           break;
+       }
+       ++rects;
+    }
+    return ret;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * XDrawArc, XDrawArcs --
+ *
+ *     Draw an arc.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Draws an arc on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+XDrawArc(
+    Display *display,
+    Drawable d,
+    GC gc,
+    int x, int y,
+    unsigned int width, unsigned int height,
+    int start, int extent)
+{
+    display->request++;
+
+    return DrawOrFillArc(display, d, gc, x, y, width, height, start, extent, 0);
+}
+
+int
+XDrawArcs(
+    Display *display,
+    Drawable d,
+    GC gc,
+    XArc *arcs,
+    int narcs)
+{
+    int ret = Success;
+
+    display->request++;
+
+    while (narcs-- > 0) {
+       ret = DrawOrFillArc(display, d, gc, arcs[0].x, arcs[0].y,
+                   arcs[0].width, arcs[0].height,
+                   arcs[0].angle1, arcs[0].angle2, 0);
+       if (ret != Success) {
+           break;
+       }
+       ++arcs;
+    }
+    return ret;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * XFillArc, XFillArcs --
+ *
+ *     Draw a filled arc.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Draws a filled arc on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+XFillArc(
+    Display *display,
+    Drawable d,
+    GC gc,
+    int x, int y,
+    unsigned int width, unsigned int height,
+    int start, int extent)
+{
+    display->request++;
+
+    return DrawOrFillArc(display, d, gc, x, y, width, height, start, extent, 1);
+}
+
+int
+XFillArcs(
+    Display *display,
+    Drawable d,
+    GC gc,
+    XArc *arcs,
+    int narcs)
+{
+    int ret = Success;
+
+    display->request++;
+
+    while (narcs-- > 0) {
+       ret = DrawOrFillArc(display, d, gc, arcs[0].x, arcs[0].y,
+                   arcs[0].width, arcs[0].height,
+                   arcs[0].angle1, arcs[0].angle2, 1);
+       if (ret != Success) {
+           break;
+       }
+       ++arcs;
+    }
+    return ret;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawOrFillArc --
+ *
+ *     This function handles the rendering of drawn or filled arcs and
+ *     chords.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Renders the requested arc.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+DrawOrFillArc(
+    Display *display,
+    Drawable d,
+    GC gc,
+    int x, int y,              /* left top */
+    unsigned int width, unsigned int height,
+    int start,                 /* start: three-o'clock (deg*64) */
+    int extent,                        /* extent: relative (deg*64) */
+    int fill)                  /* ==0 draw, !=0 fill */
+{
+    HDC dc;
+    HBRUSH brush, oldBrush;
+    HPEN pen, oldPen;
+    TkWinDCState state;
+    int clockwise = (extent < 0); /* non-zero if clockwise */
+    int xstart, ystart, xend, yend;
+    double radian_start, radian_end, xr, yr;
+
+    if (d == None) {
+       return BadDrawable;
+    }
+
+    dc = TkWinGetDrawableDC(display, d, &state);
+
+    SetROP2(dc, tkpWinRopModes[gc->function]);
+
+    /*
+     * Compute the absolute starting and ending angles in normalized radians.
+     * Swap the start and end if drawing clockwise.
+     */
+
+    start = start % (64*360);
+    if (start < 0) {
+       start += (64*360);
+    }
+    extent = (start+extent) % (64*360);
+    if (extent < 0) {
+       extent += (64*360);
+    }
+    if (clockwise) {
+       int tmp = start;
+       start = extent;
+       extent = tmp;
+    }
+    radian_start = XAngleToRadians(start);
+    radian_end = XAngleToRadians(extent);
+
+    /*
+     * Now compute points on the radial lines that define the starting and
+     * ending angles. Be sure to take into account that the y-coordinate
+     * system is inverted.
+     */
+
+    xr = x + width / 2.0;
+    yr = y + height / 2.0;
+    xstart = (int)((xr + cos(radian_start)*width/2.0) + 0.5);
+    ystart = (int)((yr + sin(-radian_start)*height/2.0) + 0.5);
+    xend = (int)((xr + cos(radian_end)*width/2.0) + 0.5);
+    yend = (int)((yr + sin(-radian_end)*height/2.0) + 0.5);
+
+    /*
+     * Now draw a filled or open figure. Note that we have to increase the
+     * size of the bounding box by one to account for the difference in pixel
+     * definitions between X and Windows.
+     */
+
+    pen = SetUpGraphicsPort(gc);
+    oldPen = SelectObject(dc, pen);
+    if (!fill) {
+       /*
+        * Note that this call will leave a gap of one pixel at the end of the
+        * arc for thin arcs. We can't use ArcTo because it's only supported
+        * under Windows NT.
+        */
+
+       SetBkMode(dc, TRANSPARENT);
+       Arc(dc, x, y, (int) (x+width+1), (int) (y+height+1), xstart, ystart,
+               xend, yend);
+    } else {
+       brush = CreateSolidBrush(gc->foreground);
+       oldBrush = SelectObject(dc, brush);
+       if (gc->arc_mode == ArcChord) {
+           Chord(dc, x, y, (int) (x+width+1), (int) (y+height+1),
+                   xstart, ystart, xend, yend);
+       } else if (gc->arc_mode == ArcPieSlice) {
+           Pie(dc, x, y, (int) (x+width+1), (int) (y+height+1),
+                   xstart, ystart, xend, yend);
+       }
+       DeleteObject(SelectObject(dc, oldBrush));
+    }
+    DeleteObject(SelectObject(dc, oldPen));
+    TkWinReleaseDrawableDC(d, dc, &state);
+    return Success;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetUpGraphicsPort --
+ *
+ *     Set up the graphics port from the given GC.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     The current port is adjusted.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static HPEN
+SetUpGraphicsPort(
+    GC gc)
+{
+    DWORD style;
+
+    if (gc->line_style == LineOnOffDash) {
+       unsigned char *p = (unsigned char *) &(gc->dashes);
+                               /* pointer to the dash-list */
+
+       /*
+        * Below is a simple translation of serveral dash patterns to valid
+        * windows pen types. Far from complete, but I don't know how to do it
+        * better. Any ideas: <mailto:j.nijtmans@chello.nl>
+        */
+
+       if (p[1] && p[2]) {
+           if (!p[3] || p[4]) {
+               style = PS_DASHDOTDOT;          /*      -..     */
+           } else {
+               style = PS_DASHDOT;             /*      -.      */
+           }
+       } else {
+           if (p[0] > (4 * gc->line_width)) {
+               style = PS_DASH;                /*      -       */
+           } else {
+               style = PS_DOT;                 /*      .       */
+           }
+       }
+    } else {
+       style = PS_SOLID;
+    }
+    if (gc->line_width < 2) {
+       return CreatePen((int) style, gc->line_width, gc->foreground);
+    } else {
+       LOGBRUSH lb;
+
+       lb.lbStyle = BS_SOLID;
+       lb.lbColor = gc->foreground;
+       lb.lbHatch = 0;
+
+       style |= PS_GEOMETRIC;
+       switch (gc->cap_style) {
+       case CapNotLast:
+       case CapButt:
+           style |= PS_ENDCAP_FLAT;
+           break;
+       case CapRound:
+           style |= PS_ENDCAP_ROUND;
+           break;
+       default:
+           style |= PS_ENDCAP_SQUARE;
+           break;
+       }
+       switch (gc->join_style) {
+       case JoinMiter:
+           style |= PS_JOIN_MITER;
+           break;
+       case JoinRound:
+           style |= PS_JOIN_ROUND;
+           break;
+       default:
+           style |= PS_JOIN_BEVEL;
+           break;
+       }
+       return ExtCreatePen(style, (DWORD) gc->line_width, &lb, 0, NULL);
+    }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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, int width, int height,
+                               /* Position rectangle to be scrolled. */
+    int dx, int dy,            /* Distance rectangle should be moved. */
+    TkRegion damageRgn)                /* Region to accumulate damage in. */
+{
+    HWND hwnd = TkWinGetHWND(Tk_WindowId(tkwin));
+    RECT scrollRect;
+
+    scrollRect.left = x;
+    scrollRect.top = y;
+    scrollRect.right = x + width;
+    scrollRect.bottom = y + height;
+    return (ScrollWindowEx(hwnd, dx, dy, &scrollRect, NULL, (HRGN) damageRgn,
+           NULL, 0) == NULLREGION) ? 0 : 1;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinFillRect --
+ *
+ *     This routine fills a rectangle with the foreground color from the
+ *     specified GC ignoring all other GC values. This is the fastest way to
+ *     fill a drawable with a solid color.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Modifies the contents of the DC drawing surface.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkWinFillRect(
+    HDC dc,
+    int x, int y, int width, int height,
+    int pixel)
+{
+    RECT rect;
+    COLORREF oldColor;
+
+    rect.left = x;
+    rect.top = y;
+    rect.right = x + width;
+    rect.bottom = y + height;
+    oldColor = SetBkColor(dc, (COLORREF)pixel);
+    SetBkMode(dc, OPAQUE);
+    ExtTextOutW(dc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
+    SetBkColor(dc, oldColor);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDrawHighlightBorder --
+ *
+ *     This function draws a rectangular ring around the outside of a widget
+ *     to indicate that it has received the input focus.
+ *
+ *      On Windows, we just draw the simple inset ring. On other sytems, e.g.
+ *      the Mac, the focus ring is a little more complicated, so we need this
+ *      abstraction.
+ *
+ * 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)
+{
+    TkDrawInsetFocusHighlight(tkwin, fgGC, highlightWidth, drawable, 0);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDrawFrame --
+ *
+ *     This function draws the rectangular frame area.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Draws inside the tkwin area.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpDrawFrame(
+    Tk_Window tkwin,
+    Tk_3DBorder border,
+    int highlightWidth,
+    int borderWidth,
+    int relief)
+{
+    Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), border, highlightWidth,
+           highlightWidth, Tk_Width(tkwin) - 2 * highlightWidth,
+           Tk_Height(tkwin) - 2 * highlightWidth, borderWidth, relief);
+}
+\f
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */