1/* 2 * tkImgBmap.c -- 3 * 4 * This procedure implements images of type "bitmap" for Tk. 5 * 6 * Copyright (c) 1994 The Regents of the University of California. 7 * Copyright (c) 1994-1997 Sun Microsystems, Inc. 8 * Copyright (c) 1999 by Scriptics Corporation. 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: tkImgBmap.c,v 1.15.2.2 2006/09/22 14:53:06 dkf Exp $ 14 */ 15 16#include "tkInt.h" 17#include "tkPort.h" 18 19/* 20 * The following data structure represents the master for a bitmap 21 * image: 22 */ 23 24typedef struct BitmapMaster { 25 Tk_ImageMaster tkMaster; /* Tk's token for image master. NULL means 26 * the image is being deleted. */ 27 Tcl_Interp *interp; /* Interpreter for application that is 28 * using image. */ 29 Tcl_Command imageCmd; /* Token for image command (used to delete 30 * it when the image goes away). NULL means 31 * the image command has already been 32 * deleted. */ 33 int width, height; /* Dimensions of image. */ 34 char *data; /* Data comprising bitmap (suitable for 35 * input to XCreateBitmapFromData). May 36 * be NULL if no data. Malloc'ed. */ 37 char *maskData; /* Data for bitmap's mask (suitable for 38 * input to XCreateBitmapFromData). 39 * Malloc'ed. */ 40 Tk_Uid fgUid; /* Value of -foreground option (malloc'ed). */ 41 Tk_Uid bgUid; /* Value of -background option (malloc'ed). */ 42 char *fileString; /* Value of -file option (malloc'ed). */ 43 char *dataString; /* Value of -data option (malloc'ed). */ 44 char *maskFileString; /* Value of -maskfile option (malloc'ed). */ 45 char *maskDataString; /* Value of -maskdata option (malloc'ed). */ 46 struct BitmapInstance *instancePtr; 47 /* First in list of all instances associated 48 * with this master. */ 49} BitmapMaster; 50 51/* 52 * The following data structure represents all of the instances of an 53 * image that lie within a particular window: 54 */ 55 56typedef struct BitmapInstance { 57 int refCount; /* Number of instances that share this 58 * data structure. */ 59 BitmapMaster *masterPtr; /* Pointer to master for image. */ 60 Tk_Window tkwin; /* Window in which the instances will be 61 * displayed. */ 62 XColor *fg; /* Foreground color for displaying image. */ 63 XColor *bg; /* Background color for displaying image. */ 64 Pixmap bitmap; /* The bitmap to display. */ 65 Pixmap mask; /* Mask: only display bitmap pixels where 66 * there are 1's here. */ 67 GC gc; /* Graphics context for displaying bitmap. 68 * None means there was an error while 69 * setting up the instance, so it cannot 70 * be displayed. */ 71 struct BitmapInstance *nextPtr; 72 /* Next in list of all instance structures 73 * associated with masterPtr (NULL means 74 * end of list). */ 75} BitmapInstance; 76 77/* 78 * The type record for bitmap images: 79 */ 80 81static int GetByte _ANSI_ARGS_((Tcl_Channel chan)); 82static int ImgBmapCreate _ANSI_ARGS_((Tcl_Interp *interp, 83 char *name, int argc, Tcl_Obj *CONST objv[], 84 Tk_ImageType *typePtr, Tk_ImageMaster master, 85 ClientData *clientDataPtr)); 86static ClientData ImgBmapGet _ANSI_ARGS_((Tk_Window tkwin, 87 ClientData clientData)); 88static void ImgBmapDisplay _ANSI_ARGS_((ClientData clientData, 89 Display *display, Drawable drawable, 90 int imageX, int imageY, int width, int height, 91 int drawableX, int drawableY)); 92static void ImgBmapFree _ANSI_ARGS_((ClientData clientData, 93 Display *display)); 94static void ImgBmapDelete _ANSI_ARGS_((ClientData clientData)); 95static int ImgBmapPostscript _ANSI_ARGS_((ClientData clientData, 96 Tcl_Interp *interp, Tk_Window tkwin, 97 Tk_PostscriptInfo psinfo, int x, int y, 98 int width, int height, int prepass)); 99 100Tk_ImageType tkBitmapImageType = { 101 "bitmap", /* name */ 102 ImgBmapCreate, /* createProc */ 103 ImgBmapGet, /* getProc */ 104 ImgBmapDisplay, /* displayProc */ 105 ImgBmapFree, /* freeProc */ 106 ImgBmapDelete, /* deleteProc */ 107 ImgBmapPostscript, /* postscriptProc */ 108 (Tk_ImageType *) NULL /* nextPtr */ 109}; 110 111/* 112 * Information used for parsing configuration specs: 113 */ 114 115static Tk_ConfigSpec configSpecs[] = { 116 {TK_CONFIG_UID, "-background", (char *) NULL, (char *) NULL, 117 "", Tk_Offset(BitmapMaster, bgUid), 0}, 118 {TK_CONFIG_STRING, "-data", (char *) NULL, (char *) NULL, 119 (char *) NULL, Tk_Offset(BitmapMaster, dataString), TK_CONFIG_NULL_OK}, 120 {TK_CONFIG_STRING, "-file", (char *) NULL, (char *) NULL, 121 (char *) NULL, Tk_Offset(BitmapMaster, fileString), TK_CONFIG_NULL_OK}, 122 {TK_CONFIG_UID, "-foreground", (char *) NULL, (char *) NULL, 123 "#000000", Tk_Offset(BitmapMaster, fgUid), 0}, 124 {TK_CONFIG_STRING, "-maskdata", (char *) NULL, (char *) NULL, 125 (char *) NULL, Tk_Offset(BitmapMaster, maskDataString), 126 TK_CONFIG_NULL_OK}, 127 {TK_CONFIG_STRING, "-maskfile", (char *) NULL, (char *) NULL, 128 (char *) NULL, Tk_Offset(BitmapMaster, maskFileString), 129 TK_CONFIG_NULL_OK}, 130 {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, 131 (char *) NULL, 0, 0} 132}; 133 134/* 135 * The following data structure is used to describe the state of 136 * parsing a bitmap file or string. It is used for communication 137 * between TkGetBitmapData and NextBitmapWord. 138 */ 139 140#define MAX_WORD_LENGTH 100 141typedef struct ParseInfo { 142 char *string; /* Next character of string data for bitmap, 143 * or NULL if bitmap is being read from 144 * file. */ 145 Tcl_Channel chan; /* File containing bitmap data, or NULL 146 * if no file. */ 147 char word[MAX_WORD_LENGTH+1]; 148 /* Current word of bitmap data, NULL 149 * terminated. */ 150 int wordLength; /* Number of non-NULL bytes in word. */ 151} ParseInfo; 152 153/* 154 * Prototypes for procedures used only locally in this file: 155 */ 156 157static int ImgBmapCmd _ANSI_ARGS_((ClientData clientData, 158 Tcl_Interp *interp, int argc, Tcl_Obj *CONST objv[])); 159static void ImgBmapCmdDeletedProc _ANSI_ARGS_(( 160 ClientData clientData)); 161static void ImgBmapConfigureInstance _ANSI_ARGS_(( 162 BitmapInstance *instancePtr)); 163static int ImgBmapConfigureMaster _ANSI_ARGS_(( 164 BitmapMaster *masterPtr, int argc, Tcl_Obj *CONST objv[], 165 int flags)); 166static int NextBitmapWord _ANSI_ARGS_((ParseInfo *parseInfoPtr)); 167 168/* 169 *---------------------------------------------------------------------- 170 * 171 * ImgBmapCreate -- 172 * 173 * This procedure is called by the Tk image code to create "test" 174 * images. 175 * 176 * Results: 177 * A standard Tcl result. 178 * 179 * Side effects: 180 * The data structure for a new image is allocated. 181 * 182 *---------------------------------------------------------------------- 183 */ 184 185 /* ARGSUSED */ 186static int 187ImgBmapCreate(interp, name, argc, argv, typePtr, master, clientDataPtr) 188 Tcl_Interp *interp; /* Interpreter for application containing 189 * image. */ 190 char *name; /* Name to use for image. */ 191 int argc; /* Number of arguments. */ 192 Tcl_Obj *CONST argv[]; /* Argument objects for options (doesn't 193 * include image name or type). */ 194 Tk_ImageType *typePtr; /* Pointer to our type record (not used). */ 195 Tk_ImageMaster master; /* Token for image, to be used by us in 196 * later callbacks. */ 197 ClientData *clientDataPtr; /* Store manager's token for image here; 198 * it will be returned in later callbacks. */ 199{ 200 BitmapMaster *masterPtr; 201 202 masterPtr = (BitmapMaster *) ckalloc(sizeof(BitmapMaster)); 203 masterPtr->tkMaster = master; 204 masterPtr->interp = interp; 205 masterPtr->imageCmd = Tcl_CreateObjCommand(interp, name, ImgBmapCmd, 206 (ClientData) masterPtr, ImgBmapCmdDeletedProc); 207 masterPtr->width = masterPtr->height = 0; 208 masterPtr->data = NULL; 209 masterPtr->maskData = NULL; 210 masterPtr->fgUid = NULL; 211 masterPtr->bgUid = NULL; 212 masterPtr->fileString = NULL; 213 masterPtr->dataString = NULL; 214 masterPtr->maskFileString = NULL; 215 masterPtr->maskDataString = NULL; 216 masterPtr->instancePtr = NULL; 217 if (ImgBmapConfigureMaster(masterPtr, argc, argv, 0) != TCL_OK) { 218 ImgBmapDelete((ClientData) masterPtr); 219 return TCL_ERROR; 220 } 221 *clientDataPtr = (ClientData) masterPtr; 222 return TCL_OK; 223} 224 225/* 226 *---------------------------------------------------------------------- 227 * 228 * ImgBmapConfigureMaster -- 229 * 230 * This procedure is called when a bitmap image is created or 231 * reconfigured. It process configuration options and resets 232 * any instances of the image. 233 * 234 * Results: 235 * A standard Tcl return value. If TCL_ERROR is returned then 236 * an error message is left in the masterPtr->interp's result. 237 * 238 * Side effects: 239 * Existing instances of the image will be redisplayed to match 240 * the new configuration options. 241 * 242 *---------------------------------------------------------------------- 243 */ 244 245static int 246ImgBmapConfigureMaster(masterPtr, objc, objv, flags) 247 BitmapMaster *masterPtr; /* Pointer to data structure describing 248 * overall bitmap image to (reconfigure). */ 249 int objc; /* Number of entries in objv. */ 250 Tcl_Obj *CONST objv[]; /* Pairs of configuration options for image. */ 251 int flags; /* Flags to pass to Tk_ConfigureWidget, 252 * such as TK_CONFIG_ARGV_ONLY. */ 253{ 254 BitmapInstance *instancePtr; 255 int maskWidth, maskHeight, dummy1, dummy2; 256 257 CONST char **argv = (CONST char **) ckalloc((objc+1) * sizeof(char *)); 258 for (dummy1 = 0; dummy1 < objc; dummy1++) { 259 argv[dummy1]=Tcl_GetString(objv[dummy1]); 260 } 261 argv[objc] = NULL; 262 263 if (Tk_ConfigureWidget(masterPtr->interp, Tk_MainWindow(masterPtr->interp), 264 configSpecs, objc, argv, (char *) masterPtr, flags) 265 != TCL_OK) { 266 ckfree((char *) argv); 267 return TCL_ERROR; 268 } 269 ckfree((char *) argv); 270 271 /* 272 * Parse the bitmap and/or mask to create binary data. Make sure that 273 * the bitmap and mask have the same dimensions. 274 */ 275 276 if (masterPtr->data != NULL) { 277 ckfree(masterPtr->data); 278 masterPtr->data = NULL; 279 } 280 if ((masterPtr->fileString != NULL) || (masterPtr->dataString != NULL)) { 281 masterPtr->data = TkGetBitmapData(masterPtr->interp, 282 masterPtr->dataString, masterPtr->fileString, 283 &masterPtr->width, &masterPtr->height, &dummy1, &dummy2); 284 if (masterPtr->data == NULL) { 285 return TCL_ERROR; 286 } 287 } 288 if (masterPtr->maskData != NULL) { 289 ckfree(masterPtr->maskData); 290 masterPtr->maskData = NULL; 291 } 292 if ((masterPtr->maskFileString != NULL) 293 || (masterPtr->maskDataString != NULL)) { 294 if (masterPtr->data == NULL) { 295 Tcl_SetResult(masterPtr->interp, "can't have mask without bitmap", 296 TCL_STATIC); 297 return TCL_ERROR; 298 } 299 masterPtr->maskData = TkGetBitmapData(masterPtr->interp, 300 masterPtr->maskDataString, masterPtr->maskFileString, 301 &maskWidth, &maskHeight, &dummy1, &dummy2); 302 if (masterPtr->maskData == NULL) { 303 return TCL_ERROR; 304 } 305 if ((maskWidth != masterPtr->width) 306 || (maskHeight != masterPtr->height)) { 307 ckfree(masterPtr->maskData); 308 masterPtr->maskData = NULL; 309 Tcl_SetResult(masterPtr->interp, 310 "bitmap and mask have different sizes", TCL_STATIC); 311 return TCL_ERROR; 312 } 313 } 314 315 /* 316 * Cycle through all of the instances of this image, regenerating 317 * the information for each instance. Then force the image to be 318 * redisplayed everywhere that it is used. 319 */ 320 321 for (instancePtr = masterPtr->instancePtr; instancePtr != NULL; 322 instancePtr = instancePtr->nextPtr) { 323 ImgBmapConfigureInstance(instancePtr); 324 } 325 Tk_ImageChanged(masterPtr->tkMaster, 0, 0, masterPtr->width, 326 masterPtr->height, masterPtr->width, masterPtr->height); 327 return TCL_OK; 328} 329 330/* 331 *---------------------------------------------------------------------- 332 * 333 * ImgBmapConfigureInstance -- 334 * 335 * This procedure is called to create displaying information for 336 * a bitmap image instance based on the configuration information 337 * in the master. It is invoked both when new instances are 338 * created and when the master is reconfigured. 339 * 340 * Results: 341 * None. 342 * 343 * Side effects: 344 * Generates errors via Tcl_BackgroundError if there are problems 345 * in setting up the instance. 346 * 347 *---------------------------------------------------------------------- 348 */ 349 350static void 351ImgBmapConfigureInstance(instancePtr) 352 BitmapInstance *instancePtr; /* Instance to reconfigure. */ 353{ 354 BitmapMaster *masterPtr = instancePtr->masterPtr; 355 XColor *colorPtr; 356 XGCValues gcValues; 357 GC gc; 358 unsigned int mask; 359 Pixmap oldBitmap, oldMask; 360 361 /* 362 * For each of the options in masterPtr, translate the string 363 * form into an internal form appropriate for instancePtr. 364 */ 365 366 if (*masterPtr->bgUid != 0) { 367 colorPtr = Tk_GetColor(masterPtr->interp, instancePtr->tkwin, 368 masterPtr->bgUid); 369 if (colorPtr == NULL) { 370 goto error; 371 } 372 } else { 373 colorPtr = NULL; 374 } 375 if (instancePtr->bg != NULL) { 376 Tk_FreeColor(instancePtr->bg); 377 } 378 instancePtr->bg = colorPtr; 379 380 colorPtr = Tk_GetColor(masterPtr->interp, instancePtr->tkwin, 381 masterPtr->fgUid); 382 if (colorPtr == NULL) { 383 goto error; 384 } 385 if (instancePtr->fg != NULL) { 386 Tk_FreeColor(instancePtr->fg); 387 } 388 instancePtr->fg = colorPtr; 389 390 oldMask = instancePtr->mask; 391 instancePtr->mask = None; 392 393 /* 394 * Careful: We have to allocate new Pixmaps before deleting the old ones. 395 * Otherwise, The XID allocator will always return the same XID for the 396 * new Pixmaps as was used for the old Pixmaps. And that will prevent the 397 * data and/or mask from changing in the GC below. 398 */ 399 400 oldBitmap = instancePtr->bitmap; 401 instancePtr->bitmap = None; 402 oldMask = instancePtr->mask; 403 instancePtr->mask = None; 404 405 if (masterPtr->data != NULL) { 406 instancePtr->bitmap = XCreateBitmapFromData( 407 Tk_Display(instancePtr->tkwin), 408 RootWindowOfScreen(Tk_Screen(instancePtr->tkwin)), 409 masterPtr->data, (unsigned) masterPtr->width, 410 (unsigned) masterPtr->height); 411 } 412 if (masterPtr->maskData != NULL) { 413 instancePtr->mask = XCreateBitmapFromData( 414 Tk_Display(instancePtr->tkwin), 415 RootWindowOfScreen(Tk_Screen(instancePtr->tkwin)), 416 masterPtr->maskData, (unsigned) masterPtr->width, 417 (unsigned) masterPtr->height); 418 } 419 420 if (oldMask != None) { 421 Tk_FreePixmap(Tk_Display(instancePtr->tkwin), oldMask); 422 } 423 if (oldBitmap != None) { 424 Tk_FreePixmap(Tk_Display(instancePtr->tkwin), oldBitmap); 425 } 426 427 if (masterPtr->data != NULL) { 428 gcValues.foreground = instancePtr->fg->pixel; 429 gcValues.graphics_exposures = False; 430 mask = GCForeground|GCGraphicsExposures; 431 if (instancePtr->bg != NULL) { 432 gcValues.background = instancePtr->bg->pixel; 433 mask |= GCBackground; 434 if (instancePtr->mask != None) { 435 gcValues.clip_mask = instancePtr->mask; 436 mask |= GCClipMask; 437 } 438 } else { 439 gcValues.clip_mask = instancePtr->bitmap; 440 mask |= GCClipMask; 441 } 442 gc = Tk_GetGC(instancePtr->tkwin, mask, &gcValues); 443 } else { 444 gc = None; 445 } 446 if (instancePtr->gc != None) { 447 Tk_FreeGC(Tk_Display(instancePtr->tkwin), instancePtr->gc); 448 } 449 instancePtr->gc = gc; 450 return; 451 452 error: 453 /* 454 * An error occurred: clear the graphics context in the instance to 455 * make it clear that this instance cannot be displayed. Then report 456 * the error. 457 */ 458 459 if (instancePtr->gc != None) { 460 Tk_FreeGC(Tk_Display(instancePtr->tkwin), instancePtr->gc); 461 } 462 instancePtr->gc = None; 463 Tcl_AddErrorInfo(masterPtr->interp, "\n (while configuring image \""); 464 Tcl_AddErrorInfo(masterPtr->interp, Tk_NameOfImage(masterPtr->tkMaster)); 465 Tcl_AddErrorInfo(masterPtr->interp, "\")"); 466 Tcl_BackgroundError(masterPtr->interp); 467} 468 469/* 470 *---------------------------------------------------------------------- 471 * 472 * TkGetBitmapData -- 473 * 474 * Given a file name or ASCII string, this procedure parses the 475 * file or string contents to produce binary data for a bitmap. 476 * 477 * Results: 478 * If the bitmap description was parsed successfully then the 479 * return value is a malloc-ed array containing the bitmap data. 480 * The dimensions of the data are stored in *widthPtr and 481 * *heightPtr. *hotXPtr and *hotYPtr are set to the bitmap 482 * hotspot if one is defined, otherwise they are set to -1, -1. 483 * If an error occurred, NULL is returned and an error message is 484 * left in the interp's result. 485 * 486 * Side effects: 487 * A bitmap is created. 488 * 489 *---------------------------------------------------------------------- 490 */ 491 492char * 493TkGetBitmapData(interp, string, fileName, widthPtr, heightPtr, 494 hotXPtr, hotYPtr) 495 Tcl_Interp *interp; /* For reporting errors, or NULL. */ 496 char *string; /* String describing bitmap. May 497 * be NULL. */ 498 char *fileName; /* Name of file containing bitmap 499 * description. Used only if string 500 * is NULL. Must not be NULL if 501 * string is NULL. */ 502 int *widthPtr, *heightPtr; /* Dimensions of bitmap get returned 503 * here. */ 504 int *hotXPtr, *hotYPtr; /* Position of hot spot or -1,-1. */ 505{ 506 int width, height, numBytes, hotX, hotY; 507 CONST char *expandedFileName; 508 char *p, *end; 509 ParseInfo pi; 510 char *data = NULL; 511 Tcl_DString buffer; 512 513 pi.string = string; 514 if (string == NULL) { 515 if ((interp != NULL) && Tcl_IsSafe(interp)) { 516 Tcl_AppendResult(interp, "can't get bitmap data from a file in a", 517 " safe interpreter", (char *) NULL); 518 return NULL; 519 } 520 expandedFileName = Tcl_TranslateFileName(interp, fileName, &buffer); 521 if (expandedFileName == NULL) { 522 return NULL; 523 } 524 pi.chan = Tcl_OpenFileChannel(interp, expandedFileName, "r", 0); 525 Tcl_DStringFree(&buffer); 526 if (pi.chan == NULL) { 527 if (interp != NULL) { 528 Tcl_ResetResult(interp); 529 Tcl_AppendResult(interp, "couldn't read bitmap file \"", 530 fileName, "\": ", Tcl_PosixError(interp), 531 (char *) NULL); 532 } 533 return NULL; 534 } 535 536 if (Tcl_SetChannelOption(interp, pi.chan, "-translation", "binary") 537 != TCL_OK) { 538 return NULL; 539 } 540 if (Tcl_SetChannelOption(interp, pi.chan, "-encoding", "binary") 541 != TCL_OK) { 542 return NULL; 543 } 544 } else { 545 pi.chan = NULL; 546 } 547 548 /* 549 * Parse the lines that define the dimensions of the bitmap, 550 * plus the first line that defines the bitmap data (it declares 551 * the name of a data variable but doesn't include any actual 552 * data). These lines look something like the following: 553 * 554 * #define foo_width 16 555 * #define foo_height 16 556 * #define foo_x_hot 3 557 * #define foo_y_hot 3 558 * static char foo_bits[] = { 559 * 560 * The x_hot and y_hot lines may or may not be present. It's 561 * important to check for "char" in the last line, in order to 562 * reject old X10-style bitmaps that used shorts. 563 */ 564 565 width = 0; 566 height = 0; 567 hotX = -1; 568 hotY = -1; 569 while (1) { 570 if (NextBitmapWord(&pi) != TCL_OK) { 571 goto error; 572 } 573 if ((pi.wordLength >= 6) && (pi.word[pi.wordLength-6] == '_') 574 && (strcmp(pi.word+pi.wordLength-6, "_width") == 0)) { 575 if (NextBitmapWord(&pi) != TCL_OK) { 576 goto error; 577 } 578 width = strtol(pi.word, &end, 0); 579 if ((end == pi.word) || (*end != 0)) { 580 goto error; 581 } 582 } else if ((pi.wordLength >= 7) && (pi.word[pi.wordLength-7] == '_') 583 && (strcmp(pi.word+pi.wordLength-7, "_height") == 0)) { 584 if (NextBitmapWord(&pi) != TCL_OK) { 585 goto error; 586 } 587 height = strtol(pi.word, &end, 0); 588 if ((end == pi.word) || (*end != 0)) { 589 goto error; 590 } 591 } else if ((pi.wordLength >= 6) && (pi.word[pi.wordLength-6] == '_') 592 && (strcmp(pi.word+pi.wordLength-6, "_x_hot") == 0)) { 593 if (NextBitmapWord(&pi) != TCL_OK) { 594 goto error; 595 } 596 hotX = strtol(pi.word, &end, 0); 597 if ((end == pi.word) || (*end != 0)) { 598 goto error; 599 } 600 } else if ((pi.wordLength >= 6) && (pi.word[pi.wordLength-6] == '_') 601 && (strcmp(pi.word+pi.wordLength-6, "_y_hot") == 0)) { 602 if (NextBitmapWord(&pi) != TCL_OK) { 603 goto error; 604 } 605 hotY = strtol(pi.word, &end, 0); 606 if ((end == pi.word) || (*end != 0)) { 607 goto error; 608 } 609 } else if ((pi.word[0] == 'c') && (strcmp(pi.word, "char") == 0)) { 610 while (1) { 611 if (NextBitmapWord(&pi) != TCL_OK) { 612 goto error; 613 } 614 if ((pi.word[0] == '{') && (pi.word[1] == 0)) { 615 goto getData; 616 } 617 } 618 } else if ((pi.word[0] == '{') && (pi.word[1] == 0)) { 619 if (interp != NULL) { 620 Tcl_AppendResult(interp, "format error in bitmap data; ", 621 "looks like it's an obsolete X10 bitmap file", 622 (char *) NULL); 623 } 624 goto errorCleanup; 625 } 626 } 627 628 /* 629 * Now we've read everything but the data. Allocate an array 630 * and read in the data. 631 */ 632 633 getData: 634 if ((width <= 0) || (height <= 0)) { 635 goto error; 636 } 637 numBytes = ((width+7)/8) * height; 638 data = (char *) ckalloc((unsigned) numBytes); 639 for (p = data; numBytes > 0; p++, numBytes--) { 640 if (NextBitmapWord(&pi) != TCL_OK) { 641 goto error; 642 } 643 *p = (char) strtol(pi.word, &end, 0); 644 if (end == pi.word) { 645 goto error; 646 } 647 } 648 649 /* 650 * All done. Clean up and return. 651 */ 652 653 if (pi.chan != NULL) { 654 Tcl_Close(NULL, pi.chan); 655 } 656 *widthPtr = width; 657 *heightPtr = height; 658 *hotXPtr = hotX; 659 *hotYPtr = hotY; 660 return data; 661 662 error: 663 if (interp != NULL) { 664 Tcl_SetResult(interp, "format error in bitmap data", TCL_STATIC); 665 } 666 667 errorCleanup: 668 if (data != NULL) { 669 ckfree(data); 670 } 671 if (pi.chan != NULL) { 672 Tcl_Close(NULL, pi.chan); 673 } 674 return NULL; 675} 676 677/* 678 *---------------------------------------------------------------------- 679 * 680 * NextBitmapWord -- 681 * 682 * This procedure retrieves the next word of information (stuff 683 * between commas or white space) from a bitmap description. 684 * 685 * Results: 686 * Returns TCL_OK if all went well. In this case the next word, 687 * and its length, will be availble in *parseInfoPtr. If the end 688 * of the bitmap description was reached then TCL_ERROR is returned. 689 * 690 * Side effects: 691 * None. 692 * 693 *---------------------------------------------------------------------- 694 */ 695 696static int 697NextBitmapWord(parseInfoPtr) 698 ParseInfo *parseInfoPtr; /* Describes what we're reading 699 * and where we are in it. */ 700{ 701 char *src, *dst; 702 int c; 703 704 parseInfoPtr->wordLength = 0; 705 dst = parseInfoPtr->word; 706 if (parseInfoPtr->string != NULL) { 707 for (src = parseInfoPtr->string; isspace(UCHAR(*src)) || (*src == ','); 708 src++) { 709 if (*src == 0) { 710 return TCL_ERROR; 711 } 712 } 713 for ( ; !isspace(UCHAR(*src)) && (*src != ',') && (*src != 0); src++) { 714 *dst = *src; 715 dst++; 716 parseInfoPtr->wordLength++; 717 if (parseInfoPtr->wordLength > MAX_WORD_LENGTH) { 718 return TCL_ERROR; 719 } 720 } 721 parseInfoPtr->string = src; 722 } else { 723 for (c = GetByte(parseInfoPtr->chan); isspace(UCHAR(c)) || (c == ','); 724 c = GetByte(parseInfoPtr->chan)) { 725 if (c == EOF) { 726 return TCL_ERROR; 727 } 728 } 729 for ( ; !isspace(UCHAR(c)) && (c != ',') && (c != EOF); 730 c = GetByte(parseInfoPtr->chan)) { 731 *dst = c; 732 dst++; 733 parseInfoPtr->wordLength++; 734 if (parseInfoPtr->wordLength > MAX_WORD_LENGTH) { 735 return TCL_ERROR; 736 } 737 } 738 } 739 if (parseInfoPtr->wordLength == 0) { 740 return TCL_ERROR; 741 } 742 parseInfoPtr->word[parseInfoPtr->wordLength] = 0; 743 return TCL_OK; 744} 745 746/* 747 *-------------------------------------------------------------- 748 * 749 * ImgBmapCmd -- 750 * 751 * This procedure is invoked to process the Tcl command 752 * that corresponds to an image managed by this module. 753 * See the user documentation for details on what it does. 754 * 755 * Results: 756 * A standard Tcl result. 757 * 758 * Side effects: 759 * See the user documentation. 760 * 761 *-------------------------------------------------------------- 762 */ 763 764static int 765ImgBmapCmd(clientData, interp, objc, objv) 766 ClientData clientData; /* Information about the image master. */ 767 Tcl_Interp *interp; /* Current interpreter. */ 768 int objc; /* Number of arguments. */ 769 Tcl_Obj *CONST objv[]; /* Argument objects. */ 770{ 771 static CONST char *bmapOptions[] = {"cget", "configure", (char *) NULL}; 772 BitmapMaster *masterPtr = (BitmapMaster *) clientData; 773 int code, index; 774 775 if (objc < 2) { 776 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); 777 return TCL_ERROR; 778 } 779 if (Tcl_GetIndexFromObj(interp, objv[1], bmapOptions, "option", 0, 780 &index) != TCL_OK) { 781 return TCL_ERROR; 782 } 783 switch (index) { 784 case 0: { 785 if (objc != 3) { 786 Tcl_WrongNumArgs(interp, 2, objv, "option"); 787 return TCL_ERROR; 788 } 789 return Tk_ConfigureValue(interp, Tk_MainWindow(interp), configSpecs, 790 (char *) masterPtr, Tcl_GetString(objv[2]), 0); 791 } 792 case 1: { 793 if (objc == 2) { 794 code = Tk_ConfigureInfo(interp, Tk_MainWindow(interp), 795 configSpecs, (char *) masterPtr, (char *) NULL, 0); 796 } else if (objc == 3) { 797 code = Tk_ConfigureInfo(interp, Tk_MainWindow(interp), 798 configSpecs, (char *) masterPtr, 799 Tcl_GetString(objv[2]), 0); 800 } else { 801 code = ImgBmapConfigureMaster(masterPtr, objc-2, objv+2, 802 TK_CONFIG_ARGV_ONLY); 803 } 804 return code; 805 } 806 default: { 807 panic("bad const entries to bmapOptions in ImgBmapCmd"); 808 } 809 } 810 return TCL_OK; 811} 812 813/* 814 *---------------------------------------------------------------------- 815 * 816 * ImgBmapGet -- 817 * 818 * This procedure is called for each use of a bitmap image in a 819 * widget. 820 * 821 * Results: 822 * The return value is a token for the instance, which is passed 823 * back to us in calls to ImgBmapDisplay and ImgBmapFree. 824 * 825 * Side effects: 826 * A data structure is set up for the instance (or, an existing 827 * instance is re-used for the new one). 828 * 829 *---------------------------------------------------------------------- 830 */ 831 832static ClientData 833ImgBmapGet(tkwin, masterData) 834 Tk_Window tkwin; /* Window in which the instance will be 835 * used. */ 836 ClientData masterData; /* Pointer to our master structure for the 837 * image. */ 838{ 839 BitmapMaster *masterPtr = (BitmapMaster *) masterData; 840 BitmapInstance *instancePtr; 841 842 /* 843 * See if there is already an instance for this window. If so 844 * then just re-use it. 845 */ 846 847 for (instancePtr = masterPtr->instancePtr; instancePtr != NULL; 848 instancePtr = instancePtr->nextPtr) { 849 if (instancePtr->tkwin == tkwin) { 850 instancePtr->refCount++; 851 return (ClientData) instancePtr; 852 } 853 } 854 855 /* 856 * The image isn't already in use in this window. Make a new 857 * instance of the image. 858 */ 859 860 instancePtr = (BitmapInstance *) ckalloc(sizeof(BitmapInstance)); 861 instancePtr->refCount = 1; 862 instancePtr->masterPtr = masterPtr; 863 instancePtr->tkwin = tkwin; 864 instancePtr->fg = NULL; 865 instancePtr->bg = NULL; 866 instancePtr->bitmap = None; 867 instancePtr->mask = None; 868 instancePtr->gc = None; 869 instancePtr->nextPtr = masterPtr->instancePtr; 870 masterPtr->instancePtr = instancePtr; 871 ImgBmapConfigureInstance(instancePtr); 872 873 /* 874 * If this is the first instance, must set the size of the image. 875 */ 876 877 if (instancePtr->nextPtr == NULL) { 878 Tk_ImageChanged(masterPtr->tkMaster, 0, 0, 0, 0, masterPtr->width, 879 masterPtr->height); 880 } 881 882 return (ClientData) instancePtr; 883} 884 885/* 886 *---------------------------------------------------------------------- 887 * 888 * ImgBmapDisplay -- 889 * 890 * This procedure is invoked to draw a bitmap image. 891 * 892 * Results: 893 * None. 894 * 895 * Side effects: 896 * A portion of the image gets rendered in a pixmap or window. 897 * 898 *---------------------------------------------------------------------- 899 */ 900 901static void 902ImgBmapDisplay(clientData, display, drawable, imageX, imageY, width, 903 height, drawableX, drawableY) 904 ClientData clientData; /* Pointer to BitmapInstance structure for 905 * for instance to be displayed. */ 906 Display *display; /* Display on which to draw image. */ 907 Drawable drawable; /* Pixmap or window in which to draw image. */ 908 int imageX, imageY; /* Upper-left corner of region within image 909 * to draw. */ 910 int width, height; /* Dimensions of region within image to draw. */ 911 int drawableX, drawableY; /* Coordinates within drawable that 912 * correspond to imageX and imageY. */ 913{ 914 BitmapInstance *instancePtr = (BitmapInstance *) clientData; 915 int masking; 916 917 /* 918 * If there's no graphics context, it means that an error occurred 919 * while creating the image instance so it can't be displayed. 920 */ 921 922 if (instancePtr->gc == None) { 923 return; 924 } 925 926 /* 927 * If masking is in effect, must modify the mask origin within 928 * the graphics context to line up with the image's origin. 929 * Then draw the image and reset the clip origin, if there's 930 * a mask. 931 */ 932 933 masking = (instancePtr->mask != None) || (instancePtr->bg == NULL); 934 if (masking) { 935 XSetClipOrigin(display, instancePtr->gc, drawableX - imageX, 936 drawableY - imageY); 937 } 938 XCopyPlane(display, instancePtr->bitmap, drawable, instancePtr->gc, 939 imageX, imageY, (unsigned) width, (unsigned) height, 940 drawableX, drawableY, 1); 941 if (masking) { 942 XSetClipOrigin(display, instancePtr->gc, 0, 0); 943 } 944} 945 946/* 947 *---------------------------------------------------------------------- 948 * 949 * ImgBmapFree -- 950 * 951 * This procedure is called when a widget ceases to use a 952 * particular instance of an image. 953 * 954 * Results: 955 * None. 956 * 957 * Side effects: 958 * Internal data structures get cleaned up. 959 * 960 *---------------------------------------------------------------------- 961 */ 962 963static void 964ImgBmapFree(clientData, display) 965 ClientData clientData; /* Pointer to BitmapInstance structure for 966 * for instance to be displayed. */ 967 Display *display; /* Display containing window that used image. */ 968{ 969 BitmapInstance *instancePtr = (BitmapInstance *) clientData; 970 BitmapInstance *prevPtr; 971 972 instancePtr->refCount--; 973 if (instancePtr->refCount > 0) { 974 return; 975 } 976 977 /* 978 * There are no more uses of the image within this widget. Free 979 * the instance structure. 980 */ 981 982 if (instancePtr->fg != NULL) { 983 Tk_FreeColor(instancePtr->fg); 984 } 985 if (instancePtr->bg != NULL) { 986 Tk_FreeColor(instancePtr->bg); 987 } 988 if (instancePtr->bitmap != None) { 989 Tk_FreePixmap(display, instancePtr->bitmap); 990 } 991 if (instancePtr->mask != None) { 992 Tk_FreePixmap(display, instancePtr->mask); 993 } 994 if (instancePtr->gc != None) { 995 Tk_FreeGC(display, instancePtr->gc); 996 } 997 if (instancePtr->masterPtr->instancePtr == instancePtr) { 998 instancePtr->masterPtr->instancePtr = instancePtr->nextPtr; 999 } else { 1000 for (prevPtr = instancePtr->masterPtr->instancePtr; 1001 prevPtr->nextPtr != instancePtr; prevPtr = prevPtr->nextPtr) { 1002 /* Empty loop body */ 1003 } 1004 prevPtr->nextPtr = instancePtr->nextPtr; 1005 } 1006 ckfree((char *) instancePtr); 1007} 1008 1009/* 1010 *---------------------------------------------------------------------- 1011 * 1012 * ImgBmapDelete -- 1013 * 1014 * This procedure is called by the image code to delete the 1015 * master structure for an image. 1016 * 1017 * Results: 1018 * None. 1019 * 1020 * Side effects: 1021 * Resources associated with the image get freed. 1022 * 1023 *---------------------------------------------------------------------- 1024 */ 1025 1026static void 1027ImgBmapDelete(masterData) 1028 ClientData masterData; /* Pointer to BitmapMaster structure for 1029 * image. Must not have any more instances. */ 1030{ 1031 BitmapMaster *masterPtr = (BitmapMaster *) masterData; 1032 1033 if (masterPtr->instancePtr != NULL) { 1034 panic("tried to delete bitmap image when instances still exist"); 1035 } 1036 masterPtr->tkMaster = NULL; 1037 if (masterPtr->imageCmd != NULL) { 1038 Tcl_DeleteCommandFromToken(masterPtr->interp, masterPtr->imageCmd); 1039 } 1040 if (masterPtr->data != NULL) { 1041 ckfree(masterPtr->data); 1042 } 1043 if (masterPtr->maskData != NULL) { 1044 ckfree(masterPtr->maskData); 1045 } 1046 Tk_FreeOptions(configSpecs, (char *) masterPtr, (Display *) NULL, 0); 1047 ckfree((char *) masterPtr); 1048} 1049 1050/* 1051 *---------------------------------------------------------------------- 1052 * 1053 * ImgBmapCmdDeletedProc -- 1054 * 1055 * This procedure is invoked when the image command for an image 1056 * is deleted. It deletes the image. 1057 * 1058 * Results: 1059 * None. 1060 * 1061 * Side effects: 1062 * The image is deleted. 1063 * 1064 *---------------------------------------------------------------------- 1065 */ 1066 1067static void 1068ImgBmapCmdDeletedProc(clientData) 1069 ClientData clientData; /* Pointer to BitmapMaster structure for 1070 * image. */ 1071{ 1072 BitmapMaster *masterPtr = (BitmapMaster *) clientData; 1073 1074 masterPtr->imageCmd = NULL; 1075 if (masterPtr->tkMaster != NULL) { 1076 Tk_DeleteImage(masterPtr->interp, Tk_NameOfImage(masterPtr->tkMaster)); 1077 } 1078} 1079 1080/* 1081 *---------------------------------------------------------------------- 1082 * 1083 * GetByte -- 1084 * 1085 * Get the next byte from the open channel. 1086 * 1087 * Results: 1088 * The next byte or EOF. 1089 * 1090 * Side effects: 1091 * We read from the channel. 1092 * 1093 *---------------------------------------------------------------------- 1094 */ 1095 1096static int 1097GetByte(chan) 1098 Tcl_Channel chan; /* The channel we read from. */ 1099{ 1100 char buffer; 1101 int size; 1102 1103 size = Tcl_Read(chan, &buffer, 1); 1104 if (size <= 0) { 1105 return EOF; 1106 } else { 1107 return buffer; 1108 } 1109} 1110 1111 1112/* 1113 *---------------------------------------------------------------------- 1114 * 1115 * ImgBmapPsImagemask -- 1116 * 1117 * This procedure generates postscript suitable for rendering a 1118 * single bitmap of an image. A single bitmap image might contain both 1119 * a foreground and a background bitmap. This routine is called once 1120 * for each such bitmap in a bitmap image. 1121 * 1122 * Prior to invoking this routine, the following setup has occurred: 1123 * 1124 * 1. The postscript foreground color has been set to the color 1125 * used to render the bitmap. 1126 * 1127 * 2. The origin of the postscript coordinate system is set to 1128 * the lower left corner of the bitmap. 1129 * 1130 * 3. The postscript coordinate system has been scaled so that 1131 * the entire bitmap is one unit squared. 1132 * 1133 * Some postscript implementations cannot handle bitmap strings 1134 * longer than about 60k characters. If the bitmap data is that big 1135 * or bigger, then we render it by splitting it into several smaller 1136 * bitmaps. 1137 * 1138 * Results: 1139 * Returns TCL_OK on success. Returns TCL_ERROR and leaves and error 1140 * message in interp->result if there is a problem. 1141 * 1142 * Side effects: 1143 * Postscript code is appended to interp->result. 1144 * 1145 *---------------------------------------------------------------------- 1146 */ 1147 1148static int 1149ImgBmapPsImagemask(interp, width, height, data) 1150 Tcl_Interp *interp; /* Append postscript to this interpreter */ 1151 int width, height; /* Width and height of the bitmap in pixels */ 1152 char *data; /* Data for the bitmap */ 1153{ 1154 int i, j, nBytePerRow; 1155 char buffer[200]; 1156 1157 /* 1158 * The bit order of bitmaps in Tk is the opposite of the bit order that 1159 * postscript uses. (In Tk, the least significant bit is on the right 1160 * side of the bitmap and in postscript the least significant bit is shown 1161 * on the left.) The following array is used to reverse the order of bits 1162 * within a byte so that the bits will be in the order postscript expects. 1163 */ 1164 static unsigned char bit_reverse[] = { 1165 0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240, 1166 8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248, 1167 4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244, 1168 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252, 1169 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242, 1170 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, 1171 6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, 1172 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254, 1173 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, 1174 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, 1175 5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, 1176 13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, 1177 3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243, 1178 11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251, 1179 7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247, 1180 15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255, 1181 }; 1182 1183 if (width*height > 60000) { 1184 Tcl_ResetResult(interp); 1185 Tcl_AppendResult(interp, "unable to generate postscript for bitmaps " 1186 "larger than 60000 pixels", NULL); 1187 return TCL_ERROR; 1188 } 1189 sprintf(buffer, "0 0 moveto %d %d true [%d 0 0 %d 0 %d] {<\n", 1190 width, height, width, -height, height); 1191 Tcl_AppendResult(interp, buffer, NULL); 1192 nBytePerRow = (width+7)/8; 1193 for(i=0; i<height; i++){ 1194 for(j=0; j<nBytePerRow; j++){ 1195 sprintf(buffer, " %02x", bit_reverse[0xff & data[i*nBytePerRow + j]]); 1196 Tcl_AppendResult(interp, buffer, NULL); 1197 } 1198 Tcl_AppendResult(interp, "\n", NULL); 1199 } 1200 Tcl_AppendResult(interp, ">} imagemask \n", NULL); 1201 return TCL_OK; 1202} 1203 1204/* 1205 *---------------------------------------------------------------------- 1206 * 1207 * ImgBmapPostscript -- 1208 * 1209 * This procedure generates postscript for rendering a bitmap image. 1210 * 1211 * Results: 1212 * On success, this routine writes postscript code into interp->result 1213 * and returns TCL_OK TCL_ERROR is returned and an error 1214 * message is left in interp->result if anything goes wrong. 1215 * 1216 * Side effects: 1217 * None. 1218 * 1219 *---------------------------------------------------------------------- 1220 */ 1221 1222static int 1223ImgBmapPostscript(clientData, interp, tkwin, psinfo, x, y, width, height, 1224 prepass) 1225 ClientData clientData; 1226 Tcl_Interp *interp; 1227 Tk_Window tkwin; 1228 Tk_PostscriptInfo psinfo; 1229 int x, y, width, height, prepass; 1230{ 1231 BitmapMaster *masterPtr = (BitmapMaster *) clientData; 1232 char buffer[200]; 1233 1234 if (prepass) { 1235 return TCL_OK; 1236 } 1237 1238 /* 1239 * There is nothing to do for bitmaps with zero width or height 1240 */ 1241 if( width<=0 || height<=0 || masterPtr->width<=0 || masterPtr->height<=0 ){ 1242 return TCL_OK; 1243 } 1244 1245 /* 1246 * Translate the origin of the coordinate system to be the lower-left 1247 * corner of the bitmap and adjust the scale of the coordinate system 1248 * so that entire bitmap covers one square unit of the page. 1249 * The calling function put a "gsave" into the postscript and 1250 * will add a "grestore" at after this routine returns, so it is safe 1251 * to make whatever changes are necessary here. 1252 */ 1253 if( x!=0 || y!=0 ){ 1254 sprintf(buffer, "%d %d moveto\n", x, y); 1255 Tcl_AppendResult(interp, buffer, NULL); 1256 } 1257 if( width!=1 || height!=1 ){ 1258 sprintf(buffer, "%d %d scale\n", width, height); 1259 Tcl_AppendResult(interp, buffer, NULL); 1260 } 1261 1262 /* 1263 * Color the background, if there is one. This step is skipped if the 1264 * background is transparent. If the background is not transparent and 1265 * there is no background mask, then color the complete rectangle that 1266 * encloses the bitmap. If there is a background mask, then only apply 1267 * color to the bits specified by the mask. 1268 */ 1269 if ((masterPtr->bgUid != NULL) && (masterPtr->bgUid[0] != '\000')) { 1270 XColor color; 1271 XParseColor(Tk_Display(tkwin), Tk_Colormap(tkwin), masterPtr->bgUid, 1272 &color); 1273 if (Tk_PostscriptColor(interp, psinfo, &color) != TCL_OK) { 1274 return TCL_ERROR; 1275 } 1276 if (masterPtr->maskData == NULL) { 1277 Tcl_AppendResult(interp, 1278 "0 0 moveto 1 0 rlineto 0 1 rlineto -1 0 rlineto " 1279 "closepath fill\n", NULL); 1280 } else if (ImgBmapPsImagemask(interp, masterPtr->width, 1281 masterPtr->height, masterPtr->maskData) != TCL_OK) { 1282 return TCL_ERROR; 1283 } 1284 } 1285 1286 /* 1287 * Draw the bitmap foreground, assuming there is one. 1288 */ 1289 if ( (masterPtr->fgUid != NULL) && (masterPtr->data != NULL) ) { 1290 XColor color; 1291 XParseColor(Tk_Display(tkwin), Tk_Colormap(tkwin), masterPtr->fgUid, 1292 &color); 1293 if (Tk_PostscriptColor(interp, psinfo, &color) != TCL_OK) { 1294 return TCL_ERROR; 1295 } 1296 if (ImgBmapPsImagemask(interp, masterPtr->width, masterPtr->height, 1297 masterPtr->data) != TCL_OK) { 1298 return TCL_ERROR; 1299 } 1300 } 1301 return TCL_OK; 1302} 1303