summaryrefslogtreecommitdiffstats
path: root/tk8.6/win/tkWinDraw.c
diff options
context:
space:
mode:
Diffstat (limited to 'tk8.6/win/tkWinDraw.c')
-rw-r--r--tk8.6/win/tkWinDraw.c1523
1 files changed, 1523 insertions, 0 deletions
diff --git a/tk8.6/win/tkWinDraw.c b/tk8.6/win/tkWinDraw.c
new file mode 100644
index 0000000..e13a5e5
--- /dev/null
+++ b/tk8.6/win/tkWinDraw.c
@@ -0,0 +1,1523 @@
+/*
+ * 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 ThreadSpecificData {
+ 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);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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");
+ DeleteDC(dcMem);
+ TkWinReleaseDrawableDC(d, dc, &state);
+ return BadValue;
+ }
+ 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);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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);
+ ExtTextOut(dc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
+ SetBkColor(dc, oldColor);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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);
+}
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */