/*1* tkImage.c --2*3* This module implements the image protocol, which allows lots4* of different kinds of images to be used in lots of different5* widgets.6*7* Copyright (c) 1994 The Regents of the University of California.8* Copyright (c) 1994-1996 Sun Microsystems, Inc.9*10* See the file "license.terms" for information on usage and redistribution11* of this file, and for a DISCLAIMER OF ALL WARRANTIES.12*13* SCCS: @(#) tkImage.c 1.11 96/03/01 17:19:2814*/1516#include "tkInt.h"1718/*19* Each call to Tk_GetImage returns a pointer to one of the following20* structures, which is used as a token by clients (widgets) that21* display images.22*/2324typedef struct Image {25Tk_Window tkwin; /* Window passed to Tk_GetImage (needed to26* "re-get" the image later if the manager27* changes). */28Display *display; /* Display for tkwin. Needed because when29* the image is eventually freed tkwin may30* not exist anymore. */31struct ImageMaster *masterPtr;32/* Master for this image (identifiers image33* manager, for example). */34ClientData instanceData;35/* One word argument to pass to image manager36* when dealing with this image instance. */37Tk_ImageChangedProc *changeProc;38/* Code in widget to call when image changes39* in a way that affects redisplay. */40ClientData widgetClientData;41/* Argument to pass to changeProc. */42struct Image *nextPtr; /* Next in list of all image instances43* associated with the same name. */4445} Image;4647/*48* For each image master there is one of the following structures,49* which represents a name in the image table and all of the images50* instantiated from it. Entries in mainPtr->imageTable point to51* these structures.52*/5354typedef struct ImageMaster {55Tk_ImageType *typePtr; /* Information about image type. NULL means56* that no image manager owns this image: the57* image was deleted. */58ClientData masterData; /* One-word argument to pass to image mgr59* when dealing with the master, as opposed60* to instances. */61int width, height; /* Last known dimensions for image. */62Tcl_HashTable *tablePtr; /* Pointer to hash table containing image63* (the imageTable field in some TkMainInfo64* structure). */65Tcl_HashEntry *hPtr; /* Hash entry in mainPtr->imageTable for66* this structure (used to delete the hash67* entry). */68Image *instancePtr; /* Pointer to first in list of instances69* derived from this name. */70} ImageMaster;7172/*73* The following variable points to the first in a list of all known74* image types.75*/7677static Tk_ImageType *imageTypeList = NULL;7879/*80* Prototypes for local procedures:81*/8283static void DeleteImage _ANSI_ARGS_((ImageMaster *masterPtr));8485/*86*----------------------------------------------------------------------87*88* Tk_CreateImageType --89*90* This procedure is invoked by an image manager to tell Tk about91* a new kind of image and the procedures that manage the new type.92* The procedure is typically invoked during Tcl_AppInit.93*94* Results:95* None.96*97* Side effects:98* The new image type is entered into a table used in the "image99* create" command.100*101*----------------------------------------------------------------------102*/103104void105Tk_CreateImageType(typePtr)106Tk_ImageType *typePtr; /* Structure describing the type. All of107* the fields except "nextPtr" must be filled108* in by caller. Must not have been passed109* to Tk_CreateImageType previously. */110{111Tk_ImageType *typePtr2;112113typePtr2 = (Tk_ImageType *) ckalloc(sizeof(Tk_ImageType));114*typePtr2 = *typePtr;115typePtr2->name = (char *) ckalloc((unsigned) (strlen(typePtr->name) + 1));116strcpy(typePtr2->name, typePtr->name);117typePtr2->nextPtr = imageTypeList;118imageTypeList = typePtr2;119}120121/*122*----------------------------------------------------------------------123*124* Tk_ImageCmd --125*126* This procedure is invoked to process the "image" Tcl command.127* See the user documentation for details on what it does.128*129* Results:130* A standard Tcl result.131*132* Side effects:133* See the user documentation.134*135*----------------------------------------------------------------------136*/137138int139Tk_ImageCmd(clientData, interp, argc, argv)140ClientData clientData; /* Main window associated with interpreter. */141Tcl_Interp *interp; /* Current interpreter. */142int argc; /* Number of arguments. */143char **argv; /* Argument strings. */144{145TkWindow *winPtr = (TkWindow *) clientData;146int c, i, new, firstOption;147size_t length;148Tk_ImageType *typePtr;149ImageMaster *masterPtr;150Image *imagePtr;151Tcl_HashEntry *hPtr;152Tcl_HashSearch search;153char idString[30], *name;154static int id = 0;155156if (argc < 2) {157Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],158" option ?args?\"", (char *) NULL);159return TCL_ERROR;160}161c = argv[1][0];162length = strlen(argv[1]);163if ((c == 'c') && (strncmp(argv[1], "create", length) == 0)) {164if (argc < 3) {165Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],166" create type ?name? ?options?\"", (char *) NULL);167return TCL_ERROR;168}169c = argv[2][0];170171/*172* Look up the image type.173*/174175for (typePtr = imageTypeList; typePtr != NULL;176typePtr = typePtr->nextPtr) {177if ((c == typePtr->name[0])178&& (strcmp(argv[2], typePtr->name) == 0)) {179break;180}181}182if (typePtr == NULL) {183Tcl_AppendResult(interp, "image type \"", argv[2],184"\" doesn't exist", (char *) NULL);185return TCL_ERROR;186}187188/*189* Figure out a name to use for the new image.190*/191192if ((argc == 3) || (argv[3][0] == '-')) {193id++;194sprintf(idString, "image%d", id);195name = idString;196firstOption = 3;197} else {198name = argv[3];199firstOption = 4;200}201202/*203* Create the data structure for the new image.204*/205206hPtr = Tcl_CreateHashEntry(&winPtr->mainPtr->imageTable, name, &new);207if (new) {208masterPtr = (ImageMaster *) ckalloc(sizeof(ImageMaster));209masterPtr->typePtr = NULL;210masterPtr->masterData = NULL;211masterPtr->width = masterPtr->height = 1;212masterPtr->tablePtr = &winPtr->mainPtr->imageTable;213masterPtr->hPtr = hPtr;214masterPtr->instancePtr = NULL;215Tcl_SetHashValue(hPtr, masterPtr);216} else {217/*218* An image already exists by this name. Disconnect the219* instances from the master.220*/221222masterPtr = (ImageMaster *) Tcl_GetHashValue(hPtr);223if (masterPtr->typePtr != NULL) {224for (imagePtr = masterPtr->instancePtr; imagePtr != NULL;225imagePtr = imagePtr->nextPtr) {226(*masterPtr->typePtr->freeProc)(227imagePtr->instanceData, imagePtr->display);228(*imagePtr->changeProc)(imagePtr->widgetClientData, 0, 0,229masterPtr->width, masterPtr->height, masterPtr->width,230masterPtr->height);231}232(*masterPtr->typePtr->deleteProc)(masterPtr->masterData);233masterPtr->typePtr = NULL;234}235}236237/*238* Call the image type manager so that it can perform its own239* initialization, then re-"get" for any existing instances of240* the image.241*/242243if ((*typePtr->createProc)(interp, name, argc-firstOption,244argv+firstOption, typePtr, (Tk_ImageMaster) masterPtr,245&masterPtr->masterData) != TCL_OK) {246DeleteImage(masterPtr);247return TCL_ERROR;248}249masterPtr->typePtr = typePtr;250for (imagePtr = masterPtr->instancePtr; imagePtr != NULL;251imagePtr = imagePtr->nextPtr) {252imagePtr->instanceData = (*typePtr->getProc)(253imagePtr->tkwin, masterPtr->masterData);254}255interp->result = Tcl_GetHashKey(&winPtr->mainPtr->imageTable, hPtr);256} else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {257for (i = 2; i < argc; i++) {258hPtr = Tcl_FindHashEntry(&winPtr->mainPtr->imageTable, argv[i]);259if (hPtr == NULL) {260Tcl_AppendResult(interp, "image \"", argv[i],261"\" doesn't exist", (char *) NULL);262return TCL_ERROR;263}264masterPtr = (ImageMaster *) Tcl_GetHashValue(hPtr);265DeleteImage(masterPtr);266}267} else if ((c == 'h') && (strncmp(argv[1], "height", length) == 0)) {268if (argc != 3) {269Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],270" height name\"", (char *) NULL);271return TCL_ERROR;272}273hPtr = Tcl_FindHashEntry(&winPtr->mainPtr->imageTable, argv[2]);274if (hPtr == NULL) {275Tcl_AppendResult(interp, "image \"", argv[2],276"\" doesn't exist", (char *) NULL);277return TCL_ERROR;278}279masterPtr = (ImageMaster *) Tcl_GetHashValue(hPtr);280sprintf(interp->result, "%d", masterPtr->height);281} else if ((c == 'n') && (strncmp(argv[1], "names", length) == 0)) {282if (argc != 2) {283Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],284" names\"", (char *) NULL);285return TCL_ERROR;286}287for (hPtr = Tcl_FirstHashEntry(&winPtr->mainPtr->imageTable, &search);288hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {289Tcl_AppendElement(interp, Tcl_GetHashKey(290&winPtr->mainPtr->imageTable, hPtr));291}292} else if ((c == 't') && (strcmp(argv[1], "type") == 0)) {293if (argc != 3) {294Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],295" type name\"", (char *) NULL);296return TCL_ERROR;297}298hPtr = Tcl_FindHashEntry(&winPtr->mainPtr->imageTable, argv[2]);299if (hPtr == NULL) {300Tcl_AppendResult(interp, "image \"", argv[2],301"\" doesn't exist", (char *) NULL);302return TCL_ERROR;303}304masterPtr = (ImageMaster *) Tcl_GetHashValue(hPtr);305if (masterPtr->typePtr != NULL) {306interp->result = masterPtr->typePtr->name;307}308} else if ((c == 't') && (strcmp(argv[1], "types") == 0)) {309if (argc != 2) {310Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],311" types\"", (char *) NULL);312return TCL_ERROR;313}314for (typePtr = imageTypeList; typePtr != NULL;315typePtr = typePtr->nextPtr) {316Tcl_AppendElement(interp, typePtr->name);317}318} else if ((c == 'w') && (strncmp(argv[1], "width", length) == 0)) {319if (argc != 3) {320Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],321" width name\"", (char *) NULL);322return TCL_ERROR;323}324hPtr = Tcl_FindHashEntry(&winPtr->mainPtr->imageTable, argv[2]);325if (hPtr == NULL) {326Tcl_AppendResult(interp, "image \"", argv[2],327"\" doesn't exist", (char *) NULL);328return TCL_ERROR;329}330masterPtr = (ImageMaster *) Tcl_GetHashValue(hPtr);331sprintf(interp->result, "%d", masterPtr->width);332} else {333Tcl_AppendResult(interp, "bad option \"", argv[1],334"\": must be create, delete, height, names, type, types,",335" or width", (char *) NULL);336return TCL_ERROR;337}338return TCL_OK;339}340341/*342*----------------------------------------------------------------------343*344* Tk_ImageChanged --345*346* This procedure is called by an image manager whenever something347* has happened that requires the image to be redrawn (some of its348* pixels have changed, or its size has changed).349*350* Results:351* None.352*353* Side effects:354* Any widgets that display the image are notified so that they355* can redisplay themselves as appropriate.356*357*----------------------------------------------------------------------358*/359360void361Tk_ImageChanged(imageMaster, x, y, width, height, imageWidth,362imageHeight)363Tk_ImageMaster imageMaster; /* Image that needs redisplay. */364int x, y; /* Coordinates of upper-left pixel of365* region of image that needs to be366* redrawn. */367int width, height; /* Dimensions (in pixels) of region of368* image to redraw. If either dimension369* is zero then the image doesn't need to370* be redrawn (perhaps all that happened is371* that its size changed). */372int imageWidth, imageHeight;/* New dimensions of image. */373{374ImageMaster *masterPtr = (ImageMaster *) imageMaster;375Image *imagePtr;376377masterPtr->width = imageWidth;378masterPtr->height = imageHeight;379for (imagePtr = masterPtr->instancePtr; imagePtr != NULL;380imagePtr = imagePtr->nextPtr) {381(*imagePtr->changeProc)(imagePtr->widgetClientData, x, y,382width, height, imageWidth, imageHeight);383}384}385386/*387*----------------------------------------------------------------------388*389* Tk_NameOfImage --390*391* Given a token for an image master, this procedure returns392* the name of the image.393*394* Results:395* The return value is the string name for imageMaster.396*397* Side effects:398* None.399*400*----------------------------------------------------------------------401*/402403char *404Tk_NameOfImage(imageMaster)405Tk_ImageMaster imageMaster; /* Token for image. */406{407ImageMaster *masterPtr = (ImageMaster *) imageMaster;408409return Tcl_GetHashKey(masterPtr->tablePtr, masterPtr->hPtr);410}411412/*413*----------------------------------------------------------------------414*415* Tk_GetImage --416*417* This procedure is invoked by a widget when it wants to use418* a particular image in a particular window.419*420* Results:421* The return value is a token for the image. If there is no image422* by the given name, then NULL is returned and an error message is423* left in interp->result.424*425* Side effects:426* Tk records the fact that the widget is using the image, and427* it will invoke changeProc later if the widget needs redisplay428* (i.e. its size changes or some of its pixels change). The429* caller must eventually invoke Tk_FreeImage when it no longer430* needs the image.431*432*----------------------------------------------------------------------433*/434435Tk_Image436Tk_GetImage(interp, tkwin, name, changeProc, clientData)437Tcl_Interp *interp; /* Place to leave error message if image438* can't be found. */439Tk_Window tkwin; /* Token for window in which image will440* be used. */441char *name; /* Name of desired image. */442Tk_ImageChangedProc *changeProc;443/* Procedure to invoke when redisplay is444* needed because image's pixels or size445* changed. */446ClientData clientData; /* One-word argument to pass to damageProc. */447{448Tcl_HashEntry *hPtr;449ImageMaster *masterPtr;450Image *imagePtr;451452hPtr = Tcl_FindHashEntry(&((TkWindow *) tkwin)->mainPtr->imageTable, name);453if (hPtr == NULL) {454goto noSuchImage;455}456masterPtr = (ImageMaster *) Tcl_GetHashValue(hPtr);457if (masterPtr->typePtr == NULL) {458goto noSuchImage;459}460imagePtr = (Image *) ckalloc(sizeof(Image));461imagePtr->tkwin = tkwin;462imagePtr->display = Tk_Display(tkwin);463imagePtr->masterPtr = masterPtr;464imagePtr->instanceData =465(*masterPtr->typePtr->getProc)(tkwin, masterPtr->masterData);466imagePtr->changeProc = changeProc;467imagePtr->widgetClientData = clientData;468imagePtr->nextPtr = masterPtr->instancePtr;469masterPtr->instancePtr = imagePtr;470return (Tk_Image) imagePtr;471472noSuchImage:473Tcl_AppendResult(interp, "image \"", name, "\" doesn't exist",474(char *) NULL);475return NULL;476}477478/*479*----------------------------------------------------------------------480*481* Tk_FreeImage --482*483* This procedure is invoked by a widget when it no longer needs484* an image acquired by a previous call to Tk_GetImage. For each485* call to Tk_GetImage there must be exactly one call to Tk_FreeImage.486*487* Results:488* None.489*490* Side effects:491* The association between the image and the widget is removed.492*493*----------------------------------------------------------------------494*/495496void497Tk_FreeImage(image)498Tk_Image image; /* Token for image that is no longer499* needed by a widget. */500{501Image *imagePtr = (Image *) image;502ImageMaster *masterPtr = imagePtr->masterPtr;503Image *prevPtr;504505/*506* Clean up the particular instance.507*/508509if (masterPtr->typePtr != NULL) {510(*masterPtr->typePtr->freeProc)(imagePtr->instanceData,511imagePtr->display);512}513prevPtr = masterPtr->instancePtr;514if (prevPtr == imagePtr) {515masterPtr->instancePtr = imagePtr->nextPtr;516} else {517while (prevPtr->nextPtr != imagePtr) {518prevPtr = prevPtr->nextPtr;519}520prevPtr->nextPtr = imagePtr->nextPtr;521}522ckfree((char *) imagePtr);523524/*525* If there are no more instances left for the master, and if the526* master image has been deleted, then delete the master too.527*/528529if ((masterPtr->typePtr == NULL) && (masterPtr->instancePtr == NULL)) {530Tcl_DeleteHashEntry(masterPtr->hPtr);531ckfree((char *) masterPtr);532}533}534535/*536*----------------------------------------------------------------------537*538* Tk_RedrawImage --539*540* This procedure is called by widgets that contain images in order541* to redisplay an image on the screen or an off-screen pixmap.542*543* Results:544* None.545*546* Side effects:547* The image's manager is notified, and it redraws the desired548* portion of the image before returning.549*550*----------------------------------------------------------------------551*/552553void554Tk_RedrawImage(image, imageX, imageY, width, height, drawable,555drawableX, drawableY)556Tk_Image image; /* Token for image to redisplay. */557int imageX, imageY; /* Upper-left pixel of region in image that558* needs to be redisplayed. */559int width, height; /* Dimensions of region to redraw. */560Drawable drawable; /* Drawable in which to display image561* (window or pixmap). If this is a pixmap,562* it must have the same depth as the window563* used in the Tk_GetImage call for the564* image. */565int drawableX, drawableY; /* Coordinates in drawable that correspond566* to imageX and imageY. */567{568Image *imagePtr = (Image *) image;569570if (imagePtr->masterPtr->typePtr == NULL) {571/*572* No master for image, so nothing to display.573*/574575return;576}577578/*579* Clip the redraw area to the area of the image.580*/581582if (imageX < 0) {583width += imageX;584drawableX -= imageX;585imageX = 0;586}587if (imageY < 0) {588height += imageY;589drawableY -= imageY;590imageY = 0;591}592if ((imageX + width) > imagePtr->masterPtr->width) {593width = imagePtr->masterPtr->width - imageX;594}595if ((imageY + height) > imagePtr->masterPtr->height) {596height = imagePtr->masterPtr->height - imageY;597}598(*imagePtr->masterPtr->typePtr->displayProc)(599imagePtr->instanceData, imagePtr->display, drawable,600imageX, imageY, width, height, drawableX, drawableY);601}602603/*604*----------------------------------------------------------------------605*606* Tk_SizeOfImage --607*608* This procedure returns the current dimensions of an image.609*610* Results:611* The width and height of the image are returned in *widthPtr612* and *heightPtr.613*614* Side effects:615* None.616*617*----------------------------------------------------------------------618*/619620void621Tk_SizeOfImage(image, widthPtr, heightPtr)622Tk_Image image; /* Token for image whose size is wanted. */623int *widthPtr; /* Return width of image here. */624int *heightPtr; /* Return height of image here. */625{626Image *imagePtr = (Image *) image;627628*widthPtr = imagePtr->masterPtr->width;629*heightPtr = imagePtr->masterPtr->height;630}631632/*633*----------------------------------------------------------------------634*635* Tk_DeleteImage --636*637* Given the name of an image, this procedure destroys the638* image.639*640* Results:641* None.642*643* Side effects:644* The image is destroyed; existing instances will display as645* blank areas. If no such image exists then the procedure does646* nothing.647*648*----------------------------------------------------------------------649*/650651void652Tk_DeleteImage(interp, name)653Tcl_Interp *interp; /* Interpreter in which the image was654* created. */655char *name; /* Name of image. */656{657Tcl_HashEntry *hPtr;658Tcl_CmdInfo info;659TkWindow *winPtr;660661if (Tcl_GetCommandInfo(interp, "winfo", &info) == 0) {662return;663}664winPtr = (TkWindow *) info.clientData;665hPtr = Tcl_FindHashEntry(&winPtr->mainPtr->imageTable, name);666if (hPtr == NULL) {667return;668}669DeleteImage((ImageMaster *) Tcl_GetHashValue(hPtr));670}671672/*673*----------------------------------------------------------------------674*675* DeleteImage --676*677* This procedure is responsible for deleting an image.678*679* Results:680* None.681*682* Side effects:683* The connection is dropped between instances of this image and684* an image master. Image instances will redisplay themselves685* as empty areas, but existing instances will not be deleted.686*687*----------------------------------------------------------------------688*/689690static void691DeleteImage(masterPtr)692ImageMaster *masterPtr; /* Pointer to main data structure for image. */693{694Image *imagePtr;695Tk_ImageType *typePtr;696697typePtr = masterPtr->typePtr;698masterPtr->typePtr = NULL;699if (typePtr != NULL) {700for (imagePtr = masterPtr->instancePtr; imagePtr != NULL;701imagePtr = imagePtr->nextPtr) {702(*typePtr->freeProc)(imagePtr->instanceData,703imagePtr->display);704(*imagePtr->changeProc)(imagePtr->widgetClientData, 0, 0,705masterPtr->width, masterPtr->height, masterPtr->width,706masterPtr->height);707}708(*typePtr->deleteProc)(masterPtr->masterData);709}710if (masterPtr->instancePtr == NULL) {711Tcl_DeleteHashEntry(masterPtr->hPtr);712ckfree((char *) masterPtr);713}714}715716/*717*----------------------------------------------------------------------718*719* TkDeleteAllImages --720*721* This procedure is called when an application is deleted. It722* calls back all of the managers for all images so that they723* can cleanup, then it deletes all of Tk's internal information724* about images.725*726* Results:727* None.728*729* Side effects:730* All information for all images gets deleted.731*732*----------------------------------------------------------------------733*/734735void736TkDeleteAllImages(mainPtr)737TkMainInfo *mainPtr; /* Structure describing application that is738* going away. */739{740Tcl_HashSearch search;741Tcl_HashEntry *hPtr;742ImageMaster *masterPtr;743744for (hPtr = Tcl_FirstHashEntry(&mainPtr->imageTable, &search);745hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {746masterPtr = (ImageMaster *) Tcl_GetHashValue(hPtr);747DeleteImage(masterPtr);748}749Tcl_DeleteHashTable(&mainPtr->imageTable);750}751752753