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 (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 of
11 * 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
29int 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 of
50 * the operations defined in X don't have names, so we have to construct new
51 * 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
60int tkpWinBltModes[] = {
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 pattern.
81 * This is used to draw in a foreground color but leave the background color
82 * 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
106typedef BOOL (CALLBACK *WinDrawFunc)(HDC dc, CONST POINT* points, int npoints);
107
108typedef struct ThreadSpecificData {
109    POINT *winPoints;		/* Array of points that is reused. */
110    int nWinPoints;		/* Current size of point array. */
111} ThreadSpecificData;
112static Tcl_ThreadDataKey dataKey;
113
114/*
115 * Forward declarations for functions defined in this file:
116 */
117
118static POINT *		ConvertPoints(XPoint *points, int npoints, int mode,
119			    RECT *bbox);
120static void		DrawOrFillArc(Display *display, Drawable d, GC gc,
121			    int x, int y, unsigned int width,
122			    unsigned int height, int start, int extent,
123			    int fill);
124static void		RenderObject(HDC dc, GC gc, XPoint* points,
125			    int npoints, int mode, HPEN pen, WinDrawFunc func);
126static HPEN		SetUpGraphicsPort(GC gc);
127
128/*
129 *----------------------------------------------------------------------
130 *
131 * TkWinGetDrawableDC --
132 *
133 *	Retrieve the DC from a drawable.
134 *
135 * Results:
136 *	Returns the window DC for windows. Returns a new memory DC for
137 *	pixmaps.
138 *
139 * Side effects:
140 *	Sets up the palette for the device context, and saves the old device
141 *	context state in the passed in TkWinDCState structure.
142 *
143 *----------------------------------------------------------------------
144 */
145
146HDC
147TkWinGetDrawableDC(
148    Display *display,
149    Drawable d,
150    TkWinDCState *state)
151{
152    HDC dc;
153    TkWinDrawable *twdPtr = (TkWinDrawable *)d;
154    Colormap cmap;
155
156    if (twdPtr->type == TWD_WINDOW) {
157	TkWindow *winPtr = twdPtr->window.winPtr;
158
159 	dc = GetDC(twdPtr->window.handle);
160	if (winPtr == NULL) {
161	    cmap = DefaultColormap(display, DefaultScreen(display));
162	} else {
163	    cmap = winPtr->atts.colormap;
164	}
165    } else if (twdPtr->type == TWD_WINDC) {
166	dc = twdPtr->winDC.hdc;
167	cmap = DefaultColormap(display, DefaultScreen(display));
168    } else {
169	dc = CreateCompatibleDC(NULL);
170	SelectObject(dc, twdPtr->bitmap.handle);
171	cmap = twdPtr->bitmap.colormap;
172    }
173    state->palette = TkWinSelectPalette(dc, cmap);
174    state->bkmode  = GetBkMode(dc);
175    return dc;
176}
177
178/*
179 *----------------------------------------------------------------------
180 *
181 * TkWinReleaseDrawableDC --
182 *
183 *	Frees the resources associated with a drawable's DC.
184 *
185 * Results:
186 *	None.
187 *
188 * Side effects:
189 *	Restores the old bitmap handle to the memory DC for pixmaps.
190 *
191 *----------------------------------------------------------------------
192 */
193
194void
195TkWinReleaseDrawableDC(
196    Drawable d,
197    HDC dc,
198    TkWinDCState *state)
199{
200    TkWinDrawable *twdPtr = (TkWinDrawable *)d;
201
202    SetBkMode(dc, state->bkmode);
203    SelectPalette(dc, state->palette, TRUE);
204    RealizePalette(dc);
205    if (twdPtr->type == TWD_WINDOW) {
206	ReleaseDC(TkWinGetHWND(d), dc);
207    } else if (twdPtr->type == TWD_BITMAP) {
208	DeleteDC(dc);
209    }
210}
211
212/*
213 *----------------------------------------------------------------------
214 *
215 * ConvertPoints --
216 *
217 *	Convert an array of X points to an array of Win32 points.
218 *
219 * Results:
220 *	Returns the converted array of POINTs.
221 *
222 * Side effects:
223 *	Allocates a block of memory in thread local storage that should not be
224 *	freed.
225 *
226 *----------------------------------------------------------------------
227 */
228
229static POINT *
230ConvertPoints(
231    XPoint *points,
232    int npoints,
233    int mode,			/* CoordModeOrigin or CoordModePrevious. */
234    RECT *bbox)			/* Bounding box of points. */
235{
236    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
237	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
238    int i;
239
240    /*
241     * To avoid paying the cost of a malloc on every drawing routine, we reuse
242     * the last array if it is large enough.
243     */
244
245    if (npoints > tsdPtr->nWinPoints) {
246	if (tsdPtr->winPoints != NULL) {
247	    ckfree((char *) tsdPtr->winPoints);
248	}
249	tsdPtr->winPoints = (POINT *) ckalloc(sizeof(POINT) * npoints);
250	if (tsdPtr->winPoints == NULL) {
251	    tsdPtr->nWinPoints = -1;
252	    return NULL;
253	}
254	tsdPtr->nWinPoints = npoints;
255    }
256
257    bbox->left = bbox->right = points[0].x;
258    bbox->top = bbox->bottom = points[0].y;
259
260    if (mode == CoordModeOrigin) {
261	for (i = 0; i < npoints; i++) {
262	    tsdPtr->winPoints[i].x = points[i].x;
263	    tsdPtr->winPoints[i].y = points[i].y;
264	    bbox->left = MIN(bbox->left, tsdPtr->winPoints[i].x);
265	    bbox->right = MAX(bbox->right, tsdPtr->winPoints[i].x);
266	    bbox->top = MIN(bbox->top, tsdPtr->winPoints[i].y);
267	    bbox->bottom = MAX(bbox->bottom, tsdPtr->winPoints[i].y);
268	}
269    } else {
270	tsdPtr->winPoints[0].x = points[0].x;
271	tsdPtr->winPoints[0].y = points[0].y;
272	for (i = 1; i < npoints; i++) {
273	    tsdPtr->winPoints[i].x = tsdPtr->winPoints[i-1].x + points[i].x;
274	    tsdPtr->winPoints[i].y = tsdPtr->winPoints[i-1].y + points[i].y;
275	    bbox->left = MIN(bbox->left, tsdPtr->winPoints[i].x);
276	    bbox->right = MAX(bbox->right, tsdPtr->winPoints[i].x);
277	    bbox->top = MIN(bbox->top, tsdPtr->winPoints[i].y);
278	    bbox->bottom = MAX(bbox->bottom, tsdPtr->winPoints[i].y);
279	}
280    }
281    return tsdPtr->winPoints;
282}
283
284/*
285 *----------------------------------------------------------------------
286 *
287 * XCopyArea --
288 *
289 *	Copies data from one drawable to another using block transfer
290 *	routines.
291 *
292 * Results:
293 *	None.
294 *
295 * Side effects:
296 *	Data is moved from a window or bitmap to a second window or bitmap.
297 *
298 *----------------------------------------------------------------------
299 */
300
301void
302XCopyArea(
303    Display *display,
304    Drawable src,
305    Drawable dest,
306    GC gc,
307    int src_x, int src_y,
308    unsigned int width, unsigned int height,
309    int dest_x, int dest_y)
310{
311    HDC srcDC, destDC;
312    TkWinDCState srcState, destState;
313    TkpClipMask *clipPtr = (TkpClipMask*)gc->clip_mask;
314
315    srcDC = TkWinGetDrawableDC(display, src, &srcState);
316
317    if (src != dest) {
318	destDC = TkWinGetDrawableDC(display, dest, &destState);
319    } else {
320	destDC = srcDC;
321    }
322
323    if (clipPtr && clipPtr->type == TKP_CLIP_REGION) {
324	SelectClipRgn(destDC, (HRGN) clipPtr->value.region);
325	OffsetClipRgn(destDC, gc->clip_x_origin, gc->clip_y_origin);
326    }
327
328    BitBlt(destDC, dest_x, dest_y, (int) width, (int) height, srcDC,
329	    src_x, src_y, (DWORD) tkpWinBltModes[gc->function]);
330
331    SelectClipRgn(destDC, NULL);
332
333    if (src != dest) {
334	TkWinReleaseDrawableDC(dest, destDC, &destState);
335    }
336    TkWinReleaseDrawableDC(src, srcDC, &srcState);
337}
338
339/*
340 *----------------------------------------------------------------------
341 *
342 * XCopyPlane --
343 *
344 *	Copies a bitmap from a source drawable to a destination drawable. The
345 *	plane argument specifies which bit plane of the source contains the
346 *	bitmap. Note that this implementation ignores the gc->function.
347 *
348 * Results:
349 *	None.
350 *
351 * Side effects:
352 *	Changes the destination drawable.
353 *
354 *----------------------------------------------------------------------
355 */
356
357void
358XCopyPlane(
359    Display *display,
360    Drawable src,
361    Drawable dest,
362    GC gc,
363    int src_x, int src_y,
364    unsigned int width, unsigned int height,
365    int dest_x, int dest_y,
366    unsigned long plane)
367{
368    HDC srcDC, destDC;
369    TkWinDCState srcState, destState;
370    HBRUSH bgBrush, fgBrush, oldBrush;
371    TkpClipMask *clipPtr = (TkpClipMask*)gc->clip_mask;
372
373    display->request++;
374
375    if (plane != 1) {
376	Tcl_Panic("Unexpected plane specified for XCopyPlane");
377    }
378
379    srcDC = TkWinGetDrawableDC(display, src, &srcState);
380
381    if (src != dest) {
382	destDC = TkWinGetDrawableDC(display, dest, &destState);
383    } else {
384	destDC = srcDC;
385    }
386
387    if (clipPtr == NULL || clipPtr->type == TKP_CLIP_REGION) {
388	/*
389	 * Case 1: opaque bitmaps. Windows handles the conversion from one bit
390	 * to multiple bits by setting 0 to the foreground color, and 1 to the
391	 * background color (seems backwards, but there you are).
392	 */
393
394	if (clipPtr && clipPtr->type == TKP_CLIP_REGION) {
395	    SelectClipRgn(destDC, (HRGN) clipPtr->value.region);
396	    OffsetClipRgn(destDC, gc->clip_x_origin, gc->clip_y_origin);
397	}
398
399	SetBkMode(destDC, OPAQUE);
400	SetBkColor(destDC, gc->foreground);
401	SetTextColor(destDC, gc->background);
402	BitBlt(destDC, dest_x, dest_y, (int) width, (int) height, srcDC,
403		src_x, src_y, SRCCOPY);
404
405	SelectClipRgn(destDC, NULL);
406    } else if (clipPtr->type == TKP_CLIP_PIXMAP) {
407	if (clipPtr->value.pixmap == src) {
408
409	    /*
410	     * Case 2: transparent bitmaps are handled by setting the
411	     * destination to the foreground color whenever the source pixel
412	     * is set.
413	     */
414
415	    fgBrush = CreateSolidBrush(gc->foreground);
416	    oldBrush = SelectObject(destDC, fgBrush);
417	    SetBkColor(destDC, RGB(255,255,255));
418	    SetTextColor(destDC, RGB(0,0,0));
419	    BitBlt(destDC, dest_x, dest_y, (int) width, (int) height, srcDC,
420		    src_x, src_y, MASKPAT);
421	    SelectObject(destDC, oldBrush);
422	    DeleteObject(fgBrush);
423	} else {
424
425	    /*
426	     * Case 3: two arbitrary bitmaps. Copy the source rectangle into a
427	     * color pixmap. Use the result as a brush when copying the clip
428	     * mask into the destination.
429	     */
430
431	    HDC memDC, maskDC;
432	    HBITMAP bitmap;
433	    TkWinDCState maskState;
434
435	    fgBrush = CreateSolidBrush(gc->foreground);
436	    bgBrush = CreateSolidBrush(gc->background);
437	    maskDC = TkWinGetDrawableDC(display, clipPtr->value.pixmap,
438		    &maskState);
439	    memDC = CreateCompatibleDC(destDC);
440	    bitmap = CreateBitmap((int) width, (int) height, 1, 1, NULL);
441	    SelectObject(memDC, bitmap);
442
443	    /*
444	     * Set foreground bits. We create a new bitmap containing (source
445	     * AND mask), then use it to set the foreground color into the
446	     * destination.
447	     */
448
449	    BitBlt(memDC, 0, 0, (int) width, (int) height, srcDC, src_x, src_y,
450		    SRCCOPY);
451	    BitBlt(memDC, 0, 0, (int) width, (int) height, maskDC,
452		    dest_x - gc->clip_x_origin, dest_y - gc->clip_y_origin,
453		    SRCAND);
454	    oldBrush = SelectObject(destDC, fgBrush);
455	    BitBlt(destDC, dest_x, dest_y, (int) width, (int) height, memDC,
456		    0, 0, MASKPAT);
457
458	    /*
459	     * Set background bits. Same as foreground, except we use ((NOT
460	     * source) AND mask) and the background brush.
461	     */
462
463	    BitBlt(memDC, 0, 0, (int) width, (int) height, srcDC, src_x, src_y,
464		    NOTSRCCOPY);
465	    BitBlt(memDC, 0, 0, (int) width, (int) height, maskDC,
466		    dest_x - gc->clip_x_origin, dest_y - gc->clip_y_origin,
467		    SRCAND);
468	    SelectObject(destDC, bgBrush);
469	    BitBlt(destDC, dest_x, dest_y, (int) width, (int) height, memDC,
470		    0, 0, MASKPAT);
471
472	    TkWinReleaseDrawableDC(clipPtr->value.pixmap, maskDC, &maskState);
473	    SelectObject(destDC, oldBrush);
474	    DeleteDC(memDC);
475	    DeleteObject(bitmap);
476	    DeleteObject(fgBrush);
477	    DeleteObject(bgBrush);
478	}
479    }
480    if (src != dest) {
481	TkWinReleaseDrawableDC(dest, destDC, &destState);
482    }
483    TkWinReleaseDrawableDC(src, srcDC, &srcState);
484}
485
486/*
487 *----------------------------------------------------------------------
488 *
489 * TkPutImage --
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
503void
504TkPutImage(
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((char *) infoPtr);
587    }
588    if (!bitmap) {
589	Tcl_Panic("Fail to allocate bitmap\n");
590	DeleteDC(dcMem);
591    	TkWinReleaseDrawableDC(d, dc, &state);
592	return;
593    }
594    bitmap = SelectObject(dcMem, bitmap);
595    BitBlt(dc, dest_x, dest_y, (int) width, (int) height, dcMem, src_x, src_y,
596	    SRCCOPY);
597    DeleteObject(SelectObject(dcMem, bitmap));
598    DeleteDC(dcMem);
599    TkWinReleaseDrawableDC(d, dc, &state);
600}
601
602/*
603 *----------------------------------------------------------------------
604 *
605 * XFillRectangles --
606 *
607 *	Fill multiple rectangular areas in the given drawable.
608 *
609 * Results:
610 *	None.
611 *
612 * Side effects:
613 *	Draws onto the specified drawable.
614 *
615 *----------------------------------------------------------------------
616 */
617
618void
619XFillRectangles(
620    Display *display,
621    Drawable d,
622    GC gc,
623    XRectangle *rectangles,
624    int nrectangles)
625{
626    HDC dc;
627    int i;
628    RECT rect;
629    TkWinDCState state;
630    HBRUSH brush, oldBrush;
631
632    if (d == None) {
633	return;
634    }
635
636    dc = TkWinGetDrawableDC(display, d, &state);
637    SetROP2(dc, tkpWinRopModes[gc->function]);
638    brush = CreateSolidBrush(gc->foreground);
639
640    if ((gc->fill_style == FillStippled
641	    || gc->fill_style == FillOpaqueStippled)
642	    && gc->stipple != None) {
643	TkWinDrawable *twdPtr = (TkWinDrawable *)gc->stipple;
644	HBRUSH stipple;
645	HBITMAP oldBitmap, bitmap;
646	HDC dcMem;
647	HBRUSH bgBrush = CreateSolidBrush(gc->background);
648
649	if (twdPtr->type != TWD_BITMAP) {
650	    Tcl_Panic("unexpected drawable type in stipple");
651	}
652
653	/*
654	 * Select stipple pattern into destination dc.
655	 */
656
657	stipple = CreatePatternBrush(twdPtr->bitmap.handle);
658	SetBrushOrgEx(dc, gc->ts_x_origin, gc->ts_y_origin, NULL);
659	oldBrush = SelectObject(dc, stipple);
660	dcMem = CreateCompatibleDC(dc);
661
662	/*
663	 * For each rectangle, create a drawing surface which is the size of
664	 * the rectangle and fill it with the background color. Then merge the
665	 * result with the stipple pattern.
666	 */
667
668	for (i = 0; i < nrectangles; i++) {
669	    bitmap = CreateCompatibleBitmap(dc, rectangles[i].width,
670		    rectangles[i].height);
671	    oldBitmap = SelectObject(dcMem, bitmap);
672	    rect.left = 0;
673	    rect.top = 0;
674	    rect.right = rectangles[i].width;
675	    rect.bottom = rectangles[i].height;
676	    FillRect(dcMem, &rect, brush);
677	    BitBlt(dc, rectangles[i].x, rectangles[i].y, rectangles[i].width,
678		    rectangles[i].height, dcMem, 0, 0, COPYFG);
679	    if (gc->fill_style == FillOpaqueStippled) {
680		FillRect(dcMem, &rect, bgBrush);
681		BitBlt(dc, rectangles[i].x, rectangles[i].y,
682			rectangles[i].width, rectangles[i].height, dcMem,
683			0, 0, COPYBG);
684	    }
685	    SelectObject(dcMem, oldBitmap);
686	    DeleteObject(bitmap);
687	}
688
689	DeleteDC(dcMem);
690	SelectObject(dc, oldBrush);
691	DeleteObject(stipple);
692	DeleteObject(bgBrush);
693    } else {
694	if (gc->function == GXcopy) {
695	    for (i = 0; i < nrectangles; i++) {
696		rect.left = rectangles[i].x;
697		rect.right = rect.left + rectangles[i].width;
698		rect.top = rectangles[i].y;
699		rect.bottom = rect.top + rectangles[i].height;
700		FillRect(dc, &rect, brush);
701	    }
702	} else {
703	    HPEN newPen = CreatePen(PS_NULL, 0, gc->foreground);
704	    HPEN oldPen = SelectObject(dc, newPen);
705	    oldBrush = SelectObject(dc, brush);
706
707	    for (i = 0; i < nrectangles; i++) {
708		Rectangle(dc, rectangles[i].x, rectangles[i].y,
709		    rectangles[i].x + rectangles[i].width + 1,
710		    rectangles[i].y + rectangles[i].height + 1);
711	    }
712
713	    SelectObject(dc, oldBrush);
714	    SelectObject(dc, oldPen);
715	    DeleteObject(newPen);
716	}
717    }
718    DeleteObject(brush);
719    TkWinReleaseDrawableDC(d, dc, &state);
720}
721
722/*
723 *----------------------------------------------------------------------
724 *
725 * RenderObject --
726 *
727 *	This function draws a shape using a list of points, a stipple pattern,
728 *	and the specified drawing function.
729 *
730 * Results:
731 *	None.
732 *
733 * Side effects:
734 *	None.
735 *
736 *----------------------------------------------------------------------
737 */
738
739static void
740RenderObject(
741    HDC dc,
742    GC gc,
743    XPoint *points,
744    int npoints,
745    int mode,
746    HPEN pen,
747    WinDrawFunc func)
748{
749    RECT rect;
750    HPEN oldPen;
751    HBRUSH oldBrush;
752    POINT *winPoints = ConvertPoints(points, npoints, mode, &rect);
753
754    if ((gc->fill_style == FillStippled
755	    || gc->fill_style == FillOpaqueStippled)
756	    && gc->stipple != None) {
757
758	TkWinDrawable *twdPtr = (TkWinDrawable *)gc->stipple;
759	HDC dcMem;
760	LONG width, height;
761	HBITMAP oldBitmap;
762	int i;
763	HBRUSH oldMemBrush;
764
765	if (twdPtr->type != TWD_BITMAP) {
766	    Tcl_Panic("unexpected drawable type in stipple");
767	}
768
769	/*
770	 * Grow the bounding box enough to account for line width.
771	 */
772
773	rect.left -= gc->line_width;
774	rect.top -= gc->line_width;
775	rect.right += gc->line_width;
776	rect.bottom += gc->line_width;
777
778	width = rect.right - rect.left;
779	height = rect.bottom - rect.top;
780
781	/*
782	 * Select stipple pattern into destination dc.
783	 */
784
785	SetBrushOrgEx(dc, gc->ts_x_origin, gc->ts_y_origin, NULL);
786	oldBrush = SelectObject(dc, CreatePatternBrush(twdPtr->bitmap.handle));
787
788	/*
789	 * Create temporary drawing surface containing a copy of the
790	 * destination equal in size to the bounding box of the object.
791	 */
792
793	dcMem = CreateCompatibleDC(dc);
794	oldBitmap = SelectObject(dcMem, CreateCompatibleBitmap(dc, width,
795		height));
796	oldPen = SelectObject(dcMem, pen);
797	BitBlt(dcMem, 0, 0, width, height, dc, rect.left, rect.top, SRCCOPY);
798
799	/*
800	 * Translate the object for rendering in the temporary drawing
801	 * surface.
802	 */
803
804	for (i = 0; i < npoints; i++) {
805	    winPoints[i].x -= rect.left;
806	    winPoints[i].y -= rect.top;
807	}
808
809	/*
810	 * Draw the object in the foreground color and copy it to the
811	 * destination wherever the pattern is set.
812	 */
813
814	SetPolyFillMode(dcMem, (gc->fill_rule == EvenOddRule) ? ALTERNATE
815		: WINDING);
816	oldMemBrush = SelectObject(dcMem, CreateSolidBrush(gc->foreground));
817	(*func)(dcMem, winPoints, npoints);
818	BitBlt(dc, rect.left, rect.top, width, height, dcMem, 0, 0, COPYFG);
819
820	/*
821	 * If we are rendering an opaque stipple, then draw the polygon in the
822	 * background color and copy it to the destination wherever the
823	 * pattern is clear.
824	 */
825
826	if (gc->fill_style == FillOpaqueStippled) {
827	    DeleteObject(SelectObject(dcMem,
828		    CreateSolidBrush(gc->background)));
829	    (*func)(dcMem, winPoints, npoints);
830	    BitBlt(dc, rect.left, rect.top, width, height, dcMem, 0, 0,
831		    COPYBG);
832	}
833
834	SelectObject(dcMem, oldPen);
835	DeleteObject(SelectObject(dcMem, oldMemBrush));
836	DeleteObject(SelectObject(dcMem, oldBitmap));
837	DeleteDC(dcMem);
838    } else {
839	oldPen = SelectObject(dc, pen);
840	oldBrush = SelectObject(dc, CreateSolidBrush(gc->foreground));
841	SetROP2(dc, tkpWinRopModes[gc->function]);
842
843	SetPolyFillMode(dc, (gc->fill_rule == EvenOddRule) ? ALTERNATE
844		: WINDING);
845
846	(*func)(dc, winPoints, npoints);
847
848	SelectObject(dc, oldPen);
849    }
850    DeleteObject(SelectObject(dc, oldBrush));
851}
852
853/*
854 *----------------------------------------------------------------------
855 *
856 * XDrawLines --
857 *
858 *	Draw connected lines.
859 *
860 * Results:
861 *	None.
862 *
863 * Side effects:
864 *	Renders a series of connected lines.
865 *
866 *----------------------------------------------------------------------
867 */
868
869void
870XDrawLines(
871    Display *display,
872    Drawable d,
873    GC gc,
874    XPoint *points,
875    int npoints,
876    int mode)
877{
878    HPEN pen;
879    TkWinDCState state;
880    HDC dc;
881
882    if (d == None) {
883	return;
884    }
885
886    dc = TkWinGetDrawableDC(display, d, &state);
887
888    pen = SetUpGraphicsPort(gc);
889    SetBkMode(dc, TRANSPARENT);
890    RenderObject(dc, gc, points, npoints, mode, pen, Polyline);
891    DeleteObject(pen);
892
893    TkWinReleaseDrawableDC(d, dc, &state);
894}
895
896/*
897 *----------------------------------------------------------------------
898 *
899 * XFillPolygon --
900 *
901 *	Draws a filled polygon.
902 *
903 * Results:
904 *	None.
905 *
906 * Side effects:
907 *	Draws a filled polygon on the specified drawable.
908 *
909 *----------------------------------------------------------------------
910 */
911
912void
913XFillPolygon(
914    Display *display,
915    Drawable d,
916    GC gc,
917    XPoint *points,
918    int npoints,
919    int shape,
920    int mode)
921{
922    HPEN pen;
923    TkWinDCState state;
924    HDC dc;
925
926    if (d == None) {
927	return;
928    }
929
930    dc = TkWinGetDrawableDC(display, d, &state);
931
932    pen = GetStockObject(NULL_PEN);
933    RenderObject(dc, gc, points, npoints, mode, pen, Polygon);
934
935    TkWinReleaseDrawableDC(d, dc, &state);
936}
937
938/*
939 *----------------------------------------------------------------------
940 *
941 * XDrawRectangle --
942 *
943 *	Draws a rectangle.
944 *
945 * Results:
946 *	None.
947 *
948 * Side effects:
949 *	Draws a rectangle on the specified drawable.
950 *
951 *----------------------------------------------------------------------
952 */
953
954void
955XDrawRectangle(
956    Display *display,
957    Drawable d,
958    GC gc,
959    int x, int y,
960    unsigned int width, unsigned int height)
961{
962    HPEN pen, oldPen;
963    TkWinDCState state;
964    HBRUSH oldBrush;
965    HDC dc;
966
967    if (d == None) {
968	return;
969    }
970
971    dc = TkWinGetDrawableDC(display, d, &state);
972
973    pen = SetUpGraphicsPort(gc);
974    SetBkMode(dc, TRANSPARENT);
975    oldPen = SelectObject(dc, pen);
976    oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH));
977    SetROP2(dc, tkpWinRopModes[gc->function]);
978
979    Rectangle(dc, x, y, (int) x+width+1, (int) y+height+1);
980
981    DeleteObject(SelectObject(dc, oldPen));
982    SelectObject(dc, oldBrush);
983    TkWinReleaseDrawableDC(d, dc, &state);
984}
985
986/*
987 *----------------------------------------------------------------------
988 *
989 * XDrawArc --
990 *
991 *	Draw an arc.
992 *
993 * Results:
994 *	None.
995 *
996 * Side effects:
997 *	Draws an arc on the specified drawable.
998 *
999 *----------------------------------------------------------------------
1000 */
1001
1002void
1003XDrawArc(
1004    Display *display,
1005    Drawable d,
1006    GC gc,
1007    int x, int y,
1008    unsigned int width, unsigned int height,
1009    int start, int extent)
1010{
1011    display->request++;
1012
1013    DrawOrFillArc(display, d, gc, x, y, width, height, start, extent, 0);
1014}
1015
1016/*
1017 *----------------------------------------------------------------------
1018 *
1019 * XFillArc --
1020 *
1021 *	Draw a filled arc.
1022 *
1023 * Results:
1024 *	None.
1025 *
1026 * Side effects:
1027 *	Draws a filled arc on the specified drawable.
1028 *
1029 *----------------------------------------------------------------------
1030 */
1031
1032void
1033XFillArc(
1034    Display *display,
1035    Drawable d,
1036    GC gc,
1037    int x, int y,
1038    unsigned int width, unsigned int height,
1039    int start, int extent)
1040{
1041    display->request++;
1042
1043    DrawOrFillArc(display, d, gc, x, y, width, height, start, extent, 1);
1044}
1045
1046/*
1047 *----------------------------------------------------------------------
1048 *
1049 * DrawOrFillArc --
1050 *
1051 *	This function handles the rendering of drawn or filled arcs and
1052 *	chords.
1053 *
1054 * Results:
1055 *	None.
1056 *
1057 * Side effects:
1058 *	Renders the requested arc.
1059 *
1060 *----------------------------------------------------------------------
1061 */
1062
1063static void
1064DrawOrFillArc(
1065    Display *display,
1066    Drawable d,
1067    GC gc,
1068    int x, int y,		/* left top */
1069    unsigned int width, unsigned int height,
1070    int start,			/* start: three-o'clock (deg*64) */
1071    int extent,			/* extent: relative (deg*64) */
1072    int fill)			/* ==0 draw, !=0 fill */
1073{
1074    HDC dc;
1075    HBRUSH brush, oldBrush;
1076    HPEN pen, oldPen;
1077    TkWinDCState state;
1078    int clockwise = (extent < 0); /* non-zero if clockwise */
1079    int xstart, ystart, xend, yend;
1080    double radian_start, radian_end, xr, yr;
1081
1082    if (d == None) {
1083	return;
1084    }
1085
1086    dc = TkWinGetDrawableDC(display, d, &state);
1087
1088    SetROP2(dc, tkpWinRopModes[gc->function]);
1089
1090    /*
1091     * Compute the absolute starting and ending angles in normalized radians.
1092     * Swap the start and end if drawing clockwise.
1093     */
1094
1095    start = start % (64*360);
1096    if (start < 0) {
1097	start += (64*360);
1098    }
1099    extent = (start+extent) % (64*360);
1100    if (extent < 0) {
1101	extent += (64*360);
1102    }
1103    if (clockwise) {
1104	int tmp = start;
1105	start = extent;
1106	extent = tmp;
1107    }
1108    radian_start = XAngleToRadians(start);
1109    radian_end = XAngleToRadians(extent);
1110
1111    /*
1112     * Now compute points on the radial lines that define the starting and
1113     * ending angles. Be sure to take into account that the y-coordinate
1114     * system is inverted.
1115     */
1116
1117    xr = x + width / 2.0;
1118    yr = y + height / 2.0;
1119    xstart = (int)((xr + cos(radian_start)*width/2.0) + 0.5);
1120    ystart = (int)((yr + sin(-radian_start)*height/2.0) + 0.5);
1121    xend = (int)((xr + cos(radian_end)*width/2.0) + 0.5);
1122    yend = (int)((yr + sin(-radian_end)*height/2.0) + 0.5);
1123
1124    /*
1125     * Now draw a filled or open figure. Note that we have to increase the
1126     * size of the bounding box by one to account for the difference in pixel
1127     * definitions between X and Windows.
1128     */
1129
1130    pen = SetUpGraphicsPort(gc);
1131    oldPen = SelectObject(dc, pen);
1132    if (!fill) {
1133	/*
1134	 * Note that this call will leave a gap of one pixel at the end of the
1135	 * arc for thin arcs. We can't use ArcTo because it's only supported
1136	 * under Windows NT.
1137	 */
1138
1139	SetBkMode(dc, TRANSPARENT);
1140	Arc(dc, x, y, (int) (x+width+1), (int) (y+height+1), xstart, ystart,
1141		xend, yend);
1142    } else {
1143	brush = CreateSolidBrush(gc->foreground);
1144	oldBrush = SelectObject(dc, brush);
1145	if (gc->arc_mode == ArcChord) {
1146	    Chord(dc, x, y, (int) (x+width+1), (int) (y+height+1),
1147		    xstart, ystart, xend, yend);
1148	} else if (gc->arc_mode == ArcPieSlice) {
1149	    Pie(dc, x, y, (int) (x+width+1), (int) (y+height+1),
1150		    xstart, ystart, xend, yend);
1151	}
1152	DeleteObject(SelectObject(dc, oldBrush));
1153    }
1154    DeleteObject(SelectObject(dc, oldPen));
1155    TkWinReleaseDrawableDC(d, dc, &state);
1156}
1157
1158/*
1159 *----------------------------------------------------------------------
1160 *
1161 * SetUpGraphicsPort --
1162 *
1163 *	Set up the graphics port from the given GC.
1164 *
1165 * Results:
1166 *	None.
1167 *
1168 * Side effects:
1169 *	The current port is adjusted.
1170 *
1171 *----------------------------------------------------------------------
1172 */
1173
1174static HPEN
1175SetUpGraphicsPort(
1176    GC gc)
1177{
1178    DWORD style;
1179
1180    if (gc->line_style == LineOnOffDash) {
1181	unsigned char *p = (unsigned char *) &(gc->dashes);
1182				/* pointer to the dash-list */
1183
1184	/*
1185	 * Below is a simple translation of serveral dash patterns to valid
1186	 * windows pen types. Far from complete, but I don't know how to do it
1187	 * better. Any ideas: <mailto:j.nijtmans@chello.nl>
1188	 */
1189
1190	if (p[1] && p[2]) {
1191	    if (!p[3] || p[4]) {
1192		style = PS_DASHDOTDOT;		/*	-..	*/
1193	    } else {
1194		style = PS_DASHDOT;		/*	-.	*/
1195	    }
1196	} else {
1197	    if (p[0] > (4 * gc->line_width)) {
1198		style = PS_DASH;		/*	-	*/
1199	    } else {
1200		style = PS_DOT;			/*	.	*/
1201	    }
1202	}
1203    } else {
1204	style = PS_SOLID;
1205    }
1206    if (gc->line_width < 2) {
1207	return CreatePen((int) style, gc->line_width, gc->foreground);
1208    } else {
1209	LOGBRUSH lb;
1210
1211	lb.lbStyle = BS_SOLID;
1212	lb.lbColor = gc->foreground;
1213	lb.lbHatch = 0;
1214
1215	style |= PS_GEOMETRIC;
1216	switch (gc->cap_style) {
1217	case CapNotLast:
1218	case CapButt:
1219	    style |= PS_ENDCAP_FLAT;
1220	    break;
1221	case CapRound:
1222	    style |= PS_ENDCAP_ROUND;
1223	    break;
1224	default:
1225	    style |= PS_ENDCAP_SQUARE;
1226	    break;
1227	}
1228	switch (gc->join_style) {
1229	case JoinMiter:
1230	    style |= PS_JOIN_MITER;
1231	    break;
1232	case JoinRound:
1233	    style |= PS_JOIN_ROUND;
1234	    break;
1235	default:
1236	    style |= PS_JOIN_BEVEL;
1237	    break;
1238	}
1239	return ExtCreatePen(style, (DWORD) gc->line_width, &lb, 0, NULL);
1240    }
1241}
1242
1243/*
1244 *----------------------------------------------------------------------
1245 *
1246 * TkScrollWindow --
1247 *
1248 *	Scroll a rectangle of the specified window and accumulate a damage
1249 *	region.
1250 *
1251 * Results:
1252 *	Returns 0 if the scroll genereated no additional damage. Otherwise,
1253 *	sets the region that needs to be repainted after scrolling and returns
1254 *	1.
1255 *
1256 * Side effects:
1257 *	Scrolls the bits in the window.
1258 *
1259 *----------------------------------------------------------------------
1260 */
1261
1262int
1263TkScrollWindow(
1264    Tk_Window tkwin,		/* The window to be scrolled. */
1265    GC gc,			/* GC for window to be scrolled. */
1266    int x, int y, int width, int height,
1267				/* Position rectangle to be scrolled. */
1268    int dx, int dy,		/* Distance rectangle should be moved. */
1269    TkRegion damageRgn)		/* Region to accumulate damage in. */
1270{
1271    HWND hwnd = TkWinGetHWND(Tk_WindowId(tkwin));
1272    RECT scrollRect;
1273
1274    scrollRect.left = x;
1275    scrollRect.top = y;
1276    scrollRect.right = x + width;
1277    scrollRect.bottom = y + height;
1278    return (ScrollWindowEx(hwnd, dx, dy, &scrollRect, NULL, (HRGN) damageRgn,
1279	    NULL, 0) == NULLREGION) ? 0 : 1;
1280}
1281
1282/*
1283 *----------------------------------------------------------------------
1284 *
1285 * TkWinFillRect --
1286 *
1287 *	This routine fills a rectangle with the foreground color from the
1288 *	specified GC ignoring all other GC values. This is the fastest way to
1289 *	fill a drawable with a solid color.
1290 *
1291 * Results:
1292 *	None.
1293 *
1294 * Side effects:
1295 *	Modifies the contents of the DC drawing surface.
1296 *
1297 *----------------------------------------------------------------------
1298 */
1299
1300void
1301TkWinFillRect(
1302    HDC dc,
1303    int x, int y, int width, int height,
1304    int pixel)
1305{
1306    RECT rect;
1307    COLORREF oldColor;
1308
1309    rect.left = x;
1310    rect.top = y;
1311    rect.right = x + width;
1312    rect.bottom = y + height;
1313    oldColor = SetBkColor(dc, (COLORREF)pixel);
1314    SetBkMode(dc, OPAQUE);
1315    ExtTextOut(dc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
1316    SetBkColor(dc, oldColor);
1317}
1318
1319/*
1320 *----------------------------------------------------------------------
1321 *
1322 * TkpDrawHighlightBorder --
1323 *
1324 *	This function draws a rectangular ring around the outside of a widget
1325 *	to indicate that it has received the input focus.
1326 *
1327 *      On Windows, we just draw the simple inset ring. On other sytems, e.g.
1328 *      the Mac, the focus ring is a little more complicated, so we need this
1329 *      abstraction.
1330 *
1331 * Results:
1332 *	None.
1333 *
1334 * Side effects:
1335 *	A rectangle "width" pixels wide is drawn in "drawable", corresponding
1336 *	to the outer area of "tkwin".
1337 *
1338 *----------------------------------------------------------------------
1339 */
1340
1341void
1342TkpDrawHighlightBorder(
1343    Tk_Window tkwin,
1344    GC fgGC,
1345    GC bgGC,
1346    int highlightWidth,
1347    Drawable drawable)
1348{
1349    TkDrawInsetFocusHighlight(tkwin, fgGC, highlightWidth, drawable, 0);
1350}
1351
1352/*
1353 *----------------------------------------------------------------------
1354 *
1355 * TkpDrawFrame --
1356 *
1357 *	This function draws the rectangular frame area.
1358 *
1359 * Results:
1360 *	None.
1361 *
1362 * Side effects:
1363 *	Draws inside the tkwin area.
1364 *
1365 *----------------------------------------------------------------------
1366 */
1367
1368void
1369TkpDrawFrame(
1370    Tk_Window tkwin,
1371    Tk_3DBorder border,
1372    int highlightWidth,
1373    int borderWidth,
1374    int relief)
1375{
1376    Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), border, highlightWidth,
1377	    highlightWidth, Tk_Width(tkwin) - 2 * highlightWidth,
1378	    Tk_Height(tkwin) - 2 * highlightWidth, borderWidth, relief);
1379}
1380
1381/*
1382 * Local Variables:
1383 * mode: c
1384 * c-basic-offset: 4
1385 * fill-column: 78
1386 * End:
1387 */
1388