OSDN Git Service

touched all tk files to ease next import
[pf3gnuchains/pf3gnuchains4x.git] / tk / win / tkWinDraw.c
1 /* 
2  * tkWinDraw.c --
3  *
4  *      This file contains the Xlib emulation functions pertaining to
5  *      actually drawing objects on a window.
6  *
7  * Copyright (c) 1995 Sun Microsystems, Inc.
8  * Copyright (c) 1994 Software Research Associates, Inc.
9  *
10  * See the file "license.terms" for information on usage and redistribution
11  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12  *
13  * RCS: @(#) $Id$
14  */
15
16 #include "tkWinInt.h"
17
18 /*
19  * These macros convert between X's bizarre angle units to radians.
20  */
21
22 #define PI 3.14159265358979
23 #define XAngleToRadians(a) ((double)(a) / 64 * PI / 180);
24
25 /*
26  * Translation table between X gc functions and Win32 raster op modes.
27  */
28
29 int tkpWinRopModes[] = {
30     R2_BLACK,                   /* GXclear */
31     R2_MASKPEN,                 /* GXand */
32     R2_MASKPENNOT,              /* GXandReverse */
33     R2_COPYPEN,                 /* GXcopy */
34     R2_MASKNOTPEN,              /* GXandInverted */
35     R2_NOT,                     /* GXnoop */
36     R2_XORPEN,                  /* GXxor */
37     R2_MERGEPEN,                /* GXor */
38     R2_NOTMERGEPEN,             /* GXnor */
39     R2_NOTXORPEN,               /* GXequiv */
40     R2_NOT,                     /* GXinvert */
41     R2_MERGEPENNOT,             /* GXorReverse */
42     R2_NOTCOPYPEN,              /* GXcopyInverted */
43     R2_MERGENOTPEN,             /* GXorInverted */
44     R2_NOTMASKPEN,              /* GXnand */
45     R2_WHITE                    /* GXset */
46 };
47
48 /*
49  * Translation table between X gc functions and Win32 BitBlt op modes.  Some
50  * of the operations defined in X don't have names, so we have to construct
51  * new opcodes for those functions.  This is arcane and probably not all that
52  * useful, but at least it's accurate.
53  */
54
55 #define NOTSRCAND       (DWORD)0x00220326 /* dest = (NOT source) AND dest */
56 #define NOTSRCINVERT    (DWORD)0x00990066 /* dest = (NOT source) XOR dest */
57 #define SRCORREVERSE    (DWORD)0x00DD0228 /* dest = source OR (NOT dest) */
58 #define SRCNAND         (DWORD)0x007700E6 /* dest = NOT (source AND dest) */
59
60 static int bltModes[] = {
61     BLACKNESS,                  /* GXclear */
62     SRCAND,                     /* GXand */
63     SRCERASE,                   /* GXandReverse */
64     SRCCOPY,                    /* GXcopy */
65     NOTSRCAND,                  /* GXandInverted */
66     PATCOPY,                    /* GXnoop */
67     SRCINVERT,                  /* GXxor */
68     SRCPAINT,                   /* GXor */
69     NOTSRCERASE,                /* GXnor */
70     NOTSRCINVERT,               /* GXequiv */
71     DSTINVERT,                  /* GXinvert */
72     SRCORREVERSE,               /* GXorReverse */
73     NOTSRCCOPY,                 /* GXcopyInverted */
74     MERGEPAINT,                 /* GXorInverted */
75     SRCNAND,                    /* GXnand */
76     WHITENESS                   /* GXset */
77 };
78
79 /*
80  * The following raster op uses the source bitmap as a mask for the
81  * pattern.  This is used to draw in a foreground color but leave the
82  * background color transparent.
83  */
84
85 #define MASKPAT         0x00E20746 /* dest = (src & pat) | (!src & dst) */
86
87 /*
88  * The following two raster ops are used to copy the foreground and background
89  * bits of a source pattern as defined by a stipple used as the pattern.
90  */
91
92 #define COPYFG          0x00CA0749 /* dest = (pat & src) | (!pat & dst) */
93 #define COPYBG          0x00AC0744 /* dest = (!pat & src) | (pat & dst) */
94
95 /*
96  * Macros used later in the file.
97  */
98
99 #define MIN(a,b)        ((a>b) ? b : a)
100 #define MAX(a,b)        ((a<b) ? b : a)
101
102 /*
103  * The followng typedef is used to pass Windows GDI drawing functions.
104  */
105
106 typedef BOOL (CALLBACK *WinDrawFunc) _ANSI_ARGS_((HDC dc,
107                             CONST POINT* points, int npoints));
108
109 typedef struct ThreadSpecificData {
110     POINT *winPoints;    /* Array of points that is reused. */
111     int nWinPoints;     /* Current size of point array. */
112 } ThreadSpecificData;
113 static Tcl_ThreadDataKey dataKey;
114
115 /*
116  * Forward declarations for procedures defined in this file:
117  */
118
119 static POINT *          ConvertPoints _ANSI_ARGS_((XPoint *points, int npoints,
120                             int mode, RECT *bbox));
121 static void             DrawOrFillArc _ANSI_ARGS_((Display *display,
122                             Drawable d, GC gc, int x, int y,
123                             unsigned int width, unsigned int height,
124                             int start, int extent, int fill));
125 static void             RenderObject _ANSI_ARGS_((HDC dc, GC gc,
126                             XPoint* points, int npoints, int mode, HPEN pen,
127                             WinDrawFunc func));
128 static HPEN             SetUpGraphicsPort _ANSI_ARGS_((GC gc));
129 \f
130 /*
131  *----------------------------------------------------------------------
132  *
133  * TkWinGetDrawableDC --
134  *
135  *      Retrieve the DC from a drawable.
136  *
137  * Results:
138  *      Returns the window DC for windows.  Returns a new memory DC
139  *      for pixmaps.
140  *
141  * Side effects:
142  *      Sets up the palette for the device context, and saves the old
143  *      device context state in the passed in TkWinDCState structure.
144  *
145  *----------------------------------------------------------------------
146  */
147
148 HDC
149 TkWinGetDrawableDC(display, d, state)
150     Display *display;
151     Drawable d;
152     TkWinDCState* state;
153 {
154     HDC dc;
155     TkWinDrawable *twdPtr = (TkWinDrawable *)d;
156     Colormap cmap;
157
158     if (twdPtr->type == TWD_WINDOW) {
159         TkWindow *winPtr = twdPtr->window.winPtr;
160     
161         dc = GetDC(twdPtr->window.handle);
162         if (winPtr == NULL) {
163             cmap = DefaultColormap(display, DefaultScreen(display));
164         } else {
165             cmap = winPtr->atts.colormap;
166         }
167     } else if (twdPtr->type == TWD_WINDC) {
168         dc = twdPtr->winDC.hdc;
169         cmap = DefaultColormap(display, DefaultScreen(display));
170     } else {
171         dc = CreateCompatibleDC(NULL);
172         SelectObject(dc, twdPtr->bitmap.handle);
173         cmap = twdPtr->bitmap.colormap;
174     }
175     state->palette = TkWinSelectPalette(dc, cmap);
176     state->bkmode  = GetBkMode(dc);
177     return dc;
178 }
179 \f
180 /*
181  *----------------------------------------------------------------------
182  *
183  * TkWinReleaseDrawableDC --
184  *
185  *      Frees the resources associated with a drawable's DC.
186  *
187  * Results:
188  *      None.
189  *
190  * Side effects:
191  *      Restores the old bitmap handle to the memory DC for pixmaps.
192  *
193  *----------------------------------------------------------------------
194  */
195
196 void
197 TkWinReleaseDrawableDC(d, dc, state)
198     Drawable d;
199     HDC dc;
200     TkWinDCState *state;
201 {
202     TkWinDrawable *twdPtr = (TkWinDrawable *)d;
203     SetBkMode(dc, state->bkmode);
204     SelectPalette(dc, state->palette, TRUE);
205     RealizePalette(dc);
206     if (twdPtr->type == TWD_WINDOW) {
207         ReleaseDC(TkWinGetHWND(d), dc);
208     } else if (twdPtr->type == TWD_BITMAP) {
209         DeleteDC(dc);
210     }
211 }
212 \f
213 /*
214  *----------------------------------------------------------------------
215  *
216  * ConvertPoints --
217  *
218  *      Convert an array of X points to an array of Win32 points.
219  *
220  * Results:
221  *      Returns the converted array of POINTs.
222  *
223  * Side effects:
224  *      Allocates a block of memory in thread local storage that 
225  *      should not be freed.
226  *
227  *----------------------------------------------------------------------
228  */
229
230 static POINT *
231 ConvertPoints(points, npoints, mode, bbox)
232     XPoint *points;
233     int npoints;
234     int mode;                   /* CoordModeOrigin or CoordModePrevious. */
235     RECT *bbox;                 /* Bounding box of points. */
236 {
237     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
238             Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
239     int i;
240
241     /*
242      * To avoid paying the cost of a malloc on every drawing routine,
243      * we reuse the last array if it is large enough.
244      */
245
246     if (npoints > tsdPtr->nWinPoints) {
247         if (tsdPtr->winPoints != NULL) {
248             ckfree((char *) tsdPtr->winPoints);
249         }
250         tsdPtr->winPoints = (POINT *) ckalloc(sizeof(POINT) * npoints);
251         if (tsdPtr->winPoints == NULL) {
252             tsdPtr->nWinPoints = -1;
253             return NULL;
254         }
255         tsdPtr->nWinPoints = npoints;
256     }
257
258     bbox->left = bbox->right = points[0].x;
259     bbox->top = bbox->bottom = points[0].y;
260     
261     if (mode == CoordModeOrigin) {
262         for (i = 0; i < npoints; i++) {
263             tsdPtr->winPoints[i].x = points[i].x;
264             tsdPtr->winPoints[i].y = points[i].y;
265             bbox->left = MIN(bbox->left, tsdPtr->winPoints[i].x);
266             bbox->right = MAX(bbox->right, tsdPtr->winPoints[i].x);
267             bbox->top = MIN(bbox->top, tsdPtr->winPoints[i].y);
268             bbox->bottom = MAX(bbox->bottom, tsdPtr->winPoints[i].y);
269         }
270     } else {
271         tsdPtr->winPoints[0].x = points[0].x;
272         tsdPtr->winPoints[0].y = points[0].y;
273         for (i = 1; i < npoints; i++) {
274             tsdPtr->winPoints[i].x = tsdPtr->winPoints[i-1].x + points[i].x;
275             tsdPtr->winPoints[i].y = tsdPtr->winPoints[i-1].y + points[i].y;
276             bbox->left = MIN(bbox->left, tsdPtr->winPoints[i].x);
277             bbox->right = MAX(bbox->right, tsdPtr->winPoints[i].x);
278             bbox->top = MIN(bbox->top, tsdPtr->winPoints[i].y);
279             bbox->bottom = MAX(bbox->bottom, tsdPtr->winPoints[i].y);
280         }
281     }
282     return tsdPtr->winPoints;
283 }
284 \f
285 /*
286  *----------------------------------------------------------------------
287  *
288  * XCopyArea --
289  *
290  *      Copies data from one drawable to another using block transfer
291  *      routines.
292  *
293  * Results:
294  *      None.
295  *
296  * Side effects:
297  *      Data is moved from a window or bitmap to a second window or
298  *      bitmap.
299  *
300  *----------------------------------------------------------------------
301  */
302
303 void
304 XCopyArea(display, src, dest, gc, src_x, src_y, width, height, dest_x, dest_y)
305     Display* display;
306     Drawable src;
307     Drawable dest;
308     GC gc;
309     int src_x, src_y;
310     unsigned int width, height;
311     int dest_x, dest_y;
312 {
313     HDC srcDC, destDC;
314     TkWinDCState srcState, destState;
315     TkpClipMask *clipPtr = (TkpClipMask*)gc->clip_mask;
316
317     srcDC = TkWinGetDrawableDC(display, src, &srcState);
318
319     if (src != dest) {
320         destDC = TkWinGetDrawableDC(display, dest, &destState);
321     } else {
322         destDC = srcDC;
323     }
324
325     if (clipPtr && clipPtr->type == TKP_CLIP_REGION) {
326         SelectClipRgn(destDC, (HRGN) clipPtr->value.region);
327         OffsetClipRgn(destDC, gc->clip_x_origin, gc->clip_y_origin);
328     }
329
330     BitBlt(destDC, dest_x, dest_y, width, height, srcDC, src_x, src_y,
331             bltModes[gc->function]);
332
333     SelectClipRgn(destDC, NULL);
334
335     if (src != dest) {
336         TkWinReleaseDrawableDC(dest, destDC, &destState);
337     }
338     TkWinReleaseDrawableDC(src, srcDC, &srcState);
339 }
340 \f
341 /*
342  *----------------------------------------------------------------------
343  *
344  * XCopyPlane --
345  *
346  *      Copies a bitmap from a source drawable to a destination
347  *      drawable.  The plane argument specifies which bit plane of
348  *      the source contains the bitmap.  Note that this implementation
349  *      ignores the gc->function.
350  *
351  * Results:
352  *      None.
353  *
354  * Side effects:
355  *      Changes the destination drawable.
356  *
357  *----------------------------------------------------------------------
358  */
359
360 void
361 XCopyPlane(display, src, dest, gc, src_x, src_y, width, height, dest_x,
362         dest_y, plane)
363     Display* display;
364     Drawable src;
365     Drawable dest;
366     GC gc;
367     int src_x, src_y;
368     unsigned int width, height;
369     int dest_x, dest_y;
370     unsigned long plane;
371 {
372     HDC srcDC, destDC;
373     TkWinDCState srcState, destState;
374     HBRUSH bgBrush, fgBrush, oldBrush;
375     TkpClipMask *clipPtr = (TkpClipMask*)gc->clip_mask;
376
377     display->request++;
378
379     if (plane != 1) {
380         panic("Unexpected plane specified for XCopyPlane");
381     }
382
383     srcDC = TkWinGetDrawableDC(display, src, &srcState);
384
385     if (src != dest) {
386         destDC = TkWinGetDrawableDC(display, dest, &destState);
387     } else {
388         destDC = srcDC;
389     }
390
391     if (clipPtr == NULL || clipPtr->type == TKP_CLIP_REGION) {
392
393         /*
394          * Case 1: opaque bitmaps.  Windows handles the conversion
395          * from one bit to multiple bits by setting 0 to the
396          * foreground color, and 1 to the background color (seems
397          * backwards, but there you are).
398          */
399
400         if (clipPtr && clipPtr->type == TKP_CLIP_REGION) {
401             SelectClipRgn(destDC, (HRGN) clipPtr->value.region);
402             OffsetClipRgn(destDC, gc->clip_x_origin, gc->clip_y_origin);
403         }
404
405         SetBkMode(destDC, OPAQUE);
406         SetBkColor(destDC, gc->foreground);
407         SetTextColor(destDC, gc->background);
408         BitBlt(destDC, dest_x, dest_y, width, height, srcDC, src_x, src_y,
409                 SRCCOPY);
410
411         SelectClipRgn(destDC, NULL);
412     } else if (clipPtr->type == TKP_CLIP_PIXMAP) {
413         if (clipPtr->value.pixmap == src) {
414
415             /*
416              * Case 2: transparent bitmaps are handled by setting the
417              * destination to the foreground color whenever the source
418              * pixel is set.
419              */
420
421             fgBrush = CreateSolidBrush(gc->foreground);
422             oldBrush = SelectObject(destDC, fgBrush);
423             BitBlt(destDC, dest_x, dest_y, width, height, srcDC, src_x, src_y,
424                     MASKPAT);
425             SelectObject(destDC, oldBrush);
426             DeleteObject(fgBrush);
427         } else {
428
429             /*
430              * Case 3: two arbitrary bitmaps.  Copy the source rectangle
431              * into a color pixmap.  Use the result as a brush when
432              * copying the clip mask into the destination.       
433              */
434
435             HDC memDC, maskDC;
436             HBITMAP bitmap;
437             TkWinDCState maskState;
438
439             fgBrush = CreateSolidBrush(gc->foreground);
440             bgBrush = CreateSolidBrush(gc->background);
441             maskDC = TkWinGetDrawableDC(display, clipPtr->value.pixmap,
442                     &maskState);
443             memDC = CreateCompatibleDC(destDC);
444             bitmap = CreateBitmap(width, height, 1, 1, NULL);
445             SelectObject(memDC, bitmap);
446
447             /*
448              * Set foreground bits.  We create a new bitmap containing
449              * (source AND mask), then use it to set the foreground color
450              * into the destination.
451              */
452
453             BitBlt(memDC, 0, 0, width, height, srcDC, src_x, src_y, SRCCOPY);
454             BitBlt(memDC, 0, 0, width, height, maskDC,
455                     dest_x - gc->clip_x_origin, dest_y - gc->clip_y_origin,
456                     SRCAND);
457             oldBrush = SelectObject(destDC, fgBrush);
458             BitBlt(destDC, dest_x, dest_y, width, height, memDC, 0, 0,
459                     MASKPAT);
460
461             /*
462              * Set background bits.  Same as foreground, except we use
463              * ((NOT source) AND mask) and the background brush.
464              */
465
466             BitBlt(memDC, 0, 0, width, height, srcDC, src_x, src_y,
467                     NOTSRCCOPY);
468             BitBlt(memDC, 0, 0, width, height, maskDC,
469                     dest_x - gc->clip_x_origin, dest_y - gc->clip_y_origin,
470                     SRCAND);
471             SelectObject(destDC, bgBrush);
472             BitBlt(destDC, dest_x, dest_y, width, height, memDC, 0, 0,
473                     MASKPAT);
474
475             TkWinReleaseDrawableDC(clipPtr->value.pixmap, maskDC, &maskState);
476             SelectObject(destDC, oldBrush);
477             DeleteDC(memDC);
478             DeleteObject(bitmap);
479             DeleteObject(fgBrush);
480             DeleteObject(bgBrush);
481         }
482     }
483     if (src != dest) {
484         TkWinReleaseDrawableDC(dest, destDC, &destState);
485     }
486     TkWinReleaseDrawableDC(src, srcDC, &srcState);
487 }
488 \f
489 /*
490  *----------------------------------------------------------------------
491  *
492  * TkPutImage --
493  *
494  *      Copies a subimage from an in-memory image to a rectangle of
495  *      of the specified drawable.
496  *
497  * Results:
498  *      None.
499  *
500  * Side effects:
501  *      Draws the image on the specified drawable.
502  *
503  *----------------------------------------------------------------------
504  */
505
506 void
507 TkPutImage(colors, ncolors, display, d, gc, image, src_x, src_y, dest_x,
508         dest_y, width, height)
509     unsigned long *colors;              /* Array of pixel values used by this
510                                          * image.  May be NULL. */
511     int ncolors;                        /* Number of colors used, or 0. */
512     Display* display;
513     Drawable d;                         /* Destination drawable. */
514     GC gc;
515     XImage* image;                      /* Source image. */
516     int src_x, src_y;                   /* Offset of subimage. */      
517     int dest_x, dest_y;                 /* Position of subimage origin in
518                                          * drawable.  */
519     unsigned int width, height;         /* Dimensions of subimage. */
520 {
521     HDC dc, dcMem;
522     TkWinDCState state;
523     BITMAPINFO *infoPtr;
524     HBITMAP bitmap;
525     char *data;
526
527     display->request++;
528
529     dc = TkWinGetDrawableDC(display, d, &state);
530     SetROP2(dc, tkpWinRopModes[gc->function]);
531     dcMem = CreateCompatibleDC(dc);
532
533     if (image->bits_per_pixel == 1) {
534         /*
535          * If the image isn't in the right format, we have to copy
536          * it into a new buffer in MSBFirst and word-aligned format.
537          */
538
539         if ((image->bitmap_bit_order != MSBFirst)
540                 || (image->bitmap_pad != sizeof(WORD))) {
541             data = TkAlignImageData(image, sizeof(WORD), MSBFirst);
542             bitmap = CreateBitmap(image->width, image->height, 1, 1, data);
543             ckfree(data);
544         } else {
545             bitmap = CreateBitmap(image->width, image->height, 1, 1,
546                     image->data);
547         }
548         SetTextColor(dc, gc->foreground);
549         SetBkColor(dc, gc->background);
550     } else {    
551         int i, usePalette;
552
553         /*
554          * Do not use a palette for TrueColor images.
555          */
556         
557         usePalette = (image->bits_per_pixel < 16);
558         
559         if (usePalette) {
560             infoPtr = (BITMAPINFO*) ckalloc(sizeof(BITMAPINFOHEADER)
561                     + sizeof(RGBQUAD)*ncolors);
562         } else {
563             infoPtr = (BITMAPINFO*) ckalloc(sizeof(BITMAPINFOHEADER));
564         }
565         
566         infoPtr->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
567         infoPtr->bmiHeader.biWidth = image->width;
568         infoPtr->bmiHeader.biHeight = -image->height; /* Top-down order */
569         infoPtr->bmiHeader.biPlanes = 1;
570         infoPtr->bmiHeader.biBitCount = image->bits_per_pixel;
571         infoPtr->bmiHeader.biCompression = BI_RGB;
572         infoPtr->bmiHeader.biSizeImage = 0;
573         infoPtr->bmiHeader.biXPelsPerMeter = 0;
574         infoPtr->bmiHeader.biYPelsPerMeter = 0;
575         infoPtr->bmiHeader.biClrImportant = 0;
576
577         if (usePalette) {
578             infoPtr->bmiHeader.biClrUsed = ncolors;
579             for (i = 0; i < ncolors; i++) {
580                 infoPtr->bmiColors[i].rgbBlue = GetBValue(colors[i]);
581                 infoPtr->bmiColors[i].rgbGreen = GetGValue(colors[i]);
582                 infoPtr->bmiColors[i].rgbRed = GetRValue(colors[i]);
583                 infoPtr->bmiColors[i].rgbReserved = 0;
584             }
585         } else {
586             infoPtr->bmiHeader.biClrUsed = 0;
587         }
588         bitmap = CreateDIBitmap(dc, &infoPtr->bmiHeader, CBM_INIT,
589                 image->data, infoPtr, DIB_RGB_COLORS);
590         ckfree((char *) infoPtr);
591     }
592     bitmap = SelectObject(dcMem, bitmap);
593     BitBlt(dc, dest_x, dest_y, width, height, dcMem, src_x, src_y, SRCCOPY);
594     DeleteObject(SelectObject(dcMem, bitmap));
595     DeleteDC(dcMem);
596     TkWinReleaseDrawableDC(d, dc, &state);
597 }
598 \f
599 /*
600  *----------------------------------------------------------------------
601  *
602  * XFillRectangles --
603  *
604  *      Fill multiple rectangular areas in the given drawable.
605  *
606  * Results:
607  *      None.
608  *
609  * Side effects:
610  *      Draws onto the specified drawable.
611  *
612  *----------------------------------------------------------------------
613  */
614
615 void
616 XFillRectangles(display, d, gc, rectangles, nrectangles)
617     Display* display;
618     Drawable d;
619     GC gc;
620     XRectangle* rectangles;
621     int nrectangles;
622 {
623     HDC dc;
624     int i;
625     RECT rect;
626     TkWinDCState state;
627     HBRUSH brush;
628
629     if (d == None) {
630         return;
631     }
632
633     dc = TkWinGetDrawableDC(display, d, &state);
634     SetROP2(dc, tkpWinRopModes[gc->function]);
635     brush = CreateSolidBrush(gc->foreground);
636
637     if ((gc->fill_style == FillStippled
638             || gc->fill_style == FillOpaqueStippled)
639             && gc->stipple != None) {
640         TkWinDrawable *twdPtr = (TkWinDrawable *)gc->stipple;
641         HBRUSH oldBrush, stipple;
642         HBITMAP oldBitmap, bitmap;
643         HDC dcMem;
644         HBRUSH bgBrush = CreateSolidBrush(gc->background);
645
646         if (twdPtr->type != TWD_BITMAP) {
647             panic("unexpected drawable type in stipple");
648         }
649
650         /*
651          * Select stipple pattern into destination dc.
652          */
653         
654         stipple = CreatePatternBrush(twdPtr->bitmap.handle);
655         SetBrushOrgEx(dc, gc->ts_x_origin, gc->ts_y_origin, NULL);
656         oldBrush = SelectObject(dc, stipple);
657         dcMem = CreateCompatibleDC(dc);
658
659         /*
660          * For each rectangle, create a drawing surface which is the size of
661          * the rectangle and fill it with the background color.  Then merge the
662          * result with the stipple pattern.
663          */
664
665         for (i = 0; i < nrectangles; i++) {
666             bitmap = CreateCompatibleBitmap(dc, rectangles[i].width,
667                     rectangles[i].height);
668             oldBitmap = SelectObject(dcMem, bitmap);
669             rect.left = 0;
670             rect.top = 0;
671             rect.right = rectangles[i].width;
672             rect.bottom = rectangles[i].height;
673             FillRect(dcMem, &rect, brush);
674             BitBlt(dc, rectangles[i].x, rectangles[i].y, rectangles[i].width,
675                     rectangles[i].height, dcMem, 0, 0, COPYFG);
676             if (gc->fill_style == FillOpaqueStippled) {
677                 FillRect(dcMem, &rect, bgBrush);
678                 BitBlt(dc, rectangles[i].x, rectangles[i].y,
679                         rectangles[i].width, rectangles[i].height, dcMem,
680                         0, 0, COPYBG);
681             }
682             SelectObject(dcMem, oldBitmap);
683             DeleteObject(bitmap);
684         }
685         
686         DeleteDC(dcMem);
687         SelectObject(dc, oldBrush);
688         DeleteObject(stipple);
689         DeleteObject(bgBrush);
690     } else {
691         for (i = 0; i < nrectangles; i++) {
692             TkWinFillRect(dc, rectangles[i].x, rectangles[i].y,
693                     rectangles[i].width, rectangles[i].height, gc->foreground);
694         }
695     }
696     DeleteObject(brush);
697     TkWinReleaseDrawableDC(d, dc, &state);
698 }
699 \f
700 /*
701  *----------------------------------------------------------------------
702  *
703  * RenderObject --
704  *
705  *      This function draws a shape using a list of points, a
706  *      stipple pattern, and the specified drawing function.
707  *
708  * Results:
709  *      None.
710  *
711  * Side effects:
712  *      None.
713  *
714  *----------------------------------------------------------------------
715  */
716
717 static void
718 RenderObject(dc, gc, points, npoints, mode, pen, func)
719     HDC dc;
720     GC gc;
721     XPoint* points;
722     int npoints;
723     int mode;
724     HPEN pen;
725     WinDrawFunc func;
726 {
727     RECT rect;
728     HPEN oldPen;
729     HBRUSH oldBrush;
730     POINT *winPoints = ConvertPoints(points, npoints, mode, &rect);
731     
732     if ((gc->fill_style == FillStippled
733             || gc->fill_style == FillOpaqueStippled)
734             && gc->stipple != None) {
735
736         TkWinDrawable *twdPtr = (TkWinDrawable *)gc->stipple;
737         HDC dcMem;
738         LONG width, height;
739         HBITMAP oldBitmap;
740         int i;
741         HBRUSH oldMemBrush;
742         
743         if (twdPtr->type != TWD_BITMAP) {
744             panic("unexpected drawable type in stipple");
745         }
746
747         /*
748          * Grow the bounding box enough to account for wide lines.
749          */
750
751         if (gc->line_width > 1) {
752             rect.left -= gc->line_width;
753             rect.top -= gc->line_width;
754             rect.right += gc->line_width;
755             rect.bottom += gc->line_width;
756         }
757
758         width = rect.right - rect.left;
759         height = rect.bottom - rect.top;
760
761         /*
762          * Select stipple pattern into destination dc.
763          */
764         
765         SetBrushOrgEx(dc, gc->ts_x_origin, gc->ts_y_origin, NULL);
766         oldBrush = SelectObject(dc, CreatePatternBrush(twdPtr->bitmap.handle));
767
768         /*
769          * Create temporary drawing surface containing a copy of the
770          * destination equal in size to the bounding box of the object.
771          */
772         
773         dcMem = CreateCompatibleDC(dc);
774         oldBitmap = SelectObject(dcMem, CreateCompatibleBitmap(dc, width,
775                 height));
776         oldPen = SelectObject(dcMem, pen);
777         BitBlt(dcMem, 0, 0, width, height, dc, rect.left, rect.top, SRCCOPY);
778
779         /*
780          * Translate the object for rendering in the temporary drawing
781          * surface. 
782          */
783
784         for (i = 0; i < npoints; i++) {
785             winPoints[i].x -= rect.left;
786             winPoints[i].y -= rect.top;
787         }
788
789         /*
790          * Draw the object in the foreground color and copy it to the
791          * destination wherever the pattern is set.
792          */
793
794         SetPolyFillMode(dcMem, (gc->fill_rule == EvenOddRule) ? ALTERNATE
795                 : WINDING);
796         oldMemBrush = SelectObject(dcMem, CreateSolidBrush(gc->foreground));
797         (*func)(dcMem, winPoints, npoints);
798         BitBlt(dc, rect.left, rect.top, width, height, dcMem, 0, 0, COPYFG);
799
800         /*
801          * If we are rendering an opaque stipple, then draw the polygon in the
802          * background color and copy it to the destination wherever the pattern
803          * is clear.
804          */
805
806         if (gc->fill_style == FillOpaqueStippled) {
807             DeleteObject(SelectObject(dcMem,
808                     CreateSolidBrush(gc->background)));
809             (*func)(dcMem, winPoints, npoints);
810             BitBlt(dc, rect.left, rect.top, width, height, dcMem, 0, 0,
811                     COPYBG);
812         }
813
814         SelectObject(dcMem, oldPen);
815         DeleteObject(SelectObject(dcMem, oldMemBrush));
816         DeleteObject(SelectObject(dcMem, oldBitmap));
817         DeleteDC(dcMem);
818     } else {
819         oldPen = SelectObject(dc, pen);
820         oldBrush = SelectObject(dc, CreateSolidBrush(gc->foreground));
821         SetROP2(dc, tkpWinRopModes[gc->function]);
822
823         SetPolyFillMode(dc, (gc->fill_rule == EvenOddRule) ? ALTERNATE
824                 : WINDING);
825
826         (*func)(dc, winPoints, npoints);
827
828         SelectObject(dc, oldPen);
829     }
830     DeleteObject(SelectObject(dc, oldBrush));
831 }
832 \f
833 /*
834  *----------------------------------------------------------------------
835  *
836  * XDrawLines --
837  *
838  *      Draw connected lines.
839  *
840  * Results:
841  *      None.
842  *
843  * Side effects:
844  *      Renders a series of connected lines.
845  *
846  *----------------------------------------------------------------------
847  */
848
849 void
850 XDrawLines(display, d, gc, points, npoints, mode)
851     Display* display;
852     Drawable d;
853     GC gc;
854     XPoint* points;
855     int npoints;
856     int mode;
857 {
858     HPEN pen;
859     TkWinDCState state;
860     HDC dc;
861     
862     if (d == None) {
863         return;
864     }
865
866     dc = TkWinGetDrawableDC(display, d, &state);
867
868     pen = SetUpGraphicsPort(gc);
869     SetBkMode(dc, TRANSPARENT);
870     RenderObject(dc, gc, points, npoints, mode, pen, Polyline);
871     DeleteObject(pen);
872     
873     TkWinReleaseDrawableDC(d, dc, &state);
874 }
875 \f
876 /*
877  *----------------------------------------------------------------------
878  *
879  * XFillPolygon --
880  *
881  *      Draws a filled polygon.
882  *
883  * Results:
884  *      None.
885  *
886  * Side effects:
887  *      Draws a filled polygon on the specified drawable.
888  *
889  *----------------------------------------------------------------------
890  */
891
892 void
893 XFillPolygon(display, d, gc, points, npoints, shape, mode)
894     Display* display;
895     Drawable d;
896     GC gc;
897     XPoint* points;
898     int npoints;
899     int shape;
900     int mode;
901 {
902     HPEN pen;
903     TkWinDCState state;
904     HDC dc;
905
906     if (d == None) {
907         return;
908     }
909
910     dc = TkWinGetDrawableDC(display, d, &state);
911
912     pen = GetStockObject(NULL_PEN);
913     RenderObject(dc, gc, points, npoints, mode, pen, Polygon);
914
915     TkWinReleaseDrawableDC(d, dc, &state);
916 }
917 \f
918 /*
919  *----------------------------------------------------------------------
920  *
921  * XDrawRectangle --
922  *
923  *      Draws a rectangle.
924  *
925  * Results:
926  *      None.
927  *
928  * Side effects:
929  *      Draws a rectangle on the specified drawable.
930  *
931  *----------------------------------------------------------------------
932  */
933
934 void
935 XDrawRectangle(display, d, gc, x, y, width, height)
936     Display* display;
937     Drawable d;
938     GC gc;
939     int x;
940     int y;
941     unsigned int width;
942     unsigned int height;
943 {
944     HPEN pen, oldPen;
945     TkWinDCState state;
946     HBRUSH oldBrush;
947     HDC dc;
948
949     if (d == None) {
950         return;
951     }
952
953     dc = TkWinGetDrawableDC(display, d, &state);
954
955     pen = SetUpGraphicsPort(gc);
956     SetBkMode(dc, TRANSPARENT);
957     oldPen = SelectObject(dc, pen);
958     oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH));
959     SetROP2(dc, tkpWinRopModes[gc->function]);
960
961     Rectangle(dc, x, y, x+width+1, y+height+1);
962
963     DeleteObject(SelectObject(dc, oldPen));
964     SelectObject(dc, oldBrush);
965     TkWinReleaseDrawableDC(d, dc, &state);
966 }
967 \f
968 /*
969  *----------------------------------------------------------------------
970  *
971  * XDrawArc --
972  *
973  *      Draw an arc.
974  *
975  * Results:
976  *      None.
977  *
978  * Side effects:
979  *      Draws an arc on the specified drawable.
980  *
981  *----------------------------------------------------------------------
982  */
983
984 void
985 XDrawArc(display, d, gc, x, y, width, height, start, extent)
986     Display* display;
987     Drawable d;
988     GC gc;
989     int x;
990     int y;
991     unsigned int width;
992     unsigned int height;
993     int start;
994     int extent;
995 {
996     display->request++;
997
998     DrawOrFillArc(display, d, gc, x, y, width, height, start, extent, 0);
999 }
1000 \f
1001 /*
1002  *----------------------------------------------------------------------
1003  *
1004  * XFillArc --
1005  *
1006  *      Draw a filled arc.
1007  *
1008  * Results:
1009  *      None.
1010  *
1011  * Side effects:
1012  *      Draws a filled arc on the specified drawable.
1013  *
1014  *----------------------------------------------------------------------
1015  */
1016
1017 void
1018 XFillArc(display, d, gc, x, y, width, height, start, extent)
1019     Display* display;
1020     Drawable d;
1021     GC gc;
1022     int x;
1023     int y;
1024     unsigned int width;
1025     unsigned int height;
1026     int start;
1027     int extent;
1028 {
1029     display->request++;
1030
1031     DrawOrFillArc(display, d, gc, x, y, width, height, start, extent, 1);
1032 }
1033 \f
1034 /*
1035  *----------------------------------------------------------------------
1036  *
1037  * DrawOrFillArc --
1038  *
1039  *      This procedure handles the rendering of drawn or filled
1040  *      arcs and chords.
1041  *
1042  * Results:
1043  *      None.
1044  *
1045  * Side effects:
1046  *      Renders the requested arc.
1047  *
1048  *----------------------------------------------------------------------
1049  */
1050
1051 static void
1052 DrawOrFillArc(display, d, gc, x, y, width, height, start, extent, fill)
1053     Display *display;
1054     Drawable d;
1055     GC gc;
1056     int x, y;                   /* left top */
1057     unsigned int width, height;
1058     int start;                  /* start: three-o'clock (deg*64) */
1059     int extent;                 /* extent: relative (deg*64) */
1060     int fill;                   /* ==0 draw, !=0 fill */
1061 {
1062     HDC dc;
1063     HBRUSH brush, oldBrush;
1064     HPEN pen, oldPen;
1065     TkWinDCState state;
1066     int clockwise = (extent < 0); /* non-zero if clockwise */
1067     int xstart, ystart, xend, yend;
1068     double radian_start, radian_end, xr, yr;
1069
1070     if (d == None) {
1071         return;
1072     }
1073
1074     dc = TkWinGetDrawableDC(display, d, &state);
1075
1076     SetROP2(dc, tkpWinRopModes[gc->function]);
1077
1078     /*
1079      * Compute the absolute starting and ending angles in normalized radians.
1080      * Swap the start and end if drawing clockwise.
1081      */
1082
1083     start = start % (64*360);
1084     if (start < 0) {
1085         start += (64*360);
1086     }
1087     extent = (start+extent) % (64*360);
1088     if (extent < 0) {
1089         extent += (64*360);
1090     }
1091     if (clockwise) {
1092         int tmp = start;
1093         start = extent;
1094         extent = tmp;
1095     }
1096     radian_start = XAngleToRadians(start);
1097     radian_end = XAngleToRadians(extent);
1098
1099     /*
1100      * Now compute points on the radial lines that define the starting and
1101      * ending angles.  Be sure to take into account that the y-coordinate
1102      * system is inverted.
1103      */
1104
1105     xr = x + width / 2.0;
1106     yr = y + height / 2.0;
1107     xstart = (int)((xr + cos(radian_start)*width/2.0) + 0.5);
1108     ystart = (int)((yr + sin(-radian_start)*height/2.0) + 0.5);
1109     xend = (int)((xr + cos(radian_end)*width/2.0) + 0.5);
1110     yend = (int)((yr + sin(-radian_end)*height/2.0) + 0.5);
1111
1112     /*
1113      * Now draw a filled or open figure.  Note that we have to
1114      * increase the size of the bounding box by one to account for the
1115      * difference in pixel definitions between X and Windows.
1116      */
1117
1118     pen = SetUpGraphicsPort(gc);
1119     oldPen = SelectObject(dc, pen);
1120     if (!fill) {
1121         /*
1122          * Note that this call will leave a gap of one pixel at the
1123          * end of the arc for thin arcs.  We can't use ArcTo because
1124          * it's only supported under Windows NT.
1125          */
1126
1127         SetBkMode(dc, TRANSPARENT);
1128         Arc(dc, x, y, x+width+1, y+height+1, xstart, ystart, xend, yend);
1129     } else {
1130         brush = CreateSolidBrush(gc->foreground);
1131         oldBrush = SelectObject(dc, brush);
1132         if (gc->arc_mode == ArcChord) {
1133             Chord(dc, x, y, x+width+1, y+height+1, xstart, ystart, xend, yend);
1134         } else if ( gc->arc_mode == ArcPieSlice ) {
1135             Pie(dc, x, y, x+width+1, y+height+1, xstart, ystart, xend, yend);
1136         }
1137         DeleteObject(SelectObject(dc, oldBrush));
1138     }
1139     DeleteObject(SelectObject(dc, oldPen));
1140     TkWinReleaseDrawableDC(d, dc, &state);
1141 }
1142 \f
1143 /*
1144  *----------------------------------------------------------------------
1145  *
1146  * SetUpGraphicsPort --
1147  *
1148  *      Set up the graphics port from the given GC.
1149  *
1150  * Results:
1151  *      None.
1152  *
1153  * Side effects:
1154  *      The current port is adjusted.
1155  *
1156  *----------------------------------------------------------------------
1157  */
1158
1159 static HPEN
1160 SetUpGraphicsPort(gc)
1161     GC gc;
1162 {
1163     DWORD style;
1164
1165     if (gc->line_style == LineOnOffDash) {
1166         unsigned char *p = (unsigned char *) &(gc->dashes);
1167                                 /* pointer to the dash-list */
1168
1169         /*
1170          * Below is a simple translation of serveral dash patterns
1171          * to valid windows pen types. Far from complete,
1172          * but I don't know how to do it better.
1173          * Any ideas: <mailto:j.nijtmans@chello.nl>
1174          */
1175
1176         if (p[1] && p[2]) {
1177             if (!p[3] || p[4]) {
1178                 style = PS_DASHDOTDOT;          /*      -..     */
1179             } else {
1180                 style = PS_DASHDOT;             /*      -.      */
1181             }
1182         } else {
1183             if (p[0] > (4 * gc->line_width)) {
1184                 style = PS_DASH;                /*      -       */
1185             } else {
1186                 style = PS_DOT;                 /*      .       */
1187             }
1188         }
1189     } else {
1190         style = PS_SOLID;
1191     }
1192     if (gc->line_width < 2) {
1193         return CreatePen(style, gc->line_width, gc->foreground);
1194     } else {
1195         LOGBRUSH lb;
1196
1197         lb.lbStyle = BS_SOLID;
1198         lb.lbColor = gc->foreground;
1199         lb.lbHatch = 0;
1200
1201         style |= PS_GEOMETRIC;
1202         switch (gc->cap_style) {
1203             case CapNotLast:
1204             case CapButt:
1205                 style |= PS_ENDCAP_FLAT; 
1206                 break;
1207             case CapRound:
1208                 style |= PS_ENDCAP_ROUND; 
1209                 break;
1210             default:
1211                 style |= PS_ENDCAP_SQUARE; 
1212                 break;
1213         }
1214         switch (gc->join_style) {
1215             case JoinMiter: 
1216                 style |= PS_JOIN_MITER; 
1217                 break;
1218             case JoinRound:
1219                 style |= PS_JOIN_ROUND; 
1220                 break;
1221             default:
1222                 style |= PS_JOIN_BEVEL; 
1223                 break;
1224         }
1225         return ExtCreatePen(style, gc->line_width, &lb, 0, NULL);
1226     }
1227 }
1228 \f
1229 /*
1230  *----------------------------------------------------------------------
1231  *
1232  * TkScrollWindow --
1233  *
1234  *      Scroll a rectangle of the specified window and accumulate
1235  *      a damage region.
1236  *
1237  * Results:
1238  *      Returns 0 if the scroll genereated no additional damage.
1239  *      Otherwise, sets the region that needs to be repainted after
1240  *      scrolling and returns 1.
1241  *
1242  * Side effects:
1243  *      Scrolls the bits in the window.
1244  *
1245  *----------------------------------------------------------------------
1246  */
1247
1248 int
1249 TkScrollWindow(tkwin, gc, x, y, width, height, dx, dy, damageRgn)
1250     Tk_Window tkwin;            /* The window to be scrolled. */
1251     GC gc;                      /* GC for window to be scrolled. */
1252     int x, y, width, height;    /* Position rectangle to be scrolled. */
1253     int dx, dy;                 /* Distance rectangle should be moved. */
1254     TkRegion damageRgn;         /* Region to accumulate damage in. */
1255 {
1256     HWND hwnd = TkWinGetHWND(Tk_WindowId(tkwin));
1257     RECT scrollRect;
1258
1259     scrollRect.left = x;
1260     scrollRect.top = y;
1261     scrollRect.right = x + width;
1262     scrollRect.bottom = y + height;
1263     return (ScrollWindowEx(hwnd, dx, dy, &scrollRect, NULL, (HRGN) damageRgn,
1264             NULL, 0) == NULLREGION) ? 0 : 1;
1265 }
1266 \f
1267 /*
1268  *----------------------------------------------------------------------
1269  *
1270  * TkWinFillRect --
1271  *
1272  *      This routine fills a rectangle with the foreground color
1273  *      from the specified GC ignoring all other GC values.  This
1274  *      is the fastest way to fill a drawable with a solid color.
1275  *
1276  * Results:
1277  *      None.
1278  *
1279  * Side effects:
1280  *      Modifies the contents of the DC drawing surface.
1281  *
1282  *----------------------------------------------------------------------
1283  */
1284
1285 void
1286 TkWinFillRect(dc, x, y, width, height, pixel)
1287     HDC dc;
1288     int x, y, width, height;
1289     int pixel;
1290 {
1291     RECT rect;
1292     COLORREF oldColor;
1293
1294     rect.left = x;
1295     rect.top = y;
1296     rect.right = x + width;
1297     rect.bottom = y + height;
1298     oldColor = SetBkColor(dc, (COLORREF)pixel);
1299     SetBkMode(dc, OPAQUE);
1300     ExtTextOut(dc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
1301     SetBkColor(dc, oldColor);
1302 }
1303 \f
1304 /*
1305  *----------------------------------------------------------------------
1306  *
1307  * TkpDrawHighlightBorder --
1308  *
1309  *      This procedure draws a rectangular ring around the outside of
1310  *      a widget to indicate that it has received the input focus.
1311  *
1312  *      On Windows, we just draw the simple inset ring.  On other sytems,
1313  *      e.g. the Mac, the focus ring is a little more complicated, so we
1314  *      need this abstraction.
1315  *
1316  * Results:
1317  *      None.
1318  *
1319  * Side effects:
1320  *      A rectangle "width" pixels wide is drawn in "drawable",
1321  *      corresponding to the outer area of "tkwin".
1322  *
1323  *----------------------------------------------------------------------
1324  */
1325
1326 void 
1327 TkpDrawHighlightBorder(tkwin, fgGC, bgGC, highlightWidth, drawable)
1328     Tk_Window tkwin;
1329     GC fgGC;
1330     GC bgGC;
1331     int highlightWidth;
1332     Drawable drawable;
1333 {
1334     TkDrawInsetFocusHighlight(tkwin, fgGC, highlightWidth, drawable, 0);
1335 }
1336
1337