1/* 2 * tkCanvUtil.c -- 3 * 4 * This procedure contains a collection of utility procedures 5 * used by the implementations of various canvas item types. 6 * 7 * Copyright (c) 1994 Sun Microsystems, Inc. 8 * Copyright (c) 1994 Sun Microsystems, 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: tkCanvUtil.c,v 1.10 2003/01/17 19:54:09 drh Exp $ 14 */ 15 16#include "tkInt.h" 17#include "tkCanvas.h" 18#include "tkPort.h" 19#include <assert.h> 20 21 22/* 23 *---------------------------------------------------------------------- 24 * 25 * Tk_CanvasTkwin -- 26 * 27 * Given a token for a canvas, this procedure returns the 28 * widget that represents the canvas. 29 * 30 * Results: 31 * The return value is a handle for the widget. 32 * 33 * Side effects: 34 * None. 35 * 36 *---------------------------------------------------------------------- 37 */ 38 39Tk_Window 40Tk_CanvasTkwin(canvas) 41 Tk_Canvas canvas; /* Token for the canvas. */ 42{ 43 TkCanvas *canvasPtr = (TkCanvas *) canvas; 44 return canvasPtr->tkwin; 45} 46 47/* 48 *---------------------------------------------------------------------- 49 * 50 * Tk_CanvasDrawableCoords -- 51 * 52 * Given an (x,y) coordinate pair within a canvas, this procedure 53 * returns the corresponding coordinates at which the point should 54 * be drawn in the drawable used for display. 55 * 56 * Results: 57 * There is no return value. The values at *drawableXPtr and 58 * *drawableYPtr are filled in with the coordinates at which 59 * x and y should be drawn. These coordinates are clipped 60 * to fit within a "short", since this is what X uses in 61 * most cases for drawing. 62 * 63 * Side effects: 64 * None. 65 * 66 *---------------------------------------------------------------------- 67 */ 68 69void 70Tk_CanvasDrawableCoords(canvas, x, y, drawableXPtr, drawableYPtr) 71 Tk_Canvas canvas; /* Token for the canvas. */ 72 double x, y; /* Coordinates in canvas space. */ 73 short *drawableXPtr, *drawableYPtr; /* Screen coordinates are stored 74 * here. */ 75{ 76 TkCanvas *canvasPtr = (TkCanvas *) canvas; 77 double tmp; 78 79 tmp = x - canvasPtr->drawableXOrigin; 80 if (tmp > 0) { 81 tmp += 0.5; 82 } else { 83 tmp -= 0.5; 84 } 85 if (tmp > 32767) { 86 *drawableXPtr = 32767; 87 } else if (tmp < -32768) { 88 *drawableXPtr = -32768; 89 } else { 90 *drawableXPtr = (short) tmp; 91 } 92 93 tmp = y - canvasPtr->drawableYOrigin; 94 if (tmp > 0) { 95 tmp += 0.5; 96 } else { 97 tmp -= 0.5; 98 } 99 if (tmp > 32767) { 100 *drawableYPtr = 32767; 101 } else if (tmp < -32768) { 102 *drawableYPtr = -32768; 103 } else { 104 *drawableYPtr = (short) tmp; 105 } 106} 107 108/* 109 *---------------------------------------------------------------------- 110 * 111 * Tk_CanvasWindowCoords -- 112 * 113 * Given an (x,y) coordinate pair within a canvas, this procedure 114 * returns the corresponding coordinates in the canvas's window. 115 * 116 * Results: 117 * There is no return value. The values at *screenXPtr and 118 * *screenYPtr are filled in with the coordinates at which 119 * (x,y) appears in the canvas's window. These coordinates 120 * are clipped to fit within a "short", since this is what X 121 * uses in most cases for drawing. 122 * 123 * Side effects: 124 * None. 125 * 126 *---------------------------------------------------------------------- 127 */ 128 129void 130Tk_CanvasWindowCoords(canvas, x, y, screenXPtr, screenYPtr) 131 Tk_Canvas canvas; /* Token for the canvas. */ 132 double x, y; /* Coordinates in canvas space. */ 133 short *screenXPtr, *screenYPtr; /* Screen coordinates are stored 134 * here. */ 135{ 136 TkCanvas *canvasPtr = (TkCanvas *) canvas; 137 double tmp; 138 139 tmp = x - canvasPtr->xOrigin; 140 if (tmp > 0) { 141 tmp += 0.5; 142 } else { 143 tmp -= 0.5; 144 } 145 if (tmp > 32767) { 146 *screenXPtr = 32767; 147 } else if (tmp < -32768) { 148 *screenXPtr = -32768; 149 } else { 150 *screenXPtr = (short) tmp; 151 } 152 153 tmp = y - canvasPtr->yOrigin; 154 if (tmp > 0) { 155 tmp += 0.5; 156 } else { 157 tmp -= 0.5; 158 } 159 if (tmp > 32767) { 160 *screenYPtr = 32767; 161 } else if (tmp < -32768) { 162 *screenYPtr = -32768; 163 } else { 164 *screenYPtr = (short) tmp; 165 } 166} 167 168/* 169 *-------------------------------------------------------------- 170 * 171 * Tk_CanvasGetCoord -- 172 * 173 * Given a string, returns a floating-point canvas coordinate 174 * corresponding to that string. 175 * 176 * Results: 177 * The return value is a standard Tcl return result. If 178 * TCL_OK is returned, then everything went well and the 179 * canvas coordinate is stored at *doublePtr; otherwise 180 * TCL_ERROR is returned and an error message is left in 181 * the interp's result. 182 * 183 * Side effects: 184 * None. 185 * 186 *-------------------------------------------------------------- 187 */ 188 189int 190Tk_CanvasGetCoord(interp, canvas, string, doublePtr) 191 Tcl_Interp *interp; /* Interpreter for error reporting. */ 192 Tk_Canvas canvas; /* Canvas to which coordinate applies. */ 193 CONST char *string; /* Describes coordinate (any screen 194 * coordinate form may be used here). */ 195 double *doublePtr; /* Place to store converted coordinate. */ 196{ 197 TkCanvas *canvasPtr = (TkCanvas *) canvas; 198 if (Tk_GetScreenMM(canvasPtr->interp, canvasPtr->tkwin, string, 199 doublePtr) != TCL_OK) { 200 return TCL_ERROR; 201 } 202 *doublePtr *= canvasPtr->pixelsPerMM; 203 return TCL_OK; 204} 205 206/* 207 *-------------------------------------------------------------- 208 * 209 * Tk_CanvasGetCoordFromObj -- 210 * 211 * Given a string, returns a floating-point canvas coordinate 212 * corresponding to that string. 213 * 214 * Results: 215 * The return value is a standard Tcl return result. If 216 * TCL_OK is returned, then everything went well and the 217 * canvas coordinate is stored at *doublePtr; otherwise 218 * TCL_ERROR is returned and an error message is left in 219 * interp->result. 220 * 221 * Side effects: 222 * None. 223 * 224 *-------------------------------------------------------------- 225 */ 226 227int 228Tk_CanvasGetCoordFromObj(interp, canvas, obj, doublePtr) 229 Tcl_Interp *interp; /* Interpreter for error reporting. */ 230 Tk_Canvas canvas; /* Canvas to which coordinate applies. */ 231 Tcl_Obj *obj; /* Describes coordinate (any screen 232 * coordinate form may be used here). */ 233 double *doublePtr; /* Place to store converted coordinate. */ 234{ 235 TkCanvas *canvasPtr = (TkCanvas *) canvas; 236 if (Tk_GetMMFromObj(canvasPtr->interp, canvasPtr->tkwin, obj, 237 doublePtr) != TCL_OK) { 238 return TCL_ERROR; 239 } 240 *doublePtr *= canvasPtr->pixelsPerMM; 241 return TCL_OK; 242} 243 244/* 245 *---------------------------------------------------------------------- 246 * 247 * Tk_CanvasSetStippleOrigin -- 248 * 249 * This procedure sets the stipple origin in a graphics context 250 * so that stipples drawn with the GC will line up with other 251 * stipples previously drawn in the canvas. 252 * 253 * Results: 254 * None. 255 * 256 * Side effects: 257 * The graphics context is modified. 258 * 259 *---------------------------------------------------------------------- 260 */ 261 262void 263Tk_CanvasSetStippleOrigin(canvas, gc) 264 Tk_Canvas canvas; /* Token for a canvas. */ 265 GC gc; /* Graphics context that is about to be 266 * used to draw a stippled pattern as 267 * part of redisplaying the canvas. */ 268 269{ 270 TkCanvas *canvasPtr = (TkCanvas *) canvas; 271 272 XSetTSOrigin(canvasPtr->display, gc, -canvasPtr->drawableXOrigin, 273 -canvasPtr->drawableYOrigin); 274} 275 276/* 277 *---------------------------------------------------------------------- 278 * 279 * Tk_CanvasSetOffset-- 280 * 281 * This procedure sets the stipple offset in a graphics 282 * context so that stipples drawn with the GC will 283 * line up with other stipples with the same offset. 284 * 285 * Results: 286 * None. 287 * 288 * Side effects: 289 * The graphics context is modified. 290 * 291 *---------------------------------------------------------------------- 292 */ 293 294void 295Tk_CanvasSetOffset(canvas, gc, offset) 296 Tk_Canvas canvas; /* Token for a canvas. */ 297 GC gc; /* Graphics context that is about to be 298 * used to draw a stippled pattern as 299 * part of redisplaying the canvas. */ 300 Tk_TSOffset *offset; /* offset (may be NULL pointer)*/ 301{ 302 TkCanvas *canvasPtr = (TkCanvas *) canvas; 303 int flags = 0; 304 int x = - canvasPtr->drawableXOrigin; 305 int y = - canvasPtr->drawableYOrigin; 306 307 if (offset != NULL) { 308 flags = offset->flags; 309 x += offset->xoffset; 310 y += offset->yoffset; 311 } 312 if ((flags & TK_OFFSET_RELATIVE) && !(flags & TK_OFFSET_INDEX)) { 313 Tk_SetTSOrigin(canvasPtr->tkwin, gc, x - canvasPtr->xOrigin, 314 y - canvasPtr->yOrigin); 315 } else { 316 XSetTSOrigin(canvasPtr->display, gc, x, y); 317 } 318} 319 320/* 321 *---------------------------------------------------------------------- 322 * 323 * Tk_CanvasGetTextInfo -- 324 * 325 * This procedure returns a pointer to a structure containing 326 * information about the selection and insertion cursor for 327 * a canvas widget. Items such as text items save the pointer 328 * and use it to share access to the information with the generic 329 * canvas code. 330 * 331 * Results: 332 * The return value is a pointer to the structure holding text 333 * information for the canvas. Most of the fields should not 334 * be modified outside the generic canvas code; see the user 335 * documentation for details. 336 * 337 * Side effects: 338 * None. 339 * 340 *---------------------------------------------------------------------- 341 */ 342 343Tk_CanvasTextInfo * 344Tk_CanvasGetTextInfo(canvas) 345 Tk_Canvas canvas; /* Token for the canvas widget. */ 346{ 347 return &((TkCanvas *) canvas)->textInfo; 348} 349 350/* 351 *-------------------------------------------------------------- 352 * 353 * Tk_CanvasTagsParseProc -- 354 * 355 * This procedure is invoked during option processing to handle 356 * "-tags" options for canvas items. 357 * 358 * Results: 359 * A standard Tcl return value. 360 * 361 * Side effects: 362 * The tags for a given item get replaced by those indicated 363 * in the value argument. 364 * 365 *-------------------------------------------------------------- 366 */ 367 368int 369Tk_CanvasTagsParseProc(clientData, interp, tkwin, value, widgRec, offset) 370 ClientData clientData; /* Not used.*/ 371 Tcl_Interp *interp; /* Used for reporting errors. */ 372 Tk_Window tkwin; /* Window containing canvas widget. */ 373 CONST char *value; /* Value of option (list of tag 374 * names). */ 375 char *widgRec; /* Pointer to record for item. */ 376 int offset; /* Offset into item (ignored). */ 377{ 378 register Tk_Item *itemPtr = (Tk_Item *) widgRec; 379 int argc, i; 380 CONST char **argv; 381 Tk_Uid *newPtr; 382 383 /* 384 * Break the value up into the individual tag names. 385 */ 386 387 if (Tcl_SplitList(interp, value, &argc, &argv) != TCL_OK) { 388 return TCL_ERROR; 389 } 390 391 /* 392 * Make sure that there's enough space in the item to hold the 393 * tag names. 394 */ 395 396 if (itemPtr->tagSpace < argc) { 397 newPtr = (Tk_Uid *) ckalloc((unsigned) (argc * sizeof(Tk_Uid))); 398 for (i = itemPtr->numTags-1; i >= 0; i--) { 399 newPtr[i] = itemPtr->tagPtr[i]; 400 } 401 if (itemPtr->tagPtr != itemPtr->staticTagSpace) { 402 ckfree((char *) itemPtr->tagPtr); 403 } 404 itemPtr->tagPtr = newPtr; 405 itemPtr->tagSpace = argc; 406 } 407 itemPtr->numTags = argc; 408 for (i = 0; i < argc; i++) { 409 itemPtr->tagPtr[i] = Tk_GetUid(argv[i]); 410 } 411 ckfree((char *) argv); 412 return TCL_OK; 413} 414 415/* 416 *-------------------------------------------------------------- 417 * 418 * Tk_CanvasTagsPrintProc -- 419 * 420 * This procedure is invoked by the Tk configuration code 421 * to produce a printable string for the "-tags" configuration 422 * option for canvas items. 423 * 424 * Results: 425 * The return value is a string describing all the tags for 426 * the item referred to by "widgRec". In addition, *freeProcPtr 427 * is filled in with the address of a procedure to call to free 428 * the result string when it's no longer needed (or NULL to 429 * indicate that the string doesn't need to be freed). 430 * 431 * Side effects: 432 * None. 433 * 434 *-------------------------------------------------------------- 435 */ 436 437char * 438Tk_CanvasTagsPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr) 439 ClientData clientData; /* Ignored. */ 440 Tk_Window tkwin; /* Window containing canvas widget. */ 441 char *widgRec; /* Pointer to record for item. */ 442 int offset; /* Ignored. */ 443 Tcl_FreeProc **freeProcPtr; /* Pointer to variable to fill in with 444 * information about how to reclaim 445 * storage for return string. */ 446{ 447 register Tk_Item *itemPtr = (Tk_Item *) widgRec; 448 449 if (itemPtr->numTags == 0) { 450 *freeProcPtr = (Tcl_FreeProc *) NULL; 451 return ""; 452 } 453 if (itemPtr->numTags == 1) { 454 *freeProcPtr = (Tcl_FreeProc *) NULL; 455 return (char *) itemPtr->tagPtr[0]; 456 } 457 *freeProcPtr = TCL_DYNAMIC; 458 return Tcl_Merge(itemPtr->numTags, (CONST char **) itemPtr->tagPtr); 459} 460 461 462static int DashConvert _ANSI_ARGS_((char *l, CONST char *p, 463 int n, double width)); 464#define ABS(a) ((a>=0)?(a):(-(a))) 465 466/* 467 *-------------------------------------------------------------- 468 * 469 * TkCanvasDashParseProc -- 470 * 471 * This procedure is invoked during option processing to handle 472 * "-dash", "-activedash" and "-disableddash" options for canvas 473 * objects. 474 * 475 * Results: 476 * A standard Tcl return value. 477 * 478 * Side effects: 479 * The dash list for a given canvas object gets replaced by 480 * those indicated in the value argument. 481 * 482 *-------------------------------------------------------------- 483 */ 484 485int 486TkCanvasDashParseProc(clientData, interp, tkwin, value, widgRec, offset) 487 ClientData clientData; /* Not used.*/ 488 Tcl_Interp *interp; /* Used for reporting errors. */ 489 Tk_Window tkwin; /* Window containing canvas widget. */ 490 CONST char *value; /* Value of option. */ 491 char *widgRec; /* Pointer to record for item. */ 492 int offset; /* Offset into item. */ 493{ 494 return Tk_GetDash(interp, value, (Tk_Dash *)(widgRec+offset)); 495} 496 497/* 498 *-------------------------------------------------------------- 499 * 500 * TkCanvasDashPrintProc -- 501 * 502 * This procedure is invoked by the Tk configuration code 503 * to produce a printable string for the "-dash", "-activedash" 504 * and "-disableddash" configuration options for canvas items. 505 * 506 * Results: 507 * The return value is a string describing all the dash list for 508 * the item referred to by "widgRec"and "offset". In addition, 509 * *freeProcPtr is filled in with the address of a procedure to 510 * call to free the result string when it's no longer needed (or 511 * NULL to indicate that the string doesn't need to be freed). 512 * 513 * Side effects: 514 * None. 515 * 516 *-------------------------------------------------------------- 517 */ 518 519char * 520TkCanvasDashPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr) 521 ClientData clientData; /* Ignored. */ 522 Tk_Window tkwin; /* Window containing canvas widget. */ 523 char *widgRec; /* Pointer to record for item. */ 524 int offset; /* Offset in record for item. */ 525 Tcl_FreeProc **freeProcPtr; /* Pointer to variable to fill in with 526 * information about how to reclaim 527 * storage for return string. */ 528{ 529 Tk_Dash *dash = (Tk_Dash *) (widgRec+offset); 530 char *buffer; 531 char *p; 532 int i = dash->number; 533 534 if (i < 0) { 535 i = -i; 536 *freeProcPtr = TCL_DYNAMIC; 537 buffer = (char *) ckalloc((unsigned int) (i+1)); 538 p = (i > sizeof(char *)) ? dash->pattern.pt : dash->pattern.array; 539 memcpy(buffer, p, (unsigned int) i); 540 buffer[i] = 0; 541 return buffer; 542 } else if (!i) { 543 *freeProcPtr = (Tcl_FreeProc *) NULL; 544 return ""; 545 } 546 buffer = (char *)ckalloc((unsigned int) (4*i)); 547 *freeProcPtr = TCL_DYNAMIC; 548 549 p = (i > sizeof(char *)) ? dash->pattern.pt : dash->pattern.array; 550 sprintf(buffer, "%d", *p++ & 0xff); 551 while(--i) { 552 sprintf(buffer+strlen(buffer), " %d", *p++ & 0xff); 553 } 554 return buffer; 555} 556 557/* 558 *-------------------------------------------------------------- 559 * 560 * Tk_CreateSmoothMethod -- 561 * 562 * This procedure is invoked to add additional values 563 * for the "-smooth" option to the list. 564 * 565 * Results: 566 * A standard Tcl return value. 567 * 568 * Side effects: 569 * In the future "-smooth <name>" will be accepted as 570 * smooth method for the line and polygon. 571 * 572 *-------------------------------------------------------------- 573 */ 574 575Tk_SmoothMethod tkBezierSmoothMethod = { 576 "bezier", 577 TkMakeBezierCurve, 578 (void (*) _ANSI_ARGS_((Tcl_Interp *interp, Tk_Canvas canvas, 579 double *coordPtr, int numPoints, int numSteps))) 580 TkMakeBezierPostscript, 581}; 582 583static void SmoothMethodCleanupProc _ANSI_ARGS_((ClientData clientData, 584 Tcl_Interp *interp)); 585 586typedef struct SmoothAssocData { 587 struct SmoothAssocData *nextPtr; /* pointer to next SmoothAssocData */ 588 Tk_SmoothMethod smooth; /* name and functions associated with this 589 * option */ 590} SmoothAssocData; 591 592void 593Tk_CreateSmoothMethod(interp, smooth) 594 Tcl_Interp *interp; 595 Tk_SmoothMethod *smooth; 596{ 597 SmoothAssocData *methods, *typePtr2, *prevPtr, *ptr; 598 methods = (SmoothAssocData *) Tcl_GetAssocData(interp, "smoothMethod", 599 (Tcl_InterpDeleteProc **) NULL); 600 601 /* 602 * If there's already a smooth method with the given name, remove it. 603 */ 604 605 for (typePtr2 = methods, prevPtr = NULL; typePtr2 != NULL; 606 prevPtr = typePtr2, typePtr2 = typePtr2->nextPtr) { 607 if (!strcmp(typePtr2->smooth.name, smooth->name)) { 608 if (prevPtr == NULL) { 609 methods = typePtr2->nextPtr; 610 } else { 611 prevPtr->nextPtr = typePtr2->nextPtr; 612 } 613 ckfree((char *) typePtr2); 614 break; 615 } 616 } 617 ptr = (SmoothAssocData *) ckalloc(sizeof(SmoothAssocData)); 618 ptr->smooth.name = smooth->name; 619 ptr->smooth.coordProc = smooth->coordProc; 620 ptr->smooth.postscriptProc = smooth->postscriptProc; 621 ptr->nextPtr = methods; 622 Tcl_SetAssocData(interp, "smoothMethod", SmoothMethodCleanupProc, 623 (ClientData) ptr); 624} 625/* 626 *---------------------------------------------------------------------- 627 * 628 * SmoothMethodCleanupProc -- 629 * 630 * This procedure is invoked whenever an interpreter is deleted 631 * to cleanup the smooth methods. 632 * 633 * Results: 634 * None. 635 * 636 * Side effects: 637 * Smooth methods are removed. 638 * 639 *---------------------------------------------------------------------- 640 */ 641 642static void 643SmoothMethodCleanupProc(clientData, interp) 644 ClientData clientData; /* Points to "smoothMethod" AssocData 645 * for the interpreter. */ 646 Tcl_Interp *interp; /* Interpreter that is being deleted. */ 647{ 648 SmoothAssocData *ptr, *methods = (SmoothAssocData *) clientData; 649 650 while (methods != NULL) { 651 methods = (ptr = methods)->nextPtr; 652 ckfree((char *) ptr); 653 } 654} 655/* 656 *-------------------------------------------------------------- 657 * 658 * TkSmoothParseProc -- 659 * 660 * This procedure is invoked during option processing to handle 661 * the "-smooth" option. 662 * 663 * Results: 664 * A standard Tcl return value. 665 * 666 * Side effects: 667 * The smooth option for a given item gets replaced by the value 668 * indicated in the value argument. 669 * 670 *-------------------------------------------------------------- 671 */ 672 673int 674TkSmoothParseProc(clientData, interp, tkwin, value, widgRec, offset) 675 ClientData clientData; /* some flags.*/ 676 Tcl_Interp *interp; /* Used for reporting errors. */ 677 Tk_Window tkwin; /* Window containing canvas widget. */ 678 CONST char *value; /* Value of option. */ 679 char *widgRec; /* Pointer to record for item. */ 680 int offset; /* Offset into item. */ 681{ 682 register Tk_SmoothMethod **smoothPtr = 683 (Tk_SmoothMethod **) (widgRec + offset); 684 Tk_SmoothMethod *smooth = NULL; 685 int b; 686 size_t length; 687 SmoothAssocData *methods; 688 689 if (value == NULL || *value == 0) { 690 *smoothPtr = (Tk_SmoothMethod *) NULL; 691 return TCL_OK; 692 } 693 length = strlen(value); 694 methods = (SmoothAssocData *) Tcl_GetAssocData(interp, "smoothMethod", 695 (Tcl_InterpDeleteProc **) NULL); 696 while (methods != (SmoothAssocData *) NULL) { 697 if (strncmp(value, methods->smooth.name, length) == 0) { 698 if (smooth != (Tk_SmoothMethod *) NULL) { 699 Tcl_AppendResult(interp, "ambigeous smooth method \"", value, 700 "\"", (char *) NULL); 701 return TCL_ERROR; 702 } 703 smooth = &methods->smooth; 704 } 705 methods = methods->nextPtr; 706 } 707 if (smooth) { 708 *smoothPtr = smooth; 709 return TCL_OK; 710 } else if (strncmp(value, tkBezierSmoothMethod.name, length) == 0) { 711 /* 712 * We need to do handle the built-in bezier method. 713 */ 714 *smoothPtr = &tkBezierSmoothMethod; 715 return TCL_OK; 716 } 717 718 719 if (Tcl_GetBoolean(interp, (char *) value, &b) != TCL_OK) { 720 return TCL_ERROR; 721 } 722 *smoothPtr = b ? &tkBezierSmoothMethod : (Tk_SmoothMethod*) NULL; 723 return TCL_OK; 724} 725/* 726 *-------------------------------------------------------------- 727 * 728 * TkSmoothPrintProc -- 729 * 730 * This procedure is invoked by the Tk configuration code 731 * to produce a printable string for the "-smooth" 732 * configuration option. 733 * 734 * Results: 735 * The return value is a string describing the smooth option for 736 * the item referred to by "widgRec". In addition, *freeProcPtr 737 * is filled in with the address of a procedure to call to free 738 * the result string when it's no longer needed (or NULL to 739 * indicate that the string doesn't need to be freed). 740 * 741 * Side effects: 742 * None. 743 * 744 *-------------------------------------------------------------- 745 */ 746 747char * 748TkSmoothPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr) 749 ClientData clientData; /* Ignored. */ 750 Tk_Window tkwin; /* Window containing canvas widget. */ 751 char *widgRec; /* Pointer to record for item. */ 752 int offset; /* Offset into item. */ 753 Tcl_FreeProc **freeProcPtr; /* Pointer to variable to fill in with 754 * information about how to reclaim 755 * storage for return string. */ 756{ 757 register Tk_SmoothMethod **smoothPtr = (Tk_SmoothMethod **) (widgRec + offset); 758 759 return (*smoothPtr) ? (*smoothPtr)->name : "0"; 760} 761/* 762 *-------------------------------------------------------------- 763 * 764 * Tk_GetDash 765 * 766 * This procedure is used to parse a string, assuming 767 * it is dash information. 768 * 769 * Results: 770 * The return value is a standard Tcl result: TCL_OK means 771 * that the dash information was parsed ok, and 772 * TCL_ERROR means it couldn't be parsed. 773 * 774 * Side effects: 775 * Dash information in the dash structure is updated. 776 * 777 *-------------------------------------------------------------- 778 */ 779 780int 781Tk_GetDash(interp, value, dash) 782 Tcl_Interp *interp; /* Used for error reporting. */ 783 CONST char *value; /* Textual specification of dash list. */ 784 Tk_Dash *dash; /* Pointer to record in which to 785 * store dash information. */ 786{ 787 int argc, i; 788 CONST char **largv, **argv = NULL; 789 char *pt; 790 791 if ((value==(char *) NULL) || (*value==0) ) { 792 dash->number = 0; 793 return TCL_OK; 794 } 795 if ((*value == '.') || (*value == ',') || 796 (*value == '-') || (*value == '_')) { 797 i = DashConvert((char *) NULL, value, -1, 0.0); 798 if (i>0) { 799 i = strlen(value); 800 } else { 801 goto badDashList; 802 } 803 if (i > sizeof(char *)) { 804 dash->pattern.pt = pt = (char *) ckalloc(strlen(value)); 805 } else { 806 pt = dash->pattern.array; 807 } 808 memcpy(pt,value, (unsigned int) i); 809 dash->number = -i; 810 return TCL_OK; 811 } 812 if (Tcl_SplitList(interp, (char *) value, &argc, &argv) != TCL_OK) { 813 Tcl_ResetResult(interp); 814 badDashList: 815 Tcl_AppendResult(interp, "bad dash list \"", value, 816 "\": must be a list of integers or a format like \"-..\"", 817 (char *) NULL); 818 syntaxError: 819 if (argv != NULL) { 820 ckfree((char *) argv); 821 } 822 if (ABS(dash->number) > sizeof(char *)) 823 ckfree((char *) dash->pattern.pt); 824 dash->number = 0; 825 return TCL_ERROR; 826 } 827 828 if (ABS(dash->number) > sizeof(char *)) { 829 ckfree((char *) dash->pattern.pt); 830 } 831 if (argc > sizeof(char *)) { 832 dash->pattern.pt = pt = (char *) ckalloc((unsigned int) argc); 833 } else { 834 pt = dash->pattern.array; 835 } 836 dash->number = argc; 837 838 largv = argv; 839 while(argc>0) { 840 if (Tcl_GetInt(interp, *largv, &i) != TCL_OK || 841 i < 1 || i>255) { 842 Tcl_ResetResult(interp); 843 Tcl_AppendResult(interp, "expected integer in the range 1..255 but got \"", 844 *largv, "\"", (char *) NULL); 845 goto syntaxError; 846 } 847 *pt++ = i; 848 argc--; largv++; 849 } 850 851 if (argv != NULL) { 852 ckfree((char *) argv); 853 } 854 855 return TCL_OK; 856} 857 858/* 859 *-------------------------------------------------------------- 860 * 861 * Tk_CreateOutline 862 * 863 * This procedure initializes the Tk_Outline structure 864 * with default values. 865 * 866 * Results: 867 * None 868 * 869 * Side effects: 870 * None 871 * 872 *-------------------------------------------------------------- 873 */ 874 875void Tk_CreateOutline(outline) 876 Tk_Outline *outline; 877{ 878 outline->gc = None; 879 outline->width = 1.0; 880 outline->activeWidth = 0.0; 881 outline->disabledWidth = 0.0; 882 outline->offset = 0; 883 outline->dash.number = 0; 884 outline->activeDash.number = 0; 885 outline->disabledDash.number = 0; 886 outline->tsoffset.flags = 0; 887 outline->tsoffset.xoffset = 0; 888 outline->tsoffset.yoffset = 0; 889 outline->color = NULL; 890 outline->activeColor = NULL; 891 outline->disabledColor = NULL; 892 outline->stipple = None; 893 outline->activeStipple = None; 894 outline->disabledStipple = None; 895} 896 897/* 898 *-------------------------------------------------------------- 899 * 900 * Tk_DeleteOutline 901 * 902 * This procedure frees all memory that might be 903 * allocated and referenced in the Tk_Outline structure. 904 * 905 * Results: 906 * None 907 * 908 * Side effects: 909 * None 910 * 911 *-------------------------------------------------------------- 912 */ 913 914void Tk_DeleteOutline(display, outline) 915 Display *display; /* Display containing window */ 916 Tk_Outline *outline; 917{ 918 if (outline->gc != None) { 919 Tk_FreeGC(display, outline->gc); 920 } 921 if (ABS(outline->dash.number) > sizeof(char *)) { 922 ckfree((char *) outline->dash.pattern.pt); 923 } 924 if (ABS(outline->activeDash.number) > sizeof(char *)) { 925 ckfree((char *) outline->activeDash.pattern.pt); 926 } 927 if (ABS(outline->disabledDash.number) > sizeof(char *)) { 928 ckfree((char *) outline->disabledDash.pattern.pt); 929 } 930 if (outline->color != NULL) { 931 Tk_FreeColor(outline->color); 932 } 933 if (outline->activeColor != NULL) { 934 Tk_FreeColor(outline->activeColor); 935 } 936 if (outline->disabledColor != NULL) { 937 Tk_FreeColor(outline->disabledColor); 938 } 939 if (outline->stipple != None) { 940 Tk_FreeBitmap(display, outline->stipple); 941 } 942 if (outline->activeStipple != None) { 943 Tk_FreeBitmap(display, outline->activeStipple); 944 } 945 if (outline->disabledStipple != None) { 946 Tk_FreeBitmap(display, outline->disabledStipple); 947 } 948} 949 950/* 951 *-------------------------------------------------------------- 952 * 953 * Tk_ConfigOutlineGC 954 * 955 * This procedure should be called in the canvas object 956 * during the configure command. The graphics context 957 * description in gcValues is updated according to the 958 * information in the dash structure, as far as possible. 959 * 960 * Results: 961 * The return-value is a mask, indicating which 962 * elements of gcValues have been updated. 963 * 0 means there is no outline. 964 * 965 * Side effects: 966 * GC information in gcValues is updated. 967 * 968 *-------------------------------------------------------------- 969 */ 970 971int Tk_ConfigOutlineGC(gcValues, canvas, item, outline) 972 XGCValues *gcValues; 973 Tk_Canvas canvas; 974 Tk_Item *item; 975 Tk_Outline *outline; 976{ 977 int mask = 0; 978 double width; 979 Tk_Dash *dash; 980 XColor *color; 981 Pixmap stipple; 982 Tk_State state = item->state; 983 984 if (outline->width < 0.0) { 985 outline->width = 0.0; 986 } 987 if (outline->activeWidth < 0.0) { 988 outline->activeWidth = 0.0; 989 } 990 if (outline->disabledWidth < 0) { 991 outline->disabledWidth = 0.0; 992 } 993 if (state==TK_STATE_HIDDEN) { 994 return 0; 995 } 996 997 width = outline->width; 998 if (width < 1.0) { 999 width = 1.0; 1000 } 1001 dash = &(outline->dash); 1002 color = outline->color; 1003 stipple = outline->stipple; 1004 if (state == TK_STATE_NULL) { 1005 state = ((TkCanvas *)canvas)->canvas_state; 1006 } 1007 if (((TkCanvas *)canvas)->currentItemPtr == item) { 1008 if (outline->activeWidth>width) { 1009 width = outline->activeWidth; 1010 } 1011 if (outline->activeDash.number != 0) { 1012 dash = &(outline->activeDash); 1013 } 1014 if (outline->activeColor!=NULL) { 1015 color = outline->activeColor; 1016 } 1017 if (outline->activeStipple!=None) { 1018 stipple = outline->activeStipple; 1019 } 1020 } else if (state==TK_STATE_DISABLED) { 1021 if (outline->disabledWidth>0) { 1022 width = outline->disabledWidth; 1023 } 1024 if (outline->disabledDash.number != 0) { 1025 dash = &(outline->disabledDash); 1026 } 1027 if (outline->disabledColor!=NULL) { 1028 color = outline->disabledColor; 1029 } 1030 if (outline->disabledStipple!=None) { 1031 stipple = outline->disabledStipple; 1032 } 1033 } 1034 1035 if (color==NULL) { 1036 return 0; 1037 } 1038 1039 gcValues->line_width = (int) (width + 0.5); 1040 if (color != NULL) { 1041 gcValues->foreground = color->pixel; 1042 mask = GCForeground|GCLineWidth; 1043 if (stipple != None) { 1044 gcValues->stipple = stipple; 1045 gcValues->fill_style = FillStippled; 1046 mask |= GCStipple|GCFillStyle; 1047 } 1048 } 1049 if (mask && (dash->number != 0)) { 1050 gcValues->line_style = LineOnOffDash; 1051 gcValues->dash_offset = outline->offset; 1052 if (dash->number >= 2) { 1053 gcValues->dashes = 4; 1054 } else if (dash->number > 0) { 1055 gcValues->dashes = dash->pattern.array[0]; 1056 } else { 1057 gcValues->dashes = (char) (4 * width); 1058 } 1059 mask |= GCLineStyle|GCDashList|GCDashOffset; 1060 } 1061 return mask; 1062} 1063 1064/* 1065 *-------------------------------------------------------------- 1066 * 1067 * Tk_ChangeOutlineGC 1068 * 1069 * Updates the GC to represent the full information of 1070 * the dash structure. Partly this is already done in 1071 * Tk_ConfigOutlineGC(). 1072 * This function should be called just before drawing 1073 * the dashed item. 1074 * 1075 * Results: 1076 * 1 if there is a stipple pattern. 1077 * 0 otherwise. 1078 * 1079 * Side effects: 1080 * GC is updated. 1081 * 1082 *-------------------------------------------------------------- 1083 */ 1084 1085int 1086Tk_ChangeOutlineGC(canvas, item, outline) 1087 Tk_Canvas canvas; 1088 Tk_Item *item; 1089 Tk_Outline *outline; 1090{ 1091 CONST char *p; 1092 double width; 1093 Tk_Dash *dash; 1094 XColor *color; 1095 Pixmap stipple; 1096 Tk_State state = item->state; 1097 1098 width = outline->width; 1099 if (width < 1.0) { 1100 width = 1.0; 1101 } 1102 dash = &(outline->dash); 1103 color = outline->color; 1104 stipple = outline->stipple; 1105 if (state == TK_STATE_NULL) { 1106 state = ((TkCanvas *)canvas)->canvas_state; 1107 } 1108 if (((TkCanvas *)canvas)->currentItemPtr == item) { 1109 if (outline->activeWidth > width) { 1110 width = outline->activeWidth; 1111 } 1112 if (outline->activeDash.number != 0) { 1113 dash = &(outline->activeDash); 1114 } 1115 if (outline->activeColor != NULL) { 1116 color = outline->activeColor; 1117 } 1118 if (outline->activeStipple != None) { 1119 stipple = outline->activeStipple; 1120 } 1121 } else if (state == TK_STATE_DISABLED) { 1122 if (outline->disabledWidth > width) { 1123 width = outline->disabledWidth; 1124 } 1125 if (outline->disabledDash.number != 0) { 1126 dash = &(outline->disabledDash); 1127 } 1128 if (outline->disabledColor != NULL) { 1129 color = outline->disabledColor; 1130 } 1131 if (outline->disabledStipple != None) { 1132 stipple = outline->disabledStipple; 1133 } 1134 } 1135 if (color==NULL) { 1136 return 0; 1137 } 1138 1139 if ((dash->number<-1) || ((dash->number == -1) && (dash->pattern.array[1]!=','))) { 1140 char *q; 1141 int i = -dash->number; 1142 1143 p = (i > sizeof(char *)) ? dash->pattern.pt : dash->pattern.array; 1144 q = (char *) ckalloc(2*(unsigned int)i); 1145 i = DashConvert(q, p, i, width); 1146 XSetDashes(((TkCanvas *)canvas)->display, outline->gc, outline->offset, q, i); 1147 ckfree(q); 1148 } else if ( dash->number>2 || (dash->number==2 && 1149 (dash->pattern.array[0]!=dash->pattern.array[1]))) { 1150 p = (char *) (dash->number > sizeof(char *)) ? dash->pattern.pt : dash->pattern.array; 1151 XSetDashes(((TkCanvas *)canvas)->display, outline->gc, outline->offset, p, dash->number); 1152 } 1153 if (stipple!=None) { 1154 int w=0; int h=0; 1155 Tk_TSOffset *tsoffset = &outline->tsoffset; 1156 int flags = tsoffset->flags; 1157 if (!(flags & TK_OFFSET_INDEX) && (flags & (TK_OFFSET_CENTER|TK_OFFSET_MIDDLE))) { 1158 Tk_SizeOfBitmap(((TkCanvas *)canvas)->display, stipple, &w, &h); 1159 if (flags & TK_OFFSET_CENTER) { 1160 w /= 2; 1161 } else { 1162 w = 0; 1163 } 1164 if (flags & TK_OFFSET_MIDDLE) { 1165 h /= 2; 1166 } else { 1167 h = 0; 1168 } 1169 } 1170 tsoffset->xoffset -= w; 1171 tsoffset->yoffset -= h; 1172 Tk_CanvasSetOffset(canvas, outline->gc, tsoffset); 1173 tsoffset->xoffset += w; 1174 tsoffset->yoffset += h; 1175 return 1; 1176 } 1177 return 0; 1178} 1179 1180 1181/* 1182 *-------------------------------------------------------------- 1183 * 1184 * Tk_ResetOutlineGC 1185 * 1186 * Restores the GC to the situation before 1187 * Tk_ChangeDashGC() was called. 1188 * This function should be called just after the dashed 1189 * item is drawn, because the GC is supposed to be 1190 * read-only. 1191 * 1192 * Results: 1193 * 1 if there is a stipple pattern. 1194 * 0 otherwise. 1195 * 1196 * Side effects: 1197 * GC is updated. 1198 * 1199 *-------------------------------------------------------------- 1200 */ 1201int 1202Tk_ResetOutlineGC(canvas, item, outline) 1203 Tk_Canvas canvas; 1204 Tk_Item *item; 1205 Tk_Outline *outline; 1206{ 1207 char dashList; 1208 double width; 1209 Tk_Dash *dash; 1210 XColor *color; 1211 Pixmap stipple; 1212 Tk_State state = item->state; 1213 1214 width = outline->width; 1215 if (width < 1.0) { 1216 width = 1.0; 1217 } 1218 dash = &(outline->dash); 1219 color = outline->color; 1220 stipple = outline->stipple; 1221 if (state == TK_STATE_NULL) { 1222 state = ((TkCanvas *)canvas)->canvas_state; 1223 } 1224 if (((TkCanvas *)canvas)->currentItemPtr == item) { 1225 if (outline->activeWidth>width) { 1226 width = outline->activeWidth; 1227 } 1228 if (outline->activeDash.number != 0) { 1229 dash = &(outline->activeDash); 1230 } 1231 if (outline->activeColor!=NULL) { 1232 color = outline->activeColor; 1233 } 1234 if (outline->activeStipple!=None) { 1235 stipple = outline->activeStipple; 1236 } 1237 } else if (state==TK_STATE_DISABLED) { 1238 if (outline->disabledWidth>width) { 1239 width = outline->disabledWidth; 1240 } 1241 if (outline->disabledDash.number != 0) { 1242 dash = &(outline->disabledDash); 1243 } 1244 if (outline->disabledColor!=NULL) { 1245 color = outline->disabledColor; 1246 } 1247 if (outline->disabledStipple!=None) { 1248 stipple = outline->disabledStipple; 1249 } 1250 } 1251 if (color==NULL) { 1252 return 0; 1253 } 1254 1255 if ((dash->number > 2) || (dash->number < -1) || (dash->number==2 && 1256 (dash->pattern.array[0] != dash->pattern.array[1])) || 1257 ((dash->number == -1) && (dash->pattern.array[1] != ','))) { 1258 if (dash->number < 0) { 1259 dashList = (int) (4 * width + 0.5); 1260 } else if (dash->number<3) { 1261 dashList = dash->pattern.array[0]; 1262 } else { 1263 dashList = 4; 1264 } 1265 XSetDashes(((TkCanvas *)canvas)->display, outline->gc, 1266 outline->offset, &dashList , 1); 1267 } 1268 if (stipple != None) { 1269 XSetTSOrigin(((TkCanvas *)canvas)->display, outline->gc, 0, 0); 1270 return 1; 1271 } 1272 return 0; 1273} 1274 1275 1276/* 1277 *-------------------------------------------------------------- 1278 * 1279 * Tk_CanvasPsOutline 1280 * 1281 * Creates the postscript command for the correct 1282 * Outline-information (width, dash, color and stipple). 1283 * 1284 * Results: 1285 * TCL_OK if succeeded, otherwise TCL_ERROR. 1286 * 1287 * Side effects: 1288 * canvas->interp->result contains the postscript string, 1289 * or an error message if the result was TCL_ERROR. 1290 * 1291 *-------------------------------------------------------------- 1292 */ 1293int 1294Tk_CanvasPsOutline(canvas, item, outline) 1295 Tk_Canvas canvas; 1296 Tk_Item *item; 1297 Tk_Outline *outline; 1298{ 1299 char string[41]; 1300 char pattern[11]; 1301 int i; 1302 char *ptr; 1303 char *str = string; 1304 char *lptr = pattern; 1305 Tcl_Interp *interp = ((TkCanvas *)canvas)->interp; 1306 double width; 1307 Tk_Dash *dash; 1308 XColor *color; 1309 Pixmap stipple; 1310 Tk_State state = item->state; 1311 1312 width = outline->width; 1313 dash = &(outline->dash); 1314 color = outline->color; 1315 stipple = outline->stipple; 1316 if (state == TK_STATE_NULL) { 1317 state = ((TkCanvas *)canvas)->canvas_state; 1318 } 1319 if (((TkCanvas *)canvas)->currentItemPtr == item) { 1320 if (outline->activeWidth > width) { 1321 width = outline->activeWidth; 1322 } 1323 if (outline->activeDash.number > 0) { 1324 dash = &(outline->activeDash); 1325 } 1326 if (outline->activeColor != NULL) { 1327 color = outline->activeColor; 1328 } 1329 if (outline->activeStipple != None) { 1330 stipple = outline->activeStipple; 1331 } 1332 } else if (state == TK_STATE_DISABLED) { 1333 if (outline->disabledWidth > 0) { 1334 width = outline->disabledWidth; 1335 } 1336 if (outline->disabledDash.number > 0) { 1337 dash = &(outline->disabledDash); 1338 } 1339 if (outline->disabledColor != NULL) { 1340 color = outline->disabledColor; 1341 } 1342 if (outline->disabledStipple != None) { 1343 stipple = outline->disabledStipple; 1344 } 1345 } 1346 sprintf(string, "%.15g setlinewidth\n", width); 1347 Tcl_AppendResult(interp, string, (char *) NULL); 1348 1349 if (dash->number > 10) { 1350 str = (char *)ckalloc((unsigned int) (1 + 4*dash->number)); 1351 } else if (dash->number < -5) { 1352 str = (char *)ckalloc((unsigned int) (1 - 8*dash->number)); 1353 lptr = (char *)ckalloc((unsigned int) (1 - 2*dash->number)); 1354 } 1355 ptr = (char *) ((ABS(dash->number) > sizeof(char *)) ) ? 1356 dash->pattern.pt : dash->pattern.array; 1357 if (dash->number > 0) { 1358 char *ptr0 = ptr; 1359 sprintf(str, "[%d", *ptr++ & 0xff); 1360 i = dash->number-1; 1361 while (i--) { 1362 sprintf(str+strlen(str), " %d", *ptr++ & 0xff); 1363 } 1364 Tcl_AppendResult(interp, str, (char *)NULL); 1365 if (dash->number&1) { 1366 Tcl_AppendResult(interp, " ", str+1, (char *)NULL); 1367 } 1368 sprintf(str, "] %d setdash\n", outline->offset); 1369 Tcl_AppendResult(interp, str, (char *)NULL); 1370 ptr = ptr0; 1371 } else if (dash->number < 0) { 1372 if ((i = DashConvert(lptr, ptr, -dash->number, width)) != 0) { 1373 char *lptr0 = lptr; 1374 sprintf(str, "[%d", *lptr++ & 0xff); 1375 while (--i) { 1376 sprintf(str+strlen(str), " %d", *lptr++ & 0xff); 1377 } 1378 Tcl_AppendResult(interp, str, (char *)NULL); 1379 sprintf(str, "] %d setdash\n", outline->offset); 1380 Tcl_AppendResult(interp, str, (char *)NULL); 1381 lptr = lptr0; 1382 } else { 1383 Tcl_AppendResult(interp, "[] 0 setdash\n", (char *)NULL); 1384 } 1385 } else { 1386 Tcl_AppendResult(interp, "[] 0 setdash\n", (char *)NULL); 1387 } 1388 if (str != string) { 1389 ckfree(str); 1390 } 1391 if (lptr != pattern) { 1392 ckfree(lptr); 1393 } 1394 if (Tk_CanvasPsColor(interp, canvas, color) != TCL_OK) { 1395 return TCL_ERROR; 1396 } 1397 if (stipple != None) { 1398 Tcl_AppendResult(interp, "StrokeClip ", (char *) NULL); 1399 if (Tk_CanvasPsStipple(interp, canvas, stipple) != TCL_OK) { 1400 return TCL_ERROR; 1401 } 1402 } else { 1403 Tcl_AppendResult(interp, "stroke\n", (char *) NULL); 1404 } 1405 1406 return TCL_OK; 1407} 1408 1409 1410/* 1411 *-------------------------------------------------------------- 1412 * 1413 * DashConvert 1414 * 1415 * Converts a character-like dash-list (e.g. "-..") 1416 * into an X11-style. l must point to a string that 1417 * holds room to at least 2*n characters. if 1418 * l == NULL, this function can be used for 1419 * syntax checking only. 1420 * 1421 * Results: 1422 * The length of the resulting X11 compatible 1423 * dash-list. -1 if failed. 1424 * 1425 * Side effects: 1426 * None 1427 * 1428 *-------------------------------------------------------------- 1429 */ 1430 1431static int 1432DashConvert (l, p, n, width) 1433 char *l; 1434 CONST char *p; 1435 int n; 1436 double width; 1437{ 1438 int result = 0; 1439 int size, intWidth; 1440 1441 if (n<0) { 1442 n = strlen(p); 1443 } 1444 intWidth = (int) (width + 0.5); 1445 if (intWidth < 1) { 1446 intWidth = 1; 1447 } 1448 while (n-- && *p) { 1449 switch (*p++) { 1450 case ' ': 1451 if (result) { 1452 if (l) { 1453 l[-1] += intWidth + 1; 1454 } 1455 continue; 1456 } else { 1457 return 0; 1458 } 1459 break; 1460 case '_': 1461 size = 8; 1462 break; 1463 case '-': 1464 size = 6; 1465 break; 1466 case ',': 1467 size = 4; 1468 break; 1469 case '.': 1470 size = 2; 1471 break; 1472 default: 1473 return -1; 1474 } 1475 if (l) { 1476 *l++ = size * intWidth; 1477 *l++ = 4 * intWidth; 1478 } 1479 result += 2; 1480 } 1481 return result; 1482} 1483 1484/* 1485 *---------------------------------------------------------------------- 1486 * 1487 * translateAndAppendCoords -- 1488 * 1489 * This is a helper routine for TkCanvTranslatePath() below. 1490 * 1491 * Given an (x,y) coordinate pair within a canvas, this procedure 1492 * computes the corresponding coordinates at which the point should 1493 * be drawn in the drawable used for display. Those coordinates are 1494 * then written into outArr[numOut*2] and outArr[numOut*2+1]. 1495 * 1496 * Results: 1497 * There is no return value. 1498 * 1499 * Side effects: 1500 * None. 1501 * 1502 *---------------------------------------------------------------------- 1503 */ 1504 1505static void 1506translateAndAppendCoords(canvPtr, x, y, outArr, numOut) 1507 TkCanvas *canvPtr; /* The canvas. */ 1508 double x, y; /* Coordinates in canvas space. */ 1509 XPoint *outArr; /* Write results into this array */ 1510 int numOut; /* Num of prior entries in outArr[] */ 1511{ 1512 double tmp; 1513 1514 tmp = x - canvPtr->drawableXOrigin; 1515 if (tmp > 0) { 1516 tmp += 0.5; 1517 } else { 1518 tmp -= 0.5; 1519 } 1520 outArr[numOut].x = (short) tmp; 1521 1522 tmp = y - canvPtr->drawableYOrigin; 1523 if (tmp > 0) { 1524 tmp += 0.5; 1525 } else { 1526 tmp -= 0.5; 1527 } 1528 outArr[numOut].y = (short) tmp; 1529} 1530 1531/* 1532 *-------------------------------------------------------------- 1533 * 1534 * TkCanvTranslatePath 1535 * 1536 * Translate a line or polygon path so that all vertices are 1537 * within a rectangle that is 1000 pixels larger than the total 1538 * size of the canvas window. This will prevent pixel coordinates 1539 * from overflowing the 16-bit integer size limitation imposed by 1540 * most windowing systems. 1541 * 1542 * coordPtr must point to an array of doubles, two doubles per 1543 * vertex. There are a total of numVertex vertices, or 2*numVertex 1544 * entries in coordPtr. The result vertices written into outArr 1545 * have their coordinate origin shifted to canvPtr->drawableXOrigin 1546 * by canvPtr->drawableYOrigin. There might be as many as 3 times 1547 * more output vertices than there are input vertices. The calling 1548 * function should allocate space accordingly. 1549 * 1550 * This routine limits the width and height of a canvas window 1551 * to 31767 pixels. At the highest resolution display devices 1552 * available today (210 ppi in Jan 2003) that's a window that is 1553 * over 13 feet wide and tall. Should be enough for the near 1554 * future. 1555 * 1556 * Results: 1557 * Clipped and translated path vertices are written into outArr[]. 1558 * There might be as many as twice the vertices in outArr[] as there 1559 * are in coordPtr[]. The return value is the number of vertices 1560 * actually written into outArr[]. 1561 * 1562 * Side effects: 1563 * None 1564 * 1565 *-------------------------------------------------------------- 1566 */ 1567int 1568TkCanvTranslatePath (canvPtr, numVertex, coordArr, closedPath, outArr) 1569 TkCanvas *canvPtr; /* The canvas */ 1570 int numVertex; /* Number of vertices specified by coordArr[] */ 1571 double *coordArr; /* X and Y coordinates for each vertex */ 1572 int closedPath; /* True if this is a closed polygon */ 1573 XPoint *outArr; /* Write results here, if not NULL */ 1574{ 1575 int numOutput = 0; /* Number of output coordinates */ 1576 double lft, rgh; /* Left and right sides of the bounding box */ 1577 double top, btm; /* Top and bottom sizes of the bounding box */ 1578 double *tempArr; /* Temporary storage used by the clipper */ 1579 double *a, *b, *t; /* Pointers to parts of the temporary storage */ 1580 int i, j; /* Loop counters */ 1581 int maxOutput; /* Maximum number of outputs that we will allow */ 1582 double limit[4]; /* Boundries at which clipping occurs */ 1583 double staticSpace[480]; /* Temp space from the stack */ 1584 1585 /* 1586 ** Constrain all vertices of the path to be within a box that is no 1587 ** larger than 32000 pixels wide or height. The top-left corner of 1588 ** this clipping box is 1000 pixels above and to the left of the top 1589 ** left corner of the window on which the canvas is displayed. 1590 ** 1591 ** This means that a canvas will not display properly on a canvas 1592 ** window that is larger than 31000 pixels wide or high. That is not 1593 ** a problem today, but might someday become a factor for ultra-high 1594 ** resolutions displays. 1595 ** 1596 ** The X11 protocol allows us (in theory) to expand the size of the 1597 ** clipping box to 32767 pixels. But we have found experimentally that 1598 ** XFree86 sometimes fails to draw lines correctly if they are longer 1599 ** than about 32500 pixels. So we have left a little margin in the 1600 ** size to mask that bug. 1601 */ 1602 lft = canvPtr->xOrigin - 1000.0; 1603 top = canvPtr->yOrigin - 1000.0; 1604 rgh = lft + 32000.0; 1605 btm = top + 32000.0; 1606 1607 /* Try the common case first - no clipping. Loop over the input 1608 ** coordinates and translate them into appropriate output coordinates. 1609 ** But if a vertex outside of the bounding box is seen, break out of 1610 ** the loop. 1611 ** 1612 ** Most of the time, no clipping is needed, so this one loop is 1613 ** sufficient to do the translation. 1614 */ 1615 for(i=0; i<numVertex; i++){ 1616 double x, y; 1617 x = coordArr[i*2]; 1618 y = coordArr[i*2+1]; 1619 if( x<lft || x>rgh || y<top || y>btm ) break; 1620 translateAndAppendCoords(canvPtr, x, y, outArr, numOutput++); 1621 } 1622 if( i==numVertex ){ 1623 assert( numOutput==numVertex ); 1624 return numOutput; 1625 } 1626 1627 /* If we reach this point, it means that some clipping is required. 1628 ** Begin by allocating some working storage - at least 6 times as much space 1629 ** as coordArr[] requires. Divide this space into two separate arrays 1630 ** a[] and b[]. Initialize a[] to be equal to coordArr[]. 1631 */ 1632 if( numVertex*12 <= sizeof(staticSpace)/sizeof(staticSpace[0]) ){ 1633 tempArr = staticSpace; 1634 } else { 1635 tempArr = (double*)ckalloc( numVertex*12*sizeof(tempArr[0]) ); 1636 } 1637 for(i=0; i<numVertex*2; i++){ 1638 tempArr[i] = coordArr[i]; 1639 } 1640 a = tempArr; 1641 b = &tempArr[numVertex*6]; 1642 1643 /* We will make four passes through the input data. On each pass, 1644 ** we copy the contents of a[] over into b[]. As we copy, we clip 1645 ** any line segments that extend to the right past xClip then we 1646 ** rotate the coordinate system 90 degrees clockwise. After each 1647 ** pass is complete, we interchange a[] and b[] in preparation for 1648 ** the next pass. 1649 ** 1650 ** Each pass clips line segments that extend beyond a single side 1651 ** of the bounding box, and four passes rotate the coordinate system 1652 ** back to its original value. I'm not an expert on graphics 1653 ** algorithms, but I think this is called Cohen-Sutherland polygon 1654 ** clipping. 1655 ** 1656 ** The limit[] array contains the xClip value used for each of the 1657 ** four passes. 1658 */ 1659 limit[0] = rgh; 1660 limit[1] = -top; 1661 limit[2] = -lft; 1662 limit[3] = btm; 1663 1664 /* This is the loop that makes the four passes through the data. 1665 */ 1666 maxOutput = numVertex*3; 1667 for(j=0; j<4; j++){ 1668 double xClip = limit[j]; 1669 int inside = a[0]<xClip; 1670 double priorY = a[1]; 1671 numOutput = 0; 1672 1673 /* Clip everything to the right of xClip. Store the results in 1674 ** b[] rotated by 90 degrees clockwise. 1675 */ 1676 for(i=0; i<numVertex; i++){ 1677 double x = a[i*2]; 1678 double y = a[i*2+1]; 1679 if( x>=xClip ){ 1680 /* The current vertex is to the right of xClip. 1681 */ 1682 if( inside ){ 1683 /* If the current vertex is to the right of xClip but 1684 ** the previous vertex was left of xClip, then draw a 1685 ** line segment from the previous vertex to until it 1686 ** intersects the vertical at xClip. 1687 */ 1688 double x0, y0, yN; 1689 assert( i>0 ); 1690 x0 = a[i*2-2]; 1691 y0 = a[i*2-1]; 1692 yN = y0 + (y - y0)*(xClip-x0)/(x-x0); 1693 b[numOutput*2] = -yN; 1694 b[numOutput*2+1] = xClip; 1695 numOutput++; 1696 assert( numOutput<=maxOutput ); 1697 priorY = yN; 1698 inside = 0; 1699 }else if( i==0 ){ 1700 /* If the first vertex is to the right of xClip, add 1701 ** a vertex that is the projection of the first vertex 1702 ** onto the vertical xClip line. 1703 */ 1704 b[0] = -y; 1705 b[1] = xClip; 1706 numOutput = 1; 1707 priorY = y; 1708 } 1709 }else{ 1710 /* The current vertex is to the left of xClip 1711 */ 1712 if( !inside ){ 1713 /* If the current vertex is on the left of xClip and 1714 ** one or more prior vertices where to the right, then 1715 ** we have to draw a line segment along xClip that extends 1716 ** from the spot where we first crossed from left to right 1717 ** to the spot where we cross back from right to left. 1718 */ 1719 double x0, y0, yN; 1720 assert( i>0 ); 1721 x0 = a[i*2-2]; 1722 y0 = a[i*2-1]; 1723 yN = y0 + (y - y0)*(xClip-x0)/(x-x0); 1724 if( yN!=priorY ){ 1725 b[numOutput*2] = -yN; 1726 b[numOutput*2+1] = xClip; 1727 numOutput++; 1728 assert( numOutput<=maxOutput ); 1729 } 1730 inside = 1; 1731 } 1732 b[numOutput*2] = -y; 1733 b[numOutput*2+1] = x; 1734 numOutput++; 1735 assert( numOutput<=maxOutput ); 1736 } 1737 } 1738 1739 /* Interchange a[] and b[] in preparation for the next pass. 1740 */ 1741 t = a; 1742 a = b; 1743 b = t; 1744 numVertex = numOutput; 1745 } 1746 1747 /* All clipping is now finished. Convert the coordinates from doubles 1748 ** into XPoints and translate the origin for the drawable. 1749 */ 1750 for(i=0; i<numVertex; i++){ 1751 translateAndAppendCoords(canvPtr, a[i*2], a[i*2+1], outArr, i); 1752 } 1753 if( tempArr!=staticSpace ){ 1754 ckfree((char *) tempArr); 1755 } 1756 return numOutput; 1757} 1758