diff options
Diffstat (limited to 'win/tkWinDraw.c')
-rw-r--r-- | win/tkWinDraw.c | 1264 |
1 files changed, 1264 insertions, 0 deletions
diff --git a/win/tkWinDraw.c b/win/tkWinDraw.c new file mode 100644 index 0000000..e972365 --- /dev/null +++ b/win/tkWinDraw.c @@ -0,0 +1,1264 @@ +/* + * 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. + * + * SCCS: @(#) tkWinDraw.c 1.30 97/03/21 11:20:05 + */ + +#include "tkWinInt.h" + +/* + * These macros convert between X's bizarre angle units to radians. + */ + +#define PI 3.14159265358979 +#define XAngleToRadians(a) ((double)(a) / 64 * PI / 180); + +/* + * Translation table between X gc functions and Win32 raster op modes. + */ + +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) */ + +static int bltModes[] = { + 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. + */ + +#define MIN(a,b) ((a>b) ? b : a) +#define MAX(a,b) ((a<b) ? b : a) + +/* + * The followng typedef is used to pass Windows GDI drawing functions. + */ + +typedef BOOL (CALLBACK *WinDrawFunc) _ANSI_ARGS_((HDC dc, + CONST POINT* points, int npoints)); + +/* + * Forward declarations for procedures defined in this file: + */ + +static POINT * ConvertPoints _ANSI_ARGS_((XPoint *points, int npoints, + int mode, RECT *bbox)); +static void DrawOrFillArc _ANSI_ARGS_((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 _ANSI_ARGS_((HDC dc, GC gc, + XPoint* points, int npoints, int mode, HPEN pen, + WinDrawFunc func)); + +/* + *---------------------------------------------------------------------- + * + * 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, d, state) + 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); + 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(d, dc, state) + Drawable d; + HDC dc; + TkWinDCState *state; +{ + TkWinDrawable *twdPtr = (TkWinDrawable *)d; + 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 that should not be freed. + * + *---------------------------------------------------------------------- + */ + +static POINT * +ConvertPoints(points, npoints, mode, bbox) + XPoint *points; + int npoints; + int mode; /* CoordModeOrigin or CoordModePrevious. */ + RECT *bbox; /* Bounding box of points. */ +{ + static POINT *winPoints = NULL; /* Array of points that is reused. */ + static int nWinPoints = -1; /* Current size of point array. */ + 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 > nWinPoints) { + if (winPoints != NULL) { + ckfree((char *) winPoints); + } + winPoints = (POINT *) ckalloc(sizeof(POINT) * npoints); + if (winPoints == NULL) { + nWinPoints = -1; + return NULL; + } + 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++) { + winPoints[i].x = points[i].x; + winPoints[i].y = points[i].y; + bbox->left = MIN(bbox->left, winPoints[i].x); + bbox->right = MAX(bbox->right, winPoints[i].x); + bbox->top = MIN(bbox->top, winPoints[i].y); + bbox->bottom = MAX(bbox->bottom, winPoints[i].y); + } + } else { + winPoints[0].x = points[0].x; + winPoints[0].y = points[0].y; + for (i = 1; i < npoints; i++) { + winPoints[i].x = winPoints[i-1].x + points[i].x; + winPoints[i].y = winPoints[i-1].y + points[i].y; + bbox->left = MIN(bbox->left, winPoints[i].x); + bbox->right = MAX(bbox->right, winPoints[i].x); + bbox->top = MIN(bbox->top, winPoints[i].y); + bbox->bottom = MAX(bbox->bottom, winPoints[i].y); + } + } + return 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. + * + *---------------------------------------------------------------------- + */ + +void +XCopyArea(display, src, dest, gc, src_x, src_y, width, height, dest_x, dest_y) + Display* display; + Drawable src; + Drawable dest; + GC gc; + int src_x, src_y; + unsigned int width, height; + int dest_x, 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, width, height, srcDC, src_x, src_y, + bltModes[gc->function]); + + SelectClipRgn(destDC, NULL); + + if (src != dest) { + TkWinReleaseDrawableDC(dest, destDC, &destState); + } + TkWinReleaseDrawableDC(src, srcDC, &srcState); +} + +/* + *---------------------------------------------------------------------- + * + * 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, src, dest, gc, src_x, src_y, width, height, dest_x, + dest_y, plane) + Display* display; + Drawable src; + Drawable dest; + GC gc; + int src_x, src_y; + unsigned int width, height; + int dest_x, 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) { + 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, width, 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); + BitBlt(destDC, dest_x, dest_y, width, 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(width, 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, width, height, srcDC, src_x, src_y, SRCCOPY); + BitBlt(memDC, 0, 0, width, 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, width, 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, width, height, srcDC, src_x, src_y, + NOTSRCCOPY); + BitBlt(memDC, 0, 0, width, height, maskDC, + dest_x - gc->clip_x_origin, dest_y - gc->clip_y_origin, + SRCAND); + SelectObject(destDC, bgBrush); + BitBlt(destDC, dest_x, dest_y, width, 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); +} + +/* + *---------------------------------------------------------------------- + * + * 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(colors, ncolors, display, d, gc, image, src_x, src_y, dest_x, + dest_y, width, height) + 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, src_y; /* Offset of subimage. */ + int dest_x, dest_y; /* Position of subimage origin in + * drawable. */ + unsigned int width, 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 = (BITMAPINFO*) ckalloc(sizeof(BITMAPINFOHEADER) + + sizeof(RGBQUAD)*ncolors); + } else { + infoPtr = (BITMAPINFO*) ckalloc(sizeof(BITMAPINFOHEADER)); + } + + infoPtr->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + infoPtr->bmiHeader.biWidth = image->width; + + /* + * The following code works around a bug in Win32s. CreateDIBitmap + * fails under Win32s for top-down images. So we have to reverse the + * order of the scanlines. If we are not running under Win32s, we can + * just declare the image to be top-down. + */ + + if (tkpIsWin32s) { + int y; + char *srcPtr, *dstPtr, *temp; + + temp = ckalloc((unsigned) image->bytes_per_line); + srcPtr = image->data; + dstPtr = image->data+(image->bytes_per_line * (image->height - 1)); + for (y = 0; y < (image->height/2); y++) { + memcpy(temp, srcPtr, image->bytes_per_line); + memcpy(srcPtr, dstPtr, image->bytes_per_line); + memcpy(dstPtr, temp, image->bytes_per_line); + srcPtr += image->bytes_per_line; + dstPtr -= image->bytes_per_line; + } + ckfree(temp); + infoPtr->bmiHeader.biHeight = image->height; /* Bottom-up order */ + } else { + 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((char *) infoPtr); + } + bitmap = SelectObject(dcMem, bitmap); + BitBlt(dc, dest_x, dest_y, width, height, dcMem, src_x, src_y, SRCCOPY); + DeleteObject(SelectObject(dcMem, bitmap)); + DeleteDC(dcMem); + TkWinReleaseDrawableDC(d, dc, &state); +} + +/* + *---------------------------------------------------------------------- + * + * XFillRectangles -- + * + * Fill multiple rectangular areas in the given drawable. + * + * Results: + * None. + * + * Side effects: + * Draws onto the specified drawable. + * + *---------------------------------------------------------------------- + */ + +void +XFillRectangles(display, d, gc, rectangles, nrectangles) + Display* display; + Drawable d; + GC gc; + XRectangle* rectangles; + int nrectangles; +{ + HDC dc; + int i; + RECT rect; + TkWinDCState state; + HBRUSH brush; + + if (d == None) { + return; + } + + 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 oldBrush, stipple; + HBITMAP oldBitmap, bitmap; + HDC dcMem; + HBRUSH bgBrush = CreateSolidBrush(gc->background); + + if (twdPtr->type != TWD_BITMAP) { + 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. + */ + + for (i = 0; i < nrectangles; i++) { + bitmap = CreateCompatibleBitmap(dc, rectangles[i].width, + rectangles[i].height); + oldBitmap = SelectObject(dcMem, bitmap); + rect.left = 0; + rect.top = 0; + rect.right = rectangles[i].width; + rect.bottom = rectangles[i].height; + FillRect(dcMem, &rect, brush); + BitBlt(dc, rectangles[i].x, rectangles[i].y, rectangles[i].width, + rectangles[i].height, dcMem, 0, 0, COPYFG); + if (gc->fill_style == FillOpaqueStippled) { + FillRect(dcMem, &rect, bgBrush); + BitBlt(dc, rectangles[i].x, rectangles[i].y, + rectangles[i].width, rectangles[i].height, dcMem, + 0, 0, COPYBG); + } + SelectObject(dcMem, oldBitmap); + DeleteObject(bitmap); + } + + DeleteDC(dcMem); + SelectObject(dc, oldBrush); + DeleteObject(stipple); + DeleteObject(bgBrush); + } else { + for (i = 0; i < nrectangles; i++) { + TkWinFillRect(dc, rectangles[i].x, rectangles[i].y, + rectangles[i].width, rectangles[i].height, gc->foreground); + } + } + DeleteObject(brush); + TkWinReleaseDrawableDC(d, dc, &state); +} + +/* + *---------------------------------------------------------------------- + * + * 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(dc, gc, points, npoints, mode, pen, func) + HDC dc; + GC gc; + XPoint* points; + int npoints; + int mode; + HPEN pen; + WinDrawFunc func; +{ + RECT rect; + 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) { + panic("unexpected drawable type in stipple"); + } + + /* + * Grow the bounding box enough to account for wide lines. + */ + + if (gc->line_width > 1) { + 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)); + (*func)(dcMem, winPoints, npoints); + 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))); + (*func)(dcMem, winPoints, npoints); + 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); + + (*func)(dc, winPoints, npoints); + + SelectObject(dc, oldPen); + } + DeleteObject(SelectObject(dc, oldBrush)); +} + +/* + *---------------------------------------------------------------------- + * + * XDrawLines -- + * + * Draw connected lines. + * + * Results: + * None. + * + * Side effects: + * Renders a series of connected lines. + * + *---------------------------------------------------------------------- + */ + +void +XDrawLines(display, d, gc, points, npoints, mode) + Display* display; + Drawable d; + GC gc; + XPoint* points; + int npoints; + int mode; +{ + HPEN pen; + TkWinDCState state; + HDC dc; + + if (d == None) { + return; + } + + dc = TkWinGetDrawableDC(display, d, &state); + + if (!tkpIsWin32s && (gc->line_width > 1)) { + LOGBRUSH lb; + DWORD style; + + lb.lbStyle = BS_SOLID; + lb.lbColor = gc->foreground; + lb.lbHatch = 0; + + style = PS_GEOMETRIC|PS_COSMETIC; + 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; + } + pen = ExtCreatePen(style, gc->line_width, &lb, 0, NULL); + } else { + pen = CreatePen(PS_SOLID, gc->line_width, gc->foreground); + } + RenderObject(dc, gc, points, npoints, mode, pen, Polyline); + DeleteObject(pen); + + TkWinReleaseDrawableDC(d, dc, &state); +} + +/* + *---------------------------------------------------------------------- + * + * XFillPolygon -- + * + * Draws a filled polygon. + * + * Results: + * None. + * + * Side effects: + * Draws a filled polygon on the specified drawable. + * + *---------------------------------------------------------------------- + */ + +void +XFillPolygon(display, d, gc, points, npoints, shape, mode) + Display* display; + Drawable d; + GC gc; + XPoint* points; + int npoints; + int shape; + int mode; +{ + HPEN pen; + TkWinDCState state; + HDC dc; + + if (d == None) { + return; + } + + dc = TkWinGetDrawableDC(display, d, &state); + + pen = GetStockObject(NULL_PEN); + RenderObject(dc, gc, points, npoints, mode, pen, Polygon); + + TkWinReleaseDrawableDC(d, dc, &state); +} + +/* + *---------------------------------------------------------------------- + * + * XDrawRectangle -- + * + * Draws a rectangle. + * + * Results: + * None. + * + * Side effects: + * Draws a rectangle on the specified drawable. + * + *---------------------------------------------------------------------- + */ + +void +XDrawRectangle(display, d, gc, x, y, width, height) + 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; + } + + dc = TkWinGetDrawableDC(display, d, &state); + + pen = CreatePen(PS_SOLID, gc->line_width, gc->foreground); + oldPen = SelectObject(dc, pen); + oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH)); + SetROP2(dc, tkpWinRopModes[gc->function]); + + Rectangle(dc, x, y, x+width+1, y+height+1); + + DeleteObject(SelectObject(dc, oldPen)); + SelectObject(dc, oldBrush); + TkWinReleaseDrawableDC(d, dc, &state); +} + +/* + *---------------------------------------------------------------------- + * + * XDrawArc -- + * + * Draw an arc. + * + * Results: + * None. + * + * Side effects: + * Draws an arc on the specified drawable. + * + *---------------------------------------------------------------------- + */ + +void +XDrawArc(display, d, gc, x, y, width, height, start, extent) + Display* display; + Drawable d; + GC gc; + int x; + int y; + unsigned int width; + unsigned int height; + int start; + int extent; +{ + display->request++; + + DrawOrFillArc(display, d, gc, x, y, width, height, start, extent, 0); +} + +/* + *---------------------------------------------------------------------- + * + * XFillArc -- + * + * Draw a filled arc. + * + * Results: + * None. + * + * Side effects: + * Draws a filled arc on the specified drawable. + * + *---------------------------------------------------------------------- + */ + +void +XFillArc(display, d, gc, x, y, width, height, start, extent) + Display* display; + Drawable d; + GC gc; + int x; + int y; + unsigned int width; + unsigned int height; + int start; + int extent; +{ + display->request++; + + DrawOrFillArc(display, d, gc, x, y, width, height, start, extent, 1); +} + +/* + *---------------------------------------------------------------------- + * + * DrawOrFillArc -- + * + * This procedure handles the rendering of drawn or filled + * arcs and chords. + * + * Results: + * None. + * + * Side effects: + * Renders the requested arc. + * + *---------------------------------------------------------------------- + */ + +static void +DrawOrFillArc(display, d, gc, x, y, width, height, start, extent, fill) + Display *display; + Drawable d; + GC gc; + int x, y; /* left top */ + unsigned int width, 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; + } + + 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 = CreatePen(PS_SOLID, gc->line_width, gc->foreground); + 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. + */ + + Arc(dc, x, y, x+width+1, 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, x+width+1, y+height+1, xstart, ystart, xend, yend); + } else if ( gc->arc_mode == ArcPieSlice ) { + Pie(dc, x, y, x+width+1, y+height+1, xstart, ystart, xend, yend); + } + DeleteObject(SelectObject(dc, oldBrush)); + } + DeleteObject(SelectObject(dc, oldPen)); + TkWinReleaseDrawableDC(d, dc, &state); +} + +/* + *---------------------------------------------------------------------- + * + * 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(tkwin, gc, x, y, width, height, dx, dy, damageRgn) + Tk_Window tkwin; /* The window to be scrolled. */ + GC gc; /* GC for window to be scrolled. */ + int x, y, width, height; /* Position rectangle to be scrolled. */ + int dx, 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(dc, x, y, width, height, pixel) + HDC dc; + int x, y, width, 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); +} |