/*
 * rbcWinDraw.c --
 *
 *      This module contains WIN32 routines not included in the Tcl/Tk
 *      libraries.
 *
 * Copyright (c) 2009 Samuel Green, Nicholas Hudson, Stanton Sievers, Jarrod Stormo
 * All rights reserved.
 *
 * See "license.terms" for details.
 */

#include "rbcInt.h"
#include <tkWinInt.h>
#include <X11/Xutil.h>
#include <X11/Xlib.h>

#define WINDEBUG 0

#ifdef WIN32
#define COPYFG          0x00CA0749 /* dest = (pat & src) | (!pat & dst) */
#define COPYBG          0x00AC0744 /* dest = (!pat & src) | (pat & dst) */
#endif

/*
 * Data structure for setting graphics context.
 */
typedef struct {
    int function;		/* logical operation */
    unsigned long plane_mask;	/* plane mask */
    unsigned long foreground;	/* foreground pixel */
    unsigned long background;	/* background pixel */
    int line_width;		/* line width */
    int line_style;		/* LineSolid, LineOnOffDash, LineDoubleDash */
    int cap_style;		/* CapNotLast, CapButt,
				   CapRound, CapProjecting */
    int join_style;		/* JoinMiter, JoinRound, JoinBevel */
    int fill_style;		/* FillSolid, FillTiled,
				   FillStippled, FillOpaeueStippled */
    int fill_rule;		/* EvenOddRule, WindingRule */
    int arc_mode;		/* ArcChord, ArcPieSlice */
    Pixmap tile;		/* tile pixmap for tiling operations */
    Pixmap stipple;		/* stipple 1 plane pixmap for stipping */
    int ts_x_origin;		/* offset for tile or stipple operations */
    int ts_y_origin;
    Font font;			/* default text font for text operations */
    int subwindow_mode;		/* ClipByChildren, IncludeInferiors */
    Bool graphics_exposures;	/* boolean, should exposures be generated */
    int clip_x_origin;		/* origin for clipping */
    int clip_y_origin;
    Pixmap clip_mask;		/* bitmap clipping; other calls for rects */
    int dash_offset;		/* patterned/dashed line information */
    char dashes;		/* If -1, indicates that the extended
				 * information below is available. */
    int nDashValues;
    char dashValues[12];
} XGCValuesEx;

#define bltModes tkpWinBltModes

static Tcl_Encoding systemEncoding = NULL;

/*
 *--------------------------------------------------------------
 *
 * Rbc_GetSystemPalette --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
HPALETTE
Rbc_GetSystemPalette()
{
    HDC hDC;
    HPALETTE hPalette;
    DWORD flags;

    hPalette = NULL;
    hDC = GetDC(NULL);		/* Get the desktop device context */
    flags = GetDeviceCaps(hDC, RASTERCAPS);
    if (flags & RC_PALETTE) {
        LOGPALETTE *palettePtr;

        palettePtr = (LOGPALETTE *)
                     GlobalAlloc(GPTR, sizeof(LOGPALETTE) + 256 * sizeof(PALETTEENTRY));
        palettePtr->palVersion = 0x300;
        palettePtr->palNumEntries = 256;
        GetSystemPaletteEntries(hDC, 0, 256, palettePtr->palPalEntry);
        hPalette = CreatePalette(palettePtr);
        GlobalFree(palettePtr);
    }
    ReleaseDC(NULL, hDC);
    return hPalette;
}

/*
 *--------------------------------------------------------------
 *
 * CreateRotatedFont --
 *
 *      Creates a rotated copy of the given font.  This only works
 *      for TrueType fonts.
 *
 * Results:
 *      Returns the newly create font or NULL if the font could not
 *      be created.
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
HFONT
CreateRotatedFont(fontId, theta)
    unsigned long fontId; /* Font identifier (actually a Tk_Font) */
    double theta; /* Number of degrees to rotate font */
{
    RbcFontAttributes *faPtr;	/* Set of attributes to match. */
    TkFont *fontPtr;
    HFONT hFont;
    LOGFONTW lf;

    fontPtr = (TkFont *) fontId;
    faPtr = &fontPtr->fa;
    ZeroMemory(&lf, sizeof(LOGFONT));
    lf.lfHeight = -faPtr->pointsize;
    if (lf.lfHeight < 0) {
        HDC dc;

        dc = GetDC(NULL);
        lf.lfHeight = -MulDiv(faPtr->pointsize,
                              GetDeviceCaps(dc, LOGPIXELSY), 72);
        ReleaseDC(NULL, dc);
    }
    lf.lfWidth = 0;
    lf.lfEscapement = lf.lfOrientation = ROUND(theta * 10.0);
#define TK_FW_NORMAL	0
    lf.lfWeight = (faPtr->weight == TK_FW_NORMAL) ? FW_NORMAL : FW_BOLD;
    lf.lfItalic = faPtr->slant;
    lf.lfUnderline = faPtr->underline;
    lf.lfStrikeOut = faPtr->overstrike;
    lf.lfCharSet = DEFAULT_CHARSET;
    lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
    lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
    lf.lfQuality = DEFAULT_QUALITY;
    lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;

    hFont = NULL;
    if (faPtr->family == NULL) {
        lf.lfFaceName[0] = '\0';
    } else {
        Tcl_DString dString;

        Tcl_UtfToExternalDString(systemEncoding, faPtr->family, -1, &dString);

        if (Rbc_GetPlatformId() == VER_PLATFORM_WIN32_NT) {
            Tcl_UniChar *src, *dst;

            /*
             * We can only store up to LF_FACESIZE wide characters
             */
            if (Tcl_DStringLength(&dString) >= (LF_FACESIZE * sizeof(WCHAR))) {
                Tcl_DStringSetLength(&dString, LF_FACESIZE);
            }
            src = (Tcl_UniChar *)Tcl_DStringValue(&dString);
            dst = (Tcl_UniChar *)lf.lfFaceName;
            while (*src != '\0') {
                *dst++ = *src++;
            }
            *dst = '\0';
            hFont = CreateFontIndirectW((LOGFONTW *)&lf);
        } else {
            /*
             * We can only store up to LF_FACESIZE characters
             */
            if (Tcl_DStringLength(&dString) >= LF_FACESIZE) {
                Tcl_DStringSetLength(&dString, LF_FACESIZE);
            }
            strcpy((char *)lf.lfFaceName, Tcl_DStringValue(&dString));
            hFont = CreateFontIndirectA((LOGFONTA *)&lf);
        }
        Tcl_DStringFree(&dString);
    }

    if (hFont == NULL) {
#if WINDEBUG
        PurifyPrintf("can't create font: %s\n", Rbc_LastError());
#endif
    } else {
        HFONT oldFont;
        TEXTMETRIC tm;
        HDC hRefDC;
        int result;

        /* Check if the rotated font is really a TrueType font. */

        hRefDC = GetDC(NULL);		/* Get the desktop device context */
        oldFont = SelectFont(hRefDC, hFont);
        result = ((GetTextMetrics(hRefDC, &tm)) &&
                  (tm.tmPitchAndFamily & TMPF_TRUETYPE));
        SelectFont(hRefDC, oldFont);
        ReleaseDC(NULL, hRefDC);
        if (!result) {
#if WINDEBUG
            PurifyPrintf("not a true type font\n");
#endif
            DeleteFont(hFont);
            return NULL;
        }
    }
    return hFont;
}

/*
 *--------------------------------------------------------------
 *
 * Rbc_GetBitmapData --
 *
 *      Returns the DIB bits from a bitmap.
 *
 * Results:
 *      Returns a byte array of bitmap data or NULL if an error
 *      occurred.  The parameter pitchPtr returns the number
 *      of bytes per row.
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
unsigned char *
Rbc_GetBitmapData(display, bitmap, width, height, pitchPtr)
    Display *display; /* Display of bitmap */
    Pixmap bitmap; /* Bitmap to query */
    int width; /* Width of bitmap */
    int height; /* Height of bitmap */
    int *pitchPtr; /* (out) Number of bytes per row */
{
    TkWinDCState state;
    HDC dc;
    int result;
    unsigned char *bits;
    unsigned int size;
    HBITMAP hBitmap;
    BITMAPINFOHEADER *bmiPtr;
    HANDLE hMem, hMem2;
    int bytesPerRow, imageSize;

    size = sizeof(BITMAPINFOHEADER) + 2 * sizeof(RGBQUAD);
    hMem = GlobalAlloc(GHND, size);
    bmiPtr = (BITMAPINFOHEADER *)GlobalLock(hMem);
    bmiPtr->biSize = sizeof(BITMAPINFOHEADER);
    bmiPtr->biPlanes = 1;
    bmiPtr->biBitCount = 1;
    bmiPtr->biCompression = BI_RGB;
    bmiPtr->biWidth = width;
    bmiPtr->biHeight = height;

    hBitmap = ((TkWinDrawable *)bitmap)->bitmap.handle;
    dc = TkWinGetDrawableDC(display, bitmap, &state);
    result = GetDIBits(dc, hBitmap, 0, height, (LPVOID)NULL,
                       (BITMAPINFO *)bmiPtr, DIB_RGB_COLORS);
    TkWinReleaseDrawableDC(bitmap, dc, &state);
    if (!result) {
        GlobalUnlock(hMem);
        GlobalFree(hMem);
        return NULL;
    }
    imageSize = bmiPtr->biSizeImage;
    GlobalUnlock(hMem);
    bytesPerRow = ((width + 31) & ~31) / 8;
    if (imageSize == 0) {
        imageSize = bytesPerRow * height;
    }
    hMem2 = GlobalReAlloc(hMem, size + imageSize, 0);
    if (hMem2 == NULL) {
        GlobalFree(hMem);
        return NULL;
    }
    hMem = hMem2;
    bmiPtr = (LPBITMAPINFOHEADER)GlobalLock(hMem);
    dc = TkWinGetDrawableDC(display, bitmap, &state);
    result = GetDIBits(dc, hBitmap, 0, height, (unsigned char *)bmiPtr + size,
                       (BITMAPINFO *)bmiPtr, DIB_RGB_COLORS);
    TkWinReleaseDrawableDC(bitmap, dc, &state);
    bits = NULL;
    if (!result) {
        OutputDebugString("GetDIBits failed\n");
    } else {
        bits = (unsigned char *)ckalloc(imageSize);
        if (bits != NULL) {
            memcpy (bits, (unsigned char *)bmiPtr + size, imageSize);
        }
    }
    *pitchPtr = bytesPerRow;
    GlobalUnlock(hMem);
    GlobalFree(hMem);
    return bits;
}

/*
 *--------------------------------------------------------------
 *
 * Rbc_EmulateXFree --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
void
Rbc_EmulateXFree(ptr)
    void *ptr;
{
    ckfree((char *)ptr);
}

/*
 *--------------------------------------------------------------
 *
 * Rbc_EmulateXMaxRequestSize --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
long
Rbc_EmulateXMaxRequestSize(display)
    Display *display;
{
    return (SHRT_MAX / 4);
}

/*
 *--------------------------------------------------------------
 *
 * Rbc_EmulateXLowerWindow --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
void
Rbc_EmulateXLowerWindow(display, window)
    Display *display;
    Window window;
{
    HWND hWnd;

    hWnd = Tk_GetHWND(window);
    LastKnownRequestProcessed(display) = NextRequest(display);
    SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}

/*
 *--------------------------------------------------------------
 *
 * Rbc_EmulateXRaiseWindow --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
void
Rbc_EmulateXRaiseWindow(display, window)
    Display *display;
    Window window;
{
    HWND hWnd;

    hWnd = Tk_GetHWND(window);
    LastKnownRequestProcessed(display) = NextRequest(display);
    SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}

/*
 *--------------------------------------------------------------
 *
 * Rbc_EmulateXUnmapWindow --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
void
Rbc_EmulateXUnmapWindow(display, window)
    Display *display;
    Window window;
{
    HWND hWnd;

    hWnd = Tk_GetHWND(window);
    LastKnownRequestProcessed(display) = NextRequest(display);
    ShowWindow(hWnd, SW_HIDE);
    /* SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); */
}

/*
 *--------------------------------------------------------------
 *
 * Rbc_EmulateXWarpPointer --
 *
 *      If destWindow is None, moves the pointer by the offsets (destX,
 *      destY) relative to the current position of the pointer.
 *      If destWindow is a window, moves the pointer to the offsets
 *      (destX, destY) relative to the origin of destWindow.  However,
 *      if srcWindow is a window, the move only takes place if the window
 *      srcWindow contains the pointer and if the specified rectangle of
 *      srcWindow contains the pointer.
 *
 *      The srcX and srcY coordinates are relative to the origin of
 *      srcWindow.  If srcHeight is zero, it is replaced with the current
 *      height of srcWindow minus srcY.  If srcWidth is zero, it is
 *      replaced with the current width of srcWindow minus srcX.
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
void
Rbc_EmulateXWarpPointer(display, srcWindow, destWindow, srcX, srcY, srcWidth, srcHeight, destX, destY)
    Display *display;
    Window srcWindow;
    Window destWindow;
    int srcX;
    int srcY;
    unsigned int srcWidth;
    unsigned int srcHeight;
    int destX;
    int destY;
{
    HWND hWnd;
    POINT point;

    hWnd = Tk_GetHWND(destWindow);
    point.x = destX, point.y = destY;
    if (ClientToScreen(hWnd, &point)) {
        SetCursorPos(point.x, point.y);
    }
}

#ifdef notdef
static Tcl_HashTable gcTable;
static int gcInitialized = FALSE;
#endif

typedef struct {
    HDC dc;
    int count;
    COLORREF color;
    int offset, nBits;
} DashInfo;

/*
 *--------------------------------------------------------------
 *
 * Rbc_SetDashes --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
void
Rbc_SetDashes(display, gc, dashesPtr)
    Display *display;
    GC gc;
    Rbc_Dashes *dashesPtr;
{
    XGCValuesEx *gcPtr = (XGCValuesEx *)gc;

    /* This must be used only with a privately created GC */
    assert((int)gcPtr->dashes == -1);
    gcPtr->nDashValues = strlen((char *)dashesPtr->values);
    gcPtr->dash_offset = dashesPtr->offset;
    strcpy(gcPtr->dashValues, (char *)dashesPtr->values);
}

/*
 *--------------------------------------------------------------
 *
 * GetDashInfo --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
static int
GetDashInfo(dc, gc, infoPtr)
    HDC dc;
    GC gc;
    DashInfo *infoPtr;
{
    int dashOffset, dashValue;

    dashValue = 0;
    dashOffset = gc->dash_offset;
    if ((int)gc->dashes == -1) {
        XGCValuesEx *gcPtr = (XGCValuesEx *)gc;

        if (gcPtr->nDashValues == 1) {
            dashValue = gcPtr->dashValues[0];
        }
    } else if (gc->dashes > 0) {
        dashValue = (int)gc->dashes;
    }
    if (dashValue == 0) {
        return FALSE;
    }
    infoPtr->dc = dc;
    infoPtr->nBits = dashValue;
    infoPtr->offset = dashOffset;
    infoPtr->count = 0;
    infoPtr->color = gc->foreground;
    return TRUE;
}

/*
 *--------------------------------------------------------------
 *
 * Rbc_SetROP2 --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
void
Rbc_SetROP2(dc, function)
    HDC dc;
    int function;
{
    SetROP2(dc, tkpWinRopModes[function]);
}

/*
 *--------------------------------------------------------------
 *
 * CreateGC --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
static XGCValuesEx *
CreateGC()
{
    XGCValuesEx *gcPtr;

    gcPtr = (XGCValuesEx *)ckalloc(sizeof(XGCValuesEx));
    if (gcPtr == NULL) {
        return NULL;
    }
    gcPtr->arc_mode = ArcPieSlice;
    gcPtr->background = 0xffffff;
    gcPtr->cap_style = CapNotLast;
    gcPtr->clip_mask = None;
    gcPtr->clip_x_origin = gcPtr->clip_y_origin = 0;
    gcPtr->dash_offset	= 0;
    gcPtr->fill_rule = WindingRule;
    gcPtr->fill_style = FillSolid;
    gcPtr->font = None;
    gcPtr->foreground = 0;
    gcPtr->function = GXcopy;
    gcPtr->graphics_exposures = True;
    gcPtr->join_style = JoinMiter;
    gcPtr->line_style = LineSolid;
    gcPtr->line_width = 0;
    gcPtr->plane_mask = ~0;
    gcPtr->stipple = None;
    gcPtr->subwindow_mode = ClipByChildren;
    gcPtr->tile = None;
    gcPtr->ts_x_origin = gcPtr->ts_y_origin = 0;

    gcPtr->dashes = -1;    /* Mark that this an extended GC */
    gcPtr->nDashValues	= 0;

    return gcPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * Rbc_EmulateXCreateGC --
 *
 *      Allocate a new extended GC, and initialize the specified fields.
 *
 * Results:
 *      Returns a newly allocated GC.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */
GC
Rbc_EmulateXCreateGC(display, drawable, mask, srcPtr)
    Display *display;
    Drawable drawable;
    unsigned long mask;
    XGCValues *srcPtr;
{
    XGCValuesEx *destPtr;

    destPtr = CreateGC();
    if (destPtr == NULL) {
        return None;
    }
    if (mask & GCFunction) {
        destPtr->function = srcPtr->function;
    }
    if (mask & GCPlaneMask) {
        destPtr->plane_mask = srcPtr->plane_mask;
    }
    if (mask & GCForeground) {
        destPtr->foreground = srcPtr->foreground;
    }
    if (mask & GCBackground) {
        destPtr->background = srcPtr->background;
    }
    if (mask & GCLineWidth) {
        destPtr->line_width = srcPtr->line_width;
    }
    if (mask & GCLineStyle) {
        destPtr->line_style = srcPtr->line_style;
    }
    if (mask & GCCapStyle) {
        destPtr->cap_style = srcPtr->cap_style;
    }
    if (mask & GCJoinStyle) {
        destPtr->join_style = srcPtr->join_style;
    }
    if (mask & GCFillStyle) {
        destPtr->fill_style = srcPtr->fill_style;
    }
    if (mask & GCFillRule) {
        destPtr->fill_rule = srcPtr->fill_rule;
    }
    if (mask & GCArcMode) {
        destPtr->arc_mode = srcPtr->arc_mode;
    }
    if (mask & GCTile) {
        destPtr->tile = srcPtr->tile;
    }
    if (mask & GCStipple) {
        destPtr->stipple = srcPtr->stipple;
    }
    if (mask & GCTileStipXOrigin) {
        destPtr->ts_x_origin = srcPtr->ts_x_origin;
    }
    if (mask & GCTileStipXOrigin) {
        destPtr->ts_y_origin = srcPtr->ts_y_origin;
    }
    if (mask & GCFont) {
        destPtr->font = srcPtr->font;
    }
    if (mask & GCSubwindowMode) {
        destPtr->subwindow_mode = srcPtr->subwindow_mode;
    }
    if (mask & GCGraphicsExposures) {
        destPtr->graphics_exposures = srcPtr->graphics_exposures;
    }
    if (mask & GCClipXOrigin) {
        destPtr->clip_x_origin = srcPtr->clip_x_origin;
    }
    if (mask & GCClipYOrigin) {
        destPtr->clip_y_origin = srcPtr->clip_y_origin;
    }
    if (mask & GCDashOffset) {
        destPtr->dash_offset = srcPtr->dash_offset;
    }
    if (mask & GCDashList) {
        destPtr->dashes = srcPtr->dashes;
    }
    if (mask & GCClipMask) {
        struct ClipMask {
            int type;		/* TKP_CLIP_PIXMAP or TKP_CLIP_REGION */
            Pixmap pixmap;
        } *clipPtr;

        clipPtr = (struct ClipMask *)ckalloc(sizeof(struct ClipMask));
#define TKP_CLIP_PIXMAP 0
        clipPtr->type = TKP_CLIP_PIXMAP;
        clipPtr->pixmap = srcPtr->clip_mask;
        destPtr->clip_mask = (Pixmap) clipPtr;
    }
    return (GC)destPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * Rbc_GCToPen --
 *
 *      Set up the graphics port from the given GC.
 *
 *      Geometric and cosmetic pens available under both 95 and NT.
 *      Geometric pens differ from cosmetic pens in that they can
 *        1. Draw in world units (can have thick lines: line width > 1).
 *        2. Under NT, allow arbitrary line style.
 *        3. Can have caps and join (needed for thick lines).
 *        4. Draw very, very slowly.
 *
 *      Cosmetic pens are single line width only.
 *
 *                       95   98   NT
 *        PS_SOLID      c,g  c,g  c,g
 *        PS_DASH       c,g  c,g  c,g
 *        PS_DOT          c    c  c,g
 *        PS_DASHDOT      c    -  c,g
 *        PS_DASHDOTDOT   c    -  c,g
 *        PS_USERSTYLE    -    -  c,g
 *        PS_ALTERNATE    -    -    c
 *
 *      Geometric only for 95/98
 *
 *        PS_ENDCAP_ROUND
 *        PS_ENDCAP_SQUARE
 *        PS_ENDCAP_FLAT
 *        PS_JOIN_BEVEL
 *        PS_JOIN_ROUND
 *        PS_JOIN_MITER
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      The current port is adjusted.
 *
 *----------------------------------------------------------------------
 */
HPEN
Rbc_GCToPen(dc, gc)
    HDC dc;
    GC gc;
{
    DWORD lineAttrs, lineStyle;
    DWORD dashArr[12];
    DWORD *dashPtr;
    int nValues, lineWidth;
    LOGBRUSH lBrush;
    HPEN pen;

    nValues = 0;
    lineWidth = (gc->line_width < 1) ? 1 : gc->line_width;
    if ((gc->line_style == LineOnOffDash) ||
            (gc->line_style == LineDoubleDash)) {
        XGCValuesEx *gcPtr = (XGCValuesEx *)gc;

        if ((int)gc->dashes == -1) {
            register int i;

            nValues = strlen(gcPtr->dashValues);
            for (i = 0; i < nValues; i++) {
                dashArr[i] = (DWORD)gcPtr->dashValues[i];
            }
            if (nValues == 1) {
                dashArr[1] = dashArr[0];
                nValues = 2;
            }
        } else {
            dashArr[1] = dashArr[0] = (DWORD) gc->dashes;
            nValues = 2;
            gc->dashes = -1;
        }
    }

    switch (nValues) {
        case 0:
            lineStyle = PS_SOLID;
            break;
        case 3:
            lineStyle = PS_DASHDOT;
            break;
        case 4:
            lineStyle = PS_DASHDOTDOT;
            break;
        case 2:
        default:
            /* PS_DASH style dash length is too long. */
            lineStyle = PS_DOT;
            break;
    }

    lBrush.lbStyle = BS_SOLID;
    lBrush.lbColor = gc->foreground;
    lBrush.lbHatch = 0;		/* Value is ignored when style is BS_SOLID. */

    lineAttrs = 0;
    switch (gc->cap_style) {
        case CapNotLast:
        case CapButt:
            lineAttrs |= PS_ENDCAP_FLAT;
            break;
        case CapRound:
            lineAttrs |= PS_ENDCAP_ROUND;
            break;
        default:
            lineAttrs |= PS_ENDCAP_SQUARE;
            break;
    }
    switch (gc->join_style) {
        case JoinMiter:
            lineAttrs |= PS_JOIN_MITER;
            break;
        case JoinBevel:
            lineAttrs |= PS_JOIN_BEVEL;
            break;
        case JoinRound:
        default:
            lineAttrs |= PS_JOIN_ROUND;
            break;
    }
    SetBkMode(dc, TRANSPARENT);

    if (Rbc_GetPlatformId() == VER_PLATFORM_WIN32_NT) {
        /* Windows NT/2000/XP. */
        if (nValues > 0) {
            lineStyle = PS_USERSTYLE;
            dashPtr = dashArr;
        } else {
            dashPtr = NULL;
        }
        if (lineWidth > 1) {
            /* Limit the use of geometric pens to thick lines. */
            pen = ExtCreatePen(PS_GEOMETRIC | lineAttrs | lineStyle, lineWidth,
                               &lBrush, nValues, dashPtr);
        } else {
            /* Cosmetic pens are much faster. */
            pen = ExtCreatePen(PS_COSMETIC | lineAttrs | lineStyle, 1, &lBrush,
                               nValues, dashPtr);
        }
    } else {
        /* Windows 95/98. */
        if ((lineStyle == PS_SOLID) && (lineWidth > 1)) {
            /* Use geometric pens with solid, thick lines only. */
            pen = ExtCreatePen(PS_GEOMETRIC | lineAttrs | lineStyle, lineWidth,
                               &lBrush, 0, NULL);
        } else {
            /* Otherwise sacrifice thick lines for dashes. */
            pen = ExtCreatePen(PS_COSMETIC | lineStyle, 1, &lBrush, 0, NULL);
        }
    }
    assert(pen != NULL);
    return pen;
}

/*
 *----------------------------------------------------------------------
 *
 * Rbc_EmulateXDrawRectangles --
 *
 *      Draws the outlines of the specified rectangles as if a
 *      five-point PolyLine protocol request were specified for each
 *      rectangle:
 *
 *        [x,y] [x+width,y] [x+width,y+height] [x,y+height]
 *        [x,y]
 *
 *      For the specified rectangles, these functions do not draw a
 *      pixel more than once.  XDrawRectangles draws the rectangles in
 *      the order listed in the array.  If rectangles intersect, the
 *      intersecting pixels are drawn multiple times.  Draws a
 *      rectangle.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Draws rectangles on the specified drawable.
 *
 *----------------------------------------------------------------------
 */
void
Rbc_EmulateXDrawRectangles(display, drawable, gc, rectArr, nRects)
    Display *display;
    Drawable drawable;
    GC gc;
    XRectangle *rectArr;
    int nRects;
{
    HPEN pen, oldPen;
    TkWinDCState state;
    HBRUSH brush, oldBrush;
    HDC dc;
    register XRectangle *rectPtr;
    register int i;

    if (drawable == None) {
        return;
    }
    dc = TkWinGetDrawableDC(display, drawable, &state);
    pen = Rbc_GCToPen(dc, gc);
    brush = GetStockObject(NULL_BRUSH);
    oldPen = SelectPen(dc, pen);
    oldBrush = SelectBrush(dc, brush);
    SetROP2(dc, tkpWinRopModes[gc->function]);
    rectPtr = rectArr;
    for (i = 0; i < nRects; i++, rectPtr++) {
        Rectangle(dc, (int)rectPtr->x, (int)rectPtr->y,
                  (int)(rectPtr->x + rectPtr->width + 1),
                  (int)(rectPtr->y + rectPtr->height + 1));
    }
    DeletePen(SelectPen(dc, oldPen));
    DeleteBrush(SelectBrush(dc, oldBrush));
    TkWinReleaseDrawableDC(drawable, dc, &state);
}

#ifdef notdef
/*
 * Implements the "pixeling" of small arcs, because GDI-performance
 * for this is awful
 * was made especially for rbc, graph4 demo now runs 4x faster
 *
 */
/* O-outer , I-inner, B-both */
#define NEITHER_ 0
#define OUTLINE 1
#define FILL 2
#define BOTH (OUTLINE|FILL)
#define MINIARCS 5
static int arcus0[1] = {
    BOTH
};
static int arcus1[4] = {
    BOTH, BOTH,
    BOTH, BOTH
};

static int arcus2[9] = {
    NEITHER, OUTLINE, NEITHER,
    OUTLINE, FILL, OUTLINE,
    NEITHER, OUTLINE, NEITHER
};

static int arcus3[16] = {
    NEITHER, OUTLINE, OUTLINE, NEITHER,
    OUTLINE, FILL, FILL, OUTLINE,
    OUTLINE, FILL, FILL, OUTLINE,
    NEITHER, OUTLINE, OUTLINE, NEITHER
};

static int arcus4[25] = {
    NEITHER, OUTLINE, OUTLINE, OUTLINE, NEITHER,
    OUTLINE, FILL, FILL, FILL, OUTLINE,
    OUTLINE, FILL, FILL, FILL, OUTLINE,
    OUTLINE, FILL, FILL, FILL, OUTLINE,
    NEITHER, OUTLINE, OUTLINE, OUTLINE, NEITHER
};

static int *arcis[MINIARCS] = {
    arcus0, arcus1, arcus2, arcus3, arcus4
};

static void
DrawMiniArc(
    HDC dc,
    int width,
    int x,
    int y,
    int mask,
    COLORREF inner,
    COLORREF outer)
{
    int *arc;
    int i, j;

    if (width > MINIARCS) {
        return;
    }
    arc = arcis[width];
    for (i = 0; i <= width; i++) {
        for (j = 0; j <= width; j++) {
            bit = (mask & *arc);
            if (bit & OUTLINE) {
                SetPixelV(dc, x + i, y + j, outer);
            } else if (bit & FILL) {
                SetPixelV(dc, x + i, y + j, inner);
            }
            arc++;
        }
    }
}

#endif

/*
 *----------------------------------------------------------------------
 *
 * DrawArc --
 *
 *      This procedure handles the rendering of drawn or filled
 *      arcs and chords.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Renders the requested arcs.
 *
 *----------------------------------------------------------------------
 */
static void
DrawArc(dc, arcMode, arcPtr, pen, brush)
    HDC dc;
    int arcMode; /* Mode: either ArcChord or ArcPieSlice */
    XArc *arcPtr;
    HPEN pen;
    HBRUSH brush;
{
    int start, extent, clockwise;
    int xstart, ystart, xend, yend;
    double radian_start, radian_end, xr, yr;
    double dx, dy;

    if ((arcPtr->angle1 == 0) && (arcPtr->angle2 == 23040)) {
        /* Handle special case of circle or ellipse */
        Ellipse(dc, arcPtr->x, arcPtr->y, arcPtr->x + arcPtr->width + 1,
                arcPtr->y + arcPtr->height + 1);
        return;
    }
    start = arcPtr->angle1, extent = arcPtr->angle2;
    clockwise = (extent < 0);	/* Non-zero if clockwise */

    /*
     * 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;
    }
#define XAngleToRadians(a) ((double)(a) / 64 * M_PI / 180);
    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.
     */
    dx = arcPtr->width * 0.5;
    dy = arcPtr->height * 0.5;

    xr = arcPtr->x + dx;
    yr = arcPtr->y + dy;
    xstart = (int)((xr + cos(radian_start) * dx) + 0.5);
    ystart = (int)((yr + sin(-radian_start) * dy) + 0.5);
    xend = (int)((xr + cos(radian_end) * dx) + 0.5);
    yend = (int)((yr + sin(-radian_end) * dy) + 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.
     */

    if (brush == 0) {
        /*
         * 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, arcPtr->x, arcPtr->y, arcPtr->x + arcPtr->width + 1,
            arcPtr->y + arcPtr->height + 1, xstart, ystart, xend, yend);
        /* FIXME: */
    } else {
        if (arcMode == ArcChord) {
            Chord(dc, arcPtr->x, arcPtr->y, arcPtr->x + arcPtr->width + 1,
                  arcPtr->y + arcPtr->height + 1, xstart, ystart, xend, yend);
        } else if (arcMode == ArcPieSlice) {
            Pie(dc, arcPtr->x, arcPtr->y, arcPtr->x + arcPtr->width + 1,
                arcPtr->y + arcPtr->height + 1, xstart, ystart, xend, yend);
        }
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Rbc_EmulateXDrawArcs --
 *
 *      Draws multiple circular or elliptical arcs.  Each arc is
 *      specified by a rectangle and two angles.  The center of the
 *      circle or ellipse is the center of the rect- angle, and the
 *      major and minor axes are specified by the width and height.
 *      Positive angles indicate counterclock- wise motion, and
 *      negative angles indicate clockwise motion.  If the magnitude
 *      of angle2 is greater than 360 degrees, XDrawArcs truncates it
 *      to 360 degrees.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Draws an arc for each array element on the specified drawable.
 *
 *----------------------------------------------------------------------
 */
void
Rbc_EmulateXDrawArcs(display, drawable, gc, arcArr, nArcs)
    Display *display;
    Drawable drawable;
    GC gc;
    XArc *arcArr;
    int nArcs;
{
    HPEN pen, oldPen;
    HBRUSH brush, oldBrush;
    HDC dc;
    TkWinDCState state;
    register XArc *arcPtr, *endPtr;

    LastKnownRequestProcessed(display) = NextRequest(display);
    if (drawable == None) {
        return;
    }
    dc = TkWinGetDrawableDC(display, drawable, &state);
    SetROP2(dc, tkpWinRopModes[gc->function]);
    pen = Rbc_GCToPen(dc, gc);
    oldPen = SelectPen(dc, pen);
    brush = GetStockBrush(NULL_BRUSH);
    oldBrush = SelectBrush(dc, brush);
    endPtr = arcArr + nArcs;
    for (arcPtr = arcArr; arcPtr < endPtr; arcPtr++) {
        DrawArc(dc, gc->arc_mode, arcPtr, pen, 0);
    }
    DeleteBrush(SelectBrush(dc, oldBrush));
    DeletePen(SelectPen(dc, oldPen));
    TkWinReleaseDrawableDC(drawable, dc, &state);
}

/*
 *----------------------------------------------------------------------
 *
 * Rbc_EmulateXFillArcs --
 *
 *      Draw a filled arc.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Draws a filled arc for each array element on the specified drawable.
 *
 *----------------------------------------------------------------------
 */
void
Rbc_EmulateXFillArcs(display, drawable, gc, arcArr, nArcs)
    Display *display;
    Drawable drawable;
    GC gc;
    XArc *arcArr;
    int nArcs;
{
    HBRUSH brush, oldBrush;
    HPEN pen, oldPen;
    HDC dc;
    register XArc *arcPtr, *endPtr;
    TkWinDCState state;

    LastKnownRequestProcessed(display) = NextRequest(display);
    if (drawable == None) {
        return;
    }
    dc = TkWinGetDrawableDC(display, drawable, &state);
    SetROP2(dc, tkpWinRopModes[gc->function]);
    pen = Rbc_GCToPen(dc, gc);
    oldPen = SelectPen(dc, pen);
    brush = CreateSolidBrush(gc->foreground);
    oldBrush = SelectBrush(dc, brush);
    endPtr = arcArr + nArcs;
    for (arcPtr = arcArr; arcPtr < endPtr; arcPtr++) {
        DrawArc(dc, gc->arc_mode, arcPtr, pen, brush);
    }
    DeleteBrush(SelectBrush(dc, oldBrush));
    DeletePen(SelectPen(dc, oldPen));
    TkWinReleaseDrawableDC(drawable, dc, &state);
}

/*
 *----------------------------------------------------------------------
 *
 * DrawDot --
 *
 *      Draws a dot.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Renders a dot.
 *
 *----------------------------------------------------------------------
 */
static void CALLBACK
DrawDot(x, y, clientData)
    int x; /* y-coordinates of point */
    int y; /* y-coordinates of point */
    LPARAM clientData;
{				/* Line information */
    DashInfo *infoPtr = (DashInfo *) clientData;
    int count;

    infoPtr->count++;
    count = (infoPtr->count + infoPtr->offset) / infoPtr->nBits;
    if (count & 0x1) {
        SetPixelV(infoPtr->dc, x, y, infoPtr->color);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Rbc_EmulateXDrawLine --
 *
 *      Draw connected lines.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Renders a series of connected lines.
 *
 *----------------------------------------------------------------------
 */
void
Rbc_EmulateXDrawLine(display, drawable, gc, x1, y1, x2, y2)
    Display *display;
    Drawable drawable;
    GC gc;
    int x1;
    int y1;
    int x2;
    int y2;
{
    TkWinDCState state;
    HDC dc;

    if (drawable == None) {
        return;
    }
    dc = TkWinGetDrawableDC(display, drawable, &state);
    SetROP2(dc, tkpWinRopModes[gc->function]);
    if (gc->line_style != LineSolid) {
        /* Handle dotted lines specially */
        DashInfo info;

        if (!GetDashInfo(dc, gc, &info)) {
            goto solidLine;
        }
        LineDDA(x1, y1, x2, y2, DrawDot, (LPARAM)&info);
    } else {
        HPEN pen, oldPen;
        HBRUSH brush, oldBrush;

solidLine:
        pen = Rbc_GCToPen(dc, gc);
        oldPen = SelectPen(dc, pen);
        brush = CreateSolidBrush(gc->foreground);
        oldBrush = SelectBrush(dc, brush);
        MoveToEx(dc, x1, y1, (LPPOINT)NULL);
        LineTo(dc, x2, y2);
        DeletePen(SelectPen(dc, oldPen));
        DeleteBrush(SelectBrush(dc, oldBrush));
    }
    TkWinReleaseDrawableDC(drawable, dc, &state);
}

/*
 *----------------------------------------------------------------------
 *
 * DrawLine --
 *
 *      Draw connected lines.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Renders a series of connected lines.
 *
 *----------------------------------------------------------------------
 */
static void
DrawLine(display, drawable, gc, points, nPoints)
    Display *display;
    Drawable drawable;
    GC gc;
    POINT *points;
    int nPoints;
{
    TkWinDCState state;
    HDC dc;
    register int i, n;
    int start, extra, size;
    HPEN pen, oldPen;
    HBRUSH brush, oldBrush;

    if (drawable == None) {
        return;
    }
    dc = TkWinGetDrawableDC(display, drawable, &state);
    pen = Rbc_GCToPen(dc, gc);
    oldPen = SelectPen(dc, pen);
    brush = CreateSolidBrush(gc->foreground);
    oldBrush = SelectBrush(dc, brush);
    SetROP2(dc, tkpWinRopModes[gc->function]);

    start = extra = 0;
    /*
     * Depending if the line is wide (> 1 pixel), arbitrarily break
     * the line in sections of 100 points.  This bit of weirdness has
     * to do with wide geometric pens.  The longer the polyline, the
     * slower it draws.  The trade off is that we lose dash and
     * cap uniformity for unbearably slow polyline draws.
     */
    if (gc->line_width > 1) {
        size = 100;
    } else {
        size = nPoints;
    }
    for (i = nPoints; i > 0; i -= size) {
        n = MIN(i, size);
        Polyline(dc, points + start, n + extra);
        start += (n - 1);
        extra = 1;
    }
    DeletePen(SelectPen(dc, oldPen));
    DeleteBrush(SelectBrush(dc, oldBrush));
    TkWinReleaseDrawableDC(drawable, dc, &state);
}

/*
 *--------------------------------------------------------------
 *
 * Rbc_EmulateXDrawLines --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
void
Rbc_EmulateXDrawLines(display, drawable, gc, pointArr, nPoints, mode)
    Display *display;
    Drawable drawable;
    GC gc;
    XPoint *pointArr;
    int nPoints;
    int mode;
{
    if (drawable == None) {
        return;
    }
    if (gc->line_style != LineSolid) { /* Handle dotted lines specially */
        DashInfo info;
        TkWinDCState state;
        HDC dc;
        int result;

        dc = TkWinGetDrawableDC(display, drawable, &state);
        SetROP2(dc, tkpWinRopModes[gc->function]);
        result = GetDashInfo(dc, gc, &info);
        if (result) {
            register XPoint *p1, *p2;
            register int i;

            p1 = pointArr;
            p2 = p1 + 1;
            for (i = 1; i < nPoints; i++, p1++, p2++) {
                LineDDA(p1->x, p1->y, p2->x, p2->y, DrawDot, (LPARAM)&info);
            }
            result = TCL_OK;
        }
        TkWinReleaseDrawableDC(drawable, dc, &state);
        if (result) {
            return;
        }
    } else {
        POINT *points, *destPtr;
        XPoint *srcPtr, *endPtr;

        points = (POINT *)ckalloc(sizeof(POINT) * nPoints);
        if (points == NULL) {
            return;
        }
        endPtr = pointArr + nPoints;
        if (mode == CoordModeOrigin) {
            destPtr = points;
            for (srcPtr = pointArr; srcPtr < endPtr; srcPtr++) {
                destPtr->x = (int)srcPtr->x;
                destPtr->y = (int)srcPtr->y;
                destPtr++;
            }
        } else {
            POINT *lastPtr;

            srcPtr = pointArr;
            destPtr = points;
            destPtr->x = (int)srcPtr->x;
            destPtr->y = (int)srcPtr->y;
            lastPtr = destPtr;
            srcPtr++, destPtr++;
            for (/*empty*/; srcPtr < endPtr; srcPtr++) {
                destPtr->x = lastPtr->x + (int)srcPtr->x;
                destPtr->y = lastPtr->y + (int)srcPtr->y;
                lastPtr = destPtr;
                destPtr++;
            }
        }
        DrawLine(display, drawable, gc, points, nPoints);
        ckfree((char *)points);
    }
}



/*
 *----------------------------------------------------------------------
 *
 * Rbc_EmultateXDrawSegments --
 *
 *      Draws multiple, unconnected lines. For each segment, draws a
 *      line between (x1, y1) and (x2, y2).  It draws the lines in the
 *      order listed in the array of XSegment structures and does not
 *      perform joining at coincident endpoints.  For any given line,
 *      does not draw a pixel more than once. If lines intersect, the
 *      intersecting pixels are drawn multiple times.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Draws unconnected line segments on the specified drawable.
 *
 *----------------------------------------------------------------------
 */
void
Rbc_EmulateXDrawSegments(display, drawable, gc, segArr, nSegments)
    Display *display;
    Drawable drawable;
    GC gc;
    XSegment *segArr;
    int nSegments;
{
    HDC dc;
    register XSegment *segPtr, *endPtr;
    TkWinDCState state;

    LastKnownRequestProcessed(display) = NextRequest(display);
    if (drawable == None) {
        return;
    }
    dc = TkWinGetDrawableDC(display, drawable, &state);
    SetROP2(dc, tkpWinRopModes[gc->function]);
    if (gc->line_style != LineSolid) {
        /* Handle dotted lines specially */
        DashInfo info;

        if (!GetDashInfo(dc, gc, &info)) {
            goto solidLine;
        }
        endPtr = segArr + nSegments;
        for (segPtr = segArr; segPtr < endPtr; segPtr++) {
            info.count = 0; /* Reset dash counter after every segment. */
            LineDDA(segPtr->x1, segPtr->y1, segPtr->x2, segPtr->y2, DrawDot,
                    (LPARAM)&info);
        }
    } else {
        HPEN pen, oldPen;

solidLine:
        pen = Rbc_GCToPen(dc, gc);
        oldPen = SelectPen(dc, pen);
        endPtr = segArr + nSegments;
        for (segPtr = segArr; segPtr < endPtr; segPtr++) {
            MoveToEx(dc, segPtr->x1, segPtr->y1, (LPPOINT)NULL);
            LineTo(dc, segPtr->x2, segPtr->y2);
        }
        DeletePen(SelectPen(dc, oldPen));
    }
    TkWinReleaseDrawableDC(drawable, dc, &state);
}

/*
 *----------------------------------------------------------------------
 *
 * Rbc_EmultateXDrawRectangle --
 *
 *      Draws the outlines of the specified rectangle as if a
 *      five-point PolyLine protocol request were specified for each
 *      rectangle:
 *
 *        [x,y] [x+width,y] [x+width,y+height] [x,y+height]
 *        [x,y]
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Draws a rectangle on the specified drawable.
 *
 *----------------------------------------------------------------------
 */
void
Rbc_EmulateXDrawRectangle(display, drawable, gc, x, y, width, height)
    Display *display;
    Drawable drawable;
    GC gc;
    int x;
    int y;
    unsigned int width;
    unsigned int height;
{
    TkWinDCState state;
    HPEN pen, oldPen;
    HBRUSH brush, oldBrush;
    HDC dc;

    if (drawable == None) {
        return;
    }
    dc = TkWinGetDrawableDC(display, drawable, &state);
    pen = Rbc_GCToPen(dc, gc);
    brush = GetStockObject(NULL_BRUSH);
    oldPen = SelectPen(dc, pen);
    oldBrush = SelectBrush(dc, brush);
    SetROP2(dc, tkpWinRopModes[gc->function]);
    if (gc->line_style != LineSolid) {
        /* Handle dotted lines specially */
        register int x2, y2;
        DashInfo info;

        if (!GetDashInfo(dc, gc, &info)) {
            goto solidLine;
        }
        x2 = x + width;
        y2 = y + height;
        LineDDA(x, y, x2, y, DrawDot, (LPARAM)&info);
        LineDDA(x2, y, x2, y2, DrawDot, (LPARAM)&info);
        LineDDA(x2, y2, x, y2, DrawDot, (LPARAM)&info);
        LineDDA(x, y2, x, y, DrawDot, (LPARAM)&info);
    } else {
solidLine:
        Rectangle(dc, x, y, x + width + 1, y + height + 1);
    }
    DeletePen(SelectPen(dc, oldPen));
    DeleteBrush(SelectBrush(dc, oldBrush));
    TkWinReleaseDrawableDC(drawable, dc, &state);
}

/*
 *----------------------------------------------------------------------
 *
 * Rbc_EmulateXDrawPoints --
 *
 *      Uses the foreground pixel and function components of the GC to
 *      draw a multiple points into the specified drawable.
 *      CoordModeOrigin treats all coordinates as relative to the
 *      origin, and CoordModePrevious treats all coordinates after
 *      the first as relative to the previous point.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Draws points on the specified drawable.
 *
 *----------------------------------------------------------------------
 */
void
Rbc_EmulateXDrawPoints(display, drawable, gc, pointArr, nPoints, mode)
    Display *display;
    Drawable drawable;
    GC gc;
    XPoint *pointArr;
    int nPoints;
    int mode;
{				/* Ignored. CoordModeOrigin is assumed. */
    HDC dc;
    register XPoint *pointPtr, *endPtr;
    TkWinDCState state;

    LastKnownRequestProcessed(display) = NextRequest(display);
    if (drawable == None) {
        return;
    }
    dc = TkWinGetDrawableDC(display, drawable, &state);
    SetROP2(dc, tkpWinRopModes[gc->function]);
    endPtr = pointArr + nPoints;
    for (pointPtr = pointArr; pointPtr < endPtr; pointPtr++) {
        SetPixelV(dc, pointPtr->x, pointPtr->y, gc->foreground);
    }
    TkWinReleaseDrawableDC(drawable, dc, &state);
}

/*
 *----------------------------------------------------------------------
 *
 * Rbc_EmultateXReparentWindow --
 *
 *      If the specified window is mapped, automatically performs an
 *      UnmapWindow request on it, removes it from its current
 *      position in the hierarchy, and inserts it as the child of the
 *      specified parent.  The window is placed in the stacking order
 *      on top with respect to sibling windows.
 *
 *      Note: In WIN32 you can't reparent to/from another application.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Reparents the specified window.
 *
 *----------------------------------------------------------------------
 */
void
Rbc_EmulateXReparentWindow(display, window, parent, x, y)
    Display *display;
    Window window;
    Window parent;
    int x;
    int y;
{
    HWND child, newParent;

    child = Tk_GetHWND(window);
    newParent = Tk_GetHWND(parent);
    SetParent(child, newParent);
    SetWindowLong(child, GWL_STYLE, WS_CHILD | WS_CLIPCHILDREN |
                  WS_CLIPSIBLINGS);

    XMoveWindow(display, window, x, y);
    XRaiseWindow(display, window);
    XMapWindow(display, window);
}

/*
 *--------------------------------------------------------------
 *
 * Rbc_EmulateXSetDashes --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
void
Rbc_EmulateXSetDashes(display, gc, dashOffset, dashList, n)
    Display *display;
    GC gc;
    int dashOffset;
    _Xconst char *dashList;
    int n;
{
    gc->dashes = (unsigned char)strlen(dashList);
    gc->dash_offset = (int)dashList;
}

/*
 *----------------------------------------------------------------------
 *
 * Rbc_EmultateXDrawString --
 *
 *      Draw a single string in the current font.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Renders the specified string in the drawable.
 *
 *----------------------------------------------------------------------
 */
void
Rbc_EmulateXDrawString(display, drawable, gc, x, y, string, length)
    Display *display;
    Drawable drawable;
    GC gc;
    int x;
    int y;
    _Xconst char *string;
    int length;
{
    if (drawable == None) {
        return;
    }
    Tk_DrawChars(display, drawable, gc, (Tk_Font)gc->font, string, length,
                 x, y);
}

/*
 *--------------------------------------------------------------
 *
 * TileArea --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
static void
TileArea(destDC, srcDC, tileOriginX, tileOriginY, tileWidth, tileHeight,
         x, y, width, height)
      HDC destDC, srcDC;
      int tileOriginX;
      int tileOriginY;
      int tileWidth;
      int tileHeight;
      int x;
      int y;
      int width;
      int height;
{
    int destX, destY;
    int destWidth, destHeight;
    int srcX, srcY;
    int xOrigin, yOrigin;
    int delta;
    int left, top, right, bottom;

    xOrigin = x, yOrigin = y;
    if (x < tileOriginX) {
        delta = (tileOriginX - x) % tileWidth;
        if (delta > 0) {
            xOrigin -= (tileWidth - delta);
        }
    } else if (x > tileOriginX) {
        delta = (x - tileOriginX) % tileWidth;
        if (delta > 0) {
            xOrigin -= delta;
        }
    }
    if (y < tileOriginY) {
        delta = (tileOriginY - y) % tileHeight;
        if (delta > 0) {
            yOrigin -= (tileHeight - delta);
        }
    } else if (y >= tileOriginY) {
        delta = (y - tileOriginY) % tileHeight;
        if (delta > 0) {
            yOrigin -= delta;
        }
    }
#ifdef notdef
    PurifyPrintf("tile is (%d,%d,%d,%d)\n", tileOriginX, tileOriginY,
                 tileWidth, tileHeight);
    PurifyPrintf("region is (%d,%d,%d,%d)\n", x, y, width, height);
    PurifyPrintf("starting at %d,%d\n", xOrigin, yOrigin);
#endif
    left = x;
    right = x + width;
    top = y;
    bottom = y + height;
    for (y = yOrigin; y < bottom; y += tileHeight) {
        srcY = 0;
        destY = y;
        destHeight = tileHeight;
        if (y < top) {
            srcY = (top - y);
            destHeight = tileHeight - srcY;
            destY = top;
        }
        if ((destY + destHeight) > bottom) {
            destHeight = (bottom - destY);
        }
        for (x = xOrigin; x < right; x += tileWidth) {
            srcX = 0;
            destX = x;
            destWidth = tileWidth;
            if (x < left) {
                srcX = (left - x);
                destWidth = tileWidth - srcX;
                destX = left;
            }
            if ((destX + destWidth) > right) {
                destWidth = (right - destX);
            }
#ifdef notdef
            PurifyPrintf("drawing pattern (%d,%d,%d,%d) at %d,%d\n",
                         srcX , srcY, destWidth, destHeight, destX, destY);
#endif
            BitBlt(destDC, destX, destY, destWidth, destHeight,
                   srcDC, srcX, srcY, SRCCOPY);
        }
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Rbc_EmulateXFillRectangles --
 *
 *      Fill multiple rectangular areas in the given drawable.
 *      Handles tiling.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Draws onto the specified drawable.
 *
 *----------------------------------------------------------------------
 */
void
Rbc_EmulateXFillRectangles(display, drawable, gc, rectArr, nRectangles)
    Display *display;
    Drawable drawable;
    GC gc;
    XRectangle *rectArr;
    int nRectangles;
{
    BITMAP bm;
    HBITMAP oldBitmap, hBitmap;
    HBRUSH oldBrush, hFgBrush, hBgBrush, hBrush;
    HDC hDC;
    HDC memDC;
    RECT rect;
    TkWinDCState state;
    TkWinDrawable *twdPtr;
    register XRectangle *rectPtr, *endPtr;

    if (drawable == None) {
        return;
    }
    hDC = TkWinGetDrawableDC(display, drawable, &state);
    SetROP2(hDC, tkpWinRopModes[gc->function]);

    switch (gc->fill_style) {
        case FillTiled:
            if (gc->tile == None) {
                goto fillSolid;
            }
#ifdef notdef
            if ((GetDeviceCaps(hDC, RASTERCAPS) & RC_BITBLT) == 0) {
                goto fillSolid;
            }
#endif
            twdPtr = (TkWinDrawable *)gc->tile;
            GetObject(twdPtr->bitmap.handle, sizeof(BITMAP), &bm);
            memDC = CreateCompatibleDC(hDC);
            oldBitmap = SelectBitmap(memDC, twdPtr->bitmap.handle);
            endPtr = rectArr + nRectangles;
            for (rectPtr = rectArr; rectPtr < endPtr; rectPtr++) {
                TileArea(hDC, memDC, gc->ts_x_origin, gc->ts_y_origin, bm.bmWidth,
                         bm.bmHeight, (int)rectPtr->x, (int)rectPtr->y,
                         (int)rectPtr->width, (int)rectPtr->height);
            }
            SelectBitmap(memDC, oldBitmap);
            DeleteDC(memDC);
            break;

        case FillOpaqueStippled:
        case FillStippled:
            if (gc->stipple == None) {
                goto fillSolid;
            }
            twdPtr = (TkWinDrawable *)gc->stipple;
            if (twdPtr->type != TWD_BITMAP) {
                panic("unexpected drawable type in stipple");
            }
            hBrush = CreatePatternBrush(twdPtr->bitmap.handle);
            SetBrushOrgEx(hDC, gc->ts_x_origin, gc->ts_y_origin, NULL);
            oldBrush = SelectBrush(hDC, hBrush);
            memDC = CreateCompatibleDC(hDC);

            /*
             * 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.
             */
            hFgBrush = CreateSolidBrush(gc->foreground);
            hBgBrush = CreateSolidBrush(gc->background);
            endPtr = rectArr + nRectangles;
            for (rectPtr = rectArr; rectPtr < endPtr; rectPtr++) {
                hBitmap = CreateCompatibleBitmap(hDC, rectPtr->width,
                                                 rectPtr->height);
                oldBitmap = SelectObject(memDC, hBitmap);
                rect.left = rect.top = 0;
                rect.right = rectPtr->width;
                rect.bottom = rectPtr->height;
                FillRect(memDC, &rect, hFgBrush);
                BitBlt(hDC, rectPtr->x, rectPtr->y, rectPtr->width, rectPtr->height,
                       memDC, 0, 0, COPYBG);
                if (gc->fill_style == FillOpaqueStippled) {
                    FillRect(memDC, &rect, hBgBrush);
                    BitBlt(hDC, rectPtr->x, rectPtr->y, rectPtr->width,
                           rectPtr->height, memDC, 0, 0, COPYFG);
                }
                SelectObject(memDC, oldBitmap);
                DeleteObject(hBitmap);
            }
            DeleteBrush(hFgBrush);
            DeleteBrush(hBgBrush);
            DeleteDC(memDC);
            SelectBrush(hDC, oldBrush);
            DeleteBrush(hBrush);
            break;

        case FillSolid:
fillSolid:
            memDC = CreateCompatibleDC(hDC);
            hFgBrush = CreateSolidBrush(gc->foreground);
            endPtr = rectArr + nRectangles;
            for (rectPtr = rectArr; rectPtr < endPtr; rectPtr++) {
                hBitmap = CreateCompatibleBitmap(hDC, rectPtr->width,
                                                 rectPtr->height);
                oldBitmap = SelectObject(memDC, hBitmap);
                rect.left = rect.top = 0;
                rect.right = rectPtr->width;
                rect.bottom = rectPtr->height;
                FillRect(memDC, &rect, hFgBrush);
                BitBlt(hDC, rectPtr->x, rectPtr->y, rectPtr->width, rectPtr->height,
                       memDC, 0, 0, SRCCOPY);
                SelectObject(memDC, oldBitmap);
                DeleteObject(hBitmap);
            }
            DeleteBrush(hFgBrush);
            DeleteDC(memDC);
            break;
    }
    TkWinReleaseDrawableDC(drawable, hDC, &state);
}

/*
 *--------------------------------------------------------------
 *
 * Rbc_EmulateXFillRectangle --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
void
Rbc_EmulateXFillRectangle(display, drawable, gc, x, y, width, height)
    Display *display;
    Drawable drawable;
    GC gc;
    int x;
    int y;
    unsigned int width;
    unsigned int height;
{
    HDC hDC;
    RECT rect;
    TkWinDCState state;

    if (drawable == None) {
        return;
    }
    hDC = TkWinGetDrawableDC(display, drawable, &state);
    SetROP2(hDC, tkpWinRopModes[gc->function]);
    rect.left = rect.top = 0;
    rect.right = width;
    rect.bottom = height;

    switch (gc->fill_style) {
        case FillTiled: {
                TkWinDrawable *twdPtr;
                HBITMAP oldBitmap;
                HDC memDC;
                BITMAP bm;

                if (gc->tile == None) {
                    goto fillSolid;
                }
#ifdef notdef
                if ((GetDeviceCaps(hDC, RASTERCAPS) & RC_BITBLT) == 0) {
                    goto fillSolid;
                }
#endif
                twdPtr = (TkWinDrawable *)gc->tile;
                /* The tiling routine needs to know the size of the bitmap */
                GetObject(twdPtr->bitmap.handle, sizeof(BITMAP), &bm);

                memDC = CreateCompatibleDC(hDC);
                oldBitmap = SelectBitmap(memDC, twdPtr->bitmap.handle);
                TileArea(hDC, memDC, gc->ts_x_origin, gc->ts_y_origin, bm.bmWidth,
                         bm.bmHeight, x, y, width, height);
                SelectBitmap(memDC, oldBitmap);
                DeleteDC(memDC);
            }
            break;

        case FillOpaqueStippled:
        case FillStippled: {
                TkWinDrawable *twdPtr;
                HBRUSH oldBrush, hBrush;
                HBRUSH hBrushFg, hBrushBg;
                HBITMAP oldBitmap, hBitmap;
                HDC memDC;

                if (gc->stipple == None) {
                    goto fillSolid;
                }
                twdPtr = (TkWinDrawable *)gc->stipple;
                if (twdPtr->type != TWD_BITMAP) {
                    panic("unexpected drawable type in stipple");
                }
                hBrush = CreatePatternBrush(twdPtr->bitmap.handle);
                SetBrushOrgEx(hDC, gc->ts_x_origin, gc->ts_y_origin, NULL);
                oldBrush = SelectBrush(hDC, hBrush);
                memDC = CreateCompatibleDC(hDC);

                hBrushFg = CreateSolidBrush(gc->foreground);
                hBrushBg = CreateSolidBrush(gc->background);
                hBitmap = CreateCompatibleBitmap(hDC, width, height);
                oldBitmap = SelectObject(memDC, hBitmap);
                FillRect(memDC, &rect, hBrushFg);
                SetBkMode(hDC, TRANSPARENT);
                BitBlt(hDC, x, y, width, height, memDC, 0, 0, COPYFG);
                if (gc->fill_style == FillOpaqueStippled) {
                    FillRect(memDC, &rect, hBrushBg);
                    BitBlt(hDC, x, y, width, height, memDC, 0, 0, COPYBG);
                }
                SelectBrush(hDC, oldBrush);
                SelectBitmap(memDC, oldBitmap);
                DeleteBrush(hBrushFg);
                DeleteBrush(hBrushBg);
                DeleteBrush(hBrush);
                DeleteBitmap(hBitmap);
                DeleteDC(memDC);
            }
            break;

        case FillSolid: {
                HBRUSH hBrush;
                HBITMAP oldBitmap, hBitmap;
                HDC memDC;

fillSolid:
                /* TkWinFillRect(hDC, x, y, width, height, gc->foreground);  */
                memDC = CreateCompatibleDC(hDC);
                hBrush = CreateSolidBrush(gc->foreground);
                hBitmap = CreateCompatibleBitmap(hDC, width, height);
                oldBitmap = SelectBitmap(memDC, hBitmap);
                rect.left = rect.top = 0;
                rect.right = width;
                rect.bottom = height;
                FillRect(memDC, &rect, hBrush);
                BitBlt(hDC, x, y, width, height, memDC, 0, 0, SRCCOPY);
                SelectObject(memDC, oldBitmap);
                DeleteBitmap(hBitmap);
                DeleteBrush(hBrush);
                DeleteDC(memDC);
            }
            break;
    }
    TkWinReleaseDrawableDC(drawable, hDC, &state);
}

/*
 *--------------------------------------------------------------
 *
 * DrawChars --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
static BOOL
DrawChars(dc, x, y, string, length)
    HDC dc;
    int x;
    int y;
    char *string;
    int length;
{
    BOOL result;

    if (systemEncoding == NULL) {
        result = TextOutA(dc, x, y, string, length);
    } else {
        const unsigned short *wstring;
        Tcl_DString dString;

        Tcl_DStringInit(&dString);
        Tcl_UtfToExternalDString(systemEncoding, string, length, &dString);
        length = Tcl_NumUtfChars(string, -1);
        wstring = (const unsigned short *)Tcl_DStringValue(&dString);
        result = TextOutW(dc, x, y, wstring, length);
        Tcl_DStringFree(&dString);
    }
    return result;
}

/*
 *--------------------------------------------------------------
 *
 * Rbc_DrawRotatedText --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
int
Rbc_DrawRotatedText(display, drawable, x, y, theta, tsPtr, textPtr)
    Display *display;
    Drawable drawable;
    int x;
    int y;
    double theta;
    TextStyle *tsPtr;
    TextLayout *textPtr;
{
    HFONT hFont, oldFont;
    TkWinDCState state;
    HDC hDC;
    int isActive;
    int bbWidth, bbHeight;
    double rotWidth, rotHeight;
    double sinTheta, cosTheta;
    Point2D p, q, center;
    register TextFragment *fragPtr, *endPtr;
    static int initialized = 0;

    if (!initialized) {
        if (Rbc_GetPlatformId() == VER_PLATFORM_WIN32_NT) {
            /*
             * If running NT, then we will be calling some Unicode functions
             * explictly.  So, even if the Tcl system encoding isn't Unicode,
             * make sure we convert to/from the Unicode char set.
             */
            systemEncoding = Tcl_GetEncoding(NULL, "unicode");
        }
        initialized = 1;
    }
    hFont = CreateRotatedFont(tsPtr->gc->font, theta);
    if (hFont == NULL) {
        return FALSE;
    }
    isActive = (tsPtr->state & STATE_ACTIVE);
    hDC = TkWinGetDrawableDC(display, drawable, &state);
    SetROP2(hDC, tsPtr->gc->function);
    oldFont = SelectFont(hDC, hFont);

    Rbc_GetBoundingBox(textPtr->width, textPtr->height, theta, &rotWidth,
                       &rotHeight, (Point2D *)NULL);
    bbWidth = ROUND(rotWidth);
    bbHeight = ROUND(rotHeight);
    Rbc_TranslateAnchor(x, y, bbWidth, bbHeight, tsPtr->anchor, &x, &y);
    center.x = (double)textPtr->width * -0.5;
    center.y = (double)textPtr->height * -0.5;
    theta = (-theta / 180.0) * M_PI;
    sinTheta = sin(theta), cosTheta = cos(theta);

    endPtr = textPtr->fragArr + textPtr->nFrags;

    for (fragPtr = textPtr->fragArr; fragPtr < endPtr; fragPtr++) {
        p.x = center.x + (double)fragPtr->x;
        p.y = center.y + (double)fragPtr->y;
        q.x = x + (p.x * cosTheta) - (p.y * sinTheta) + (bbWidth * 0.5);
        q.y = y + (p.x * sinTheta) + (p.y * cosTheta) + (bbHeight * 0.5);
        fragPtr->sx = ROUND(q.x);
        fragPtr->sy = ROUND(q.y);
    }
    SetBkMode(hDC, TRANSPARENT);
    SetTextAlign(hDC, TA_LEFT | TA_BASELINE);

    if (tsPtr->state & (STATE_DISABLED | STATE_EMPHASIS)) {
        TkBorder *borderPtr = (TkBorder *) tsPtr->border;
        XColor *color1, *color2;

        color1 = borderPtr->lightColor, color2 = borderPtr->darkColor;
        if (tsPtr->state & STATE_EMPHASIS) {
            XColor *hold;

            hold = color1, color1 = color2, color2 = hold;
        }
        if (color1 != NULL) {
            SetTextColor(hDC, color1->pixel);
            for (fragPtr = textPtr->fragArr; fragPtr < endPtr; fragPtr++) {
                DrawChars(hDC, fragPtr->sx, fragPtr->sy, fragPtr->text,
                          fragPtr->count);
            }
        }
        if (color2 != NULL) {
            SetTextColor(hDC, color2->pixel);
            for (fragPtr = textPtr->fragArr; fragPtr < endPtr; fragPtr++) {
                DrawChars(hDC, fragPtr->sx + 1, fragPtr->sy + 1, fragPtr->text,
                          fragPtr->count);
            }
        }
        goto done;		/* Done */
    }
    SetBkMode(hDC, TRANSPARENT);
    if ((tsPtr->shadow.offset > 0) && (tsPtr->shadow.color != NULL)) {
        SetTextColor(hDC, tsPtr->shadow.color->pixel);
        for (fragPtr = textPtr->fragArr; fragPtr < endPtr; fragPtr++) {
            DrawChars(hDC, fragPtr->sx + tsPtr->shadow.offset,
                      fragPtr->sy + tsPtr->shadow.offset, fragPtr->text,
                      fragPtr->count);
        }
    }
    if (isActive) {
        SetTextColor(hDC, tsPtr->activeColor->pixel);
    } else {
        SetTextColor(hDC, tsPtr->color->pixel);
    }

    for (fragPtr = textPtr->fragArr; fragPtr < endPtr; fragPtr++) {
        DrawChars(hDC, fragPtr->sx, fragPtr->sy, fragPtr->text,
                  fragPtr->count);
    }

    if (isActive) {
        SetTextColor(hDC, tsPtr->color->pixel);
    }
done:
    SelectFont(hDC, oldFont);
    DeleteFont(hFont);
    TkWinReleaseDrawableDC(drawable, hDC, &state);
    return TRUE;
}

/*
 *--------------------------------------------------------------
 *
 * DrawPixel --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
static void
DrawPixel(hDC, x, y, color)
    HDC hDC;
    int x;
    int y;
    COLORREF color;
{
    HDC memDC;
    HBRUSH hBrushFg;
    HBITMAP oldBitmap, hBitmap;
    RECT rect;
    int size;

    size = 1;
    memDC = CreateCompatibleDC(hDC);
    hBrushFg = CreateSolidBrush(color);
    hBitmap = CreateCompatibleBitmap(hDC, size, size);
    oldBitmap = SelectObject(memDC, hBitmap);
    rect.left = rect.top = 0;
    rect.right = rect.bottom = size;
    FillRect(memDC, &rect, hBrushFg);
    BitBlt(hDC, x, y, size, size, memDC, 0, 0, SRCCOPY);
    SelectObject(memDC, oldBitmap);
    DeleteObject(hBitmap);
    DeleteBrush(hBrushFg);
    DeleteDC(memDC);
}

/*
 *----------------------------------------------------------------------
 *
 * PixelateBitmap --
 *
 *      Draws a masked bitmap in given device (should be printer)
 *      context.  Bit operations on print devices usually fail because
 *      there's no way to read back from the device surface to get the
 *      previous pixel values, rendering BitBlt useless. The bandaid
 *      solution here is to draw 1x1 pixel rectangles at each
 *      coordinate as directed by the the mask and source bitmaps.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Draws onto the specified drawable.
 *
 *----------------------------------------------------------------------
 */
static void
PixelateBitmap(display, drawable, srcBitmap, maskBitmap, width, height, gc, destX, destY)
    Display *display;
    Drawable drawable;
    Pixmap srcBitmap;
    Pixmap maskBitmap;
    int width;
    int height;
    GC gc;
    int destX;
    int destY;
{
    register int x, y;
    register int dx, dy;
    int pixel;
    unsigned char *srcBits;
    register unsigned char *srcPtr;
    int bitPos, bytesPerRow;
    HDC hDC;
    TkWinDCState state;

    srcBits = Rbc_GetBitmapData(display, srcBitmap, width, height,
                                &bytesPerRow);
    if (srcBits == NULL) {
        return;
    }
    hDC = TkWinGetDrawableDC(display, drawable, &state);
    if (maskBitmap != None) {
        register unsigned char *maskPtr;
        unsigned char *maskBits;
        maskBits = Rbc_GetBitmapData(display, maskBitmap, width, height,
                                     &bytesPerRow);
        bytesPerRow = ((width + 31) & ~31) / 8;
        for (dy = destY, y = height - 1; y >= 0; y--, dy++) {
            maskPtr = maskBits + (bytesPerRow * y);
            srcPtr = srcBits + (bytesPerRow * y);
            for (dx = destX, x = 0; x < width; x++, dx++) {
                bitPos = x % 8;
                pixel = (*maskPtr & (0x80 >> bitPos));
                if (pixel) {
                    pixel = (*srcPtr & (0x80 >> bitPos));
                    DrawPixel(hDC, dx, dy,
                              (pixel) ? gc->foreground : gc->background);
                }
                if (bitPos == 7) {
                    srcPtr++, maskPtr++;
                }
            }			/* x */
        }
        ckfree((char *)maskBits);
    } else {
        bytesPerRow = ((width + 31) & ~31) / 8;
        for (dy = destY, y = height - 1; y >= 0; y--, dy++) {
            srcPtr = srcBits + (bytesPerRow * y);
            for (dx = destX, x = 0; x < width; x++, dx++) {
                bitPos = x % 8;
                pixel = (*srcPtr & (0x80 >> bitPos));
                DrawPixel(hDC, dx, dy,
                          (pixel) ? gc->foreground : gc->background);
                if (bitPos == 7) {
                    srcPtr++;
                }
            }
        }
    }
    TkWinReleaseDrawableDC(drawable, hDC, &state);
    ckfree((char *)srcBits);
}

/*
 *----------------------------------------------------------------------
 *
 * Rbc_EmulateXCopyPlane --
 *
 *      Simplified version of XCopyPlane.  Right now it ignores
 *        function,
 *        clip_x_origin,
 *        clip_y_origin
 *
 *      The plane argument must always be 1.
 *
 *      This routine differs from the Tk version in how it handles
 *      transparency.  It uses a different method of drawing transparent
 *      bitmaps that doesn't copy the background or use brushes.  The
 *      second change is to call a special routine when the destDC is
 *      a printer.   Stippling is done by a very slow brute-force
 *      method of drawing 1x1 rectangles for each pixel (bleech).
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Changes the destination drawable.
 *
 *----------------------------------------------------------------------
 */
void
Rbc_EmulateXCopyPlane(display, src, dest, gc, srcX, srcY, width, height, destX, destY, plane)
    Display *display;
    Drawable src;
    Drawable dest;
    GC gc;
    int srcX;
    int srcY;
    unsigned int width;
    unsigned int height;
    int destX;
    int destY;
    unsigned long plane;
{
    HDC srcDC, destDC;
    TkWinDCState srcState, destState;
    TkpClipMask *clipPtr = (TkpClipMask *) gc->clip_mask;

    LastKnownRequestProcessed(display) = NextRequest(display);

    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 != NULL) && (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, destX, destY, width, height, srcDC, srcX, srcY,
               SRCCOPY);

        SelectClipRgn(destDC, NULL);

    } else if (clipPtr->type == TKP_CLIP_PIXMAP) {
        Drawable mask;
        /*
         * Case 2: transparent bitmaps are handled by setting the
         * destination to the foreground color whenever the source
         * pixel is set.
         */
        /*
         * 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.
         */
        mask = clipPtr->value.pixmap;

#if WINDEBUG
        PurifyPrintf("mask %s src\n", (mask == src) ? "==" : "!=");
        PurifyPrintf("GetDeviceCaps=%x\n",
                     GetDeviceCaps(destDC, TECHNOLOGY) & DT_RASDISPLAY);
#endif
        {
            HDC maskDC;
            TkWinDCState maskState;

            if (mask != src) {
                maskDC = TkWinGetDrawableDC(display, mask, &maskState);
            } else {
                maskDC = srcDC;
            }
            SetBkMode(destDC, OPAQUE);
            SetTextColor(destDC, gc->background);
            SetBkColor(destDC, gc->foreground);
            BitBlt(destDC, destX, destY, width, height, srcDC, srcX, srcY,
                   SRCINVERT);
            /*
             * Make sure we treat the mask as a monochrome bitmap.
             * We can get alpha-blending with non-black/white fg/bg
             * color selections.
             */
            SetTextColor(destDC, RGB(255,255,255));
            SetBkColor(destDC, RGB(0,0,0));

            /* FIXME: Handle gc->clip_?_origin's */
            BitBlt(destDC, destX, destY, width, height, maskDC, 0, 0, SRCAND);

            SetTextColor(destDC, gc->background);
            SetBkColor(destDC, gc->foreground);
            BitBlt(destDC, destX, destY, width, height, srcDC, srcX, srcY,
                   SRCINVERT);
            if (mask != src) {
                TkWinReleaseDrawableDC(mask, maskDC, &maskState);
            }
        }
    }
    if (src != dest) {
        TkWinReleaseDrawableDC(dest, destDC, &destState);
    }
    TkWinReleaseDrawableDC(src, srcDC, &srcState);
}

/*
 *----------------------------------------------------------------------
 *
 * Rbc_EmulateXCopyArea --
 *
 *      Copies data from one drawable to another using block transfer
 *      routines.  The small enhancement over the version in Tk is
 *      that it doesn't assume that the source and destination devices
 *      have the same resolution. This isn't true when the destination
 *      device is a printer.
 *
 *      FIXME: not true anymore.  delete this routine.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Data is moved from a window or bitmap to a second window,
 *      bitmap, or printer.
 *
 *----------------------------------------------------------------------
 */
void
Rbc_EmulateXCopyArea(display, src, dest, gc, srcX, srcY, width, height, destX, destY)
    Display *display;
    Drawable src;
    Drawable dest;
    GC gc;
    int srcX; /* Source X-coordinate */
    int srcY; /* Source Y-coordinate. */
    unsigned int width; /* Width of area. */
    unsigned int height; /* Height of area. */
    int destX; /* Destination X-coordinate (in screen
                * coordinates). */
    int destY; /* Destination Y-coordinate (in screen
                * coordinates). */
{
    HDC srcDC, destDC;
    TkWinDCState srcState, destState;
    TkpClipMask *clipPtr;

    srcDC = TkWinGetDrawableDC(display, src, &srcState);
    if (src != dest) {
        destDC = TkWinGetDrawableDC(display, dest, &destState);
    } else {
        destDC = srcDC;
    }
    clipPtr = (TkpClipMask *)gc->clip_mask;
    if ((clipPtr != NULL) && (clipPtr->type == TKP_CLIP_REGION)) {
        SelectClipRgn(destDC, (HRGN)clipPtr->value.region);
        OffsetClipRgn(destDC, gc->clip_x_origin, gc->clip_y_origin);
    }

    BitBlt(destDC, destX, destY, width, height, srcDC, srcX, srcY,
           bltModes[gc->function]);
    SelectClipRgn(destDC, NULL);

    if (src != dest) {
        TkWinReleaseDrawableDC(dest, destDC, &destState);
    }
    TkWinReleaseDrawableDC(src, srcDC, &srcState);
}

/*
 *--------------------------------------------------------------
 *
 * StippleRegion --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
static void
StippleRegion(display, hDC, gc, x, y, width, height)
    Display *display;
    HDC hDC; /* Device context: For polygons, clip
              * region will be installed. */
    GC gc;
    int x;
    int y;
    int width;
    int height;
{
    BITMAP bm;
    HBITMAP oldBitmap;
    HDC maskDC, memDC;
    Pixmap mask;
    TkWinDCState maskState;
    TkWinDrawable *twdPtr;
    int destX, destY, destWidth, destHeight;
    int dx, dy;
    int left, top, right, bottom;
    int srcX, srcY;
    int startX, startY;		/* Starting upper left corner of region. */

    twdPtr = (TkWinDrawable *)gc->stipple;
    GetObject(twdPtr->bitmap.handle, sizeof(BITMAP), &bm);

    startX = x;
    if (x < gc->ts_x_origin) {
        dx = (gc->ts_x_origin - x) % bm.bmWidth;
        if (dx > 0) {
            startX -= (bm.bmWidth - dx);
        }
    } else if (x > gc->ts_x_origin) {
        dx = (x - gc->ts_x_origin) % bm.bmWidth;
        if (dx > 0) {
            startX -= dx;
        }
    }
    startY = y;
    if (y < gc->ts_y_origin) {
        dy = (gc->ts_y_origin - y) % bm.bmHeight;
        if (dy > 0) {
            startY -= (bm.bmHeight - dy);
        }
    } else if (y >= gc->ts_y_origin) {
        dy = (y - gc->ts_y_origin) % bm.bmHeight;
        if (dy > 0) {
            startY -= dy;
        }
    }
#ifdef notdef
    PurifyPrintf("tile is (%d,%d,%d,%d)\n", gc->ts_x_origin, gc->ts_y_origin,
                 bm.bmWidth, bm.bmHeight);
    PurifyPrintf("region is (%d,%d,%d,%d)\n", x, y, width, height);
    PurifyPrintf("starting at %d,%d\n", startX, startY);
#endif
    left = x;
    right = x + width;
    top = y;
    bottom = y + height;

    maskDC = memDC = CreateCompatibleDC(hDC);
    oldBitmap = SelectBitmap(memDC, twdPtr->bitmap.handle);
    mask = gc->stipple;
    if (gc->fill_style == FillStippled) { /* With transparency. */
        if (gc->clip_mask != None) {
            TkpClipMask *clipPtr;

            mask = gc->stipple;
            clipPtr = (TkpClipMask *)gc->clip_mask;
            if  (clipPtr->type == TKP_CLIP_PIXMAP) {
                mask = clipPtr->value.pixmap;
            }
        }
        if (mask != gc->stipple) {
            maskDC = TkWinGetDrawableDC(display, mask, &maskState);
        }
    }

    for (y = startY; y < bottom; y += bm.bmHeight) {
        srcY = 0;
        destY = y;
        destHeight = bm.bmHeight;
        if (y < top) {
            srcY = (top - y);
            destHeight = bm.bmHeight - srcY;
            destY = top;
        }
        if ((destY + destHeight) > bottom) {
            destHeight = (bottom - destY);
        }
        for (x = startX; x < right; x += bm.bmWidth) {
            srcX = 0;
            destX = x;
            destWidth = bm.bmWidth;
            if (x < left) {
                srcX = (left - x);
                destWidth = bm.bmWidth - srcX;
                destX = left;
            }
            if ((destX + destWidth) > right) {
                destWidth = (right - destX);
            }
#ifdef notdef
            PurifyPrintf("drawing pattern (%d,%d,%d,%d) at %d,%d\n",
                         srcX , srcY, destWidth, destHeight, destX, destY);
#endif
            if (gc->fill_style == FillStippled) { /* With transparency. */
                SetBkMode(hDC, OPAQUE);
                SetTextColor(hDC, gc->background);
                SetBkColor(hDC, gc->foreground);
                BitBlt(hDC, destX, destY, destWidth, destHeight, memDC,
                       srcX, srcY, SRCINVERT);
                SetTextColor(hDC, RGB(255,255,255));
                SetBkColor(hDC, RGB(0,0,0));
                BitBlt(hDC, destX, destY, destWidth, destHeight, maskDC,
                       srcX, srcY, SRCAND);
                SetTextColor(hDC, gc->background);
                SetBkColor(hDC, gc->foreground);
                BitBlt(hDC, destX, destY, destWidth, destHeight, memDC,
                       srcX, srcY, SRCINVERT);
            } else if (gc->fill_style == FillOpaqueStippled) { /* Opaque. */
                SetBkColor(hDC, gc->foreground);
                SetTextColor(hDC, gc->background);
                BitBlt(hDC, destX, destY, destWidth, destHeight, memDC,
                       srcX, srcY, SRCCOPY);
            }
        }
    }
    SelectBitmap(memDC, oldBitmap);
    if (maskDC != memDC) {
        TkWinReleaseDrawableDC(mask, maskDC, &maskState);
    }
    DeleteDC(memDC);
}

/*
 *--------------------------------------------------------------
 *
 * Rbc_EmulateXFillPolygon --
 *
 *      This differs from Tk's XFillPolygon in that it works
 *      around deficencies in Windows 95/98:
 *        1. Stippling bitmap is limited to 8x8.
 *        2. No tiling (with or without mask).
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
void
Rbc_EmulateXFillPolygon(display, drawable, gc, pointPtr, nPoints, shape, mode)
    Display *display;
    Drawable drawable;
    GC gc;
    XPoint *pointPtr;
    int nPoints;
    int shape;
    int mode;
{
    HDC hDC;
    HRGN hRgn;
    POINT *p, *winPts, *endPtr;
    Region2D bbox;
    TkWinDCState state;
    int fillMode;

    if (drawable == None) {
        return;
    }

    /* Determine the bounding box of the polygon. */
    bbox.left = bbox.right = pointPtr->x;
    bbox.top = bbox.bottom = pointPtr->y;

    hDC = TkWinGetDrawableDC(display, drawable, &state);

    /* Allocate array of POINTS to create the polygon's path. */
    winPts = (POINT *)ckalloc(sizeof(POINT) * nPoints);
    endPtr = winPts + nPoints;
    for (p = winPts; p < endPtr; p++) {
        if (pointPtr->x < bbox.left) {
            bbox.left = pointPtr->x;
        }
        if (pointPtr->x > bbox.right) {
            bbox.right = pointPtr->x;
        }
        if (pointPtr->y < bbox.top) {
            bbox.top = pointPtr->y;
        }
        if (pointPtr->y > bbox.bottom) {
            bbox.bottom = pointPtr->y;
        }
        p->x = pointPtr->x;
        p->y = pointPtr->y;
        pointPtr++;
    }

    SetROP2(hDC, tkpWinRopModes[gc->function]);
    fillMode = (gc->fill_rule == EvenOddRule) ? ALTERNATE : WINDING;

    if ((gc->fill_style == FillStippled) ||
            (gc->fill_style == FillOpaqueStippled)) {
        int width, height;

        /* Points are offsets within the bounding box. */
        for (p = winPts; p < endPtr; p++) {
            p->x -= bbox.left;
            p->y -= bbox.top;
        }
        /* Use the polygon as a clip path. */
        LPtoDP(hDC, winPts, nPoints);
        hRgn = CreatePolygonRgn(winPts, nPoints, fillMode);
        SelectClipRgn(hDC, hRgn);
        OffsetClipRgn(hDC, bbox.left, bbox.top);

        /* Tile the bounding box. */
        width = bbox.right - bbox.left + 1;
        height = bbox.bottom - bbox.top + 1;
        StippleRegion(display, hDC, gc, bbox.left, bbox.top, width, height);

        SelectClipRgn(hDC, NULL);
        DeleteRgn(hRgn);
    } else {
        HPEN oldPen;
        HBRUSH oldBrush;

        /*
         * FIXME: Right now, we're assuming that it's solid or
         * stippled and ignoring tiling. I'll merge the bits from
         * Rbc_TilePolygon later.
         */
        oldPen = SelectPen(hDC, GetStockObject(NULL_PEN));
        oldBrush = SelectBrush(hDC, CreateSolidBrush(gc->foreground));
        SetPolyFillMode(hDC, fillMode);
        Polygon(hDC, winPts, nPoints);
        SelectPen(hDC, oldPen);
        DeleteBrush(SelectBrush(hDC, oldBrush));
    }
    ckfree((char *)winPts);
    TkWinReleaseDrawableDC(drawable, hDC, &state);
}
