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