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