/*1* tkCanvImg.c --2*3* This file implements image items for canvas widgets.4*5* Copyright (c) 1994 The Regents of the University of California.6* Copyright (c) 1994-1996 Sun Microsystems, Inc.7*8* See the file "license.terms" for information on usage and redistribution9* of this file, and for a DISCLAIMER OF ALL WARRANTIES.10*11* SCCS: @(#) tkCanvImg.c 1.17 96/02/17 17:18:4312*/1314#include "tkInt.h"15#include "tkCanvas.h"1617/*18* The structure below defines the record for each image item.19*/2021typedef struct ImageItem {22Tk_Item header; /* Generic stuff that's the same for all23* types. MUST BE FIRST IN STRUCTURE. */24Tk_Canvas canvas; /* Canvas containing the image. */25double x, y; /* Coordinates of positioning point for26* image. */27Tk_Anchor anchor; /* Where to anchor image relative to28* (x,y). */29char *imageString; /* String describing -image option (malloc-ed).30* NULL means no image right now. */31Tk_Image image; /* Image to display in window, or NULL if32* no image at present. */33} ImageItem;3435/*36* Information used for parsing configuration specs:37*/3839static Tk_CustomOption tagsOption = {Tk_CanvasTagsParseProc,40Tk_CanvasTagsPrintProc, (ClientData) NULL41};4243static Tk_ConfigSpec configSpecs[] = {44{TK_CONFIG_ANCHOR, "-anchor", (char *) NULL, (char *) NULL,45"center", Tk_Offset(ImageItem, anchor), TK_CONFIG_DONT_SET_DEFAULT},46{TK_CONFIG_STRING, "-image", (char *) NULL, (char *) NULL,47(char *) NULL, Tk_Offset(ImageItem, imageString), TK_CONFIG_NULL_OK},48{TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,49(char *) NULL, 0, TK_CONFIG_NULL_OK, &tagsOption},50{TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,51(char *) NULL, 0, 0}52};5354/*55* Prototypes for procedures defined in this file:56*/5758static void ImageChangedProc _ANSI_ARGS_((ClientData clientData,59int x, int y, int width, int height, int imgWidth,60int imgHeight));61static int ImageCoords _ANSI_ARGS_((Tcl_Interp *interp,62Tk_Canvas canvas, Tk_Item *itemPtr, int argc,63char **argv));64static int ImageToArea _ANSI_ARGS_((Tk_Canvas canvas,65Tk_Item *itemPtr, double *rectPtr));66static double ImageToPoint _ANSI_ARGS_((Tk_Canvas canvas,67Tk_Item *itemPtr, double *coordPtr));68static void ComputeImageBbox _ANSI_ARGS_((Tk_Canvas canvas,69ImageItem *imgPtr));70static int ConfigureImage _ANSI_ARGS_((Tcl_Interp *interp,71Tk_Canvas canvas, Tk_Item *itemPtr, int argc,72char **argv, int flags));73static int CreateImage _ANSI_ARGS_((Tcl_Interp *interp,74Tk_Canvas canvas, struct Tk_Item *itemPtr,75int argc, char **argv));76static void DeleteImage _ANSI_ARGS_((Tk_Canvas canvas,77Tk_Item *itemPtr, Display *display));78static void DisplayImage _ANSI_ARGS_((Tk_Canvas canvas,79Tk_Item *itemPtr, Display *display, Drawable dst,80int x, int y, int width, int height));81static void ScaleImage _ANSI_ARGS_((Tk_Canvas canvas,82Tk_Item *itemPtr, double originX, double originY,83double scaleX, double scaleY));84static void TranslateImage _ANSI_ARGS_((Tk_Canvas canvas,85Tk_Item *itemPtr, double deltaX, double deltaY));8687/*88* The structures below defines the image item type in terms of89* procedures that can be invoked by generic item code.90*/9192Tk_ItemType tkImageType = {93"image", /* name */94sizeof(ImageItem), /* itemSize */95CreateImage, /* createProc */96configSpecs, /* configSpecs */97ConfigureImage, /* configureProc */98ImageCoords, /* coordProc */99DeleteImage, /* deleteProc */100DisplayImage, /* displayProc */1010, /* alwaysRedraw */102ImageToPoint, /* pointProc */103ImageToArea, /* areaProc */104(Tk_ItemPostscriptProc *) NULL, /* postscriptProc */105ScaleImage, /* scaleProc */106TranslateImage, /* translateProc */107(Tk_ItemIndexProc *) NULL, /* indexProc */108(Tk_ItemCursorProc *) NULL, /* icursorProc */109(Tk_ItemSelectionProc *) NULL, /* selectionProc */110(Tk_ItemInsertProc *) NULL, /* insertProc */111(Tk_ItemDCharsProc *) NULL, /* dTextProc */112(Tk_ItemType *) NULL /* nextPtr */113};114115/*116*--------------------------------------------------------------117*118* CreateImage --119*120* This procedure is invoked to create a new image121* item in a canvas.122*123* Results:124* A standard Tcl return value. If an error occurred in125* creating the item, then an error message is left in126* interp->result; in this case itemPtr is left uninitialized,127* so it can be safely freed by the caller.128*129* Side effects:130* A new image item is created.131*132*--------------------------------------------------------------133*/134135static int136CreateImage(interp, canvas, itemPtr, argc, argv)137Tcl_Interp *interp; /* Interpreter for error reporting. */138Tk_Canvas canvas; /* Canvas to hold new item. */139Tk_Item *itemPtr; /* Record to hold new item; header140* has been initialized by caller. */141int argc; /* Number of arguments in argv. */142char **argv; /* Arguments describing rectangle. */143{144ImageItem *imgPtr = (ImageItem *) itemPtr;145146if (argc < 2) {147Tcl_AppendResult(interp, "wrong # args: should be \"",148Tk_PathName(Tk_CanvasTkwin(canvas)), " create ",149itemPtr->typePtr->name, " x y ?options?\"",150(char *) NULL);151return TCL_ERROR;152}153154/*155* Initialize item's record.156*/157158imgPtr->canvas = canvas;159imgPtr->anchor = TK_ANCHOR_CENTER;160imgPtr->imageString = NULL;161imgPtr->image = NULL;162163/*164* Process the arguments to fill in the item record.165*/166167if ((Tk_CanvasGetCoord(interp, canvas, argv[0], &imgPtr->x) != TCL_OK)168|| (Tk_CanvasGetCoord(interp, canvas, argv[1], &imgPtr->y)169!= TCL_OK)) {170return TCL_ERROR;171}172173if (ConfigureImage(interp, canvas, itemPtr, argc-2, argv+2, 0) != TCL_OK) {174DeleteImage(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));175return TCL_ERROR;176}177return TCL_OK;178}179180/*181*--------------------------------------------------------------182*183* ImageCoords --184*185* This procedure is invoked to process the "coords" widget186* command on image items. See the user documentation for187* details on what it does.188*189* Results:190* Returns TCL_OK or TCL_ERROR, and sets interp->result.191*192* Side effects:193* The coordinates for the given item may be changed.194*195*--------------------------------------------------------------196*/197198static int199ImageCoords(interp, canvas, itemPtr, argc, argv)200Tcl_Interp *interp; /* Used for error reporting. */201Tk_Canvas canvas; /* Canvas containing item. */202Tk_Item *itemPtr; /* Item whose coordinates are to be203* read or modified. */204int argc; /* Number of coordinates supplied in205* argv. */206char **argv; /* Array of coordinates: x1, y1,207* x2, y2, ... */208{209ImageItem *imgPtr = (ImageItem *) itemPtr;210char x[TCL_DOUBLE_SPACE], y[TCL_DOUBLE_SPACE];211212if (argc == 0) {213Tcl_PrintDouble(interp, imgPtr->x, x);214Tcl_PrintDouble(interp, imgPtr->y, y);215Tcl_AppendResult(interp, x, " ", y, (char *) NULL);216} else if (argc == 2) {217if ((Tk_CanvasGetCoord(interp, canvas, argv[0], &imgPtr->x) != TCL_OK)218|| (Tk_CanvasGetCoord(interp, canvas, argv[1],219&imgPtr->y) != TCL_OK)) {220return TCL_ERROR;221}222ComputeImageBbox(canvas, imgPtr);223} else {224sprintf(interp->result,225"wrong # coordinates: expected 0 or 2, got %d", argc);226return TCL_ERROR;227}228return TCL_OK;229}230231/*232*--------------------------------------------------------------233*234* ConfigureImage --235*236* This procedure is invoked to configure various aspects237* of an image item, such as its anchor position.238*239* Results:240* A standard Tcl result code. If an error occurs, then241* an error message is left in interp->result.242*243* Side effects:244* Configuration information may be set for itemPtr.245*246*--------------------------------------------------------------247*/248249static int250ConfigureImage(interp, canvas, itemPtr, argc, argv, flags)251Tcl_Interp *interp; /* Used for error reporting. */252Tk_Canvas canvas; /* Canvas containing itemPtr. */253Tk_Item *itemPtr; /* Image item to reconfigure. */254int argc; /* Number of elements in argv. */255char **argv; /* Arguments describing things to configure. */256int flags; /* Flags to pass to Tk_ConfigureWidget. */257{258ImageItem *imgPtr = (ImageItem *) itemPtr;259Tk_Window tkwin;260Tk_Image image;261262tkwin = Tk_CanvasTkwin(canvas);263if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc,264argv, (char *) imgPtr, flags) != TCL_OK) {265return TCL_ERROR;266}267268/*269* Create the image. Save the old image around and don't free it270* until after the new one is allocated. This keeps the reference271* count from going to zero so the image doesn't have to be recreated272* if it hasn't changed.273*/274275if (imgPtr->imageString != NULL) {276image = Tk_GetImage(interp, tkwin, imgPtr->imageString,277ImageChangedProc, (ClientData) imgPtr);278if (image == NULL) {279return TCL_ERROR;280}281} else {282image = NULL;283}284if (imgPtr->image != NULL) {285Tk_FreeImage(imgPtr->image);286}287imgPtr->image = image;288ComputeImageBbox(canvas, imgPtr);289return TCL_OK;290}291292/*293*--------------------------------------------------------------294*295* DeleteImage --296*297* This procedure is called to clean up the data structure298* associated with a image item.299*300* Results:301* None.302*303* Side effects:304* Resources associated with itemPtr are released.305*306*--------------------------------------------------------------307*/308309static void310DeleteImage(canvas, itemPtr, display)311Tk_Canvas canvas; /* Info about overall canvas widget. */312Tk_Item *itemPtr; /* Item that is being deleted. */313Display *display; /* Display containing window for314* canvas. */315{316ImageItem *imgPtr = (ImageItem *) itemPtr;317318if (imgPtr->imageString != NULL) {319ckfree(imgPtr->imageString);320}321if (imgPtr->image != NULL) {322Tk_FreeImage(imgPtr->image);323}324}325326/*327*--------------------------------------------------------------328*329* ComputeImageBbox --330*331* This procedure is invoked to compute the bounding box of332* all the pixels that may be drawn as part of a image item.333* This procedure is where the child image's placement is334* computed.335*336* Results:337* None.338*339* Side effects:340* The fields x1, y1, x2, and y2 are updated in the header341* for itemPtr.342*343*--------------------------------------------------------------344*/345346/* ARGSUSED */347static void348ComputeImageBbox(canvas, imgPtr)349Tk_Canvas canvas; /* Canvas that contains item. */350ImageItem *imgPtr; /* Item whose bbox is to be351* recomputed. */352{353int width, height;354int x, y;355356x = imgPtr->x + ((imgPtr->x >= 0) ? 0.5 : - 0.5);357y = imgPtr->y + ((imgPtr->y >= 0) ? 0.5 : - 0.5);358359if (imgPtr->image == None) {360imgPtr->header.x1 = imgPtr->header.x2 = x;361imgPtr->header.y1 = imgPtr->header.y2 = y;362return;363}364365/*366* Compute location and size of image, using anchor information.367*/368369Tk_SizeOfImage(imgPtr->image, &width, &height);370switch (imgPtr->anchor) {371case TK_ANCHOR_N:372x -= width/2;373break;374case TK_ANCHOR_NE:375x -= width;376break;377case TK_ANCHOR_E:378x -= width;379y -= height/2;380break;381case TK_ANCHOR_SE:382x -= width;383y -= height;384break;385case TK_ANCHOR_S:386x -= width/2;387y -= height;388break;389case TK_ANCHOR_SW:390y -= height;391break;392case TK_ANCHOR_W:393y -= height/2;394break;395case TK_ANCHOR_NW:396break;397case TK_ANCHOR_CENTER:398x -= width/2;399y -= height/2;400break;401}402403/*404* Store the information in the item header.405*/406407imgPtr->header.x1 = x;408imgPtr->header.y1 = y;409imgPtr->header.x2 = x + width;410imgPtr->header.y2 = y + height;411}412413/*414*--------------------------------------------------------------415*416* DisplayImage --417*418* This procedure is invoked to draw a image item in a given419* drawable.420*421* Results:422* None.423*424* Side effects:425* ItemPtr is drawn in drawable using the transformation426* information in canvas.427*428*--------------------------------------------------------------429*/430431static void432DisplayImage(canvas, itemPtr, display, drawable, x, y, width, height)433Tk_Canvas canvas; /* Canvas that contains item. */434Tk_Item *itemPtr; /* Item to be displayed. */435Display *display; /* Display on which to draw item. */436Drawable drawable; /* Pixmap or window in which to draw437* item. */438int x, y, width, height; /* Describes region of canvas that439* must be redisplayed (not used). */440{441ImageItem *imgPtr = (ImageItem *) itemPtr;442short drawableX, drawableY;443444if (imgPtr->image == NULL) {445return;446}447448/*449* Translate the coordinates to those of the image, then redisplay it.450*/451452Tk_CanvasDrawableCoords(canvas, (double) x, (double) y,453&drawableX, &drawableY);454Tk_RedrawImage(imgPtr->image, x - imgPtr->header.x1, y - imgPtr->header.y1,455width, height, drawable, drawableX, drawableY);456}457458/*459*--------------------------------------------------------------460*461* ImageToPoint --462*463* Computes the distance from a given point to a given464* rectangle, in canvas units.465*466* Results:467* The return value is 0 if the point whose x and y coordinates468* are coordPtr[0] and coordPtr[1] is inside the image. If the469* point isn't inside the image then the return value is the470* distance from the point to the image.471*472* Side effects:473* None.474*475*--------------------------------------------------------------476*/477478static double479ImageToPoint(canvas, itemPtr, coordPtr)480Tk_Canvas canvas; /* Canvas containing item. */481Tk_Item *itemPtr; /* Item to check against point. */482double *coordPtr; /* Pointer to x and y coordinates. */483{484ImageItem *imgPtr = (ImageItem *) itemPtr;485double x1, x2, y1, y2, xDiff, yDiff;486487x1 = imgPtr->header.x1;488y1 = imgPtr->header.y1;489x2 = imgPtr->header.x2;490y2 = imgPtr->header.y2;491492/*493* Point is outside rectangle.494*/495496if (coordPtr[0] < x1) {497xDiff = x1 - coordPtr[0];498} else if (coordPtr[0] > x2) {499xDiff = coordPtr[0] - x2;500} else {501xDiff = 0;502}503504if (coordPtr[1] < y1) {505yDiff = y1 - coordPtr[1];506} else if (coordPtr[1] > y2) {507yDiff = coordPtr[1] - y2;508} else {509yDiff = 0;510}511512return hypot(xDiff, yDiff);513}514515/*516*--------------------------------------------------------------517*518* ImageToArea --519*520* This procedure is called to determine whether an item521* lies entirely inside, entirely outside, or overlapping522* a given rectangle.523*524* Results:525* -1 is returned if the item is entirely outside the area526* given by rectPtr, 0 if it overlaps, and 1 if it is entirely527* inside the given area.528*529* Side effects:530* None.531*532*--------------------------------------------------------------533*/534535static int536ImageToArea(canvas, itemPtr, rectPtr)537Tk_Canvas canvas; /* Canvas containing item. */538Tk_Item *itemPtr; /* Item to check against rectangle. */539double *rectPtr; /* Pointer to array of four coordinates540* (x1, y1, x2, y2) describing rectangular541* area. */542{543ImageItem *imgPtr = (ImageItem *) itemPtr;544545if ((rectPtr[2] <= imgPtr->header.x1)546|| (rectPtr[0] >= imgPtr->header.x2)547|| (rectPtr[3] <= imgPtr->header.y1)548|| (rectPtr[1] >= imgPtr->header.y2)) {549return -1;550}551if ((rectPtr[0] <= imgPtr->header.x1)552&& (rectPtr[1] <= imgPtr->header.y1)553&& (rectPtr[2] >= imgPtr->header.x2)554&& (rectPtr[3] >= imgPtr->header.y2)) {555return 1;556}557return 0;558}559560/*561*--------------------------------------------------------------562*563* ScaleImage --564*565* This procedure is invoked to rescale an item.566*567* Results:568* None.569*570* Side effects:571* The item referred to by itemPtr is rescaled so that the572* following transformation is applied to all point coordinates:573* x' = originX + scaleX*(x-originX)574* y' = originY + scaleY*(y-originY)575*576*--------------------------------------------------------------577*/578579static void580ScaleImage(canvas, itemPtr, originX, originY, scaleX, scaleY)581Tk_Canvas canvas; /* Canvas containing rectangle. */582Tk_Item *itemPtr; /* Rectangle to be scaled. */583double originX, originY; /* Origin about which to scale rect. */584double scaleX; /* Amount to scale in X direction. */585double scaleY; /* Amount to scale in Y direction. */586{587ImageItem *imgPtr = (ImageItem *) itemPtr;588589imgPtr->x = originX + scaleX*(imgPtr->x - originX);590imgPtr->y = originY + scaleY*(imgPtr->y - originY);591ComputeImageBbox(canvas, imgPtr);592}593594/*595*--------------------------------------------------------------596*597* TranslateImage --598*599* This procedure is called to move an item by a given amount.600*601* Results:602* None.603*604* Side effects:605* The position of the item is offset by (xDelta, yDelta), and606* the bounding box is updated in the generic part of the item607* structure.608*609*--------------------------------------------------------------610*/611612static void613TranslateImage(canvas, itemPtr, deltaX, deltaY)614Tk_Canvas canvas; /* Canvas containing item. */615Tk_Item *itemPtr; /* Item that is being moved. */616double deltaX, deltaY; /* Amount by which item is to be617* moved. */618{619ImageItem *imgPtr = (ImageItem *) itemPtr;620621imgPtr->x += deltaX;622imgPtr->y += deltaY;623ComputeImageBbox(canvas, imgPtr);624}625626/*627*----------------------------------------------------------------------628*629* ImageChangedProc --630*631* This procedure is invoked by the image code whenever the manager632* for an image does something that affects the image's size or633* how it is displayed.634*635* Results:636* None.637*638* Side effects:639* Arranges for the canvas to get redisplayed.640*641*----------------------------------------------------------------------642*/643644static void645ImageChangedProc(clientData, x, y, width, height, imgWidth, imgHeight)646ClientData clientData; /* Pointer to canvas item for image. */647int x, y; /* Upper left pixel (within image)648* that must be redisplayed. */649int width, height; /* Dimensions of area to redisplay650* (may be <= 0). */651int imgWidth, imgHeight; /* New dimensions of image. */652{653ImageItem *imgPtr = (ImageItem *) clientData;654655/*656* If the image's size changed and it's not anchored at its657* northwest corner then just redisplay the entire area of the658* image. This is a bit over-conservative, but we need to do659* something because a size change also means a position change.660*/661662if (((imgPtr->header.x2 - imgPtr->header.x1) != imgWidth)663|| ((imgPtr->header.y2 - imgPtr->header.y1) != imgHeight)) {664x = y = 0;665width = imgWidth;666height = imgHeight;667Tk_CanvasEventuallyRedraw(imgPtr->canvas, imgPtr->header.x1,668imgPtr->header.y1, imgPtr->header.x2, imgPtr->header.y2);669}670ComputeImageBbox(imgPtr->canvas, imgPtr);671Tk_CanvasEventuallyRedraw(imgPtr->canvas, imgPtr->header.x1 + x,672imgPtr->header.y1 + y, (int) (imgPtr->header.x1 + x + width),673(int) (imgPtr->header.y1 + y + height));674}675676677