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