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